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  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * - Redistributions of source code must retain the above copyright
00011  *   notice, this list of conditions and the following disclaimer.
00012  * - Redistributions in binary form must reproduce the above copyright
00013  *   notice, this list of conditions and the following disclaimer in the
00014  *   documentation and/or other materials provided with the distribution.
00015  * - The name of the author may not be used to endorse or promote products
00016  *   derived from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00019  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00020  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00021  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00023  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00024  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00025  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00026  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00027  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00038 #include <unistd.h>
00039 #include <stdio.h>
00040 #include <io/printf_core.h>
00041 #include <ctype.h>
00042 #include <string.h>
00043 /* For serialization */
00044 #include <async.h>
00045 
00046 #define __PRINTF_FLAG_PREFIX            0x00000001      
00047 #define __PRINTF_FLAG_SIGNED            0x00000002      
00048 #define __PRINTF_FLAG_ZEROPADDED        0x00000004      
00049 #define __PRINTF_FLAG_LEFTALIGNED       0x00000010      
00050 #define __PRINTF_FLAG_SHOWPLUS          0x00000020      
00051 #define __PRINTF_FLAG_SPACESIGN         0x00000040      
00052 #define __PRINTF_FLAG_BIGCHARS          0x00000080      
00053 #define __PRINTF_FLAG_NEGATIVE          0x00000100      
00055 #define PRINT_NUMBER_BUFFER_SIZE        (64+5)          
00062 typedef enum {
00063         PrintfQualifierByte = 0,
00064         PrintfQualifierShort,
00065         PrintfQualifierInt,
00066         PrintfQualifierLong,
00067         PrintfQualifierLongLong,
00068         PrintfQualifierSizeT,
00069         PrintfQualifierPointer
00070 } qualifier_t;
00071 
00072 static char digits_small[] = "0123456789abcdef";        
00073 static char digits_big[] = "0123456789ABCDEF";          
00081 static int printf_putnchars(const char * buf, size_t count, struct printf_spec *ps)
00082 {
00083         return ps->write((void *)buf, count, ps->data);
00084 }
00085 
00091 static int printf_putstr(const char * str, struct printf_spec *ps)
00092 {
00093         size_t count;
00094         
00095         if (str == NULL) {
00096                 return printf_putnchars("(NULL)", 6, ps);
00097         }
00098 
00099         for (count = 0; str[count] != 0; count++);
00100 
00101         if (ps->write((void *) str, count, ps->data) == count) {
00102                 return 0;
00103         }
00104         
00105         return EOF;
00106 }
00107 
00113 static int printf_putchar(int c, struct printf_spec *ps)
00114 {
00115         unsigned char ch = c;
00116         
00117         return ps->write((void *) &ch, 1, ps->data);
00118 }
00119 
00126 static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps)
00127 {
00128         int counter = 0;
00129         
00130         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
00131                 while (--width > 0) {   /* one space is consumed by character itself hence predecrement */
00132                         if (printf_putchar(' ', ps) > 0)        
00133                                 ++counter;
00134                 }
00135         }
00136         
00137         if (printf_putchar(c, ps) > 0)
00138                 counter++;
00139         
00140         while (--width > 0) { /* one space is consumed by character itself hence predecrement */
00141                 if (printf_putchar(' ', ps) > 0)
00142                         ++counter;
00143         }
00144         
00145         return ++counter;
00146 }
00147 
00156 static int print_string(char *s, int width, int precision, uint64_t flags, struct printf_spec *ps)
00157 {
00158         int counter = 0;
00159         size_t size;
00160         int retval;
00161 
00162         if (s == NULL) {
00163                 return printf_putstr("(NULL)", ps);
00164         }
00165         
00166         size = strlen(s);
00167 
00168         /* print leading spaces */
00169 
00170         if (precision == 0) 
00171                 precision = size;
00172 
00173         width -= precision;
00174         
00175         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
00176                 while (width-- > 0) {   
00177                         if (printf_putchar(' ', ps) == 1)       
00178                                 counter++;
00179                 }
00180         }
00181 
00182         while (precision > size) {
00183                 precision--;
00184                 if (printf_putchar(' ', ps) == 1)       
00185                         ++counter;
00186         }
00187         
00188         if ((retval = printf_putnchars(s, precision, ps)) < 0) {
00189                 return -counter;
00190         }
00191 
00192         counter += retval;      
00193 
00194         while (width-- > 0) {
00195                 if (printf_putchar(' ', ps) == 1)       
00196                         ++counter;
00197         }
00198         
00199         return counter;
00200 }
00201 
00202 
00217 static int print_number(uint64_t num, int width, int precision, int base , uint64_t flags, struct printf_spec *ps)
00218 {
00219         char *digits = digits_small;
00220         char d[PRINT_NUMBER_BUFFER_SIZE];       /* this is good enough even for base == 2, prefix and sign */
00221         char *ptr = &d[PRINT_NUMBER_BUFFER_SIZE - 1];
00222         int size = 0; /* size of number with all prefixes and signs */
00223         int number_size; /* size of plain number */
00224         char sgn;
00225         int retval;
00226         int counter = 0;
00227         
00228         if (flags & __PRINTF_FLAG_BIGCHARS) 
00229                 digits = digits_big;    
00230         
00231         *ptr-- = 0; /* Put zero at end of string */
00232 
00233         if (num == 0) {
00234                 *ptr-- = '0';
00235                 size++;
00236         } else {
00237                 do {
00238                         *ptr-- = digits[num % base];
00239                         size++;
00240                 } while (num /= base);
00241         }
00242         
00243         number_size = size;
00244 
00245         /* Collect sum of all prefixes/signs/... to calculate padding and leading zeroes */
00246         if (flags & __PRINTF_FLAG_PREFIX) {
00247                 switch(base) {
00248                         case 2: /* Binary formating is not standard, but usefull */
00249                                 size += 2;
00250                                 break;
00251                         case 8:
00252                                 size++;
00253                                 break;
00254                         case 16:
00255                                 size += 2;
00256                                 break;
00257                 }
00258         }
00259 
00260         sgn = 0;
00261         if (flags & __PRINTF_FLAG_SIGNED) {
00262                 if (flags & __PRINTF_FLAG_NEGATIVE) {
00263                         sgn = '-';
00264                         size++;
00265                 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
00266                                 sgn = '+';
00267                                 size++;
00268                         } else if (flags & __PRINTF_FLAG_SPACESIGN) {
00269                                         sgn = ' ';
00270                                         size++;
00271                                 }
00272         }
00273 
00274         if (flags & __PRINTF_FLAG_LEFTALIGNED) {
00275                 flags &= ~__PRINTF_FLAG_ZEROPADDED;
00276         }
00277 
00278         /* if number is leftaligned or precision is specified then zeropadding is ignored */
00279         if (flags & __PRINTF_FLAG_ZEROPADDED) {
00280                 if ((precision == 0) && (width > size)) {
00281                         precision = width - size + number_size;
00282                 }
00283         }
00284 
00285         /* print leading spaces */
00286         if (number_size > precision) /* We must print whole number not only a part */
00287                 precision = number_size;
00288 
00289         width -= precision + size - number_size;
00290         
00291         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
00292                 while (width-- > 0) {   
00293                         if (printf_putchar(' ', ps) == 1)       
00294                                 counter++;
00295                 }
00296         }
00297         
00298         /* print sign */
00299         if (sgn) {
00300                 if (printf_putchar(sgn, ps) == 1)
00301                         counter++;
00302         }
00303         
00304         /* print prefix */
00305         
00306         if (flags & __PRINTF_FLAG_PREFIX) {
00307                 switch(base) {
00308                         case 2: /* Binary formating is not standard, but usefull */
00309                                 if (printf_putchar('0', ps) == 1)
00310                                         counter++;
00311                                 if (flags & __PRINTF_FLAG_BIGCHARS) {
00312                                         if (printf_putchar('B', ps) == 1)
00313                                                 counter++;
00314                                 } else {
00315                                         if (printf_putchar('b', ps) == 1)
00316                                                 counter++;
00317                                 }
00318                                 break;
00319                         case 8:
00320                                 if (printf_putchar('o', ps) == 1)
00321                                         counter++;
00322                                 break;
00323                         case 16:
00324                                 if (printf_putchar('0', ps) == 1)
00325                                         counter++;
00326                                 if (flags & __PRINTF_FLAG_BIGCHARS) {
00327                                         if (printf_putchar('X', ps) == 1)
00328                                                 counter++;
00329                                 } else {
00330                                         if (printf_putchar('x', ps) == 1)
00331                                                 counter++;
00332                                 }
00333                                 break;
00334                 }
00335         }
00336 
00337         /* print leading zeroes */
00338         precision -= number_size;
00339         while (precision-- > 0) {       
00340                 if (printf_putchar('0', ps) == 1)
00341                         counter++;
00342         }
00343 
00344         
00345         /* print number itself */
00346 
00347         if ((retval = printf_putstr(++ptr, ps)) > 0) {
00348                 counter += retval;
00349         }
00350         
00351         /* print ending spaces */
00352         
00353         while (width-- > 0) {   
00354                 if (printf_putchar(' ', ps) == 1)       
00355                         counter++;
00356         }
00357 
00358         return counter;
00359 }
00360 
00361 
00436 int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
00437 {
00438         int i = 0, j = 0; /* i is index of currently processed char from fmt, j is index to the first not printed nonformating character */
00439         int end;
00440         int counter; /* counter of printed characters */
00441         int retval; /* used to store return values from called functions */
00442         char c;
00443         qualifier_t qualifier;  /* type of argument */
00444         int base;       /* base in which will be parameter (numbers only) printed */
00445         uint64_t number; /* argument value */
00446         size_t  size; /* byte size of integer parameter */
00447         int width, precision;
00448         uint64_t flags;
00449         
00450         /* Don't let other threads interfere */
00451         async_serialize_start();
00452 
00453         counter = 0;
00454         
00455         while ((c = fmt[i])) {
00456                 /* control character */
00457                 if (c == '%' ) { 
00458                         /* print common characters if any processed */  
00459                         if (i > j) {
00460                                 if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */
00461                                         goto minus_out;
00462                                 }
00463                                 counter += retval;
00464                         }
00465                 
00466                         j = i;
00467                         /* parse modifiers */
00468                         flags = 0;
00469                         end = 0;
00470                         
00471                         do {
00472                                 ++i;
00473                                 switch (c = fmt[i]) {
00474                                         case '#': flags |= __PRINTF_FLAG_PREFIX; break;
00475                                         case '-': flags |= __PRINTF_FLAG_LEFTALIGNED; break;
00476                                         case '+': flags |= __PRINTF_FLAG_SHOWPLUS; break;
00477                                         case ' ': flags |= __PRINTF_FLAG_SPACESIGN; break;
00478                                         case '0': flags |= __PRINTF_FLAG_ZEROPADDED; break;
00479                                         default: end = 1;
00480                                 };      
00481                                 
00482                         } while (end == 0);     
00483                         
00484                         /* width & '*' operator */
00485                         width = 0;
00486                         if (isdigit(fmt[i])) {
00487                                 while (isdigit(fmt[i])) {
00488                                         width *= 10;
00489                                         width += fmt[i++] - '0';
00490                                 }
00491                         } else if (fmt[i] == '*') {
00492                                 /* get width value from argument list*/
00493                                 i++;
00494                                 width = (int)va_arg(ap, int);
00495                                 if (width < 0) {
00496                                         /* negative width means to set '-' flag */
00497                                         width *= -1;
00498                                         flags |= __PRINTF_FLAG_LEFTALIGNED;
00499                                 }
00500                         }
00501                         
00502                         /* precision and '*' operator */        
00503                         precision = 0;
00504                         if (fmt[i] == '.') {
00505                                 ++i;
00506                                 if (isdigit(fmt[i])) {
00507                                         while (isdigit(fmt[i])) {
00508                                                 precision *= 10;
00509                                                 precision += fmt[i++] - '0';
00510                                         }
00511                                 } else if (fmt[i] == '*') {
00512                                         /* get precision value from argument list*/
00513                                         i++;
00514                                         precision = (int)va_arg(ap, int);
00515                                         if (precision < 0) {
00516                                                 /* negative precision means to ignore it */
00517                                                 precision = 0;
00518                                         }
00519                                 }
00520                         }
00521 
00522                         switch (fmt[i++]) {
00526                                 case 'h':       /* char or short */
00527                                         qualifier = PrintfQualifierShort;
00528                                         if (fmt[i] == 'h') {
00529                                                 i++;
00530                                                 qualifier = PrintfQualifierByte;
00531                                         }
00532                                         break;
00533                                 case 'l':       /* long or long long*/
00534                                         qualifier = PrintfQualifierLong;
00535                                         if (fmt[i] == 'l') {
00536                                                 i++;
00537                                                 qualifier = PrintfQualifierLongLong;
00538                                         }
00539                                         break;
00540                                 case 'z':       /* size_t */
00541                                         qualifier = PrintfQualifierSizeT;
00542                                         break;
00543                                 default:
00544                                         qualifier = PrintfQualifierInt; /* default type */
00545                                         --i;
00546                         }       
00547                         
00548                         base = 10;
00549 
00550                         switch (c = fmt[i]) {
00551 
00552                                 /*
00553                                 * String and character conversions.
00554                                 */
00555                                 case 's':
00556                                         if ((retval = print_string(va_arg(ap, char*), width, precision, flags, ps)) < 0) {
00557                                                 goto minus_out;
00558                                         };
00559                                         
00560                                         counter += retval;
00561                                         j = i + 1; 
00562                                         goto next_char;
00563                                 case 'c':
00564                                         c = va_arg(ap, unsigned int);
00565                                         if ((retval = print_char(c, width, flags, ps)) < 0) {
00566                                                 goto minus_out;
00567                                         };
00568                                         
00569                                         counter += retval;
00570                                         j = i + 1;
00571                                         goto next_char;
00572 
00573                                 /* 
00574                                  * Integer values
00575                                 */
00576                                 case 'P': /* pointer */
00577                                         flags |= __PRINTF_FLAG_BIGCHARS;
00578                                 case 'p':
00579                                         flags |= __PRINTF_FLAG_PREFIX;
00580                                         base = 16;
00581                                         qualifier = PrintfQualifierPointer;
00582                                         break;  
00583                                 case 'b': 
00584                                         base = 2;
00585                                         break;
00586                                 case 'o':
00587                                         base = 8;
00588                                         break;
00589                                 case 'd':
00590                                 case 'i':
00591                                         flags |= __PRINTF_FLAG_SIGNED;  
00592                                 case 'u':
00593                                         break;
00594                                 case 'X':
00595                                         flags |= __PRINTF_FLAG_BIGCHARS;
00596                                 case 'x':
00597                                         base = 16;
00598                                         break;
00599                                 /* percentile itself */
00600                                 case '%': 
00601                                         j = i;
00602                                         goto next_char;
00603                                 /*
00604                                 * Bad formatting.
00605                                 */
00606                                 default:
00607                                         /* Unknown format
00608                                          *  now, the j is index of '%' so we will
00609                                          * print whole bad format sequence
00610                                          */
00611                                         goto next_char;         
00612                         }
00613                 
00614                 
00615                 /* Print integers */
00616                         /* print number */
00617                         switch (qualifier) {
00618                                 case PrintfQualifierByte:
00619                                         size = sizeof(unsigned char);
00620                                         number = (uint64_t)va_arg(ap, unsigned int);
00621                                         break;
00622                                 case PrintfQualifierShort:
00623                                         size = sizeof(unsigned short);
00624                                         number = (uint64_t)va_arg(ap, unsigned int);
00625                                         break;
00626                                 case PrintfQualifierInt:
00627                                         size = sizeof(unsigned int);
00628                                         number = (uint64_t)va_arg(ap, unsigned int);
00629                                         break;
00630                                 case PrintfQualifierLong:
00631                                         size = sizeof(unsigned long);
00632                                         number = (uint64_t)va_arg(ap, unsigned long);
00633                                         break;
00634                                 case PrintfQualifierLongLong:
00635                                         size = sizeof(unsigned long long);
00636                                         number = (uint64_t)va_arg(ap, unsigned long long);
00637                                         break;
00638                                 case PrintfQualifierPointer:
00639                                         size = sizeof(void *);
00640                                         number = (uint64_t)(unsigned long)va_arg(ap, void *);
00641                                         break;
00642                                 case PrintfQualifierSizeT:
00643                                         size = sizeof(size_t);
00644                                         number = (uint64_t)va_arg(ap, size_t);
00645                                         break;
00646                                 default: /* Unknown qualifier */
00647                                         goto minus_out;
00648                                         
00649                         }
00650                         
00651                         if (flags & __PRINTF_FLAG_SIGNED) {
00652                                 if (number & (0x1 << (size*8 - 1))) {
00653                                         flags |= __PRINTF_FLAG_NEGATIVE;
00654                                 
00655                                         if (size == sizeof(uint64_t)) {
00656                                                 number = -((int64_t)number);
00657                                         } else {
00658                                                 number = ~number;
00659                                                 number &= (~((0xFFFFFFFFFFFFFFFFll) <<  (size * 8)));
00660                                                 number++;
00661                                         }
00662                                 }
00663                         }
00664 
00665                         if ((retval = print_number(number, width, precision, base, flags, ps)) < 0 ) {
00666                                 goto minus_out;
00667                         };
00668 
00669                         counter += retval;
00670                         j = i + 1;
00671                 }       
00672 next_char:
00673                         
00674                 ++i;
00675         }
00676         
00677         if (i > j) {
00678                 if ((retval = printf_putnchars(&fmt[j], (size_t)(i - j), ps)) < 0) { /* error */
00679                         goto minus_out;
00680                 }
00681                 counter += retval;
00682         }
00683         
00684         async_serialize_end();
00685         return counter;
00686 minus_out:
00687         async_serialize_end();
00688         return -counter;
00689 }
00690 
00691 
00692 

Generated on Sun Jun 18 18:00:18 2006 for HelenOS Userspace (ia64) by  doxygen 1.4.6