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-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 
157 int
__gmp_doprnt(const struct doprnt_funs_t * funs,void * data,const char * orig_fmt,va_list orig_ap)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