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- */
13 #include "term/z-form.h"
14 #include "term/z-util.h"
15 #include "term/z-virt.h"
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".
73 * Format("%Ld", long long int i)
74 * Append the long long integer "i".
77 * Append the integer "i".
79 * Format("%lu", unsigned long int i)
80 * Append the unsigned long integer "i".
82 * Format("%Lu", unsigned long long int i)
83 * Append the unsigned long long integer "i".
85 * Format("%u", unsigned int i)
86 * Append the unsigned integer "i".
88 * Format("%lo", unsigned long int i)
89 * Append the unsigned long integer "i", in octal.
91 * Format("%o", unsigned int i)
92 * Append the unsigned integer "i", in octal.
94 * Format("%lX", unsigned long int i)
95 * Note -- use all capital letters
96 * Format("%lx", unsigned long int i)
97 * Append the unsigned long integer "i", in hexidecimal.
99 * Format("%X", unsigned int i)
100 * Note -- use all capital letters
101 * Format("%x", unsigned int i)
102 * Append the unsigned integer "i", in hexidecimal.
104 * Format("%c", char c)
105 * Append the character "c".
106 * Do not use the "+" or "0" flags.
108 * Format("%s", concptr s)
109 * Append the string "s".
110 * Do not use the "+" or "0" flags.
111 * Note that a "nullptr" value of "s" is converted to the empty string.
113 * Format("%V", vptr v)
114 * Note -- possibly significant mode flag
115 * Format("%v", vptr v)
116 * Append the object "v", using the current "user defined print routine".
117 * User specified modifiers, often ignored.
119 * Format("%r", vstrnfmt_aux_func *fp)
120 * Set the "user defined print routine" (vstrnfmt_aux) to "fp".
121 * No legal modifiers.
124 * For examples below, assume "int n = 0; int m = 100; char buf[100];",
125 * plus "char *s = nullptr;", and unknown values "char *txt; int i;".
127 * For example: "n = strnfmt(buf, -1, "(Max %d)", i);" will have a
128 * similar effect as "sprintf(buf, "(Max %d)", i); n = strlen(buf);".
130 * For example: "(void)strnfmt(buf, 16, "%s", txt);" will have a similar
131 * effect as "strncpy(buf, txt, 16); buf[15] = '\0';".
133 * For example: "if (strnfmt(buf, 16, "%s", txt) < 16) ..." will have
134 * a similar effect as "strcpy(buf, txt)" but with bounds checking.
136 * For example: "s = buf; s += vstrnfmt(s, -1, ...); ..." will allow
137 * multiple "appends" to "buf" (at the cost of losing the max-length info).
139 * For example: "s = buf; n = vstrnfmt(s+n, 100-n, ...); ..." will allow
140 * multiple bounded "appends" to "buf", with constant access to "strlen(buf)".
142 * For example: "format("The %r%v was destroyed!", obj_desc, obj);"
143 * (where "obj_desc(buf, max, fmt, obj)" will "append" a "description"
144 * of the given object to the given buffer, and return the total length)
145 * will return a "useful message" about the object "obj", for example,
146 * "The Large Shield was destroyed!".
148 * For example: "format("%^-.*s", i, txt)" will produce a string containing
149 * the first "i" characters of "txt", left justified, with the first non-space
150 * character capitilized, if reasonable.
154 * The "type" of the "user defined print routine" pointer
156 typedef uint (*vstrnfmt_aux_func)(char *buf, uint max, concptr fmt, vptr arg);
159 * The "default" user defined print routine. Ignore the "fmt" string.
161 static uint vstrnfmt_aux_dflt(char *buf, uint max, concptr fmt, vptr arg)
169 /* Pointer display */
170 sprintf(tmp, "<<%p>>", arg);
181 * The "current" user defined print routine. It can be changed
182 * dynamically by sending the proper "%r" sequence to "vstrnfmt()"
184 static vstrnfmt_aux_func vstrnfmt_aux = vstrnfmt_aux_dflt;
187 * Basic "vararg" format function.
189 * This function takes a buffer, a max byte count, a format string, and
190 * a va_list of arguments to the format string, and uses the format string
191 * and the arguments to create a string to the buffer. The string is
192 * derived from the format string and the arguments in the manner of the
193 * "sprintf()" function, but with some extra "format" commands. Note that
194 * this function will never use more than the given number of bytes in the
195 * buffer, preventing messy invalid memory references. This function then
196 * returns the total number of non-null bytes written into the buffer.
198 * Method: Let "str" be the (unlimited) created string, and let "len" be the
199 * smaller of "max-1" and "strlen(str)". We copy the first "len" chars of
200 * "str" into "buf", place "\0" into buf[len], and return "len".
202 * In English, we do a sprintf() into "buf", a buffer with size "max",
203 * and we return the resulting value of "strlen(buf)", but we allow some
204 * special format commands, and we are more careful than "sprintf()".
206 * Typically, "max" is in fact the "size" of "buf", and thus represents
207 * the "number" of chars in "buf" which are ALLOWED to be used. An
208 * alternative definition would have required "buf" to hold at least
209 * "max+1" characters, and would have used that extra character only
210 * in the case where "buf" was too short for the result. This would
211 * give an easy test for "overflow", but a less "obvious" semantics.
213 * Note that if the buffer was "too short" to hold the result, we will
214 * always return "max-1", but we also return "max-1" if the buffer was
215 * "just long enough". We could have returned "max" if the buffer was
216 * too short, not written a null, and forced the programmer to deal with
217 * this special case, but I felt that it is better to at least give a
218 * "usable" result when the buffer was too long instead of either giving
219 * a memory overwrite like "sprintf()" or a non-terminted string like
220 * "strncpy()". Note that "strncpy()" also "null-pads" the result.
222 * Note that in most cases "just long enough" is probably "too short".
224 * We should also consider extracting and processing the "width" and other
225 * "flags" by hand, it might be more "accurate", and it would allow us to
226 * remove the limit (1000 chars) on the result of format sequences.
228 * Also, some sequences, such as "%+d" by hand, do not work on all machines,
229 * and could thus be correctly handled here.
231 * Error detection in this routine is not very graceful, in particular,
232 * if an error is detected in the format string, we simply "pre-terminate"
233 * the given buffer to a length of zero, and return a "length" of zero.
234 * The contents of "buf", except for "buf[0]", may then be undefined.
236 uint vstrnfmt(char *buf, uint max, concptr fmt, va_list vp)
240 /* The argument is "long" */
243 /* The argument is "long long" */
246 /* The argument needs "processing" */
249 /* Bytes used in buffer */
252 /* Bytes used in format sequence */
255 /* Format sequence */
258 /* Resulting string */
261 /* Mega-Hack -- treat "illegal" length as "infinite" */
266 /* Mega-Hack -- treat "no format" as "empty string" */
271 /* Begin the buffer */
274 /* Begin the format string */
277 /* Scan the format string */
284 /* Normal character */
286 /* Check total length */
291 /* Save the character */
296 /* Skip the "percent" */
299 /* Pre-process "%%" */
301 /* Check total length */
306 /* Save the percent */
314 /* Pre-process "%n" */
318 /* Access the next argument */
319 arg = va_arg(vp, int *);
321 /* Save the current length */
329 /* Hack -- Pre-process "%r" */
331 /* Extract the next argument, and save it (globally) */
332 vstrnfmt_aux = va_arg(vp, vstrnfmt_aux_func);
339 /* Begin the "aux" string */
342 /* Save the "percent" */
345 /* Assume no "long" argument */
348 /* Assume no "long long" argument */
349 do_long_long = false;
351 /* Assume no "xtra" processing */
354 /* Build the "aux" string */
356 /* Error -- format sequence is not terminated */
358 /* Terminate the buffer */
365 /* Error -- format sequence may be too long */
367 /* Terminate the buffer */
374 /* Handle "alphabetic" chars */
376 /* Hack -- handle "long" request */
378 /* Save the character */
381 /* Note the "long" flag */
385 /* Mega-Hack -- handle "extra-long" request */
386 else if (*s == 'L') {
387 /* Save the character */
392 /* Note the "long long" flag */
396 /* Handle normal end of format sequence */
398 /* Save the character */
401 /* Stop processing the format sequence */
406 /* Handle "non-alphabetic" chars */
408 /* Hack -- Handle 'star' (for "variable length" argument) */
412 /* Access the next argument */
413 arg = va_arg(vp, int);
415 /* Hack -- append the "length" */
416 sprintf(aux + q, "%d", arg);
418 /* Hack -- accept the "length" */
427 /* Mega-Hack -- Handle 'caret' (for "uppercase" request) */
428 else if (*s == '^') {
429 /* Note the "xtra" flag */
436 /* Collect "normal" characters (digits, "-", "+", ".", etc) */
438 /* Save the character */
444 /* Terminate "aux" */
450 /* Process the "format" char */
451 switch (aux[q - 1]) {
452 /* 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 */
471 /* Access next argument */
472 arg = va_arg(vp, long);
474 /* Format the argument */
475 sprintf(tmp, aux, arg);
476 } else if (do_long_long) {
479 /* Access next argument */
480 arg = va_arg(vp, long long);
482 /* Format the argument */
483 sprintf(tmp, aux, arg);
487 /* Access next argument */
488 arg = va_arg(vp, int);
490 /* Format the argument */
491 sprintf(tmp, aux, arg);
497 /* Unsigned Integers -- various formats */
505 /* Access next argument */
506 arg = va_arg(vp, ulong);
508 sprintf(tmp, aux, arg);
509 } else if (do_long_long) {
510 unsigned long long arg;
512 /* Access next argument */
513 arg = va_arg(vp, unsigned long long);
515 sprintf(tmp, aux, arg);
519 /* Access next argument */
520 arg = va_arg(vp, uint);
521 sprintf(tmp, aux, arg);
527 /* Floating Point -- various formats */
535 /* Access next argument */
536 arg = va_arg(vp, double);
538 /* Format the argument */
539 sprintf(tmp, aux, arg);
544 /* Pointer -- implementation varies */
548 /* Access next argument */
549 arg = va_arg(vp, vptr);
551 /* Format the argument */
552 sprintf(tmp, aux, arg);
562 /* Access next argument */
563 arg = va_arg(vp, concptr);
565 /* Hack -- convert nullptr to EMPTY */
570 /* Prevent buffer overflows */
571 strncpy(arg2, arg, 1024);
574 /* Format the argument */
575 sprintf(tmp, aux, arg);
580 /* User defined data */
585 /* Access next argument */
586 arg = va_arg(vp, vptr);
588 /* Format the "user data" */
589 sprintf(tmp, aux, arg);
595 /* Error -- illegal format char */
604 for (q = 0; tmp[q]; q++) {
605 if (iskanji(tmp[q])) {
611 /* Mega-Hack -- handle "capitilization" */
613 /* Now append "tmp" to "buf" */
614 for (q = 0; tmp[q]; q++) {
615 /* Notice first non-space */
616 if (!iswspace(tmp[q])) {
617 /* Capitalize if possible */
618 if (islower(tmp[q])) {
619 tmp[q] = (char)toupper(tmp[q]);
627 /* Now append "tmp" to "buf" */
628 for (q = 0; tmp[q]; q++) {
629 /* Check total length */
634 /* Save the character */
636 if (iskanji(tmp[q])) {
640 // 最後の文字が2バイト文字の前半で終わる場合は空白で置き換えて終了する
650 /* Terminate buffer */
658 * Do a vstrnfmt (see above) into a (growable) static buffer.
659 * This buffer is usable for very short term formatting of results.
661 char *vformat(concptr fmt, va_list vp)
663 /* Initial allocation */
664 static std::vector<char> format_buf(1024);
666 /* Null format yields last result */
668 return format_buf.data();
671 /* Keep going until successful */
675 /* Build the string */
676 len = vstrnfmt(format_buf.data(), format_buf.size(), fmt, vp);
679 if (len < format_buf.size() - 1) {
683 /* Grow the buffer */
684 format_buf.resize(format_buf.size() * 2);
687 /* Return the new buffer */
688 return format_buf.data();
692 * Do a vstrnfmt (see above) into a buffer of a given size.
694 uint strnfmt(char *buf, uint max, concptr fmt, ...)
700 /* Begin the Varargs Stuff */
703 /* Do a virtual fprintf to stderr */
704 len = vstrnfmt(buf, max, fmt, vp);
706 /* End the Varargs Stuff */
709 /* Return the number of bytes written */
714 * Do a vstrnfmt (see above) into a buffer of unknown size.
715 * Since the buffer size is unknown, the user better verify the args.
717 uint strfmt(char *buf, concptr fmt, ...)
723 /* Begin the Varargs Stuff */
726 /* Build the string, assume 32K buffer */
727 len = vstrnfmt(buf, 32767, fmt, vp);
729 /* End the Varargs Stuff */
732 /* Return the number of bytes written */
737 * Do a vstrnfmt() into (see above) into a (growable) static buffer.
738 * This buffer is usable for very short term formatting of results.
739 * Note that the buffer is (technically) writable, but only up to
740 * the length of the string contained inside it.
742 char *format(concptr fmt, ...)
747 /* Begin the Varargs Stuff */
750 /* Format the args */
751 res = vformat(fmt, vp);
753 /* End the Varargs Stuff */
756 /* Return the result */
761 * Vararg interface to plog()
763 void plog_fmt(concptr fmt, ...)
768 /* Begin the Varargs Stuff */
771 /* Format the args */
772 res = vformat(fmt, vp);
774 /* End the Varargs Stuff */
782 * Vararg interface to quit()
784 void quit_fmt(concptr fmt, ...)
789 /* Begin the Varargs Stuff */
793 res = vformat(fmt, vp);
795 /* End the Varargs Stuff */
803 * Vararg interface to core()
805 void core_fmt(concptr fmt, ...)
810 /* Begin the Varargs Stuff */
813 /* If requested, Do a virtual fprintf to stderr */
814 res = vformat(fmt, vp);
816 /* End the Varargs Stuff */