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- */
20 * Here is some information about the routines in this file.
22 * In general, the following routines take a "buffer", a "max length",
23 * a "format string", and some "arguments", and use the format string
24 * and the arguments to create a (terminated) string in the buffer
25 * (using only the first "max length" bytes), and return the "length"
26 * of the resulting string, not including the (mandatory) terminator.
28 * The format strings allow the basic "sprintf()" format sequences, though
29 * some of them are processed slightly more carefully or portably, as well
30 * as a few "special" sequences, including the "%r" and "%v" sequences, and
31 * the "capilitization" sequences of "%C", "%S", and "%V".
33 * Note that some "limitations" are enforced by the current implementation,
34 * for example, no "format sequence" can exceed 100 characters, including any
35 * "length" restrictions, and the result of combining and "format sequence"
36 * with the relevent "arguments" must not exceed 1000 characters.
38 * These limitations could be fixed by stealing some of the code from,
39 * say, "vsprintf()" and placing it into my "vstrnfmt()" function.
41 * Note that a "^" inside a "format sequence" causes the first non-space
42 * character in the string resulting from the combination of the format
43 * sequence and the argument(s) to be "capitalized" if possible. Note
44 * that the "^" character is removed before the "standard" formatting
45 * routines are called. Likewise, a "*" inside a "format sequence" is
46 * removed from the "format sequence", and replaced by the textual form
47 * of the next argument in the argument list. See examples below.
49 * Legal format characters: %,n,p,c,s,d,i,o,u,X,x,E,e,F,f,G,g,r,v.
52 * Append the literal "%".
55 * Format("%n", int *np)
56 * Save the current length into (*np).
59 * Format("%p", vptr v)
60 * Append the pointer "v" (implementation varies).
63 * Format("%E", double r)
64 * Format("%F", double r)
65 * Format("%G", double r)
66 * Format("%e", double r)
67 * Format("%f", double r)
68 * Format("%g", double r)
69 * Append the double "r", in various formats.
71 * Format("%ld", long int i)
72 * Append the long integer "i".
75 * Append the integer "i".
77 * Format("%lu", unsigned long int i)
78 * Append the unsigned long integer "i".
80 * Format("%u", unsigned int i)
81 * Append the unsigned integer "i".
83 * Format("%lo", unsigned long int i)
84 * Append the unsigned long integer "i", in octal.
86 * Format("%o", unsigned int i)
87 * Append the unsigned integer "i", in octal.
89 * Format("%lX", unsigned long int i)
90 * Note -- use all capital letters
91 * Format("%lx", unsigned long int i)
92 * Append the unsigned long integer "i", in hexidecimal.
94 * Format("%X", unsigned int i)
95 * Note -- use all capital letters
96 * Format("%x", unsigned int i)
97 * Append the unsigned integer "i", in hexidecimal.
99 * Format("%c", char c)
100 * Append the character "c".
101 * Do not use the "+" or "0" flags.
103 * Format("%s", cptr s)
104 * Append the string "s".
105 * Do not use the "+" or "0" flags.
106 * Note that a "NULL" value of "s" is converted to the empty string.
108 * Format("%V", vptr v)
109 * Note -- possibly significant mode flag
110 * Format("%v", vptr v)
111 * Append the object "v", using the current "user defined print routine".
112 * User specified modifiers, often ignored.
114 * Format("%r", vstrnfmt_aux_func *fp)
115 * Set the "user defined print routine" (vstrnfmt_aux) to "fp".
116 * No legal modifiers.
119 * For examples below, assume "int n = 0; int m = 100; char buf[100];",
120 * plus "char *s = NULL;", and unknown values "char *txt; int i;".
122 * For example: "n = strnfmt(buf, -1, "(Max %d)", i);" will have a
123 * similar effect as "sprintf(buf, "(Max %d)", i); n = strlen(buf);".
125 * For example: "(void)strnfmt(buf, 16, "%s", txt);" will have a similar
126 * effect as "strncpy(buf, txt, 16); buf[15] = '\0';".
128 * For example: "if (strnfmt(buf, 16, "%s", txt) < 16) ..." will have
129 * a similar effect as "strcpy(buf, txt)" but with bounds checking.
131 * For example: "s = buf; s += vstrnfmt(s, -1, ...); ..." will allow
132 * multiple "appends" to "buf" (at the cost of losing the max-length info).
134 * For example: "s = buf; n = vstrnfmt(s+n, 100-n, ...); ..." will allow
135 * multiple bounded "appends" to "buf", with constant access to "strlen(buf)".
137 * For example: "format("The %r%v was destroyed!", obj_desc, obj);"
138 * (where "obj_desc(buf, max, fmt, obj)" will "append" a "description"
139 * of the given object to the given buffer, and return the total length)
140 * will return a "useful message" about the object "obj", for example,
141 * "The Large Shield was destroyed!".
143 * For example: "format("%^-.*s", i, txt)" will produce a string containing
144 * the first "i" characters of "txt", left justified, with the first non-space
145 * character capitilized, if reasonable.
153 * The "type" of the "user defined print routine" pointer
155 typedef uint (*vstrnfmt_aux_func)(char *buf, uint max, cptr fmt, vptr arg);
158 * The "default" user defined print routine. Ignore the "fmt" string.
160 static uint vstrnfmt_aux_dflt(char *buf, uint max, cptr fmt, vptr arg)
168 /* Pointer display */
169 sprintf(tmp, "<<%p>>", arg);
171 if (len >= max) len = max - 1;
178 * The "current" user defined print routine. It can be changed
179 * dynamically by sending the proper "%r" sequence to "vstrnfmt()"
181 static vstrnfmt_aux_func vstrnfmt_aux = vstrnfmt_aux_dflt;
186 * Basic "vararg" format function.
188 * This function takes a buffer, a max byte count, a format string, and
189 * a va_list of arguments to the format string, and uses the format string
190 * and the arguments to create a string to the buffer. The string is
191 * derived from the format string and the arguments in the manner of the
192 * "sprintf()" function, but with some extra "format" commands. Note that
193 * this function will never use more than the given number of bytes in the
194 * buffer, preventing messy invalid memory references. This function then
195 * returns the total number of non-null bytes written into the buffer.
197 * Method: Let "str" be the (unlimited) created string, and let "len" be the
198 * smaller of "max-1" and "strlen(str)". We copy the first "len" chars of
199 * "str" into "buf", place "\0" into buf[len], and return "len".
201 * In English, we do a sprintf() into "buf", a buffer with size "max",
202 * and we return the resulting value of "strlen(buf)", but we allow some
203 * special format commands, and we are more careful than "sprintf()".
205 * Typically, "max" is in fact the "size" of "buf", and thus represents
206 * the "number" of chars in "buf" which are ALLOWED to be used. An
207 * alternative definition would have required "buf" to hold at least
208 * "max+1" characters, and would have used that extra character only
209 * in the case where "buf" was too short for the result. This would
210 * give an easy test for "overflow", but a less "obvious" semantics.
212 * Note that if the buffer was "too short" to hold the result, we will
213 * always return "max-1", but we also return "max-1" if the buffer was
214 * "just long enough". We could have returned "max" if the buffer was
215 * too short, not written a null, and forced the programmer to deal with
216 * this special case, but I felt that it is better to at least give a
217 * "usable" result when the buffer was too long instead of either giving
218 * a memory overwrite like "sprintf()" or a non-terminted string like
219 * "strncpy()". Note that "strncpy()" also "null-pads" the result.
221 * Note that in most cases "just long enough" is probably "too short".
223 * We should also consider extracting and processing the "width" and other
224 * "flags" by hand, it might be more "accurate", and it would allow us to
225 * remove the limit (1000 chars) on the result of format sequences.
227 * Also, some sequences, such as "%+d" by hand, do not work on all machines,
228 * and could thus be correctly handled here.
230 * Error detection in this routine is not very graceful, in particular,
231 * if an error is detected in the format string, we simply "pre-terminate"
232 * the given buffer to a length of zero, and return a "length" of zero.
233 * The contents of "buf", except for "buf[0]", may then be undefined.
235 uint vstrnfmt(char *buf, uint max, cptr fmt, va_list vp)
239 /* The argument is "long" */
242 /* The argument needs "processing" */
245 /* Bytes used in buffer */
248 /* Bytes used in format sequence */
251 /* Format sequence */
254 /* Resulting string */
258 /* Mega-Hack -- treat "illegal" length as "infinite" */
259 if (!max) max = 32767;
261 /* Mega-Hack -- treat "no format" as "empty string" */
265 /* Begin the buffer */
268 /* Begin the format string */
271 /* Scan the format string */
277 /* Normal character */
280 /* Check total length */
281 if (n == max-1) break;
283 /* Save the character */
290 /* Skip the "percent" */
293 /* Pre-process "%%" */
296 /* Check total length */
297 if (n == max-1) break;
299 /* Save the percent */
309 /* Pre-process "%n" */
314 /* Access the next argument */
315 arg = va_arg(vp, int *);
317 /* Save the current length */
327 /* Hack -- Pre-process "%r" */
330 /* Extract the next argument, and save it (globally) */
331 vstrnfmt_aux = va_arg(vp, vstrnfmt_aux_func);
341 /* Begin the "aux" string */
344 /* Save the "percent" */
347 /* Assume no "long" argument */
350 /* Assume no "xtra" processing */
353 /* Build the "aux" string */
356 /* Error -- format sequence is not terminated */
359 /* Terminate the buffer */
366 /* Error -- format sequence may be too long */
369 /* Terminate the buffer */
376 /* Handle "alphabetic" chars */
379 /* Hack -- handle "long" request */
382 /* Save the character */
385 /* Note the "long" flag */
389 /* Mega-Hack -- handle "extra-long" request */
392 /* Error -- illegal format char */
399 /* Handle normal end of format sequence */
402 /* Save the character */
405 /* Stop processing the format sequence */
410 /* Handle "non-alphabetic" chars */
413 /* Hack -- Handle 'star' (for "variable length" argument) */
418 /* Access the next argument */
419 arg = va_arg(vp, int);
421 /* Hack -- append the "length" */
422 sprintf(aux + q, "%d", arg);
424 /* Hack -- accept the "length" */
431 /* Mega-Hack -- Handle 'caret' (for "uppercase" request) */
434 /* Note the "xtra" flag */
441 /* Collect "normal" characters (digits, "-", "+", ".", etc) */
444 /* Save the character */
451 /* Terminate "aux" */
457 /* Process the "format" char */
460 /* Simple Character -- standard format */
465 /* Access next argument */
466 arg = va_arg(vp, int);
468 /* Format the argument */
469 sprintf(tmp, "%c", arg);
474 /* Signed Integers -- standard format */
481 /* Access next argument */
482 arg = va_arg(vp, long);
484 /* Format the argument */
485 sprintf(tmp, aux, arg);
491 /* Access next argument */
492 arg = va_arg(vp, int);
494 /* Format the argument */
495 sprintf(tmp, aux, arg);
501 /* Unsigned Integers -- various formats */
502 case 'u': case 'o': case 'x': case 'X':
508 /* Access next argument */
509 arg = va_arg(vp, unsigned long);
511 sprintf(tmp, aux, arg);
517 /* Access next argument */
518 arg = va_arg(vp, unsigned int);
519 sprintf(tmp, aux, arg);
526 /* Floating Point -- various formats */
533 /* Access next argument */
534 arg = va_arg(vp, double);
536 /* Format the argument */
537 sprintf(tmp, aux, arg);
542 /* Pointer -- implementation varies */
547 /* Access next argument */
548 arg = va_arg(vp, vptr);
550 /* Format the argument */
551 sprintf(tmp, aux, arg);
562 /* Access next argument */
563 arg = va_arg(vp, cptr);
565 /* Hack -- convert NULL to EMPTY */
568 /* Prevent buffer overflows */
569 strncpy(arg2, arg, 1024);
572 /* Format the argument */
573 sprintf(tmp, aux, arg);
578 /* User defined data */
584 /* Access next argument */
585 arg = va_arg(vp, vptr);
587 /* Format the "user data" */
588 sprintf(tmp, aux, arg);
596 /* Error -- illegal format char */
606 for (q = 0; tmp[q]; q++) if (iskanji(tmp[q])) { do_xtra=FALSE;break;}
608 /* Mega-Hack -- handle "capitilization" */
611 /* Now append "tmp" to "buf" */
612 for (q = 0; tmp[q]; q++)
614 /* Notice first non-space */
615 if (!iswspace(tmp[q]))
617 /* Capitalize if possible */
619 tmp[q] = (char)toupper(tmp[q]);
626 /* Now append "tmp" to "buf" */
627 for (q = 0; tmp[q]; q++)
629 /* Check total length */
630 if (n == max-1) break;
632 /* Save the character */
638 /* Terminate buffer */
647 * Do a vstrnfmt (see above) into a (growable) static buffer.
648 * This buffer is usable for very short term formatting of results.
650 char *vformat(cptr fmt, va_list vp)
652 static char *format_buf = NULL;
653 static huge format_len = 0;
655 /* Initial allocation */
659 C_MAKE(format_buf, format_len, char);
662 /* Null format yields last result */
663 if (!fmt) return (format_buf);
665 /* Keep going until successful */
670 /* Build the string */
671 len = vstrnfmt(format_buf, format_len, fmt, vp);
674 if (len < format_len-1) break;
676 /* Grow the buffer */
677 C_KILL(format_buf, format_len, char);
678 format_len = format_len * 2;
679 C_MAKE(format_buf, format_len, char);
682 /* Return the new buffer */
689 * Do a vstrnfmt (see above) into a buffer of a given size.
691 uint strnfmt(char *buf, uint max, cptr fmt, ...)
697 /* Begin the Varargs Stuff */
700 /* Do a virtual fprintf to stderr */
701 len = vstrnfmt(buf, max, fmt, vp);
703 /* End the Varargs Stuff */
706 /* Return the number of bytes written */
712 * Do a vstrnfmt (see above) into a buffer of unknown size.
713 * Since the buffer size is unknown, the user better verify the args.
715 uint strfmt(char *buf, cptr fmt, ...)
721 /* Begin the Varargs Stuff */
724 /* Build the string, assume 32K buffer */
725 len = vstrnfmt(buf, 32767, fmt, vp);
727 /* End the Varargs Stuff */
730 /* Return the number of bytes written */
738 * Do a vstrnfmt() into (see above) into a (growable) static buffer.
739 * This buffer is usable for very short term formatting of results.
740 * Note that the buffer is (technically) writable, but only up to
741 * the length of the string contained inside it.
743 char *format(cptr fmt, ...)
748 /* Begin the Varargs Stuff */
751 /* Format the args */
752 res = vformat(fmt, vp);
754 /* End the Varargs Stuff */
757 /* Return the result */
765 * Vararg interface to plog()
767 void plog_fmt(cptr fmt, ...)
772 /* Begin the Varargs Stuff */
775 /* Format the args */
776 res = vformat(fmt, vp);
778 /* End the Varargs Stuff */
788 * Vararg interface to quit()
790 void quit_fmt(cptr fmt, ...)
795 /* Begin the Varargs Stuff */
799 res = vformat(fmt, vp);
801 /* End the Varargs Stuff */
811 * Vararg interface to core()
813 void core_fmt(cptr fmt, ...)
818 /* Begin the Varargs Stuff */
821 /* If requested, Do a virtual fprintf to stderr */
822 res = vformat(fmt, vp);
824 /* End the Varargs Stuff */