1 /************************************************************************
2 * This program is Copyright (C) 1986-1996 by Jonathan Payne. JOVE is *
3 * provided to you without charge, and with no warranty. You may give *
4 * away copies of JOVE, including sources, provided that this notice is *
5 * included in all the files. *
6 ************************************************************************/
7
8 #include "jove.h"
9 #include "chars.h"
10 #include "fp.h"
11 #include "jctype.h"
12 #include "disp.h"
13 #include "extend.h"
14 #include "fmt.h"
15
16 #ifdef MAC
17 # include "mac.h"
18 #endif
19
20 private void
21 doformat proto((File *, const char *, va_list)),
22 outld proto((long, int)),
23 pad proto((DAPchar, int));
24
25 char mesgbuf[MESG_SIZE];
26
27 /* Formatting codes supported:
28 *
29 * %%: => '%'
30 * %O, %D, %X: long => octal, decimal, or hex
31 * %lo, %ld, %lx: long => octal, decimal, or hex
32 * %o, %d, %x: int => octal, decimal, or hex
33 * %c: char => character
34 * %s: char* => string
35 *
36 * %b: buffer pointer => buffer's name
37 * %f: void => current command's name
38 * %n: int => int == 1? "" : "s"
39 * %p: char => visible rep
40 */
41
42 #ifdef ZTCDOS
43 /* ZTCDOS only accepts va_list in a prototype */
44 void
format(char * buf,size_t len,const char * fmt,va_list ap)45 format(char *buf, size_t len, const char *fmt, va_list ap)
46 #else
47 void
48 format(buf, len, fmt, ap)
49 char *buf;
50 size_t len;
51 const char *fmt;
52 va_list ap;
53 #endif
54 {
55 File strbuf;
56
57 strbuf.f_ptr = strbuf.f_base = buf;
58 strbuf.f_fd = -1; /* Not legit for files */
59 strbuf.f_bufsize = strbuf.f_cnt = len;
60 strbuf.f_flags = F_STRING;
61
62 doformat(&strbuf, fmt, ap);
63 f_putc('\0', &strbuf); /* f_putc will place this, even if overflow */
64 }
65
66 /* pretty-print character c into buffer cp (up to PPWIDTH bytes) */
67
68 void
PPchar(c,cp)69 PPchar(c, cp)
70 ZXchar c;
71 char *cp;
72 {
73 if (jisprint(c)) {
74 cp[0] = c;
75 cp[1] = '\0';
76 } else if (c < DEL) {
77 strcpy(cp, "^?");
78 cp[1] = c +'@';
79 } else if (c == DEL) {
80 strcpy(cp, "^?");
81 } else {
82 cp[0] = '\\';
83 cp[1] = '0'+(c >> 6);
84 cp[2] = '0'+((c >> 3) & 07);
85 cp[3] = '0'+(c & 07);
86 cp[4] = '\0';
87 }
88 }
89
90 private struct fmt_state {
91 int precision,
92 width;
93 bool leftadj;
94 char padc;
95 File *iop;
96 } current_fmt;
97
98 private void
putld(d,base)99 putld(d, base)
100 long d;
101 int base;
102 {
103 int len = 1;
104 long tmpd = d;
105
106 if (current_fmt.width == 0 && current_fmt.precision) {
107 current_fmt.width = current_fmt.precision;
108 current_fmt.padc = '0';
109 }
110 while ((tmpd = (tmpd / base)) != 0)
111 len += 1;
112 if (d < 0)
113 len += 1;
114 if (!current_fmt.leftadj)
115 pad(current_fmt.padc, current_fmt.width - len);
116 if (d < 0) {
117 f_putc('-', current_fmt.iop);
118 d = -d;
119 }
120 outld(d, base);
121 if (current_fmt.leftadj)
122 pad(current_fmt.padc, current_fmt.width - len);
123 }
124
125 private void
outld(d,base)126 outld(d, base)
127 long d;
128 int base;
129 {
130 register long n;
131 static const char chars[] = {'0', '1', '2', '3', '4', '5', '6',
132 '7', '8', '9', 'a', 'b', 'c', 'd',
133 'e', 'f'};
134
135 if ((n = (d / base)) != 0)
136 outld(n, base);
137 f_putc((int) (chars[(int) (d % base)]), current_fmt.iop);
138 }
139
140 private void
fmt_puts(str)141 fmt_puts(str)
142 char *str;
143 {
144 int len;
145 register char *cp;
146
147 if (str == NULL)
148 str = "(null)";
149 len = strlen(str);
150 if (current_fmt.precision == 0 || len < current_fmt.precision)
151 current_fmt.precision = len;
152 else
153 len = current_fmt.precision;
154 cp = str;
155 if (!current_fmt.leftadj)
156 pad(' ', current_fmt.width - len);
157 while (--current_fmt.precision >= 0)
158 f_putc(*cp++, current_fmt.iop);
159 if (current_fmt.leftadj)
160 pad(' ', current_fmt.width - len);
161 }
162
163 private void
pad(c,amount)164 pad(c, amount)
165 register char c;
166 register int amount;
167 {
168 while (--amount >= 0)
169 f_putc(c, current_fmt.iop);
170 }
171
172 #ifdef ZTCDOS
173 /* ZTCDOS only accepts va_list in a prototype */
174 private void
doformat(register File * sp,register const char * fmt,va_list ap)175 doformat(register File *sp, register const char *fmt, va_list ap)
176 #else
177 private void
178 doformat(sp, fmt, ap)
179 register File *sp;
180 register const char *fmt;
181 va_list ap;
182 #endif
183 {
184 register char c;
185 struct fmt_state prev_fmt;
186
187 prev_fmt = current_fmt;
188 current_fmt.iop = sp;
189
190 while ((c = *fmt++) != '\0') {
191 if (c != '%') {
192 f_putc(c, current_fmt.iop);
193 continue;
194 }
195
196 current_fmt.padc = ' ';
197 current_fmt.precision = current_fmt.width = 0;
198 current_fmt.leftadj = NO;
199 c = *fmt++;
200 if (c == '-') {
201 current_fmt.leftadj = YES;
202 c = *fmt++;
203 }
204 if (c == '0') {
205 current_fmt.padc = '0';
206 c = *fmt++;
207 }
208 while (c >= '0' && c <= '9') {
209 current_fmt.width = current_fmt.width * 10 + (c - '0');
210 c = *fmt++;
211 }
212 if (c == '*') {
213 current_fmt.width = va_arg(ap, int);
214 c = *fmt++;
215 }
216 if (c == '.') {
217 c = *fmt++;
218 while (c >= '0' && c <= '9') {
219 current_fmt.precision = current_fmt.precision * 10 + (c - '0');
220 c = *fmt++;
221 }
222 if (c == '*') {
223 current_fmt.precision = va_arg(ap, int);
224 c = *fmt++;
225 }
226 }
227 reswitch:
228 /* At this point, fmt points at one past the format letter. */
229 switch (c) {
230 case '%':
231 f_putc('%', current_fmt.iop);
232 break;
233
234 case 'O':
235 case 'D':
236 case 'X':
237 putld(va_arg(ap, long), (c == 'O') ? 8 :
238 (c == 'D') ? 10 : 16);
239 break;
240
241 case 'b':
242 {
243 Buffer *b = va_arg(ap, Buffer *);
244
245 fmt_puts(b->b_name);
246 break;
247 }
248
249 case 'c':
250 f_putc(va_arg(ap, DAPchar), current_fmt.iop);
251 break;
252
253 case 'o':
254 case 'd':
255 case 'x':
256 putld((long) va_arg(ap, int), (c == 'o') ? 8 :
257 (c == 'd') ? 10 : 16);
258 break;
259
260 case 'f': /* current command name gets inserted here! */
261 fmt_puts(LastCmd->Name);
262 break;
263
264 case 'l':
265 c = CharUpcase(*++fmt);
266 goto reswitch;
267
268 case 'n':
269 if (va_arg(ap, int) != 1)
270 fmt_puts("s");
271 break;
272
273 case 'p':
274 {
275 ZXchar cc = ZXC(va_arg(ap, DAPchar));
276
277 if (cc == ESC) {
278 fmt_puts("ESC");
279 } else {
280 char cbuf[PPWIDTH];
281
282 PPchar(cc, cbuf);
283 fmt_puts(cbuf);
284 }
285 }
286 break;
287
288 case 's':
289 fmt_puts(va_arg(ap, char *));
290 break;
291
292 default:
293 complain("Unknown format directive: \"%%%c\"", c);
294 }
295 }
296 current_fmt = prev_fmt;
297 }
298
299 #ifdef STDARGS
300 char *
sprint(const char * fmt,...)301 sprint(const char *fmt, ...)
302 #else
303 /*VARARGS1*/ char *
304 sprint(fmt, va_alist)
305 const char *fmt;
306 va_dcl
307 #endif
308 {
309 va_list ap;
310 static char line[LBSIZE];
311
312 va_init(ap, fmt);
313 format(line, sizeof line, fmt, ap);
314 va_end(ap);
315 return line;
316 }
317
318 #ifdef STDARGS
319 void
writef(const char * fmt,...)320 writef(const char *fmt, ...)
321 #else
322 /*VARARGS1*/ void
323 writef(fmt, va_alist)
324 const char *fmt;
325 va_dcl
326 #endif
327 {
328 va_list ap;
329
330 va_init(ap, fmt);
331 #ifdef NO_JSTDOUT
332 /* Can't use sprint because caller might have
333 * passed the result of sprint as an arg.
334 */
335 {
336 char line[100];
337
338 format(line, sizeof(line), fmt, ap);
339 putstr(line);
340 }
341 #else /* !NO_JSTDOUT */
342 doformat(jstdout, fmt, ap);
343 #endif /* !NO_JSTDOUT */
344 va_end(ap);
345 }
346
347 #ifdef STDARGS
348 void
fwritef(File * fp,const char * fmt,...)349 fwritef(File *fp, const char *fmt, ...)
350 #else
351 /*VARARGS2*/ void
352 fwritef(fp, fmt, va_alist)
353 File *fp;
354 const char *fmt;
355 va_dcl
356 #endif
357 {
358 va_list ap;
359
360 va_init(ap, fmt);
361 doformat(fp, fmt, ap);
362 va_end(ap);
363 }
364
365 #ifdef STDARGS
366 void
swritef(char * str,size_t size,const char * fmt,...)367 swritef(char *str, size_t size, const char *fmt, ...)
368 #else
369 /*VARARGS3*/ void
370 swritef(str, size, fmt, va_alist)
371 char *str;
372 size_t size;
373 const char *fmt;
374 va_dcl
375 #endif
376 {
377 va_list ap;
378
379 va_init(ap, fmt);
380 format(str, size, fmt, ap);
381 va_end(ap);
382 }
383
384 /* send a message (supressed if input pending) */
385
386 #ifdef STDARGS
387 void
s_mess(const char * fmt,...)388 s_mess(const char *fmt, ...)
389 #else
390 /*VARARGS1*/ void
391 s_mess(fmt, va_alist)
392 const char *fmt;
393 va_dcl
394 #endif
395 {
396 va_list ap;
397
398 if (InJoverc)
399 return;
400 va_init(ap, fmt);
401 format(mesgbuf, sizeof mesgbuf, fmt, ap);
402 va_end(ap);
403 message(mesgbuf);
404 }
405
406 /* force a message: display it now no matter what.
407 * If you wish it to stick, set stickymsg on after calling f_mess.
408 */
409
410 #ifdef STDARGS
411 void
f_mess(const char * fmt,...)412 f_mess(const char *fmt, ...)
413 #else
414 /*VARARGS1*/ void
415 f_mess(fmt, va_alist)
416 const char *fmt;
417 va_dcl
418 #endif
419 {
420 va_list ap;
421
422 va_init(ap, fmt);
423 format(mesgbuf, sizeof mesgbuf, fmt, ap);
424 va_end(ap);
425 DrawMesg(NO);
426 stickymsg = NO;
427 UpdMesg = YES; /* still needs updating (for convenience) */
428 }
429
430 #ifdef STDARGS
431 void
add_mess(const char * fmt,...)432 add_mess(const char *fmt, ...)
433 #else
434 /*VARARGS1*/ void
435 add_mess(fmt, va_alist)
436 const char *fmt;
437 va_dcl
438 #endif
439 {
440 int mesg_len = strlen(mesgbuf);
441 va_list ap;
442
443 if (InJoverc)
444 return;
445 va_init(ap, fmt);
446 format(&mesgbuf[mesg_len], (sizeof mesgbuf) - mesg_len, fmt, ap);
447 va_end(ap);
448 message(mesgbuf);
449 }
450