printf_core.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2004 Jakub Jermar
00003  * Copyright (c) 2006 Josef Cejka
00004  * Copyright (c) 2009 Martin Decky
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * - Redistributions of source code must retain the above copyright
00012  *   notice, this list of conditions and the following disclaimer.
00013  * - Redistributions in binary form must reproduce the above copyright
00014  *   notice, this list of conditions and the following disclaimer in the
00015  *   documentation and/or other materials provided with the distribution.
00016  * - The name of the author may not be used to endorse or promote products
00017  *   derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00020  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00021  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00022  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00024  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00025  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00026  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00028  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  */
00030 
00039 #include <unistd.h>
00040 #include <stdio.h>
00041 #include <io/printf_core.h>
00042 #include <ctype.h>
00043 #include <str.h>
00044 
00046 #define __PRINTF_FLAG_PREFIX       0x00000001
00047 
00049 #define __PRINTF_FLAG_SIGNED       0x00000002
00050 
00052 #define __PRINTF_FLAG_ZEROPADDED   0x00000004
00053 
00055 #define __PRINTF_FLAG_LEFTALIGNED  0x00000010
00056 
00058 #define __PRINTF_FLAG_SHOWPLUS     0x00000020
00059 
00061 #define __PRINTF_FLAG_SPACESIGN    0x00000040
00062 
00064 #define __PRINTF_FLAG_BIGCHARS     0x00000080
00065 
00067 #define __PRINTF_FLAG_NEGATIVE     0x00000100
00068 
00074 #define PRINT_NUMBER_BUFFER_SIZE  (64 + 5)
00075 
00078 typedef enum {
00079         PrintfQualifierByte = 0,
00080         PrintfQualifierShort,
00081         PrintfQualifierInt,
00082         PrintfQualifierLong,
00083         PrintfQualifierLongLong,
00084         PrintfQualifierPointer,
00085         PrintfQualifierSize
00086 } qualifier_t;
00087 
00088 static const char *nullstr = "(NULL)";
00089 static const char *digits_small = "0123456789abcdef";
00090 static const char *digits_big = "0123456789ABCDEF";
00091 static const char invalch = U_SPECIAL;
00092 
00103 static int printf_putnchars(const char *buf, size_t size,
00104     printf_spec_t *ps)
00105 {
00106         return ps->str_write((void *) buf, size, ps->data);
00107 }
00108 
00119 static int printf_wputnchars(const wchar_t *buf, size_t size,
00120     printf_spec_t *ps)
00121 {
00122         return ps->wstr_write((void *) buf, size, ps->data);
00123 }
00124 
00133 static int printf_putstr(const char *str, printf_spec_t *ps)
00134 {
00135         if (str == NULL)
00136                 return printf_putnchars(nullstr, str_size(nullstr), ps);
00137         
00138         return ps->str_write((void *) str, str_size(str), ps->data);
00139 }
00140 
00149 static int printf_putchar(const char ch, printf_spec_t *ps)
00150 {
00151         if (!ascii_check(ch))
00152                 return ps->str_write((void *) &invalch, 1, ps->data);
00153         
00154         return ps->str_write(&ch, 1, ps->data);
00155 }
00156 
00165 static int printf_putwchar(const wchar_t ch, printf_spec_t *ps)
00166 {
00167         if (!chr_check(ch))
00168                 return ps->str_write((void *) &invalch, 1, ps->data);
00169         
00170         return ps->wstr_write(&ch, sizeof(wchar_t), ps->data);
00171 }
00172 
00182 static int print_char(const char ch, int width, uint32_t flags, printf_spec_t *ps)
00183 {
00184         size_t counter = 0;
00185         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
00186                 while (--width > 0) {
00187                         /*
00188                          * One space is consumed by the character itself, hence
00189                          * the predecrement.
00190                          */
00191                         if (printf_putchar(' ', ps) > 0)
00192                                 counter++;
00193                 }
00194         }
00195         
00196         if (printf_putchar(ch, ps) > 0)
00197                 counter++;
00198         
00199         while (--width > 0) {
00200                 /*
00201                  * One space is consumed by the character itself, hence
00202                  * the predecrement.
00203                  */
00204                 if (printf_putchar(' ', ps) > 0)
00205                         counter++;
00206         }
00207         
00208         return (int) (counter + 1);
00209 }
00210 
00220 static int print_wchar(const wchar_t ch, int width, uint32_t flags, printf_spec_t *ps)
00221 {
00222         size_t counter = 0;
00223         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
00224                 while (--width > 0) {
00225                         /*
00226                          * One space is consumed by the character itself, hence
00227                          * the predecrement.
00228                          */
00229                         if (printf_putchar(' ', ps) > 0)
00230                                 counter++;
00231                 }
00232         }
00233         
00234         if (printf_putwchar(ch, ps) > 0)
00235                 counter++;
00236         
00237         while (--width > 0) {
00238                 /*
00239                  * One space is consumed by the character itself, hence
00240                  * the predecrement.
00241                  */
00242                 if (printf_putchar(' ', ps) > 0)
00243                         counter++;
00244         }
00245         
00246         return (int) (counter + 1);
00247 }
00248 
00258 static int print_str(char *str, int width, unsigned int precision,
00259     uint32_t flags, printf_spec_t *ps)
00260 {
00261         if (str == NULL)
00262                 return printf_putstr(nullstr, ps);
00263         
00264         /* Print leading spaces. */
00265         size_t strw = str_length(str);
00266         if (precision == 0)
00267                 precision = strw;
00268         
00269         /* Left padding */
00270         size_t counter = 0;
00271         width -= precision;
00272         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
00273                 while (width-- > 0) {
00274                         if (printf_putchar(' ', ps) == 1)
00275                                 counter++;
00276                 }
00277         }
00278         
00279         /* Part of @a str fitting into the alloted space. */
00280         int retval;
00281         size_t size = str_lsize(str, precision);
00282         if ((retval = printf_putnchars(str, size, ps)) < 0)
00283                 return -counter;
00284 
00285         counter += retval;
00286 
00287         /* Right padding */
00288         while (width-- > 0) {
00289                 if (printf_putchar(' ', ps) == 1)
00290                         counter++;
00291         }
00292 
00293         return ((int) counter);
00294 
00295 }
00296 
00306 static int print_wstr(wchar_t *str, int width, unsigned int precision,
00307     uint32_t flags, printf_spec_t *ps)
00308 {
00309         if (str == NULL)
00310                 return printf_putstr(nullstr, ps);
00311         
00312         /* Print leading spaces. */
00313         size_t strw = wstr_length(str);
00314         if (precision == 0)
00315                 precision = strw;
00316         
00317         /* Left padding */
00318         size_t counter = 0;
00319         width -= precision;
00320         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
00321                 while (width-- > 0) {
00322                         if (printf_putchar(' ', ps) == 1)
00323                                 counter++;
00324                 }
00325         }
00326         
00327         /* Part of @a wstr fitting into the alloted space. */
00328         int retval;
00329         size_t size = wstr_lsize(str, precision);
00330         if ((retval = printf_wputnchars(str, size, ps)) < 0)
00331                 return -counter;
00332         
00333         counter += retval;
00334         
00335         /* Right padding */
00336         while (width-- > 0) {
00337                 if (printf_putchar(' ', ps) == 1)
00338                         counter++;
00339         }
00340 
00341         return ((int) counter);
00342 }
00343 
00357 static int print_number(uint64_t num, int width, int precision, int base,
00358     uint32_t flags, printf_spec_t *ps)
00359 {
00360         const char *digits;
00361         if (flags & __PRINTF_FLAG_BIGCHARS)
00362                 digits = digits_big;
00363         else
00364                 digits = digits_small;
00365         
00366         char data[PRINT_NUMBER_BUFFER_SIZE];
00367         char *ptr = &data[PRINT_NUMBER_BUFFER_SIZE - 1];
00368         
00369         /* Size of number with all prefixes and signs */
00370         int size = 0;
00371         
00372         /* Put zero at end of string */
00373         *ptr-- = 0;
00374         
00375         if (num == 0) {
00376                 *ptr-- = '0';
00377                 size++;
00378         } else {
00379                 do {
00380                         *ptr-- = digits[num % base];
00381                         size++;
00382                 } while (num /= base);
00383         }
00384         
00385         /* Size of plain number */
00386         int number_size = size;
00387         
00388         /*
00389          * Collect the sum of all prefixes/signs/etc. to calculate padding and
00390          * leading zeroes.
00391          */
00392         if (flags & __PRINTF_FLAG_PREFIX) {
00393                 switch (base) {
00394                 case 2:
00395                         /* Binary formating is not standard, but usefull */
00396                         size += 2;
00397                         break;
00398                 case 8:
00399                         size++;
00400                         break;
00401                 case 16:
00402                         size += 2;
00403                         break;
00404                 }
00405         }
00406         
00407         char sgn = 0;
00408         if (flags & __PRINTF_FLAG_SIGNED) {
00409                 if (flags & __PRINTF_FLAG_NEGATIVE) {
00410                         sgn = '-';
00411                         size++;
00412                 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
00413                         sgn = '+';
00414                         size++;
00415                 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
00416                         sgn = ' ';
00417                         size++;
00418                 }
00419         }
00420         
00421         if (flags & __PRINTF_FLAG_LEFTALIGNED)
00422                 flags &= ~__PRINTF_FLAG_ZEROPADDED;
00423         
00424         /*
00425          * If the number is left-aligned or precision is specified then
00426          * padding with zeros is ignored.
00427          */
00428         if (flags & __PRINTF_FLAG_ZEROPADDED) {
00429                 if ((precision == 0) && (width > size))
00430                         precision = width - size + number_size;
00431         }
00432         
00433         /* Print leading spaces */
00434         if (number_size > precision) {
00435                 /* Print the whole number, not only a part */
00436                 precision = number_size;
00437         }
00438         
00439         width -= precision + size - number_size;
00440         size_t counter = 0;
00441         
00442         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
00443                 while (width-- > 0) {
00444                         if (printf_putchar(' ', ps) == 1)
00445                                 counter++;
00446                 }
00447         }
00448         
00449         /* Print sign */
00450         if (sgn) {
00451                 if (printf_putchar(sgn, ps) == 1)
00452                         counter++;
00453         }
00454         
00455         /* Print prefix */
00456         if (flags & __PRINTF_FLAG_PREFIX) {
00457                 switch (base) {
00458                 case 2:
00459                         /* Binary formating is not standard, but usefull */
00460                         if (printf_putchar('0', ps) == 1)
00461                                 counter++;
00462                         if (flags & __PRINTF_FLAG_BIGCHARS) {
00463                                 if (printf_putchar('B', ps) == 1)
00464                                         counter++;
00465                         } else {
00466                                 if (printf_putchar('b', ps) == 1)
00467                                         counter++;
00468                         }
00469                         break;
00470                 case 8:
00471                         if (printf_putchar('o', ps) == 1)
00472                                 counter++;
00473                         break;
00474                 case 16:
00475                         if (printf_putchar('0', ps) == 1)
00476                                 counter++;
00477                         if (flags & __PRINTF_FLAG_BIGCHARS) {
00478                                 if (printf_putchar('X', ps) == 1)
00479                                         counter++;
00480                         } else {
00481                                 if (printf_putchar('x', ps) == 1)
00482                                         counter++;
00483                         }
00484                         break;
00485                 }
00486         }
00487         
00488         /* Print leading zeroes */
00489         precision -= number_size;
00490         while (precision-- > 0) {
00491                 if (printf_putchar('0', ps) == 1)
00492                         counter++;
00493         }
00494         
00495         /* Print the number itself */
00496         int retval;
00497         if ((retval = printf_putstr(++ptr, ps)) > 0)
00498                 counter += retval;
00499         
00500         /* Print trailing spaces */
00501         
00502         while (width-- > 0) {
00503                 if (printf_putchar(' ', ps) == 1)
00504                         counter++;
00505         }
00506         
00507         return ((int) counter);
00508 }
00509 
00599 int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
00600 {
00601         size_t i;        /* Index of the currently processed character from fmt */
00602         size_t nxt = 0;  /* Index of the next character from fmt */
00603         size_t j = 0;    /* Index to the first not printed nonformating character */
00604         
00605         size_t counter = 0;   /* Number of characters printed */
00606         int retval;           /* Return values from nested functions */
00607         
00608         while (true) {
00609                 i = nxt;
00610                 wchar_t uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00611                 
00612                 if (uc == 0)
00613                         break;
00614                 
00615                 /* Control character */
00616                 if (uc == '%') {
00617                         /* Print common characters if any processed */
00618                         if (i > j) {
00619                                 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
00620                                         /* Error */
00621                                         counter = -counter;
00622                                         goto out;
00623                                 }
00624                                 counter += retval;
00625                         }
00626                         
00627                         j = i;
00628                         
00629                         /* Parse modifiers */
00630                         uint32_t flags = 0;
00631                         bool end = false;
00632                         
00633                         do {
00634                                 i = nxt;
00635                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00636                                 switch (uc) {
00637                                 case '#':
00638                                         flags |= __PRINTF_FLAG_PREFIX;
00639                                         break;
00640                                 case '-':
00641                                         flags |= __PRINTF_FLAG_LEFTALIGNED;
00642                                         break;
00643                                 case '+':
00644                                         flags |= __PRINTF_FLAG_SHOWPLUS;
00645                                         break;
00646                                 case ' ':
00647                                         flags |= __PRINTF_FLAG_SPACESIGN;
00648                                         break;
00649                                 case '0':
00650                                         flags |= __PRINTF_FLAG_ZEROPADDED;
00651                                         break;
00652                                 default:
00653                                         end = true;
00654                                 };
00655                         } while (!end);
00656                         
00657                         /* Width & '*' operator */
00658                         int width = 0;
00659                         if (isdigit(uc)) {
00660                                 while (true) {
00661                                         width *= 10;
00662                                         width += uc - '0';
00663                                         
00664                                         i = nxt;
00665                                         uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00666                                         if (uc == 0)
00667                                                 break;
00668                                         if (!isdigit(uc))
00669                                                 break;
00670                                 }
00671                         } else if (uc == '*') {
00672                                 /* Get width value from argument list */
00673                                 i = nxt;
00674                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00675                                 width = (int) va_arg(ap, int);
00676                                 if (width < 0) {
00677                                         /* Negative width sets '-' flag */
00678                                         width *= -1;
00679                                         flags |= __PRINTF_FLAG_LEFTALIGNED;
00680                                 }
00681                         }
00682                         
00683                         /* Precision and '*' operator */
00684                         int precision = 0;
00685                         if (uc == '.') {
00686                                 i = nxt;
00687                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00688                                 if (isdigit(uc)) {
00689                                         while (true) {
00690                                                 precision *= 10;
00691                                                 precision += uc - '0';
00692                                                 
00693                                                 i = nxt;
00694                                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00695                                                 if (uc == 0)
00696                                                         break;
00697                                                 if (!isdigit(uc))
00698                                                         break;
00699                                         }
00700                                 } else if (uc == '*') {
00701                                         /* Get precision value from the argument list */
00702                                         i = nxt;
00703                                         uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00704                                         precision = (int) va_arg(ap, int);
00705                                         if (precision < 0) {
00706                                                 /* Ignore negative precision */
00707                                                 precision = 0;
00708                                         }
00709                                 }
00710                         }
00711                         
00712                         qualifier_t qualifier;
00713                         
00714                         switch (uc) {
00718                         case 'h':
00719                                 /* Char or short */
00720                                 qualifier = PrintfQualifierShort;
00721                                 i = nxt;
00722                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00723                                 if (uc == 'h') {
00724                                         i = nxt;
00725                                         uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00726                                         qualifier = PrintfQualifierByte;
00727                                 }
00728                                 break;
00729                         case 'l':
00730                                 /* Long or long long */
00731                                 qualifier = PrintfQualifierLong;
00732                                 i = nxt;
00733                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00734                                 if (uc == 'l') {
00735                                         i = nxt;
00736                                         uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00737                                         qualifier = PrintfQualifierLongLong;
00738                                 }
00739                                 break;
00740                         case 'z':
00741                                 qualifier = PrintfQualifierSize;
00742                                 i = nxt;
00743                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
00744                                 break;
00745                         default:
00746                                 /* Default type */
00747                                 qualifier = PrintfQualifierInt;
00748                         }
00749                         
00750                         unsigned int base = 10;
00751                         
00752                         switch (uc) {
00753                         /*
00754                          * String and character conversions.
00755                          */
00756                         case 's':
00757                                 if (qualifier == PrintfQualifierLong)
00758                                         retval = print_wstr(va_arg(ap, wchar_t *), width, precision, flags, ps);
00759                                 else
00760                                         retval = print_str(va_arg(ap, char *), width, precision, flags, ps);
00761                                 
00762                                 if (retval < 0) {
00763                                         counter = -counter;
00764                                         goto out;
00765                                 }
00766                                 
00767                                 counter += retval;
00768                                 j = nxt;
00769                                 goto next_char;
00770                         case 'c':
00771                                 if (qualifier == PrintfQualifierLong)
00772                                         retval = print_wchar(va_arg(ap, wint_t), width, flags, ps);
00773                                 else
00774                                         retval = print_char(va_arg(ap, unsigned int), width, flags, ps);
00775                                 
00776                                 if (retval < 0) {
00777                                         counter = -counter;
00778                                         goto out;
00779                                 };
00780                                 
00781                                 counter += retval;
00782                                 j = nxt;
00783                                 goto next_char;
00784                         
00785                         /*
00786                          * Integer values
00787                          */
00788                         case 'P':
00789                                 /* Pointer */
00790                                 flags |= __PRINTF_FLAG_BIGCHARS;
00791                         case 'p':
00792                                 flags |= __PRINTF_FLAG_PREFIX;
00793                                 flags |= __PRINTF_FLAG_ZEROPADDED;
00794                                 base = 16;
00795                                 qualifier = PrintfQualifierPointer;
00796                                 break;
00797                         case 'b':
00798                                 base = 2;
00799                                 break;
00800                         case 'o':
00801                                 base = 8;
00802                                 break;
00803                         case 'd':
00804                         case 'i':
00805                                 flags |= __PRINTF_FLAG_SIGNED;
00806                         case 'u':
00807                                 break;
00808                         case 'X':
00809                                 flags |= __PRINTF_FLAG_BIGCHARS;
00810                         case 'x':
00811                                 base = 16;
00812                                 break;
00813                         
00814                         /* Percentile itself */
00815                         case '%':
00816                                 j = i;
00817                                 goto next_char;
00818                         
00819                         /*
00820                          * Bad formatting.
00821                          */
00822                         default:
00823                                 /*
00824                                  * Unknown format. Now, j is the index of '%'
00825                                  * so we will print whole bad format sequence.
00826                                  */
00827                                 goto next_char;
00828                         }
00829                         
00830                         /* Print integers */
00831                         size_t size;
00832                         uint64_t number;
00833                         switch (qualifier) {
00834                         case PrintfQualifierByte:
00835                                 size = sizeof(unsigned char);
00836                                 number = (uint64_t) va_arg(ap, unsigned int);
00837                                 break;
00838                         case PrintfQualifierShort:
00839                                 size = sizeof(unsigned short);
00840                                 number = (uint64_t) va_arg(ap, unsigned int);
00841                                 break;
00842                         case PrintfQualifierInt:
00843                                 size = sizeof(unsigned int);
00844                                 number = (uint64_t) va_arg(ap, unsigned int);
00845                                 break;
00846                         case PrintfQualifierLong:
00847                                 size = sizeof(unsigned long);
00848                                 number = (uint64_t) va_arg(ap, unsigned long);
00849                                 break;
00850                         case PrintfQualifierLongLong:
00851                                 size = sizeof(unsigned long long);
00852                                 number = (uint64_t) va_arg(ap, unsigned long long);
00853                                 break;
00854                         case PrintfQualifierPointer:
00855                                 size = sizeof(void *);
00856                                 precision = size << 1;
00857                                 number = (uint64_t) (uintptr_t) va_arg(ap, void *);
00858                                 break;
00859                         case PrintfQualifierSize:
00860                                 size = sizeof(size_t);
00861                                 number = (uint64_t) va_arg(ap, size_t);
00862                                 break;
00863                         default:
00864                                 /* Unknown qualifier */
00865                                 counter = -counter;
00866                                 goto out;
00867                         }
00868                         
00869                         if (flags & __PRINTF_FLAG_SIGNED) {
00870                                 if (number & (0x1 << (size * 8 - 1))) {
00871                                         flags |= __PRINTF_FLAG_NEGATIVE;
00872                                         
00873                                         if (size == sizeof(uint64_t)) {
00874                                                 number = -((int64_t) number);
00875                                         } else {
00876                                                 number = ~number;
00877                                                 number &=
00878                                                     ~(0xFFFFFFFFFFFFFFFFll <<
00879                                                     (size * 8));
00880                                                 number++;
00881                                         }
00882                                 }
00883                         }
00884                         
00885                         if ((retval = print_number(number, width, precision,
00886                             base, flags, ps)) < 0) {
00887                                 counter = -counter;
00888                                 goto out;
00889                         }
00890                         
00891                         counter += retval;
00892                         j = nxt;
00893                 }
00894 next_char:
00895                 ;
00896         }
00897         
00898         if (i > j) {
00899                 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
00900                         /* Error */
00901                         counter = -counter;
00902                         goto out;
00903                 }
00904                 counter += retval;
00905         }
00906         
00907 out:
00908         return ((int) counter);
00909 }
00910 

Generated on Thu Jun 2 07:45:46 2011 for HelenOS/USB by  doxygen 1.4.7