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