1 /* NetHack 3.6 pline.c $NHDT-Date: 1549327495 2019/02/05 00:44:55 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.73 $ */
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. */
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
9 /* JNetHack may be freely redistributed. See license for details. */
11 #define NEED_VARARGS /* Uses ... */ /* comment line for pre-compiled headers */
14 #define BIGBUFSZ (5 * BUFSZ) /* big enough to format a 4*BUFSZ string (from
15 * config file parsing) with modest decoration;
16 * result will then be truncated to BUFSZ-1 */
18 static unsigned pline_flags = 0;
19 static char prevmsg[BUFSZ];
21 static void FDECL(putmesg, (const char *));
22 static char *FDECL(You_buf, (int));
23 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
24 static void FDECL(execplinehandler, (const char *));
28 /* also used in end.c */
29 unsigned saved_pline_index = 0; /* slot in saved_plines[] to use next */
30 char *saved_plines[DUMPLOG_MSG_COUNT] = { (char *) 0 };
32 /* keep the most recent DUMPLOG_MSG_COUNT messages */
39 * This essentially duplicates message history, which is
40 * currently implemented in an interface-specific manner.
41 * The core should take responsibility for that and have
44 unsigned indx = saved_pline_index; /* next slot to use */
45 char *oldest = saved_plines[indx]; /* current content of that slot */
47 if (oldest && strlen(oldest) >= strlen(line)) {
48 /* this buffer will gradually shrink until the 'else' is needed;
49 there's no pressing need to track allocation size instead */
53 free((genericptr_t) oldest);
54 saved_plines[indx] = dupstr(line);
56 saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT;
59 /* called during save (unlike the interface-specific message history,
60 this data isn't saved and restored); end-of-game releases saved_pline[]
61 while writing its contents to the final dump log */
67 for (indx = 0; indx < DUMPLOG_MSG_COUNT; ++indx)
68 if (saved_plines[indx])
69 free((genericptr_t) saved_plines[indx]), saved_plines[indx] = 0;
70 saved_pline_index = 0;
74 /* keeps windowprocs usage out of pline() */
81 if ((pline_flags & URGENT_MESSAGE) != 0
82 && (windowprocs.wincap2 & WC2_URGENT_MESG) != 0)
84 if ((pline_flags & SUPPRESS_HISTORY) != 0
85 && (windowprocs.wincap2 & WC2_SUPPRESS_HIST) != 0)
86 attr |= ATR_NOHISTORY;
88 putstr(WIN_MESSAGE, attr, line);
91 /* Note that these declarations rely on knowledge of the internals
92 * of the variable argument handling stuff in "tradstdc.h"
95 #if defined(USE_STDARG) || defined(USE_VARARGS)
96 static void FDECL(vpline, (const char *, va_list));
101 VA_DECL(const char *, line)
104 VA_INIT(line, char *);
105 vpline(line, VA_ARGS);
111 vpline(const char *line, va_list the_args)
114 vpline(line, the_args)
119 #else /* USE_STDARG | USE_VARARG */
121 # define vpline pline
126 VA_DECL(const char *, line)
127 #endif /* USE_STDARG | USE_VARARG */
128 { /* start of vpline() or of nested block in USE_OLDARG's pline() */
129 static int in_pline = 0;
130 char pbuf[BIGBUFSZ]; /* will get chopped down to BUFSZ-1 if longer */
133 #if !defined(NO_VSNPRINTF)
139 /* Do NOT use VA_START and VA_END in here... see above */
143 #ifdef HANGUPHANDLING
144 if (program_state.done_hup)
147 if (program_state.wizkit_wishing)
150 if (index(line, '%')) {
151 #if !defined(NO_VSNPRINTF)
153 vlen = vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
155 vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
157 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG)
158 if (vlen >= (int) sizeof pbuf)
159 panic("%s: truncation of buffer at %zu of %d bytes",
160 "pline", sizeof pbuf, vlen);
163 Vsprintf(pbuf, line, VA_ARGS);
167 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
168 if (line != pbuf) /* no '%' was present */
169 (void) strncpy(pbuf, line, BUFSZ - 1); /* caveat: unterminated */
170 /* truncate, preserving the final 3 characters:
171 "___ extremely long text" -> "___ extremely l...ext"
172 (this may be suboptimal if overflow is less than 3) */
173 (void) strncpy(pbuf + BUFSZ - 1 - 6, "...", 3);
174 /* avoid strncpy; buffers could overlap if excess is small */
175 pbuf[BUFSZ - 1 - 3] = line[ln - 3];
176 pbuf[BUFSZ - 1 - 2] = line[ln - 2];
177 pbuf[BUFSZ - 1 - 1] = line[ln - 1];
178 pbuf[BUFSZ - 1] = '\0';
183 /* We hook here early to have options-agnostic output.
184 * Unfortunately, that means Norep() isn't honored (general issue) and
185 * that short lines aren't combined into one longer one (tty behavior).
187 if ((pline_flags & SUPPRESS_HISTORY) == 0)
190 /* use raw_print() if we're called too early (or perhaps too late
191 during shutdown) or if we're being called recursively (probably
192 via debugpline() in the interface code) */
193 if (in_pline++ || !iflags.window_inited) {
194 /* [we should probably be using raw_printf("\n%s", line) here] */
196 iflags.last_msg = PLNMSG_UNKNOWN;
200 msgtyp = MSGTYP_NORMAL;
201 no_repeat = (pline_flags & PLINE_NOREPEAT) ? TRUE : FALSE;
202 if ((pline_flags & OVERRIDE_MSGTYPE) == 0) {
203 msgtyp = msgtype_type(line, no_repeat);
204 if ((pline_flags & URGENT_MESSAGE) == 0
205 && (msgtyp == MSGTYP_NOSHOW
206 || (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg))))
207 /* FIXME: we need a way to tell our caller that this message
208 * was suppressed so that caller doesn't set iflags.last_msg
209 * for something that hasn't been shown, otherwise a subsequent
210 * message which uses alternate wording based on that would be
211 * doing so out of context and probably end up seeming silly.
212 * (Not an issue for no-repeat but matters for no-show.)
217 if (vision_full_recalc)
220 flush_screen(1); /* %% */
224 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
225 execplinehandler(line);
228 /* this gets cleared after every pline message */
229 iflags.last_msg = PLNMSG_UNKNOWN;
230 (void) strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0';
231 if (msgtyp == MSGTYP_STOP)
232 display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
238 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
239 /* provide closing brace for the nested block
240 which immediately follows USE_OLDARGS's VA_DECL() */
245 /* pline() variant which can override MSGTYPE handling or suppress
246 message history (tty interface uses pline() to issue prompts and
247 they shouldn't be blockable via MSGTYPE=hide) */
250 VA_DECL2(unsigned, pflags, const char *, line)
253 VA_INIT(line, const char *);
254 pline_flags = pflags;
255 vpline(line, VA_ARGS);
263 VA_DECL(const char *, line)
266 VA_INIT(line, const char *);
267 pline_flags = PLINE_NOREPEAT;
268 vpline(line, VA_ARGS);
274 /* work buffer for You(), &c and verbalize() */
275 static char *you_buf = 0;
276 static int you_buf_siz = 0;
282 if (siz > you_buf_siz) {
284 free((genericptr_t) you_buf);
285 you_buf_siz = siz + 10;
286 you_buf = (char *) alloc((unsigned) you_buf_siz);
295 free((genericptr_t) you_buf), you_buf = (char *) 0;
299 /* `prefix' must be a string literal, not a pointer */
300 #define YouPrefix(pointer, prefix, text) \
301 Strcpy((pointer = You_buf((int) (strlen(text) + sizeof prefix))), prefix)
303 #define YouMessage(pointer, prefix, text) \
304 strcat((YouPrefix(pointer, prefix, text), pointer), text)
308 VA_DECL(const char *, line)
313 VA_INIT(line, const char *);
315 vpline(YouMessage(tmp, "You ", line), VA_ARGS);
317 vpline(YouMessage(tmp, "
\82 \82È
\82½
\82Í", line), VA_ARGS);
323 VA_DECL(const char *, line)
328 VA_INIT(line, const char *);
330 vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
332 vpline(YouMessage(tmp, "
\82 \82È
\82½
\82Ì", line), VA_ARGS);
338 VA_DECL(const char *, line)
343 VA_INIT(line, const char *);
346 YouPrefix(tmp, "You dream that you feel ", line);
348 YouPrefix(tmp, "
\82 \82È
\82½
\82Í
\96²
\82Ì
\92\86\82Å", line);
351 YouPrefix(tmp, "You feel ", line);
353 YouPrefix(tmp, "
\82 \82È
\82½
\82Í", line);
354 vpline(strcat(tmp, line), VA_ARGS);
360 VA_DECL(const char *, line)
365 VA_INIT(line, const char *);
367 vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
369 vpline(YouMessage(tmp, "
\82 \82È
\82½
\82Í", line), VA_ARGS);
375 VA_DECL(const char *, line)
380 VA_INIT(line, const char *);
382 vpline(YouMessage(tmp, "The ", line), VA_ARGS);
384 vpline(YouMessage(tmp, "", line), VA_ARGS);
390 VA_DECL(const char *, line)
395 VA_INIT(line, const char *);
397 vpline(YouMessage(tmp, "There ", line), VA_ARGS);
399 vpline(YouMessage(tmp, "", line), VA_ARGS);
405 VA_DECL(const char *, line)
413 if (Deaf || !flags.acoustics)
416 VA_INIT(line, const char *);
419 YouPrefix(tmp, "You barely hear ", line);
421 YouPrefix(tmp, "You dream that you hear ", line);
423 YouPrefix(tmp, "You hear ", line); /* Deaf-aware */
424 vpline(strcat(tmp, line), VA_ARGS);
427 adj = "
\82©
\82·
\82©
\82É";
429 adj = "
\96²
\82Ì
\92\86\82Å";
432 tmp = You_buf(strlen(adj) + strlen(line) + sizeof("
\82 \82È
\82½
\82Í "));
434 p = (char *)strstr(line, "
\95·
\82±") ;
436 Strcpy(tmp, "
\82 \82È
\82½
\82Í");
439 if (p != NULL || (p = (char *)strstr(line, "
\95·
\82¢")) != NULL){
440 strncat(tmp, line, (p - line));
446 vpline(tmp, VA_ARGS);
453 VA_DECL(const char *, line)
458 VA_INIT(line, const char *);
461 YouPrefix(tmp, "You dream that you see ", line);
463 YouPrefix(tmp, "
\82 \82È
\82½
\82Í
\96²
\82Ì
\92\86\82Å", line);
464 #if 0 /*JP*//*
\82±
\82±
\82Í
\8cÄ
\82Ñ
\8fo
\82µ
\8c³
\82Å
\8f\88\97\9d\82·
\82é?*/
465 else if (Blind) /* caller should have caught this... */
466 YouPrefix(tmp, "You sense ", line);
470 YouPrefix(tmp, "You see ", line);
472 YouPrefix(tmp, "
\82 \82È
\82½
\82Í", line);
473 vpline(strcat(tmp, line), VA_ARGS);
477 /* Print a message inside double-quotes.
478 * The caller is responsible for checking deafness.
479 * Gods can speak directly to you in spite of deafness.
483 VA_DECL(const char *, line)
488 VA_INIT(line, const char *);
490 tmp = You_buf((int) strlen(line) + sizeof "\"\"");
495 tmp = You_buf((int) strlen(line) + sizeof "
\81u
\81v");
500 vpline(tmp, VA_ARGS);
505 /* Note that these declarations rely on knowledge of the internals
506 * of the variable argument handling stuff in "tradstdc.h"
509 #if defined(USE_STDARG) || defined(USE_VARARGS)
510 static void FDECL(vraw_printf, (const char *, va_list));
513 VA_DECL(const char *, line)
516 VA_INIT(line, char *);
517 vraw_printf(line, VA_ARGS);
523 vraw_printf(const char *line, va_list the_args)
526 vraw_printf(line, the_args)
531 #else /* USE_STDARG | USE_VARARG */
534 VA_DECL(const char *, line)
537 char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
538 /* Do NOT use VA_START and VA_END in here... see above */
540 if (index(line, '%')) {
541 #if !defined(NO_VSNPRINTF)
542 (void) vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
544 Vsprintf(pbuf, line, VA_ARGS);
548 if ((int) strlen(line) > BUFSZ - 1) {
550 line = strncpy(pbuf, line, BUFSZ - 1);
551 /* unlike pline, we don't futz around to keep last few chars */
552 pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */
555 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
556 execplinehandler(line);
558 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
559 VA_END(); /* (see vpline) */
565 VA_DECL(const char *, s)
567 char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
570 VA_INIT(s, const char *);
571 if (program_state.in_impossible)
572 panic("impossible called impossible");
574 program_state.in_impossible = 1;
575 #if !defined(NO_VSNPRINTF)
576 (void) vsnprintf(pbuf, sizeof pbuf, s, VA_ARGS);
578 Vsprintf(pbuf, s, VA_ARGS);
580 pbuf[BUFSZ - 1] = '\0'; /* sanity */
581 paniclog("impossible", pbuf);
582 if (iflags.debug_fuzzer)
584 pline("%s", VA_PASS1(pbuf));
587 Strcpy(pbuf, "Program in disorder!");
589 Strcpy(pbuf, "
\83v
\83\8d\83O
\83\89\83\80\82É
\8fá
\8aQ
\94
\90¶
\81I");
590 if (program_state.something_worth_saving)
592 Strcat(pbuf, " (Saving and reloading may fix this problem.)");
594 Strcat(pbuf, " (
\95Û
\91¶
\82µ
\82Ä
\8dÄ
\93Ç
\82Ý
\8d\9e\82Ý
\82·
\82ê
\82Î
\96â
\91è
\89ð
\8c\88\82·
\82é
\82©
\82à
\82µ
\82ê
\82È
\82¢
\81D)");
595 pline("%s", VA_PASS1(pbuf));
597 program_state.in_impossible = 0;
601 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
602 static boolean use_pline_handler = TRUE;
605 execplinehandler(line)
612 if (!use_pline_handler)
615 if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
616 use_pline_handler = FALSE;
621 if (f == 0) { /* child */
625 (void) setgid(getgid());
626 (void) setuid(getuid());
627 (void) execv(args[0], (char *const *) args);
629 (void) fprintf(stderr, "Exec to message handler %s failed.\n", env);
630 nh_terminate(EXIT_FAILURE);
634 waitpid(f, &status, 0);
635 } else if (f == -1) {
637 use_pline_handler = FALSE;
638 pline("%s", VA_PASS1("Fork to message handler failed."));
641 #endif /* MSGHANDLER && (POSIX_TYPES || __GNUC__) */
644 * varargs handling for files.c
646 #if defined(USE_STDARG) || defined(USE_VARARGS)
647 static void FDECL(vconfig_error_add, (const char *, va_list));
652 VA_DECL(const char *, str)
655 VA_INIT(str, char *);
656 vconfig_error_add(str, VA_ARGS);
662 vconfig_error_add(const char *str, va_list the_args)
665 vconfig_error_add(str, the_args)
670 #else /* !(USE_STDARG || USE_VARARG) => USE_OLDARGS */
675 VA_DECL(const char *, str)
676 #endif /* ?(USE_STDARG || USE_VARARG) */
677 { /* start of vconf...() or of nested block in USE_OLDARG's conf...() */
678 #if !defined(NO_VSNPRINTF)
683 char buf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
685 #if !defined(NO_VSNPRINTF)
687 vlen = vsnprintf(buf, sizeof buf, str, VA_ARGS);
689 vsnprintf(buf, sizeof buf, str, VA_ARGS);
691 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG)
692 if (vlen >= (int) sizeof buf)
693 panic("%s: truncation of buffer at %zu of %d bytes",
694 "config_error_add", sizeof buf, vlen);
697 Vsprintf(buf, str, VA_ARGS);
699 buf[BUFSZ - 1] = '\0';
702 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
703 VA_END(); /* (see pline/vpline -- ends nested block for USE_OLDARGS) */