OSDN Git Service

add Github Actions
[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 #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     int vlen = 0;
135 #endif
136     boolean no_repeat;
137     /* Do NOT use VA_START and VA_END in here... see above */
138
139     if (!line || !*line)
140         return;
141 #ifdef HANGUPHANDLING
142     if (program_state.done_hup)
143         return;
144 #endif
145     if (program_state.wizkit_wishing)
146         return;
147
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);
155 #endif
156 #else
157         Vsprintf(pbuf, line, VA_ARGS);
158 #endif
159         line = pbuf;
160     }
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';
173         line = pbuf;
174     }
175
176 #ifdef DUMPLOG
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).
180      */
181     if ((pline_flags & SUPPRESS_HISTORY) == 0)
182         dumplogmsg(line);
183 #endif
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] */
189         raw_print(line);
190         iflags.last_msg = PLNMSG_UNKNOWN;
191         goto pline_done;
192     }
193
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.)
207              */
208             goto pline_done;
209     }
210
211     if (vision_full_recalc)
212         vision_recalc(0);
213     if (u.ux)
214         flush_screen(1); /* %% */
215
216     putmesg(line);
217
218 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
219     execplinehandler(line);
220 #endif
221
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-- */
227
228  pline_done:
229     --in_pline;
230     return;
231
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() */
235     VA_END();
236 #endif
237 }
238
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) */
242 /*VARARGS2*/
243 void custompline
244 VA_DECL2(unsigned, pflags, const char *, line)
245 {
246     VA_START(line);
247     VA_INIT(line, const char *);
248     pline_flags = pflags;
249     vpline(line, VA_ARGS);
250     pline_flags = 0;
251     VA_END();
252     return;
253 }
254
255 /*VARARGS1*/
256 void Norep
257 VA_DECL(const char *, line)
258 {
259     VA_START(line);
260     VA_INIT(line, const char *);
261     pline_flags = PLINE_NOREPEAT;
262     vpline(line, VA_ARGS);
263     pline_flags = 0;
264     VA_END();
265     return;
266 }
267
268 /* work buffer for You(), &c and verbalize() */
269 static char *you_buf = 0;
270 static int you_buf_siz = 0;
271
272 static char *
273 You_buf(siz)
274 int siz;
275 {
276     if (siz > you_buf_siz) {
277         if (you_buf)
278             free((genericptr_t) you_buf);
279         you_buf_siz = siz + 10;
280         you_buf = (char *) alloc((unsigned) you_buf_siz);
281     }
282     return you_buf;
283 }
284
285 void
286 free_youbuf()
287 {
288     if (you_buf)
289         free((genericptr_t) you_buf), you_buf = (char *) 0;
290     you_buf_siz = 0;
291 }
292
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)
296
297 #define YouMessage(pointer, prefix, text) \
298     strcat((YouPrefix(pointer, prefix, text), pointer), text)
299
300 /*VARARGS1*/
301 void You
302 VA_DECL(const char *, line)
303 {
304     char *tmp;
305
306     VA_START(line);
307     VA_INIT(line, const char *);
308 /*JP
309     vpline(YouMessage(tmp, "You ", line), VA_ARGS);
310 */
311     vpline(YouMessage(tmp, "\82 \82È\82½\82Í", line), VA_ARGS);
312     VA_END();
313 }
314
315 /*VARARGS1*/
316 void Your
317 VA_DECL(const char *, line)
318 {
319     char *tmp;
320
321     VA_START(line);
322     VA_INIT(line, const char *);
323 /*JP
324     vpline(YouMessage(tmp, "Your ", line), VA_ARGS);
325 */
326     vpline(YouMessage(tmp, "\82 \82È\82½\82Ì", line), VA_ARGS);
327     VA_END();
328 }
329
330 /*VARARGS1*/
331 void You_feel
332 VA_DECL(const char *, line)
333 {
334     char *tmp;
335
336     VA_START(line);
337     VA_INIT(line, const char *);
338     if (Unaware)
339 /*JP
340         YouPrefix(tmp, "You dream that you feel ", line);
341 */
342         YouPrefix(tmp, "\82 \82È\82½\82Í\96²\82Ì\92\86\82Å", line);
343     else
344 /*JP
345         YouPrefix(tmp, "You feel ", line);
346 */
347         YouPrefix(tmp, "\82 \82È\82½\82Í", line);
348     vpline(strcat(tmp, line), VA_ARGS);
349     VA_END();
350 }
351
352 /*VARARGS1*/
353 void You_cant
354 VA_DECL(const char *, line)
355 {
356     char *tmp;
357
358     VA_START(line);
359     VA_INIT(line, const char *);
360 /*JP
361     vpline(YouMessage(tmp, "You can't ", line), VA_ARGS);
362 */
363     vpline(YouMessage(tmp, "\82 \82È\82½\82Í", line), VA_ARGS);
364     VA_END();
365 }
366
367 /*VARARGS1*/
368 void pline_The
369 VA_DECL(const char *, line)
370 {
371     char *tmp;
372
373     VA_START(line);
374     VA_INIT(line, const char *);
375 /*JP
376     vpline(YouMessage(tmp, "The ", line), VA_ARGS);
377 */
378     vpline(YouMessage(tmp, "", line), VA_ARGS);
379     VA_END();
380 }
381
382 /*VARARGS1*/
383 void There
384 VA_DECL(const char *, line)
385 {
386     char *tmp;
387
388     VA_START(line);
389     VA_INIT(line, const char *);
390 /*JP
391     vpline(YouMessage(tmp, "There ", line), VA_ARGS);
392 */
393     vpline(YouMessage(tmp, "", line), VA_ARGS);
394     VA_END();
395 }
396
397 /*VARARGS1*/
398 void You_hear
399 VA_DECL(const char *, line)
400 {
401     char *tmp;
402 #if 1 /*JP*/
403         const char *adj;
404         char *p;
405 #endif
406
407     if (Deaf || !flags.acoustics)
408         return;
409     VA_START(line);
410     VA_INIT(line, const char *);
411 #if 0 /*JP*/
412     if (Underwater)
413         YouPrefix(tmp, "You barely hear ", line);
414     else if (Unaware)
415         YouPrefix(tmp, "You dream that you hear ", line);
416     else
417         YouPrefix(tmp, "You hear ", line);  /* Deaf-aware */
418     vpline(strcat(tmp, line), VA_ARGS);
419 #else
420     if (Underwater)
421         adj = "\82©\82·\82©\82É";
422     else if (Unaware)
423         adj = "\96²\82Ì\92\86\82Å";
424     else
425         adj = "";
426     tmp = You_buf(strlen(adj) + strlen(line) + sizeof("\82 \82È\82½\82Í   "));
427
428     p = (char *)strstr(line, "\95·\82±") ;
429     if (p == NULL)
430         Strcpy(tmp, "\82 \82È\82½\82Í");
431     else
432         Strcpy(tmp, "");
433     if (p != NULL || (p = (char *)strstr(line, "\95·\82¢")) != NULL){
434         strncat(tmp, line, (p - line));
435         strcat(tmp, adj);
436         strcat(tmp, p);
437     } else {
438         Strcat(tmp, line);
439     }
440     vpline(tmp, VA_ARGS);
441 #endif
442     VA_END();
443 }
444
445 /*VARARGS1*/
446 void You_see
447 VA_DECL(const char *, line)
448 {
449     char *tmp;
450
451     VA_START(line);
452     VA_INIT(line, const char *);
453     if (Unaware)
454 /*JP
455         YouPrefix(tmp, "You dream that you see ", line);
456 */
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);
461 #endif
462     else
463 /*JP
464         YouPrefix(tmp, "You see ", line);
465 */
466         YouPrefix(tmp, "\82 \82È\82½\82Í", line);
467     vpline(strcat(tmp, line), VA_ARGS);
468     VA_END();
469 }
470
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.
474  */
475 /*VARARGS1*/
476 void verbalize
477 VA_DECL(const char *, line)
478 {
479     char *tmp;
480
481     VA_START(line);
482     VA_INIT(line, const char *);
483 #if 0 /*JP*/
484     tmp = You_buf((int) strlen(line) + sizeof "\"\"");
485     Strcpy(tmp, "\"");
486     Strcat(tmp, line);
487     Strcat(tmp, "\"");
488 #else
489     tmp = You_buf((int) strlen(line) + sizeof "\81u\81v");
490     Strcpy(tmp, "\81u");
491     Strcat(tmp, line);
492     Strcat(tmp, "\81v");
493 #endif
494     vpline(tmp, VA_ARGS);
495     VA_END();
496 }
497
498 /*VARARGS1*/
499 /* Note that these declarations rely on knowledge of the internals
500  * of the variable argument handling stuff in "tradstdc.h"
501  */
502
503 #if defined(USE_STDARG) || defined(USE_VARARGS)
504 static void FDECL(vraw_printf, (const char *, va_list));
505
506 void raw_printf
507 VA_DECL(const char *, line)
508 {
509     VA_START(line);
510     VA_INIT(line, char *);
511     vraw_printf(line, VA_ARGS);
512     VA_END();
513 }
514
515 # ifdef USE_STDARG
516 static void
517 vraw_printf(const char *line, va_list the_args)
518 # else
519 static void
520 vraw_printf(line, the_args)
521 const char *line;
522 va_list the_args;
523 # endif
524
525 #else /* USE_STDARG | USE_VARARG */
526
527 void raw_printf
528 VA_DECL(const char *, line)
529 #endif
530 {
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 */
533
534     if (index(line, '%')) {
535 #if !defined(NO_VSNPRINTF)
536         (void) vsnprintf(pbuf, sizeof pbuf, line, VA_ARGS);
537 #else
538         Vsprintf(pbuf, line, VA_ARGS);
539 #endif
540         line = pbuf;
541     }
542     if ((int) strlen(line) > BUFSZ - 1) {
543         if (line != pbuf)
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 */
547     }
548     raw_print(line);
549 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
550     execplinehandler(line);
551 #endif
552 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
553     VA_END(); /* (see vpline) */
554 #endif
555 }
556
557 /*VARARGS1*/
558 void impossible
559 VA_DECL(const char *, s)
560 {
561     char pbuf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
562
563     VA_START(s);
564     VA_INIT(s, const char *);
565     if (program_state.in_impossible)
566         panic("impossible called impossible");
567
568     program_state.in_impossible = 1;
569 #if !defined(NO_VSNPRINTF)
570     (void) vsnprintf(pbuf, sizeof pbuf, s, VA_ARGS);
571 #else
572     Vsprintf(pbuf, s, VA_ARGS);
573 #endif
574     pbuf[BUFSZ - 1] = '\0'; /* sanity */
575     paniclog("impossible", pbuf);
576     if (iflags.debug_fuzzer)
577         panic("%s", pbuf);
578     pline("%s", VA_PASS1(pbuf));
579     /* reuse pbuf[] */
580 /*JP
581     Strcpy(pbuf, "Program in disorder!");
582 */
583     Strcpy(pbuf, "\83v\83\8d\83O\83\89\83\80\82É\8fá\8aQ\94­\90\81I");
584     if (program_state.something_worth_saving)
585 /*JP
586         Strcat(pbuf, "  (Saving and reloading may fix this problem.)");
587 */
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));
590
591     program_state.in_impossible = 0;
592     VA_END();
593 }
594
595 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
596 static boolean use_pline_handler = TRUE;
597
598 static void
599 execplinehandler(line)
600 const char *line;
601 {
602     int f;
603     const char *args[3];
604     char *env;
605
606     if (!use_pline_handler)
607         return;
608
609     if (!(env = nh_getenv("NETHACK_MSGHANDLER"))) {
610         use_pline_handler = FALSE;
611         return;
612     }
613
614     f = fork();
615     if (f == 0) { /* child */
616         args[0] = env;
617         args[1] = line;
618         args[2] = NULL;
619         (void) setgid(getgid());
620         (void) setuid(getuid());
621         (void) execv(args[0], (char *const *) args);
622         perror((char *) 0);
623         (void) fprintf(stderr, "Exec to message handler %s failed.\n", env);
624         nh_terminate(EXIT_FAILURE);
625     } else if (f > 0) {
626         int status;
627
628         waitpid(f, &status, 0);
629     } else if (f == -1) {
630         perror((char *) 0);
631         use_pline_handler = FALSE;
632         pline("%s", VA_PASS1("Fork to message handler failed."));
633     }
634 }
635 #endif /* MSGHANDLER && (POSIX_TYPES || __GNUC__) */
636
637 /*
638  * varargs handling for files.c
639  */
640 #if defined(USE_STDARG) || defined(USE_VARARGS)
641 static void FDECL(vconfig_error_add, (const char *, va_list));
642
643 /*VARARGS1*/
644 void
645 config_error_add
646 VA_DECL(const char *, str)
647 {
648     VA_START(str);
649     VA_INIT(str, char *);
650     vconfig_error_add(str, VA_ARGS);
651     VA_END();
652 }
653
654 # ifdef USE_STDARG
655 static void
656 vconfig_error_add(const char *str, va_list the_args)
657 # else
658 static void
659 vconfig_error_add(str, the_args)
660 const char *str;
661 va_list the_args;
662 # endif
663
664 #else /* !(USE_STDARG || USE_VARARG) => USE_OLDARGS */
665
666 /*VARARGS1*/
667 void
668 config_error_add
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)
673     int vlen = 0;
674 #endif
675     char buf[BIGBUFSZ]; /* will be chopped down to BUFSZ-1 if longer */
676
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);
683 #endif
684 #else
685     Vsprintf(buf, str, VA_ARGS);
686 #endif
687     buf[BUFSZ - 1] = '\0';
688     config_erradd(buf);
689
690 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
691     VA_END(); /* (see pline/vpline -- ends nested block for USE_OLDARGS) */
692 #endif
693 }
694
695 /*pline.c*/