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