]> git.pond.sub.org Git - empserver/blob - src/lib/subs/pr.c
710e8aedab0a3e262ee2ce1c73451f0a48ed52a2
[empserver] / src / lib / subs / pr.c
1 /*
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
5  *
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.
10  *
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.
15  *
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
19  *
20  *  ---
21  *
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.
25  *
26  *  ---
27  *
28  *  pr.c: Use to do output to a player
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986, 1989 
32  *     Steve McClure, 1998-2000
33  */
34 /*
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.
41  */
42
43 #include <string.h>
44 #include <fcntl.h>
45 #include <ctype.h>
46 #include <stdarg.h>
47 #include "proto.h"
48 #include "misc.h"
49 #include "player.h"
50 #include "nat.h"
51 #include "empio.h"
52 #include "file.h"
53 #include "com.h"
54 #include "tel.h"
55 #include "server.h"
56 #include "prototypes.h"
57
58 static void outid(struct player *pl, int n);
59
60 /*VARARGS*/
61 void
62 pr(char *format, ...)
63 {
64     char buf[4096];
65     va_list ap;
66
67     va_start(ap, format);
68     (void)vsprintf(buf, format, ap);
69     va_end(ap);
70     if (player->flags & PF_UTF8)
71         upr_player(player, C_DATA, buf);
72     else
73         pr_player(player, C_DATA, buf);
74 }
75
76 /*
77  * Print UTF-8 text BUF to current player.
78  */
79 void
80 uprnf(char *buf)
81 {
82     char *p;
83
84     if (!(player->flags & PF_UTF8)) {
85         p = malloc(strlen(buf) + 1);
86         copy_utf8_to_ascii_no_funny(p, buf);
87         pr_player(player, C_DATA, p);
88         free(p);
89     } else
90         pr_player(player, C_DATA, buf);
91 }
92
93 /*VARARGS*/
94 void
95 pr_id(struct player *p, int id, s_char *format, ...)
96 {
97     s_char buf[4096];
98     va_list ap;
99
100     if (p->curid >= 0) {
101         io_puts(p->iop, "\n");
102         p->curid = -1;
103     }
104     va_start(ap, format);
105     (void)vsprintf(buf, format, ap);
106     va_end(ap);
107     pr_player(p, id, buf);
108 }
109
110 void
111 pr_flash(struct player *pl, char *format
112          /* format is message text */, ...)
113 {
114     char buf[4096]; /* buf is message text */
115     va_list ap;
116
117     if (pl->state != PS_PLAYING)
118         return;
119     va_start(ap, format);
120     (void)vsprintf(buf, format, ap);
121     va_end(ap);
122     if (!(pl->flags & PF_UTF8))
123         copy_utf8_to_ascii_no_funny(buf, buf);
124     pr_player(pl, C_FLASH, buf);
125     io_output(pl->iop, IO_NOWAIT);
126 }
127
128 void
129 pr_inform(struct player *pl, s_char *format, ...)
130 {
131     s_char buf[4096];
132     va_list ap;
133
134     if (pl->state != PS_PLAYING)
135         return;
136     va_start(ap, format);
137     (void)vsprintf(buf, format, ap);
138     va_end(ap);
139     pr_player(pl, C_INFORM, buf);
140     io_output(pl->iop, IO_NOWAIT);
141 }
142
143 void
144 pr_wall(s_char *format, ...)
145 {
146     s_char buf[4096];
147     struct player *p;
148     va_list ap;
149
150     va_start(ap, format);
151     (void)vsprintf(buf, format, ap);
152     va_end(ap);
153     for (p = player_next(0); p; p = player_next(p)) {
154         if (p->state != PS_PLAYING)
155             continue;
156         pr_player(p, C_FLASH, buf);
157         io_output(p->iop, IO_NOWAIT);
158     }
159 }
160
161 void
162 pr_player(struct player *pl, int id, s_char *buf)
163 {
164     register s_char *p;
165     register s_char *bp;
166     register int len;
167
168     bp = buf;
169     while (*bp != '\0') {
170         if (pl->curid != -1 && pl->curid != id) {
171             io_puts(pl->iop, "\n");
172             pl->curid = -1;
173         }
174         if (pl->curid == -1)
175             outid(pl, id);
176         p = strchr(bp, '\n');
177         if (p != 0) {
178             len = (p - bp) + 1;
179             if (pl->command && (pl->command->c_flags & C_MOD))
180                 io_write(pl->iop, bp, len, IO_NOWAIT);
181             else
182                 io_write(pl->iop, bp, len, IO_WAIT);
183             bp += len;
184             pl->curid = -1;
185         } else {
186             len = io_puts(pl->iop, bp);
187             bp += len;
188         }
189     }
190 }
191
192 void
193 upr_player(struct player *pl, int id, char *buf
194                       /* buf is message text */)
195 {
196     register char *bp; /* bp is message text */
197     register int standout = 0;
198     char printbuf[2]; /* bp is message text */
199     char ch;
200
201     printbuf[0] = '\0';
202     printbuf[1] = '\0';
203
204     bp = buf;
205     while ((ch = *bp++)) {
206         if (pl->curid != -1 && pl->curid != id) {
207             io_puts(pl->iop, "\n");
208             pl->curid = -1;
209         }
210         if (pl->curid == -1)
211             outid(pl, id);
212
213         if (ch & 0x80) {
214             if (standout == 0) {
215                 printbuf[0] = 0x0e;
216                 io_puts(pl->iop, printbuf);
217                 standout = 1;
218             }
219             ch &= 0x7f;
220         } else {
221             if (standout == 1) {
222                 printbuf[0] = 0x0f;
223                 io_puts(pl->iop, printbuf);
224                 standout = 0;
225             }
226         }
227         if (ch == '\n') {
228             if (pl->command && (pl->command->c_flags & C_MOD))
229                 io_write(pl->iop, &ch, 1, IO_NOWAIT);
230             else
231                 io_write(pl->iop, &ch, 1, IO_WAIT);
232             pl->curid = -1;
233         } else {
234             printbuf[0] = ch;
235             io_puts(pl->iop, printbuf);
236         }
237     }
238 }
239
240 /*
241  * highlighted characters have hex 80 or'ed in
242  * with them to designate their highlightedness
243  */
244 void
245 pr_hilite(s_char *buf)
246 {
247     register s_char *bp;
248     register s_char c;
249     s_char *p;
250
251     p = malloc(strlen(buf) + 1);
252     strcpy(p, buf);
253     for (bp = p; 0 != (c = *bp); bp++)
254         if (isprint(c))
255             *bp |= 0x80;
256     pr(p);
257     free(p);
258 }
259
260 /*
261  * output hex code + space
262  */
263 static void
264 outid(struct player *pl, int n)
265 {
266     s_char c;
267     s_char buf[3];
268
269     if (n > C_LAST) {
270         logerror("outid: %d not valid code\n", n);
271         return;
272     }
273     if (n >= 10)
274         c = 'a' - 10 + n;
275     else
276         c = '0' + n;
277     buf[0] = c;
278     buf[1] = ' ';
279     buf[2] = '\0';
280     io_puts(pl->iop, buf);
281     pl->curid = n;
282 }
283
284 void
285 prredir(s_char *redir)
286 {
287     pr_id(player, *redir == '>' ? C_REDIR : C_PIPE, "%s\n", redir);
288 }
289
290 void
291 prexec(s_char *file)
292 {
293     pr_id(player, C_EXECUTE, "%s\n", file);
294 }
295
296 void
297 prprompt(int min, int btu)
298 {
299     pr_id(player, C_PROMPT, "%d %d\n", min, btu);
300 }
301
302 int
303 prmptrd(char *prompt, char *str, int size)
304 {
305     int r;
306     char *cp;
307
308     pr_id(player, C_FLUSH, "%s\n", prompt);
309     if ((r = recvclient(str, size)) < 0)
310         return r;
311     time(&player->curup);
312     if (*str == 0)
313         return 1;
314     if (player->flags & PF_UTF8)
315         return copy_utf8_to_ascii_no_funny(str, str);
316     return copy_ascii_no_funny(str, str);
317 }
318
319 int
320 uprmptrd(char *prompt, char *str /* str is message text */, int size)
321 {
322     int r;
323     char *cp; /* cp is message text */
324
325     pr_id(player, C_FLUSH, "%s\n", prompt);
326     if ((r = recvclient(str, size)) < 0)
327         return r;
328     time(&player->curup);
329     if (*str == 0)
330         return 1;
331     if (player->flags & PF_UTF8)
332         return copy_utf8_no_funny(str, str);
333     return copy_ascii_no_funny(str, str);
334 }
335
336 void
337 prdate(void)
338 {
339     time_t now;
340
341     (void)time(&now);
342     pr(ctime(&now));
343 }
344
345 /*
346  * print x,y formatting as country
347  */
348 void
349 prxy(s_char *format, coord x, coord y, natid country)
350 {
351     s_char buf[255];
352     struct natstr *np;
353
354     np = getnatp(country);
355     sprintf(buf, format, xrel(np, x), yrel(np, y));
356     pr(buf);
357 }
358
359 /*VARARGS*/
360 void
361 PR(int cn, s_char *format, ...)
362 {
363     /* XXX should really do this on a per-nation basis */
364     static s_char longline[MAXNOC][512];
365     int newline;
366     va_list ap;
367     s_char buf[1024];
368
369     va_start(ap, format);
370     (void)vsprintf(buf, format, ap);
371     va_end(ap);
372     newline = strrchr(buf, '\n') ? 1 : 0;
373     strcat(longline[cn], buf);
374     if (newline) {
375         if (update_pending || (cn && cn != player->cnum))
376             typed_wu(0, cn, longline[cn], TEL_BULLETIN);
377         else
378             pr_player(player, C_DATA, longline[cn]);
379         longline[cn][0] = '\0';
380     }
381 }
382
383 void
384 PRdate(natid cn)
385 {
386     time_t now;
387
388     (void)time(&now);
389     PR(cn, ctime(&now));
390 }
391
392 void
393 pr_beep(void)
394 {
395     struct natstr *np = getnatp(player->cnum);
396
397     if (np->nat_flags & NF_BEEP)
398         pr("\07");
399 }
400
401 void
402 mpr(int cn, s_char *format, ...)
403 {
404     s_char buf[4096];
405     va_list ap;
406
407     va_start(ap, format);
408     (void)vsprintf(buf, format, ap);
409     va_end(ap);
410     if (cn) {
411         if (update_pending || cn != player->cnum)
412             typed_wu(0, cn, buf, TEL_BULLETIN);
413         else
414             pr_player(player, C_DATA, buf);
415     }
416 }
417
418 /*
419  * Copy SRC without funny characters to DST.
420  * Drop control characters, except for '\t'.
421  * Replace non-ASCII characters by '?'.
422  * Return length of DST.
423  * DST must have space.  If it overlaps SRC, then DST <= SRC must
424  * hold.
425  */
426 size_t
427 copy_ascii_no_funny(char *dst, char *src)
428 {
429     char *p;
430     unsigned char ch;
431
432     p = dst;
433     while ((ch = *src++)) {
434         if ((ch < 0x20 && ch != '\t') || ch == 0x7f)
435             ;                   /* ignore control */
436         else if (ch > 0x7f)
437             *p++ = '?'; /* replace non-ASCII */
438         else
439             *p++ = ch;
440     }
441     *p = 0;
442
443     return p - dst;
444 }
445
446 /*
447  * Copy UTF-8 SRC without funny characters to DST.
448  * Drop control characters, except for '\t'.
449  * FIXME Replace malformed UTF-8 sequences by '?'.
450  * Return byte length of DST.
451  * DST must have space.  If it overlaps SRC, then DST <= SRC must
452  * hold.
453  */
454 size_t
455 copy_utf8_no_funny(char *dst, char *src)
456 {
457     char *p;
458     unsigned char ch;
459
460     p = dst;
461     while ((ch = *src++)) {
462         /* FIXME do the right thing for malformed and overlong sequences */
463         if ((ch < 0x20 && ch != '\t') || ch == 0x7f)
464             ;                   /* ignore control */
465         else
466             *p++ = ch;
467     }
468     *p = 0;
469
470     return p - dst;
471 }
472
473 /*
474  * Copy UTF-8 SRC without funny characters to ASCII DST.
475  * Drop control characters, except for '\t'.
476  * Replace non-ASCII characters by '?'.
477  * Return length of DST.
478  * DST must have space.  If it overlaps SRC, then DST <= SRC must
479  * hold.
480  */
481 size_t
482 copy_utf8_to_ascii_no_funny(char *dst, char *src)
483 {
484     char *p;
485     unsigned char ch;
486
487     p = dst;
488     while ((ch = *src++)) {
489         /* FIXME do the right thing for malformed and overlong sequences */
490         if ((ch < 0x20 && ch != '\t') || ch == 0x7f)
491             ;                   /* ignore control */
492         else if (ch > 0x7f) {
493             *p++ = '?';         /* replace non-ASCII */
494             while ((*src++ & 0xc0) == 0x80) ;
495         } else
496             *p++ = ch;
497     }
498     *p = 0;
499
500     return p - dst;
501 }
502
503 /*
504  * Return byte-index of the N-th UTF-8 character in UTF-8 string S.
505  * If S doesn't have that many characters, return its length instead.
506  */
507 int
508 ufindpfx(char *s, int n)
509 {
510     int i = 0;
511
512     while (n && s[i])
513     {
514         if ((s[i++] & 0xc0) == 0xc0)
515             while ((s[i] & 0xc0) == 0x80)
516                 i++;
517         --n;
518     }
519     return i;
520 }