sources-request@mirror.UUCP (10/24/86)
Submitted by: Rayan Zachariassen <seismo!utai!rayan> Mod.sources: Volume 7, Issue 35 Archive-name: aaaxlkeys [ If you are fortunate enough to have both an XL and USG Unix, note that this program uses index/rindex, not strchr/strrchr. -r$ ] #!/bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If all goes well, you will see the message "No problems found." # Exit status; set to 1 on "wc" errors or if would overwrite. STATUS=0 # Contents: README Makefile aaxl.c aaxl.l echo x - README if test -f README ; then echo README exists, putting output in $$README OUT=$$README STATUS=1 else OUT=README fi sed 's/^X//' > $OUT <<'@//E*O*F README//' X Ann Arbor XL series programmable strings uploader XI wrote this one evening partly for relaxation, partly as a tangible example Xof my coding style. I was having a friendly discussion with one of our local Xpurists (hi Geoff!) about how code should look like, which tradeoffs to make, Xetc. Some of his comments on the first draft of this were adopted in the code, Xbecause they seemed quite sensible within my philosophy as well. I am not a Xgreat fan of remapping keyboards (though it is useful at times) and don't use Xthis program regularly, though another fellow around here does. I'm sending it Xout primarily as a sample of good (I think) coding, in case anyone cares for Xa non-trivial example. Besides, some people might find it useful. No claims Xare made about the portability of this stuff. It works on 4.n on Vaxen. XThere is one large compiler dependency, clearly marked. I'll take elegant over Xproper most any day... Xrayan XRayan Zachariassen <rayan@utai.uucp> XAI group, University of Toronto @//E*O*F README// chmod u=rw,g=rw,o=rw $OUT echo x - Makefile if test -f Makefile ; then echo Makefile exists, putting output in $$Makefile OUT=$$Makefile STATUS=1 else OUT=Makefile fi sed 's/^X//' > $OUT <<'@//E*O*F Makefile//' X## Quick Makefile for Ann Arbor XL program XCFLAGS=-O Xaaxl: aaxl.c X $(CC) $(CFLAGS) aaxl.c -o aaxl Xinstall: X @echo cp aaxl and aaxl.l to appropriate places X cp aaxl /usr/bin X strip /usr/bin/aaxl X cp aaxl.l /usr/man/man1/aaxl.1 @//E*O*F Makefile// chmod u=rw,g=rw,o=rw $OUT echo x - aaxl.c if test -f aaxl.c ; then echo aaxl.c exists, putting output in $$aaxl.c OUT=$$aaxl.c STATUS=1 else OUT=aaxl.c fi sed 's/^X//' > $OUT <<'@//E*O*F aaxl.c//' X/* X * Ann Arbor XL series programmable strings uploader. X */ X#ifndef lint Xstatic char *RCSid = "$Header: aaxl.c,v 1.1 86/05/09 19:48:10 rayan Exp $"; X#endif X/* X * Copyright (c) 1986 by Rayan S. Zachariassen. X * X * Permission is granted to anyone to use this software for any X * purpose on any computer system, and to redistribute it, subject X * to the following four restrictions: X * X * 1. The author is not responsible for the consequences of use of X * this software, no matter how awful, even if they arise X * from imperfections in it. X * X * 2. The origin of this software must not be misrepresented, either X * by explicit claim or by omission. X * X * 3. Altered versions must be plainly marked as such, and must not X * be misrepresented as being the original software, but must be X * marked as being an alteration of this software. X * X * 4. Commercial or any for-profit redistribution of this software is X * prohibited unless by prior written agreement from the author. X */ X/* X * Several files may be given as arguments. With no files specified, X * $HOME/.aaxlrc is read. A filename of - reads from stdin. X * The options are: X * X * -a append, i.e. don't clear to default programmable strings. X * -d debug, prints out memory usage stats instead of uploading. X * -v verbose, print out body of upload string to stdout. X * -l lock, to lock programmable strings until power-up or SETUP-Z X * X * To initialize the programmable strings on your XL, use X * X * aaxl /dev/null X * X * Format of files: X * X * Each input file contains lines specifying a key to upload a programmable X * string for. Lines starting with #, and empty lines, are ignored. X * The format of each line is X * X * name <whitespace> options [ <whitespace> <tab> text ] X * X * where <whitespace> is blanks, tabs, and commas. The text is optional, X * if not there the key will revert to default programmed text (usually none). X * This can be used to change other behavior of a key. X * X * The options field consists of keywords from the following lists: X * X * ctrl, shift, ctrl-shift, meta, 0-31, 64 - shift level X * host, display, graphics - transmit direction X * never, always - repeat attribute X * level - this is a shift key X * normal - everything normal X * X * The defaults for all but the 'normal' keyword are whatever the previous X * option value was for the previous key. The normal (and initial default) X * values correspond to all-zero parameters. See the XL manual for detailed X * explanation of the options. X * X * The text field follows one or more blanks or tabs after the option field X * and consists of everything from there till a newline. The string may contain X * all the usual C string escapes (\###, \n\t\s\a\b\f\r\e), and in addition X * may contain one sequence like \`...` where ... is a command to be executed X * by the shell. The result (stdout) of the command is interpolated in place X * of the \` escape. X * X * This program reports errors, including XL memory overflow that could X * occur. All errors are fatal. The program doesn't send an upload string X * to the terminal unless no errors were detected at all. Memory allocation X * for the upload string is done dynamically in the program, so excessively X * large string sequences are handled. Space-optimal output is produced. X * X * Rayan Zachariassen X * rayan@utai.uucp X * X * Toronto, March 27 1986 X * X */ X#include <stdio.h> X#include <ctype.h> X#define USAGE "aaxl [-a] [-d] [-l] [-v] [file ...]" X#define RCFILE ".aaxlrc" /* file to read from in $HOME */ X#define MEMSIZ 896 /* memory size in bytes of an XL */ X#define MAXPOWERMEM 255 /* non-volatile power-on memory size */ X#define CLICK 1024 /* memory increased in chunks of this */ X#define PREAMBLE "\033P>" X#define POSTAMBLE "\033\\" X#define MAXONEUSE (2*MEMSIZ+3+2+1+1+1+4+2+(sizeof POSTAMBLE)-1) Xint lineno, memusage; Xchar *program, *file, *out; Xextern char *index(), *rindex(), *itoa(), *malloc(), *realloc(); Xextern FILE *popen(); Xextern int errno; Xextern char *sys_errlist[]; Xextern int getopt(); Xextern char *optarg; Xextern int optind; Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X int ch, errflg, keysappend, debug, lock, verbose; X unsigned int xlsize; X char *xlmem, buf[BUFSIZ]; X FILE *fp; X program = argv[0]; X if (out = rindex(program, '/')) X program = out + 1; X errflg = keysappend = debug = lock = verbose = 0; X while ((ch = getopt(argc, argv, "adlv")) != EOF) { X switch ((char) ch) { X case 'a': X keysappend = 1; X break; X case 'd': X debug = 1; X break; X case 'l': X lock = 1; X break; X case 'v': X verbose = 1; X break; X default: X errflg++; X break; X } X } X if (errflg) { X fprintf(stderr, "Usage: %s\n", USAGE); X exit(errflg); X } X if (optind == argc) { X char *cp; X extern char *getenv(); X cp = getenv("HOME"); X if (*cp && chdir(cp) < 0) { X fprintf(stderr, "%s: chdir(%s): %s\n", program, X cp, sys_errlist[errno]); X exit(1); X } else if (cp == 0) { X fprintf(stderr, X "%s: no $HOME environment variable\n", program); X exit(1); X } X argv[--optind] = RCFILE; X } X xlsize = CLICK + MAXONEUSE; X if ((xlmem = malloc(xlsize)) == 0) { X fprintf(stderr, "%s: malloc failure\n", program); X exit(1); X } X out = xlmem; X append(PREAMBLE); X /* X * keysappend lock global parameter X * X * 0 0 0 or 1 X * 0 1 3 X * 1 0 empty X * 1 1 2 X */ X if (!keysappend) X append(itoa(3*lock)); X else if (lock) X append("2"); X for (; optind < argc; optind++) { X file = argv[optind]; X if (strcmp(file, "-") == 0 && (fp = fdopen(0, "r")) == NULL) { X perror("stdin"); X exit(1); X } else if ((fp = fopen(file, "r")) == NULL) { X perror(file); X exit(1); X } X lineno = 0; X memusage = 0; X while (fgets(buf, sizeof buf, fp)) { X lineno++; X buf[strlen(buf)-1] = '\0'; X if (buf[0] == '#' || buf[0] == '\0') X continue; X doline(buf); X if (out - xlmem >= xlsize - MAXONEUSE) { X char *cp; X if ((cp = realloc(xlmem, xlsize + CLICK)) == 0) X error("realloc failure\n", (char *)0); X out = cp + (xlmem - out); X xlmem = cp; X xlsize += CLICK; X } X } X (void) fclose(fp); X *out = 0; X } X if (verbose) X printf("%s\n", xlmem + sizeof PREAMBLE - 1); X if (memusage > MEMSIZ) { X fprintf(stderr, X "%s: Ann Arbor XL memory not large enough!\n", program); X debug = 1; X errflg = 1; X } X if (!debug) { X append(POSTAMBLE); X if (write(1, xlmem, strlen(xlmem)) < 0) { X fprintf(stderr, "%s: write failure\n", program); X exit(1); X } X } else X printf("Memory usage: %d bytes, or %d%%\n", X memusage, (100*memusage)/MEMSIZ); X exit(errflg); X} X#define KEY 0 X#define LEVEL 1 X#define DIR 2 X#define REPEAT 3 X#define LFLAG 4 X#define NUMATTR 5 Xdoline(buf) Xchar *buf; X{ X int i, nomore, mustbreak; X register char *cp; X char *start; X short new[NUMATTR]; X static short old[NUMATTR] = { -2, 0, 0, 0, 0 }; X for (i = 0; i < NUMATTR; i++) X new[i] = old[i]; X cp = buf; X while (*cp && isascii(*cp) && !isspace(*cp)) X cp++; X nomore = *cp == 0; X *cp++ = 0; X if ((new[KEY] = find(buf)) < 0) X error("unknown key name '%s'\n", buf); X append("|"); X if (nomore) { X if (new[KEY] != old[KEY] + 1) X append(itoa(new[KEY])); X old[KEY] = new[KEY]; X append("|"); X return; X } X while (*cp && isascii(*cp) && (isspace(*cp) || *cp == ',')) X cp++; X if (!*cp) { X if (new[KEY] != old[KEY] + 1) X append(itoa(new[KEY])); X old[KEY] = new[KEY]; X append("|"); X return; X } X /* X * Option parsing X * X * The options field consists of space-seperated keywords X * from the following lists: X * X * ctrl, shift, ctrl-shift, meta, 0-31, 64 - shift level X * host, display, graphics - transmit direction X * never, always - repeat attribute X * level - this is a shift key X * normal - everything normal X */ X mustbreak = 0; X while (!mustbreak && *cp && isascii(*cp) && !isspace(*cp)) { X start = cp; /* assert isascii(*start) */ X while (*cp && isascii(*cp) && !isspace(*cp) && *cp != ',') X cp++; X if (*cp == '\t') X mustbreak = 1; X *cp++ = 0; /* assert cp > start */ X switch (*start) { X case 'a': X new[REPEAT] = 2; X break; X case 'c': X if (index(start, 's')) X new[LEVEL] = 3; X else X new[LEVEL] = 2; X break; X case 'd': X new[DIR] = 2; X break; X case 'g': X new[DIR] = 3; X break; X case 'h': X new[DIR] = 1; X break; X case 'l': X new[LFLAG] = 1; X break; X case 'm': X new[LEVEL] = 64; X break; X case 'n': X if (*(start+1) == 'e') X new[REPEAT] = 1; X else X new[LEVEL] = new[DIR] = new[REPEAT] = new[LFLAG] = 0; X break; X case 's': X new[LEVEL] = 1; X break; X default: X if (isdigit(*start)) { X new[LEVEL] = atoi(start); X if (new[LEVEL] > 31 && new[LEVEL] != 64) X error("illegal shift level %s\n", X itoa(new[LEVEL])); X } else X error("unknown option '%s'\n", start); X } X /* do NOT skip tabs, but DO skip commas */ X while (*cp && (*cp == ' ' || *cp == '\n' || *cp == ',')) X cp++; X } X for (i = 0; i < NUMATTR; i++) { X if (i == KEY) X continue; X if (new[i] != old[i]) X break; X } X if (i == NUMATTR) { /* old attributes are identical to new */ X if (new[KEY] != old[KEY] + 1) X append(itoa(new[KEY])); X } else { X if (new[KEY] == 200) X error("%s string must have normal attributes\n", buf); X for (i = 0; i < NUMATTR; i++) { X if (new[i] != old[i]) X append(itoa(new[i])); X append(";"); X old[i] = new[i]; X } X trim(';'); X memusage++; X } X append("|"); X while (*cp && isascii(*cp) && isspace(*cp)) X cp++; X if (!*cp) X return; X if (new[KEY] == 200) { X int i = memusage; X encode(cp); X if (memusage - i > MAXPOWERMEM) X error("%s string exceeds available memory\n", buf); X memusage = i; X } else if (new[KEY] == 120 && new[LEVEL] == 3) X error("ctrl-shift-reset is not programmable\n", (char *)0); X else X encode(cp); X return; X} Xappend(s) Xchar *s; X{ X while (*s) X *out++ = *s++; X} Xtrim(c) Xchar c; X{ X while (*--out == c) X ; X out++; X} Xencode(in) Xchar *in; X{ X int i; X while (*in != 0) { X if (*in == '\\') { X switch (*++in) { X case 'n': X *in = '\n'; X break; X case 'r': X *in = '\r'; X break; X case 'e': X *in = '\033'; X break; X case 'b': X *in = '\b'; X break; X case 'f': X *in = '\f'; X break; X case 'a': X *in = '\007'; X break; X case 't': X *in = '\t'; X break; X case 's': X *in = ' '; X break; X case '`': X /* X * I know I'm gonna regret this... X * \`echo hi` X * is replaced by the result (stdout) of X * executing that command... X */ X if (rindex(in, '`') && rindex(in, '`') != in) { X char *cmd, buf[MEMSIZ+1]; X FILE *pfp; X cmd = in + 1; X in = rindex(in, '`'); X *in++ = 0; X if ((pfp = popen(cmd, "r")) != NULL) { X int n; X n = fread(buf, 1, MEMSIZ, pfp); X if (n > 0) { X buf[n] = 0; X encode(buf); X } X (void) pclose(pfp); X } X continue; X } else X *in = '`'; X break; X default: X if (!(*in >= '0' && *in <= '7')) { X in--; X break; X } else X i = *in++ - '0'; X if (*in >= '0' && *in <= '7') { X i = 8*i + (*in++ - '0'); X if (*in >= '0' && *in <= '7') X i = 8*i + (*in++ - '0'); X } X *--in = (char) i; X } X } X if (*in < ' ') { X *out++ = '~'; X *out++ = (char)((*in & 037)+0100); X } else { X switch (*in) { X case '\177': X *out++ = '~'; X *out++ = '?'; X break; X case '~': X case '|': X *out++ = '~'; X *out++ = *in; X break; X default: X *out++ = *in; X } X } X memusage++; X in++; X } X} X/* X * The following table gives correspondence between key name and number. X * Names are seperated by space, and each string represents 20 keys in X * increasing number order. X */ Xchar *lookup[] = X{ "s1 s2 space s3 s4 s5 s6", X "pause shift-l z x c v b n m , . / shift-r scroll zoom", X "_ ctrl a s d f g h j k l ; ' return back-space", X "tab q w e r t y u i o p [ ] line-feed del", X "esc 1 2 3 4 5 6 7 8 9 0 - = ` \\ break", X "0P _ 00P .P enter ,P 1P 2P 3P _ tabP 4P 5P 6P +P _ 7P 8P 9P -P", X "reset setup f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 f16 f17 f18", X "send erase edit delete insert print _ _ _ _ Header Trailer ENQ DA", X "_", X "_", X "Power-on", X 0 X}; X/* X * This little baby relies on contiguous allocation of the strings in X * the lookup[] array. If your compiler doesn't grok that, it's simplicity X * itself to wrap a loop around the body of this thing. X */ Xint Xfind(keyname) Xchar *keyname; X{ X register char *cp, *s; X int key; X key = 0; X for (cp = lookup[0]; *cp; cp++) { X s = keyname; X while (*s && *cp == *s) X cp++, s++; X if (*s == 0 && (*cp == ' ' || *cp == 0)) X return key; X while (*cp && *cp != ' ') X cp++; X if (!*cp) X key = 20*((key/20)+1); X else X key++; X } X return -1; X} Xchar * Xitoa(num) Xint num; X{ X static char digs[10]; X char *cp; X cp = digs + 9; X *cp-- = '\0'; X if (num == 0) { X *cp = '0'; X return cp; X } X while (num > 0) { X *cp-- = (num % 10) + '0'; X num /= 10; X } X return cp+1; X} Xerror(format, string) Xchar *format, *string; X{ X fprintf(stderr, "%s: file %s line %d: ", program, file, lineno); X fprintf(stderr, format, string); X exit(1); X} @//E*O*F aaxl.c// chmod u=rw,g=rw,o=rw $OUT echo x - aaxl.l if test -f aaxl.l ; then echo aaxl.l exists, putting output in $$aaxl.l OUT=$$aaxl.l STATUS=1 else OUT=aaxl.l fi sed 's/^X//' > $OUT <<'@//E*O*F aaxl.l//' X.TH AAXL l "University of Toronto" X.SH NAME Xaaxl - program Ann Arbor XL keyboard X.SH SYNOPSIS X.B aaxl X[ X.B \-a X] [ X.B \-d X] [ X.B \-l X] [ X.B \-v X] [ X.I file X\&... ] X.SH DESCRIPTION X.PP XAaxl reads a description of keynames and corresponding attributes and a text Xto program the key with. Several files may be given as arguments. With no files Xspecified, $HOME/.aaxlrc is read. A filename of \fB-\fR reads from stdin. X.PP XThe \fB-a\fR flag is used to append the downloaded strings to the current Xstate. If not given, programmable strings are cleared before defining any Xnew string. X.PP XThe \fB-d\fR flag is a debug flag, causing Ann Arbor XL memory usage statistics Xto be printed instead of loading the programmable strings. An absolute Xbytecount, and the percentage of programmable strings memory that would be Xused, is printed. X.PP XThe \fB-l\fR flag asks for the key programming lock to be set, after the Xprogrammable strings have been loaded. X.PP XThe \fB-v\fR flag asks the program to be verbose and print the body of the Xcontrol string it would send to the Ann Arbor, to stdout. The entire control Xstring is still written out, unless \fB-d\fR is used. X.PP XThe program collects definitions of programmable keys from the files it reads. XThe definitions are wrapped in preamble and postamble control sequences, and Xsend to stdout. Other simultaneous output to the terminal will probably corrupt Xthe programming command, though it is written using a single (hopefully atomic) Xoutput operation. X.PP XAs a side benefit, to initialize the programmable strings on the XL, use X.sp X.nf X aaxl /dev/null X.fi X.PP XEach input contains lines specifying a key to upload a programmable Xstring for. Lines starting with #, and empty lines, are ignored. XThe format of each line is X.sp X.nf X name <whitespace> options [ <whitespace> <tab> text ] X.fi X.sp Xwhere <whitespace> is blanks, tabs, and commas. The text is optional, Xif not there the key will revert to default programmed text (usually none). XThis can be used to change other behavior of a key. X.PP XThe options field consists of keywords from the following lists: X.sp X.nf Xctrl, shift, ctrl-shift, meta, 0-31, 64 - shift level Xhost, display, graphics - transmit direction Xnever, always - repeat attribute Xlevel - this is a shift key Xnormal - everything normal X.fi X.PP XThe defaults for all but the 'normal' keyword are whatever the previous Xoption value was for the previous key. The normal (and initial default) Xvalues correspond to all-zero parameters. See the XL manual for detailed Xexplanation of the options. X.PP XThe text field follows one or more blanks or tabs after the option field Xand consists of everything from there till a newline. The string may contain Xall the usual C string escapes (\\###, \\n\\t\\s\\a\\b\\f\\r\\e), and in Xaddition may contain one sequence like \\`...` where ... is a command to be Xexecuted by the shell. The result (stdout) of the command is interpolated in Xplace of the \\` escape. X.PP XKey names used in the first field correspond to the legend on the caps. Keys Xin the keypad group have a capital P appended to the cap name. The following Xlist shows the complete list of recognized names. Note that key names which Xstart with a capital letter do not correspond to a keyboard key, but rahter Xto other capabilities of the XL. For example, DA programs the answerback Xstring (Device Attributes), Power-on the power-on string, etc. X.sp Xs1 s2 space s3 s4 s5 s6 Xpause shift-l z x c v b n m , . / shift-r scroll zoom Xctrl a s d f g h j k l ; ' return back-space Xtab q w e r t y u i o p [ ] line-feed del Xesc 1 2 3 4 5 6 7 8 9 0 - = ` \\ break X0P 00P .P enter ,P 1P 2P 3P tabP 4P 5P 6P +P 7P 8P 9P -P Xreset setup f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 f16 f17 f18 Xsend erase edit delete insert print Header Trailer ENQ DA XPower-on X.PP XThis program reports errors, including XL memory overflow that could Xoccur. All errors are fatal. The program doesn't send an upload string Xto the terminal unless no errors were detected at all. Memory allocation Xfor the upload string is done dynamically in the program, so excessively Xlarge string sequences are handled. Space-optimal output is produced. X.SH ENVIRONMENT X.PP XWhen no file arguments are given to the program, the value of the X.I HOME Xenvironment variable is assumed to be a directory containing a .aaxlrc file, Xwhich will be read if it exists. X.SH SEE ALSO X"XL Series User Guide" by Ann Arbor Terminals, Inc. X.SH AUTHOR XRayan Zachariassen, University of Toronto CSRI/AI. X.SH BUGS XIf a command is embedded in the key text, the last backquote in the text Xmust correspond to the end of the command escape. X.PP XProbably doesn't handle NUL's in strings terribly gracefully. Who does. @//E*O*F aaxl.l// chmod u=rw,g=rw,o=rw $OUT echo Inspecting for damage in transit... temp=/tmp/sharin$$; dtemp=/tmp/sharout$$ trap "rm -f $temp $dtemp; exit" 0 1 2 3 15 cat > $temp <<\!!! 20 170 1002 README 11 33 229 Makefile 579 2171 12997 aaxl.c 121 851 4723 aaxl.l 731 3225 18951 total !!! wc README Makefile aaxl.c aaxl.l | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp if test -s $dtemp ; then echo "Ouch [diff of wc output]:" cat $dtemp STATUS=1 elif test $STATUS = 0 ; then echo "No problems found." else echo "WARNING -- PROBLEMS WERE FOUND..." fi exit $STATUS