Merge branch 'vendor/LIBARCHIVE'
[dragonfly.git] / contrib / gmp / printf / doprnt.c
1 /* __gmp_doprnt -- printf style formatted output.
2
3    THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY.  THEY'RE ALMOST
4    CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
5    FUTURE GNU MP RELEASES.
6
7 Copyright 2001, 2002, 2003 Free Software Foundation, Inc.
8
9 This file is part of the GNU MP Library.
10
11 The GNU MP Library is free software; you can redistribute it and/or modify
12 it under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or (at your
14 option) any later version.
15
16 The GNU MP Library is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
19 License for more details.
20
21 You should have received a copy of the GNU Lesser General Public License
22 along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */
23
24 #define _GNU_SOURCE    /* for DECIMAL_POINT in glibc langinfo.h */
25
26 #include "config.h"
27
28 #if HAVE_STDARG
29 #include <stdarg.h>
30 #else
31 #include <varargs.h>
32 #endif
33
34 #include <ctype.h>     /* for isdigit */
35 #include <stddef.h>    /* for ptrdiff_t */
36 #include <string.h>
37 #include <stdio.h>     /* for NULL */
38 #include <stdlib.h>
39
40 #if HAVE_INTTYPES_H
41 # include <inttypes.h> /* for intmax_t */
42 #else
43 # if HAVE_STDINT_H
44 #  include <stdint.h>
45 # endif
46 #endif
47
48 #if HAVE_LANGINFO_H
49 #include <langinfo.h>  /* for nl_langinfo */
50 #endif
51
52 #if HAVE_LOCALE_H
53 #include <locale.h>    /* for localeconv */
54 #endif
55
56 #if HAVE_SYS_TYPES_H
57 #include <sys/types.h> /* for quad_t */
58 #endif
59
60 #include "gmp.h"
61 #include "gmp-impl.h"
62
63
64 /* change this to "#define TRACE(x) x" for diagnostics */
65 #define TRACE(x)
66
67
68 /* Should be portable, but in any case this is only used under some ASSERTs. */
69 #define va_equal(x, y)                           \
70   (memcmp (&(x), &(y), sizeof(va_list)) == 0)
71
72
73 /* printf is convenient because it allows various types to be printed in one
74    fairly compact call, so having gmp_printf support the standard types as
75    well as the gmp ones is important.  This ends up meaning all the standard
76    parsing must be duplicated, to get a new routine recognising the gmp
77    extras.
78
79    With the currently favoured handling of mpz etc as Z, Q and F type
80    markers, it's not possible to use glibc register_printf_function since
81    that only accepts new conversion characters, not new types.  If Z was a
82    conversion there'd be no way to specify hex, decimal or octal, or
83    similarly with F no way to specify fixed point or scientific format.
84
85    It seems wisest to pass conversions %f, %e and %g of float, double and
86    long double over to the standard printf.  It'd be hard to be sure of
87    getting the right handling for NaNs, rounding, etc.  Integer conversions
88    %d etc and string conversions %s on the other hand could be easily enough
89    handled within gmp_doprnt, but if floats are going to libc then it's just
90    as easy to send all non-gmp types there.
91
92    "Z" was a type marker for size_t in old glibc, but there seems no need to
93    provide access to that now "z" is standard.
94
95    In GMP 4.1.1 we documented "ll" and "L" as being equivalent, but in C99
96    in fact "ll" is just for long long and "L" just for long double.
97    Apparentely GLIBC allows "L" for long long though.  This doesn't affect
98    us as such, since both are passed through to the C library.  To be
99    consistent with what we said before, the two are treated equivalently
100    here, and it's left to the C library to do what it thinks with them.
101
102    Possibilities:
103
104    "b" might be nice for binary output, and could even be supported for the
105    standard C types too if desired.
106
107    POSIX style "%n$" parameter numbering would be possible, but would need
108    to be handled completely within gmp_doprnt, since the numbering will be
109    all different once the format string it cut into pieces.
110
111    Some options for mpq formatting would be good.  Perhaps a non-zero
112    precision field could give a width for the denominator and mean always
113    put a "/".  A form "n+p/q" might interesting too, though perhaps that's
114    better left to applications.
115
116    Right now there's no way for an application to know whether types like
117    intmax_t are supported here.  If configure is doing its job and the same
118    compiler is used for gmp as for the application then there shouldn't be
119    any problem, but perhaps gmp.h should have some preprocessor symbols to
120    say what libgmp can do.  */
121
122
123
124 /* If a gmp format is the very first thing or there are two gmp formats with
125    nothing in between then we'll reach here with this_fmt == last_fmt and we
126    can do nothing in that case.
127
128    last_ap is always replaced after a FLUSH, so it doesn't matter if va_list
129    is a call-by-reference and the funs->format routine modifies it.  */
130
131 #define FLUSH()                                         \
132   do {                                                  \
133     if (this_fmt == last_fmt)                           \
134       {                                                 \
135         TRACE (printf ("nothing to flush\n"));          \
136         ASSERT (va_equal (this_ap, last_ap));           \
137       }                                                 \
138     else                                                \
139       {                                                 \
140         ASSERT (*this_fmt == '%');                      \
141         *this_fmt = '\0';                               \
142         TRACE (printf ("flush \"%s\"\n", last_fmt));    \
143         DOPRNT_FORMAT (last_fmt, last_ap);              \
144       }                                                 \
145   } while (0)
146
147
148 /* Parse up the given format string and do the appropriate output using the
149    given "funs" routines.  The data parameter is passed through to those
150    routines.  */
151
152 int
153 __gmp_doprnt (const struct doprnt_funs_t *funs, void *data,
154               const char *orig_fmt, va_list orig_ap)
155 {
156   va_list  ap, this_ap, last_ap;
157   size_t   alloc_fmt_size;
158   char     *fmt, *alloc_fmt, *last_fmt, *this_fmt, *gmp_str;
159   int      retval = 0;
160   int      type, fchar, *value, seen_precision;
161   struct doprnt_params_t param;
162
163   TRACE (printf ("gmp_doprnt \"%s\"\n", orig_fmt));
164
165   /* Don't modify orig_ap, if va_list is actually an array and hence call by
166      reference.  It could be argued that it'd be more efficient to leave the
167      caller to make a copy if it cared, but doing so here is going to be a
168      very small part of the total work, and we may as well keep applications
169      out of trouble.  */
170   va_copy (ap, orig_ap);
171
172   /* The format string is chopped up into pieces to be passed to
173      funs->format.  Unfortunately that means it has to be copied so each
174      piece can be null-terminated.  We're not going to be very fast here, so
175      use __gmp_allocate_func rather than TMP_ALLOC, to avoid overflowing the
176      stack if a long output string is given.  */
177   alloc_fmt_size = strlen (orig_fmt) + 1;
178 #if _LONG_LONG_LIMB
179   /* for a long long limb we change %Mx to %llx, so could need an extra 1
180      char for every 3 existing */
181   alloc_fmt_size += alloc_fmt_size / 3;
182 #endif
183   alloc_fmt = __GMP_ALLOCATE_FUNC_TYPE (alloc_fmt_size, char);
184   fmt = alloc_fmt;
185   strcpy (fmt, orig_fmt);
186
187   /* last_fmt and last_ap are just after the last output, and hence where
188      the next output will begin, when that's done */
189   last_fmt = fmt;
190   va_copy (last_ap, ap);
191
192   for (;;)
193     {
194       TRACE (printf ("next: \"%s\"\n", fmt));
195
196       fmt = strchr (fmt, '%');
197       if (fmt == NULL)
198         break;
199
200       /* this_fmt and this_ap are the current '%' sequence being considered */
201       this_fmt = fmt;
202       va_copy (this_ap, ap);
203       fmt++; /* skip the '%' */
204
205       TRACE (printf ("considering\n");
206              printf ("  last: \"%s\"\n", last_fmt);
207              printf ("  this: \"%s\"\n", this_fmt));
208
209       type = '\0';
210       value = &param.width;
211
212       param.base = 10;
213       param.conv = 0;
214       param.expfmt = "e%c%02ld";
215       param.exptimes4 = 0;
216       param.fill = ' ';
217       param.justify = DOPRNT_JUSTIFY_RIGHT;
218       param.prec = 6;
219       param.showbase = DOPRNT_SHOWBASE_NO;
220       param.showpoint = 0;
221       param.showtrailing = 1;
222       param.sign = '\0';
223       param.width = 0;
224       seen_precision = 0;
225
226       /* This loop parses a single % sequence.  "break" from the switch
227          means continue with this %, "goto next" means the conversion
228          character has been seen and a new % should be sought.  */
229       for (;;)
230         {
231           fchar = *fmt++;
232           if (fchar == '\0')
233             break;
234
235           switch (fchar) {
236
237           case 'a':
238             /* %a behaves like %e, but defaults to all significant digits,
239                and there's no leading zeros on the exponent (which is in
240                fact bit-based) */
241             param.base = 16;
242             param.expfmt = "p%c%ld";
243             goto conv_a;
244           case 'A':
245             param.base = -16;
246             param.expfmt = "P%c%ld";
247           conv_a:
248             param.conv = DOPRNT_CONV_SCIENTIFIC;
249             param.exptimes4 = 1;
250             if (! seen_precision)
251               param.prec = -1;  /* default to all digits */
252             param.showbase = DOPRNT_SHOWBASE_YES;
253             param.showtrailing = 1;
254             goto floating_a;
255
256           case 'c':
257             /* Let's assume wchar_t will be promoted to "int" in the call,
258                the same as char will be. */
259             (void) va_arg (ap, int);
260             goto next;
261
262           case 'd':
263           case 'i':
264           case 'u':
265           integer:
266             TRACE (printf ("integer, base=%d\n", param.base));
267             if (! seen_precision)
268               param.prec = -1;
269             switch (type) {
270             case 'j':
271               /* Let's assume uintmax_t is the same size as intmax_t. */
272 #if HAVE_INTMAX_T
273               (void) va_arg (ap, intmax_t);
274 #else
275               ASSERT_FAIL (intmax_t not available);
276 #endif
277               break;
278             case 'l':
279               (void) va_arg (ap, long);
280               break;
281             case 'L':
282 #if HAVE_LONG_LONG
283               (void) va_arg (ap, long long);
284 #else
285               ASSERT_FAIL (long long not available);
286 #endif
287               break;
288             case 'N':
289               {
290                 mp_ptr     xp;
291                 mp_size_t  xsize, abs_xsize;
292                 mpz_t      z;
293                 FLUSH ();
294                 xp = va_arg (ap, mp_ptr);
295                 PTR(z) = xp;
296                 xsize = (int) va_arg (ap, mp_size_t);
297                 abs_xsize = ABS (xsize);
298                 MPN_NORMALIZE (xp, abs_xsize);
299                 SIZ(z) = (xsize >= 0 ? abs_xsize : -abs_xsize);
300                 ASSERT_CODE (ALLOC(z) = abs_xsize);
301                 gmp_str = mpz_get_str (NULL, param.base, z);
302                 goto gmp_integer;
303               }
304               /* break; */
305             case 'q':
306               /* quad_t is probably the same as long long, but let's treat
307                  it separately just to be sure.  Also let's assume u_quad_t
308                  will be the same size as quad_t.  */
309 #if HAVE_QUAD_T
310               (void) va_arg (ap, quad_t);
311 #else
312               ASSERT_FAIL (quad_t not available);
313 #endif
314               break;
315             case 'Q':
316               FLUSH ();
317               gmp_str = mpq_get_str (NULL, param.base, va_arg(ap, mpq_srcptr));
318               goto gmp_integer;
319             case 't':
320 #if HAVE_PTRDIFF_T
321               (void) va_arg (ap, ptrdiff_t);
322 #else
323               ASSERT_FAIL (ptrdiff_t not available);
324 #endif
325               break;
326             case 'z':
327               (void) va_arg (ap, size_t);
328               break;
329             case 'Z':
330               {
331                 int   ret;
332                 FLUSH ();
333                 gmp_str = mpz_get_str (NULL, param.base,
334                                        va_arg (ap, mpz_srcptr));
335               gmp_integer:
336                 ret = __gmp_doprnt_integer (funs, data, &param, gmp_str);
337                 (*__gmp_free_func) (gmp_str, strlen(gmp_str)+1);
338                 DOPRNT_ACCUMULATE (ret);
339                 va_copy (last_ap, ap);
340                 last_fmt = fmt;
341               }
342               break;
343             default:
344               /* default is an "int", and this includes h=short and hh=char
345                  since they're promoted to int in a function call */
346               (void) va_arg (ap, int);
347               break;
348             }
349             goto next;
350
351           case 'E':
352             param.base = -10;
353             param.expfmt = "E%c%02ld";
354             /*FALLTHRU*/
355           case 'e':
356             param.conv = DOPRNT_CONV_SCIENTIFIC;
357           floating:
358             if (param.showbase == DOPRNT_SHOWBASE_NONZERO)
359               {
360                 /* # in %e, %f and %g */
361                 param.showpoint = 1;
362                 param.showtrailing = 1;
363               }
364           floating_a:
365             switch (type) {
366             case 'F':
367               FLUSH ();
368               DOPRNT_ACCUMULATE (__gmp_doprnt_mpf (funs, data, &param,
369                                                    GMP_DECIMAL_POINT,
370                                                    va_arg (ap, mpf_srcptr)));
371               va_copy (last_ap, ap);
372               last_fmt = fmt;
373               break;
374             case 'L':
375 #if HAVE_LONG_DOUBLE
376               (void) va_arg (ap, long double);
377 #else
378               ASSERT_FAIL (long double not available);
379 #endif
380               break;
381             default:
382               (void) va_arg (ap, double);
383               break;
384             }
385             goto next;
386
387           case 'f':
388             param.conv = DOPRNT_CONV_FIXED;
389             goto floating;
390
391           case 'F': /* mpf_t     */
392           case 'j': /* intmax_t  */
393           case 'L': /* long long */
394           case 'N': /* mpn       */
395           case 'q': /* quad_t    */
396           case 'Q': /* mpq_t     */
397           case 't': /* ptrdiff_t */
398           case 'z': /* size_t    */
399           case 'Z': /* mpz_t     */
400           set_type:
401             type = fchar;
402             break;
403
404           case 'G':
405             param.base = -10;
406             param.expfmt = "E%c%02ld";
407             /*FALLTHRU*/
408           case 'g':
409             param.conv = DOPRNT_CONV_GENERAL;
410             param.showtrailing = 0;
411             goto floating;
412
413           case 'h':
414             if (type != 'h')
415               goto set_type;
416             type = 'H';   /* internal code for "hh" */
417             break;
418
419           case 'l':
420             if (type != 'l')
421               goto set_type;
422             type = 'L';   /* "ll" means "L" */
423             break;
424
425           case 'm':
426             /* glibc strerror(errno), no argument */
427             goto next;
428
429           case 'M': /* mp_limb_t */
430             /* mung format string to l or ll and let plain printf handle it */
431 #if _LONG_LONG_LIMB
432             memmove (fmt+1, fmt, strlen (fmt)+1);
433             fmt[-1] = 'l';
434             fmt[0] = 'l';
435             fmt++;
436             type = 'L';
437 #else
438             fmt[-1] = 'l';
439             type = 'l';
440 #endif
441             break;
442
443           case 'n':
444             {
445               void  *p;
446               FLUSH ();
447               p = va_arg (ap, void *);
448               switch (type) {
449               case '\0': * (int       *) p = retval; break;
450               case 'F':  mpf_set_si ((mpf_ptr) p, (long) retval); break;
451               case 'H':  * (char      *) p = retval; break;
452               case 'h':  * (short     *) p = retval; break;
453 #if HAVE_INTMAX_T
454               case 'j':  * (intmax_t  *) p = retval; break;
455 #else
456               case 'j':  ASSERT_FAIL (intmax_t not available); break;
457 #endif
458               case 'l':  * (long      *) p = retval; break;
459 #if HAVE_QUAD_T && HAVE_LONG_LONG
460               case 'q':
461                 ASSERT_ALWAYS (sizeof (quad_t) == sizeof (long long));
462                 /*FALLTHRU*/
463 #else
464               case 'q':  ASSERT_FAIL (quad_t not available); break;
465 #endif
466 #if HAVE_LONG_LONG
467               case 'L':  * (long long *) p = retval; break;
468 #else
469               case 'L':  ASSERT_FAIL (long long not available); break;
470 #endif
471               case 'N':
472                 {
473                   mp_size_t  n;
474                   n = va_arg (ap, mp_size_t);
475                   n = ABS (n);
476                   if (n != 0)
477                     {
478                       * (mp_ptr) p = retval;
479                       MPN_ZERO ((mp_ptr) p + 1, n - 1);
480                     }
481                 }
482                 break;
483               case 'Q':  mpq_set_si ((mpq_ptr) p, (long) retval, 1L); break;
484 #if HAVE_PTRDIFF_T
485               case 't':  * (ptrdiff_t *) p = retval; break;
486 #else
487               case 't':  ASSERT_FAIL (ptrdiff_t not available); break;
488 #endif
489               case 'z':  * (size_t    *) p = retval; break;
490               case 'Z':  mpz_set_si ((mpz_ptr) p, (long) retval); break;
491               }
492             }
493             va_copy (last_ap, ap);
494             last_fmt = fmt;
495             goto next;
496
497           case 'o':
498             param.base = 8;
499             goto integer;
500
501           case 'p':
502           case 's':
503             /* "void *" will be good enough for "char *" or "wchar_t *", no
504                need for separate code.  */
505             (void) va_arg (ap, const void *);
506             goto next;
507
508           case 'x':
509             param.base = 16;
510             goto integer;
511           case 'X':
512             param.base = -16;
513             goto integer;
514
515           case '%':
516             goto next;
517
518           case '#':
519             param.showbase = DOPRNT_SHOWBASE_NONZERO;
520             break;
521
522           case '\'':
523             /* glibc digit grouping, just pass it through, no support for it
524                on gmp types */
525             break;
526
527           case '+':
528           case ' ':
529             param.sign = fchar;
530             break;
531
532           case '-':
533             param.justify = DOPRNT_JUSTIFY_LEFT;
534             break;
535           case '.':
536             seen_precision = 1;
537             param.prec = -1; /* "." alone means all necessary digits */
538             value = &param.prec;
539             break;
540
541           case '*':
542             {
543               int n = va_arg (ap, int);
544
545               if (value == &param.width)
546                 {
547                   /* negative width means left justify */
548                   if (n < 0)
549                     {
550                       param.justify = DOPRNT_JUSTIFY_LEFT;
551                       n = -n;
552                     }
553                   param.width = n;
554                 }
555               else
556                 {
557                   /* don't allow negative precision */
558                   param.prec = MAX (0, n);
559                 }
560             }
561             break;
562
563           case '0':
564             if (value == &param.width)
565               {
566                 /* in width field, set fill */
567                 param.fill = '0';
568
569                 /* for right justify, put the fill after any minus sign */
570                 if (param.justify == DOPRNT_JUSTIFY_RIGHT)
571                   param.justify = DOPRNT_JUSTIFY_INTERNAL;
572               }
573             else
574               {
575                 /* in precision field, set value */
576                 *value = 0;
577               }
578             break;
579
580           case '1': case '2': case '3': case '4': case '5':
581           case '6': case '7': case '8': case '9':
582             /* process all digits to form a value */
583             {
584               int  n = 0;
585               do {
586                 n = n * 10 + (fchar-'0');
587                 fchar = *fmt++;
588               } while (isascii (fchar) && isdigit (fchar));
589               fmt--; /* unget the non-digit */
590               *value = n;
591             }
592             break;
593
594           default:
595             /* something invalid */
596             ASSERT (0);
597             goto next;
598           }
599         }
600
601     next:
602       /* Stop parsing the current "%" format, look for a new one. */
603       ;
604     }
605
606   TRACE (printf ("remainder: \"%s\"\n", last_fmt));
607   if (*last_fmt != '\0')
608     DOPRNT_FORMAT (last_fmt, last_ap);
609
610   if (funs->final != NULL)
611     if ((*funs->final) (data) == -1)
612       goto error;
613
614  done:
615   (*__gmp_free_func) (alloc_fmt, alloc_fmt_size);
616   return retval;
617
618  error:
619   retval = -1;
620   goto done;
621 }