OSDN Git Service

Rewrite console driver to use multi-char write
[qmiga/bboot.git] / libc / printf.c
1 /*
2  * This file is part of the libpayload project.
3  *
4  * It has originally been taken from the HelenOS project
5  * (http://www.helenos.eu), and slightly modified for our purposes.
6  *
7  * Copyright (C) 2001-2004 Jakub Jermar
8  * Copyright (C) 2006 Josef Cejka
9  * Copyright (C) 2008 Uwe Hermann <uwe@hermann-uwe.de>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  *
16  * - Redistributions of source code must retain the above copyright
17  *   notice, this list of conditions and the following disclaimer.
18  * - Redistributions in binary form must reproduce the above copyright
19  *   notice, this list of conditions and the following disclaimer in the
20  *   documentation and/or other materials provided with the distribution.
21  * - The name of the author may not be used to endorse or promote products
22  *   derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <types.h>
41
42 static struct _FILE {
43 } _stdout, _stdin, _stderr;
44
45 FILE *stdout = &_stdout;
46 FILE *stdin = &_stdin;
47 FILE *stderr = &_stderr;
48
49 /** Structure for specifying output methods for different printf clones. */
50 struct printf_spec {
51         /* Output function, returns count of printed characters or EOF. */
52         int (*write) (const char *, size_t, void *);
53         /* Support data - output stream specification, its state, locks, ... */
54         void *data;
55 };
56
57 /** Show prefixes 0x or 0. */
58 #define __PRINTF_FLAG_PREFIX            0x00000001
59 /** Signed / unsigned number. */
60 #define __PRINTF_FLAG_SIGNED            0x00000002
61 /** Print leading zeroes. */
62 #define __PRINTF_FLAG_ZEROPADDED        0x00000004
63 /** Align to left. */
64 #define __PRINTF_FLAG_LEFTALIGNED       0x00000010
65 /** Always show + sign. */
66 #define __PRINTF_FLAG_SHOWPLUS          0x00000020
67 /** Print space instead of plus. */
68 #define __PRINTF_FLAG_SPACESIGN         0x00000040
69 /** Show big characters. */
70 #define __PRINTF_FLAG_BIGCHARS          0x00000080
71 /** Number has - sign. */
72 #define __PRINTF_FLAG_NEGATIVE          0x00000100
73
74 /**
75  * Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
76  * to terminate string (last one is only for better testing end of buffer by
77  * zero-filling subroutine).
78  */
79 #define PRINT_NUMBER_BUFFER_SIZE        (64 + 5)
80
81 /** Enumeration of possible arguments types. */
82 typedef enum {
83         PrintfQualifierByte = 0,
84         PrintfQualifierShort,
85         PrintfQualifierInt,
86         PrintfQualifierLong,
87         PrintfQualifierLongLong,
88         PrintfQualifierPointer,
89 } qualifier_t;
90
91 /**
92  * Print one or more characters without adding newline.
93  *
94  * @param buf   Buffer of >= count bytesi size. NULL pointer is not allowed!
95  * @param count Number of characters to print.
96  * @param ps    Output method and its data.
97  * @return      Number of characters printed.
98  */
99 static int printf_putnchars(const char *buf, size_t count,
100                             struct printf_spec *ps)
101 {
102         return ps->write(buf, count, ps->data);
103 }
104
105 /**
106  * Print a string without adding a newline.
107  *
108  * @param str   String to print.
109  * @param ps    Write function specification and support data.
110  * @return      Number of characters printed.
111  */
112 static inline int printf_putstr(const char *str, struct printf_spec *ps)
113 {
114         return printf_putnchars(str, strlen(str), ps);
115 }
116
117 /**
118  * Print one character.
119  *
120  * @param c     Character to be printed.
121  * @param ps    Output method.
122  * @return      Number of characters printed.
123  */
124 static int printf_putchar(int c, struct printf_spec *ps)
125 {
126         char ch = c;
127
128         return ps->write(&ch, 1, ps->data);
129 }
130
131 /**
132  * Print one formatted character.
133  *
134  * @param c     Character to print.
135  * @param width Width modifier.
136  * @param flags Flags that change the way the character is printed.
137  * @param ps    Output methods spec for different printf clones.
138  * @return      Number of characters printed, negative value on failure.
139  */
140 static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps)
141 {
142         int counter = 0;
143
144         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
145                 while (--width > 0) {
146                         if (printf_putchar(' ', ps) > 0)
147                                 ++counter;
148                 }
149         }
150
151         if (printf_putchar(c, ps) > 0)
152                 counter++;
153
154         while (--width > 0) {
155                 if (printf_putchar(' ', ps) > 0)
156                         ++counter;
157         }
158
159         return counter;
160 }
161
162 /**
163  * Print string.
164  *
165  * @param s             String to be printed.
166  * @param width         Width modifier.
167  * @param precision     Precision modifier.
168  * @param flags         Flags that modify the way the string is printed.
169  * @param ps            Output methods spec for different printf clones.
170  * @return              Number of characters printed, negative value on failure.
171  */
172 /** Structure for specifying output methods for different printf clones. */
173 static int print_string(char *s, int width, unsigned int precision,
174                         uint64_t flags, struct printf_spec *ps)
175 {
176         int counter = 0, retval;
177         size_t size;
178
179         if (s == NULL)
180                 return printf_putstr("(NULL)", ps);
181         size = strlen(s);
182         /* Print leading spaces. */
183         if (precision == 0)
184                 precision = size;
185         width -= precision;
186
187         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
188                 while (width-- > 0) {
189                         if (printf_putchar(' ', ps) == 1)
190                                 counter++;
191                 }
192         }
193
194         if ((retval = printf_putnchars(s, (size < precision ? size : precision), ps)) < 0)
195                 return -counter;
196         counter += retval;
197
198         while (width-- > 0) {
199                 if (printf_putchar(' ', ps) == 1)
200                         ++counter;
201         }
202
203         return counter;
204 }
205
206 static inline char get_digit(uint64_t num, int base, int upcase)
207 {
208         int digit = num % base;
209         if (digit < 10)
210                 return '0' + digit;
211         else
212                 return (upcase ? 'A' : 'a') - 10 + digit;
213 }
214
215 /**
216  * Print a number in a given base.
217  *
218  * Print significant digits of a number in given base.
219  *
220  * @param num           Number to print.
221  * @param width         Width modifier.h
222  * @param precision     Precision modifier.
223  * @param base          Base to print the number in (must be between 2 and 16).
224  * @param flags         Flags that modify the way the number is printed.
225  * @param ps            Output methods spec for different printf clones.
226  * @return              Number of characters printed.
227  */
228 static int print_number(uint64_t num, int width, int precision, int base,
229                         uint64_t flags, struct printf_spec *ps)
230 {
231         char d[PRINT_NUMBER_BUFFER_SIZE];
232         char *ptr = &d[PRINT_NUMBER_BUFFER_SIZE - 1];
233         int size = 0;           /* Size of number with all prefixes and signs. */
234         int number_size;        /* Size of plain number. */
235         char sgn;
236         int retval;
237         int counter = 0;
238
239         *ptr-- = 0;             /* Put zero at end of string. */
240
241         if (num == 0) {
242                 *ptr-- = '0';
243                 size++;
244         } else {
245                 do {
246                         *ptr-- = get_digit(num, base, flags & __PRINTF_FLAG_BIGCHARS);
247                         size++;
248                 } while (num /= base);
249         }
250
251         number_size = size;
252
253         /*
254          * Collect the sum of all prefixes/signs/... to calculate padding and
255          * leading zeroes.
256          */
257         if (flags & __PRINTF_FLAG_PREFIX) {
258                 switch (base) {
259                 case 2: /* Binary formating is not standard, but useful. */
260                         size += 2;
261                         break;
262                 case 8:
263                         size++;
264                         break;
265                 case 16:
266                         size += 2;
267                         break;
268                 }
269         }
270
271         sgn = 0;
272         if (flags & __PRINTF_FLAG_SIGNED) {
273                 if (flags & __PRINTF_FLAG_NEGATIVE) {
274                         sgn = '-';
275                         size++;
276                 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
277                         sgn = '+';
278                         size++;
279                 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
280                         sgn = ' ';
281                         size++;
282                 }
283         }
284
285         if (flags & __PRINTF_FLAG_LEFTALIGNED)
286                 flags &= ~__PRINTF_FLAG_ZEROPADDED;
287
288         /*
289          * If the number is left-aligned or precision is specified then
290          * zero-padding is ignored.
291          */
292         if (flags & __PRINTF_FLAG_ZEROPADDED) {
293                 if ((precision == 0) && (width > size))
294                         precision = width - size + number_size;
295         }
296
297         /* Print leading spaces. */
298         if (number_size > precision) {
299                 /* Print the whole number not only a part. */
300                 precision = number_size;
301         }
302
303         width -= precision + size - number_size;
304
305         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
306                 while (width-- > 0) {
307                         if (printf_putchar(' ', ps) == 1)
308                                 counter++;
309                 }
310         }
311
312         /* Print sign. */
313         if (sgn) {
314                 if (printf_putchar(sgn, ps) == 1)
315                         counter++;
316         }
317
318         /* Print prefix. */
319         if (flags & __PRINTF_FLAG_PREFIX) {
320                 switch (base) {
321                 case 2: /* Binary formating is not standard, but useful. */
322                         if (printf_putchar('0', ps) == 1)
323                                 counter++;
324                         if (flags & __PRINTF_FLAG_BIGCHARS) {
325                                 if (printf_putchar('B', ps) == 1)
326                                         counter++;
327                         } else {
328                                 if (printf_putchar('b', ps) == 1)
329                                         counter++;
330                         }
331                         break;
332                 case 8:
333                         if (printf_putchar('o', ps) == 1)
334                                 counter++;
335                         break;
336                 case 16:
337                         if (printf_putchar('0', ps) == 1)
338                                 counter++;
339                         if (flags & __PRINTF_FLAG_BIGCHARS) {
340                                 if (printf_putchar('X', ps) == 1)
341                                         counter++;
342                         } else {
343                                 if (printf_putchar('x', ps) == 1)
344                                         counter++;
345                         }
346                         break;
347                 }
348         }
349
350         /* Print leading zeroes. */
351         precision -= number_size;
352         while (precision-- > 0) {
353                 if (printf_putchar('0', ps) == 1)
354                         counter++;
355         }
356
357         /* Print number itself. */
358         if ((retval = printf_putstr(++ptr, ps)) > 0)
359                 counter += retval;
360
361         /* Print ending spaces. */
362         while (width-- > 0) {
363                 if (printf_putchar(' ', ps) == 1)
364                         counter++;
365         }
366
367         return counter;
368 }
369
370 /**
371  * Print formatted string.
372  *
373  * Print string formatted according to the fmt parameter and variadic arguments.
374  * Each formatting directive must have the following form:
375  *
376  *      \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
377  *
378  * FLAGS:@n
379  *      - "#"   Force to print prefix.For \%o conversion, the prefix is 0, for
380  *              \%x and \%X prefixes are 0x and 0X and for conversion \%b the
381  *              prefix is 0b.
382  *
383  *      - "-"   Align to left.
384  *
385  *      - "+"   Print positive sign just as negative.
386  *
387  *      - " "   If the printed number is positive and "+" flag is not set,
388  *              print space in place of sign.
389  *
390  *      - "0"   Print 0 as padding instead of spaces. Zeroes are placed between
391  *              sign and the rest of the number. This flag is ignored if "-"
392  *              flag is specified.
393  *
394  * WIDTH:@n
395  *      - Specify the minimal width of a printed argument. If it is bigger,
396  *      width is ignored. If width is specified with a "*" character instead of
397  *      number, width is taken from parameter list. And integer parameter is
398  *      expected before parameter for processed conversion specification. If
399  *      this value is negative its absolute value is taken and the "-" flag is
400  *      set.
401  *
402  * PRECISION:@n
403  *      - Value precision. For numbers it specifies minimum valid numbers.
404  *      Smaller numbers are printed with leading zeroes. Bigger numbers are not
405  *      affected. Strings with more than precision characters are cut off. Just
406  *      as with width, an "*" can be used used instead of a number. An integer
407  *      value is then expected in parameters. When both width and precision are
408  *      specified using "*", the first parameter is used for width and the
409  *      second one for precision.
410  *
411  * TYPE:@n
412  *      - "hh"  Signed or unsigned char.@n
413  *      - "h"   Signed or unsigned short.@n
414  *      - ""    Signed or unsigned int (default value).@n
415  *      - "l"   Signed or unsigned long int.@n
416  *      - "ll"  Signed or unsigned long long int.@n
417  *
418  *
419  * CONVERSION:@n
420  *      - %     Print percentile character itself.
421  *
422  *      - c     Print single character.
423  *
424  *      - s     Print zero terminated string. If a NULL value is passed as
425  *              value, "(NULL)" is printed instead.
426  *
427  *      - P, p  Print value of a pointer. Void * value is expected and it is
428  *              printed in hexadecimal notation with prefix (as with \%#X / \%#x
429  *              for 32-bit or \%#X / \%#x for 64-bit long pointers).
430  *
431  *      - b     Print value as unsigned binary number. Prefix is not printed by
432  *              default. (Nonstandard extension.)
433  *
434  *      - o     Print value as unsigned octal number. Prefix is not printed by
435  *              default.
436  *
437  *      - d, i  Print signed decimal number. There is no difference between d
438  *              and i conversion.
439  *
440  *      - u     Print unsigned decimal number.
441  *
442  *      - X, x  Print hexadecimal number with upper- or lower-case. Prefix is
443  *              not printed by default.
444  *
445  * All other characters from fmt except the formatting directives are printed in
446  * verbatim.
447  *
448  * @param fmt   Formatting NULL terminated string.
449  * @param ps    TODO.
450  * @param ap    TODO.
451  * @return      Number of characters printed, negative value on failure.
452  */
453 static int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
454 {
455         int i = 0;              /* Index of the currently processed char from fmt */
456         int j = 0;              /* Index to the first not printed nonformating character */
457         int end;
458         int counter;            /* Counter of printed characters */
459         int retval;             /* Used to store return values from called functions */
460         char c;
461         qualifier_t qualifier;  /* Type of argument */
462         int base;               /* Base in which a numeric parameter will be printed */
463         uint64_t number;        /* Argument value */
464         size_t size;            /* Byte size of integer parameter */
465         int width, precision;
466         uint64_t flags;
467
468         counter = 0;
469
470         while ((c = fmt[i])) {
471                 /* Control character. */
472                 if (c == '%') {
473                         /* Print common characters if any processed. */
474                         if (i > j) {
475                                 if ((retval = printf_putnchars(&fmt[j],
476                                     (size_t) (i - j), ps)) < 0) {
477                                         counter = -counter;
478                                         goto out;       /* Error */
479                                 }
480                                 counter += retval;
481                         }
482
483                         j = i;
484                         /* Parse modifiers. */
485                         flags = 0;
486                         end = 0;
487
488                         do {
489                                 ++i;
490                                 switch (c = fmt[i]) {
491                                 case '#':
492                                         flags |= __PRINTF_FLAG_PREFIX;
493                                         break;
494                                 case '-':
495                                         flags |= __PRINTF_FLAG_LEFTALIGNED;
496                                         break;
497                                 case '+':
498                                         flags |= __PRINTF_FLAG_SHOWPLUS;
499                                         break;
500                                 case ' ':
501                                         flags |= __PRINTF_FLAG_SPACESIGN;
502                                         break;
503                                 case '0':
504                                         flags |= __PRINTF_FLAG_ZEROPADDED;
505                                         break;
506                                 default:
507                                         end = 1;
508                                 };
509
510                         } while (end == 0);
511
512                         /* Width & '*' operator. */
513                         width = 0;
514                         if (isdigit(fmt[i])) {
515                                 while (isdigit(fmt[i])) {
516                                         width *= 10;
517                                         width += fmt[i++] - '0';
518                                 }
519                         } else if (fmt[i] == '*') {
520                                 /* Get width value from argument list. */
521                                 i++;
522                                 width = (int)va_arg(ap, int);
523                                 if (width < 0) {
524                                         /* Negative width sets '-' flag. */
525                                         width *= -1;
526                                         flags |= __PRINTF_FLAG_LEFTALIGNED;
527                                 }
528                         }
529
530                         /* Precision and '*' operator. */
531                         precision = 0;
532                         if (fmt[i] == '.') {
533                                 ++i;
534                                 if (isdigit(fmt[i])) {
535                                         while (isdigit(fmt[i])) {
536                                                 precision *= 10;
537                                                 precision += fmt[i++] - '0';
538                                         }
539                                 } else if (fmt[i] == '*') {
540                                         /* Get precision from argument list. */
541                                         i++;
542                                         precision = (int)va_arg(ap, int);
543                                         /* Ignore negative precision. */
544                                         if (precision < 0)
545                                                 precision = 0;
546                                 }
547                         }
548
549                         switch (fmt[i++]) {
550                         /** @todo unimplemented qualifiers:
551                          * t ptrdiff_t - ISO C 99
552                          */
553                         case 'h':       /* char or short */
554                                 qualifier = PrintfQualifierShort;
555                                 if (fmt[i] == 'h') {
556                                         i++;
557                                         qualifier = PrintfQualifierByte;
558                                 }
559                                 break;
560                         case 'z':       /* size_t or ssize_t */
561                                 qualifier = PrintfQualifierLong;
562                                 break;
563                         case 'l':       /* long or long long */
564                                 qualifier = PrintfQualifierLong;
565                                 if (fmt[i] == 'l') {
566                                         i++;
567                                         qualifier = PrintfQualifierLongLong;
568                                 }
569                                 break;
570                         default:
571                                 /* default type */
572                                 qualifier = PrintfQualifierInt;
573                                 --i;
574                         }
575
576                         base = 10;
577
578                         switch (c = fmt[i]) {
579                         /* String and character conversions */
580                         case 's':
581                                 if ((retval = print_string(va_arg(ap, char *),
582                                     width, precision, flags, ps)) < 0) {
583                                         counter = -counter;
584                                         goto out;
585                                 };
586                                 counter += retval;
587                                 j = i + 1;
588                                 goto next_char;
589                         case 'c':
590                                 c = va_arg(ap, unsigned int);
591                                 retval = print_char(c, width, flags, ps);
592                                 if (retval < 0) {
593                                         counter = -counter;
594                                         goto out;
595                                 };
596                                 counter += retval;
597                                 j = i + 1;
598                                 goto next_char;
599
600                         /* Integer values */
601                         case 'P':       /* pointer */
602                                 flags |= __PRINTF_FLAG_BIGCHARS;
603                                 /* fall through */
604                         case 'p':
605                                 flags |= __PRINTF_FLAG_PREFIX;
606                                 base = 16;
607                                 qualifier = PrintfQualifierPointer;
608                                 break;
609                         case 'b':
610                                 base = 2;
611                                 break;
612                         case 'o':
613                                 base = 8;
614                                 break;
615                         case 'd':
616                         case 'i':
617                                 flags |= __PRINTF_FLAG_SIGNED;
618                         case 'u':
619                                 break;
620                         case 'X':
621                                 flags |= __PRINTF_FLAG_BIGCHARS;
622                                 /* fall through */
623                         case 'x':
624                                 base = 16;
625                                 break;
626                         case '%': /* percentile itself */
627                                 j = i;
628                                 goto next_char;
629                         default: /* Bad formatting */
630                                 /*
631                                  * Unknown format. Now, j is the index of '%'
632                                  * so we will print whole bad format sequence.
633                                  */
634                                 goto next_char;
635                         }
636
637                         /* Print integers. */
638                         /* Print number. */
639                         switch (qualifier) {
640                         case PrintfQualifierByte:
641                                 size = sizeof(unsigned char);
642                                 number = (uint64_t) va_arg(ap, unsigned int);
643                                 break;
644                         case PrintfQualifierShort:
645                                 size = sizeof(unsigned short);
646                                 number = (uint64_t) va_arg(ap, unsigned int);
647                                 break;
648                         case PrintfQualifierInt:
649                                 size = sizeof(unsigned int);
650                                 number = (uint64_t) va_arg(ap, unsigned int);
651                                 break;
652                         case PrintfQualifierLong:
653                                 size = sizeof(unsigned long);
654                                 number = (uint64_t) va_arg(ap, unsigned long);
655                                 break;
656                         case PrintfQualifierLongLong:
657                                 size = sizeof(unsigned long long);
658                                 number = (uint64_t) va_arg(ap, unsigned long long);
659                                 break;
660                         case PrintfQualifierPointer:
661                                 size = sizeof(void *);
662                                 number = (uint64_t) (unsigned long)va_arg(ap, void *);
663                                 break;
664                         default:        /* Unknown qualifier */
665                                 counter = -counter;
666                                 goto out;
667                         }
668
669                         if (flags & __PRINTF_FLAG_SIGNED) {
670                                 if (number & (0x1 << (size * 8 - 1))) {
671                                         flags |= __PRINTF_FLAG_NEGATIVE;
672
673                                         if (size == sizeof(uint64_t)) {
674                                                 number = -((int64_t) number);
675                                         } else {
676                                                 number = ~number;
677                                                 number &= ~(0xFFFFFFFFFFFFFFFFll << (size * 8));
678                                                 number++;
679                                         }
680                                 }
681                         }
682
683                         if ((retval = print_number(number, width, precision,
684                                                    base, flags, ps)) < 0) {
685                                 counter = -counter;
686                                 goto out;
687                         }
688
689                         counter += retval;
690                         j = i + 1;
691                 }
692 next_char:
693                 ++i;
694         }
695
696         if (i > j) {
697                 if ((retval = printf_putnchars(&fmt[j],
698                     (u64) (i - j), ps)) < 0) {
699                         counter = -counter;
700                         goto out;       /* Error */
701
702                 }
703                 counter += retval;
704         }
705
706 out:
707         return counter;
708 }
709
710 int snprintf(char *str, size_t size, const char *fmt, ...)
711 {
712         int ret;
713         va_list args;
714
715         va_start(args, fmt);
716         ret = vsnprintf(str, size, fmt, args);
717         va_end(args);
718
719         return ret;
720 }
721
722 int sprintf(char *str, const char *fmt, ...)
723 {
724         int ret;
725         va_list args;
726
727         va_start(args, fmt);
728         ret = vsprintf(str, fmt, args);
729         va_end(args);
730
731         return ret;
732 }
733
734 int fprintf(FILE *file, const char *fmt, ...)
735 {
736         int ret;
737         if ((file == stdout) || (file == stderr)) {
738                 va_list args;
739                 va_start(args, fmt);
740                 ret = vprintf(fmt, args);
741                 va_end(args);
742
743                 return ret;
744         }
745         return -1;
746 }
747
748 struct vsnprintf_data {
749         size_t size;            /* Total space for string */
750         size_t len;             /* Count of currently used characters */
751         char *string;           /* Destination string */
752 };
753
754 /**
755  * Write string to given buffer.
756  *
757  * Write at most data->size characters including trailing zero. According to
758  * C99, snprintf() has to return number of characters that would have been
759  * written if enough space had been available. Hence the return value is not
760  * number of really printed characters but size of the input string.
761  * Number of really used characters is stored in data->len.
762  *
763  * @param str   Source string to print.
764  * @param count Size of source string.
765  * @param _data Structure with destination string, counter of used space
766  *              and total string size.
767  * @return Number of characters to print (not characters really printed!).
768  */
769 static int vsnprintf_write(const char *str, size_t count, void *_data)
770 {
771         struct vsnprintf_data *data = _data;
772         size_t i;
773
774         i = data->size - data->len;
775         if (i == 0)
776                 return count;
777
778         /* We have only one free byte left in buffer => write trailing zero. */
779         if (i == 1) {
780                 data->string[data->size - 1] = 0;
781                 data->len = data->size;
782                 return count;
783         }
784
785         /*
786          * We have not enough space for whole string with the trailing
787          * zero => print only a part of string.
788          */
789         if (i <= count) {
790                 memcpy((void *)(data->string + data->len), (void *)str, i - 1);
791                 data->string[data->size - 1] = 0;
792                 data->len = data->size;
793                 return count;
794         }
795
796         /* Buffer is big enough to print whole string. */
797         memcpy((void *)(data->string + data->len), (void *)str, count);
798         data->len += count;
799         /*
800          * Put trailing zero at end, but not count it into data->len so
801          * it could be rewritten next time.
802          */
803         data->string[data->len] = 0;
804
805         return count;
806 }
807
808 int vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
809 {
810         struct vsnprintf_data data = { size, 0, str };
811         struct printf_spec ps = { vsnprintf_write, &data };
812
813         /* Print 0 at end of string - fix case that nothing will be printed. */
814         if (size > 0)
815                 str[0] = 0;
816
817         /* vsnprintf_write() ensures that str will be terminated by zero. */
818         return printf_core(fmt, &ps, ap);
819 }
820
821 int vsprintf(char *str, const char *fmt, va_list ap)
822 {
823         return vsnprintf(str, (size_t) - 1, fmt, ap);
824 }
825
826 int printf(const char *fmt, ...)
827 {
828         int ret;
829         va_list args;
830
831         va_start(args, fmt);
832         ret = vprintf(fmt, args);
833         va_end(args);
834
835         return ret;
836 }
837
838 static int vprintf_write(const char *str, size_t count, void *unused)
839 {
840         (void)unused;
841         putchars(str, count);
842         return count;
843 }
844
845 int vprintf(const char *fmt, va_list ap)
846 {
847         struct printf_spec ps = { vprintf_write, NULL };
848
849         return printf_core(fmt, &ps, ap);
850 }