From decwrl!ucbvax!ucsd!usc!cs.utexas.edu!uunet!allbery Mon May  7 07:40:55 PDT 1990
Article 1529 of comp.sources.misc:
Path: decwrl!ucbvax!ucsd!usc!cs.utexas.edu!uunet!allbery
From: daniel@island.UUCP (Dan Smith "Happy Birthday James Brown G.O.S.")
Newsgroups: comp.sources.misc
Subject: v12i049: Grabchars 1.9, get keystrokes direct from user BSD/SYSV part 1
Message-ID: <87552@uunet.UU.NET>
Date: 5 May 90 17:27:37 GMT
Sender: allbery@uunet.UU.NET
Lines: 1973
Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)

Posting-number: Volume 12, Issue 49
Submitted-by: daniel@island.UUCP (Dan Smith "Happy Birthday James Brown G.O.S.")
Archive-name: grabchars-1.9/part01

	Here is a long overdue update to grabchars.  I now have
SYS V support.  Grabchars lets you write highly interactive shell
scripts, by allowing you to grab one or more keystrokes. See the
README for more.  I have also included a script which allows you
to generate csh menu-based scripts; you lay out the menu in your
favorite editor, it writes the skeleton script.  Enjoy!

			Daniel

   dansmith@well.sf.ca.us   daniel@island.uu.net   unicom!daniel@pacbell.com
ph: (415) 332 3278 (h), 491 1000 (w) disclaimer: Island's coffee was laced :-)


#! /bin/sh

# 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, &ltc);
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'''
X.ie n \{\
X.tr \(bs-\*(Tr
X.ds -- \(bs-
X.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
X.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
X.ds L" ""
X.ds R" ""
X.ds L' '
X.ds R' '
X'br\}
X.el\{\
X.ds -- \(em\|
X.tr \*(Tr
X.ds L" ``
X.ds R" ''
X.ds L' `
X.ds R' '
X'br\}
X.TH GRABCHARS 1 LOCAL
X.SH NAME
Xgrabchars - get keystrokes directly from user
X.SH SYNOPSIS
X.B grabchars
X[options]
X.SH DESCRIPTION
X\fBGrabchars\fP gets characters from the user as they are
Xtyped in, without having to wait for the return key to
Xbe pressed.  Among other things, this allows shell scripts
Xto be written with highly interactive menus.
X.PP
XBy default,
X.I grabchars
Xwill obtain one character from stdin, echo that character to stdout,
Xand return with a status of one; meaning one character read.
X.TP 5
X.B \-b
XBoth
X.I stdout
Xand
X.I stderr
Xare used for output.  This is useful for setting a variable in
Xa shell script and echoing a keystroke to the screen at the
Xsame time.
X.TP 5
X.B \-c<valid characters>
XOnly characters in
X.I <valid characters>
Xare accepted.  Regular expressions such as [a-z]
Xmay be used to specify ranges.  All other characters are ignored.
X.TP 5
X.B \-d<char(s)>
XDefault char or string to output if the user hits
X.B RETURN
Xor lets
X.B grabchars
Xtimeout.  The status that is returned is the same as if the user had
Xtyped in the character or string, so this option may be used with
Xthe
X.B \-s
X(silent) flag.
X.TP 5
X.B \-e
XOutput goes to
X.I stderr
Xrather than
X.I stdout.
X.TP 5
X.B \-f
XFlush any previous input.  By default,
X.I grabchars
Xwill see any characters present in
X.I stdin,
Xwhich allows for some typeahead in shell scripts.
X.TP 5
X.B \-h
XHelp/usage screen.
X.TP 5
X.B \-p<prompt>
XSets up a prompt for the user.  See
X.I EXAMPLES.
X.TP 5
X.B \-q<prompt>
XSets up a prompt for the user, except it is printed to
X.I stderr
Xrather than
X.I stdout.
X.TP 5
X.B \-r
XThe
X.B RETURN
Xkey exits.  Use this with the -n option to allow for variable
Xnumbers of characters to be typed in.
X.TP 5
X.B \-n<number>
XNumber of characters to read.  By default,
X.I grabchars
Xlooks for one character.
X.TP 5
X.B \-s
XSilent.  Do not output anything. Just return a status.
X.TP 5
X.B \-t<seconds>
XTime to allow the user to respond.  By default, the user
Xcan take as long as he or she wants to.  The timeout option allows
Xyou to write shell scripts where you can offer some assistance
Xif it's obvious that the user might be stuck.
X.TP 5
X.B \-E
XErase/kill processing is done.  You have use of the keys (usually
XDELETE and ^U or ^X) that you would normally have from the
Xshell for deleting characters.  This is useful with the
X.B \-n
Xoption, where many characters are being typed in.  This code hasn't
Xbeen thoroughly tested, and currently only applies to a BSD compiled
Xversion.
X.TP 5
X.B \-L
XMap characters to lower case.
X.TP 5
X.B \-L
XMap characters to upper case.  Note that if \fB-U\fP and \fB-L\fP
Xare both specified on the command line, the last one that occurs
Xwill win out.  This is because it doesn't make sense to try to do
Xboth at once.
X.SH EXAMPLES
X.TP 5
X.B grabchars
Xgets one keystroke
X.TP 5
X.B grabchars \-caeiou
Xget one of the vowels
X.TP 5
X.B grabchars -c i
Xget the letter 'i'
X.TP 5
X.B grabchars '\-penter a letter '
Xprint the prompt "enter a letter "
X.TP 5
X.B grabchars '\-qenter a letter '
Xprint the prompt ('q' for question) "enter a letter " through
X.I stderr.
X.TP 5
X.B grabchars \-n4
Xget four characters.
X.TP 5
X.B grabchars \-d a
XIf the first character typed is a
X.B RETURN,
Xexpand it to 'a'.
X.TP 5
X.B grabchars \-d gumby
XIf the first character typed is a
X.B RETURN,
Xexpand it to "gumby".
X.TP 5
X.B grabchars \-r
XThe
X.B RETURN
Xkey will exit
X.I grabchars.  You would use this with the
X.B -n
Xoption, so that variable numbers of characters may be entered.
X.TP
X.B grabchars \-n 4 \-r \-t 10
XAccept up to four characters, or exit when
X.B RETURN
Xis hit, or exit when 10 seconds have elapsed.
X.TP 5
X.B grabchars \-t2
Xtimeout after two seconds.
X.TP 5
X.B grabchars \-d gumby \-t2
XIf the first character typed is a
X.B RETURN,
Xor if two seconds have gone by,
Xpretend that the user typed in "gumby".
X.TP 5
X.B grabchars \-n3 \-p 'initials: '
Xprint a prompt and grab three characters.
X.TP 5
X.B grabchars \-c 0123456789 \-n2 \-t10
Xget two numbers with a ten second timeout.
X.PP
Xnote that arguments like "-n4" or "-n 4" are handled the same way
X.SH SEE ALSO
Xcsh(1) and sh(1)
Xfor syntax of
X.I csh
Xand
X.I sh
Xscripts, respectively.
XSee "The Unix Csh Field Guide", by Gail and Paul Anderson (Prentice Hall),
Xfor an excellent tour of csh and good examples of writing csh scripts.
X.SH DIAGNOSTICS
X.I
XGrabchars
Xreturns
X.B \-2
Xif it times out, or
X.B \-1
Xif it gives a usage statement.  Otherwise, it
Xreturns the number of characters successfully read.
X.SH AUTHOR
X.nf
XDan Smith (daniel@island.uu.net or {ucbvax!ucbcad,well,sun}!island!daniel)
!STUFFY!FUNK!
echo Extracting web/gensource
sed >web/gensource <<'!STUFFY!FUNK!' -e 's/X//'
X#! /bin/csh -f
X
Xcat << +++ >! $$.menu
X		XXX Menu
X
X#	change XXX to the name of the menu, if it is the main menu then
X#	it is a good idea to indicate it, such as "Gumby Main Menu".
X#	The first word will become the name of the routine.
X#	now, put all of the options for this menu here, (or if you are just
X#	generating a template for a routine, leave it blank)... There is
X#	some documentation below for reference...
X#
X#	Your options (common ones at the end can just be uncommented):
X
X
X#	now that you are done specifying options, save the file
X#	and quit the editor, and your routine will be worked on...
X
X#	Documentation/Reference:
X#
X#	Lines can look like:
X#
X#	f	file
X#	s	sort - sort all of the info
X#	?	help - help menu
X#
X#	General Rules:
X#		one character for the action, followed by a one word
X#		description, which will help form the name of the
X#		routine that will get called when that character is
X#		pressed...everything after is just description for the
X#		user
X#
X#		the menu will appear in alphabetical order no matter
X#		what you put at this point, you can edit that later...
X#
X#		don't put something like:
X#		s save
X#		s sort
X#		every character gets bound to a routine...
X#
X#		always try to use '?' for help to be consistent.
X
X#	Common Options:
X#	These are just suggestions for some menu entries, just uncomment
X#	them (remember, you can only use a letter once in the same menu)
X#	to use them...
X
X#	!	shell
X#	?	help
X#	a	add
X#	c	config - change parameters
X#	D	default
X#	d	database
X#	d	display
X#	d	dump
X#	e	edit
X#	f	file
X#	f	forward
X#	f	format
X#	h	help
X#	i	init
X#	j	join
X#	k	kill - stop a process
X#	l	load
X#	l	list
X#	m	mail/messages
X#	m	merge
X#	m	main - go to main menu
X#	n	new
X#	o	open
X#	p	print
X#	q	quit
X#	r	reset
X#	r	restore
X#	s	start
X#	s	shell
X#	t	tape
X#	t	tar
X#	u	undo
X#	v	verbose
X#	w	write
X
X+++
X
Xecho going to $EDITOR...
X
X$EDITOR $$.menu
Xsed -n -e '/^[$#]/d' -e '/[a-zA-Z0-9]/p' $$.menu >! foo.$$ ;  mv -f foo.$$ $$.menu
Xsed -n -e 's/^[	 ]*//p'  $$.menu >! foo.$$ ;  mv -f foo.$$ $$.menu
Xecho -n finding title...
Xset first_line=(`head -1 $$.menu`)
Xset title=`head -1 $$.menu | tr ' 	A-Z' '__a-z'`
Xecho -n "($title)..."
X
X@ num_lines=`wc -l $$.menu | awk ' { print $1 } '`
X@ num_lines--
Xecho -n scanning file...
Xtail -${num_lines} $$.menu | sed -e 's/^[	 ]*//' | tr '/' '_'  | sort -u >! foo.$$; mv -f foo.$$ $$.menu
X
Xset noglob
Xset menu_entries=`awk ' { if ($NF >= 2) print $1 } ' $$.menu `
Xset menu_routines=`awk ' { if ($NF >= 2) print $2 } ' $$.menu `
X
Xecho setup...
X
Xset output=$title
Xcp /dev/null $output
X
Xcat << +++ >> $output
X#	start of $title
X
X${title}:
X
Xcat << menu_screen
X\`clear\`
X
X		$first_line
X
X+++
X
Xecho body of menu...
Xawk ' { printf ("\t%s\n", $0) }' $$.menu >> $output
X
Xecho switch statement...
Xcat << +++ >> $output
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+++
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...
X
Xforeach try_bin (/usr/local{/bin,} /usr/public/bin)
X	echo -n looking at $try_bin...
X	if (-w $try_bin) then
X		set bin_dir=$try_bin && echo yep... && break
X	endif
X	echo ""
Xend
X
X#	figure out where to put the man pages when the user
X#	"make install"...a few places come to mind...
X
Xforeach try_man (/usr/{,local/,public/}man/man1)
X	echo -n looking at $try_man...
X	if (-w $try_man) then
X		set man_dir=$try_man && echo yep... && break
X	endif
X	echo ""
Xend
X
Xif ($?bin_dir) then
X	echo BIN_DIR looks like it will be $bin_dir
Xelse
X	echo BIN_DIR is unknown...check the Makefile when it comes up...
X	set bin_dir=""
Xendif
X
Xif ($?man_dir) then
X	echo MAN_DIR looks like it will be $man_dir
Xelse
X	echo MAN_DIR is unknown...check the Makefile when it comes up...
X	set man_dir=""
Xendif
X
Xset myedit=vi
Xif ($?EDITOR) then
X	set myedit=$EDITOR
Xendif
X
Xoh_really:
X	if (! -e $myedit) then
X		echo -n "I don't see a $myedit...where is your editor? "
X		set myedit=$<
X		echo ok...
X	endif
X
Xecho editor of choice seems to be \"$myedit\"...
X
Xif  (-e Makefile) mv Makefile{,.old} >& /dev/null
X
Xecho making makefile...
Xsed	-e "s+^BIN_DIR.*+BIN_DIR = $bin_dir+" \
X	-e "s+^MAN_DIR.*+MAN_DIR = $man_dir+" \
X	-e "s+^EDITOR.*+EDITOR = $myedit+" \
X	< Makefile.dist > Makefile
X
Xecho going to $myedit with Makefile for you to check out..
Xecho -n press return...
Xset ignore=$<
X$myedit Makefile || \
X	echo where is your editor\?\!\!  && goto oh_really
X
Xecho " "
Xecho clean up any residue from before, doing a \"make clean\"
Xsleep 2
Xmake clean
X
Xecho " "
Xecho going to run a \"make depend\"...
Xsleep 2
Xmake depend
X
Xecho " "
Xecho ok, let\'s try to make this with \"make release\"
Xsleep 2
Xmake release
Xecho you can try this out with:
Xecho demo
!STUFFY!FUNK!
echo Extracting TODO
sed >TODO <<'!STUFFY!FUNK!' -e 's/X//'
XTODO file...
X
X	[Author's request:  if you implement any of this, please
X	send me a patch!  I'd appreciate it]
X
X	add support for arrow keys
X	handle control characters better...
X
X	I'm not sure what would constitute reasonable
X	return values (have negative values? values greater than 128?)
X	Is there a canonical return value list for keyboard events?
X
X
X	KEY BINDINGS/ABBREVATIONS
X
X	This can be handled in one step.  Command lines could be
X	specified as "a=1 b=2 c=3 d=error ex=expand".  This would map
X	the letter a to "1", "b" to "2", "c" to "3", "d" to "error", and
X	"ex" to "expand".
X
X	INPUT MAPPING
X
X	The present input mapping is primitive.  It would be better
X	to map on a character by character basis.  I haven't worked
X	out a complete syntax.  One possibilty is:
X
X	"Ulllnnn"
X
X	which would be an uppercase character, followed by three
X	lowercase characters, followed by three numbers.  Also:
X
X	"[aeiou]lllWUUU"
X
X	would be a vowel for the first character, followed by
X	a whitespace char (space or tab), followed by three uppercase
X	characters.
X
X	It would be good to introduce more flexibility into this:
X
X	"Ul*WUl*"
X
X	would be an uppercase character, followed by ZERO or More
X	lowercase characters, whitespace, then upper and ZERO
X	or more lowers.
X
X	c	character [A-Za-z]
X	U	uppercase char
X	l	lowercase char
X	n	number 0-9
X	p	punctuation
X	W	whitespace (SPC or TAB)
X	[]	group ("c" would be [Ul])
X	*	zero or more 
X
X
X	stronger erase/kill processing...
X		check isatty ()...
X		this needs to be implemented for SYS V
X
X	default should/could occur with whitespace...not limited to return.
X		rather than add yet another flag, I think I will
X		extend the -r flag, so that you can say "-rw" or
X		"-r w" and have an *initial* whitespace (such as a TAB,
X		and aside from a RETURN, which would continue to work
X		anywhere) give the same effect as if RETURN had been typed...
X		a "-rW" or "-r W" would give you the ability to finish
X		input as soon as any whitespace had been typed...
X
X	give an option where the status returned is a count of seconds
X	that it took to answer, rather than the count of characters
X	entered....could be useful for some people
X
X	When some/all of this is implemented, and support for other
Xmachines starts coming together, I'll cobble together a 2.0.
X
X	better Makefile and Config :-)
X
X	Thatzit for now...
!STUFFY!FUNK!
echo Extracting Makefile.dist
sed >Makefile.dist <<'!STUFFY!FUNK!' -e 's/X//'
X#	$Header: Makefile.dist,v 1.9 89/12/29 21:14:31 daniel grabchars_1_9 $
X#
X#       Makefile for grabchars
X#
X#	Dan Smith (daniel@island.uu.net), November 1988, April 1990
X#
X#	defines...
X#
X#	BSD or SYS_V, if you port to something else, add a comment
X#	here, #ifdef the source and please send me the patches, thanks!
X#
X#	DV_ERASE is a stab at putting erase/kill processing
X#	in this, it will only work under BSD at the moment 
X#
X#	Use -DSIGRET=void for Sun OS 4.X...
X#
X#	Uncomment the one that applies...
XDEFS = -DBSD -DDV_ERASE -DSIGRET=void
X#DEFS = -DSYS_V
X
XSRCS =	globals.c grabchars.c sys.c
XOBJS =	globals.o grabchars.o sys.o
XHDRS =	grabchars.h
XMAN_PAGE = grabchars.1
XOTHERS = README Config TODO Makefile.dist demo
XALL_TEXT = $(OTHERS) $(MAN_PAGE) $(SRCS) $(HDRS)
X
X#	where to put things...
XBIN_DIR =
XMAN_DIR =
X
X#	for rcs...
XSTATE = grabchars_1_9
XVERSION = 1.9
X
X#	change to suit...
XPRINTER = -Pceylonlw
XEDITOR = /usr/ucb/vi
X
X#	if you have this, check the path.  If you don't, leave it alone...
XMKID = mkid
XSHAR = shar -v
X
XCC = cc
XCFLAGS = -O
X
X#	force this, leave csh out of it...
XSHELL=/bin/sh
X
X.c.o:
X	$(CC) -c $(CFLAGS) $(DEFS) $*.c
X
Xall: grabchars
X
Xrelease:
X	@make CFLAGS=-O all
X
Xgrabchars: $(OBJS)
X	- mv grabchars grabchars.old 2>/dev/null
X	@echo loading...
X	$(CC) $(CFLAGS) $(OBJS)  -o grabchars
X	@echo done...
X
Xinstall: release
X	- cp grabchars $(BIN_DIR)
X	- cp grabchars.1 $(MAN_DIR) && echo "preparing man page" \
X	&& man grabchars
X 
Xid: $(SRCS) $(HDRS)
X	- $(MKID) $(SRCS) $(HDRS) || echo $(MKID) not available...
X
Xe: $(SRCS) $(HDRS)
X	$(EDITOR) $(SRCS) $(HDRS) Makefile
X
Xpgrind:
X	@echo pgrinding out sources...
X	lpq $(PRINTER)
X	pgrind $(SRCS) $(HDRS) Makefile
X
Xrcs:
X	@echo checking grabchars in to RCS...
X	ci -s$(STATE) -r$(VERSION) -f -u $(ALL_TEXT)
X
Xshar:
X	@echo bundling up grabchars for transit...
X	$(SHAR) $(ALL_TEXT) > grabchars.shar
X
Xclean: id tags
X	- touch $(OBJS) grabchars
X	- /bin/rm $(OBJS) grabchars
X
Xdepend: 
X	@echo making dependencies...
X	sed -n '1,/^#	lines after this point/p' Makefile >.depends &&\
X	cc -M $(SRCS) | grep -v "/usr/include" | \
X	tee /dev/tty >> .depends && mv .depends Makefile
X
Xtags: $(SRCS)
X	ctags $(SRCS)
X
X#       lines after this point produced with cc -M, leave this line here
!STUFFY!FUNK!
echo ""
echo "End of kit 1 (of 2)"
cat /dev/null >kit1isdone
run=''
config=''
for iskit in 1 2; do
    if test -f kit${iskit}isdone; then
	run="$run $iskit"
    else
	todo="$todo $iskit"
    fi
done
case $todo in
    '')
	echo "You have run all your kits.  Please read README and then type Config."
	chmod 755 Config
	;;
    *)  echo "You have run$run."
	echo "You still need to run$todo."
	;;
esac
: Someone might mail this, so...
exit


