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