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. 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.
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.
63 #include "prototypes.h"
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);
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
82 (void)vsprintf(buf, format, ap);
84 if (player->flags & PF_UTF8)
85 /* normal text needs to be converted to user text */
86 upr_player(player, C_DATA, buf);
88 /* normal text and user text are identical */
89 pr_player(player, C_DATA, buf);
93 * Print UTF-8 text BUF to current player.
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);
106 pr_player(player, C_DATA, buf);
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
119 pr_id(struct player *p, int id, char *format, ...)
125 io_puts(p->iop, "\n");
128 va_start(ap, format);
129 (void)vsprintf(buf, format, ap);
131 pr_player(p, id, buf);
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.
141 pr_flash(struct player *pl, char *format, ...)
143 char buf[4096]; /* UTF-8 */
146 if (pl->state != PS_PLAYING)
148 va_start(ap, format);
149 (void)vsprintf(buf, format, 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);
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.
164 pr_inform(struct player *pl, char *format, ...)
169 if (pl->state != PS_PLAYING)
171 va_start(ap, format);
172 (void)vsprintf(buf, format, ap);
174 pr_player(pl, C_INFORM, buf);
175 io_output(pl->iop, IO_NOWAIT);
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.
185 pr_wall(char *format, ...)
187 char buf[4096]; /* UTF-8 */
191 va_start(ap, format);
192 (void)vsprintf(buf, format, ap);
194 for (p = player_next(0); p; p = player_next(p)) {
195 if (p->state != PS_PLAYING)
197 pr_player(p, C_FLASH, buf);
198 io_output(p->iop, IO_NOWAIT);
203 * Send ID text BUF to PL, line-buffered.
205 * If a partial line with different id is buffered, terminate it with
209 pr_player(struct player *pl, int id, char *buf)
216 while (*bp != '\0') {
217 if (pl->curid != -1 && pl->curid != id) {
218 io_puts(pl->iop, "\n");
223 p = strchr(bp, '\n');
226 if ((pl->command && (pl->command->c_flags & C_MOD)) ||
228 io_write(pl->iop, bp, len, IO_NOWAIT);
230 io_write(pl->iop, bp, len, IO_WAIT);
234 len = io_puts(pl->iop, bp);
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
247 upr_player(struct player *pl, int id, char *buf)
258 while ((ch = *bp++)) {
259 if (pl->curid != -1 && pl->curid != id) {
260 io_puts(pl->iop, "\n");
269 io_puts(pl->iop, printbuf);
276 io_puts(pl->iop, printbuf);
281 if ((pl->command && (pl->command->c_flags & C_MOD)) ||
283 io_write(pl->iop, &ch, 1, IO_NOWAIT);
285 io_write(pl->iop, &ch, 1, IO_WAIT);
289 io_puts(pl->iop, printbuf);
296 * This runs always at the beginning of a line.
299 outid(struct player *pl, int n)
303 if (CANT_HAPPEN(n > C_LAST))
307 buf[0] = 'a' - 10 + n;
312 io_puts(pl->iop, buf);
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.
324 pr_id(player, *redir == '>' ? C_REDIR : C_PIPE, "%s\n", redir);
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.
335 pr_id(player, C_EXECUTE, "%s\n", file);
339 * Send a command prompt to the current player.
342 prprompt(int min, int btu)
344 pr_id(player, C_PROMPT, "%d %d\n", min, btu);
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,
357 prmptrd(char *prompt, char *buf, int size)
361 pr_id(player, C_FLUSH, "%s\n", prompt);
362 if ((r = recvclient(buf, size)) < 0)
364 time(&player->curup);
367 if (player->flags & PF_UTF8)
368 return copy_utf8_to_ascii_no_funny(buf, buf);
369 return copy_ascii_no_funny(buf, buf);
373 * Prompt for a line of non-command, UTF-8 input.
374 * Send C_FLUSH prompt PROMPT to the current player.
375 * Read a line of input into BUF[SIZE], replacing funny characters by
376 * '?'. The result is UTF-8.
377 * This may block for input, yielding the processor. Flush buffered
378 * output when blocking, to make sure player sees the prompt.
379 * Return number of bytes in BUF[], not counting the terminating 0,
383 uprmptrd(char *prompt, char *buf, int size)
387 pr_id(player, C_FLUSH, "%s\n", prompt);
388 if ((r = recvclient(buf, size)) < 0)
390 time(&player->curup);
393 if (player->flags & PF_UTF8)
394 return copy_utf8_no_funny(buf, buf);
395 return copy_ascii_no_funny(buf, buf);
399 * Print the current time in ctime() format.
411 * Print coordinates X, Y for COUNTRY.
412 * FORMAT must be a printf-style format string that converts exactly
416 prxy(char *format, coord x, coord y, natid country)
421 np = getnatp(country);
422 sprintf(buf, format, xrel(np, x), yrel(np, y));
427 * Print to country CN similar to printf().
428 * Use printf-style FORMAT with the optional arguments.
429 * Output is buffered until a newline arrives.
430 * If CN is the current player and we're not in the update, print just
431 * like pr(). Else print into a bulletin.
432 * Because printing like pr() requires normal text, and bulletins
433 * require user text, only plain ASCII is allowed.
436 PR(int cn, char *format, ...)
438 /* XXX should really do this on a per-nation basis */
439 static char longline[MAXNOC][512];
444 va_start(ap, format);
445 (void)vsprintf(buf, format, ap);
447 newline = strrchr(buf, '\n') ? 1 : 0;
448 strcat(longline[cn], buf);
450 if (update_pending || (cn && cn != player->cnum))
451 typed_wu(0, cn, longline[cn], TEL_BULLETIN);
453 pr_player(player, C_DATA, longline[cn]);
454 longline[cn][0] = '\0';
459 * Print the current time in ctime() format to country CN.
460 * If CN is the current player and we're not in the update, print just
461 * like prdate(). Else print into a bulletin.
473 * Sound the current player's bell.
478 struct natstr *np = getnatp(player->cnum);
480 if (np->nat_flags & NF_BEEP)
485 * Print to country CN similar to printf().
486 * Use printf-style FORMAT with the optional arguments.
487 * If CN is the current player and we're not in the update, print just
488 * like pr(). Else print into a bulletin.
489 * Because printing like pr() requires normal text, and bulletins
490 * require user text, only plain ASCII is allowed.
493 mpr(int cn, char *format, ...)
498 va_start(ap, format);
499 (void)vsprintf(buf, format, ap);
502 if (update_pending || cn != player->cnum)
503 typed_wu(0, cn, buf, TEL_BULLETIN);
505 pr_player(player, C_DATA, buf);
510 * Copy SRC without funny characters to DST.
511 * Drop control characters, except for '\t'.
512 * Replace non-ASCII characters by '?'.
513 * Return length of DST.
514 * DST must have space. If it overlaps SRC, then DST <= SRC must
518 copy_ascii_no_funny(char *dst, char *src)
524 while ((ch = *src++)) {
525 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
526 ; /* ignore funny control */
528 *p++ = '?'; /* replace non-ASCII */
538 * Copy UTF-8 SRC without funny characters to DST.
539 * Drop control characters, except for '\t'.
540 * FIXME Replace malformed UTF-8 sequences by '?'.
541 * Return byte length of DST.
542 * DST must have space. If it overlaps SRC, then DST <= SRC must
546 copy_utf8_no_funny(char *dst, char *src)
552 while ((ch = *src++)) {
553 /* FIXME do the right thing for malformed and overlong sequences */
554 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
555 ; /* ignore funny control */
565 * Copy UTF-8 SRC without funny characters to ASCII DST.
566 * Drop control characters, except for '\t'.
567 * Replace non-ASCII characters by '?'.
568 * Return length of DST.
569 * DST must have space. If it overlaps SRC, then DST <= SRC must
573 copy_utf8_to_ascii_no_funny(char *dst, char *src)
579 while ((ch = *src++)) {
580 /* FIXME do the right thing for malformed and overlong sequences */
581 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
582 ; /* ignore funny control */
583 else if (ch > 0x7f) {
584 *p++ = '?'; /* replace non-ASCII */
585 while ((*src++ & 0xc0) == 0x80) ;
595 * Return byte-index of the N-th UTF-8 character in UTF-8 string S.
596 * If S doesn't have that many characters, return its length instead.
599 ufindpfx(char *s, int n)
605 if ((s[i++] & 0xc0) == 0xc0)
606 while ((s[i] & 0xc0) == 0x80)