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