1 /* NetHack 3.7 pline.c $NHDT-Date: 1606504240 2020/11/27 19:10:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.100 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2018. */
4 /* NetHack may be freely redistributed. See license for details. */
5
6 #include "hack.h"
7
8 #define BIGBUFSZ (5 * BUFSZ) /* big enough to format a 4*BUFSZ string (from
9 * config file parsing) with modest decoration;
10 * result will then be truncated to BUFSZ-1 */
11
12 static void putmesg(const char *);
13 static char *You_buf(int);
14 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
15 static void execplinehandler(const char *);
16 #endif
17
18 #ifdef USER_SOUNDS
19 extern void maybe_play_sound(const char *);
20 #endif
21
22 #if defined(DUMPLOG) || defined(DUMPHTML)
23
24 /* keep the most recent DUMPLOG_MSG_COUNT messages */
25 void
dumplogmsg(const char * line)26 dumplogmsg(const char *line)
27 {
28 /*
29 * TODO:
30 * This essentially duplicates message history, which is
31 * currently implemented in an interface-specific manner.
32 * The core should take responsibility for that and have
33 * this share it.
34 */
35 unsigned indx = g.saved_pline_index; /* next slot to use */
36 char *oldest = g.saved_plines[indx]; /* current content of that slot */
37
38 if (!strncmp(line, "Unknown command", 15))
39 return;
40 if (oldest && strlen(oldest) >= strlen(line)) {
41 /* this buffer will gradually shrink until the 'else' is needed;
42 there's no pressing need to track allocation size instead */
43 Strcpy(oldest, line);
44 } else {
45 if (oldest)
46 free((genericptr_t) oldest);
47 g.saved_plines[indx] = dupstr(line);
48 }
49 g.saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT;
50 }
51
52 /* called during save (unlike the interface-specific message history,
53 this data isn't saved and restored); end-of-game releases saved_plines[]
54 while writing its contents to the final dump log */
55 void
dumplogfreemessages(void)56 dumplogfreemessages(void)
57 {
58 unsigned i;
59
60 for (i = 0; i < DUMPLOG_MSG_COUNT; ++i)
61 if (g.saved_plines[i])
62 free((genericptr_t) g.saved_plines[i]), g.saved_plines[i] = 0;
63 g.saved_pline_index = 0;
64 }
65 #endif
66
67 /* keeps windowprocs usage out of pline() */
68 static void
putmesg(const char * line)69 putmesg(const char *line)
70 {
71 int attr = ATR_NONE;
72
73 if ((g.pline_flags & URGENT_MESSAGE) != 0
74 && (windowprocs.wincap2 & WC2_URGENT_MESG) != 0)
75 attr |= ATR_URGENT;
76 if ((g.pline_flags & SUPPRESS_HISTORY) != 0
77 && (windowprocs.wincap2 & WC2_SUPPRESS_HIST) != 0)
78 attr |= ATR_NOHISTORY;
79
80 putstr(WIN_MESSAGE, attr, line);
81 }
82
83 static void vpline(const char *, va_list);
84
85 DISABLE_WARNING_FORMAT_NONLITERAL
86
87 void
pline(const char * line,...)88 pline(const char *line, ...)
89 {
90 va_list the_args;
91
92 va_start(the_args, line);
93 vpline(line, the_args);
94 va_end(the_args);
95 }
96
97 static void
vpline(const char * line,va_list the_args)98 vpline(const char *line, va_list the_args)
99 {
100 static int in_pline = 0;
101 char pbuf[BIGBUFSZ]; /* will get chopped down to BUFSZ-1 if longer */
102 int ln;
103 int msgtyp;
104 #if !defined(NO_VSNPRINTF)
105 int vlen = 0;
106 #endif
107 boolean no_repeat;
108
109 if (!line || !*line)
110 return;
111 #ifdef HANGUPHANDLING
112 if (g.program_state.done_hup)
113 return;
114 #endif
115 if (g.program_state.wizkit_wishing)
116 return;
117
118 if (index(line, '%')) {
119 #if !defined(NO_VSNPRINTF)
120 vlen = vsnprintf(pbuf, sizeof(pbuf), line, the_args);
121 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG)
122 if (vlen >= (int) sizeof pbuf)
123 panic("%s: truncation of buffer at %zu of %d bytes",
124 "pline", sizeof pbuf, vlen);
125 #else
126 nhUse(vlen);
127 #endif
128 #else
129 Vsprintf(pbuf, line, the_args);
130 #endif
131 line = pbuf;
132 }
133 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
134 if (line != pbuf) /* no '%' was present */
135 (void) strncpy(pbuf, line, BUFSZ - 1); /* caveat: unterminated */
136 /* truncate, preserving the final 3 characters:
137 "___ extremely long text" -> "___ extremely l...ext"
138 (this may be suboptimal if overflow is less than 3) */
139 memcpy(pbuf + BUFSZ - 1 - 6, "...", 3);
140 /* avoid strncpy; buffers could overlap if excess is small */
141 pbuf[BUFSZ - 1 - 3] = line[ln - 3];
142 pbuf[BUFSZ - 1 - 2] = line[ln - 2];
143 pbuf[BUFSZ - 1 - 1] = line[ln - 1];
144 pbuf[BUFSZ - 1] = '\0';
145 line = pbuf;
146 }
147 msgtyp = MSGTYP_NORMAL;
148
149 #if defined(DUMPLOG) || defined(DUMPHTML)
150 /* We hook here early to have options-agnostic output.
151 * Unfortunately, that means Norep() isn't honored (general issue) and
152 * that short lines aren't combined into one longer one (tty behavior).
153 */
154 if ((g.pline_flags & SUPPRESS_HISTORY) == 0)
155 dumplogmsg(line);
156 #endif
157 FUZLOG(line);
158 /* use raw_print() if we're called too early (or perhaps too late
159 during shutdown) or if we're being called recursively (probably
160 via debugpline() in the interface code) */
161 if (in_pline++ || !iflags.window_inited) {
162 /* [we should probably be using raw_printf("\n%s", line) here] */
163 raw_print(line);
164 iflags.last_msg = PLNMSG_UNKNOWN;
165 goto pline_done;
166 }
167
168 no_repeat = (g.pline_flags & PLINE_NOREPEAT) ? TRUE : FALSE;
169 if ((g.pline_flags & OVERRIDE_MSGTYPE) == 0) {
170 msgtyp = msgtype_type(line, no_repeat);
171 #ifdef USER_SOUNDS
172 if (msgtyp == MSGTYP_NORMAL || msgtyp == MSGTYP_NOSHOW)
173 maybe_play_sound(line);
174 #endif
175 if ((g.pline_flags & URGENT_MESSAGE) == 0
176 && (msgtyp == MSGTYP_NOSHOW
177 || (msgtyp == MSGTYP_NOREP && !strcmp(line, g.prevmsg))))
178 /* FIXME: we need a way to tell our caller that this message
179 * was suppressed so that caller doesn't set iflags.last_msg
180 * for something that hasn't been shown, otherwise a subsequent
181 * message which uses alternate wording based on that would be
182 * doing so out of context and probably end up seeming silly.
183 * (Not an issue for no-repeat but matters for no-show.)
184 */
185 goto pline_done;
186 }
187
188 if (g.vision_full_recalc)
189 vision_recalc(0);
190 if (u.ux)
191 flush_screen(1); /* %% */
192
193 putmesg(line);
194
195 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
196 execplinehandler(line);
197 #endif
198
199 /* this gets cleared after every pline message */
200 iflags.last_msg = PLNMSG_UNKNOWN;
201 (void) strncpy(g.prevmsg, line, BUFSZ), g.prevmsg[BUFSZ - 1] = '\0';
202 switch (msgtyp) {
203 case MSGTYP_ALERT:
204 iflags.msg_is_alert = TRUE; /* <TAB> */
205 /* FALLTHRU */
206 case MSGTYP_STOP:
207 display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
208 break;
209 }
210 iflags.msg_is_alert = FALSE;
211
212 pline_done:
213 --in_pline;
214 }
215
216 RESTORE_WARNING_FORMAT_NONLITERAL
217
218 /* pline() variant which can override MSGTYPE handling or suppress
219 message history (tty interface uses pline() to issue prompts and
220 they shouldn't be blockable via MSGTYPE=hide) */
221 void
custompline(unsigned pflags,const char * line,...)222 custompline(unsigned pflags, const char * line, ...)
223 {
224 va_list the_args;
225
226 va_start(the_args, line);
227 g.pline_flags = pflags;
228 vpline(line, the_args);
229 g.pline_flags = 0;
230 va_end(the_args);
231 }
232
233 void
Norep(const char * line,...)234 Norep(const char *line, ...)
235 {
236 va_list the_args;
237
238 va_start(the_args, line);
239 g.pline_flags = PLINE_NOREPEAT;
240 vpline(line, the_args);
241 g.pline_flags = 0;
242 va_end(the_args);
243 }
244
245 static char *
You_buf(int siz)246 You_buf(int siz)
247 {
248 if (siz > g.you_buf_siz) {
249 if (g.you_buf)
250 free((genericptr_t) g.you_buf);
251 g.you_buf_siz = siz + 10;
252 g.you_buf = (char *) alloc((unsigned) g.you_buf_siz);
253 }
254 return g.you_buf;
255 }
256
257 void
free_youbuf(void)258 free_youbuf(void)
259 {
260 if (g.you_buf)
261 free((genericptr_t) g.you_buf), g.you_buf = (char *) 0;
262 g.you_buf_siz = 0;
263 }
264
265 /* `prefix' must be a string literal, not a pointer */
266 #define YouPrefix(pointer, prefix, text) \
267 Strcpy((pointer = You_buf((int) (strlen(text) + sizeof prefix))), prefix)
268
269 #define YouMessage(pointer, prefix, text) \
270 strcat((YouPrefix(pointer, prefix, text), pointer), text)
271
272 void
You(const char * line,...)273 You(const char *line, ...)
274 {
275 va_list the_args;
276 char *tmp;
277
278 va_start(the_args, line);
279 vpline(YouMessage(tmp, "You ", line), the_args);
280 va_end(the_args);
281 }
282
283 void
Your(const char * line,...)284 Your(const char *line, ...)
285 {
286 va_list the_args;
287 char *tmp;
288
289 va_start(the_args, line);
290 vpline(YouMessage(tmp, "Your ", line), the_args);
291 va_end(the_args);
292 }
293
294 void
You_feel(const char * line,...)295 You_feel(const char *line, ...)
296 {
297 va_list the_args;
298 char *tmp;
299
300 va_start(the_args, line);
301 if (Unaware)
302 YouPrefix(tmp, "You dream that you feel ", line);
303 else
304 YouPrefix(tmp, "You feel ", line);
305 vpline(strcat(tmp, line), the_args);
306 va_end(the_args);
307 }
308
309 void
You_cant(const char * line,...)310 You_cant(const char *line, ...)
311 {
312 va_list the_args;
313 char *tmp;
314
315 va_start(the_args, line);
316 vpline(YouMessage(tmp, "You can't ", line), the_args);
317 va_end(the_args);
318 }
319
320 void
pline_The(const char * line,...)321 pline_The(const char *line, ...)
322 {
323 va_list the_args;
324 char *tmp;
325
326 va_start(the_args, line);
327 vpline(YouMessage(tmp, "The ", line), the_args);
328 va_end(the_args);
329 }
330
331 void
There(const char * line,...)332 There(const char *line, ...)
333 {
334 va_list the_args;
335 char *tmp;
336
337 va_start(the_args, line);
338 vpline(YouMessage(tmp, "There ", line), the_args);
339 va_end(the_args);
340 }
341
342 void
You_hear(const char * line,...)343 You_hear(const char *line, ...)
344 {
345 va_list the_args;
346 char *tmp;
347
348 if (Deaf || !flags.acoustics)
349 return;
350 va_start(the_args, line);
351 if (Underwater)
352 YouPrefix(tmp, "You barely hear ", line);
353 else if (Unaware)
354 YouPrefix(tmp, "You dream that you hear ", line);
355 else
356 YouPrefix(tmp, "You hear ", line); /* Deaf-aware */
357 vpline(strcat(tmp, line), the_args);
358 va_end(the_args);
359 }
360
361 void
You_see(const char * line,...)362 You_see(const char *line, ...)
363 {
364 va_list the_args;
365 char *tmp;
366
367 va_start(the_args, line);
368 if (Unaware)
369 YouPrefix(tmp, "You dream that you see ", line);
370 else if (Blind) /* caller should have caught this... */
371 YouPrefix(tmp, "You sense ", line);
372 else
373 YouPrefix(tmp, "You see ", line);
374 vpline(strcat(tmp, line), the_args);
375 va_end(the_args);
376 }
377
378 /* Print a message inside double-quotes.
379 * The caller is responsible for checking deafness.
380 * Gods can speak directly to you in spite of deafness.
381 */
382 void
verbalize(const char * line,...)383 verbalize(const char *line, ...)
384 {
385 va_list the_args;
386 char *tmp;
387
388 va_start(the_args, line);
389 tmp = You_buf((int) strlen(line) + sizeof "\"\"");
390 Strcpy(tmp, "\"");
391 Strcat(tmp, line);
392 Strcat(tmp, "\"");
393 vpline(tmp, the_args);
394 va_end(the_args);
395 }
396
397 static void vraw_printf(const char *, va_list);
398
399 void
raw_printf(const char * line,...)400 raw_printf(const char *line, ...)
401 {
402 va_list the_args;
403
404 va_start(the_args, line);
405 vraw_printf(line, the_args);
406 va_end(the_args);
407 }
408
409 DISABLE_WARNING_FORMAT_NONLITERAL
410
411 static void
vraw_printf(const char * line,va_list the_args)412 vraw_printf(const char *line, va_list the_args)
413 {
414 char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
415
416 if (index(line, '%')) {
417 #if !defined(NO_VSNPRINTF)
418 (void) vsnprintf(pbuf, sizeof(pbuf), line, the_args);
419 #else
420 Vsprintf(pbuf, line, the_args);
421 #endif
422 line = pbuf;
423 }
424 if ((int) strlen(line) > BUFSZ - 1) {
425 if (line != pbuf)
426 line = strncpy(pbuf, line, BUFSZ - 1);
427 /* unlike pline, we don't futz around to keep last few chars */
428 pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */
429 }
430 raw_print(line);
431 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
432 execplinehandler(line);
433 #endif
434 }
435
436 void
impossible(const char * s,...)437 impossible(const char *s, ...)
438 {
439 va_list the_args;
440 char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
441
442 va_start(the_args, s);
443 if (g.program_state.in_impossible)
444 panic("impossible called impossible");
445
446 g.program_state.in_impossible = 1;
447 #if !defined(NO_VSNPRINTF)
448 (void) vsnprintf(pbuf, sizeof(pbuf), s, the_args);
449 #else
450 Vsprintf(pbuf, s, the_args);
451 #endif
452 va_end(the_args);
453 pbuf[BUFSZ - 1] = '\0'; /* sanity */
454 paniclog("impossible", pbuf);
455 if (iflags.debug_fuzzer)
456 panic("%s", pbuf);
457 pline("%s", pbuf);
458 /* reuse pbuf[] */
459 Strcpy(pbuf, "Program in disorder!");
460 if (g.program_state.something_worth_saving)
461 Strcat(pbuf, " (Saving and reloading may fix this problem.)");
462 pline("%s", pbuf);
463 pline("Please report these messages to %s.", DEVTEAM_EMAIL);
464 if (sysopt.support) {
465 pline("Alternatively, contact local support: %s", sysopt.support);
466 }
467
468 g.program_state.in_impossible = 0;
469 }
470
471 RESTORE_WARNING_FORMAT_NONLITERAL
472
473 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
474 static boolean use_pline_handler = TRUE;
475
476 static void
execplinehandler(const char * line)477 execplinehandler(const char *line)
478 {
479 int f;
480 const char *args[3];
481 char *env;
482
483 if (!use_pline_handler)
484 return;
485
486 if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
487 use_pline_handler = FALSE;
488 return;
489 }
490
491 f = fork();
492 if (f == 0) { /* child */
493 args[0] = env;
494 args[1] = line;
495 args[2] = NULL;
496 (void) setgid(getgid());
497 (void) setuid(getuid());
498 (void) execv(args[0], (char *const *) args);
499 perror((char *) 0);
500 (void) fprintf(stderr, "Exec to message handler %s failed.\n", env);
501 nh_terminate(EXIT_FAILURE);
502 } else if (f > 0) {
503 int status;
504
505 waitpid(f, &status, 0);
506 } else if (f == -1) {
507 perror((char *) 0);
508 use_pline_handler = FALSE;
509 pline("%s", "Fork to message handler failed.");
510 }
511 }
512 #endif /* MSGHANDLER && (POSIX_TYPES || __GNUC__) */
513
514 /*
515 * varargs handling for files.c
516 */
517 static void vconfig_error_add(const char *, va_list);
518
519 DISABLE_WARNING_FORMAT_NONLITERAL
520
521 void
config_error_add(const char * str,...)522 config_error_add(const char *str, ...)
523 {
524 va_list the_args;
525
526 va_start(the_args, str);
527 vconfig_error_add(str, the_args);
528 va_end(the_args);
529 }
530
531 static void
vconfig_error_add(const char * str,va_list the_args)532 vconfig_error_add(const char *str, va_list the_args)
533 { /* start of vconf...() or of nested block in USE_OLDARG's conf...() */
534 #if !defined(NO_VSNPRINTF)
535 int vlen = 0;
536 #endif
537 char buf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
538
539 #if !defined(NO_VSNPRINTF)
540 vlen = vsnprintf(buf, sizeof(buf), str, the_args);
541 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG)
542 if (vlen >= (int) sizeof buf)
543 panic("%s: truncation of buffer at %zu of %d bytes",
544 "config_error_add", sizeof buf, vlen);
545 #else
546 nhUse(vlen);
547 #endif
548 #else
549 Vsprintf(buf, str, the_args);
550 #endif
551 buf[BUFSZ - 1] = '\0';
552 config_erradd(buf);
553 }
554
555 RESTORE_WARNING_FORMAT_NONLITERAL
556
557 /* nhassert_failed is called when an nhassert's condition is false */
558 void
nhassert_failed(const char * expression,const char * filepath,int line)559 nhassert_failed(const char *expression, const char *filepath, int line)
560 {
561 const char * filename;
562
563 /* attempt to get filename from path. TODO: we really need a port provided
564 * function to return a filename from a path */
565 filename = strrchr(filepath, '/');
566 filename = (filename == NULL ? strrchr(filepath, '\\') : filename);
567 filename = (filename == NULL ? filepath : filename + 1);
568
569 impossible("nhassert(%s) failed in file '%s' at line %d", expression, filename, line);
570 }
571
572 /*pline.c*/
573