From: PatR Date: Sat, 12 Feb 2022 19:42:17 +0000 (-0800) Subject: unix command line X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a1bb10e8f6f6c1eca7d6eef67739e48a5c523546;p=nethack unix command line I wanted to be able to specify -windowtype:foo on the command line so that I didn't have to use "NETHACKOPTIONS='windowtype:foo' nethack" and it turned out that such an option already exists, as "-wfoo". I either never knew about that or had completely forgotten it. Anyway, this makes specifying windowtype be more versatile. "-wX11" still works; now "-w X11", "-windowtype=X11", "-windowtype:X11" work too, with "--" variations of the latter too also supported. The long name can be truncated to any leading substring of "windowtype", although it has to be at least "wi" for "--"; "--w" is rejected. Also, any errors reported while processing the command line are treated like config file processing errors rather than just delivered with raw_printf(). On tty at least, they used to vanish when the screen cleared to start the game, with no chance to read them. Here's an example from after this change. It sets windowtype to tty and then overrides that with X11. |% ./nethack --w:Qt --win tty -wX11 -windowtype | | | * Unknown option: --w:Qt. | * Window type [nothing] not recognized. Choices are: tty, curses, X11, Qt. | |2 errors on command line. | | |Hit return to continue: This should probably be better integrated with argcheck() or vice versa but the only change to that was a couple of formatting bits. Anything that already worked should continue to work just the same, aside from the improvement to the error feedback. --- diff --git a/src/allmain.c b/src/allmain.c index 74260e859..7fcf18b19 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -851,7 +851,7 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) { int i, idx; boolean match = FALSE; - char *userea = (char *)0; + char *userea = (char *) 0; const char *dashdash = ""; for (idx = 0; idx < SIZE(earlyopts); idx++) { @@ -859,7 +859,7 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) break; } if ((idx >= SIZE(earlyopts)) || (argc <= 1)) - return FALSE; + return FALSE; for (i = 0; i < argc; ++i) { if (argv[i][0] != '-') @@ -873,7 +873,8 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) match = match_optname(userea, earlyopts[idx].name, earlyopts[idx].minlength, earlyopts[idx].valallowed); - if (match) break; + if (match) + break; } if (match) { diff --git a/src/files.c b/src/files.c index cb47ad4f0..edbc7b1ec 100644 --- a/src/files.c +++ b/src/files.c @@ -2992,7 +2992,9 @@ config_error_done(void) } #endif if (n) { - pline("\n%d error%s in %s.\n", n, plur(n), + boolean cmdline = !strcmp(config_error_data->source, "command line"); + + pline("\n%d error%s %s %s.\n", n, plur(n), cmdline ? "on" : "in", *config_error_data->source ? config_error_data->source : configfile); wait_synch(); diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 59f41a737..ef27160c0 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -348,30 +348,43 @@ main(int argc, char *argv[]) static void process_options(int argc, char *argv[]) { + static char novalue[] = "[nothing]"; /* note: not 'const' */ + char *p, *arg, *origarg; int i, l; + config_error_init(FALSE, "command line", FALSE); /* * Process options. + * + * We don't support "-xyz" as shortcut for "-x -y -z" and we only + * simulate longopts by allowing "--foo" for "-foo" when the user + * specifies at least 2 characters of leading substring for "foo". + * If "foo" takes a value, both "--foo=value" and "--foo value" work. */ while (argc > 1 && argv[1][0] == '-') { argv++; argc--; - l = (int) strlen(*argv); + arg = origarg = argv[0]; + /* allow second dash if arg is longer than one character */ + if (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0' + /* "--a=b" violates the "--" ok when at least 2 chars long rule */ + && (arg[3] != '\0' && arg[3] != '=' && arg[3] != ':')) + ++arg; + l = (int) strlen(arg); /* must supply at least 4 chars to match "-XXXgraphics" */ if (l < 4) l = 4; - switch (argv[0][1]) { + switch (arg[1]) { case 'D': case 'd': - if ((argv[0][1] == 'D' && !argv[0][2]) - || !strcmpi(*argv, "-debug")) { + if ((arg[1] == 'D' && !arg[2]) || !strcmpi(arg, "-debug")) { wizard = TRUE, discover = FALSE; - } else if (!strncmpi(*argv, "-DECgraphics", l)) { + } else if (!strncmpi(arg, "-DECgraphics", l)) { load_symset("DECGraphics", PRIMARY); switch_symbols(TRUE); } else { - raw_printf("Unknown option: %.60s", *argv); + config_error_add("Unknown option: %.60s", origarg); } break; case 'X': @@ -383,8 +396,8 @@ process_options(int argc, char *argv[]) break; #endif case 'u': - if (argv[0][2]) { - (void) strncpy(g.plname, argv[0] + 2, sizeof g.plname - 1); + if (arg[2]) { + (void) strncpy(g.plname, arg + 2, sizeof g.plname - 1); g.plnamelen = 0; /* plname[] might have -role-race attached */ } else if (argc > 1) { argc--; @@ -392,22 +405,22 @@ process_options(int argc, char *argv[]) (void) strncpy(g.plname, argv[0], sizeof g.plname - 1); g.plnamelen = 0; } else { - raw_print("Player name expected after -u"); + config_error_add("Player name expected after -u"); } break; case 'I': case 'i': - if (!strncmpi(*argv, "-IBMgraphics", l)) { + if (!strncmpi(arg, "-IBMgraphics", l)) { load_symset("IBMGraphics", PRIMARY); load_symset("RogueIBM", ROGUESET); switch_symbols(TRUE); } else { - raw_printf("Unknown option: %.60s", *argv); + config_error_add("Unknown option: %.60s", origarg); } break; case 'p': /* profession (role) */ - if (argv[0][2]) { - if ((i = str2role(&argv[0][2])) >= 0) + if (arg[2]) { + if ((i = str2role(&arg[2])) >= 0) flags.initrole = i; } else if (argc > 1) { argc--; @@ -417,8 +430,8 @@ process_options(int argc, char *argv[]) } break; case 'r': /* race */ - if (argv[0][2]) { - if ((i = str2race(&argv[0][2])) >= 0) + if (arg[2]) { + if ((i = str2race(&arg[2])) >= 0) flags.initrace = i; } else if (argc > 1) { argc--; @@ -427,26 +440,59 @@ process_options(int argc, char *argv[]) flags.initrace = i; } break; - case 'w': /* windowtype */ - config_error_init(FALSE, "command line", FALSE); - choose_windows(&argv[0][2]); - config_error_done(); + case 'w': /* windowtype: "-wfoo" or "-w[indowtype]=foo" + * or "-w[indowtype]:foo" or "-w[indowtype] foo" */ + if ((p = index(arg, '=')) == 0) + p = index(arg, ':'); + l = (int) (p ? (p - arg) : strlen(arg)); + if (!strncmp(arg, "-windowtype", l)) { + /* "-windowtype[=foo]" */ + if (p) + ++p; /* past '=' or ':' */ + else + p = eos(arg); /* we have "-w[indowtype]" w/o "=foo" + * so we'll take foo from next element */ + } else { + /* "-w..." but not "-w[indowtype[=foo]]" */ + if (!p) { + p = &arg[2]; /* past 'w' of "-wfoo" */ + } else { + /* "-w...=foo" but not "-w[indowtype]=foo" */ + config_error_add("Unknown option: %.60s", origarg); + continue; + } + } + if (!*p) { + /* "-w[indowtype]" w/o '='/':' use next element for "foo" */ + if (argc > 1) + --argc, ++argv, p = argv[0]; + else + p = novalue; /* there is no next element */ + } + choose_windows(p); break; case '@': flags.randomall = 1; break; + case '-': + /* "--" or "--x" or "--x=y"; need at least 2 chars after the + dashes in order to accept "--x" as an alternative to "-x"; + don't just silently ignore it */ + config_error_add("Unknown option: %.60s", origarg); + break; default: + /* default for "-x" is to play as the role that starts with "x" */ if ((i = str2role(&argv[0][1])) >= 0) { flags.initrole = i; break; } - /* else raw_printf("Unknown option: %.60s", *argv); */ + /* else config_error_add("Unknown option: %.60s", origarg); */ } } #ifdef SYSCF if (argc > 1) - raw_printf("MAXPLAYERS are set in sysconf file.\n"); + config_error_add("MAXPLAYERS are set in sysconf file.\n"); #else /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS */ if (argc > 1) @@ -462,6 +508,8 @@ process_options(int argc, char *argv[]) if (!g.locknum || (sysopt.maxplayers && g.locknum > sysopt.maxplayers)) g.locknum = sysopt.maxplayers; #endif + /* empty or "N errors on command line" */ + config_error_done(); } #ifdef CHDIR