Unlike sfrotz, xfrotz will segfault if you run it as xfrotz --help
If a user attempts to use the standard --help command line flag to request program help, sfrotz will not recognize the flag, but because it doesn't recognize the flag, it will report "sfrotz: unrecognized option '--help'", then print the standard usage summary and exit with an error result:
$ sfrotz --help
sfrotz: unrecognized option '--help'
FROTZ V2.55 - SDL graphics and audio interface.
An interpreter for all Infocom and other Z-Machine games.
Syntax: sfrotz [options] story-file [blorb file]
-a watch attribute setting -A watch attribute testing
-b <colourname> background colour -c # context lines
-C <file> load this configuration file -d disable color
-f <colorname> foreground colour -F fullscreen mode
-H # screen height -i ignore runtime errors
-I # interpreter number -l # left margin
-L <file> load this save file -o watch object movement
-O watch object locating -P alter piracy opcode
-q quiet (disable sound) -r # right margin
-s # random number seed value -S # transcript width
-t set Tandy bit -u # slots for multiple undo
-W # screen width -x expand abbreviations g/x/z
-X show extended options -v show version information
-Z # error checking (see below)
Error checking: 0 none, 1 first only (default), 2 all, 3 exit after any error.
More options and information are in the manual page. Type "man sfrotz".
This is because sfrotz's os_process_arguments() calls parse_options(), which uses getopt(). When faced with an unknown command line option, getopt will return the character '?'. parse_options() recognizes this and takes appropriate action:
// sf_util.c lines 380:382
if (c == '?') {
usage(USAGE_NORMAL);
os_quit(EXIT_FAILURE);
In both cases this is controlled by the main() function shared by all frotz variants:
// common/main.c 114:131
{
init_header();
init_setup();
os_init_setup();
os_process_arguments(argc, argv);
init_buffer();
init_err();
init_memory();
init_process();
init_sound();
os_init_screen();
init_undo();
z_restart();
interpret();
reset_screen();
reset_memory();
os_reset_screen();
os_quit(EXIT_SUCCESS);
return 0;
But when xfrotz gets an argument of --help, bad things happen:
$ xfrotz --help
Fatal error: Cannot open story file
[3] 1235753 segmentation fault (core dumped) xfrotz --help
That's because the argument passes right through os_process_arguments(), which treats it as the name of a story file to load. The xfrotz startup then continues on its initialization journey (above), through init_buffer() and init_err(), all the way to init_memory() from fastmem.c.
There, upon failing to load --help as a story file, it will attempt to report this failure with an os_fatal():
// fastmem.c lines 468:470
/* Open story file */
if ((story_fp = os_load_story()) == NULL)
os_fatal("Cannot open story file");
Problem is that, unlike os_quit(), os_fatal() in the xfrotz code assumes it's outputting to a graphical display, and calls several functions that use X protocol features, including os_set_text_style():
// x_init.c lines 93:120
void os_fatal(const char *s, ...)
{
va_list m;
char errorstring[81];
os_beep(BEEP_HIGH);
fprintf(stderr, "\nFatal error: ");
va_start(m, s);
vfprintf(stderr, s, m);
vsnprintf(errorstring, sizeof(char) * 80, s, m);
va_end(m);
fprintf(stderr, "\n\n");
os_set_text_style(BOLDFACE_STYLE);
print_c_string("Fatal error: ");
os_set_text_style(NORMAL_STYLE);
print_c_string(errorstring);
new_line();
if (f_setup.ignore_errors) {
os_display_string((zchar *) "Continuing anyway...");
new_line();
new_line();
}
os_reset_screen();
os_quit(EXIT_FAILURE);
} /* os_fatal */
os_set_text_style() attempts to call XSetFont(), passing it the current_gc which will be set to either reversed_gc or normal_gc depending on its argument.
// x_text.c lines 292:308
void os_set_text_style(int new_style)
{
if (new_style & REVERSE_STYLE)
current_gc = reversed_gc;
else
current_gc = normal_gc;
current_font_info = get_font(current_zfont, new_style);
if (current_font_info == NULL) {
fprintf(stderr, "Could not find font for style %d\n",
new_style);
current_font_info = get_font(current_zfont, 0);
}
XSetFont(dpy, current_gc, current_font_info->fid);
current_style = new_style;
} /* os_set_text_style */
Unfortunately, none of the *_gc globals have been initialized yet. That won't happen until os_init_screen() is called, which happens waaaay after init_memory() in the main() code. So os_set_text_style() calls XSetFont(dpy, NULL, current_font_info->fid) and...