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