OSDN Git Service

update year to 2020
[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-2020            */
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 #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 */
17
18 static unsigned pline_flags = 0;
19 static char prevmsg[BUFSZ];
20
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 *));
25 #endif
26
27 #ifdef DUMPLOG
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 };
31
32 /* keep the most recent DUMPLOG_MSG_COUNT messages */
33 void
34 dumplogmsg(line)
35 const char *line;
36 {
37     /*
38      * TODO:
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
42      *  this share it.
43      */
44     unsigned indx = saved_pline_index; /* next slot to use */
45     char *oldest = saved_plines[indx]; /* current content of that slot */
46
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 */
50         Strcpy(oldest, line);
51     } else {
52         if (oldest)
53             free((genericptr_t) oldest);
54         saved_plines[indx] = dupstr(line);
55     }
56     saved_pline_index = (indx + 1) % DUMPLOG_MSG_COUNT;
57 }
58
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 */
62 void
63 dumplogfreemessages()
64 {
65     unsigned indx;
66
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;
71 }
72 #endif
73
74 /* keeps windowprocs usage out of pline() */
75 static void
76 putmesg(line)
77 const char *line;
78 {
79     int attr = ATR_NONE;
80
81     if ((pline_flags & URGENT_MESSAGE) != 0
82         && (windowprocs.wincap2 & WC2_URGENT_MESG) != 0)
83         attr |= ATR_URGENT;
84     if ((pline_flags & SUPPRESS_HISTORY) != 0
85         && (windowprocs.wincap2 & WC2_SUPPRESS_HIST) != 0)
86         attr |= ATR_NOHISTORY;
87
88     putstr(WIN_MESSAGE, attr, line);
89 }
90
91 /* Note that these declarations rely on knowledge of the internals
92  * of the variable argument handling stuff in "tradstdc.h"
93  */
94
95 #if defined(USE_STDARG) || defined(USE_VARARGS)
96 static void FDECL(vpline, (const char *, va_list));
97
98 /*VARARGS1*/
99 void
100 pline
101 VA_DECL(const char *, line)
102 {
103     VA_START(line);
104     VA_INIT(line, char *);
105     vpline(line, VA_ARGS);
106     VA_END();
107 }
108
109 # ifdef USE_STDARG
110 static void
111 vpline(const char *line, va_list the_args)
112 # else
113 static void
114 vpline(line, the_args)
115 const char *line;
116 va_list the_args;
117 # endif
118
119 #else /* USE_STDARG | USE_VARARG */
120
121 # define vpline pline
122
123 /*VARARGS1*/
124 void
125 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 */
131     int ln;
132     int msgtyp;
133 #if !defined(NO_VSNPRINTF)
134 #if 0 /*JP*/
135     int vlen = 0;
136 #endif
137 #endif
138     boolean no_repeat;
139     /* Do NOT use VA_START and VA_END in here... see above */
140
141     if (!line || !*line)
142         return;
143 #ifdef HANGUPHANDLING
144     if (program_state.done_hup)
145         return;
146 #endif
147     if (program_state.wizkit_wishing)
148         return;
149
150     if (index(line, '%')) {
151 #if !defined(NO_VSNPRINTF)
152 #if 0 /*JP*/
153         vlen = vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
154 #else
155         vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
156 #endif
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);
161 #endif
162 #else
163         Vsprintf(pbuf, line, VA_ARGS);
164 #endif
165         line = pbuf;
166     }
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';
179         line = pbuf;
180     }
181
182 #ifdef DUMPLOG
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).
186      */
187     if ((pline_flags & SUPPRESS_HISTORY) == 0)
188         dumplogmsg(line);
189 #endif
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] */
195         raw_print(line);
196         iflags.last_msg = PLNMSG_UNKNOWN;
197         goto pline_done;
198     }
199
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.)
213              */
214             goto pline_done;
215     }
216
217     if (vision_full_recalc)
218         vision_recalc(0);
219     if (u.ux)
220         flush_screen(1); /* %% */
221
222     putmesg(line);
223
224 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
225     execplinehandler(line);
226 #endif
227
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-- */
233
234  pline_done:
235     --in_pline;
236     return;
237
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() */
241     VA_END();
242 #endif
243 }
244
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) */
248 /*VARARGS2*/
249 void custompline
250 VA_DECL2(unsigned, pflags, const char *, line)
251 {
252     VA_START(line);
253     VA_INIT(line, const char *);
254     pline_flags = pflags;
255     vpline(line, VA_ARGS);
256     pline_flags = 0;
257     VA_END();
258     return;
259 }
260
261 /*VARARGS1*/
262 void Norep
263 VA_DECL(const char *, line)
264 {
265     VA_START(line);
266     VA_INIT(line, const char *);
267     pline_flags = PLINE_NOREPEAT;
268     vpline(line, VA_ARGS);
269     pline_flags = 0;
270     VA_END();
271     return;
272 }
273
274 /* work buffer for You(), &c and verbalize() */
275 static char *you_buf = 0;
276 static int you_buf_siz = 0;
277
278 static char *
279 You_buf(siz)
280 int siz;
281 {
282     if (siz > you_buf_siz) {
283         if (you_buf)
284             free((genericptr_t) you_buf);
285         you_buf_siz = siz + 10;
286         you_buf = (char *) alloc((unsigned) you_buf_siz);
287     }
288     return you_buf;
289 }
290
291 void
292 free_youbuf()
293 {
294     if (you_buf)
295         free((genericptr_t) you_buf), you_buf = (char *) 0;
296     you_buf_siz = 0;
297 }
298
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)
302
303 #define YouMessage(pointer, prefix, text) \
304     strcat((YouPrefix(pointer, prefix, text), pointer), text)
305
306 /*VARARGS1*/
307 void You
308 VA_DECL(const char *, line)
309 {
310     char *tmp;
311
312     VA_START(line);
313     VA_INIT(line, const char *);
314 /*JP
315     vpline(YouMessage(tmp, "You ", line), VA_ARGS);
316 */
317     vpline(YouMessage(tmp, "\82 \82È\82½\82Í", line), VA_ARGS);
318     VA_END();
319 }
320
321 /*VARARGS1*/
322 void Your
323 VA_DECL(const char *, line)
324 {
325     char *tmp;
326
327     VA_START(line);
328     VA_INIT(line, const char *);
329 /*JP
330     vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
331 */
332     vpline(YouMessage(tmp, "\82 \82È\82½\82Ì", line), VA_ARGS);
333     VA_END();
334 }
335
336 /*VARARGS1*/
337 void You_feel
338 VA_DECL(const char *, line)
339 {
340     char *tmp;
341
342     VA_START(line);
343     VA_INIT(line, const char *);
344     if (Unaware)
345 /*JP
346         YouPrefix(tmp, "You dream that you feel ", line);
347 */
348         YouPrefix(tmp, "\82 \82È\82½\82Í\96²\82Ì\92\86\82Å", line);
349     else
350 /*JP
351         YouPrefix(tmp, "You feel ", line);
352 */
353         YouPrefix(tmp, "\82 \82È\82½\82Í", line);
354     vpline(strcat(tmp, line), VA_ARGS);
355     VA_END();
356 }
357
358 /*VARARGS1*/
359 void You_cant
360 VA_DECL(const char *, line)
361 {
362     char *tmp;
363
364     VA_START(line);
365     VA_INIT(line, const char *);
366 /*JP
367     vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
368 */
369     vpline(YouMessage(tmp, "\82 \82È\82½\82Í", line), VA_ARGS);
370     VA_END();
371 }
372
373 /*VARARGS1*/
374 void pline_The
375 VA_DECL(const char *, line)
376 {
377     char *tmp;
378
379     VA_START(line);
380     VA_INIT(line, const char *);
381 /*JP
382     vpline(YouMessage(tmp, "The ", line), VA_ARGS);
383 */
384     vpline(YouMessage(tmp, "", line), VA_ARGS);
385     VA_END();
386 }
387
388 /*VARARGS1*/
389 void There
390 VA_DECL(const char *, line)
391 {
392     char *tmp;
393
394     VA_START(line);
395     VA_INIT(line, const char *);
396 /*JP
397     vpline(YouMessage(tmp, "There ", line), VA_ARGS);
398 */
399     vpline(YouMessage(tmp, "", line), VA_ARGS);
400     VA_END();
401 }
402
403 /*VARARGS1*/
404 void You_hear
405 VA_DECL(const char *, line)
406 {
407     char *tmp;
408 #if 1 /*JP*/
409         const char *adj;
410         char *p;
411 #endif
412
413     if (Deaf || !flags.acoustics)
414         return;
415     VA_START(line);
416     VA_INIT(line, const char *);
417 #if 0 /*JP*/
418     if (Underwater)
419         YouPrefix(tmp, "You barely hear ", line);
420     else if (Unaware)
421         YouPrefix(tmp, "You dream that you hear ", line);
422     else
423         YouPrefix(tmp, "You hear ", line);  /* Deaf-aware */
424     vpline(strcat(tmp, line), VA_ARGS);
425 #else
426     if (Underwater)
427         adj = "\82©\82·\82©\82É";
428     else if (Unaware)
429         adj = "\96²\82Ì\92\86\82Å";
430     else
431         adj = "";
432     tmp = You_buf(strlen(adj) + strlen(line) + sizeof("\82 \82È\82½\82Í   "));
433
434     p = (char *)strstr(line, "\95·\82±") ;
435     if (p == NULL)
436         Strcpy(tmp, "\82 \82È\82½\82Í");
437     else
438         Strcpy(tmp, "");
439     if (p != NULL || (p = (char *)strstr(line, "\95·\82¢")) != NULL){
440         strncat(tmp, line, (p - line));
441         strcat(tmp, adj);
442         strcat(tmp, p);
443     } else {
444         Strcat(tmp, line);
445     }
446     vpline(tmp, VA_ARGS);
447 #endif
448     VA_END();
449 }
450
451 /*VARARGS1*/
452 void You_see
453 VA_DECL(const char *, line)
454 {
455     char *tmp;
456
457     VA_START(line);
458     VA_INIT(line, const char *);
459     if (Unaware)
460 /*JP
461         YouPrefix(tmp, "You dream that you see ", line);
462 */
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);
467 #endif
468     else
469 /*JP
470         YouPrefix(tmp, "You see ", line);
471 */
472         YouPrefix(tmp, "\82 \82È\82½\82Í", line);
473     vpline(strcat(tmp, line), VA_ARGS);
474     VA_END();
475 }
476
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.
480  */
481 /*VARARGS1*/
482 void verbalize
483 VA_DECL(const char *, line)
484 {
485     char *tmp;
486
487     VA_START(line);
488     VA_INIT(line, const char *);
489 #if 0 /*JP*/
490     tmp = You_buf((int) strlen(line) + sizeof "\"\"");
491     Strcpy(tmp, "\"");
492     Strcat(tmp, line);
493     Strcat(tmp, "\"");
494 #else
495     tmp = You_buf((int) strlen(line) + sizeof "\81u\81v");
496     Strcpy(tmp, "\81u");
497     Strcat(tmp, line);
498     Strcat(tmp, "\81v");
499 #endif
500     vpline(tmp, VA_ARGS);
501     VA_END();
502 }
503
504 /*VARARGS1*/
505 /* Note that these declarations rely on knowledge of the internals
506  * of the variable argument handling stuff in "tradstdc.h"
507  */
508
509 #if defined(USE_STDARG) || defined(USE_VARARGS)
510 static void FDECL(vraw_printf, (const char *, va_list));
511
512 void raw_printf
513 VA_DECL(const char *, line)
514 {
515     VA_START(line);
516     VA_INIT(line, char *);
517     vraw_printf(line, VA_ARGS);
518     VA_END();
519 }
520
521 # ifdef USE_STDARG
522 static void
523 vraw_printf(const char *line, va_list the_args)
524 # else
525 static void
526 vraw_printf(line, the_args)
527 const char *line;
528 va_list the_args;
529 # endif
530
531 #else /* USE_STDARG | USE_VARARG */
532
533 void raw_printf
534 VA_DECL(const char *, line)
535 #endif
536 {
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 */
539
540     if (index(line, '%')) {
541 #if !defined(NO_VSNPRINTF)
542         (void) vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
543 #else
544         Vsprintf(pbuf, line, VA_ARGS);
545 #endif
546         line = pbuf;
547     }
548     if ((int) strlen(line) > BUFSZ - 1) {
549         if (line != pbuf)
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 */
553     }
554     raw_print(line);
555 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
556     execplinehandler(line);
557 #endif
558 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
559     VA_END(); /* (see vpline) */
560 #endif
561 }
562
563 /*VARARGS1*/
564 void impossible
565 VA_DECL(const char *, s)
566 {
567     char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
568
569     VA_START(s);
570     VA_INIT(s, const char *);
571     if (program_state.in_impossible)
572         panic("impossible called impossible");
573
574     program_state.in_impossible = 1;
575 #if !defined(NO_VSNPRINTF)
576     (void) vsnprintf(pbuf, sizeof pbuf, s, VA_ARGS);
577 #else
578     Vsprintf(pbuf, s, VA_ARGS);
579 #endif
580     pbuf[BUFSZ - 1] = '\0'; /* sanity */
581     paniclog("impossible", pbuf);
582     if (iflags.debug_fuzzer)
583         panic("%s", pbuf);
584     pline("%s", VA_PASS1(pbuf));
585     /* reuse pbuf[] */
586 /*JP
587     Strcpy(pbuf, "Program in disorder!");
588 */
589     Strcpy(pbuf, "\83v\83\8d\83O\83\89\83\80\82É\8fá\8aQ\94­\90\81I");
590     if (program_state.something_worth_saving)
591 /*JP
592         Strcat(pbuf, "  (Saving and reloading may fix this problem.)");
593 */
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));
596
597     program_state.in_impossible = 0;
598     VA_END();
599 }
600
601 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
602 static boolean use_pline_handler = TRUE;
603
604 static void
605 execplinehandler(line)
606 const char *line;
607 {
608     int f;
609     const char *args[3];
610     char *env;
611
612     if (!use_pline_handler)
613         return;
614
615     if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
616         use_pline_handler = FALSE;
617         return;
618     }
619
620     f = fork();
621     if (f == 0) { /* child */
622         args[0] = env;
623         args[1] = line;
624         args[2] = NULL;
625         (void) setgid(getgid());
626         (void) setuid(getuid());
627         (void) execv(args[0], (char *const *) args);
628         perror((char *) 0);
629         (void) fprintf(stderr, "Exec to message handler %s failed.\n", env);
630         nh_terminate(EXIT_FAILURE);
631     } else if (f > 0) {
632         int status;
633
634         waitpid(f, &status, 0);
635     } else if (f == -1) {
636         perror((char *) 0);
637         use_pline_handler = FALSE;
638         pline("%s", VA_PASS1("Fork to message handler failed."));
639     }
640 }
641 #endif /* MSGHANDLER && (POSIX_TYPES || __GNUC__) */
642
643 /*
644  * varargs handling for files.c
645  */
646 #if defined(USE_STDARG) || defined(USE_VARARGS)
647 static void FDECL(vconfig_error_add, (const char *, va_list));
648
649 /*VARARGS1*/
650 void
651 config_error_add
652 VA_DECL(const char *, str)
653 {
654     VA_START(str);
655     VA_INIT(str, char *);
656     vconfig_error_add(str, VA_ARGS);
657     VA_END();
658 }
659
660 # ifdef USE_STDARG
661 static void
662 vconfig_error_add(const char *str, va_list the_args)
663 # else
664 static void
665 vconfig_error_add(str, the_args)
666 const char *str;
667 va_list the_args;
668 # endif
669
670 #else /* !(USE_STDARG || USE_VARARG) => USE_OLDARGS */
671
672 /*VARARGS1*/
673 void
674 config_error_add
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)
679 #if 0 /*JP*/
680     int vlen = 0;
681 #endif
682 #endif
683     char buf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
684
685 #if !defined(NO_VSNPRINTF)
686 #if 0 /*JP*/
687     vlen = vsnprintf(buf, sizeof buf, str, VA_ARGS);
688 #else
689     vsnprintf(buf, sizeof buf, str, VA_ARGS);
690 #endif
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);
695 #endif
696 #else
697     Vsprintf(buf, str, VA_ARGS);
698 #endif
699     buf[BUFSZ - 1] = '\0';
700     config_erradd(buf);
701
702 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
703     VA_END(); /* (see pline/vpline -- ends nested block for USE_OLDARGS) */
704 #endif
705 }
706
707 /*pline.c*/