1 /* Xgprintf --- extended formatted output via generic printer functions.
2 Copyright (C) 1995, 1996 Christian von Roques */
6 /* Implements ANSI-C printf formats minus locale plus extensions,
7 noteably GNU Libc-like registering of specifier-handlers. */
24 /* >= size of ASCII representation of a number using base 8 + 1 */
25 #define BUFSIZE ((sizeof(unsigned long)*CHAR_BIT)/3 + 2)
27 /* For printing double/float numbers, must be large enough for
28 arbitrary numbers with %g. @@@ Yeek! */
31 static xprintf_function *print_func[UCHAR_MAX+1];
35 xprintf_register (char spec, xprintf_function func)
37 assert ((spec > 0) && !print_func[(unsigned char)spec]);
38 print_func[(unsigned char)spec] = func;
42 xvgprintf (xgprintf_func *out, void *arg, const char *fmt, va_list args)
44 int done; /* number of chars printed */
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 */
66 unsigned short helper_short;
70 #define P(__b,__l) do { out (arg, __b, __l); done += __l; } while (0)
73 const char *next = strchr (fmt, '%');
78 } else if (next != fmt) {
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. */
94 is_neg = showsign = space = left = alt = 0; pad = ' ';
97 ++fmt; /* this also skips first '%' */
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;
106 /* get field width */
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';
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 */
121 /* get the conversion qualifier */
122 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
129 if (print_func[(unsigned char)fc]) {
136 info.is_long_double = qualifier == 'L';
137 info.is_short = qualifier == 'h';
138 info.is_long = qualifier == 'l';
142 info.showsign = showsign;
145 /* Sharing `args' with another function is not blessed by ANSI
146 C. From ISO/IEC DIS 9899, section 4.4:
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'.
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;
168 case 'c': /* Character */
169 { c = (char) va_arg (args, int);
175 case 's': /* String */
176 { static const char null[] = "(null)";
177 s = va_arg(args, char *);
180 len = (prec == -1 || prec >= (int) sizeof(null) - 1) ? sizeof(null) - 1 : 0;
186 if (prec >= 0 && prec < len)
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)) {
203 len = sizeof(nil) - 1;
207 base = 16; alt = 1; fc = 'x';
208 num = (unsigned long) ptr;
212 case 'o': /* Octal */
214 case 'u': /* Unsigned */
217 case 'X': /* heXadecimal */
218 case 'x': /* heXadecimal */
220 number: /* get and print a unsigned number */
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);
229 num = va_arg(args, unsigned int);
230 /* ANSI only specifies the `+' and ` ' flags for signed conversions. */
231 is_neg = showsign = space = 0;
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);
243 signed_num = va_arg(args, int);
244 num = (is_neg = signed_num < 0) ? - signed_num : signed_num;
246 number2: /* print number in num */
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;
257 /* prepend 0 for octal format. */
258 if (alt && base == 8 && prec<=len) {
263 /* we will print s ==> cut prec and width */
267 if (prec > 0) width -= prec; /* we will use all precision space! */
269 if (is_neg || showsign || space) --width;
270 if (base == 16 && alt) width -= 2;
272 if (!left && pad==' ')
273 while (width-- > 0) P (" ", 1);
282 if (base == 16 && alt) {
287 if (!left && pad=='0')
288 while (width-- > 0) P ("0", 1);
290 while (prec-- > 0) P ("0", 1);
294 while (width-- > 0) P (" ", 1);
297 /* @@@ NYI (just hacked) */
303 #ifdef HAVE_ANSI_SPRINTF
304 len = sprintf (fbuf, "%g", va_arg (args, double));
306 sprintf (fbuf, "%g", va_arg (args, double));
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; }
318 case 'm': /* errno, GNU extension */
319 /* strerror() is ANSI C, sys_nerr & sys_errlist are not */
320 s = strerror (errno);
334 xgprintf (xgprintf_func *out, void *arg, const char *fmt, ...)
339 va_start (args, fmt);
340 i = xvgprintf (out, arg, fmt, args);
345 #endif /* !USE_PRINTF */