Formerly ../stdio/vfprintf.c.~8~
[kopensolaris-gnu/glibc.git] / stdio / vfprintf.c
1 /* Copyright (C) 1991, 1992 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             is_neg = 0;
375
376           number:;
377             /* Number of base BASE.  */
378             {
379               char work[BUFSIZ];
380               char *CONST workend = &work[sizeof(work) - 1];
381               register char *w;
382
383               /* Supply a default precision if none was given.  */
384               if (prec == -1)
385                 prec = 1;
386
387               /* Put the number in WORK.  */
388               w = workend;
389               while (num > 0)
390                 {
391                   *w-- = digits[num % base];
392                   num /= base;
393                 }
394               width -= workend - w;
395               prec -= workend - w;
396
397               if (alt && base == 8 && prec <= 0)
398                 {
399                   *w-- = '0';
400                   --width;
401                 }
402
403               if (prec > 0)
404                 {
405                   width -= prec;
406                   while (prec-- > 0)
407                     *w-- = '0';
408                 }
409
410               if (alt && base == 16)
411                 width -= 2;
412
413               if (is_neg || showsign || space)
414                 --width;
415
416               if (!left && pad == ' ')
417                 while (width-- > 0)
418                   outchar(' ');
419
420               if (is_neg)
421                 outchar('-');
422               else if (showsign)
423                 outchar('+');
424               else if (space)
425                 outchar(' ');
426
427               if (!left && pad == '0')
428                 while (width-- > 0)
429                   outchar('0');
430
431               if (alt && base == 16)
432                 {
433                   outchar ('0');
434                   outchar (fc);
435                 }
436
437               /* Write the number.  */
438               while (++w <= workend)
439                 outchar(*w);
440
441               if (left)
442                 while (width-- > 0)
443                   outchar(' ');
444             }
445             break;
446
447           case 'e':
448           case 'E':
449           case 'f':
450           case 'g':
451           case 'G':
452             {
453               /* Floating-point number.  */
454               extern printf_function __printf_fp;
455               function = __printf_fp;
456               goto use_function;
457             }
458
459           case 'c':
460             /* Character.  */
461             nextarg(num, int);
462             if (!left)
463               while (--width > 0)
464                 outchar(' ');
465             outchar((unsigned char) num);
466             if (left)
467               while (--width > 0)
468                 outchar(' ');
469             break;
470
471           case 's':
472             {
473               static CONST char null[] = "(null)";
474               size_t len;
475
476               nextarg(str, CONST char *);
477
478             string:
479
480               if (str == NULL)
481                 /* Write "(null)" if there's space.  */
482                 if (prec == -1 || prec >= (int) sizeof(null) - 1)
483                   {
484                     str = null;
485                     len = sizeof(null) - 1;
486                   }
487                 else
488                   {
489                     str = "";
490                     len = 0;
491                   }
492               else
493                 len = strlen(str);
494
495               if (prec != -1 && (size_t) prec < len)
496                 len = prec;
497               width -= len;
498
499               if (!left)
500                 while (width-- > 0)
501                   outchar(' ');
502               if (len < 20)
503                 while (len-- > 0)
504                   outchar(*str++);
505               else
506                 if (fwrite(str, 1, len, s) != len)
507                   RETURN(-1);
508                 else
509                   done += len;
510               if (left)
511                 while (width-- > 0)
512                   outchar(' ');
513             }
514             break;
515
516           case 'p':
517             /* Generic pointer.  */
518             {
519               CONST PTR ptr;
520               nextarg(ptr, CONST PTR);
521               if (ptr != NULL)
522                 {
523                   /* If the pointer is not NULL, write it as a %#x spec.  */
524                   base = 16;
525                   fc = 'x';
526                   alt = 1;
527                   num = (unsigned LONGLONG int) (unsigned long int) ptr;
528                   is_neg = 0;
529                   goto number;
530                 }
531               else
532                 {
533                   /* Write "(nil)" for a nil pointer.  */
534                   static CONST char nil[] = "(nil)";
535                   register CONST char *p;
536
537                   width -= sizeof(nil) - 1;
538                   if (left)
539                     while (width-- > 0)
540                       outchar(' ');
541                   for (p = nil; *p != '\0'; ++p)
542                     outchar(*p);
543                   if (!left)
544                     while (width-- > 0)
545                       outchar(' ');
546                 }
547             }
548             break;
549
550           case 'n':
551             /* Answer the count of characters written.  */
552             if (is_longlong)
553               {
554                 LONGLONG int *p;
555                 nextarg(p, LONGLONG int *);
556                 *p = done;
557               }
558             else if (is_long)
559               {
560                 long int *p;
561                 nextarg(p, long int *);
562                 *p = done;
563               }
564             else if (!is_short)
565               {
566                 int *p;
567                 nextarg(p, int *);
568                 *p = done;
569               }
570             else
571               {
572                 short int *p;
573                 nextarg(p, short int *);
574                 *p = done;
575               }
576             break;
577
578           case 'm':
579 #ifndef HAVE_GNU_LD
580 #define _sys_errlist sys_errlist
581 #define _sys_nerr sys_nerr
582 #endif
583             if (errno < 0 || errno > _sys_nerr)
584               {
585                 sprintf (unknown_error, "Unknown error %d", errno);
586                 str = unknown_error;
587               }
588             else
589               str = _sys_errlist[errno];
590             goto string;
591
592           default:
593             /* Unrecognized format specifier.  */
594             function = printf_unknown;
595             goto use_function;
596           }
597       else
598       use_function:
599         {
600           int function_done;
601           struct printf_info info;
602
603           info.prec = prec;
604           info.width = width;
605           info.spec = fc;
606           info.is_long_double = is_long_double;
607           info.is_short = is_short;
608           info.is_long = is_long;
609           info.alt = alt;
610           info.space = space;
611           info.left = left;
612           info.showsign = showsign;
613           info.pad = pad;
614
615           function_done = (*function)(s, &info, &args);
616           if (function_done < 0)
617             RETURN(-1);
618
619           done += function_done;
620         }
621     }
622
623  do_return:;
624   if (our_buffer)
625     {
626       if (fflush(s) == EOF)
627         return -1;
628       s->__buffer = s->__bufp = s->__get_limit = s->__put_limit = NULL;
629       s->__bufsize = 0;
630     }
631   return done;
632 }
633
634
635 #undef  RETURN
636 #define RETURN  return
637
638 static int
639 DEFUN(printf_unknown, (s, type, info, arg),
640       FILE *s AND CONST struct printf_info *info AND va_list *arg)
641 {
642   int done = 0;
643   char work[BUFSIZ];
644   char *CONST workend = &work[sizeof(work) - 1];
645   register char *w;
646   register int prec = info->prec, width = info->width;
647
648   outchar('%');
649
650   if (info->alt)
651     outchar('#');
652   if (info->showsign)
653     outchar('+');
654   else if (info->space)
655     outchar(' ');
656   if (info->left)
657     outchar('-');
658   if (info->pad == '0')
659     outchar('0');
660
661   w = workend;
662   while (width > 0)
663     {
664       *w-- = '0' + (width % 10);
665       width /= 10;
666     }
667   while (++w <= workend)
668     outchar(*w);
669
670   if (info->prec != -1)
671     {
672       outchar('.');
673       w = workend;
674       while (prec > 0)
675         {
676           *w-- = '0' + (prec % 10);
677           prec /= 10;
678         }
679       while (++w <= workend)
680         outchar(*w);
681     }
682
683   outchar(info->spec);
684
685   return done;
686 }