format.cxx

Go to the documentation of this file.
00001 /**************************************************************************
00002  *
00003  * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, The EROS
00004  *   Group, LLC.
00005  * Copyright (C) 2004, 2005, 2006, Johns Hopkins University.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or
00009  * without modification, are permitted provided that the following
00010  * conditions are met:
00011  *
00012  *   - Redistributions of source code must contain the above
00013  *     copyright notice, this list of conditions, and the following
00014  *     disclaimer.
00015  *
00016  *   - Redistributions in binary form must reproduce the above
00017  *     copyright notice, this list of conditions, and the following
00018  *     disclaimer in the documentation and/or other materials
00019  *     provided with the distribution.
00020  *
00021  *   - Neither the names of the copyright holders nor the names of any
00022  *     of any contributors may be used to endorse or promote products
00023  *     derived from this software without specific prior written
00024  *     permission.
00025  *
00026  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00027  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00028  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00029  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00030  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00031  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00032  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00033  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00034  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00035  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00036  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00037  *
00038  **************************************************************************/
00039 
00040 #include <stdarg.h>
00041 #include <stdint.h>
00042 #include <assert.h>
00043 #include <string.h>
00044 
00045 #include <string>
00046 #include <iostream>
00047 #include <sstream>
00048 
00049 #include <libsherpa/format.hxx>
00050 
00051 namespace sherpa {
00052   std::string vformat(const char *fmt, va_list ap)
00053   {
00054     std::ostringstream oss;
00055 
00056 #define PUT_DIGIT(d)                       \
00057     do {                                   \
00058     *bufp = '0'+(d);                       \
00059     if (d >= 10) *bufp = (cnv + ((d)-10)); \
00060     bufp++;                                \
00061   } while(false)
00062 
00063     while (*fmt) {
00064       if (*fmt != '%') {
00065         oss << *fmt++;
00066         continue;
00067       }
00068 
00069       /* *fmt is '%', so we are looking at an argument specification. */
00070       fmt++;
00071 
00072       {
00073         char buf[40];           /* long enough for anything */
00074         char *bufp = buf;
00075         char *sptr = 0;
00076         size_t slen = 0;
00077 
00078         bool needSign = false;  /* true if we need space for a sign */
00079         bool isNegative = false;
00080         bool ladjust = false;
00081         char posSign = ' ';
00082         unsigned width = 0;
00083         unsigned prec = 0;
00084         unsigned base = 10;
00085         char len = ' ';
00086         char padc = ' ';
00087         char cnv = ' ';
00088 
00089         /* Parse the flag characters */
00090         for(;;) {
00091           switch (*fmt) {
00092           case ' ': /* Initial sign/space? */
00093             {
00094               needSign = true;
00095               fmt++;
00096               continue;
00097             }
00098 
00099           case '+': /* Initial +/- mandatory */
00100             {
00101               needSign = true;
00102               posSign = '+';
00103               fmt++;
00104               continue;
00105             }
00106 
00107           case '-': /* Left adjust */
00108             {
00109               ladjust = true;
00110               fmt++;
00111               continue;
00112             }
00113 
00114           case '0': /* Leading zero? */
00115             {
00116               padc = '0';
00117               fmt++;
00118               continue;
00119             }
00120 
00121             /* Following are ignored: */
00122           case '#': /* Alternate output format */
00123           case '\'': /* SUSv2 Group by thousands according to locale */
00124           case 'I': /* GLIBC use locale-specific digits */
00125             fmt++;
00126             continue;
00127 
00128           default:
00129             break;
00130           }
00131 
00132           break;                        /* proceed to field width */
00133         }
00134 
00135         /* Field width */
00136         while (isdigit(*fmt)) {
00137           width *= 10;
00138           width += (*fmt - '0');
00139           fmt++;
00140         }
00141 
00142         /* Precision */
00143         if (*fmt == '.') {
00144           fmt++;
00145           while (isdigit(*fmt)) {
00146             prec *= 10;
00147             prec += (*fmt - '0');
00148             fmt++;
00149           }
00150         }
00151 
00152         /* Length modifier */
00153         switch (len = *fmt) {
00154         case 'h':                       /* short, char */
00155           {
00156             fmt++;
00157 
00158             if (*fmt == 'h') {
00159               fmt++;
00160               len = 'c';
00161             }
00162             break;
00163           }
00164         case 'l':                       /* long, long long */
00165           {
00166             fmt++;
00167             if (*fmt == 'l') {
00168               fmt++;
00169               len = 'q';
00170             }
00171             break;
00172           }
00173 #if 0
00174         case 'L':                       /* long double */
00175           {
00176             fmt++;
00177             len = sizeof(long double);
00178             break;
00179           }
00180 #endif
00181           // 'q' legacy BSD 4.4 not supported
00182         case 'j':                       /* intmax_t */
00183           {
00184             fmt++;
00185             len = sizeof(intmax_t);
00186             break;
00187           }
00188         case 'z':                       /* size_t */
00189           {
00190             fmt++;
00191             len = sizeof(size_t);
00192             break;
00193           }
00194         case 't':                       /* ptrdiff_t */
00195           {
00196             fmt++;
00197             len = sizeof(ptrdiff_t);
00198             break;
00199           }
00200         }
00201 
00202         /* Conversion specifier */
00203         switch (cnv = *fmt++) {
00204           /* FIRST ALL THE SIGNED INTEGER CASES */
00205         case 'o':                       /* octal */
00206           {
00207             base = 8;
00208             /* FALL THROUGH */
00209           }
00210         case 'd':                       /* signed int */
00211         case 'i':
00212           {
00213             long long ll;
00214 
00215             switch (len) {
00216             case 'q':
00217               ll = va_arg(ap, long long);
00218               break;
00219             case 'j':
00220               ll = va_arg(ap, intmax_t);
00221               break;
00222             case 'z':
00223               ll = va_arg(ap, size_t);
00224               break;
00225             case 't':
00226               ll = va_arg(ap, ptrdiff_t);
00227               break;
00228             default:
00229               ll = va_arg(ap, long); /* handles char, short too */
00230               break;
00231             }
00232 
00233             if (ll < 0ll) {
00234               isNegative = true;
00235               needSign = true;
00236               ll = -ll;
00237             }
00238 
00239             if (ll == 0ll)
00240               PUT_DIGIT(0);
00241 
00242             while (ll) {
00243               int digit = ll % base;
00244               ll = ll / base;
00245               PUT_DIGIT(digit);
00246             }
00247             break;
00248           }
00249         case 'p':                       /* pointer */
00250           {
00251             cnv = 'x';
00252             if (len == ' ')
00253               len = 'p';
00254 
00255             /* FALL THROUGH */
00256           }
00257         case 'x':                       /* unsigned hexadecimal, lowercase */
00258         case 'X':                       /* unsigned hexadecimal, uppercase */
00259           {
00260             base = 16;
00261             cnv = cnv - ('x' - 'a');
00262             /* FALL THROUGH */
00263           }
00264         case 'u':                       /* unsigned int */
00265           {
00266             unsigned long long ull;
00267 
00268             switch (len) {
00269             case 'q':
00270               ull = va_arg(ap, unsigned long long);
00271               break;
00272             case 'j':
00273               ull = va_arg(ap, intmax_t);
00274               break;
00275             case 'z':
00276               ull = va_arg(ap, size_t);
00277               break;
00278             case 't':
00279               ull = va_arg(ap, ptrdiff_t);
00280               break;
00281             case 'p':
00282               ull = (uintptr_t) va_arg(ap, void *);
00283               break;
00284             default:
00285               ull = va_arg(ap, unsigned long); /* handles char, short too */
00286               break;
00287             }
00288 
00289             if (ull == 0llu)
00290               PUT_DIGIT(0);
00291 
00292             while (ull) {
00293               unsigned int digit = ull % base;
00294               ull = ull / base;
00295               PUT_DIGIT(digit);
00296             }
00297             break;
00298           }
00299         case 'c':                       /* character, long character */
00300           // SUSv2 'C' not supported
00301           {
00302             char c = va_arg(ap, int);
00303             *bufp++ = c;
00304             break;
00305           }
00306         case 's':                       /* string, long string */
00307           // SUSv2 'S' not supported
00308           {
00309             sptr = va_arg(ap, char *);
00310             slen = strlen(sptr);
00311             break;
00312           }
00313           // 'n' not supported
00314         case '%':
00315           {
00316             // FIX: What is supposed to be done about field widths?
00317             oss << '%';
00318             continue;
00319           }
00320 #if 0
00321           /* Floating point formats not supported */
00322         case 'e':                       /* double */
00323         case 'E':                       /* double */
00324         case 'f':                       /* double */
00325         case 'F':                       /* double */
00326         case 'g':                       /* double */
00327         case 'G':                       /* double */
00328         case 'a':                       /* double */
00329         case 'A':                       /* double */
00330           {
00331             break;
00332           }
00333 #endif
00334         }
00335 
00336         if (bufp != buf)
00337           slen = bufp - buf;
00338 
00339         // FIX: I am not sure if the sign should be considered part of
00340         // the field for field width purposes. I think that it is, but
00341         // this complicates the padding algorithm in the right-adjust
00342         // case:
00343         //
00344         //     Sign in field     Sign not in field
00345         //     xxxxxxxxxx         xxxxxxxxxxxx
00346         //     -        3        -           3
00347         //     -000000003        -000000000003
00348         //
00349         // My resolution here is to reduce the field width by one if a
00350         // sign is required, and then treat the sign as appearing
00351         // outside the field.
00352 
00353         if (needSign && width)
00354           width--;
00355 
00356         if (needSign) {
00357           oss << (isNegative ? '-' : posSign);
00358         }
00359 
00360         // If we are right adjusting, insert needed pad characters: */
00361         // FIX: Doesn't zero padding *require* left adjust logic?
00362         if (ladjust == false) {
00363           while (slen < width) {
00364             oss << padc;
00365             slen++;
00366           }
00367         }
00368 
00369         if (bufp != buf) {
00370           do {
00371             --bufp;
00372             oss << *bufp;
00373           } while (bufp != buf);
00374         }
00375         if (sptr)
00376           while(*sptr)
00377             oss << *sptr++;
00378 
00379         /* If we are left adjusting, pad things out to end of field */
00380         if (ladjust) {
00381           while (slen < width) {
00382             oss << padc;
00383             slen++;
00384           }
00385         }
00386       }
00387     }
00388 
00389     return oss.str();
00390   }
00391 
00392   std::string format(const char *fmt, ...)
00393   {
00394     va_list ap;
00395 
00396     va_start(ap, fmt);
00397 
00398     return vformat(fmt, ap);
00399 
00400     /* va_end(ap); */
00401   }
00402 
00403 } /* namespace sherpa */

Generated on Sat Feb 4 23:59:28 2012 for BitC Compiler by  doxygen 1.4.7