Implemented debug support.
[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   unsigned short helper_short;
67
68   done = 0;
69
70 #define P(__b,__l) do { out (arg, __b, __l); done += __l; } while (0)
71
72   while (*fmt) {
73     const char *next = strchr (fmt, '%');
74
75     if (!next) {
76       P (fmt, strlen(fmt));
77       break;
78     } else if (next != fmt) {
79       P (fmt, next-fmt);
80       fmt = next;
81     }
82
83     /* Check for "%%".  Note that although the ANSI standard lists
84        '%' as a conversion specifier, it says "The complete format
85        specification shall be `%%'," so we can avoid all the width
86        and prec processing.  */
87     if (fmt[1] == '%') {
88       P (fmt, 1);
89       fmt += 2;
90       continue;
91     }
92
93     /* process flags */
94     is_neg = showsign = space = left = alt = 0;  pad = ' ';
95
96   repeat:
97     ++fmt;                      /* this also skips first '%' */
98     switch (*fmt) {
99     case '-': left = 1; goto repeat;
100     case '+': showsign = 1; goto repeat;
101     case ' ': space = 1; goto repeat;
102     case '#': alt = 1; goto repeat;
103     case '0': pad = '0'; goto repeat;
104     }
105
106     /* get field width */
107     width = 0;
108     if (*fmt == '*') {
109       ++fmt, width = va_arg(args, int);
110       if (width < 0) left = 1, width = -width;
111     } else while (isdigit(*fmt)) 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                   const char *conv = fc=='X' ? conv_TABLE : conv_table;
251                   str = buf+BUFSIZE;    /* str = _itoa(buf+BUFSIZE,num,base,fc=='X') */
252                   if (!num) *--str = '0';
253                   else do *--str = conv[num % base]; while (num/=base);
254                   len = buf + BUFSIZE - str;
255                 }
256
257                 /* prepend 0 for octal format. */
258                 if (alt && base == 8 && prec<=len) {
259                   *--str = '0';
260                   ++len;
261                 }
262
263                 /* we will print s ==> cut prec and width */
264                 prec -= len;
265                 width -= len;
266
267                 if (prec > 0) width -= prec; /* we will use all precision space! */
268
269                 if (is_neg || showsign || space) --width;
270                 if (base == 16 && alt) width -= 2;
271
272                 if (!left && pad==' ')
273                   while (width-- > 0) P (" ", 1);
274
275                 if (is_neg)
276                   P ("-", 1);
277                 else if (showsign)
278                   P ("+", 1);
279                 else if (space)
280                   P (" ", 1);
281
282                 if (base == 16 && alt) {
283                   P ("0", 1);
284                   P (&fc, 1);
285                 }
286
287                 if (!left && pad=='0')
288                   while (width-- > 0) P ("0", 1);
289
290                 while (prec-- > 0) P ("0", 1);
291
292                 P (str, len);
293
294                 while (width-- > 0)  P (" ", 1);
295                 break;
296
297                 /* @@@ NYI (just hacked) */
298       case 'e':
299       case 'E':
300       case 'f':
301       case 'g':
302       case 'G':
303 #ifdef HAVE_ANSI_SPRINTF
304                 len = sprintf (fbuf, "%g", va_arg (args, double));
305 #else
306                 sprintf (fbuf, "%g", va_arg (args, double));
307                 len = strlen (fbuf);
308 #endif
309                 s = fbuf;
310                 goto string;
311
312       case 'n':                 /* assign #printed characters */
313                 if (qualifier == 'l')           *va_arg (args, long *) = done;
314                 else if (qualifier == 'h')      *va_arg (args, short *) = done;
315                 else { assert (qualifier == -1); *va_arg (args, int *) = done; }
316                 break;
317
318       case 'm':                 /* errno, GNU extension */
319                 /* strerror() is ANSI C, sys_nerr & sys_errlist are not */
320                 s = strerror (errno);
321                 len = strlen (s);
322                 goto string;
323
324       default:
325                 assert (0);
326       }
327     }
328   }
329   return done;
330 }
331
332
333 int
334 xgprintf (xgprintf_func *out, void *arg, const char *fmt, ...)
335 {
336   va_list args;
337   int i;
338
339   va_start (args, fmt);
340   i = xvgprintf (out, arg, fmt, args);
341   va_end (args);
342   return i;
343 }
344
345 #endif /* !USE_PRINTF */