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-2019 */
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)
137 /* Do NOT use VA_START and VA_END in here... see above */
141 #ifdef HANGUPHANDLING
142 if (program_state.done_hup)
145 if (program_state.wizkit_wishing)
148 if (index(line, '%')) {
149 #if !defined(NO_VSNPRINTF)
150 vlen = vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
151 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG)
152 if (vlen >= (int) sizeof pbuf)
153 panic("%s: truncation of buffer at %zu of %d bytes",
154 "pline", sizeof pbuf, vlen);
157 Vsprintf(pbuf, line, VA_ARGS);
161 if ((ln = (int) strlen(line)) > BUFSZ - 1) {
162 if (line != pbuf) /* no '%' was present */
163 (void) strncpy(pbuf, line, BUFSZ - 1); /* caveat: unterminated */
164 /* truncate, preserving the final 3 characters:
165 "___ extremely long text" -> "___ extremely l...ext"
166 (this may be suboptimal if overflow is less than 3) */
167 (void) strncpy(pbuf + BUFSZ - 1 - 6, "...", 3);
168 /* avoid strncpy; buffers could overlap if excess is small */
169 pbuf[BUFSZ - 1 - 3] = line[ln - 3];
170 pbuf[BUFSZ - 1 - 2] = line[ln - 2];
171 pbuf[BUFSZ - 1 - 1] = line[ln - 1];
172 pbuf[BUFSZ - 1] = '\0';
177 /* We hook here early to have options-agnostic output.
178 * Unfortunately, that means Norep() isn't honored (general issue) and
179 * that short lines aren't combined into one longer one (tty behavior).
181 if ((pline_flags & SUPPRESS_HISTORY) == 0)
184 /* use raw_print() if we're called too early (or perhaps too late
185 during shutdown) or if we're being called recursively (probably
186 via debugpline() in the interface code) */
187 if (in_pline++ || !iflags.window_inited) {
188 /* [we should probably be using raw_printf("\n%s", line) here] */
190 iflags.last_msg = PLNMSG_UNKNOWN;
194 msgtyp = MSGTYP_NORMAL;
195 no_repeat = (pline_flags & PLINE_NOREPEAT) ? TRUE : FALSE;
196 if ((pline_flags & OVERRIDE_MSGTYPE) == 0) {
197 msgtyp = msgtype_type(line, no_repeat);
198 if ((pline_flags & URGENT_MESSAGE) == 0
199 && (msgtyp == MSGTYP_NOSHOW
200 || (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg))))
201 /* FIXME: we need a way to tell our caller that this message
202 * was suppressed so that caller doesn't set iflags.last_msg
203 * for something that hasn't been shown, otherwise a subsequent
204 * message which uses alternate wording based on that would be
205 * doing so out of context and probably end up seeming silly.
206 * (Not an issue for no-repeat but matters for no-show.)
211 if (vision_full_recalc)
214 flush_screen(1); /* %% */
218 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
219 execplinehandler(line);
222 /* this gets cleared after every pline message */
223 iflags.last_msg = PLNMSG_UNKNOWN;
224 (void) strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0';
225 if (msgtyp == MSGTYP_STOP)
226 display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
232 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
233 /* provide closing brace for the nested block
234 which immediately follows USE_OLDARGS's VA_DECL() */
239 /* pline() variant which can override MSGTYPE handling or suppress
240 message history (tty interface uses pline() to issue prompts and
241 they shouldn't be blockable via MSGTYPE=hide) */
244 VA_DECL2(unsigned, pflags, const char *, line)
247 VA_INIT(line, const char *);
248 pline_flags = pflags;
249 vpline(line, VA_ARGS);
257 VA_DECL(const char *, line)
260 VA_INIT(line, const char *);
261 pline_flags = PLINE_NOREPEAT;
262 vpline(line, VA_ARGS);
268 /* work buffer for You(), &c and verbalize() */
269 static char *you_buf = 0;
270 static int you_buf_siz = 0;
276 if (siz > you_buf_siz) {
278 free((genericptr_t) you_buf);
279 you_buf_siz = siz + 10;
280 you_buf = (char *) alloc((unsigned) you_buf_siz);
289 free((genericptr_t) you_buf), you_buf = (char *) 0;
293 /* `prefix' must be a string literal, not a pointer */
294 #define YouPrefix(pointer, prefix, text) \
295 Strcpy((pointer = You_buf((int) (strlen(text) + sizeof prefix))), prefix)
297 #define YouMessage(pointer, prefix, text) \
298 strcat((YouPrefix(pointer, prefix, text), pointer), text)
302 VA_DECL(const char *, line)
307 VA_INIT(line, const char *);
309 vpline(YouMessage(tmp, "You ", line), VA_ARGS);
311 vpline(YouMessage(tmp, "
\82 \82È
\82½
\82Í", line), VA_ARGS);
317 VA_DECL(const char *, line)
322 VA_INIT(line, const char *);
324 vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
326 vpline(YouMessage(tmp, "
\82 \82È
\82½
\82Ì", line), VA_ARGS);
332 VA_DECL(const char *, line)
337 VA_INIT(line, const char *);
340 YouPrefix(tmp, "You dream that you feel ", line);
342 YouPrefix(tmp, "
\82 \82È
\82½
\82Í
\96²
\82Ì
\92\86\82Å", line);
345 YouPrefix(tmp, "You feel ", line);
347 YouPrefix(tmp, "
\82 \82È
\82½
\82Í", line);
348 vpline(strcat(tmp, line), VA_ARGS);
354 VA_DECL(const char *, line)
359 VA_INIT(line, const char *);
361 vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
363 vpline(YouMessage(tmp, "
\82 \82È
\82½
\82Í", line), VA_ARGS);
369 VA_DECL(const char *, line)
374 VA_INIT(line, const char *);
376 vpline(YouMessage(tmp, "The ", line), VA_ARGS);
378 vpline(YouMessage(tmp, "", line), VA_ARGS);
384 VA_DECL(const char *, line)
389 VA_INIT(line, const char *);
391 vpline(YouMessage(tmp, "There ", line), VA_ARGS);
393 vpline(YouMessage(tmp, "", line), VA_ARGS);
399 VA_DECL(const char *, line)
407 if (Deaf || !flags.acoustics)
410 VA_INIT(line, const char *);
413 YouPrefix(tmp, "You barely hear ", line);
415 YouPrefix(tmp, "You dream that you hear ", line);
417 YouPrefix(tmp, "You hear ", line); /* Deaf-aware */
418 vpline(strcat(tmp, line), VA_ARGS);
421 adj = "
\82©
\82·
\82©
\82É";
423 adj = "
\96²
\82Ì
\92\86\82Å";
426 tmp = You_buf(strlen(adj) + strlen(line) + sizeof("
\82 \82È
\82½
\82Í "));
428 p = (char *)strstr(line, "
\95·
\82±") ;
430 Strcpy(tmp, "
\82 \82È
\82½
\82Í");
433 if (p != NULL || (p = (char *)strstr(line, "
\95·
\82¢")) != NULL){
434 strncat(tmp, line, (p - line));
440 vpline(tmp, VA_ARGS);
447 VA_DECL(const char *, line)
452 VA_INIT(line, const char *);
455 YouPrefix(tmp, "You dream that you see ", line);
457 YouPrefix(tmp, "
\82 \82È
\82½
\82Í
\96²
\82Ì
\92\86\82Å", line);
458 #if 0 /*JP*//*
\82±
\82±
\82Í
\8cÄ
\82Ñ
\8fo
\82µ
\8c³
\82Å
\8f\88\97\9d\82·
\82é?*/
459 else if (Blind) /* caller should have caught this... */
460 YouPrefix(tmp, "You sense ", line);
464 YouPrefix(tmp, "You see ", line);
466 YouPrefix(tmp, "
\82 \82È
\82½
\82Í", line);
467 vpline(strcat(tmp, line), VA_ARGS);
471 /* Print a message inside double-quotes.
472 * The caller is responsible for checking deafness.
473 * Gods can speak directly to you in spite of deafness.
477 VA_DECL(const char *, line)
482 VA_INIT(line, const char *);
484 tmp = You_buf((int) strlen(line) + sizeof "\"\"");
489 tmp = You_buf((int) strlen(line) + sizeof "
\81u
\81v");
494 vpline(tmp, VA_ARGS);
499 /* Note that these declarations rely on knowledge of the internals
500 * of the variable argument handling stuff in "tradstdc.h"
503 #if defined(USE_STDARG) || defined(USE_VARARGS)
504 static void FDECL(vraw_printf, (const char *, va_list));
507 VA_DECL(const char *, line)
510 VA_INIT(line, char *);
511 vraw_printf(line, VA_ARGS);
517 vraw_printf(const char *line, va_list the_args)
520 vraw_printf(line, the_args)
525 #else /* USE_STDARG | USE_VARARG */
528 VA_DECL(const char *, line)
531 char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
532 /* Do NOT use VA_START and VA_END in here... see above */
534 if (index(line, '%')) {
535 #if !defined(NO_VSNPRINTF)
536 (void) vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
538 Vsprintf(pbuf, line, VA_ARGS);
542 if ((int) strlen(line) > BUFSZ - 1) {
544 line = strncpy(pbuf, line, BUFSZ - 1);
545 /* unlike pline, we don't futz around to keep last few chars */
546 pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */
549 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
550 execplinehandler(line);
552 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
553 VA_END(); /* (see vpline) */
559 VA_DECL(const char *, s)
561 char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
564 VA_INIT(s, const char *);
565 if (program_state.in_impossible)
566 panic("impossible called impossible");
568 program_state.in_impossible = 1;
569 #if !defined(NO_VSNPRINTF)
570 (void) vsnprintf(pbuf, sizeof pbuf, s, VA_ARGS);
572 Vsprintf(pbuf, s, VA_ARGS);
574 pbuf[BUFSZ - 1] = '\0'; /* sanity */
575 paniclog("impossible", pbuf);
576 if (iflags.debug_fuzzer)
578 pline("%s", VA_PASS1(pbuf));
581 Strcpy(pbuf, "Program in disorder!");
583 Strcpy(pbuf, "
\83v
\83\8d\83O
\83\89\83\80\82É
\8fá
\8aQ
\94
\90¶
\81I");
584 if (program_state.something_worth_saving)
586 Strcat(pbuf, " (Saving and reloading may fix this problem.)");
588 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)");
589 pline("%s", VA_PASS1(pbuf));
591 program_state.in_impossible = 0;
595 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
596 static boolean use_pline_handler = TRUE;
599 execplinehandler(line)
606 if (!use_pline_handler)
609 if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
610 use_pline_handler = FALSE;
615 if (f == 0) { /* child */
619 (void) setgid(getgid());
620 (void) setuid(getuid());
621 (void) execv(args[0], (char *const *) args);
623 (void) fprintf(stderr, "Exec to message handler %s failed.\n", env);
624 nh_terminate(EXIT_FAILURE);
628 waitpid(f, &status, 0);
629 } else if (f == -1) {
631 use_pline_handler = FALSE;
632 pline("%s", VA_PASS1("Fork to message handler failed."));
635 #endif /* MSGHANDLER && (POSIX_TYPES || __GNUC__) */
638 * varargs handling for files.c
640 #if defined(USE_STDARG) || defined(USE_VARARGS)
641 static void FDECL(vconfig_error_add, (const char *, va_list));
646 VA_DECL(const char *, str)
649 VA_INIT(str, char *);
650 vconfig_error_add(str, VA_ARGS);
656 vconfig_error_add(const char *str, va_list the_args)
659 vconfig_error_add(str, the_args)
664 #else /* !(USE_STDARG || USE_VARARG) => USE_OLDARGS */
669 VA_DECL(const char *, str)
670 #endif /* ?(USE_STDARG || USE_VARARG) */
671 { /* start of vconf...() or of nested block in USE_OLDARG's conf...() */
672 #if !defined(NO_VSNPRINTF)
675 char buf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
677 #if !defined(NO_VSNPRINTF)
678 vlen = vsnprintf(buf, sizeof buf, str, VA_ARGS);
679 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) && defined(DEBUG)
680 if (vlen >= (int) sizeof buf)
681 panic("%s: truncation of buffer at %zu of %d bytes",
682 "config_error_add", sizeof buf, vlen);
685 Vsprintf(buf, str, VA_ARGS);
687 buf[BUFSZ - 1] = '\0';
690 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
691 VA_END(); /* (see pline/vpline -- ends nested block for USE_OLDARGS) */