'nethack --usage' and '?' menu

Write up a description of how the command line works on UNIX and put
that in new file dat/usagehlp.  Add support for
|nethack --usage | --help | -? | ?
to display it and exit.

Also add a menu entry for nethack's help command to show it during
play.  That can be suppressed by uncommenting new '#define HIDE_USAGE'
in config.h since it won't be useful on servers that don't give
players access to command lines.

New genl_display_file() just writes to stdout.  opt_usage(), which
calls it, might need some suid/sgid handling to make sure the output
is done as the player rather than as nethack.

doc/nethack.6 is already out of date again.
This commit is contained in:
PatR
2022-11-18 16:07:15 -08:00
parent 75eaf59354
commit 9a7f8418ee
9 changed files with 262 additions and 37 deletions

132
dat/usagehlp Normal file
View File

@@ -0,0 +1,132 @@
This is a terse description of nethack's command line arguments.
It is oriented toward UNIX (including descendants such as linux and
macOS) and might not be accurate for other platforms.
This text is available in the menu for the game's '?' command or can be
viewed via 'nethack --usage | more' at the shell prompt.
In all cases, if there is a save file for the chosen character name then
it will be restored, otherwise a new game using that name will start.
nethack
with no arguments; uses character name from run-time configuration file's
OPTIONS=name:character-name entry, or player's username if none.
nethack -u character-name [-X or -D]
'-u character-name' specifies the name to use for this game's character;
-u must be lowercase; the space between it and character-name may
be omitted;
'-X' play in non-scoring explore mode also known as discovery mode;
-X must be uppercase; character starts with a wand of wishing and
player may opt to be life-saved and keep going if character dies;
'-D' run in debug mode also known as wizard mode; -D must be uppercase;
if player is not allowed then nethack will switch to -X.
A character name may have a suffix specifying any or all of role, race,
gender, and alignment such as -u Conan-Bar-Hum-Mal-Neu or -u Tim-Wiz.
The components present must be at least three letters long but can be
longer; their case doesn't matter. See also -p and -r, next,
nethack -p Ppp -r Rrr [-@]
'-p Ppp' specify role; p for "profession" is used because -r is in use;
'Ppp' is three or more letters of the role name such as Val for
Valkyrie; unlike -p itself, upper/lower case of Ppp doesn't matter
'-r Rrr' specify race or species: Hum[an], Elf, Orc, Dwa[rf], Gno[me];
'-@' force non-interactive start; any of role, race, gender, and
alignment that is not specified on the command line or in the
run-time configuration file gets chosen randomly without prompting;
the at-sign might need to be quoted by preceding it with backslash.
The old form for role is also still accepted: -A or -Arc[heologist],
-B or -Bar[barian], -C or -Cav[eman] or -Cavew[oman], -H or -Hea[ler],
-K or -Kni[ght], -M or -Mon[k], -P or -Pri[est] or -Prieste[ss],
-Ran[ger], -R or -Rog[ue], -S or -Sam[urai], -T or -Tou[rist],
-V or -Val[kyrie], -W or -Wiz[ard]. The single-letter form must be in
uppercase, the three or more letter form can be in any case. There is
no single-letter option for the Ranger role.
nethack -DEC[graphics]
nethack -IBM[graphics]
selects the DEC or IBM symbol set to use line-drawing characters on a
text map; might be ignored depending on interface, or ineffective or
even scrambled depending on display capability; -DECgraphics and
-IBMgraphics are mutually exclusive; they can be any case but must use
at least three letters.
nethack -wIii
nethack --windowtype:Iii
where 'Iii' represents an interface designation: tty, curses, X11, or
Qt; only useful if the program was built to support more than one
interface (the game's '#version' command will disclose that); overrides
OPTIONS=windowtype:Iii in run-time configuration file and build-time
default; '-w' or '--windowtype' must be lowercase, the interface
designation itself may be any case; variations '--windowtype Iii' and
'-w Iii' work too.
On Windows, nethack.exe might support both tty and curses; nethackW.exe
supports mswin only. MS-DOS is tty only.
nethack -n
don't show the 'news' file is one is present in nethack's directory.
nethack --nethackrc:File
use File instead of the default run-time configuration file (which is
usually '~/.nethackrc'); the file name should include full path unless
located in nethack's directory;
nethack --no-nethackrc
don't use any run-time configuration file; equivalent to
--nethackrc:/dev/null which behaves as if an empty file.
nethack -dDir
nethack --directory:Dir
could be used to override the build-time value of NETHACKDIR with
location Dir; if used, it should precede other command line arguments.
The assorted options above can be combined on a single command line;
they're listed separately for readability.
*******
Other options which perform some action and then exit rather than play
the game:
nethack --usage
nethack --help
show this text; 'nethack ?' and 'nethack -?' might also work but the
question mark will need to be quoted to prevent the shell from attempting
filename matching.
nethack -s
nethack --scores
show scores for the default character; optional additional arguments:
nethack -s character-name [character-name2 [character-name3 [...]]]
show scores for one or more specific character names (might not be
effective if PERS_IS_UID=1 is specified in nethack's sysconf file);
character names may be preceded by '-u' but that isn't required;
special character-name "all" is used to display all scores that pass
other criteria;
nethack -s -p Ppp -r Rrr
show scores for specific roles or races; multiple instances can be used;
if both '-p' and '-r' are used, scores that match either will be shown
rather than scores that match both;
nethack -s -v
show scores only for the current version if the high scores file (record)
also contains entries for any older versions; when -v is used, it should
immediately follow -s or --scores, preceding any name(s) or -p or -r;
nethack -dDir -s
nethack --directory:Dir -s
as above; alternative directory, if specified, should come first.
nethack --version
nethack --version:paste
'--version' display the program's version number and exit;
'--version:paste' display version number and also copy it into system
pasteboard (should work on macOS and Windows; might not work on other
systems) so that it could be copied from there into a subsequent email
or web contact form, then exit.
nethack --showpaths
list expected locations for various files and directorys, then exit;
includes the name and location for the run-time configuration file which
can vary from platform to platform.

View File

@@ -421,6 +421,15 @@
*/ */
#endif /* CHDIR */ #endif /* CHDIR */
/*
* Define HIDE_USAGE to keep the "description of command line" out of
* the help menu if players have no access to a command line or if that
* is radically different from the description for UNIX in dat/usagehlp
* (a better solution for that would be a separate file and different
* value for USAGEHELP in global.h).
*/
/* #define HIDE_USAGE */ /* */
/* /*
* Section 3: Definitions that may vary with system type. * Section 3: Definitions that may vary with system type.
* For example, both schar and uchar should be short ints on * For example, both schar and uchar should be short ints on

View File

@@ -3303,6 +3303,7 @@ extern char *encglyph(int);
extern int decode_glyph(const char *str, int *glyph_ptr); extern int decode_glyph(const char *str, int *glyph_ptr);
extern char *decode_mixed(char *, const char *); extern char *decode_mixed(char *, const char *);
extern void genl_putmixed(winid, int, const char *); extern void genl_putmixed(winid, int, const char *);
extern void genl_display_file(const char *, boolean);
extern boolean menuitem_invert_test(int, unsigned, boolean); extern boolean menuitem_invert_test(int, unsigned, boolean);
/* ### windows.c ### */ /* ### windows.c ### */

View File

@@ -26,6 +26,7 @@
#define LICENSE "license" /* file with license information */ #define LICENSE "license" /* file with license information */
#define OPTIONFILE "opthelp" /* file explaining runtime options */ #define OPTIONFILE "opthelp" /* file explaining runtime options */
#define OPTMENUHELP "optmenu" /* file explaining #options command */ #define OPTMENUHELP "optmenu" /* file explaining #options command */
#define USAGEHELP "usagehlp" /* file explaining command line use */
#define OPTIONS_USED "options" /* compile-time options, for #version */ #define OPTIONS_USED "options" /* compile-time options, for #version */
#define SYMBOLS "symbols" /* replacement symbol sets */ #define SYMBOLS "symbols" /* replacement symbol sets */
#define EPITAPHFILE "epitaph" /* random epitaphs on graves */ #define EPITAPHFILE "epitaph" /* random epitaphs on graves */

View File

@@ -32,6 +32,9 @@ static void dispfile_optionfile(void);
static void dispfile_optmenu(void); static void dispfile_optmenu(void);
static void dispfile_license(void); static void dispfile_license(void);
static void dispfile_debughelp(void); static void dispfile_debughelp(void);
#ifndef HIDE_USAGE
static void dispfile_usagehelp(void);
#endif
static void hmenu_doextversion(void); static void hmenu_doextversion(void);
static void hmenu_dohistory(void); static void hmenu_dohistory(void);
static void hmenu_dowhatis(void); static void hmenu_dowhatis(void);
@@ -2355,6 +2358,14 @@ dispfile_debughelp(void)
display_file(DEBUGHELP, TRUE); display_file(DEBUGHELP, TRUE);
} }
#ifndef HIDE_USAGE
static void
dispfile_usagehelp(void)
{
display_file(USAGEHELP, TRUE);
}
#endif
static void static void
hmenu_doextversion(void) hmenu_doextversion(void)
{ {
@@ -2389,6 +2400,7 @@ static void
domenucontrols(void) domenucontrols(void)
{ {
winid cwin = create_nhwindow(NHW_TEXT); winid cwin = create_nhwindow(NHW_TEXT);
show_menu_controls(cwin, FALSE); show_menu_controls(cwin, FALSE);
display_nhwindow(cwin, FALSE); display_nhwindow(cwin, FALSE);
destroy_nhwindow(cwin); destroy_nhwindow(cwin);
@@ -2411,6 +2423,9 @@ static const struct {
{ dokeylist, "Full list of keyboard commands." }, { dokeylist, "Full list of keyboard commands." },
{ hmenu_doextlist, "List of extended commands." }, { hmenu_doextlist, "List of extended commands." },
{ domenucontrols, "List menu control keys." }, { domenucontrols, "List menu control keys." },
#ifndef HIDE_USAGE
{ dispfile_usagehelp, "Description of NetHack's command line." },
#endif
{ dispfile_license, "The NetHack license." }, { dispfile_license, "The NetHack license." },
{ docontact, "Support information." }, { docontact, "Support information." },
#ifdef PORT_HELP #ifdef PORT_HELP

View File

@@ -3,6 +3,7 @@
/* NetHack may be freely redistributed. See license for details. */ /* NetHack may be freely redistributed. See license for details. */
#include "hack.h" #include "hack.h"
#include "dlb.h"
#ifdef TTY_GRAPHICS #ifdef TTY_GRAPHICS
#include "wintty.h" #include "wintty.h"
#endif #endif
@@ -533,7 +534,8 @@ static win_request_info *hup_ctrl_nhwindow(winid, int, win_request_info *);
static struct window_procs hup_procs = { static struct window_procs hup_procs = {
WPID(hup), 0L, 0L, WPID(hup), 0L, 0L,
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }, /* colors */
hup_init_nhwindows, hup_init_nhwindows,
hup_void_ndecl, /* player_selection */ hup_void_ndecl, /* player_selection */
hup_void_ndecl, /* askname */ hup_void_ndecl, /* askname */
@@ -577,7 +579,7 @@ static struct window_procs hup_procs = {
hup_void_ndecl, /* status_finish */ hup_void_ndecl, /* status_finish */
genl_status_enablefield, hup_status_update, genl_status_enablefield, hup_status_update,
genl_can_suspend_no, genl_can_suspend_no,
hup_void_fdecl_int, /* update_inventory */ hup_void_fdecl_int, /* update_inventory */
hup_ctrl_nhwindow, hup_ctrl_nhwindow,
}; };
@@ -634,9 +636,10 @@ hup_nhgetch(void)
/*ARGSUSED*/ /*ARGSUSED*/
static char static char
hup_yn_function(const char *prompt UNUSED, hup_yn_function(
const char *resp UNUSED, const char *prompt UNUSED,
char deflt) const char *resp UNUSED,
char deflt)
{ {
if (!deflt) if (!deflt)
deflt = '\033'; deflt = '\033';
@@ -673,23 +676,26 @@ hup_create_nhwindow(int type UNUSED)
/*ARGSUSED*/ /*ARGSUSED*/
static int static int
hup_select_menu(winid window UNUSED, int how UNUSED, hup_select_menu(
struct mi **menu_list UNUSED) winid window UNUSED,
int how UNUSED,
struct mi **menu_list UNUSED)
{ {
return -1; return -1;
} }
/*ARGSUSED*/ /*ARGSUSED*/
static void static void
hup_add_menu(winid window UNUSED, hup_add_menu(
const glyph_info *glyphinfo UNUSED, winid window UNUSED,
const anything *identifier UNUSED, const glyph_info *glyphinfo UNUSED,
char sel UNUSED, const anything *identifier UNUSED,
char grpsel UNUSED, char sel UNUSED,
int attr UNUSED, char grpsel UNUSED,
int clr UNUSED, int attr UNUSED,
const char *txt UNUSED, int clr UNUSED,
unsigned int itemflags UNUSED) const char *txt UNUSED,
unsigned int itemflags UNUSED)
{ {
return; return;
} }
@@ -710,10 +716,11 @@ hup_putstr(winid window UNUSED, int attr UNUSED, const char *text UNUSED)
/*ARGSUSED*/ /*ARGSUSED*/
static void static void
hup_print_glyph(winid window UNUSED, hup_print_glyph(
coordxy x UNUSED, coordxy y UNUSED, winid window UNUSED,
const glyph_info *glyphinfo UNUSED, coordxy x UNUSED, coordxy y UNUSED,
const glyph_info *bkglyphinfo UNUSED) const glyph_info *glyphinfo UNUSED,
const glyph_info *bkglyphinfo UNUSED)
{ {
return; return;
} }
@@ -781,9 +788,10 @@ hup_get_color_string(void)
/*ARGSUSED*/ /*ARGSUSED*/
static void static void
hup_status_update(int idx UNUSED, genericptr_t ptr UNUSED, int chg UNUSED, hup_status_update(
int pc UNUSED, int color UNUSED, int idx UNUSED, genericptr_t ptr UNUSED,
unsigned long *colormasks UNUSED) int chg UNUSED, int pc UNUSED,
int color UNUSED, unsigned long *colormasks UNUSED)
{ {
return; return;
} }
@@ -820,8 +828,9 @@ hup_void_fdecl_winid(winid window UNUSED)
/*ARGUSED*/ /*ARGUSED*/
static void static void
hup_void_fdecl_winid_ulong(winid window UNUSED, hup_void_fdecl_winid_ulong(
unsigned long mbehavior UNUSED) winid window UNUSED,
unsigned long mbehavior UNUSED)
{ {
return; return;
} }
@@ -885,8 +894,11 @@ genl_status_finish(void)
} }
void void
genl_status_enablefield(int fieldidx, const char *nm, const char *fmt, genl_status_enablefield(
boolean enable) int fieldidx,
const char *nm,
const char *fmt,
boolean enable)
{ {
status_fieldfmt[fieldidx] = fmt; status_fieldfmt[fieldidx] = fmt;
status_fieldnm[fieldidx] = nm; status_fieldnm[fieldidx] = nm;
@@ -897,9 +909,11 @@ DISABLE_WARNING_FORMAT_NONLITERAL
/* call once for each field, then call with BL_FLUSH to output the result */ /* call once for each field, then call with BL_FLUSH to output the result */
void void
genl_status_update(int idx, genericptr_t ptr, int chg UNUSED, genl_status_update(
int percent UNUSED, int color UNUSED, int idx,
unsigned long *colormasks UNUSED) genericptr_t ptr,
int chg UNUSED, int percent UNUSED,
int color UNUSED, unsigned long *colormasks UNUSED)
{ {
char newbot1[MAXCO], newbot2[MAXCO]; char newbot1[MAXCO], newbot2[MAXCO];
long cond, *condptr = (long *) ptr; long cond, *condptr = (long *) ptr;
@@ -1084,11 +1098,12 @@ static FILE *dumplog_file;
static time_t dumplog_now; static time_t dumplog_now;
char * char *
dump_fmtstr(const char *fmt, char *buf, dump_fmtstr(
boolean fullsubs) /* True -> full substitution for file name, const char *fmt,
False -> partial substitution for char *buf,
'--showpaths' feedback where there's boolean fullsubs) /* True -> full substitution for file name,
no game in progress when executed */ * False -> partial substitution for '--showpaths'
* feedback where there's no game in progress */
{ {
const char *fp = fmt; const char *fp = fmt;
char *bp = buf; char *bp = buf;
@@ -1493,6 +1508,27 @@ genl_putmixed(winid window, int attr, const char *str)
putstr(window, attr, decode_mixed(buf, str)); putstr(window, attr, decode_mixed(buf, str));
} }
/* possibly called to show usage info during command line processing when
an interface hasn't yet been chosen and set up */
void
genl_display_file(const char *fname, boolean complain)
{
char buf[BUFSZ];
dlb *f = dlb_fopen(fname, "r");
if (!f) {
if (complain) /* send complaint to stdout rather than to stderr */
fprintf(stdout, "\nCannot open \"%s\".\n", fname);
} else {
/* straight copy to stdout, no pagination or other interaction */
while (dlb_fgets(buf, BUFSZ, f)) {
if (fputs(buf, stdout) < 0)
break;
}
(void) dlb_fclose(f);
}
}
/* /*
* Window port helper function for menu invert routines to move the decision * Window port helper function for menu invert routines to move the decision
* logic into one place instead of 7 different window-port routines. * logic into one place instead of 7 different window-port routines.

View File

@@ -1191,7 +1191,7 @@ $(TARGETPFX)vision.o: vision.c $(HACK_H)
$(TARGETPFX)weapon.o: weapon.c $(HACK_H) $(TARGETPFX)weapon.o: weapon.c $(HACK_H)
$(TARGETPFX)were.o: were.c $(HACK_H) $(TARGETPFX)were.o: were.c $(HACK_H)
$(TARGETPFX)wield.o: wield.c $(HACK_H) $(TARGETPFX)wield.o: wield.c $(HACK_H)
$(TARGETPFX)windows.o: windows.c $(HACK_H) ../include/wintty.h $(TARGETPFX)windows.o: windows.c $(HACK_H) ../include/dlb.h ../include/wintty.h
$(TARGETPFX)wizard.o: wizard.c $(HACK_H) $(TARGETPFX)wizard.o: wizard.c $(HACK_H)
$(TARGETPFX)worm.o: worm.c $(HACK_H) $(TARGETPFX)worm.o: worm.c $(HACK_H)
$(TARGETPFX)worn.o: worn.c $(HACK_H) $(TARGETPFX)worn.o: worn.c $(HACK_H)

View File

@@ -91,7 +91,7 @@ LUA_VERSION = 5.4.4
# end of configuration # end of configuration
# #
DATHELP = help hh cmdhelp keyhelp history opthelp optmenu wizhelp DATHELP = help hh cmdhelp keyhelp history opthelp optmenu usagehlp wizhelp
SPEC_LEVS = asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua \ SPEC_LEVS = asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua \
juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua \ juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua \

View File

@@ -36,6 +36,7 @@ static void consume_arg(int, int *, char ***);
static void consume_two_args(int, int *, char ***); static void consume_two_args(int, int *, char ***);
static void early_options(int *, char ***, char **); static void early_options(int *, char ***, char **);
static void opt_terminate(void) NORETURN; static void opt_terminate(void) NORETURN;
static void opt_usage(const char *) NORETURN;
static void opt_showpaths(const char *); static void opt_showpaths(const char *);
static void scores_only(int, char **, const char *) NORETURN; static void scores_only(int, char **, const char *) NORETURN;
@@ -571,6 +572,12 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p)
config_error_init(FALSE, "command line", FALSE); config_error_init(FALSE, "command line", FALSE);
/* treat "nethack ?" as a request for usage info; due to shell
processing, player likely has to use "nethack \?" or "nethack '?'"
[won't work if used as "nethack -dpath ?" or "nethack -d path ?"] */
if (*argc_p > 1 && !strcmp((*argv_p)[1], "?"))
opt_usage(*hackdir_p); /* doesn't return */
/* /*
* Both *argc_p and *argv_p account for the program name as (*argv_p)[0]; * Both *argc_p and *argv_p account for the program name as (*argv_p)[0];
* local argc and argv impicitly discard that (by starting 'ndx' at 1). * local argc and argv impicitly discard that (by starting 'ndx' at 1).
@@ -617,6 +624,12 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p)
#endif /* CHDIR */ #endif /* CHDIR */
} }
break; break;
case 'h':
case '?':
if (lopt(arg, ArgValDisallowed, "-help", origarg, &argc, &argv)
|| lopt(arg, ArgValDisallowed, "-?", origarg, &argc, &argv))
opt_usage(*hackdir_p); /* doesn't return */
break;
case 'n': case 'n':
oldargc = argc; oldargc = argc;
if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */ if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */
@@ -653,6 +666,10 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p)
/*NOTREACHED*/ /*NOTREACHED*/
} }
break; break;
case 'u':
if (lopt(arg, ArgValDisallowed, "-usage", origarg, &argc, &argv))
opt_usage(*hackdir_p);
break;
case 'v': case 'v':
if (argcheck(argc, argv, ARG_VERSION) == 2) { if (argcheck(argc, argv, ARG_VERSION) == 2) {
opt_terminate(); opt_terminate();
@@ -689,6 +706,20 @@ opt_terminate(void)
/*NOTREACHED*/ /*NOTREACHED*/
} }
static void
opt_usage(const char *hackdir)
{
#ifdef CHDIR
chdirx(hackdir, TRUE);
#else
nhUse(hackdir);
#endif
dlb_init();
genl_display_file(USAGEHELP, TRUE);
opt_terminate();
}
/* show the sysconf file name, playground directory, run-time configuration /* show the sysconf file name, playground directory, run-time configuration
file name, dumplog file name if applicable, and some other things */ file name, dumplog file name if applicable, and some other things */
static void static void