daniel@unicom.UUCP (Dan Smith "vote early and often...") (10/28/88)
Posting-number: Volume 5, Issue 19 Submitted-by: "Dan Smith "vote early and often..."" <daniel@unicom.UUCP> Archive-name: grabchars "grabchars" gets one or more keystrokes from the user, without requiring them to hit return. It was written to make shell scripts (doesn't matter what type) more interactive. I know that it works fine on Suns running SUN OS 3.2-3.5, and a Vax 11/750 running Mt. Xinu 4.3 BSD. You'll find uses for this in all sorts of places. The prime candidate is in your .login file, it's easy to use this to select different options. I've provided a "demo" csh script which runs through many of the options. See the README for more... dan dan smith, island graphics, marin co, ca| +1 (415) 491 1000(W), 332 FAST(H) 4000 civic center dr, san rafael 94903 | dis: they're solely my opinions daniel@island.uu.net {ucbvax!ucbcad,sun}!island!daniel pacbell!unicom!daniel I'd rather have Roosevelt in a wheelchair, than Reagan & Bush on a horse -Jesse #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # TODO # Makefile # grabchars.c # patchlevel.h # grabchars.1 # demo # This archive created: Wed Oct 26 21:08:10 1988 # By: Dan Smith "vote early and often..." () export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'README'" '(2463 characters)' if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \SHAR_EOF > 'README' "grabchars" gets one or more keystrokes from the user, without requiring them to hit return. It was written to make shell scripts (doesn't matter what type) more interactive. I know that it works fine on Suns running SUN OS 3.2-3.5, and a Vax 11/750 running Mt. Xinu 4.3 BSD. You should be able to type "make depend", followed by "make" or "make install". I haven't tried this on a Sys V machine. If you do, and it works, let me know. You will need getopt (3) to compile this program. If you're not sure if you have this, try "nm /lib/libc.a | grep getopt". Get a Public Domain version if you don't (write me if you get really stuck, I have it). You'll find uses for this in all sorts of places. The prime candidate is in your .login file, it's easy to use this to select different options. I've provided a "demo" csh script which runs through many of the options. If you make any changes, *please* send me diffs! This is in the public domain, don't make money off of it, and don't pretend you wrote it, and I'll be happy :-) Have fun! dan Usage rundown: grabchars gets one keystroke grabchars -c<valid characters> only <valid chars> are returned grabchars -e output to stderr instead of stdout grabchars -p<prompt> prompt to help user grabchars -q<prompt> prompt to help user (through stderr) grabchars -n<number> number of characters to read grabchars -t<seconds> timeout after <seconds> examples: (values to arguments can be in the same word or the next one) grabchars -caeiou or grabchars -c aeiou get one of the vowels grabchars -c i get the letter 'i' grabchars '-penter a letter ' print the prompt "enter a letter " grabchars '-qenter a letter ' print the prompt ('q' for question) "enter a letter " through stderr... grabchars -n4 get four characters grabchars -t2 timeout after two seconds print a prompt and grab three characters... grabchars -p 'enter three characters >> ' -n 3 get two numbers with a ten second timeout... grabchars -c 0123456789 -n2 -t10 note that arguments like "-n4" or "-n 4" are handled the same way grabchars -h will give a usage screen... dan smith, island graphics, marin co, ca| +1 (415) 491 1000(W), 332 FAST(H) 4000 civic center dr, san rafael 94903 | dis: they're solely my opinions daniel@island.uu.net {ucbvax!ucbcad,sun}!island!daniel pacbell!unicom!daniel I'd rather have Roosevelt in a wheelchair, than Reagan & Bush on a horse -Jesse SHAR_EOF fi echo shar: "extracting 'TODO'" '(496 characters)' if test -f 'TODO' then echo shar: "will not over-write existing file 'TODO'" else cat << \SHAR_EOF > 'TODO' TODO file... add "-s" option (silent) so that we can just work off of the return value... this is cinchy, I just haven't gotten to it yet, but I want to get this out. add support for arrow keys and other special keys... I have the code for this in my "phonemail" program (to be posted soon). I'm not sure what would constitute reasonable return values (have negative values? values greater than 128?) Is there a canonical return value list for keyboard events? Thatzit for now... SHAR_EOF fi echo shar: "extracting 'Makefile'" '(2122 characters)' if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else cat << \SHAR_EOF > 'Makefile' # # Makefile for grabchars # # 10/88 dan smith, daniel@island.uu.net # SRCS = grabchars.c OBJS = grabchars.c HDRS = patchlevel.h DESTBIN = /usr/public/bin DESTMAN = /usr/public/man/man1 CC = cc CFLAGS = -O .c.o: $(CC) -c $(CFLAGS) $(DEFS) $*.c all: grabchars grabchars: $(OBJS) touch grabchars mv grabchars grabchars.old @echo starting... $(CC) $(CFLAGS) $(OBJS) -o grabchars @echo done... pgrind: @echo pgrinding out sources... lpq $(PRINTER) pgrind $(SRCS) $(HDRS) Makefile shar: @echo bundling up grabchars for transit... shar -v README TODO Makefile $(SRCS) $(HDRS) grabchars.1 demo > sendme clean: touch $(OBJS) grabchars grabchars.old - /bin/rm $(OBJS) grabchars grabchars.old install: grabchars - cp grabchars $(DESTBIN) - cp grabchars.1 $(DESTMAN) @echo formatting man page... man grabchars depend: @echo making dependencies... sed -n '1,/^# lines after this point/p' Makefile >.depends &&\ cc -M $(SRCS) >> .depends && mv .depends Makefile tags: $(SRCS) ctags $(SRCS) # lines after this point produced with cc -M, leave this line here grabchars.o: grabchars.c grabchars.o: /usr/include/stdio.h grabchars.o: /usr/include/signal.h grabchars.o: /usr/include/sgtty.h grabchars.o: /usr/include/sys/ioctl.h grabchars.o: /usr/include/sys/ttychars.h grabchars.o: /usr/include/sys/ttydev.h grabchars.o: /usr/include/ctype.h grabchars.o: /usr/include/string.h grabchars.o: grabchars.c grabchars.o: /usr/include/stdio.h grabchars.o: /usr/include/signal.h grabchars.o: /usr/include/sgtty.h grabchars.o: /usr/include/sys/ioctl.h grabchars.o: /usr/include/sys/ttychars.h grabchars.o: /usr/include/sys/ttydev.h grabchars.o: /usr/include/ctype.h grabchars.o: /usr/include/string.h grabchars.o: /usr/include/strings.h grabchars.o: grabchars.c grabchars.o: /usr/include/stdio.h grabchars.o: /usr/include/signal.h grabchars.o: /usr/include/sgtty.h grabchars.o: /usr/include/sys/ioctl.h grabchars.o: /usr/include/sys/ttychars.h grabchars.o: /usr/include/sys/ttydev.h grabchars.o: /usr/include/ctype.h grabchars.o: /usr/include/string.h grabchars.o: /usr/include/strings.h SHAR_EOF fi echo shar: "extracting 'grabchars.c'" '(4787 characters)' if test -f 'grabchars.c' then echo shar: "will not over-write existing file 'grabchars.c'" else cat << \SHAR_EOF > 'grabchars.c' /* ** grabchars.c - get characters directly from the user ** ** October 23, 1988, Dan Smith (daniel@island.uu.net) ** ** This program grabs characters from the user as they are ** typed in, without having to wait for the return key to ** be pressed. Among other things, this allows shell scripts ** to be written with highly interactive menus... ** ** Usage rundown: ** ** grabchars gets one keystroke ** grabchars -c<valid characters> only <valid chars> are returned ** grabchars -e output to stderr instead of stdout ** grabchars -p<prompt> prompt to help user ** grabchars -q<prompt> prompt to help user (through stderr) ** grabchars -n<number> number of characters to read ** grabchars -t<seconds> timeout after <seconds> ** ** examples: (values to arguments can be in the same word or the next one) ** ** grabchars -caeiou or ** grabchars -c aeiou get one of the vowels ** grabchars -c i get the letter 'i' ** grabchars '-penter a letter ' print the prompt "enter a letter " ** grabchars '-qenter a letter ' print the prompt ('q' for question) ** "enter a letter " through stderr... ** grabchars -n4 get four characters ** grabchars -t2 timeout after two seconds ** ** print a prompt and grab three characters... ** grabchars -p 'enter three characters >> ' -n 3 ** ** get two numbers with a ten second timeout... ** grabchars -c 0123456789 -n2 -t10 ** ** note that arguments like "-n4" or "-n 4" are handled the same way */ #include <stdio.h> #include <signal.h> #include <sgtty.h> #include <ctype.h> #include <string.h> struct sgttyb orig, new; int exit_stat; char *usage_statement[] = { "usage:", "grabchars gets one keystroke", " -c<valid characters> only <valid chars> are returned", " -e output to stderr instead of stdout", " -p<prompt> prompt to help user", " -q<prompt> prompt to help user (through stderr)", " -n<number> number of characters to read", " -t<seconds> timeout after <seconds>", " ", "examples: (values to arguments can be in the same word or the next one)", " ", "grabchars -caeiou or", "grabchars -c aeiou get one of the vowels", "grabchars -c i get the letter 'i'", " ", "grabchars '-penter a letter ' print the prompt \"enter a letter \"", "grabchars -n4 get four characters", "grabchars -t2 timeout after two seconds", " ", "print a prompt and grab three characters...", "grabchars -p 'enter three characters >> ' -n 3", 0 }; main (argc, argv) int argc; char **argv; { extern int optind, opterr; extern char *optarg; int how_many = 1, check_flag = 0; int i; int timeout; char ch; char comarg; char valid_chars[128]; FILE *outfile = stdout; int lets_go (), overtime (); /* handle the outside world */ signal (SIGINT, lets_go); signal (SIGTSTP, lets_go); signal (SIGQUIT, lets_go); alarm (0); opterr = 0; exit_stat = -1; /* if we're interrupted, exit with this status */ while ((comarg = getopt (argc, argv, "ec:n:p:q:t:")) != EOF) { switch (comarg) { case 'c': check_flag = 1; strcpy (valid_chars, optarg); break; case 'e': outfile = stderr; break; case 'n': how_many = atoi (optarg); if (how_many <= 0) { fprintf (stderr, "number of characters to read must be greater than zero\n"); exit (-1); } break; case 'p': fprintf (stdout, "%s", optarg); break; case 'q': fprintf (stderr, "%s", optarg); break; case 't': timeout = atoi (optarg); if (timeout <= 0) { fprintf (stderr, "number of seconds to timeout must be greater than zero\n"); exit (-1); } /* ** we must have some valid time >0 seconds to ** get here, so we'll set an alarm... */ signal (SIGALRM, overtime); alarm ((unsigned int) timeout); break; /* ** I bet I could leave out "default", but ** I also bet that all getopt () routines ** are not created equal, so in it stays! */ case '?': default: i = 0; while (usage_statement[i]) puts (usage_statement[i++]); exit (-1); } } /* play havoc with the terminal :-) */ ioctl (0, TIOCGETP, &orig); new = orig; new.sg_flags &= ~ECHO; new.sg_flags |= CBREAK; ioctl (0, TIOCSETP, &new); for (i = 0; i < how_many; i++) { ch = getchar (); if (check_flag) if ( ! (any (ch, valid_chars))) { i--; continue; } putc (ch, outfile); } exit_stat = i; lets_go (); } /* ** something's up with the user...give a useful exit status so ** we can ask things like "do you need help?" */ int overtime () { exit_stat = -2; lets_go (); } /* clean up and get out of here... */ int lets_go () { ioctl (0, TIOCSETP, &orig); exit (exit_stat); } /* ** do any chars "s" match 'c'? */ any (c, s) register int c; register char *s; { while (*s) if (*s++ == c) return (1); return (0); } SHAR_EOF fi echo shar: "extracting 'patchlevel.h'" '(21 characters)' if test -f 'patchlevel.h' then echo shar: "will not over-write existing file 'patchlevel.h'" else cat << \SHAR_EOF > 'patchlevel.h' #define PATCHLEVEL 0 SHAR_EOF fi echo shar: "extracting 'grabchars.1'" '(3009 characters)' if test -f 'grabchars.1' then echo shar: "will not over-write existing file 'grabchars.1'" else cat << \SHAR_EOF > 'grabchars.1' ''' Man page for grabchars, uses Larry Wall's "patch" man page as ''' a template. .de Sh .br .ne 5 .PP \fB\\$1\fR .PP .. .de Sp .if t .sp .5v .if n .sp .. ''' ''' Set up \*(-- to give an unbreakable dash; ''' string Tr holds user defined translation string. ''' Bell System Logo is used as a dummy character. ''' .ie n \{\ .tr \(bs-\*(Tr .ds -- \(bs- .if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch .if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch .ds L" "" .ds R" "" .ds L' ' .ds R' ' 'br\} .el\{\ .ds -- \(em\| .tr \*(Tr .ds L" `` .ds R" '' .ds L' ` .ds R' ' 'br\} .TH GRABCHARS 1 LOCAL .SH NAME grabchars - get keystrokes directly from user .SH SYNOPSIS .B grabchars [options] .SH DESCRIPTION .I Grabchars gets characters from the user as they are typed in, without having to wait for the return key to be pressed. Among other things, this allows shell scripts to be written with highly interactive menus. .PP By default, .I grabchars will obtain one character from stdin, echo that character to stdout, and return with a status of one; meaning one character read. .TP 5 .B \-c<valid characters> Only characters in .I <valid characters> are accepted. All other characters are ignored. .TP 5 .B \-e Output goes to .I stderr rather than .I stdout. .TP 5 .B \-p<prompt> Sets up a prompt for the user. See .I EXAMPLES. .TP 5 .B \-q<prompt> Sets up a prompt for the user, except it is printed to .I stderr rather than .I stdout. .TP 5 .B \-n<number> Number of characters to read. By default, .I grabchars looks for one character. .TP 5 .B \-t<seconds> Time to allow the user to respond. By default, the user can take as long as he or she wants to. The timeout option allows you to write shell scripts where you can offer some assistance if it's obvious that the user might be stuck. .SH EXAMPLES .TP 5 .B grabchars gets one keystroke .TP 5 .B grabchars \-caeiou get one of the vowels .TP 5 .B grabchars -c i get the letter 'i' .TP 5 .B grabchars '\-penter a letter ' print the prompt "enter a letter " .TP 5 .B grabchars '\-qenter a letter ' print the prompt ('q' for question) "enter a letter " through .I stderr. .TP 5 .B grabchars \-n4 get four characters. .TP 5 .B grabchars \-t2 timeout after two seconds. .TP 5 .B grabchars \-n3 \-p 'initials: ' print a prompt and grab three characters. .TP 5 .B grabchars \-c 0123456789 \-n2 \-t10 get two numbers with a ten second timeout. .PP note that arguments like "-n4" or "-n 4" are handled the same way .SH SEE ALSO csh(1) and sh(1) for syntax of .I csh and .I sh scripts, respectively. See "The Unix Csh Field Guide", by Gail and Paul Anderson (Prentice Hall), for an excellent tour of csh and good examples of writing csh scripts. .SH DIAGNOSTICS .I Grabchars returns .B \-2 if it times out, or .B \-1 if it gives a usage statement. Otherwise, it returns the number of characters successfully read. .SH AUTHOR .nf Dan Smith (daniel@island.uu.net or {ucbvax!ucbcad,well,sun}!island!daniel) SHAR_EOF fi echo shar: "extracting 'demo'" '(1412 characters)' if test -f 'demo' then echo shar: "will not over-write existing file 'demo'" else cat << \SHAR_EOF > 'demo' #!/bin/csh -f clear cat << GUMBY Grabchars demo... get one character with "grabchars" GUMBY grabchars echo " status returned was $status" cat << POKEY grab a vowel with "grabchars -caeiou" Type something that isn't a vowel at first... POKEY grabchars -caeiou echo " status returned was $status" cat << WILMA prompt the user with "grabchars -p 'give me any character >> '" WILMA grabchars -p 'give me any character >> ' echo " status returned was $status" cat << FRED prompt through stderr with "grabchars -q 'give me any character >> '", so that we can set the variable "user_char"... FRED set user_char=`grabchars -q 'give me any character >> '` echo " status returned was $status" echo '$user_char = '$user_char cat << BETTY enter three characters... "grabchars -n3" BETTY grabchars -n3 echo " status returned was $status" cat << BARNEY enter 10 characters within 3 seconds... "grabchars -n10 -t3" BARNEY grabchars -n10 -t3 set really_typed=$status if ($really_typed == 10) then echo 'hey\! you got 10\!?' else echo " $really_typed returned...means that grabchars timed out..." endif cat << PEBBLES The last one... get two numbers with a ten second timeout... trying "grabchars -c 0123456789 -n2 -t10 -p 'give me 2 numbers >> ' PEBBLES grabchars -c 0123456789 -n2 -t10 -p 'give me 2 numbers >> ' echo " status returned was $status" echo "" echo test/demo done...enjoy\! SHAR_EOF chmod +x 'demo' fi exit 0 # End of shell archive