option parsing buffer overflow vulnerability
[aNetHack.git] / src / rumors.c
blob18656e5e71301c635ac2015c08ec54e68fa463a1
1 /* NetHack 3.6 rumors.c $NHDT-Date: 1446713640 2015/11/05 08:54:00 $ $NHDT-Branch: master $:$NHDT-Revision: 1.27 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "lev.h"
7 #include "dlb.h"
9 /* [note: this comment is fairly old, but still accurate for 3.1]
10 * Rumors have been entirely rewritten to speed up the access. This is
11 * essential when working from floppies. Using fseek() the way that's done
12 * here means rumors following longer rumors are output more often than those
13 * following shorter rumors. Also, you may see the same rumor more than once
14 * in a particular game (although the odds are highly against it), but
15 * this also happens with real fortune cookies. -dgk
18 /* 3.6
19 * The rumors file consists of a "do not edit" line, then a line containing
20 * three sets of three counts (first two in decimal, third in hexadecimal).
21 * The first set has the number of true rumors, the count in bytes for all
22 * true rumors, and the file offset to the first one. The second set has
23 * the same group of numbers for the false rumors. The third set has 0 for
24 * count, 0 for size, and the file offset for end-of-file. The offset of
25 * the first true rumor plus the size of the true rumors matches the offset
26 * of the first false rumor. Likewise, the offset of the first false rumor
27 * plus the size of the false rumors matches the offset for end-of-file.
30 /* 3.1 [now obsolete for rumors but still accurate for oracles]
31 * The rumors file consists of a "do not edit" line, a hexadecimal number
32 * giving the number of bytes of useful/true rumors, followed by those
33 * true rumors (one per line), followed by the useless/false/misleading/cute
34 * rumors (also one per line). Number of bytes of untrue rumors is derived
35 * via fseek(EOF)+ftell().
37 * The oracles file consists of a "do not edit" comment, a decimal count N
38 * and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
39 * records, separated by "---" lines. The first oracle is a special case,
40 * and placed there by 'makedefs'.
43 STATIC_DCL void FDECL(init_rumors, (dlb *));
44 STATIC_DCL void FDECL(init_oracles, (dlb *));
46 /* rumor size variables are signed so that value -1 can be used as a flag */
47 static long true_rumor_size = 0L, false_rumor_size;
48 /* rumor start offsets are unsigned because they're handled via %lx format */
49 static unsigned long true_rumor_start, false_rumor_start;
50 /* rumor end offsets are signed because they're compared with [dlb_]ftell() */
51 static long true_rumor_end, false_rumor_end;
52 /* oracles are handled differently from rumors... */
53 static int oracle_flg = 0; /* -1=>don't use, 0=>need init, 1=>init done */
54 static unsigned oracle_cnt = 0;
55 static unsigned long *oracle_loc = 0;
57 STATIC_OVL void
58 init_rumors(fp)
59 dlb *fp;
61 static const char rumors_header[] = "%d,%ld,%lx;%d,%ld,%lx;0,0,%lx\n";
62 int true_count, false_count; /* in file but not used here */
63 unsigned long eof_offset;
64 char line[BUFSZ];
66 (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment */
67 (void) dlb_fgets(line, sizeof line, fp);
68 if (sscanf(line, rumors_header, &true_count, &true_rumor_size,
69 &true_rumor_start, &false_count, &false_rumor_size,
70 &false_rumor_start, &eof_offset) == 7
71 && true_rumor_size > 0L
72 && false_rumor_size > 0L) {
73 true_rumor_end = (long) true_rumor_start + true_rumor_size;
74 /* assert( true_rumor_end == false_rumor_start ); */
75 false_rumor_end = (long) false_rumor_start + false_rumor_size;
76 /* assert( false_rumor_end == eof_offset ); */
77 } else {
78 true_rumor_size = -1L; /* init failed */
79 (void) dlb_fclose(fp);
83 /* exclude_cookie is a hack used because we sometimes want to get rumors in a
84 * context where messages such as "You swallowed the fortune!" that refer to
85 * cookies should not appear. This has no effect for true rumors since none
86 * of them contain such references anyway.
88 char *
89 getrumor(truth, rumor_buf, exclude_cookie)
90 int truth; /* 1=true, -1=false, 0=either */
91 char *rumor_buf;
92 boolean exclude_cookie;
94 dlb *rumors;
95 long tidbit, beginning;
96 char *endp, line[BUFSZ], xbuf[BUFSZ];
98 rumor_buf[0] = '\0';
99 if (true_rumor_size < 0L) /* we couldn't open RUMORFILE */
100 return rumor_buf;
102 rumors = dlb_fopen(RUMORFILE, "r");
104 if (rumors) {
105 int count = 0;
106 int adjtruth;
108 do {
109 rumor_buf[0] = '\0';
110 if (true_rumor_size == 0L) { /* if this is 1st outrumor() */
111 init_rumors(rumors);
112 if (true_rumor_size < 0L) { /* init failed */
113 Sprintf(rumor_buf, "Error reading \"%.80s\".", RUMORFILE);
114 return rumor_buf;
118 * input: 1 0 -1
119 * rn2 \ +1 2=T 1=T 0=F
120 * adj./ +0 1=T 0=F -1=F
122 switch (adjtruth = truth + rn2(2)) {
123 case 2: /*(might let a bogus input arg sneak thru)*/
124 case 1:
125 beginning = (long) true_rumor_start;
126 tidbit = Rand() % true_rumor_size;
127 break;
128 case 0: /* once here, 0 => false rather than "either"*/
129 case -1:
130 beginning = (long) false_rumor_start;
131 tidbit = Rand() % false_rumor_size;
132 break;
133 default:
134 impossible("strange truth value for rumor");
135 return strcpy(rumor_buf, "Oops...");
137 (void) dlb_fseek(rumors, beginning + tidbit, SEEK_SET);
138 (void) dlb_fgets(line, sizeof line, rumors);
139 if (!dlb_fgets(line, sizeof line, rumors)
140 || (adjtruth > 0 && dlb_ftell(rumors) > true_rumor_end)) {
141 /* reached end of rumors -- go back to beginning */
142 (void) dlb_fseek(rumors, beginning, SEEK_SET);
143 (void) dlb_fgets(line, sizeof line, rumors);
145 if ((endp = index(line, '\n')) != 0)
146 *endp = 0;
147 Strcat(rumor_buf, xcrypt(line, xbuf));
148 } while (
149 count++ < 50 && exclude_cookie
150 && (strstri(rumor_buf, "fortune") || strstri(rumor_buf, "pity")));
151 (void) dlb_fclose(rumors);
152 if (count >= 50)
153 impossible("Can't find non-cookie rumor?");
154 else if (!in_mklev) /* avoid exercizing wisdom for graffiti */
155 exercise(A_WIS, (adjtruth > 0));
156 } else {
157 pline("Can't open rumors file!");
158 true_rumor_size = -1; /* don't try to open it again */
160 /* this is safe either way, so do it always since we can't get the definition
161 * out of makedefs.c
163 #define PAD_RUMORS_TO
164 #ifdef PAD_RUMORS_TO
165 /* remove padding */
167 char *x = eos(rumor_buf) - 1;
169 while (x > rumor_buf && *x == '_')
170 x--;
171 *++x = '\n';
172 *x = '\0';
174 #endif
175 return rumor_buf;
179 * test that the true/false rumor boundaries are valid.
181 void
182 rumor_check()
184 dlb *rumors;
185 winid tmpwin;
186 char *endp, line[BUFSZ], xbuf[BUFSZ], rumor_buf[BUFSZ];
188 if (true_rumor_size < 0L) { /* we couldn't open RUMORFILE */
189 no_rumors:
190 pline("rumors not accessible.");
191 return;
194 rumors = dlb_fopen(RUMORFILE, "r");
196 if (rumors) {
197 long ftell_rumor_start = 0L;
199 rumor_buf[0] = '\0';
200 if (true_rumor_size == 0L) { /* if this is 1st outrumor() */
201 init_rumors(rumors);
202 if (true_rumor_size < 0L)
203 goto no_rumors; /* init failed */
205 tmpwin = create_nhwindow(NHW_TEXT);
208 * reveal the values.
211 Sprintf(
212 rumor_buf,
213 "T start=%06ld (%06lx), end=%06ld (%06lx), size=%06ld (%06lx)",
214 (long) true_rumor_start, true_rumor_start, true_rumor_end,
215 (unsigned long) true_rumor_end, true_rumor_size,
216 (unsigned long) true_rumor_size);
217 putstr(tmpwin, 0, rumor_buf);
219 Sprintf(
220 rumor_buf,
221 "F start=%06ld (%06lx), end=%06ld (%06lx), size=%06ld (%06lx)",
222 (long) false_rumor_start, false_rumor_start, false_rumor_end,
223 (unsigned long) false_rumor_end, false_rumor_size,
224 (unsigned long) false_rumor_size);
225 putstr(tmpwin, 0, rumor_buf);
228 * check the first rumor (start of true rumors) by
229 * skipping the first two lines.
231 * Then seek to the start of the false rumors (based on
232 * the value read in rumors, and display it.
234 rumor_buf[0] = '\0';
235 (void) dlb_fseek(rumors, (long) true_rumor_start, SEEK_SET);
236 ftell_rumor_start = dlb_ftell(rumors);
237 (void) dlb_fgets(line, sizeof line, rumors);
238 if ((endp = index(line, '\n')) != 0)
239 *endp = 0;
240 Sprintf(rumor_buf, "T %06ld %s", ftell_rumor_start,
241 xcrypt(line, xbuf));
242 putstr(tmpwin, 0, rumor_buf);
243 /* find last true rumor */
244 while (dlb_fgets(line, sizeof line, rumors)
245 && dlb_ftell(rumors) < true_rumor_end)
246 continue;
247 if ((endp = index(line, '\n')) != 0)
248 *endp = 0;
249 Sprintf(rumor_buf, " %6s %s", "", xcrypt(line, xbuf));
250 putstr(tmpwin, 0, rumor_buf);
252 rumor_buf[0] = '\0';
253 (void) dlb_fseek(rumors, (long) false_rumor_start, SEEK_SET);
254 ftell_rumor_start = dlb_ftell(rumors);
255 (void) dlb_fgets(line, sizeof line, rumors);
256 if ((endp = index(line, '\n')) != 0)
257 *endp = 0;
258 Sprintf(rumor_buf, "F %06ld %s", ftell_rumor_start,
259 xcrypt(line, xbuf));
260 putstr(tmpwin, 0, rumor_buf);
261 /* find last false rumor */
262 while (dlb_fgets(line, sizeof line, rumors)
263 && dlb_ftell(rumors) < false_rumor_end)
264 continue;
265 if ((endp = index(line, '\n')) != 0)
266 *endp = 0;
267 Sprintf(rumor_buf, " %6s %s", "", xcrypt(line, xbuf));
268 putstr(tmpwin, 0, rumor_buf);
270 (void) dlb_fclose(rumors);
271 display_nhwindow(tmpwin, TRUE);
272 destroy_nhwindow(tmpwin);
273 } else {
274 impossible("Can't open rumors file!");
275 true_rumor_size = -1; /* don't try to open it again */
279 /* Gets a random line of text from file 'fname', and returns it. */
280 char *
281 get_rnd_text(fname, buf)
282 const char *fname;
283 char *buf;
285 dlb *fh;
287 buf[0] = '\0';
289 fh = dlb_fopen(fname, "r");
291 if (fh) {
292 /* TODO: cache sizetxt, starttxt, endtxt. maybe cache file contents?
294 long sizetxt = 0, starttxt = 0, endtxt = 0, tidbit = 0;
295 char *endp, line[BUFSZ], xbuf[BUFSZ];
296 (void) dlb_fgets(line, sizeof line,
297 fh); /* skip "don't edit" comment */
299 (void) dlb_fseek(fh, 0L, SEEK_CUR);
300 starttxt = dlb_ftell(fh);
301 (void) dlb_fseek(fh, 0L, SEEK_END);
302 endtxt = dlb_ftell(fh);
303 sizetxt = endtxt - starttxt;
304 tidbit = Rand() % sizetxt;
306 (void) dlb_fseek(fh, starttxt + tidbit, SEEK_SET);
307 (void) dlb_fgets(line, sizeof line, fh);
308 if (!dlb_fgets(line, sizeof line, fh)) {
309 (void) dlb_fseek(fh, starttxt, SEEK_SET);
310 (void) dlb_fgets(line, sizeof line, fh);
312 if ((endp = index(line, '\n')) != 0)
313 *endp = 0;
314 Strcat(buf, xcrypt(line, xbuf));
315 (void) dlb_fclose(fh);
316 } else
317 impossible("Can't open file %s!", fname);
318 return buf;
321 void
322 outrumor(truth, mechanism)
323 int truth; /* 1=true, -1=false, 0=either */
324 int mechanism;
326 static const char fortune_msg[] =
327 "This cookie has a scrap of paper inside.";
328 const char *line;
329 char buf[BUFSZ];
330 boolean reading = (mechanism == BY_COOKIE || mechanism == BY_PAPER);
332 if (reading) {
333 /* deal with various things that prevent reading */
334 if (is_fainted() && mechanism == BY_COOKIE)
335 return;
336 else if (Blind) {
337 if (mechanism == BY_COOKIE)
338 pline(fortune_msg);
339 pline("What a pity that you cannot read it!");
340 return;
343 line = getrumor(truth, buf, reading ? FALSE : TRUE);
344 if (!*line)
345 line = "NetHack rumors file closed for renovation.";
346 switch (mechanism) {
347 case BY_ORACLE:
348 /* Oracle delivers the rumor */
349 pline("True to her word, the Oracle %ssays: ",
350 (!rn2(4) ? "offhandedly "
351 : (!rn2(3) ? "casually "
352 : (rn2(2) ? "nonchalantly " : ""))));
353 verbalize1(line);
354 /* [WIS exercized by getrumor()] */
355 return;
356 case BY_COOKIE:
357 pline(fortune_msg);
358 /* FALLTHRU */
359 case BY_PAPER:
360 pline("It reads:");
361 break;
363 pline1(line);
366 STATIC_OVL void
367 init_oracles(fp)
368 dlb *fp;
370 register int i;
371 char line[BUFSZ];
372 int cnt = 0;
374 /* this assumes we're only called once */
375 (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment*/
376 (void) dlb_fgets(line, sizeof line, fp);
377 if (sscanf(line, "%5d\n", &cnt) == 1 && cnt > 0) {
378 oracle_cnt = (unsigned) cnt;
379 oracle_loc = (unsigned long *) alloc((unsigned) cnt * sizeof(long));
380 for (i = 0; i < cnt; i++) {
381 (void) dlb_fgets(line, sizeof line, fp);
382 (void) sscanf(line, "%5lx\n", &oracle_loc[i]);
385 return;
388 void
389 save_oracles(fd, mode)
390 int fd, mode;
392 if (perform_bwrite(mode)) {
393 bwrite(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
394 if (oracle_cnt)
395 bwrite(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof(long));
397 if (release_data(mode)) {
398 if (oracle_cnt) {
399 free((genericptr_t) oracle_loc);
400 oracle_loc = 0, oracle_cnt = 0, oracle_flg = 0;
405 void
406 restore_oracles(fd)
407 int fd;
409 mread(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt);
410 if (oracle_cnt) {
411 oracle_loc = (unsigned long *) alloc(oracle_cnt * sizeof(long));
412 mread(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof(long));
413 oracle_flg = 1; /* no need to call init_oracles() */
417 void
418 outoracle(special, delphi)
419 boolean special;
420 boolean delphi;
422 char line[COLNO];
423 char *endp;
424 dlb *oracles;
425 int oracle_idx;
426 char xbuf[BUFSZ];
428 /* early return if we couldn't open ORACLEFILE on previous attempt,
429 or if all the oracularities are already exhausted */
430 if (oracle_flg < 0 || (oracle_flg > 0 && oracle_cnt == 0))
431 return;
433 oracles = dlb_fopen(ORACLEFILE, "r");
435 if (oracles) {
436 winid tmpwin;
437 if (oracle_flg == 0) { /* if this is the first outoracle() */
438 init_oracles(oracles);
439 oracle_flg = 1;
440 if (oracle_cnt == 0)
441 return;
443 /* oracle_loc[0] is the special oracle;
444 oracle_loc[1..oracle_cnt-1] are normal ones */
445 if (oracle_cnt <= 1 && !special)
446 return; /*(shouldn't happen)*/
447 oracle_idx = special ? 0 : rnd((int) oracle_cnt - 1);
448 (void) dlb_fseek(oracles, (long) oracle_loc[oracle_idx], SEEK_SET);
449 if (!special) /* move offset of very last one into this slot */
450 oracle_loc[oracle_idx] = oracle_loc[--oracle_cnt];
452 tmpwin = create_nhwindow(NHW_TEXT);
453 if (delphi)
454 putstr(tmpwin, 0,
455 special
456 ? "The Oracle scornfully takes all your money and says:"
457 : "The Oracle meditates for a moment and then intones:");
458 else
459 putstr(tmpwin, 0, "The message reads:");
460 putstr(tmpwin, 0, "");
462 while (dlb_fgets(line, COLNO, oracles) && strcmp(line, "---\n")) {
463 if ((endp = index(line, '\n')) != 0)
464 *endp = 0;
465 putstr(tmpwin, 0, xcrypt(line, xbuf));
467 display_nhwindow(tmpwin, TRUE);
468 destroy_nhwindow(tmpwin);
469 (void) dlb_fclose(oracles);
470 } else {
471 pline("Can't open oracles file!");
472 oracle_flg = -1; /* don't try to open it again */
477 doconsult(oracl)
478 struct monst *oracl;
480 long umoney;
481 int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel;
482 int add_xpts;
483 char qbuf[QBUFSZ];
485 multi = 0;
486 umoney = money_cnt(invent);
488 if (!oracl) {
489 There("is no one here to consult.");
490 return 0;
491 } else if (!oracl->mpeaceful) {
492 pline("%s is in no mood for consultations.", Monnam(oracl));
493 return 0;
494 } else if (!umoney) {
495 You("have no money.");
496 return 0;
499 Sprintf(qbuf, "\"Wilt thou settle for a minor consultation?\" (%d %s)",
500 minor_cost, currency((long) minor_cost));
501 switch (ynq(qbuf)) {
502 default:
503 case 'q':
504 return 0;
505 case 'y':
506 if (umoney < (long) minor_cost) {
507 You("don't even have enough money for that!");
508 return 0;
510 u_pay = minor_cost;
511 break;
512 case 'n':
513 if (umoney <= (long) minor_cost /* don't even ask */
514 || (oracle_cnt == 1 || oracle_flg < 0))
515 return 0;
516 Sprintf(qbuf, "\"Then dost thou desire a major one?\" (%d %s)",
517 major_cost, currency((long) major_cost));
518 if (yn(qbuf) != 'y')
519 return 0;
520 u_pay = (umoney < (long) major_cost) ? (int) umoney : major_cost;
521 break;
523 money2mon(oracl, (long) u_pay);
524 context.botl = 1;
525 add_xpts = 0; /* first oracle of each type gives experience points */
526 if (u_pay == minor_cost) {
527 outrumor(1, BY_ORACLE);
528 if (!u.uevent.minor_oracle)
529 add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10);
530 /* 5 pts if very 1st, or 2 pts if major already done */
531 u.uevent.minor_oracle = TRUE;
532 } else {
533 boolean cheapskate = u_pay < major_cost;
535 outoracle(cheapskate, TRUE);
536 if (!cheapskate && !u.uevent.major_oracle)
537 add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10);
538 /* ~100 pts if very 1st, ~40 pts if minor already done */
539 u.uevent.major_oracle = TRUE;
540 exercise(A_WIS, !cheapskate);
542 if (add_xpts) {
543 more_experienced(add_xpts, u_pay / 50);
544 newexplevel();
546 return 1;
549 /*rumors.c*/