10bbfda8aSnia/*
20bbfda8aSnia * Command-line arg handling
30bbfda8aSnia */
40bbfda8aSnia
50bbfda8aSnia#include "ctwm.h"
60bbfda8aSnia
70bbfda8aSnia#include <getopt.h>
80bbfda8aSnia#include <stdio.h>
90bbfda8aSnia#include <stdlib.h>
100bbfda8aSnia#include <string.h>
110bbfda8aSnia
120bbfda8aSnia#include "clargs.h"
130bbfda8aSnia#include "ctopts.h"
140bbfda8aSnia#include "deftwmrc.h"
150bbfda8aSnia#include "screen.h"
160bbfda8aSnia#include "version.h"
170bbfda8aSnia
180bbfda8aSniastatic void usage(void) __attribute__((noreturn));
190bbfda8aSniastatic void print_version(void);
200bbfda8aSniastatic void DisplayInfo(void);
210bbfda8aSniastatic void dump_default_config(void);
220bbfda8aSnia
230bbfda8aSnia
240bbfda8aSnia/*
250bbfda8aSnia * Command-line args.  Initialize with useful default values.
260bbfda8aSnia */
270bbfda8aSniactwm_cl_args CLarg = {
280bbfda8aSnia	.MultiScreen     = true,
290bbfda8aSnia	.Monochrome      = false,
300bbfda8aSnia	.cfgchk          = false,
310bbfda8aSnia	.InitFile        = NULL,
320bbfda8aSnia	.display_name    = NULL,
330bbfda8aSnia	.PrintErrorMessages = false,
340bbfda8aSnia#ifdef DEBUG
350bbfda8aSnia	.ShowWelcomeWindow  = false,
360bbfda8aSnia#else
370bbfda8aSnia	.ShowWelcomeWindow  = true,
380bbfda8aSnia#endif
39b18c2d1eSnia#ifdef CAPTIVE
400bbfda8aSnia	.is_captive      = false,
410bbfda8aSnia	.capwin          = (Window) 0,
420bbfda8aSnia	.captivename     = NULL,
43b18c2d1eSnia#endif
440bbfda8aSnia#ifdef USEM4
450bbfda8aSnia	.KeepTmpFile     = false,
460bbfda8aSnia	.keepM4_filename = NULL,
470bbfda8aSnia	.GoThroughM4     = true,
480bbfda8aSnia#endif
490bbfda8aSnia#ifdef EWMH
500bbfda8aSnia	.ewmh_replace    = false,
510bbfda8aSnia#endif
520bbfda8aSnia	.client_id       = NULL,
530bbfda8aSnia	.restore_filename = NULL,
540bbfda8aSnia};
550bbfda8aSnia
560bbfda8aSnia
570bbfda8aSnia
580bbfda8aSnia/*
590bbfda8aSnia * Parse them out and setup CLargs.
600bbfda8aSnia */
610bbfda8aSniavoid
620bbfda8aSniaclargs_parse(int argc, char *argv[])
630bbfda8aSnia{
640bbfda8aSnia	int ch, optidx;
650bbfda8aSnia
660bbfda8aSnia	/*
670bbfda8aSnia	 * Setup long options for arg parsing
680bbfda8aSnia	 */
690bbfda8aSnia	static struct option long_options[] = {
700bbfda8aSnia		/* Simple flags */
710bbfda8aSnia		{ "single",    no_argument,       NULL, 0 },
720bbfda8aSnia		{ "mono",      no_argument,       NULL, 0 },
730bbfda8aSnia		{ "verbose",   no_argument,       NULL, 'v' },
740bbfda8aSnia		{ "quiet",     no_argument,       NULL, 'q' },
750bbfda8aSnia		{ "nowelcome", no_argument,       NULL, 'W' },
760bbfda8aSnia
770bbfda8aSnia		/* Config/file related */
780bbfda8aSnia		{ "file",      required_argument, NULL, 'f' },
790bbfda8aSnia		{ "cfgchk",    no_argument,       NULL, 0 },
800bbfda8aSnia
810bbfda8aSnia		/* Show something and exit right away */
820bbfda8aSnia		{ "help",      no_argument,       NULL, 'h' },
830bbfda8aSnia		{ "version",   no_argument,       NULL, 0 },
840bbfda8aSnia		{ "info",      no_argument,       NULL, 0 },
850bbfda8aSnia		{ "dumpcfg",   no_argument,       NULL, 0 },
860bbfda8aSnia
870bbfda8aSnia		/* Misc control bits */
880bbfda8aSnia		{ "display",   required_argument, NULL, 'd' },
89b18c2d1eSnia		{ "xrm",       required_argument, NULL, 0 },
90b18c2d1eSnia#ifdef CAPTIVE
910bbfda8aSnia		{ "window",    optional_argument, NULL, 'w' },
920bbfda8aSnia		{ "name",      required_argument, NULL, 0 },
93b18c2d1eSnia#endif
940bbfda8aSnia
950bbfda8aSnia#ifdef EWMH
960bbfda8aSnia		{ "replace",   no_argument,       NULL, 0 },
970bbfda8aSnia#endif
980bbfda8aSnia
990bbfda8aSnia		/* M4 control params */
1000bbfda8aSnia#ifdef USEM4
1010bbfda8aSnia		{ "keep-defs", no_argument,       NULL, 'k' },
1020bbfda8aSnia		{ "keep",      required_argument, NULL, 'K' },
1030bbfda8aSnia		{ "nom4",      no_argument,       NULL, 'n' },
1040bbfda8aSnia#endif
1050bbfda8aSnia
1060bbfda8aSnia		/* Random session-related bits */
1070bbfda8aSnia		{ "clientId",  required_argument, NULL, 0 },
1080bbfda8aSnia		{ "restore",   required_argument, NULL, 0 },
1090bbfda8aSnia
1100bbfda8aSnia		{ NULL,        0,                 NULL, 0 },
1110bbfda8aSnia	};
1120bbfda8aSnia
1130bbfda8aSnia
1140bbfda8aSnia	/*
1150bbfda8aSnia	 * Short aliases for some
1160bbfda8aSnia	 *
1170bbfda8aSnia	 * I assume '::' for optional args is portable; getopt_long(3)
1180bbfda8aSnia	 * doesn't describe it, but it's a GNU extension for getopt(3).
1190bbfda8aSnia	 */
120b18c2d1eSnia	const char *short_options = "vqWf:hd:"
121b18c2d1eSnia#ifdef CAPTIVE
122b18c2d1eSnia	                            "w::"
123b18c2d1eSnia#endif
1240bbfda8aSnia#ifdef USEM4
1250bbfda8aSnia	                            "kK:n"
1260bbfda8aSnia#endif
1270bbfda8aSnia	                            ;
1280bbfda8aSnia
1290bbfda8aSnia
1300bbfda8aSnia	/*
1310bbfda8aSnia	 * Backward-compat cheat: accept a few old-style long args if they
1320bbfda8aSnia	 * came first.  Of course, this assumed argv[x] is editable, which on
1330bbfda8aSnia	 * most systems it is, and C99 requires it.
1340bbfda8aSnia	 */
1350bbfda8aSnia	if(argc > 1) {
1360bbfda8aSnia#define CHK(x) else if(strcmp(argv[1], (x)) == 0)
1370bbfda8aSnia		if(0) {
1380bbfda8aSnia			/* nada */
1390bbfda8aSnia		}
1400bbfda8aSnia		CHK("-version") {
1410bbfda8aSnia			print_version();
1420bbfda8aSnia			exit(0);
1430bbfda8aSnia		}
1440bbfda8aSnia		CHK("-info") {
1450bbfda8aSnia			DisplayInfo();
1460bbfda8aSnia			exit(0);
1470bbfda8aSnia		}
1480bbfda8aSnia		CHK("-cfgchk") {
1490bbfda8aSnia			CLarg.cfgchk = true;
1500bbfda8aSnia			*argv[1] = '\0';
1510bbfda8aSnia		}
1520bbfda8aSnia		CHK("-display") {
1530bbfda8aSnia			if(argc <= 2 || strlen(argv[2]) < 1) {
1540bbfda8aSnia				usage();
1550bbfda8aSnia			}
1560bbfda8aSnia			CLarg.display_name = strdup(argv[2]);
1570bbfda8aSnia
1580bbfda8aSnia			*argv[1] = '\0';
1590bbfda8aSnia			*argv[2] = '\0';
1600bbfda8aSnia		}
1610bbfda8aSnia#undef CHK
1620bbfda8aSnia	}
1630bbfda8aSnia
1640bbfda8aSnia
1650bbfda8aSnia	/*
1660bbfda8aSnia	 * Parse out the args
1670bbfda8aSnia	 */
1680bbfda8aSnia	optidx = 0;
1690bbfda8aSnia	while((ch = getopt_long(argc, argv, short_options, long_options,
1700bbfda8aSnia	                        &optidx)) != -1) {
1710bbfda8aSnia		switch(ch) {
1720bbfda8aSnia			/* First handle the simple cases that have short args */
1730bbfda8aSnia			case 'v':
1740bbfda8aSnia				CLarg.PrintErrorMessages = true;
1750bbfda8aSnia				break;
1760bbfda8aSnia			case 'q':
1770bbfda8aSnia				CLarg.PrintErrorMessages = false;
1780bbfda8aSnia				break;
1790bbfda8aSnia			case 'W':
1800bbfda8aSnia				CLarg.ShowWelcomeWindow = false;
1810bbfda8aSnia				break;
1820bbfda8aSnia			case 'f':
1830bbfda8aSnia				CLarg.InitFile = optarg;
1840bbfda8aSnia				break;
1850bbfda8aSnia			case 'h':
1860bbfda8aSnia				usage();
1870bbfda8aSnia			case 'd':
1880bbfda8aSnia				CLarg.display_name = optarg;
1890bbfda8aSnia				break;
190b18c2d1eSnia#ifdef CAPTIVE
1910bbfda8aSnia			case 'w':
1920bbfda8aSnia				CLarg.is_captive = true;
1930bbfda8aSnia				CLarg.MultiScreen = false;
1940bbfda8aSnia				if(optarg != NULL) {
1950bbfda8aSnia					sscanf(optarg, "%x", (unsigned int *)&CLarg.capwin);
1960bbfda8aSnia					/* Failure will just leave capwin as initialized */
1970bbfda8aSnia				}
1980bbfda8aSnia				break;
199b18c2d1eSnia#endif
2000bbfda8aSnia
2010bbfda8aSnia#ifdef USEM4
2020bbfda8aSnia			/* Args that only mean anything if we're built with m4 */
2030bbfda8aSnia			case 'k':
2040bbfda8aSnia				CLarg.KeepTmpFile = true;
2050bbfda8aSnia				break;
2060bbfda8aSnia			case 'K':
2070bbfda8aSnia				CLarg.keepM4_filename = optarg;
2080bbfda8aSnia				break;
2090bbfda8aSnia			case 'n':
2100bbfda8aSnia				CLarg.GoThroughM4 = false;
2110bbfda8aSnia				break;
2120bbfda8aSnia#endif
2130bbfda8aSnia
2140bbfda8aSnia
2150bbfda8aSnia			/*
2160bbfda8aSnia			 * Now the stuff that doesn't have short variants.
2170bbfda8aSnia			 */
2180bbfda8aSnia			case 0:
2190bbfda8aSnia
2200bbfda8aSnia#define IFIS(x) if(strcmp(long_options[optidx].name, (x)) == 0)
2210bbfda8aSnia				/* Simple flag-setting */
2220bbfda8aSnia				IFIS("single") {
2230bbfda8aSnia					CLarg.MultiScreen = false;
2240bbfda8aSnia					break;
2250bbfda8aSnia				}
2260bbfda8aSnia				IFIS("mono") {
2270bbfda8aSnia					CLarg.Monochrome = true;
2280bbfda8aSnia					break;
2290bbfda8aSnia				}
2300bbfda8aSnia				IFIS("cfgchk") {
2310bbfda8aSnia					CLarg.cfgchk = true;
2320bbfda8aSnia					break;
2330bbfda8aSnia				}
2340bbfda8aSnia#ifdef EWMH
2350bbfda8aSnia				IFIS("replace") {
2360bbfda8aSnia					CLarg.ewmh_replace = true;
2370bbfda8aSnia					break;
2380bbfda8aSnia				}
2390bbfda8aSnia#endif
2400bbfda8aSnia
2410bbfda8aSnia				/* Simple value-setting */
242b18c2d1eSnia#ifdef CAPTIVE
2430bbfda8aSnia				IFIS("name") {
2440bbfda8aSnia					CLarg.captivename = optarg;
2450bbfda8aSnia					break;
2460bbfda8aSnia				}
247b18c2d1eSnia#endif
2480bbfda8aSnia				IFIS("clientId") {
2490bbfda8aSnia					CLarg.client_id = optarg;
2500bbfda8aSnia					break;
2510bbfda8aSnia				}
2520bbfda8aSnia				IFIS("restore") {
2530bbfda8aSnia					CLarg.restore_filename = optarg;
2540bbfda8aSnia					break;
2550bbfda8aSnia				}
2560bbfda8aSnia
2570bbfda8aSnia				/* Some immediate actions */
2580bbfda8aSnia				IFIS("version") {
2590bbfda8aSnia					print_version();
2600bbfda8aSnia					exit(0);
2610bbfda8aSnia				}
2620bbfda8aSnia				IFIS("info") {
2630bbfda8aSnia					DisplayInfo();
2640bbfda8aSnia					exit(0);
2650bbfda8aSnia				}
2660bbfda8aSnia				IFIS("dumpcfg") {
2670bbfda8aSnia					dump_default_config();
2680bbfda8aSnia					exit(0);
2690bbfda8aSnia				}
2700bbfda8aSnia
2710bbfda8aSnia				/* Misc */
2720bbfda8aSnia				IFIS("xrm") {
2730bbfda8aSnia					/*
2740bbfda8aSnia					 * Quietly ignored by us; Xlib processes it
2750bbfda8aSnia					 * internally in XtToolkitInitialize();
2760bbfda8aSnia					 */
2770bbfda8aSnia					break;
2780bbfda8aSnia				}
2790bbfda8aSnia#undef IFIS
2800bbfda8aSnia
2810bbfda8aSnia				/*
2820bbfda8aSnia				 * Some choices may just be internally setting a flag.
2830bbfda8aSnia				 * We have none right now, but leave this in case we grow
2840bbfda8aSnia				 * more later.
2850bbfda8aSnia				 */
2860bbfda8aSnia				if(long_options[optidx].flag != NULL) {
2870bbfda8aSnia					break;
2880bbfda8aSnia				}
2890bbfda8aSnia
2900bbfda8aSnia				/* Don't think it should be possible to get here... */
2910bbfda8aSnia				fprintf(stderr, "Internal error in getopt: '%s' unhandled.\n",
2920bbfda8aSnia				        long_options[optidx].name);
2930bbfda8aSnia				usage();
2940bbfda8aSnia
2950bbfda8aSnia			/* Something totally unexpected */
2960bbfda8aSnia			case '?':
2970bbfda8aSnia				/* getopt_long() already printed an error */
2980bbfda8aSnia				usage();
2990bbfda8aSnia
3000bbfda8aSnia			default:
3010bbfda8aSnia				/* Uhhh...  */
3020bbfda8aSnia				fprintf(stderr, "Internal error: getopt confused us.\n");
3030bbfda8aSnia				usage();
3040bbfda8aSnia		}
3050bbfda8aSnia	}
3060bbfda8aSnia
3070bbfda8aSnia
3080bbfda8aSnia	/* Should do it */
3090bbfda8aSnia	return;
3100bbfda8aSnia}
3110bbfda8aSnia
3120bbfda8aSnia
3130bbfda8aSnia/*
3140bbfda8aSnia * Sanity check CLarg's
3150bbfda8aSnia */
3160bbfda8aSniavoid
3170bbfda8aSniaclargs_check(void)
3180bbfda8aSnia{
3190bbfda8aSnia
3200bbfda8aSnia#ifdef USEM4
3210bbfda8aSnia	/* If we're not doing m4, don't specify m4 options */
3220bbfda8aSnia	if(!CLarg.GoThroughM4) {
3230bbfda8aSnia		if(CLarg.KeepTmpFile) {
3240bbfda8aSnia			fprintf(stderr, "--keep-defs is incompatible with --nom4.\n");
3250bbfda8aSnia			usage();
3260bbfda8aSnia		}
3270bbfda8aSnia		if(CLarg.keepM4_filename) {
3280bbfda8aSnia			fprintf(stderr, "--keep is incompatible with --nom4.\n");
3290bbfda8aSnia			usage();
3300bbfda8aSnia		}
3310bbfda8aSnia	}
3320bbfda8aSnia#endif
3330bbfda8aSnia
334b18c2d1eSnia#ifdef CAPTIVE
3350bbfda8aSnia	/* If we're not captive, captivename is meaningless too */
3360bbfda8aSnia	if(CLarg.captivename && !CLarg.is_captive) {
3370bbfda8aSnia		fprintf(stderr, "--name is meaningless without --window.\n");
3380bbfda8aSnia		usage();
3390bbfda8aSnia	}
3400bbfda8aSnia
341b18c2d1eSnia	/*
342b18c2d1eSnia	 * Being captive and --cfgchk'ing is kinda meaningless.  There's no
343b18c2d1eSnia	 * reason to create a window just to destroy things, and it never
344b18c2d1eSnia	 * adds anything.  And it's one more way we're forcing changes on the
345b18c2d1eSnia	 * X side before we parse the actual config, so let's just disallow
346b18c2d1eSnia	 * it.
347b18c2d1eSnia	 */
348b18c2d1eSnia	if(CLarg.is_captive && CLarg.cfgchk) {
349b18c2d1eSnia		fprintf(stderr, "--window is incompatible with --cfgchk.\n");
350b18c2d1eSnia		usage();
351b18c2d1eSnia	}
352b18c2d1eSnia#endif
353b18c2d1eSnia
3540bbfda8aSnia	/* Guess that's it */
3550bbfda8aSnia	return;
3560bbfda8aSnia}
3570bbfda8aSnia
3580bbfda8aSnia
3590bbfda8aSnia/*
3600bbfda8aSnia * Small utils only currently used in this file.  Over time they may need
3610bbfda8aSnia * to be exported, if we start using them from more places.
3620bbfda8aSnia */
3630bbfda8aSniastatic void
3640bbfda8aSniausage(void)
3650bbfda8aSnia{
3660bbfda8aSnia	/* How far to indent continuation lines */
3670bbfda8aSnia	int llen = 10;
3680bbfda8aSnia
3690bbfda8aSnia	fprintf(stderr, "usage: %s [(--display | -d) dpy]  "
3700bbfda8aSnia#ifdef EWMH
3710bbfda8aSnia	        "[--replace]  "
3720bbfda8aSnia#endif
3730bbfda8aSnia	        "[--single]\n", ProgramName);
3740bbfda8aSnia
3750bbfda8aSnia	fprintf(stderr, "%*s[(--file | -f) initfile]  [--cfgchk]  [--dumpcfg]\n",
3760bbfda8aSnia	        llen, "");
3770bbfda8aSnia
3780bbfda8aSnia#ifdef USEM4
3790bbfda8aSnia	fprintf(stderr, "%*s[--nom4 | -n]  [--keep-defs | -k]  "
3800bbfda8aSnia	        "[(--keep | -K) m4file]\n", llen, "");
3810bbfda8aSnia#endif
3820bbfda8aSnia
3830bbfda8aSnia	fprintf(stderr, "%*s[--verbose | -v]  [--quiet | -q]  [--mono]  "
3840bbfda8aSnia	        "[--xrm resource]\n", llen, "");
3850bbfda8aSnia
3860bbfda8aSnia	fprintf(stderr, "%*s[--version]  [--info]  [--nowelcome | -W]\n",
3870bbfda8aSnia	        llen, "");
3880bbfda8aSnia
389b18c2d1eSnia#ifdef CAPTIVE
3900bbfda8aSnia	fprintf(stderr, "%*s[(--window | -w) [win-id]]  [--name name]\n", llen, "");
391b18c2d1eSnia#endif
3920bbfda8aSnia
3930bbfda8aSnia	/* Semi-intentionally not documenting --clientId/--restore */
3940bbfda8aSnia
3950bbfda8aSnia	fprintf(stderr, "%*s[--help]\n", llen, "");
3960bbfda8aSnia
3970bbfda8aSnia
3980bbfda8aSnia	exit(1);
3990bbfda8aSnia}
4000bbfda8aSnia
4010bbfda8aSnia
4020bbfda8aSniastatic void
4030bbfda8aSniaprint_version(void)
4040bbfda8aSnia{
4050bbfda8aSnia	printf("ctwm %s\n", VersionNumberFull);
4060bbfda8aSnia	if(VCSType && VCSRevision) {
4070bbfda8aSnia		printf(" (%s:%s)\n", VCSType, VCSRevision);
4080bbfda8aSnia	}
4090bbfda8aSnia}
4100bbfda8aSnia
4110bbfda8aSnia
4120bbfda8aSniastatic void
4130bbfda8aSniaDisplayInfo(void)
4140bbfda8aSnia{
4150bbfda8aSnia	char *ctopts;
4160bbfda8aSnia
4170bbfda8aSnia	printf("Twm version:  %s\n", TwmVersion);
4180bbfda8aSnia
4190bbfda8aSnia	ctopts = ctopts_string(" ");
4200bbfda8aSnia	printf("Compile time options : %s\n", ctopts);
4210bbfda8aSnia	free(ctopts);
4220bbfda8aSnia}
4230bbfda8aSnia
4240bbfda8aSnia
4250bbfda8aSniastatic void
4260bbfda8aSniadump_default_config(void)
4270bbfda8aSnia{
4280bbfda8aSnia	int i;
4290bbfda8aSnia
4300bbfda8aSnia	for(i = 0 ; defTwmrc[i] != NULL ; i++) {
4310bbfda8aSnia		printf("%s\n", defTwmrc[i]);
4320bbfda8aSnia	}
4330bbfda8aSnia}
434