3 * File name: ir/common/xgprintf.c
4 * Purpose: Xgprintf --- extended formatted output via generic printer functions.
5 * Author: Christian von Roques
7 * Created: 1999 by getting from fiasco
9 * Copyright: (c) 1995, 1996 Christian von Roques
10 * Licence: This file protected by GPL - GNU GENERAL PUBLIC LICENSE.
13 /* Implements ANSI-C printf formats minus locale plus extensions,
14 noteably GNU Libc-like registering of specifier-handlers. */
31 /* >= size of ASCII representation of a number using base 8 + 1 */
32 #define BUFSIZE ((sizeof(unsigned long)*CHAR_BIT)/3 + 2)
34 /* For printing double/float numbers, must be large enough for
35 arbitrary numbers with %g. @@@ Yeek! */
38 static xprintf_function *print_func[UCHAR_MAX+1];
42 xprintf_register (char spec, xprintf_function *func)
44 assert ((spec > 0) && !print_func[(unsigned char)spec]);
45 print_func[(unsigned char)spec] = func;
49 xvgprintf (xgprintf_func *out, void *arg, const char *fmt, va_list args)
51 int done; /* number of chars printed */
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 */
75 #define P(__b,__l) do { out (arg, __b, __l); done += __l; } while (0)
78 const char *next = strchr (fmt, '%');
83 } else if (next != fmt) {
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. */
99 is_neg = showsign = space = left = alt = 0; pad = ' ';
102 ++fmt; /* this also skips first '%' */
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;
111 /* get field width */
114 ++fmt, width = va_arg(args, int);
115 if (width < 0) {left = 1; width = -width;}
117 while (isdigit(*fmt))
118 width = 10*width + *fmt++ - '0';
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 */
128 /* get the conversion qualifier */
129 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
136 if (print_func[(unsigned char)fc]) {
143 info.is_long_double = qualifier == 'L';
144 info.is_short = qualifier == 'h';
145 info.is_long = qualifier == 'l';
149 info.showsign = showsign;
152 /* Sharing `args' with another function is not blessed by ANSI
153 C. From ISO/IEC DIS 9899, section 4.4:
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'.
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;
175 case 'c': /* Character */
176 { c = (char) va_arg (args, int);
182 case 's': /* String */
183 { static const char null[] = "(null)";
184 s = va_arg(args, char *);
187 len = (prec == -1 || prec >= (int) sizeof(null) - 1) ? sizeof(null) - 1 : 0;
193 if (prec >= 0 && prec < len)
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)) {
210 len = sizeof(nil) - 1;
214 base = 16; alt = 1; fc = 'x';
215 num = (unsigned long) ptr;
219 case 'o': /* Octal */
221 case 'u': /* Unsigned */
224 case 'X': /* heXadecimal */
225 case 'x': /* heXadecimal */
227 number: /* get and print a unsigned number */
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);
236 num = va_arg(args, unsigned int);
237 /* ANSI only specifies the `+' and ` ' flags for signed conversions. */
238 is_neg = showsign = space = 0;
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);
250 signed_num = va_arg(args, int);
251 num = (is_neg = signed_num < 0) ? - signed_num : signed_num;
253 number2: /* print number in num */
255 static const char conv_TABLE[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
256 static const char conv_table[] = "0123456789abcdefghijklmnopqrstuvwxyz";
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;
265 /* prepend 0 for octal format. */
266 if (alt && base == 8 && prec<=len) {
271 /* we will print s ==> cut prec and width */
275 if (prec > 0) width -= prec; /* we will use all precision space! */
277 if (is_neg || showsign || space) --width;
278 if (base == 16 && alt) width -= 2;
280 if (!left && pad==' ')
281 while (width-- > 0) P (" ", 1);
290 if (base == 16 && alt) {
295 if (!left && pad=='0')
296 while (width-- > 0) P ("0", 1);
298 while (prec-- > 0) P ("0", 1);
302 while (width-- > 0) P (" ", 1);
305 /* @@@ NYI (just hacked) */
311 #ifdef HAVE_ANSI_SPRINTF
312 len = sprintf (fbuf, "%1.20e", va_arg (args, double));
314 sprintf (fbuf, "%1.20e", va_arg (args, double));
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; }
326 case 'm': /* errno, GNU extension */
327 /* strerror() is ANSI C, sys_nerr & sys_errlist are not */
328 s = strerror (errno);
342 xgprintf (xgprintf_func *out, void *arg, const char *fmt, ...)
347 va_start (args, fmt);
348 i = xvgprintf (out, arg, fmt, args);
353 #endif /* !USE_PRINTF */