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