OSDN Git Service

Initial revision
[hengband/hengband.git] / src / z-form.c
1 /* File: z-form.c */
2
3 /* Purpose: Low level text formatting -BEN- */
4
5 #include "z-form.h"
6
7 #include "z-util.h"
8 #include "z-virt.h"
9
10
11 /*
12  * Here is some information about the routines in this file.
13  *
14  * In general, the following routines take a "buffer", a "max length",
15  * a "format string", and some "arguments", and use the format string
16  * and the arguments to create a (terminated) string in the buffer
17  * (using only the first "max length" bytes), and return the "length"
18  * of the resulting string, not including the (mandatory) terminator.
19  *
20  * The format strings allow the basic "sprintf()" format sequences, though
21  * some of them are processed slightly more carefully or portably, as well
22  * as a few "special" sequences, including the "%r" and "%v" sequences, and
23  * the "capilitization" sequences of "%C", "%S", and "%V".
24  *
25  * Note that some "limitations" are enforced by the current implementation,
26  * for example, no "format sequence" can exceed 100 characters, including any
27  * "length" restrictions, and the result of combining and "format sequence"
28  * with the relevent "arguments" must not exceed 1000 characters.
29  *
30  * These limitations could be fixed by stealing some of the code from,
31  * say, "vsprintf()" and placing it into my "vstrnfmt()" function.
32  *
33  * Note that a "^" inside a "format sequence" causes the first non-space
34  * character in the string resulting from the combination of the format
35  * sequence and the argument(s) to be "capitalized" if possible.  Note
36  * that the "^" character is removed before the "standard" formatting
37  * routines are called.  Likewise, a "*" inside a "format sequence" is
38  * removed from the "format sequence", and replaced by the textual form
39  * of the next argument in the argument list.  See examples below.
40  *
41  * Legal format characters: %,n,p,c,s,d,i,o,u,X,x,E,e,F,f,G,g,r,v.
42  *
43  * Format("%%")
44  *   Append the literal "%".
45  *   No legal modifiers.
46  *
47  * Format("%n", int *np)
48  *   Save the current length into (*np).
49  *   No legal modifiers.
50  *
51  * Format("%p", vptr v)
52  *   Append the pointer "v" (implementation varies).
53  *   No legal modifiers.
54  *
55  * Format("%E", double r)
56  * Format("%F", double r)
57  * Format("%G", double r)
58  * Format("%e", double r)
59  * Format("%f", double r)
60  * Format("%g", double r)
61  *   Append the double "r", in various formats.
62  *
63  * Format("%ld", long int i)
64  *   Append the long integer "i".
65  *
66  * Format("%d", int i)
67  *   Append the integer "i".
68  *
69  * Format("%lu", unsigned long int i)
70  *   Append the unsigned long integer "i".
71  *
72  * Format("%u", unsigned int i)
73  *   Append the unsigned integer "i".
74  *
75  * Format("%lo", unsigned long int i)
76  *   Append the unsigned long integer "i", in octal.
77  *
78  * Format("%o", unsigned int i)
79  *   Append the unsigned integer "i", in octal.
80  *
81  * Format("%lX", unsigned long int i)
82  *   Note -- use all capital letters
83  * Format("%lx", unsigned long int i)
84  *   Append the unsigned long integer "i", in hexidecimal.
85  *
86  * Format("%X", unsigned int i)
87  *   Note -- use all capital letters
88  * Format("%x", unsigned int i)
89  *   Append the unsigned integer "i", in hexidecimal.
90  *
91  * Format("%c", char c)
92  *   Append the character "c".
93  *   Do not use the "+" or "0" flags.
94  *
95  * Format("%s", cptr s)
96  *   Append the string "s".
97  *   Do not use the "+" or "0" flags.
98  *   Note that a "NULL" value of "s" is converted to the empty string.
99  *
100  * Format("%V", vptr v)
101  *   Note -- possibly significant mode flag
102  * Format("%v", vptr v)
103  *   Append the object "v", using the current "user defined print routine".
104  *   User specified modifiers, often ignored.
105  *
106  * Format("%r", vstrnfmt_aux_func *fp)
107  *   Set the "user defined print routine" (vstrnfmt_aux) to "fp".
108  *   No legal modifiers.
109  *
110  *
111  * For examples below, assume "int n = 0; int m = 100; char buf[100];",
112  * plus "char *s = NULL;", and unknown values "char *txt; int i;".
113  *
114  * For example: "n = strnfmt(buf, -1, "(Max %d)", i);" will have a
115  * similar effect as "sprintf(buf, "(Max %d)", i); n = strlen(buf);".
116  *
117  * For example: "(void)strnfmt(buf, 16, "%s", txt);" will have a similar
118  * effect as "strncpy(buf, txt, 16); buf[15] = '\0';".
119  *
120  * For example: "if (strnfmt(buf, 16, "%s", txt) < 16) ..." will have
121  * a similar effect as "strcpy(buf, txt)" but with bounds checking.
122  *
123  * For example: "s = buf; s += vstrnfmt(s, -1, ...); ..." will allow
124  * multiple "appends" to "buf" (at the cost of losing the max-length info).
125  *
126  * For example: "s = buf; n = vstrnfmt(s+n, 100-n, ...); ..." will allow
127  * multiple bounded "appends" to "buf", with constant access to "strlen(buf)".
128  *
129  * For example: "format("The %r%v was destroyed!", obj_desc, obj);"
130  * (where "obj_desc(buf, max, fmt, obj)" will "append" a "description"
131  * of the given object to the given buffer, and return the total length)
132  * will return a "useful message" about the object "obj", for example,
133  * "The Large Shield was destroyed!".
134  *
135  * For example: "format("%^-.*s", i, txt)" will produce a string containing
136  * the first "i" characters of "txt", left justified, with the first non-space
137  * character capitilized, if reasonable.
138  */
139
140
141
142
143
144 /*
145  * The "type" of the "user defined print routine" pointer
146  */
147 typedef uint (*vstrnfmt_aux_func)(char *buf, uint max, cptr fmt, vptr arg);
148
149 /*
150  * The "default" user defined print routine.  Ignore the "fmt" string.
151  */
152 static uint vstrnfmt_aux_dflt(char *buf, uint max, cptr fmt, vptr arg)
153 {
154         uint len;
155         char tmp[32];
156
157         /* XXX XXX */
158         fmt = fmt ? fmt : 0;
159
160         /* Pointer display */
161         sprintf(tmp, "<<%p>>", arg);
162         len = strlen(tmp);
163         if (len >= max) len = max - 1;
164         tmp[len] = '\0';
165         strcpy(buf, tmp);
166         return (len);
167 }
168
169 /*
170  * The "current" user defined print routine.  It can be changed
171  * dynamically by sending the proper "%r" sequence to "vstrnfmt()"
172  */
173 static vstrnfmt_aux_func vstrnfmt_aux = vstrnfmt_aux_dflt;
174
175
176
177 /*
178  * Basic "vararg" format function.
179  *
180  * This function takes a buffer, a max byte count, a format string, and
181  * a va_list of arguments to the format string, and uses the format string
182  * and the arguments to create a string to the buffer.  The string is
183  * derived from the format string and the arguments in the manner of the
184  * "sprintf()" function, but with some extra "format" commands.  Note that
185  * this function will never use more than the given number of bytes in the
186  * buffer, preventing messy invalid memory references.  This function then
187  * returns the total number of non-null bytes written into the buffer.
188  *
189  * Method: Let "str" be the (unlimited) created string, and let "len" be the
190  * smaller of "max-1" and "strlen(str)".  We copy the first "len" chars of
191  * "str" into "buf", place "\0" into buf[len], and return "len".
192  *
193  * In English, we do a sprintf() into "buf", a buffer with size "max",
194  * and we return the resulting value of "strlen(buf)", but we allow some
195  * special format commands, and we are more careful than "sprintf()".
196  *
197  * Typically, "max" is in fact the "size" of "buf", and thus represents
198  * the "number" of chars in "buf" which are ALLOWED to be used.  An
199  * alternative definition would have required "buf" to hold at least
200  * "max+1" characters, and would have used that extra character only
201  * in the case where "buf" was too short for the result.  This would
202  * give an easy test for "overflow", but a less "obvious" semantics.
203  *
204  * Note that if the buffer was "too short" to hold the result, we will
205  * always return "max-1", but we also return "max-1" if the buffer was
206  * "just long enough".  We could have returned "max" if the buffer was
207  * too short, not written a null, and forced the programmer to deal with
208  * this special case, but I felt that it is better to at least give a
209  * "usable" result when the buffer was too long instead of either giving
210  * a memory overwrite like "sprintf()" or a non-terminted string like
211  * "strncpy()".  Note that "strncpy()" also "null-pads" the result.
212  *
213  * Note that in most cases "just long enough" is probably "too short".
214  *
215  * We should also consider extracting and processing the "width" and other
216  * "flags" by hand, it might be more "accurate", and it would allow us to
217  * remove the limit (1000 chars) on the result of format sequences.
218  *
219  * Also, some sequences, such as "%+d" by hand, do not work on all machines,
220  * and could thus be correctly handled here.
221  *
222  * Error detection in this routine is not very graceful, in particular,
223  * if an error is detected in the format string, we simply "pre-terminate"
224  * the given buffer to a length of zero, and return a "length" of zero.
225  * The contents of "buf", except for "buf[0]", may then be undefined.
226  */
227 uint vstrnfmt(char *buf, uint max, cptr fmt, va_list vp)
228 {
229         cptr s;
230
231         /* The argument is "long" */
232         bool do_long;
233
234         /* The argument needs "processing" */
235         bool do_xtra;
236
237         /* Bytes used in buffer */
238         uint n;
239
240         /* Bytes used in format sequence */
241         uint q;
242
243         /* Format sequence */
244         char aux[128];
245
246         /* Resulting string */
247         char tmp[1024];
248
249
250         /* Mega-Hack -- treat "illegal" length as "infinite" */
251         if (!max) max = 32767;
252
253         /* Mega-Hack -- treat "no format" as "empty string" */
254         if (!fmt) fmt = "";
255
256
257         /* Begin the buffer */
258         n = 0;
259
260         /* Begin the format string */
261         s = fmt;
262
263         /* Scan the format string */
264         while (TRUE)
265         {
266                 /* All done */
267                 if (!*s) break;
268
269                 /* Normal character */
270                 if (*s != '%')
271                 {
272                         /* Check total length */
273                         if (n == max-1) break;
274
275                         /* Save the character */
276                         buf[n++] = *s++;
277
278                         /* Continue */
279                         continue;
280                 }
281
282                 /* Skip the "percent" */
283                 s++;
284
285                 /* Pre-process "%%" */
286                 if (*s == '%')
287                 {
288                         /* Check total length */
289                         if (n == max-1) break;
290
291                         /* Save the percent */
292                         buf[n++] = '%';
293
294                         /* Skip the "%" */
295                         s++;
296
297                         /* Continue */
298                         continue;
299                 }
300
301                 /* Pre-process "%n" */
302                 if (*s == 'n')
303                 {
304                         int *arg;
305
306                         /* Access the next argument */
307                         arg = va_arg(vp, int *);
308
309                         /* Save the current length */
310                         (*arg) = n;
311
312                         /* Skip the "n" */
313                         s++;
314
315                         /* Continue */
316                         continue;
317                 }
318
319                 /* Hack -- Pre-process "%r" */
320                 if (*s == 'r')
321                 {
322                         /* Extract the next argument, and save it (globally) */
323                         vstrnfmt_aux = va_arg(vp, vstrnfmt_aux_func);
324
325                         /* Skip the "r" */
326                         s++;
327
328                         /* Continue */
329                         continue;
330                 }
331
332
333                 /* Begin the "aux" string */
334                 q = 0;
335
336                 /* Save the "percent" */
337                 aux[q++] = '%';
338
339                 /* Assume no "long" argument */
340                 do_long = FALSE;
341
342                 /* Assume no "xtra" processing */
343                 do_xtra = FALSE;
344
345                 /* Build the "aux" string */
346                 while (TRUE)
347                 {
348                         /* Error -- format sequence is not terminated */
349                         if (!*s)
350                         {
351                                 /* Terminate the buffer */
352                                 buf[0] = '\0';
353
354                                 /* Return "error" */
355                                 return (0);
356                         }
357
358                         /* Error -- format sequence may be too long */
359                         if (q > 100)
360                         {
361                                 /* Terminate the buffer */
362                                 buf[0] = '\0';
363
364                                 /* Return "error" */
365                                 return (0);
366                         }
367
368                         /* Handle "alphabetic" chars */
369                         if (isalpha(*s))
370                         {
371                                 /* Hack -- handle "long" request */
372                                 if (*s == 'l')
373                                 {
374                                         /* Save the character */
375                                         aux[q++] = *s++;
376
377                                         /* Note the "long" flag */
378                                         do_long = TRUE;
379                                 }
380
381                                 /* Mega-Hack -- handle "extra-long" request */
382                                 else if (*s == 'L')
383                                 {
384                                         /* Error -- illegal format char */
385                                         buf[0] = '\0';
386
387                                         /* Return "error" */
388                                         return (0);
389                                 }
390
391                                 /* Handle normal end of format sequence */
392                                 else
393                                 {
394                                         /* Save the character */
395                                         aux[q++] = *s++;
396
397                                         /* Stop processing the format sequence */
398                                         break;
399                                 }
400                         }
401
402                         /* Handle "non-alphabetic" chars */
403                         else
404                         {
405                                 /* Hack -- Handle 'star' (for "variable length" argument) */
406                                 if (*s == '*')
407                                 {
408                                         int arg;
409
410                                         /* Access the next argument */
411                                         arg = va_arg(vp, int);
412
413                                         /* Hack -- append the "length" */
414                                         sprintf(aux + q, "%d", arg);
415
416                                         /* Hack -- accept the "length" */
417                                         while (aux[q]) q++;
418
419                                         /* Skip the "*" */
420                                         s++;
421                                 }
422
423                                 /* Mega-Hack -- Handle 'caret' (for "uppercase" request) */
424                                 else if (*s == '^')
425                                 {
426                                         /* Note the "xtra" flag */
427                                         do_xtra = TRUE;
428
429                                         /* Skip the "^" */
430                                         s++;
431                                 }
432
433                                 /* Collect "normal" characters (digits, "-", "+", ".", etc) */
434                                 else
435                                 {
436                                         /* Save the character */
437                                         aux[q++] = *s++;
438                                 }
439                         }
440                 }
441
442
443                 /* Terminate "aux" */
444                 aux[q] = '\0';
445
446                 /* Clear "tmp" */
447                 tmp[0] = '\0';
448
449                 /* Process the "format" char */
450                 switch (aux[q-1])
451                 {
452                         /* Simple Character -- standard format */
453                         case 'c':
454                         {
455                                 int arg;
456
457                                 /* Access next argument */
458                                 arg = va_arg(vp, int);
459
460                                 /* Format the argument */
461                                 sprintf(tmp, aux, arg);
462
463                                 /* Done */
464                                 break;
465                         }
466
467                         /* Signed Integers -- standard format */
468                         case 'd': case 'i':
469                         {
470                                 if (do_long)
471                                 {
472                                         long arg;
473
474                                         /* Access next argument */
475                                         arg = va_arg(vp, long);
476
477                                         /* Format the argument */
478                                         sprintf(tmp, aux, arg);
479                                 }
480                                 else
481                                 {
482                                         int arg;
483
484                                         /* Access next argument */
485                                         arg = va_arg(vp, int);
486
487                                         /* Format the argument */
488                                         sprintf(tmp, aux, arg);
489                                 }
490
491                                 /* Done */
492                                 break;
493                         }
494
495                         /* Unsigned Integers -- various formats */
496                         case 'u': case 'o': case 'x': case 'X':
497                         {
498                                 if (do_long)
499                                 {
500                                         unsigned long arg;
501
502                                         /* Access next argument */
503                                         arg = va_arg(vp, unsigned long);
504
505                                         /* Format the argument */
506                                         sprintf(tmp, aux, arg);
507                                 }
508                                 else
509                                 {
510                                         unsigned int arg;
511
512                                         /* Access next argument */
513                                         arg = va_arg(vp, unsigned int);
514
515                                         /* Format the argument */
516                                         sprintf(tmp, aux, arg);
517                                 }
518
519                                 /* Done */
520                                 break;
521                         }
522
523                         /* Floating Point -- various formats */
524                         case 'f':
525                         case 'e': case 'E':
526                         case 'g': case 'G':
527                         {
528                                 double arg;
529
530                                 /* Access next argument */
531                                 arg = va_arg(vp, double);
532
533                                 /* Format the argument */
534                                 sprintf(tmp, aux, arg);
535
536                                 /* Done */
537                                 break;
538                         }
539
540                         /* Pointer -- implementation varies */
541                         case 'p':
542                         {
543                                 vptr arg;
544
545                                 /* Access next argument */
546                                 arg = va_arg(vp, vptr);
547
548                                 /* Format the argument */
549                                 sprintf(tmp, aux, arg);
550
551                                 /* Done */
552                                 break;
553                         }
554
555                         /* String */
556                         case 's':
557                         {
558                                 cptr arg;
559                                 char arg2[1024];
560
561                                 /* Access next argument */
562                                 arg = va_arg(vp, cptr);
563
564                                 /* Hack -- convert NULL to EMPTY */
565                                 if (!arg) arg = "";
566
567                                 /* Prevent buffer overflows */
568                                 strncpy(arg2, arg, 1024);
569                                 arg2[1023] = '\0';
570
571                                 /* Format the argument */
572                                 sprintf(tmp, aux, arg2);
573
574                                 /* Done */
575                                 break;
576                         }
577
578                         /* User defined data */
579                         case 'V':
580                         case 'v':
581                         {
582                                 vptr arg;
583
584                                 /* Access next argument */
585                                 arg = va_arg(vp, vptr);
586
587                                 /* Format the "user data" */
588                                 (void)vstrnfmt_aux(tmp, 1000, aux, arg);
589
590                                 /* Done */
591                                 break;
592                         }
593
594
595                         /* Oops */
596                         default:
597                         {
598                                 /* Error -- illegal format char */
599                                 buf[0] = '\0';
600
601                                 /* Return "error" */
602                                 return (0);
603                         }
604                 }
605
606
607 #ifdef JP
608                   for (q = 0; tmp[q]; q++) if ( iskanji(tmp[q]) ) { do_xtra=FALSE;break;} 
609 #endif
610                 /* Mega-Hack -- handle "capitilization" */
611                 if (do_xtra)
612                 {
613                         /* Now append "tmp" to "buf" */
614                         for (q = 0; tmp[q]; q++)
615                         {
616                                 /* Notice first non-space */
617                                 if (!isspace(tmp[q]))
618                                 {
619                                         /* Capitalize if possible */
620                                         if (islower(tmp[q])) tmp[q] = toupper(tmp[q]);
621
622                                         /* Done */
623                                         break;
624                                 }
625                         }
626                 }
627
628                 /* Now append "tmp" to "buf" */
629                 for (q = 0; tmp[q]; q++)
630                 {
631                         /* Check total length */
632                         if (n == max-1) break;
633
634                         /* Save the character */
635                         buf[n++] = tmp[q];
636                 }
637         }
638
639
640         /* Terminate buffer */
641         buf[n] = '\0';
642
643         /* Return length */
644         return (n);
645 }
646
647
648 /*
649  * Do a vstrnfmt (see above) into a (growable) static buffer.
650  * This buffer is usable for very short term formatting of results.
651  */
652 char *vformat(cptr fmt, va_list vp)
653 {
654         static char *format_buf = NULL;
655         static huge format_len = 0;
656
657         /* Initial allocation */
658         if (!format_buf)
659         {
660                 format_len = 1024;
661                 C_MAKE(format_buf, format_len, char);
662         }
663
664         /* Null format yields last result */
665         if (!fmt) return (format_buf);
666
667         /* Keep going until successful */
668         while (1)
669         {
670                 uint len;
671
672                 /* Build the string */
673                 len = vstrnfmt(format_buf, format_len, fmt, vp);
674
675                 /* Success */
676                 if (len < format_len-1) break;
677
678                 /* Grow the buffer */
679                 C_KILL(format_buf, format_len, char);
680                 format_len = format_len * 2;
681                 C_MAKE(format_buf, format_len, char);
682         }
683
684         /* Return the new buffer */
685         return (format_buf);
686 }
687
688
689
690 /*
691  * Do a vstrnfmt (see above) into a buffer of a given size.
692  */
693 uint strnfmt(char *buf, uint max, cptr fmt, ...)
694 {
695         uint len;
696
697         va_list vp;
698
699         /* Begin the Varargs Stuff */
700         va_start(vp, fmt);
701
702         /* Do a virtual fprintf to stderr */
703         len = vstrnfmt(buf, max, fmt, vp);
704
705         /* End the Varargs Stuff */
706         va_end(vp);
707
708         /* Return the number of bytes written */
709         return (len);
710 }
711
712
713 /*
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.
716  */
717 uint strfmt(char *buf, cptr fmt, ...)
718 {
719         uint len;
720
721         va_list vp;
722
723         /* Begin the Varargs Stuff */
724         va_start(vp, fmt);
725
726         /* Build the string, assume 32K buffer */
727         len = vstrnfmt(buf, 32767, fmt, vp);
728
729         /* End the Varargs Stuff */
730         va_end(vp);
731
732         /* Return the number of bytes written */
733         return (len);
734 }
735
736
737
738
739 /*
740  * Do a vstrnfmt() into (see above) into a (growable) static buffer.
741  * This buffer is usable for very short term formatting of results.
742  * Note that the buffer is (technically) writable, but only up to
743  * the length of the string contained inside it.
744  */
745 char *format(cptr fmt, ...)
746 {
747         char *res;
748         va_list vp;
749
750         /* Begin the Varargs Stuff */
751         va_start(vp, fmt);
752
753         /* Format the args */
754         res = vformat(fmt, vp);
755
756         /* End the Varargs Stuff */
757         va_end(vp);
758
759         /* Return the result */
760         return (res);
761 }
762
763
764
765
766 /*
767  * Vararg interface to plog()
768  */
769 void plog_fmt(cptr fmt, ...)
770 {
771         char *res;
772         va_list vp;
773
774         /* Begin the Varargs Stuff */
775         va_start(vp, fmt);
776
777         /* Format the args */
778         res = vformat(fmt, vp);
779
780         /* End the Varargs Stuff */
781         va_end(vp);
782
783         /* Call plog */
784         plog(res);
785 }
786
787
788
789 /*
790  * Vararg interface to quit()
791  */
792 void quit_fmt(cptr fmt, ...)
793 {
794         char *res;
795         va_list vp;
796
797         /* Begin the Varargs Stuff */
798         va_start(vp, fmt);
799
800         /* Format */
801         res = vformat(fmt, vp);
802
803         /* End the Varargs Stuff */
804         va_end(vp);
805
806         /* Call quit() */
807         quit(res);
808 }
809
810
811
812 /*
813  * Vararg interface to core()
814  */
815 void core_fmt(cptr fmt, ...)
816 {
817         char *res;
818         va_list vp;
819
820         /* Begin the Varargs Stuff */
821         va_start(vp, fmt);
822
823         /* If requested, Do a virtual fprintf to stderr */
824         res = vformat(fmt, vp);
825
826         /* End the Varargs Stuff */
827         va_end(vp);
828
829         /* Call core() */
830         core(res);
831 }
832
833