home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / X / mit / clients / xmodmap / handle.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-17  |  28.1 KB  |  1,291 lines

  1. /*
  2.  * xmodmap - program for loading keymap definitions into server
  3.  *
  4.  * $XConsortium: handle.c,v 1.25 91/07/18 10:26:00 rws Exp $
  5.  *
  6.  * Copyright 1988 Massachusetts Institute of Technology
  7.  *
  8.  * Permission to use, copy, modify, and distribute this software and its
  9.  * documentation for any purpose and without fee is hereby granted, provided
  10.  * that the above copyright notice appear in all copies and that both that
  11.  * copyright notice and this permission notice appear in supporting
  12.  * documentation, and that the name of M.I.T. not be used in advertising or
  13.  * publicity pertaining to distribution of the software without specific,
  14.  * written prior permission.  M.I.T. makes no representations about the
  15.  * suitability of this software for any purpose.  It is provided "as is"
  16.  * without express or implied warranty.
  17.  *
  18.  * Author:  Jim Fulton, MIT X Consortium
  19.  */
  20.  
  21. #include <X11/Xos.h>
  22. #include <X11/Xlib.h>
  23. #include <stdio.h>
  24. #include <ctype.h>
  25. #include "xmodmap.h"
  26. #include "wq.h"
  27.  
  28. static XModifierKeymap *map = NULL;
  29.  
  30.  
  31. /*
  32.  * The routines in this file manipulate a queue of intructions.  Instead of
  33.  * executing each line as it is entered, we build up a list of actions to 
  34.  * take and execute them all at the end.  This allows us to find all errors
  35.  * at once, and to preserve the context in which we are looking up keysyms.
  36.  */
  37.  
  38. struct wq work_queue = {NULL, NULL};
  39.  
  40.  
  41. /*
  42.  * common utility routines
  43.  */
  44.  
  45. KeyCode *KeysymToKeycodes(dpy, keysym, pnum_kcs)
  46.     Display *dpy;
  47.     KeySym keysym;
  48.     int *pnum_kcs;
  49. {
  50.     KeyCode *kcs = NULL;
  51.     int i, j;
  52.  
  53.     *pnum_kcs = 0;
  54.     for (i = min_keycode; i <= max_keycode; i++) {
  55.     for (j = 0; j < 8; j++) {
  56.         if (XKeycodeToKeysym(dpy, (KeyCode) i, j) == keysym) {
  57.         if (!kcs)
  58.             kcs = (KeyCode *)malloc(sizeof(KeyCode));
  59.         else
  60.             kcs = (KeyCode *)realloc((char *)kcs, *pnum_kcs + 1);
  61.         kcs[*pnum_kcs] = i;
  62.         *pnum_kcs += 1;
  63.         break;
  64.         }
  65.     }
  66.     }
  67.     return kcs;
  68. }
  69.  
  70. char *copy_to_scratch (s, len)
  71.     char *s;
  72.     int len;
  73. {
  74.     static char *buf = NULL;
  75.     static int buflen = 0;
  76.  
  77.     if (len > buflen) {
  78.     if (buf) free (buf);
  79.     buflen = (len < 40) ? 80 : (len * 2);
  80.     buf = (char *) malloc (buflen+1);
  81.     }
  82.     if (len > 0)
  83.       strncpy (buf, s, len);
  84.     else 
  85.       len = 0;
  86.  
  87.     buf[len] = '\0';
  88.     return (buf);
  89. }
  90.  
  91. static badheader ()
  92. {
  93.     fprintf (stderr, "%s:  %s:%d:  bad ", ProgramName, inputFilename, lineno);
  94.     parse_errors++;
  95. }
  96.  
  97. #define badmsg(what,arg) { badheader(); fprintf (stderr, what, arg); \
  98.                putc ('\n', stderr); }
  99.  
  100. #define badmsgn(what,s,len) badmsg (what, copy_to_scratch (s, len))
  101.  
  102. void initialize_map ()
  103. {
  104.     map = XGetModifierMapping (dpy);
  105.     return;
  106. }
  107.  
  108. static void do_keycode(), do_keysym(), finish_keycodes();
  109. static void do_add(), do_remove(), do_clear(), do_pointer();
  110. static int get_keysym_list();
  111.  
  112. int skip_word(), skip_space(), skip_chars();
  113.  
  114. static struct dt {
  115.     char *command;            /* name of input command */
  116.     int length;                /* length of command */
  117.     void (*proc)();            /* handler */
  118. } dispatch_table[] = {
  119.     { "keycode", 7, do_keycode },
  120.     { "keysym", 6, do_keysym },
  121.     { "add", 3, do_add },
  122.     { "remove", 6, do_remove },
  123.     { "clear", 5, do_clear },
  124.     { "pointer", 7, do_pointer },
  125.     { NULL, 0, NULL }};
  126.  
  127. /*
  128.  * handle_line - this routine parses the input line (which has had all leading
  129.  * and trailing whitespace removed) and builds up the work queue.
  130.  */
  131.  
  132. void handle_line (line, len)
  133.     char *line;                /* string to parse */
  134.     int len;                /* length of line */
  135. {
  136.     int n;
  137.     struct dt *dtp;
  138.  
  139.     n = skip_chars (line, len);
  140.     if (n < 0) {
  141.     badmsg ("input line '%s'", line);
  142.     return;
  143.     }
  144.  
  145.     for (dtp = dispatch_table; dtp->command != NULL; dtp++) {
  146.     if (n == dtp->length &&
  147.         strncmp (line, dtp->command, dtp->length) == 0) {
  148.  
  149.         n += skip_space (line+n, len-n);
  150.         line += n, len -= n;
  151.  
  152.         (*(dtp->proc)) (line, len);
  153.         return;
  154.     }
  155.     }
  156.  
  157.     fprintf (stderr, "%s:  unknown command on line %s:%d\n",
  158.          ProgramName, inputFilename, lineno);
  159.     parse_errors++;
  160. }
  161.  
  162. /*
  163.  * the following routines are useful for parsing
  164.  */ 
  165.  
  166. int skip_word (s, len)
  167.     register char *s;
  168.     register int len;
  169. {
  170.     register int n;
  171.  
  172.     n = skip_chars (s, len);
  173.     return (n + skip_space (s+n, len-n));
  174. }
  175.  
  176. int skip_chars (s, len)
  177.     register char *s;
  178.     register int len;
  179. {
  180.     register int i;
  181.  
  182.     if (len <= 0 || !s || *s == '\0') return (0);
  183.  
  184.     for (i = 0; i < len; i++) {
  185.     if (isspace(s[i])) break;
  186.     }
  187.     return (i);
  188. }
  189.  
  190. int skip_space (s, len)
  191.     register char *s;
  192.     register int len;
  193. {
  194.     register int i;
  195.  
  196.     if (len <= 0 || !s || *s == '\0') return (0);
  197.  
  198.     for (i = 0; i < len; i++) {
  199.     if (!s[i] || !isspace(s[i])) break;
  200.     }
  201.     return (i);
  202. }
  203.  
  204.  
  205. int skip_until_char (s, len, c)
  206.     register char *s;
  207.     register int len;
  208.     register char c;
  209. {
  210.     register int i;
  211.  
  212.     for (i = 0; i < len; i++) {
  213.     if (s[i] == c) break;
  214.     }
  215.     return (i);
  216. }
  217.  
  218. int skip_until_chars (s, len, cs, cslen)
  219.     char *s;
  220.     int len;
  221.     register char *cs;
  222.     register int cslen;
  223. {
  224.     int i;
  225.  
  226.     for (i = 0; i < len; i++) {
  227.     register int j;
  228.     register char c = s[i];
  229.  
  230.     for (j = 0; j < cslen; j++) {
  231.         if (c == cs[j]) goto done;
  232.     }
  233.     }
  234.   done:
  235.     return (i);
  236. }
  237.  
  238. /*
  239.  * The action routines.
  240.  *
  241.  * This is where the real work gets done.  Each routine is responsible for
  242.  * parsing its input (note that the command keyword has been stripped off)
  243.  * and adding to the work queue.  They are also in charge of outputting
  244.  * error messages and returning non-zero if there is a problem.
  245.  *
  246.  * The following global variables are available:
  247.  *     dpy                the display descriptor
  248.  *     work_queue         linked list of opcodes
  249.  *     inputFilename      name of the file being processed
  250.  *     lineno             line number of current line in input file
  251.  */
  252.  
  253. add_to_work_queue (p)            /* this can become a macro someday */
  254.     union op *p;
  255. {
  256.     if (work_queue.head == NULL) {    /* nothing on the list */
  257.     work_queue.head = work_queue.tail = p;    /* head, tail, no prev */
  258.     } else {
  259.     work_queue.tail->generic.next = p;  /* head okay, prev */
  260.     work_queue.tail = p;        /* tail */
  261.     }
  262.     p->generic.next = NULL;
  263.  
  264.     if (verbose) {
  265.     print_opcode (p);
  266.     }
  267.     return;
  268. }
  269.  
  270. char *copystring (s, len)
  271.     char *s;
  272.     int len;
  273. {
  274.     char *retval;
  275.  
  276.     retval = (char *) malloc (len+1);
  277.     if (retval) {
  278.     strncpy (retval, s, len);
  279.     retval[len] = '\0';
  280.     }
  281.     return (retval);
  282. }
  283.  
  284. static Bool parse_number (str, val)
  285.     char *str;
  286.     unsigned long *val;
  287. {
  288.     char *fmt = "%ld";
  289.  
  290.     if (*str == '0') {
  291.     str++;
  292.     fmt = "%lo";
  293.     if (*str == '\0') {
  294.         *val = 0;
  295.         return 1;
  296.     }
  297.     if (*str == 'x' || *str == 'X') {
  298.         str++;
  299.         fmt = "%lx";
  300.     }
  301.     }
  302.     return (sscanf (str, fmt, val) == 1);
  303. }
  304.  
  305. static Bool parse_keysym (line, n, name, keysym)
  306.     char *line;
  307.     int n;
  308.     char **name;
  309.     KeySym *keysym;
  310. {
  311.     *name = copy_to_scratch (line, n);
  312.     if (!strcmp(*name, "NoSymbol")) {
  313.     *keysym = NoSymbol;
  314.     return (True);
  315.     }
  316.     *keysym = XStringToKeysym (*name);
  317.     if (*keysym == NoSymbol && '0' <= **name && **name <= '9')
  318.     return parse_number(*name, keysym);
  319.     return (*keysym != NoSymbol);
  320. }
  321.  
  322. /*
  323.  * do_keycode - parse off lines of the form
  324.  *
  325.  *                 "keycode" number "=" [keysym ...]
  326.  *                           ^
  327.  *
  328.  * where number is in decimal, hex, or octal.  Any number of keysyms may be
  329.  * listed.
  330.  */
  331.  
  332. static void do_keycode (line, len)
  333.     char *line;
  334.     int len;
  335. {
  336.     int dummy;
  337.     char *fmt = "%d";
  338.     KeyCode keycode;
  339.  
  340.     if (len < 3 || !line || *line == '\0') {  /* 5=a minimum */
  341.     badmsg ("keycode input line", NULL);
  342.     return;
  343.     }
  344.  
  345.     if (*line == '0') line++, len--, fmt = "%o";
  346.     if (*line == 'x' || *line == 'X') line++, len--, fmt = "%x";
  347.  
  348.     dummy = 0;
  349.     if (sscanf (line, fmt, &dummy) != 1 || dummy == 0) {
  350.     badmsg ("keycode value", NULL);
  351.     return;
  352.     }
  353.     keycode = (KeyCode) dummy;
  354.     if ((int)keycode < min_keycode || (int)keycode > max_keycode) {
  355.     badmsg ("keycode value (out of range)", NULL);
  356.     return;
  357.     }
  358.  
  359.     finish_keycodes (line, len, &keycode, 1);
  360. }
  361.  
  362.  
  363. /*
  364.  * do_keysym - parse off lines of the form
  365.  *
  366.  *                 "keysym" keysym "=" [keysym ...]
  367.  *                          ^
  368.  *
  369.  * The left keysyms has to be checked for validity and evaluated.
  370.  */
  371.  
  372. static void do_keysym (line, len)
  373.     char *line;
  374.     int len;
  375. {
  376.     int n;
  377.     KeyCode *keycodes;
  378.     KeySym keysym;
  379.     char *tmpname;
  380.  
  381.     if (len < 3 || !line || *line == '\0') {  /* a=b minimum */
  382.     badmsg ("keysym input line", NULL);
  383.     return;
  384.     }
  385.  
  386.     n = skip_chars (line, len);
  387.     if (n < 1) {
  388.     badmsg ("target keysym name", NULL);
  389.     return;
  390.     }
  391.     if (!parse_keysym(line, n, &tmpname, &keysym)) {
  392.     badmsg ("keysym target key symbol '%s'", tmpname);
  393.     return;
  394.     }
  395.  
  396.     keycodes = KeysymToKeycodes (dpy, keysym, &n);
  397.     if (n == 0) {
  398.     badmsg ("keysym target keysym '%s', no corresponding keycodes",
  399.         tmpname);
  400.     return;
  401.     }
  402.     if (verbose) {
  403.     int i;
  404.     printf ("! Keysym %s (0x%lx) corresponds to keycode(s)",
  405.         tmpname, (long) keysym);
  406.     for (i = 0; i < n; i++)
  407.         printf (" 0x%x", keycodes[i]);
  408.     printf("\n");
  409.     }
  410.  
  411.     finish_keycodes (line, len, keycodes, n);
  412. }
  413.  
  414. static void finish_keycodes (line, len, keycodes, count)
  415.     char *line;
  416.     int len;
  417.     KeyCode *keycodes;
  418.     int count;
  419. {
  420.     int n;
  421.     KeySym *kslist;
  422.     union op *uop;
  423.     struct op_keycode *opk;
  424.    
  425.     n = skip_until_char (line, len, '=');
  426.     line += n, len -= n;
  427.     
  428.     if (len < 1 || *line != '=') {    /* = minimum */
  429.     badmsg ("keycode command (missing keysym list),", NULL);
  430.     return;
  431.     }
  432.     line++, len--;            /* skip past the = */
  433.  
  434.     n = skip_space (line, len);
  435.     line += n, len -= n;
  436.  
  437.     /* allow empty list */
  438.     if (get_keysym_list (line, len, &n, &kslist) < 0)
  439.     return;
  440.  
  441.     while (--count >= 0) {
  442.     uop = AllocStruct (union op);
  443.     if (!uop) {
  444.         badmsg ("attempt to allocate a %ld byte keycode opcode",
  445.             (long) sizeof (struct op_keycode));
  446.         return;
  447.     }
  448.     opk = &uop->keycode;
  449.  
  450.     opk->type = doKeycode;
  451.     opk->target_keycode = keycodes[count];
  452.     opk->count = n;
  453.     opk->keysyms = kslist;
  454.  
  455.     add_to_work_queue (uop);
  456.     }
  457.  
  458. #ifdef later
  459.     /* make sure we handle any special keys */
  460.     check_special_keys (keycode, n, kslist);
  461. #endif
  462. }
  463.  
  464.  
  465. /*
  466.  * parse_modifier - convert a modifier string name to its index
  467.  */
  468.  
  469. struct modtab modifier_table[] = {    /* keep in order so it can be index */
  470.     { "shift", 5, 0 },
  471.     { "lock", 4, 1 },
  472.     { "control", 7, 2 },
  473.     { "mod1", 4, 3 },
  474.     { "mod2", 4, 4 },
  475.     { "mod3", 4, 5 },
  476.     { "mod4", 4, 6 },
  477.     { "mod5", 4, 7 },
  478.     { "ctrl", 4, 2 },
  479.     { NULL, 0, 0 }};
  480.  
  481. static int parse_modifier (line, n)
  482.     register char *line;
  483.     register int n;
  484. {
  485.     register int i;
  486.     struct modtab *mt;
  487.  
  488.     /* lowercase for comparison against table */
  489.     for (i = 0; i < n; i++) {
  490.     if (isupper (line[i])) line[i] = tolower (line[i]);
  491.     }
  492.  
  493.     for (mt = modifier_table; mt->name; mt++) {
  494.     if (n == mt->length && strncmp (line, mt->name, n) == 0)
  495.       return (mt->value);
  496.     }
  497.     return (-1);
  498. }
  499.  
  500.  
  501. /*
  502.  * do_add - parse off lines of the form
  503.  *
  504.  *                 add MODIFIER = keysym ...
  505.  *                     ^
  506.  * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case
  507.  * is not important.  There should also be an alias Ctrl for control.
  508.  */
  509.  
  510. static void do_add (line, len)
  511.     char *line;
  512.     int len;
  513. {
  514.     int n;
  515.     int modifier;
  516.     KeySym *kslist;
  517.     union op *uop;
  518.     struct op_addmodifier *opam;
  519.  
  520.     if (len < 6 || !line || *line == '\0') {  /* Lock=a minimum */
  521.     badmsg ("add modifier input line", NULL);
  522.     return;
  523.     }
  524.  
  525.     n = skip_chars (line, len);
  526.     if (n < 1) {
  527.     badmsg ("add modifier name %s", line);
  528.     return;
  529.     }
  530.  
  531.     modifier = parse_modifier (line, n);
  532.     if (modifier < 0) {
  533.     badmsgn ("add modifier name '%s', not allowed", line, n);
  534.     return;
  535.     }
  536.  
  537.     line += n, len -= n;
  538.     n = skip_until_char (line, len, '=');
  539.     if (n < 0) {
  540.     badmsg ("add modifier = keysym", NULL);
  541.     return;
  542.     }
  543.  
  544.     n++;                /* skip = */
  545.     n += skip_space (line+n, len-n);
  546.     line += n, len -= n;
  547.  
  548.     if (get_keysym_list (line, len, &n, &kslist) < 0)
  549.     return;
  550.     if (n == 0) {
  551.     badmsg ("add modifier keysym list (empty)", NULL);
  552.     return;
  553.     }
  554.  
  555.     uop = AllocStruct (union op);
  556.     if (!uop) {
  557.     badmsg ("attempt to allocate %ld byte addmodifier opcode",
  558.         (long) sizeof (struct op_addmodifier));
  559.     return;
  560.     }
  561.     opam = &uop->addmodifier;
  562.  
  563.     opam->type = doAddModifier;
  564.     opam->modifier = modifier;
  565.     opam->count = n;
  566.     opam->keysyms = kslist;
  567.  
  568.     add_to_work_queue (uop);
  569. }
  570.  
  571. #ifdef AUTO_ADD_REMOVE
  572. /*
  573.  * make_add - stick a single add onto the queue
  574.  */
  575. static void make_add (modifier, keysym)
  576.     int modifier;
  577.     KeySym keysym;
  578. {
  579.     union op *uop;
  580.     struct op_addmodifier *opam;
  581.  
  582.     uop = AllocStruct (union op);
  583.     if (!uop) {
  584.     badmsg ("attempt to allocate %ld byte addmodifier opcode",
  585.         (long) sizeof (struct op_addmodifier));
  586.     return;
  587.     }
  588.     opam = &uop->addmodifier;
  589.  
  590.     opam->type = doAddModifier;
  591.     opam->modifier = modifier;
  592.     opam->count = 1;
  593.     opam->keysyms = (KeySym *) malloc (sizeof (KeySym));
  594.     if (!opam->keysyms) {
  595.     badmsg ("attempt to allocate %ld byte KeySym", (long) sizeof (KeySym));
  596.     free ((char *) opam);
  597.     return;
  598.     }
  599.     opam->keysyms[0] = keysym;
  600.  
  601.     add_to_work_queue (uop);
  602.     return;
  603. }
  604. #endif /* AUTO_ADD_REMOVE */
  605.  
  606.  
  607. /*
  608.  * do_remove - parse off lines of the form
  609.  *
  610.  *                 remove MODIFIER = oldkeysym ...
  611.  *                        ^
  612.  * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case
  613.  * is not important.  There should also be an alias Ctrl for control.
  614.  */
  615.  
  616. static void do_remove (line, len)
  617.     char *line;
  618.     int len;
  619. {
  620.     int n;
  621.     int nc;
  622.     int i;
  623.     int tot;
  624.     int modifier;
  625.     KeySym *kslist;
  626.     KeyCode *kclist;
  627.     union op *uop;
  628.     struct op_removemodifier *oprm;
  629.  
  630.     if (len < 6 || !line || *line == '\0') {  /* Lock=a minimum */
  631.     badmsg ("remove modifier input line", NULL);
  632.     return;
  633.     }
  634.  
  635.     n = skip_chars (line, len);
  636.     if (n < 1) {
  637.     badmsg ("remove modifier name %s", line);
  638.     return;
  639.     }
  640.  
  641.     modifier = parse_modifier (line, n);
  642.     if (modifier < 0) {
  643.     badmsgn ("remove modifier name '%s', not allowed", line, n);
  644.     return;
  645.     }
  646.  
  647.     line += n, len -= n;
  648.     n = skip_until_char (line, len, '=');
  649.     if (n < 0) {
  650.     badmsg ("remove modifier = keysym", NULL);
  651.     return;
  652.     }
  653.  
  654.     n++;
  655.     n += skip_space (line+n, len-n);
  656.     line += n, len -= n;
  657.  
  658.     if (get_keysym_list (line, len, &n, &kslist) < 0)
  659.     return;
  660.     if (n == 0) {
  661.     badmsg ("remove modifier keysym list (empty)", NULL);
  662.     return;
  663.     }
  664.  
  665.     /*
  666.      * unlike the add command, we have to now evaluate the keysyms
  667.      */
  668.  
  669.     kclist = (KeyCode *) malloc (n * sizeof (KeyCode));
  670.     if (!kclist) {
  671.     badmsg ("attempt to allocate %ld byte keycode list",
  672.         (long) (n * sizeof (KeyCode)));
  673.     free ((char *) kslist);
  674.     return;
  675.     }
  676.  
  677.     tot = n;
  678.     nc = 0;
  679.     for (i = 0; i < n; i++) {
  680.         int num_kcs;
  681.     KeyCode *kcs;
  682.     kcs = KeysymToKeycodes (dpy, kslist[i], &num_kcs);
  683.     if (num_kcs == 0) {
  684.         char *tmpname = XKeysymToString (kslist[i]);
  685.         badmsg ("keysym in remove modifier list '%s', no corresponding keycodes",
  686.             tmpname ? tmpname : "?");
  687.         continue;
  688.     }
  689.     if (verbose) {
  690.         int j;
  691.         char *tmpname = XKeysymToString (kslist[i]);
  692.         printf ("! Keysym %s (0x%lx) corresponds to keycode(s)", 
  693.             tmpname ? tmpname : "?", (long) kslist[i]);
  694.         for (j = 0; j < num_kcs; j++)
  695.         printf(" 0x%x", kcs[j]);
  696.         printf("\n");
  697.     }
  698.     if (nc + num_kcs > tot) {
  699.         int j;
  700.         tot = nc + num_kcs;
  701.         kclist = (KeyCode *)realloc((char *)kclist, tot * sizeof(KeyCode));
  702.         if (!kclist) {
  703.         badmsg ("attempt to allocate %ld byte keycode list",
  704.             (long) (tot * sizeof (KeyCode)));
  705.         free ((char *) kslist);
  706.         return;
  707.         }
  708.     }
  709.     while (--num_kcs >= 0)
  710.         kclist[nc++] = *kcs++;        /* okay, add it to list */
  711.     }
  712.  
  713.     free ((char *) kslist);        /* all done with it */
  714.  
  715.     uop = AllocStruct (union op);
  716.     if (!uop) {
  717.     badmsg ("attempt to allocate %ld byte removemodifier opcode",
  718.         (long) sizeof (struct op_removemodifier));
  719.     return;
  720.     }
  721.     oprm = &uop->removemodifier;
  722.  
  723.     oprm->type = doRemoveModifier;
  724.     oprm->modifier = modifier;
  725.     oprm->count = nc;
  726.     oprm->keycodes = kclist;
  727.  
  728.     add_to_work_queue (uop);
  729. }
  730.  
  731. #ifdef AUTO_ADD_REMOVE
  732. /*
  733.  * make_remove - stick a single remove onto the queue
  734.  */
  735. static void make_remove (modifier, keycode)
  736.     int modifier;
  737.     KeyCode keycode;
  738. {
  739.     union op *uop;
  740.     struct op_removemodifier *oprm;
  741.  
  742.     uop = AllocStruct (union op);
  743.     if (!uop) {
  744.     badmsg ("attempt to allocate %ld byte removemodifier opcode",
  745.         (long) sizeof (struct op_removemodifier));
  746.     return;
  747.     }
  748.     oprm = &uop->removemodifier;
  749.  
  750.     oprm->type = doRemoveModifier;
  751.     oprm->modifier = modifier;
  752.     oprm->count = 1;
  753.     oprm->keycodes = (KeyCode *) malloc (sizeof (KeyCode));
  754.     if (!oprm->keycodes) {
  755.     badmsg ("attempt to allocate %ld byte KeyCode",
  756.         (long) sizeof (KeyCode));
  757.     free ((char *) oprm);
  758.     return;
  759.     }
  760.     oprm->keycodes[0] = keycode;
  761.  
  762.     add_to_work_queue (uop);
  763.     return;
  764. }
  765. #endif /* AUTO_ADD_REMOVE */
  766.  
  767.  
  768. /*
  769.  * do_clear - parse off lines of the form
  770.  *
  771.  *                 clear MODIFIER
  772.  *                       ^
  773.  */
  774.  
  775. static void do_clear (line, len)
  776.     char *line;
  777.     int len;
  778. {
  779.     int n;
  780.     int modifier;
  781.     union op *uop;
  782.     struct op_clearmodifier *opcm;
  783.  
  784.     if (len < 4 || !line || *line == '\0') {  /* Lock minimum */
  785.     badmsg ("clear modifier input line", NULL);
  786.     return;
  787.     }
  788.  
  789.     n = skip_chars (line, len);
  790.  
  791.     modifier = parse_modifier (line, n);
  792.     if (modifier < 0) {
  793.     badmsgn ("clear modifier name '%s'", line, n);
  794.     return;
  795.     }
  796.     n += skip_space (line+n, len-n);
  797.     if (n != len) {
  798.     badmsgn ("extra argument '%s' to clear modifier", line+n, len-n);
  799.     /* okay to continue */
  800.     }
  801.  
  802.     uop = AllocStruct (union op);
  803.     if (!uop) {
  804.     badmsg ("attempt to allocate %d byte clearmodifier opcode",
  805.         (long) sizeof (struct op_clearmodifier));
  806.     return;
  807.     }
  808.     opcm = &uop->clearmodifier;
  809.  
  810.     opcm->type = doClearModifier;
  811.     opcm->modifier = modifier;
  812.  
  813.     add_to_work_queue (uop);
  814. }
  815.  
  816. static int strncmp_nocase (a, b, n)
  817.     char *a, *b;
  818.     int n;
  819. {
  820.     int i;
  821.     int a1, b1;
  822.  
  823.     for (i = 0; i < n; i++, a++, b++) {
  824.     if (!*a) return -1;
  825.     if (!*b) return 1;
  826.  
  827.     if (*a != *b) {
  828.         a1 = (isascii(*a) && isupper(*a)) ? tolower(*a) : *a;
  829.         b1 = (isascii(*b) && isupper(*b)) ? tolower(*b) : *b;
  830.         if (a1 != b1) return b1 - a1;
  831.     }
  832.     }
  833.     return 0;
  834. }
  835.  
  836.  
  837. /*
  838.  * do_pointer = get list of numbers of the form
  839.  *
  840.  *                 buttons = NUMBER ...
  841.  *                         ^
  842.  */
  843.  
  844. static void do_pointer (line, len)
  845.     char *line;
  846.     int len;
  847. {
  848.     int n;
  849.     int i;
  850.     unsigned long val;
  851.     union op *uop;
  852.     struct op_pointer *opp;
  853.     unsigned char buttons[MAXBUTTONCODES];
  854.     int nbuttons;
  855.     char *strval;
  856.     Bool ok;
  857.  
  858.     if (len < 2 || !line || *line == '\0') {  /* =1 minimum */
  859.     badmsg ("buttons input line", NULL);
  860.     return;
  861.     }
  862.  
  863.     nbuttons = XGetPointerMapping (dpy, buttons, MAXBUTTONCODES);
  864.  
  865.     n = skip_space (line, len);
  866.     line += n, len -= n;
  867.  
  868.     if (line[0] != '=') {
  869.     badmsg ("buttons pointer code list, missing equal sign", NULL);
  870.     return;
  871.     }
  872.  
  873.     line++, len--;            /* skip = */
  874.     n = skip_space (line, len);
  875.     line += n, len -= n;
  876.  
  877.     i = 0;
  878.     if (len < 7 || strncmp_nocase (line, "default", 7) != 0) {
  879.     while (len > 0) {
  880.         n = skip_space (line, len);
  881.         line += n, len -= n;
  882.         if (line[0] == '\0') break;
  883.         n = skip_word (line, len);
  884.         if (n < 1) {
  885.         badmsg ("skip of word in buttons line:  %s", line);
  886.         return;
  887.         }
  888.         strval = copy_to_scratch(line, n);
  889.         ok = parse_number (strval, &val);
  890.         if (!ok || val >= MAXBUTTONCODES) {
  891.         badmsg ("value %s given for buttons list", strval);
  892.         return;
  893.         }
  894.         buttons[i++] = (unsigned char) val;
  895.         line += n, len -= n;
  896.     }
  897.     }
  898.     
  899.     if (i > 0 && i != nbuttons) {
  900.     badheader ();
  901.     fprintf (stderr, "number of buttons, must have %d instead of %d\n",
  902.          nbuttons, i);
  903.     return;
  904.     }
  905.  
  906.     uop = AllocStruct (union op);
  907.     if (!uop) {
  908.     badmsg ("attempt to allocate a %ld byte pointer opcode",
  909.         (long) sizeof (struct op_pointer));
  910.     return;
  911.     }
  912.     opp = &uop->pointer;
  913.  
  914.     opp->type = doPointer;
  915.     opp->count = i;
  916.     for (i = 0; i < opp->count; i++) {
  917.     opp->button_codes[i] = buttons[i];
  918.     }
  919.  
  920.     add_to_work_queue (uop);
  921. }
  922.  
  923.  
  924. /*
  925.  * get_keysym_list - parses the rest of the line into a keysyms assumes
  926.  * that the = sign has been parsed off but there may be leading whitespace
  927.  *
  928.  *                 keysym ...
  929.  *                 ^
  930.  *
  931.  * this involves getting the word containing the keysym, checking its range,
  932.  * and adding it to the list.
  933.  */
  934.  
  935. static int get_keysym_list (line, len, np, kslistp)
  936.     char *line;
  937.     int len;
  938.     int *np;
  939.     KeySym **kslistp;
  940. {
  941.     int havesofar, maxcanhave;
  942.     KeySym *keysymlist;
  943.  
  944.     *np = 0;
  945.     *kslistp = NULL;
  946.  
  947.     if (len == 0) return (0);        /* empty list */
  948.  
  949.     havesofar = 0;
  950.     maxcanhave = 4;            /* most lists are small */
  951.     keysymlist = (KeySym *) malloc (maxcanhave * sizeof (KeySym));
  952.     if (!keysymlist) {
  953.     badmsg ("attempt to allocate %ld byte initial keysymlist",
  954.         (long) (maxcanhave * sizeof (KeySym)));
  955.     return (-1);
  956.     }
  957.  
  958.     while (len > 0) {
  959.     KeySym keysym;
  960.     int n;
  961.     char *tmpname;
  962.     Bool ok;
  963.  
  964.     n = skip_space (line, len);
  965.     line += n, len -= n;
  966.  
  967.     n = skip_chars (line, len);
  968.     if (n < 0) {
  969.         badmsg ("keysym name list", NULL);
  970.         return (-1);
  971.     }
  972.  
  973.     ok = parse_keysym (line, n, &tmpname, &keysym);
  974.     line += n, len -= n;
  975.     if (!ok) {
  976.         badmsg ("keysym name '%s' in keysym list", tmpname);
  977.         /* do NOT return here, look for others */
  978.         continue;
  979.     }
  980.  
  981.     /*
  982.      * Do NOT test to see if the keysym translates to a keycode or you
  983.      * won't be able to assign new ones....
  984.      */
  985.  
  986.     /* grow the list bigger if necessary */
  987.     if (havesofar >= maxcanhave) {
  988.         maxcanhave *= 2;
  989.         keysymlist = (KeySym *) realloc (keysymlist,
  990.                          maxcanhave * sizeof (KeySym));
  991.         if (!keysymlist) {
  992.         badmsg ("attempt to grow keysym list to %ld bytes",
  993.             (long) (maxcanhave * sizeof (KeySym)));
  994.         return (-1);
  995.         }
  996.     }
  997.  
  998.     /* and add it to the list */
  999.     keysymlist[havesofar++] = keysym;
  1000.     }
  1001.  
  1002.     *kslistp = keysymlist;
  1003.     *np = havesofar;
  1004.     return (0);
  1005. }
  1006.  
  1007.  
  1008. #ifdef later
  1009. /*
  1010.  * check_special_keys - run through list of keysyms and generate "add" or
  1011.  * "remove" commands for for any of the key syms that appear in the modifier
  1012.  * list.  this involves running down the modifier map which is an array of
  1013.  * 8 by map->max_keypermod keycodes.
  1014.  */
  1015.  
  1016. static void check_special_keys (keycode, n, kslist)
  1017.     KeyCode keycode;
  1018.     int n;
  1019.     KeySym *kslist;
  1020. {
  1021.     int i;                /* iterator variable */
  1022.     KeyCode *kcp;            /* keycode pointer */
  1023.  
  1024.     /*
  1025.      * walk the modifiermap array.  since its dimensions are not known at
  1026.      * compile time, we have to walk it by hand instead of indexing.  this
  1027.      * is why it is initialized outside the loop, but incremented inside the
  1028.      * second loop.
  1029.      */
  1030.  
  1031.     kcp = map->modifiermap;        /* start at beginning and iterate */
  1032.     for (i = 0; i < 8; i++) {        /* there are 8 modifier keys */
  1033.     int j;
  1034.  
  1035.     for (j = 0; j < map->max_keypermod; j++, kcp++) {
  1036.         KeySym keysym;
  1037.         int k;
  1038.  
  1039.         if (!*kcp) continue;    /* only non-zero entries significant */
  1040.  
  1041.         /*
  1042.          * check to see if the target keycode is already a modifier; if so,
  1043.          * then we have to remove it
  1044.          */
  1045.         if (keycode == *kcp) {
  1046.         make_remove (i, keycode);
  1047.         }
  1048.  
  1049.         /*
  1050.          * now, check to see if any of the keysyms map to keycodes
  1051.          * that are in the modifier list
  1052.          */
  1053.         for (k = 0; k < n; k++) {
  1054.         KeyCodes kc;
  1055.  
  1056.         kc = XKeysymToKeycode (dpy, kslist[k]);
  1057.         if (kc == *kcp) {    /* yup, found one */
  1058.             /*
  1059.              * have to generate a remove of the CURRENT keycode
  1060.              * and then an add of the new KEYCODE
  1061.              */
  1062.             make_remove (i, kc);  /* modifier, keycode */
  1063.             make_add (i, kslist[k]);  /* modifier, keysym */
  1064.         }
  1065.         }
  1066.     }
  1067.     }
  1068.     return;
  1069. }
  1070. #endif
  1071.  
  1072. /*
  1073.  * print_work_queue - disassemble the work queue and print it on stdout
  1074.  */
  1075.  
  1076. void print_work_queue ()
  1077. {
  1078.     union op *op;
  1079.  
  1080.     printf ("! dump of work queue\n");
  1081.     for (op = work_queue.head; op; op = op->generic.next) {
  1082.     print_opcode (op);
  1083.     }
  1084.     return;
  1085. }
  1086.  
  1087. void print_opcode (op)
  1088.     union op *op;
  1089. {
  1090.     int i;
  1091.  
  1092.     printf ("        ");
  1093.     switch (op->generic.type) {
  1094.       case doKeycode:
  1095.     printf ("keycode 0x%lx =", (long) op->keycode.target_keycode);
  1096.     for (i = 0; i < op->keycode.count; i++) {
  1097.         char *name = XKeysymToString (op->keycode.keysyms[i]);
  1098.  
  1099.         printf (" %s", name ? name : "BADKEYSYM");
  1100.     }
  1101.     printf ("\n");
  1102.     break;
  1103.       case doAddModifier:
  1104.     printf ("add %s =", modifier_table[op->addmodifier.modifier].name);
  1105.     for (i = 0; i < op->addmodifier.count; i++) {
  1106.         char *name = XKeysymToString (op->addmodifier.keysyms[i]);
  1107.         printf (" %s", name ? name : "BADKEYSYM");
  1108.     }
  1109.     printf ("\n");
  1110.     break;
  1111.       case doRemoveModifier:
  1112.     printf ("remove %s = ",
  1113.         modifier_table[op->removemodifier.modifier].name);
  1114.     for (i = 0; i < op->removemodifier.count; i++) {
  1115.         printf (" 0x%lx", (long) op->removemodifier.keycodes[i]);
  1116.     }
  1117.     printf ("\n");
  1118.     break;
  1119.       case doClearModifier:
  1120.     printf ("clear %s\n", modifier_table[op->clearmodifier.modifier].name);
  1121.     break;
  1122.       default:
  1123.     printf ("! unknown opcode %d\n", op->generic.type);
  1124.     break;
  1125.     }                /* end switch */
  1126.     return;
  1127. }
  1128.  
  1129. /*
  1130.  * execute_work_queue - do the real meat and potatoes now that we know what
  1131.  * we need to do and that all of the input is correct.
  1132.  */
  1133.  
  1134. static int exec_keycode(), exec_add(), exec_remove(), exec_clear();
  1135. static int exec_pointer();
  1136.  
  1137. int execute_work_queue ()
  1138. {
  1139.     union op *op;
  1140.     int errors;
  1141.     Bool update_map = False;
  1142.     enum opcode lastop;
  1143.  
  1144.     if (verbose) {
  1145.     printf ("!\n");
  1146.     printf ("! executing work queue\n");
  1147.     printf ("!\n");
  1148.     }
  1149.  
  1150.     errors = 0;
  1151.     lastop = doClearModifier;                /* fake it for op test */
  1152.  
  1153.     for (op = work_queue.head; op; op = op->generic.next) {
  1154.     if (verbose) print_opcode (op);
  1155.  
  1156.     /* check to see if we have to update the keyboard mapping */
  1157.     if (lastop == doKeycode && op->generic.type != doKeycode) {
  1158.         XSync (dpy, 0);
  1159.         while (XEventsQueued (dpy, QueuedAlready) > 0) {
  1160.         XEvent event;
  1161.         XNextEvent (dpy, &event);
  1162.         if (event.type == MappingNotify) {
  1163.             /* read all MappingNotify events */
  1164.             while (XCheckTypedEvent (dpy, MappingNotify, &event)) ;
  1165.             XRefreshKeyboardMapping (&event.xmapping);
  1166.         } else {
  1167.             fprintf (stderr, "%s:  unknown event %ld\n", 
  1168.                      ProgramName, (long) event.type);
  1169.         }
  1170.         }
  1171.     }
  1172.  
  1173.     switch (op->generic.type) {
  1174.       case doKeycode:
  1175.         if (exec_keycode (op) < 0) errors++;
  1176.         break;
  1177.       case doAddModifier:
  1178.         if (exec_add (op) < 0) errors++;
  1179.         else update_map = True;
  1180.         break;
  1181.       case doRemoveModifier:
  1182.         if (exec_remove (op) < 0) errors++;
  1183.         else update_map = True;
  1184.         break;
  1185.       case doClearModifier:
  1186.         if (exec_clear (op) < 0) errors++;
  1187.         else update_map = True;
  1188.         break;
  1189.       case doPointer:
  1190.         if (exec_pointer (op) < 0) errors++;
  1191.         break;
  1192.       default:
  1193.         fprintf (stderr, "%s:  unknown opcode %d\n", 
  1194.              ProgramName, op->generic.type);
  1195.         break;
  1196.     }
  1197.     lastop = op->generic.type;
  1198.  
  1199.     }
  1200.  
  1201.     if (update_map) {
  1202.     if (UpdateModifierMapping (map) < 0) errors++;
  1203.     }
  1204.  
  1205.     return (errors > 0 ? -1 : 0);
  1206. }
  1207.  
  1208. static int exec_keycode (opk)
  1209.     struct op_keycode *opk;
  1210. {
  1211.     if (opk->count == 0) {
  1212.     KeySym dummy = NoSymbol;
  1213.     XChangeKeyboardMapping (dpy, opk->target_keycode, 1,
  1214.                 &dummy, 1);
  1215.     } else {
  1216.     XChangeKeyboardMapping (dpy, opk->target_keycode, opk->count, 
  1217.                 opk->keysyms, 1);
  1218.     }
  1219.     return (0);
  1220. }
  1221.  
  1222. static int exec_add (opam)
  1223.     struct op_addmodifier *opam;
  1224. {
  1225.     int i;
  1226.     int status;
  1227.  
  1228.     status = 0;
  1229.     for (i = 0; i < opam->count; i++) {
  1230.     int num_kcs;
  1231.     KeyCode *kcs;
  1232.  
  1233.     kcs = KeysymToKeycodes (dpy, opam->keysyms[i], &num_kcs);
  1234.     if (num_kcs == 0)
  1235.         status = -1;
  1236.     while (--num_kcs >= 0) {
  1237.         if (AddModifier (&map, *kcs++, opam->modifier) < 0)
  1238.         status = -1;
  1239.     }
  1240.     }
  1241.     return (status);
  1242. }
  1243.  
  1244. static int exec_remove (oprm)
  1245.     struct op_removemodifier *oprm;
  1246. {
  1247.     int i;
  1248.     int status;
  1249.  
  1250.     status = 0;
  1251.     for (i = 0; i < oprm->count; i++) {
  1252.     if (RemoveModifier (&map, oprm->keycodes[i], oprm->modifier) < 0)
  1253.       status = -1;
  1254.     }
  1255.     return (status);
  1256. }
  1257.  
  1258. static int exec_clear (opcm)
  1259.     struct op_clearmodifier *opcm;
  1260. {
  1261.     return (ClearModifier (&map, opcm->modifier));
  1262. }
  1263.  
  1264.  
  1265. static int exec_pointer (opp)
  1266.     struct op_pointer *opp;
  1267. {
  1268.     return (SetPointerMap (opp->button_codes, opp->count));
  1269. }
  1270.  
  1271. void print_modifier_map ()
  1272. {
  1273.     PrintModifierMapping (map, stdout);
  1274.     return;
  1275. }
  1276.  
  1277. void print_key_table (exprs)
  1278.     Bool exprs;
  1279. {
  1280.     PrintKeyTable (exprs, stdout);
  1281.     return;
  1282. }
  1283.  
  1284. void print_pointer_map ()
  1285. {
  1286.     PrintPointerMap (stdout);
  1287.     return;
  1288. }
  1289.  
  1290.  
  1291.