11a30de1fSmrg/*
21a30de1fSmrg
31a30de1fSmrgCopyright 1988, 1998  The Open Group
41a30de1fSmrg
51a30de1fSmrgPermission to use, copy, modify, distribute, and sell this software and its
61a30de1fSmrgdocumentation for any purpose is hereby granted without fee, provided that
71a30de1fSmrgthe above copyright notice appear in all copies and that both that
81a30de1fSmrgcopyright notice and this permission notice appear in supporting
91a30de1fSmrgdocumentation.
101a30de1fSmrg
111a30de1fSmrgThe above copyright notice and this permission notice shall be included
121a30de1fSmrgin all copies or substantial portions of the Software.
131a30de1fSmrg
141a30de1fSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
151a30de1fSmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
161a30de1fSmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
171a30de1fSmrgIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
181a30de1fSmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
191a30de1fSmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
201a30de1fSmrgOTHER DEALINGS IN THE SOFTWARE.
211a30de1fSmrg
221a30de1fSmrgExcept as contained in this notice, the name of The Open Group shall
231a30de1fSmrgnot be used in advertising or otherwise to promote the sale, use or
241a30de1fSmrgother dealings in this Software without prior written authorization
251a30de1fSmrgfrom The Open Group.
261a30de1fSmrg
271a30de1fSmrg*/
281a30de1fSmrg
29976cff14Smrg#ifdef HAVE_CONFIG_H
301a30de1fSmrg#include "config.h"
31976cff14Smrg#endif
32976cff14Smrg
331a30de1fSmrg#include <X11/Xos.h>
341a30de1fSmrg#include <X11/Xlib.h>
351a30de1fSmrg#include <stdio.h>
361a30de1fSmrg#include <ctype.h>
371a30de1fSmrg#include "xmodmap.h"
381a30de1fSmrg#include "wq.h"
391a30de1fSmrg#include <stdlib.h>
40bfce1ac2Smrg#include <stdint.h>
411a30de1fSmrg
42a733a5bfSmrg#ifdef HAVE_STRNCASECMP
43a733a5bfSmrg#include <strings.h>
44a733a5bfSmrg#endif
45a733a5bfSmrg
461a30de1fSmrgstatic XModifierKeymap *map = NULL;
471a30de1fSmrg
481a30de1fSmrg
491a30de1fSmrg/*
501a30de1fSmrg * The routines in this file manipulate a queue of intructions.  Instead of
511a30de1fSmrg * executing each line as it is entered, we build up a list of actions to
521a30de1fSmrg * take and execute them all at the end.  This allows us to find all errors
531a30de1fSmrg * at once, and to preserve the context in which we are looking up keysyms.
541a30de1fSmrg */
551a30de1fSmrg
561a30de1fSmrgstruct wq work_queue = {NULL, NULL};
571a30de1fSmrg
581a30de1fSmrg
591a30de1fSmrg/*
601a30de1fSmrg * common utility routines
611a30de1fSmrg */
621a30de1fSmrg
63bfce1ac2Smrg/*
64bfce1ac2Smrg * This is a combination of reallocf() and reallocarray().
65bfce1ac2Smrg * If the realloc fails, it frees the old pointer so it doesn't leak.
66bfce1ac2Smrg */
67bfce1ac2Smrgstatic void *
68bfce1ac2Smrgreallocfarray(void *old, size_t num, size_t size)
69bfce1ac2Smrg{
70bfce1ac2Smrg    static void *new;
71bfce1ac2Smrg
72bfce1ac2Smrg    if (size > 0 && num > (SIZE_MAX / size)) /* overflow would happen */
73bfce1ac2Smrg        new = NULL;
74bfce1ac2Smrg    else
75bfce1ac2Smrg        new = realloc(old, num * size);
76bfce1ac2Smrg
77bfce1ac2Smrg    if (new == NULL)
78bfce1ac2Smrg        free(old);
79bfce1ac2Smrg
80bfce1ac2Smrg    return new;
81bfce1ac2Smrg}
82bfce1ac2Smrg
831a30de1fSmrgstatic KeyCode *
841a30de1fSmrgKeysymToKeycodes(Display *dpy, KeySym keysym, int *pnum_kcs)
851a30de1fSmrg{
861a30de1fSmrg    KeyCode *kcs = NULL;
871a30de1fSmrg    int i, j;
881a30de1fSmrg
891a30de1fSmrg    *pnum_kcs = 0;
901a30de1fSmrg    for (i = min_keycode; i <= max_keycode; i++) {
911a30de1fSmrg	for (j = 0; j < 8; j++) {
921a30de1fSmrg	    if (XKeycodeToKeysym(dpy, (KeyCode) i, j) == keysym) {
931a30de1fSmrg		if (!kcs)
941b983734Smrg		    kcs = malloc(sizeof(KeyCode));
951a30de1fSmrg		else
96bfce1ac2Smrg		    kcs = reallocfarray(kcs, (*pnum_kcs + 1), sizeof(KeyCode));
97bfce1ac2Smrg		if (!kcs) {
98bfce1ac2Smrg		    fprintf(stderr, "attempt to allocate %ld byte keycode set",
99bfce1ac2Smrg			    (long) ((*pnum_kcs + 1) * sizeof (KeyCode)));
100bfce1ac2Smrg		    return NULL;
101bfce1ac2Smrg		}
1021a30de1fSmrg		kcs[*pnum_kcs] = i;
1031a30de1fSmrg		*pnum_kcs += 1;
1041a30de1fSmrg		break;
1051a30de1fSmrg	    }
1061a30de1fSmrg	}
1071a30de1fSmrg    }
1081a30de1fSmrg    return kcs;
1091a30de1fSmrg}
1101a30de1fSmrg
1111a30de1fSmrgstatic char *
1121a30de1fSmrgcopy_to_scratch(const char *s, int len)
1131a30de1fSmrg{
1141a30de1fSmrg    static char *buf = NULL;
1151a30de1fSmrg    static int buflen = 0;
1161a30de1fSmrg
117a733a5bfSmrg    if (len < 0)
118a733a5bfSmrg        len = 0;
119a733a5bfSmrg
120a733a5bfSmrg    if (len >= buflen) {
1211a30de1fSmrg	if (buf) free (buf);
1221a30de1fSmrg	buflen = (len < 40) ? 80 : (len * 2);
1231b983734Smrg	buf = malloc (buflen+1);
124a733a5bfSmrg	if (!buf) {
125a733a5bfSmrg	    fprintf (stderr, "attempt to allocate %d byte scratch buffer\n", buflen + 1);
126a733a5bfSmrg	    return NULL;
127a733a5bfSmrg	}
1281a30de1fSmrg    }
1291a30de1fSmrg
130a733a5bfSmrg    strncpy (buf, s, len);
1311a30de1fSmrg    buf[len] = '\0';
132a733a5bfSmrg
1331a30de1fSmrg    return (buf);
1341a30de1fSmrg}
1351a30de1fSmrg
1361a30de1fSmrgstatic void
1371a30de1fSmrgbadheader(void)
1381a30de1fSmrg{
1391a30de1fSmrg    fprintf (stderr, "%s:  %s:%d:  bad ", ProgramName, inputFilename, lineno+1);
1401a30de1fSmrg    parse_errors++;
1411a30de1fSmrg}
1421a30de1fSmrg
1431a30de1fSmrg#define badmsg0(what) { badheader(); fprintf (stderr, what); \
1441a30de1fSmrg			   putc ('\n', stderr); }
1451a30de1fSmrg
1461a30de1fSmrg#define badmsg(what,arg) { badheader(); fprintf (stderr, what, arg); \
1471a30de1fSmrg			   putc ('\n', stderr); }
1481a30de1fSmrg
1491a30de1fSmrg#define badmsgn(what,s,len) badmsg (what, copy_to_scratch (s, len))
1501a30de1fSmrg
1511a30de1fSmrgvoid
1521a30de1fSmrginitialize_map (void)
1531a30de1fSmrg{
1541a30de1fSmrg    map = XGetModifierMapping (dpy);
1551a30de1fSmrg    return;
1561a30de1fSmrg}
1571a30de1fSmrg
1581a30de1fSmrgstatic void do_keycode ( char *line, int len );
1591a30de1fSmrgstatic void do_keysym ( char *line, int len );
1601a30de1fSmrgstatic void finish_keycodes ( const char *line, int len, KeyCode *keycodes,
1611a30de1fSmrg			      int count );
1621a30de1fSmrgstatic void do_add ( char *line, int len );
1631a30de1fSmrgstatic void do_remove ( char *line, int len );
1641a30de1fSmrgstatic void do_clear ( char *line, int len );
1651a30de1fSmrgstatic void do_pointer ( char *line, int len );
1661a30de1fSmrgstatic int get_keysym_list ( const char *line, int len, int *np, KeySym **kslistp );
1671a30de1fSmrg
1681a30de1fSmrgstatic void print_opcode(union op *op);
1691a30de1fSmrg
1701a30de1fSmrgstatic int skip_word ( const char *s, int len );
1711a30de1fSmrgstatic int skip_chars ( const char *s, int len );
1721a30de1fSmrgstatic int skip_space ( const char *s, int len );
1731a30de1fSmrg
1741a30de1fSmrgstatic struct dt {
175a733a5bfSmrg    const char *command;		/* name of input command */
1761a30de1fSmrg    int length;				/* length of command */
1771a30de1fSmrg    void (*proc)(char *, int);		/* handler */
1781a30de1fSmrg} dispatch_table[] = {
1791a30de1fSmrg    { "keycode", 7, do_keycode },
1801a30de1fSmrg    { "keysym", 6, do_keysym },
1811a30de1fSmrg    { "add", 3, do_add },
1821a30de1fSmrg    { "remove", 6, do_remove },
1831a30de1fSmrg    { "clear", 5, do_clear },
1841a30de1fSmrg    { "pointer", 7, do_pointer },
1851a30de1fSmrg    { NULL, 0, NULL }};
1861a30de1fSmrg
1871a30de1fSmrg/*
1881a30de1fSmrg * handle_line - this routine parses the input line (which has had all leading
1891a30de1fSmrg * and trailing whitespace removed) and builds up the work queue.
1901a30de1fSmrg */
1911a30de1fSmrg
1921a30de1fSmrgvoid
1931a30de1fSmrghandle_line(char *line,		/* string to parse */
1941a30de1fSmrg	    int len)		/* length of line */
1951a30de1fSmrg{
1961a30de1fSmrg    int n;
1971a30de1fSmrg    struct dt *dtp;
1981a30de1fSmrg
1991a30de1fSmrg    n = skip_chars (line, len);
2001a30de1fSmrg    if (n < 0) {
2011a30de1fSmrg	badmsg ("input line '%s'", line);
2021a30de1fSmrg	return;
2031a30de1fSmrg    }
2041a30de1fSmrg
2051a30de1fSmrg    for (dtp = dispatch_table; dtp->command != NULL; dtp++) {
2061a30de1fSmrg	if (n == dtp->length &&
2071a30de1fSmrg	    strncmp (line, dtp->command, dtp->length) == 0) {
2081a30de1fSmrg
2091a30de1fSmrg	    n += skip_space (line+n, len-n);
2101a30de1fSmrg	    line += n, len -= n;
2111a30de1fSmrg
2121a30de1fSmrg	    (*(dtp->proc)) (line, len);
2131a30de1fSmrg	    return;
2141a30de1fSmrg	}
2151a30de1fSmrg    }
2161a30de1fSmrg
2171a30de1fSmrg    fprintf (stderr, "%s:  unknown command on line %s:%d\n",
2181a30de1fSmrg	     ProgramName, inputFilename, lineno+1);
2191a30de1fSmrg    parse_errors++;
2201a30de1fSmrg}
2211a30de1fSmrg
2221a30de1fSmrg/*
2231a30de1fSmrg * the following routines are useful for parsing
2241a30de1fSmrg */
2251a30de1fSmrg
2261a30de1fSmrgstatic int
2271a30de1fSmrgskip_word (const char *s, int len)
2281a30de1fSmrg{
2291a30de1fSmrg    register int n;
2301a30de1fSmrg
2311a30de1fSmrg    n = skip_chars (s, len);
2321a30de1fSmrg    return (n + skip_space (s+n, len-n));
2331a30de1fSmrg}
2341a30de1fSmrg
2351a30de1fSmrgstatic int
2361a30de1fSmrgskip_chars(const char *s, int len)
2371a30de1fSmrg{
2381a30de1fSmrg    register int i;
2391a30de1fSmrg
2401a30de1fSmrg    if (len <= 0 || !s || *s == '\0') return (0);
2411a30de1fSmrg
2421a30de1fSmrg    for (i = 0; i < len; i++) {
2431a30de1fSmrg	if (isspace(s[i])) break;
2441a30de1fSmrg    }
2451a30de1fSmrg    return (i);
2461a30de1fSmrg}
2471a30de1fSmrg
2481a30de1fSmrgstatic int
2491a30de1fSmrgskip_space(const char *s, int len)
2501a30de1fSmrg{
2511a30de1fSmrg    register int i;
2521a30de1fSmrg
2531a30de1fSmrg    if (len <= 0 || !s || *s == '\0') return (0);
2541a30de1fSmrg
2551a30de1fSmrg    for (i = 0; i < len; i++) {
2561a30de1fSmrg	if (!s[i] || !isspace(s[i])) break;
2571a30de1fSmrg    }
2581a30de1fSmrg    return (i);
2591a30de1fSmrg}
2601a30de1fSmrg
2611a30de1fSmrg
2621a30de1fSmrgstatic int
2631a30de1fSmrgskip_until_char(const char *s, int len, char c)
2641a30de1fSmrg{
2651a30de1fSmrg    register int i;
2661a30de1fSmrg
2671a30de1fSmrg    for (i = 0; i < len; i++) {
2681a30de1fSmrg	if (s[i] == c) break;
2691a30de1fSmrg    }
2701a30de1fSmrg    return (i);
2711a30de1fSmrg}
2721a30de1fSmrg
2731a30de1fSmrg
2741a30de1fSmrg/*
2751a30de1fSmrg * The action routines.
2761a30de1fSmrg *
2771a30de1fSmrg * This is where the real work gets done.  Each routine is responsible for
2781a30de1fSmrg * parsing its input (note that the command keyword has been stripped off)
2791a30de1fSmrg * and adding to the work queue.  They are also in charge of outputting
2801a30de1fSmrg * error messages and returning non-zero if there is a problem.
2811a30de1fSmrg *
2821a30de1fSmrg * The following global variables are available:
2831a30de1fSmrg *     dpy                the display descriptor
2841a30de1fSmrg *     work_queue         linked list of opcodes
2851a30de1fSmrg *     inputFilename      name of the file being processed
2861a30de1fSmrg *     lineno             line number of current line in input file
2871a30de1fSmrg */
2881a30de1fSmrgstatic void
2891a30de1fSmrgadd_to_work_queue(union op *p)	/* this can become a macro someday */
2901a30de1fSmrg{
2911a30de1fSmrg    if (work_queue.head == NULL) {	/* nothing on the list */
2921a30de1fSmrg	work_queue.head = work_queue.tail = p;	/* head, tail, no prev */
2931a30de1fSmrg    } else {
2941a30de1fSmrg	work_queue.tail->generic.next = p;  /* head okay, prev */
2951a30de1fSmrg	work_queue.tail = p;		/* tail */
2961a30de1fSmrg    }
2971a30de1fSmrg    p->generic.next = NULL;
2981a30de1fSmrg
2991a30de1fSmrg    if (verbose) {
3001a30de1fSmrg	print_opcode (p);
3011a30de1fSmrg    }
3021a30de1fSmrg    return;
3031a30de1fSmrg}
3041a30de1fSmrg
3051a30de1fSmrgstatic Bool
3061a30de1fSmrgparse_number(const char *str, unsigned long *val)
3071a30de1fSmrg{
308a733a5bfSmrg    const char *fmt = "%ld";
3091a30de1fSmrg
3101a30de1fSmrg    if (*str == '0') {
3111a30de1fSmrg	str++;
312b7fb5eacSmrg	while (isspace(*str))
313b7fb5eacSmrg	    str++;
3141a30de1fSmrg	fmt = "%lo";
3151a30de1fSmrg	if (*str == '\0') {
3161a30de1fSmrg	    *val = 0;
3171a30de1fSmrg	    return 1;
3181a30de1fSmrg	}
3191a30de1fSmrg	if (*str == 'x' || *str == 'X') {
3201a30de1fSmrg	    str++;
3211a30de1fSmrg	    fmt = "%lx";
3221a30de1fSmrg	}
3231a30de1fSmrg    }
3241a30de1fSmrg    return (sscanf (str, fmt, val) == 1);
3251a30de1fSmrg}
3261a30de1fSmrg
3271a30de1fSmrgstatic Bool
3281a30de1fSmrgparse_keysym(const char *line, int n, char **name, KeySym *keysym)
3291a30de1fSmrg{
3301a30de1fSmrg    *name = copy_to_scratch (line, n);
3311a30de1fSmrg    if (!strcmp(*name, "NoSymbol")) {
3321a30de1fSmrg	*keysym = NoSymbol;
3331a30de1fSmrg	return (True);
3341a30de1fSmrg    }
3351a30de1fSmrg    *keysym = XStringToKeysym (*name);
3361a30de1fSmrg    if (*keysym == NoSymbol && '0' <= **name && **name <= '9')
3371a30de1fSmrg	return parse_number(*name, keysym);
3381a30de1fSmrg    return (*keysym != NoSymbol);
3391a30de1fSmrg}
3401a30de1fSmrg
3411a30de1fSmrg/*
3421a30de1fSmrg * do_keycode - parse off lines of the form
3431a30de1fSmrg *
3441a30de1fSmrg *                 "keycode" number "=" [keysym ...]
3451a30de1fSmrg *                           ^
3461a30de1fSmrg *
3471a30de1fSmrg * where number is in decimal, hex, or octal.  Any number of keysyms may be
3481a30de1fSmrg * listed.
3491a30de1fSmrg */
3501a30de1fSmrg
3511a30de1fSmrgstatic void
3521a30de1fSmrgdo_keycode(char *line, int len)
3531a30de1fSmrg{
3541a30de1fSmrg    int dummy;
355a733a5bfSmrg    const char *fmt = "%d";
3561a30de1fSmrg    KeyCode keycode;
3571a30de1fSmrg
3581a30de1fSmrg    if (len < 3 || !line || *line == '\0') {  /* 5=a minimum */
3591a30de1fSmrg	badmsg0 ("keycode input line");
3601a30de1fSmrg	return;
3611a30de1fSmrg    }
3621a30de1fSmrg
3631a30de1fSmrg    /*
3641a30de1fSmrg     * We need not bother to advance line/len past the
3651a30de1fSmrg     * number (or the string 'any') as finish_keycodes() will
3661a30de1fSmrg     * first advance past the '='.
3671a30de1fSmrg     */
3681a30de1fSmrg    if (!strncmp("any", line, 3)) {
3691a30de1fSmrg	keycode = 0;
3701a30de1fSmrg    } else {
3711a30de1fSmrg	if (*line == '0') line++, len--, fmt = "%o";
3721a30de1fSmrg	if (*line == 'x' || *line == 'X') line++, len--, fmt = "%x";
3731a30de1fSmrg
3741a30de1fSmrg	dummy = 0;
3751a30de1fSmrg	if (sscanf (line, fmt, &dummy) != 1 || dummy == 0) {
3761a30de1fSmrg	    badmsg0 ("keycode value");
3771a30de1fSmrg	    return;
3781a30de1fSmrg	}
3791a30de1fSmrg	keycode = (KeyCode) dummy;
3801a30de1fSmrg	if ((int)keycode < min_keycode || (int)keycode > max_keycode) {
3811a30de1fSmrg	    badmsg0 ("keycode value (out of range)");
3821a30de1fSmrg	    return;
3831a30de1fSmrg	}
3841a30de1fSmrg    }
3851a30de1fSmrg
3861a30de1fSmrg    finish_keycodes (line, len, &keycode, 1);
3871a30de1fSmrg}
3881a30de1fSmrg
3891a30de1fSmrg
3901a30de1fSmrg/*
3911a30de1fSmrg * do_keysym - parse off lines of the form
3921a30de1fSmrg *
3931a30de1fSmrg *                 "keysym" keysym "=" [keysym ...]
3941a30de1fSmrg *                          ^
3951a30de1fSmrg *
3961a30de1fSmrg * The left keysyms has to be checked for validity and evaluated.
3971a30de1fSmrg */
3981a30de1fSmrg
3991a30de1fSmrgstatic void
4001a30de1fSmrgdo_keysym(char *line, int len)
4011a30de1fSmrg{
4021a30de1fSmrg    int n;
4031a30de1fSmrg    KeyCode *keycodes;
4041a30de1fSmrg    KeySym keysym;
4051a30de1fSmrg    char *tmpname;
4061a30de1fSmrg
4071a30de1fSmrg    if (len < 3 || !line || *line == '\0') {  /* a=b minimum */
4081a30de1fSmrg	badmsg0 ("keysym input line");
4091a30de1fSmrg	return;
4101a30de1fSmrg    }
4111a30de1fSmrg
4121a30de1fSmrg    n = skip_chars (line, len);
4131a30de1fSmrg    if (n < 1) {
4141a30de1fSmrg	badmsg0 ("target keysym name");
4151a30de1fSmrg	return;
4161a30de1fSmrg    }
4171a30de1fSmrg    if (!parse_keysym(line, n, &tmpname, &keysym)) {
4181a30de1fSmrg	badmsg ("keysym target key symbol '%s'", tmpname);
4191a30de1fSmrg	return;
4201a30de1fSmrg    }
4211a30de1fSmrg
4221a30de1fSmrg    keycodes = KeysymToKeycodes (dpy, keysym, &n);
4231a30de1fSmrg    if (n == 0) {
4241a30de1fSmrg	badmsg ("keysym target keysym '%s', no corresponding keycodes",
4251a30de1fSmrg		tmpname);
4261a30de1fSmrg	return;
4271a30de1fSmrg    }
4281a30de1fSmrg    if (verbose) {
4291a30de1fSmrg	int i;
4301a30de1fSmrg	printf ("! Keysym %s (0x%lx) corresponds to keycode(s)",
4311a30de1fSmrg		tmpname, (long) keysym);
4321a30de1fSmrg	for (i = 0; i < n; i++)
4331a30de1fSmrg	    printf (" 0x%x", keycodes[i]);
4341a30de1fSmrg	printf("\n");
4351a30de1fSmrg    }
4361a30de1fSmrg
4371a30de1fSmrg    finish_keycodes (line, len, keycodes, n);
4381a30de1fSmrg}
4391a30de1fSmrg
4401a30de1fSmrgstatic void
4411a30de1fSmrgfinish_keycodes(const char *line, int len, KeyCode *keycodes, int count)
4421a30de1fSmrg{
4431a30de1fSmrg    int n;
4441a30de1fSmrg    KeySym *kslist;
4451a30de1fSmrg    union op *uop;
4461a30de1fSmrg    struct op_keycode *opk;
4471a30de1fSmrg
4481a30de1fSmrg    n = skip_until_char (line, len, '=');
4491a30de1fSmrg    line += n, len -= n;
4501a30de1fSmrg
4511a30de1fSmrg    if (len < 1 || *line != '=') {	/* = minimum */
4521a30de1fSmrg	badmsg0 ("keycode command (missing keysym list),");
4531a30de1fSmrg	return;
4541a30de1fSmrg    }
4551a30de1fSmrg    line++, len--;			/* skip past the = */
4561a30de1fSmrg
4571a30de1fSmrg    n = skip_space (line, len);
4581a30de1fSmrg    line += n, len -= n;
4591a30de1fSmrg
4601a30de1fSmrg    /* allow empty list */
4611a30de1fSmrg    if (get_keysym_list (line, len, &n, &kslist) < 0)
4621a30de1fSmrg	return;
4631a30de1fSmrg
4641a30de1fSmrg    while (--count >= 0) {
4651a30de1fSmrg	uop = AllocStruct (union op);
4661a30de1fSmrg	if (!uop) {
4671a30de1fSmrg	    badmsg ("attempt to allocate a %ld byte keycode opcode",
4681a30de1fSmrg		    (long) sizeof (struct op_keycode));
4691a30de1fSmrg	    return;
4701a30de1fSmrg	}
4711a30de1fSmrg	opk = &uop->keycode;
4721a30de1fSmrg
4731a30de1fSmrg	opk->type = doKeycode;
4741a30de1fSmrg	opk->target_keycode = keycodes[count];
4751a30de1fSmrg	opk->count = n;
4761a30de1fSmrg	opk->keysyms = kslist;
4771a30de1fSmrg
4781a30de1fSmrg	add_to_work_queue (uop);
4791a30de1fSmrg    }
4801a30de1fSmrg
4811a30de1fSmrg#ifdef later
4821a30de1fSmrg    /* make sure we handle any special keys */
4831a30de1fSmrg    check_special_keys (keycode, n, kslist);
4841a30de1fSmrg#endif
4851a30de1fSmrg}
4861a30de1fSmrg
4871a30de1fSmrg
4881a30de1fSmrg/*
4891a30de1fSmrg * parse_modifier - convert a modifier string name to its index
4901a30de1fSmrg */
4911a30de1fSmrg
4921a30de1fSmrgstruct modtab modifier_table[] = {	/* keep in order so it can be index */
4931a30de1fSmrg    { "shift", 5, 0 },
4941a30de1fSmrg    { "lock", 4, 1 },
4951a30de1fSmrg    { "control", 7, 2 },
4961a30de1fSmrg    { "mod1", 4, 3 },
4971a30de1fSmrg    { "mod2", 4, 4 },
4981a30de1fSmrg    { "mod3", 4, 5 },
4991a30de1fSmrg    { "mod4", 4, 6 },
5001a30de1fSmrg    { "mod5", 4, 7 },
5011a30de1fSmrg    { "ctrl", 4, 2 },
5021a30de1fSmrg    { NULL, 0, 0 }};
5031a30de1fSmrg
5041a30de1fSmrgstatic int
5051a30de1fSmrgparse_modifier(char *line, int n)
5061a30de1fSmrg{
5071a30de1fSmrg    register int i;
5081a30de1fSmrg    struct modtab *mt;
5091a30de1fSmrg
5101a30de1fSmrg    /* lowercase for comparison against table */
5111a30de1fSmrg    for (i = 0; i < n; i++) {
5121a30de1fSmrg	if (isupper (line[i])) line[i] = tolower (line[i]);
5131a30de1fSmrg    }
5141a30de1fSmrg
5151a30de1fSmrg    for (mt = modifier_table; mt->name; mt++) {
5161a30de1fSmrg	if (n == mt->length && strncmp (line, mt->name, n) == 0)
5171a30de1fSmrg	  return (mt->value);
5181a30de1fSmrg    }
5191a30de1fSmrg    return (-1);
5201a30de1fSmrg}
5211a30de1fSmrg
5221a30de1fSmrg
5231a30de1fSmrg/*
5241a30de1fSmrg * do_add - parse off lines of the form
5251a30de1fSmrg *
5261a30de1fSmrg *                 add MODIFIER = keysym ...
5271a30de1fSmrg *                     ^
5281a30de1fSmrg * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case
5291a30de1fSmrg * is not important.  There should also be an alias Ctrl for control.
5301a30de1fSmrg */
5311a30de1fSmrg
5321a30de1fSmrgstatic void
5331a30de1fSmrgdo_add(char *line, int len)
5341a30de1fSmrg{
5351a30de1fSmrg    int n;
5361a30de1fSmrg    int modifier;
5371a30de1fSmrg    KeySym *kslist;
5381a30de1fSmrg    union op *uop;
5391a30de1fSmrg    struct op_addmodifier *opam;
5401a30de1fSmrg
5411a30de1fSmrg    if (len < 6 || !line || *line == '\0') {  /* Lock=a minimum */
5421a30de1fSmrg	badmsg0 ("add modifier input line");
5431a30de1fSmrg	return;
5441a30de1fSmrg    }
5451a30de1fSmrg
5461a30de1fSmrg    n = skip_chars (line, len);
5471a30de1fSmrg    if (n < 1) {
5481a30de1fSmrg	badmsg ("add modifier name %s", line);
5491a30de1fSmrg	return;
5501a30de1fSmrg    }
5511a30de1fSmrg
5521a30de1fSmrg    modifier = parse_modifier (line, n);
5531a30de1fSmrg    if (modifier < 0) {
5541a30de1fSmrg	badmsgn ("add modifier name '%s', not allowed", line, n);
5551a30de1fSmrg	return;
5561a30de1fSmrg    }
5571a30de1fSmrg
5581a30de1fSmrg    line += n, len -= n;
5591a30de1fSmrg    n = skip_until_char (line, len, '=');
5601a30de1fSmrg    if (n < 0) {
5611a30de1fSmrg	badmsg0 ("add modifier = keysym");
5621a30de1fSmrg	return;
5631a30de1fSmrg    }
5641a30de1fSmrg
5651a30de1fSmrg    n++;				/* skip = */
5661a30de1fSmrg    n += skip_space (line+n, len-n);
5671a30de1fSmrg    line += n, len -= n;
5681a30de1fSmrg
5691a30de1fSmrg    if (get_keysym_list (line, len, &n, &kslist) < 0)
5701a30de1fSmrg	return;
5711a30de1fSmrg    if (n == 0) {
5721a30de1fSmrg	badmsg0 ("add modifier keysym list (empty)");
5731a30de1fSmrg	return;
5741a30de1fSmrg    }
5751a30de1fSmrg
5761a30de1fSmrg    uop = AllocStruct (union op);
5771a30de1fSmrg    if (!uop) {
5781a30de1fSmrg	badmsg ("attempt to allocate %ld byte addmodifier opcode",
5791a30de1fSmrg		(long) sizeof (struct op_addmodifier));
5801a30de1fSmrg	return;
5811a30de1fSmrg    }
5821a30de1fSmrg    opam = &uop->addmodifier;
5831a30de1fSmrg
5841a30de1fSmrg    opam->type = doAddModifier;
5851a30de1fSmrg    opam->modifier = modifier;
5861a30de1fSmrg    opam->count = n;
5871a30de1fSmrg    opam->keysyms = kslist;
5881a30de1fSmrg
5891a30de1fSmrg    add_to_work_queue (uop);
5901a30de1fSmrg}
5911a30de1fSmrg
5921a30de1fSmrg#ifdef AUTO_ADD_REMOVE
5931a30de1fSmrg/*
5941a30de1fSmrg * make_add - stick a single add onto the queue
5951a30de1fSmrg */
5961a30de1fSmrgstatic void
5971a30de1fSmrgmake_add(int modifier, KeySym keysym)
5981a30de1fSmrg{
5991a30de1fSmrg    union op *uop;
6001a30de1fSmrg    struct op_addmodifier *opam;
6011a30de1fSmrg
6021a30de1fSmrg    uop = AllocStruct (union op);
6031a30de1fSmrg    if (!uop) {
6041a30de1fSmrg	badmsg ("attempt to allocate %ld byte addmodifier opcode",
6051a30de1fSmrg		(long) sizeof (struct op_addmodifier));
6061a30de1fSmrg	return;
6071a30de1fSmrg    }
6081a30de1fSmrg    opam = &uop->addmodifier;
6091a30de1fSmrg
6101a30de1fSmrg    opam->type = doAddModifier;
6111a30de1fSmrg    opam->modifier = modifier;
6121a30de1fSmrg    opam->count = 1;
6131b983734Smrg    opam->keysyms = malloc (sizeof (KeySym));
6141a30de1fSmrg    if (!opam->keysyms) {
6151a30de1fSmrg	badmsg ("attempt to allocate %ld byte KeySym", (long) sizeof (KeySym));
6161b983734Smrg	free (opam);
6171a30de1fSmrg	return;
6181a30de1fSmrg    }
6191a30de1fSmrg    opam->keysyms[0] = keysym;
6201a30de1fSmrg
6211a30de1fSmrg    add_to_work_queue (uop);
6221a30de1fSmrg    return;
6231a30de1fSmrg}
6241a30de1fSmrg#endif /* AUTO_ADD_REMOVE */
6251a30de1fSmrg
6261a30de1fSmrg
6271a30de1fSmrg/*
6281a30de1fSmrg * do_remove - parse off lines of the form
6291a30de1fSmrg *
6301a30de1fSmrg *                 remove MODIFIER = oldkeysym ...
6311a30de1fSmrg *                        ^
6321a30de1fSmrg * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case
6331a30de1fSmrg * is not important.  There should also be an alias Ctrl for control.
6341a30de1fSmrg */
6351a30de1fSmrg
6361a30de1fSmrgstatic void
6371a30de1fSmrgdo_remove(char *line, int len)
6381a30de1fSmrg{
6391a30de1fSmrg    int n;
6401a30de1fSmrg    int nc;
6411a30de1fSmrg    int i;
6421a30de1fSmrg    int tot;
6431a30de1fSmrg    int modifier;
6441a30de1fSmrg    KeySym *kslist;
6451a30de1fSmrg    KeyCode *kclist;
6461a30de1fSmrg    union op *uop;
6471a30de1fSmrg    struct op_removemodifier *oprm;
6481a30de1fSmrg
6491a30de1fSmrg    if (len < 6 || !line || *line == '\0') {  /* Lock=a minimum */
6501a30de1fSmrg	badmsg0 ("remove modifier input line");
6511a30de1fSmrg	return;
6521a30de1fSmrg    }
6531a30de1fSmrg
6541a30de1fSmrg    n = skip_chars (line, len);
6551a30de1fSmrg    if (n < 1) {
6561a30de1fSmrg	badmsg ("remove modifier name %s", line);
6571a30de1fSmrg	return;
6581a30de1fSmrg    }
6591a30de1fSmrg
6601a30de1fSmrg    modifier = parse_modifier (line, n);
6611a30de1fSmrg    if (modifier < 0) {
6621a30de1fSmrg	badmsgn ("remove modifier name '%s', not allowed", line, n);
6631a30de1fSmrg	return;
6641a30de1fSmrg    }
6651a30de1fSmrg
6661a30de1fSmrg    line += n, len -= n;
6671a30de1fSmrg    n = skip_until_char (line, len, '=');
6681a30de1fSmrg    if (n < 0) {
6691a30de1fSmrg	badmsg0 ("remove modifier = keysym");
6701a30de1fSmrg	return;
6711a30de1fSmrg    }
6721a30de1fSmrg
6731a30de1fSmrg    n++;
6741a30de1fSmrg    n += skip_space (line+n, len-n);
6751a30de1fSmrg    line += n, len -= n;
6761a30de1fSmrg
6771a30de1fSmrg    if (get_keysym_list (line, len, &n, &kslist) < 0)
6781a30de1fSmrg	return;
6791a30de1fSmrg    if (n == 0) {
6801a30de1fSmrg	badmsg0 ("remove modifier keysym list (empty)");
6811a30de1fSmrg	return;
6821a30de1fSmrg    }
6831a30de1fSmrg
6841a30de1fSmrg    /*
6851a30de1fSmrg     * unlike the add command, we have to now evaluate the keysyms
6861a30de1fSmrg     */
6871a30de1fSmrg
6881b983734Smrg    kclist = malloc (n * sizeof (KeyCode));
6891a30de1fSmrg    if (!kclist) {
6901a30de1fSmrg	badmsg ("attempt to allocate %ld byte keycode list",
6911a30de1fSmrg		(long) (n * sizeof (KeyCode)));
6921b983734Smrg	free (kslist);
6931a30de1fSmrg	return;
6941a30de1fSmrg    }
6951a30de1fSmrg
6961a30de1fSmrg    tot = n;
6971a30de1fSmrg    nc = 0;
6981a30de1fSmrg    for (i = 0; i < n; i++) {
6991a30de1fSmrg        int num_kcs;
7001a30de1fSmrg	KeyCode *kcs;
7011a30de1fSmrg	kcs = KeysymToKeycodes (dpy, kslist[i], &num_kcs);
7021a30de1fSmrg	if (num_kcs == 0) {
7031a30de1fSmrg	    char *tmpname = XKeysymToString (kslist[i]);
7041a30de1fSmrg	    badmsg ("keysym in remove modifier list '%s', no corresponding keycodes",
7051a30de1fSmrg		    tmpname ? tmpname : "?");
7061a30de1fSmrg	    continue;
7071a30de1fSmrg	}
7081a30de1fSmrg	if (verbose) {
7091a30de1fSmrg	    int j;
7101a30de1fSmrg	    char *tmpname = XKeysymToString (kslist[i]);
7111a30de1fSmrg	    printf ("! Keysym %s (0x%lx) corresponds to keycode(s)",
7121a30de1fSmrg		    tmpname ? tmpname : "?", (long) kslist[i]);
7131a30de1fSmrg	    for (j = 0; j < num_kcs; j++)
7141a30de1fSmrg		printf(" 0x%x", kcs[j]);
7151a30de1fSmrg	    printf("\n");
7161a30de1fSmrg	}
7171a30de1fSmrg	if (nc + num_kcs > tot) {
7181a30de1fSmrg	    tot = nc + num_kcs;
719bfce1ac2Smrg	    kclist = reallocfarray(kclist, tot, sizeof(KeyCode));
7201a30de1fSmrg	    if (!kclist) {
7211a30de1fSmrg		badmsg ("attempt to allocate %ld byte keycode list",
7221a30de1fSmrg			(long) (tot * sizeof (KeyCode)));
7231b983734Smrg		free (kslist);
7241a30de1fSmrg		return;
7251a30de1fSmrg	    }
7261a30de1fSmrg	}
7271a30de1fSmrg	while (--num_kcs >= 0)
7281a30de1fSmrg	    kclist[nc++] = *kcs++;		/* okay, add it to list */
7291a30de1fSmrg    }
7301a30de1fSmrg
7311b983734Smrg    free (kslist);		/* all done with it */
7321a30de1fSmrg
7331a30de1fSmrg    uop = AllocStruct (union op);
7341a30de1fSmrg    if (!uop) {
7351a30de1fSmrg	badmsg ("attempt to allocate %ld byte removemodifier opcode",
7361a30de1fSmrg		(long) sizeof (struct op_removemodifier));
737bfce1ac2Smrg	free(kclist);
7381a30de1fSmrg	return;
7391a30de1fSmrg    }
7401a30de1fSmrg    oprm = &uop->removemodifier;
7411a30de1fSmrg
7421a30de1fSmrg    oprm->type = doRemoveModifier;
7431a30de1fSmrg    oprm->modifier = modifier;
7441a30de1fSmrg    oprm->count = nc;
7451a30de1fSmrg    oprm->keycodes = kclist;
7461a30de1fSmrg
7471a30de1fSmrg    add_to_work_queue (uop);
7481a30de1fSmrg}
7491a30de1fSmrg
7501a30de1fSmrg#ifdef AUTO_ADD_REMOVE
7511a30de1fSmrg/*
7521a30de1fSmrg * make_remove - stick a single remove onto the queue
7531a30de1fSmrg */
7541a30de1fSmrgstatic void
7551a30de1fSmrgmake_remove(int modifier, KeyCode keycode)
7561a30de1fSmrg{
7571a30de1fSmrg    union op *uop;
7581a30de1fSmrg    struct op_removemodifier *oprm;
7591a30de1fSmrg
7601a30de1fSmrg    uop = AllocStruct (union op);
7611a30de1fSmrg    if (!uop) {
7621a30de1fSmrg	badmsg ("attempt to allocate %ld byte removemodifier opcode",
7631a30de1fSmrg		(long) sizeof (struct op_removemodifier));
7641a30de1fSmrg	return;
7651a30de1fSmrg    }
7661a30de1fSmrg    oprm = &uop->removemodifier;
7671a30de1fSmrg
7681a30de1fSmrg    oprm->type = doRemoveModifier;
7691a30de1fSmrg    oprm->modifier = modifier;
7701a30de1fSmrg    oprm->count = 1;
7711b983734Smrg    oprm->keycodes = malloc (sizeof (KeyCode));
7721a30de1fSmrg    if (!oprm->keycodes) {
7731a30de1fSmrg	badmsg ("attempt to allocate %ld byte KeyCode",
7741a30de1fSmrg		(long) sizeof (KeyCode));
7751b983734Smrg	free (oprm);
7761a30de1fSmrg	return;
7771a30de1fSmrg    }
7781a30de1fSmrg    oprm->keycodes[0] = keycode;
7791a30de1fSmrg
7801a30de1fSmrg    add_to_work_queue (uop);
7811a30de1fSmrg    return;
7821a30de1fSmrg}
7831a30de1fSmrg#endif /* AUTO_ADD_REMOVE */
7841a30de1fSmrg
7851a30de1fSmrg
7861a30de1fSmrg/*
7871a30de1fSmrg * do_clear - parse off lines of the form
7881a30de1fSmrg *
7891a30de1fSmrg *                 clear MODIFIER
7901a30de1fSmrg *                       ^
7911a30de1fSmrg */
7921a30de1fSmrg
7931a30de1fSmrgstatic void
7941a30de1fSmrgdo_clear(char *line, int len)
7951a30de1fSmrg{
7961a30de1fSmrg    int n;
7971a30de1fSmrg    int modifier;
7981a30de1fSmrg    union op *uop;
7991a30de1fSmrg    struct op_clearmodifier *opcm;
8001a30de1fSmrg
8011a30de1fSmrg    if (len < 4 || !line || *line == '\0') {  /* Lock minimum */
8021a30de1fSmrg	badmsg0 ("clear modifier input line");
8031a30de1fSmrg	return;
8041a30de1fSmrg    }
8051a30de1fSmrg
8061a30de1fSmrg    n = skip_chars (line, len);
8071a30de1fSmrg
8081a30de1fSmrg    modifier = parse_modifier (line, n);
8091a30de1fSmrg    if (modifier < 0) {
8101a30de1fSmrg	badmsgn ("clear modifier name '%s'", line, n);
8111a30de1fSmrg	return;
8121a30de1fSmrg    }
8131a30de1fSmrg    n += skip_space (line+n, len-n);
8141a30de1fSmrg    if (n != len) {
8151a30de1fSmrg	badmsgn ("extra argument '%s' to clear modifier", line+n, len-n);
8161a30de1fSmrg	/* okay to continue */
8171a30de1fSmrg    }
8181a30de1fSmrg
8191a30de1fSmrg    uop = AllocStruct (union op);
8201a30de1fSmrg    if (!uop) {
8211a30de1fSmrg	badmsg ("attempt to allocate %ld byte clearmodifier opcode",
8221a30de1fSmrg		(long) sizeof (struct op_clearmodifier));
8231a30de1fSmrg	return;
8241a30de1fSmrg    }
8251a30de1fSmrg    opcm = &uop->clearmodifier;
8261a30de1fSmrg
8271a30de1fSmrg    opcm->type = doClearModifier;
8281a30de1fSmrg    opcm->modifier = modifier;
8291a30de1fSmrg
8301a30de1fSmrg    add_to_work_queue (uop);
8311a30de1fSmrg}
8321a30de1fSmrg
8331a30de1fSmrg#ifndef HAVE_STRNCASECMP
8341a30de1fSmrgstatic int
8351a30de1fSmrgstrncasecmp(const char *a, const char *b, int n)
8361a30de1fSmrg{
8371a30de1fSmrg    int i;
8381a30de1fSmrg    int a1, b1;
8391a30de1fSmrg
8401a30de1fSmrg    for (i = 0; i < n; i++, a++, b++) {
8411a30de1fSmrg	if (!*a) return -1;
8421a30de1fSmrg	if (!*b) return 1;
8431a30de1fSmrg
8441a30de1fSmrg	if (*a != *b) {
8451a30de1fSmrg	    a1 = (isascii(*a) && isupper(*a)) ? tolower(*a) : *a;
8461a30de1fSmrg	    b1 = (isascii(*b) && isupper(*b)) ? tolower(*b) : *b;
8471a30de1fSmrg	    if (a1 != b1) return b1 - a1;
8481a30de1fSmrg	}
8491a30de1fSmrg    }
8501a30de1fSmrg    return 0;
8511a30de1fSmrg}
8521a30de1fSmrg#endif
8531a30de1fSmrg
8541a30de1fSmrg/*
8551a30de1fSmrg * do_pointer = get list of numbers of the form
8561a30de1fSmrg *
8571a30de1fSmrg *                 buttons = NUMBER ...
8581a30de1fSmrg *                         ^
8591a30de1fSmrg */
8601a30de1fSmrg
8611a30de1fSmrgstatic void
8621a30de1fSmrgdo_pointer(char *line, int len)
8631a30de1fSmrg{
8641a30de1fSmrg    int n;
8651a30de1fSmrg    int i;
8661a30de1fSmrg    unsigned long val;
8671a30de1fSmrg    union op *uop;
8681a30de1fSmrg    struct op_pointer *opp;
8691a30de1fSmrg    unsigned char buttons[MAXBUTTONCODES];
8701a30de1fSmrg    int nbuttons;
8711a30de1fSmrg    char *strval;
8721a30de1fSmrg    Bool ok;
8731a30de1fSmrg
8741a30de1fSmrg    if (len < 2 || !line || *line == '\0') {  /* =1 minimum */
8751a30de1fSmrg	badmsg0 ("buttons input line");
8761a30de1fSmrg	return;
8771a30de1fSmrg    }
8781a30de1fSmrg
8791a30de1fSmrg    nbuttons = XGetPointerMapping (dpy, buttons, MAXBUTTONCODES);
8801a30de1fSmrg
8811a30de1fSmrg    n = skip_space (line, len);
8821a30de1fSmrg    line += n, len -= n;
8831a30de1fSmrg
8841a30de1fSmrg    if (line[0] != '=') {
8851a30de1fSmrg	badmsg0 ("buttons pointer code list, missing equal sign");
8861a30de1fSmrg	return;
8871a30de1fSmrg    }
8881a30de1fSmrg
8891a30de1fSmrg    line++, len--;			/* skip = */
8901a30de1fSmrg    n = skip_space (line, len);
8911a30de1fSmrg    line += n, len -= n;
8921a30de1fSmrg
8931a30de1fSmrg    i = 0;
8941a30de1fSmrg    if (len < 7 || strncasecmp (line, "default", 7) != 0) {
8951a30de1fSmrg	while (len > 0) {
8961a30de1fSmrg	    n = skip_space (line, len);
8971a30de1fSmrg	    line += n, len -= n;
8981a30de1fSmrg	    if (line[0] == '\0') break;
8991a30de1fSmrg	    n = skip_word (line, len);
9001a30de1fSmrg	    if (n < 1) {
9011a30de1fSmrg		badmsg ("skip of word in buttons line:  %s", line);
9021a30de1fSmrg		return;
9031a30de1fSmrg	    }
9041a30de1fSmrg	    strval = copy_to_scratch(line, n);
9051b983734Smrg	    if (strval == NULL)
9061b983734Smrg		/* copy_to_scratch already printed error message */
9071b983734Smrg		return;
9081a30de1fSmrg	    ok = parse_number (strval, &val);
9091a30de1fSmrg	    if (!ok || val >= MAXBUTTONCODES) {
9101a30de1fSmrg		badmsg ("value %s given for buttons list", strval);
9111a30de1fSmrg		return;
9121a30de1fSmrg	    }
9131a30de1fSmrg	    buttons[i++] = (unsigned char) val;
9141a30de1fSmrg	    line += n, len -= n;
9151a30de1fSmrg	}
9161a30de1fSmrg    }
9171a30de1fSmrg
9181a30de1fSmrg    if (i > 0 && i != nbuttons) {
919bfce1ac2Smrg	if (i < nbuttons) {
920bfce1ac2Smrg	    fprintf (stderr,
921bfce1ac2Smrg		     "Warning: Only changing the first %d of %d buttons.\n",
922bfce1ac2Smrg		     i, nbuttons);
923bfce1ac2Smrg	}
924bfce1ac2Smrg	else {  /* i > nbuttons */
925bfce1ac2Smrg	    fprintf (stderr,
926bfce1ac2Smrg		     "Warning: Not changing %d extra buttons beyond %d.\n",
927bfce1ac2Smrg		     i - nbuttons, nbuttons);
928bfce1ac2Smrg	}
9291a30de1fSmrg	i = nbuttons;
9301a30de1fSmrg    }
9311a30de1fSmrg
9321a30de1fSmrg    uop = AllocStruct (union op);
9331a30de1fSmrg    if (!uop) {
9341a30de1fSmrg	badmsg ("attempt to allocate a %ld byte pointer opcode",
9351a30de1fSmrg		(long) sizeof (struct op_pointer));
9361a30de1fSmrg	return;
9371a30de1fSmrg    }
9381a30de1fSmrg    opp = &uop->pointer;
9391a30de1fSmrg
9401a30de1fSmrg    opp->type = doPointer;
9411a30de1fSmrg    opp->count = i;
9421a30de1fSmrg    for (i = 0; i < opp->count; i++) {
9431a30de1fSmrg	opp->button_codes[i] = buttons[i];
9441a30de1fSmrg    }
9451a30de1fSmrg
9461a30de1fSmrg    add_to_work_queue (uop);
9471a30de1fSmrg}
9481a30de1fSmrg
9491a30de1fSmrg
9501a30de1fSmrg/*
9511a30de1fSmrg * get_keysym_list - parses the rest of the line into a keysyms assumes
9521a30de1fSmrg * that the = sign has been parsed off but there may be leading whitespace
9531a30de1fSmrg *
9541a30de1fSmrg *                 keysym ...
9551a30de1fSmrg *                 ^
9561a30de1fSmrg *
9571a30de1fSmrg * this involves getting the word containing the keysym, checking its range,
9581a30de1fSmrg * and adding it to the list.
9591a30de1fSmrg */
9601a30de1fSmrg
9611a30de1fSmrgstatic int
9621a30de1fSmrgget_keysym_list(const char *line, int len, int *np, KeySym **kslistp)
9631a30de1fSmrg{
9641a30de1fSmrg    int havesofar, maxcanhave;
9651a30de1fSmrg    KeySym *keysymlist;
9661a30de1fSmrg
9671a30de1fSmrg    *np = 0;
9681a30de1fSmrg    *kslistp = NULL;
9691a30de1fSmrg
9701a30de1fSmrg    if (len == 0) return (0);		/* empty list */
9711a30de1fSmrg
9721a30de1fSmrg    havesofar = 0;
9731a30de1fSmrg    maxcanhave = 4;			/* most lists are small */
9741b983734Smrg    keysymlist = malloc (maxcanhave * sizeof (KeySym));
9751a30de1fSmrg    if (!keysymlist) {
9761a30de1fSmrg	badmsg ("attempt to allocate %ld byte initial keysymlist",
9771a30de1fSmrg		(long) (maxcanhave * sizeof (KeySym)));
9781a30de1fSmrg	return (-1);
9791a30de1fSmrg    }
9801a30de1fSmrg
9811a30de1fSmrg    while (len > 0) {
9821a30de1fSmrg	KeySym keysym;
9831a30de1fSmrg	int n;
9841a30de1fSmrg	char *tmpname;
9851a30de1fSmrg	Bool ok;
9861a30de1fSmrg
9871a30de1fSmrg	n = skip_space (line, len);
9881a30de1fSmrg	line += n, len -= n;
9891a30de1fSmrg
9901a30de1fSmrg	n = skip_chars (line, len);
9911a30de1fSmrg	if (n < 0) {
9921a30de1fSmrg	    badmsg0 ("keysym name list");
9931a30de1fSmrg	    free(keysymlist);
9941a30de1fSmrg	    return (-1);
9951a30de1fSmrg	}
9961a30de1fSmrg
9971a30de1fSmrg	ok = parse_keysym (line, n, &tmpname, &keysym);
9981a30de1fSmrg	line += n, len -= n;
9991a30de1fSmrg	if (!ok) {
10001a30de1fSmrg	    badmsg ("keysym name '%s' in keysym list", tmpname);
10011a30de1fSmrg	    /* do NOT return here, look for others */
10021a30de1fSmrg	    continue;
10031a30de1fSmrg	}
10041a30de1fSmrg
10051a30de1fSmrg	/*
10061a30de1fSmrg	 * Do NOT test to see if the keysym translates to a keycode or you
10071a30de1fSmrg	 * won't be able to assign new ones....
10081a30de1fSmrg	 */
10091a30de1fSmrg
10101a30de1fSmrg	/* grow the list bigger if necessary */
10111a30de1fSmrg	if (havesofar >= maxcanhave) {
10121a30de1fSmrg	    maxcanhave *= 2;
1013bfce1ac2Smrg	    keysymlist = reallocfarray(keysymlist, maxcanhave, sizeof(KeySym));
10141a30de1fSmrg	    if (!keysymlist) {
10151a30de1fSmrg		badmsg ("attempt to grow keysym list to %ld bytes",
10161a30de1fSmrg			(long) (maxcanhave * sizeof (KeySym)));
10171a30de1fSmrg		return (-1);
10181a30de1fSmrg	    }
10191a30de1fSmrg	}
10201a30de1fSmrg
10211a30de1fSmrg	/* and add it to the list */
10221a30de1fSmrg	keysymlist[havesofar++] = keysym;
10231a30de1fSmrg    }
10241a30de1fSmrg
10251a30de1fSmrg    *kslistp = keysymlist;
10261a30de1fSmrg    *np = havesofar;
10271a30de1fSmrg    return (0);
10281a30de1fSmrg}
10291a30de1fSmrg
10301a30de1fSmrg
10311a30de1fSmrg#ifdef later
10321a30de1fSmrg/*
10331a30de1fSmrg * check_special_keys - run through list of keysyms and generate "add" or
10341a30de1fSmrg * "remove" commands for for any of the key syms that appear in the modifier
10351a30de1fSmrg * list.  this involves running down the modifier map which is an array of
10361a30de1fSmrg * 8 by map->max_keypermod keycodes.
10371a30de1fSmrg */
10381a30de1fSmrg
10391a30de1fSmrgstatic void
10401a30de1fSmrgcheck_special_keys(KeyCode keycode, int n, KeySym *kslist)
10411a30de1fSmrg{
10421a30de1fSmrg    int i;				/* iterator variable */
10431a30de1fSmrg    KeyCode *kcp;			/* keycode pointer */
10441a30de1fSmrg
10451a30de1fSmrg    /*
10461a30de1fSmrg     * walk the modifiermap array.  since its dimensions are not known at
10471a30de1fSmrg     * compile time, we have to walk it by hand instead of indexing.  this
10481a30de1fSmrg     * is why it is initialized outside the loop, but incremented inside the
10491a30de1fSmrg     * second loop.
10501a30de1fSmrg     */
10511a30de1fSmrg
10521a30de1fSmrg    kcp = map->modifiermap;		/* start at beginning and iterate */
10531a30de1fSmrg    for (i = 0; i < 8; i++) {		/* there are 8 modifier keys */
10541a30de1fSmrg	int j;
10551a30de1fSmrg
10561a30de1fSmrg	for (j = 0; j < map->max_keypermod; j++, kcp++) {
10571a30de1fSmrg	    KeySym keysym;
10581a30de1fSmrg	    int k;
10591a30de1fSmrg
10601a30de1fSmrg	    if (!*kcp) continue;	/* only non-zero entries significant */
10611a30de1fSmrg
10621a30de1fSmrg	    /*
10631a30de1fSmrg	     * check to see if the target keycode is already a modifier; if so,
10641a30de1fSmrg	     * then we have to remove it
10651a30de1fSmrg	     */
10661a30de1fSmrg	    if (keycode == *kcp) {
10671a30de1fSmrg		make_remove (i, keycode);
10681a30de1fSmrg	    }
10691a30de1fSmrg
10701a30de1fSmrg	    /*
10711a30de1fSmrg	     * now, check to see if any of the keysyms map to keycodes
10721a30de1fSmrg	     * that are in the modifier list
10731a30de1fSmrg	     */
10741a30de1fSmrg	    for (k = 0; k < n; k++) {
10751a30de1fSmrg		KeyCodes kc;
10761a30de1fSmrg
10771a30de1fSmrg		kc = XKeysymToKeycode (dpy, kslist[k]);
10781a30de1fSmrg		if (kc == *kcp) {	/* yup, found one */
10791a30de1fSmrg		    /*
10801a30de1fSmrg		     * have to generate a remove of the CURRENT keycode
10811a30de1fSmrg		     * and then an add of the new KEYCODE
10821a30de1fSmrg		     */
10831a30de1fSmrg		    make_remove (i, kc);  /* modifier, keycode */
10841a30de1fSmrg		    make_add (i, kslist[k]);  /* modifier, keysym */
10851a30de1fSmrg		}
10861a30de1fSmrg	    }
10871a30de1fSmrg	}
10881a30de1fSmrg    }
10891a30de1fSmrg    return;
10901a30de1fSmrg}
10911a30de1fSmrg#endif
10921a30de1fSmrg
10931a30de1fSmrg/*
10941a30de1fSmrg * print_work_queue - disassemble the work queue and print it on stdout
10951a30de1fSmrg */
10961a30de1fSmrg
10971a30de1fSmrgvoid
10981a30de1fSmrgprint_work_queue(void)
10991a30de1fSmrg{
11001a30de1fSmrg    union op *op;
11011a30de1fSmrg
11021a30de1fSmrg    printf ("! dump of work queue\n");
11031a30de1fSmrg    for (op = work_queue.head; op; op = op->generic.next) {
11041a30de1fSmrg	print_opcode (op);
11051a30de1fSmrg    }
11061a30de1fSmrg    return;
11071a30de1fSmrg}
11081a30de1fSmrg
11091a30de1fSmrgstatic void
11101a30de1fSmrgprint_opcode(union op *op)
11111a30de1fSmrg{
11121a30de1fSmrg    int i;
11131a30de1fSmrg
11141a30de1fSmrg    printf ("        ");
11151a30de1fSmrg    switch (op->generic.type) {
11161a30de1fSmrg      case doKeycode:
11171a30de1fSmrg	if (op->keycode.target_keycode)
11181a30de1fSmrg	    printf ("keycode 0x%lx =", (long) op->keycode.target_keycode);
11191a30de1fSmrg	else
11201a30de1fSmrg	    printf ("keycode any =");
11211a30de1fSmrg	for (i = 0; i < op->keycode.count; i++) {
11221a30de1fSmrg	    char *name = XKeysymToString (op->keycode.keysyms[i]);
11231a30de1fSmrg
11241a30de1fSmrg	    printf (" %s", name ? name : "BADKEYSYM");
11251a30de1fSmrg	}
11261a30de1fSmrg	printf ("\n");
11271a30de1fSmrg	break;
11281a30de1fSmrg      case doAddModifier:
11291a30de1fSmrg	printf ("add %s =", modifier_table[op->addmodifier.modifier].name);
11301a30de1fSmrg	for (i = 0; i < op->addmodifier.count; i++) {
11311a30de1fSmrg	    char *name = XKeysymToString (op->addmodifier.keysyms[i]);
11321a30de1fSmrg	    printf (" %s", name ? name : "BADKEYSYM");
11331a30de1fSmrg	}
11341a30de1fSmrg	printf ("\n");
11351a30de1fSmrg	break;
11361a30de1fSmrg      case doRemoveModifier:
11371a30de1fSmrg	printf ("remove %s = ",
11381a30de1fSmrg		modifier_table[op->removemodifier.modifier].name);
11391a30de1fSmrg	for (i = 0; i < op->removemodifier.count; i++) {
11401a30de1fSmrg	    printf (" 0x%lx", (long) op->removemodifier.keycodes[i]);
11411a30de1fSmrg	}
11421a30de1fSmrg	printf ("\n");
11431a30de1fSmrg	break;
11441a30de1fSmrg      case doClearModifier:
11451a30de1fSmrg	printf ("clear %s\n", modifier_table[op->clearmodifier.modifier].name);
11461a30de1fSmrg	break;
11471a30de1fSmrg      case doPointer:
11481a30de1fSmrg	printf ("pointer = ");
11491a30de1fSmrg	if (op->pointer.count == 0)
11501a30de1fSmrg	    printf(" default");
11511a30de1fSmrg	else for (i=0; i < op->pointer.count; i++)
11521a30de1fSmrg	    printf(" %d", op->pointer.button_codes[i]);
11531a30de1fSmrg	printf ("\n");
11541a30de1fSmrg	break;
11551a30de1fSmrg      default:
11561a30de1fSmrg	printf ("! unknown opcode %d\n", op->generic.type);
11571a30de1fSmrg	break;
11581a30de1fSmrg    }				/* end switch */
11591a30de1fSmrg    return;
11601a30de1fSmrg}
11611a30de1fSmrg
11621a30de1fSmrg/*
11631a30de1fSmrg * execute_work_queue - do the real meat and potatoes now that we know what
11641a30de1fSmrg * we need to do and that all of the input is correct.
11651a30de1fSmrg */
11661a30de1fSmrgstatic int exec_keycode ( struct op_keycode *opk );
11671a30de1fSmrgstatic int exec_add ( struct op_addmodifier *opam );
11681a30de1fSmrgstatic int exec_remove ( struct op_removemodifier *oprm );
11691a30de1fSmrgstatic int exec_clear ( struct op_clearmodifier *opcm );
11701a30de1fSmrgstatic int exec_pointer ( struct op_pointer *opp );
11711a30de1fSmrg
11721a30de1fSmrg
11731a30de1fSmrgint
11741a30de1fSmrgexecute_work_queue (void)
11751a30de1fSmrg{
11761a30de1fSmrg    union op *op;
11771a30de1fSmrg    int errors;
11781a30de1fSmrg    Bool update_map = False;
11791a30de1fSmrg    int dosync;
11801a30de1fSmrg
11811a30de1fSmrg    if (verbose) {
11821a30de1fSmrg	printf ("!\n");
11831a30de1fSmrg	printf ("! executing work queue\n");
11841a30de1fSmrg	printf ("!\n");
11851a30de1fSmrg    }
11861a30de1fSmrg
11871a30de1fSmrg    errors = 0;
11881a30de1fSmrg    dosync = 0;
11891a30de1fSmrg
11901a30de1fSmrg    for (op = work_queue.head; op; op = op->generic.next) {
11911a30de1fSmrg	if (verbose) print_opcode (op);
11921a30de1fSmrg
11931a30de1fSmrg	/* check to see if we have to update the keyboard mapping */
11941a30de1fSmrg	if (dosync &&
11951a30de1fSmrg	    (dosync < 0 ||
11961a30de1fSmrg	     op->generic.type != doKeycode ||
11971a30de1fSmrg	     !op->keycode.target_keycode)) {
11981a30de1fSmrg	    XSync (dpy, 0);
11991a30de1fSmrg	    while (XEventsQueued (dpy, QueuedAlready) > 0) {
12001a30de1fSmrg		XEvent event;
12011a30de1fSmrg		XNextEvent (dpy, &event);
12021a30de1fSmrg		if (event.type == MappingNotify) {
12031a30de1fSmrg		    /* read all MappingNotify events */
12041a30de1fSmrg		    while (XCheckTypedEvent (dpy, MappingNotify, &event)) ;
12051a30de1fSmrg		    XRefreshKeyboardMapping (&event.xmapping);
12061a30de1fSmrg		} else {
12071a30de1fSmrg		    fprintf (stderr, "%s:  unknown event %ld\n",
12081a30de1fSmrg		    	     ProgramName, (long) event.type);
12091a30de1fSmrg		}
12101a30de1fSmrg	    }
12111a30de1fSmrg	}
12121a30de1fSmrg	dosync = 0;
12131a30de1fSmrg	switch (op->generic.type) {
12141a30de1fSmrg	  case doKeycode:
12151a30de1fSmrg	    if (exec_keycode (&op->keycode) < 0) errors++;
12161a30de1fSmrg	    if (op->keycode.target_keycode)
12171a30de1fSmrg		dosync = 1;
12181a30de1fSmrg	    else
12191a30de1fSmrg		dosync = -1;
12201a30de1fSmrg	    break;
12211a30de1fSmrg	  case doAddModifier:
12221a30de1fSmrg	    if (exec_add (&op->addmodifier) < 0) errors++;
12231a30de1fSmrg	    else update_map = True;
12241a30de1fSmrg	    break;
12251a30de1fSmrg	  case doRemoveModifier:
12261a30de1fSmrg	    if (exec_remove (&op->removemodifier) < 0) errors++;
12271a30de1fSmrg	    else update_map = True;
12281a30de1fSmrg	    break;
12291a30de1fSmrg	  case doClearModifier:
12301a30de1fSmrg	    if (exec_clear (&op->clearmodifier) < 0) errors++;
12311a30de1fSmrg	    else update_map = True;
12321a30de1fSmrg	    break;
12331a30de1fSmrg	  case doPointer:
12341a30de1fSmrg	    if (exec_pointer (&op->pointer) < 0) errors++;
12351a30de1fSmrg	    break;
12361a30de1fSmrg	  default:
12371a30de1fSmrg	    fprintf (stderr, "%s:  unknown opcode %d\n",
12381a30de1fSmrg		     ProgramName, op->generic.type);
12391a30de1fSmrg	    break;
12401a30de1fSmrg	}
12411a30de1fSmrg    }
12421a30de1fSmrg
12431a30de1fSmrg    if (update_map) {
12441a30de1fSmrg	if (UpdateModifierMapping (map) < 0) errors++;
12451a30de1fSmrg    }
12461a30de1fSmrg
12471a30de1fSmrg    return (errors > 0 ? -1 : 0);
12481a30de1fSmrg}
12491a30de1fSmrg
12501a30de1fSmrgstatic int
12511a30de1fSmrgexec_keycode(struct op_keycode *opk)
12521a30de1fSmrg{
12531a30de1fSmrg    if (!opk->target_keycode) {
12541a30de1fSmrg	int i, j;
12551a30de1fSmrg	KeyCode free;
12561a30de1fSmrg	if (!opk->count)
12571a30de1fSmrg	    return (0);
12581a30de1fSmrg	free = 0;
12591a30de1fSmrg	for (i = min_keycode; i <= max_keycode; i++) {
12601a30de1fSmrg	    for (j = 0; j < opk->count; j++) {
12611a30de1fSmrg		if (XKeycodeToKeysym(dpy, (KeyCode) i, j) != opk->keysyms[j])
12621a30de1fSmrg		    break;
12631a30de1fSmrg	    }
12641a30de1fSmrg	    if (j >= opk->count)
12651a30de1fSmrg		return (0);
12661a30de1fSmrg	    if (free)
12671a30de1fSmrg		continue;
12681a30de1fSmrg	    for (j = 0; j < 8; j++) {
12691a30de1fSmrg		if (XKeycodeToKeysym(dpy, (KeyCode) i, j) != None)
12701a30de1fSmrg		    break;
12711a30de1fSmrg	    }
12721a30de1fSmrg	    if (j >= 8)
12731a30de1fSmrg		free = i;
12741a30de1fSmrg	}
12751a30de1fSmrg	if (!free) {
12761a30de1fSmrg	    fprintf(stderr, "%s: no available keycode for assignment\n",
12771a30de1fSmrg		    ProgramName);
12781a30de1fSmrg	    return (-1);
12791a30de1fSmrg	}
12801a30de1fSmrg	XChangeKeyboardMapping (dpy, free, opk->count, opk->keysyms, 1);
12811a30de1fSmrg    } else if (opk->count == 0) {
12821a30de1fSmrg	KeySym dummy = NoSymbol;
12831a30de1fSmrg	XChangeKeyboardMapping (dpy, opk->target_keycode, 1,
12841a30de1fSmrg				&dummy, 1);
12851a30de1fSmrg    } else {
12861a30de1fSmrg	XChangeKeyboardMapping (dpy, opk->target_keycode, opk->count,
12871a30de1fSmrg				opk->keysyms, 1);
12881a30de1fSmrg    }
12891a30de1fSmrg    return (0);
12901a30de1fSmrg}
12911a30de1fSmrg
12921a30de1fSmrgstatic int
12931a30de1fSmrgexec_add(struct op_addmodifier *opam)
12941a30de1fSmrg{
12951a30de1fSmrg    int i;
12961a30de1fSmrg    int status;
12971a30de1fSmrg
12981a30de1fSmrg    status = 0;
12991a30de1fSmrg    for (i = 0; i < opam->count; i++) {
13001a30de1fSmrg	int num_kcs;
13011a30de1fSmrg	KeyCode *kcs;
13021a30de1fSmrg
13031a30de1fSmrg	kcs = KeysymToKeycodes (dpy, opam->keysyms[i], &num_kcs);
13041a30de1fSmrg	if (num_kcs == 0)
13051a30de1fSmrg	    status = -1;
13061a30de1fSmrg	while (--num_kcs >= 0) {
13071a30de1fSmrg	    if (AddModifier (&map, *kcs++, opam->modifier) < 0)
13081a30de1fSmrg		status = -1;
13091a30de1fSmrg	}
13101a30de1fSmrg    }
13111a30de1fSmrg    return (status);
13121a30de1fSmrg}
13131a30de1fSmrg
13141a30de1fSmrgstatic int
13151a30de1fSmrgexec_remove(struct op_removemodifier *oprm)
13161a30de1fSmrg{
13171a30de1fSmrg    int i;
13181a30de1fSmrg    int status;
13191a30de1fSmrg
13201a30de1fSmrg    status = 0;
13211a30de1fSmrg    for (i = 0; i < oprm->count; i++) {
13221a30de1fSmrg	if (RemoveModifier (&map, oprm->keycodes[i], oprm->modifier) < 0)
13231a30de1fSmrg	  status = -1;
13241a30de1fSmrg    }
13251a30de1fSmrg    return (status);
13261a30de1fSmrg}
13271a30de1fSmrg
13281a30de1fSmrgstatic int
13291a30de1fSmrgexec_clear(struct op_clearmodifier *opcm)
13301a30de1fSmrg{
13311a30de1fSmrg    return (ClearModifier (&map, opcm->modifier));
13321a30de1fSmrg}
13331a30de1fSmrg
13341a30de1fSmrg
13351a30de1fSmrgstatic int
13361a30de1fSmrgexec_pointer(struct op_pointer *opp)
13371a30de1fSmrg{
13381a30de1fSmrg    return (SetPointerMap (opp->button_codes, opp->count));
13391a30de1fSmrg}
13401a30de1fSmrg
13411a30de1fSmrgvoid
13421a30de1fSmrgprint_modifier_map(void)
13431a30de1fSmrg{
13441a30de1fSmrg    PrintModifierMapping (map, stdout);
13451a30de1fSmrg    return;
13461a30de1fSmrg}
13471a30de1fSmrg
13481a30de1fSmrgvoid
13491a30de1fSmrgprint_key_table(Bool exprs)
13501a30de1fSmrg{
13511a30de1fSmrg    PrintKeyTable (exprs, stdout);
13521a30de1fSmrg    return;
13531a30de1fSmrg}
13541a30de1fSmrg
13551a30de1fSmrgvoid
13561a30de1fSmrgprint_pointer_map(void)
13571a30de1fSmrg{
13581a30de1fSmrg    PrintPointerMap (stdout);
13591a30de1fSmrg    return;
13601a30de1fSmrg}
13611a30de1fSmrg
13621a30de1fSmrg
1363