handle.c revision 1a30de1f
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	fmt = "%lo";
278	if (*str == '\0') {
279	    *val = 0;
280	    return 1;
281	}
282	if (*str == 'x' || *str == 'X') {
283	    str++;
284	    fmt = "%lx";
285	}
286    }
287    return (sscanf (str, fmt, val) == 1);
288}
289
290static Bool
291parse_keysym(const char *line, int n, char **name, KeySym *keysym)
292{
293    *name = copy_to_scratch (line, n);
294    if (!strcmp(*name, "NoSymbol")) {
295	*keysym = NoSymbol;
296	return (True);
297    }
298    *keysym = XStringToKeysym (*name);
299    if (*keysym == NoSymbol && '0' <= **name && **name <= '9')
300	return parse_number(*name, keysym);
301    return (*keysym != NoSymbol);
302}
303
304/*
305 * do_keycode - parse off lines of the form
306 *
307 *                 "keycode" number "=" [keysym ...]
308 *                           ^
309 *
310 * where number is in decimal, hex, or octal.  Any number of keysyms may be
311 * listed.
312 */
313
314static void
315do_keycode(char *line, int len)
316{
317    int dummy;
318    char *fmt = "%d";
319    KeyCode keycode;
320
321    if (len < 3 || !line || *line == '\0') {  /* 5=a minimum */
322	badmsg0 ("keycode input line");
323	return;
324    }
325
326    /*
327     * We need not bother to advance line/len past the
328     * number (or the string 'any') as finish_keycodes() will
329     * first advance past the '='.
330     */
331    if (!strncmp("any", line, 3)) {
332	keycode = 0;
333    } else {
334	if (*line == '0') line++, len--, fmt = "%o";
335	if (*line == 'x' || *line == 'X') line++, len--, fmt = "%x";
336
337	dummy = 0;
338	if (sscanf (line, fmt, &dummy) != 1 || dummy == 0) {
339	    badmsg0 ("keycode value");
340	    return;
341	}
342	keycode = (KeyCode) dummy;
343	if ((int)keycode < min_keycode || (int)keycode > max_keycode) {
344	    badmsg0 ("keycode value (out of range)");
345	    return;
346	}
347    }
348
349    finish_keycodes (line, len, &keycode, 1);
350}
351
352
353/*
354 * do_keysym - parse off lines of the form
355 *
356 *                 "keysym" keysym "=" [keysym ...]
357 *                          ^
358 *
359 * The left keysyms has to be checked for validity and evaluated.
360 */
361
362static void
363do_keysym(char *line, int len)
364{
365    int n;
366    KeyCode *keycodes;
367    KeySym keysym;
368    char *tmpname;
369
370    if (len < 3 || !line || *line == '\0') {  /* a=b minimum */
371	badmsg0 ("keysym input line");
372	return;
373    }
374
375    n = skip_chars (line, len);
376    if (n < 1) {
377	badmsg0 ("target keysym name");
378	return;
379    }
380    if (!parse_keysym(line, n, &tmpname, &keysym)) {
381	badmsg ("keysym target key symbol '%s'", tmpname);
382	return;
383    }
384
385    keycodes = KeysymToKeycodes (dpy, keysym, &n);
386    if (n == 0) {
387	badmsg ("keysym target keysym '%s', no corresponding keycodes",
388		tmpname);
389	return;
390    }
391    if (verbose) {
392	int i;
393	printf ("! Keysym %s (0x%lx) corresponds to keycode(s)",
394		tmpname, (long) keysym);
395	for (i = 0; i < n; i++)
396	    printf (" 0x%x", keycodes[i]);
397	printf("\n");
398    }
399
400    finish_keycodes (line, len, keycodes, n);
401}
402
403static void
404finish_keycodes(const char *line, int len, KeyCode *keycodes, int count)
405{
406    int n;
407    KeySym *kslist;
408    union op *uop;
409    struct op_keycode *opk;
410
411    n = skip_until_char (line, len, '=');
412    line += n, len -= n;
413
414    if (len < 1 || *line != '=') {	/* = minimum */
415	badmsg0 ("keycode command (missing keysym list),");
416	return;
417    }
418    line++, len--;			/* skip past the = */
419
420    n = skip_space (line, len);
421    line += n, len -= n;
422
423    /* allow empty list */
424    if (get_keysym_list (line, len, &n, &kslist) < 0)
425	return;
426
427    while (--count >= 0) {
428	uop = AllocStruct (union op);
429	if (!uop) {
430	    badmsg ("attempt to allocate a %ld byte keycode opcode",
431		    (long) sizeof (struct op_keycode));
432	    return;
433	}
434	opk = &uop->keycode;
435
436	opk->type = doKeycode;
437	opk->target_keycode = keycodes[count];
438	opk->count = n;
439	opk->keysyms = kslist;
440
441	add_to_work_queue (uop);
442    }
443
444#ifdef later
445    /* make sure we handle any special keys */
446    check_special_keys (keycode, n, kslist);
447#endif
448}
449
450
451/*
452 * parse_modifier - convert a modifier string name to its index
453 */
454
455struct modtab modifier_table[] = {	/* keep in order so it can be index */
456    { "shift", 5, 0 },
457    { "lock", 4, 1 },
458    { "control", 7, 2 },
459    { "mod1", 4, 3 },
460    { "mod2", 4, 4 },
461    { "mod3", 4, 5 },
462    { "mod4", 4, 6 },
463    { "mod5", 4, 7 },
464    { "ctrl", 4, 2 },
465    { NULL, 0, 0 }};
466
467static int
468parse_modifier(char *line, int n)
469{
470    register int i;
471    struct modtab *mt;
472
473    /* lowercase for comparison against table */
474    for (i = 0; i < n; i++) {
475	if (isupper (line[i])) line[i] = tolower (line[i]);
476    }
477
478    for (mt = modifier_table; mt->name; mt++) {
479	if (n == mt->length && strncmp (line, mt->name, n) == 0)
480	  return (mt->value);
481    }
482    return (-1);
483}
484
485
486/*
487 * do_add - parse off lines of the form
488 *
489 *                 add MODIFIER = keysym ...
490 *                     ^
491 * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case
492 * is not important.  There should also be an alias Ctrl for control.
493 */
494
495static void
496do_add(char *line, int len)
497{
498    int n;
499    int modifier;
500    KeySym *kslist;
501    union op *uop;
502    struct op_addmodifier *opam;
503
504    if (len < 6 || !line || *line == '\0') {  /* Lock=a minimum */
505	badmsg0 ("add modifier input line");
506	return;
507    }
508
509    n = skip_chars (line, len);
510    if (n < 1) {
511	badmsg ("add modifier name %s", line);
512	return;
513    }
514
515    modifier = parse_modifier (line, n);
516    if (modifier < 0) {
517	badmsgn ("add modifier name '%s', not allowed", line, n);
518	return;
519    }
520
521    line += n, len -= n;
522    n = skip_until_char (line, len, '=');
523    if (n < 0) {
524	badmsg0 ("add modifier = keysym");
525	return;
526    }
527
528    n++;				/* skip = */
529    n += skip_space (line+n, len-n);
530    line += n, len -= n;
531
532    if (get_keysym_list (line, len, &n, &kslist) < 0)
533	return;
534    if (n == 0) {
535	badmsg0 ("add modifier keysym list (empty)");
536	return;
537    }
538
539    uop = AllocStruct (union op);
540    if (!uop) {
541	badmsg ("attempt to allocate %ld byte addmodifier opcode",
542		(long) sizeof (struct op_addmodifier));
543	return;
544    }
545    opam = &uop->addmodifier;
546
547    opam->type = doAddModifier;
548    opam->modifier = modifier;
549    opam->count = n;
550    opam->keysyms = kslist;
551
552    add_to_work_queue (uop);
553}
554
555#ifdef AUTO_ADD_REMOVE
556/*
557 * make_add - stick a single add onto the queue
558 */
559static void
560make_add(int modifier, KeySym keysym)
561{
562    union op *uop;
563    struct op_addmodifier *opam;
564
565    uop = AllocStruct (union op);
566    if (!uop) {
567	badmsg ("attempt to allocate %ld byte addmodifier opcode",
568		(long) sizeof (struct op_addmodifier));
569	return;
570    }
571    opam = &uop->addmodifier;
572
573    opam->type = doAddModifier;
574    opam->modifier = modifier;
575    opam->count = 1;
576    opam->keysyms = (KeySym *) malloc (sizeof (KeySym));
577    if (!opam->keysyms) {
578	badmsg ("attempt to allocate %ld byte KeySym", (long) sizeof (KeySym));
579	free ((char *) opam);
580	return;
581    }
582    opam->keysyms[0] = keysym;
583
584    add_to_work_queue (uop);
585    return;
586}
587#endif /* AUTO_ADD_REMOVE */
588
589
590/*
591 * do_remove - parse off lines of the form
592 *
593 *                 remove MODIFIER = oldkeysym ...
594 *                        ^
595 * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case
596 * is not important.  There should also be an alias Ctrl for control.
597 */
598
599static void
600do_remove(char *line, int len)
601{
602    int n;
603    int nc;
604    int i;
605    int tot;
606    int modifier;
607    KeySym *kslist;
608    KeyCode *kclist;
609    union op *uop;
610    struct op_removemodifier *oprm;
611
612    if (len < 6 || !line || *line == '\0') {  /* Lock=a minimum */
613	badmsg0 ("remove modifier input line");
614	return;
615    }
616
617    n = skip_chars (line, len);
618    if (n < 1) {
619	badmsg ("remove modifier name %s", line);
620	return;
621    }
622
623    modifier = parse_modifier (line, n);
624    if (modifier < 0) {
625	badmsgn ("remove modifier name '%s', not allowed", line, n);
626	return;
627    }
628
629    line += n, len -= n;
630    n = skip_until_char (line, len, '=');
631    if (n < 0) {
632	badmsg0 ("remove modifier = keysym");
633	return;
634    }
635
636    n++;
637    n += skip_space (line+n, len-n);
638    line += n, len -= n;
639
640    if (get_keysym_list (line, len, &n, &kslist) < 0)
641	return;
642    if (n == 0) {
643	badmsg0 ("remove modifier keysym list (empty)");
644	return;
645    }
646
647    /*
648     * unlike the add command, we have to now evaluate the keysyms
649     */
650
651    kclist = (KeyCode *) malloc (n * sizeof (KeyCode));
652    if (!kclist) {
653	badmsg ("attempt to allocate %ld byte keycode list",
654		(long) (n * sizeof (KeyCode)));
655	free ((char *) kslist);
656	return;
657    }
658
659    tot = n;
660    nc = 0;
661    for (i = 0; i < n; i++) {
662        int num_kcs;
663	KeyCode *kcs;
664	kcs = KeysymToKeycodes (dpy, kslist[i], &num_kcs);
665	if (num_kcs == 0) {
666	    char *tmpname = XKeysymToString (kslist[i]);
667	    badmsg ("keysym in remove modifier list '%s', no corresponding keycodes",
668		    tmpname ? tmpname : "?");
669	    continue;
670	}
671	if (verbose) {
672	    int j;
673	    char *tmpname = XKeysymToString (kslist[i]);
674	    printf ("! Keysym %s (0x%lx) corresponds to keycode(s)",
675		    tmpname ? tmpname : "?", (long) kslist[i]);
676	    for (j = 0; j < num_kcs; j++)
677		printf(" 0x%x", kcs[j]);
678	    printf("\n");
679	}
680	if (nc + num_kcs > tot) {
681	    tot = nc + num_kcs;
682	    kclist = (KeyCode *)realloc((char *)kclist, tot * sizeof(KeyCode));
683	    if (!kclist) {
684		badmsg ("attempt to allocate %ld byte keycode list",
685			(long) (tot * sizeof (KeyCode)));
686		free ((char *) kslist);
687		return;
688	    }
689	}
690	while (--num_kcs >= 0)
691	    kclist[nc++] = *kcs++;		/* okay, add it to list */
692    }
693
694    free ((char *) kslist);		/* all done with it */
695
696    uop = AllocStruct (union op);
697    if (!uop) {
698	badmsg ("attempt to allocate %ld byte removemodifier opcode",
699		(long) sizeof (struct op_removemodifier));
700	return;
701    }
702    oprm = &uop->removemodifier;
703
704    oprm->type = doRemoveModifier;
705    oprm->modifier = modifier;
706    oprm->count = nc;
707    oprm->keycodes = kclist;
708
709    add_to_work_queue (uop);
710}
711
712#ifdef AUTO_ADD_REMOVE
713/*
714 * make_remove - stick a single remove onto the queue
715 */
716static void
717make_remove(int modifier, KeyCode keycode)
718{
719    union op *uop;
720    struct op_removemodifier *oprm;
721
722    uop = AllocStruct (union op);
723    if (!uop) {
724	badmsg ("attempt to allocate %ld byte removemodifier opcode",
725		(long) sizeof (struct op_removemodifier));
726	return;
727    }
728    oprm = &uop->removemodifier;
729
730    oprm->type = doRemoveModifier;
731    oprm->modifier = modifier;
732    oprm->count = 1;
733    oprm->keycodes = (KeyCode *) malloc (sizeof (KeyCode));
734    if (!oprm->keycodes) {
735	badmsg ("attempt to allocate %ld byte KeyCode",
736		(long) sizeof (KeyCode));
737	free ((char *) oprm);
738	return;
739    }
740    oprm->keycodes[0] = keycode;
741
742    add_to_work_queue (uop);
743    return;
744}
745#endif /* AUTO_ADD_REMOVE */
746
747
748/*
749 * do_clear - parse off lines of the form
750 *
751 *                 clear MODIFIER
752 *                       ^
753 */
754
755static void
756do_clear(char *line, int len)
757{
758    int n;
759    int modifier;
760    union op *uop;
761    struct op_clearmodifier *opcm;
762
763    if (len < 4 || !line || *line == '\0') {  /* Lock minimum */
764	badmsg0 ("clear modifier input line");
765	return;
766    }
767
768    n = skip_chars (line, len);
769
770    modifier = parse_modifier (line, n);
771    if (modifier < 0) {
772	badmsgn ("clear modifier name '%s'", line, n);
773	return;
774    }
775    n += skip_space (line+n, len-n);
776    if (n != len) {
777	badmsgn ("extra argument '%s' to clear modifier", line+n, len-n);
778	/* okay to continue */
779    }
780
781    uop = AllocStruct (union op);
782    if (!uop) {
783	badmsg ("attempt to allocate %ld byte clearmodifier opcode",
784		(long) sizeof (struct op_clearmodifier));
785	return;
786    }
787    opcm = &uop->clearmodifier;
788
789    opcm->type = doClearModifier;
790    opcm->modifier = modifier;
791
792    add_to_work_queue (uop);
793}
794
795#ifndef HAVE_STRNCASECMP
796static int
797strncasecmp(const char *a, const char *b, int n)
798{
799    int i;
800    int a1, b1;
801
802    for (i = 0; i < n; i++, a++, b++) {
803	if (!*a) return -1;
804	if (!*b) return 1;
805
806	if (*a != *b) {
807	    a1 = (isascii(*a) && isupper(*a)) ? tolower(*a) : *a;
808	    b1 = (isascii(*b) && isupper(*b)) ? tolower(*b) : *b;
809	    if (a1 != b1) return b1 - a1;
810	}
811    }
812    return 0;
813}
814#endif
815
816/*
817 * do_pointer = get list of numbers of the form
818 *
819 *                 buttons = NUMBER ...
820 *                         ^
821 */
822
823static void
824do_pointer(char *line, int len)
825{
826    int n;
827    int i;
828    unsigned long val;
829    union op *uop;
830    struct op_pointer *opp;
831    unsigned char buttons[MAXBUTTONCODES];
832    int nbuttons;
833    char *strval;
834    Bool ok;
835
836    if (len < 2 || !line || *line == '\0') {  /* =1 minimum */
837	badmsg0 ("buttons input line");
838	return;
839    }
840
841    nbuttons = XGetPointerMapping (dpy, buttons, MAXBUTTONCODES);
842
843    n = skip_space (line, len);
844    line += n, len -= n;
845
846    if (line[0] != '=') {
847	badmsg0 ("buttons pointer code list, missing equal sign");
848	return;
849    }
850
851    line++, len--;			/* skip = */
852    n = skip_space (line, len);
853    line += n, len -= n;
854
855    i = 0;
856    if (len < 7 || strncasecmp (line, "default", 7) != 0) {
857	while (len > 0) {
858	    n = skip_space (line, len);
859	    line += n, len -= n;
860	    if (line[0] == '\0') break;
861	    n = skip_word (line, len);
862	    if (n < 1) {
863		badmsg ("skip of word in buttons line:  %s", line);
864		return;
865	    }
866	    strval = copy_to_scratch(line, n);
867	    ok = parse_number (strval, &val);
868	    if (!ok || val >= MAXBUTTONCODES) {
869		badmsg ("value %s given for buttons list", strval);
870		return;
871	    }
872	    buttons[i++] = (unsigned char) val;
873	    line += n, len -= n;
874	}
875    }
876
877    if (i > 0 && i != nbuttons) {
878	fprintf (stderr, "Warning: Only changing the first %d of %d buttons.\n",
879		 i, nbuttons);
880	i = nbuttons;
881    }
882
883    uop = AllocStruct (union op);
884    if (!uop) {
885	badmsg ("attempt to allocate a %ld byte pointer opcode",
886		(long) sizeof (struct op_pointer));
887	return;
888    }
889    opp = &uop->pointer;
890
891    opp->type = doPointer;
892    opp->count = i;
893    for (i = 0; i < opp->count; i++) {
894	opp->button_codes[i] = buttons[i];
895    }
896
897    add_to_work_queue (uop);
898}
899
900
901/*
902 * get_keysym_list - parses the rest of the line into a keysyms assumes
903 * that the = sign has been parsed off but there may be leading whitespace
904 *
905 *                 keysym ...
906 *                 ^
907 *
908 * this involves getting the word containing the keysym, checking its range,
909 * and adding it to the list.
910 */
911
912static int
913get_keysym_list(const char *line, int len, int *np, KeySym **kslistp)
914{
915    int havesofar, maxcanhave;
916    KeySym *keysymlist;
917
918    *np = 0;
919    *kslistp = NULL;
920
921    if (len == 0) return (0);		/* empty list */
922
923    havesofar = 0;
924    maxcanhave = 4;			/* most lists are small */
925    keysymlist = (KeySym *) malloc (maxcanhave * sizeof (KeySym));
926    if (!keysymlist) {
927	badmsg ("attempt to allocate %ld byte initial keysymlist",
928		(long) (maxcanhave * sizeof (KeySym)));
929	return (-1);
930    }
931
932    while (len > 0) {
933	KeySym keysym;
934	int n;
935	char *tmpname;
936	Bool ok;
937
938	n = skip_space (line, len);
939	line += n, len -= n;
940
941	n = skip_chars (line, len);
942	if (n < 0) {
943	    badmsg0 ("keysym name list");
944	    free(keysymlist);
945	    return (-1);
946	}
947
948	ok = parse_keysym (line, n, &tmpname, &keysym);
949	line += n, len -= n;
950	if (!ok) {
951	    badmsg ("keysym name '%s' in keysym list", tmpname);
952	    /* do NOT return here, look for others */
953	    continue;
954	}
955
956	/*
957	 * Do NOT test to see if the keysym translates to a keycode or you
958	 * won't be able to assign new ones....
959	 */
960
961	/* grow the list bigger if necessary */
962	if (havesofar >= maxcanhave) {
963	    KeySym *origkeysymlist = keysymlist;
964	    maxcanhave *= 2;
965	    keysymlist = (KeySym *) realloc (keysymlist,
966					     maxcanhave * sizeof (KeySym));
967	    if (!keysymlist) {
968		badmsg ("attempt to grow keysym list to %ld bytes",
969			(long) (maxcanhave * sizeof (KeySym)));
970		free(origkeysymlist);
971		return (-1);
972	    }
973	}
974
975	/* and add it to the list */
976	keysymlist[havesofar++] = keysym;
977    }
978
979    *kslistp = keysymlist;
980    *np = havesofar;
981    return (0);
982}
983
984
985#ifdef later
986/*
987 * check_special_keys - run through list of keysyms and generate "add" or
988 * "remove" commands for for any of the key syms that appear in the modifier
989 * list.  this involves running down the modifier map which is an array of
990 * 8 by map->max_keypermod keycodes.
991 */
992
993static void
994check_special_keys(KeyCode keycode, int n, KeySym *kslist)
995{
996    int i;				/* iterator variable */
997    KeyCode *kcp;			/* keycode pointer */
998
999    /*
1000     * walk the modifiermap array.  since its dimensions are not known at
1001     * compile time, we have to walk it by hand instead of indexing.  this
1002     * is why it is initialized outside the loop, but incremented inside the
1003     * second loop.
1004     */
1005
1006    kcp = map->modifiermap;		/* start at beginning and iterate */
1007    for (i = 0; i < 8; i++) {		/* there are 8 modifier keys */
1008	int j;
1009
1010	for (j = 0; j < map->max_keypermod; j++, kcp++) {
1011	    KeySym keysym;
1012	    int k;
1013
1014	    if (!*kcp) continue;	/* only non-zero entries significant */
1015
1016	    /*
1017	     * check to see if the target keycode is already a modifier; if so,
1018	     * then we have to remove it
1019	     */
1020	    if (keycode == *kcp) {
1021		make_remove (i, keycode);
1022	    }
1023
1024	    /*
1025	     * now, check to see if any of the keysyms map to keycodes
1026	     * that are in the modifier list
1027	     */
1028	    for (k = 0; k < n; k++) {
1029		KeyCodes kc;
1030
1031		kc = XKeysymToKeycode (dpy, kslist[k]);
1032		if (kc == *kcp) {	/* yup, found one */
1033		    /*
1034		     * have to generate a remove of the CURRENT keycode
1035		     * and then an add of the new KEYCODE
1036		     */
1037		    make_remove (i, kc);  /* modifier, keycode */
1038		    make_add (i, kslist[k]);  /* modifier, keysym */
1039		}
1040	    }
1041	}
1042    }
1043    return;
1044}
1045#endif
1046
1047/*
1048 * print_work_queue - disassemble the work queue and print it on stdout
1049 */
1050
1051void
1052print_work_queue(void)
1053{
1054    union op *op;
1055
1056    printf ("! dump of work queue\n");
1057    for (op = work_queue.head; op; op = op->generic.next) {
1058	print_opcode (op);
1059    }
1060    return;
1061}
1062
1063static void
1064print_opcode(union op *op)
1065{
1066    int i;
1067
1068    printf ("        ");
1069    switch (op->generic.type) {
1070      case doKeycode:
1071	if (op->keycode.target_keycode)
1072	    printf ("keycode 0x%lx =", (long) op->keycode.target_keycode);
1073	else
1074	    printf ("keycode any =");
1075	for (i = 0; i < op->keycode.count; i++) {
1076	    char *name = XKeysymToString (op->keycode.keysyms[i]);
1077
1078	    printf (" %s", name ? name : "BADKEYSYM");
1079	}
1080	printf ("\n");
1081	break;
1082      case doAddModifier:
1083	printf ("add %s =", modifier_table[op->addmodifier.modifier].name);
1084	for (i = 0; i < op->addmodifier.count; i++) {
1085	    char *name = XKeysymToString (op->addmodifier.keysyms[i]);
1086	    printf (" %s", name ? name : "BADKEYSYM");
1087	}
1088	printf ("\n");
1089	break;
1090      case doRemoveModifier:
1091	printf ("remove %s = ",
1092		modifier_table[op->removemodifier.modifier].name);
1093	for (i = 0; i < op->removemodifier.count; i++) {
1094	    printf (" 0x%lx", (long) op->removemodifier.keycodes[i]);
1095	}
1096	printf ("\n");
1097	break;
1098      case doClearModifier:
1099	printf ("clear %s\n", modifier_table[op->clearmodifier.modifier].name);
1100	break;
1101      case doPointer:
1102	printf ("pointer = ");
1103	if (op->pointer.count == 0)
1104	    printf(" default");
1105	else for (i=0; i < op->pointer.count; i++)
1106	    printf(" %d", op->pointer.button_codes[i]);
1107	printf ("\n");
1108	break;
1109      default:
1110	printf ("! unknown opcode %d\n", op->generic.type);
1111	break;
1112    }				/* end switch */
1113    return;
1114}
1115
1116/*
1117 * execute_work_queue - do the real meat and potatoes now that we know what
1118 * we need to do and that all of the input is correct.
1119 */
1120static int exec_keycode ( struct op_keycode *opk );
1121static int exec_add ( struct op_addmodifier *opam );
1122static int exec_remove ( struct op_removemodifier *oprm );
1123static int exec_clear ( struct op_clearmodifier *opcm );
1124static int exec_pointer ( struct op_pointer *opp );
1125
1126
1127int
1128execute_work_queue (void)
1129{
1130    union op *op;
1131    int errors;
1132    Bool update_map = False;
1133    int dosync;
1134
1135    if (verbose) {
1136	printf ("!\n");
1137	printf ("! executing work queue\n");
1138	printf ("!\n");
1139    }
1140
1141    errors = 0;
1142    dosync = 0;
1143
1144    for (op = work_queue.head; op; op = op->generic.next) {
1145	if (verbose) print_opcode (op);
1146
1147	/* check to see if we have to update the keyboard mapping */
1148	if (dosync &&
1149	    (dosync < 0 ||
1150	     op->generic.type != doKeycode ||
1151	     !op->keycode.target_keycode)) {
1152	    XSync (dpy, 0);
1153	    while (XEventsQueued (dpy, QueuedAlready) > 0) {
1154		XEvent event;
1155		XNextEvent (dpy, &event);
1156		if (event.type == MappingNotify) {
1157		    /* read all MappingNotify events */
1158		    while (XCheckTypedEvent (dpy, MappingNotify, &event)) ;
1159		    XRefreshKeyboardMapping (&event.xmapping);
1160		} else {
1161		    fprintf (stderr, "%s:  unknown event %ld\n",
1162		    	     ProgramName, (long) event.type);
1163		}
1164	    }
1165	}
1166	dosync = 0;
1167	switch (op->generic.type) {
1168	  case doKeycode:
1169	    if (exec_keycode (&op->keycode) < 0) errors++;
1170	    if (op->keycode.target_keycode)
1171		dosync = 1;
1172	    else
1173		dosync = -1;
1174	    break;
1175	  case doAddModifier:
1176	    if (exec_add (&op->addmodifier) < 0) errors++;
1177	    else update_map = True;
1178	    break;
1179	  case doRemoveModifier:
1180	    if (exec_remove (&op->removemodifier) < 0) errors++;
1181	    else update_map = True;
1182	    break;
1183	  case doClearModifier:
1184	    if (exec_clear (&op->clearmodifier) < 0) errors++;
1185	    else update_map = True;
1186	    break;
1187	  case doPointer:
1188	    if (exec_pointer (&op->pointer) < 0) errors++;
1189	    break;
1190	  default:
1191	    fprintf (stderr, "%s:  unknown opcode %d\n",
1192		     ProgramName, op->generic.type);
1193	    break;
1194	}
1195    }
1196
1197    if (update_map) {
1198	if (UpdateModifierMapping (map) < 0) errors++;
1199    }
1200
1201    return (errors > 0 ? -1 : 0);
1202}
1203
1204static int
1205exec_keycode(struct op_keycode *opk)
1206{
1207    if (!opk->target_keycode) {
1208	int i, j;
1209	KeyCode free;
1210	if (!opk->count)
1211	    return (0);
1212	free = 0;
1213	for (i = min_keycode; i <= max_keycode; i++) {
1214	    for (j = 0; j < opk->count; j++) {
1215		if (XKeycodeToKeysym(dpy, (KeyCode) i, j) != opk->keysyms[j])
1216		    break;
1217	    }
1218	    if (j >= opk->count)
1219		return (0);
1220	    if (free)
1221		continue;
1222	    for (j = 0; j < 8; j++) {
1223		if (XKeycodeToKeysym(dpy, (KeyCode) i, j) != None)
1224		    break;
1225	    }
1226	    if (j >= 8)
1227		free = i;
1228	}
1229	if (!free) {
1230	    fprintf(stderr, "%s: no available keycode for assignment\n",
1231		    ProgramName);
1232	    return (-1);
1233	}
1234	XChangeKeyboardMapping (dpy, free, opk->count, opk->keysyms, 1);
1235    } else if (opk->count == 0) {
1236	KeySym dummy = NoSymbol;
1237	XChangeKeyboardMapping (dpy, opk->target_keycode, 1,
1238				&dummy, 1);
1239    } else {
1240	XChangeKeyboardMapping (dpy, opk->target_keycode, opk->count,
1241				opk->keysyms, 1);
1242    }
1243    return (0);
1244}
1245
1246static int
1247exec_add(struct op_addmodifier *opam)
1248{
1249    int i;
1250    int status;
1251
1252    status = 0;
1253    for (i = 0; i < opam->count; i++) {
1254	int num_kcs;
1255	KeyCode *kcs;
1256
1257	kcs = KeysymToKeycodes (dpy, opam->keysyms[i], &num_kcs);
1258	if (num_kcs == 0)
1259	    status = -1;
1260	while (--num_kcs >= 0) {
1261	    if (AddModifier (&map, *kcs++, opam->modifier) < 0)
1262		status = -1;
1263	}
1264    }
1265    return (status);
1266}
1267
1268static int
1269exec_remove(struct op_removemodifier *oprm)
1270{
1271    int i;
1272    int status;
1273
1274    status = 0;
1275    for (i = 0; i < oprm->count; i++) {
1276	if (RemoveModifier (&map, oprm->keycodes[i], oprm->modifier) < 0)
1277	  status = -1;
1278    }
1279    return (status);
1280}
1281
1282static int
1283exec_clear(struct op_clearmodifier *opcm)
1284{
1285    return (ClearModifier (&map, opcm->modifier));
1286}
1287
1288
1289static int
1290exec_pointer(struct op_pointer *opp)
1291{
1292    return (SetPointerMap (opp->button_codes, opp->count));
1293}
1294
1295void
1296print_modifier_map(void)
1297{
1298    PrintModifierMapping (map, stdout);
1299    return;
1300}
1301
1302void
1303print_key_table(Bool exprs)
1304{
1305    PrintKeyTable (exprs, stdout);
1306    return;
1307}
1308
1309void
1310print_pointer_map(void)
1311{
1312    PrintPointerMap (stdout);
1313    return;
1314}
1315
1316
1317