[alt.sources] simple_menu program - part2/2

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