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