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