2 * This file is part of DisOrder
3 * Copyright (C) 2004, 2007, 2008 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 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU 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, see <http://www.gnu.org/licenses/>.
18 /** @file lib/printf.c
19 * @brief UTF-8 *printf workalike (core)
22 #define NO_MEMORY_ALLOCATION
23 /* because byte_snprintf used from log.c */
67 int (*check
)(const struct conversion
*c
);
68 int (*output
)(struct state
*s
, struct conversion
*c
);
79 const struct specifier
*specifier
;
82 static const char flags
[] = "'-+ #0";
84 /* write @nbytes@ to the output. Return -1 on error, 0 on success.
85 * Keeps track of the number of bytes written. */
86 static int do_write(struct state
*s
,
89 if(s
->bytes
> INT_MAX
- nbytes
) {
95 if(s
->output
->write(s
->output
, buffer
, nbytes
) < 0)
101 /* write character @ch@ @n@ times, reasonably efficiently */
102 static int do_pad(struct state
*s
, int ch
, unsigned n
) {
107 case ' ': padding
= " "; break;
108 case '0': padding
= "00000000000000000000000000000000"; break;
114 if(do_write(s
, padding
, 32) < 0)
117 if(do_write(s
, padding
, n
) < 0)
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
)
137 /* consistency checks for various conversion specifications */
139 static int check_integer(const struct conversion
*c
) {
156 static int check_string(const struct conversion
*c
) {
159 /* XXX don't support %ls, %lc */
166 static int check_pointer(const struct conversion
*c
) {
172 static int check_percent(const struct conversion
*c
) {
173 if(c
->flags
|| c
->width
|| c
->precision
|| c
->length
)
178 /* output functions for various conversion specifications */
180 static int output_percent(struct state
*s
,
181 struct conversion
attribute((unused
)) *c
) {
182 return do_write(s
, "%", 1);
185 static int output_integer(struct state
*s
, struct conversion
*c
) {
189 int base
, dp
, iszero
, ndigits
, prec
, xform
, sign_bytes
, pad
;
190 char digits
[CHAR_BIT
* sizeof (uintmax_t)]; /* overestimate */
192 switch(c
->specifier
->ch
) {
194 if(c
->specifier
->base
< 0) {
196 case 0: l
= va_arg(s
->ap
, int); break;
197 case l_char
: l
= (signed char)va_arg(s
->ap
, int); break;
198 case l_short
: l
= (short)va_arg(s
->ap
, int); break;
199 case l_long
: l
= va_arg(s
->ap
, long); break;
200 case l_longlong
: l
= va_arg(s
->ap
, long_long
); break;
201 case l_intmax_t
: l
= va_arg(s
->ap
, intmax_t); break;
202 case l_size_t
: l
= va_arg(s
->ap
, ssize_t
); break;
203 case l_ptrdiff_t
: l
= va_arg(s
->ap
, ptrdiff_t); break;
206 base
= -c
->specifier
->base
;
216 case 0: u
= va_arg(s
->ap
, unsigned int); break;
217 case l_char
: u
= (unsigned char)va_arg(s
->ap
, unsigned int); break;
218 case l_short
: u
= (unsigned short)va_arg(s
->ap
, unsigned int); break;
219 case l_long
: u
= va_arg(s
->ap
, unsigned long); break;
220 case l_longlong
: u
= va_arg(s
->ap
, u_long_long
); break;
221 case l_intmax_t
: u
= va_arg(s
->ap
, uintmax_t); break;
222 case l_size_t
: u
= va_arg(s
->ap
, size_t); break;
223 case l_ptrdiff_t
: u
= va_arg(s
->ap
, ptrdiff_t); break;
226 base
= c
->specifier
->base
;
231 u
= (uintptr_t)va_arg(s
->ap
, void *);
233 base
= c
->specifier
->base
;
237 /* default precision */
238 if(!(c
->flags
& f_precision
))
241 if((c
->flags
& f_sign
) && !sign
)
243 /* compute the digits */
247 digits
[--dp
] = c
->specifier
->digits
[u
% base
];
250 ndigits
= sizeof digits
- dp
;
251 /* alternative form */
252 if(c
->flags
& f_hash
) {
255 if((dp
== sizeof digits
|| digits
[dp
] != '0')
256 && c
->precision
<= ndigits
)
257 c
->precision
= ndigits
+ 1;
260 if(!iszero
&& c
->specifier
->xform
)
261 xform
= strlen(c
->specifier
->xform
);
266 /* calculate number of 0s to add for precision */
267 if(ndigits
< c
->precision
)
268 prec
= c
->precision
- ndigits
;
271 /* bytes occupied by the sign */
276 /* XXX implement the ' ' flag */
277 /* calculate number of bytes of padding */
278 if(c
->flags
& f_width
) {
279 if((pad
= c
->width
- (ndigits
+ prec
+ xform
+ sign_bytes
)) < 0)
283 /* now we are ready to output. Possibilities are:
284 * [space pad][sign][xform][0 prec]digits
285 * [sign][xform][0 pad][0 prec]digits
286 * [sign][xform][0 prec]digits[space pad]
290 if(c
->flags
& f_left
) {
291 if(sign
&& do_write(s
, &sign
, 1))
293 if(xform
&& do_write(s
, c
->specifier
->xform
, xform
))
295 if(prec
&& do_pad(s
, '0', prec
) < 0)
297 if(ndigits
&& do_write(s
, digits
+ dp
, ndigits
))
299 if(pad
&& do_pad(s
, ' ', pad
) < 0)
301 } else if(c
->flags
& f_zero
) {
302 if(sign
&& do_write(s
, &sign
, 1))
304 if(xform
&& do_write(s
, c
->specifier
->xform
, xform
))
306 if(pad
&& do_pad(s
, '0', pad
) < 0)
308 if(prec
&& do_pad(s
, '0', prec
) < 0)
310 if(ndigits
&& do_write(s
, digits
+ dp
, ndigits
))
313 if(pad
&& do_pad(s
, ' ', pad
) < 0)
315 if(sign
&& do_write(s
, &sign
, 1))
317 if(xform
&& do_write(s
, c
->specifier
->xform
, xform
))
319 if(prec
&& do_pad(s
, '0', prec
) < 0)
321 if(ndigits
&& do_write(s
, digits
+ dp
, ndigits
))
327 static int output_string(struct state
*s
, struct conversion
*c
) {
331 str
= va_arg(s
->ap
, const char *);
332 if(c
->flags
& f_precision
) {
333 if((n
= memchr(str
, 0, c
->precision
)))
339 if(c
->flags
& f_width
) {
340 if((pad
= c
->width
- len
) < 0)
344 if(c
->flags
& f_left
) {
345 if(do_write(s
, str
, len
) < 0)
347 if(pad
&& do_pad(s
, ' ', pad
) < 0)
350 if(pad
&& do_pad(s
, ' ', pad
) < 0)
352 if(do_write(s
, str
, len
) < 0)
359 static int output_char(struct state
*s
, struct conversion
*c
) {
363 ch
= va_arg(s
->ap
, int);
364 if(c
->flags
& f_width
) {
365 if((pad
= c
->width
- 1) < 0)
369 if(c
->flags
& f_left
) {
370 if(do_write(s
, &ch
, 1) < 0)
372 if(pad
&& do_pad(s
, ' ', pad
) < 0)
375 if(pad
&& do_pad(s
, ' ', pad
) < 0)
377 if(do_write(s
, &ch
, 1) < 0)
383 static int output_count(struct state
*s
, struct conversion
*c
) {
385 case 0: *va_arg(s
->ap
, int *) = s
->bytes
; break;
386 case l_char
: *va_arg(s
->ap
, signed char *) = s
->bytes
; break;
387 case l_short
: *va_arg(s
->ap
, short *) = s
->bytes
; break;
388 case l_long
: *va_arg(s
->ap
, long *) = s
->bytes
; break;
389 case l_longlong
: *va_arg(s
->ap
, long_long
*) = s
->bytes
; break;
390 case l_intmax_t
: *va_arg(s
->ap
, intmax_t *) = s
->bytes
; break;
391 case l_size_t
: *va_arg(s
->ap
, ssize_t
*) = s
->bytes
; break;
392 case l_ptrdiff_t
: *va_arg(s
->ap
, ptrdiff_t *) = s
->bytes
; break;
398 /* table of conversion specifiers */
399 static const struct specifier specifiers
[] = {
400 /* XXX don't support floating point conversions */
401 { '%', check_percent
, output_percent
, 0, 0, 0 },
402 { 'X', check_integer
, output_integer
, 16, "0123456789ABCDEF", "0X" },
403 { 'c', check_string
, output_char
, 0, 0, 0 },
404 { 'd', check_integer
, output_integer
, -10, "0123456789", 0 },
405 { 'i', check_integer
, output_integer
, -10, "0123456789", 0 },
406 { 'n', check_integer
, output_count
, 0, 0, 0 },
407 { 'o', check_integer
, output_integer
, 8, "01234567", 0 },
408 { 'p', check_pointer
, output_integer
, 16, "0123456789abcdef", "0x" },
409 { 's', check_string
, output_string
, 0, 0, 0 },
410 { 'u', check_integer
, output_integer
, 10, "0123456789", 0 },
411 { 'x', check_integer
, output_integer
, 16, "0123456789abcdef", "0x" },
414 /* collect and check information about a conversion specification */
415 static int parse_conversion(struct conversion
*c
, const char *ptr
) {
417 const char *q
, *start
= ptr
;
419 memset(c
, 0, sizeof *c
);
421 while(*ptr
&& (q
= strchr(flags
, *ptr
))) {
422 c
->flags
|= (1 << (q
- flags
));
425 /* minimum field width */
426 if(*ptr
>= '0' && *ptr
<= '9') {
427 if((n
= get_integer(&c
->width
, ptr
)) < 0)
431 } else if(*ptr
== '*') {
439 if(*ptr
>= '0' && *ptr
<= '9') {
440 if((n
= get_integer(&c
->precision
, ptr
)) < 0)
443 } else if(*ptr
== '*') {
448 c
->flags
|= f_precision
;
450 /* length modifier */
451 switch(ch
= *ptr
++) {
453 if((ch
= *ptr
++) == 'h') {
461 if((ch
= *ptr
++) == 'l') {
462 c
->length
= l_longlong
;
468 case 'q': c
->length
= l_longlong
; ch
= *ptr
++; break;
469 case 'j': c
->length
= l_intmax_t
; ch
= *ptr
++; break;
470 case 'z': c
->length
= l_size_t
; ch
= *ptr
++; break;
471 case 't': c
->length
= l_ptrdiff_t
; ch
= *ptr
++; break;
472 case 'L': c
->length
= l_longdouble
; ch
= *ptr
++; break;
474 /* conversion specifier */
476 r
= sizeof specifiers
/ sizeof *specifiers
;
477 while(l
<= r
&& (specifiers
[m
= (l
+ r
) / 2].ch
!= ch
))
478 if(ch
< specifiers
[m
].ch
)
482 if(specifiers
[m
].ch
!= ch
)
484 if(specifiers
[m
].check(c
))
486 c
->specifier
= &specifiers
[m
];
490 /* ISO/IEC 9899:1999 7.19.6.1 */
491 /* http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html */
493 int byte_vsinkprintf(struct sink
*output
,
501 memset(&s
, 0, sizeof s
);
505 /* output text up to next conversion specification */
506 for(ptr
= fmt
; *fmt
&& *fmt
!= '%'; ++fmt
)
509 if(do_write(&s
, ptr
, n
) < 0)
514 /* parse conversion */
515 if((n
= parse_conversion(&c
, fmt
)) < 0)
518 /* fill in width and precision */
519 if((c
.flags
& f_width
) && c
.width
== -1)
520 if((c
.width
= va_arg(s
.ap
, int)) < 0) {
524 if((c
.flags
& f_precision
) && c
.precision
== -1)
525 if((c
.precision
= va_arg(s
.ap
, int)) < 0)
526 c
.flags
^= f_precision
;
527 /* generate the output */
528 if(c
.specifier
->output(&s
, &c
) < 0)
538 int byte_sinkprintf(struct sink
*output
, const char *fmt
, ...) {
543 n
= byte_vsinkprintf(output
, fmt
, ap
);