1 /* Xgprintf --- extended formatted output via generic printer functions.
2 Copyright (C) 1995, 1996 Christian von Roques */
4 /* Implements ANSI-C printf formats minus locale plus extensions,
5 noteably GNU Libc-like registering of specifier-handlers. */
22 /* >= size of ASCII representation of a number using base 8 + 1 */
23 #define BUFSIZE ((sizeof(unsigned long)*CHAR_BIT)/3 + 2)
25 /* For printing double/float numbers, must be large enough for
26 arbitrary numbers with %g. @@@ Yeek! */
29 static xprintf_function *print_func[UCHAR_MAX+1];
33 xprintf_register (char spec, xprintf_function func)
35 assert ((spec > 0) && !print_func[(unsigned char)spec]);
36 print_func[(unsigned char)spec] = func;
41 xvgprintf (xgprintf_func *out, void *arg, const char *fmt, va_list args)
43 int done; /* number of chars printed */
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 */
67 #define P(__b,__l) do { out (arg, __b, __l); done += __l; } while (0)
70 const char *next = strchr (fmt, '%');
75 } else if (next != fmt) {
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. */
91 is_neg = showsign = space = left = alt = 0; pad = ' ';
94 ++fmt; /* this also skips first '%' */
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;
103 /* get field width */
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';
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 */
118 /* get the conversion qualifier */
119 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
126 if (print_func[(unsigned char)fc]) {
133 info.is_long_double = qualifier == 'L';
134 info.is_short = qualifier == 'h';
135 info.is_long = qualifier == 'l';
139 info.showsign = showsign;
142 /* Sharing `args' with another function is not blessed by ANSI
143 C. From ISO/IEC DIS 9899, section 4.4:
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'.
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;
165 case 'c': /* Character */
166 { c = (char) va_arg (args, int);
172 case 's': /* String */
173 { static const char null[] = "(null)";
174 s = va_arg(args, char *);
177 len = (prec == -1 || prec >= (int) sizeof(null) - 1) ? sizeof(null) - 1 : 0;
183 if (prec >= 0 && prec < len)
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)) {
200 len = sizeof(nil) - 1;
204 base = 16; alt = 1; fc = 'x';
205 num = (unsigned long) ptr;
209 case 'o': /* Octal */
211 case 'u': /* Unsigned */
214 case 'X': /* heXadecimal */
215 case 'x': /* heXadecimal */
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);
223 num = va_arg(args, unsigned int);
224 /* ANSI only specifies the `+' and ` ' flags for signed conversions. */
225 is_neg = showsign = space = 0;
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);
235 signed_num = va_arg(args, int);
236 num = (is_neg = signed_num < 0) ? - signed_num : signed_num;
238 number2: /* print number in num */
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;
249 /* prepend 0 for octal format. */
250 if (alt && base == 8 && prec<=len) {
255 /* we will print s ==> cut prec and width */
259 if (prec > 0) width -= prec; /* we will use all precision space! */
261 if (is_neg || showsign || space) --width;
262 if (base == 16 && alt) width -= 2;
264 if (!left && pad==' ')
265 while (width-- > 0) P (" ", 1);
274 if (base == 16 && alt) {
279 if (!left && pad=='0')
280 while (width-- > 0) P ("0", 1);
282 while (prec-- > 0) P ("0", 1);
286 while (width-- > 0) P (" ", 1);
289 /* @@@ NYI (just hacked) */
295 #ifdef HAVE_ANSI_SPRINTF
296 len = sprintf (fbuf, "%g", va_arg (args, double));
298 sprintf (fbuf, "%g", va_arg (args, double));
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; }
310 case 'm': /* errno, GNU extension */
311 /* strerror() is ANSI C, sys_nerr & sys_errlist are not */
312 s = strerror (errno);
326 xgprintf (xgprintf_func *out, void *arg, const char *fmt, ...)
331 va_start (args, fmt);
332 i = xvgprintf (out, arg, fmt, args);
337 #endif /* !USE_PRINTF */