OSDN Git Service

fix #39767
[jnethack/source.git] / src / pline.c
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. */
5
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. */
10
11 #define NEED_VARARGS /* Uses ... */ /* comment line for pre-compiled headers */
12 #include "hack.h"
13
14 static unsigned pline_flags = 0;
15 static char prevmsg[BUFSZ];
16
17 static void FDECL(putmesg, (const char *));
18 static char *FDECL(You_buf, (int));
19 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
20 static void FDECL(execplinehandler, (const char *));
21 #endif
22
23 #ifdef DUMPLOG
24 /* also used in end.c */
25 unsigned saved_pline_index = 0; /* slot in saved_plines[] to use next */
26 char *saved_plines[DUMPLOG_MSG_COUNT] = { (char *) 0 };
27
28 /* keep the most recent DUMPLOG_MSG_COUNT messages */
29 void
30 dumplogmsg(line)
31 const char *line;
32 {
33     /*
34      * TODO:
35      *  This essentially duplicates message history, which is
36      *  currently implemented in an interface-specific manner.
37      *  The core should take responsibility for that and have
38      *  this share it.
39      */
40     unsigned indx = saved_pline_index; /* next slot to use */
41     char *oldest = saved_plines[indx]; /* current content of that slot */
42
43     if (oldest && strlen(oldest) >= strlen(line)) {
44         /* this buffer will gradually shrink until the 'else' is needed;
45            there's no pressing need to track allocation size instead */
46         Strcpy(oldest, line);
47     } else {
48         if (oldest)
49             free((genericptr_t) oldest);
50         saved_plines[indx] = dupstr(line);
51     }
52     saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT;
53 }
54
55 /* called during save (unlike the interface-specific message history,
56    this data isn't saved and restored); end-of-game releases saved_pline[]
57    while writing its contents to the final dump log */
58 void
59 dumplogfreemessages()
60 {
61     unsigned indx;
62
63     for (indx = 0; indx < DUMPLOG_MSG_COUNT; ++indx)
64         if (saved_plines[indx])
65             free((genericptr_t) saved_plines[indx]), saved_plines[indx] = 0;
66     saved_pline_index = 0;
67 }
68 #endif
69
70 /* keeps windowprocs usage out of pline() */
71 static void
72 putmesg(line)
73 const char *line;
74 {
75     int attr = ATR_NONE;
76
77     if ((pline_flags & URGENT_MESSAGE) != 0
78         && (windowprocs.wincap2 & WC2_URGENT_MESG) != 0)
79         attr |= ATR_URGENT;
80     if ((pline_flags & SUPPRESS_HISTORY) != 0
81         && (windowprocs.wincap2 & WC2_SUPPRESS_HIST) != 0)
82         attr |= ATR_NOHISTORY;
83
84     putstr(WIN_MESSAGE, attr, line);
85 }
86
87 /* Note that these declarations rely on knowledge of the internals
88  * of the variable argument handling stuff in "tradstdc.h"
89  */
90
91 #if defined(USE_STDARG) || defined(USE_VARARGS)
92 static void FDECL(vpline, (const char *, va_list));
93
94 /*VARARGS1*/
95 void
96 pline
97 VA_DECL(const char *, line)
98 {
99     VA_START(line);
100     VA_INIT(line, char *);
101     vpline(line, VA_ARGS);
102     VA_END();
103 }
104
105 # ifdef USE_STDARG
106 static void
107 vpline(const char *line, va_list the_args)
108 # else
109 static void
110 vpline(line, the_args)
111 const char *line;
112 va_list the_args;
113 # endif
114
115 #else /* USE_STDARG | USE_VARARG */
116
117 # define vpline pline
118
119 /*VARARGS1*/
120 void
121 pline
122 VA_DECL(const char *, line)
123 #endif /* USE_STDARG | USE_VARARG */
124 {       /* start of vpline() or of nested block in USE_OLDARG's pline() */
125     static int in_pline = 0;
126     char pbuf[3 * BUFSZ];
127     int ln;
128     int msgtyp;
129     boolean no_repeat;
130     /* Do NOT use VA_START and VA_END in here... see above */
131
132     if (!line || !*line)
133         return;
134 #ifdef HANGUPHANDLING
135     if (program_state.done_hup)
136         return;
137 #endif
138     if (program_state.wizkit_wishing)
139         return;
140
141     if (index(line, '%')) {
142         Vsprintf(pbuf, line, VA_ARGS);
143         line = pbuf;
144     }
145     if ((ln = (int) strlen(line)) > BUFSZ - 1) {
146         if (line != pbuf)                          /* no '%' was present */
147             (void) strncpy(pbuf, line, BUFSZ - 1); /* caveat: unterminated */
148         /* truncate, preserving the final 3 characters:
149            "___ extremely long text" -> "___ extremely l...ext"
150            (this may be suboptimal if overflow is less than 3) */
151         (void) strncpy(pbuf + BUFSZ - 1 - 6, "...", 3);
152         /* avoid strncpy; buffers could overlap if excess is small */
153         pbuf[BUFSZ - 1 - 3] = line[ln - 3];
154         pbuf[BUFSZ - 1 - 2] = line[ln - 2];
155         pbuf[BUFSZ - 1 - 1] = line[ln - 1];
156         pbuf[BUFSZ - 1] = '\0';
157         line = pbuf;
158     }
159
160 #ifdef DUMPLOG
161     /* We hook here early to have options-agnostic output.
162      * Unfortunately, that means Norep() isn't honored (general issue) and
163      * that short lines aren't combined into one longer one (tty behavior).
164      */
165     if ((pline_flags & SUPPRESS_HISTORY) == 0)
166         dumplogmsg(line);
167 #endif
168     /* use raw_print() if we're called too early (or perhaps too late
169        during shutdown) or if we're being called recursively (probably
170        via debugpline() in the interface code) */
171     if (in_pline++ || !iflags.window_inited) {
172         /* [we should probably be using raw_printf("\n%s", line) here] */
173         raw_print(line);
174         iflags.last_msg = PLNMSG_UNKNOWN;
175         goto pline_done;
176     }
177
178     msgtyp = MSGTYP_NORMAL;
179     no_repeat = (pline_flags & PLINE_NOREPEAT) ? TRUE : FALSE;
180     if ((pline_flags & OVERRIDE_MSGTYPE) == 0) {
181         msgtyp = msgtype_type(line, no_repeat);
182         if ((pline_flags & URGENT_MESSAGE) == 0
183             && (msgtyp == MSGTYP_NOSHOW
184                 || (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg))))
185             /* FIXME: we need a way to tell our caller that this message
186              * was suppressed so that caller doesn't set iflags.last_msg
187              * for something that hasn't been shown, otherwise a subsequent
188              * message which uses alternate wording based on that would be
189              * doing so out of context and probably end up seeming silly.
190              * (Not an issue for no-repeat but matters for no-show.)
191              */
192             goto pline_done;
193     }
194
195     if (vision_full_recalc)
196         vision_recalc(0);
197     if (u.ux)
198         flush_screen(1); /* %% */
199
200     putmesg(line);
201
202 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
203     execplinehandler(line);
204 #endif
205
206     /* this gets cleared after every pline message */
207     iflags.last_msg = PLNMSG_UNKNOWN;
208     (void) strncpy(prevmsg, line, BUFSZ), prevmsg[BUFSZ - 1] = '\0';
209     if (msgtyp == MSGTYP_STOP)
210         display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
211
212  pline_done:
213     --in_pline;
214     return;
215
216 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
217     /* provide closing brace for the nested block
218        which immediately follows USE_OLDARGS's VA_DECL() */
219     VA_END();
220 #endif
221 }
222
223 /* pline() variant which can override MSGTYPE handling or suppress
224    message history (tty interface uses pline() to issue prompts and
225    they shouldn't be blockable via MSGTYPE=hide) */
226 /*VARARGS2*/
227 void custompline
228 VA_DECL2(unsigned, pflags, const char *, line)
229 {
230     VA_START(line);
231     VA_INIT(line, const char *);
232     pline_flags = pflags;
233     vpline(line, VA_ARGS);
234     pline_flags = 0;
235     VA_END();
236     return;
237 }
238
239 /*VARARGS1*/
240 void Norep
241 VA_DECL(const char *, line)
242 {
243     VA_START(line);
244     VA_INIT(line, const char *);
245     pline_flags = PLINE_NOREPEAT;
246     vpline(line, VA_ARGS);
247     pline_flags = 0;
248     VA_END();
249     return;
250 }
251
252 /* work buffer for You(), &c and verbalize() */
253 static char *you_buf = 0;
254 static int you_buf_siz = 0;
255
256 static char *
257 You_buf(siz)
258 int siz;
259 {
260     if (siz > you_buf_siz) {
261         if (you_buf)
262             free((genericptr_t) you_buf);
263         you_buf_siz = siz + 10;
264         you_buf = (char *) alloc((unsigned) you_buf_siz);
265     }
266     return you_buf;
267 }
268
269 void
270 free_youbuf()
271 {
272     if (you_buf)
273         free((genericptr_t) you_buf), you_buf = (char *) 0;
274     you_buf_siz = 0;
275 }
276
277 /* `prefix' must be a string literal, not a pointer */
278 #define YouPrefix(pointer, prefix, text) \
279     Strcpy((pointer = You_buf((int) (strlen(text) + sizeof prefix))), prefix)
280
281 #define YouMessage(pointer, prefix, text) \
282     strcat((YouPrefix(pointer, prefix, text), pointer), text)
283
284 /*VARARGS1*/
285 void You
286 VA_DECL(const char *, line)
287 {
288     char *tmp;
289
290     VA_START(line);
291     VA_INIT(line, const char *);
292 /*JP
293     vpline(YouMessage(tmp, "You ", line), VA_ARGS);
294 */
295     vpline(YouMessage(tmp, "\82 \82È\82½\82Í", line), VA_ARGS);
296     VA_END();
297 }
298
299 /*VARARGS1*/
300 void Your
301 VA_DECL(const char *, line)
302 {
303     char *tmp;
304
305     VA_START(line);
306     VA_INIT(line, const char *);
307 /*JP
308     vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
309 */
310     vpline(YouMessage(tmp, "\82 \82È\82½\82Ì", line), VA_ARGS);
311     VA_END();
312 }
313
314 /*VARARGS1*/
315 void You_feel
316 VA_DECL(const char *, line)
317 {
318     char *tmp;
319
320     VA_START(line);
321     VA_INIT(line, const char *);
322     if (Unaware)
323 /*JP
324         YouPrefix(tmp, "You dream that you feel ", line);
325 */
326         YouPrefix(tmp, "\82 \82È\82½\82Í\96²\82Ì\92\86\82Å", line);
327     else
328 /*JP
329         YouPrefix(tmp, "You feel ", line);
330 */
331         YouPrefix(tmp, "\82 \82È\82½\82Í", line);
332     vpline(strcat(tmp, line), VA_ARGS);
333     VA_END();
334 }
335
336 /*VARARGS1*/
337 void You_cant
338 VA_DECL(const char *, line)
339 {
340     char *tmp;
341
342     VA_START(line);
343     VA_INIT(line, const char *);
344 /*JP
345     vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
346 */
347     vpline(YouMessage(tmp, "\82 \82È\82½\82Í", line), VA_ARGS);
348     VA_END();
349 }
350
351 /*VARARGS1*/
352 void pline_The
353 VA_DECL(const char *, line)
354 {
355     char *tmp;
356
357     VA_START(line);
358     VA_INIT(line, const char *);
359 /*JP
360     vpline(YouMessage(tmp, "The ", line), VA_ARGS);
361 */
362     vpline(YouMessage(tmp, "", line), VA_ARGS);
363     VA_END();
364 }
365
366 /*VARARGS1*/
367 void There
368 VA_DECL(const char *, line)
369 {
370     char *tmp;
371
372     VA_START(line);
373     VA_INIT(line, const char *);
374 /*JP
375     vpline(YouMessage(tmp, "There ", line), VA_ARGS);
376 */
377     vpline(YouMessage(tmp, "", line), VA_ARGS);
378     VA_END();
379 }
380
381 /*VARARGS1*/
382 void You_hear
383 VA_DECL(const char *, line)
384 {
385     char *tmp;
386 #if 1 /*JP*/
387         const char *adj;
388         char *p;
389 #endif
390
391     if (Deaf || !flags.acoustics)
392         return;
393     VA_START(line);
394     VA_INIT(line, const char *);
395 #if 0 /*JP*/
396     if (Underwater)
397         YouPrefix(tmp, "You barely hear ", line);
398     else if (Unaware)
399         YouPrefix(tmp, "You dream that you hear ", line);
400     else
401         YouPrefix(tmp, "You hear ", line);
402     vpline(strcat(tmp, line), VA_ARGS);
403 #else
404     if (Underwater)
405         adj = "\82©\82·\82©\82É";
406     else if (Unaware)
407         adj = "\96²\82Ì\92\86\82Å";
408     else
409         adj = "";
410     tmp = You_buf(strlen(adj) + strlen(line) + sizeof("\82 \82È\82½\82Í   "));
411
412     p = (char *)strstr(line, "\95·\82±") ;
413     if (p == NULL)
414         Strcpy(tmp, "\82 \82È\82½\82Í");
415     else
416         Strcpy(tmp, "");
417     if (p != NULL || (p = (char *)strstr(line, "\95·\82¢")) != NULL){
418         strncat(tmp, line, (p - line));
419         strcat(tmp, adj);
420         strcat(tmp, p);
421     } else {
422         Strcat(tmp, line);
423     }
424     vpline(tmp, VA_ARGS);
425 #endif
426     VA_END();
427 }
428
429 /*VARARGS1*/
430 void You_see
431 VA_DECL(const char *, line)
432 {
433     char *tmp;
434
435     VA_START(line);
436     VA_INIT(line, const char *);
437     if (Unaware)
438 /*JP
439         YouPrefix(tmp, "You dream that you see ", line);
440 */
441         YouPrefix(tmp, "\82 \82È\82½\82Í\96²\82Ì\92\86\82Å", line);
442 #if 0 /*JP*//*\82±\82±\82Í\8cÄ\82Ñ\8fo\82µ\8c³\82Å\8f\88\97\9d\82·\82é?*/
443     else if (Blind) /* caller should have caught this... */
444         YouPrefix(tmp, "You sense ", line);
445 #endif
446     else
447 /*JP
448         YouPrefix(tmp, "You see ", line);
449 */
450         YouPrefix(tmp, "\82 \82È\82½\82Í", line);
451     vpline(strcat(tmp, line), VA_ARGS);
452     VA_END();
453 }
454
455 /* Print a message inside double-quotes.
456  * The caller is responsible for checking deafness.
457  * Gods can speak directly to you in spite of deafness.
458  */
459 /*VARARGS1*/
460 void verbalize
461 VA_DECL(const char *, line)
462 {
463     char *tmp;
464
465     VA_START(line);
466     VA_INIT(line, const char *);
467 #if 0 /*JP*/
468     tmp = You_buf((int) strlen(line) + sizeof "\"\"");
469     Strcpy(tmp, "\"");
470     Strcat(tmp, line);
471     Strcat(tmp, "\"");
472 #else
473     tmp = You_buf((int) strlen(line) + sizeof "\81u\81v");
474     Strcpy(tmp, "\81u");
475     Strcat(tmp, line);
476     Strcat(tmp, "\81v");
477 #endif
478     vpline(tmp, VA_ARGS);
479     VA_END();
480 }
481
482 /*VARARGS1*/
483 /* Note that these declarations rely on knowledge of the internals
484  * of the variable argument handling stuff in "tradstdc.h"
485  */
486
487 #if defined(USE_STDARG) || defined(USE_VARARGS)
488 static void FDECL(vraw_printf, (const char *, va_list));
489
490 void raw_printf
491 VA_DECL(const char *, line)
492 {
493     VA_START(line);
494     VA_INIT(line, char *);
495     vraw_printf(line, VA_ARGS);
496     VA_END();
497 }
498
499 # ifdef USE_STDARG
500 static void
501 vraw_printf(const char *line, va_list the_args)
502 # else
503 static void
504 vraw_printf(line, the_args)
505 const char *line;
506 va_list the_args;
507 # endif
508
509 #else /* USE_STDARG | USE_VARARG */
510
511 void raw_printf
512 VA_DECL(const char *, line)
513 #endif
514 {
515     char pbuf[3 * BUFSZ];
516     int ln;
517     /* Do NOT use VA_START and VA_END in here... see above */
518
519     if (index(line, '%')) {
520         Vsprintf(pbuf, line, VA_ARGS);
521         line = pbuf;
522     }
523     if ((ln = (int) strlen(line)) > BUFSZ - 1) {
524         if (line != pbuf)
525             line = strncpy(pbuf, line, BUFSZ - 1);
526         /* unlike pline, we don't futz around to keep last few chars */
527         pbuf[BUFSZ - 1] = '\0'; /* terminate strncpy or truncate vsprintf */
528     }
529     raw_print(line);
530 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
531     execplinehandler(line);
532 #endif
533 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
534     VA_END(); /* (see vpline) */
535 #endif
536 }
537
538 /*VARARGS1*/
539 void impossible
540 VA_DECL(const char *, s)
541 {
542     char pbuf[2 * BUFSZ];
543
544     VA_START(s);
545     VA_INIT(s, const char *);
546     if (program_state.in_impossible)
547         panic("impossible called impossible");
548
549     program_state.in_impossible = 1;
550     Vsprintf(pbuf, s, VA_ARGS);
551     pbuf[BUFSZ - 1] = '\0'; /* sanity */
552     paniclog("impossible", pbuf);
553     if (iflags.debug_fuzzer)
554         panic("%s", pbuf);
555     pline("%s", VA_PASS1(pbuf));
556     /* reuse pbuf[] */
557 /*JP
558     Strcpy(pbuf, "Program in disorder!");
559 */
560     Strcpy(pbuf, "\83v\83\8d\83O\83\89\83\80\82É\8fá\8aQ\94­\90\81I");
561     if (program_state.something_worth_saving)
562 /*JP
563         Strcat(pbuf, "  (Saving and reloading may fix this problem.)");
564 */
565         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)");
566     pline("%s", VA_PASS1(pbuf));
567
568     program_state.in_impossible = 0;
569     VA_END();
570 }
571
572 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
573 static boolean use_pline_handler = TRUE;
574
575 static void
576 execplinehandler(line)
577 const char *line;
578 {
579     int f;
580     const char *args[3];
581     char *env;
582
583     if (!use_pline_handler)
584         return;
585
586     if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
587         use_pline_handler = FALSE;
588         return;
589     }
590
591     f = fork();
592     if (f == 0) { /* child */
593         args[0] = env;
594         args[1] = line;
595         args[2] = NULL;
596         (void) setgid(getgid());
597         (void) setuid(getuid());
598         (void) execv(args[0], (char *const *) args);
599         perror((char *) 0);
600         (void) fprintf(stderr, "Exec to message handler %s failed.\n", env);
601         nh_terminate(EXIT_FAILURE);
602     } else if (f > 0) {
603         int status;
604
605         waitpid(f, &status, 0);
606     } else if (f == -1) {
607         perror((char *) 0);
608         use_pline_handler = FALSE;
609         pline("%s", VA_PASS1("Fork to message handler failed."));
610     }
611 }
612 #endif /* MSGHANDLER && (POSIX_TYPES || __GNUC__) */
613
614 /*
615  * varargs handling for files.c
616  */
617 #if defined(USE_STDARG) || defined(USE_VARARGS)
618 static void FDECL(vconfig_error_add, (const char *, va_list));
619
620 /*VARARGS1*/
621 void
622 config_error_add
623 VA_DECL(const char *, str)
624 {
625     VA_START(str);
626     VA_INIT(str, char *);
627     vconfig_error_add(str, VA_ARGS);
628     VA_END();
629 }
630
631 # ifdef USE_STDARG
632 static void
633 vconfig_error_add(const char *str, va_list the_args)
634 # else
635 static void
636 vconfig_error_add(str, the_args)
637 const char *str;
638 va_list the_args;
639 # endif
640
641 #else /* !(USE_STDARG || USE_VARARG) => USE_OLDARGS */
642
643 /*VARARGS1*/
644 void
645 config_error_add
646 VA_DECL(const char *, str)
647 #endif /* ?(USE_STDARG || USE_VARARG) */
648 {       /* start of vconf...() or of nested block in USE_OLDARG's conf...() */
649     char buf[2 * BUFSZ];
650
651     Vsprintf(buf, str, VA_ARGS);
652     buf[BUFSZ - 1] = '\0';
653     config_erradd(buf);
654
655 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
656     VA_END(); /* (see pline/vpline -- ends nested block for USE_OLDARGS) */
657 #endif
658 }
659
660 /*pline.c*/