how@milhow1.UU.NET (Mike Howard) (03/26/91)
Submitted-by: Mike Howard <how%milhow1@uunet.uu.net> Archive-name: simple_menu/part02 part 2 of simple menu --------------------------------cut here-------------------------------------- #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 2 (of 2)." # Contents: simple_menu.1 simple_menu.c # Wrapped by mike@milhow4 on Mon Mar 25 16:45:32 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'simple_menu.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'simple_menu.1'\" else echo shar: Extracting \"'simple_menu.1'\" \(12214 characters\) sed "s/^X//" >'simple_menu.1' <<'END_OF_FILE' X. \" -*- nroff -*- X. \" @(#)simple_menu.1 1.5 91/03/25 X.DA March 22, 1991 X.TH SIMPLE_MENU 1 "local" X.SH NAME Xsimple_menu \- executes a simple minded menu X.SH SYNOPSIS X.B simple_menu X[ -h | options ] [menu-definition-file-name | -] X.SH DESCRIPTION X.PP X.B simple_menu Xprovides a simple user interface consisting of one or more menus Xof numbered choices. XThe interface is intentionally simple in order to minimize the Xdifficulties inherent in messing with vdu's. X.PP XSimplified Theory of operation: XIn the context of this program, a menu consists of a collection of Xenumerated choices. XChoices are of one of two types: X.HP XA sub menu X.HP XA shell script. X.PP XThe user either selects a choice or quits. If the user selects a Xchoice which is a menu, the program recursively displays that menu and Xetc. If the user selects a shell process, then the program builds a Xshell script in a temporary file and feeds it to X.I /bin/sh. XIf the user quits, the program terminates. X.PP XThings are a bit more complex than that, but this is the basic model. X.PP XA minimum of cosmetics is supported: X.HP X\- the title for the menu may be displayed in ``stand out mode'' X.HP X\- blank lines may be placed in the menu. X.HP X\- menu choices can be either numbers or upper case letters. X.HP X\- the screen may be cleared on each menu choice cycle. X.PP XThe menu definition is read from a menu definition file. The file may Xbe: X.HP X\- standard input \- if the file is explicitly named ``\-''. X.HP X\- an absolute file \- if the file name begins with a slash (/). X.HP X\- a file in the current directory \- if the file name begins with ./ X.HP X\- the named file or X.I menu.def Xif it can be found on in any directory in the X.I menu path. XThe X.I menu path Xis a colon separated list of directory paths which may be specified Xdirectly on the command line using X.B -M Xoption, by the environment variable X.B MENU_PATH, Xor by default. XThe default menu path is: X.IP X$HOME:/usr/local/lib/simple_menu:/usr/local/lib/dumb_menu X.PP XSystem interaction is performed by executing shell scripts as Xsubprocesses. Customization of these scripts is supported by Xmenu-environment variables and shell script parameters. These are Xstring valued variables which set in the manners defined below and are Xdefined at the beginning of the script which is written to the Xtemporary file. X.PP XCommand Line Options: X.PP X.B -M path Xsets the search path for the menu definition file. X.PP X.B -s path Xsets path for the shell which will execute the selected menu command. XDefault is X.I /bin/sh X.PP X.B -f fmt Xsets the X.I printf Xstyle format string used to define parameters while building the shell Xscript temporary file. The default string is X.I \"%s=\\\"%s\\\"\\n. XReplacement format strings must have two (2) %s format specifications Xand must include the newline character. This is added as a creeping Xfeature rather than because we really expect to need it. X.PP X.B -D Xincrements debug mode. XA single X.B -D Xcauses X.B simple_menu Xto parse the menu definition file and then displays its Xinterpretation. X.B -DD Xcauses some additional output to occur during the parsing phase. XThese options are supposed to make up for poor error handling by the Xparser and to aid in debugging menu definitions. X.B -v Ximplements a quasi-verbose mode. This is done by prepending the XBourne shell command X.I set -xv Xto the front of each shell script prior to feeding it to a shell. XNote that this assumes that ``set -xv'' does what you want. The Xpurpose of this option is to facilitate debugging shell scripts in Xsyntactically correct menu definition files. X.sp X.PP XMenu Definition Language: X.PP XLexical tokens consist of key words, names, text-strings, white Xspace, and the semi-colon (;). X.PP X.B names Xare text delimited by double parenthesis (") and consisting only of Xupper and lower case letters, digits, or the underscore character. X.B names Xare used to name sub menus and shell script parameters. Menus must Xbe named in order to be referenced in X.B do-menu Xmenu items; shell script parameters must be named in order to instantiated Xin the associated shell scripts. X.PP X.B text-strings Xare streams of arbitrary text and may be written in three different Xways. Be warned that all text strings have X.I all Xleading and trailing white space stripped when they are used. They Xmay be written in three different ways. Convention (and design) tends Xto use the First method for shell scripts and the Second for prompt Xstrings, titles, and file names. X.HP XFirst: delimited at the beginning by the Xsequence %( and at the end by the sequence %). Any non-null Xcharacters may be included except the terminating sequence. If the Xterminating sequence must be included, as is the case for dynamically Xconstructed menus, it must be written %%). This is the preferred form Xfor writing shell script actions. X.HP XSecond: delimited by single quote marks ('). The single quote mark Xmay be included if doubled, i.e. 'a single quote '' mark'. This is Xthe preferred form for writing user displayed text and parameter Xinitializations. X.HP XThird: delimited by curly braces '{' and '}'. The right curly brace Xmay be included if preceded by a backslash character '\\' \- i.e. X\&'\\}'. Similarly, the backslash must be doubled ('\\\\')if it is to Xbe included. This style is for backward compatibility reasons and was Xa mistake \- so don't use it. This is not the preferred form for Xanything except to make some of my old menus continue to work. X.PP X.B text-strings Xare used for all shell scripts and for all Xuser visible text \- menu titles, choice and parameter prompts, Xand initial values for parameters. X.PP X.B key words Xare case insensitive and can not be abbreviated or misspelled. X.PP X.B white space Xseparates tokens and the X.B semi-colon Xis used to terminate some statements. X.PP XA menu definition file is a plain text file consisting of an optional Xenvironment section followed by one or more menu definitions. The Xenvironment section allows the definition of alternate command Xprocessor and associated assignment format and the definition of Xmenu-environment variables. X.sp X.PP XThe path to the shell which executes scripts and associated variable Xassignment format are changed by the statements: X.br X.nf X.sp Xshell-path = 'new path' ; # default is '/bin/sh' X.sp Xasg-fmt = 'new fmt' ; # default is '"%s=\"%s\"\n"' X # try '"setenv %s %s\n"' X.fi X.PP XThe shell path should be an absolute file name. The asg-fmt is a Xformat string which is passed to X.I sprintf Xand must contain format specifications for two strings. The left Xstring is always the parameter identifier and the right string is Xalways the value. X.B Neither Xare checked for validity. X.PP XThe menu-environment variables are variables which take their values Xeither from the user's environment or from a named file. They may Xoptionally have a declared default value. X.IP XFor example: X.br X.nf Xfrom-env "EV_ONE" ; X.sp Xfrom-env "EV_TWO" = 'default value' ; X.sp Xfrom-file '/usr/lib/foo-file' "EV_THREE" ; X.sp Xfrom-file '/usr/lib/foo-file' "EV_FOUR" = 'default value two' ; X.fi X.PP XThe variables X.B EV_ONE Xand X.B EV_TWO Xhave default values of "" and "default value", respectively. The Xuser's environment is queried to see if these variables are defined Xand, if they are, the user's environment values are used, otherwise Xthe default values are retained. X.PP XThe variables X.B EV_THREE Xand X.B EV_FOUR Xhave default values of "" and "default value two", respectively. The Xfile X.I /usr/lib/foo-file Xis examined, if it exists, looking for lines of the form: X.br X.nf XEV_THREE = some stuff X.sp XEV_FOUR = some stuff X.fi X.PP XIf definition lines exist in X.I foo-file Xthen the values to the right of the equal sign (=) and up to the end Xof line are used for the values of these variables, otherwise they Xretain their defaults. All lines which are not of the form described Xabove in foo-file are ignored, so there is quite a bit of latitude Xhere for commenting text and other things. The first valid definition Xis used. X.sp X.PP XThe first menu definition is the top Xlevel menu and is named X.B MAIN Xby default. Subsequent menu definitions must be named using the X.B menu Xkeyword and will only be invoked if they are referred to by a X.B do-menu Xcommand in the main menu or one of its descendents. X.sp X.PP XMenu declaration statements: X.HP X.B menu name X\- begins and names a menu. This is optional for the first menu and Xmandatory for subsequent menus. X.IP XFor example, X.br X.nf Xmenu "A_FUNKY_menu_Name" X.fi X.HP X.B title text-string X\- defines the title of the menu. This is mandatory. It follows the X.B menu Xstatement and precedes all menu items. X.IP XFor example, X.br X.nf Xtitle %( this is the text which will Xbe displayed at the top of the menu X all leading and trailing white space will be stripped, X but not any interior white space X%) X.fi X.sp X.PP XMenu Options: Xzero or more of the following menu options may follow the title and Xprecede the menu items. X.HP X.B bold X\- requests that the menu title be displayed in standout mode. X.HP X.B clear X\- requests that the screen be cleared prior to displaying and Xredisplaying the menu. X.HP X.B wait X\- applies to menus which use the X.B clear Xoption. The default action upon finishing a menu item for a X.B clear Xmenu is to wait for either 10 seconds or the user to hit the return Xkey. If the X.B wait Xoption is included, then we wait for the user to hit the return key. X.HP X.B always-show X\- requests that the menu choices be always displayed. The default Xaction is to display all choices upon initiation of a menu and then to Xprompt for choice, quit, or ? \- where '?' causes the choices to be Xdisplayed. This is a ``feature'' to accommodate slow terminals. X.HP X.B alpha X\- list the choices using upper case letters rather than numbers X(default). X.HP X.B once X\- run the menu one time only rather than looping until done. I Xsuppose there are applications for this, but I suspect that I had an Xattack of ``creeping featurism'' and succumbed. X.sp X.PP XMenu Items: Xone or more of the following items must be present. These define the Xmenu actions. X.HP X.B Shell Item X\- a shell item consists of some prompt text, a shell script, and zero Xor more shell script parameters. X.IP XFor example, X.nf Xshell ' this text is associated with this choice ' X%( X echo $PARM1 $PARM2 X%) Xparm "PARM1" 'input value for PARM1' Xparm "PARM2" = 'foo' 'input value for PARM2' Xparm "PARM3" = "EV_ONE" ' input value for PARM3' X; X.fi X.IP XIn the above example, three parameters are set - the first has no Xdefault value. The second has a default value declared in the menu Xdefinition. The last one takes its default value from the Xmenu-environment variable EV_ONE. X.B simple_menu Xwill display this default and will substitute it if the user simply X\&``hits return''. All white space strings are used \- we assume that Xthe user knows what he is doing and really does want Xleading/trailing/all white space for his variable. X.HP X.B Prelude Item X\- is identical to a normal item except that: (1) it begins with the Xkey word X.B prelude Xand (2) the shell script is run X.I prior Xto displaying the menu. X.IP XFor example, X.nf Xprelude ' checking for mail ' X%( X mail X%) X; X.fi X.HP X.B Epilogue Item X\- parallels the X.B prelude Xitem except that its shell script is run after terminating the menu. XIt begins with the key word X.B epilogue. X.HP X.B Menu Item X\- specifies that the named sub-menu is to be run. X.IP XFor example, X.nf Xdo-menu "FOO" X' run FOO menu ' X; X.fi X.HP X.B Skip Item X\- Xconsists of the sequence X.B skip ; Xwhich causes a blank line to occur in the menu between this and the Xsucceeding menu item. X.HP X.B commenting X\- exterior to text-strings, the hash mark (#) causes itself and the Xremainder of that line to be ignored. This allows both full comment Xlines and in-line comments. X.HP X.B white space X\- white space is ignored, so it may be used liberally to make the Xdefinitions look nice. Further, all leading and trailing white space Xin text-strings is stripped. X.PP XSee the sample menus for more detail and creative ideas. X.SH DIAGNOSTICS X.PP XThe diagnostics during the parse of the menu definition are all but Xnon-existent. X.SH BUGS X.PP XHas no provision for menus which exceed one screen in size. END_OF_FILE if test 12214 -ne `wc -c <'simple_menu.1'`; then echo shar: \"'simple_menu.1'\" unpacked with wrong size! fi # end of 'simple_menu.1' fi if test -f 'simple_menu.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'simple_menu.c'\" else echo shar: Extracting \"'simple_menu.c'\" \(18494 characters\) sed "s/^X//" >'simple_menu.c' <<'END_OF_FILE' X/* %W% %D% */ X Xstatic char *cpy_str = X "Copyright (c), Mike Howard, 1990,1991 all rights reserved"; X X/* Conditions of use: X X This software is not for sale and is not to be sold by or X to anyone. X X You may use this software and may distribute it to anyone X you wish to provided you distribute the entire distribution X package w/o any deletions (i.e. include all the source code). X X I do not warrent this software to do anything at all and X am not responsible for anything which happens as a result of X its use. X*/ X X#include <stdio.h> X#include <fcntl.h> X#include <string.h> X#include <signal.h> X#include <ctype.h> X X#define MAIN X#include "simple_menu.h" X#include "patchlevel.h" X Xint Xmain(argc, argv) Xint argc; Xchar **argv; X{ X init(argc, argv); X X if (yyparse()) X fatal("Parse Error"); X X if (lex_errors || yacc_errors || check_menu()) X fatal("corrupt menu definition"); X X init_environment(); X init_parm_defaults(); X X if (debug_mode) { X display_parsed_menus(); X simple_menu_exit(0); X } X X /* close all input - rely on terminal stuff to deal with all I/O */ X init_terminal(); X X push_menu(menu_list_head); X do_menu(); X X /* not reached */ X simple_menu_exit(0); X} X XVOID Xdisplay_parsed_menus() X{ X int item_idx; X struct menu *menu_ptr; X struct ev_var *ev_ptr; X char *cp; X char c; X X printf("shell path: '%s'\n", cmd_path); X printf("asg-fmt: '"); X cp = asg_fmt; X while (c = *cp++) { X if (iscntrl(c)) { X switch (c) { X case '\n': X putchar('\\'); X putchar('n'); X break; X case '\t': X putchar('\\'); X putchar('t'); X break; X case '\f': X putchar('\\'); X putchar('f'); X break; X case '\b': X putchar('\\'); X putchar('b'); X break; X default: X putchar('^'); X putchar(c | 0x40); X break; X } X } X else if (c == '"' || c == '\\') { X putchar('\\'); X putchar(c); X } X else X putchar(c); X } X putchar('\n'); X X if (environment_list) { X for (ev_ptr=environment_list;ev_ptr;ev_ptr=ev_ptr->next) { X switch (ev_ptr->flag) { X case EV_FROM_FILE: X printf("Env variable %s from File %s\n default: '%s'\n", X ev_ptr->identifier, ev_ptr->file_name, X ev_ptr->deflt ? ev_ptr->deflt : ""); X break; X case EV_FROM_ENV: X printf("Env variable %s from environment\n default: '%s'\n", X ev_ptr->identifier, ev_ptr->deflt ? ev_ptr->deflt : ""); X break; X } X printf(" value: '%s'\n", ev_ptr->value); X } X } X else X printf("No environment variables defined\n"); X X menu_ptr = menu_list_head; X while (menu_ptr) { X item_idx = 1; X printf("\nMenu: %s\nPrompt: %s\n", menu_ptr->menu_name, X menu_ptr->menu_title); X printf("Options: %s %s %s\n", X (menu_ptr->flags&CLEAR_FLAG) ? "clear" : "", X (menu_ptr->flags&BOLD_FLAG) ? "bold" : "", X (menu_ptr->flags&ALWAYS_DISPLAY_FLAG) ? "always-display" : ""); X if (menu_ptr->prelude) X prt_item(menu_ptr->prelude); X for (selected_item=menu_ptr->item_head;selected_item; X selected_item = selected_item->next) X prt_item(selected_item); X if (menu_ptr->epilogue) X prt_item(menu_ptr->epilogue); X menu_ptr = menu_ptr->next; X } X X printf("\ndisplaying all menus\n"); X while (menu_list_head) { X push_menu(menu_list_head); X display_menu(); X menu_list_head = menu_list_head->next; X } X simple_menu_exit(0); X} X Xstatic char *hlp[] = { X"Option Function", X"-M path menu path", X"-s path path to shell to execute scripts", X"-f fmt variable assignment format [default = \"%s=\\\"%s\\\"", X"-v make command execution verbose (via set -xv)", X"-D increment debug mode", X"-t num set default timeout value to 'num' seconds [10]", X(char *)0}; X XVOID Xinit(argc, argv) Xint argc; Xchar **argv; X{ X int i; X int c; X extern char *optarg; X extern int optind, opterr; X X /* initializations */ X argcc = argc; X argvv = argv; X progname = argv[0]; X default_timeout = 10; X in_fname = "stdin"; X cmd_path = "/bin/sh"; X asg_fmt = "%s=\"%s\"\n"; X display_menu_flag = 1; X X while ((c = getopt(argc, argv, "hs:f:m:M:vDt:")) != EOF) { X switch (c) { X case 'h': X for (i=0;hlp[i];i++) X printf("%s\n", hlp[i]); X fatal((char *)0); X case 's': X cmd_path = optarg; X break; X case 'f': X asg_fmt = optarg; X break; X case 'm': X /* this option is for backward compatability. I did not handle X menu file name specification correctly in the original version X and am too lazy to change everything I have done. It will come X out some day. */ X menu_fname = optarg; X break; X case 'M': X menu_path = optarg; X break; X case 'v': X verbose++; X break; X case 'D': X debug_mode++; X break; X case 't': X if ((default_timeout = atoi(optarg)) < 0) X default_timeout = 0; X break; X case '?': X fatal((char *)0); X } X } X X if (!menu_fname) X menu_fname = optind == argc ? MENU_FNAME : argv[optind]; X open_menu_file(); X X if (cmd_name = strrchr(cmd_path, '/')) X cmd_name++; X else X cmd_name = cmd_path; X} X Xstruct parm * Xmake_new_parm(flag, identifier, prompt, def_or_evname) X int flag; X char *identifier; X char *prompt; X char *def_or_evname; X{ X struct parm *parm_ptr = (struct parm *)Malloc(sizeof(struct parm)); X X parm_ptr->next = (struct parm *)0; X parm_ptr->flag = flag; X parm_ptr->prompt = prompt; X parm_ptr->identifier = identifier; X parm_ptr->value = ""; X parm_ptr->def_or_evname = def_or_evname; X X return parm_ptr; X} X Xstruct ev_var * Xmake_new_ev_var(flag, identifier, deflt, fname) X int flag; X char *identifier; X char *deflt; X char *fname; X{ X struct ev_var *ev_ptr = (struct ev_var *)Malloc(sizeof(struct ev_var)); X X ev_ptr->next = (struct ev_var *)0; X ev_ptr->flag = flag; X ev_ptr->identifier = identifier; X ev_ptr->value = ""; X ev_ptr->deflt = deflt ? deflt : ""; X ev_ptr->file_name = fname ? fname : ""; X X return ev_ptr; X} X Xstruct item * Xmake_new_item(prompt, command, parms, action) X char *prompt; X char *command; X struct parm *parms; X int action; X{ X struct item *item_ptr = (struct item *)Malloc(sizeof(struct item)); X X item_ptr->next = (struct item *)0; X item_ptr->prompt = prompt; X item_ptr->command = command; X item_ptr->parms = parms; X item_ptr->action = action; X X return item_ptr; X} X Xstruct menu * Xmake_new_menu() X{ X struct menu *menu_ptr = (struct menu *)Malloc(sizeof(struct menu)); X X menu_ptr->next = (struct menu *)0; X menu_ptr->menu_stack_ptr = (struct menu *)0; X menu_ptr->item_head = (struct item *)0; X menu_ptr->item_tail = (struct item *)0; X menu_ptr->prelude = (struct item *)0; X menu_ptr->epilogue = (struct item *)0; X menu_ptr->menu_name = "MAIN"; X menu_ptr->menu_title = (char *)0; X menu_ptr->flags = 0; X menu_ptr->item_head = X menu_ptr->item_tail = (struct item *)0; X menu_ptr->max_item = 0; X X return menu_ptr; X} X X XVOID Xinit_environment() X{ X struct ev_var *ev_ptr; X int c; X char *cp; X char *buf; X char *getenv(); X int fd; X X for (ev_ptr=environment_list;ev_ptr;ev_ptr=ev_ptr->next) { X switch (ev_ptr->flag) { X case EV_FROM_ENV: X ev_ptr->value = (cp = getenv(ev_ptr->identifier)) ? cp : X ev_ptr->deflt; X break; X case EV_FROM_FILE: X if ((fd = open(ev_ptr->file_name, O_RDONLY)) < 0) { X fprintf(stderr, "from-file environment variable %s requests non-existant file %s\n", X ev_ptr->identifier, ev_ptr->file_name); X ev_ptr->value = ev_ptr->deflt; X break; X } X flush_char_input(); X while (1) { X while (isspace(c = next_char(fd))) X ; X if (!c) X break; X while (isalnum(c) || c == '_') { X add_char(c); X c = next_char(fd); X } X /* if no token on line, then skip to end of line */ X if (!(cp = take_saved_text())) { X while ((c = next_char(fd)) && c != '\n') X ; X continue; X } X /* if this matches the identifier, then find the value and assign it */ X if (cp[0] != ev_ptr->identifier[0] || X !strcmp(cp, ev_ptr->identifier)) { X free(cp); X while (c != '=' && c != '\n' && c) X c = next_char(fd); X if (c == '\n' || c == '\0') { X ev_ptr->value = ""; X break; X } X while ((c = next_char(fd)) && c != '\n') X add_char(c); X strip_white_space(); X if (!(ev_ptr->value = take_saved_text())) X ev_ptr->value = ""; X break; X } X /* haven't found correct line, so flush rest of line */ X free(cp); X while ((c = next_char(fd)) && c != '\n') X ; X } X close(fd); X break; X } X } X} X Xstruct ev_var * Xfind_ev_var_by_name(name) Xchar *name; X{ X struct ev_var *ev_ptr = environment_list; X X while (ev_ptr) { X if (ev_ptr->identifier[0] == name[0] && X !strcmp(ev_ptr->identifier, name)) X return ev_ptr; X ev_ptr = ev_ptr->next; X } X X return (struct ev_var *)0; X} X X/* parm stuff */ X XVOID Xinit_parm_defaults() X{ X struct menu *m_ptr; X struct item *i_ptr; X X for (m_ptr=menu_list_head;m_ptr;m_ptr=m_ptr->next) { X init_item_parm_defaults(m_ptr->prelude); X for (i_ptr=m_ptr->item_head;i_ptr;i_ptr=i_ptr->next) X init_item_parm_defaults(i_ptr); X init_item_parm_defaults(m_ptr->epilogue); X } X} X XVOID Xinit_item_parm_defaults(i_ptr) Xstruct item *i_ptr; X{ X struct parm *p_ptr; X struct ev_var *ev_ptr; X X if (!i_ptr) X return; X X for (p_ptr=i_ptr->parms;p_ptr;p_ptr = p_ptr->next) { X switch (p_ptr->flag) { X case PARM_NO_DEFAULT: X p_ptr->def_or_evname = ""; X break; X case PARM_STATIC_DEFAULT: X break; X case PARM_ENV_DEFAULT: X if (ev_ptr = find_ev_var_by_name(p_ptr->def_or_evname)) X p_ptr->def_or_evname = ev_ptr->value; X else X p_ptr->def_or_evname = ""; X break; X } X } X} X X/* menu manipulation routines/ */ X XVOID Xpush_menu(menu_ptr) X struct menu *menu_ptr; X{ X menu_ptr->menu_stack_ptr = active_menu; X active_menu = menu_ptr; X} X Xint Xpop_menu() X{ X if (active_menu = active_menu->menu_stack_ptr) { X display_menu_flag = Always_Display_Flag; X return 0; X } X X return -1; X} X Xstruct menu * Xfind_menu(name) X char *name; X{ X struct menu *menu_ptr = menu_list_head; X X while (menu_ptr) { X if (!strcmp(menu_ptr->menu_name, name)) X return menu_ptr; X menu_ptr = menu_ptr->next; X } X X return (struct menu *)0; X} X Xint Xcheck_menu() X{ X struct menu *menu_ptr; X struct item *item_ptr; X int errorcount = 0; X int item_idx; X X for (menu_ptr=menu_list_head;menu_ptr;menu_ptr=menu_ptr->next) { X /* a convenient place to set up the short menu */ X if (strlen(menu_ptr->menu_title) > 10) { X menu_ptr->short_title = Malloc(11); X strncpy(menu_ptr->short_title, menu_ptr->menu_title, 10); X menu_ptr->short_title[10] = '\0'; X } X else X menu_ptr->short_title = menu_ptr->menu_title; X X item_idx = 0; X for (item_ptr=menu_ptr->item_head;item_ptr;item_ptr=item_ptr->next) { X switch (item_ptr->action) { X case ITEM_SHELL: X item_ptr->item_idx = ++item_idx; X break; X case ITEM_SKIP: X break; X case ITEM_MENU: X if (!find_menu(item_ptr->command)) { X printf("Error in menu %s - item calls for non existant menu %s\n", X menu_ptr->menu_name, item_ptr->command); X errorcount++; X } X item_ptr->item_idx = ++item_idx; X break; X } X } X menu_ptr->max_item = item_idx; X } X X return errorcount; X} X XVOID Xdo_menu() X{ X int item_idx; X char buf[80]; X char *cp; X int loops = 0; X char c; X X do_prelude(); X X again: X setjmp(env); X set_signals(SIGS_FOR_JMP); X X while (1) { X /* count loops and exit if this is a once only menu */ X if (loops++ && Once_Flag) { X do_epilogue(); X if (pop_menu()) X simple_menu_exit(0); X return; X } X X display_menu(); X switch (item_idx = get_user_rsp()) { X case 'q': X do_epilogue(); X if (pop_menu()) X simple_menu_exit(0); X return; X case '\n': X break; X case 0: X putchar('\n'); X do_epilogue(); X if (pop_menu()) X simple_menu_exit(0); X close(0); X open(tty_fname, O_RDWR); X return; X case '?': X display_menu_flag = 1; X goto again; X default: X if (item_idx < 1 || item_idx > active_menu->max_item) { X printf("'%s' is not a legal response\n", buf); X do_pause(0); X if (item_idx != 0) X loops = 0; X goto again; X } X X selected_item = active_menu->item_head; X while (item_idx != selected_item->item_idx) X selected_item = selected_item->next; X X switch (selected_item->action) { X case ITEM_SKIP: X printf("\007"); X break; X case ITEM_MENU: X push_menu(find_menu(selected_item->command)); X do_menu(); X break; X case ITEM_SHELL: X do_shell_script(selected_item); X break; X } X do_pause(1); X break; X } X } X} X X/* invoke a shell script */ X XVOID Xdo_shell_script(selected_item) X struct item *selected_item; X{ X int pid; X struct parm *p_ptr; X struct ev_var *ev_ptr; X X p_ptr = selected_item->parms; X X while (p_ptr) { X char c; X char *cp; X X p_ptr->value = get_variable_value(p_ptr->prompt, p_ptr->def_or_evname); X p_ptr = p_ptr->next; X } X X tmp_fname = tmpnam((char *)0); X set_signals(SIGS_FOR_CHILD); X if ((tmp_file = fopen(tmp_fname, "w")) == NULL) X fatal("cannot create temp file for shell"); X if (verbose) X fprintf(tmp_file, "set -xv\n"); X for (ev_ptr=environment_list;ev_ptr;ev_ptr=ev_ptr->next) X fprintf(tmp_file, asg_fmt, ev_ptr->identifier, X ev_ptr->value); X for (p_ptr=selected_item->parms;p_ptr; X p_ptr=p_ptr->next) { X fprintf(tmp_file, asg_fmt, p_ptr->identifier, X p_ptr->value); X free(p_ptr->value); X p_ptr->value = ""; X } X fprintf(tmp_file, "%s\n", selected_item->command); X fclose(tmp_file); X X prepare_for_subshell(); X if ( !(pid = fork()) ) { X /* reset signals so that DEL,... work correctly */ X reset_signals(); X dup(0); X execl(cmd_path, cmd_name, tmp_fname, (char *)0); X fatal("exec of command failed"); X } X X wait_for_child(pid); X return_from_subshell(); X unlink(tmp_fname); X tmp_fname = (char *)0; X} X XVOID Xdo_prelude() X{ X char *cp; X X if (active_menu->prelude) { X if ((cp = active_menu->prelude->prompt) && strlen(cp)) X printf("%s\n", cp); X do_shell_script(active_menu->prelude); X } X} X XVOID Xdo_epilogue() X{ X char *cp; X X if (active_menu->epilogue) { X if ((cp = active_menu->epilogue->prompt) && strlen(cp)) X printf("%s\n", cp); X do_shell_script(active_menu->epilogue); X } X} X XVOID Xfatal(s) X char *s; X{ X extern int errno; X int lerrno = errno; /* save errno */ X extern char *sys_errlist[]; X X fprintf(stdout, use_msg, progname, PATCHLEVEL); X if (s) X fprintf(stdout, "%s\n", s); X X if (lerrno) { X fprintf(stdout, "fatal error '%s': line %d: %s\n", menu_fname, X line_number, sys_errlist[lerrno]); X } X simple_menu_exit(1); X} X XVOID Xtrapoid(sig) X int sig; X{ X if (tmp_fname) X unlink(tmp_fname); X X simple_menu_exit(sig); X} X XVOID Xdo_longjmp(sig) X int sig; X{ X longjmp(env, 0); X} X XVOID Xwait_for_child(pid) X int pid; X{ X int wait_ret; X int status; X extern int errno; X X while ((wait_ret = wait(&status)) != pid) { X /* test to see if child is still there - if not, then return */ X if (kill(pid, 0) < 0) X break; X } X} X XVOID Xset_signals(flag) X int flag; X{ X switch (flag) { X case SIGS_FOR_JMP: X signal(SIGHUP, trapoid); X signal(SIGINT, do_longjmp); X signal(SIGQUIT, do_longjmp); X signal(SIGTERM, trapoid); X break; X case SIGS_FOR_CHILD: X signal(SIGHUP, trapoid); X signal(SIGINT, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X signal(SIGTERM, trapoid); X break; X } X} X XVOID Xreset_signals() X{ X signal(SIGHUP, SIG_DFL); X signal(SIGINT, SIG_DFL); X signal(SIGQUIT, SIG_DFL); X signal(SIGTERM, SIG_DFL); X signal(SIGCLD, SIG_DFL); X} X Xint Xopen_menu_file() X{ X int menu_fd; X char *getenv(); X X /* check to see if we are reading from stdin */ X if (!strcmp(menu_fname, "-")) X return; X X /* absolute paths and ./<file-name> paths over-ride path search */ X if (menu_fname[0] == '/' || !strncmp(menu_fname, "./", 2)) { X if ((menu_fd = open(menu_fname, O_RDONLY)) < 0) X fatal("cannot open menu file"); X close(0); X dup(menu_fd); X close(menu_fd); X return; X } X X /* search for named menu along the menu path */ X if (menu_path || (menu_path = getenv("MENU_PATH"))) X return search_menu_path(menu_path, menu_fname); X X /* build a default menu path */ X { X char *home; X char buf[256]; X X if (home = getenv("HOME")) X sprintf(buf, "%s:%s" , home, DEFAULT_MENU_PATH); X else X strcpy(buf, DEFAULT_MENU_PATH); X search_menu_path(buf, menu_fname); X return; X } X} X Xint Xsearch_menu_path(path, fname) X char *path; X char *fname; X{ X char *cp = path; X int size = 256; X int len; X char *buf = Malloc(256); X int menu_fd; X X while (cp) { X if (cp = strchr(path, ':')) X *cp++ = '\0'; X if (size < (len = strlen(path) + strlen(fname) + 2)) { X buf = Realloc(buf, len); X size = len; X } X X strcpy(buf, path); X len = strlen(buf); X if (buf[len-1] != '/') X buf[len++] = '/'; X strcpy(buf + len, fname); X X if ((menu_fd = open(buf, O_RDONLY)) >= 0) { X free(buf); X close(0); X dup(menu_fd); X close(menu_fd); X return; X } X path = cp; X } X X fatal("cannot open menu definition"); X} X Xchar * XMalloc(size) X unsigned size; X{ X char *malloc(); X char *cp = malloc(size); X X if (!cp) X fatal("out of memory in Malloc"); X X return cp; X} X Xchar * XRealloc(ptr, size) X char *ptr; X unsigned size; X{ X char *realloc(); X char *cp = realloc(ptr, size); X X if (!cp) X fatal("out of memory in Realloc"); X X return cp; X} X XVOID Xyyerror(s) Xchar *s; X{ X printf("%s\n", s); X} X XVOID Xprt_item(item_ptr) X struct item *item_ptr; X{ X struct parm *p_ptr; X X switch (item_ptr->action) { X case ITEM_SKIP: X printf("(skip one line)\n"); X break; X case ITEM_MENU: X printf("%2d. %s: %s\n%s\n", item_ptr->item_idx, X item_types[item_ptr->action], X item_ptr->command, item_ptr->prompt); X break; X case ITEM_PRELUDE: X case ITEM_EPILOGUE: X case ITEM_SHELL: X printf("%2d. %s: %s\n", item_ptr->item_idx, item_types[item_ptr->action], X item_ptr->prompt); X printf("%s\n", item_ptr->command); X for (p_ptr=item_ptr->parms;p_ptr; X p_ptr = p_ptr->next) X switch (p_ptr->flag) { X case PARM_NO_DEFAULT: X printf("%s: '%s'\n", p_ptr->prompt, p_ptr->identifier); X break; X case PARM_STATIC_DEFAULT: X printf("%s: '%s'=%s\n", p_ptr->prompt, p_ptr->identifier, X p_ptr->def_or_evname); X break; X case PARM_ENV_DEFAULT: X printf("%s: '%s'=\"%s\"\n", p_ptr->prompt, p_ptr->identifier, X p_ptr->def_or_evname); X break; X } X break; X } X} X XVOID Xsimple_menu_exit(code) X int code; X{ X close_terminal(); X X exit(code); X} END_OF_FILE if test 18494 -ne `wc -c <'simple_menu.c'`; then echo shar: \"'simple_menu.c'\" unpacked with wrong size! fi # end of 'simple_menu.c' fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 --------------------------------cut here-------------------------------------- -- Mike Howard uunet!milhow1!how or how%milhow1@uunet.uu.net