printf/vsnprintf.c

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

Generated on Sun Dec 3 17:45:53 2006 for lucid by  doxygen 1.5.1