src/lib/subs/pr.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  pr.c: Output to players
00029  * 
00030  *  Known contributors to this file:
00031  *     Dave Pare, 1986, 1989 
00032  *     Steve McClure, 1998-2000
00033  *     Ron Koenderink, 2005
00034  *     Markus Armbruster, 2005-2007
00035  */
00036 
00037 /*
00038  * Player output is fully buffered.  It can block only if the
00039  * receiving player is the current player and his last command doesn't
00040  * have the C_MOD flag.  Output to another player must not block
00041  * because that player could be gone when the printing thread wakes
00042  * up, and the code isn't prepared for that.  Output within C_MOD
00043  * command never blocks, so that such commands can print freely
00044  * without yielding the processor.
00045  *
00046  * Each line of output starts with an identification character
00047  * encoding the output id, followed by space.  Ids less than 10 are
00048  * encoded as decimal digits, and larger ids as lower case letters,
00049  * starting with 'a'.  Symbolic names for ids are defined in proto.h.
00050  */
00051 
00052 #include <config.h>
00053 
00054 #include <stdarg.h>
00055 #include <stdlib.h>
00056 #include "com.h"
00057 #include "empio.h"
00058 #include "file.h"
00059 #include "misc.h"
00060 #include "nat.h"
00061 #include "player.h"
00062 #include "proto.h"
00063 #include "prototypes.h"
00064 #include "server.h"
00065 #include "tel.h"
00066 
00067 static void pr_player(struct player *pl, int id, char *buf);
00068 static void upr_player(struct player *pl, int id, char *buf);
00069 static void outid(struct player *pl, int n);
00070 
00071 /*
00072  * Print to current player similar to printf().
00073  * Use printf-style FORMAT with the optional arguments.
00074  * Note: `to print' without further qualifications means sending
00075  * C_DATA text.
00076  */
00077 void
00078 pr(char *format, ...)
00079 {
00080     char buf[4096];
00081     va_list ap;
00082 
00083     va_start(ap, format);
00084     (void)vsprintf(buf, format, ap);
00085     va_end(ap);
00086     if (player->flags & PF_UTF8)
00087         /* normal text needs to be converted to user text */
00088         upr_player(player, C_DATA, buf);
00089     else
00090         /* normal text and user text are identical */
00091         pr_player(player, C_DATA, buf);
00092 }
00093 
00094 /*
00095  * Print UTF-8 text BUF to current player.
00096  */
00097 void
00098 uprnf(char *buf)
00099 {
00100     char *p;
00101 
00102     if (!(player->flags & PF_UTF8)) {
00103         p = malloc(strlen(buf) + 1);
00104         copy_utf8_to_ascii_no_funny(p, buf);
00105         pr_player(player, C_DATA, p);
00106         free(p);
00107     } else
00108         pr_player(player, C_DATA, buf);
00109 }
00110 
00111 /*
00112  * Send some text to P with id ID, line-buffered.
00113  * Format text to send using printf-style FORMAT and optional
00114  * arguments.  It is assumed to be already user text.  Plain ASCII and
00115  * text received from the same player are fine, for anything else the
00116  * caller has to deal with output filtering.
00117  * If a partial line is buffered, terminate it with a newline first.
00118  */
00119 void
00120 pr_id(struct player *p, int id, char *format, ...)
00121 {
00122     char buf[4096];
00123     va_list ap;
00124 
00125     if (p->curid >= 0) {
00126         io_puts(p->iop, "\n");
00127         p->curid = -1;
00128     }
00129     va_start(ap, format);
00130     (void)vsprintf(buf, format, ap);
00131     va_end(ap);
00132     pr_player(p, id, buf);
00133 }
00134 
00135 /*
00136  * Send C_FLASH text to PL.
00137  * Format text to send using printf-style FORMAT and optional
00138  * arguments.  It is assumed to be UTF-8.
00139  * Initiate an output queue flush, but do not wait for it to complete.
00140  */
00141 void
00142 pr_flash(struct player *pl, char *format, ...)
00143 {
00144     char buf[4096];             /* UTF-8 */
00145     va_list ap;
00146 
00147     if (pl->state != PS_PLAYING)
00148         return;
00149     va_start(ap, format);
00150     (void)vsprintf(buf, format, ap);
00151     va_end(ap);
00152     if (!(pl->flags & PF_UTF8))
00153         copy_utf8_to_ascii_no_funny(buf, buf);
00154     pr_player(pl, C_FLASH, buf);
00155     io_output(pl->iop, IO_NOWAIT);
00156 }
00157 
00158 /*
00159  * Send C_INFORM text to PL.
00160  * Format text to send using printf-style FORMAT and optional
00161  * arguments.  It is assumed to be plain ASCII.
00162  * Initiate an output queue flush, but do not wait for it to complete.
00163  */
00164 void
00165 pr_inform(struct player *pl, char *format, ...)
00166 {
00167     char buf[4096];
00168     va_list ap;
00169 
00170     if (pl->state != PS_PLAYING)
00171         return;
00172     va_start(ap, format);
00173     (void)vsprintf(buf, format, ap);
00174     va_end(ap);
00175     pr_player(pl, C_INFORM, buf);
00176     io_output(pl->iop, IO_NOWAIT);
00177 }
00178 
00179 /*
00180  * Send C_FLASH text to everyone.
00181  * Format text to send using printf-style FORMAT and optional
00182  * arguments.  It is assumed to be plain ASCII.
00183  * Prefix text it with a header suitable for broadcast from deity.
00184  * Initiate an output queue flush, but do not wait for it to complete.
00185  */
00186 void
00187 pr_wall(char *format, ...)
00188 {
00189     time_t now;
00190     struct tm *tm;
00191     char buf[4096];             /* UTF-8 */
00192     int n;
00193     struct player *p;
00194     va_list ap;
00195 
00196     time(&now);
00197     tm = localtime(&now);
00198     n = sprintf(buf, "BROADCAST from %s @ %02d:%02d: ",
00199                 getnatp(0)->nat_cnam, tm->tm_hour, tm->tm_min);
00200 
00201     va_start(ap, format);
00202     (void)vsprintf(buf + n, format, ap);
00203     va_end(ap);
00204     for (p = player_next(0); p; p = player_next(p)) {
00205         if (p->state != PS_PLAYING)
00206             continue;
00207         pr_player(p, C_FLASH, buf);
00208         io_output(p->iop, IO_NOWAIT);
00209     }
00210 }
00211 
00212 /*
00213  * Send ID text BUF to PL, line-buffered.
00214  * BUF is user text.
00215  * If a partial line with different id is buffered, terminate it with
00216  * a newline first.
00217  */
00218 static void
00219 pr_player(struct player *pl, int id, char *buf)
00220 {
00221     char *p;
00222     char *bp;
00223     int len;
00224 
00225     bp = buf;
00226     while (*bp != '\0') {
00227         if (pl->curid != -1 && pl->curid != id) {
00228             io_puts(pl->iop, "\n");
00229             pl->curid = -1;
00230         }
00231         if (pl->curid == -1)
00232             outid(pl, id);
00233         p = strchr(bp, '\n');
00234         if (p != NULL) {
00235             len = (p - bp) + 1;
00236             if ((pl->command && (pl->command->c_flags & C_MOD)) ||
00237                 (player != pl))
00238                 io_write(pl->iop, bp, len, IO_NOWAIT);
00239             else
00240                 io_write(pl->iop, bp, len, IO_WAIT);
00241             bp += len;
00242             pl->curid = -1;
00243         } else {
00244             len = io_puts(pl->iop, bp);
00245             bp += len;
00246         }
00247     }
00248 }
00249 
00250 /*
00251  * Send ID text BUF to PL, line-buffered.
00252  * This function translates from normal text to user text.
00253  * If a partial line with different id is buffered, terminate it with
00254  * a newline first.
00255  */
00256 static void
00257 upr_player(struct player *pl, int id, char *buf)
00258 {
00259     char *bp;
00260     int standout = 0;
00261     char printbuf[2];
00262     char ch;
00263 
00264     printbuf[0] = '\0';
00265     printbuf[1] = '\0';
00266 
00267     bp = buf;
00268     while ((ch = *bp++)) {
00269         if (pl->curid != -1 && pl->curid != id) {
00270             io_puts(pl->iop, "\n");
00271             pl->curid = -1;
00272         }
00273         if (pl->curid == -1)
00274             outid(pl, id);
00275 
00276         if (ch & 0x80) {
00277             if (standout == 0) {
00278                 printbuf[0] = 0x0e;
00279                 io_puts(pl->iop, printbuf);
00280                 standout = 1;
00281             }
00282             ch &= 0x7f;
00283         } else {
00284             if (standout == 1) {
00285                 printbuf[0] = 0x0f;
00286                 io_puts(pl->iop, printbuf);
00287                 standout = 0;
00288             }
00289         }
00290         if (ch == '\n') {
00291             if ((pl->command && (pl->command->c_flags & C_MOD)) ||
00292                 (player != pl))
00293                 io_write(pl->iop, &ch, 1, IO_NOWAIT);
00294             else
00295                 io_write(pl->iop, &ch, 1, IO_WAIT);
00296             pl->curid = -1;
00297         } else {
00298             printbuf[0] = ch;
00299             io_puts(pl->iop, printbuf);
00300         }
00301     }
00302 }
00303 
00304 /*
00305  * Send id N to PL.
00306  * This runs always at the beginning of a line.
00307  */
00308 static void
00309 outid(struct player *pl, int n)
00310 {
00311     char buf[3];
00312 
00313     if (CANT_HAPPEN(n > C_LAST))
00314         n = C_DATA;
00315 
00316     if (n >= 10)
00317         buf[0] = 'a' - 10 + n;
00318     else
00319         buf[0] = '0' + n;
00320     buf[1] = ' ';
00321     buf[2] = '\0';
00322     io_puts(pl->iop, buf);
00323     pl->curid = n;
00324 }
00325 
00326 /*
00327  * Send redirection request REDIR to the current player.
00328  * REDIR is UTF-8, but non-ASCII characters can occur only if the
00329  * player sent them.  Therefore, it is also user text.
00330  */
00331 void
00332 prredir(char *redir)
00333 {
00334     pr_id(player, *redir == '>' ? C_REDIR : C_PIPE, "%s\n", redir);
00335 }
00336 
00337 /*
00338  * Send script execute request FILE to the current player.
00339  * REDIR is UTF-8, but non-ASCII characters can occur only if the
00340  * player sent them.  Therefore, it is also user text.
00341  */
00342 void
00343 prexec(char *file)
00344 {
00345     pr_id(player, C_EXECUTE, "%s\n", file);
00346 }
00347 
00348 /*
00349  * Send a command prompt to the current player.
00350  */
00351 void
00352 prprompt(int min, int btu)
00353 {
00354     pr_id(player, C_PROMPT, "%d %d\n", min, btu);
00355 }
00356 
00357 /*
00358  * Prompt for a line of non-command input.
00359  * Send C_FLUSH prompt PROMPT to the current player.
00360  * Read a line of input into BUF[SIZE] and convert it to ASCII.
00361  * This may block for input, yielding the processor.  Flush buffered
00362  * output when blocking, to make sure player sees the prompt.
00363  * Return number of bytes in BUF[], not counting the terminating 0,
00364  * or -1 on error.
00365  */
00366 int
00367 prmptrd(char *prompt, char *buf, int size)
00368 {
00369     int r;
00370 
00371     /*
00372      * Each prompt must consume one line of input.  recvclient()
00373      * doesn't do that while player->aborted.
00374      */
00375     CANT_HAPPEN(player->aborted);
00376 
00377     if (CANT_HAPPEN(!prompt))
00378         prompt = "? ";
00379 
00380     pr_id(player, C_FLUSH, "%s\n", prompt);
00381     if ((r = recvclient(buf, size)) < 0)
00382         return r;
00383     time(&player->curup);
00384     if (*buf == 0)
00385         return 1;
00386     if (player->flags & PF_UTF8)
00387         return copy_utf8_to_ascii_no_funny(buf, buf);
00388     return copy_ascii_no_funny(buf, buf);
00389 }
00390 
00391 /*
00392  * Prompt for a line of non-command, UTF-8 input.
00393  * Send C_FLUSH prompt PROMPT to the current player.
00394  * Read a line of input into BUF[SIZE], replacing funny characters by
00395  * '?'.  The result is UTF-8.
00396  * This may block for input, yielding the processor.  Flush buffered
00397  * output when blocking, to make sure player sees the prompt.
00398  * Return number of bytes in BUF[], not counting the terminating 0,
00399  * or -1 on error.
00400  */
00401 int
00402 uprmptrd(char *prompt, char *buf, int size)
00403 {
00404     int r;
00405 
00406     /* See prmptrd() */
00407     CANT_HAPPEN(player->aborted);
00408 
00409     if (CANT_HAPPEN(!prompt))
00410         prompt = "? ";
00411 
00412     pr_id(player, C_FLUSH, "%s\n", prompt);
00413     if ((r = recvclient(buf, size)) < 0)
00414         return r;
00415     time(&player->curup);
00416     if (*buf == 0)
00417         return 1;
00418     if (player->flags & PF_UTF8)
00419         return copy_utf8_no_funny(buf, buf);
00420     return copy_ascii_no_funny(buf, buf);
00421 }
00422 
00423 /*
00424  * Print the current time in ctime() format.
00425  */
00426 void
00427 prdate(void)
00428 {
00429     time_t now;
00430 
00431     (void)time(&now);
00432     pr(ctime(&now));
00433 }
00434 
00435 /*
00436  * Print coordinates X, Y for COUNTRY.
00437  * FORMAT must be a printf-style format string that converts exactly
00438  * two int values.
00439  */
00440 void
00441 prxy(char *format, coord x, coord y, natid country)
00442 {
00443     struct natstr *np;
00444 
00445     np = getnatp(country);
00446     pr(format, xrel(np, x), yrel(np, y));
00447 }
00448 
00449 /*
00450  * Print to country CN similar to printf().
00451  * Use printf-style FORMAT with the optional arguments.
00452  * Output is buffered until a newline arrives.
00453  * If CN is the current player and we're not in the update, print just
00454  * like pr().  Else print into a bulletin.
00455  * Because printing like pr() requires normal text, and bulletins
00456  * require user text, only plain ASCII is allowed.
00457  */
00458 void
00459 PR(int cn, char *format, ...)
00460 {
00461     /* XXX should really do this on a per-nation basis */
00462     static char longline[MAXNOC][512];
00463     int newline;
00464     va_list ap;
00465     char buf[1024];
00466 
00467     va_start(ap, format);
00468     (void)vsprintf(buf, format, ap);
00469     va_end(ap);
00470     newline = strrchr(buf, '\n') ? 1 : 0;
00471     strcat(longline[cn], buf);
00472     if (newline) {
00473         if (update_running || (cn && cn != player->cnum))
00474             typed_wu(0, cn, longline[cn], TEL_BULLETIN);
00475         else
00476             pr_player(player, C_DATA, longline[cn]);
00477         longline[cn][0] = '\0';
00478     }
00479 }
00480 
00481 /*
00482  * Print the current time in ctime() format to country CN.
00483  * If CN is the current player and we're not in the update, print just
00484  * like prdate().  Else print into a bulletin.
00485  */
00486 void
00487 PRdate(natid cn)
00488 {
00489     time_t now;
00490 
00491     (void)time(&now);
00492     PR(cn, ctime(&now));
00493 }
00494 
00495 /*
00496  * Sound the current player's bell.
00497  */
00498 void
00499 pr_beep(void)
00500 {
00501     struct natstr *np = getnatp(player->cnum);
00502 
00503     if (np->nat_flags & NF_BEEP)
00504         pr("\07");
00505 }
00506 
00507 /*
00508  * Print to country CN similar to printf().
00509  * Use printf-style FORMAT with the optional arguments.
00510  * If CN is the current player and we're not in the update, print just
00511  * like pr().  Else print into a bulletin.
00512  * Because printing like pr() requires normal text, and bulletins
00513  * require user text, only plain ASCII is allowed.
00514  */
00515 void
00516 mpr(int cn, char *format, ...)
00517 {
00518     char buf[4096];
00519     va_list ap;
00520 
00521     va_start(ap, format);
00522     (void)vsprintf(buf, format, ap);
00523     va_end(ap);
00524     if (cn) {
00525         if (update_running || cn != player->cnum)
00526             typed_wu(0, cn, buf, TEL_BULLETIN);
00527         else
00528             pr_player(player, C_DATA, buf);
00529     }
00530 }
00531 
00532 /*
00533  * Copy SRC without funny characters to DST.
00534  * Drop control characters, except for '\t'.
00535  * Replace non-ASCII characters by '?'.
00536  * Return length of DST.
00537  * DST must have space.  If it overlaps SRC, then DST <= SRC must
00538  * hold.
00539  */
00540 size_t
00541 copy_ascii_no_funny(char *dst, char *src)
00542 {
00543     char *p;
00544     unsigned char ch;
00545 
00546     p = dst;
00547     while ((ch = *src++)) {
00548         if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
00549             ;                   /* ignore funny control */
00550         else if (ch > 0x7f)
00551             *p++ = '?'; /* replace non-ASCII */
00552         else
00553             *p++ = ch;
00554     }
00555     *p = 0;
00556 
00557     return p - dst;
00558 }
00559 
00560 /*
00561  * Copy UTF-8 SRC without funny characters to DST.
00562  * Drop control characters, except for '\t'.
00563  * FIXME Replace malformed UTF-8 sequences by '?'.
00564  * Return byte length of DST.
00565  * DST must have space.  If it overlaps SRC, then DST <= SRC must
00566  * hold.
00567  */
00568 size_t
00569 copy_utf8_no_funny(char *dst, char *src)
00570 {
00571     char *p;
00572     unsigned char ch;
00573 
00574     p = dst;
00575     while ((ch = *src++)) {
00576         /* FIXME do the right thing for malformed and overlong sequences */
00577         if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
00578             ;                   /* ignore funny control */
00579         else
00580             *p++ = ch;
00581     }
00582     *p = 0;
00583 
00584     return p - dst;
00585 }
00586 
00587 /*
00588  * Copy UTF-8 SRC without funny characters to ASCII DST.
00589  * Drop control characters, except for '\t'.
00590  * Replace non-ASCII characters by '?'.
00591  * Return length of DST.
00592  * DST must have space.  If it overlaps SRC, then DST <= SRC must
00593  * hold.
00594  */
00595 size_t
00596 copy_utf8_to_ascii_no_funny(char *dst, char *src)
00597 {
00598     char *p;
00599     unsigned char ch;
00600 
00601     p = dst;
00602     while ((ch = *src++)) {
00603         /* FIXME do the right thing for malformed and overlong sequences */
00604         if ((ch < 0x20 && ch != '\t' && ch != '\n') || ch == 0x7f)
00605             ;                   /* ignore funny control */
00606         else if (ch > 0x7f) {
00607             *p++ = '?';         /* replace non-ASCII */
00608             while ((*src++ & 0xc0) == 0x80) ;
00609         } else
00610             *p++ = ch;
00611     }
00612     *p = 0;
00613 
00614     return p - dst;
00615 }
00616 
00617 /*
00618  * Return byte-index of the N-th UTF-8 character in UTF-8 string S.
00619  * If S doesn't have that many characters, return its length instead.
00620  */
00621 int
00622 ufindpfx(char *s, int n)
00623 {
00624     int i = 0;
00625 
00626     while (n && s[i])
00627     {
00628         if ((s[i++] & 0xc0) == 0xc0)
00629             while ((s[i] & 0xc0) == 0x80)
00630                 i++;
00631         --n;
00632     }
00633     return i;
00634 }

Generated on Fri Mar 28 11:01:15 2008 for empserver by  doxygen 1.5.2