OSDN Git Service

Merge pull request #2037 from OwenGHB/curses-fix
[hengbandforosx/hengbandosx.git] / src / main.cpp
1 /*
2  * Copyright (c) 1997 Ben Harrison, and others
3  *
4  * This software may be copied and distributed for educational, research,
5  * and not for profit purposes provided that this copyright and statement
6  * are included in all such copies.
7  */
8
9 #include "core/asking-player.h"
10 #include "core/game-play.h"
11 #include "core/scores.h"
12 #include "game-option/runtime-arguments.h"
13 #include "io/files-util.h"
14 #include "io/inet.h"
15 #include "io/record-play-movie.h"
16 #include "io/signal-handlers.h"
17 #include "io/uid-checker.h"
18 #include "main/angband-initializer.h"
19 #include "player/process-name.h"
20 #include "system/angband-version.h"
21 #include "system/angband.h"
22 #include "system/player-type-definition.h"
23 #include "system/system-variables.h"
24 #include "term/gameterm.h"
25 #include "term/term-color-types.h"
26 #include "util/angband-files.h"
27 #include "util/string-processor.h"
28 #include "view/display-scores.h"
29 #include "wizard/wizard-spoiler.h"
30 #include <string>
31
32 /*
33  * Available graphic modes
34  */
35 #define GRAPHICS_NONE 0
36 #define GRAPHICS_ORIGINAL 1
37 #define GRAPHICS_ADAM_BOLT 2
38 #define GRAPHICS_HENGBAND 3
39
40 /*
41  * Some machines have a "main()" function in their "main-xxx.c" file,
42  * all the others use this file for their "main()" function.
43  */
44
45 #ifndef WINDOWS
46 /*
47  * A hook for "quit()".
48  *
49  * Close down, then fall back into "quit()".
50  */
51 static void quit_hook(concptr s)
52 {
53     int j;
54
55     /* Unused */
56     (void)s;
57
58     /* Scan windows */
59     for (j = 8 - 1; j >= 0; j--) {
60         /* Unused */
61         if (!angband_term[j])
62             continue;
63
64         /* Nuke it */
65         term_nuke(angband_term[j]);
66     }
67 }
68
69 /*
70  * Set the stack size and overlay buffer (see main-286.c")
71  */
72 #ifdef PRIVATE_USER_PATH
73
74 /*
75  * Create an ".angband/" directory in the users home directory.
76  *
77  * ToDo: Add error handling.
78  * ToDo: Only create the directories when actually writing files.
79  */
80 static void create_user_dir(void)
81 {
82     char dirpath[1024];
83     char subdirpath[1024];
84
85     /* Get an absolute path from the filename */
86     path_parse(dirpath, 1024, PRIVATE_USER_PATH);
87
88     /* Create the ~/.angband/ directory */
89     mkdir(dirpath, 0700);
90
91     /* Build the path to the variant-specific sub-directory */
92     path_build(subdirpath, sizeof(subdirpath), dirpath, VARIANT_NAME.data());
93
94     /* Create the directory */
95     mkdir(subdirpath, 0700);
96 }
97
98 #endif /* PRIVATE_USER_PATH */
99
100 /*
101  * Initialize and verify the file paths, and the score file.
102  *
103  * Use the ANGBAND_PATH environment var if possible, else use
104  * DEFAULT_(LIB|VAR)_PATH, and in either case, branch off
105  * appropriately.
106  *
107  * First, we'll look for the ANGBAND_PATH environment variable,
108  * and then look for the files in there.  If that doesn't work,
109  * we'll try the DEFAULT_(LIB|VAR)_PATH constants.  So be sure
110  * that one of these two things works...
111  *
112  * We must ensure that the path ends with "PATH_SEP" if needed,
113  * since the "init_file_paths()" function will simply append the
114  * relevant "sub-directory names" to the given path.
115  *
116  * Make sure that the path doesn't overflow the buffer.  We have
117  * to leave enough space for the path separator, directory, and
118  * filenames.
119  */
120 static void init_stuff(void)
121 {
122     char libpath[1024], varpath[1024];
123
124     concptr tail;
125
126     /* Get the environment variable */
127     tail = getenv("ANGBAND_PATH");
128
129     /* Use the angband_path, or a default */
130     strncpy(libpath, tail ? tail : DEFAULT_LIB_PATH, 511);
131     strncpy(varpath, tail ? tail : DEFAULT_VAR_PATH, 511);
132
133     /* Make sure they're terminated */
134     libpath[511] = '\0';
135     varpath[511] = '\0';
136
137     /* Hack -- Add a path separator (only if needed) */
138     if (!suffix(libpath, PATH_SEP))
139         strcat(libpath, PATH_SEP);
140     if (!suffix(varpath, PATH_SEP))
141         strcat(varpath, PATH_SEP);
142
143     /* Initialize */
144     init_file_paths(libpath, varpath);
145 }
146
147 /*
148  * Handle a "-d<what>=<path>" option
149  *
150  * The "<what>" can be any string starting with the same letter as the
151  * name of a subdirectory of the "lib" folder (i.e. "i" or "info").
152  *
153  * The "<path>" can be any legal path for the given system, and should
154  * not end in any special path separator (i.e. "/tmp" or "~/.ang-info").
155  */
156 static void change_path(concptr info)
157 {
158     concptr s;
159
160     /* Find equal sign */
161     s = angband_strchr(info, '=');
162
163     /* Verify equal sign */
164     if (!s)
165         quit_fmt("Try '-d<what>=<path>' not '-d%s'", info);
166
167     /* Analyze */
168     switch (tolower(info[0])) {
169     case 'a': {
170         string_free(ANGBAND_DIR_APEX);
171         ANGBAND_DIR_APEX = string_make(s + 1);
172         break;
173     }
174
175     case 'f': {
176         string_free(ANGBAND_DIR_FILE);
177         ANGBAND_DIR_FILE = string_make(s + 1);
178         break;
179     }
180
181     case 'h': {
182         string_free(ANGBAND_DIR_HELP);
183         ANGBAND_DIR_HELP = string_make(s + 1);
184         break;
185     }
186
187     case 'i': {
188         string_free(ANGBAND_DIR_INFO);
189         ANGBAND_DIR_INFO = string_make(s + 1);
190         break;
191     }
192
193     case 'u': {
194         string_free(ANGBAND_DIR_USER);
195         ANGBAND_DIR_USER = string_make(s + 1);
196         break;
197     }
198
199     case 'x': {
200         string_free(ANGBAND_DIR_XTRA);
201         ANGBAND_DIR_XTRA = string_make(s + 1);
202         break;
203     }
204
205     case 'b': {
206         string_free(ANGBAND_DIR_BONE);
207         ANGBAND_DIR_BONE = string_make(s + 1);
208         break;
209     }
210
211     case 'd': {
212         string_free(ANGBAND_DIR_DATA);
213         ANGBAND_DIR_DATA = string_make(s + 1);
214         break;
215     }
216
217     case 'e': {
218         string_free(ANGBAND_DIR_EDIT);
219         ANGBAND_DIR_EDIT = string_make(s + 1);
220         break;
221     }
222
223     case 's': {
224         string_free(ANGBAND_DIR_SAVE);
225         ANGBAND_DIR_SAVE = string_make(s + 1);
226         break;
227     }
228
229     case 'z': {
230         string_free(ANGBAND_DIR_SCRIPT);
231         ANGBAND_DIR_SCRIPT = string_make(s + 1);
232         break;
233     }
234
235     default: {
236         quit_fmt("Bad semantics in '-d%s'", info);
237     }
238     }
239 }
240
241 static void display_usage(const char *program)
242 {
243     /* Dump usage information */
244     printf("Usage: %s [options] [-- subopts]\n", program);
245     puts("  -n       Start a new character");
246     puts("  -f       Request fiddle mode");
247     puts("  -w       Request wizard mode");
248     puts("  -b       Request BGM mode");
249     puts("  -v       Request sound mode");
250     puts("  -g       Request graphics mode");
251     puts("  -o       Request original keyset");
252     puts("  -r       Request rogue-like keyset");
253     puts("  -M       Request monochrome mode");
254     puts("  -s<num>  Show <num> high scores");
255     puts("  -u<who>  Use your <who> savefile");
256     puts("  -m<sys>  Force 'main-<sys>.c' usage");
257     puts("  -d<def>  Define a 'lib' dir sub-path");
258     puts("  --output-spoilers");
259     puts("           Output auto generated spoilers and exit");
260     puts("");
261
262 #ifdef USE_X11
263     puts("  -mx11    To use X11");
264     puts("  --       Sub options");
265     puts("  -- -d    Set display name");
266     puts("  -- -o    Request old 8x8 tile graphics");
267     puts("  -- -a    Request Adam Bolt 16x16 tile graphics");
268     puts("  -- -b    Request Bigtile graphics mode");
269     puts("  -- -s    Turn off smoothscaling graphics");
270     puts("  -- -n#   Number of terms to use");
271     puts("");
272 #endif /* USE_X11 */
273
274 #ifdef USE_GCU
275     puts("  -mgcu    To use GCU (GNU Curses)");
276     puts("  --       Sub options");
277     puts("  -- -o    old subwindow layout (no bigscreen)");
278 #endif /* USE_GCU */
279
280 #ifdef USE_CAP
281     puts("  -mcap    To use CAP (\"Termcap\" calls)");
282 #endif /* USE_CAP */
283
284     /* Actually abort the process */
285     quit(nullptr);
286 }
287
288 /*
289  * @brief 2文字以上のコマンドライン引数 (オプション)を実行する
290  * @param opt コマンドライン引数
291  * @return Usageを表示する必要があるか否か
292  * @details v3.0.0 Alpha21時点では、スポイラー出力モードの判定及び実行を行う
293  */
294 static bool parse_long_opt(const char *opt)
295 {
296     if (strcmp(opt + 2, "output-spoilers") != 0) {
297         return true;
298     }
299
300     init_stuff();
301     init_angband(p_ptr, true);
302     switch (output_all_spoilers()) {
303     case SpoilerOutputResultType::SPOILER_OUTPUT_SUCCESS:
304         puts("Successfully created a spoiler file.");
305         quit(nullptr);
306         break;
307     case SpoilerOutputResultType::SPOILER_OUTPUT_FAIL_FOPEN:
308         quit("Cannot create spoiler file.");
309         break;
310     case SpoilerOutputResultType::SPOILER_OUTPUT_FAIL_FCLOSE:
311         quit("Cannot close spoiler file.");
312         break;
313     default:
314         break;
315     }
316
317     return false;
318 }
319
320 /*
321  * Simple "main" function for multiple platforms.
322  *
323  * Note the special "--" option which terminates the processing of
324  * standard options.  All non-standard options (if any) are passed
325  * directly to the "init_xxx()" function.
326  */
327 int main(int argc, char *argv[])
328 {
329     int i;
330
331     bool done = false;
332     bool new_game = false;
333     int show_score = 0;
334     concptr mstr = nullptr;
335     bool args = true;
336
337     /* Save the "program name" XXX XXX XXX */
338     argv0 = argv[0];
339
340 #ifdef SET_UID
341
342     /* Default permissions on files */
343     (void)umask(022);
344
345 #endif
346
347     /* Get the file paths */
348     init_stuff();
349
350 #ifdef SET_UID
351
352     /* Get the user id (?) */
353     p_ptr->player_uid = getuid();
354
355 #ifdef VMS
356     /* Mega-Hack -- Factor group id */
357     p_ptr->player_uid += (getgid() * 1000);
358 #endif
359
360 #ifdef SAFE_SETUID
361
362 #ifdef _POSIX_SAVED_IDS
363
364     /* Save some info for later */
365     p_ptr->player_euid = geteuid();
366     p_ptr->player_egid = getegid();
367
368 #endif
369
370 #endif
371
372 #endif
373
374     /* Drop permissions */
375     safe_setuid_drop();
376
377 #ifdef SET_UID
378
379     /* Acquire the "user name" as a default player name */
380     user_name(p_ptr->name, p_ptr->player_uid);
381
382 #ifdef PRIVATE_USER_PATH
383
384     /* Create a directory for the users files. */
385     create_user_dir();
386
387 #endif /* PRIVATE_USER_PATH */
388
389 #endif /* SET_UID */
390
391     /* Process the command line arguments */
392     bool browsing_movie = false;
393     for (i = 1; args && (i < argc); i++) {
394         /* Require proper options */
395         if (argv[i][0] != '-') {
396             display_usage(argv[0]);
397             continue;
398         }
399
400         /* Analyze option */
401         bool is_usage_needed = false;
402         switch (argv[i][1]) {
403         case 'N':
404         case 'n': {
405             new_game = true;
406             break;
407         }
408         case 'B':
409         case 'b': {
410             arg_music = true;
411             break;
412         }
413         case 'V':
414         case 'v': {
415             arg_sound = true;
416             break;
417         }
418         case 'G':
419         case 'g': {
420             /* HACK - Graphics mode switches on the original tiles */
421             arg_graphics = GRAPHICS_ORIGINAL;
422             break;
423         }
424         case 'R':
425         case 'r': {
426             arg_force_roguelike = true;
427             break;
428         }
429         case 'O':
430         case 'o': {
431             arg_force_original = true;
432             break;
433         }
434         case 'S':
435         case 's': {
436             show_score = atoi(&argv[i][2]);
437             if (show_score <= 0)
438                 show_score = 10;
439             break;
440         }
441         case 'u':
442         case 'U': {
443             if (!argv[i][2]) {
444                 is_usage_needed = true;
445                 break;
446             }
447
448             strcpy(p_ptr->name, &argv[i][2]);
449             break;
450         }
451         case 'm': {
452             if (!argv[i][2]) {
453                 is_usage_needed = true;
454                 break;
455             }
456
457             mstr = &argv[i][2];
458             break;
459         }
460         case 'M': {
461             arg_monochrome = true;
462             break;
463         }
464         case 'd':
465         case 'D': {
466             change_path(&argv[i][2]);
467             break;
468         }
469         case 'x': {
470             if (!argv[i][2]) {
471                 is_usage_needed = true;
472                 break;
473             }
474
475             prepare_browse_movie_with_path_build(&argv[i][2]);
476             browsing_movie = true;
477             break;
478         }
479         case '-': {
480             if (argv[i][2] == '\0') {
481                 argv[i] = argv[0];
482                 argc = argc - i;
483                 argv = argv + i;
484                 args = false;
485             } else {
486                 is_usage_needed = parse_long_opt(argv[i]);
487             }
488             break;
489         }
490         default: {
491             is_usage_needed = true;
492             break;
493         }
494         }
495
496         if (!is_usage_needed)
497             continue;
498
499         display_usage(argv[0]);
500     }
501
502     /* Hack -- Forget standard args */
503     if (args) {
504         argc = 1;
505         argv[1] = nullptr;
506     }
507
508     /* Process the player name */
509     process_player_name(p_ptr, true);
510
511     /* Install "quit" hook */
512     quit_aux = quit_hook;
513
514 #ifdef USE_XAW
515     /* Attempt to use the "main-xaw.c" support */
516     if (!done && (!mstr || (streq(mstr, "xaw")))) {
517         extern errr init_xaw(int, char **);
518         if (0 == init_xaw(argc, argv)) {
519             ANGBAND_SYS = "xaw";
520             done = true;
521         }
522     }
523 #endif
524
525 #ifdef USE_X11
526     /* Attempt to use the "main-x11.c" support */
527     if (!done && (!mstr || (streq(mstr, "x11")))) {
528         extern errr init_x11(int, char **);
529         if (0 == init_x11(argc, argv)) {
530             ANGBAND_SYS = "x11";
531             done = true;
532         }
533     }
534 #endif
535
536 #ifdef USE_GCU
537     /* Attempt to use the "main-gcu.c" support */
538     if (!done && (!mstr || (streq(mstr, "gcu")))) {
539         extern errr init_gcu(int, char **);
540         if (0 == init_gcu(argc, argv)) {
541             ANGBAND_SYS = "gcu";
542             done = true;
543         }
544     }
545 #endif
546
547 #ifdef USE_CAP
548     /* Attempt to use the "main-cap.c" support */
549     if (!done && (!mstr || (streq(mstr, "cap")))) {
550         extern errr init_cap(int, char **);
551         if (0 == init_cap(argc, argv)) {
552             ANGBAND_SYS = "cap";
553             done = true;
554         }
555     }
556 #endif
557
558     /* Make sure we have a display! */
559     if (!done)
560         quit("Unable to prepare any 'display module'!");
561
562     /* Hack -- If requested, display scores and quit */
563     if (show_score > 0)
564         display_scores(0, show_score);
565
566     /* Catch nasty signals */
567     signals_init();
568
569     /* Initialize */
570     init_angband(p_ptr, false);
571
572     /* Wait for response */
573     pause_line(23);
574
575     /* Play the game */
576     play_game(p_ptr, new_game, browsing_movie);
577
578     /* Quit */
579     quit(nullptr);
580
581     /* Exit */
582     return (0);
583 }
584
585 #endif