1/*
2
3Copyright 1988, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29#ifdef HAVE_CONFIG_H
30# include "config.h"
31#endif
32
33#include <X11/Xos.h>
34#include <X11/Xlib.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <ctype.h>
38#include <stdarg.h>
39#include "xmodmap.h"
40
41const char *ProgramName;
42Display *dpy = NULL;
43int min_keycode, max_keycode;
44Bool verbose = False;
45Bool dontExecute = False;
46
47void
48_X_NORETURN
49Exit(int status)
50{
51    if (dpy) {
52	XCloseDisplay (dpy);
53	dpy = NULL;
54    }
55    exit (status);
56}
57
58static void _X_NORETURN
59FatalError(const char *message)
60{
61    fprintf(stderr, "%s: %s\n", ProgramName, message);
62    Exit(-1);
63}
64
65#ifndef HAVE_ASPRINTF
66/* sprintf variant found in newer libc's which allocates string to print to */
67static int _X_ATTRIBUTE_PRINTF(2,3)
68asprintf(char ** ret, const char *format, ...)
69{
70    char buf[256];
71    int len;
72    va_list ap;
73
74    va_start(ap, format);
75    len = vsnprintf(buf, sizeof(buf), format, ap);
76    va_end(ap);
77
78    if (len < 0)
79	return -1;
80
81    if (len < sizeof(buf))
82    {
83	*ret = strdup(buf);
84    }
85    else
86    {
87	*ret = malloc(len + 1); /* snprintf doesn't count trailing '\0' */
88	if (*ret != NULL)
89	{
90	    va_start(ap, format);
91	    len = vsnprintf(*ret, len + 1, format, ap);
92	    va_end(ap);
93	    if (len < 0) {
94		free(*ret);
95		*ret = NULL;
96	    }
97	}
98    }
99
100    if (*ret == NULL)
101	return -1;
102
103    return len;
104}
105#endif /* HAVE_ASPRINTF */
106
107static const char help_message[] =
108"\nwhere options include:\n"
109"    -display host:dpy            X server to use\n"
110"    -verbose, -quiet             turn logging on or off\n"
111"    -n                           don't execute changes, just show like make\n"
112"    -e expression                execute string\n"
113"    -pm                          print modifier map\n"
114"    -pk                          print keymap table\n"
115"    -pke                         print keymap table as expressions\n"
116"    -pp                          print pointer map\n"
117"    -help                        print this usage message\n"
118"    -grammar                     print out short help on allowable input\n"
119"    -version                     print program version\n"
120"    -                            read standard input\n"
121"\n";
122
123
124static void
125_X_NORETURN _X_COLD
126usage(int exitcode)
127{
128    fprintf (stderr, "usage:  %s [-options ...] [filename]\n", ProgramName);
129    fprintf (stderr, "%s\n", help_message);
130    Exit (exitcode);
131}
132
133static void
134_X_NORETURN _X_COLD
135missing_arg(const char *arg)
136{
137    fprintf (stderr, "%s: %s requires an argument\n\n", ProgramName, arg);
138    usage(1);
139}
140
141static void
142_X_NORETURN _X_COLD
143unknown_arg(const char *arg)
144{
145    fprintf (stderr, "%s: unrecognized argument %s\n\n", ProgramName, arg);
146    usage(1);
147}
148
149static const char grammar_message[] =
150"    pointer = default              reset pointer buttons to default\n"
151"    pointer = NUMBER ...           set pointer button codes\n"
152"    keycode NUMBER = [KEYSYM ...]  map keycode to given keysyms\n"
153"    keysym KEYSYM = [KEYSYM ...]   look up keysym and do a keycode operation\n"
154"    clear MODIFIER                 remove all keys for this modifier\n"
155"    add MODIFIER = KEYSYM ...      add the keysyms to the modifier\n"
156"    remove MODIFIER = KEYSYM ...   remove the keysyms from the modifier\n"
157"\n"
158"where NUMBER is a decimal, octal, or hex constant; KEYSYM is a valid\n"
159"Key Symbol name; and MODIFIER is one of the eight modifier names:  Shift,\n"
160"Lock, Control, Mod1, Mod2, Mod3, Mod4, or Mod5.  Lines beginning with\n"
161"an exclamation mark (!) are taken as comments.  Case is significant except\n"
162"for MODIFIER names.\n"
163"\n"
164"Keysyms on the left hand side of the = sign are looked up before any changes\n"
165"are made; keysyms on the right are looked up after all of those on the left\n"
166"have been resolved.  This makes it possible to swap modifier keys.\n"
167"\n";
168
169
170static void
171_X_NORETURN
172grammar_usage(void)
173{
174    fprintf (stderr, "%s accepts the following input expressions:\n\n",
175	     ProgramName);
176    fprintf (stderr, "%s\n", grammar_message);
177    Exit (0);
178}
179
180int parse_errors = 0;
181
182int
183main(int argc, char *argv[])
184{
185    int i;
186    char *displayname = NULL;
187    int status;
188    Bool printMap = False;
189    Bool printKeyTable = False;
190    Bool printKeyTableExprs = False;
191    Bool printPointerMap = False;
192    Bool didAnything = False;
193
194    ProgramName = argv[0];
195
196    /*
197     * scan the arg list once to find out which display to use
198     */
199
200    for (i = 1; i < argc; i++) {
201	const char *arg = argv[i];
202
203	if (arg[0] == '-') {
204	    switch (arg[1]) {
205	    case 'd':			/* -display host:dpy */
206		if (++i >= argc) missing_arg(arg);
207		displayname = argv[i];
208		break;
209	    case 'g':			/* -grammar */
210		grammar_usage ();
211		/*NOTREACHED*/
212	    case 'h':			/* -help */
213	    case '?':
214		usage(0);
215	    case 'v':
216		if (strcmp(arg, "-version") == 0) {
217		    puts(PACKAGE_STRING);
218		    exit(0);
219		}
220	    }
221	}
222    }
223
224    dpy = XOpenDisplay (displayname);
225    if (!dpy) {
226	fprintf (stderr, "%s:  unable to open display '%s'\n",
227		 ProgramName, XDisplayName (displayname));
228	Exit (1);
229    }
230
231    XDisplayKeycodes (dpy, &min_keycode, &max_keycode);
232
233    initialize_map ();
234
235    /*
236     * scan the arg list again to do the actual work (since it requires
237     * the display being open.
238     */
239
240    for (i = 1; i < argc; i++) {
241	char *arg = argv[i];
242
243	if (arg[0] == '-') {
244	    switch (arg[1]) {
245	      case 'd':			/* -display host:dpy */
246		++i;			/* handled above */
247		continue;
248	      case 'v':			/* -verbose */
249		verbose = True;
250		continue;
251	      case 'q':			/* -quiet */
252		verbose = False;
253		continue;
254	      case 'n':			/* -n (like make) */
255		dontExecute = True;
256		continue;
257	      case 'e':			/* -e expression */
258		didAnything = True;
259		if (++i >= argc) missing_arg(arg);
260		process_line (argv[i]);
261		continue;
262	      case 'p':			/* -p... */
263		switch (arg[2]) {
264		  case '\0':
265		  case 'm':		/* -pm */
266		    printMap = True;
267		    break;
268		  case 'k':		/* -pk, -pke */
269		    switch (arg[3]) {
270		    case '\0':
271			printKeyTable = True;
272			break;
273		    case 'e':
274			printKeyTableExprs = True;
275			break;
276		    default:
277			unknown_arg(arg);
278		    }
279		    break;
280		  case 'p':		/* -pp */
281		    printPointerMap = True;
282		    break;
283		  default:
284		    unknown_arg(arg);
285		    /* NOTREACHED */
286		}
287		didAnything = True;
288		continue;
289	      case 'g':			/* -grammar */
290		grammar_usage ();
291		/*NOTREACHED*/
292	      case '\0':		/* - (use standard input) */
293		didAnything = True;
294		process_file (NULL);
295		continue;
296
297	      /*
298	       * provide old xmodmap args
299	       */
300	      case 'S':
301		didAnything = True;
302		process_line ("clear shift");
303		continue;
304	      case 'L':
305		didAnything = True;
306		process_line ("clear lock");
307		continue;
308	      case 'C':
309		didAnything = True;
310		process_line ("clear control");
311		continue;
312	      case '1':
313	      case '2':
314	      case '3':
315	      case '4':
316	      case '5': {
317		  char cmd[11] = "clear modX";
318		  cmd[9] = arg[1];
319		  process_line (cmd);
320		  continue;
321	      }
322	      case 's':
323	      case 'l':
324	      case 'c': {
325		  char *cmd;
326		  didAnything = True;
327		  if (++i >= argc) missing_arg(arg);
328		  if (asprintf (&cmd, "remove %s = %s",
329				  ((arg[1] == 's') ? "shift" :
330				   ((arg[1] == 'l') ? "lock" :
331				    "control")), argv[i]) == -1)
332		      FatalError("Could not allocate memory for remove cmd");
333		  process_line (cmd);
334		  continue;
335	      }
336	      default:
337		unknown_arg(arg);
338		/*NOTREACHED*/
339	    }
340	} else if (arg[0] == '+') {	/* old xmodmap args */
341	    switch (arg[1]) {
342	      case '1':
343	      case '2':
344	      case '3':
345	      case '4':
346	      case '5': {
347		  char *cmd;
348		  didAnything = True;
349		  if (++i >= argc) missing_arg(arg);
350		  if (asprintf (&cmd, "add mod%c = %s", arg[1], argv[i]) == -1)
351		      FatalError("Could not allocate memory for add cmd");
352		  process_line (cmd);
353		  continue;
354	      }
355	      case 'S':
356	      case 'L':
357	      case 'C':
358		arg[1] = tolower (arg[1]);
359		/* fall through - to handler below */
360	      case 's':
361	      case 'l':
362	      case 'c': {
363		  char *cmd;
364		  didAnything = True;
365		  if (++i >= argc) missing_arg(arg);
366		  if (asprintf (&cmd, "add %s = %s",
367				  ((arg[1] == 's') ? "shift" :
368				   ((arg[1] == 'l') ? "lock" :
369				    "control")), argv[i]) == -1)
370		      FatalError("Could not allocate memory for remove cmd");
371		  process_line (cmd);
372		  continue;
373	      }
374	      default:
375		unknown_arg(arg);
376	    }
377	} else {
378	    didAnything = True;
379	    process_file (arg);
380	    continue;
381	}
382    }					/* end for loop */
383
384    /* for compatibility */
385    if (!didAnything) printMap = True;
386
387    /*
388     * at this point, the work list has been built and we can view it or
389     * execute it
390     */
391
392    if (dontExecute) {
393	print_work_queue ();
394	Exit (0);
395    }
396
397    if (parse_errors != 0) {
398	fprintf (stderr, "%s:  %d error%s encountered, aborting.\n",
399		 ProgramName, parse_errors,
400		 (parse_errors == 1 ? "" : "s"));
401	status = -1;	/* return an error condition */
402    } else {
403	status = execute_work_queue ();
404    }
405
406    if (printMap) {
407	print_modifier_map ();
408    }
409
410    if (printKeyTable) {
411	print_key_table (False);
412    }
413
414    if (printKeyTableExprs) {
415	print_key_table (True);
416    }
417
418    if (printPointerMap) {
419	print_pointer_map ();
420    }
421
422    Exit (status < 0 ? 1 : 0);
423
424    /* Muffle gcc */
425    return 0;
426}
427
428