]> git.pond.sub.org Git - empserver/blob - src/lib/subs/pr.c
New journal event output
[empserver] / src / lib / subs / pr.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  pr.c: Output to players
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986, 1989 
32  *     Steve McClure, 1998-2000
33  *     Ron Koenderink, 2005
34  *     Markus Armbruster, 2005-2007
35  */
36
37 /*
38  * Player output is fully buffered.  It can block only if the
39  * receiving player is the current player and his last command doesn't
40  * have the C_MOD flag.  Output to another player must not block
41  * because that player could be gone when the printing thread wakes
42  * up, and the code isn't prepared for that.  Output within C_MOD
43  * command never blocks, so that such commands can print freely
44  * without yielding the processor.
45  *
46  * Each line of output starts with an identification character
47  * encoding the output id, followed by space.  Ids less than 10 are
48  * encoded as decimal digits, and larger ids as lower case letters,
49  * starting with 'a'.  Symbolic names for ids are defined in proto.h.
50  */
51
52 #include <config.h>
53
54 #include <stdarg.h>
55 #include <stdlib.h>
56 #include "com.h"
57 #include "empio.h"
58 #include "file.h"
59 #include "journal.h"
60 #include "misc.h"
61 #include "nat.h"
62 #include "player.h"
63 #include "proto.h"
64 #include "prototypes.h"
65 #include "server.h"
66 #include "tel.h"
67
68 static void pr_player(struct player *pl, int id, char *buf);
69 static void upr_player(struct player *pl, int id, char *buf);
70 static void outid(struct player *pl, int n);
71
72 /*
73  * Print to current player similar to printf().
74  * Use printf-style FORMAT with the optional arguments.
75  * Note: `to print' without further qualifications means sending
76  * C_DATA text.
77  */
78 void
79 pr(char *format, ...)
80 {
81     char buf[4096];
82     va_list ap;
83
84     va_start(ap, format);
85     (void)vsprintf(buf, format, ap);
86     va_end(ap);
87     if (player->flags & PF_UTF8)
88         /* normal text needs to be converted to user text */
89         upr_player(player, C_DATA, buf);
90     else
91         /* normal text and user text are identical */
92         pr_player(player, C_DATA, buf);
93 }
94
95 /*
96  * Print UTF-8 text BUF to current player.
97  */
98 void
99 uprnf(char *buf)
100 {
101     char *p;
102
103     if (!(player->flags & PF_UTF8)) {
104         p = malloc(strlen(buf) + 1);
105         copy_utf8_to_ascii_no_funny(p, buf);
106         pr_player(player, C_DATA, p);
107         free(p);
108     } else
109         pr_player(player, C_DATA, buf);
110 }
111
112 /*
113  * Send some text to P with id ID, line-buffered.
114  * Format text to send using printf-style FORMAT and optional
115  * arguments.  It is assumed to be already user text.  Plain ASCII and
116  * text received from the same player are fine, for anything else the
117  * caller has to deal with output filtering.
118  * If a partial line is buffered, terminate it with a newline first.
119  */
120 void
121 pr_id(struct player *p, int id, char *format, ...)
122 {
123     char buf[4096];
124     va_list ap;
125
126     if (p->curid >= 0) {
127         io_puts(p->iop, "\n");
128         p->curid = -1;
129     }
130     va_start(ap, format);
131     (void)vsprintf(buf, format, ap);
132     va_end(ap);
133     pr_player(p, id, buf);
134 }
135
136 /*
137  * Send C_FLASH text to PL.
138  * Format text to send using printf-style FORMAT and optional
139  * arguments.  It is assumed to be UTF-8.
140  * Initiate an output queue flush, but do not wait for it to complete.
141  */
142 void
143 pr_flash(struct player *pl, char *format, ...)
144 {
145     char buf[4096];             /* UTF-8 */
146     va_list ap;
147
148     if (pl->state != PS_PLAYING)
149         return;
150     va_start(ap, format);
151     (void)vsprintf(buf, format, ap);
152     va_end(ap);
153     if (!(pl->flags & PF_UTF8))
154         copy_utf8_to_ascii_no_funny(buf, buf);
155     pr_player(pl, C_FLASH, buf);
156     io_output(pl->iop, IO_NOWAIT);
157 }
158
159 /*
160  * Send C_INFORM text to PL.
161  * Format text to send using printf-style FORMAT and optional
162  * arguments.  It is assumed to be plain ASCII.
163  * Initiate an output queue flush, but do not wait for it to complete.
164  */
165 void
166 pr_inform(struct player *pl, char *format, ...)
167 {
168     char buf[4096];
169     va_list ap;
170
171     if (pl->state != PS_PLAYING)
172         return;
173     va_start(ap, format);
174     (void)vsprintf(buf, format, ap);
175     va_end(ap);
176     pr_player(pl, C_INFORM, buf);
177     io_output(pl->iop, IO_NOWAIT);
178 }
179
180 /*
181  * Send C_FLASH text to everyone.
182  * Format text to send using printf-style FORMAT and optional
183  * arguments.  It is assumed to be plain ASCII.
184  * Prefix text it with a header suitable for broadcast from deity.
185  * Initiate an output queue flush, but do not wait for it to complete.
186  */
187 void
188 pr_wall(char *format, ...)
189 {
190     time_t now;
191     struct tm *tm;
192     char buf[4096];             /* UTF-8 */
193     int n;
194     struct player *p;
195     va_list ap;
196
197     time(&now);
198     tm = localtime(&now);
199     n = sprintf(buf, "BROADCAST from %s @ %02d:%02d: ",
200                 getnatp(0)->nat_cnam, tm->tm_hour, tm->tm_min);
201
202     va_start(ap, format);
203     (void)vsprintf(buf + n, format, ap);
204     va_end(ap);
205     for (p = player_next(0); p; p = player_next(p)) {
206         if (p->state != PS_PLAYING)
207             continue;
208         pr_player(p, C_FLASH, buf);
209         io_output(p->iop, IO_NOWAIT);
210     }
211 }
212
213 /*
214  * Send ID text BUF to PL, line-buffered.
215  * BUF is user text.
216  * If a partial line with different id is buffered, terminate it with
217  * a newline first.
218  */
219 static void
220 pr_player(struct player *pl, int id, char *buf)
221 {
222     char *p;
223     char *bp;
224     int len;
225
226     bp = buf;
227     while (*bp != '\0') {
228         if (pl->curid != -1 && pl->curid != id) {
229             io_puts(pl->iop, "\n");
230             pl->curid = -1;
231         }
232         if (pl->curid == -1)
233             outid(pl, id);
234         p = strchr(bp, '\n');
235         if (p != NULL) {
236             len = (p - bp) + 1;
237             if ((pl->command && (pl->command->c_flags & C_MOD)) ||
238                 (player != pl))
239                 io_write(pl->iop, bp, len, IO_NOWAIT);
240             else
241                 io_write(pl->iop, bp, len, IO_WAIT);
242             bp += len;
243             pl->curid = -1;
244         } else {
245             len = io_puts(pl->iop, bp);
246             bp += len;
247         }
248     }
249     journal_output(pl, id, buf);
250 }
251
252 /*
253  * Send ID text BUF to PL, line-buffered.
254  * This function translates from normal text to user text.
255  * If a partial line with different id is buffered, terminate it with
256  * a newline first.
257  */
258 static void
259 upr_player(struct player *pl, int id, char *buf)
260 {
261     char *bp;
262     int standout = 0;
263     char printbuf[2];
264     char ch;
265
266     printbuf[0] = '\0';
267     printbuf[1] = '\0';
268
269     bp = buf;
270     while ((ch = *bp++)) {
271         if (pl->curid != -1 && pl->curid != id) {
272             io_puts(pl->iop, "\n");
273             pl->curid = -1;
274         }
275         if (pl->curid == -1)
276             outid(pl, id);
277
278         if (ch & 0x80) {
279             if (standout == 0) {
280                 printbuf[0] = 0x0e;
281                 io_puts(pl->iop, printbuf);
282                 standout = 1;
283             }
284             ch &= 0x7f;
285         } else {
286             if (standout == 1) {
287                 printbuf[0] = 0x0f;
288                 io_puts(pl->iop, printbuf);
289                 standout = 0;
290             }
291         }
292         if (ch == '\n') {
293             if ((pl->command && (pl->command->c_flags & C_MOD)) ||
294                 (player != pl))
295                 io_write(pl->iop, &ch, 1, IO_NOWAIT);
296             else
297                 io_write(pl->iop, &ch, 1, IO_WAIT);
298             pl->curid = -1;
299         } else {
300             printbuf[0] = ch;
301             io_puts(pl->iop, printbuf);
302         }
303     }
304     journal_output(pl, id, buf);
305 }
306
307 /*
308  * Send id N to PL.
309  * This runs always at the beginning of a line.
310  */
311 static void
312 outid(struct player *pl, int n)
313 {
314     char buf[3];
315
316     if (CANT_HAPPEN(n > C_LAST))
317         n = C_DATA;
318
319     if (n >= 10)
320         buf[0] = 'a' - 10 + n;
321     else
322         buf[0] = '0' + n;
323     buf[1] = ' ';
324     buf[2] = '\0';
325     io_puts(pl->iop, buf);
326     pl->curid = n;
327 }
328
329 /*
330  * Send redirection request REDIR to the current player.
331  * REDIR is UTF-8, but non-ASCII characters can occur only if the
332  * player sent them.  Therefore, it is also user text.
333  */
334 void
335 prredir(char *redir)
336 {
337     pr_id(player, *redir == '>' ? C_REDIR : C_PIPE, "%s\n", redir);
338 }
339
340 /*
341  * Send script execute request FILE to the current player.
342  * REDIR is UTF-8, but non-ASCII characters can occur only if the
343  * player sent them.  Therefore, it is also user text.
344  */
345 void
346 prexec(char *file)
347 {
348     pr_id(player, C_EXECUTE, "%s\n", file);
349 }
350
351 /*
352  * Send a command prompt to the current player.
353  */
354 void
355 prprompt(int min, int btu)
356 {
357     pr_id(player, C_PROMPT, "%d %d\n", min, btu);
358 }
359
360 /*
361  * Prompt for a line of non-command input.
362  * Send C_FLUSH prompt PROMPT to the current player.
363  * Read a line of input into BUF[SIZE] and convert it to ASCII.
364  * This may block for input, yielding the processor.  Flush buffered
365  * output when blocking, to make sure player sees the prompt.
366  * Return number of bytes in BUF[], not counting the terminating 0,
367  * or -1 on error.
368  */
369 int
370 prmptrd(char *prompt, char *buf, int size)
371 {
372     int r;
373
374     if (CANT_HAPPEN(!prompt))
375         prompt = "? ";
376
377     pr_id(player, C_FLUSH, "%s\n", prompt);
378     if ((r = recvclient(buf, size)) < 0)
379         return r;
380     time(&player->curup);
381     if (*buf == 0)
382         return 1;
383     if (player->flags & PF_UTF8)
384         return copy_utf8_to_ascii_no_funny(buf, buf);
385     return copy_ascii_no_funny(buf, buf);
386 }
387
388 /*
389  * Prompt for a line of non-command, UTF-8 input.
390  * Send C_FLUSH prompt PROMPT to the current player.
391  * Read a line of input into BUF[SIZE], replacing funny characters by
392  * '?'.  The result is UTF-8.
393  * This may block for input, yielding the processor.  Flush buffered
394  * output when blocking, to make sure player sees the prompt.
395  * Return number of bytes in BUF[], not counting the terminating 0,
396  * or -1 on error.
397  */
398 int
399 uprmptrd(char *prompt, char *buf, int size)
400 {
401     int r;
402
403     if (CANT_HAPPEN(!prompt))
404         prompt = "? ";
405
406     pr_id(player, C_FLUSH, "%s\n", prompt);
407     if ((r = recvclient(buf, size)) < 0)
408         return r;
409     time(&player->curup);
410     if (*buf == 0)
411         return 1;
412     if (player->flags & PF_UTF8)
413         return copy_utf8_no_funny(buf, buf);
414     return copy_ascii_no_funny(buf, buf);
415 }
416
417 /*
418  * Print the current time in ctime() format.
419  */
420 void
421 prdate(void)
422 {
423     time_t now;
424
425     (void)time(&now);
426     pr(ctime(&now));
427 }
428
429 /*
430  * Print coordinates X, Y for COUNTRY.
431  * FORMAT must be a printf-style format string that converts exactly
432  * two int values.
433  */
434 void
435 prxy(char *format, coord x, coord y, natid country)
436 {
437     struct natstr *np;
438
439     np = getnatp(country);
440     pr(format, xrel(np, x), yrel(np, y));
441 }
442
443 /*
444  * Print to country CN similar to printf().
445  * Use printf-style FORMAT with the optional arguments.
446  * Output is buffered until a newline arrives.
447  * If CN is the current player and we're not in the update, print just
448  * like pr().  Else print into a bulletin.
449  * Because printing like pr() requires normal text, and bulletins
450  * require user text, only plain ASCII is allowed.
451  */
452 void
453 PR(int cn, char *format, ...)
454 {
455     /* XXX should really do this on a per-nation basis */
456     static char longline[MAXNOC][512];
457     int newline;
458     va_list ap;
459     char buf[1024];
460
461     va_start(ap, format);
462     (void)vsprintf(buf, format, ap);
463     va_end(ap);
464     newline = strrchr(buf, '\n') ? 1 : 0;
465     strcat(longline[cn], buf);
466     if (newline) {
467         if (update_running || (cn && cn != player->cnum))
468             typed_wu(0, cn, longline[cn], TEL_BULLETIN);
469         else
470             pr_player(player, C_DATA, longline[cn]);
471         longline[cn][0] = '\0';
472     }
473 }
474
475 /*
476  * Print the current time in ctime() format to country CN.
477  * If CN is the current player and we're not in the update, print just
478  * like prdate().  Else print into a bulletin.
479  */
480 void
481 PRdate(natid cn)
482 {
483     time_t now;
484
485     (void)time(&now);
486     PR(cn, ctime(&now));
487 }
488
489 /*
490  * Sound the current player's bell.
491  */
492 void
493 pr_beep(void)
494 {
495     struct natstr *np = getnatp(player->cnum);
496
497     if (np->nat_flags & NF_BEEP)
498         pr("\07");
499 }
500
501 /*
502  * Print to country CN similar to printf().
503  * Use printf-style FORMAT with the optional arguments.
504  * If CN is zero, don't print anything.
505  * Else, if CN is the current player and we're not in the update,
506  * print just like pr().  Else print into a bulletin.
507  * Because printing like pr() requires normal text, and bulletins
508  * require user text, only plain ASCII is allowed.
509  */
510 void
511 mpr(int cn, char *format, ...)
512 {
513     char buf[4096];
514     va_list ap;
515
516     if (!cn)
517         return;
518     va_start(ap, format);
519     (void)vsprintf(buf, format, ap);
520     va_end(ap);
521     if (update_running || cn != player->cnum)
522         typed_wu(0, cn, buf, TEL_BULLETIN);
523     else
524         pr_player(player, C_DATA, buf);
525 }
526
527 /*
528  * Copy SRC without funny characters to DST.
529  * Drop control characters, except for '\t'.
530  * Replace non-ASCII characters by '?'.
531  * Return length of DST.
532  * DST must have space.  If it overlaps SRC, then DST <= SRC must
533  * hold.
534  */
535 size_t
536 copy_ascii_no_funny(char *dst, char *src)
537 {
538     char *p;
539     unsigned char ch;
540
541     p = dst;
542     while ((ch = *src++)) {
543         if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
544             ;                   /* ignore funny control */
545         else if (ch > 0x7f)
546             *p++ = '?'; /* replace non-ASCII */
547         else
548             *p++ = ch;
549     }
550     *p = 0;
551
552     return p - dst;
553 }
554
555 /*
556  * Copy UTF-8 SRC without funny characters to DST.
557  * Drop control characters, except for '\t'.
558  * FIXME Replace malformed UTF-8 sequences by '?'.
559  * Return byte length of DST.
560  * DST must have space.  If it overlaps SRC, then DST <= SRC must
561  * hold.
562  */
563 size_t
564 copy_utf8_no_funny(char *dst, char *src)
565 {
566     char *p;
567     unsigned char ch;
568
569     p = dst;
570     while ((ch = *src++)) {
571         /* FIXME do the right thing for malformed and overlong sequences */
572         if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
573             ;                   /* ignore funny control */
574         else
575             *p++ = ch;
576     }
577     *p = 0;
578
579     return p - dst;
580 }
581
582 /*
583  * Copy UTF-8 SRC without funny characters to ASCII DST.
584  * Drop control characters, except for '\t'.
585  * Replace non-ASCII characters by '?'.
586  * Return length of DST.
587  * DST must have space.  If it overlaps SRC, then DST <= SRC must
588  * hold.
589  */
590 size_t
591 copy_utf8_to_ascii_no_funny(char *dst, char *src)
592 {
593     char *p;
594     unsigned char ch;
595
596     p = dst;
597     while ((ch = *src++)) {
598         /* FIXME do the right thing for malformed and overlong sequences */
599         if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
600             ;                   /* ignore funny control */
601         else if (ch > 0x7f) {
602             *p++ = '?';         /* replace non-ASCII */
603             while ((*src++ & 0xc0) == 0x80) ;
604         } else
605             *p++ = ch;
606     }
607     *p = 0;
608
609     return p - dst;
610 }
611
612 /*
613  * Return byte-index of the N-th UTF-8 character in UTF-8 string S.
614  * If S doesn't have that many characters, return its length instead.
615  */
616 int
617 ufindpfx(char *s, int n)
618 {
619     int i = 0;
620
621     while (n && s[i])
622     {
623         if ((s[i++] & 0xc0) == 0xc0)
624             while ((s[i] & 0xc0) == 0x80)
625                 i++;
626         --n;
627     }
628     return i;
629 }