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