2 * This file is part of DisOrder
3 * Copyright (C) 2004, 2007 Richard Kettlewell
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 #define NO_MEMORY_ALLOCATION
22 /* because byte_snprintf used from log.c */
70 int (*check)(const struct conversion *c);
71 int (*output)(struct state *s, struct conversion *c);
82 const struct specifier *specifier;
85 static const char flags[] = "'-+ #0";
87 /* write @nbytes@ to the output. Return -1 on error, 0 on success.
88 * Keeps track of the number of bytes written. */
89 static int do_write(struct state *s,
92 if(s->bytes > INT_MAX - nbytes) {
98 if(s->output->write(s->output, buffer, nbytes) < 0) return -1;
103 /* write character @ch@ @n@ times, reasonably efficiently */
104 static int do_pad(struct state *s, int ch, unsigned n) {
109 case ' ': padding = " "; break;
110 case '0': padding = "00000000000000000000000000000000"; break;
116 if(do_write(s, padding, 32) < 0) return -1;
118 if(do_write(s, padding, n) < 0) return -1;
122 /* pick up the integer at @ptr@, returning it via @intp@. Return the
123 * number of characters consumed. Return 0 if there is no integer
124 * there and -1 if an error occurred (e.g. too big) */
125 static int get_integer(int *intp, const char *ptr) {
130 n = strtol(ptr, &e, 10);
131 if(errno || n > INT_MAX || n < INT_MIN || e == ptr) return -1;
136 /* consistency checks for various conversion specifications */
138 static int check_integer(const struct conversion *c) {
154 static int check_string(const struct conversion *c) {
157 /* XXX don't support %ls, %lc */
164 static int check_pointer(const struct conversion *c) {
165 if(c->length) return -1;
169 static int check_percent(const struct conversion *c) {
170 if(c->flags || c->width || c->precision || c->length) return -1;
174 /* output functions for various conversion specifications */
176 static int output_percent(struct state *s,
177 struct conversion attribute((unused)) *c) {
178 return do_write(s, "%", 1);
181 static int output_integer(struct state *s, struct conversion *c) {
185 int base, dp, iszero, ndigits, prec, xform, sign_bytes, pad;
186 char digits[CHAR_BIT * sizeof (uintmax_t)]; /* overestimate */
188 switch(c->specifier->ch) {
190 if(c->specifier->base < 0) {
192 case 0: l = va_arg(s->ap, int); break;
193 case l_char: l = (signed char)va_arg(s->ap, int); break;
194 case l_short: l = (short)va_arg(s->ap, int); break;
195 case l_long: l = va_arg(s->ap, long); break;
196 case l_longlong: l = va_arg(s->ap, long_long); break;
197 case l_intmax_t: l = va_arg(s->ap, intmax_t); break;
198 case l_size_t: l = va_arg(s->ap, ssize_t); break;
199 case l_ptrdiff_t: l = va_arg(s->ap, ptrdiff_t); break;
202 base = -c->specifier->base;
212 case 0: u = va_arg(s->ap, unsigned int); break;
213 case l_char: u = (unsigned char)va_arg(s->ap, unsigned int); break;
214 case l_short: u = (unsigned short)va_arg(s->ap, unsigned int); break;
215 case l_long: u = va_arg(s->ap, unsigned long); break;
216 case l_longlong: u = va_arg(s->ap, u_long_long); break;
217 case l_intmax_t: u = va_arg(s->ap, uintmax_t); break;
218 case l_size_t: u = va_arg(s->ap, size_t); break;
219 case l_ptrdiff_t: u = va_arg(s->ap, ptrdiff_t); break;
222 base = c->specifier->base;
227 u = (uintptr_t)va_arg(s->ap, void *);
229 base = c->specifier->base;
233 /* default precision */
234 if(!(c->flags & f_precision))
237 if((c->flags & f_sign) && !sign) sign = '+';
238 /* compute the digits */
242 digits[--dp] = c->specifier->digits[u % base];
245 ndigits = sizeof digits - dp;
246 /* alternative form */
247 if(c->flags & f_hash) {
250 if((dp == sizeof digits || digits[dp] != '0')
251 && c->precision <= ndigits)
252 c->precision = ndigits + 1;
255 if(!iszero && c->specifier->xform)
256 xform = strlen(c->specifier->xform);
261 /* calculate number of 0s to add for precision */
262 if(ndigits < c->precision)
263 prec = c->precision - ndigits;
266 /* bytes occupied by the sign */
271 /* XXX implement the ' ' flag */
272 /* calculate number of bytes of padding */
273 if(c->flags & f_width) {
274 if((pad = c->width - (ndigits + prec + xform + sign_bytes)) < 0)
278 /* now we are ready to output. Possibilities are:
279 * [space pad][sign][xform][0 prec]digits
280 * [sign][xform][0 pad][0 prec]digits
281 * [sign][xform][0 prec]digits[space pad]
285 if(c->flags & f_left) {
286 if(pad && do_pad(s, ' ', pad) < 0) return -1;
287 if(sign && do_write(s, &sign, 1)) return -1;
288 if(xform && do_write(s, c->specifier->xform, xform)) return -1;
289 if(prec && do_pad(s, '0', prec) < 0) return -1;
290 if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
291 } else if(c->flags & f_zero) {
292 if(sign && do_write(s, &sign, 1)) return -1;
293 if(xform && do_write(s, c->specifier->xform, xform)) return -1;
294 if(pad && do_pad(s, '0', pad) < 0) return -1;
295 if(prec && do_pad(s, '0', prec) < 0) return -1;
296 if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
298 if(sign && do_write(s, &sign, 1)) return -1;
299 if(xform && do_write(s, c->specifier->xform, xform)) return -1;
300 if(prec && do_pad(s, '0', prec) < 0) return -1;
301 if(ndigits && do_write(s, digits + dp, ndigits)) return -1;
302 if(pad && do_pad(s, ' ', pad) < 0) return -1;
307 static int output_string(struct state *s, struct conversion *c) {
311 str = va_arg(s->ap, const char *);
312 if(c->flags & f_precision) {
313 if((n = memchr(str, 0, c->precision)))
319 if(c->flags & f_width) {
320 if((pad = c->width - len) < 0)
324 if(c->flags & f_left) {
325 if(pad && do_pad(s, ' ', pad) < 0) return -1;
326 if(do_write(s, str, len) < 0) return -1;
328 if(do_write(s, str, len) < 0) return -1;
329 if(pad && do_pad(s, ' ', pad) < 0) return -1;
335 static int output_char(struct state *s, struct conversion *c) {
339 ch = va_arg(s->ap, int);
340 if(c->flags & f_width) {
341 if((pad = c->width - 1) < 0)
345 if(c->flags & f_left) {
346 if(pad && do_pad(s, ' ', pad) < 0) return -1;
347 if(do_write(s, &ch, 1) < 0) return -1;
349 if(do_write(s, &ch, 1) < 0) return -1;
350 if(pad && do_pad(s, ' ', pad) < 0) return -1;
355 static int output_count(struct state *s, struct conversion *c) {
357 case 0: *va_arg(s->ap, int *) = s->bytes; break;
358 case l_char: *va_arg(s->ap, signed char *) = s->bytes; break;
359 case l_short: *va_arg(s->ap, short *) = s->bytes; break;
360 case l_long: *va_arg(s->ap, long *) = s->bytes; break;
361 case l_longlong: *va_arg(s->ap, long_long *) = s->bytes; break;
362 case l_intmax_t: *va_arg(s->ap, intmax_t *) = s->bytes; break;
363 case l_size_t: *va_arg(s->ap, ssize_t *) = s->bytes; break;
364 case l_ptrdiff_t: *va_arg(s->ap, ptrdiff_t *) = s->bytes; break;
370 /* table of conversion specifiers */
371 static const struct specifier specifiers[] = {
372 /* XXX don't support floating point conversions */
373 { '%', check_percent, output_percent, 0, 0, 0 },
374 { 'X', check_integer, output_integer, 16, "0123456789ABCDEF", "0X" },
375 { 'c', check_string, output_char, 0, 0, 0 },
376 { 'd', check_integer, output_integer, -10, "0123456789", 0 },
377 { 'i', check_integer, output_integer, -10, "0123456789", 0 },
378 { 'n', check_integer, output_count, 0, 0, 0 },
379 { 'o', check_integer, output_integer, 8, "01234567", 0 },
380 { 'p', check_pointer, output_integer, 16, "0123456789abcdef", "0x" },
381 { 's', check_string, output_string, 0, 0, 0 },
382 { 'u', check_integer, output_integer, 10, "0123456789", 0 },
383 { 'x', check_integer, output_integer, 16, "0123456789abcdef", "0x" },
386 /* collect and check information about a conversion specification */
387 static int parse_conversion(struct conversion *c, const char *ptr) {
389 const char *q, *start = ptr;
391 memset(c, 0, sizeof *c);
393 while(*ptr && (q = strchr(flags, *ptr))) {
394 c->flags |= (1 << (q - flags));
397 /* minimum field width */
398 if(*ptr >= '0' && *ptr <= '9') {
399 if((n = get_integer(&c->width, ptr)) < 0) return -1;
402 } else if(*ptr == '*') {
410 if(*ptr >= '0' && *ptr <= '9') {
411 if((n = get_integer(&c->precision, ptr)) < 0) return -1;
413 } else if(*ptr == '*') {
418 c->flags |= f_precision;
420 /* length modifier */
421 switch(ch = *ptr++) {
423 if((ch = *ptr++) == 'h') { c->length = l_char; ch = *ptr++; }
424 else c->length = l_short;
427 if((ch = *ptr++) == 'l') { c->length = l_longlong; ch = *ptr++; }
428 else c->length = l_long;
430 case 'q': c->length = l_longlong; ch = *ptr++; break;
431 case 'j': c->length = l_intmax_t; ch = *ptr++; break;
432 case 'z': c->length = l_size_t; ch = *ptr++; break;
433 case 't': c->length = l_ptrdiff_t; ch = *ptr++; break;
434 case 'L': c->length = l_longdouble; ch = *ptr++; break;
436 /* conversion specifier */
438 r = sizeof specifiers / sizeof *specifiers;
439 while(l <= r && (specifiers[m = (l + r) / 2].ch != ch))
440 if(ch < specifiers[m].ch) r = m - 1;
442 if(specifiers[m].ch != ch) return -1;
443 if(specifiers[m].check(c)) return -1;
444 c->specifier = &specifiers[m];
448 /* ISO/IEC 9899:1999 7.19.6.1 */
449 /* http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html */
451 int byte_vsinkprintf(struct sink *output,
459 memset(&s, 0, sizeof s);
463 /* output text up to next conversion specification */
464 for(ptr = fmt; *fmt && *fmt != '%'; ++fmt)
467 if(do_write(&s, ptr, n) < 0) goto error;
471 /* parse conversion */
472 if((n = parse_conversion(&c, fmt)) < 0) goto error;
474 /* fill in width and precision */
475 if((c.flags & f_width) && c.width == -1)
476 if((c.width = va_arg(s.ap, int)) < 0) {
480 if((c.flags & f_precision) && c.precision == -1)
481 if((c.precision = va_arg(s.ap, int)) < 0)
482 c.flags ^= f_precision;
483 /* generate the output */
484 if(c.specifier->output(&s, &c) < 0) goto error;