printf/vsnprintf.c

Go to the documentation of this file.
00001 // Copyright (C) 2006-2007 Benedikt Böhm <hollow@gentoo.org>
00002 //
00003 // This program is free software; you can redistribute it and/or
00004 // modify it under the terms of the GNU General Public License
00005 // as published by the Free Software Foundation; either version 2
00006 // of the License, or (at your option) any later version.
00007 //
00008 // This program is distributed in the hope that it will be useful,
00009 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 // GNU General Public License for more details.
00012 //
00013 // You should have received a copy of the GNU General Public License
00014 // along with this program; if not, write to the Free Software
00015 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
00016 
00017 #include "mem.h"
00018 #include "printf.h"
00019 #include "str.h"
00020 
00021 enum __printf_flags {
00022         PFL_ALT    = 0x01,
00023         PFL_ZERO   = 0x02,
00024         PFL_LEFT   = 0x04,
00025         PFL_BLANK  = 0x08,
00026         PFL_SIGN   = 0x10,
00027         PFL_UPPER  = 0x20,
00028         PFL_SIGNED = 0x40,
00029 };
00030 
00031 enum __printf_rank {
00032         PFR_CHAR,
00033         PFR_SHORT,
00034         PFR_INT,
00035         PFR_LONG,
00036         PFR_LLONG,
00037 };
00038 
00039 #define PFR_MIN PFR_CHAR
00040 #define PFR_MAX PFR_LLONG
00041 
00042 enum __printf_state {
00043         PFS_NORMAL,
00044         PFS_FLAGS,
00045         PFS_WIDTH,
00046         PFS_PREC,
00047         PFS_MOD,
00048         PFS_CONV,
00049 };
00050 
00051 typedef struct {
00052         unsigned int f; /* flags */
00053         int l;          /* length */
00054         int p;          /* precision */
00055         int s;          /* state */
00056         unsigned int w; /* width */
00057 } __printf_t;
00058 
00059 #define EMIT(C) { if (idx < size - 1) { *str++ = C; } idx++; }
00060 
00061 static
00062 int __printf_int(char *str, int size, unsigned long long int val,
00063                 int base, __printf_t f)
00064 {
00065         static const char lcdigits[] = "0123456789abcdef";
00066         static const char ucdigits[] = "0123456789ABCDEF";
00067         const char *digits;
00068 
00069         int idx = 0, ndigits = 0, nchars, minus = 0;
00070         unsigned long long int tmpval;
00071 
00072         /* select type of digits */
00073         digits = (f.f & PFL_UPPER) ? ucdigits : lcdigits;
00074 
00075         /* separate out the minus */
00076         if (f.f & PFL_SIGNED && (signed long long int) val < 0) {
00077                 minus = 1;
00078                 val = (unsigned long long int) (-(signed long long int) val);
00079         }
00080 
00081         /* count number of digits needed */
00082         tmpval = val;
00083 
00084         while (tmpval) {
00085                 tmpval /= base;
00086                 ndigits++;
00087         }
00088 
00089         /* zero still requires space */
00090         if (val == 0)
00091                 ndigits = 1;
00092 
00093         /* compute number of nondigits */
00094         nchars = f.p > ndigits ? f.p : ndigits;
00095 
00096         /* need space for sign */
00097         if (minus || (f.f & (PFL_SIGN | PFL_BLANK)))
00098                 nchars++;
00099 
00100         /* add prefix for alternate form */
00101         if (f.f & PFL_ALT) {
00102                 if (base == 16)
00103                         nchars += 2;
00104                 else if (base == 8 && val != 0)
00105                         nchars += 1;
00106         }
00107 
00108         /* early space padding */
00109         if ((f.f & (PFL_LEFT|PFL_ZERO)) == 0) {
00110                 while (f.w > nchars) {
00111                         EMIT(' ')
00112                         f.w--;
00113                 }
00114         }
00115 
00116         /* zero padding */
00117         if ((f.f & PFL_ZERO) > 0) {
00118                 while (f.w > nchars) {
00119                         EMIT('0')
00120                         f.w--;
00121                 }
00122         }
00123 
00124         /* nondigits */
00125         if (minus)
00126                 EMIT('-')
00127 
00128         else if (f.f & PFL_SIGN)
00129                 EMIT('+')
00130 
00131         else if (f.f & PFL_BLANK)
00132                 EMIT(' ')
00133 
00134         if (f.f & PFL_ALT) {
00135                 if (base == 16) {
00136                         EMIT('0')
00137                         EMIT((f.f & PFL_UPPER) ? 'X' : 'x');
00138                 }
00139 
00140                 else if (base == 8 && val != 0)
00141                         EMIT('0')
00142         }
00143 
00144         /* precision */
00145         while (f.p > ndigits) {
00146                 EMIT('0')
00147                 f.p--;
00148         }
00149 
00150         /* generate the number from right to left */
00151         str += ndigits;
00152 
00153         do {
00154                 if (idx < size)
00155                         *--str = digits[val % base];
00156 
00157                 idx++;
00158         } while ((val /= base));
00159 
00160         str += ndigits;
00161 
00162         /* late space padding */
00163         if ((f.f & PFL_LEFT) > 0) {
00164                 while (f.w > nchars) {
00165                         EMIT(' ')
00166                         f.w--;
00167                 }
00168         }
00169 
00170         return idx;
00171 }
00172 
00173 /* supported formats:
00174 ** - format flags: #, 0, -, ' ', +
00175 ** - field width
00176 ** - argument precision
00177 ** - length mods: hh, h, l, and ll
00178 ** - conversion spec: d, i, u, o, x, X, c, s, p, P, n */
00179 int _lucid_vsnprintf(char *str, int size, const char *fmt, va_list _ap)
00180 {
00181         /* generic counter */
00182         int i;
00183 
00184         /* generic pointer */
00185         const char *p;
00186 
00187         /* keep track of string length */
00188         int idx = 0;
00189 
00190         /* save pointer to start of current conversion */
00191         const char *ccp = fmt;
00192 
00193         /* current character in format */
00194         char c;
00195 
00196         /* current conversion data */
00197         __printf_t f;
00198 
00199         /* arguments */
00200         union {
00201                 /* signed argument */
00202                 signed long long int d;
00203 
00204                 /* unsigned argument */
00205                 unsigned long long int u;
00206 
00207                 /* float argument */
00208                 double f;
00209 
00210                 /* character argument */
00211                 int c;
00212 
00213                 /* string argument */
00214                 const char *s;
00215 
00216                 /* pointer argument */
00217                 void *p;
00218 
00219                 /* number argument */
00220                 int *n;
00221         } arg;
00222 
00223         /* base used for integer conversions */
00224         int base;
00225 
00226         /* number of consumed bytes in conversions */
00227         int len;
00228 
00229         /* don't consume original ap */
00230         va_list ap;
00231         va_copy(ap, _ap);
00232 
00233         /* initialize conversion data */
00234         f.f = 0;
00235         f.l = PFR_INT;
00236         f.p = -1;
00237         f.s = PFS_NORMAL;
00238         f.w = 0;
00239 
00240         if (size > 0)
00241                 mem_set(str, 0, size);
00242 
00243         while ((c = *fmt++)) {
00244                 switch (f.s) {
00245                 case PFS_NORMAL:
00246                         if (c == '%') {
00247                                 f.f = 0;
00248                                 f.l = PFR_INT;
00249                                 f.p = -1;
00250                                 f.s = PFS_FLAGS;
00251                                 f.w = 0;
00252                                 ccp = &c;
00253                         }
00254 
00255                         else
00256                                 EMIT(c)
00257 
00258                         break;
00259 
00260                 case PFS_FLAGS:
00261                         switch (c) {
00262                         case '#':
00263                                 f.f |= PFL_ALT;
00264                                 break;
00265 
00266                         case '0':
00267                                 if (!(f.f & PFL_LEFT))
00268                                         f.f |= PFL_ZERO;
00269                                 break;
00270 
00271                         case '-':
00272                                 f.f &= ~PFL_ZERO; /* left overrides zero */
00273                                 f.f |=  PFL_LEFT;
00274                                 break;
00275 
00276                         case ' ':
00277                                 f.f |= PFL_BLANK;
00278                                 break;
00279 
00280                         case '+':
00281                                 f.f &= ~PFL_BLANK; /* sign overrides blank */
00282                                 f.f |=  PFL_SIGN;
00283                                 break;
00284 
00285                         default:
00286                                 f.s = PFS_WIDTH;
00287                                 fmt--;
00288                                 break;
00289                         }
00290 
00291                         break;
00292 
00293                 case PFS_WIDTH:
00294                         if (c == '-') {
00295                                         f.f &= PFL_ZERO; /* left overrides zero */
00296                                         f.f |= PFL_LEFT;
00297                         }
00298 
00299                         else if (c >= '0' && c <= '9')
00300                                 f.w = f.w * 10  + (c - '0');
00301 
00302                         else if (c == '*') {
00303                                 f.w = va_arg(ap, int);
00304 
00305                                 if (f.w < 0) {
00306                                         f.w = -f.w;
00307                                         f.f &= PFL_ZERO; /* left overrides zero */
00308                                         f.f |= PFL_LEFT;
00309                                 }
00310                         }
00311 
00312                         else if (c == '.') {
00313                                 f.p = 0;
00314                                 f.s = PFS_PREC;
00315                         }
00316 
00317                         else {
00318                                 f.s = PFS_MOD;
00319                                 fmt--;
00320                         }
00321 
00322                         break;
00323 
00324                 case PFS_PREC:
00325                         if (c >= '0' && c <= '9')
00326                                 f.p = f.p * 10  + (c - '0');
00327 
00328                         else if (c == '*') {
00329                                 f.p = va_arg(ap, int);
00330 
00331                                 if (f.p < 0)
00332                                         f.p = 0;
00333                         }
00334 
00335                         else {
00336                                 f.s = PFS_MOD;
00337                                 fmt--;
00338                         }
00339 
00340                         break;
00341 
00342                 case PFS_MOD:
00343                         switch (c) {
00344                         case 'h':
00345                                 f.l--;
00346                                 break;
00347 
00348                         case 'l':
00349                                 f.l++;
00350                                 break;
00351 
00352                         default:
00353                                 f.s = PFS_CONV;
00354                                 fmt--;
00355                                 break;
00356                         }
00357 
00358                         break;
00359 
00360                 case PFS_CONV:
00361                         f.s = PFS_NORMAL;
00362 
00363                         if (f.l > PFR_MAX)
00364                                 f.l = PFR_MAX;
00365 
00366                         if (f.l < PFR_MIN)
00367                                 f.l = PFR_MIN;
00368 
00369                         switch (c) {
00370                         case 'P':
00371                                 f.f |= PFL_UPPER;
00372 
00373                         case 'p':
00374                                 base = 16;
00375                                 f.p  = (8 * sizeof(void *) + 3)/4;
00376                                 f.f |= PFL_ALT;
00377 
00378                                 arg.u = (unsigned long long int) (unsigned long int) va_arg(ap, void *);
00379 
00380                                 goto is_integer;
00381 
00382                         case 'd':
00383                         case 'i': /* signed conversion */
00384                                 base = 10;
00385                                 f.f |= PFL_SIGNED;
00386 
00387                                 switch (f.l) {
00388                                 case PFR_CHAR:
00389                                         arg.d = (signed char) va_arg(ap, signed int);
00390                                         break;
00391 
00392                                 case PFR_SHORT:
00393                                         arg.d = (signed short int) va_arg(ap, signed int);
00394                                         break;
00395 
00396                                 case PFR_INT:
00397                                         arg.d = (signed int) va_arg(ap, signed int);
00398                                         break;
00399 
00400                                 case PFR_LONG:
00401                                         arg.d = (signed long int) va_arg(ap, signed long int);
00402                                         break;
00403 
00404                                 case PFR_LLONG:
00405                                         arg.d = (signed long long int) va_arg(ap, signed long long int);
00406                                         break;
00407 
00408                                 default:
00409                                         arg.d = (signed long long int) va_arg(ap, signed int);
00410                                         break;
00411                                 }
00412 
00413                                 arg.u = (unsigned long long int) arg.d;
00414 
00415                                 goto is_integer;
00416 
00417                         case 'o':
00418                                 base = 8;
00419                                 goto is_unsigned;
00420 
00421                         case 'u':
00422                                 base = 10;
00423                                 goto is_unsigned;
00424 
00425                         case 'X':
00426                                 f.f |= PFL_UPPER;
00427 
00428                         case 'x':
00429                                 base = 16;
00430                                 goto is_unsigned;
00431 
00432                         is_unsigned:
00433                                 switch (f.l) {
00434                                 case PFR_CHAR:
00435                                         arg.u = (unsigned char) va_arg(ap, unsigned int);
00436                                         break;
00437 
00438                                 case PFR_SHORT:
00439                                         arg.u = (unsigned short int) va_arg(ap, unsigned int);
00440                                         break;
00441 
00442                                 case PFR_INT:
00443                                         arg.u = (unsigned int) va_arg(ap, unsigned int);
00444                                         break;
00445 
00446                                 case PFR_LONG:
00447                                         arg.u = (unsigned long int) va_arg(ap, unsigned long int);
00448                                         break;
00449 
00450                                 case PFR_LLONG:
00451                                         arg.u = (unsigned long long int) va_arg(ap, unsigned long long int);
00452                                         break;
00453 
00454                                 default:
00455                                         arg.u = (unsigned long long int) va_arg(ap, unsigned int);
00456                                         break;
00457                                 }
00458 
00459                         is_integer:
00460                                 len = __printf_int(str, size, arg.u, base, f);
00461 
00462                                 str += len;
00463                                 idx += len;
00464                                 break;
00465 
00466                         case 'c': /* character conversion */
00467                                 arg.c = (char) va_arg(ap, int);
00468                                 EMIT(arg.c)
00469                                 break;
00470 
00471                         case 's': /* string conversion */
00472                                 arg.s = va_arg(ap, const char *);
00473                                 arg.s = arg.s ? arg.s : "(null)";
00474                                 len   = str_len(arg.s);
00475 
00476                         is_string:
00477                                 if (f.p != -1 && len > f.p)
00478                                         len = f.p;
00479 
00480                                 if ((f.f & (PFL_LEFT|PFL_ZERO)) == 0) {
00481                                         while (f.w > len) {
00482                                                 EMIT(' ')
00483                                                 f.w--;
00484                                         }
00485                                 }
00486 
00487                                 if ((f.f & PFL_ZERO) > 0) {
00488                                         while (f.w > len) {
00489                                                 EMIT('0')
00490                                                 f.w--;
00491                                         }
00492                                 }
00493 
00494                                 for (i = len; i; i--)
00495                                         EMIT(*arg.s++)
00496 
00497                                 if ((f.f & PFL_LEFT) > 0) {
00498                                         while (f.w > len) {
00499                                                 EMIT(' ')
00500                                                 f.w--;
00501                                         }
00502                                 }
00503 
00504                                 break;
00505 
00506                         case 'n':
00507                                 arg.n  = va_arg(ap, int *);
00508                                 *arg.n = idx;
00509 
00510                                 break;
00511 
00512                         case '%':
00513                                 EMIT(c)
00514                                 break;
00515 
00516                         default:
00517                                 /* no padding for unknown conversion */
00518                                 f.w =  0;
00519                                 f.p = -1;
00520 
00521                                 arg.s = ccp;
00522                                 len   = str_len(arg.s);
00523                                 fmt   = ccp + len;
00524 
00525                                 p = str_chr(arg.s + 1, '%', len - 1);
00526 
00527                                 if (p != 0) {
00528                                         len = p - arg.s - 1;
00529                                         fmt = p - 1;
00530                                 }
00531 
00532                                 goto is_string;
00533                         }
00534 
00535                         break;
00536                 }
00537         }
00538 
00539         va_end(ap);
00540 
00541         return idx;
00542 }

Generated on Tue Jun 19 20:38:26 2007 for lucid by  doxygen 1.5.2