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