Separated library source code from testing code, fixing #6
[projects/chimara/chimara.git] / interpreters / nitfol / startunix.c
1 #line 228 "opt2glkc.pl"
2 #include "nitfol.h"
3 #include <libchimara/glkstart.h>
4 #ifdef DEBUGGING
5 #include <signal.h>
6 #endif
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <ctype.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <dirent.h>
13 #include <limits.h>
14
15 static char *game_filename = NULL;
16
17 static void set_game_filename(const char *name)
18 {
19   n_free(game_filename);
20   game_filename = 0;
21
22 #if defined(_GNU_SOURCE)
23   game_filename = canonicalize_file_name(name);
24 #else
25 #if defined(_BSD_SOURCE) || defined(_XOPEN_SOURCE)
26   game_filename = (char *) n_malloc(PATH_MAX);
27   if(!realpath(name, game_filename)) {
28     n_free(game_filename);
29     game_filename = 0;
30   }
31 #else
32 #ifdef __DJGPP__
33   game_filename = (char *) n_malloc(FILENAME_MAX);
34   _fixpath(name, game_filename);
35 #endif
36 #endif
37 #endif
38
39   if(!game_filename)
40     game_filename = n_strdup(name);
41 }
42
43
44 strid_t startup_findfile(void)
45 {
46   static DIR *dir = NULL;
47   static char *pathstart = NULL;
48   static char *path = NULL;
49   strid_t stream;
50   struct dirent *d;
51   char *name = NULL;
52
53   if(!pathstart) {
54     char *p = search_path;
55     if(!p)
56       return 0;
57     pathstart = n_strdup(p);
58     if(!(path = n_strtok(pathstart, ":"))) {
59       n_free(pathstart);
60       pathstart = 0;
61       return 0;
62     }
63   }
64
65   do {
66     if(!dir) {
67       dir = opendir(path);
68       if(!dir) {
69         n_free(pathstart);
70         pathstart = 0;
71         return 0;
72       }
73     }
74     d = readdir(dir);
75     if(!d) {
76       closedir(dir);
77       dir = NULL;
78       if(!(path = n_strtok(NULL, ":"))) {
79         n_free(pathstart);
80         pathstart = 0;
81         return 0;
82       }
83     }
84   } while(!dir);
85
86   name = (char *) n_malloc(n_strlen(path) + n_strlen(d->d_name) + 2);
87   n_strcpy(name, path);
88   n_strcat(name, "/");
89   n_strcat(name, d->d_name);
90   stream = glkunix_stream_open_pathname(name, fileusage_Data |
91                                         fileusage_BinaryMode, 0);
92   if(stream)
93     set_game_filename(name);
94   n_free(name);
95   return stream;
96 }
97
98
99 strid_t intd_filehandle_open(strid_t savefile, glui32 operating_id,
100                              glui32 contents_id, glui32 interp_id,
101                              glui32 length)
102 {
103   char *name;
104   strid_t str;
105   if(operating_id != 0x554e4958 /* 'UNIX' */)
106     return 0;
107   if(contents_id != 0)
108     return 0;
109   if(interp_id != 0x20202020 /* '    ' */)
110     return 0;
111
112   name = (char *) n_malloc(length+1);
113   glk_get_buffer_stream(savefile, name, length);
114   name[length] = 0;
115   str = glkunix_stream_open_pathname(name, fileusage_Data |
116                                      fileusage_BinaryMode, 0);
117   if(str)
118     set_game_filename(name);
119   n_free(name);
120   return str;
121 }
122
123 void intd_filehandle_make(strid_t savefile)
124 {
125   if(!game_filename)
126     return;
127   w_glk_put_string_stream(savefile, "UNIX");
128   glk_put_char_stream(savefile, b00000010); /* Flags */
129   glk_put_char_stream(savefile, 0);         /* Contents ID */
130   glk_put_char_stream(savefile, 0);         /* Reserved */
131   glk_put_char_stream(savefile, 0);         /* Reserved */
132   w_glk_put_string_stream(savefile, "    "); /* Interpreter ID */
133   w_glk_put_string_stream(savefile, game_filename);
134 }
135
136 glui32 intd_get_size(void)
137 {
138   if(!game_filename)
139     return 0;
140   return n_strlen(game_filename) + 12;
141 }
142
143 strid_t startup_open(const char *name)
144 {
145   strid_t str;
146   char *s;
147
148   str = glkunix_stream_open_pathname((char *) name, fileusage_Data | fileusage_BinaryMode, 0);
149   if(str) {
150     set_game_filename(name);
151     s = strrchr(name, '\\');
152     if (!s) s = strrchr(name, '/');
153     garglk_set_story_name(s ? s + 1 : name);
154   } else {
155     char *path = search_path;
156     if(path) {
157       char *p;
158       char *newname = (char *) n_malloc(strlen(path) + strlen(name) + 2);
159       path = n_strdup(path);
160       for(p = n_strtok(path, ":"); p; p = n_strtok(NULL, ":")) {
161         n_strcpy(newname, p);
162         n_strcat(newname, "/");
163         n_strcat(newname, name);
164         str = glkunix_stream_open_pathname((char *) newname, fileusage_Data |
165                                            fileusage_BinaryMode, 0);
166         if(str) {
167           set_game_filename(newname);
168           s = strrchr(newname, '\\');
169           if (!s) s = strrchr(newname, '/');
170           garglk_set_story_name(s ? s + 1 : newname);
171           break;
172         }
173       }
174       n_free(path);
175     }
176   }
177
178   if(!str)
179     fprintf(stderr, "Cannot open '%s'\n", name);
180
181   return str;
182 }
183
184 #line 717 "opt2glkc.pl"
185 static strid_t startup_wopen(const char *name)
186 {
187   return n_file_name(fileusage_Data | fileusage_BinaryMode,
188                      filemode_Write, name);
189 }
190 glkunix_argumentlist_t glkunix_arguments[] = {
191   { (char *) "", glkunix_arg_ValueCanFollow, (char *) "filename file to load" },
192   { (char *) "-help", glkunix_arg_NoValue, (char *) "list command-line options" },
193   { (char *) "--help", glkunix_arg_NoValue, (char *) "list command-line options" },
194   { (char *) "-version", glkunix_arg_NoValue, (char *) "get version number" },
195   { (char *) "--version", glkunix_arg_NoValue, (char *) "get version number" },
196   { (char *) "-i", glkunix_arg_NoValue, (char *) "-i" },
197   { (char *) "-no-ignore", glkunix_arg_NoValue, (char *) "-no-ignore" },
198   { (char *) "--no-ignore", glkunix_arg_NoValue, (char *) "--no-ignore" },
199   { (char *) "-ignore", glkunix_arg_NoValue, (char *) "-ignore" },
200   { (char *) "--ignore", glkunix_arg_NoValue, (char *) "--ignore        Ignore Z-machine strictness errors" },
201   { (char *) "-f", glkunix_arg_NoValue, (char *) "-f" },
202   { (char *) "-no-fullname", glkunix_arg_NoValue, (char *) "-no-fullname" },
203   { (char *) "--no-fullname", glkunix_arg_NoValue, (char *) "--no-fullname" },
204   { (char *) "-fullname", glkunix_arg_NoValue, (char *) "-fullname" },
205   { (char *) "--fullname", glkunix_arg_NoValue, (char *) "--fullname    For running under Emacs or DDD" },
206   { (char *) "-x", glkunix_arg_ValueFollows, (char *) "-x" },
207   { (char *) "-command", glkunix_arg_ValueFollows, (char *) "-command" },
208   { (char *) "--command", glkunix_arg_ValueFollows, (char *) "--command Read commands from this file" },
209   { (char *) "-P", glkunix_arg_NoValue, (char *) "-P" },
210   { (char *) "-no-pirate", glkunix_arg_NoValue, (char *) "-no-pirate" },
211   { (char *) "--no-pirate", glkunix_arg_NoValue, (char *) "--no-pirate" },
212   { (char *) "-pirate", glkunix_arg_NoValue, (char *) "-pirate" },
213   { (char *) "--pirate", glkunix_arg_NoValue, (char *) "--pirate        Aye, matey" },
214   { (char *) "-q", glkunix_arg_NoValue, (char *) "-q" },
215   { (char *) "-no-quiet", glkunix_arg_NoValue, (char *) "-no-quiet" },
216   { (char *) "--no-quiet", glkunix_arg_NoValue, (char *) "--no-quiet" },
217   { (char *) "-quiet", glkunix_arg_NoValue, (char *) "-quiet" },
218   { (char *) "--quiet", glkunix_arg_NoValue, (char *) "--quiet  Do not print introductory messages" },
219   { (char *) "-no-spell", glkunix_arg_NoValue, (char *) "-no-spell" },
220   { (char *) "--no-spell", glkunix_arg_NoValue, (char *) "--no-spell" },
221   { (char *) "-spell", glkunix_arg_NoValue, (char *) "-spell" },
222   { (char *) "--spell", glkunix_arg_NoValue, (char *) "--spell  Perform spelling correction" },
223   { (char *) "-no-expand", glkunix_arg_NoValue, (char *) "-no-expand" },
224   { (char *) "--no-expand", glkunix_arg_NoValue, (char *) "--no-expand" },
225   { (char *) "-expand", glkunix_arg_NoValue, (char *) "-expand" },
226   { (char *) "--expand", glkunix_arg_NoValue, (char *) "--expand        Expand one letter abbreviations" },
227   { (char *) "-s", glkunix_arg_ValueFollows, (char *) "-s" },
228   { (char *) "-symbols", glkunix_arg_ValueFollows, (char *) "-symbols" },
229   { (char *) "--symbols", glkunix_arg_ValueFollows, (char *) "--symbols Specify symbol file for game" },
230   { (char *) "-t", glkunix_arg_NoValue, (char *) "-t" },
231   { (char *) "-no-tandy", glkunix_arg_NoValue, (char *) "-no-tandy" },
232   { (char *) "--no-tandy", glkunix_arg_NoValue, (char *) "--no-tandy" },
233   { (char *) "-tandy", glkunix_arg_NoValue, (char *) "-tandy" },
234   { (char *) "--tandy", glkunix_arg_NoValue, (char *) "--tandy  Censors some Infocom games" },
235   { (char *) "-T", glkunix_arg_ValueFollows, (char *) "-T" },
236   { (char *) "-transcript", glkunix_arg_ValueFollows, (char *) "-transcript" },
237   { (char *) "--transcript", glkunix_arg_ValueFollows, (char *) "--transcript   Write transcript to this file" },
238   { (char *) "-d", glkunix_arg_NoValue, (char *) "-d" },
239   { (char *) "-no-debug", glkunix_arg_NoValue, (char *) "-no-debug" },
240   { (char *) "--no-debug", glkunix_arg_NoValue, (char *) "--no-debug" },
241   { (char *) "-debug", glkunix_arg_NoValue, (char *) "-debug" },
242   { (char *) "--debug", glkunix_arg_NoValue, (char *) "--debug  Enter debugger immediatly" },
243   { (char *) "-prompt", glkunix_arg_ValueFollows, (char *) "-prompt" },
244   { (char *) "--prompt", glkunix_arg_ValueFollows, (char *) "--prompt   Specify debugging prompt" },
245   { (char *) "-path", glkunix_arg_ValueFollows, (char *) "-path" },
246   { (char *) "--path", glkunix_arg_ValueFollows, (char *) "--path       Look for games in this directory" },
247   { (char *) "-no-autoundo", glkunix_arg_NoValue, (char *) "-no-autoundo" },
248   { (char *) "--no-autoundo", glkunix_arg_NoValue, (char *) "--no-autoundo" },
249   { (char *) "-autoundo", glkunix_arg_NoValue, (char *) "-autoundo" },
250   { (char *) "--autoundo", glkunix_arg_NoValue, (char *) "--autoundo    Ensure @code{@@save_undo} is called every turn" },
251   { (char *) "-S", glkunix_arg_NumberValue, (char *) "-S" },
252   { (char *) "-stacklimit", glkunix_arg_NumberValue, (char *) "-stacklimit" },
253   { (char *) "--stacklimit", glkunix_arg_NumberValue, (char *) "--stacklimit    Exit when the stack is this deep" },
254   { (char *) "-a", glkunix_arg_ValueFollows, (char *) "-a" },
255   { (char *) "-alias", glkunix_arg_ValueFollows, (char *) "-alias" },
256   { (char *) "--alias", glkunix_arg_ValueFollows, (char *) "--alias     Specify an alias" },
257   { (char *) "-ralias", glkunix_arg_ValueFollows, (char *) "-ralias" },
258   { (char *) "--ralias", glkunix_arg_ValueFollows, (char *) "--ralias   Specify an recursive alias" },
259   { (char *) "-unalias", glkunix_arg_ValueFollows, (char *) "-unalias" },
260   { (char *) "--unalias", glkunix_arg_ValueFollows, (char *) "--unalias Remove an alias" },
261   { (char *) "-r", glkunix_arg_NumberValue, (char *) "-r" },
262   { (char *) "-random", glkunix_arg_NumberValue, (char *) "-random" },
263   { (char *) "--random", glkunix_arg_NumberValue, (char *) "--random    Set random seed" },
264   { (char *) "-mapsym", glkunix_arg_ValueFollows, (char *) "-mapsym" },
265   { (char *) "--mapsym", glkunix_arg_ValueFollows, (char *) "--mapsym   Specify mapping glyphs" },
266   { (char *) "-mapsize", glkunix_arg_NumberValue, (char *) "-mapsize" },
267   { (char *) "--mapsize", glkunix_arg_NumberValue, (char *) "--mapsize  Specify map size" },
268   { (char *) "-maploc", glkunix_arg_ValueFollows, (char *) "-maploc" },
269   { (char *) "--maploc", glkunix_arg_ValueFollows, (char *) "--maploc   Specify map location" },
270   { (char *) "-terpnum", glkunix_arg_NumberValue, (char *) "-terpnum" },
271   { (char *) "--terpnum", glkunix_arg_NumberValue, (char *) "--terpnum  Specify interpreter number" },
272   { (char *) "-terpver", glkunix_arg_ValueFollows, (char *) "-terpver" },
273   { (char *) "--terpver", glkunix_arg_ValueFollows, (char *) "--terpver Specify interpreter version" },
274   { NULL, glkunix_arg_End, NULL }
275 };
276
277 static void code_ignore(int flag)
278 #line 6 "nitfol.opt"
279 { ignore_errors = flag; }
280
281 static void code_fullname(int flag)
282 #line 9 "nitfol.opt"
283 { fullname = flag; }
284
285 static void code_command(strid_t stream)
286 #line 12 "nitfol.opt"
287 { if(stream) input_stream1 = stream; }
288
289 static void code_pirate(int flag)
290 #line 15 "nitfol.opt"
291 { aye_matey = flag; }
292
293 static void code_quiet(int flag)
294 #line 18 "nitfol.opt"
295 { quiet = flag; }
296
297 static void code_spell(int flag)
298 #line 21 "nitfol.opt"
299 { do_spell_correct = flag; }
300
301 static void code_expand(int flag)
302 #line 24 "nitfol.opt"
303 { do_expand = flag; }
304
305 static void code_symbols(strid_t stream)
306 #line 27 "nitfol.opt"
307 { if(stream) init_infix(stream); }
308
309 static void code_tandy(int flag)
310 #line 30 "nitfol.opt"
311 { do_tandy = flag; }
312
313 static void code_transcript(strid_t stream)
314 #line 33 "nitfol.opt"
315 { if(stream) set_transcript(stream); }
316
317 static void code_debug(int flag)
318 #line 36 "nitfol.opt"
319 { enter_debugger = flag; do_check_watches = flag; }
320
321 static void code_prompt(const char *string)
322 #line 39 "nitfol.opt"
323 { n_free(db_prompt); db_prompt = n_strdup(string); }
324
325 static void code_path(const char *string)
326 #line 42 "nitfol.opt"
327 { n_free(search_path); search_path = n_strdup(string); }
328
329 static void code_autoundo(int flag)
330 #line 45 "nitfol.opt"
331 { auto_save_undo = flag; }
332
333 static void code_stacklimit(int number)
334 #line 52 "nitfol.opt"
335 { stacklimit = number; }
336
337 static void code_alias(const char *string)
338 #line 55 "nitfol.opt"
339 { if(string) parse_new_alias(string, FALSE); }
340
341 static void code_ralias(const char *string)
342 #line 58 "nitfol.opt"
343 { if(string) parse_new_alias(string, TRUE); }
344
345 static void code_unalias(const char *string)
346 #line 61 "nitfol.opt"
347 { if(string) remove_alias(string); }
348
349 static void code_random(int number)
350 #line 64 "nitfol.opt"
351 { faked_random_seed = number; }
352
353 static void code_mapsym(const char *string)
354 #line 67 "nitfol.opt"
355 { n_free(roomsymbol); roomsymbol = n_strdup(string); }
356
357 static void code_mapsize(int number)
358 #line 70 "nitfol.opt"
359 { automap_size = number; }
360
361 static void code_maploc(const char *string)
362 #line 73 "nitfol.opt"
363 { switch(glk_char_to_lower(*string)) { case 'a': case 't': case 'u': automap_split = winmethod_Above; break; case 'b': case 'd': automap_split = winmethod_Below; break; case 'l': automap_split = winmethod_Left; break; case 'r': automap_split = winmethod_Right; } }
364
365 static void code_terpnum(int number)
366 #line 76 "nitfol.opt"
367 { interp_num = number; }
368
369 static void code_terpver(const char *string)
370 #line 79 "nitfol.opt"
371 { if(string) { if(n_strlen(string) == 1) interp_ver = *string; else interp_ver = n_strtol(string, NULL, 10); } }
372
373 #line 760 "opt2glkc.pl"
374 typedef enum { option_flag, option_file, option_wfile, option_number, option_string } option_type;
375 typedef struct { const char *longname; char shortname; const char *description; option_type type; void (*int_func)(int); int defint; void (*str_func)(strid_t); strid_t defstream; void (*string_func)(const char *); const char *defstring; } option_option;
376
377 static option_option options[] = {
378   { "ignore", 'i', "Ignore Z-machine strictness errors", option_flag, code_ignore, 1, NULL, NULL, NULL, NULL },
379   { "fullname", 'f', "For running under Emacs or DDD", option_flag, code_fullname, 0, NULL, NULL, NULL, NULL },
380   { "command", 'x', "Read commands from this file", option_file, NULL, 0, code_command, NULL, NULL, NULL },
381   { "pirate", 'P', "Aye, matey", option_flag, code_pirate, 0, NULL, NULL, NULL, NULL },
382   { "quiet", 'q', "Do not print introductory messages", option_flag, code_quiet, 1, NULL, NULL, NULL, NULL },
383   { "spell", '-', "Perform spelling correction", option_flag, code_spell, 1, NULL, NULL, NULL, NULL },
384   { "expand", '-', "Expand one letter abbreviations", option_flag, code_expand, 1, NULL, NULL, NULL, NULL },
385   { "symbols", 's', "Specify symbol file for game", option_file, NULL, 0, code_symbols, NULL, NULL, NULL },
386   { "tandy", 't', "Censors some Infocom games", option_flag, code_tandy, 0, NULL, NULL, NULL, NULL },
387   { "transcript", 'T', "Write transcript to this file", option_wfile, NULL, 0, code_transcript, NULL, NULL, NULL },
388   { "debug", 'd', "Enter debugger immediatly", option_flag, code_debug, 0, NULL, NULL, NULL, NULL },
389   { "prompt", '-', "Specify debugging prompt", option_string, NULL, 0, NULL, NULL, code_prompt, "(nitfol) " },
390   { "path", '-', "Look for games in this directory", option_string, NULL, 0, NULL, NULL, code_path, NULL },
391   { "autoundo", '-', "Ensure '@save_undo' is called every turn", option_flag, code_autoundo, 1, NULL, NULL, NULL, NULL },
392   { "stacklimit", 'S', "Exit when the stack is this deep", option_number, code_stacklimit, 0, NULL, NULL, NULL, NULL },
393   { "alias", 'a', "Specify an alias", option_string, NULL, 0, NULL, NULL, code_alias, NULL },
394   { "ralias", '-', "Specify an recursive alias", option_string, NULL, 0, NULL, NULL, code_ralias, NULL },
395   { "unalias", '-', "Remove an alias", option_string, NULL, 0, NULL, NULL, code_unalias, NULL },
396   { "random", 'r', "Set random seed", option_number, code_random, 0, NULL, NULL, NULL, NULL },
397   { "mapsym", '-', "Specify mapping glyphs", option_string, NULL, 0, NULL, NULL, code_mapsym, "*udb@UDB+" },
398   { "mapsize", '-', "Specify map size", option_number, code_mapsize, 12, NULL, NULL, NULL, NULL },
399   { "maploc", '-', "Specify map location", option_string, NULL, 0, NULL, NULL, code_maploc, "above" },
400   { "terpnum", '-', "Specify interpreter number", option_number, code_terpnum, 2, NULL, NULL, NULL, NULL },
401   { "terpver", '-', "Specify interpreter version", option_string, NULL, 0, NULL, NULL, code_terpver, "N" }
402 };
403
404 #line 811 "opt2glkc.pl"
405 static void set_defaults(void)
406 {
407   unsigned n;
408   for(n = 0; n < sizeof(options) / sizeof(*options); n++) {
409     if(options[n].int_func)
410       options[n].int_func(options[n].defint);
411     if(options[n].str_func)
412       options[n].str_func(options[n].defstream);
413     if(options[n].string_func)
414       options[n].string_func(options[n].defstring);
415   }
416 }
417
418 #line 829 "opt2glkc.pl"
419 static void read_textpref(strid_t pref, const char *progname)
420 {
421   unsigned n;
422   char buffer[1024];
423   int prognamelen = n_strlen(progname);
424   if(!pref)
425     return;
426   while(glk_get_line_stream(pref, buffer, sizeof(buffer))) {
427     char *optname;
428     char *optval;
429     long int optnum;
430
431     if(buffer[0] == '#')
432       continue;
433     while(buffer[0] == '[') {
434       if(n_strncasecmp(buffer+1, progname, prognamelen) != 0
435          || buffer[1+prognamelen] != ']') {
436         while(glk_get_line_stream(pref, buffer, sizeof(buffer)))
437           if(buffer[0] == '[')
438             break;
439       } else {
440         glk_get_line_stream(pref, buffer, sizeof(buffer));
441       }
442     }
443
444     optname = buffer;
445     while(isspace(*optname))
446       optname++;
447     if((optval = n_strchr(optname, '=')) != NULL) {
448       char *p;
449       *optval = 0;
450       optval++;
451
452       if((p = n_strchr(optname, ' ')) != NULL)
453         *p = 0;
454       
455       while(isspace(*optval))
456         optval++;
457
458       while(isspace(optval[strlen(optval)-1]))
459         optval[strlen(optval)-1] = 0;
460
461       optnum = n_strtol(optval, NULL, 0);
462       if(n_strcasecmp(optval, "false") == 0
463          || n_strcasecmp(optval, "f") == 0)
464         optnum = FALSE;
465       if(n_strcasecmp(optval, "true") == 0
466          || n_strcasecmp(optval, "t") == 0)
467         optnum = TRUE;
468
469       for(n = 0; n < sizeof(options) / sizeof(*options); n++) {
470         if(n_strcmp(options[n].longname, optname) == 0) {
471           switch(options[n].type) {
472           case option_flag:
473           case option_number:
474             options[n].int_func(optnum);
475             break;
476           case option_file:
477             options[n].str_func(startup_open(optval));
478             break;
479           case option_wfile:
480             options[n].str_func(startup_wopen(optval));
481             break;
482           case option_string:
483             options[n].string_func(optval);
484             break;
485           }
486           break;
487         }
488       }
489     }
490   }
491   glk_stream_close(pref, NULL);
492 }
493
494 #line 910 "opt2glkc.pl"
495 static void show_help(void)
496 {
497   unsigned n;
498   printf("Usage: nitfol [OPTIONS] gamefile\n");
499   for(n = 0; n < sizeof(options) / sizeof(*options); n++) {
500     if(options[n].shortname != '-')
501       printf(" -%c, ", options[n].shortname);
502     else
503       printf("     ");
504     printf("-%-15s %s\n", options[n].longname, options[n].description);
505   }
506 }
507
508 #line 928 "opt2glkc.pl"
509 static BOOL parse_commands(int argc, char **argv)
510 {
511   int i;
512   unsigned n;
513
514   for(i = 1; i < argc; i++) {
515     BOOL flag = TRUE;
516
517     const char *p = argv[i];
518     
519     if(p[0] == '-') {
520       BOOL found = FALSE;
521
522       while(*p == '-')
523         p++;
524       if(n_strncmp(p, "no-", 3) == 0) {
525         flag = FALSE;
526         p+=3;
527       }
528
529       if(n_strcasecmp(p, "help") == 0) {
530         show_help();
531         exit(0);
532       }
533       if(n_strcasecmp(p, "version") == 0) {
534         printf("nitfol version %d.%d\n", NITFOL_MAJOR, NITFOL_MINOR);
535         exit(0);
536       }
537       
538       for(n = 0; n < sizeof(options) / sizeof(*options); n++) {
539         if((n_strlen(p) == 1 && *p == options[n].shortname) ||
540            n_strcmp(options[n].longname, p) == 0) {
541           found = TRUE;
542
543           switch(options[n].type) {
544           case option_flag:
545             options[n].int_func(flag);
546             break;
547
548           case option_file:
549             i++;
550             options[n].str_func(startup_open(argv[i]));
551             break;
552
553           case option_wfile:
554             i++;
555             options[n].str_func(startup_wopen(argv[i]));
556             break;
557
558           case option_number:
559             i++;
560             options[n].int_func(n_strtol(argv[i], NULL, 0));
561             break;
562
563           case option_string:
564             i++;
565             options[n].string_func(argv[i]);
566             break;
567           }
568         }
569       }
570
571       if(!found)
572         return FALSE;
573
574     } else {
575       strid_t s = startup_open(argv[i]);
576       if(!s)
577         return FALSE;
578       if(!game_use_file(s))
579         return FALSE;
580     }
581   }
582
583   return TRUE;
584 }
585
586 #line 415 "opt2glkc.pl"
587
588 #ifdef DEBUGGING
589 static void sighandle(int unused);
590
591 static void sighandle(int unused)
592 {
593 /*  signal(SIGINT, sighandle); */ /* SysV resets default behaviour - foil it */
594   enter_debugger = TRUE;
595 }
596 #endif
597
598 #ifdef __cplusplus
599 extern "C" {
600 #endif
601 int glkunix_startup_code(glkunix_startup_t *data)
602 {
603   set_defaults();
604
605         garglk_set_program_name("Nitfol 0.5");
606         garglk_set_program_info(
607                 "Nitfol 0.5 by Evin Robertson\n"
608                 "With countless patches by other people.\n");
609
610   return parse_commands(data->argc, data->argv);
611 }
612 #ifdef __cplusplus
613 }
614 #endif