Angband 3.0.9b.
[angband.git] / src / z-form.c
blob045ee631c1042e6050f0213605773869251710bd
1 /*
2 * File: z-form.c
3 * Purpose: Low-level text formatting (snprintf() replacement)
5 * Copyright (c) 1997 Ben Harrison
7 * This work is free software; you can redistribute it and/or modify it
8 * under the terms of either:
10 * a) the GNU General Public License as published by the Free Software
11 * Foundation, version 2, or
13 * b) the "Angband licence":
14 * This software may be copied and distributed for educational, research,
15 * and not for profit purposes provided that this copyright and statement
16 * are included in all such copies. Other copyrights may also apply.
18 #include "z-form.h"
19 #include "z-type.h"
21 #include "z-util.h"
22 #include "z-virt.h"
26 * Here is some information about the routines in this file.
28 * In general, the following routines take a "buffer", a "max length",
29 * a "format string", and some "arguments", and use the format string
30 * and the arguments to create a (terminated) string in the buffer
31 * (using only the first "max length" bytes), and return the "length"
32 * of the resulting string, not including the (mandatory) terminator.
34 * The format strings allow the basic "sprintf()" format sequences, though
35 * some of them are processed slightly more carefully or portably, as well
36 * as a few "special" sequences, including the "capilitization" sequences of
37 * "%C" and "%S".
39 * Note that some "limitations" are enforced by the current implementation,
40 * for example, no "format sequence" can exceed 100 characters, including any
41 * "length" restrictions, and the result of combining and "format sequence"
42 * with the relevent "arguments" must not exceed 1000 characters.
44 * These limitations could be fixed by stealing some of the code from,
45 * say, "vsprintf()" and placing it into my "vstrnfmt()" function.
47 * Note that a "^" inside a "format sequence" causes the first non-space
48 * character in the string resulting from the combination of the format
49 * sequence and the argument(s) to be "capitalized" if possible. Note
50 * that the "^" character is removed before the "standard" formatting
51 * routines are called. Likewise, a "*" inside a "format sequence" is
52 * removed from the "format sequence", and replaced by the textual form
53 * of the next argument in the argument list. See examples below.
55 * Legal format characters: %,b,n,p,c,s,d,i,o,u,X,x,E,e,F,f,G,g,r,v.
57 * Format("%%")
58 * Append the literal "%".
59 * No legal modifiers.
61 * Format("%n", size_t *np)
62 * Save the current length into (*np).
63 * No legal modifiers.
65 * Format("%p", void *v)
66 * Append the pointer "v" (implementation varies).
67 * No legal modifiers.
69 * format("%b", int b)
70 * Append the integer formatted as binary.
71 * If a modifier of 1, 2, 3 or 4 is provided, then only append 2**n bits, not
72 * all 32.
74 * Format("%E", double r)
75 * Format("%F", double r)
76 * Format("%G", double r)
77 * Format("%e", double r)
78 * Format("%f", double r)
79 * Format("%g", double r)
80 * Append the double "r", in various formats.
82 * Format("%ld", long int i)
83 * Append the long integer "i".
85 * Format("%d", int i)
86 * Append the integer "i".
88 * Format("%lu", unsigned long int i)
89 * Append the unsigned long integer "i".
91 * Format("%u", unsigned int i)
92 * Append the unsigned integer "i".
94 * Format("%lo", unsigned long int i)
95 * Append the unsigned long integer "i", in octal.
97 * Format("%o", unsigned int i)
98 * Append the unsigned integer "i", in octal.
100 * Format("%lX", unsigned long int i)
101 * Note -- use all capital letters
102 * Format("%lx", unsigned long int i)
103 * Append the unsigned long integer "i", in hexidecimal.
105 * Format("%X", unsigned int i)
106 * Note -- use all capital letters
107 * Format("%x", unsigned int i)
108 * Append the unsigned integer "i", in hexidecimal.
110 * Format("%c", char c)
111 * Append the character "c".
112 * Do not use the "+" or "0" flags.
114 * Format("%s", cptr s)
115 * Append the string "s".
116 * Do not use the "+" or "0" flags.
117 * Note that a "NULL" value of "s" is converted to the empty string.
119 * Format("%y", type_union *y). Use any of the above patterns;
120 * z is interpreted as one of c, d, f, or s in the patterns above,
121 * as appropriate for the type of the corresponding argument.
122 * (There is currently no way to render a typeunion in octal or hex.)
124 * For examples below, assume "int n = 0; int m = 100; char buf[100];",
125 * plus "char *s = NULL;", 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("%^-.*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.
149 * Basic "vararg" format function.
151 * This function takes a buffer, a max byte count, a format string, and
152 * a va_list of arguments to the format string, and uses the format string
153 * and the arguments to create a string to the buffer. The string is
154 * derived from the format string and the arguments in the manner of the
155 * "sprintf()" function, but with some extra "format" commands. Note that
156 * this function will never use more than the given number of bytes in the
157 * buffer, preventing messy invalid memory references. This function then
158 * returns the total number of non-null bytes written into the buffer.
160 * Method: Let "str" be the (unlimited) created string, and let "len" be the
161 * smaller of "max-1" and "strlen(str)". We copy the first "len" chars of
162 * "str" into "buf", place "\0" into buf[len], and return "len".
164 * In English, we do a sprintf() into "buf", a buffer with size "max",
165 * and we return the resulting value of "strlen(buf)", but we allow some
166 * special format commands, and we are more careful than "sprintf()".
168 * Typically, "max" is in fact the "size" of "buf", and thus represents
169 * the "number" of chars in "buf" which are ALLOWED to be used. An
170 * alternative definition would have required "buf" to hold at least
171 * "max+1" characters, and would have used that extra character only
172 * in the case where "buf" was too short for the result. This would
173 * give an easy test for "overflow", but a less "obvious" semantics.
175 * Note that if the buffer was "too short" to hold the result, we will
176 * always return "max-1", but we also return "max-1" if the buffer was
177 * "just long enough". We could have returned "max" if the buffer was
178 * too short, not written a null, and forced the programmer to deal with
179 * this special case, but I felt that it is better to at least give a
180 * "usable" result when the buffer was too long instead of either giving
181 * a memory overwrite like "sprintf()" or a non-terminted string like
182 * "strncpy()". Note that "strncpy()" also "null-pads" the result.
184 * Note that in most cases "just long enough" is probably "too short".
186 * We should also consider extracting and processing the "width" and other
187 * "flags" by hand, it might be more "accurate", and it would allow us to
188 * remove the limit (1000 chars) on the result of format sequences.
190 * Also, some sequences, such as "%+d" by hand, do not work on all machines,
191 * and could thus be correctly handled here.
193 * Error detection in this routine is not very graceful, in particular,
194 * if an error is detected in the format string, we simply "pre-terminate"
195 * the given buffer to a length of zero, and return a "length" of zero.
196 * The contents of "buf", except for "buf[0]", may then be undefined.
198 size_t vstrnfmt(char *buf, size_t max, cptr fmt, va_list vp)
200 cptr s;
202 /* The argument is "long" */
203 bool do_long;
205 /* The argument needs "processing" */
206 bool do_xtra;
208 /* Bytes used in buffer */
209 size_t n;
211 /* Bytes used in format sequence */
212 size_t q;
214 /* Format sequence */
215 char aux[128];
217 /* Resulting string */
218 char tmp[1024];
221 /* Fatal error - no buffer length */
222 if (!max) quit("Called vstrnfmt() with empty buffer!");
225 /* Mega-Hack -- treat "no format" as "empty string" */
226 if (!fmt) fmt = "";
229 /* Begin the buffer */
230 n = 0;
232 /* Begin the format string */
233 s = fmt;
235 /* Scan the format string */
236 while (TRUE)
238 type_union tval = END;
240 /* All done */
241 if (!*s) break;
243 /* Normal character */
244 if (*s != '%')
246 /* Check total length */
247 if (n == max-1) break;
249 /* Save the character */
250 buf[n++] = *s++;
252 /* Continue */
253 continue;
256 /* Skip the "percent" */
257 s++;
259 /* Pre-process "%%" */
260 if (*s == '%')
262 /* Check total length */
263 if (n == max-1) break;
265 /* Save the percent */
266 buf[n++] = '%';
268 /* Skip the "%" */
269 s++;
271 /* Continue */
272 continue;
275 /* Pre-process "%n" */
276 if (*s == 'n')
278 size_t *arg;
280 /* Get the next argument */
281 arg = va_arg(vp, size_t *);
283 /* Save the current length */
284 (*arg) = n;
286 /* Skip the "n" */
287 s++;
289 /* Continue */
290 continue;
294 /* Begin the "aux" string */
295 q = 0;
297 /* Save the "percent" */
298 aux[q++] = '%';
300 /* Assume no "long" argument */
301 do_long = FALSE;
303 /* Assume no "xtra" processing */
304 do_xtra = FALSE;
306 /* Build the "aux" string */
307 while (TRUE)
309 /* Error -- format sequence is not terminated */
310 if (!*s)
312 /* Terminate the buffer */
313 buf[0] = '\0';
315 /* Return "error" */
316 return (0);
319 /* Error -- format sequence may be too long */
320 if (q > 100)
322 /* Terminate the buffer */
323 buf[0] = '\0';
325 /* Return "error" */
326 return (0);
329 /* Handle "alphabetic" chars */
330 if (isalpha((unsigned char)*s))
332 /* Hack -- handle "long" request */
333 if (*s == 'l')
335 /* Save the character */
336 aux[q++] = *s++;
338 /* Note the "long" flag */
339 do_long = TRUE;
342 /* Mega-Hack -- handle "extra-long" request */
343 else if (*s == 'L')
345 /* Error -- illegal format char */
346 buf[0] = '\0';
348 /* Return "error" */
349 return (0);
352 /* Handle normal end of format sequence */
353 else
355 /* Save the character */
356 aux[q++] = *s++;
358 /* Stop processing the format sequence */
359 break;
363 /* Handle "non-alphabetic" chars */
364 else
366 /* Hack -- Handle 'star' (for "variable length" argument) */
367 if (*s == '*')
369 int arg;
371 /* Get the next argument */
372 arg = va_arg(vp, int);
374 /* Hack -- append the "length" */
375 sprintf(aux + q, "%d", arg);
377 /* Hack -- accept the "length" */
378 while (aux[q]) q++;
380 /* Skip the "*" */
381 s++;
384 /* Mega-Hack -- Handle 'caret' (for "uppercase" request) */
385 else if (*s == '^')
387 /* Note the "xtra" flag */
388 do_xtra = TRUE;
390 /* Skip the "^" */
391 s++;
394 /* Collect "normal" characters (digits, "-", "+", ".", etc) */
395 else
397 /* Save the character */
398 aux[q++] = *s++;
404 /* Terminate "aux" */
405 aux[q] = '\0';
407 /* Clear "tmp" */
408 tmp[0] = '\0';
410 /* Parse a type_union */
411 if (aux[q-1] == 'y')
413 tval = va_arg(vp, type_union);
415 if (do_long)
417 /* Error -- illegal type_union argument */
418 buf[0] = '\0';
420 /* Return "error" */
421 return (0);
424 /* Replace aux terminator with proper printf char */
425 if (tval.t == T_CHAR) aux[q-1] = 'c';
426 else if (tval.t == T_INT) aux[q-1] = 'd';
427 else if (tval.t == T_FLOAT) aux[q-1] = 'f';
428 else if (tval.t == T_STRING) aux[q-1] = 's';
429 else
431 buf[0] = '\0';
432 return (0);
436 /* Process the "format" symbol */
437 switch (aux[q-1])
439 /* Simple Character -- standard format */
440 case 'c':
442 int arg;
444 /* Get the next argument */
445 arg = tval.t == T_END ? va_arg(vp, int) : tval.u.c;
447 /* Format the argument */
448 sprintf(tmp, aux, arg);
450 /* Done */
451 break;
454 /* Signed Integers -- standard format */
455 case 'd': case 'i':
457 if (do_long)
459 long arg;
461 /* Get the next argument */
462 arg = va_arg(vp, long);
464 /* Format the argument */
465 sprintf(tmp, aux, arg);
467 else
469 int arg;
471 /* Get the next argument */
472 arg = tval.t == T_END ? va_arg(vp, int) : tval.u.i;
474 /* Format the argument */
475 sprintf(tmp, aux, arg);
478 /* Done */
479 break;
482 /* Unsigned Integers -- various formats */
483 case 'u': case 'o': case 'x': case 'X':
485 if (do_long)
487 unsigned long arg;
489 /* Get the next argument */
490 arg = va_arg(vp, unsigned long);
492 /* Format the argument */
493 sprintf(tmp, aux, arg);
495 else
497 unsigned int arg;
499 /* Get the next argument */
500 arg = va_arg(vp, unsigned int);
502 /* Format the argument */
503 sprintf(tmp, aux, arg);
506 /* Done */
507 break;
510 /* Floating Point -- various formats */
511 case 'f':
512 case 'e': case 'E':
513 case 'g': case 'G':
515 double arg;
517 /* Get the next argument */
518 arg = tval.t == T_END ? va_arg(vp, double) : tval.u.f;
520 /* Format the argument */
521 sprintf(tmp, aux, arg);
523 /* Done */
524 break;
527 /* Pointer -- implementation varies */
528 case 'p':
530 void *arg;
532 /* Get the next argument */
533 arg = va_arg(vp, void*);
535 /* Format the argument */
536 sprintf(tmp, aux, arg);
538 /* Done */
539 break;
542 /* String */
543 case 's':
545 cptr arg;
546 char arg2[1024];
548 /* Get the next argument */
549 arg = tval.t == T_END ? va_arg(vp, cptr) : tval.u.s;
551 /* Hack -- convert NULL to EMPTY */
552 if (!arg) arg = "";
554 /* Prevent buffer overflows */
555 (void)my_strcpy(arg2, arg, sizeof(arg2));
557 /* Format the argument */
558 sprintf(tmp, aux, arg2);
560 /* Done */
561 break;
564 #if 0 /* Later */
565 /* Binary */
566 case 'b':
568 int arg;
569 size_t i, max = 32;
570 u32b bitmask;
571 char out[32 + 1];
573 /* Get the next argument */
574 arg = va_arg(vp, int);
576 /* Check our aux string */
577 switch (aux[0])
579 case '1': max = 2; break;
580 case '2': max = 4; break;
581 case '3': max = 8; break;
582 case '4': max = 16; break;
583 default:
584 case '5': max = 32; break;
586 /* Format specially */
587 for (i = 1; i <= max; i++, bitmask *= 2)
589 if (arg & bitmask) out[max - i] = '1';
590 else out[max - i] = '0';
593 /* Terminate */
594 out[max] = '\0';
596 /* Append the argument */
597 my_strcpy(tmp, out, sizeof tmp);
599 /* Done */
600 break;
602 #endif
604 /* Oops */
605 default:
607 /* Error -- illegal format char */
608 buf[0] = '\0';
610 /* Return "error" */
611 return (0);
616 /* Mega-Hack -- handle "capitalization" */
617 if (do_xtra)
619 for (q = 0; tmp[q]; q++)
621 /* Notice first non-space */
622 if (!isspace((unsigned char)tmp[q]))
624 /* Capitalize if possible */
625 if (islower((unsigned char)tmp[q]))
626 tmp[q] = toupper((unsigned char)tmp[q]);
628 /* Done */
629 break;
634 /* Now append "tmp" to "buf" */
635 for (q = 0; tmp[q]; q++)
637 /* Check total length */
638 if (n == max-1) break;
640 /* Save the character */
641 buf[n++] = tmp[q];
646 /* Terminate buffer */
647 buf[n] = '\0';
649 /* Return length */
650 return (n);
655 * Add a formatted string to the end of a string
657 void strnfcat(char *str, size_t max, size_t *end, cptr fmt, ...)
659 size_t len;
661 va_list vp;
663 /* Paranoia */
664 if (*end >= max) return;
666 /* Begin the Varargs Stuff */
667 va_start(vp, fmt);
669 /* Build the string */
670 len = vstrnfmt(&str[*end], max - *end, fmt, vp);
672 /* End the Varargs Stuff */
673 va_end(vp);
675 /* Change the end value */
676 *end += len;
680 static char *format_buf = NULL;
681 static size_t format_len = 0;
685 * Do a vstrnfmt (see above) into a (growable) static buffer.
686 * This buffer is usable for very short term formatting of results.
688 char *vformat(cptr fmt, va_list vp)
690 /* Initial allocation */
691 if (!format_buf)
693 format_len = 1024;
694 C_MAKE(format_buf, format_len, char);
697 /* Null format yields last result */
698 if (!fmt) return (format_buf);
700 /* Keep going until successful */
701 while (1)
703 size_t len;
705 /* Build the string */
706 len = vstrnfmt(format_buf, format_len, fmt, vp);
708 /* Success */
709 if (len < format_len-1) break;
711 /* Grow the buffer */
712 KILL(format_buf);
713 format_len = format_len * 2;
714 C_MAKE(format_buf, format_len, char);
717 /* Return the new buffer */
718 return (format_buf);
721 void vformat_kill(void)
723 KILL(format_buf);
728 * Do a vstrnfmt (see above) into a buffer of a given size.
730 size_t strnfmt(char *buf, size_t max, cptr fmt, ...)
732 size_t len;
734 va_list vp;
736 /* Begin the Varargs Stuff */
737 va_start(vp, fmt);
739 /* Do the va_arg fmt to the buffer */
740 len = vstrnfmt(buf, max, fmt, vp);
742 /* End the Varargs Stuff */
743 va_end(vp);
745 /* Return the number of bytes written */
746 return (len);
751 * Do a vstrnfmt() into (see above) into a (growable) static buffer.
752 * This buffer is usable for very short term formatting of results.
753 * Note that the buffer is (technically) writable, but only up to
754 * the length of the string contained inside it.
756 char *format(cptr fmt, ...)
758 char *res;
759 va_list vp;
761 /* Begin the Varargs Stuff */
762 va_start(vp, fmt);
764 /* Format the args */
765 res = vformat(fmt, vp);
767 /* End the Varargs Stuff */
768 va_end(vp);
770 /* Return the result */
771 return (res);
778 * Vararg interface to plog()
780 void plog_fmt(cptr fmt, ...)
782 char *res;
783 va_list vp;
785 /* Begin the Varargs Stuff */
786 va_start(vp, fmt);
788 /* Format the args */
789 res = vformat(fmt, vp);
791 /* End the Varargs Stuff */
792 va_end(vp);
794 /* Call plog */
795 plog(res);
801 * Vararg interface to quit()
803 void quit_fmt(cptr fmt, ...)
805 char *res;
806 va_list vp;
808 /* Begin the Varargs Stuff */
809 va_start(vp, fmt);
811 /* Format */
812 res = vformat(fmt, vp);
814 /* End the Varargs Stuff */
815 va_end(vp);
817 /* Call quit() */
818 quit(res);