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