unused var removed
[libfirm] / ir / common / xgprintf.c
1 /* Xgprintf --- extended formatted output via generic printer functions.
2    Copyright (C) 1995, 1996 Christian von Roques */
3
4 /* $Id$ */
5
6 /* Implements ANSI-C printf formats minus locale plus extensions,
7    noteably GNU Libc-like registering of specifier-handlers.  */
8
9 #ifdef HAVE_CONFIG_H
10 # include <config.h>
11 #endif
12
13 #ifndef USE_PRINTF
14
15 #include <assert.h>
16 #include <ctype.h>
17 #include <errno.h>
18 #include <limits.h>
19 #include <string.h>
20 #include "misc.h"
21 #include "xprintf.h"
22
23
24 /* >= size of ASCII representation of a number using base 8 + 1 */
25 #define BUFSIZE ((sizeof(unsigned long)*CHAR_BIT)/3 + 2)
26
27 /* For printing double/float numbers, must be large enough for
28    arbitrary numbers with %g.  @@@ Yeek! */
29 #define FBUFSIZE 128
30
31 static xprintf_function *print_func[UCHAR_MAX+1];
32
33
34 void
35 xprintf_register (char spec, xprintf_function func)
36 {
37   assert ((spec > 0) && !print_func[(unsigned char)spec]);
38   print_func[(unsigned char)spec] = func;
39 }
40
41 int
42 xvgprintf (xgprintf_func *out, void *arg, const char *fmt, va_list args)
43 {
44   int done;             /* number of chars printed */
45   unsigned long num;
46   int is_neg;
47   long signed_num;
48   int base;
49   char c;
50   char fbuf[FBUFSIZE];  /* buffer for sprintf @@@ Yeek! */
51   char buf[BUFSIZE];    /* buffer for itoa */
52   char *str;            /* buffer pointer for number conversion */
53   const char *s;        /* string to be printed by string: */
54   int len;              /* length of s */
55   char pad;             /* padding: ' ' or '0' */
56   int showsign;         /* always show sign ['+'] */
57   int space;            /* print space if positive */
58   int left;             /* left justify */
59   int alt;              /* alternate format 0x... */
60   char fc;              /* conversion specifier */
61   int width;            /* width of output field */
62   int prec;             /* min. # of digits for integers; max
63                            number of chars for from string */
64   int qualifier;        /* 'h', 'l', or 'L' for integer fields */
65
66   done = 0;
67
68 #define P(__b,__l) do { out (arg, __b, __l); done += __l; } while (0)
69
70   while (*fmt) {
71     const char *next = strchr (fmt, '%');
72
73     if (!next) {
74       P (fmt, strlen(fmt));
75       break;
76     } else if (next != fmt) {
77       P (fmt, next-fmt);
78       fmt = next;
79     }
80
81     /* Check for "%%".  Note that although the ANSI standard lists
82        '%' as a conversion specifier, it says "The complete format
83        specification shall be `%%'," so we can avoid all the width
84        and prec processing.  */
85     if (fmt[1] == '%') {
86       P (fmt, 1);
87       fmt += 2;
88       continue;
89     }
90
91     /* process flags */
92     is_neg = showsign = space = left = alt = 0;  pad = ' ';
93
94   repeat:
95     ++fmt;                      /* this also skips first '%' */
96     switch (*fmt) {
97     case '-': left = 1; goto repeat;
98     case '+': showsign = 1; goto repeat;
99     case ' ': space = 1; goto repeat;
100     case '#': alt = 1; goto repeat;
101     case '0': pad = '0'; goto repeat;
102     }
103
104     /* get field width */
105     width = 0;
106     if (*fmt == '*') {
107       ++fmt, width = va_arg(args, int);
108       if (width < 0) left = 1, width = -width;
109     } else while (isdigit(*fmt)) width = 10*width + *fmt++ - '0';
110
111     /* get the prec */
112     if (*fmt == '.') {
113       ++fmt;
114       if (*fmt == '*') ++fmt, prec = va_arg(args, int);
115       else { prec = 0; while (isdigit(*fmt)) prec = 10*prec + *fmt++ - '0'; }
116       if (prec < 0) prec = 0;
117     } else prec = -1;           /* -1 == unspecified */
118
119     /* get the conversion qualifier */
120     if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
121       qualifier = *fmt++;
122     else
123       qualifier = -1;
124
125     fc = *fmt++;
126
127     if (print_func[(unsigned char)fc]) {
128       xprintf_info info;
129       int func_done;
130
131       info.prec = prec;
132       info.width = width;
133       info.spec = fc;
134       info.is_long_double = qualifier == 'L';
135       info.is_short       = qualifier == 'h';
136       info.is_long        = qualifier == 'l';
137       info.alt = alt;
138       info.space = space;
139       info.left = left;
140       info.showsign = showsign;
141       info.pad = pad;
142
143       /* Sharing `args' with another function is not blessed by ANSI
144          C.  From ISO/IEC DIS 9899, section 4.4:
145
146          If access to the varying arguments is desired, the called
147          function shall declare an object (referred as `ap' in this
148          section) having type va_list.  The object `ap' may be passed
149          as an argument to another function; if that function invokes
150          the va_arg macro with parameter `ap', the value of `ap' in
151          the calling function is indeterminate and shall be passed to
152          the va_end macro prior to any futher reference to `ap'.
153
154          Nevertheless, it works with most compilers, including gcc.  */
155       func_done = print_func[(unsigned char)fc] (out, arg, &info, &args);
156       if (func_done < 0) return -1;
157       else done += func_done;
158
159     } else {
160
161       /* default base */
162       base = 10;
163
164       switch (fc) {
165
166       case 'c':                 /* Character */
167                 { c = (char) va_arg (args, int);
168                 s = &c;
169                 len = 1;
170                 goto string;
171                 }
172
173       case 's':                 /* String */
174                 { static const char null[] = "(null)";
175                 s = va_arg(args, char *);
176                 if (!s) {
177                   s = null;
178                   len = (prec == -1 || prec >= (int) sizeof(null) - 1) ? sizeof(null) - 1 : 0;
179                 } else {
180                   len = strlen (s);
181                 }
182
183                 string:
184                 if (prec >= 0 && prec < len)
185                   len = prec;
186                 width -= len;
187
188                 if (!left)
189                   while (width-- > 0)
190                         P (" ", 1);
191                 P (s, len);
192                 while (width-- > 0)
193                   P (" ", 1);
194                 break;
195                 }
196       case 'p':                 /* Pointer */
197                 {       const char nil[] = "(nil)";
198                 const void *ptr = va_arg (args, void *);
199                 if (!ptr && (prec==-1 || prec>=(int)sizeof(nil)-1)) {
200                   s = nil;
201                   len = sizeof(nil) - 1;
202                   goto string;
203                 }
204
205                 base = 16; alt = 1; fc = 'x';
206                 num = (unsigned long) ptr;
207                 goto number2;
208                 }
209
210       case 'o':                 /* Octal */
211                 base = 8;
212       case 'u':                 /* Unsigned */
213                 goto number;
214
215       case 'X':                 /* heXadecimal */
216       case 'x':                 /* heXadecimal */
217                 base = 16;
218       number:                   /* get and print a unsigned number */
219
220                 if (qualifier == 'l')
221                   num = va_arg(args, unsigned long);
222                 else if (qualifier == 'h')
223                   /* vormals unsigned short, falsch fuer gcc 2.96
224                      siehe http://mail.gnu.org/pipermail/discuss-gnustep/1999-October/010624.html */
225                   num = va_arg(args, unsigned int);
226                 else
227                   num = va_arg(args, unsigned int);
228                 /* ANSI only specifies the `+' and ` ' flags for signed conversions.  */
229                 is_neg = showsign = space = 0;
230                 goto number2;
231
232       case 'd':                 /* Decimal */
233       case 'i':                 /* Integer */
234                 if (qualifier == 'l')
235                   signed_num = va_arg(args, long);
236                 else if (qualifier == 'h')
237                   /* vormals short, falsch fuer gcc 2.96 siehe
238                      http://mail.gnu.org/pipermail/discuss-gnustep/1999-October/010624.html */
239                   signed_num = va_arg(args, int);
240                 else
241                   signed_num = va_arg(args, int);
242                 num = (is_neg = signed_num < 0) ? - signed_num : signed_num;
243
244       number2:                  /* print number in num */
245                 {
246                   static const char conv_TABLE[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
247                   static const char conv_table[] = "0123456789abcdefghijklmnopqrstuvwxyz";
248                   const char *conv = fc=='X' ? conv_TABLE : conv_table;
249                   str = buf+BUFSIZE;    /* str = _itoa(buf+BUFSIZE,num,base,fc=='X') */
250                   if (!num) *--str = '0';
251                   else do *--str = conv[num % base]; while (num/=base);
252                   len = buf + BUFSIZE - str;
253                 }
254
255                 /* prepend 0 for octal format. */
256                 if (alt && base == 8 && prec<=len) {
257                   *--str = '0';
258                   ++len;
259                 }
260
261                 /* we will print s ==> cut prec and width */
262                 prec -= len;
263                 width -= len;
264
265                 if (prec > 0) width -= prec; /* we will use all precision space! */
266
267                 if (is_neg || showsign || space) --width;
268                 if (base == 16 && alt) width -= 2;
269
270                 if (!left && pad==' ')
271                   while (width-- > 0) P (" ", 1);
272
273                 if (is_neg)
274                   P ("-", 1);
275                 else if (showsign)
276                   P ("+", 1);
277                 else if (space)
278                   P (" ", 1);
279
280                 if (base == 16 && alt) {
281                   P ("0", 1);
282                   P (&fc, 1);
283                 }
284
285                 if (!left && pad=='0')
286                   while (width-- > 0) P ("0", 1);
287
288                 while (prec-- > 0) P ("0", 1);
289
290                 P (str, len);
291
292                 while (width-- > 0)  P (" ", 1);
293                 break;
294
295                 /* @@@ NYI (just hacked) */
296       case 'e':
297       case 'E':
298       case 'f':
299       case 'g':
300       case 'G':
301 #ifdef HAVE_ANSI_SPRINTF
302                 len = sprintf (fbuf, "%1.20e", va_arg (args, double));
303 #else
304                 sprintf (fbuf, "%1.20e", va_arg (args, double));
305                 len = strlen (fbuf);
306 #endif
307                 s = fbuf;
308                 goto string;
309
310       case 'n':                 /* assign #printed characters */
311                 if (qualifier == 'l')           *va_arg (args, long *) = done;
312                 else if (qualifier == 'h')      *va_arg (args, short *) = done;
313                 else { assert (qualifier == -1); *va_arg (args, int *) = done; }
314                 break;
315
316       case 'm':                 /* errno, GNU extension */
317                 /* strerror() is ANSI C, sys_nerr & sys_errlist are not */
318                 s = strerror (errno);
319                 len = strlen (s);
320                 goto string;
321
322       default:
323                 assert (0);
324       }
325     }
326   }
327   return done;
328 }
329
330
331 int
332 xgprintf (xgprintf_func *out, void *arg, const char *fmt, ...)
333 {
334   va_list args;
335   int i;
336
337   va_start (args, fmt);
338   i = xvgprintf (out, arg, fmt, args);
339   va_end (args);
340   return i;
341 }
342
343 #endif /* !USE_PRINTF */