# Make a new directory for the grabchars sources, cd to it, and run kits 1
# thru 2 through sh. When all 2 kits have been run, read README.
echo "This is grabchars 1.9 kit 1 (of 2). If kit 1 is complete, the line"
echo '"'"End of kit 1 (of 2)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
mkdir web 2>/dev/null
echo Extracting README
sed >README <<'!STUFFY!FUNK!' -e 's/X//'
X Grabchars 1.9
X
X Copyright (c) 1988, 1989, 1990, Dan Smith
X
X daniel@island.uu.net dansmith@well.sf.ca.us
X unicom!daniel@pacbell.com
X
XWhat this is:
X
X "grabchars" gets one or more keystrokes from the user, without
Xrequiring them to hit return. It was written to make all types of shell
Xscripts more interactive.
X
X I know that it works fine on Suns running SUN OS 3.2-4.0.3,
Xan Apollo 4500 running 10.2, and a Vax 11/750 running Mt. Xinu 4.3
XBSD. System V support is there, except for the erase routine.
XI'd like to see support for this on VMS, MS-DOS, OS/2, and
Xwhatever else you find yourself using :-) Tell a friend, send
Xme patches.
X
X You'll find uses for this in all sorts of places. A prime
Xcandidate is in your .login file, it's easy to use this to select different
Xoptions. I've provided a "demo" csh script which runs through many of the
Xoptions. For the most part, grabchars can replace the use of "$<" in
Xcsh scripts and "read" in sh scripts, and offer filtering, timeouts, and
Xdefault answers. I've also provided a directory that will show
Xyou an simple way to generate interactive scripts: you lay out
Xthe menu, and it writes a script based on the menu - after that
Xyou just fill in the details.
X
XPutting this together:
X
X Run Config; it will figure out where you might want to
Xput the executable and the man page. It will also put you into an
Xeditor with the Makefile, and then it will run a "make clean",
X"make depend", and a "make release". You will need getopt (3)
Xto compile this program. If you're not sure if you have this, try
X"nm /lib/libc.a | grep getopt". Get a Public Domain version if you
Xdon't (write me if you get really stuck, I have it).
X
XUsage: (see the man page for more...)
X
X grabchars gets one keystroke
X grabchars -b output to stdout and stderr
X grabchars -c<valid characters> only <valid chars> are returned
X grabchars -d<char(s)> default char or string to return
X grabchars -e output to stderr instead of stdout
X grabchars -f flush any previous input
X grabchars -h help screen
X grabchars -n<number> number of characters to read
X grabchars -p<prompt> prompt to help user
X grabchars -q<prompt> prompt to help user (through stderr)
X grabchars -r RETURN exits (use with -n)
X grabchars -s silent, just return status
X grabchars -t<seconds> timeout after <seconds>
X grabchars -E erase/kill character processing
X
X examples: (values to arguments can be in the same word or the next one)
X
X grabchars -caeiou or
X grabchars -c aeiou get one of the vowels
X grabchars -c i get the letter 'i'
X grabchars '-penter a letter ' print the prompt "enter a letter "
X grabchars '-qenter a letter ' print the prompt ('q' for question)
X "enter a letter " through stderr...
X grabchars -n4 get four characters
X grabchars -t2 timeout after two seconds
X
X print a prompt and grab three characters...
X grabchars -p 'enter three characters >> ' -n 3
X
X get two numbers with a ten second timeout...
X grabchars -c 0123456789 -n2 -t10
X
X note that arguments like "-n4" or "-n 4" are handled the same way
X
X grabchars -h will give a usage screen...
X
XLegal paragraph:
X
X Use and copy Grabchars. Please send me any modifications
Xthat you make so that I may post official patches. Don't remove my
Xheaders. Don't say you wrote it. I am not responsible for mishaps
Xarising out of the use of this program, on the other hand, I haven't
Xrun into any problems with this. Lastly, do not use any part of
Xgrabchars in any product for profit (electronic, paper, or any other
Xmedia) without obtaining my written permission.
X
XPatches and things like that:
X
X "Help! Save The World!" (LW)...if you make any modifications
Xto grabchars, please send me a diff suitable for use with the patch program.
XI don't want ten different versions of this running around. The
XTODO file outlines some ideas I have for grabchars.
X
XWhere to reach me:
X
X Dan Smith
X Island Graphics
X 4000 Civic Center Drive
X San Rafael, Ca 94903
X +1 (415) 491 1000 (w), 491 0402 (fax)
X
X daniel@island.uu.net or
X {pixar,grenada,ucbvax!ucbcad,unicom,well,sun,uunet}!island!daniel
X
X unicom!daniel@pacbell.com
X
X {apple,lll-crg}!well!dansmith
X
!STUFFY!FUNK!
echo Extracting web/README
sed >web/README <<'!STUFFY!FUNK!' -e 's/X//'
X
X This directory contains:
X
Xgensource called by mkmenu
Xmkmenu use to generate scripts that source each other
Xscript.template example of a menuing script (self contained)
X
X The idea here is to show two ways of writing scripts
Xthat use menus. They call grabchars.
X
X "script.template" is pretty straightforward. It can be used
Xas the basis of csh program contained within one file. I show
Xthe use of three aliases: push_point, pop_point, and local_point,
Xwhich allow you to break csh scripts into "functions".
X
X "mkmenu" and "gensource" go further. They allow you to have
Xa directory, "menus", which contains csh files that can source each
Xother. You can set up some program with, say, 2 or 3 files, and then
Xgo ahead and start using it...but, you can also write a new routine
Xor two that call routines from the first program, and so on. Eventually
Xyou'll have a directory full of routines, and putting together a new
X"program" would be a matter of defining a menu that ties the right
Xroutines together.
X
!STUFFY!FUNK!
echo Extracting grabchars.c
sed >grabchars.c <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X** $Header: grabchars.c,v 1.9 89/12/29 21:14:48 daniel grabchars_1_9 $
X**
X** grabchars.c - get characters directly from the user
X**
X** Dan Smith (daniel@island.uu.net), October 23, 1988
X**
X** This program grabs characters from the user as they are
X** typed in, without having to wait for the return key to
X** be pressed. Among other things, this allows shell scripts
X** to be written with highly interactive menus...
X**
X** [to jump right to the code, search for "start of grabchars"]
X**
X** Usage rundown:
X**
X** grabchars gets one keystroke
X** grabchars -b output to stdout and stderr
X** grabchars -c<valid characters> only <valid chars> are returned
X** grabchars -d<char(s)> default char or string to return
X** grabchars -e output to stderr instead of stdout
X** grabchars -f flush any previous input
X** grabchars -h help screen
X** grabchars -n<number> number of characters to read
X** grabchars -p<prompt> prompt to help user
X** grabchars -q<prompt> prompt to help user (through stderr)
X** grabchars -r RETURN key exits (use with -n)
X** grabchars -s silent, just return status
X** grabchars -t<seconds> timeout after <seconds>
X** grabchars -E erase/kill character processing
X** grabchars -L lower case mapping of chars
X** grabchars -U upper case mapping of chars
X**
X** examples: (values to arguments can be in the same word or the next one)
X**
X** grabchars -caeiou or
X** grabchars -c aeiou get one of the vowels
X** grabchars -c i get the letter 'i'
X** grabchars '-penter a letter ' print the prompt "enter a letter "
X** grabchars '-qenter a letter ' print the prompt ('q' for question)
X** "enter a letter " through stderr...
X** grabchars -n4 get four characters
X** grabchars -t2 timeout after two seconds
X**
X** print a prompt and grab three characters...
X** grabchars -p 'enter three characters >> ' -n 3
X**
X** get two numbers with a ten second timeout...
X** grabchars -c 0123456789 -n2 -t10
X**
X** map 5 characters to upper case, or default to "HELP"
X** grabchars -d "HELP" -n5 -U
X**
X** note that arguments like "-n4" or "-n 4" are handled the same way
X**
X** History:
X**
X** Oct 1988: versions 1.0 - 1.1
X**
X** November 6, 1988 (1.15)
X** added -f flag to flush input, default is to use TIOCSETN instead
X** of TIOCSETP
X**
X** November 22, 1988 (1.16)
X** added -d flag for a default character or string to use if the
X** user hits return first thing or times out. handle_default ()
X** was added at this time
X**
X** November 23, 1988 (1.19)
X** added -r flag to exit when RETURN is hit. This was suggested by
X** David Vezie.
X**
X** November 29, 1988 (1.2)
X** Disaster strikes...I was updating Makefile.dist, and copied SRCS
X** to OBJS, and forgot to change grabchars.c to grabchars.o, and
X** then (after Config) did a "make clean"! I realized that I did not
X** have a backup, but fortunately had David Vezie's hack of grabchars.c
X** from a few days ago (he had added erase/line kill character processing)
X** ...moral: use RCS, check your Makefiles! Used this as an opportunity
X** to split things up into grabchars.h, globals.c, and sys.c
X** Got -c to handle ranges (via re_comp() and re_exec())
X**
X** December 12, 1988 (1.3.1)
X** This finally appeared on the net. Grabbed it and added -U and
X** -L to do upper/lower case mapping
X**
X** August 17, 1989 (1.5)
X** Finally integrated Randy's changes in for Sys V.
X*/
X
X/*
X * Changes made to allow compilation and operation under Unix System V
X * Release 2 or 3 on 6/30/89 by R. J. Davis, ocrjd.att.com!randy
X */
X
X/* start of grabchars... */
X#include <stdio.h>
X#include <signal.h>
X#include <ctype.h>
X#include "grabchars.h"
X
X/* see globals.c */
Xextern FILE *outfile, *otherout;
Xextern FLAG *flags;
Xextern int exit_stat;
Xextern char *usage_statement[];
X
X/*
X** David Vezie (unicom!dv) took a great shot at putting in
X** erase/kill processing. I need to test this some more, and I'll
X** most likely change it a bit. I put DV_ERASE in the Makefile
X** as a default; take it out if it misbehaves :-)
X*/
X#ifdef DV_ERASE
Xextern char *erase_buf; /* DV: Malloc'ed later */
X#endif
X
Xmain (argc, argv)
Xint argc;
Xregister char **argv;
X{
X /* two signal/wrapup handling routines in sys.c */
X int lets_go (), overtime ();
X
X /* for -d option */
X void handle_default ();
X
X /* for getopt () */
X extern int optind, opterr;
X extern char *optarg;
X char comarg;
X
X int how_many = 1; /* how many chars to read */
X int num_read; /* how many we have read... */
X int timeout; /* and an optional time to do it in... */
X int i; /* for usage_statement if we need it.. */
X
X /*
X ** re_comp (), re_exec () let us do things
X ** like "grabchars -c '[a-d]'" or "grabchars -c '[^a-z]'"...
X */
X char *re_comp (), *re_error;
X extern char valid_chars[128], default_string[128];
X char ch, check_str[2];
X
X alarm (0);
X opterr = 0;
X exit_stat = -1; /* if we're interrupted, exit with this status */
X
X outfile = stdout;
X otherout = stderr;
X if ((flags = (FLAG *) malloc (sizeof (FLAG))) == (FLAG *) NULL) {
X fprintf (stderr, "can't find enough memory...bye!\n");
X exit (exit_stat); /* we don't need lets_go () for this */
X }
X
X init_flags ();
X init_signal ();
X
X while ((comarg = getopt (argc, argv, "befrsELUc:d:n:p:q:t:")) != EOF) {
X switch (comarg) {
X case 'b':
X flags->both = 1;
X break;
X case 'c':
X flags->check = 1;
X strcpy (valid_chars, optarg);
X if (strlen (optarg) == 0) {
X fprintf (stderr, "-c option: must have at least one valid character\n");
X exit (-1);
X }
X /*
X ** most of the time, grabchars can be
X ** called safely with things like
X ** "a-z", because we can check to
X ** see if we need to add brackets...
X */
X if (valid_chars[0] != '[' &&
X valid_chars[strlen (valid_chars) - 1] != ']')
X sprintf (valid_chars, "%c%s%c",
X '[', optarg, ']');
X
X if ((re_error = re_comp (valid_chars))
X != NULL) {
X fprintf (stderr,
X "-c option: %s\n", re_error);
X exit (-1);
X }
X break;
X case 'd':
X flags->dflt = 1;
X strcpy (default_string, optarg);
X if (strlen (optarg) == 0) {
X fprintf (stderr, "-d option: must have at least one character for default\n");
X exit (-1);
X }
X break;
X case 'e':
X outfile = stderr;
X otherout = stdout;
X break;
X case 'f':
X flags->flush = 1;
X break;
X case 'n':
X how_many = atoi (optarg);
X if (how_many <= 0) {
X fprintf (stderr, "-n option: number of characters to read must be greater than zero\n");
X exit (-1);
X }
X break;
X case 'p':
X fprintf (stdout, "%s", optarg);
X break;
X case 'q':
X fprintf (stderr, "%s", optarg);
X break;
X case 'r':
X flags->ret_key = 1;
X break;
X case 's':
X flags->silent = 1;
X break;
X case 't':
X timeout = atoi (optarg);
X if (timeout <= 0) {
X fprintf (stderr, "-t option: number of seconds to timeout must be greater than zero\n");
X exit (-1);
X }
X
X /*
X ** we must have some valid time >0 seconds to
X ** get here, so we'll set an alarm...
X */
X (SIGRET) signal (SIGALRM, overtime);
X alarm ((unsigned int) timeout);
X break;
X case 'E': /* DV: honor erase/kill flag */
X#ifdef DV_ERASE
X flags->erase = 1;
X#else
X fprintf (stderr, "-E is disabled\n");
X exit (-1);
X#endif
X break;
X
X /*
X ** upper/lower case mapping...if both
X ** are specified on the command line,
X ** the last one wins out for sanity's
X ** sake...
X */
X case 'L':
X flags->lower = 1;
X flags->upper = 0;
X break;
X case 'U':
X flags->upper = 1;
X flags->lower = 0;
X break;
X
X /*
X ** I bet I could leave out "default", but
X ** I also bet that all getopt () routines
X ** are not created equal, so in it stays!
X */
X case '?':
X default:
X i = 0;
X while (usage_statement[i])
X puts (usage_statement[i++]);
X exit (-1);
X }
X }
X
X /* we're still here, really running...now change the tty... */
X init_term ();
X
X#ifdef DV_ERASE
X /* DV: malloc (okay, well calloc) space for the erase buffer */
X if (flags->erase) {
X /* We can't do it up in the switch, because we don't know
X ** how many is how_many
X */
X erase_buf = (char *)calloc (1, how_many);
X if (erase_buf == NULL) {
X fprintf (stderr,
X "Error: Couldn't malloc space for erase buffer\n");
X exit_stat = -1;
X lets_go();
X }
X }
X#endif
X
X for (num_read = 0; num_read < how_many; num_read++) {
X ch = getchar ();
X
X /* use default_string, this does *not* return */
X if (ch == '\n' && flags->dflt && num_read == 0)
X handle_default ();
X
X /*
X ** set by -r, a RETURN key gets us out (use with -n)
X ** suggested by David Vezie
X */
X if (ch == '\n' && flags->ret_key)
X break;
X
X /* filter chars... */
X /*
X ** known bug.... need to check the erase/kill keys somehow
X ** the workaround is to make them part of the allowable
X ** string of chars that can be input
X */
X if (flags->check) {
X sprintf (check_str, "%c", ch);
X if (re_exec (check_str) != 1) {
X num_read--;
X continue;
X }
X }
X
X /* upper/lower case mapping, added 12/12/88 */
X if (flags->upper)
X ch = mk_upper (ch);
X if (flags->lower)
X ch = mk_lower (ch);
X
X /*
X ** if we're just looking for a return status
X ** then have flags->silent set (-s)
X **
X ** DV: Also, we don't want to output yet,
X ** if we're processing erase/kill charfacters.
X */
X#ifdef DV_ERASE
X if (! flags->silent && ! flags->erase) {
X#else
X if (! flags->silent) {
X#endif
X putc (ch, outfile);
X if (flags->both)
X putc (ch, otherout);
X }
X
X#ifdef DV_ERASE
X if (flags->erase)
X handle_erase (ch, &num_read);
X#endif
X }
X
X#ifdef DV_ERASE
X if (flags->erase) {
X fprintf (outfile, "%s", erase_buf);
X if (flags->both)
X fprintf (otherout, "%s", erase_buf);
X }
X#endif
X
X exit_stat = num_read;
X lets_go ();
X}
!STUFFY!FUNK!
echo Extracting web/mkmenu
sed >web/mkmenu <<'!STUFFY!FUNK!' -e 's/X//'
X#!/bin/csh -f
X
X
Xgoto basic_setup
X
X# start of mkscript_main_menu_routines
X
Xmkscript_main_menu_routines:
X
Xonintr mkscript_main_menu_routines
X
Xcat << menu_screen
X`clear`
X
X Mkscript Main Menu
X
X ! shell
X ? help
X a add a menu
X e edit a routine
X l list menus and routines
X r run it!
X
X q quit
X
Xmenu_screen
X
X set noglob
X set choice=`$grabchars -q " your choice >> " | cat -v`
X if ($choice =~ '?') set choice=help
X switch ($choice)
X case "!":
X sh
X breaksw
X case "help":
X echo -n "help..."
X push_point help_menu_routines
X breaksw
X case "a":
X echo -n "add..."
X push_point add_menu_routines
X breaksw
X case "e":
X echo -n "edit..."
X push_point edit_a_routine
X breaksw
X case "l":
X echo -n "list..."
X push_point list_routines
X breaksw
X case "r":
X echo "test run...hit ^C to exit..."
X push_point run_routines
X breaksw
X case "q":
X echo "quit..."
X exit
X breaksw
X default:
X if ($ret_pos > 1) then
X echo -n " back..."
X pop_point
X else
X echo no such option...
X endif
X breaksw
X endsw
X goto mkscript_main_menu_routines
X
X# end of mkscript_main_menu_routines
X
X# start of add_menu_routines
X
Xadd_menu_routines:
X
X echo ""
X ../gensource
X unset noglob
X set known_routines=(`ls -t *menu`)
X if (! $?real_proj_name) then
X set real_proj_name=`echo $known_routines[1]:r`
X endif
X pop_point
X
X# end of add_menu_routines
X
X# start of edit_a_routine
Xedit_a_routine:
X echo nothing yet...
X pop_point
X
X# end of edit_a_routine
X
X
X# start of list_routines
Xlist_routines:
X
X unset noglob
X set known_routines=(`ls -t *menu`)
X foreach rout ($known_routines)
X echo ""
X echo the menu "$rout:r" has these entries:
X grep 'source' $rout | awk '{ print $2 }'
X echo ""
X end
X $grabchars -s -t 30 -q 'press any key...'
X
X pop_point
X
X# end of list_routines
X
X
X# start of run_routines
Xrun_routines:
X
X if ( ! $?real_proj_name) then
X echo hey\! nothing to run...
X sleep 2
X pop_point
X endif
X echo name of project: $real_proj_name
X sleep 3
X
X set proj_name=${real_proj_name}_run
X cp /dev/null $proj_name && chmod 755 $proj_name
X
X cat << +++ >> $proj_name
X#! /bin/csh -f
X
Xgoto basic_setup
X
X+++
X
X cat $real_proj_name >> $proj_name
X
X echo adding basic startup routine...
X cat << +++ >> $proj_name
X
X`echo basic_setup:`
X set base_dir=$cwd
X set menu_loop=""
X
X # these three aliases allow me to make a very flexible menu
X # structure...you "push" every menu you go to, and pop out
X # to get back to where you came from..
X
X alias push_point 'set return_point=(\$return_point[1-\$ret_pos] \!*); @ ret_pos++; goto \!*; if (\$?pverbose) echo push_point produces \$return_point'
X
X alias pop_point 'set back_pos=\$return_point[\$ret_pos]; @ ret_pos--; goto \$return_point[\$ret_pos]; if (\$?pverbose) echo push_point produces \$return_point'
X
X alias local_point '@ ret_pos--; set return_point=(\$return_point[1-\$ret_pos] \!*); @ ret_pos++; if (\$?pverbose) echo local point produces \$return_point'
X
X # initialize this, just in case it gets hit...
X set back_pos=$real_proj_name
X set return_point=$real_proj_name
X @ ret_pos=0
X
X
X if (! \$?EDITOR) setenv EDITOR /usr/ucb/vi
X if (! \$?PAGER) setenv PAGER /usr/ucb/more
X
X set this_host=\`hostname\`
X set grabchars=/usr/local/bin/grabchars
X set grab_opts=""
X
X push_point $real_proj_name
X
X+++
X $proj_name
X pop_point
X
X# end of run_routines
X
X
X# start of help_menu_routines
X
Xhelp_menu_routines:
X
Xcat << the_end_of_help | $PAGER
X`clear`
X
X Hello $USER,
X
X This is only scanty documentation for what should be
Xconsidered as a "work in progress", ok?
X
X The main menu looks like this:
X
X---
X Mkscript Main Menu
X
X ! shell
X ? help
X a add a menu
X e edit a routine
X l list menus and routines
X r run it!
X
X q quit
X
X---
X '!' will push a shell...
X '?' got you to here
X 'a' means that you are going to define a menu, this is
X accomplished by calling the script "gensource".
X What happens in that script is that you lay out
X a menu in an editor (currently $EDITOR),
X and then a csh "routine" is generated based
X on the menu. This gives you a working menu
X to play with...afterwards you go into the
X resulting file and add whatever functionality
X you need in the routine.
X 'e' is unimplemented, what it should do is allow you
X to edit an existing routine.
X 'l' displays known routines and what routines are called
X from them...you would use this so that you
X could keep track of what needed to be defined in
X an application with many menus.
X 'r' ties things together and lets you do a test run on
X what you have defined. You should hit ^C
X (or whatever you normally use as a Break character)
X to get out of your test run. Further: what
X 'r' does is figure out what your "project" is
X by using the name of the first "routine" you've
X defined, and builds a wrapper around that routine.
X Look in the "menus" directory and you'll see that
X your main routine has a "_run" copy. There's
X a good reason why it's a copy, you may call your
X main menu as a "submenu" in some other "application".
X Aha! The whole idea is that you write routines,
X and can link them together by defining other
X routines, and so on... a "change directory" routine
X might be called from many different "applications".
X It's all accomplished by using the csh source
X builtin...don't go more than 20 routines deep.
X
Xthe_end_of_help
X ckpager
X pop_point
X
X# end of help_menu_routines
X
X
Xbasic_setup:
X set base_dir=$cwd
X
X # these three aliases allow me to make a very flexible menu
X # structure...you "push" every menu you go to, and pop out
X # to get back to where you came from..
X
X alias push_point 'set return_point=($return_point[1-$ret_pos] \!*); @ ret_pos++; goto \!*; if ($?pverbose) echo push_point produces $return_point'
X
X alias pop_point 'set back_pos=$return_point[$ret_pos]; @ ret_pos--; goto $return_point[$ret_pos]; if ($?pverbose) echo push_point produces $return_point'
X
X alias local_point '@ ret_pos--; set return_point=($return_point[1-$ret_pos] \!*); @ ret_pos++; if ($?pverbose) echo local point produces $return_point'
X
X # initialize this, just in case it gets hit...
X set back_pos="mkscript_main_menu_routines"
X set return_point="mkscript_main_menu_routines"
X @ ret_pos=0
X
X
X if (! $?EDITOR) setenv EDITOR /usr/ucb/vi
X if (! $?PAGER) setenv PAGER /usr/ucb/more
X alias ckpager 'if ($PAGER =~ *more*) $grabchars -f -p "...press any key..."'
X
X set this_host=`hostname`
X set grabchars=grabchars # this should be /usr/local/bin/grabchars
X set grab_opts=""
X
X setenv RUNNING_MKSCRIPT
X set known_routines=""
X
X if ( ! -d menus) then
X echo creating menu dir...
X mkdir menus
X endif
X cd menus
X
X push_point mkscript_main_menu_routines
!STUFFY!FUNK!
echo Extracting sys.c
sed >sys.c <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X** $Header: sys.c,v 1.9 89/12/29 21:14:55 daniel grabchars_1_9 $
X**
X** sys.c - terminal routines for grabchars
X**
X** Dan Smith (daniel@island.uu.net), November 29, 1988
X**
X** History:
X**
X** December 2, 1988
X** made #ifdefs for DV_ERASE and BSD, wrote notes in handle_erase ()
X** for changes and improvements
X**
X** May 11, 1989
X** Started to add SYS_V code
X**
X** December 26, 1989
X** added flush/no flush for System V
X*/
X
X#include <stdio.h>
X#include <signal.h>
X#include "grabchars.h"
X
X
X#ifdef BSD
X# include <sgtty.h>
X struct sgttyb orig, new;
X#endif /* BSD */
X
X#ifdef SYS_V
X# include <termio.h>
X struct termio orig, new;
X#endif /* SYS_V */
X
X
X/* all declared in globals.c */
Xextern FILE *outfile, *otherout;
Xextern FLAG *flags;
Xextern int exit_stat;
Xextern char default_string[128];
X
X#ifdef DV_ERASE
Xextern char *erase_buf;
X#endif
X
X/* initialize global flags */
Xinit_flags ()
X{
X flags->both = 0;
X flags->check = 0;
X flags->dflt = 0;
X flags->flush = 0;
X flags->ret_key = 0;
X flags->silent = 0;
X flags->erase = 0;
X flags->lower = 0;
X flags->upper = 0;
X}
X
X/*
X** initialize tty
X*/
Xinit_term ()
X{
X /* play havoc with the terminal :-) */
X
X#ifdef BSD
X ioctl (0, TIOCGETP, &orig);
X new = orig;
X new.sg_flags &= ~ECHO;
X new.sg_flags |= CBREAK;
X
X (flags->flush) ? ioctl (0, TIOCSETP, &new) : /* to flush... */
X ioctl (0, TIOCSETN, &new); /* ...or not to flush */
X#endif
X
X#ifdef SYS_V
X ioctl(0, TCGETA, &orig); /* Added 6/30/89 for Sys V - rjd */
X new = orig; /* Added 6/30/89 for Sys V - rjd */
X new.c_iflag = 0; /* Added 6/30/89 for Sys V - rjd */
X new.c_iflag |= ICRNL | ISTRIP; /* Added 6/30/89 for Sys V - rjd */
X new.c_lflag &= ~(ISIG | ICANON | ECHO); /* Added 6/30/89 for Sys V - rjd */
X new.c_cc[4] = new.c_cc[5] = '\001'; /* Added 6/30/89 for Sys V - rjd */
X
X /* 12/26/89....flush for SYS V...daniel */
X (flags->flush) ? ioctl(0, TCSETAF, &new) :
X ioctl(0, TCSETA, &new);
X
X#endif /* SYS_V */
X
X}
X
X/* handle the outside world */
Xinit_signal ()
X{
X int lets_go ();
X
X#ifdef BSD
X (SIGRET) signal (SIGTSTP, lets_go);
X#endif
X (SIGRET) signal (SIGINT, lets_go);
X (SIGRET) signal (SIGQUIT, lets_go);
X}
X
X/*
X** something's up with the user...give a useful exit status so
X** we can ask things like "do you need help?"
X*/
Xint overtime ()
X{
X int lets_go ();
X void handle_default ();
X
X /* does not return */
X if (exit_stat == -1 && flags->dflt)
X handle_default ();
X
X exit_stat = -2;
X lets_go ();
X}
X
X/*
X** the default_flag is set, and the user either typed a return
X** or timed out. This routine does not return.
X*/
Xvoid handle_default ()
X{
X int lets_go ();
X
X if (! flags->silent) {
X fputs (default_string, outfile);
X if (flags->both || flags->ret_key)
X fputs (default_string, otherout);
X }
X exit_stat = strlen (default_string);
X lets_go ();
X}
X
X/* clean up and get out of here... */
Xint lets_go ()
X{
X#ifdef BSD
X ioctl (0, TIOCSETP, &orig);
X#endif
X
X#ifdef SYS_V
X ioctl(0, TCSETA, &orig); /* Added 6/30/89 for Sys V - rjd */
X#endif
X exit (exit_stat);
X}
X
X#ifdef DV_ERASE
X
X/*
X** December 2, 1988
X** in progress notes for changing this...
X**
X** first time through processing should be called as its' own
X** function from the -E case in the main (getopt ()) switch...
X**
X** stdout and stderr should never be affected by any erasures...
X** (they probably are not now, I haven't thoroughly tested this...)
X**
X** I need to drag in my word erase routine; never can tell how
X** long some people are going to want their lines with -n! :-)
X**
X** If someone wants a control char (via literal (^V)), we should
X** give it to them...this would be more compatible with $< (csh)
X** and read (sh)... grabchars almost completely replaces these
X** now.
X**
X** we can also be sensitive to pipe/no pipe via isatty ()...
X*/
X
X/* DV: handle erase characters, kill characters, etc. */
Xhandle_erase (ch, cnt)
Xchar ch;
Xint *cnt;
X{
X static char first = 1;
X static char erasec, killc, werasec, lnextc, rprntc;
X static char lnextflg = 0;
X static char *cp;
X static FILE *tty;
X int i;
X
X if (first) {
X /* initialize static things */
X struct sgttyb sb;
X struct ltchars ltc;
X
X first = 0;
X cp = erase_buf;
X tty = fopen ("/dev/tty", "w");
X
X /* this isn't going to do... what if -e is set?...dan */
X if (tty == NULL)
X tty = stderr;
X ioctl (0, TIOCGETP, &sb);
X ioctl (0, TIOCGLTC, <c);
X erasec = sb.sg_erase;
X killc = sb.sg_kill;
X werasec = ltc.t_werasc;
X lnextc = ltc.t_lnextc;
X rprntc = ltc.t_rprntc;
X }
X
X if (lnextflg) {
X ch |= 0x80;
X lnextflg = 0;
X }
X (*cnt) --;
X if (ch == erasec) {
X if (*cnt < 0)
X return;
X fprintf (tty, "\b \b");
X (*cnt) --;
X *--cp = 0;
X } else if (ch == killc) {
X while (*cnt >= 0) {
X fprintf (tty, "\b \b");
X (*cnt) --;
X *--cp = 0;
X }
X } else if (ch == werasec) {
X if (*cnt < 0)
X return;
X while ((cp[-1] == ' ' || cp[-1] == '\t') && (*cnt) >= 0) {
X fprintf (tty, "\b");
X (*cnt) --;
X *--cp = 0;
X }
X while (cp[-1] != ' ' && cp[-1] != '\t' && (*cnt) >= 0) {
X fprintf (tty, "\b \b");
X (*cnt) --;
X *--cp = 0;
X }
X } else if (ch == lnextc) {
X lnextflg = 1;
X fprintf (tty, "^\b");
X } else if (ch == rprntc) {
X for (i = strlen (erase_buf); i > 0; i--)
X putc ('\b', tty);
X fprintf (tty, "%s", erase_buf);
X } else {
X ch &= 0x7f;
X fprintf (tty, "%c", ch);
X *cp++ = ch;
X (*cnt) ++;
X }
X fflush (tty);
X return;
X}
X#endif
!STUFFY!FUNK!
echo Extracting grabchars.1
sed >grabchars.1 <<'!STUFFY!FUNK!' -e 's/X//'
X''' Man page for grabchars, uses Larry Wall's "patch" man page as
X''' a template.
X.de Sh
X.br
X.ne 5
X.PP
X\fB\\$1\fR
X.PP
X..
X.de Sp
X.if t .sp .5v
X.if n .sp
X..
X'''
X''' Set up \*(-- to give an unbreakable dash;
X''' string Tr holds user defined translation string.
X''' Bell System Logo is used as a dummy character.
X set choice=\`\$grabchars -q " your choice >> " | cat -v\`
X if (\$choice =~ '?') set choice=help
X switch (\$choice)
X+++
X
X@ count=1
Xforeach item ( $menu_entries)
X echo -n $item...
X if ($item =~ '?') set item=help
X cat << +++ >> $output
X case "$item":
X echo -n " $menu_routines[$count]..."
X @ ret_pos++
X source ${menu_routines[$count]}_menu
X @ ret_pos--
X breaksw
X+++
X @ count++
Xend
X
Xecho default statement...
Xcat << +++ >> $output
X case "^L":
X case "^R":
X echo -n redraw...
X breaksw
X default:
X if (\$ret_pos > 1) then
X echo -n " back..."
X set menu_loop="false"
X else
X echo no such option...
X endif
X breaksw
X endsw
X
X \$menu_loop goto $title
X set menu_loop=""
X
X# end of ${title}
X
X+++
Xecho cleanup....
X
Xrm $$.menu
!STUFFY!FUNK!
echo Extracting web/script.template
sed >web/script.template <<'!STUFFY!FUNK!' -e 's/X//'
X#!/bin/csh -f
X#
X# This script is a template for csh programs that are contained
X# within one file. It demonstrates the use of aliases to push
X# and pop "routines". This is a different method from that used
X# by mkmenu/gensource, which produce modules that source each other.
X# Enjoy!
X#
X# daniel@island.uu.net
X
Xgoto basic_setup
X
X# start of template_main_menu_routines
X
Xtemplate_main_menu_routines:
X
Xonintr template_main_menu_routines
X
Xcat << menu_screen
X`clear`
X
X Template Main Menu
X
X ! shell
X ? help
X e edit
X l list
X
X q quit
X
Xmenu_screen
X
X set noglob
X set choice=`$grabchars -q " your choice >> " | cat -v`
X if ($choice =~ '?') set choice=help
X switch ($choice)
X case "!":
X sh
X breaksw
X case "help":
X echo -n "help..."
X push_point help_menu_routines
X breaksw
X case "e":
X echo -n "edit..."
X push_point edit_routines
X breaksw
X case "l":
X echo -n "list..."
X push_point list_routines
X breaksw
X case "q":
X echo "quit..."
X exit
X breaksw
X default:
X if ($ret_pos > 1) then
X echo -n " back..."
X pop_point
X else
X echo no such option...
X endif
X breaksw
X endsw
X goto template_main_menu_routines
X
X# end of template_main_menu_routines
X
X# start of edit_routines
Xedit_routines:
X cat << +++
X`clear`
X`ls -l`
X
X+++
X set which_file=`$grabchars -b -d "a_new_file" -t 60 -n 80 -r -q 'enter a filename or just hit return >> '`
X echo " "
X echo going to \"$EDITOR\" with \"$which_file\"...
X $EDITOR $which_file
X pop_point
X
X# end of edit_routines
X
X# start of list_routines
Xlist_routines:
X
X # this demonstrates the use of local points, which
X # are used to break up huge "functions" up into
X # smaller ones..
X
X local_point list_1
X push_point do_list
Xlist_1:
X echo done with list...
X pop_point
X
X# end of list_routines
X
X# start of do_list
Xdo_list:
X ls -l | $PAGER
X $ckpager $grabchars -s -t 30 -q 'press any key...'
X pop_point
X
X# end of do_list
X
X# start of help_menu_routines
X
Xhelp_menu_routines:
X
Xcat << the_end_of_help | $PAGER
X`clear`
X
X Hello $USER,
X
X A sample help screen
X
X The main menu looks like this:
X
X---
X Template Main Menu
X
X ! shell
X ? help
X e edit
X l list
X
X q quit
X
X---
X '!' will push a shell...
X '?' got you to here
X 'e' edit a file
X 'l' ls -l files here
X 'q' leave
X
Xthe_end_of_help
X $ckpager $grabchars -f -p "...press any key..."
X pop_point
X
X# end of help_menu_routines
X
X
Xbasic_setup:
X set base_dir=$cwd
X
X # these three aliases allow me to make a very flexible menu
X # structure...you "push" every menu you go to, and pop out
X # to get back to where you came from..
X
X alias push_point 'set return_point=($return_point[1-$ret_pos] \!*); @ ret_pos++; goto \!*; if ($?pverbose) echo push_point produces $return_point'
X
X alias pop_point 'set back_pos=$return_point[$ret_pos]; @ ret_pos--; goto $return_point[$ret_pos]; if ($?pverbose) echo push_point produces $return_point'
X
X alias local_point '@ ret_pos--; set return_point=($return_point[1-$ret_pos] \!*); @ ret_pos++; if ($?pverbose) echo local point produces $return_point'
X
X # initialize this, just in case it gets hit...
X set back_pos="template_main_menu_routines"
X set return_point="template_main_menu_routines"
X @ ret_pos=0
X
X
X if (! $?EDITOR) setenv EDITOR /usr/ucb/vi
X if (! $?PAGER) setenv PAGER /usr/ucb/more
X
X set ckpager=false
X if ($PAGER =~ *more*) then
X set ckpager="" # we'll need it...
X endif
X
X set this_host=`hostname`
X set grabchars=grabchars # this should be /usr/local/bin/grabchars
X set grab_opts=""
X
X setenv RUNNING_TEMPLATE
X
X push_point template_main_menu_routines
!STUFFY!FUNK!
echo Extracting Config
sed >Config <<'!STUFFY!FUNK!' -e 's/X//'
X#!/bin/csh -f
X#
X# $Header: Config,v 1.9 89/12/29 21:14:18 daniel grabchars_1_9 $
X#
X# Config - set up Makefile for grabchars
X#
X# Dan Smith (daniel@island.uu.net), November 1988, April 1990
X#
X# Config file for grabchars... must be csh, no attempt
X# made to be eunice (i.e. echo " ") compatible...
X#
Xclear
Xcat << GUMBY
X
X Config for grabchars
X
X This csh script will figure out a few things about your
Xsystem, and then will run a "make clean", a "make depend", and
Xa "make release". You should then try out grabchars, and, once assured
Xthat it's behaving itself, you should run a "make install"
X
XGUMBY
Xecho -n 'press return to start...'
Xset ignore=$<
X
X# figure out where to put this when the user types
X# "make install"...we'll try in three likely places...