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