doprnt.c
上传用户:qaz666999
上传日期:2022-08-06
资源大小:2570k
文件大小:17k
源码类别:

数学计算

开发平台:

Unix_Linux

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