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
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 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.
28 * pr.c: Output to players
30 * Known contributors to this file:
31 * Dave Pare, 1986, 1989
32 * Steve McClure, 1998-2000
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.
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.
60 #include "prototypes.h"
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);
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
79 (void)vsprintf(buf, format, ap);
81 if (player->flags & PF_UTF8)
82 /* normal text needs to be converted to user text */
83 upr_player(player, C_DATA, buf);
85 /* normal text and user text are identical */
86 pr_player(player, C_DATA, buf);
90 * Print UTF-8 text BUF to current player.
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);
103 pr_player(player, C_DATA, buf);
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
116 pr_id(struct player *p, int id, char *format, ...)
122 io_puts(p->iop, "\n");
125 va_start(ap, format);
126 (void)vsprintf(buf, format, ap);
128 pr_player(p, id, buf);
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.
138 pr_flash(struct player *pl, char *format, ...)
140 char buf[4096]; /* UTF-8 */
143 if (pl->state != PS_PLAYING)
145 va_start(ap, format);
146 (void)vsprintf(buf, format, 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);
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.
161 pr_inform(struct player *pl, char *format, ...)
166 if (pl->state != PS_PLAYING)
168 va_start(ap, format);
169 (void)vsprintf(buf, format, ap);
171 pr_player(pl, C_INFORM, buf);
172 io_output(pl->iop, IO_NOWAIT);
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.
182 pr_wall(char *format, ...)
184 char buf[4096]; /* UTF-8 */
188 va_start(ap, format);
189 (void)vsprintf(buf, format, ap);
191 for (p = player_next(0); p; p = player_next(p)) {
192 if (p->state != PS_PLAYING)
194 pr_player(p, C_FLASH, buf);
195 io_output(p->iop, IO_NOWAIT);
200 * Send ID text BUF to PL, line-buffered.
202 * If a partial line with different id is buffered, terminate it with
206 pr_player(struct player *pl, int id, char *buf)
213 while (*bp != '\0') {
214 if (pl->curid != -1 && pl->curid != id) {
215 io_puts(pl->iop, "\n");
220 p = strchr(bp, '\n');
223 if (pl->command && (pl->command->c_flags & C_MOD))
224 io_write(pl->iop, bp, len, IO_NOWAIT);
226 io_write(pl->iop, bp, len, IO_WAIT);
230 len = io_puts(pl->iop, bp);
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
243 upr_player(struct player *pl, int id, char *buf)
254 while ((ch = *bp++)) {
255 if (pl->curid != -1 && pl->curid != id) {
256 io_puts(pl->iop, "\n");
265 io_puts(pl->iop, printbuf);
272 io_puts(pl->iop, printbuf);
277 if (pl->command && (pl->command->c_flags & C_MOD))
278 io_write(pl->iop, &ch, 1, IO_NOWAIT);
280 io_write(pl->iop, &ch, 1, IO_WAIT);
284 io_puts(pl->iop, printbuf);
291 * This runs always at the beginning of a line.
294 outid(struct player *pl, int n)
298 if (CANT_HAPPEN(n > C_LAST))
302 buf[0] = 'a' - 10 + n;
307 io_puts(pl->iop, buf);
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.
319 pr_id(player, *redir == '>' ? C_REDIR : C_PIPE, "%s\n", redir);
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.
330 pr_id(player, C_EXECUTE, "%s\n", file);
334 * Send a command prompt to the current player.
337 prprompt(int min, int btu)
339 pr_id(player, C_PROMPT, "%d %d\n", min, btu);
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,
352 prmptrd(char *prompt, char *buf, int size)
357 pr_id(player, C_FLUSH, "%s\n", prompt);
358 if ((r = recvclient(buf, size)) < 0)
360 time(&player->curup);
363 if (player->flags & PF_UTF8)
364 return copy_utf8_to_ascii_no_funny(buf, buf);
365 return copy_ascii_no_funny(buf, buf);
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,
379 uprmptrd(char *prompt, char *buf, int size)
384 pr_id(player, C_FLUSH, "%s\n", prompt);
385 if ((r = recvclient(buf, size)) < 0)
387 time(&player->curup);
390 if (player->flags & PF_UTF8)
391 return copy_utf8_no_funny(buf, buf);
392 return copy_ascii_no_funny(buf, buf);
396 * Print the current time in ctime() format.
408 * Print coordinates X, Y for COUNTRY.
409 * FORMAT must be a printf-style format string that converts exactly
413 prxy(char *format, coord x, coord y, natid country)
418 np = getnatp(country);
419 sprintf(buf, format, xrel(np, x), yrel(np, y));
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.
433 PR(int cn, char *format, ...)
435 /* XXX should really do this on a per-nation basis */
436 static char longline[MAXNOC][512];
441 va_start(ap, format);
442 (void)vsprintf(buf, format, ap);
444 newline = strrchr(buf, '\n') ? 1 : 0;
445 strcat(longline[cn], buf);
447 if (update_pending || (cn && cn != player->cnum))
448 typed_wu(0, cn, longline[cn], TEL_BULLETIN);
450 pr_player(player, C_DATA, longline[cn]);
451 longline[cn][0] = '\0';
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.
470 * Sound the current player's bell.
475 struct natstr *np = getnatp(player->cnum);
477 if (np->nat_flags & NF_BEEP)
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.
490 mpr(int cn, char *format, ...)
495 va_start(ap, format);
496 (void)vsprintf(buf, format, ap);
499 if (update_pending || cn != player->cnum)
500 typed_wu(0, cn, buf, TEL_BULLETIN);
502 pr_player(player, C_DATA, buf);
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
515 copy_ascii_no_funny(char *dst, char *src)
521 while ((ch = *src++)) {
522 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
523 ; /* ignore funny control */
525 *p++ = '?'; /* replace non-ASCII */
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
543 copy_utf8_no_funny(char *dst, char *src)
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 */
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
570 copy_utf8_to_ascii_no_funny(char *dst, char *src)
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) ;
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.
596 ufindpfx(char *s, int n)
602 if ((s[i++] & 0xc0) == 0xc0)
603 while ((s[i] & 0xc0) == 0x80)