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*/
28a733a5bfSmrg
29a733a5bfSmrg#ifdef HAVE_CONFIG_H
30a733a5bfSmrg# include "config.h"
31a733a5bfSmrg#endif
321a30de1fSmrg
331a30de1fSmrg#include <X11/Xos.h>
341a30de1fSmrg#include <X11/Xlib.h>
351a30de1fSmrg#include <stdio.h>
361a30de1fSmrg#include <stdlib.h>
371a30de1fSmrg#include <ctype.h>
38a733a5bfSmrg#include <stdarg.h>
391a30de1fSmrg#include "xmodmap.h"
401a30de1fSmrg
411a30de1fSmrgconst char *ProgramName;
421a30de1fSmrgDisplay *dpy = NULL;
431a30de1fSmrgint min_keycode, max_keycode;
441a30de1fSmrgBool verbose = False;
451a30de1fSmrgBool dontExecute = False;
461a30de1fSmrg
47a733a5bfSmrgvoid
48a733a5bfSmrg_X_NORETURN
491a30de1fSmrgExit(int status)
501a30de1fSmrg{
511a30de1fSmrg    if (dpy) {
521a30de1fSmrg	XCloseDisplay (dpy);
531a30de1fSmrg	dpy = NULL;
541a30de1fSmrg    }
551a30de1fSmrg    exit (status);
561a30de1fSmrg}
571a30de1fSmrg
58a733a5bfSmrgstatic void _X_NORETURN
59a733a5bfSmrgFatalError(const char *message)
60a733a5bfSmrg{
61a733a5bfSmrg    fprintf(stderr, "%s: %s\n", ProgramName, message);
62a733a5bfSmrg    Exit(-1);
63a733a5bfSmrg}
64a733a5bfSmrg
65a733a5bfSmrg#ifndef HAVE_ASPRINTF
66a733a5bfSmrg/* sprintf variant found in newer libc's which allocates string to print to */
67a733a5bfSmrgstatic int _X_ATTRIBUTE_PRINTF(2,3)
68a733a5bfSmrgasprintf(char ** ret, const char *format, ...)
691a30de1fSmrg{
70a733a5bfSmrg    char buf[256];
71a733a5bfSmrg    int len;
72a733a5bfSmrg    va_list ap;
73a733a5bfSmrg
74a733a5bfSmrg    va_start(ap, format);
75a733a5bfSmrg    len = vsnprintf(buf, sizeof(buf), format, ap);
76a733a5bfSmrg    va_end(ap);
77a733a5bfSmrg
78a733a5bfSmrg    if (len < 0)
79a733a5bfSmrg	return -1;
80a733a5bfSmrg
81a733a5bfSmrg    if (len < sizeof(buf))
82a733a5bfSmrg    {
83a733a5bfSmrg	*ret = strdup(buf);
841a30de1fSmrg    }
85a733a5bfSmrg    else
86a733a5bfSmrg    {
87a733a5bfSmrg	*ret = malloc(len + 1); /* snprintf doesn't count trailing '\0' */
88a733a5bfSmrg	if (*ret != NULL)
89a733a5bfSmrg	{
90a733a5bfSmrg	    va_start(ap, format);
91a733a5bfSmrg	    len = vsnprintf(*ret, len + 1, format, ap);
92a733a5bfSmrg	    va_end(ap);
93a733a5bfSmrg	    if (len < 0) {
94a733a5bfSmrg		free(*ret);
95a733a5bfSmrg		*ret = NULL;
96a733a5bfSmrg	    }
97a733a5bfSmrg	}
98a733a5bfSmrg    }
99a733a5bfSmrg
100a733a5bfSmrg    if (*ret == NULL)
101a733a5bfSmrg	return -1;
102a733a5bfSmrg
103a733a5bfSmrg    return len;
1041a30de1fSmrg}
105a733a5bfSmrg#endif /* HAVE_ASPRINTF */
1061a30de1fSmrg
1071a30de1fSmrgstatic const char help_message[] =
1081a30de1fSmrg"\nwhere options include:\n"
1091a30de1fSmrg"    -display host:dpy            X server to use\n"
1101a30de1fSmrg"    -verbose, -quiet             turn logging on or off\n"
1111a30de1fSmrg"    -n                           don't execute changes, just show like make\n"
1121a30de1fSmrg"    -e expression                execute string\n"
1131a30de1fSmrg"    -pm                          print modifier map\n"
1141a30de1fSmrg"    -pk                          print keymap table\n"
1151a30de1fSmrg"    -pke                         print keymap table as expressions\n"
1161a30de1fSmrg"    -pp                          print pointer map\n"
117a733a5bfSmrg"    -help                        print this usage message\n"
1181a30de1fSmrg"    -grammar                     print out short help on allowable input\n"
1191b983734Smrg"    -version                     print program version\n"
1201a30de1fSmrg"    -                            read standard input\n"
1211a30de1fSmrg"\n";
1221a30de1fSmrg
1231a30de1fSmrg
1241b983734Smrgstatic void
1251b983734Smrg_X_NORETURN _X_COLD
126a733a5bfSmrgusage(int exitcode)
1271a30de1fSmrg{
1281a30de1fSmrg    fprintf (stderr, "usage:  %s [-options ...] [filename]\n", ProgramName);
1291a30de1fSmrg    fprintf (stderr, "%s\n", help_message);
130a733a5bfSmrg    Exit (exitcode);
1311a30de1fSmrg}
1321a30de1fSmrg
1331b983734Smrgstatic void
1341b983734Smrg_X_NORETURN _X_COLD
1351b983734Smrgmissing_arg(const char *arg)
1361b983734Smrg{
1371b983734Smrg    fprintf (stderr, "%s: %s requires an argument\n\n", ProgramName, arg);
1381b983734Smrg    usage(1);
1391b983734Smrg}
1401b983734Smrg
1411b983734Smrgstatic void
1421b983734Smrg_X_NORETURN _X_COLD
1431b983734Smrgunknown_arg(const char *arg)
1441b983734Smrg{
1451b983734Smrg    fprintf (stderr, "%s: unrecognized argument %s\n\n", ProgramName, arg);
1461b983734Smrg    usage(1);
1471b983734Smrg}
1481b983734Smrg
1491a30de1fSmrgstatic const char grammar_message[] =
1501a30de1fSmrg"    pointer = default              reset pointer buttons to default\n"
1511a30de1fSmrg"    pointer = NUMBER ...           set pointer button codes\n"
1521a30de1fSmrg"    keycode NUMBER = [KEYSYM ...]  map keycode to given keysyms\n"
1531a30de1fSmrg"    keysym KEYSYM = [KEYSYM ...]   look up keysym and do a keycode operation\n"
1541a30de1fSmrg"    clear MODIFIER                 remove all keys for this modifier\n"
1551a30de1fSmrg"    add MODIFIER = KEYSYM ...      add the keysyms to the modifier\n"
1561a30de1fSmrg"    remove MODIFIER = KEYSYM ...   remove the keysyms from the modifier\n"
1571a30de1fSmrg"\n"
1581a30de1fSmrg"where NUMBER is a decimal, octal, or hex constant; KEYSYM is a valid\n"
1591a30de1fSmrg"Key Symbol name; and MODIFIER is one of the eight modifier names:  Shift,\n"
1601a30de1fSmrg"Lock, Control, Mod1, Mod2, Mod3, Mod4, or Mod5.  Lines beginning with\n"
1611a30de1fSmrg"an exclamation mark (!) are taken as comments.  Case is significant except\n"
1621a30de1fSmrg"for MODIFIER names.\n"
1631a30de1fSmrg"\n"
1641a30de1fSmrg"Keysyms on the left hand side of the = sign are looked up before any changes\n"
1651a30de1fSmrg"are made; keysyms on the right are looked up after all of those on the left\n"
1661a30de1fSmrg"have been resolved.  This makes it possible to swap modifier keys.\n"
1671a30de1fSmrg"\n";
1681a30de1fSmrg
1691a30de1fSmrg
1701a30de1fSmrgstatic void
171a733a5bfSmrg_X_NORETURN
1721a30de1fSmrggrammar_usage(void)
1731a30de1fSmrg{
1741a30de1fSmrg    fprintf (stderr, "%s accepts the following input expressions:\n\n",
1751a30de1fSmrg	     ProgramName);
1761a30de1fSmrg    fprintf (stderr, "%s\n", grammar_message);
1771a30de1fSmrg    Exit (0);
1781a30de1fSmrg}
1791a30de1fSmrg
1801a30de1fSmrgint parse_errors = 0;
1811a30de1fSmrg
1821a30de1fSmrgint
1831a30de1fSmrgmain(int argc, char *argv[])
1841a30de1fSmrg{
1851a30de1fSmrg    int i;
1861a30de1fSmrg    char *displayname = NULL;
1871a30de1fSmrg    int status;
1881a30de1fSmrg    Bool printMap = False;
1891a30de1fSmrg    Bool printKeyTable = False;
1901a30de1fSmrg    Bool printKeyTableExprs = False;
1911a30de1fSmrg    Bool printPointerMap = False;
1921a30de1fSmrg    Bool didAnything = False;
1931a30de1fSmrg
1941a30de1fSmrg    ProgramName = argv[0];
1951a30de1fSmrg
1961a30de1fSmrg    /*
1971a30de1fSmrg     * scan the arg list once to find out which display to use
1981a30de1fSmrg     */
1991a30de1fSmrg
2001a30de1fSmrg    for (i = 1; i < argc; i++) {
201a733a5bfSmrg	const char *arg = argv[i];
202a733a5bfSmrg
203a733a5bfSmrg	if (arg[0] == '-') {
204a733a5bfSmrg	    switch (arg[1]) {
205a733a5bfSmrg	    case 'd':			/* -display host:dpy */
2061b983734Smrg		if (++i >= argc) missing_arg(arg);
207a733a5bfSmrg		displayname = argv[i];
208a733a5bfSmrg		break;
209a733a5bfSmrg	    case 'g':			/* -grammar */
210a733a5bfSmrg		grammar_usage ();
211a733a5bfSmrg		/*NOTREACHED*/
212a733a5bfSmrg	    case 'h':			/* -help */
213a733a5bfSmrg	    case '?':
214a733a5bfSmrg		usage(0);
2151b983734Smrg	    case 'v':
2161b983734Smrg		if (strcmp(arg, "-version") == 0) {
2171b983734Smrg		    puts(PACKAGE_STRING);
2181b983734Smrg		    exit(0);
2191b983734Smrg		}
220a733a5bfSmrg	    }
2211a30de1fSmrg	}
2221a30de1fSmrg    }
2231a30de1fSmrg
2241a30de1fSmrg    dpy = XOpenDisplay (displayname);
2251a30de1fSmrg    if (!dpy) {
2261a30de1fSmrg	fprintf (stderr, "%s:  unable to open display '%s'\n",
2271a30de1fSmrg		 ProgramName, XDisplayName (displayname));
2281a30de1fSmrg	Exit (1);
2291a30de1fSmrg    }
2301a30de1fSmrg
2311a30de1fSmrg    XDisplayKeycodes (dpy, &min_keycode, &max_keycode);
2321a30de1fSmrg
2331a30de1fSmrg    initialize_map ();
2341a30de1fSmrg
2351a30de1fSmrg    /*
2361a30de1fSmrg     * scan the arg list again to do the actual work (since it requires
2371a30de1fSmrg     * the display being open.
2381a30de1fSmrg     */
2391a30de1fSmrg
2401a30de1fSmrg    for (i = 1; i < argc; i++) {
2411a30de1fSmrg	char *arg = argv[i];
2421a30de1fSmrg
2431a30de1fSmrg	if (arg[0] == '-') {
2441a30de1fSmrg	    switch (arg[1]) {
2451a30de1fSmrg	      case 'd':			/* -display host:dpy */
2461a30de1fSmrg		++i;			/* handled above */
2471a30de1fSmrg		continue;
2481a30de1fSmrg	      case 'v':			/* -verbose */
2491a30de1fSmrg		verbose = True;
2501a30de1fSmrg		continue;
2511a30de1fSmrg	      case 'q':			/* -quiet */
2521a30de1fSmrg		verbose = False;
2531a30de1fSmrg		continue;
2541a30de1fSmrg	      case 'n':			/* -n (like make) */
2551a30de1fSmrg		dontExecute = True;
2561a30de1fSmrg		continue;
2571a30de1fSmrg	      case 'e':			/* -e expression */
2581a30de1fSmrg		didAnything = True;
2591b983734Smrg		if (++i >= argc) missing_arg(arg);
2601a30de1fSmrg		process_line (argv[i]);
2611a30de1fSmrg		continue;
2621a30de1fSmrg	      case 'p':			/* -p... */
2631a30de1fSmrg		switch (arg[2]) {
2641a30de1fSmrg		  case '\0':
2651a30de1fSmrg		  case 'm':		/* -pm */
2661a30de1fSmrg		    printMap = True;
2671a30de1fSmrg		    break;
2681a30de1fSmrg		  case 'k':		/* -pk, -pke */
2691a30de1fSmrg		    switch (arg[3]) {
2701a30de1fSmrg		    case '\0':
2711a30de1fSmrg			printKeyTable = True;
2721a30de1fSmrg			break;
2731a30de1fSmrg		    case 'e':
2741a30de1fSmrg			printKeyTableExprs = True;
2751a30de1fSmrg			break;
2761a30de1fSmrg		    default:
2771b983734Smrg			unknown_arg(arg);
2781a30de1fSmrg		    }
2791a30de1fSmrg		    break;
2801a30de1fSmrg		  case 'p':		/* -pp */
2811a30de1fSmrg		    printPointerMap = True;
2821a30de1fSmrg		    break;
2831a30de1fSmrg		  default:
2841b983734Smrg		    unknown_arg(arg);
2851a30de1fSmrg		    /* NOTREACHED */
2861a30de1fSmrg		}
2871a30de1fSmrg		didAnything = True;
2881a30de1fSmrg		continue;
2891a30de1fSmrg	      case 'g':			/* -grammar */
2901a30de1fSmrg		grammar_usage ();
2911a30de1fSmrg		/*NOTREACHED*/
2921a30de1fSmrg	      case '\0':		/* - (use standard input) */
2931a30de1fSmrg		didAnything = True;
2941a30de1fSmrg		process_file (NULL);
2951a30de1fSmrg		continue;
2961a30de1fSmrg
2971a30de1fSmrg	      /*
2981a30de1fSmrg	       * provide old xmodmap args
2991a30de1fSmrg	       */
3001a30de1fSmrg	      case 'S':
3011a30de1fSmrg		didAnything = True;
3021a30de1fSmrg		process_line ("clear shift");
3031a30de1fSmrg		continue;
3041a30de1fSmrg	      case 'L':
3051a30de1fSmrg		didAnything = True;
3061a30de1fSmrg		process_line ("clear lock");
3071a30de1fSmrg		continue;
3081a30de1fSmrg	      case 'C':
3091a30de1fSmrg		didAnything = True;
3101a30de1fSmrg		process_line ("clear control");
3111a30de1fSmrg		continue;
3121a30de1fSmrg	      case '1':
3131a30de1fSmrg	      case '2':
3141a30de1fSmrg	      case '3':
3151a30de1fSmrg	      case '4':
3161a30de1fSmrg	      case '5': {
3171a30de1fSmrg		  char cmd[11] = "clear modX";
3181a30de1fSmrg		  cmd[9] = arg[1];
3191a30de1fSmrg		  process_line (cmd);
3201a30de1fSmrg		  continue;
3211a30de1fSmrg	      }
3221a30de1fSmrg	      case 's':
3231a30de1fSmrg	      case 'l':
3241a30de1fSmrg	      case 'c': {
3251a30de1fSmrg		  char *cmd;
3261a30de1fSmrg		  didAnything = True;
3271b983734Smrg		  if (++i >= argc) missing_arg(arg);
328a733a5bfSmrg		  if (asprintf (&cmd, "remove %s = %s",
3291a30de1fSmrg				  ((arg[1] == 's') ? "shift" :
3301a30de1fSmrg				   ((arg[1] == 'l') ? "lock" :
331a733a5bfSmrg				    "control")), argv[i]) == -1)
332a733a5bfSmrg		      FatalError("Could not allocate memory for remove cmd");
3331a30de1fSmrg		  process_line (cmd);
3341a30de1fSmrg		  continue;
3351a30de1fSmrg	      }
3361a30de1fSmrg	      default:
3371b983734Smrg		unknown_arg(arg);
3381a30de1fSmrg		/*NOTREACHED*/
3391a30de1fSmrg	    }
3401a30de1fSmrg	} else if (arg[0] == '+') {	/* old xmodmap args */
3411a30de1fSmrg	    switch (arg[1]) {
3421a30de1fSmrg	      case '1':
3431a30de1fSmrg	      case '2':
3441a30de1fSmrg	      case '3':
3451a30de1fSmrg	      case '4':
3461a30de1fSmrg	      case '5': {
3471a30de1fSmrg		  char *cmd;
3481a30de1fSmrg		  didAnything = True;
3491b983734Smrg		  if (++i >= argc) missing_arg(arg);
350a733a5bfSmrg		  if (asprintf (&cmd, "add mod%c = %s", arg[1], argv[i]) == -1)
351a733a5bfSmrg		      FatalError("Could not allocate memory for add cmd");
3521a30de1fSmrg		  process_line (cmd);
3531a30de1fSmrg		  continue;
3541a30de1fSmrg	      }
3551a30de1fSmrg	      case 'S':
3561a30de1fSmrg	      case 'L':
3571a30de1fSmrg	      case 'C':
3581a30de1fSmrg		arg[1] = tolower (arg[1]);
359423b239cSmrg		/* fall through - to handler below */
3601a30de1fSmrg	      case 's':
3611a30de1fSmrg	      case 'l':
3621a30de1fSmrg	      case 'c': {
3631a30de1fSmrg		  char *cmd;
3641a30de1fSmrg		  didAnything = True;
3651b983734Smrg		  if (++i >= argc) missing_arg(arg);
366a733a5bfSmrg		  if (asprintf (&cmd, "add %s = %s",
3671a30de1fSmrg				  ((arg[1] == 's') ? "shift" :
3681a30de1fSmrg				   ((arg[1] == 'l') ? "lock" :
369a733a5bfSmrg				    "control")), argv[i]) == -1)
370a733a5bfSmrg		      FatalError("Could not allocate memory for remove cmd");
3711a30de1fSmrg		  process_line (cmd);
3721a30de1fSmrg		  continue;
3731a30de1fSmrg	      }
3741a30de1fSmrg	      default:
3751b983734Smrg		unknown_arg(arg);
3761a30de1fSmrg	    }
3771a30de1fSmrg	} else {
3781a30de1fSmrg	    didAnything = True;
3791a30de1fSmrg	    process_file (arg);
3801a30de1fSmrg	    continue;
3811a30de1fSmrg	}
3821a30de1fSmrg    }					/* end for loop */
3831a30de1fSmrg
3841a30de1fSmrg    /* for compatibility */
3851a30de1fSmrg    if (!didAnything) printMap = True;
3861a30de1fSmrg
3871a30de1fSmrg    /*
3881a30de1fSmrg     * at this point, the work list has been built and we can view it or
3891a30de1fSmrg     * execute it
3901a30de1fSmrg     */
3911a30de1fSmrg
3921a30de1fSmrg    if (dontExecute) {
3931a30de1fSmrg	print_work_queue ();
3941a30de1fSmrg	Exit (0);
3951a30de1fSmrg    }
3961a30de1fSmrg
3971a30de1fSmrg    if (parse_errors != 0) {
3981a30de1fSmrg	fprintf (stderr, "%s:  %d error%s encountered, aborting.\n",
3991a30de1fSmrg		 ProgramName, parse_errors,
4001a30de1fSmrg		 (parse_errors == 1 ? "" : "s"));
4011a30de1fSmrg	status = -1;	/* return an error condition */
4021a30de1fSmrg    } else {
4031a30de1fSmrg	status = execute_work_queue ();
4041a30de1fSmrg    }
4051a30de1fSmrg
4061a30de1fSmrg    if (printMap) {
4071a30de1fSmrg	print_modifier_map ();
4081a30de1fSmrg    }
4091a30de1fSmrg
4101a30de1fSmrg    if (printKeyTable) {
4111a30de1fSmrg	print_key_table (False);
4121a30de1fSmrg    }
4131a30de1fSmrg
4141a30de1fSmrg    if (printKeyTableExprs) {
4151a30de1fSmrg	print_key_table (True);
4161a30de1fSmrg    }
4171a30de1fSmrg
4181a30de1fSmrg    if (printPointerMap) {
4191a30de1fSmrg	print_pointer_map ();
4201a30de1fSmrg    }
4211a30de1fSmrg
4221a30de1fSmrg    Exit (status < 0 ? 1 : 0);
4231a30de1fSmrg
4241a30de1fSmrg    /* Muffle gcc */
4251a30de1fSmrg    return 0;
4261a30de1fSmrg}
4271a30de1fSmrg
428