home *** CD-ROM | disk | FTP | other *** search
- /*
- * Ann Arbor XL series programmable strings uploader.
- */
-
- #ifndef lint
- static char *RCSid = "$Header: aaxl.c,v 1.1 86/05/09 19:48:10 rayan Exp $";
- #endif
-
- /*
- * Copyright (c) 1986 by Rayan S. Zachariassen.
- *
- * Permission is granted to anyone to use this software for any
- * purpose on any computer system, and to redistribute it, subject
- * to the following four restrictions:
- *
- * 1. The author is not responsible for the consequences of use of
- * this software, no matter how awful, even if they arise
- * from imperfections in it.
- *
- * 2. The origin of this software must not be misrepresented, either
- * by explicit claim or by omission.
- *
- * 3. Altered versions must be plainly marked as such, and must not
- * be misrepresented as being the original software, but must be
- * marked as being an alteration of this software.
- *
- * 4. Commercial or any for-profit redistribution of this software is
- * prohibited unless by prior written agreement from the author.
- */
-
-
- /*
- * Several files may be given as arguments. With no files specified,
- * $HOME/.aaxlrc is read. A filename of - reads from stdin.
- * The options are:
- *
- * -a append, i.e. don't clear to default programmable strings.
- * -d debug, prints out memory usage stats instead of uploading.
- * -v verbose, print out body of upload string to stdout.
- * -l lock, to lock programmable strings until power-up or SETUP-Z
- *
- * To initialize the programmable strings on your XL, use
- *
- * aaxl /dev/null
- *
- * Format of files:
- *
- * Each input file contains lines specifying a key to upload a programmable
- * string for. Lines starting with #, and empty lines, are ignored.
- * The format of each line is
- *
- * name <whitespace> options [ <whitespace> <tab> text ]
- *
- * where <whitespace> is blanks, tabs, and commas. The text is optional,
- * if not there the key will revert to default programmed text (usually none).
- * This can be used to change other behavior of a key.
- *
- * The options field consists of keywords from the following lists:
- *
- * ctrl, shift, ctrl-shift, meta, 0-31, 64 - shift level
- * host, display, graphics - transmit direction
- * never, always - repeat attribute
- * level - this is a shift key
- * normal - everything normal
- *
- * The defaults for all but the 'normal' keyword are whatever the previous
- * option value was for the previous key. The normal (and initial default)
- * values correspond to all-zero parameters. See the XL manual for detailed
- * explanation of the options.
- *
- * The text field follows one or more blanks or tabs after the option field
- * and consists of everything from there till a newline. The string may contain
- * all the usual C string escapes (\###, \n\t\s\a\b\f\r\e), and in addition
- * may contain one sequence like \`...` where ... is a command to be executed
- * by the shell. The result (stdout) of the command is interpolated in place
- * of the \` escape.
- *
- * This program reports errors, including XL memory overflow that could
- * occur. All errors are fatal. The program doesn't send an upload string
- * to the terminal unless no errors were detected at all. Memory allocation
- * for the upload string is done dynamically in the program, so excessively
- * large string sequences are handled. Space-optimal output is produced.
- *
- * Rayan Zachariassen
- * rayan@utai.uucp
- *
- * Toronto, March 27 1986
- *
- */
-
- #include <stdio.h>
- #include <ctype.h>
-
- #define USAGE "aaxl [-a] [-d] [-l] [-v] [file ...]"
-
- #define RCFILE ".aaxlrc" /* file to read from in $HOME */
- #define MEMSIZ 896 /* memory size in bytes of an XL */
- #define MAXPOWERMEM 255 /* non-volatile power-on memory size */
-
- #define CLICK 1024 /* memory increased in chunks of this */
- #define PREAMBLE "\033P>"
- #define POSTAMBLE "\033\\"
- #define MAXONEUSE (2*MEMSIZ+3+2+1+1+1+4+2+(sizeof POSTAMBLE)-1)
-
- int lineno, memusage;
- char *program, *file, *out;
-
- extern char *index(), *rindex(), *itoa(), *malloc(), *realloc();
- extern FILE *popen();
-
- extern int errno;
- extern char *sys_errlist[];
-
- extern int getopt();
- extern char *optarg;
- extern int optind;
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int ch, errflg, keysappend, debug, lock, verbose;
- unsigned int xlsize;
- char *xlmem, buf[BUFSIZ];
- FILE *fp;
-
- program = argv[0];
- if (out = rindex(program, '/'))
- program = out + 1;
-
- errflg = keysappend = debug = lock = verbose = 0;
- while ((ch = getopt(argc, argv, "adlv")) != EOF) {
- switch ((char) ch) {
- case 'a':
- keysappend = 1;
- break;
- case 'd':
- debug = 1;
- break;
- case 'l':
- lock = 1;
- break;
- case 'v':
- verbose = 1;
- break;
- default:
- errflg++;
- break;
- }
- }
- if (errflg) {
- fprintf(stderr, "Usage: %s\n", USAGE);
- exit(errflg);
- }
- if (optind == argc) {
- char *cp;
- extern char *getenv();
-
- cp = getenv("HOME");
- if (*cp && chdir(cp) < 0) {
- fprintf(stderr, "%s: chdir(%s): %s\n", program,
- cp, sys_errlist[errno]);
- exit(1);
- } else if (cp == 0) {
- fprintf(stderr,
- "%s: no $HOME environment variable\n", program);
- exit(1);
- }
- argv[--optind] = RCFILE;
- }
- xlsize = CLICK + MAXONEUSE;
- if ((xlmem = malloc(xlsize)) == 0) {
- fprintf(stderr, "%s: malloc failure\n", program);
- exit(1);
- }
- out = xlmem;
- append(PREAMBLE);
- /*
- * keysappend lock global parameter
- *
- * 0 0 0 or 1
- * 0 1 3
- * 1 0 empty
- * 1 1 2
- */
- if (!keysappend)
- append(itoa(3*lock));
- else if (lock)
- append("2");
- for (; optind < argc; optind++) {
- file = argv[optind];
- if (strcmp(file, "-") == 0 && (fp = fdopen(0, "r")) == NULL) {
- perror("stdin");
- exit(1);
- } else if ((fp = fopen(file, "r")) == NULL) {
- perror(file);
- exit(1);
- }
- lineno = 0;
- memusage = 0;
- while (fgets(buf, sizeof buf, fp)) {
- lineno++;
- buf[strlen(buf)-1] = '\0';
- if (buf[0] == '#' || buf[0] == '\0')
- continue;
- doline(buf);
- if (out - xlmem >= xlsize - MAXONEUSE) {
- char *cp;
-
- if ((cp = realloc(xlmem, xlsize + CLICK)) == 0)
- error("realloc failure\n", (char *)0);
- out = cp + (xlmem - out);
- xlmem = cp;
- xlsize += CLICK;
- }
- }
- (void) fclose(fp);
- *out = 0;
- }
- if (verbose)
- printf("%s\n", xlmem + sizeof PREAMBLE - 1);
- if (memusage > MEMSIZ) {
- fprintf(stderr,
- "%s: Ann Arbor XL memory not large enough!\n", program);
- debug = 1;
- errflg = 1;
- }
- if (!debug) {
- append(POSTAMBLE);
- if (write(1, xlmem, strlen(xlmem)) < 0) {
- fprintf(stderr, "%s: write failure\n", program);
- exit(1);
- }
- } else
- printf("Memory usage: %d bytes, or %d%%\n",
- memusage, (100*memusage)/MEMSIZ);
- exit(errflg);
- }
-
- #define KEY 0
- #define LEVEL 1
- #define DIR 2
- #define REPEAT 3
- #define LFLAG 4
- #define NUMATTR 5
-
- doline(buf)
- char *buf;
- {
- int i, nomore, mustbreak;
- register char *cp;
- char *start;
- short new[NUMATTR];
- static short old[NUMATTR] = { -2, 0, 0, 0, 0 };
-
- for (i = 0; i < NUMATTR; i++)
- new[i] = old[i];
- cp = buf;
- while (*cp && isascii(*cp) && !isspace(*cp))
- cp++;
- nomore = *cp == 0;
- *cp++ = 0;
- if ((new[KEY] = find(buf)) < 0)
- error("unknown key name '%s'\n", buf);
- append("|");
- if (nomore) {
- if (new[KEY] != old[KEY] + 1)
- append(itoa(new[KEY]));
- old[KEY] = new[KEY];
- append("|");
- return;
- }
- while (*cp && isascii(*cp) && (isspace(*cp) || *cp == ','))
- cp++;
- if (!*cp) {
- if (new[KEY] != old[KEY] + 1)
- append(itoa(new[KEY]));
- old[KEY] = new[KEY];
- append("|");
- return;
- }
- /*
- * Option parsing
- *
- * The options field consists of space-seperated keywords
- * from the following lists:
- *
- * ctrl, shift, ctrl-shift, meta, 0-31, 64 - shift level
- * host, display, graphics - transmit direction
- * never, always - repeat attribute
- * level - this is a shift key
- * normal - everything normal
- */
-
- mustbreak = 0;
- while (!mustbreak && *cp && isascii(*cp) && !isspace(*cp)) {
- start = cp; /* assert isascii(*start) */
- while (*cp && isascii(*cp) && !isspace(*cp) && *cp != ',')
- cp++;
- if (*cp == '\t')
- mustbreak = 1;
- *cp++ = 0; /* assert cp > start */
- switch (*start) {
- case 'a':
- new[REPEAT] = 2;
- break;
- case 'c':
- if (index(start, 's'))
- new[LEVEL] = 3;
- else
- new[LEVEL] = 2;
- break;
- case 'd':
- new[DIR] = 2;
- break;
- case 'g':
- new[DIR] = 3;
- break;
- case 'h':
- new[DIR] = 1;
- break;
- case 'l':
- new[LFLAG] = 1;
- break;
- case 'm':
- new[LEVEL] = 64;
- break;
- case 'n':
- if (*(start+1) == 'e')
- new[REPEAT] = 1;
- else
- new[LEVEL] = new[DIR] = new[REPEAT] = new[LFLAG] = 0;
- break;
- case 's':
- new[LEVEL] = 1;
- break;
- default:
- if (isdigit(*start)) {
- new[LEVEL] = atoi(start);
- if (new[LEVEL] > 31 && new[LEVEL] != 64)
- error("illegal shift level %s\n",
- itoa(new[LEVEL]));
- } else
- error("unknown option '%s'\n", start);
- }
- /* do NOT skip tabs, but DO skip commas */
- while (*cp && (*cp == ' ' || *cp == '\n' || *cp == ','))
- cp++;
- }
- for (i = 0; i < NUMATTR; i++) {
- if (i == KEY)
- continue;
- if (new[i] != old[i])
- break;
- }
- if (i == NUMATTR) { /* old attributes are identical to new */
- if (new[KEY] != old[KEY] + 1)
- append(itoa(new[KEY]));
- } else {
- if (new[KEY] == 200)
- error("%s string must have normal attributes\n", buf);
- for (i = 0; i < NUMATTR; i++) {
- if (new[i] != old[i])
- append(itoa(new[i]));
- append(";");
- old[i] = new[i];
- }
- trim(';');
- memusage++;
- }
- append("|");
- while (*cp && isascii(*cp) && isspace(*cp))
- cp++;
- if (!*cp)
- return;
- if (new[KEY] == 200) {
- int i = memusage;
- encode(cp);
- if (memusage - i > MAXPOWERMEM)
- error("%s string exceeds available memory\n", buf);
- memusage = i;
- } else if (new[KEY] == 120 && new[LEVEL] == 3)
- error("ctrl-shift-reset is not programmable\n", (char *)0);
- else
- encode(cp);
- return;
- }
-
-
- append(s)
- char *s;
- {
- while (*s)
- *out++ = *s++;
- }
-
- trim(c)
- char c;
- {
- while (*--out == c)
- ;
- out++;
- }
-
- encode(in)
- char *in;
- {
- int i;
-
- while (*in != 0) {
- if (*in == '\\') {
- switch (*++in) {
- case 'n':
- *in = '\n';
- break;
- case 'r':
- *in = '\r';
- break;
- case 'e':
- *in = '\033';
- break;
- case 'b':
- *in = '\b';
- break;
- case 'f':
- *in = '\f';
- break;
- case 'a':
- *in = '\007';
- break;
- case 't':
- *in = '\t';
- break;
- case 's':
- *in = ' ';
- break;
- case '`':
- /*
- * I know I'm gonna regret this...
- * \`echo hi`
- * is replaced by the result (stdout) of
- * executing that command...
- */
- if (rindex(in, '`') && rindex(in, '`') != in) {
- char *cmd, buf[MEMSIZ+1];
- FILE *pfp;
-
- cmd = in + 1;
- in = rindex(in, '`');
- *in++ = 0;
- if ((pfp = popen(cmd, "r")) != NULL) {
- int n;
-
- n = fread(buf, 1, MEMSIZ, pfp);
- if (n > 0) {
- buf[n] = 0;
- encode(buf);
- }
- (void) pclose(pfp);
- }
- continue;
- } else
- *in = '`';
- break;
- default:
- if (!(*in >= '0' && *in <= '7')) {
- in--;
- break;
- } else
- i = *in++ - '0';
- if (*in >= '0' && *in <= '7') {
- i = 8*i + (*in++ - '0');
- if (*in >= '0' && *in <= '7')
- i = 8*i + (*in++ - '0');
- }
- *--in = (char) i;
- }
- }
- if (*in < ' ') {
- *out++ = '~';
- *out++ = (char)((*in & 037)+0100);
- } else {
- switch (*in) {
- case '\177':
- *out++ = '~';
- *out++ = '?';
- break;
- case '~':
- case '|':
- *out++ = '~';
- *out++ = *in;
- break;
- default:
- *out++ = *in;
- }
- }
- memusage++;
- in++;
- }
- }
-
- /*
- * The following table gives correspondence between key name and number.
- * Names are seperated by space, and each string represents 20 keys in
- * increasing number order.
- */
-
- char *lookup[] =
- { "s1 s2 space s3 s4 s5 s6",
- "pause shift-l z x c v b n m , . / shift-r scroll zoom",
- "_ ctrl a s d f g h j k l ; ' return back-space",
- "tab q w e r t y u i o p [ ] line-feed del",
- "esc 1 2 3 4 5 6 7 8 9 0 - = ` \\ break",
- "0P _ 00P .P enter ,P 1P 2P 3P _ tabP 4P 5P 6P +P _ 7P 8P 9P -P",
- "reset setup f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 f16 f17 f18",
- "send erase edit delete insert print _ _ _ _ Header Trailer ENQ DA",
- "_",
- "_",
- "Power-on",
- 0
- };
-
- /*
- * This little baby relies on contiguous allocation of the strings in
- * the lookup[] array. If your compiler doesn't grok that, it's simplicity
- * itself to wrap a loop around the body of this thing.
- */
-
- int
- find(keyname)
- char *keyname;
- {
- register char *cp, *s;
- int key;
-
- key = 0;
- for (cp = lookup[0]; *cp; cp++) {
- s = keyname;
- while (*s && *cp == *s)
- cp++, s++;
- if (*s == 0 && (*cp == ' ' || *cp == 0))
- return key;
- while (*cp && *cp != ' ')
- cp++;
- if (!*cp)
- key = 20*((key/20)+1);
- else
- key++;
- }
- return -1;
- }
-
- char *
- itoa(num)
- int num;
- {
- static char digs[10];
- char *cp;
-
- cp = digs + 9;
- *cp-- = '\0';
- if (num == 0) {
- *cp = '0';
- return cp;
- }
- while (num > 0) {
- *cp-- = (num % 10) + '0';
- num /= 10;
- }
- return cp+1;
- }
-
- error(format, string)
- char *format, *string;
- {
- fprintf(stderr, "%s: file %s line %d: ", program, file, lineno);
- fprintf(stderr, format, string);
- exit(1);
- }
-