home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / glibc-1.06 / stdio / vfprintf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-08  |  14.5 KB  |  689 lines

  1. /* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3.  
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public License as
  6. published by the Free Software Foundation; either version 2 of the
  7. License, or (at your option) any later version.
  8.  
  9. The GNU C Library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. Library General Public License for more details.
  13.  
  14. You should have received a copy of the GNU Library General Public
  15. License along with the GNU C Library; see the file COPYING.LIB.  If
  16. not, write to the Free Software Foundation, Inc., 675 Mass Ave,
  17. Cambridge, MA 02139, USA.  */
  18.  
  19. #include <ansidecl.h>
  20. #include <localeinfo.h>
  21. #include <ctype.h>
  22. #include <errno.h>
  23. #include <float.h>
  24. #include <limits.h>
  25. #include <math.h>
  26. #include <stdarg.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <printf.h>
  31.  
  32.  
  33. /* If it's an unbuffered stream that we provided
  34.    temporary buffering for, remove that buffering.  */
  35. #define    RETURN(x)                                  \
  36.   do                                          \
  37.     {                                          \
  38.       done = (x);                                  \
  39.       goto do_return;                                  \
  40.     } while (0)
  41.  
  42. #define    outchar(x)                                  \
  43.   do                                          \
  44.     {                                          \
  45.       register CONST int outc = (x);                          \
  46.       if (putc(outc, s) == EOF)                              \
  47.     RETURN(-1);                                  \
  48.       else                                      \
  49.     ++done;                                      \
  50.     } while (0)
  51.  
  52. /* Cast the next arg, of type ARGTYPE, into CASTTYPE, and put it in VAR.  */
  53. #define    castarg(var, argtype, casttype) \
  54.   var = (casttype) va_arg(args, argtype)
  55. /* Get the next arg, of type TYPE, and put it in VAR.  */
  56. #define    nextarg(var, type)    castarg(var, type, type)
  57.  
  58. static printf_function printf_unknown;
  59.  
  60. extern printf_function **__printf_function_table;
  61.  
  62. #ifdef    __GNUC__
  63. #define    HAVE_LONGLONG
  64. #define    LONGLONG    long long
  65. #else
  66. #define    LONGLONG    long
  67. #endif
  68.  
  69.  
  70. int
  71. DEFUN(vfprintf, (s, format, args),
  72.       register FILE *s AND CONST char *format AND va_list args)
  73. {
  74.   /* Lower-case digits.  */
  75.   static CONST char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
  76.   /* Upper-case digits.  */
  77.   static CONST char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  78.   /* Base-36 digits for numbers.  */
  79.   CONST char *digits = lower_digits;
  80.  
  81.   /* Pointer into the format string.  */
  82.   register CONST char *f;
  83.  
  84.   /* Number of characters written.  */
  85.   register size_t done = 0;
  86.  
  87.   /* Nonzero we're providing buffering.  */
  88.   char our_buffer;
  89.   /* Temporary buffer for unbuffered streams.  */
  90.   char temporary_buffer[BUFSIZ];
  91.  
  92.   if (!__validfp(s) || !s->__mode.__write || format == NULL)
  93.     {
  94.       errno = EINVAL;
  95.       return -1;
  96.     }
  97.  
  98.   if (!s->__seen)
  99.     {
  100.       if (__flshfp(s, EOF) == EOF)
  101.     return EOF;
  102.     }
  103.  
  104.   our_buffer = s->__buffer == NULL;
  105.   if (our_buffer)
  106.     {
  107.       /* If it's an unbuffered stream, buffer it
  108.      at least inside this function call.  */
  109.       s->__bufp = s->__buffer = temporary_buffer;
  110.       s->__bufsize = sizeof(temporary_buffer);
  111.       s->__put_limit = s->__buffer + s->__bufsize;
  112.       s->__get_limit = s->__buffer;
  113.     }
  114.  
  115.   /* Reset multibyte characters to their initial state.  */
  116.   (void) mblen((char *) NULL, 0);
  117.  
  118.   f = format;
  119.   while (*f != '\0')
  120.     {
  121.       /* Type modifiers.  */
  122.       char is_short, is_long, is_long_double;
  123. #ifdef    HAVE_LONGLONG
  124.       /* We use the `L' modifier for `long long int'.  */
  125. #define    is_longlong    is_long_double
  126. #else
  127. #define    is_longlong    0
  128. #endif
  129.       /* Format spec modifiers.  */
  130.       char space, showsign, left, alt;
  131.  
  132.       /* Padding character: ' ' or '0'.  */
  133.       char pad;
  134.       /* Width of a field.  */
  135.       register int width;
  136.       /* Precision of a field.  */
  137.       int prec;
  138.  
  139.       /* Decimal integer is negative.  */
  140.       char is_neg;
  141.  
  142.       /* Current character of the format.  */
  143.       char fc;
  144.  
  145.       /* Base of a number to be written.  */
  146.       int base;
  147.       /* Integral values to be written.  */
  148.       unsigned LONGLONG int num;
  149.       LONGLONG int signed_num;
  150.  
  151.       /* String to be written.  */
  152.       CONST char *str;
  153.       char unknown_error[256];    /* Buffer sometimes used by %m.  */
  154.  
  155.       /* Auxiliary function to do output.  */
  156.       printf_function *function;
  157.  
  158.       if (!isascii(*f))
  159.     {
  160.       /* Non-ASCII, may be a multibyte.  */
  161.       int len = mblen(f, strlen(f));
  162.       if (len > 0)
  163.         {
  164.           while (len-- > 0)
  165.         outchar(*f++);
  166.           continue;
  167.         }
  168.     }
  169.  
  170.       if (*f != '%')
  171.     {
  172.       /* This isn't a format spec, so write
  173.          everything out until the next one.  */
  174.       CONST char *next = strchr(f + 1, '%');
  175.       if (next == NULL)
  176.         next = strchr(f + 1, '\0');
  177.       if (next - f > 20)
  178.         {
  179.           size_t written = fwrite((PTR) f, 1, next - f, s);
  180.           done += written;
  181.           if (written != next - f)
  182.         break;
  183.           f += written;
  184.         }
  185.       else
  186.         while (f < next)
  187.           outchar(*f++);
  188.       continue;
  189.     }
  190.  
  191.       ++f;
  192.  
  193.       /* Check for "%%".  Note that although the ANSI standard lists
  194.      '%' as a conversion specifier, it says "The complete format
  195.      specification shall be `%%'," so we can avoid all the width
  196.      and precision processing.  */
  197.       if (*f == '%')
  198.     {
  199.       ++f;
  200.       outchar('%');
  201.       continue;
  202.     }
  203.  
  204.       /* Check for spec modifiers.  */
  205.       space = showsign = left = alt = 0;
  206.       pad = ' ';
  207.       while (*f == ' ' || *f == '+' || *f == '-' || *f == '#' || *f == '0')
  208.     switch (*f++)
  209.       {
  210.       case ' ':
  211.         /* Output a space in place of a sign, when there is no sign.  */
  212.         space = 1;
  213.         break;
  214.       case '+':
  215.         /* Always output + or - for numbers.  */
  216.         showsign = 1;
  217.         break;
  218.       case '-':
  219.         /* Left-justify things.  */
  220.         left = 1;
  221.         break;
  222.       case '#':
  223.         /* Use the "alternate form":
  224.            Hex has 0x or 0X, FP always has a decimal point.  */
  225.         alt = 1;
  226.         break;
  227.       case '0':
  228.         /* Pad with 0s.  */
  229.         pad = '0';
  230.         break;
  231.       }
  232.       if (left)
  233.     pad = ' ';
  234.  
  235.       /* Get the field width.  */
  236.       width = 0;
  237.       if (*f == '*')
  238.     {
  239.       /* The field width is given in an argument.
  240.          A negative field width indicates left justification.  */
  241.       nextarg(width, int);
  242.       if (width < 0)
  243.         {
  244.           width = - width;
  245.           left = 1;
  246.         }
  247.       ++f;
  248.     }
  249.       else
  250.     while (isdigit(*f))
  251.       {
  252.         width *= 10;
  253.         width += *f++ - '0';
  254.       }
  255.  
  256.       /* Get the precision.  */
  257.       /* -1 means none given; 0 means explicit 0.  */
  258.       prec = -1;
  259.       if (*f == '.')
  260.     {
  261.       ++f;
  262.       if (*f == '*')
  263.         {
  264.           /* The precision is given in an argument.  */
  265.           nextarg(prec, int);
  266.           /* Avoid idiocy.  */
  267.           if (prec < 0)
  268.         prec = -1;
  269.           ++f;
  270.         }
  271.       else if (isdigit(*f))
  272.         {
  273.           prec = 0;
  274.           while (*f != '\0' && isdigit(*f))
  275.         {
  276.           prec *= 10;
  277.           prec += *f++ - '0';
  278.         }
  279.         }
  280.     }
  281.  
  282.       /* Check for type modifiers.  */
  283.       is_short = is_long = is_long_double = 0;
  284.       while (*f == 'h' || *f == 'l' || *f == 'L')
  285.     switch (*f++)
  286.       {
  287.       case 'h':
  288.         /* int's are short int's.  */
  289.         is_short = 1;
  290.         break;
  291.       case 'l':
  292. #ifdef    HAVE_LONGLONG
  293.         if (is_long)
  294.           /* A double `l' is equivalent to an `L'.  */
  295.           is_longlong = 1;
  296.         else
  297. #endif
  298.           /* int's are long int's.  */
  299.           is_long = 1;
  300.         break;
  301.       case 'L':
  302.         /* double's are long double's, and int's are long long int's.  */
  303.         is_long_double = 1;
  304.         break;
  305.       }
  306.  
  307.       /* Format specification.  */
  308.       fc = *f++;
  309.       function = (__printf_function_table == NULL ? NULL :
  310.           __printf_function_table[fc]);
  311.       if (function == NULL)
  312.     switch (fc)
  313.       {
  314.       case 'i':
  315.       case 'd':
  316.         /* Decimal integer.  */
  317.         base = 10;
  318.         if (is_longlong)
  319.           nextarg(signed_num, LONGLONG int);
  320.         else if (is_long)
  321.           nextarg(signed_num, long int);
  322.         else if (!is_short)
  323.           castarg(signed_num, int, long int);
  324.         else
  325.           castarg(signed_num, int, short int);
  326.  
  327.         is_neg = signed_num < 0;
  328.         num = is_neg ? (- signed_num) : signed_num;
  329.         goto number;
  330.  
  331.       case 'Z':
  332.         /* `size_t' value.  */
  333. #ifdef    HAVE_LONGLONG
  334.         if (sizeof(size_t) > sizeof(unsigned long long int))
  335.           __libc_fatal("`size_t' is bigger than any known type!");
  336.         else
  337.           is_longlong = sizeof(size_t) > sizeof(unsigned long int);
  338. #endif
  339.         is_long = sizeof(size_t) > sizeof(unsigned int);
  340.  
  341.         /* Fall through, to print a `size_t' as a decimal integer.  */
  342.  
  343.       case 'u':
  344.         /* Decimal unsigned integer.  */
  345.         base = 10;
  346.         goto unsigned_number;
  347.  
  348.       case 'o':
  349.         /* Octal unsigned integer.  */
  350.         base = 8;
  351.         goto unsigned_number;
  352.  
  353.       case 'X':
  354.         /* Hexadecimal unsigned integer.  */
  355.       case 'x':
  356.         /* Hex with lower-case digits.  */
  357.  
  358.         digits = fc == 'X' ? upper_digits : lower_digits;
  359.  
  360.         base = 16;
  361.  
  362.       unsigned_number:
  363.         /* Unsigned number of base BASE.  */
  364.  
  365.         if (is_longlong)
  366.           castarg(num, LONGLONG int, unsigned LONGLONG int);
  367.         else if (is_long)
  368.           castarg(num, long int, unsigned long int);
  369.         else if (!is_short)
  370.           castarg(num, int, unsigned int);
  371.         else
  372.           castarg(num, int, unsigned short int);
  373.  
  374.         /* ANSI only specifies the `+' and
  375.            ` ' flags for signed conversions.  */
  376.         is_neg = showsign = space = 0;
  377.  
  378.       number:
  379.         /* Number of base BASE.  */
  380.         {
  381.           char work[BUFSIZ];
  382.           char *CONST workend = &work[sizeof(work) - 1];
  383.           register char *w;
  384.  
  385.           /* Supply a default precision if none was given.  */
  386.           if (prec == -1)
  387.         prec = 1;
  388.  
  389.           /* Put the number in WORK.  */
  390.           w = workend;
  391.           while (num > 0)
  392.         {
  393.           *w-- = digits[num % base];
  394.           num /= base;
  395.         }
  396.           width -= workend - w;
  397.           prec -= workend - w;
  398.  
  399.           if (alt && base == 8 && prec <= 0)
  400.         {
  401.           *w-- = '0';
  402.           --width;
  403.         }
  404.  
  405.           if (prec > 0)
  406.         {
  407.           width -= prec;
  408.           while (prec-- > 0)
  409.             *w-- = '0';
  410.         }
  411.  
  412.           if (alt && base == 16)
  413.         width -= 2;
  414.  
  415.           if (is_neg || showsign || space)
  416.         --width;
  417.  
  418.           if (!left && pad == ' ')
  419.         while (width-- > 0)
  420.           outchar(' ');
  421.  
  422.           if (is_neg)
  423.         outchar('-');
  424.           else if (showsign)
  425.         outchar('+');
  426.           else if (space)
  427.         outchar(' ');
  428.  
  429.           if (alt && base == 16)
  430.         {
  431.           outchar ('0');
  432.           outchar (fc);
  433.         }
  434.  
  435.           if (!left && pad == '0')
  436.         while (width-- > 0)
  437.           outchar('0');
  438.  
  439.           /* Write the number.  */
  440.           while (++w <= workend)
  441.         outchar(*w);
  442.  
  443.           if (left)
  444.         while (width-- > 0)
  445.           outchar(' ');
  446.         }
  447.         break;
  448.  
  449.       case 'e':
  450.       case 'E':
  451.       case 'f':
  452.       case 'g':
  453.       case 'G':
  454.         {
  455.           /* Floating-point number.  */
  456.           extern printf_function __printf_fp;
  457.           function = __printf_fp;
  458.           goto use_function;
  459.         }
  460.  
  461.       case 'c':
  462.         /* Character.  */
  463.         nextarg(num, int);
  464.         if (!left)
  465.           while (--width > 0)
  466.         outchar(' ');
  467.         outchar((unsigned char) num);
  468.         if (left)
  469.           while (--width > 0)
  470.         outchar(' ');
  471.         break;
  472.  
  473.       case 's':
  474.         {
  475.           static CONST char null[] = "(null)";
  476.           size_t len;
  477.  
  478.           nextarg(str, CONST char *);
  479.  
  480.         string:
  481.  
  482.           if (str == NULL)
  483.         /* Write "(null)" if there's space.  */
  484.         if (prec == -1 || prec >= (int) sizeof(null) - 1)
  485.           {
  486.             str = null;
  487.             len = sizeof(null) - 1;
  488.           }
  489.         else
  490.           {
  491.             str = "";
  492.             len = 0;
  493.           }
  494.           else
  495.         len = strlen(str);
  496.  
  497.           if (prec != -1 && (size_t) prec < len)
  498.         len = prec;
  499.           width -= len;
  500.  
  501.           if (!left)
  502.         while (width-- > 0)
  503.           outchar(' ');
  504.           if (len < 20)
  505.         while (len-- > 0)
  506.           outchar(*str++);
  507.           else
  508.         if (fwrite(str, 1, len, s) != len)
  509.           RETURN(-1);
  510.         else
  511.           done += len;
  512.           if (left)
  513.         while (width-- > 0)
  514.           outchar(' ');
  515.         }
  516.         break;
  517.  
  518.       case 'p':
  519.         /* Generic pointer.  */
  520.         {
  521.           CONST PTR ptr;
  522.           nextarg(ptr, CONST PTR);
  523.           if (ptr != NULL)
  524.         {
  525.           /* If the pointer is not NULL, write it as a %#x spec.  */
  526.           base = 16;
  527.           fc = 'x';
  528.           alt = 1;
  529.           num = (unsigned LONGLONG int) (unsigned long int) ptr;
  530.           is_neg = 0;
  531.           goto number;
  532.         }
  533.           else
  534.         {
  535.           /* Write "(nil)" for a nil pointer.  */
  536.           static CONST char nil[] = "(nil)";
  537.           register CONST char *p;
  538.  
  539.           width -= sizeof (nil) - 1;
  540.           if (!left)
  541.             while (width-- > 0)
  542.               outchar (' ');
  543.           for (p = nil; *p != '\0'; ++p)
  544.             outchar (*p);
  545.           if (left)
  546.             while (width-- > 0)
  547.               outchar (' ');
  548.         }
  549.         }
  550.         break;
  551.  
  552.       case 'n':
  553.         /* Answer the count of characters written.  */
  554.         if (is_longlong)
  555.           {
  556.         LONGLONG int *p;
  557.         nextarg(p, LONGLONG int *);
  558.         *p = done;
  559.           }
  560.         else if (is_long)
  561.           {
  562.         long int *p;
  563.         nextarg(p, long int *);
  564.         *p = done;
  565.           }
  566.         else if (!is_short)
  567.           {
  568.         int *p;
  569.         nextarg(p, int *);
  570.         *p = done;
  571.           }
  572.         else
  573.           {
  574.         short int *p;
  575.         nextarg(p, short int *);
  576.         *p = done;
  577.           }
  578.         break;
  579.  
  580.       case 'm':
  581. #ifndef HAVE_GNU_LD
  582. #define _sys_errlist sys_errlist
  583. #define _sys_nerr sys_nerr
  584. #endif
  585.         if (errno < 0 || errno > _sys_nerr)
  586.           {
  587.         sprintf (unknown_error, "Unknown error %d", errno);
  588.         str = unknown_error;
  589.           }
  590.         else
  591.           str = _sys_errlist[errno];
  592.         goto string;
  593.  
  594.       default:
  595.         /* Unrecognized format specifier.  */
  596.         function = printf_unknown;
  597.         goto use_function;
  598.       }
  599.       else
  600.       use_function:
  601.     {
  602.       int function_done;
  603.       struct printf_info info;
  604.  
  605.       info.prec = prec;
  606.       info.width = width;
  607.       info.spec = fc;
  608.       info.is_long_double = is_long_double;
  609.       info.is_short = is_short;
  610.       info.is_long = is_long;
  611.       info.alt = alt;
  612.       info.space = space;
  613.       info.left = left;
  614.       info.showsign = showsign;
  615.       info.pad = pad;
  616.  
  617.       function_done = (*function)(s, &info, &args);
  618.       if (function_done < 0)
  619.         RETURN(-1);
  620.  
  621.       done += function_done;
  622.     }
  623.     }
  624.  
  625.  do_return:;
  626.   if (our_buffer)
  627.     {
  628.       if (fflush(s) == EOF)
  629.     return -1;
  630.       s->__buffer = s->__bufp = s->__get_limit = s->__put_limit = NULL;
  631.       s->__bufsize = 0;
  632.     }
  633.   return done;
  634. }
  635.  
  636.  
  637. #undef    RETURN
  638. #define    RETURN    return
  639.  
  640. static int
  641. DEFUN(printf_unknown, (s, type, info, arg),
  642.       FILE *s AND CONST struct printf_info *info AND va_list *arg)
  643. {
  644.   int done = 0;
  645.   char work[BUFSIZ];
  646.   char *CONST workend = &work[sizeof(work) - 1];
  647.   register char *w;
  648.   register int prec = info->prec, width = info->width;
  649.  
  650.   outchar('%');
  651.  
  652.   if (info->alt)
  653.     outchar('#');
  654.   if (info->showsign)
  655.     outchar('+');
  656.   else if (info->space)
  657.     outchar(' ');
  658.   if (info->left)
  659.     outchar('-');
  660.   if (info->pad == '0')
  661.     outchar('0');
  662.  
  663.   w = workend;
  664.   while (width > 0)
  665.     {
  666.       *w-- = '0' + (width % 10);
  667.       width /= 10;
  668.     }
  669.   while (++w <= workend)
  670.     outchar(*w);
  671.  
  672.   if (info->prec != -1)
  673.     {
  674.       outchar('.');
  675.       w = workend;
  676.       while (prec > 0)
  677.     {
  678.       *w-- = '0' + (prec % 10);
  679.       prec /= 10;
  680.     }
  681.       while (++w <= workend)
  682.     outchar(*w);
  683.     }
  684.  
  685.   outchar(info->spec);
  686.  
  687.   return done;
  688. }
  689.