2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure, Markus Armbruster
6 * Empire 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 3 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, see <http://www.gnu.org/licenses/>.
21 * See files README, COPYING and CREDITS in the root of the source
22 * tree for related information and legal notices. It is expected
23 * that future projects/authors will amend these files as needed.
27 * pr.c: Output to players
29 * Known contributors to this file:
30 * Dave Pare, 1986, 1989
31 * Steve McClure, 1998-2000
32 * Ron Koenderink, 2005
33 * Markus Armbruster, 2005-2012
37 * Player output is fully buffered. It can block only if the
38 * receiving player is the current player and his last command doesn't
39 * have the C_MOD flag. Output to another player must not block
40 * because that player could be gone when the printing thread wakes
41 * up, and the code isn't prepared for that. Output within C_MOD
42 * command never blocks, so that such commands can print freely
43 * without yielding the processor.
45 * Each line of output starts with an identification character
46 * encoding the output id, followed by space. Ids less than 10 are
47 * encoded as decimal digits, and larger ids as lower case letters,
48 * starting with 'a'. Symbolic names for ids are defined in proto.h.
64 #include "prototypes.h"
69 static void pr_player(struct player *pl, int id, char *buf);
70 static void upr_player(struct player *pl, int id, char *buf);
71 static void outid(struct player *pl, int n);
72 static void player_output_some(void);
75 * Print to current player similar to printf().
76 * Use printf-style FORMAT with the optional arguments.
77 * Note: `to print' without further qualifications means sending
87 (void)vsprintf(buf, format, ap);
89 if (player->flags & PF_UTF8)
90 /* normal text needs to be converted to user text */
91 upr_player(player, C_DATA, buf);
93 /* normal text and user text are identical */
94 pr_player(player, C_DATA, buf);
98 * Print UTF-8 text BUF to current player.
105 if (!(player->flags & PF_UTF8)) {
106 p = malloc(strlen(buf) + 1);
107 copy_utf8_to_ascii_no_funny(p, buf);
108 pr_player(player, C_DATA, p);
111 pr_player(player, C_DATA, buf);
115 * Send some text to P with id ID, line-buffered.
116 * Format text to send using printf-style FORMAT and optional
117 * arguments. It is assumed to be already user text. Plain ASCII and
118 * text received from the same player are fine, for anything else the
119 * caller has to deal with output filtering.
120 * If a partial line is buffered, terminate it with a newline first.
123 pr_id(struct player *p, int id, char *format, ...)
129 io_puts(p->iop, "\n");
130 journal_output(p, p->curid, "\n");
133 va_start(ap, format);
134 (void)vsprintf(buf, format, ap);
136 pr_player(p, id, buf);
140 * Send C_FLASH text to PL.
141 * Format text to send using printf-style FORMAT and optional
142 * arguments. It is assumed to be UTF-8.
143 * Initiate an output queue flush, but do not wait for it to complete.
146 pr_flash(struct player *pl, char *format, ...)
148 char buf[4096]; /* UTF-8 */
151 if (pl->state != PS_PLAYING)
153 va_start(ap, format);
154 (void)vsprintf(buf, format, ap);
156 if (!(pl->flags & PF_UTF8))
157 copy_utf8_to_ascii_no_funny(buf, buf);
158 pr_player(pl, C_FLASH, buf);
159 io_output(pl->iop, 0);
163 * Send C_INFORM text to PL.
164 * Format text to send using printf-style FORMAT and optional
165 * arguments. It is assumed to be plain ASCII.
166 * Initiate an output queue flush, but do not wait for it to complete.
169 pr_inform(struct player *pl, char *format, ...)
174 if (pl->state != PS_PLAYING)
176 va_start(ap, format);
177 (void)vsprintf(buf, format, ap);
179 pr_player(pl, C_INFORM, buf);
180 io_output(pl->iop, 0);
184 * Send C_FLASH text to everyone.
185 * Format text to send using printf-style FORMAT and optional
186 * arguments. It is assumed to be plain ASCII.
187 * Prefix text it with a header suitable for broadcast from deity.
188 * Initiate an output queue flush, but do not wait for it to complete.
191 pr_wall(char *format, ...)
195 char buf[4096]; /* UTF-8 */
201 tm = localtime(&now);
202 n = sprintf(buf, "BROADCAST from %s @ %02d:%02d: ",
203 getnatp(0)->nat_cnam, tm->tm_hour, tm->tm_min);
205 va_start(ap, format);
206 (void)vsprintf(buf + n, format, ap);
208 for (p = player_next(NULL); p; p = player_next(p)) {
209 if (p->state != PS_PLAYING)
211 pr_player(p, C_FLASH, buf);
212 io_output(p->iop, 0);
217 * Send ID text BUF to PL, line-buffered.
219 * If a partial line with different id is buffered, terminate it with
223 pr_player(struct player *pl, int id, char *buf)
229 journal_output(pl, id, buf);
232 while (*bp != '\0') {
233 if (pl->curid != -1 && pl->curid != id) {
234 io_puts(pl->iop, "\n");
239 p = strchr(bp, '\n');
242 io_write(pl->iop, bp, len);
246 len = io_puts(pl->iop, bp);
252 player_output_some();
256 * Send ID text BUF to PL, line-buffered.
257 * This function translates from normal text to user text.
258 * If a partial line with different id is buffered, terminate it with
262 upr_player(struct player *pl, int id, char *buf)
269 journal_output(pl, id, buf);
275 while ((ch = *bp++)) {
276 if (pl->curid != -1 && pl->curid != id) {
277 io_puts(pl->iop, "\n");
286 io_puts(pl->iop, printbuf);
293 io_puts(pl->iop, printbuf);
298 io_write(pl->iop, &ch, 1);
302 io_puts(pl->iop, printbuf);
307 player_output_some();
312 * This runs always at the beginning of a line.
315 outid(struct player *pl, int n)
319 if (CANT_HAPPEN(n > C_LAST))
323 buf[0] = 'a' - 10 + n;
328 io_puts(pl->iop, buf);
333 player_output_some(void)
337 deadline = player->curup + minutes(max_idle);
338 if (player->may_sleep != PLAYER_SLEEP_FREELY)
340 while (io_output_if_queue_long(player->iop, deadline) > 0)
345 * Send redirection request REDIR to the current player.
346 * REDIR is UTF-8, but non-ASCII characters can occur only if the
347 * player sent them. Therefore, it is also user text.
352 pr_id(player, *redir == '>' ? C_REDIR : C_PIPE, "%s\n", redir);
356 * Send script execute request FILE to the current player.
357 * FILE is UTF-8, but non-ASCII characters can occur only if the
358 * player sent them. Therefore, it is also user text.
363 pr_id(player, C_EXECUTE, "%s\n", file);
367 * Send a command prompt to the current player.
370 prprompt(int min, int btu)
372 pr_id(player, C_PROMPT, "%d %d\n", min, btu);
376 * Prompt for a line of non-command input.
377 * Send C_FLUSH prompt PROMPT to the current player.
378 * Read a line of input into BUF[SIZE] and convert it to ASCII.
379 * This may block for input, yielding the processor. Flush buffered
380 * output when blocking, to make sure player sees the prompt.
381 * Return number of bytes in BUF[], not counting the terminating 0,
385 prmptrd(char *prompt, char *buf, int size)
389 if (CANT_HAPPEN(!prompt))
392 pr_id(player, C_FLUSH, "%s\n", prompt);
393 if ((r = recvclient(buf, size)) < 0)
395 time(&player->curup);
398 if (player->flags & PF_UTF8)
399 return copy_utf8_to_ascii_no_funny(buf, buf);
400 return copy_ascii_no_funny(buf, buf);
404 * Prompt for a line of non-command, UTF-8 input.
405 * Send C_FLUSH prompt PROMPT to the current player.
406 * Read a line of input into BUF[SIZE], replacing funny characters by
407 * '?'. The result is UTF-8.
408 * This may block for input, yielding the processor. Flush buffered
409 * output when blocking, to make sure player sees the prompt.
410 * Return number of bytes in BUF[], not counting the terminating 0,
414 uprmptrd(char *prompt, char *buf, int size)
418 if (CANT_HAPPEN(!prompt))
421 pr_id(player, C_FLUSH, "%s\n", prompt);
422 if ((r = recvclient(buf, size)) < 0)
424 time(&player->curup);
427 if (player->flags & PF_UTF8)
428 return copy_utf8_no_funny(buf, buf);
429 return copy_ascii_no_funny(buf, buf);
433 * Print the current time in ctime() format.
445 * Print coordinates X, Y.
446 * FORMAT must be a printf-style format string that converts exactly
450 prxy(char *format, coord x, coord y)
454 np = getnatp(player->cnum);
455 pr(format, xrel(np, x), yrel(np, y));
459 * Sound the current player's bell.
464 struct natstr *np = getnatp(player->cnum);
466 if (np->nat_flags & NF_BEEP)
471 * Print complete lines to country CN similar to printf().
472 * Use printf-style FORMAT with the optional arguments. FORMAT must
474 * If CN is zero, don't print anything.
475 * Else, if CN is the current player and we're not in the update,
476 * print just like pr(). Else print into a bulletin.
477 * Because printing like pr() requires normal text, and bulletins
478 * require user text, only plain ASCII is allowed.
481 mpr(int cn, char *format, ...)
486 CANT_HAPPEN(!format[0] || format[strlen(format) - 1] != '\n');
489 va_start(ap, format);
490 (void)vsprintf(buf, format, ap);
492 if (update_running || cn != player->cnum)
493 wu(0, cn, "%s", buf);
495 pr_player(player, C_DATA, buf);
499 * Copy SRC without funny characters to DST.
500 * Drop control characters, except for '\t'.
501 * Replace non-ASCII characters by '?'.
502 * Return length of DST.
503 * DST must have space. If it overlaps SRC, then DST <= SRC must
507 copy_ascii_no_funny(char *dst, char *src)
513 while ((ch = *src++)) {
514 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
515 ; /* ignore funny control */
517 *p++ = '?'; /* replace non-ASCII */
527 * Copy UTF-8 SRC without funny characters to DST.
528 * Drop control characters, except for '\t'.
529 * FIXME Replace malformed UTF-8 sequences by '?'.
530 * Return byte length of DST.
531 * DST must have space. If it overlaps SRC, then DST <= SRC must
535 copy_utf8_no_funny(char *dst, char *src)
541 while ((ch = *src++)) {
542 /* FIXME do the right thing for malformed and overlong sequences */
543 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
544 ; /* ignore funny control */
554 * Copy UTF-8 SRC without funny characters to ASCII DST.
555 * Drop control characters, except for '\t'.
556 * Replace non-ASCII characters by '?'.
557 * Return length of DST.
558 * DST must have space. If it overlaps SRC, then DST <= SRC must
562 copy_utf8_to_ascii_no_funny(char *dst, char *src)
568 while ((ch = *src++)) {
569 /* FIXME do the right thing for malformed and overlong sequences */
570 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
571 ; /* ignore funny control */
572 else if (ch > 0x7f) {
573 *p++ = '?'; /* replace non-ASCII */
574 while ((*src & 0xc0) == 0x80)
585 * Return byte-index of the N-th UTF-8 character in UTF-8 string S.
586 * If S doesn't have that many characters, return its length instead.
589 ufindpfx(char *s, int n)
594 if ((s[i++] & 0xc0) == 0xc0)
595 while ((s[i] & 0xc0) == 0x80)