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