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