1/* 2 * Command-line arg handling 3 */ 4 5#include "ctwm.h" 6 7#include <getopt.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11 12#include "clargs.h" 13#include "ctopts.h" 14#include "deftwmrc.h" 15#include "screen.h" 16#include "version.h" 17 18static void usage(void) __attribute__((noreturn)); 19static void print_version(void); 20static void DisplayInfo(void); 21static void dump_default_config(void); 22 23 24/* 25 * Command-line args. Initialize with useful default values. 26 */ 27ctwm_cl_args CLarg = { 28 .MultiScreen = true, 29 .Monochrome = false, 30 .cfgchk = false, 31 .InitFile = NULL, 32 .display_name = NULL, 33 .PrintErrorMessages = false, 34#ifdef DEBUG 35 .ShowWelcomeWindow = false, 36#else 37 .ShowWelcomeWindow = true, 38#endif 39#ifdef CAPTIVE 40 .is_captive = false, 41 .capwin = (Window) 0, 42 .captivename = NULL, 43#endif 44#ifdef USEM4 45 .KeepTmpFile = false, 46 .keepM4_filename = NULL, 47 .GoThroughM4 = true, 48#endif 49#ifdef EWMH 50 .ewmh_replace = false, 51#endif 52 .client_id = NULL, 53 .restore_filename = NULL, 54}; 55 56 57 58/* 59 * Parse them out and setup CLargs. 60 */ 61void 62clargs_parse(int argc, char *argv[]) 63{ 64 int ch, optidx; 65 66 /* 67 * Setup long options for arg parsing 68 */ 69 static struct option long_options[] = { 70 /* Simple flags */ 71 { "single", no_argument, NULL, 0 }, 72 { "mono", no_argument, NULL, 0 }, 73 { "verbose", no_argument, NULL, 'v' }, 74 { "quiet", no_argument, NULL, 'q' }, 75 { "nowelcome", no_argument, NULL, 'W' }, 76 77 /* Config/file related */ 78 { "file", required_argument, NULL, 'f' }, 79 { "cfgchk", no_argument, NULL, 0 }, 80 81 /* Show something and exit right away */ 82 { "help", no_argument, NULL, 'h' }, 83 { "version", no_argument, NULL, 0 }, 84 { "info", no_argument, NULL, 0 }, 85 { "dumpcfg", no_argument, NULL, 0 }, 86 87 /* Misc control bits */ 88 { "display", required_argument, NULL, 'd' }, 89 { "xrm", required_argument, NULL, 0 }, 90#ifdef CAPTIVE 91 { "window", optional_argument, NULL, 'w' }, 92 { "name", required_argument, NULL, 0 }, 93#endif 94 95#ifdef EWMH 96 { "replace", no_argument, NULL, 0 }, 97#endif 98 99 /* M4 control params */ 100#ifdef USEM4 101 { "keep-defs", no_argument, NULL, 'k' }, 102 { "keep", required_argument, NULL, 'K' }, 103 { "nom4", no_argument, NULL, 'n' }, 104#endif 105 106 /* Random session-related bits */ 107 { "clientId", required_argument, NULL, 0 }, 108 { "restore", required_argument, NULL, 0 }, 109 110 { NULL, 0, NULL, 0 }, 111 }; 112 113 114 /* 115 * Short aliases for some 116 * 117 * I assume '::' for optional args is portable; getopt_long(3) 118 * doesn't describe it, but it's a GNU extension for getopt(3). 119 */ 120 const char *short_options = "vqWf:hd:" 121#ifdef CAPTIVE 122 "w::" 123#endif 124#ifdef USEM4 125 "kK:n" 126#endif 127 ; 128 129 130 /* 131 * Backward-compat cheat: accept a few old-style long args if they 132 * came first. Of course, this assumed argv[x] is editable, which on 133 * most systems it is, and C99 requires it. 134 */ 135 if(argc > 1) { 136#define CHK(x) else if(strcmp(argv[1], (x)) == 0) 137 if(0) { 138 /* nada */ 139 } 140 CHK("-version") { 141 print_version(); 142 exit(0); 143 } 144 CHK("-info") { 145 DisplayInfo(); 146 exit(0); 147 } 148 CHK("-cfgchk") { 149 CLarg.cfgchk = true; 150 *argv[1] = '\0'; 151 } 152 CHK("-display") { 153 if(argc <= 2 || strlen(argv[2]) < 1) { 154 usage(); 155 } 156 CLarg.display_name = strdup(argv[2]); 157 158 *argv[1] = '\0'; 159 *argv[2] = '\0'; 160 } 161#undef CHK 162 } 163 164 165 /* 166 * Parse out the args 167 */ 168 optidx = 0; 169 while((ch = getopt_long(argc, argv, short_options, long_options, 170 &optidx)) != -1) { 171 switch(ch) { 172 /* First handle the simple cases that have short args */ 173 case 'v': 174 CLarg.PrintErrorMessages = true; 175 break; 176 case 'q': 177 CLarg.PrintErrorMessages = false; 178 break; 179 case 'W': 180 CLarg.ShowWelcomeWindow = false; 181 break; 182 case 'f': 183 CLarg.InitFile = optarg; 184 break; 185 case 'h': 186 usage(); 187 case 'd': 188 CLarg.display_name = optarg; 189 break; 190#ifdef CAPTIVE 191 case 'w': 192 CLarg.is_captive = true; 193 CLarg.MultiScreen = false; 194 if(optarg != NULL) { 195 sscanf(optarg, "%x", (unsigned int *)&CLarg.capwin); 196 /* Failure will just leave capwin as initialized */ 197 } 198 break; 199#endif 200 201#ifdef USEM4 202 /* Args that only mean anything if we're built with m4 */ 203 case 'k': 204 CLarg.KeepTmpFile = true; 205 break; 206 case 'K': 207 CLarg.keepM4_filename = optarg; 208 break; 209 case 'n': 210 CLarg.GoThroughM4 = false; 211 break; 212#endif 213 214 215 /* 216 * Now the stuff that doesn't have short variants. 217 */ 218 case 0: 219 220#define IFIS(x) if(strcmp(long_options[optidx].name, (x)) == 0) 221 /* Simple flag-setting */ 222 IFIS("single") { 223 CLarg.MultiScreen = false; 224 break; 225 } 226 IFIS("mono") { 227 CLarg.Monochrome = true; 228 break; 229 } 230 IFIS("cfgchk") { 231 CLarg.cfgchk = true; 232 break; 233 } 234#ifdef EWMH 235 IFIS("replace") { 236 CLarg.ewmh_replace = true; 237 break; 238 } 239#endif 240 241 /* Simple value-setting */ 242#ifdef CAPTIVE 243 IFIS("name") { 244 CLarg.captivename = optarg; 245 break; 246 } 247#endif 248 IFIS("clientId") { 249 CLarg.client_id = optarg; 250 break; 251 } 252 IFIS("restore") { 253 CLarg.restore_filename = optarg; 254 break; 255 } 256 257 /* Some immediate actions */ 258 IFIS("version") { 259 print_version(); 260 exit(0); 261 } 262 IFIS("info") { 263 DisplayInfo(); 264 exit(0); 265 } 266 IFIS("dumpcfg") { 267 dump_default_config(); 268 exit(0); 269 } 270 271 /* Misc */ 272 IFIS("xrm") { 273 /* 274 * Quietly ignored by us; Xlib processes it 275 * internally in XtToolkitInitialize(); 276 */ 277 break; 278 } 279#undef IFIS 280 281 /* 282 * Some choices may just be internally setting a flag. 283 * We have none right now, but leave this in case we grow 284 * more later. 285 */ 286 if(long_options[optidx].flag != NULL) { 287 break; 288 } 289 290 /* Don't think it should be possible to get here... */ 291 fprintf(stderr, "Internal error in getopt: '%s' unhandled.\n", 292 long_options[optidx].name); 293 usage(); 294 295 /* Something totally unexpected */ 296 case '?': 297 /* getopt_long() already printed an error */ 298 usage(); 299 300 default: 301 /* Uhhh... */ 302 fprintf(stderr, "Internal error: getopt confused us.\n"); 303 usage(); 304 } 305 } 306 307 308 /* Should do it */ 309 return; 310} 311 312 313/* 314 * Sanity check CLarg's 315 */ 316void 317clargs_check(void) 318{ 319 320#ifdef USEM4 321 /* If we're not doing m4, don't specify m4 options */ 322 if(!CLarg.GoThroughM4) { 323 if(CLarg.KeepTmpFile) { 324 fprintf(stderr, "--keep-defs is incompatible with --nom4.\n"); 325 usage(); 326 } 327 if(CLarg.keepM4_filename) { 328 fprintf(stderr, "--keep is incompatible with --nom4.\n"); 329 usage(); 330 } 331 } 332#endif 333 334#ifdef CAPTIVE 335 /* If we're not captive, captivename is meaningless too */ 336 if(CLarg.captivename && !CLarg.is_captive) { 337 fprintf(stderr, "--name is meaningless without --window.\n"); 338 usage(); 339 } 340 341 /* 342 * Being captive and --cfgchk'ing is kinda meaningless. There's no 343 * reason to create a window just to destroy things, and it never 344 * adds anything. And it's one more way we're forcing changes on the 345 * X side before we parse the actual config, so let's just disallow 346 * it. 347 */ 348 if(CLarg.is_captive && CLarg.cfgchk) { 349 fprintf(stderr, "--window is incompatible with --cfgchk.\n"); 350 usage(); 351 } 352#endif 353 354 /* Guess that's it */ 355 return; 356} 357 358 359/* 360 * Small utils only currently used in this file. Over time they may need 361 * to be exported, if we start using them from more places. 362 */ 363static void 364usage(void) 365{ 366 /* How far to indent continuation lines */ 367 int llen = 10; 368 369 fprintf(stderr, "usage: %s [(--display | -d) dpy] " 370#ifdef EWMH 371 "[--replace] " 372#endif 373 "[--single]\n", ProgramName); 374 375 fprintf(stderr, "%*s[(--file | -f) initfile] [--cfgchk] [--dumpcfg]\n", 376 llen, ""); 377 378#ifdef USEM4 379 fprintf(stderr, "%*s[--nom4 | -n] [--keep-defs | -k] " 380 "[(--keep | -K) m4file]\n", llen, ""); 381#endif 382 383 fprintf(stderr, "%*s[--verbose | -v] [--quiet | -q] [--mono] " 384 "[--xrm resource]\n", llen, ""); 385 386 fprintf(stderr, "%*s[--version] [--info] [--nowelcome | -W]\n", 387 llen, ""); 388 389#ifdef CAPTIVE 390 fprintf(stderr, "%*s[(--window | -w) [win-id]] [--name name]\n", llen, ""); 391#endif 392 393 /* Semi-intentionally not documenting --clientId/--restore */ 394 395 fprintf(stderr, "%*s[--help]\n", llen, ""); 396 397 398 exit(1); 399} 400 401 402static void 403print_version(void) 404{ 405 printf("ctwm %s\n", VersionNumberFull); 406 if(VCSType && VCSRevision) { 407 printf(" (%s:%s)\n", VCSType, VCSRevision); 408 } 409} 410 411 412static void 413DisplayInfo(void) 414{ 415 char *ctopts; 416 417 printf("Twm version: %s\n", TwmVersion); 418 419 ctopts = ctopts_string(" "); 420 printf("Compile time options : %s\n", ctopts); 421 free(ctopts); 422} 423 424 425static void 426dump_default_config(void) 427{ 428 int i; 429 430 for(i = 0 ; defTwmrc[i] != NULL ; i++) { 431 printf("%s\n", defTwmrc[i]); 432 } 433} 434