'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 */
/*
* 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.
* 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 char *decode_mixed(char *, 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);
/* ### windows.c ### */

View File

@@ -26,6 +26,7 @@
#define LICENSE "license" /* file with license information */
#define OPTIONFILE "opthelp" /* file explaining runtime options */
#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 SYMBOLS "symbols" /* replacement symbol sets */
#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_license(void);
static void dispfile_debughelp(void);
#ifndef HIDE_USAGE
static void dispfile_usagehelp(void);
#endif
static void hmenu_doextversion(void);
static void hmenu_dohistory(void);
static void hmenu_dowhatis(void);
@@ -2355,6 +2358,14 @@ dispfile_debughelp(void)
display_file(DEBUGHELP, TRUE);
}
#ifndef HIDE_USAGE
static void
dispfile_usagehelp(void)
{
display_file(USAGEHELP, TRUE);
}
#endif
static void
hmenu_doextversion(void)
{
@@ -2389,6 +2400,7 @@ static void
domenucontrols(void)
{
winid cwin = create_nhwindow(NHW_TEXT);
show_menu_controls(cwin, FALSE);
display_nhwindow(cwin, FALSE);
destroy_nhwindow(cwin);
@@ -2411,6 +2423,9 @@ static const struct {
{ dokeylist, "Full list of keyboard commands." },
{ hmenu_doextlist, "List of extended commands." },
{ domenucontrols, "List menu control keys." },
#ifndef HIDE_USAGE
{ dispfile_usagehelp, "Description of NetHack's command line." },
#endif
{ dispfile_license, "The NetHack license." },
{ docontact, "Support information." },
#ifdef PORT_HELP

View File

@@ -3,6 +3,7 @@
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#include "dlb.h"
#ifdef TTY_GRAPHICS
#include "wintty.h"
#endif
@@ -533,7 +534,8 @@ static win_request_info *hup_ctrl_nhwindow(winid, int, win_request_info *);
static struct window_procs hup_procs = {
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_void_ndecl, /* player_selection */
hup_void_ndecl, /* askname */
@@ -577,7 +579,7 @@ static struct window_procs hup_procs = {
hup_void_ndecl, /* status_finish */
genl_status_enablefield, hup_status_update,
genl_can_suspend_no,
hup_void_fdecl_int, /* update_inventory */
hup_void_fdecl_int, /* update_inventory */
hup_ctrl_nhwindow,
};
@@ -634,9 +636,10 @@ hup_nhgetch(void)
/*ARGSUSED*/
static char
hup_yn_function(const char *prompt UNUSED,
const char *resp UNUSED,
char deflt)
hup_yn_function(
const char *prompt UNUSED,
const char *resp UNUSED,
char deflt)
{
if (!deflt)
deflt = '\033';
@@ -673,23 +676,26 @@ hup_create_nhwindow(int type UNUSED)
/*ARGSUSED*/
static int
hup_select_menu(winid window UNUSED, int how UNUSED,
struct mi **menu_list UNUSED)
hup_select_menu(
winid window UNUSED,
int how UNUSED,
struct mi **menu_list UNUSED)
{
return -1;
}
/*ARGSUSED*/
static void
hup_add_menu(winid window UNUSED,
const glyph_info *glyphinfo UNUSED,
const anything *identifier UNUSED,
char sel UNUSED,
char grpsel UNUSED,
int attr UNUSED,
int clr UNUSED,
const char *txt UNUSED,
unsigned int itemflags UNUSED)
hup_add_menu(
winid window UNUSED,
const glyph_info *glyphinfo UNUSED,
const anything *identifier UNUSED,
char sel UNUSED,
char grpsel UNUSED,
int attr UNUSED,
int clr UNUSED,
const char *txt UNUSED,
unsigned int itemflags UNUSED)
{
return;
}
@@ -710,10 +716,11 @@ hup_putstr(winid window UNUSED, int attr UNUSED, const char *text UNUSED)
/*ARGSUSED*/
static void
hup_print_glyph(winid window UNUSED,
coordxy x UNUSED, coordxy y UNUSED,
const glyph_info *glyphinfo UNUSED,
const glyph_info *bkglyphinfo UNUSED)
hup_print_glyph(
winid window UNUSED,
coordxy x UNUSED, coordxy y UNUSED,
const glyph_info *glyphinfo UNUSED,
const glyph_info *bkglyphinfo UNUSED)
{
return;
}
@@ -781,9 +788,10 @@ hup_get_color_string(void)
/*ARGSUSED*/
static void
hup_status_update(int idx UNUSED, genericptr_t ptr UNUSED, int chg UNUSED,
int pc UNUSED, int color UNUSED,
unsigned long *colormasks UNUSED)
hup_status_update(
int idx UNUSED, genericptr_t ptr UNUSED,
int chg UNUSED, int pc UNUSED,
int color UNUSED, unsigned long *colormasks UNUSED)
{
return;
}
@@ -820,8 +828,9 @@ hup_void_fdecl_winid(winid window UNUSED)
/*ARGUSED*/
static void
hup_void_fdecl_winid_ulong(winid window UNUSED,
unsigned long mbehavior UNUSED)
hup_void_fdecl_winid_ulong(
winid window UNUSED,
unsigned long mbehavior UNUSED)
{
return;
}
@@ -885,8 +894,11 @@ genl_status_finish(void)
}
void
genl_status_enablefield(int fieldidx, const char *nm, const char *fmt,
boolean enable)
genl_status_enablefield(
int fieldidx,
const char *nm,
const char *fmt,
boolean enable)
{
status_fieldfmt[fieldidx] = fmt;
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 */
void
genl_status_update(int idx, genericptr_t ptr, int chg UNUSED,
int percent UNUSED, int color UNUSED,
unsigned long *colormasks UNUSED)
genl_status_update(
int idx,
genericptr_t ptr,
int chg UNUSED, int percent UNUSED,
int color UNUSED, unsigned long *colormasks UNUSED)
{
char newbot1[MAXCO], newbot2[MAXCO];
long cond, *condptr = (long *) ptr;
@@ -1084,11 +1098,12 @@ static FILE *dumplog_file;
static time_t dumplog_now;
char *
dump_fmtstr(const char *fmt, char *buf,
boolean fullsubs) /* True -> full substitution for file name,
False -> partial substitution for
'--showpaths' feedback where there's
no game in progress when executed */
dump_fmtstr(
const char *fmt,
char *buf,
boolean fullsubs) /* True -> full substitution for file name,
* False -> partial substitution for '--showpaths'
* feedback where there's no game in progress */
{
const char *fp = fmt;
char *bp = buf;
@@ -1493,6 +1508,27 @@ genl_putmixed(winid window, int attr, const char *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
* 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)were.o: were.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)worm.o: worm.c $(HACK_H)
$(TARGETPFX)worn.o: worn.c $(HACK_H)

View File

@@ -91,7 +91,7 @@ LUA_VERSION = 5.4.4
# 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 \
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 early_options(int *, char ***, char **);
static void opt_terminate(void) NORETURN;
static void opt_usage(const char *) NORETURN;
static void opt_showpaths(const char *);
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);
/* 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];
* 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 */
}
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':
oldargc = argc;
if (!strcmp(arg, "-no-nethackrc")) /* no abbreviation allowed */
@@ -653,6 +666,10 @@ early_options(int *argc_p, char ***argv_p, char **hackdir_p)
/*NOTREACHED*/
}
break;
case 'u':
if (lopt(arg, ArgValDisallowed, "-usage", origarg, &argc, &argv))
opt_usage(*hackdir_p);
break;
case 'v':
if (argcheck(argc, argv, ARG_VERSION) == 2) {
opt_terminate();
@@ -689,6 +706,20 @@ opt_terminate(void)
/*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
file name, dumplog file name if applicable, and some other things */
static void