xref: /dragonfly/games/hack/hack.main.c (revision 4318c66eac379e15105fe145d406dfef81b795f6)
1 /*        $NetBSD: hack.main.c,v 1.17 2011/08/06 20:42:43 dholland Exp $        */
2 
3 /*
4  * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5  * Amsterdam
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * - Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * - Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * - Neither the name of the Stichting Centrum voor Wiskunde en
20  * Informatica, nor the names of its contributors may be used to endorse or
21  * promote products derived from this software without specific prior
22  * written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39  * All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. The name of the author may not be used to endorse or promote products
50  *    derived from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
55  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62  */
63 
64 #include <signal.h>
65 #include <stdlib.h>
66 #include <unistd.h>
67 #include <fcntl.h>
68 #include "hack.h"
69 #include "extern.h"
70 
71 #ifdef QUEST
72 #define   gamename  "quest"
73 #else
74 #define   gamename  "hack"
75 #endif
76 
77 int             (*afternmv)(void);
78 int             (*occupation)(void);
79 const char           *occtxt;           /* defined when occupation != NULL */
80 
81 int             hackpid;      /* current pid */
82 int             locknum;      /* max num of players */
83 #ifdef DEF_PAGER
84 const char     *catmore;      /* default pager */
85 #endif
86 char            SAVEF[PL_NSIZ + 11] = "save/";    /* save/99999player */
87 char           *hname;                  /* name of the game (argv[0] of call) */
88 
89 static char obuf[BUFSIZ];     /* BUFSIZ is defined in stdio.h */
90 
91 int main(int, char *[]);
92 static void chdirx(const char *, boolean);
93 
94 int
main(int argc,char * argv[])95 main(int argc, char *argv[])
96 {
97           int             fd;
98 #ifdef CHDIR
99           char           *dir;
100 #endif
101 
102           /* Check for dirty tricks with closed fds 0, 1, 2 */
103           fd = open("/dev/null", O_RDONLY);
104           if (fd < 3)
105                     exit(1);
106           close(fd);
107 
108           hname = argv[0];
109           hackpid = getpid();
110 
111 #ifdef CHDIR                            /* otherwise no chdir() */
112           /*
113            * See if we must change directory to the playground.
114            * (Perhaps hack runs suid and playground is inaccessible
115            *  for the player.)
116            * The environment variable HACKDIR is overridden by a
117            *  -d command line option (must be the first option given)
118            */
119 
120           dir = getenv("HACKDIR");
121           if (argc > 1 && !strncmp(argv[1], "-d", 2)) {
122                     argc--;
123                     argv++;
124                     dir = argv[0] + 2;
125                     if (*dir == '=' || *dir == ':')
126                               dir++;
127                     if (!*dir && argc > 1) {
128                               argc--;
129                               argv++;
130                               dir = argv[0];
131                     }
132                     if (!*dir)
133                               error("Flag -d must be followed by a directory name.");
134           }
135 #endif
136 
137           /*
138            * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
139            *                            2. Use $USER or $LOGNAME      (if 1. fails)
140            *                            3. Use getlogin()             (if 2. fails)
141            * The resulting name is overridden by command line options.
142            * If everything fails, or if the resulting name is some generic
143            * account like "games", "play", "player", "hack" then eventually
144            * we'll ask him.
145            * Note that we trust him here; it is possible to play under
146            * somebody else's name.
147            */
148           {
149                     char           *s;
150 
151                     initoptions();
152                     if (!*plname && (s = getenv("USER")))
153                               (void) strncpy(plname, s, sizeof(plname) - 1);
154                     if (!*plname && (s = getenv("LOGNAME")))
155                               (void) strncpy(plname, s, sizeof(plname) - 1);
156                     if (!*plname && (s = getlogin()))
157                               (void) strncpy(plname, s, sizeof(plname) - 1);
158           }
159 
160           /*
161            * Now we know the directory containing 'record' and
162            * may do a prscore().
163            */
164           if (argc > 1 && !strncmp(argv[1], "-s", 2)) {
165 #ifdef CHDIR
166                     chdirx(dir, 0);
167 #endif
168                     prscore(argc, argv);
169                     exit(0);
170           }
171           /*
172            * It seems he really wants to play.
173            * Remember tty modes, to be restored on exit.
174            */
175           gettty();
176           setbuf(stdout, obuf);
177           setrandom();
178           startup();
179           cls();
180           u.uhp = 1;                    /* prevent RIP on early quits */
181           u.ux = FAR;                   /* prevent nscr() */
182           (void) signal(SIGHUP, hang_up);
183 
184           /*
185            * Find the creation date of this game,
186            * so as to avoid restoring outdated savefiles.
187            */
188           gethdate(hname);
189 
190           /*
191            * We cannot do chdir earlier, otherwise gethdate will fail.
192            */
193 #ifdef CHDIR
194           chdirx(dir, 1);
195 #endif
196 
197           /*
198            * Process options.
199            */
200           while (argc > 1 && argv[1][0] == '-') {
201                     argv++;
202                     argc--;
203                     switch (argv[0][1]) {
204 #ifdef WIZARD
205                     case 'D':
206                               /* if(!strcmp(getlogin(), WIZARD)) */
207                               wizard = TRUE;
208                               /*
209                                * else printf("Sorry.\n");
210                                */
211                               break;
212 #endif
213 #ifdef NEWS
214                     case 'n':
215                               flags.nonews = TRUE;
216                               break;
217 #endif
218                     case 'u':
219                               if (argv[0][2])
220                                         (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
221                               else if (argc > 1) {
222                                         argc--;
223                                         argv++;
224                                         (void) strncpy(plname, argv[0], sizeof(plname) - 1);
225                               } else
226                                         printf("Player name expected after -u\n");
227                               break;
228                     default:
229                               /* allow -T for Tourist, etc. */
230                               (void) strncpy(pl_character, argv[0] + 1,
231                                                sizeof(pl_character) - 1);
232 
233                               /* printf("Unknown option: %s\n", *argv); */
234                     }
235           }
236 
237           if (argc > 1)
238                     locknum = atoi(argv[1]);
239 #ifdef MAX_NR_OF_PLAYERS
240           if (!locknum || locknum > MAX_NR_OF_PLAYERS)
241                     locknum = MAX_NR_OF_PLAYERS;
242 #endif
243 #ifdef DEF_PAGER
244           if (((catmore = getenv("HACKPAGER")) == NULL &&
245               (catmore = getenv("PAGER")) == NULL) ||
246               catmore[0] == '\0')
247                     catmore = DEF_PAGER;
248 #endif
249 #ifdef MAIL
250           getmailstatus();
251 #endif
252 #ifdef WIZARD
253           if (wizard)
254                     (void) strcpy(plname, "wizard");
255           else
256 #endif
257                     if (!*plname || !strncmp(plname, "player", 4)
258                         || !strncmp(plname, "games", 4))
259                     askname();
260           plnamesuffix();               /* strip suffix from name; calls askname() */
261           /* again if suffix was whole name */
262           /* accepts any suffix */
263 #ifdef WIZARD
264           if (!wizard) {
265 #endif
266                     /*
267                      * check for multiple games under the same name
268                      * (if !locknum) or check max nr of players (otherwise)
269                      */
270                     (void) signal(SIGQUIT, SIG_IGN);
271                     (void) signal(SIGINT, SIG_IGN);
272                     if (!locknum)
273                               (void) strcpy(lock, plname);
274                     getlock();          /* sets lock if locknum != 0 */
275 #ifdef WIZARD
276           } else {
277                     char           *sfoo;
278                     (void) strcpy(lock, plname);
279                     if ((sfoo = getenv("MAGIC")) != NULL)
280                               while (*sfoo) {
281                                         switch (*sfoo++) {
282                                         case 'n':
283                                                   (void) srandom(*sfoo++);
284                                                   break;
285                                         }
286                               }
287                     if ((sfoo = getenv("GENOCIDED")) != NULL) {
288                               if (*sfoo == '!') {
289                                         const struct permonst *pm = mons;
290                                         char           *gp = genocided;
291 
292                                         while (pm < mons + CMNUM + 2) {
293                                                   if (!strchr(sfoo, pm->mlet))
294                                                             *gp++ = pm->mlet;
295                                                   pm++;
296                                         }
297                                         *gp = 0;
298                               } else
299                                         (void) strlcpy(genocided, sfoo,
300                                                             sizeof(genocided));
301                               (void) strcpy(fut_geno, genocided);
302                     }
303           }
304 #endif
305           setftty();
306           (void) snprintf(SAVEF, sizeof(SAVEF), "save/%d%s", getuid(), plname);
307           regularize(SAVEF + 5);        /* avoid . or / in name */
308           if ((fd = open(SAVEF, O_RDONLY)) >= 0 &&
309               (uptodate(fd) || unlink(SAVEF) == 666)) {
310                     (void) signal(SIGINT, done1);
311                     pline("Restoring old save file...");
312                     (void) fflush(stdout);
313                     if (!dorecover(fd))
314                               goto not_recovered;
315                     pline("Hello %s, welcome to %s!", plname, gamename);
316                     flags.move = 0;
317           } else {
318 not_recovered:
319                     fobj = fcobj = invent = 0;
320                     fmon = fallen_down = 0;
321                     ftrap = 0;
322                     fgold = 0;
323                     flags.ident = 1;
324                     init_objects();
325                     u_init();
326 
327                     (void) signal(SIGINT, done1);
328                     mklev();
329                     u.ux = xupstair;
330                     u.uy = yupstair;
331                     (void) inshop();
332                     setsee();
333                     flags.botlx = 1;
334                     makedog();
335                     {
336                               struct monst   *mtmp;
337                               if ((mtmp = m_at(u.ux, u.uy)) != NULL)
338                                         mnexto(mtmp);       /* riv05!a3 */
339                     }
340                     seemons();
341 #ifdef NEWS
342                     if (flags.nonews || !readnews())
343                               /* after reading news we did docrt() already */
344 #endif
345                               docrt();
346 
347                     /* give welcome message before pickup messages */
348                     pline("Hello %s, welcome to %s!", plname, gamename);
349 
350                     pickup(1);
351                     read_engr_at(u.ux, u.uy);
352                     flags.move = 1;
353           }
354 
355           flags.moonphase = phase_of_the_moon();
356           if (flags.moonphase == FULL_MOON) {
357                     pline("You are lucky! Full moon tonight.");
358                     u.uluck++;
359           } else if (flags.moonphase == NEW_MOON) {
360                     pline("Be careful! New moon tonight.");
361           }
362           initrack();
363 
364           for (;;) {
365                     if (flags.move) {   /* actual time passed */
366 
367                               settrack();
368 
369                               if (moves % 2 == 0 ||
370                                   (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
371                                         movemon();
372                                         if (!rn2(70))
373                                                   (void) makemon((struct permonst *) 0, 0, 0);
374                               }
375                               if (Glib)
376                                         glibr();
377                               timeout();
378                               ++moves;
379                               if (flags.time)
380                                         flags.botl = 1;
381                               if (u.uhp < 1) {
382                                         pline("You die...");
383                                         done("died");
384                               }
385                               if (u.uhp * 10 < u.uhpmax && moves - wailmsg > 50) {
386                                         wailmsg = moves;
387                                         if (u.uhp == 1)
388                                                   pline("You hear the wailing of the Banshee...");
389                                         else
390                                                   pline("You hear the howling of the CwnAnnwn...");
391                               }
392                               if (u.uhp < u.uhpmax) {
393                                         if (u.ulevel > 9) {
394                                                   if (Regeneration || !(moves % 3)) {
395                                                             flags.botl = 1;
396                                                             u.uhp += rnd((int) u.ulevel - 9);
397                                                             if (u.uhp > u.uhpmax)
398                                                                       u.uhp = u.uhpmax;
399                                                   }
400                                         } else if (Regeneration ||
401                                                    (!(moves % (22 - u.ulevel * 2)))) {
402                                                   flags.botl = 1;
403                                                   u.uhp++;
404                                         }
405                               }
406                               if (Teleportation && !rn2(85))
407                                         tele();
408                               if (Searching && multi >= 0)
409                                         (void) dosearch();
410                               gethungry();
411                               invault();
412                               amulet();
413                     }
414                     if (multi < 0) {
415                               if (!++multi) {
416                                         if (nomovemsg)
417                                                   pline("%s", nomovemsg);
418                                         else
419                                                   pline("You can move again.");
420                                         nomovemsg = 0;
421                                         if (afternmv)
422                                                   (*afternmv) ();
423                                         afternmv = 0;
424                               }
425                     }
426                     find_ac();
427 #ifndef QUEST
428                     if (!flags.mv || Blind)
429 #endif
430                     {
431                               seeobjs();
432                               seemons();
433                               nscr();
434                     }
435                     if (flags.botl || flags.botlx)
436                               bot();
437 
438                     flags.move = 1;
439 
440                     if (multi >= 0 && occupation) {
441                               if (monster_nearby())
442                                         stop_occupation();
443                               else if ((*occupation) () == 0)
444                                         occupation = 0;
445                               continue;
446                     }
447                     if (multi > 0) {
448 #ifdef QUEST
449                               if (flags.run >= 4)
450                                         finddir();
451 #endif
452                               lookaround();
453                               if (!multi) {       /* lookaround may clear multi */
454                                         flags.move = 0;
455                                         continue;
456                               }
457                               if (flags.mv) {
458                                         if (multi < COLNO && !--multi)
459                                                   flags.mv = flags.run = 0;
460                                         domove();
461                               } else {
462                                         --multi;
463                                         rhack(save_cm);
464                               }
465                     } else if (multi == 0) {
466 #ifdef MAIL
467                               ckmailstatus();
468 #endif
469                               rhack(NULL);
470                     }
471                     if (multi && multi % 7 == 0)
472                               (void) fflush(stdout);
473           }
474 }
475 
476 void
glo(int foo)477 glo(int foo)
478 {
479           /* construct the string  xlock.n  */
480           size_t pos;
481 
482           pos = 0;
483           while (lock[pos] && lock[pos] != '.')
484                     pos++;
485           (void) snprintf(lock + pos, sizeof(lock) - pos, ".%d", foo);
486 }
487 
488 /*
489  * plname is filled either by an option (-u Player  or  -uPlayer) or
490  * explicitly (-w implies wizard) or by askname.
491  * It may still contain a suffix denoting pl_character.
492  */
493 void
askname(void)494 askname(void)
495 {
496           int             c, ct;
497           printf("\nWho are you? ");
498           (void) fflush(stdout);
499           ct = 0;
500           while ((c = getchar()) != '\n') {
501                     if (c == EOF)
502                               error("End of input\n");
503                     /* some people get confused when their erase char is not ^H */
504                     if (c == '\010') {
505                               if (ct)
506                                         ct--;
507                               continue;
508                     }
509                     if (c != '-')
510                               if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z')
511                                         c = '_';
512                     if (ct < (int)sizeof(plname) - 1)
513                               plname[ct++] = c;
514           }
515           plname[ct] = 0;
516           if (ct == 0)
517                     askname();
518 }
519 
520 /* VARARGS1 */
521 void
impossible(const char * s,...)522 impossible(const char *s, ...)
523 {
524           va_list ap;
525 
526           va_start(ap, s);
527           vpline(s, ap);
528           va_end(ap);
529           pline("Program in disorder - perhaps you'd better Quit.");
530 }
531 
532 #ifdef CHDIR
533 static void
chdirx(const char * dir,boolean wr)534 chdirx(const char *dir, boolean wr)
535 {
536 
537 #ifdef SECURE
538           if (dir                       /* User specified directory? */
539 #ifdef HACKDIR
540               && strcmp(dir, HACKDIR)   /* and not the default? */
541 #endif
542                     ) {
543                     (void) setuid(getuid());      /* Ron Wessels */
544                     (void) setgid(getgid());
545           }
546 #endif
547 
548 #ifdef HACKDIR
549           if (dir == NULL)
550                     dir = HACKDIR;
551 #endif
552 
553           if (dir && chdir(dir) < 0) {
554                     perror(dir);
555                     error("Cannot chdir to %s.", dir);
556           }
557           /* warn the player if he cannot write the record file */
558           /* perhaps we should also test whether . is writable */
559           /* unfortunately the access systemcall is worthless */
560           if (wr) {
561                     int fd;
562 
563                     if (dir == NULL)
564                               dir = ".";
565                     if ((fd = open(RECORD, O_RDWR)) < 0) {
566                               printf("Warning: cannot write %s/%s", dir, RECORD);
567                               getret();
568                     } else
569                               (void) close(fd);
570           }
571 }
572 #endif
573 
574 void
stop_occupation(void)575 stop_occupation(void)
576 {
577           if (occupation) {
578                     pline("You stop %s.", occtxt);
579                     occupation = 0;
580           }
581 }
582