diff --git a/dat/opthelp b/dat/opthelp index 07aed9fa2..33d098cdb 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -163,6 +163,9 @@ autounlock when attempting to open a locked door or loot [Apply-Key] omitted or skipped and Apply-Key is omitted or you aren't carrying an unlocking tool or you decline to use one boulder override the default boulder symbol [`] +crash_email email address to use when filling in crash reports [] +crash_name name to use when filling in crash reports [] +crash_urlmax length of longest url we can generate for a crash report [] disclose the types of information you want [ni na nv ng nc no] offered at the end of the game (space separated list of two-character values; diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index b5deefcd0..54e701dd7 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1222,6 +1222,10 @@ Toggle the .op autopickup option on/off. Default key is \(oq@\(cq. +.lp "#bugreport" +Bring up a browser window to submit a report to the NetHack Development Team. +Can be disabled at the time the program is built; when enabled, +CRASHREPORTURL must be set in the system configuration file. .lp "#call " Call (name) a monster, or an object in inventory, on the floor, or in the discoveries list, or add an annotation for the @@ -4910,6 +4914,22 @@ window, windowframe, windowtext). If NetHack can, it should wrap long lines of text if they don't fit in the visible area of the window. .hn 2 +Crash Report Options +.pg +Please note that NetHack does not send \fBany\fP information off your +computer unless you manually click submit on a form. +.si +.lp "OPTION=crash_email:\fIemail_address\fP +.lp "OPTION=crash_name:\fIyour_name\fP +.ei +These options are used only to save you some typing on the crash +report and #bugreport forms. +.si +.lp "OPTION=crash_urlmax:\fIbytes\fP +.ei +This option is used to limit the length of the URLs generated and is only +needed if your browser cannot handle arbitrarily long URLs. +.hn 2 Platform-specific Customization options .pg Here are explanations of options that are used by specific platforms or ports @@ -6008,10 +6028,11 @@ large unless it is actively maintained. . .lp CRASHREPORTURL\ =\ If set to -\f(CRhttps://www.nethack.org/common/contact.html\fP -and support is compiled in, brings up a browser window populated with +\f(CRhttps://www.nethack.org/links/cr-37BETA.html\fP +and support is compiled in, brings up a browser window pre-populated with the information needed to report a problem if the game panics or ends -up in an internally inconsistent state. +up in an internally inconsistent state, or if the #bugreport command is +invoked. .hn 1 Scoring .pg diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 7529b5918..29d6edda4 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -26,6 +26,8 @@ \newcommand{\elist}{\end{list}} +\hyphenation{CRASHREPORTURL} + % this will make \tt underscores look better, but requires that % math subscripts will never be used in this document \catcode`\_=12 @@ -1332,6 +1334,12 @@ Show your attributes. Default key is `{\tt \^{}X}'. \item[\tb{\#autopickup}] Toggle the {\it autopickup\/} option. Default key is `{\tt @}'. %.lp +\item[\tb{\#bugreport}] +Bring up a browser window to submit a report to the {\it NetHack Development +Team}. +Can be disabled at the time the program is built; when enabled, +CRASHREPORTURL must be set in the system configuration file. +%.lp \item[\tb{\#call}] Call (name) a monster, or an object in inventory, on the floor, or in the discoveries list, or add an annotation for the @@ -5420,6 +5428,31 @@ If {\it NetHack\/} can, it should wrap long lines of text if they don't fit in the visible area of the window. \elist +%.hn 2 +\subsection*{Crash Report Options} +%.pg + +Please note that NetHack does not send {\textbf any} information off your +computer unless you manually click submit on a form. +%.si +\blist{} +%.lp +\item[OPTION=crash_email:{\it email_address}] +%.lp +\item[OPTION=crash_name:{\it your_name}] +%.ei +\elist +These options are used only to save you some typing on the crash +report and \#bugreport forms. +%.si +\blist{} +%.lp +\item[OPTION=crash_urlmax:{\it bytes}] +%.ei +\elist +This option is used to limit the length of the URLs generated and is only +needed if your browser cannot handle arbitrarily long URLs. + %.hn 2 \subsection*{Platform-specific Customization options} @@ -6616,10 +6649,11 @@ large unless it is actively maintained. %.lp \item[\ib{CRASHREPORTURL}] If set to -{\tt https://www.nethack.org/common/contact.html} +{\tt https://www.nethack.org/links/cr-37BETA.html} and support is compiled in, brings up a browser window populated with the information needed to report a problem if the game panics or ends -up in an internally inconsistent state. +up in an internally inconsistent state, or if the \#bugreport command is +invoked. \elist %.hn 1 diff --git a/doc/config.nh b/doc/config.nh index 659d70757..d8be02055 100644 --- a/doc/config.nh +++ b/doc/config.nh @@ -530,3 +530,12 @@ # Use highlighting in the status lines when it changes? #OPTIONS=hilite_status:hitpoints/30%/bright-magenta/normal +### Crash reports +# CRASHREPORTURL must be set in syscf to enable these options. +# These identify you in crash reports +#OPTIONS=crash_name:Your Name +#OPTIONS=crash_email:user@example.com +# This limits the size of the URL generated for a crash report. Only +# use this if your browser can't handle very long URLs. It should be as +# large as possible. +#OPTIONS=crash_maxurl:4000. diff --git a/include/config.h b/include/config.h index 89e81bab7..13161434f 100644 --- a/include/config.h +++ b/include/config.h @@ -241,12 +241,33 @@ #ifndef CRASHREPORT # ifdef MACOS - /* NB: This needs to be a full path unless it's in the playground. */ -//#define CRASHREPORT "NetHackCrashReport.JavaScript" +# define CRASHREPORT "/usr/bin/open" # endif # ifdef __linux__ - /* NB: This expects to find the nhlua binary as "./nhlua" */ -//#define CRASHREPORT "nhcrashreport.lua" +# define CRASHREPORT "/usr/bin/xdg-open" + /* Define this if the terminal is filled with useless error messages + * when the browser launches. */ +# define CRASHREPORT_EXEC_NOSTDERR +# endif +# ifdef WIN32 +# define CRASHREPORT /* builtin helper */ +# endif +#endif + +#ifdef CRASHREPORT +# ifndef DUMPLOG +# define DUMPLOG // required to get ^P info +# endif +# ifdef MACOS +# define PANICTRACE +# endif +# ifdef __linux__ + # define PANICTRACE +# endif +// This test isn't quite right: CNG is only available from Windows 2000 on. +// But we'll check that at runtime. +# ifdef WIN32 +# define PANICTRACE # endif #endif diff --git a/include/decl.h b/include/decl.h index 79d8f381a..969c0fa79 100644 --- a/include/decl.h +++ b/include/decl.h @@ -235,6 +235,11 @@ struct instance_globals_c { /* dog.c */ char catname[PL_PSIZ]; + /* end.c */ + char *crash_email; // email for crash reports + char *crash_name; // human name for crash reports + int crash_urlmax; // maximum length for the url of a crash report + /* symbols.c */ int currentgraphics; diff --git a/include/extern.h b/include/extern.h index 6fb7e7eb6..8cbecbb29 100644 --- a/include/extern.h +++ b/include/extern.h @@ -819,10 +819,12 @@ extern struct kinfo *find_delayed_killer(int); extern void dealloc_killer(struct kinfo *); extern void save_killers(NHFILE *); extern void restore_killers(NHFILE *); -#ifdef CRASHREPORT -extern boolean submit_web_report(const char *, char *); +#if defined(CRASHREPORT) +extern boolean submit_web_report(int, const char *, const char *); extern void crashreport_init(int, char *[]); extern void crashreport_bidshow(void); +extern boolean swr_add_uricoded(const char *, char **, int *, char *); +extern int dobugreport(void); #endif extern char *build_english_list(char *); #if defined(PANICTRACE) && !defined(NO_SIGNAL) @@ -1804,6 +1806,13 @@ extern int dosuspend(void); extern void nt_regularize(char *); extern int(*nt_kbhit)(void); extern void Delay(int); +# ifdef CRASHREPORT +struct CRctxt; +extern struct CRctxt *ctxp; +extern int win32_cr_helper(char, struct CRctxt *, void *, int); +extern int win32_cr_gettrace(int, char *, int); +extern int *win32_cr_shellexecute(const char *); +# endif #endif /* WIN32 */ #endif /* MICRO || WIN32 */ @@ -2661,7 +2670,7 @@ extern char *get_sound_effect_filename(int32_t seidint, char *buf, size_t bufsz, int32_t); #endif extern char *base_soundname_to_filename(char *, char *, size_t, int32_t); -extern void set_voice(struct monst *, int32_t, int32_t, int32_t); +extern void set_voice(struct monst *, int32_t, int32_t, int32_t); extern void sound_speak(const char *); /* ### sp_lev.c ### */ diff --git a/include/hack.h b/include/hack.h index edb654a2c..66e30ea95 100644 --- a/include/hack.h +++ b/include/hack.h @@ -418,7 +418,7 @@ enum earlyarg { #ifdef WIN32 , ARG_WINDOWS #endif -#ifdef CRASHREPORT +#if defined(CRASHREPORT) , ARG_BIDSHOW #endif }; diff --git a/include/optlist.h b/include/optlist.h index 058c3f9eb..511f85cff 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -226,6 +226,17 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(confirm, Advanced, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &flags.confirm, Term_False, "ask before hitting tame or peaceful monsters") +#ifdef CRASHREPORT + NHOPTC(crash_email, Advanced, PL_NSIZ, opt_in, set_in_game, + No, Yes, No, No, NoAlias, + "email address for reporting") + NHOPTC(crash_name, Advanced, PL_NSIZ, opt_in, set_in_game, + No, Yes, No, No, NoAlias, + "your name for reporting") + NHOPTC(crash_urlmax, Advanced, PL_NSIZ, opt_in, set_in_game, + No, Yes, No, No, NoAlias, + "length of longest url we can generate") +#endif #ifdef CURSES_GRAPHICS NHOPTC(cursesgraphics, Advanced, 70, opt_in, set_in_config, No, Yes, No, No, NoAlias, diff --git a/include/windconf.h b/include/windconf.h index d729c6f7c..d153b69e9 100644 --- a/include/windconf.h +++ b/include/windconf.h @@ -101,12 +101,7 @@ extern char *windows_exepath(void); */ #ifdef __MINGW32__ -#if 0 #define MD_USE_TMPFILE_S -#if !defined(__cplusplus) -extern errno_t tmpfile_s(FILE * restrict * restrict streamptr); -#endif -#endif # #ifdef strncasecmp #undef strncasecmp diff --git a/include/winprocs.h b/include/winprocs.h index 5e0064819..0ddb7356f 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -14,8 +14,9 @@ enum wp_ids { wp_tty = 1, wp_X11, wp_Qt, wp_mswin, wp_curses, wp_chainin, wp_chainout, wp_safestartup, wp_shim, wp_hup, wp_guistubs, wp_ttystubs, #ifdef OUTDATED_STUFF - , wp_mac, wp_Gem, wp_Gnome, wp_amii, wp_amiv + wp_mac, wp_Gem, wp_Gnome, wp_amii, wp_amiv, #endif + wp_trace // XXX do we need this? should chainin/out get an id? TBD }; /* NB: this MUST match chain_procs below */ @@ -107,6 +108,7 @@ extern /* * If you wish to only support one window system and not use procedure * pointers, add the appropriate #ifdef below. + * XXX which is what? */ #define init_nhwindows (*windowprocs.win_init_nhwindows) @@ -359,7 +361,6 @@ struct chain_procs { void (*win_end_menu)(CARGS, winid, const char *); int (*win_select_menu)(CARGS, winid, int, MENU_ITEM_P **); char (*win_message_menu)(CARGS, char, int, const char *); - void (*win_update_inventory)(CARGS, int); void (*win_mark_synch)(CARGS); void (*win_wait_synch)(CARGS); #ifdef CLIPPING @@ -407,6 +408,8 @@ struct chain_procs { void (*win_status_update)(CARGS, int, genericptr_t, int, int, int, unsigned long *); boolean (*win_can_suspend)(CARGS); + void (*win_update_inventory)(CARGS, int); + win_request_info *(*win_ctrl_nhwindow)(CARGS, winid, int, win_request_info *); }; #endif /* WINCHAIN */ diff --git a/src/allmain.c b/src/allmain.c index fdbff9fc7..ac47010bc 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -30,7 +30,7 @@ static void dump_enums(void); void early_init(int argc UNUSED, char *argv[] UNUSED) { -#ifdef CRASHREPORT +#if defined(CRASHREPORT) // Do this as early as possible, but let ports do other things first. crashreport_init(argc, argv); #endif @@ -911,7 +911,7 @@ static const struct early_opt earlyopts[] = { #ifdef WIN32 { ARG_WINDOWS, "windows", 4, TRUE }, #endif -#ifdef CRASHREPORT +#if defined(CRASHREPORT) { ARG_BIDSHOW, "bidshow", 7, FALSE }, #endif }; @@ -935,11 +935,13 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) const char *dashdash = ""; for (idx = 0; idx < SIZE(earlyopts); idx++) { - if (earlyopts[idx].e == e_arg) + if (earlyopts[idx].e == e_arg){ break; + } } - if (idx >= SIZE(earlyopts) || argc < 1) + if (idx >= SIZE(earlyopts) || argc < 1){ return FALSE; + } for (i = 0; i < argc; ++i) { if (argv[i][0] != '-') @@ -1002,10 +1004,12 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) dump_glyphids(); return 2; #endif -#ifdef CRASHREPORT +#if defined(CRASHREPORT) case ARG_BIDSHOW: crashreport_bidshow(); return 2; +#else +#warning "CRASHREPORT CODE NOT INCLUDED" #endif #ifdef WIN32 case ARG_WINDOWS: diff --git a/src/cmd.c b/src/cmd.c index ffc675724..554171e45 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -2558,6 +2558,10 @@ struct ext_func_tab extcmdlist[] = { doattributes, IFBURIED, NULL }, { '@', "autopickup", "toggle the 'autopickup' option on/off", dotogglepickup, IFBURIED, NULL }, +#ifdef CRASHREPORT + { '\0', "bugreport", "file a bug report", + dobugreport, GENERALCMD | NOFUZZERCMD, NULL }, +#endif { 'C', "call", "name a monster, specific object, or type of object", docallcmd, IFBURIED, NULL }, { 'Z', "cast", "zap (cast) a spell", diff --git a/src/decl.c b/src/decl.c index 42ed0f7d3..ff2c66ff1 100644 --- a/src/decl.c +++ b/src/decl.c @@ -272,6 +272,10 @@ const struct instance_globals_c g_init_c = { DUMMY, /* context */ /* dog.c */ DUMMY, /* catname */ + /* end.c */ + NULL, /* crash_email */ + NULL, /* crash_name */ + -1, /* crash_urlmax */ /* symbols.c */ 0, /* currentgraphics */ /* files.c */ diff --git a/src/end.c b/src/end.c index 59f7d76d0..ea5dc15e1 100644 --- a/src/end.c +++ b/src/end.c @@ -21,9 +21,9 @@ #ifndef NO_SIGNAL static void done_intr(int); -#if defined(UNIX) || defined(VMS) || defined(__EMX__) +# if defined(UNIX) || defined(VMS) || defined(__EMX__) static void done_hangup(int); -#endif +# endif #endif static void disclose(int, boolean); static void get_valuables(struct obj *); @@ -37,6 +37,9 @@ static boolean should_query_disclose_option(int, char *); static void dump_plines(void); #endif static void dump_everything(int, time_t); +#ifdef CRASHREPORT +static const char *get_saved_pline(int); +#endif #if defined(__BEOS__) || defined(MICRO) || defined(OS2) || defined(WIN32) ATTRNORETURN extern void nethack_exit(int) NORETURN; @@ -46,9 +49,10 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN; #define done_stopprint gp.program_state.stopprint -#ifndef PANICTRACE -#define NH_abort(x) NH_abort_ -#endif +// XXX is there a configuration that still needs this? +//#ifndef PANICTRACE +//#define NH_abort(x) NH_abort_ +//#endif #ifdef AMIGA #define NH_abort_ Abort(0) @@ -64,7 +68,8 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN; #endif /* !SYSV */ #endif /* !AMIGA */ -#ifdef PANICTRACE +/* NB: CRASHREPORT implies PANICTRACE */ +#if defined(PANICTRACE) #include #ifdef PANICTRACE_LIBC #include @@ -79,32 +84,30 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN; * -requires -g, which may preclude -O on some compilers * * And the UI: if sysopt.crashreporturl, and defined(CRASHREPORT) - * we gather the stacktrace (etc) and launch a helper to submit a bug report + * we gather the stacktrace (etc) and launch a browser to submit a bug report * otherwise we just use stdout. Requires libc for now. */ #ifdef SYSCF -#define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb -#ifdef PANICTRACE_LIBC -#define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc +# define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb +# ifdef PANICTRACE_LIBC +# define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc +# else +# define SYSOPT_PANICTRACE_LIBC 0 +# endif #else -#define SYSOPT_PANICTRACE_LIBC 0 -#endif -#else -#define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2) -#ifdef PANICTRACE_LIBC -#define SYSOPT_PANICTRACE_LIBC 1 -#else -#define SYSOPT_PANICTRACE_LIBC 0 -#endif +# define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2) +# ifdef PANICTRACE_LIBC +# define SYSOPT_PANICTRACE_LIBC 1 +# else +# define SYSOPT_PANICTRACE_LIBC 0 +# endif #endif -#ifdef PANICTRACE static void NH_abort(char *); -#endif #ifndef NO_SIGNAL static void panictrace_handler(int); #endif -static boolean NH_panictrace_libc(char *); +static boolean NH_panictrace_libc(void); static boolean NH_panictrace_gdb(void); #ifndef NO_SIGNAL @@ -171,7 +174,6 @@ panictrace_setsignals(boolean set) } #endif /* NO_SIGNAL */ -#ifdef PANICTRACE static void NH_abort(char *why) { @@ -185,215 +187,394 @@ NH_abort(char *why) return; aborting = TRUE; +#ifdef CRASHREPORT + if(!submit_web_report(1, "Panic", why)) +#endif + { #ifndef VMS - if (gdb_prio == libc_prio && gdb_prio > 0) - gdb_prio++; + if (gdb_prio == libc_prio && gdb_prio > 0) + gdb_prio++; - if (gdb_prio > libc_prio) { - (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc(why))); - } else { - (void) (NH_panictrace_libc(why) || (gdb_prio && NH_panictrace_gdb())); - } + if (gdb_prio > libc_prio) { + (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc())); + } else { + (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb())); + } #else /* VMS */ - /* overload otherwise unused priority for debug mode: 1 = show - traceback and exit; 2 = show traceback and stay in debugger */ - /* if (wizard && gdb_prio == 1) gdb_prio = 2; */ - vms_traceback(gdb_prio); - nhUse(libc_prio); + /* overload otherwise unused priority for debug mode: 1 = show + traceback and exit; 2 = show traceback and stay in debugger */ + /* if (wizard && gdb_prio == 1) gdb_prio = 2; */ + vms_traceback(gdb_prio); + nhUse(libc_prio); #endif /* ?VMS */ + } #ifndef NO_SIGNAL panictrace_setsignals(FALSE); #endif NH_abort_; } -#endif - -/* Build a URL with a query string and try to launch a new browser window - * to report from panic() or impossible(). Requires libc support for - * the stacktrace. Uses memory on the stack to avoid memory allocation - * (but libc can still do anything it wants). */ - -/* size of argument list for execve(2) */ -#define SWR_LINES 20 -/* max stack frames and header lines (details field) */ -#define SWR_FRAMES 20 -#define SWR_ADD(line) {if(xargc<(SWR_LINES-1)) xargv[xargc++] = line;} #ifdef CRASHREPORT # include -# define HASH_PRAGMA_START \ +# ifdef WIN32 +# define HASH_PRAGMA_START +# define HASH_PRAGMA_END +# else +# define HASH_PRAGMA_START \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -# define HASH_PRAGMA_END _Pragma("GCC diagnostic pop"); +# define HASH_PRAGMA_END _Pragma("GCC diagnostic pop"); +# endif # ifdef MACOS # include -# define HASH_CONTEXT CC_MD4_CTX -# define HASH_INIT(ctx) CC_MD4_Init(ctx) -# define HASH_UPDATE(ctx, ptr, len) CC_MD4_Update(ctx, ptr, len) -# define HASH_FINISH(ctx, out) CC_MD4_Final(out, ctx) -# define HASH_RESULT_SIZE CC_MD4_DIGEST_LENGTH +# define HASH_CONTEXTPTR(CTXP) \ + unsigned char tmp[CC_MD4_DIGEST_LENGTH]; \ + CC_MD4_CTX CTXP ## _; \ + CC_MD4_CTX *CTXP = &CTXP ## _ +# define HASH_INIT(ctxp) !CC_MD4_Init(ctxp) +# define HASH_UPDATE(ctx, ptr, len) !CC_MD4_Update(ctx, ptr, len) +# define HASH_FINISH(ctxp) !CC_MD4_Final(tmp, ctxp) +# define HASH_RESULT_SIZE(ctxp) CC_MD4_DIGEST_LENGTH +# define HASH_RESULT(ctx, inp) *inp = (unsigned char *)ctx +# define HASH_CLEANUP(ctxp) +# define HASH_OFLAGS O_RDONLY +# define HASH_BINFILE_DECL char *binfile = argv[0]; +# ifdef BETA +# define HASH_BINFILE \ + if (!binfile || !*binfile) { \ + /* If this triggers, investigate CFBundleGetMainBundle */ \ + /* or CFBundleCopyExecutableURL. */ \ + raw_print("BETA warning: crashreport_init called without useful info"); \ + goto skip; \ + } +# else +# define HASH_BINFILE() \ + if (!binfile || !*binfile) { \ + goto skip; \ + } +# endif # endif # ifdef __linux__ # include -# define HASH_CONTEXT MD4_CTX -# define HASH_INIT(ctx) MD4_Init(ctx) -# define HASH_UPDATE(ctx, ptr, len) MD4_Update(ctx, ptr, len) -# define HASH_FINISH(ctx, out) MD4_Final(out, ctx) -# define HASH_RESULT_SIZE MD4_DIGEST_LENGTH -# endif +# define HASH_CONTEXTPTR(CTXP) \ + unsigned char tmp[MD4_DIGEST_LENGTH]; \ + MD4_CTX CTXP ## _; \ + MD4_CTX *CTXP = &CTXP ## _ +# define HASH_INIT(ctxp) !MD4_Init(ctxp) +# define HASH_UPDATE(ctx, ptr, len) !MD4_Update(ctx, ptr, len) +# define HASH_FINISH(ctxp) !MD4_Final(tmp, ctxp) +# define HASH_RESULT_SIZE(ctxp) MD4_DIGEST_LENGTH +# define HASH_RESULT(ctx, inp) *inp = (unsigned char *)ctx +# define HASH_CLEANUP(ctxp) +# define HASH_OFLAGS O_RDONLY +# define HASH_BINFILE_DECL char binfile[PATH_MAX+1]; +# define HASH_BINFILE() \ + int len = readlink("/proc/self/exe", binfile, sizeof(binfile)-1); \ + if (len>0) { \ + binfile[len] = '\0'; \ + } else { \ + goto skip; \ + } +# endif // __linux__ +# ifdef WIN32 +/* WIN32 takes too much code and is dependent on OS includes we can't + * pull in here, so we call out to code in sys/windows/windsys.c */ +# define HASH_CONTEXTPTR(CTXP) +# define HASH_INIT(ctxp) win32_cr_helper('i',ctxp, NULL, 0) +# define HASH_UPDATE(ctxp, ptr, len) win32_cr_helper('u',ctxp, ptr, len) +# define HASH_FINISH(ctxp) win32_cr_helper('f',ctxp,NULL,0) +# define HASH_CLEANUP(ctxp) win32_cr_helper('c',ctxp, NULL, 0) +# define HASH_RESULT_SIZE(ctxp) win32_cr_helper('s',ctxp,NULL,0) +# define HASH_RESULT(ctxp, inp) win32_cr_helper('r',ctxp,inp,0) +# define HASH_OFLAGS _O_RDONLY | _O_BINARY +# define HASH_BINFILE_DECL char *binfile; +# define HASH_BINFILE() \ + if(win32_cr_helper('b',NULL,&binfile,0)){ \ + goto skip; \ + } +# endif // WIN32 // Binary ID - Use only as a hint to contact.html for recognizing our own // binaries. This is easily spoofed! -static char bid[(2*HASH_RESULT_SIZE)+1]; +static char bid[40]; /* ARGSUSED */ void crashreport_init(int argc UNUSED, char *argv[] UNUSED){ - unsigned char tmp[HASH_RESULT_SIZE]; + static int once=0; if(once++) return; // NetHackW.exe calls us twice + HASH_BINFILE_DECL; HASH_PRAGMA_START - HASH_CONTEXT ctx; - HASH_INIT(&ctx); -#ifdef MACOS - char *binfile = argv[0]; - if (!binfile || !*binfile) { -# ifdef BETA - // If this triggers, investigate CFBundleGetMainBundle - // or CFBundleCopyExecutableURL. - raw_print("BETA warning: crashreport_init called without useful info"); -# endif - goto skip; - } -#endif -#ifdef __linux__ - char binfile[PATH_MAX+1]; - int len = readlink("/proc/self/exe", binfile, sizeof(binfile)-1); - if (len>0) { - binfile[len] = '\0'; - } else { - goto skip; - } -#endif - int fd = open(binfile, O_RDONLY, 0); + HASH_CONTEXTPTR(ctxp); + if(HASH_INIT(ctxp)) goto skip; + HASH_BINFILE(); // Does "goto skip" on error. + + int fd = open(binfile, HASH_OFLAGS, 0); if (fd == -1) { # ifdef BETA raw_printf("open e=%s",strerror(errno)); # endif goto skip; } + int segsize; - char segment[4096]; + unsigned char segment[4096]; while (0 < (segsize = read(fd, segment,sizeof(segment)))) { - HASH_UPDATE(&ctx, segment, segsize); + if(HASH_UPDATE(ctxp, segment, segsize)) goto skip; } - HASH_FINISH(&ctx, tmp); + if(segsize < 0) { + close(fd); + goto skip; + } + if(HASH_FINISH(ctxp)) goto skip; close(fd); char *p = bid; - unsigned char *in = &tmp[0]; - char cnt=HASH_RESULT_SIZE; + unsigned char *in; + HASH_RESULT(ctxp,&in); + /* Just in case, make sure not to overflow the bid buffer. */ + char cnt=min(HASH_RESULT_SIZE(ctxp), (sizeof(bid)-1)); while (cnt--) { - p += snprintf(p, HASH_RESULT_SIZE-(p-bid), "%02x",*(in++)); + p += snprintf(p, HASH_RESULT_SIZE(ctxp) - (p - bid), "%02x", *(in++)); } *p = '\0'; return; skip: strncpy((char *)bid,"unknown",sizeof(bid)-1); + HASH_CLEANUP(ctxp); HASH_PRAGMA_END } -#undef HASH_CONTEXT +#undef HASH_CONTEXTPTR #undef HASH_INIT #undef HASH_UPDATE #undef HASH_FINISH +#undef HASH_CLEANUP +#undef HASH_RESULT #undef HASH_RESULT_SIZE #undef HASH_PRAGMA_START #undef HASH_PRAGMA_END +#undef HASH_BINFILE_DECL +#undef HASH_BINFILE void crashreport_bidshow(void){ - raw_print(bid); +#if defined(WIN32) && !defined(WIN32CON) + if(0==win32_cr_helper('D', ctxp, bid, 0)) +#endif + { + raw_print(bid); +#ifdef WIN32notyet + wait_synch(); +#endif + } } +/* Build a URL with a query string and try to launch a new browser window + * to report from panic() or impossible(). Requires libc support for + * the stacktrace. Uses memory on the stack to avoid memory allocation + * (on most platforms) (but libc can still do anything it wants). */ + +// No theoretial limit for URL length but reality is messy. +// This should work on all modern platforms. +#ifndef MAX_URL +# define MAX_URL 8192 +#endif +#ifndef SWR_FRAMES +# define SWR_FRAMES 20 +#endif + +// mark holds the initial eos; if we can't get the value in +// then we can remove the whole item if desired. For other +// semantics, caller can handle mark. +#define SWR_ADD(str) \ + utmp = strlen(str); \ + mark = uend; \ + if(utmp >= urem) goto full; \ + strncpy(uend, str, utmp); \ + uend += utmp; urem -= utmp; \ + *uend = '\0'; + +// NB: on overflow this rolls us back to mark, so if we don't +// want to roll back to the last SWR_ADD, update mark before +// calling this macro. +#define SWR_ADD_URIcoded(str) \ + if(swr_add_uricoded(str, &uend, &urem, mark))goto full; + +// On overflow, truncate to markp (but only if markp != NULL). boolean -submit_web_report(const char *msg, char *why){ - if (sysopt.crashreporturl) { - const char *xargv[SWR_LINES]; - char version[100]; - char versionstring[200]; // used twice as a temp - int xargc = 0; - char wholetrace[SWR_LINES*80]; // XXX roughly 71 on MacOS, plus buffer - int prelines = 0; // count of lines in trace header - char nbuf[6]; // number buffer - extern char **environ; - pid_t pid; - - SWR_ADD(CRASHREPORT); - SWR_ADD(sysopt.crashreporturl); - // then pairs of key value - // subject, generate something useful - SWR_ADD("subject"); - snprintf(version, sizeof(version), "%s report for NetHack %s", - msg, version_string(versionstring, sizeof(versionstring))); - SWR_ADD(version); - // name: someday, this might be stored in nethackcnf - // email: someday, this might be stored in nethackcnf - // gitver, pull from version.c - SWR_ADD("gitver"); - SWR_ADD(getversionstring(versionstring, sizeof(versionstring))); - // hardware: leave for user - // software: leave for user - // comments: leave for user - // details: stack trace - SWR_ADD("details"); - -// XXX header for wholetrace - what other info do we want? -// NB: prelines not tested against size of SWR_FRAMES. -#define SWR_HDR(line) \ - if (endp<&wholetrace[sizeof(wholetrace)]) { \ - endp+=snprintf(endp, sizeof(wholetrace)-(endp-wholetrace), "%s\n",line); \ - prelines++; \ - } -#define SWR_HDRnonl(line) \ - if (endp<&wholetrace[sizeof(wholetrace)]) { \ - endp+=snprintf(endp, sizeof(wholetrace)-(endp-wholetrace), "%s",line); \ - } - char *endp = wholetrace; - wholetrace[0] = 0; - if (why) { - SWR_HDR(why); +swr_add_uricoded(const char *in, char **out, int *remaining, char *markp){ + while(*in){ + if( + isalnum(*in) || + *in == '_' || + *in == '-' || + *in == '.' || + *in == '~' // || + ){ + **out = *in; + (*out)++; + (*remaining)--; + } else if(*in == ' '){ + **out = '+'; + (*out)++; + (*remaining)--; + } else { + if(*remaining <= 3){ + if(markp) *out = markp, *remaining = 0; + **out = '\0'; + return TRUE; + } + int x = snprintf(*out, *remaining, "%%%02X", *in); + *out += x; + *remaining -= x; } + in++; + if(! *remaining){ + if(markp) *out = markp, *remaining = 0; + **out = '\0'; + return TRUE; + } + **out = '\0'; + } + return FALSE; // normal return +} - SWR_HDRnonl("bid: "); - SWR_HDR(bid); +static char url[MAX_URL]; // XXX too bad this isn't allocated as needed +static int urem = MAX_URL; // adjusted for gc.crash_urlmax below +static char *uend = url; +static int utmp; // used inside macros +static char *mark; // holds previous terminator (generally) +boolean +submit_web_report(int cos, const char *msg, const char *why){ + urem = (gc.crash_urlmax < 0 || gc.crash_urlmax > MAX_URL) + ? MAX_URL : min(MAX_URL,gc.crash_urlmax); + char temp[200]; + char temp2[200]; + int countpp=0; // pre and post traceback lines +// URL loaded for creating reports to the NetHack DevTeam +// CRASHREPORTURL=https://nethack.org/links/cr-37BETA.html + if(!sysopt.crashreporturl) return FALSE; + SWR_ADD(sysopt.crashreporturl); + /* cos - operation, v - version */ + snprintf(temp, sizeof(temp), "?cos=%d&v=1",cos); + SWR_ADD(temp); + + /* msg==NULL for #bugreport */ + if(msg){ + SWR_ADD("&subject="); + snprintf(temp, sizeof(temp), "%s report for NetHack %s", + msg, version_string(temp2, sizeof(temp2))); + SWR_ADD_URIcoded(temp); + } + + SWR_ADD("&gitver="); + SWR_ADD_URIcoded(getversionstring(temp2, sizeof(temp2))); + + if(gc.crash_name){ + SWR_ADD("&name="); + SWR_ADD_URIcoded(gc.crash_name); + } + + if(gc.crash_email){ + SWR_ADD("&email="); + SWR_ADD_URIcoded(gc.crash_email); + } + // hardware: leave for user + // software: leave for user + // comments: leave for user + + SWR_ADD("&details="); + if (why) { + SWR_ADD_URIcoded(why); + SWR_ADD_URIcoded("\n"); + mark=uend; + countpp++; + } + + SWR_ADD_URIcoded("bid: "); + SWR_ADD_URIcoded(bid); + SWR_ADD_URIcoded("\n"); + mark=uend; + countpp++; + + int count = 0; + if(cos==1){ +#ifdef WIN32 + count=win32_cr_gettrace(SWR_FRAMES,uend, MAX_URL-(uend-url)); + uend=eos(url); +#else void *bt[SWR_FRAMES]; - int count, x; - char **info, buf[BUFSZ]; + int x; + char **info; count = backtrace(bt, SIZE(bt)); info = backtrace_symbols(bt, count); for (x = 0; x < count; x++) { - copynchars(buf, info[x], (int) sizeof buf - 1); + copynchars(temp, info[x], (int) sizeof temp - 1 - 1); // \n\0 /* try to remove up to 16 blank spaces by removing 8 twice */ - (void) strsubst(buf, " ", ""); - (void) strsubst(buf, " ", ""); - snprintf(endp, SWR_FRAMES*80-(endp-wholetrace), "[%02lu] %s\n", - (unsigned long) x, buf); - endp = eos(endp); + (void) strsubst(temp, " ", ""); + (void) strsubst(temp, " ", ""); + strncat(temp, "\n", sizeof temp - 1); +# if 0 // __linux__ +// not needed for MacOS +// XXX is it actually needed for linux? TBD + snprintf(temp2, sizeof(temp2), "[%02lu]\n", (unsigned long) x); + uend--; // remove the \n we added above + SWR_ADD_URIcoded(temp2); +# endif // linux + SWR_ADD_URIcoded(temp); + mark=uend; } - *(endp-1) = '\0'; // remove last newline - SWR_ADD(wholetrace); +#endif // !WIN32 + } - // detailrows min(actual,50) Guess since we can't know the +#ifdef DUMPLOG + // config.h turns this on, but make it easy to turn off if needed + if(cos==1) { + int k; + SWR_ADD_URIcoded("Latest messages:\n"); + mark=uend; + countpp++; + for(k=0;k<5;k++){ + const char *line = get_saved_pline(k); + if(!line) break; + SWR_ADD_URIcoded(line); + SWR_ADD_URIcoded("\n"); + countpp++; + mark=uend; + } + } +#endif + + // detailrows: Guess since we can't know the // width of the window. - SWR_ADD("detailrows"); - (void)snprintf(nbuf,sizeof(nbuf),"%d",count+prelines); - SWR_ADD(nbuf); - xargv[xargc++] = 0; // terminate array + SWR_ADD("&detailrows="); + (void)snprintf(temp,sizeof(temp),"%d",min(count+countpp,30)); + SWR_ADD_URIcoded(temp); - pid = fork(); +full: ; +//printf("URL=%ld '%s'\n",strlen(url),url); +#ifdef WIN32 + int *rv = win32_cr_shellexecute(url); +// XXX TESTING +printf("ShellExecute returned: %p\n",rv); // >32 is ok +#else + const char *xargv[] = { + CRASHREPORT, + url, + NULL + }; + int pid = fork(); + extern char **environ; if (pid == 0) { +#ifdef CRASHREPORT_EXEC_NOSTDERR + /* Keep the output clean - firefox spews useless errors on + * my system. */ + (void)close(2); + (void)open("/dev/null", O_WRONLY); +#endif execve(CRASHREPORT, (char * const *)xargv, environ); char err[100]; sprintf(err, "Can't start " CRASHREPORT ": %s", strerror(errno)); @@ -401,36 +582,45 @@ submit_web_report(const char *msg, char *why){ } else { int status; errno=0; - // XXX do we _really_ know this is the right pid? (void)waitpid(pid, &status, 0); if (status) { // XXX check could be more precise -#if 0 +#ifdef BETA // Not useful at the moment. XXX char err[100]; - sprintf(err, "pid=%d e=%d status=%0x",wpid,errno,status); + sprintf(err, "pid=%d e=%d status=%0x",pid,errno,status); raw_print(err); #endif return FALSE; } } /* free(info); -- Don't risk it. */ +#endif return TRUE; - } - return FALSE; } + +int +dobugreport(void){ + if(!submit_web_report(2, NULL, "#bugreport command")){ + pline("Unable to send bug report. Please visit %s instead.", + sysopt.crashreporturl + ? sysopt.crashreporturl + : "https://www.nethack.org" + ); + } + return ECMD_OK; +} + #endif /* CRASHREPORT */ #undef SWR_ADD +#undef SWR_ADD_URIcoded #undef SWR_FRAMES #undef SWR_HDR #undef SWR_LINES /*ARGSUSED*/ static boolean -NH_panictrace_libc(char *why UNUSED) +NH_panictrace_libc(void) { -#ifdef CRASHREPORT - if(submit_web_report("Panic",why)) return TRUE; -#endif #ifdef PANICTRACE_LIBC void *bt[20]; @@ -459,13 +649,13 @@ NH_panictrace_libc(char *why UNUSED) * fooVAR (possibly const) variable containing fooPATH */ #ifdef PANICTRACE_GDB -#ifdef SYSCF -#define GDBVAR sysopt.gdbpath -#define GREPVAR sysopt.greppath -#else /* SYSCF */ -#define GDBVAR GDBPATH -#define GREPVAR GREPPATH -#endif /* SYSCF */ +# ifdef SYSCF +# define GDBVAR sysopt.gdbpath +# define GREPVAR sysopt.greppath +# else /* SYSCF */ +# define GDBVAR GDBPATH +# define GREPVAR GREPPATH +# endif /* SYSCF */ #endif /* PANICTRACE_GDB */ static boolean @@ -878,7 +1068,7 @@ panic VA_DECL(const char *, str) ? "." : "\nand it may be possible to rebuild."; -// XXX this is probably wrong if defined(CRASHREPORT) +// XXX this may need an update if defined(CRASHREPORT) TBD if (sysopt.support) raw_printf("To report this error, %s%s", sysopt.support, maybe_rebuild); @@ -984,6 +1174,27 @@ dump_plines(void) } } } + +// lineno==0 gives the most recent message (e.g. "Do you want to call panic..." +// if called from #panic) +static const char * +get_saved_pline(int lineno){ + int p; + int limit = DUMPLOG_MSG_COUNT; + if(lineno >= DUMPLOG_MSG_COUNT) return NULL; + p = (gs.saved_pline_index-1) % DUMPLOG_MSG_COUNT; + + while(limit--){ + if(gs.saved_plines[p]){ // valid line + if(lineno--){ + p = (p-1+DUMPLOG_MSG_COUNT) % DUMPLOG_MSG_COUNT; + } else { + return gs.saved_plines[p]; + } + } + } + return NULL; +} #endif /*ARGSUSED*/ diff --git a/src/files.c b/src/files.c index bb0b32578..d2675fa91 100644 --- a/src/files.c +++ b/src/files.c @@ -4736,6 +4736,9 @@ reveal_paths(void) #endif /* ?UNIX */ raw_print(""); +#if defined(WIN32) && !defined(WIN32CON) + wait_synch(); +#endif } /* ---------- BEGIN TRIBUTE ----------- */ diff --git a/src/mdlib.c b/src/mdlib.c index 749ac87b3..cf19fbbfb 100644 --- a/src/mdlib.c +++ b/src/mdlib.c @@ -678,6 +678,12 @@ static const char *const build_opts[] = { #endif #ifdef SYSCF "system configuration at run-time", +#endif +#ifdef PANICTRACE + "show stack trace on error", +#endif +#ifdef CRASHREPORT + "launch browser to report issues", #endif save_bones_compat_buf, "and basic NetHack features" diff --git a/src/options.c b/src/options.c index 099e3e296..8b9af30fa 100644 --- a/src/options.c +++ b/src/options.c @@ -522,7 +522,7 @@ parseoptions( * determine_ambiguities() * figured out exactly how many characters are required to * unambiguously differentiate one option from all others, and it - * placed that number into each option's alloption[n].minmatch. + * placed that number into each option's allopt[n].minmatch. * */ if (!got_match) @@ -1205,6 +1205,80 @@ optfn_catname( return optn_ok; } +static int +optfn_crash_email(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op) +{ + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + if ((op = string_for_opt(opts, FALSE)) + != empty_optstr) { + gc.crash_email = dupstr(op); + } else + return optn_err; + return optn_ok; + } + if (req == get_val || req == get_cnf_val) { + if (!opts) + return optn_err; + Sprintf(opts, "%s", gc.crash_email); + return optn_ok; + } + return optn_ok; +} + +static int +optfn_crash_name(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op) +{ + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + if ((op = string_for_opt(opts, FALSE)) + != empty_optstr) { + gc.crash_name = dupstr(op); + } else + return optn_err; + return optn_ok; + } + if (req == get_val || req == get_cnf_val) { + if (!opts) + return optn_err; + Sprintf(opts, "%s", gc.crash_name); + return optn_ok; + } + return optn_ok; +} + +static int +optfn_crash_urlmax(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op) +{ + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + if ((op = string_for_opt(opts, FALSE)) + != empty_optstr) { + int temp = atoi(op); + if(temp < 75){ + config_error_add("Invalid value %d for crash_urlmax. Minimum value is 75.",temp); + return optn_err; + } + gc.crash_urlmax = temp; + } else + return optn_err; + return optn_ok; + } + if (req == get_val || req == get_cnf_val) { + if (!opts) + return optn_err; + Sprintf(opts, "%d", gc.crash_urlmax); + return optn_ok; + } + return optn_ok; +} + #ifdef CURSES_GRAPHICS static int optfn_cursesgraphics(int optidx, int req, boolean negated, @@ -6694,8 +6768,6 @@ initoptions(void) { int i; - go.opt_phase = builtin_opt; - initoptions_init(); /* * Call each option function with an init flag and give it a chance * to make any preparations that it might require. We do this @@ -6740,6 +6812,7 @@ initoptions_init(void) #endif int i; + go.opt_phase = builtin_opt; // Did I need to move this here? memcpy(allopt, allopt_init, sizeof(allopt)); determine_ambiguities(); diff --git a/src/pline.c b/src/pline.c index d961c1bd3..996f21cbb 100644 --- a/src/pline.c +++ b/src/pline.c @@ -537,7 +537,7 @@ impossible(const char *s, ...) boolean report = ('y' == yn_function("Report now?","yn",'n',FALSE)); raw_print(""); // prove to the user the character was accepted if(report){ - submit_web_report("Impossible", pbuf); + submit_web_report(1,"Impossible", pbuf); } } #endif diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top index c2e52e250..b7f4a300d 100644 --- a/sys/unix/Makefile.top +++ b/sys/unix/Makefile.top @@ -112,7 +112,7 @@ LUA2NHTOP = ../../.. LUABASELIB = liblua-$(LUA_VERSION).a TOPLUALIB = lib/lua/$(LUABASELIB) -ALLDEP = $(GAME) recover Guidebook $(VARDAT) spec_levs check-dlb check-nhlua +ALLDEP = $(GAME) recover Guidebook $(VARDAT) spec_levs check-dlb # first target is also the default target for 'make' without any arguments all: $(ALLDEP) @@ -148,10 +148,6 @@ luabin: ( cd $(LUATOP) \ && make $(LUAMAKEFILES) all && cd $(LUA2NHTOP) ) -check-nhlua: - ( util/makedefs --grep-defined CRASHREPORT && ( \ - cd $(LUATOP) && make $(LUAMAKEFLAGS) ); true ) - # hints file could set LUATESTTARGET to this if GITSUBMODULES is defined submodules/lua/lua.h: git submodule init submodules/lua diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index 4cc403dcf..097e4d75a 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -291,11 +291,7 @@ VARDIR = $(HACKDIR) POSTINSTALL+= cp -n sys/unix/sysconf $(INSTDIR)/sysconf; \ $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf; \ $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf; \ - chmod $(VARFILEPERM) $(INSTDIR)/sysconf; \ - ( util/makedefs --grep-defined CRASHREPORT && \ - ( cp win/share/nhcrashreport.lua $(INSTDIR) ; \ - chmod 555 $(INSTDIR)/nhcrashreport.lua ; \ - cp $(LUATOP)/lua $(INSTDIR)/nhlua); true) + chmod $(VARFILEPERM) $(INSTDIR)/sysconf; ifneq "$(CCISCLANG)" "" # gdb may not be installed if clang is chosen compiler so the game diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index ed49a8c4f..b570a9389 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -31,6 +31,8 @@ ifndef LIBXPM LIBXPM= -L/opt/X11/lib -lXpm endif +#WANT_WIN_CHAIN=1 + # 4. Other #----------------------------------------------------------------------------- @@ -98,7 +100,7 @@ NHCFLAGS+=-DNOMAIL #NHCFLAGS+=-DNO_CHRONICLE #NHCFLAGS+=-DLIVELOG # not NHCFLAGS - needed for makedefs -CFLAGS+=-DCRASHREPORT=\"NetHackCrashReport.JavaScript\" +CFLAGS+=-DCRASHREPORT=\"/usr/bin/open\" ifdef MAKEFILE_SRC # default @@ -334,10 +336,7 @@ PREINSTALL= . sys/unix/hints/macosx.sh user2 $(GAMEUID); \ POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ - chmod $(VARFILEPERM) $(HACKDIR)/sysconf; \ - util/makedefs --grep-defined CRASHREPORT && \ - ( cp win/macosx/NetHackCrashReport.JavaScript $(HACKDIR) && \ - chmod 0500 $(HACKDIR)/NetHackCrashReport.JavaScript ) + chmod $(VARFILEPERM) $(HACKDIR)/sysconf; else ifdef WANT_SOURCE_INSTALL @@ -353,10 +352,7 @@ CHGRP=/usr/bin/true GAMEPERM = 0700 VARFILEPERM = 0600 VARDIRPERM = 0700 -POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ - util/makedefs --grep-defined CRASHREPORT && \ - ( cp win/macosx/NetHackCrashReport.JavaScript $(HACKDIR) && \ - chmod 0500 $(HACKDIR)/NetHackCrashReport.JavaScript ) +POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; # We can use "make all" to build the whole thing - but it misses some things: MOREALL=$(MAKE) install @@ -385,10 +381,7 @@ PREINSTALL+= (mkdir $(SHELLDIR) || true); POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ - chmod $(VARFILEPERM) $(HACKDIR)/sysconf; \ - util/makedefs --grep-defined CRASHREPORT && \ - ( cp win/macosx/NetHackCrashReport.JavaScript $(HACKDIR) && \ - chmod 0500 $(HACKDIR)/NetHackCrashReport.JavaScript ) + chmod $(VARFILEPERM) $(HACKDIR)/sysconf; endif # !WANT_SHARE_INSTALL @@ -731,7 +724,6 @@ build_package_root: install -p doc/recover.6 $(PKGROOT_UG)/man/man6 install -p doc/Guidebook $(PKGROOT_UG)/doc install -p dat/nhdat $(PKGROOT_UGLN) -#XXX no code to package NetHackCrashReport.JavaScript sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(PKGROOT_UGLN)/sysconf cd dat; install -p $(DATNODLB) ../$(PKGROOT_UGLN) # XXX these files should be somewhere else for good Mac form diff --git a/sys/unix/sysconf b/sys/unix/sysconf index c02b546e0..ffcba2033 100644 --- a/sys/unix/sysconf +++ b/sys/unix/sysconf @@ -159,6 +159,9 @@ GREPPATH=/bin/grep PANICTRACE_GDB=1 PANICTRACE_LIBC=2 +# URL loaded for creating reports to the NetHack DevTeam +#CRASHREPORTURL=https://nethack.org/links/cr-37BETA.html + # 'portable_device_paths' is only supported for Windows. Starting with # 3.6.3, nethack on Windows treats the folder containing nethack.exe and # nethackW.exe as read-only and puts data files which are generated or diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 60eedcc23..32533ceaa 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -151,6 +151,7 @@ main(int argc, char *argv[]) check_linux_console(); #endif + initoptions_init(); initoptions(); #ifdef PANICTRACE ARGV0 = gh.hname; /* save for possible stack trace */ @@ -766,6 +767,7 @@ opt_showpaths(const char *dir) nhUse(dir); #endif iflags.initoptions_noterminate = TRUE; + initoptions_init(); initoptions(); iflags.initoptions_noterminate = FALSE; reveal_paths(); diff --git a/sys/windows/.nethackrc.template b/sys/windows/.nethackrc.template index d7e870cae..7cfdd1b4b 100644 --- a/sys/windows/.nethackrc.template +++ b/sys/windows/.nethackrc.template @@ -233,3 +233,7 @@ OPTIONS=hilite_status:gold/up/yellow/down/brown # St, Dx, Co, In, Wi, Ch OPTIONS=hilite_status:characteristics/up/green/down/red +# CRASHREPORTURL must be set in syscf to enable these options. +# These identify you in crash reports +#OPTIONS=crash_name:Your Name +#OPTIONS=crash_email:user@example.com diff --git a/sys/windows/Makefile.mingw32 b/sys/windows/Makefile.mingw32 index 8201af740..b4dc4cea4 100644 --- a/sys/windows/Makefile.mingw32 +++ b/sys/windows/Makefile.mingw32 @@ -52,9 +52,10 @@ SOUND_LIBRARIES = windsound # #--------------------------------------------------------------- # Do you want debug information in the executable? +# Required for CRASHREPORT. # -DEBUGINFO = N +DEBUGINFO = Y # #--------------------------------------------------------------- @@ -243,6 +244,7 @@ NHV=$(subst ",,$(NHV1)) # TTY - window port files (tty) # MSWIN - window port files (win32) # WCURSES - window port files (curses) +# WCHAIN - window port files (chain) # WSHR - Tile support files # SNDSYS - sound suppport files for win32 # QT - QT window support files @@ -257,6 +259,7 @@ SNDSYS =../sound/windsound MSWSYS =../sys/windows TTY =../win/tty MSWIN =../win/win32 +WCHAIN =../win/chain WCURSES =../win/curses WSHR =../win/share QT =../win/Qt @@ -357,7 +360,7 @@ $(OBJ): CLEAN_DIR = $(GAMEDIR) $(OBJ) -#===============-================================================= +#================================================================= # LUA library # Source from http://www.lua.org/ftp/lua-5.4.6.tar.gz #================================================================= @@ -987,6 +990,15 @@ CFLAGSW += $(NHCURSESFLAGS) NHWONLY += $(addsuffix .o, cursdial cursinit cursinvt cursmain cursmesg cursmisc cursstat curswins guitty) endif +# uncomment for WINCHAIN +#COREOBJS += $(addsuffix .o, wc_chainin wc_chainout wc_trace) + +# XXX mess for testing libbacktrace +ifeq "$(DEBUGINFO)" "Y" +CFLAGS += -I/mingw64/include -g -static -gdwarf +LIBS += -L/mingw64/lib -lbacktrace +endif + nethackw: $(NHWTARGETS) $(GAMEDIR)/NetHackW.exe: $(NHWOBJS) $(NHWRES) $(DATEW_O) $(LUALIB) $(PDCWLIB) | $(GAMEDIR) @@ -1028,6 +1040,9 @@ $(ONHW)/%.o: $(MSWSYS)/%.c $(NHLUAH) | $(ONHW) $(ONHW)/%.o: $(MSWIN)/%.c $(NHLUAH) | $(ONHW) $(cc) $(CFLAGSW) $< -o$@ +$(ONHW)/%.o: $(WCHAIN)/%.c $(NHLUAH) | $(ONHW) + $(cc) $(CFLAGSW) $< -o$@ + $(ONHW)/%.o: $(WSHR)/%.c $(NHLUAH) | $(ONHW) $(cc) $(CFLAGSW) $< -o$@ @@ -1068,6 +1083,7 @@ NHCURSESFLAGS = -DCURSES_GRAPHICS -DCURSES_UNICODE $(PDCURSESFLAGS) -DPDC_NCMOUS CFLAGSNH += $(NHCURSESFLAGS) NHONLY += $(addsuffix .o, cursdial cursinit cursinvt cursmain cursmesg cursmisc cursstat curswins) endif + DATE_O = $(addsuffix .o, $(addprefix $(ONH)/, date)) NHOBJS = $(addprefix $(ONH)/, $(COREOBJS) $(NHONLY)) @@ -1115,6 +1131,9 @@ $(ONH)/%.o: $(MSWSYS)/%.c $(NHLUAH) | $(ONH) $(ONH)/%.o: $(MSWIN)/%.c $(NHLUAH) | $(ONH) $(cc) $(CFLAGSNH) $< -o$@ +$(ONH)/%.o: $(WCHAIN)/%.c $(NHLUAH) | $(ONH) + $(cc) $(CFLAGSNH) $< -o$@ + $(ONH)/%.o: $(WSHR)/%.c $(NHLUAH) | $(ONH) $(cc) $(CFLAGSNH) $< -o$@ @@ -1148,8 +1167,9 @@ CLEAN_FILE += $(NHTARGET) $(NHOBJS) $(NHRES) all: install TO_INSTALL = $(GAMEDIR)/NetHack.exe $(RTARGETS) $(GAMEDIRDLLS) \ - $(addprefix $(GAMEDIR)/, $(addsuffix .template, sysconf .nethackrc) \ - Guidebook.txt NetHack.txt license opthelp record symbols) + $(addprefix $(GAMEDIR)/, \ + $(addsuffix .template, sysconf .nethackrc symbols) \ + Guidebook.txt NetHack.txt license opthelp record) ifeq "$(HAVE_SOUNDLIB)" "Y" TO_INSTALL += $(addprefix $(GAMEDIR)/, $(addsuffix .wav, $(WAVLIST))) @@ -1183,7 +1203,7 @@ $(GAMEDIR)/$(FMODLIBDLL): $(FMODLIBDIR)/$(FMODLIBDLL) cp $< $@ endif -$(GAMEDIR)/symbols: $(DAT)/symbols +$(GAMEDIR)/symbols.template: $(DAT)/symbols cp $< $@ $(GAMEDIR)/NetHack.txt: $(DOC)/nethack.txt diff --git a/sys/windows/sysconf.template b/sys/windows/sysconf.template index 5bb6bcff6..36f3651bc 100644 --- a/sys/windows/sysconf.template +++ b/sys/windows/sysconf.template @@ -121,3 +121,6 @@ HIDEUSAGE=1 # # The location that file synchronization locks are stored (writeable) #LOCKDIR=c:\ProgramData\NetHack\3.7 + +# URL loaded for creating reports to the NetHack DevTeam +#CRASHREPORTURL=https://nethack.org/links/cr-37BETA.html diff --git a/sys/windows/vs/NetHack/NetHack.vcxproj b/sys/windows/vs/NetHack/NetHack.vcxproj index 3ce5fdf6e..a84e06d3a 100644 --- a/sys/windows/vs/NetHack/NetHack.vcxproj +++ b/sys/windows/vs/NetHack/NetHack.vcxproj @@ -49,7 +49,7 @@ WIN32CON;NO_TILE_C;DLB;SAFEPROCS;SND_LIB_WINDSOUND;USER_SOUNDS;_LIB;HAS_STDINT_H;%(PreprocessorDefinitions) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;bcrypt.lib;%(AdditionalDependencies) + kernel32.lib;dbghelp.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;bcrypt.lib;%(AdditionalDependencies) $(SndWavDir);%(AdditionalIncludeDirectories) @@ -297,4 +297,4 @@ - + \ No newline at end of file diff --git a/sys/windows/vs/NetHackW/NetHackW.vcxproj b/sys/windows/vs/NetHackW/NetHackW.vcxproj index 802d37edd..8f97753e9 100644 --- a/sys/windows/vs/NetHackW/NetHackW.vcxproj +++ b/sys/windows/vs/NetHackW/NetHackW.vcxproj @@ -62,7 +62,7 @@ Windows - comctl32.lib;winmm.lib;bcrypt.lib;%(AdditionalDependencies) + dbghelp.lib;comctl32.lib;winmm.lib;bcrypt.lib;%(AdditionalDependencies) $(WinWin32Dir)NethackW.exe.manifest;%(AdditionalManifestFiles) @@ -351,4 +351,4 @@ - + \ No newline at end of file diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index 5966615b4..086db189e 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -545,7 +545,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (getcwd(orgdir, sizeof orgdir) == (char *) 0) error("NetHack: current directory path too long"); #endif - + initoptions_init(); // This allows OPTIONS in syscf on Windows. set_default_prefix_locations(argv[0]); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) @@ -638,6 +638,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (WINDOWPORT(tty)) consoletty_open(1); #endif +#ifdef WINCHAIN + commit_windowchain(); +#endif init_nhwindows(&argc, argv); @@ -812,6 +815,11 @@ process_options(int argc, char * argv[]) argc--; argv++; } +#if defined(CRASHREPORT) + if (argcheck(argc, argv, ARG_BIDSHOW) == 2) { + nethack_exit(EXIT_SUCCESS); + } +#endif if (argc > 1 && !strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { /* avoid matching "-dec" for DECgraphics; since the man page * says -d directory, hope nobody's using -desomething_else diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 8a970aa93..edaa83098 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -24,7 +24,10 @@ #ifdef TTY_GRAPHICS #include "wintty.h" #endif +#include + #ifdef WIN32 +#include /* * The following WIN32 API routines are used in this file. @@ -758,6 +761,281 @@ nt_assert_failed(const char *expression, const char *filepath, int line) expression, filename, line); } +/* Windows helpers for CRASHREPORT etc */ +#ifdef CRASHREPORT +struct CRctxt { + BCRYPT_ALG_HANDLE bah; + BCRYPT_HASH_HANDLE bhh; + PBYTE pbhashobj; + DWORD cbhashobj; /* temp */ + DWORD cbhash; /* hash length */ + DWORD cbdata; /* temp */ + PBYTE pbhash; /* binary hash */ + NTSTATUS st; +} ctxp_ = { NULL, NULL, NULL, 0, 0, 0, NULL, 0 }; +struct CRctxt *ctxp = &ctxp_; // XXX should this now be in gc.* ? + +#define win32err(fn) errname = fn; goto error + +int +win32_cr_helper(char cmd, struct CRctxt *ctxp, void *p, int d){ + char *errname = "unknown"; + switch (cmd) { + default: + /* Not panic - we don't want to upgrade an impossible to a + * panic due to a bug in the CRASHREPORT code. */ + impossible("win_cr_helper bad cmd %d", cmd); + return 1; + case 'D': { + char *bidstr = (char *) p; + wchar_t lbidstr[40]; // sizeof(bid), but const + swprintf_s(lbidstr, 40, L"%S", bidstr); + // XXX TODO: need something that will allow copy of just the bid + return MessageBoxW(NULL, lbidstr, L"bidshow", MB_SETFOREGROUND); + } + break; + case 'i': /* HASH_INIT(ctxp) */ + if (!IsWindowsVistaOrGreater()) + return 1; // CNG not available. + ctxp->bah = NULL; + ctxp->bhh = NULL; + ctxp->pbhashobj = NULL; + ctxp->cbhashobj = 0; + ctxp->cbhash = 0; + ctxp->cbdata = 0; + ctxp->pbhash = NULL; + ctxp->st = 0; + // win32err("test"); // TESTING - FAKE AN ERROR + if (0 > (ctxp->st = BCryptOpenAlgorithmProvider( + &ctxp->bah, BCRYPT_MD4_ALGORITHM, NULL, 0))) { + win32err("BCryptOpenAlgorithmProvider"); + }; + if (0 > (ctxp->st = + BCryptGetProperty(ctxp->bah, BCRYPT_OBJECT_LENGTH, + (unsigned char *) &ctxp->cbhashobj, + sizeof(DWORD), &ctxp->cbdata, 0))) { + win32err("BCryptGetProperty1"); + }; + if (0 + == (ctxp->pbhashobj = + HeapAlloc(GetProcessHeap(), 0, ctxp->cbhashobj))) { + win32err("HeapAlloc1"); + }; + if (0 > (ctxp->st = BCryptGetProperty( + ctxp->bah, BCRYPT_HASH_LENGTH, (PBYTE) &ctxp->cbhash, + sizeof(DWORD), &ctxp->cbdata, 0))) { + win32err("BCryptGetProperty2"); + } + if (0 + == (ctxp->pbhash = + HeapAlloc(GetProcessHeap(), 0, ctxp->cbhash))) { + + win32err("HeapAlloc2\n"); + } + if (0 > BCryptCreateHash(ctxp->bah, &ctxp->bhh, ctxp->pbhashobj, + ctxp->cbhashobj, NULL, 0, 0)) { + win32err("BCryptCreateHash"); + } + break; + case 'u': /* HASH_UPDATE(ctxp, ptr, len) */ + if (0 > (ctxp->st = BCryptHashData(ctxp->bhh, p, d, 0))) { + win32err("BCryptHashData"); + } + break; + case 'f': /* HASH_FINISH(ctxp) */ + if (0 > BCryptFinishHash(ctxp->bhh, ctxp->pbhash, ctxp->cbhash, 0)) { + win32err("BCryptFinishHash"); + } + break; + case 'c': /* HASH_CLEANUP(ctxp) */ + if (ctxp->bah) { + BCryptCloseAlgorithmProvider(ctxp->bah, 0); + } + if (ctxp->bhh) { + BCryptDestroyHash(ctxp->bhh); + } + if (ctxp->pbhashobj) { + HeapFree(GetProcessHeap(), 0, ctxp->pbhashobj); + } + if (ctxp->pbhash) { + HeapFree(GetProcessHeap(), 0, ctxp->pbhash); + } + break; + case 's': /* HASH_RESULT_SIZE(ctxp) */ + return ctxp->cbhash; + case 'r': /* HASH_RESULT(ctxp, resp) */ + *(unsigned char **)p = ctxp->pbhash; + break; + case 'b': /* HASH_BINFILE(NULL,&binfile,0) */ + // XXX This buffer should be allocated, not static (and freed in + // HASH_CLEANUP). + // NB: assumes !longPathAware in manifest (Win10+) + { + static char binfile[MAX_PATH]; + DWORD rv = GetModuleFileNameA(NULL, binfile, sizeof(binfile)); + if (rv == 0 || rv == sizeof(binfile)) + return 1; +#ifdef BETA + printf("FILE '%s'\n", binfile); +#endif + *(unsigned char **) p = (unsigned char *) binfile; + return 0; + } + } + return 0; /* ok */ +error: + raw_printf("WIN32 function %s failed: status=%" PRIx64 "\n", + errname, (uint64)ctxp->st); + return 1; /* fail */ +} +#undef win32err + + +#include +#define MAX_SYM_SIZE 100 +#ifdef __GNUC__ + // gcc can't generate .pdb files. llvm can almost do it. + // For these platforms, use github/ianlancetaylor/libbacktrace. +// XXX this doesn't work yet - we get correct addresses but no symbol info +// XXX so still needs cleanup +// XXX no mark (overflow held to last valid segment) handling yet +#include + +struct userstate { + int error_count; + int good_count; + char *out; + int outsize; + int maxframes; +} userstate; + +//backtrace_full_callback +static int +btfcb_fn(void *us0, uintptr_t pc, const char *filename, + int lineno, const char *fnname){ + struct userstate *us = us0; + //XXX generate a stack frame line +printf("C: pc=%llx f=%s line=%d fn=%s\n",pc,filename,lineno,fnname); + us->good_count++; + return 0; +} + +//backtrace_error_callback +static void +btecb_fn(void *us0, const char *msg, int errnum){ + struct userstate *us = us0; + us->error_count++; + if(errnum < 0){ +printf("E1: M=%s e=%d\n",msg,errnum); + // XXX save error message + } else { +printf("E2: M=%s e=%d\n",msg,errnum); + // errnum is an errno + //XXX save error message with strerror + } +} + +int +win32_cr_gettrace(int maxframes, char *out, int outsize){ + userstate.error_count = 0; + userstate.good_count = 0; + userstate.out = out; + userstate.outsize = outsize; + userstate.maxframes = maxframes; + static char binfile[MAX_PATH];// assumes !longPathAware in manifest (Win10+) + DWORD rv = GetModuleFileNameA(NULL, binfile, sizeof(binfile)); + struct backtrace_state *state + = backtrace_create_state(binfile, 0, btecb_fn, &userstate); + if(!state) return userstate.error_count + userstate.good_count; + rv=backtrace_full(state, 0, btfcb_fn, btecb_fn, &userstate); +printf("rv=%d\n",rv); + // XXX rv not checked + // XXX this API leaks its memory; there is no free function + return userstate.error_count + userstate.good_count; +} +#else +// Use win32 API with Visual Studio (and probably MSVC). +#include + +int +win32_cr_gettrace(int maxframes, char *out, int outsize){ + char *mark = out; + void *frames[200]; // XXX why does VS fail var array? wrong C std? + int x; + int tmp; +#define BSIZE (MAX_SYM_SIZE+50) + char buf[BSIZE]; + HANDLE me = GetCurrentProcess(); + SetLastError(0); + // XXX may need to pass the binary's dir + //XXX check for different flags + if(!SymInitialize(me, NULL, TRUE)){ + snprintf(buf, BSIZE, "no stack trace: SymInitialize: %d\n", + GetLastError()); + return 1; + } + int fcount = CaptureStackBackTrace(0, maxframes,frames,NULL); + if(!fcount)goto finish; + char symbol_info_space[sizeof(SYMBOL_INFO)+MAX_SYM_SIZE]; + SYMBOL_INFO *si = (SYMBOL_INFO *)symbol_info_space; + si->MaxNameLen = MAX_SYM_SIZE; + si->SizeOfStruct = sizeof(SYMBOL_INFO); + + for(x=0;xName[0], (long long int)disp64); + if(swr_add_uricoded(buf, &out, &outsize, mark)) + goto finish; + +#if 1 +// XXX does this block do anything useful? + DWORD disp = (DWORD) disp64; + IMAGEHLP_LINE ihl; + ihl.SizeOfStruct = sizeof(IMAGEHLP_LINE); + if (SymGetLineFromAddr(me, adr, &disp, &ihl)) { + printf("L=%d\n", ihl.LineNumber); + } else { +// 7e/1e7 - no info. May need to call SymLoadModule if we need those addrs +// BUT probably system code, so we don't care - experiment +// printf("SGLFA failed: $%08x\n", GetLastError()); + } +//XXXnow format the line +#endif + } else { + // Error 487 (invalid address) seems to mean + // "I can't find any info for this address". + tmp = snprintf(buf, BSIZE, "%d %p (error %d)\n", + x, frames[x], GetLastError()); + if(swr_add_uricoded(buf, &out, &outsize, mark)) + goto finish; + } + if(tmp < 0 || tmp >= outsize){ // XXX is test now wrong? +//printf("FAIL tmp=%d\n",tmp); + fcount = x+1; + goto finish; + } + mark = out; + } +finish: + SymCleanup(me); + return fcount; // XXX if output truncated, fcount could be wrong +} +#endif + +int * +win32_cr_shellexecute(const char *url){ +//XXX Docs say to do this, but has so many caveats I'm going to try skipping it. +//CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + int *rv = (int*)ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); + return rv; +} +#endif /* CRASHREPORT */ + #endif /* WIN32 */ /*windsys.c*/ diff --git a/win/X11/nethack.rc b/win/X11/nethack.rc index cc6a88277..417679492 100644 --- a/win/X11/nethack.rc +++ b/win/X11/nethack.rc @@ -84,3 +84,8 @@ OPTIONS=catname:Ghisteslwchlohm # 048 035 064 042 \ # 047 045 092 124 124 092 045 047 \ # 047 064 092 064 064 064 092 064 047 + +# CRASHREPORTURL must be set in syscf to enable these options. +# These identify you in crash reports +#OPTIONS=crash_name:Your Name +#OPTIONS=crash_email:user@example.com diff --git a/win/chain/wc_chainin.c b/win/chain/wc_chainin.c index d6df8de4b..6573fe826 100644 --- a/win/chain/wc_chainin.c +++ b/win/chain/wc_chainin.c @@ -108,7 +108,7 @@ chainin_procs_chain( tdp->nprocs = 0; tdp->ndata = 0; tdp->linknum = n; - cibase = 0; + cibase = tdp; break; case WINCHAIN_INIT: tdp = me; @@ -584,7 +584,7 @@ chainin_ctrl_nhwindow( int request, win_request_info *wri) { - boolean rv; + win_request_info *rv; rv = (*cibase->nprocs->win_ctrl_nhwindow)(cibase->ndata, window, request, wri); @@ -609,7 +609,7 @@ struct window_procs chainin_procs = { chainin_display_nhwindow, chainin_destroy_nhwindow, chainin_curs, chainin_putstr, chainin_putmixed, chainin_display_file, chainin_start_menu, chainin_add_menu, chainin_end_menu, - chainin_select_menu, chainin_message_menu, chainin_update_inventory, + chainin_select_menu, chainin_message_menu, chainin_mark_synch, chainin_wait_synch, #ifdef CLIPPING chainin_cliparound, diff --git a/win/chain/wc_chainout.c b/win/chain/wc_chainout.c index 0f4ebcb57..a0198173c 100644 --- a/win/chain/wc_chainout.c +++ b/win/chain/wc_chainout.c @@ -702,12 +702,13 @@ chainout_can_suspend(void *vp) win_request_info * chainout_ctrl_nhwindow( + void *vp, winid window, int request, win_request_info *wri) { struct chainout_data *tdp = vp; - boolean rv; + win_request_info *rv; rv = (*tdp->nprocs->win_ctrl_nhwindow)(window, request, wri); @@ -733,7 +734,8 @@ struct chain_procs chainout_procs = { chainout_destroy_nhwindow, chainout_curs, chainout_putstr, chainout_putmixed, chainout_display_file, chainout_start_menu, chainout_add_menu, chainout_end_menu, chainout_select_menu, - chainout_message_menu, chainout_update_inventory, chainout_mark_synch, + chainout_message_menu, + chainout_mark_synch, chainout_wait_synch, #ifdef CLIPPING chainout_cliparound, diff --git a/win/chain/wc_trace.c b/win/chain/wc_trace.c index d710f4479..6738d0b0b 100644 --- a/win/chain/wc_trace.c +++ b/win/chain/wc_trace.c @@ -9,8 +9,15 @@ #include #include -FILE *wc_tracelogf; /* Should be static, but it's just too useful to have - * access to this logfile from arbitrary other files. */ +#ifdef WIN32 +long getpid(void); +long +getpid(){ + return 0; +} +#endif + +FILE *wc_tracelogf; static unsigned int indent_level; /* Some winfuncs call other winfuncs, so * we need to support nesting. */ @@ -55,7 +62,6 @@ void trace_add_menu(void *,winid, const glyph_info *, const ANY_P *, void trace_end_menu(void *,winid, const char *); int trace_select_menu(void *,winid, int, MENU_ITEM_P **); char trace_message_menu(void *,char, int, const char *); -void trace_update_inventory(void *,int); void trace_mark_synch(void *); void trace_wait_synch(void *); #ifdef CLIPPING @@ -101,6 +107,8 @@ void trace_status_update(void *,int, genericptr_t, int, int, int, unsigned long *); boolean trace_can_suspend(void *); +void trace_update_inventory(void *,int); +win_request_info *trace_ctrl_nhwindow(void *, winid, int, win_request_info *); void trace_procs_init(int dir); void *trace_procs_chain(int cmd, int n, void *me, void *nextprocs, void *nextdata); @@ -144,7 +152,7 @@ trace_procs_chain( void trace_procs_init(int dir) { - char fname[200]; + char tfile[20]; long pid; /* processors shouldn't need this test, but just in case */ @@ -152,8 +160,14 @@ trace_procs_init(int dir) return; pid = (long) getpid(); - Sprintf(fname, "%s/tlog.%ld", HACKDIR, pid); + + Sprintf(tfile, "tlog.%ld", pid); +// XXX FQN_NUMBUF is private to files.c + const char *fname = fqname(tfile, TROUBLEPREFIX,7); + printf("TRACEFILE: %s\n",fname); + fflush(stdout); wc_tracelogf = fopen(fname, "w"); + (void)setvbuf(wc_tracelogf, NULL, _IONBF, 0); if (!wc_tracelogf) { fprintf(stderr, "Can't open trace log file %s: %s\n", fname, strerror(errno)); @@ -578,6 +592,16 @@ trace_update_inventory(void *vp, int arg) POST; } +win_request_info * +trace_ctrl_nhwindow(void *vp, winid w, int request, win_request_info *wri){ + struct trace_data *tdp = vp; + + fprintf(wc_tracelogf, "%sctrl_nhwindow(%d, %d, %p)\n", INDENT, w, request, wri); + PRE; + (*tdp->nprocs->win_ctrl_nhwindow)(tdp->ndata, w, request, wri); + POST; +} + void trace_mark_synch(void *vp) { @@ -1186,7 +1210,8 @@ trace_can_suspend(void *vp) } struct chain_procs trace_procs = { - "+trace", 0, /* wincap */ + "+trace", wp_trace, + 0, /* wincap */ 0, /* wincap2 */ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ /* @@ -1202,7 +1227,7 @@ struct chain_procs trace_procs = { trace_create_nhwindow, trace_clear_nhwindow, trace_display_nhwindow, trace_destroy_nhwindow, trace_curs, trace_putstr, trace_putmixed, trace_display_file, trace_start_menu, trace_add_menu, trace_end_menu, - trace_select_menu, trace_message_menu, trace_update_inventory, + trace_select_menu, trace_message_menu, trace_mark_synch, trace_wait_synch, #ifdef CLIPPING trace_cliparound, @@ -1228,4 +1253,6 @@ struct chain_procs trace_procs = { trace_status_init, trace_status_finish, trace_status_enablefield, trace_status_update, trace_can_suspend, + trace_update_inventory, + trace_ctrl_nhwindow }; diff --git a/win/macosx/NetHackCrashReport.JavaScript b/win/macosx/NetHackCrashReport.JavaScript deleted file mode 100755 index f6f02c88a..000000000 --- a/win/macosx/NetHackCrashReport.JavaScript +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/osascript -l JavaScript - -// NetHack 3.7 tile.h $NHDT-Date: 1693083762 2023/08/26 21:02:42 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.1 $ -// Copyright (c) 2023 Kenneth Lorber -// NetHack may be freely redistributed. See license for details. - -// Call with URL then field value pairs. Opens a new browser window -// to: URL?field=value+field=value..... -// This program encodes the values; fieldnames don't require encoding. - -// Should be installed in the playground. - -function run(argv){ - - var url = argv[0]; - var argcp = 1; - - url += "?cos=1"; // Start the query string and set mode - while(argcp < argv.length){ - url += "&" + argv[argcp] + "=" + encodeURIComponent(argv[argcp+1]) - argcp += 2; - } - - var safari = Application('Safari'); - var nw = safari.make({ new:"document" }); - nw.url = url; - safari.activate(); -} diff --git a/win/share/nhcrashreport.lua b/win/share/nhcrashreport.lua deleted file mode 100755 index a5b073381..000000000 --- a/win/share/nhcrashreport.lua +++ /dev/null @@ -1,38 +0,0 @@ -#!./nhlua --- NetHack 3.7 nhcrashreport.lua $NHDT-Date: 1693083824 2023/08/26 21:03:44 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.0 $ --- Copyright (c) 2023 Kenneth Lorber --- NetHack may be freely redistributed. See license for details. - --- Call with URL then field value pairs. Opens a new browser window --- to: URL?field=value+field=value..... --- This program encodes the values; fieldnames don't require encoding. --- --- Should be installed in the playground. - ----- --- from --- https://github.com/daurnimator/lua-http/blob/master/http/util.lua --- Encodes a character as a percent encoded string -local function char_to_pchar(c) - return string.format("%%%02X", c:byte(1,1)) -end --- encodeURIComponent escapes all characters except the following: alphabetic, decimal digits, - _ . ! ~ * ' ( ) -local function encodeURIComponent(str) - return (str:gsub("[^%w%-_%.%!%~%*%'%(%)]", char_to_pchar)) -end ----- - -function un20(str) - return str:gsub("%%20","+") -end - -url = table.remove(arg,1) .. "?cos=1"; -- Start the query string and set mode -while #arg > 0 do - local field = table.remove(arg,1) - local value = table.remove(arg,1) - url = url .. "&" .. field .. "=" .. un20(encodeURIComponent(value)) -end ---print("url='"..url.."'") -cmd = '/usr/bin/xdg-open "'..url..'"' -os.execute(cmd) -os.exit()