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