option parsing buffer overflow vulnerability
[aNetHack.git] / src / pline.c
blob0534ba33d7a7c83fdd6e75246964e530b90dd1e0
1 /* NetHack 3.6 pline.c $NHDT-Date: 1461437814 2016/04/23 18:56:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.51 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #define NEED_VARARGS /* Uses ... */ /* comment line for pre-compiled headers \
6 */
7 #include "hack.h"
9 static boolean no_repeat = FALSE;
10 static char prevmsg[BUFSZ];
12 static char *FDECL(You_buf, (int));
13 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
14 static void FDECL(execplinehandler, (const char *));
15 #endif
17 /*VARARGS1*/
18 /* Note that these declarations rely on knowledge of the internals
19 * of the variable argument handling stuff in "tradstdc.h"
22 #if defined(USE_STDARG) || defined(USE_VARARGS)
23 static void FDECL(vpline, (const char *, va_list));
25 void pline
26 VA_DECL(const char *, line)
28 VA_START(line);
29 VA_INIT(line, char *);
30 vpline(line, VA_ARGS);
31 VA_END();
34 # ifdef USE_STDARG
35 static void
36 vpline(const char *line, va_list the_args)
37 # else
38 static void
39 vpline(line, the_args)
40 const char *line;
41 va_list the_args;
42 # endif
44 #else /* USE_STDARG | USE_VARARG */
46 # define vpline pline
48 void pline
49 VA_DECL(const char *, line)
50 #endif /* USE_STDARG | USE_VARARG */
51 { /* start of vpline() or of nested block in USE_OLDARG's pline() */
52 char pbuf[3 * BUFSZ];
53 int ln;
54 xchar msgtyp;
55 /* Do NOT use VA_START and VA_END in here... see above */
57 if (!line || !*line)
58 return;
59 #ifdef HANGUPHANDLING
60 if (program_state.done_hup)
61 return;
62 #endif
63 if (program_state.wizkit_wishing)
64 return;
66 if (index(line, '%')) {
67 Vsprintf(pbuf, line, VA_ARGS);
68 line = pbuf;
70 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
71 if (line != pbuf) /* no '%' was present */
72 (void) strncpy(pbuf, line, BUFSZ - 1); /* caveat: unterminated */
73 /* truncate, preserving the final 3 characters:
74 "___ extremely long text" -> "___ extremely l...ext"
75 (this may be suboptimal if overflow is less than 3) */
76 (void) strncpy(pbuf + BUFSZ - 1 - 6, "...", 3);
77 /* avoid strncpy; buffers could overlap if excess is small */
78 pbuf[BUFSZ - 1 - 3] = line[ln - 3];
79 pbuf[BUFSZ - 1 - 2] = line[ln - 2];
80 pbuf[BUFSZ - 1 - 1] = line[ln - 1];
81 pbuf[BUFSZ - 1] = '\0';
82 line = pbuf;
84 if (!iflags.window_inited) {
85 raw_print(line);
86 iflags.last_msg = PLNMSG_UNKNOWN;
87 return;
90 msgtyp = msgtype_type(line, no_repeat);
91 if (msgtyp == MSGTYP_NOSHOW
92 || (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg)))
93 return;
94 if (vision_full_recalc)
95 vision_recalc(0);
96 if (u.ux)
97 flush_screen(1); /* %% */
99 putstr(WIN_MESSAGE, 0, line);
101 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
102 execplinehandler(line);
103 #endif
105 /* this gets cleared after every pline message */
106 iflags.last_msg = PLNMSG_UNKNOWN;
107 strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0';
108 if (msgtyp == MSGTYP_STOP)
109 display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
111 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
112 /* provide closing brace for the nested block
113 which immediately follows USE_OLDARGS's VA_DECL() */
114 VA_END();
115 #endif
118 /*VARARGS1*/
119 void Norep
120 VA_DECL(const char *, line)
122 VA_START(line);
123 VA_INIT(line, const char *);
124 no_repeat = TRUE;
125 vpline(line, VA_ARGS);
126 no_repeat = FALSE;
127 VA_END();
128 return;
131 /* work buffer for You(), &c and verbalize() */
132 static char *you_buf = 0;
133 static int you_buf_siz = 0;
135 static char *
136 You_buf(siz)
137 int siz;
139 if (siz > you_buf_siz) {
140 if (you_buf)
141 free((genericptr_t) you_buf);
142 you_buf_siz = siz + 10;
143 you_buf = (char *) alloc((unsigned) you_buf_siz);
145 return you_buf;
148 void
149 free_youbuf()
151 if (you_buf)
152 free((genericptr_t) you_buf), you_buf = (char *) 0;
153 you_buf_siz = 0;
156 /* `prefix' must be a string literal, not a pointer */
157 #define YouPrefix(pointer, prefix, text) \
158 Strcpy((pointer = You_buf((int) (strlen(text) + sizeof prefix))), prefix)
160 #define YouMessage(pointer, prefix, text) \
161 strcat((YouPrefix(pointer, prefix, text), pointer), text)
163 /*VARARGS1*/
164 void You
165 VA_DECL(const char *, line)
167 char *tmp;
168 VA_START(line);
169 VA_INIT(line, const char *);
170 vpline(YouMessage(tmp, "You ", line), VA_ARGS);
171 VA_END();
174 /*VARARGS1*/
175 void Your
176 VA_DECL(const char *, line)
178 char *tmp;
179 VA_START(line);
180 VA_INIT(line, const char *);
181 vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
182 VA_END();
185 /*VARARGS1*/
186 void You_feel
187 VA_DECL(const char *, line)
189 char *tmp;
190 VA_START(line);
191 VA_INIT(line, const char *);
192 if (Unaware)
193 YouPrefix(tmp, "You dream that you feel ", line);
194 else
195 YouPrefix(tmp, "You feel ", line);
196 vpline(strcat(tmp, line), VA_ARGS);
197 VA_END();
200 /*VARARGS1*/
201 void You_cant
202 VA_DECL(const char *, line)
204 char *tmp;
205 VA_START(line);
206 VA_INIT(line, const char *);
207 vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
208 VA_END();
211 /*VARARGS1*/
212 void pline_The
213 VA_DECL(const char *, line)
215 char *tmp;
216 VA_START(line);
217 VA_INIT(line, const char *);
218 vpline(YouMessage(tmp, "The ", line), VA_ARGS);
219 VA_END();
222 /*VARARGS1*/
223 void There
224 VA_DECL(const char *, line)
226 char *tmp;
227 VA_START(line);
228 VA_INIT(line, const char *);
229 vpline(YouMessage(tmp, "There ", line), VA_ARGS);
230 VA_END();
233 /*VARARGS1*/
234 void You_hear
235 VA_DECL(const char *, line)
237 char *tmp;
239 if (Deaf || !flags.acoustics)
240 return;
241 VA_START(line);
242 VA_INIT(line, const char *);
243 if (Underwater)
244 YouPrefix(tmp, "You barely hear ", line);
245 else if (Unaware)
246 YouPrefix(tmp, "You dream that you hear ", line);
247 else
248 YouPrefix(tmp, "You hear ", line);
249 vpline(strcat(tmp, line), VA_ARGS);
250 VA_END();
253 /*VARARGS1*/
254 void You_see
255 VA_DECL(const char *, line)
257 char *tmp;
259 VA_START(line);
260 VA_INIT(line, const char *);
261 if (Unaware)
262 YouPrefix(tmp, "You dream that you see ", line);
263 else if (Blind) /* caller should have caught this... */
264 YouPrefix(tmp, "You sense ", line);
265 else
266 YouPrefix(tmp, "You see ", line);
267 vpline(strcat(tmp, line), VA_ARGS);
268 VA_END();
271 /* Print a message inside double-quotes.
272 * The caller is responsible for checking deafness.
273 * Gods can speak directly to you in spite of deafness.
275 /*VARARGS1*/
276 void verbalize
277 VA_DECL(const char *, line)
279 char *tmp;
281 VA_START(line);
282 VA_INIT(line, const char *);
283 tmp = You_buf((int) strlen(line) + sizeof "\"\"");
284 Strcpy(tmp, "\"");
285 Strcat(tmp, line);
286 Strcat(tmp, "\"");
287 vpline(tmp, VA_ARGS);
288 VA_END();
291 /*VARARGS1*/
292 /* Note that these declarations rely on knowledge of the internals
293 * of the variable argument handling stuff in "tradstdc.h"
296 #if defined(USE_STDARG) || defined(USE_VARARGS)
297 static void FDECL(vraw_printf, (const char *, va_list));
299 void raw_printf
300 VA_DECL(const char *, line)
302 VA_START(line);
303 VA_INIT(line, char *);
304 vraw_printf(line, VA_ARGS);
305 VA_END();
308 # ifdef USE_STDARG
309 static void
310 vraw_printf(const char *line, va_list the_args)
311 # else
312 static void
313 vraw_printf(line, the_args)
314 const char *line;
315 va_list the_args;
316 # endif
318 #else /* USE_STDARG | USE_VARARG */
320 void raw_printf
321 VA_DECL(const char *, line)
322 #endif
324 char pbuf[3 * BUFSZ];
325 int ln;
326 /* Do NOT use VA_START and VA_END in here... see above */
328 if (index(line, '%')) {
329 Vsprintf(pbuf, line, VA_ARGS);
330 line = pbuf;
332 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
333 if (line != pbuf)
334 line = strncpy(pbuf, line, BUFSZ - 1);
335 /* unlike pline, we don't futz around to keep last few chars */
336 pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */
338 raw_print(line);
339 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
340 VA_END(); /* (see vpline) */
341 #endif
344 /*VARARGS1*/
345 void impossible
346 VA_DECL(const char *, s)
348 char pbuf[2 * BUFSZ];
349 VA_START(s);
350 VA_INIT(s, const char *);
351 if (program_state.in_impossible)
352 panic("impossible called impossible");
354 program_state.in_impossible = 1;
355 Vsprintf(pbuf, s, VA_ARGS);
356 pbuf[BUFSZ - 1] = '\0'; /* sanity */
357 paniclog("impossible", pbuf);
358 pline("%s", pbuf);
359 pline("Program in disorder - perhaps you'd better #quit.");
360 program_state.in_impossible = 0;
361 VA_END();
364 const char *
365 align_str(alignment)
366 aligntyp alignment;
368 switch ((int) alignment) {
369 case A_CHAOTIC:
370 return "chaotic";
371 case A_NEUTRAL:
372 return "neutral";
373 case A_LAWFUL:
374 return "lawful";
375 case A_NONE:
376 return "unaligned";
378 return "unknown";
381 void
382 mstatusline(mtmp)
383 register struct monst *mtmp;
385 aligntyp alignment = mon_aligntyp(mtmp);
386 char info[BUFSZ], monnambuf[BUFSZ];
388 info[0] = 0;
389 if (mtmp->mtame) {
390 Strcat(info, ", tame");
391 if (wizard) {
392 Sprintf(eos(info), " (%d", mtmp->mtame);
393 if (!mtmp->isminion)
394 Sprintf(eos(info), "; hungry %ld; apport %d",
395 EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport);
396 Strcat(info, ")");
398 } else if (mtmp->mpeaceful)
399 Strcat(info, ", peaceful");
401 if (mtmp->data == &mons[PM_LONG_WORM]) {
402 int segndx, nsegs = count_wsegs(mtmp);
404 /* the worm code internals don't consider the head of be one of
405 the worm's segments, but we count it as such when presenting
406 worm feedback to the player */
407 if (!nsegs) {
408 Strcat(info, ", single segment");
409 } else {
410 ++nsegs; /* include head in the segment count */
411 segndx = wseg_at(mtmp, bhitpos.x, bhitpos.y);
412 Sprintf(eos(info), ", %d%s of %d segments",
413 segndx, ordin(segndx), nsegs);
416 if (mtmp->cham >= LOW_PM && mtmp->data != &mons[mtmp->cham])
417 /* don't reveal the innate form (chameleon, vampire, &c),
418 just expose the fact that this current form isn't it */
419 Strcat(info, ", shapechanger");
420 /* pets eating mimic corpses mimic while eating, so this comes first */
421 if (mtmp->meating)
422 Strcat(info, ", eating");
423 /* a stethoscope exposes mimic before getting here so this
424 won't be relevant for it, but wand of probing doesn't */
425 if (mtmp->mundetected || mtmp->m_ap_type)
426 mhidden_description(mtmp, TRUE, eos(info));
427 if (mtmp->mcan)
428 Strcat(info, ", cancelled");
429 if (mtmp->mconf)
430 Strcat(info, ", confused");
431 if (mtmp->mblinded || !mtmp->mcansee)
432 Strcat(info, ", blind");
433 if (mtmp->mstun)
434 Strcat(info, ", stunned");
435 if (mtmp->msleeping)
436 Strcat(info, ", asleep");
437 #if 0 /* unfortunately mfrozen covers temporary sleep and being busy \
438 (donning armor, for instance) as well as paralysis */
439 else if (mtmp->mfrozen) Strcat(info, ", paralyzed");
440 #else
441 else if (mtmp->mfrozen || !mtmp->mcanmove)
442 Strcat(info, ", can't move");
443 #endif
444 /* [arbitrary reason why it isn't moving] */
445 else if (mtmp->mstrategy & STRAT_WAITMASK)
446 Strcat(info, ", meditating");
447 if (mtmp->mflee)
448 Strcat(info, ", scared");
449 if (mtmp->mtrapped)
450 Strcat(info, ", trapped");
451 if (mtmp->mspeed)
452 Strcat(info, mtmp->mspeed == MFAST ? ", fast" : mtmp->mspeed == MSLOW
453 ? ", slow"
454 : ", ???? speed");
455 if (mtmp->minvis)
456 Strcat(info, ", invisible");
457 if (mtmp == u.ustuck)
458 Strcat(info, sticks(youmonst.data)
459 ? ", held by you"
460 : !u.uswallow ? ", holding you"
461 : attacktype_fordmg(u.ustuck->data,
462 AT_ENGL, AD_DGST)
463 ? ", digesting you"
464 : is_animal(u.ustuck->data)
465 ? ", swallowing you"
466 : ", engulfing you");
467 if (mtmp == u.usteed)
468 Strcat(info, ", carrying you");
470 /* avoid "Status of the invisible newt ..., invisible" */
471 /* and unlike a normal mon_nam, use "saddled" even if it has a name */
472 Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *) 0,
473 (SUPPRESS_IT | SUPPRESS_INVISIBLE), FALSE));
475 pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", monnambuf,
476 align_str(alignment), mtmp->m_lev, mtmp->mhp, mtmp->mhpmax,
477 find_mac(mtmp), info);
480 void
481 ustatusline()
483 char info[BUFSZ];
485 info[0] = '\0';
486 if (Sick) {
487 Strcat(info, ", dying from");
488 if (u.usick_type & SICK_VOMITABLE)
489 Strcat(info, " food poisoning");
490 if (u.usick_type & SICK_NONVOMITABLE) {
491 if (u.usick_type & SICK_VOMITABLE)
492 Strcat(info, " and");
493 Strcat(info, " illness");
496 if (Stoned)
497 Strcat(info, ", solidifying");
498 if (Slimed)
499 Strcat(info, ", becoming slimy");
500 if (Strangled)
501 Strcat(info, ", being strangled");
502 if (Vomiting)
503 Strcat(info, ", nauseated"); /* !"nauseous" */
504 if (Confusion)
505 Strcat(info, ", confused");
506 if (Blind) {
507 Strcat(info, ", blind");
508 if (u.ucreamed) {
509 if ((long) u.ucreamed < Blinded || Blindfolded
510 || !haseyes(youmonst.data))
511 Strcat(info, ", cover");
512 Strcat(info, "ed by sticky goop");
513 } /* note: "goop" == "glop"; variation is intentional */
515 if (Stunned)
516 Strcat(info, ", stunned");
517 if (!u.usteed && Wounded_legs) {
518 const char *what = body_part(LEG);
519 if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
520 what = makeplural(what);
521 Sprintf(eos(info), ", injured %s", what);
523 if (Glib)
524 Sprintf(eos(info), ", slippery %s", makeplural(body_part(HAND)));
525 if (u.utrap)
526 Strcat(info, ", trapped");
527 if (Fast)
528 Strcat(info, Very_fast ? ", very fast" : ", fast");
529 if (u.uundetected)
530 Strcat(info, ", concealed");
531 if (Invis)
532 Strcat(info, ", invisible");
533 if (u.ustuck) {
534 if (sticks(youmonst.data))
535 Strcat(info, ", holding ");
536 else
537 Strcat(info, ", held by ");
538 Strcat(info, mon_nam(u.ustuck));
541 pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", plname,
542 piousness(FALSE, align_str(u.ualign.type)),
543 Upolyd ? mons[u.umonnum].mlevel : u.ulevel, Upolyd ? u.mh : u.uhp,
544 Upolyd ? u.mhmax : u.uhpmax, u.uac, info);
547 void
548 self_invis_message()
550 pline("%s %s.",
551 Hallucination ? "Far out, man! You" : "Gee! All of a sudden, you",
552 See_invisible ? "can see right through yourself"
553 : "can't see yourself");
556 char *
557 piousness(showneg, suffix)
558 boolean showneg;
559 const char *suffix;
561 static char buf[32]; /* bigger than "insufficiently neutral" */
562 const char *pio;
564 /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */
565 if (u.ualign.record >= 20)
566 pio = "piously";
567 else if (u.ualign.record > 13)
568 pio = "devoutly";
569 else if (u.ualign.record > 8)
570 pio = "fervently";
571 else if (u.ualign.record > 3)
572 pio = "stridently";
573 else if (u.ualign.record == 3)
574 pio = "";
575 else if (u.ualign.record > 0)
576 pio = "haltingly";
577 else if (u.ualign.record == 0)
578 pio = "nominally";
579 else if (!showneg)
580 pio = "insufficiently";
581 else if (u.ualign.record >= -3)
582 pio = "strayed";
583 else if (u.ualign.record >= -8)
584 pio = "sinned";
585 else
586 pio = "transgressed";
588 Sprintf(buf, "%s", pio);
589 if (suffix && (!showneg || u.ualign.record >= 0)) {
590 if (u.ualign.record != 3)
591 Strcat(buf, " ");
592 Strcat(buf, suffix);
594 return buf;
597 void
598 pudding_merge_message(otmp, otmp2)
599 struct obj *otmp;
600 struct obj *otmp2;
602 boolean visible =
603 cansee(otmp->ox, otmp->oy) || cansee(otmp2->ox, otmp2->oy);
604 boolean onfloor = otmp->where == OBJ_FLOOR || otmp2->where == OBJ_FLOOR;
605 boolean inpack = carried(otmp) || carried(otmp2);
607 /* the player will know something happened inside his own inventory */
608 if ((!Blind && visible) || inpack) {
609 if (Hallucination) {
610 if (onfloor) {
611 You_see("parts of the floor melting!");
612 } else if (inpack) {
613 Your("pack reaches out and grabs something!");
615 /* even though we can see where they should be,
616 * they'll be out of our view (minvent or container)
617 * so don't actually show anything */
618 } else if (onfloor || inpack) {
619 pline("The %s coalesce%s.", makeplural(obj_typename(otmp->otyp)),
620 inpack ? " inside your pack" : "");
622 } else {
623 You_hear("a faint sloshing sound.");
627 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
628 static boolean use_pline_handler = TRUE;
629 static void
630 execplinehandler(line)
631 const char *line;
633 int f;
634 const char *args[3];
635 char *env;
637 if (!use_pline_handler)
638 return;
640 if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
641 use_pline_handler = FALSE;
642 return;
645 f = fork();
646 if (f == 0) { /* child */
647 args[0] = env;
648 args[1] = line;
649 args[2] = NULL;
650 (void) setgid(getgid());
651 (void) setuid(getuid());
652 (void) execv(args[0], (char *const *) args);
653 perror((char *) 0);
654 (void) fprintf(stderr, "Exec to message handler %s failed.\n",
655 env);
656 terminate(EXIT_FAILURE);
657 } else if (f == -1) {
658 perror((char *) 0);
659 use_pline_handler = FALSE;
660 pline("Fork to message handler failed.");
663 #endif /* defined(POSIX_TYPES) || defined(__GNUC__) */
665 /*pline.c*/