home *** CD-ROM | disk | FTP | other *** search
- From decwrl!hplabs!hpda!hpwala!cfisun!ima!mirror!necntc!ncoast!allbery Sat Dec 17 21:43:52 PST 1988
- Article 755 of comp.sources.misc:
- Path: granite!decwrl!hplabs!hpda!hpwala!cfisun!ima!mirror!necntc!ncoast!allbery
- From: daniel@island.UUCP (Dan Smith, Release 28.0)
- Newsgroups: comp.sources.misc
- Subject: v05i073: grabchars 1.3, get/filter keystrokes directly from user (BSD)
- Message-ID: <8812060537.AA03805@island.uucp >
- Date: 7 Dec 88 00:43:23 GMT
- Sender: allbery@ncoast.UUCP
- Reply-To: daniel@island.UUCP (Dan Smith, Release 28.0)
- Lines: 1368
- Approved: allbery@ncoast.UUCP
-
- Posting-number: Volume 5, Issue 73
- Submitted-by: "Dan Smith, Release 28.0" <daniel@island.UUCP>
- Archive-name: grabchars1.3
-
- "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. I don't have support
- for SYS V or VMS or anything else like that yet; send me patches...
- Installation should be pretty straighforward, read the README and
- run Config.
-
- 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. For the most part, grabchars can replace the use of "$<" in
- csh scripts and "read" in sh scripts, and offer filtering, timeouts, and
- default answers - the exception at the moment is in trying to read control
- characters.
-
- Enjoy, and send me your comments/suggestions. 2.0 will provide
- SYS V support.
-
- dan
-
- DanSmith IslandGraphics 4000CivicCenterDr SanRafael MarinCo CA 94903 4154911000
- 415 332 FAST(h) 491 0402(Fax)|d: Nobodys' fault but mine| UnixFeastsMusicFilm
- daniel@island.uu.net unicom!daniel@pacbell.com {lll-crg,apple}!well!dansmith
-
- ------------------------ snip -------- cut ------- chop ---------
- #! /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
- # Config
- # TODO
- # Makefile.dist
- # demo
- # grabchars.1
- # globals.c
- # grabchars.c
- # sys.c
- # grabchars.h
- # This archive created: Mon Dec 5 19:52:34 1988
- # By: Dan "Bucko" Smith ()
- export PATH; PATH=/bin:/usr/bin:$PATH
- echo shar: "extracting 'README'" '(3940 characters)'
- if test -f 'README'
- then
- echo shar: "will not over-write existing file 'README'"
- else
- cat << \SHAR_EOF > 'README'
- Grabchars 1.3
-
- Copyright (c) 1988, Dan Smith
-
- What this is:
-
- "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. I don't have support
- for SYS V or VMS or anything else like that yet; send me patches...
-
- 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. For the most part, grabchars can replace the use of "$<" in
- csh scripts and "read" in sh scripts, and offer filtering, timeouts, and
- default answers - the exception at the moment is in trying to read control
- characters.
-
- Putting this together:
-
- Run Config; it will figure out where you might want to
- put the executable and the man page. It will also put you into an
- editor with the Makefile, and then it will run a "make clean",
- "make depend", and a "make release". 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).
-
- Usage: (see the man page for more...)
-
- grabchars gets one keystroke
- grabchars -b output to stdout and stderr
- grabchars -c<valid characters> only <valid chars> are returned
- grabchars -d<char(s)> default char or string to return
- grabchars -e output to stderr instead of stdout
- grabchars -f flush any previous input
- grabchars -h help screen
- grabchars -n<number> number of characters to read
- grabchars -p<prompt> prompt to help user
- grabchars -q<prompt> prompt to help user (through stderr)
- grabchars -r RETURN exits (use with -n)
- grabchars -s silent, just return status
- grabchars -t<seconds> timeout after <seconds>
- grabchars -E erase/kill character processing
-
- 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...
-
- Legal paragraph:
-
- Grabchars may be freely copied, changed, and used, in whole or
- in part. Don't try to make any money off of it. Don't remove my headers.
- Don't say you wrote it. This kit should be kept freely available, disk
- space permitting. Not responsible for mishaps arising out of the use
- of this program, on the other hand, I haven't run into any problems with
- this. Lastly, Do not use any part of grabchars in any commercial product.
- Let's change the subject :-)
-
- Patches and things like that:
-
- "Help! Save The World!" (LW)...if you make any modifications
- to grabchars, send me a diff suitable for use with the patch program.
- I don't want ten different versions of this running around. I need to
- know what changes need to be made to get this to run on Sys V and other
- Unix (or other) variants. I don't use them, so at this point I don't
- know!
-
- Where to reach me:
-
- Dan Smith
- Island Graphics
- 4000 Civic Center Drive
- San Rafael, Ca 94903
- +1 (415) 491 1000 (w), 491 0402 (fax), 332 3278 (h)
-
- daniel@island.uu.net or
- {pixar,grenada,ucbvax!ucbcad,unicom,well,sun,uunet}!island!daniel
-
- unicom!daniel@pacbell.com
-
- {apple,lll-crg}!well!dansmith
-
- SHAR_EOF
- fi
- echo shar: "extracting 'Config'" '(2396 characters)'
- if test -f 'Config'
- then
- echo shar: "will not over-write existing file 'Config'"
- else
- cat << \SHAR_EOF > 'Config'
- #!/bin/csh -f
- #
- # $Header: Config,v 1.3 88/12/05 19:45:39 daniel grabchars_1_3 $
- #
- # Config - set up Makefile for grabchars
- #
- # Dan Smith (daniel@island.uu.net), November 1988
- #
- # Config file for grabchars... must be csh, no attempt
- # made to be eunice (i.e. echo " ") compatible...
- #
- clear
- cat << GUMBY
-
- Config for grabchars
-
- This csh script will figure out a few things about your
- system, and then will run a "make clean", a "make depend", and
- a "make release". You should then try out grabchars, and, once assured
- that it's behaving itself, you should run a "make install"
-
- GUMBY
- echo -n 'press return to start...'
- set ignore=$<
-
- # figure out where to put this when the user types
- # "make install"...we'll try in three likely places...
-
- foreach try_bin (/usr/local{/bin,} /usr/public/bin)
- echo -n looking at $try_bin...
- if (-w $try_bin) then
- set bin_dir=$try_bin && echo yep... && break
- endif
- echo ""
- end
-
- # figure out where to put the man pages when the user
- # "make install"...a few places come to mind...
-
- foreach try_man (/usr/{,local/,public/}man/man1)
- echo -n looking at $try_man...
- if (-w $try_man) then
- set man_dir=$try_man && echo yep... && break
- endif
- echo ""
- end
-
- if ($?bin_dir) then
- echo BIN_DIR looks like it will be $bin_dir
- else
- echo BIN_DIR is unknown...check the Makefile when it comes up...
- set bin_dir=""
- endif
-
- if ($?man_dir) then
- echo MAN_DIR looks like it will be $man_dir
- else
- echo MAN_DIR is unknown...check the Makefile when it comes up...
- set man_dir=""
- endif
-
- set myedit=vi
- if ($?EDITOR) then
- set myedit=$EDITOR
- endif
-
- oh_really:
- if (! -e $myedit) then
- echo -n "I don't see a $myedit...where is your editor? "
- set myedit=$<
- echo ok...
- endif
-
- echo editor of choice seems to be \"$myedit\"...
-
- if (-e Makefile) mv Makefile{,.old} >& /dev/null
-
- echo making makefile...
- sed -e "s+^BIN_DIR.*+BIN_DIR = $bin_dir+" \
- -e "s+^MAN_DIR.*+MAN_DIR = $man_dir+" \
- -e "s+^EDITOR.*+EDITOR = $myedit+" \
- < Makefile.dist > Makefile
-
- echo going to $myedit with Makefile for you to check out..
- echo -n press return...
- set ignore=$<
- $myedit Makefile || \
- echo where is your editor\?\!\! && goto oh_really
-
- echo " "
- echo clean up any residue from before, doing a \"make clean\"
- sleep 2
- make clean
-
- echo " "
- echo going to run a \"make depend\"...
- sleep 2
- make depend
-
- echo " "
- echo ok, let\'s try to make this with \"make release\"
- sleep 2
- make release
- SHAR_EOF
- chmod +x 'Config'
- fi
- echo shar: "extracting 'TODO'" '(403 characters)'
- if test -f 'TODO'
- then
- echo shar: "will not over-write existing file 'TODO'"
- else
- cat << \SHAR_EOF > 'TODO'
- TODO file...
-
- add SYS V code...
-
- add support for arrow keys handle control characters better...
-
- 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?
-
- stronger erase/kill processing...
-
- Thatzit for now...
-
- SHAR_EOF
- fi
- echo shar: "extracting 'Makefile.dist'" '(2088 characters)'
- if test -f 'Makefile.dist'
- then
- echo shar: "will not over-write existing file 'Makefile.dist'"
- else
- cat << \SHAR_EOF > 'Makefile.dist'
- # $Header: Makefile.dist,v 1.3 88/12/05 19:45:47 daniel grabchars_1_3 $
- #
- # Makefile for grabchars
- #
- # Dan Smith (daniel@island.uu.net), November 1988
- #
- SRCS = globals.c grabchars.c sys.c
- OBJS = globals.o grabchars.o sys.o
- HDRS = grabchars.h
- MAN_PAGE = grabchars.1
- OTHERS = README Config TODO Makefile.dist demo
- ALL_TEXT = $(OTHERS) $(MAN_PAGE) $(SRCS) $(HDRS)
-
- # where to put things...
- BIN_DIR =
- MAN_DIR =
-
- # for rcs...
- STATE = grabchars_1_3
- VERSION = 1.3
-
- # change to suit...
- PRINTER = -Pislandlw
- EDITOR = /usr/ucb/vi
-
- # if you have this, check the path. If you don't, leave it alone...
- MKID = mkid
- SHAR = shar -v
-
- CC = cc
- CFLAGS = -g
-
- # defines...
- #
- # BSD is mandatory for now...if you patch for SYS V please
- # #ifdef for "SYS_V" in init_term (), handle_erase (),
- # and lets_go ()...
- #
- # DV_ERASE is my friends stab at putting erase/kill processing
- # in this. I haven't thoroughly tested it (-E option)
- DEFS = -DBSD -DDV_ERASE
-
- # force this, leave csh out of it...
- SHELL=/bin/sh
-
- .c.o:
- $(CC) -c $(CFLAGS) $(DEFS) $*.c
-
- all: grabchars
-
- release:
- @make CFLAGS=-O all
-
- grabchars: $(OBJS)
- - mv grabchars grabchars.old 2>/dev/null
- @echo loading...
- $(CC) $(CFLAGS) $(OBJS) -o grabchars
- @echo done...
-
- install: release
- - cp grabchars $(BIN_DIR)
- - cp grabchars.1 $(MAN_DIR) && echo "preparing man page" \
- && man grabchars
-
- id: $(SRCS) $(HDRS)
- - $(MKID) $(SRCS) $(HDRS) || echo $(MKID) not available...
-
- e: $(SRCS) $(HDRS)
- $(EDITOR) $(SRCS) $(HDRS) Makefile
-
- pgrind:
- @echo pgrinding out sources...
- lpq $(PRINTER)
- pgrind $(SRCS) $(HDRS) Makefile
-
- rcs:
- @echo checking grabchars in to RCS...
- ci -s$(STATE) -r$(VERSION) -f -u $(ALL_TEXT)
-
- shar:
- @echo bundling up grabchars for transit...
- $(SHAR) $(ALL_TEXT) > grabchars.shar
-
- clean: id tags
- - touch $(OBJS) grabchars
- - /bin/rm $(OBJS) grabchars
-
- depend:
- @echo making dependencies...
- sed -n '1,/^# lines after this point/p' Makefile >.depends &&\
- cc -M $(SRCS) | tee /dev/tty >> .depends && mv .depends Makefile
-
- tags: $(SRCS)
- ctags $(SRCS)
-
- # lines after this point produced with cc -M, leave this line here
- SHAR_EOF
- fi
- echo shar: "extracting 'demo'" '(1881 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
-
- def_test:
- cat << RADRED
-
- let the user pick a default...
- using "grabchars -d yes -p 'just hit return '...
-
- RADRED
- # the '-f' here is used to flush any previous input...
- grabchars -f -d yes -p 'just hit return '
- if ($status != 3) then
- echo whoops\! Let\'s try that again...
- goto def_test
- endif
-
- cat << MUSKRAT
-
- same idea, but let this timeout in four seconds...
- using "grabchars -f -d always -t 4"
-
- (don't type anything...)
-
- MUSKRAT
- grabchars -f -d always -t 4
-
- 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
- echo shar: "extracting 'grabchars.1'" '(4786 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
- \fBGrabchars\fP 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 \-b
- Both
- .I stdout
- and
- .I stderr
- are used for output. This is useful for setting a variable in
- a shell script and echoing a keystroke to the screen at the
- same time.
- .TP 5
- .B \-c<valid characters>
- Only characters in
- .I <valid characters>
- are accepted. Regular expressions such as [a-z]
- may be used to specify ranges. All other characters are ignored.
- .TP 5
- .B \-d<char(s)>
- Default char or string to output if the user hits
- .B RETURN
- or lets
- .B grabchars
- timeout. The status that is returned is the same as if the user had
- typed in the character or string, so this option may be used with
- the
- .B \-s
- (silent) flag.
- .TP 5
- .B \-e
- Output goes to
- .I stderr
- rather than
- .I stdout.
- .TP 5
- .B \-f
- Flush any previous input. By default,
- .I grabchars
- will see any characters present in
- .I stdin,
- which allows for some typeahead in shell scripts.
- .TP 5
- .B \-h
- Help/usage screen.
- .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 \-r
- The
- .B RETURN
- key exits. Use this with the -n option to allow for variable
- numbers of characters to be typed in.
- .TP 5
- .B \-n<number>
- Number of characters to read. By default,
- .I grabchars
- looks for one character.
- .TP 5
- .B \-s
- Silent. Do not output anything. Just return a status.
- .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.
- .TP 5
- .B \-E
- Erase/kill processing is done. You have use of the keys (usually
- DELETE and ^U or ^X) that you would normally have from the
- shell for deleting characters. This is useful with the
- .B \-n
- option, where many characters are being typed in. This code hasn't
- been thoroughly tested.
- .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 \-d a
- If the first character typed is a
- .B RETURN,
- pretend it was an 'a'.
- .TP 5
- .B grabchars \-d gumby
- If the first character typed is a
- .B RETURN,
- pretend that the user typed in "gumby".
- .TP 5
- .B grabchars \-r
- The
- .B RETURN
- key will exit
- .I grabchars. You would use this with the
- .B -n
- option, so that variable numbers of characters may be entered.
- .TP
- .B grabchars \-n 4 \-r \-t 10
- Accept up to four characters, or exit when
- .B RETURN
- is hit, or exit when 10 seconds have elapsed.
- .TP 5
- .B grabchars \-t2
- timeout after two seconds.
- .TP 5
- .B grabchars \-d gumby \-t2
- If the first character typed is a
- .B RETURN,
- or if two seconds have gone by,
- pretend that the user typed in "gumby".
- .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 'globals.c'" '(1778 characters)'
- if test -f 'globals.c'
- then
- echo shar: "will not over-write existing file 'globals.c'"
- else
- cat << \SHAR_EOF > 'globals.c'
- /*
- ** $Header: globals.c,v 1.3 88/12/05 19:46:01 daniel grabchars_1_3 $
- **
- ** globals.c - declare global variables for grabchars
- **
- ** Dan Smith (daniel@island.uu.net), November 29, 1988
- */
- #include <stdio.h>
- #include "grabchars.h" /* where we typedef FLAGS */
-
- FILE *outfile, *otherout;
- FLAG *flags;
-
- int exit_stat;
- char valid_chars[128], default_string[128];
-
- #ifdef DV_ERASE
- char *erase_buf; /* DV: Malloc'ed later */
- #endif
-
- /*
- ** this gets in the way sitting in grabchars.c, so I moved it here.
- ** If you add anything, keep in mind that you're trying to squeeze
- ** everything onto a 24 line window/terminal... The "-h" doc
- ** is just a convenience, any option that grabchars doesn't already
- ** use will give the help screen...
- */
- char *usage_statement[] = {
- "usage: grabchars gets one keystroke",
- " -b output to stdout and stderr",
- " -c<valid characters> only <valid chars> are returned",
- " -d<char(s)> default char or string to return",
- " -e output to stderr instead of stdout",
- " -f flush any previous input before reading",
- " -h help screen",
- " -n<number> number of characters to read",
- " -p<prompt> prompt to help user",
- " -q<prompt> prompt to help user (through stderr)",
- " -r RETURN key exits (use with -n)",
- " -s silent, just return status",
- " -t<seconds> timeout after <seconds>",
- " -E honor erase/kill characters", /* DV */
- "examples: (values to arguments can be in the same word or the next one)",
- "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
- };
- SHAR_EOF
- fi
- echo shar: "extracting 'grabchars.c'" '(8500 characters)'
- if test -f 'grabchars.c'
- then
- echo shar: "will not over-write existing file 'grabchars.c'"
- else
- cat << \SHAR_EOF > 'grabchars.c'
- /*
- ** $Header: grabchars.c,v 1.3 88/12/05 19:46:06 daniel grabchars_1_3 $
- **
- ** grabchars.c - get characters directly from the user
- **
- ** Dan Smith (daniel@island.uu.net), October 23, 1988
- **
- ** 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...
- **
- ** [to jump right to the code, search for "start of grabchars"]
- **
- ** Usage rundown:
- **
- ** grabchars gets one keystroke
- ** grabchars -b output to stdout and stderr
- ** grabchars -c<valid characters> only <valid chars> are returned
- ** grabchars -d<char(s)> default char or string to return
- ** grabchars -e output to stderr instead of stdout
- ** grabchars -f flush any previous input
- ** grabchars -h help screen
- ** grabchars -n<number> number of characters to read
- ** grabchars -p<prompt> prompt to help user
- ** grabchars -q<prompt> prompt to help user (through stderr)
- ** grabchars -r RETURN key exits (use with -n)
- ** grabchars -s silent, just return status
- ** grabchars -t<seconds> timeout after <seconds>
- ** grabchars -E erase/kill character processing
- **
- ** 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
- **
- ** History:
- **
- ** Oct 1988: versions 1.0 - 1.1
- **
- ** November 6, 1988 (1.15)
- ** added -f flag to flush input, default is to use TIOCSETN instead
- ** of TIOCSETP
- **
- ** November 22, 1988 (1.16)
- ** added -d flag for a default character or string to use if the
- ** user hits return first thing or times out. handle_default ()
- ** was added at this time
- **
- ** November 23, 1988 (1.19)
- ** added -r flag to exit when RETURN is hit. This was suggested by
- ** David Vezie.
- **
- ** November 29, 1988 (1.2)
- ** Disaster strikes...I was updating Makefile.dist, and copied SRCS
- ** to OBJS, and forgot to change grabchars.c to grabchars.o, and
- ** then (after Config) did a "make clean"! I realized that I did not
- ** have a backup, but fortunately had David Vezie's hack of grabchars.c
- ** from a few days ago (he had added erase/line kill character processing)
- ** ...moral: use RCS, check your Makefiles! Used this as an opportunity
- ** to split things up into grabchars.h, globals.c, and sys.c
- ** Got -c to handle ranges (via re_comp() and re_exec())
- */
-
- /* start of grabchars... */
- #include <stdio.h>
- #include <signal.h>
- #include "grabchars.h"
-
- /* see globals.c */
- extern FILE *outfile, *otherout;
- extern FLAG *flags;
- extern int exit_stat;
- extern char *usage_statement[];
-
- /*
- ** David Vezie (island!r2d2!dv) took a great shot at putting in
- ** erase/kill processing. I need to test this some more, and I'll
- ** most likely change it a bit. I put DV_ERASE in the Makefile
- ** as a default; take it out if it misbehaves :-)
- */
- #ifdef DV_ERASE
- extern char *erase_buf; /* DV: Malloc'ed later */
- #endif
-
- main (argc, argv)
- int argc;
- register char **argv;
- {
- /* two signal/wrapup handling routines in sys.c */
- int lets_go (), overtime ();
-
- /* for -d option */
- void handle_default ();
-
- /* for getopt () */
- extern int optind, opterr;
- extern char *optarg;
- char comarg;
-
- int how_many = 1; /* how many chars to read */
- int num_read; /* how many we have read... */
- int timeout; /* and an optional time to do it in... */
- int i; /* for usage_statement if we need it.. */
-
- /*
- ** re_comp (), re_exec () let us do things
- ** like "grabchars -c '[a-d]'" or "grabchars -c '[^a-z]'"...
- */
- char *re_comp (), *re_error;
- extern char valid_chars[128], default_string[128];
- char ch, check_str[2];
-
- alarm (0);
- opterr = 0;
- exit_stat = -1; /* if we're interrupted, exit with this status */
-
- outfile = stdout;
- otherout = stderr;
- if ((flags = (FLAG *) malloc (sizeof (FLAG))) == (FLAG *) NULL) {
- fprintf (stderr, "can't find enough memory...bye!\n");
- exit (exit_stat); /* we don't need lets_go () for this */
- }
-
- init_flags ();
- init_signal ();
-
- while ((comarg = getopt (argc, argv, "befrsEc:d:n:p:q:t:")) != EOF) {
- switch (comarg) {
- case 'b':
- flags->both = 1;
- break;
- case 'c':
- flags->check = 1;
- strcpy (valid_chars, optarg);
- if (strlen (optarg) == 0) {
- fprintf (stderr, "-c option: must have at least one valid character\n");
- exit (-1);
- }
- /*
- ** most of the time, grabchars can be
- ** called safely with things like
- ** "a-z", because we can check to
- ** see if we need to add brackets...
- */
- if (valid_chars[0] != '[' &&
- valid_chars[strlen (valid_chars) - 1] != ']')
- sprintf (valid_chars, "%c%s%c",
- '[', optarg, ']');
-
- if ((re_error = re_comp (valid_chars))
- != NULL) {
- fprintf (stderr,
- "-c option: %s\n", re_error);
- exit (-1);
- }
- break;
- case 'd':
- flags->dflt = 1;
- strcpy (default_string, optarg);
- if (strlen (optarg) == 0) {
- fprintf (stderr, "-d option: must have at least one character for default\n");
- exit (-1);
- }
- break;
- case 'e':
- outfile = stderr;
- otherout = stdout;
- break;
- case 'f':
- flags->flush = 1;
- break;
- case 'n':
- how_many = atoi (optarg);
- if (how_many <= 0) {
- fprintf (stderr, "-n option: 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 'r':
- flags->ret_key = 1;
- break;
- case 's':
- flags->silent = 1;
- break;
- case 't':
- timeout = atoi (optarg);
- if (timeout <= 0) {
- fprintf (stderr, "-t option: 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;
- case 'E': /* DV: honor erase/kill flag */
- #ifdef DV_ERASE
- flags->erase = 1;
- #else
- fprintf (stderr, "-E is disabled\n");
- exit (-1);
- #endif
- 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);
- }
- }
-
- /* we're still here, really running...now change the tty... */
- init_term ();
-
- #ifdef DV_ERASE
- /* DV: malloc (okay, well calloc) space for the erase buffer */
- if (flags->erase) {
- /* We can't do it up in the switch, because we don't know
- ** how many is how_many
- */
- erase_buf = (char *)calloc (1, how_many);
- if (erase_buf == NULL) {
- fprintf (stderr,
- "Error: Couldn't malloc space for erase buffer\n");
- exit_stat = -1;
- lets_go();
- }
- }
- #endif
-
- for (num_read = 0; num_read < how_many; num_read++) {
- ch = getchar ();
-
- /* use default_string, this does *not* return */
- if (ch == '\n' && flags->dflt && num_read == 0)
- handle_default ();
-
- /*
- ** set by -r, a RETURN key gets us out (use with -n)
- ** suggested by David Vezie
- */
- if (ch == '\n' && flags->ret_key)
- break;
-
- /* filter chars... */
- if (flags->check) {
- sprintf (check_str, "%c", ch);
- if (re_exec (check_str) != 1) {
- num_read--;
- continue;
- }
- }
-
- /*
- ** if we're just looking for a return status
- ** then have flags->silent set (-s)
- **
- ** DV: Also, we don't want to output yet,
- ** if we're processing erase/kill charfacters.
- */
- #ifdef DV_ERASE
- if (! flags->silent && ! flags->erase) {
- #else
- if (! flags->silent) {
- #endif
- putc (ch, outfile);
- if (flags->both)
- putc (ch, otherout);
- }
-
- #ifdef DV_ERASE
- if (flags->erase)
- handle_erase (ch, &num_read);
- #endif
- }
-
- #ifdef DV_ERASE
- if (flags->erase) {
- fprintf (outfile, "%s", erase_buf);
- if (flags->both)
- fprintf (otherout, "%s", erase_buf);
- }
- #endif
-
- exit_stat = num_read;
- lets_go ();
- }
- SHAR_EOF
- fi
- echo shar: "extracting 'sys.c'" '(4386 characters)'
- if test -f 'sys.c'
- then
- echo shar: "will not over-write existing file 'sys.c'"
- else
- cat << \SHAR_EOF > 'sys.c'
- /*
- ** $Header: sys.c,v 1.3 88/12/05 19:46:14 daniel grabchars_1_3 $
- **
- ** sys.c - terminal routines for grabchars
- **
- ** Dan Smith (daniel@island.uu.net), November 29, 1988
- **
- ** History:
- **
- ** December 2, 1988
- ** made #ifdefs for DV_ERASE and BSD, wrote notes in handle_erase ()
- ** for changes and improvements
- */
-
- #include <stdio.h>
- #include <sgtty.h>
- #include <signal.h>
- #include "grabchars.h"
-
- struct sgttyb orig, new;
-
- /* all declared in globals.c */
- extern FILE *outfile, *otherout;
- extern FLAG *flags;
- extern int exit_stat;
- extern char default_string[128];
-
- #ifdef DV_ERASE
- extern char *erase_buf;
- #endif
-
- /* initialize global flags */
- init_flags ()
- {
- flags->both = 0;
- flags->check = 0;
- flags->dflt = 0;
- flags->flush = 0;
- flags->ret_key = 0;
- flags->silent = 0;
- flags->erase = 0;
- }
-
- /*
- ** initialize tty
- **
- ** this really needs SYS V code...any patchers out there?
- */
- init_term ()
- {
- /* play havoc with the terminal :-) */
-
- #ifdef BSD
- ioctl (0, TIOCGETP, &orig);
- new = orig;
- new.sg_flags &= ~ECHO;
- new.sg_flags |= CBREAK;
-
- (flags->flush) ? ioctl (0, TIOCSETP, &new) : /* to flush... */
- ioctl (0, TIOCSETN, &new); /* ...or not to flush */
- #endif
- }
-
- /* handle the outside world */
- init_signal ()
- {
- int lets_go ();
-
- signal (SIGINT, lets_go);
- signal (SIGTSTP, lets_go);
- signal (SIGQUIT, 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 ()
- {
- int lets_go ();
- void handle_default ();
-
- /* does not return */
- if (exit_stat == -1 && flags->dflt)
- handle_default ();
-
- exit_stat = -2;
- lets_go ();
- }
-
- /*
- ** the default_flag is set, and the user either typed a return
- ** or timed out. This routine does not return.
- */
- void handle_default ()
- {
- int lets_go ();
-
- if (! flags->silent) {
- fputs (default_string, outfile);
- if (flags->both)
- fputs (default_string, otherout);
- }
- exit_stat = strlen (default_string);
- lets_go ();
- }
-
- /* clean up and get out of here... */
- int lets_go ()
- {
- #ifdef BSD
- ioctl (0, TIOCSETP, &orig);
- #endif
- exit (exit_stat);
- }
-
- #ifdef DV_ERASE
-
- /*
- ** December 2, 1988
- ** Bucko's (daniel) in progress notes for changing this...
- **
- ** first time through processing should be called as its' own
- ** function from the -E case in the main (getopt ()) switch...
- **
- ** stdout and stderr should never be affected by any erasures...
- ** (they probably are not now, I haven't thoroughly tested this...)
- **
- ** I need to drag in my word erase routine; never can tell how
- ** long some people are going to want their lines with -n! :-)
- **
- ** If someone wants a control char (via literal (^V)), we should
- ** give it to them...this would be more compatible with $< (csh)
- ** and read (sh)... grabchars almost completely replaces these
- ** now.
- **
- ** we can also be sensitive to pipe/no pipe via isatty ()...
- */
-
- /* DV: handle erase characters, kill characters, etc. */
- handle_erase (ch, cnt)
- char ch;
- int *cnt;
- {
- static char first = 1;
- static char erasec, killc, werasec, lnextc, rprntc;
- static char lnextflg = 0;
- static char *cp;
- static FILE *tty;
- int i;
-
- if (first) {
- /* initialize static things */
- struct sgttyb sb;
- struct ltchars ltc;
-
- first = 0;
- cp = erase_buf;
- tty = fopen ("/dev/tty", "w");
-
- /* . this isn't going to do... what if -e is set?...dan */
- if (tty == NULL)
- tty = stderr;
- ioctl (0, TIOCGETP, &sb);
- ioctl (0, TIOCGLTC, <c);
- erasec = sb.sg_erase;
- killc = sb.sg_kill;
- werasec = ltc.t_werasc;
- lnextc = ltc.t_lnextc;
- rprntc = ltc.t_rprntc;
- }
-
- if (lnextflg) {
- ch |= 0x80;
- lnextflg = 0;
- }
- (*cnt) --;
- if (ch == erasec) {
- if (*cnt < 0)
- return;
- fprintf (tty, "\b \b");
- (*cnt) --;
- *--cp = 0;
- } else if (ch == killc) {
- while (*cnt >= 0) {
- fprintf (tty, "\b \b");
- (*cnt) --;
- *--cp = 0;
- }
- } else if (ch == werasec) {
- if (*cnt < 0)
- return;
- while ((cp[-1] == ' ' || cp[-1] == '\t') && (*cnt) >= 0) {
- fprintf (tty, "\b");
- (*cnt) --;
- *--cp = 0;
- }
- while (cp[-1] != ' ' && cp[-1] != '\t' && (*cnt) >= 0) {
- fprintf (tty, "\b \b");
- (*cnt) --;
- *--cp = 0;
- }
- } else if (ch == lnextc) {
- lnextflg = 1;
- fprintf (tty, "^\b");
- } else if (ch == rprntc) {
- for (i = strlen (erase_buf); i > 0; i--)
- putc ('\b', tty);
- fprintf (tty, "%s", erase_buf);
- } else {
- ch &= 0x7f;
- fprintf (tty, "%c", ch);
- *cp++ = ch;
- (*cnt) ++;
- }
- fflush (tty);
- return;
- }
- #endif
- SHAR_EOF
- fi
- echo shar: "extracting 'grabchars.h'" '(553 characters)'
- if test -f 'grabchars.h'
- then
- echo shar: "will not over-write existing file 'grabchars.h'"
- else
- cat << \SHAR_EOF > 'grabchars.h'
- /*
- ** $Header: grabchars.h,v 1.3 88/12/05 19:46:20 daniel grabchars_1_3 $
- **
- ** grabchars.h - setup for grabchars
- **
- ** Dan Smith (daniel@island.uu.net), November 29, 1988
- */
-
- struct flag_type {
- int both; /* output to stdout and stderr */
- int check; /* filter input */
- int dflt; /* use following char or string as default */
- int flush; /* if set flush input buffer */
- int ret_key; /* RETURN key exits when set */
- int silent; /* be quiet, just return a status */
- int erase; /* DV: honor erase/kill characters */
- };
-
- typedef struct flag_type FLAG;
- SHAR_EOF
- fi
- exit 0
- # End of shell archive
-
-
-