some fixes for xml dumper / still buggy.
[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
110           while (isdigit(*fmt))
111                 width = 10*width + *fmt++ - '0';
112
113     /* get the prec */
114     if (*fmt == '.') {
115       ++fmt;
116       if (*fmt == '*') {++fmt; prec = va_arg(args, int);}
117       else { prec = 0; while (isdigit(*fmt)) prec = 10*prec + *fmt++ - '0'; }
118       if (prec < 0) prec = 0;
119     } else prec = -1;           /* -1 == unspecified */
120
121     /* get the conversion qualifier */
122     if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
123       qualifier = *fmt++;
124     else
125       qualifier = -1;
126
127     fc = *fmt++;
128
129     if (print_func[(unsigned char)fc]) {
130       xprintf_info info;
131       int func_done;
132
133       info.prec = prec;
134       info.width = width;
135       info.spec = fc;
136       info.is_long_double = qualifier == 'L';
137       info.is_short       = qualifier == 'h';
138       info.is_long        = qualifier == 'l';
139       info.alt = alt;
140       info.space = space;
141       info.left = left;
142       info.showsign = showsign;
143       info.pad = pad;
144
145       /* Sharing `args' with another function is not blessed by ANSI
146          C.  From ISO/IEC DIS 9899, section 4.4:
147
148          If access to the varying arguments is desired, the called
149          function shall declare an object (referred as `ap' in this
150          section) having type va_list.  The object `ap' may be passed
151          as an argument to another function; if that function invokes
152          the va_arg macro with parameter `ap', the value of `ap' in
153          the calling function is indeterminate and shall be passed to
154          the va_end macro prior to any futher reference to `ap'.
155
156          Nevertheless, it works with most compilers, including gcc.  */
157       func_done = print_func[(unsigned char)fc] (out, arg, &info, &args);
158       if (func_done < 0) return -1;
159       else done += func_done;
160
161     } else {
162
163       /* default base */
164       base = 10;
165
166       switch (fc) {
167
168       case 'c':                 /* Character */
169                 { c = (char) va_arg (args, int);
170                 s = &c;
171                 len = 1;
172                 goto string;
173                 }
174
175       case 's':                 /* String */
176                 { static const char null[] = "(null)";
177                 s = va_arg(args, char *);
178                 if (!s) {
179                   s = null;
180                   len = (prec == -1 || prec >= (int) sizeof(null) - 1) ? sizeof(null) - 1 : 0;
181                 } else {
182                   len = strlen (s);
183                 }
184
185                 string:
186                 if (prec >= 0 && prec < len)
187                   len = prec;
188                 width -= len;
189
190                 if (!left)
191                   while (width-- > 0)
192                         P (" ", 1);
193                 P (s, len);
194                 while (width-- > 0)
195                   P (" ", 1);
196                 break;
197                 }
198       case 'p':                 /* Pointer */
199                 {       const char nil[] = "(nil)";
200                 const void *ptr = va_arg (args, void *);
201                 if (!ptr && (prec==-1 || prec>=(int)sizeof(nil)-1)) {
202                   s = nil;
203                   len = sizeof(nil) - 1;
204                   goto string;
205                 }
206
207                 base = 16; alt = 1; fc = 'x';
208                 num = (unsigned long) ptr;
209                 goto number2;
210                 }
211
212       case 'o':                 /* Octal */
213                 base = 8;
214       case 'u':                 /* Unsigned */
215                 goto number;
216
217       case 'X':                 /* heXadecimal */
218       case 'x':                 /* heXadecimal */
219                 base = 16;
220       number:                   /* get and print a unsigned number */
221
222                 if (qualifier == 'l')
223                   num = va_arg(args, unsigned long);
224                 else if (qualifier == 'h')
225                   /* vormals unsigned short, falsch fuer gcc 2.96
226                      siehe http://mail.gnu.org/pipermail/discuss-gnustep/1999-October/010624.html */
227                   num = va_arg(args, unsigned int);
228                 else
229                   num = va_arg(args, unsigned int);
230                 /* ANSI only specifies the `+' and ` ' flags for signed conversions.  */
231                 is_neg = showsign = space = 0;
232                 goto number2;
233
234       case 'd':                 /* Decimal */
235       case 'i':                 /* Integer */
236                 if (qualifier == 'l')
237                   signed_num = va_arg(args, long);
238                 else if (qualifier == 'h')
239                   /* vormals short, falsch fuer gcc 2.96 siehe
240                      http://mail.gnu.org/pipermail/discuss-gnustep/1999-October/010624.html */
241                   signed_num = va_arg(args, int);
242                 else
243                   signed_num = va_arg(args, int);
244                 num = (is_neg = signed_num < 0) ? - signed_num : signed_num;
245
246       number2:                  /* print number in num */
247                 {
248                   static const char conv_TABLE[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
249                   static const char conv_table[] = "0123456789abcdefghijklmnopqrstuvwxyz";
250
251                   const char *conv = fc=='X' ? conv_TABLE : conv_table;
252                   str = buf+BUFSIZE;    /* str = _itoa(buf+BUFSIZE,num,base,fc=='X') */
253                   if (!num) *--str = '0';
254                   else do *--str = conv[num % base]; while (num/=base);
255                   len = buf + BUFSIZE - str;
256                 }
257
258                 /* prepend 0 for octal format. */
259                 if (alt && base == 8 && prec<=len) {
260                   *--str = '0';
261                   ++len;
262                 }
263
264                 /* we will print s ==> cut prec and width */
265                 prec -= len;
266                 width -= len;
267
268                 if (prec > 0) width -= prec; /* we will use all precision space! */
269
270                 if (is_neg || showsign || space) --width;
271                 if (base == 16 && alt) width -= 2;
272
273                 if (!left && pad==' ')
274                   while (width-- > 0) P (" ", 1);
275
276                 if (is_neg)
277                   P ("-", 1);
278                 else if (showsign)
279                   P ("+", 1);
280                 else if (space)
281                   P (" ", 1);
282
283                 if (base == 16 && alt) {
284                   P ("0", 1);
285                   P (&fc, 1);
286                 }
287
288                 if (!left && pad=='0')
289                   while (width-- > 0) P ("0", 1);
290
291                 while (prec-- > 0) P ("0", 1);
292
293                 P (str, len);
294
295                 while (width-- > 0)  P (" ", 1);
296                 break;
297
298                 /* @@@ NYI (just hacked) */
299       case 'e':
300       case 'E':
301       case 'f':
302       case 'g':
303       case 'G':
304 #ifdef HAVE_ANSI_SPRINTF
305                 len = sprintf (fbuf, "%1.20e", va_arg (args, double));
306 #else
307                 sprintf (fbuf, "%1.20e", va_arg (args, double));
308                 len = strlen (fbuf);
309 #endif
310                 s = fbuf;
311                 goto string;
312
313       case 'n':                 /* assign #printed characters */
314                 if (qualifier == 'l')           *va_arg (args, long *) = done;
315                 else if (qualifier == 'h')      *va_arg (args, short *) = done;
316                 else { assert (qualifier == -1); *va_arg (args, int *) = done; }
317                 break;
318
319       case 'm':                 /* errno, GNU extension */
320                 /* strerror() is ANSI C, sys_nerr & sys_errlist are not */
321                 s = strerror (errno);
322                 len = strlen (s);
323                 goto string;
324
325       default:
326                 assert (0);
327       }
328     }
329   }
330   return done;
331 }
332
333
334 int
335 xgprintf (xgprintf_func *out, void *arg, const char *fmt, ...)
336 {
337   va_list args;
338   int i;
339
340   va_start (args, fmt);
341   i = xvgprintf (out, arg, fmt, args);
342   va_end (args);
343   return i;
344 }
345
346 #endif /* !USE_PRINTF */