Home | History | Annotate | Line # | Download | only in dist
      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 
     18 static void usage(void) __attribute__((noreturn));
     19 static void print_version(void);
     20 static void DisplayInfo(void);
     21 static void dump_default_config(void);
     22 
     23 
     24 /*
     25  * Command-line args.  Initialize with useful default values.
     26  */
     27 ctwm_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  */
     61 void
     62 clargs_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  */
    316 void
    317 clargs_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  */
    363 static void
    364 usage(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 
    402 static void
    403 print_version(void)
    404 {
    405 	printf("ctwm %s\n", VersionNumberFull);
    406 	if(VCSType && VCSRevision) {
    407 		printf(" (%s:%s)\n", VCSType, VCSRevision);
    408 	}
    409 }
    410 
    411 
    412 static void
    413 DisplayInfo(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 
    425 static void
    426 dump_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