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: Use to do output to a player
30 * Known contributors to this file:
31 * Dave Pare, 1986, 1989
32 * Steve McClure, 1998-2000
35 * The pr routine historically arranged for nonbuffered i/o
36 * because stdio didn't used to automatically flush stdout before
37 * it read something from stdin. Now pr() prepends an "output id"
38 * in front of each line of text, informing the user interface
39 * what sort of item it is seeing; prompt, noecho prompt,
40 * more input data, etc.
56 #include "prototypes.h"
58 static void pr_player(struct player *pl, int id, char *buf);
59 static void upr_player(struct player *pl, int id, char *buf);
60 static void outid(struct player *pl, int n);
63 * Print to current player similar to printf().
64 * Use printf-style FORMAT with the optional arguments.
65 * Note: `to print' without further qualifications means sending
75 (void)vsprintf(buf, format, ap);
77 if (player->flags & PF_UTF8)
78 /* normal text needs to be converted to user text */
79 upr_player(player, C_DATA, buf);
81 /* normal text and user text are identical */
82 pr_player(player, C_DATA, buf);
86 * Print UTF-8 text BUF to current player.
93 if (!(player->flags & PF_UTF8)) {
94 p = malloc(strlen(buf) + 1);
95 copy_utf8_to_ascii_no_funny(p, buf);
96 pr_player(player, C_DATA, p);
99 pr_player(player, C_DATA, buf);
103 * Send some text to P with id ID, line-buffered.
104 * Format text to send using printf-style FORMAT and optional
105 * arguments. It is assumed to be already user text. Plain ASCII and
106 * text received from the same player are fine, for anything else the
107 * caller has to deal with output filtering.
108 * If a partial line with different id is buffered, terminate it with
112 pr_id(struct player *p, int id, char *format, ...)
118 io_puts(p->iop, "\n");
121 va_start(ap, format);
122 (void)vsprintf(buf, format, ap);
124 pr_player(p, id, buf);
128 * Send C_FLASH text to PL.
129 * Format text to send using printf-style FORMAT and optional
130 * arguments. It is assumed to be UTF-8.
133 pr_flash(struct player *pl, char *format, ...)
135 char buf[4096]; /* UTF-8 */
138 if (pl->state != PS_PLAYING)
140 va_start(ap, format);
141 (void)vsprintf(buf, format, ap);
143 if (!(pl->flags & PF_UTF8))
144 copy_utf8_to_ascii_no_funny(buf, buf);
145 pr_player(pl, C_FLASH, buf);
146 io_output(pl->iop, IO_NOWAIT);
150 * Send C_INFORM text to PL.
151 * Format text to send using printf-style FORMAT and optional
152 * arguments. It is assumed to be plain ASCII.
155 pr_inform(struct player *pl, char *format, ...)
160 if (pl->state != PS_PLAYING)
162 va_start(ap, format);
163 (void)vsprintf(buf, format, ap);
165 pr_player(pl, C_INFORM, buf);
166 io_output(pl->iop, IO_NOWAIT);
170 * Send C_FLASH text to everyone.
171 * Format text to send using printf-style FORMAT and optional
172 * arguments. It is assumed to be plain ASCII.
175 pr_wall(char *format, ...)
177 char buf[4096]; /* UTF-8 */
181 va_start(ap, format);
182 (void)vsprintf(buf, format, ap);
184 for (p = player_next(0); p; p = player_next(p)) {
185 if (p->state != PS_PLAYING)
187 pr_player(p, C_FLASH, buf);
188 io_output(p->iop, IO_NOWAIT);
193 * Send ID text BUF to PL, line-buffered.
195 * If a partial line with different id is buffered, terminate it with
199 pr_player(struct player *pl, int id, char *buf)
206 while (*bp != '\0') {
207 if (pl->curid != -1 && pl->curid != id) {
208 io_puts(pl->iop, "\n");
213 p = strchr(bp, '\n');
216 if (pl->command && (pl->command->c_flags & C_MOD))
217 io_write(pl->iop, bp, len, IO_NOWAIT);
219 io_write(pl->iop, bp, len, IO_WAIT);
223 len = io_puts(pl->iop, bp);
230 * Send ID text BUF to PL, line-buffered.
231 * This function translates from normal text to user text.
232 * If a partial line with different id is buffered, terminate it with
236 upr_player(struct player *pl, int id, char *buf)
247 while ((ch = *bp++)) {
248 if (pl->curid != -1 && pl->curid != id) {
249 io_puts(pl->iop, "\n");
258 io_puts(pl->iop, printbuf);
265 io_puts(pl->iop, printbuf);
270 if (pl->command && (pl->command->c_flags & C_MOD))
271 io_write(pl->iop, &ch, 1, IO_NOWAIT);
273 io_write(pl->iop, &ch, 1, IO_WAIT);
277 io_puts(pl->iop, printbuf);
284 * This runs always at the beginning of a line.
287 outid(struct player *pl, int n)
291 if (CANT_HAPPEN(n > C_LAST))
295 buf[0] = 'a' - 10 + n;
300 io_puts(pl->iop, buf);
305 * Send redirection request REDIR to the current player.
306 * REDIR is UTF-8, but non-ASCII characters can occur only if the
307 * player sent them. Therefore, it is also user text.
312 pr_id(player, *redir == '>' ? C_REDIR : C_PIPE, "%s\n", redir);
316 * Send script execute request FILE to the current player.
317 * REDIR is UTF-8, but non-ASCII characters can occur only if the
318 * player sent them. Therefore, it is also user text.
323 pr_id(player, C_EXECUTE, "%s\n", file);
327 * Send a command prompt to the current player.
330 prprompt(int min, int btu)
332 pr_id(player, C_PROMPT, "%d %d\n", min, btu);
336 * Prompt for a line of non-command input.
337 * Send C_FLUSH prompt PROMPT to the current player.
338 * Read a line of input into BUF[SIZE] and convert it to ASCII.
339 * Return number of bytes in BUF[], not counting the terminating 0,
343 prmptrd(char *prompt, char *buf, int size)
348 pr_id(player, C_FLUSH, "%s\n", prompt);
349 if ((r = recvclient(buf, size)) < 0)
351 time(&player->curup);
354 if (player->flags & PF_UTF8)
355 return copy_utf8_to_ascii_no_funny(buf, buf);
356 return copy_ascii_no_funny(buf, buf);
360 * Prompt for a line of non-command, UTF-8 input.
361 * Send C_FLUSH prompt PROMPT to the current player.
362 * Read a line of input into BUF[SIZE], replacing funny characters by
363 * '?'. The result is UTF-8.
364 * Return number of bytes in BUF[], not counting the terminating 0,
368 uprmptrd(char *prompt, char *buf, int size)
373 pr_id(player, C_FLUSH, "%s\n", prompt);
374 if ((r = recvclient(buf, size)) < 0)
376 time(&player->curup);
379 if (player->flags & PF_UTF8)
380 return copy_utf8_no_funny(buf, buf);
381 return copy_ascii_no_funny(buf, buf);
385 * Print the current time in ctime() format.
397 * Print coordinates X, Y for COUNTRY.
398 * FORMAT must be a printf-style format string that converts exactly
402 prxy(char *format, coord x, coord y, natid country)
407 np = getnatp(country);
408 sprintf(buf, format, xrel(np, x), yrel(np, y));
413 * Print to country CN similar to printf().
414 * Use printf-style FORMAT with the optional arguments.
415 * Output is buffered until a newline arrives.
416 * If CN is the current player, print just like pr().
417 * Else print into a bulletin.
418 * Because printing like pr() requires normal text, and bulletins
419 * require user text, only plain ASCII is allowed.
422 PR(int cn, char *format, ...)
424 /* XXX should really do this on a per-nation basis */
425 static char longline[MAXNOC][512];
430 va_start(ap, format);
431 (void)vsprintf(buf, format, ap);
433 newline = strrchr(buf, '\n') ? 1 : 0;
434 strcat(longline[cn], buf);
436 if (update_pending || (cn && cn != player->cnum))
437 typed_wu(0, cn, longline[cn], TEL_BULLETIN);
439 pr_player(player, C_DATA, longline[cn]);
440 longline[cn][0] = '\0';
445 * Print the current time in ctime() format to country CN.
446 * If CN is the current player, print like prdate().
447 * Else print into a bulletin.
459 * Sound the current player's bell.
464 struct natstr *np = getnatp(player->cnum);
466 if (np->nat_flags & NF_BEEP)
471 * Print to country CN similar to printf().
472 * Use printf-style FORMAT with the optional arguments.
473 * If CN is the current player, print just like pr().
474 * Else print into a bulletin.
475 * Because printing like pr() requires normal text, and bulletins
476 * require user text, only plain ASCII is allowed.
479 mpr(int cn, char *format, ...)
484 va_start(ap, format);
485 (void)vsprintf(buf, format, ap);
488 if (update_pending || cn != player->cnum)
489 typed_wu(0, cn, buf, TEL_BULLETIN);
491 pr_player(player, C_DATA, buf);
496 * Copy SRC without funny characters to DST.
497 * Drop control characters, except for '\t'.
498 * Replace non-ASCII characters by '?'.
499 * Return length of DST.
500 * DST must have space. If it overlaps SRC, then DST <= SRC must
504 copy_ascii_no_funny(char *dst, char *src)
510 while ((ch = *src++)) {
511 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
512 ; /* ignore funny control */
514 *p++ = '?'; /* replace non-ASCII */
524 * Copy UTF-8 SRC without funny characters to DST.
525 * Drop control characters, except for '\t'.
526 * FIXME Replace malformed UTF-8 sequences by '?'.
527 * Return byte length of DST.
528 * DST must have space. If it overlaps SRC, then DST <= SRC must
532 copy_utf8_no_funny(char *dst, char *src)
538 while ((ch = *src++)) {
539 /* FIXME do the right thing for malformed and overlong sequences */
540 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
541 ; /* ignore funny control */
551 * Copy UTF-8 SRC without funny characters to ASCII DST.
552 * Drop control characters, except for '\t'.
553 * Replace non-ASCII characters by '?'.
554 * Return length of DST.
555 * DST must have space. If it overlaps SRC, then DST <= SRC must
559 copy_utf8_to_ascii_no_funny(char *dst, char *src)
565 while ((ch = *src++)) {
566 /* FIXME do the right thing for malformed and overlong sequences */
567 if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
568 ; /* ignore funny control */
569 else if (ch > 0x7f) {
570 *p++ = '?'; /* replace non-ASCII */
571 while ((*src++ & 0xc0) == 0x80) ;
581 * Return byte-index of the N-th UTF-8 character in UTF-8 string S.
582 * If S doesn't have that many characters, return its length instead.
585 ufindpfx(char *s, int n)
591 if ((s[i++] & 0xc0) == 0xc0)
592 while ((s[i] & 0xc0) == 0x80)