root/common/libc/printf/__printf.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __pf_print
  2. __pf_endfield
  3. __pf_modifier
  4. __pf_getnum
  5. __pf_setbase
  6. __pf_fill
  7. __pf_printstuff
  8. __pf_printnum
  9. __pf_send
  10. __vprintf

   1 /*
   2  * Copyright (c) 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
   3  *      The President and Fellows of Harvard College.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. Neither the name of the University nor the names of its contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 /*
  31  * Guts of printf.
  32  *
  33  * This file is used in both libc and the kernel and needs to work in both
  34  * contexts. This makes a few things a bit awkward.
  35  *
  36  * This is a slightly simplified version of the real-life printf
  37  * originally used in the VINO kernel.
  38  */
  39 
  40 #ifdef _KERNEL
  41 #include <types.h>
  42 #include <lib.h>
  43 #define assert KASSERT
  44 #else
  45 
  46 #include <sys/types.h>
  47 #include <assert.h>
  48 #include <stdint.h>
  49 #include <stdio.h>
  50 #include <string.h>
  51 #endif
  52 
  53 #include <stdarg.h>
  54 
  55 
  56 /* 
  57  * Do we want to support "long long" types with %lld?
  58  *
  59  * Using 64-bit types with gcc causes gcc to emit calls to functions
  60  * like __moddi3 and __divdi3. These need to be provided at link time,
  61  * which can be a hassle; this switch is provided to help avoid
  62  * needing them.
  63  */
  64 #define USE_LONGLONG
  65 
  66 /*
  67  * Define a type that holds the longest signed integer we intend to support.
  68  */
  69 #ifdef USE_LONGLONG
  70 #define INTTYPE  long long
  71 #else
  72 #define INTTYPE  long
  73 #endif
  74 
  75 
  76 /* 
  77  * Space for a long long in base 8, plus a NUL, plus one 
  78  * character extra for slop. 
  79  *
  80  * CHAR_BIT is the number of bits in a char; thus sizeof(long long)*CHAR_BIT
  81  * is the number of bits in a long long. Each octal digit prints 3 bits.
  82  * Values printed in larger bases will be shorter strings.
  83  */
  84 #define NUMBER_BUF_SIZE ((sizeof(INTTYPE) * CHAR_BIT) / 3 + 2)
  85 
  86 /*
  87  * Structure holding the state for printf.
  88  */
  89 typedef struct {
  90         /* Callback for sending printed string data */
  91         void (*sendfunc)(void *clientdata, const char *str, size_t len);
  92         void *clientdata;
  93 
  94         /* The varargs argument pointer */
  95         va_list ap;
  96 
  97         /* Total count of characters printed */
  98         int charcount;
  99 
 100         /* Flag that's true if we are currently looking in a %-format */
 101         int in_pct;
 102 
 103         /* Size of the integer argument to retrieve */
 104         enum { 
 105                 INTSZ, 
 106                 LONGSZ, 
 107 #ifdef USE_LONGLONG
 108                 LLONGSZ,
 109 #endif
 110         } size;
 111 
 112         /* The value of the integer argument retrieved */
 113         unsigned INTTYPE num;
 114 
 115         /* Sign of the integer argument (0 = positive; -1 = negative) */
 116         int sign;
 117 
 118         /* Field width (number of spaces) */
 119         int spacing;
 120 
 121         /* Flag: align to left in field instead of right */
 122         int rightspc;
 123 
 124         /* Character to pad to field size with (space or 0) */
 125         int fillchar;
 126 
 127         /* Number base to print the integer argument in (8, 10, 16) */
 128         int base;
 129 
 130         /* Flag: if set, print 0x before hex and 0 before octal numbers */
 131         int baseprefix;
 132 
 133         /* Flag: alternative output format selected with %#... */
 134         int altformat;
 135 } PF;
 136 
 137 /*
 138  * Send some text onward to the output.
 139  *
 140  * We count the total length we send out so we can return it from __vprintf,
 141  * since that's what most printf-like functions want to return.
 142  */
 143 static
 144 void
 145 __pf_print(PF *pf, const char *txt, size_t len)
 146 {
 147         pf->sendfunc(pf->clientdata, txt, len);
 148         pf->charcount += len;
 149 }
 150 
 151 /*
 152  * Reset the state for the next %-field.
 153  */
 154 static
 155 void
 156 __pf_endfield(PF *pf)
 157 {
 158         pf->in_pct = 0;
 159         pf->size = INTSZ;
 160         pf->num = 0;
 161         pf->sign = 0;
 162         pf->spacing = 0;
 163         pf->rightspc = 0;
 164         pf->fillchar = ' ';
 165         pf->base = 0;
 166         pf->baseprefix = 0;
 167         pf->altformat = 0;
 168 }
 169 
 170 /*
 171  * Process modifier chars (between the % and the type specifier)
 172  *    #           use "alternate display format"
 173  *    -           left align in field instead of right align
 174  *    l           value is long (ll = long long)
 175  *    0-9         field width
 176  *    leading 0   pad with zeros instead of spaces
 177  */
 178 static
 179 void
 180 __pf_modifier(PF *pf, int ch)
 181 {
 182         switch (ch) {
 183         case '#':
 184                 pf->altformat = 1;
 185                 break;
 186         case '-':
 187                 pf->rightspc = 1;
 188                 break;
 189         case 'l': 
 190                 if (pf->size==LONGSZ) {
 191 #ifdef USE_LONGLONG
 192                         pf->size = LLONGSZ;
 193 #endif
 194                 }
 195                 else {
 196                         pf->size = LONGSZ;
 197                 }
 198                 break;
 199         case '0': 
 200                 if (pf->spacing>0) {
 201                         /*
 202                          * Already seen some digits; this is part of the
 203                          * field size.
 204                          */
 205                         pf->spacing = pf->spacing*10;
 206                 }
 207                 else {
 208                         /*
 209                          * Leading zero; set the padding character to 0.
 210                          */
 211                         pf->fillchar = '0';
 212                 }
 213                 break;
 214         default:
 215                 /*
 216                  * Invalid characters should be filtered out by a
 217                  * higher-level function, so if this assert goes off
 218                  * it's our fault.
 219                  */
 220                 assert(ch>'0' && ch<='9');
 221 
 222                 /*
 223                  * Got a digit; accumulate the field size.
 224                  */
 225                 pf->spacing = pf->spacing*10 + (ch-'0');
 226                 break;
 227         }
 228 }
 229 
 230 /*
 231  * Retrieve a numeric argument from the argument list and store it
 232  * in pf->num, according to the size recorded in pf->size and using
 233  * the numeric type specified by ch.
 234  */
 235 static
 236 void
 237 __pf_getnum(PF *pf, int ch)
 238 {
 239         if (ch=='p') {
 240                 /* 
 241                  * Pointer.
 242                  *
 243                  * uintptr_t is a C99 standard type that's an unsigned
 244                  * integer the same size as a pointer.
 245                  */
 246                 pf->num = (uintptr_t) va_arg(pf->ap, void *);
 247         }
 248         else if (ch=='d') {
 249                 /* signed integer */
 250                 INTTYPE signednum=0;
 251                 switch (pf->size) {
 252                 case INTSZ:
 253                         /* %d */
 254                         signednum = va_arg(pf->ap, int);
 255                         break;
 256                 case LONGSZ:
 257                         /* %ld */
 258                         signednum = va_arg(pf->ap, long);
 259                         break;
 260 #ifdef USE_LONGLONG
 261                 case LLONGSZ:
 262                         /* %lld */
 263                         signednum = va_arg(pf->ap, long long);
 264                         break;
 265 #endif
 266                 }
 267 
 268                 /*
 269                  * Check for negative numbers.
 270                  */
 271                 if (signednum < 0) {
 272                         pf->sign = -1;
 273                         pf->num = -signednum;
 274                 }
 275                 else {
 276                         pf->num = signednum;
 277                 }
 278         }
 279         else {
 280                 /* unsigned integer */
 281                 switch (pf->size) {
 282                 case INTSZ:
 283                         /* %u (or %o, %x) */
 284                         pf->num = va_arg(pf->ap, unsigned int);
 285                         break;
 286                 case LONGSZ:
 287                         /* %lu (or %lo, %lx) */
 288                         pf->num = va_arg(pf->ap, unsigned long);
 289                         break;
 290 #ifdef USE_LONGLONG
 291                 case LLONGSZ:
 292                         /* %llu, %llo, %llx */
 293                         pf->num = va_arg(pf->ap, unsigned long long);
 294                         break;
 295 #endif
 296                 }
 297         }
 298 }
 299 
 300 /*
 301  * Set the printing base based on the numeric type specified in ch.
 302  *     %o     octal
 303  *     %d,%u  decimal
 304  *     %x     hex
 305  *     %p     pointer (print as hex)
 306  *
 307  * If the "alternate format" was requested, or always for pointers,
 308  * note to print the C prefix for the type.
 309  */
 310 static
 311 void
 312 __pf_setbase(PF *pf, int ch)
 313 {
 314         switch (ch) {
 315         case 'd':
 316         case 'u':
 317                 pf->base = 10;
 318                 break;
 319         case 'x':
 320         case 'p':
 321                 pf->base = 16;
 322                 break;
 323         case 'o':
 324                 pf->base = 8;
 325                 break;
 326         }
 327         if (pf->altformat || ch=='p') {
 328                 pf->baseprefix = 1;
 329         }
 330 }
 331 
 332 /*
 333  * Function to print "spc" instances of the fill character.
 334  */
 335 static
 336 void
 337 __pf_fill(PF *pf, int spc)
 338 {
 339         char f = pf->fillchar;
 340         int i;
 341         for (i=0; i<spc; i++) {
 342                 __pf_print(pf, &f, 1);
 343         }
 344 }
 345 
 346 /*
 347  * General printing function. Prints the string "stuff".
 348  * The two prefixes (in practice one is a type prefix, such as "0x",
 349  * and the other is the sign) get printed *after* space padding but
 350  * *before* zero padding, if padding is on the left.
 351  */
 352 static
 353 void
 354 __pf_printstuff(PF *pf,
 355                 const char *prefix, const char *prefix2,
 356                 const char *stuff)
 357 {
 358         /* Total length to print. */
 359         int len = strlen(prefix)+strlen(prefix2)+strlen(stuff);
 360 
 361         /* Get field width and compute amount of padding in "spc". */
 362         int spc = pf->spacing;
 363         if (spc > len) {
 364                 spc -= len;
 365         }
 366         else {
 367                 spc = 0;
 368         }
 369 
 370         /* If padding on left and the fill char is not 0, pad first. */
 371         if (spc > 0 && pf->rightspc==0 && pf->fillchar!='0') {
 372                 __pf_fill(pf, spc);
 373         }
 374 
 375         /* Print the prefixes. */
 376         __pf_print(pf, prefix, strlen(prefix));
 377         __pf_print(pf, prefix2, strlen(prefix2));
 378 
 379         /* If padding on left and the fill char *is* 0, pad here. */
 380         if (spc > 0 && pf->rightspc==0 && pf->fillchar=='0') {
 381                 __pf_fill(pf, spc);
 382         }
 383 
 384         /* Print the actual string. */
 385         __pf_print(pf, stuff, strlen(stuff));
 386 
 387         /* If padding on the right, pad afterwards. */
 388         if (spc > 0 && pf->rightspc!=0) {
 389                 __pf_fill(pf, spc);
 390         }
 391 }
 392 
 393 /*
 394  * Function to convert a number to ascii and then print it.
 395  *
 396  * Works from right to left in a buffer of NUMBER_BUF_SIZE bytes.
 397  * NUMBER_BUF_SIZE is set so that the longest number string we can 
 398  * generate (a long long printed in octal) will fit. See above.
 399  */
 400 static
 401 void
 402 __pf_printnum(PF *pf)
 403 {
 404         /* Digits to print with. */
 405         const char *const digits = "0123456789abcdef";
 406 
 407         char buf[NUMBER_BUF_SIZE];   /* Accumulation buffer for string. */
 408         char *x;                     /* Current pointer into buf. */ 
 409         unsigned INTTYPE xnum;       /* Current value to print. */
 410         const char *bprefix;         /* Base prefix (0, 0x, or nothing) */
 411         const char *sprefix;         /* Sign prefix (- or nothing) */
 412 
 413         /* Start in the last slot of the buffer. */
 414         x = buf+sizeof(buf)-1;
 415 
 416         /* Insert null terminator. */
 417         *x-- = 0;
 418 
 419         /* Initialize value. */
 420         xnum = pf->num;
 421 
 422         /* 
 423          * Convert a single digit.
 424          * Do this loop at least once - that way 0 prints as 0 and not "". 
 425          */
 426         do {
 427                 /* 
 428                  * Get the digit character for the least significant
 429                  * part of xnum.
 430                  */
 431                 *x = digits[xnum % pf->base];
 432                 
 433                 /*
 434                  * Back up the pointer to point to the next space to the left.
 435                  */
 436                 x--;
 437 
 438                 /*
 439                  * Drop the value of the digit we just printed from xnum.
 440                  */
 441                 xnum = xnum / pf->base;
 442 
 443                 /*
 444                  * If xnum hits 0 there's no more number left.
 445                  */
 446         } while (xnum > 0);
 447 
 448         /*
 449          * x points to the *next* slot in the buffer to use.
 450          * However, we're done printing the number. So it's pointing
 451          * one slot *before* the start of the actual number text.
 452          * So advance it by one so it actually points at the number.
 453          */
 454         x++;
 455 
 456         /*
 457          * If a base prefix was requested, select it.
 458          */
 459         if (pf->baseprefix && pf->base==16) {
 460                 bprefix = "0x";
 461         }
 462         else if (pf->baseprefix && pf->base==8) {
 463                 bprefix = "0";
 464         }
 465         else {
 466                 bprefix = "";
 467         }
 468 
 469         /*
 470          * Choose the sign prefix.
 471          */
 472         sprefix = pf->sign ? "-" : "";
 473 
 474         /*
 475          * Now actually print the string we just generated.
 476          */
 477         __pf_printstuff(pf, sprefix, bprefix, x);
 478 }
 479 
 480 /*
 481  * Process a single character out of the format string.
 482  */
 483 static
 484 void
 485 __pf_send(PF *pf, int ch)
 486 {
 487         /* Cannot get NULs here. */
 488         assert(ch!=0);
 489 
 490         if (pf->in_pct==0 && ch!='%') {
 491                 /*
 492                  * Not currently in a format, and not a %. Just send
 493                  * the character on through.
 494                  */
 495                 char c = ch;
 496                 __pf_print(pf, &c, 1);
 497         }
 498         else if (pf->in_pct==0) {
 499                 /*
 500                  * Not in a format, but got a %. Start a format.
 501                  */
 502                 pf->in_pct = 1;
 503         }
 504         else if (strchr("#-l0123456789", ch)) {
 505                 /*
 506                  * These are the modifier characters we recognize.
 507                  * (These are the characters between the % and the type.)
 508                  */
 509                 __pf_modifier(pf, ch);
 510         }
 511         else if (strchr("doupx", ch)) {
 512                 /*
 513                  * Integer types.
 514                  * Fetch the number, set the base, print it, then
 515                  * reset for the next format.
 516                  */
 517                 __pf_getnum(pf, ch);
 518                 __pf_setbase(pf, ch);
 519                 __pf_printnum(pf);
 520                 __pf_endfield(pf);
 521         }
 522         else if (ch=='s') {
 523                 /*
 524                  * Print a string.
 525                  */
 526                 const char *str = va_arg(pf->ap, const char *);
 527                 if (str==NULL) {
 528                         str = "(null)";
 529                 }
 530                 __pf_printstuff(pf, "", "", str);
 531                 __pf_endfield(pf);
 532         }
 533         else {
 534                 /* 
 535                  * %%, %c, or illegal character. 
 536                  * Illegal characters are printed like %%.
 537                  * for example, %5k prints "    k".
 538                  */
 539                 char x[2];
 540                 if (ch=='c') {
 541                         x[0] = va_arg(pf->ap, int);
 542                 }
 543                 else {
 544                         x[0] = ch;
 545                 }
 546                 x[1] = 0;
 547                 __pf_printstuff(pf, "", "", x);
 548                 __pf_endfield(pf);
 549         }
 550 }
 551 
 552 /*
 553  * Do a whole printf session.
 554  * Create and initialize a printf state object,
 555  * then send it each character from the format string.
 556  */
 557 int
 558 __vprintf(void (*func)(void *clientdata, const char *str, size_t len), 
 559           void *clientdata, const char *format, va_list ap)
 560 {
 561         PF pf;
 562         int i;
 563 
 564         pf.sendfunc = func;
 565         pf.clientdata = clientdata;
 566         pf.ap = ap;
 567         pf.charcount = 0;
 568         __pf_endfield(&pf);
 569 
 570         for (i=0; format[i]; i++) {
 571                 __pf_send(&pf, format[i]);
 572         }
 573 
 574         return pf.charcount;
 575 }

/* [<][>][^][v][top][bottom][index][help] */