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