OSDN Git Service

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