4 * Copyright (c) 1997 Ben Harrison
6 * This software may be copied and distributed for educational, research,
7 * and not for profit purposes provided that this copyright and statement
8 * are included in all such copies.
11 /* Purpose: Low level text formatting -BEN- */
19 * Here is some information about the routines in this file.
21 * In general, the following routines take a "buffer", a "max length",
22 * a "format string", and some "arguments", and use the format string
23 * and the arguments to create a (terminated) string in the buffer
24 * (using only the first "max length" bytes), and return the "length"
25 * of the resulting string, not including the (mandatory) terminator.
27 * The format strings allow the basic "sprintf()" format sequences, though
28 * some of them are processed slightly more carefully or portably, as well
29 * as a few "special" sequences, including the "%r" and "%v" sequences, and
30 * the "capilitization" sequences of "%C", "%S", and "%V".
32 * Note that some "limitations" are enforced by the current implementation,
33 * for example, no "format sequence" can exceed 100 characters, including any
34 * "length" restrictions, and the result of combining and "format sequence"
35 * with the relevent "arguments" must not exceed 1000 characters.
37 * These limitations could be fixed by stealing some of the code from,
38 * say, "vsprintf()" and placing it into my "vstrnfmt()" function.
40 * Note that a "^" inside a "format sequence" causes the first non-space
41 * character in the string resulting from the combination of the format
42 * sequence and the argument(s) to be "capitalized" if possible. Note
43 * that the "^" character is removed before the "standard" formatting
44 * routines are called. Likewise, a "*" inside a "format sequence" is
45 * removed from the "format sequence", and replaced by the textual form
46 * of the next argument in the argument list. See examples below.
48 * Legal format characters: %,n,p,c,s,d,i,o,u,X,x,E,e,F,f,G,g,r,v.
51 * Append the literal "%".
54 * Format("%n", int *np)
55 * Save the current length into (*np).
58 * Format("%p", vptr v)
59 * Append the pointer "v" (implementation varies).
62 * Format("%E", double r)
63 * Format("%F", double r)
64 * Format("%G", double r)
65 * Format("%e", double r)
66 * Format("%f", double r)
67 * Format("%g", double r)
68 * Append the double "r", in various formats.
70 * Format("%ld", long int i)
71 * Append the long integer "i".
74 * Append the integer "i".
76 * Format("%lu", unsigned long int i)
77 * Append the unsigned long integer "i".
79 * Format("%u", unsigned int i)
80 * Append the unsigned integer "i".
82 * Format("%lo", unsigned long int i)
83 * Append the unsigned long integer "i", in octal.
85 * Format("%o", unsigned int i)
86 * Append the unsigned integer "i", in octal.
88 * Format("%lX", unsigned long int i)
89 * Note -- use all capital letters
90 * Format("%lx", unsigned long int i)
91 * Append the unsigned long integer "i", in hexidecimal.
93 * Format("%X", unsigned int i)
94 * Note -- use all capital letters
95 * Format("%x", unsigned int i)
96 * Append the unsigned integer "i", in hexidecimal.
98 * Format("%c", char c)
99 * Append the character "c".
100 * Do not use the "+" or "0" flags.
102 * Format("%s", concptr s)
103 * Append the string "s".
104 * Do not use the "+" or "0" flags.
105 * Note that a "NULL" value of "s" is converted to the empty string.
107 * Format("%V", vptr v)
108 * Note -- possibly significant mode flag
109 * Format("%v", vptr v)
110 * Append the object "v", using the current "user defined print routine".
111 * User specified modifiers, often ignored.
113 * Format("%r", vstrnfmt_aux_func *fp)
114 * Set the "user defined print routine" (vstrnfmt_aux) to "fp".
115 * No legal modifiers.
118 * For examples below, assume "int n = 0; int m = 100; char buf[100];",
119 * plus "char *s = NULL;", and unknown values "char *txt; int i;".
121 * For example: "n = strnfmt(buf, -1, "(Max %d)", i);" will have a
122 * similar effect as "sprintf(buf, "(Max %d)", i); n = strlen(buf);".
124 * For example: "(void)strnfmt(buf, 16, "%s", txt);" will have a similar
125 * effect as "strncpy(buf, txt, 16); buf[15] = '\0';".
127 * For example: "if (strnfmt(buf, 16, "%s", txt) < 16) ..." will have
128 * a similar effect as "strcpy(buf, txt)" but with bounds checking.
130 * For example: "s = buf; s += vstrnfmt(s, -1, ...); ..." will allow
131 * multiple "appends" to "buf" (at the cost of losing the max-length info).
133 * For example: "s = buf; n = vstrnfmt(s+n, 100-n, ...); ..." will allow
134 * multiple bounded "appends" to "buf", with constant access to "strlen(buf)".
136 * For example: "format("The %r%v was destroyed!", obj_desc, obj);"
137 * (where "obj_desc(buf, max, fmt, obj)" will "append" a "description"
138 * of the given object to the given buffer, and return the total length)
139 * will return a "useful message" about the object "obj", for example,
140 * "The Large Shield was destroyed!".
142 * For example: "format("%^-.*s", i, txt)" will produce a string containing
143 * the first "i" characters of "txt", left justified, with the first non-space
144 * character capitilized, if reasonable.
152 * The "type" of the "user defined print routine" pointer
154 typedef uint (*vstrnfmt_aux_func)(char *buf, uint max, concptr fmt, vptr arg);
157 * The "default" user defined print routine. Ignore the "fmt" string.
159 static uint vstrnfmt_aux_dflt(char *buf, uint max, concptr fmt, vptr arg)
167 /* Pointer display */
168 sprintf(tmp, "<<%p>>", arg);
170 if (len >= max) len = max - 1;
177 * The "current" user defined print routine. It can be changed
178 * dynamically by sending the proper "%r" sequence to "vstrnfmt()"
180 static vstrnfmt_aux_func vstrnfmt_aux = vstrnfmt_aux_dflt;
185 * Basic "vararg" format function.
187 * This function takes a buffer, a max byte count, a format string, and
188 * a va_list of arguments to the format string, and uses the format string
189 * and the arguments to create a string to the buffer. The string is
190 * derived from the format string and the arguments in the manner of the
191 * "sprintf()" function, but with some extra "format" commands. Note that
192 * this function will never use more than the given number of bytes in the
193 * buffer, preventing messy invalid memory references. This function then
194 * returns the total number of non-null bytes written into the buffer.
196 * Method: Let "str" be the (unlimited) created string, and let "len" be the
197 * smaller of "max-1" and "strlen(str)". We copy the first "len" chars of
198 * "str" into "buf", place "\0" into buf[len], and return "len".
200 * In English, we do a sprintf() into "buf", a buffer with size "max",
201 * and we return the resulting value of "strlen(buf)", but we allow some
202 * special format commands, and we are more careful than "sprintf()".
204 * Typically, "max" is in fact the "size" of "buf", and thus represents
205 * the "number" of chars in "buf" which are ALLOWED to be used. An
206 * alternative definition would have required "buf" to hold at least
207 * "max+1" characters, and would have used that extra character only
208 * in the case where "buf" was too short for the result. This would
209 * give an easy test for "overflow", but a less "obvious" semantics.
211 * Note that if the buffer was "too short" to hold the result, we will
212 * always return "max-1", but we also return "max-1" if the buffer was
213 * "just long enough". We could have returned "max" if the buffer was
214 * too short, not written a null, and forced the programmer to deal with
215 * this special case, but I felt that it is better to at least give a
216 * "usable" result when the buffer was too long instead of either giving
217 * a memory overwrite like "sprintf()" or a non-terminted string like
218 * "strncpy()". Note that "strncpy()" also "null-pads" the result.
220 * Note that in most cases "just long enough" is probably "too short".
222 * We should also consider extracting and processing the "width" and other
223 * "flags" by hand, it might be more "accurate", and it would allow us to
224 * remove the limit (1000 chars) on the result of format sequences.
226 * Also, some sequences, such as "%+d" by hand, do not work on all machines,
227 * and could thus be correctly handled here.
229 * Error detection in this routine is not very graceful, in particular,
230 * if an error is detected in the format string, we simply "pre-terminate"
231 * the given buffer to a length of zero, and return a "length" of zero.
232 * The contents of "buf", except for "buf[0]", may then be undefined.
234 uint vstrnfmt(char *buf, uint max, concptr fmt, va_list vp)
238 /* The argument is "long" */
241 /* The argument needs "processing" */
244 /* Bytes used in buffer */
247 /* Bytes used in format sequence */
250 /* Format sequence */
253 /* Resulting string */
257 /* Mega-Hack -- treat "illegal" length as "infinite" */
258 if (!max) max = 32767;
260 /* Mega-Hack -- treat "no format" as "empty string" */
264 /* Begin the buffer */
267 /* Begin the format string */
270 /* Scan the format string */
276 /* Normal character */
279 /* Check total length */
280 if (n == max-1) break;
282 /* Save the character */
287 /* Skip the "percent" */
290 /* Pre-process "%%" */
293 /* Check total length */
294 if (n == max-1) break;
296 /* Save the percent */
304 /* Pre-process "%n" */
309 /* Access the next argument */
310 arg = va_arg(vp, int *);
312 /* Save the current length */
320 /* Hack -- Pre-process "%r" */
323 /* Extract the next argument, and save it (globally) */
324 vstrnfmt_aux = va_arg(vp, vstrnfmt_aux_func);
332 /* Begin the "aux" string */
335 /* Save the "percent" */
338 /* Assume no "long" argument */
341 /* Assume no "xtra" processing */
344 /* Build the "aux" string */
347 /* Error -- format sequence is not terminated */
350 /* Terminate the buffer */
357 /* Error -- format sequence may be too long */
360 /* Terminate the buffer */
367 /* Handle "alphabetic" chars */
370 /* Hack -- handle "long" request */
373 /* Save the character */
376 /* Note the "long" flag */
380 /* Mega-Hack -- handle "extra-long" request */
383 /* Error -- illegal format char */
390 /* Handle normal end of format sequence */
393 /* Save the character */
396 /* Stop processing the format sequence */
401 /* Handle "non-alphabetic" chars */
404 /* Hack -- Handle 'star' (for "variable length" argument) */
409 /* Access the next argument */
410 arg = va_arg(vp, int);
412 /* Hack -- append the "length" */
413 sprintf(aux + q, "%d", arg);
415 /* Hack -- accept the "length" */
422 /* Mega-Hack -- Handle 'caret' (for "uppercase" request) */
425 /* Note the "xtra" flag */
432 /* Collect "normal" characters (digits, "-", "+", ".", etc) */
435 /* Save the character */
442 /* Terminate "aux" */
448 /* Process the "format" char */
451 /* Simple Character -- standard format */
456 /* Access next argument */
457 arg = va_arg(vp, int);
459 /* Format the argument */
460 sprintf(tmp, "%c", arg);
465 /* Signed Integers -- standard format */
472 /* Access next argument */
473 arg = va_arg(vp, long);
475 /* Format the argument */
476 sprintf(tmp, aux, arg);
482 /* Access next argument */
483 arg = va_arg(vp, int);
485 /* Format the argument */
486 sprintf(tmp, aux, arg);
492 /* Unsigned Integers -- various formats */
493 case 'u': case 'o': case 'x': case 'X':
499 /* Access next argument */
500 arg = va_arg(vp, unsigned long);
502 sprintf(tmp, aux, arg);
508 /* Access next argument */
509 arg = va_arg(vp, unsigned int);
510 sprintf(tmp, aux, arg);
517 /* Floating Point -- various formats */
524 /* Access next argument */
525 arg = va_arg(vp, double);
527 /* Format the argument */
528 sprintf(tmp, aux, arg);
533 /* Pointer -- implementation varies */
538 /* Access next argument */
539 arg = va_arg(vp, vptr);
541 /* Format the argument */
542 sprintf(tmp, aux, arg);
553 /* Access next argument */
554 arg = va_arg(vp, concptr);
556 /* Hack -- convert NULL to EMPTY */
559 /* Prevent buffer overflows */
560 strncpy(arg2, arg, 1024);
563 /* Format the argument */
564 sprintf(tmp, aux, arg);
569 /* User defined data */
575 /* Access next argument */
576 arg = va_arg(vp, vptr);
578 /* Format the "user data" */
579 sprintf(tmp, aux, arg);
587 /* Error -- illegal format char */
597 for (q = 0; tmp[q]; q++) if (iskanji(tmp[q])) { do_xtra=FALSE;break;}
599 /* Mega-Hack -- handle "capitilization" */
602 /* Now append "tmp" to "buf" */
603 for (q = 0; tmp[q]; q++)
605 /* Notice first non-space */
606 if (!iswspace(tmp[q]))
608 /* Capitalize if possible */
610 tmp[q] = (char)toupper(tmp[q]);
617 /* Now append "tmp" to "buf" */
618 for (q = 0; tmp[q]; q++)
620 /* Check total length */
621 if (n == max-1) break;
623 /* Save the character */
629 /* Terminate buffer */
638 * Do a vstrnfmt (see above) into a (growable) static buffer.
639 * This buffer is usable for very short term formatting of results.
641 char *vformat(concptr fmt, va_list vp)
643 static char *format_buf = NULL;
644 static huge format_len = 0;
646 /* Initial allocation */
650 C_MAKE(format_buf, format_len, char);
653 /* Null format yields last result */
654 if (!fmt) return (format_buf);
656 /* Keep going until successful */
661 /* Build the string */
662 len = vstrnfmt(format_buf, format_len, fmt, vp);
665 if (len < format_len-1) break;
667 /* Grow the buffer */
668 C_KILL(format_buf, format_len, char);
669 format_len = format_len * 2;
670 C_MAKE(format_buf, format_len, char);
673 /* Return the new buffer */
680 * Do a vstrnfmt (see above) into a buffer of a given size.
682 uint strnfmt(char *buf, uint max, concptr fmt, ...)
688 /* Begin the Varargs Stuff */
691 /* Do a virtual fprintf to stderr */
692 len = vstrnfmt(buf, max, fmt, vp);
694 /* End the Varargs Stuff */
697 /* Return the number of bytes written */
703 * Do a vstrnfmt (see above) into a buffer of unknown size.
704 * Since the buffer size is unknown, the user better verify the args.
706 uint strfmt(char *buf, concptr fmt, ...)
712 /* Begin the Varargs Stuff */
715 /* Build the string, assume 32K buffer */
716 len = vstrnfmt(buf, 32767, fmt, vp);
718 /* End the Varargs Stuff */
721 /* Return the number of bytes written */
729 * Do a vstrnfmt() into (see above) into a (growable) static buffer.
730 * This buffer is usable for very short term formatting of results.
731 * Note that the buffer is (technically) writable, but only up to
732 * the length of the string contained inside it.
734 char *format(concptr fmt, ...)
739 /* Begin the Varargs Stuff */
742 /* Format the args */
743 res = vformat(fmt, vp);
745 /* End the Varargs Stuff */
748 /* Return the result */
756 * Vararg interface to plog()
758 void plog_fmt(concptr fmt, ...)
763 /* Begin the Varargs Stuff */
766 /* Format the args */
767 res = vformat(fmt, vp);
769 /* End the Varargs Stuff */
779 * Vararg interface to quit()
781 void quit_fmt(concptr fmt, ...)
786 /* Begin the Varargs Stuff */
790 res = vformat(fmt, vp);
792 /* End the Varargs Stuff */
802 * Vararg interface to core()
804 void core_fmt(concptr fmt, ...)
809 /* Begin the Varargs Stuff */
812 /* If requested, Do a virtual fprintf to stderr */
813 res = vformat(fmt, vp);
815 /* End the Varargs Stuff */