home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume7 / aaakeys / aaxl.c next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  12.7 KB  |  580 lines

  1. /*
  2.  * Ann Arbor XL series programmable strings uploader.
  3.  */
  4.  
  5. #ifndef lint
  6. static char *RCSid = "$Header: aaxl.c,v 1.1 86/05/09 19:48:10 rayan Exp $";
  7. #endif
  8.  
  9. /*
  10.  * Copyright (c) 1986 by Rayan S. Zachariassen.
  11.  *
  12.  * Permission is granted to anyone to use this software for any
  13.  * purpose on any computer system, and to redistribute it, subject
  14.  * to the following four restrictions:
  15.  *
  16.  * 1. The author is not responsible for the consequences of use of
  17.  *    this software, no matter how awful, even if they arise
  18.  *    from imperfections in it.
  19.  *
  20.  * 2. The origin of this software must not be misrepresented, either
  21.  *    by explicit claim or by omission.
  22.  *
  23.  * 3. Altered versions must be plainly marked as such, and must not
  24.  *    be misrepresented as being the original software, but must be
  25.  *    marked as being an alteration of this software.
  26.  *
  27.  * 4. Commercial or any for-profit redistribution of this software is
  28.  *    prohibited unless by prior written agreement from the author.
  29.  */
  30.  
  31.  
  32. /*
  33.  * Several files may be given as arguments. With no files specified,
  34.  * $HOME/.aaxlrc is read. A filename of - reads from stdin.
  35.  * The options are:
  36.  *
  37.  *    -a    append, i.e. don't clear to default programmable strings.
  38.  *    -d    debug, prints out memory usage stats instead of uploading.
  39.  *    -v    verbose, print out body of upload string to stdout.
  40.  *    -l    lock, to lock programmable strings until power-up or SETUP-Z
  41.  *
  42.  * To initialize the programmable strings on your XL, use
  43.  *
  44.  *    aaxl /dev/null
  45.  *
  46.  * Format of files:
  47.  *
  48.  * Each input file contains lines specifying a key to upload a programmable
  49.  * string for. Lines starting with #, and empty lines, are ignored.
  50.  * The format of each line is
  51.  *
  52.  *     name <whitespace> options [ <whitespace> <tab> text ]
  53.  *
  54.  * where <whitespace> is blanks, tabs, and commas. The text is optional,
  55.  * if not there the key will revert to default programmed text (usually none).
  56.  * This can be used to change other behavior of a key.
  57.  *
  58.  * The options field consists of keywords from the following lists:
  59.  *
  60.  *    ctrl, shift, ctrl-shift, meta, 0-31, 64    - shift level
  61.  *    host, display, graphics            - transmit direction
  62.  *    never, always                - repeat attribute
  63.  *    level                    - this is a shift key
  64.  *    normal                    - everything normal
  65.  *
  66.  * The defaults for all but the 'normal' keyword are whatever the previous
  67.  * option value was for the previous key. The normal (and initial default)
  68.  * values correspond to all-zero parameters. See the XL manual for detailed
  69.  * explanation of the options.
  70.  *
  71.  * The text field follows one or more blanks or tabs after the option field
  72.  * and consists of everything from there till a newline. The string may contain
  73.  * all the usual C string escapes (\###, \n\t\s\a\b\f\r\e), and in addition
  74.  * may contain one sequence like \`...` where ... is a command to be executed
  75.  * by the shell. The result (stdout) of the command is interpolated in place
  76.  * of the \` escape.
  77.  *
  78.  * This program reports errors, including XL memory overflow that could
  79.  * occur. All errors are fatal. The program doesn't send an upload string
  80.  * to the terminal unless no errors were detected at all. Memory allocation
  81.  * for the upload string is done dynamically in the program, so excessively
  82.  * large string sequences are handled. Space-optimal output is produced.
  83.  *
  84.  * Rayan Zachariassen
  85.  * rayan@utai.uucp
  86.  *
  87.  * Toronto, March 27 1986
  88.  *
  89.  */
  90.  
  91. #include <stdio.h>
  92. #include <ctype.h>
  93.  
  94. #define USAGE    "aaxl [-a] [-d] [-l] [-v] [file ...]"
  95.  
  96. #define    RCFILE        ".aaxlrc"    /* file to read from in $HOME */
  97. #define    MEMSIZ        896        /* memory size in bytes of an XL */
  98. #define    MAXPOWERMEM    255        /* non-volatile power-on memory size */
  99.  
  100. #define    CLICK        1024        /* memory increased in chunks of this */
  101. #define    PREAMBLE    "\033P>"
  102. #define    POSTAMBLE    "\033\\"
  103. #define    MAXONEUSE    (2*MEMSIZ+3+2+1+1+1+4+2+(sizeof POSTAMBLE)-1)
  104.  
  105. int    lineno, memusage;
  106. char    *program, *file, *out;
  107.  
  108. extern char    *index(), *rindex(), *itoa(), *malloc(), *realloc();
  109. extern FILE    *popen();
  110.  
  111. extern int    errno;
  112. extern char    *sys_errlist[];
  113.  
  114. extern int    getopt();
  115. extern char    *optarg;
  116. extern int    optind;
  117.  
  118. main(argc, argv)
  119. int    argc;
  120. char    *argv[];
  121. {
  122.     int        ch, errflg, keysappend, debug, lock, verbose;
  123.     unsigned int    xlsize;
  124.     char        *xlmem, buf[BUFSIZ];
  125.     FILE        *fp;
  126.  
  127.     program = argv[0];
  128.     if (out = rindex(program, '/'))
  129.         program = out + 1;
  130.  
  131.     errflg = keysappend = debug = lock = verbose = 0;
  132.     while ((ch = getopt(argc, argv, "adlv")) != EOF) {
  133.         switch ((char) ch) {
  134.         case 'a':
  135.             keysappend = 1;
  136.             break;
  137.         case 'd':
  138.             debug = 1;
  139.             break;
  140.         case 'l':
  141.             lock = 1;
  142.             break;
  143.         case 'v':
  144.             verbose = 1;
  145.             break;
  146.         default:
  147.             errflg++;
  148.             break;
  149.         }
  150.     }
  151.     if (errflg) {
  152.         fprintf(stderr, "Usage: %s\n", USAGE);
  153.         exit(errflg);
  154.     }
  155.     if (optind == argc) {
  156.         char        *cp;
  157.         extern char    *getenv();
  158.  
  159.         cp = getenv("HOME");
  160.         if (*cp && chdir(cp) < 0) {
  161.             fprintf(stderr, "%s: chdir(%s): %s\n", program,
  162.                     cp, sys_errlist[errno]);
  163.             exit(1);
  164.         } else if (cp == 0) {
  165.             fprintf(stderr,
  166.                 "%s: no $HOME environment variable\n", program);
  167.             exit(1);
  168.         }
  169.         argv[--optind] = RCFILE;
  170.     }
  171.     xlsize = CLICK + MAXONEUSE;
  172.     if ((xlmem = malloc(xlsize)) == 0) {
  173.         fprintf(stderr, "%s: malloc failure\n", program);
  174.         exit(1);
  175.     }
  176.     out = xlmem;
  177.     append(PREAMBLE);
  178.     /*
  179.      *    keysappend    lock        global parameter
  180.      *
  181.      *        0    0        0 or 1
  182.      *        0    1        3
  183.      *        1    0        empty
  184.      *        1    1        2
  185.      */
  186.     if (!keysappend)
  187.         append(itoa(3*lock));
  188.     else if (lock)
  189.         append("2");
  190.     for (; optind < argc; optind++) {
  191.         file = argv[optind];
  192.         if (strcmp(file, "-") == 0 && (fp = fdopen(0, "r")) == NULL) {
  193.             perror("stdin");
  194.             exit(1);
  195.         } else if ((fp = fopen(file, "r")) == NULL) {
  196.             perror(file);
  197.             exit(1);
  198.         }
  199.         lineno = 0;
  200.         memusage = 0;
  201.         while (fgets(buf, sizeof buf, fp)) {
  202.             lineno++;
  203.             buf[strlen(buf)-1] = '\0';
  204.             if (buf[0] == '#' || buf[0] == '\0')
  205.                 continue;
  206.             doline(buf);
  207.             if (out - xlmem >= xlsize - MAXONEUSE) {
  208.                 char    *cp;
  209.  
  210.                 if ((cp = realloc(xlmem, xlsize + CLICK)) == 0)
  211.                     error("realloc failure\n", (char *)0);
  212.                 out = cp + (xlmem - out);
  213.                 xlmem = cp;
  214.                 xlsize += CLICK;
  215.             }
  216.         }
  217.         (void) fclose(fp);
  218.         *out = 0;
  219.     }
  220.     if (verbose)
  221.         printf("%s\n", xlmem + sizeof PREAMBLE - 1);
  222.     if (memusage > MEMSIZ) {
  223.         fprintf(stderr,
  224.             "%s: Ann Arbor XL memory not large enough!\n", program);
  225.         debug = 1;
  226.         errflg = 1;
  227.     }
  228.     if (!debug) {
  229.         append(POSTAMBLE);
  230.         if (write(1, xlmem, strlen(xlmem)) < 0) {
  231.             fprintf(stderr, "%s: write failure\n", program);
  232.             exit(1);
  233.         }
  234.     } else 
  235.         printf("Memory usage: %d bytes, or %d%%\n",
  236.             memusage, (100*memusage)/MEMSIZ);
  237.     exit(errflg);
  238. }
  239.  
  240. #define    KEY    0
  241. #define LEVEL    1
  242. #define    DIR    2
  243. #define    REPEAT    3
  244. #define    LFLAG    4
  245. #define    NUMATTR    5
  246.  
  247. doline(buf)
  248. char    *buf;
  249. {
  250.     int        i, nomore, mustbreak;
  251.     register char    *cp;
  252.     char        *start;
  253.     short        new[NUMATTR];
  254.     static short    old[NUMATTR] = { -2, 0, 0, 0, 0 };
  255.  
  256.     for (i = 0; i < NUMATTR; i++)
  257.         new[i] = old[i];
  258.     cp = buf;
  259.     while (*cp && isascii(*cp) && !isspace(*cp))
  260.         cp++;
  261.     nomore = *cp == 0;
  262.     *cp++ = 0;
  263.     if ((new[KEY] = find(buf)) < 0)
  264.         error("unknown key name '%s'\n", buf);
  265.     append("|");
  266.     if (nomore) {
  267.         if (new[KEY] != old[KEY] + 1)
  268.             append(itoa(new[KEY]));
  269.         old[KEY] = new[KEY];
  270.         append("|");
  271.         return;
  272.     }
  273.     while (*cp && isascii(*cp) && (isspace(*cp) || *cp == ','))
  274.         cp++;
  275.     if (!*cp) {
  276.         if (new[KEY] != old[KEY] + 1)
  277.             append(itoa(new[KEY]));
  278.         old[KEY] = new[KEY];
  279.         append("|");
  280.         return;
  281.     }
  282.     /*
  283.      * Option parsing
  284.      *
  285.      * The options field consists of space-seperated keywords
  286.      * from the following lists:
  287.      *
  288.      *    ctrl, shift, ctrl-shift, meta, 0-31, 64    - shift level
  289.      *    host, display, graphics            - transmit direction
  290.      *    never, always                - repeat attribute
  291.      *    level                    - this is a shift key
  292.      *    normal                    - everything normal
  293.      */
  294.  
  295.     mustbreak = 0;
  296.     while (!mustbreak && *cp && isascii(*cp) && !isspace(*cp)) {
  297.         start = cp;        /* assert isascii(*start) */
  298.         while (*cp && isascii(*cp) && !isspace(*cp) && *cp != ',')
  299.             cp++;
  300.         if (*cp == '\t')
  301.             mustbreak = 1;
  302.         *cp++ = 0;        /* assert cp > start */
  303.         switch (*start) {
  304.         case 'a':
  305.             new[REPEAT] = 2;
  306.             break;
  307.         case 'c':
  308.             if (index(start, 's'))
  309.                 new[LEVEL] = 3;
  310.             else
  311.                 new[LEVEL] = 2;
  312.             break;
  313.         case 'd':
  314.             new[DIR] = 2;
  315.             break;
  316.         case 'g':
  317.             new[DIR] = 3;
  318.             break;
  319.         case 'h':
  320.             new[DIR] = 1;
  321.             break;
  322.         case 'l':
  323.             new[LFLAG] = 1;
  324.             break;
  325.         case 'm':
  326.             new[LEVEL] = 64;
  327.             break;
  328.         case 'n':
  329.             if (*(start+1) == 'e')
  330.                 new[REPEAT] = 1;
  331.             else
  332.                 new[LEVEL] = new[DIR] = new[REPEAT] = new[LFLAG] = 0;
  333.             break;
  334.         case 's':
  335.             new[LEVEL] = 1;
  336.             break;
  337.         default:
  338.             if (isdigit(*start)) {
  339.                 new[LEVEL] = atoi(start);
  340.                 if (new[LEVEL] > 31 && new[LEVEL] != 64)
  341.                     error("illegal shift level %s\n",
  342.                                itoa(new[LEVEL]));
  343.             } else
  344.                 error("unknown option '%s'\n", start);
  345.         }
  346.         /* do NOT skip tabs, but DO skip commas */
  347.         while (*cp && (*cp == ' ' || *cp == '\n' || *cp == ','))
  348.             cp++;
  349.     }
  350.     for (i = 0; i < NUMATTR; i++) {
  351.         if (i == KEY)
  352.             continue;
  353.         if (new[i] != old[i])
  354.             break;
  355.     }
  356.     if (i == NUMATTR) {    /* old attributes are identical to new */
  357.         if (new[KEY] != old[KEY] + 1)
  358.             append(itoa(new[KEY]));
  359.     } else {
  360.         if (new[KEY] == 200)
  361.             error("%s string must have normal attributes\n", buf);
  362.         for (i = 0; i < NUMATTR; i++) {
  363.             if (new[i] != old[i])
  364.                 append(itoa(new[i]));
  365.             append(";");
  366.             old[i] = new[i];
  367.         }
  368.         trim(';');
  369.         memusage++;
  370.     }
  371.     append("|");
  372.     while (*cp && isascii(*cp) && isspace(*cp))
  373.         cp++;
  374.     if (!*cp)
  375.         return;
  376.     if (new[KEY] == 200) {
  377.         int    i = memusage;
  378.         encode(cp);
  379.         if (memusage - i > MAXPOWERMEM)
  380.             error("%s string exceeds available memory\n", buf);
  381.         memusage = i;
  382.     } else if (new[KEY] == 120 && new[LEVEL] == 3)
  383.         error("ctrl-shift-reset is not programmable\n", (char *)0);
  384.     else
  385.         encode(cp);
  386.     return;
  387. }
  388.  
  389.  
  390. append(s)
  391. char    *s;
  392. {
  393.     while (*s)
  394.         *out++ = *s++;
  395. }
  396.  
  397. trim(c)
  398. char    c;
  399. {
  400.     while (*--out == c)
  401.         ;
  402.     out++;
  403. }
  404.  
  405. encode(in)
  406. char    *in;
  407. {
  408.     int    i;
  409.  
  410.     while (*in != 0) {
  411.         if (*in == '\\') {
  412.             switch (*++in) {
  413.             case 'n':
  414.                 *in = '\n';
  415.                 break;
  416.             case 'r':
  417.                 *in = '\r';
  418.                 break;
  419.             case 'e':
  420.                 *in = '\033';
  421.                 break;
  422.             case 'b':
  423.                 *in = '\b';
  424.                 break;
  425.             case 'f':
  426.                 *in = '\f';
  427.                 break;
  428.             case 'a':
  429.                 *in = '\007';
  430.                 break;
  431.             case 't':
  432.                 *in = '\t';
  433.                 break;
  434.             case 's':
  435.                 *in = ' ';
  436.                 break;
  437.             case '`':
  438.                 /*
  439.                  * I know I'm gonna regret this...
  440.                  * \`echo hi`
  441.                  * is replaced by the result (stdout) of
  442.                  * executing that command...
  443.                  */
  444.                 if (rindex(in, '`') && rindex(in, '`') != in) {
  445.                     char    *cmd, buf[MEMSIZ+1];
  446.                     FILE    *pfp;
  447.  
  448.                     cmd = in + 1;
  449.                     in = rindex(in, '`');
  450.                     *in++ = 0;
  451.                     if ((pfp = popen(cmd, "r")) != NULL) {
  452.                         int    n;
  453.  
  454.                         n = fread(buf, 1, MEMSIZ, pfp);
  455.                         if (n > 0) {
  456.                             buf[n] = 0;
  457.                             encode(buf);
  458.                         }
  459.                         (void) pclose(pfp);
  460.                     }
  461.                     continue;
  462.                 } else
  463.                     *in = '`';
  464.                 break;
  465.             default:
  466.                 if (!(*in >= '0' && *in <= '7')) {
  467.                     in--;
  468.                     break;
  469.                 } else
  470.                     i = *in++ - '0';
  471.                 if (*in >= '0' && *in <= '7') {
  472.                     i = 8*i + (*in++ - '0');
  473.                     if (*in >= '0' && *in <= '7')
  474.                         i = 8*i + (*in++ - '0');
  475.                 }
  476.                 *--in = (char) i;
  477.             }
  478.         }
  479.         if (*in < ' ') {
  480.             *out++ = '~';
  481.             *out++ = (char)((*in & 037)+0100);
  482.         } else {
  483.             switch (*in) {
  484.             case '\177':
  485.                 *out++ = '~';
  486.                 *out++ = '?';
  487.                 break;
  488.             case '~':
  489.             case '|':
  490.                 *out++ = '~';
  491.                 *out++ = *in;
  492.                 break;
  493.             default:
  494.                 *out++ = *in;
  495.             }
  496.         }
  497.         memusage++;
  498.         in++;
  499.     }
  500. }
  501.  
  502. /*
  503.  * The following table gives correspondence between key name and number.
  504.  * Names are seperated by space, and each string represents 20 keys in
  505.  * increasing number order.
  506.  */
  507.  
  508. char    *lookup[] =
  509. { "s1 s2 space s3 s4 s5 s6",
  510.   "pause shift-l z x c v b n m , . / shift-r scroll zoom",
  511.   "_ ctrl a s d f g h j k l ; ' return back-space",
  512.   "tab q w e r t y u i o p [ ] line-feed del",
  513.   "esc 1 2 3 4 5 6 7 8 9 0 - = ` \\ break",
  514.   "0P _ 00P .P enter ,P 1P 2P 3P _ tabP 4P 5P 6P +P _ 7P 8P 9P -P",
  515.   "reset setup f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 f16 f17 f18",
  516.   "send erase edit delete insert print _ _ _ _ Header Trailer ENQ DA",
  517.   "_",
  518.   "_",
  519.   "Power-on",
  520.   0
  521. };
  522.  
  523. /*
  524.  * This little baby relies on contiguous allocation of the strings in
  525.  * the lookup[] array. If your compiler doesn't grok that, it's simplicity
  526.  * itself to wrap a loop around the body of this thing.
  527.  */
  528.  
  529. int
  530. find(keyname)
  531. char    *keyname;
  532. {
  533.     register char    *cp, *s;
  534.     int    key;
  535.  
  536.     key = 0;
  537.     for (cp = lookup[0]; *cp; cp++) {
  538.         s = keyname;
  539.         while (*s && *cp == *s)
  540.             cp++, s++;
  541.         if (*s == 0 && (*cp == ' ' || *cp == 0))
  542.             return key;
  543.         while (*cp && *cp != ' ')
  544.             cp++;
  545.         if (!*cp)
  546.             key = 20*((key/20)+1);
  547.         else
  548.             key++;
  549.     }
  550.     return -1;
  551. }
  552.  
  553. char *
  554. itoa(num)
  555. int    num;
  556. {
  557.     static char    digs[10];
  558.     char        *cp;
  559.  
  560.     cp = digs + 9;
  561.     *cp-- = '\0';
  562.     if (num == 0) {
  563.         *cp = '0';
  564.         return cp;
  565.     }
  566.     while (num > 0) {
  567.         *cp-- = (num % 10) + '0';
  568.         num /= 10;
  569.     }
  570.     return cp+1;
  571. }
  572.  
  573. error(format, string)
  574. char    *format, *string;
  575. {
  576.     fprintf(stderr, "%s: file %s line %d: ", program, file, lineno);
  577.     fprintf(stderr, format, string);
  578.     exit(1);
  579. }
  580.