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