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
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.
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.
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
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.
28 * pr.c: Output to players
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
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.
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.
63 #include "prototypes.h"
67 static void pr_player(struct player *pl, int id, char *buf);
68 static void upr_player(struct player *pl, int id, char *buf);
69 static void outid(struct player *pl, int n);
72 * Print to current player similar to printf().
73 * Use printf-style FORMAT with the optional arguments.
74 * Note: `to print' without further qualifications means sending
84 (void)vsprintf(buf, format, ap);
86 if (player->flags & PF_UTF8)
87 /* normal text needs to be converted to user text */
88 upr_player(player, C_DATA, buf);
90 /* normal text and user text are identical */
91 pr_player(player, C_DATA, buf);
95 * Print UTF-8 text BUF to current player.
102 if (!(player->flags & PF_UTF8)) {
103 p = malloc(strlen(buf) + 1);
104 copy_utf8_to_ascii_no_funny(p, buf);
105 pr_player(player, C_DATA, p);
108 pr_player(player, C_DATA, buf);
112 * Send some text to P with id ID, line-buffered.
113 * Format text to send using printf-style FORMAT and optional
114 * arguments. It is assumed to be already user text. Plain ASCII and
115 * text received from the same player are fine, for anything else the
116 * caller has to deal with output filtering.
117 * If a partial line is buffered, terminate it with a newline first.
120 pr_id(struct player *p, int id, char *format, ...)
126 io_puts(p->iop, "\n");
129 va_start(ap, format);
130 (void)vsprintf(buf, format, ap);
132 pr_player(p, id, buf);
136 * Send C_FLASH text to PL.
137 * Format text to send using printf-style FORMAT and optional
138 * arguments. It is assumed to be UTF-8.
139 * Initiate an output queue flush, but do not wait for it to complete.
142 pr_flash(struct player *pl, char *format, ...)
144 char buf[4096]; /* UTF-8 */
147 if (pl->state != PS_PLAYING)
149 va_start(ap, format);
150 (void)vsprintf(buf, format, ap);
152 if (!(pl->flags & PF_UTF8))
153 copy_utf8_to_ascii_no_funny(buf, buf);
154 pr_player(pl, C_FLASH, buf);
155 io_output(pl->iop, IO_NOWAIT);
159 * Send C_INFORM text to PL.
160 * Format text to send using printf-style FORMAT and optional
161 * arguments. It is assumed to be plain ASCII.
162 * Initiate an output queue flush, but do not wait for it to complete.
165 pr_inform(struct player *pl, char *format, ...)
170 if (pl->state != PS_PLAYING)
172 va_start(ap, format);
173 (void)vsprintf(buf, format, ap);
175 pr_player(pl, C_INFORM, buf);
176 io_output(pl->iop, IO_NOWAIT);
180 * Send C_FLASH text to everyone.
181 * Format text to send using printf-style FORMAT and optional
182 * arguments. It is assumed to be plain ASCII.
183 * Prefix text it with a header suitable for broadcast from deity.
184 * Initiate an output queue flush, but do not wait for it to complete.
187 pr_wall(char *format, ...)
191 char buf[4096]; /* UTF-8 */
197 tm = localtime(&now);
198 n = sprintf(buf, "BROADCAST from %s @ %02d:%02d: ",
199 getnatp(0)->nat_cnam, tm->tm_hour, tm->tm_min);
201 va_start(ap, format);
202 (void)vsprintf(buf + n, format, ap);
204 for (p = player_next(0); p; p = player_next(p)) {
205 if (p->state != PS_PLAYING)
207 pr_player(p, C_FLASH, buf);
208 io_output(p->iop, IO_NOWAIT);
213 * Send ID text BUF to PL, line-buffered.
215 * If a partial line with different id is buffered, terminate it with
219 pr_player(struct player *pl, int id, char *buf)
226 while (*bp != '\0') {
227 if (pl->curid != -1 && pl->curid != id) {
228 io_puts(pl->iop, "\n");
233 p = strchr(bp, '\n');
236 if ((pl->command && (pl->command->c_flags & C_MOD)) ||
238 io_write(pl->iop, bp, len, IO_NOWAIT);
240 io_write(pl->iop, bp, len, IO_WAIT);
244 len = io_puts(pl->iop, bp);
251 * Send ID text BUF to PL, line-buffered.
252 * This function translates from normal text to user text.
253 * If a partial line with different id is buffered, terminate it with
257 upr_player(struct player *pl, int id, char *buf)
268 while ((ch = *bp++)) {
269 if (pl->curid != -1 && pl->curid != id) {
270 io_puts(pl->iop, "\n");
279 io_puts(pl->iop, printbuf);
286 io_puts(pl->iop, printbuf);
291 if ((pl->command && (pl->command->c_flags & C_MOD)) ||
293 io_write(pl->iop, &ch, 1, IO_NOWAIT);
295 io_write(pl->iop, &ch, 1, IO_WAIT);
299 io_puts(pl->iop, printbuf);
306 * This runs always at the beginning of a line.
309 outid(struct player *pl, int n)
313 if (CANT_HAPPEN(n > C_LAST))
317 buf[0] = 'a' - 10 + n;
322 io_puts(pl->iop, buf);
327 * Send redirection request REDIR to the current player.
328 * REDIR is UTF-8, but non-ASCII characters can occur only if the
329 * player sent them. Therefore, it is also user text.
334 pr_id(player, *redir == '>' ? C_REDIR : C_PIPE, "%s\n", redir);
338 * Send script execute request FILE to the current player.
339 * REDIR is UTF-8, but non-ASCII characters can occur only if the
340 * player sent them. Therefore, it is also user text.
345 pr_id(player, C_EXECUTE, "%s\n", file);
349 * Send a command prompt to the current player.
352 prprompt(int min, int btu)
354 pr_id(player, C_PROMPT, "%d %d\n", min, btu);
358 * Prompt for a line of non-command input.
359 * Send C_FLUSH prompt PROMPT to the current player.
360 * Read a line of input into BUF[SIZE] and convert it to ASCII.
361 * This may block for input, yielding the processor. Flush buffered
362 * output when blocking, to make sure player sees the prompt.
363 * Return number of bytes in BUF[], not counting the terminating 0,
367 prmptrd(char *prompt, char *buf, int size)
372 * Each prompt must consume one line of input. recvclient()
373 * doesn't do that while player->aborted.
375 CANT_HAPPEN(player->aborted);
377 if (CANT_HAPPEN(!prompt))
380 pr_id(player, C_FLUSH, "%s\n", prompt);
381 if ((r = recvclient(buf, size)) < 0)
383 time(&player->curup);
386 if (player->flags & PF_UTF8)
387 return copy_utf8_to_ascii_no_funny(buf, buf);
388 return copy_ascii_no_funny(buf, buf);
392 * Prompt for a line of non-command, UTF-8 input.
393 * Send C_FLUSH prompt PROMPT to the current player.
394 * Read a line of input into BUF[SIZE], replacing funny characters by
395 * '?'. The result is UTF-8.
396 * This may block for input, yielding the processor. Flush buffered
397 * output when blocking, to make sure player sees the prompt.
398 * Return number of bytes in BUF[], not counting the terminating 0,
402 uprmptrd(char *prompt, char *buf, int size)
407 CANT_HAPPEN(player->aborted);
409 if (CANT_HAPPEN(!prompt))
412 pr_id(player, C_FLUSH, "%s\n", prompt);
413 if ((r = recvclient(buf, size)) < 0)
415 time(&player->curup);
418 if (player->flags & PF_UTF8)
419 return copy_utf8_no_funny(buf, buf);
420 return copy_ascii_no_funny(buf, buf);
424 * Print the current time in ctime() format.
436 * Print coordinates X, Y for COUNTRY.
437 * FORMAT must be a printf-style format string that converts exactly
441 prxy(char *format, coord x, coord y, natid country)
445 np = getnatp(country);
446 pr(format, xrel(np, x), yrel(np, y));
450 * Print to country CN similar to printf().
451 * Use printf-style FORMAT with the optional arguments.
452 * Output is buffered until a newline arrives.
453 * If CN is the current player and we're not in the update, print just
454 * like pr(). Else print into a bulletin.
455 * Because printing like pr() requires normal text, and bulletins
456 * require user text, only plain ASCII is allowed.
459 PR(int cn, char *format, ...)
461 /* XXX should really do this on a per-nation basis */
462 static char longline[MAXNOC][512];
467 va_start(ap, format);
468 (void)vsprintf(buf, format, ap);
470 newline = strrchr(buf, '\n') ? 1 : 0;
471 strcat(longline[cn], buf);
473 if (update_running || (cn && cn != player->cnum))
474 typed_wu(0, cn, longline[cn], TEL_BULLETIN);
476 pr_player(player, C_DATA, longline[cn]);
477 longline[cn][0] = '\0';
482 * Print the current time in ctime() format to country CN.
483 * If CN is the current player and we're not in the update, print just
484 * like prdate(). Else print into a bulletin.
496 * Sound the current player's bell.
501 struct natstr *np = getnatp(player->cnum);
503 if (np->nat_flags & NF_BEEP)
508 * Print to country CN similar to printf().
509 * Use printf-style FORMAT with the optional arguments.
510 * If CN is the current player and we're not in the update, print just
511 * like pr(). Else print into a bulletin.
512 * Because printing like pr() requires normal text, and bulletins
513 * require user text, only plain ASCII is allowed.
516 mpr(int cn, char *format, ...)
521 va_start(ap, format);
522 (void)vsprintf(buf, format, ap);
525 if (update_running || cn != player->cnum)
526 typed_wu(0, cn, buf, TEL_BULLETIN);
528 pr_player(player, C_DATA, buf);
533 * Copy SRC without funny characters to DST.
534 * Drop control characters, except for '\t'.
535 * Replace non-ASCII characters by '?'.
536 * Return length of DST.
537 * DST must have space. If it overlaps SRC, then DST <= SRC must
541 copy_ascii_no_funny(char *dst, char *src)
547 while ((ch = *src++)) {
548 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
549 ; /* ignore funny control */
551 *p++ = '?'; /* replace non-ASCII */
561 * Copy UTF-8 SRC without funny characters to DST.
562 * Drop control characters, except for '\t'.
563 * FIXME Replace malformed UTF-8 sequences by '?'.
564 * Return byte length of DST.
565 * DST must have space. If it overlaps SRC, then DST <= SRC must
569 copy_utf8_no_funny(char *dst, char *src)
575 while ((ch = *src++)) {
576 /* FIXME do the right thing for malformed and overlong sequences */
577 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
578 ; /* ignore funny control */
588 * Copy UTF-8 SRC without funny characters to ASCII DST.
589 * Drop control characters, except for '\t'.
590 * Replace non-ASCII characters by '?'.
591 * Return length of DST.
592 * DST must have space. If it overlaps SRC, then DST <= SRC must
596 copy_utf8_to_ascii_no_funny(char *dst, char *src)
602 while ((ch = *src++)) {
603 /* FIXME do the right thing for malformed and overlong sequences */
604 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
605 ; /* ignore funny control */
606 else if (ch > 0x7f) {
607 *p++ = '?'; /* replace non-ASCII */
608 while ((*src++ & 0xc0) == 0x80) ;
618 * Return byte-index of the N-th UTF-8 character in UTF-8 string S.
619 * If S doesn't have that many characters, return its length instead.
622 ufindpfx(char *s, int n)
628 if ((s[i++] & 0xc0) == 0xc0)
629 while ((s[i] & 0xc0) == 0x80)