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