strformat.c

00001 #include <strformat.h>
00002 
00003 #define HAVE_DOUBLE
00004 
00005 #define HAVE_LONGLONG
00006 #ifndef LARGEST_SIGNED
00007 #ifdef HAVE_LONGLONG
00008 #define LARGEST_SIGNED long long int
00009 #else
00010 #define LARGEST_UNSIGNED long int
00011 #endif
00012 #endif
00013 
00014 #ifndef LARGEST_UNSIGNED
00015 #ifdef HAVE_LONGLONG
00016 #define LARGEST_UNSIGNED unsigned long long int
00017 #else
00018 #define LARGEST_UNSIGNED unsigned long int
00019 #endif
00020 #endif
00021 
00022 #ifndef POINTER_INT
00023 #define POINTER_INT unsigned long
00024 #endif
00025 
00026 typedef unsigned int FormatFlags;
00027 
00028 #define MAKE_MASK(shift,size) (((1 << size) - 1) << (shift))
00029 
00030 #define JUSTIFY_SHIFT   0
00031 #define JUSTIFY_SIZE    1
00032 #define JUSTIFY_RIGHT   0x0000
00033 #define JUSTIFY_LEFT    0x0001
00034 #define JUSTIFY_MASK    MAKE_MASK(JUSTIFY_SHIFT,JUSTIFY_SIZE)
00035 
00036 
00037 /* How a positive number is prefixed */
00038 #define POSITIVE_SHIFT  (JUSTIFY_SHIFT + JUSTIFY_SIZE)
00039 #define POSITIVE_NONE   (0x0000 << POSITIVE_SHIFT)
00040 #define POSITIVE_SPACE  (0x0001 << POSITIVE_SHIFT)
00041 #define POSITIVE_PLUS   (0x0003 << POSITIVE_SHIFT)
00042 #define POSITIVE_MASK   MAKE_MASK(POSITIVE_SHIFT, POSITIVE_SIZE)
00043 
00044 #define POSITIVE_SIZE   2
00045 
00046 #define ALTERNATE_FORM_SHIFT (POSITIVE_SHIFT + POSITIVE_SIZE)
00047 #define ALTERNATE_FORM_SIZE 1
00048 #define ALTERNATE_FORM  (0x0001 << ALTERNATE_FORM_SHIFT)
00049 
00050 
00051 #define PAD_SHIFT       (ALTERNATE_FORM_SHIFT + ALTERNATE_FORM_SIZE)
00052 #define PAD_SIZE        1
00053 #define PAD_SPACE       (0x0000 << PAD_SHIFT)
00054 #define PAD_ZERO        (0x0001 << PAD_SHIFT)
00055 
00056 #define SIZE_SHIFT      (PAD_SHIFT + PAD_SIZE)
00057 #define SIZE_SIZE       3
00058 #define SIZE_CHAR       (0x0001 << SIZE_SHIFT)
00059 #define SIZE_SHORT      (0x0002 << SIZE_SHIFT)
00060 #define SIZE_INT        (0x0000 << SIZE_SHIFT)
00061 #define SIZE_LONG       (0x0003 << SIZE_SHIFT)
00062 #define SIZE_LONGLONG   (0x0004 << SIZE_SHIFT)
00063 #define SIZE_MASK       MAKE_MASK(SIZE_SHIFT,SIZE_SIZE)
00064 
00065 #define CONV_SHIFT      (SIZE_SHIFT + SIZE_SIZE)
00066 #define CONV_SIZE       3
00067 #define CONV_INTEGER    (0x0001 << CONV_SHIFT)
00068 #define CONV_FLOAT      (0x0002 << CONV_SHIFT)
00069 #define CONV_POINTER    (0x0003 << CONV_SHIFT)
00070 #define CONV_STRING     (0x0004 << CONV_SHIFT)
00071 #define CONV_CHAR       (0x0005 << CONV_SHIFT)
00072 #define CONV_PERCENT    (0x0006 << CONV_SHIFT)
00073 #define CONV_WRITTEN    (0x0007 << CONV_SHIFT)
00074 #define CONV_MASK       MAKE_MASK(CONV_SHIFT, CONV_SIZE)
00075 
00076 #define RADIX_SHIFT     (CONV_SHIFT + CONV_SIZE)
00077 #define RADIX_SIZE      2
00078 #define RADIX_DECIMAL   (0x0001 << RADIX_SHIFT)
00079 #define RADIX_OCTAL     (0x0002 << RADIX_SHIFT)
00080 #define RADIX_HEX       (0x0003 << RADIX_SHIFT)
00081 #define RADIX_MASK      MAKE_MASK(RADIX_SHIFT,RADIX_SIZE)
00082 
00083 #define SIGNED_SHIFT    (RADIX_SHIFT + RADIX_SIZE)
00084 #define SIGNED_SIZE     1
00085 #define SIGNED_NO       (0x0000 << SIGNED_SHIFT)
00086 #define SIGNED_YES      (0x0001 << SIGNED_SHIFT)
00087 #define SIGNED_MASK     MAKE_MASK(SIGNED_SHIFT,SIGNED_SIZE)
00088 
00089 #define CAPS_SHIFT      (SIGNED_SHIFT + SIGNED_SIZE)
00090 #define CAPS_SIZE       1
00091 #define CAPS_NO         (0x0000 << CAPS_SHIFT)
00092 #define CAPS_YES        (0x0001 << CAPS_SHIFT)
00093 #define CAPS_MASK       MAKE_MASK(CAPS_SHIFT,CAPS_SIZE)
00094 
00095 #define FLOAT_SHIFT     (CAPS_SHIFT + CAPS_SIZE)
00096 #define FLOAT_SIZE      2
00097 #define FLOAT_NORMAL    (0x0000 << FLOAT_SHIFT)
00098 #define FLOAT_EXPONENT  (0x0001 << FLOAT_SHIFT)
00099 #define FLOAT_DEPENDANT (0x0002 << FLOAT_SHIFT)
00100 #define FLOAT_HEX       (0x0003 << FLOAT_SHIFT)
00101 #define FLOAT_MASK      MAKE_MASK(FLOAT_SHIFT, FLOAT_SIZE)
00102 
00103 static FormatFlags
00104 parse_flags(const char **posp)
00105 {
00106   FormatFlags flags = 0;
00107   const char *pos = *posp;
00108   while (1) {
00109     switch(*pos) {
00110     case '-':
00111       flags |= JUSTIFY_LEFT;
00112       break;
00113     case '+':
00114       flags |= POSITIVE_PLUS;
00115       break;
00116     case ' ':
00117       flags |= POSITIVE_SPACE;
00118       break;
00119     case '#':
00120       flags |= ALTERNATE_FORM;
00121       break;
00122     case '0':
00123       flags |= PAD_ZERO;
00124       break;
00125     default:
00126       *posp = pos;
00127       return flags;
00128     }
00129     pos++;
00130   }
00131       
00132 }
00133 
00134 static unsigned int
00135 parse_uint(const char **posp)
00136 {
00137   unsigned v = 0;
00138   const char *pos = *posp;
00139   char ch;
00140   while((ch = *pos) >= '0' && ch <= '9') {
00141     v = v * 10 + (ch - '0');
00142     pos++;
00143   }
00144   *posp = pos;
00145   return v;
00146 }
00147 
00148 #define MAXCHARS_HEX ((sizeof(LARGEST_UNSIGNED) * 8) / 4 )
00149 
00150 /* Largest number of characters needed for converting an unsigned integer.
00151  */
00152 #define MAXCHARS ((sizeof(LARGEST_UNSIGNED) * 8  + 2) / 3 )
00153 
00154 static unsigned int
00155 output_uint_decimal(char **posp, LARGEST_UNSIGNED v)
00156 {
00157   unsigned int len;
00158   char *pos = *posp;
00159   while (v > 0) {
00160     *--pos = (v % 10) + '0';
00161     v /= 10;
00162   }
00163   len = *posp - pos;
00164   *posp = pos;
00165   return len;
00166 }
00167 
00168 static unsigned int
00169 output_uint_hex(char **posp, LARGEST_UNSIGNED v, unsigned int flags)
00170 {
00171   unsigned int len;
00172   const char *hex = (flags & CAPS_YES) ?"0123456789ABCDEF":"0123456789abcdef";
00173   char *pos = *posp;
00174   while (v > 0) {
00175     *--pos = hex[(v % 16)];
00176     v /= 16;
00177   }
00178   len = *posp - pos;
00179   *posp = pos;
00180   return len;
00181 }
00182 
00183 static unsigned int
00184 output_uint_octal(char **posp, LARGEST_UNSIGNED v)
00185 {
00186   unsigned int len;
00187   char *pos = *posp;
00188   while (v > 0) {
00189     *--pos = (v % 8) + '0';
00190     v /= 8;
00191   }
00192   len = *posp - pos;
00193   *posp = pos;
00194   return len;
00195 }
00196 
00197 static StrFormatResult
00198 fill_space(const StrFormatContext *ctxt, unsigned int len)
00199 {
00200   StrFormatResult res;
00201   static const char buffer[16] = "                ";
00202   while(len > 16) {
00203     res = ctxt->write_str(ctxt->user_data, buffer, 16);
00204     if (res != STRFORMAT_OK) return res;
00205     len -= 16;
00206   }
00207   if (len == 0) return STRFORMAT_OK;
00208   return ctxt->write_str(ctxt->user_data, buffer, len);
00209 }
00210 
00211 static StrFormatResult
00212 fill_zero(const StrFormatContext *ctxt, unsigned int len)
00213 {
00214   StrFormatResult res;
00215   static const char buffer[16] = "0000000000000000";
00216   while(len > 16) {
00217     res = ctxt->write_str(ctxt->user_data, buffer, 16);
00218     if (res != STRFORMAT_OK) return res;
00219     len -= 16;
00220   }
00221   if (len == 0) return STRFORMAT_OK;
00222   return ctxt->write_str(ctxt->user_data, buffer, len);
00223 }
00224 
00225 #define CHECKCB(res) {if ((res) != STRFORMAT_OK) {va_end(ap); return -1;}}
00226 
00227 int
00228 format_str(const StrFormatContext *ctxt, const char *format, ...)
00229 {
00230   int ret;
00231   va_list ap;
00232   va_start(ap, format);
00233   ret = format_str_v(ctxt, format, ap);
00234   va_end(ap);
00235   return ret;
00236 }
00237 
00238 int
00239 format_str_v(const StrFormatContext *ctxt, const char *format, va_list ap)
00240 {
00241   unsigned int written = 0;
00242   const char *pos = format;
00243   while(*pos != '\0') {
00244     FormatFlags flags;
00245     unsigned int minwidth = 0;
00246     int precision = -1; /* Negative means no precision */
00247     char ch;
00248     const char *start = pos;
00249     while( (ch = *pos) != '\0' && ch != '%') pos++;
00250     if (pos != start) {
00251       CHECKCB(ctxt->write_str(ctxt->user_data, start, pos - start));
00252       written += pos - start;
00253     }
00254     if (*pos == '\0') {
00255       va_end(ap);
00256       return written;
00257     }
00258     pos++;
00259     if (*pos == '\0') {
00260       va_end(ap);
00261       return written;
00262     }
00263     flags = parse_flags(&pos);
00264 
00265     /* parse width */
00266     if (*pos >= '1' && *pos <= '9') {
00267       minwidth = parse_uint(&pos);
00268     } else if (*pos == '*') {
00269       int w = va_arg(ap,int);
00270       if (w < 0) {
00271         flags |= JUSTIFY_LEFT;
00272         minwidth = w;
00273       } else {
00274         minwidth = w;
00275       }
00276       pos ++;
00277     }
00278 
00279     /* parse precision */
00280     if (*pos == '.') {
00281       pos++;
00282       if (*pos >= '0' && *pos <= '9') {
00283         precision = parse_uint(&pos);
00284       } else if (*pos == '*') {
00285         precision = va_arg(ap,int);
00286       }
00287     }
00288     if (*pos == 'l') {
00289       pos++;
00290       if (*pos == 'l') {
00291         flags |= SIZE_LONGLONG;
00292         pos++;
00293       } else {
00294         flags |= SIZE_LONG;
00295       }
00296     } else if (*pos == 'h') {
00297       pos++;
00298       if (*pos == 'h') {
00299         flags |= SIZE_CHAR;
00300         pos++;
00301       } else {
00302         flags |= SIZE_SHORT;
00303       }
00304     }
00305 
00306     /* parse conversion specifier */
00307     switch(*pos) {
00308     case 'd':
00309     case 'i':
00310       flags |= CONV_INTEGER | RADIX_DECIMAL | SIGNED_YES;
00311       break;
00312     case 'u':
00313       flags |= CONV_INTEGER | RADIX_DECIMAL | SIGNED_NO;
00314       break;
00315     case 'o':
00316       flags |= CONV_INTEGER | RADIX_OCTAL | SIGNED_NO;
00317       break;
00318     case 'x':
00319       flags |= CONV_INTEGER | RADIX_HEX | SIGNED_NO;
00320       break;
00321     case 'X':
00322       flags |= CONV_INTEGER | RADIX_HEX | SIGNED_NO | CAPS_YES;
00323       break;
00324 #ifdef HAVE_DOUBLE
00325     case 'f':
00326       flags |= CONV_FLOAT | FLOAT_NORMAL;
00327       break;
00328     case 'F':
00329       flags |= CONV_FLOAT | FLOAT_NORMAL | CAPS_YES;
00330       break;
00331     case 'e':
00332       flags |= CONV_FLOAT | FLOAT_EXPONENT;
00333       break;
00334     case 'E':
00335       flags |= CONV_FLOAT | FLOAT_EXPONENT | CAPS_YES;
00336       break;
00337     case 'g':
00338       flags |= CONV_FLOAT | FLOAT_DEPENDANT;
00339       break;
00340     case 'G':
00341       flags |= CONV_FLOAT | FLOAT_DEPENDANT | CAPS_YES;
00342       break;
00343     case 'a':
00344       flags |= CONV_FLOAT | FLOAT_HEX;
00345       break;
00346     case 'A':
00347       flags |= CONV_FLOAT | FLOAT_HEX | CAPS_YES;
00348       break;
00349 #endif
00350     case 'c':
00351       flags |= CONV_CHAR;
00352       break;
00353     case 's':
00354       flags |= CONV_STRING;
00355       break;
00356     case 'p':
00357       flags |= CONV_POINTER;
00358       break;
00359     case 'n':
00360       flags |= CONV_WRITTEN;
00361       break;
00362     case '%':
00363       flags |= CONV_PERCENT;
00364       break;
00365     case '\0':
00366       va_end(ap);
00367       return written;
00368     }
00369     pos++;
00370     switch(flags & CONV_MASK) {
00371     case CONV_PERCENT:
00372       CHECKCB(ctxt->write_str(ctxt->user_data, "%", 1));
00373       written++;
00374       break;
00375     case CONV_INTEGER:
00376       {
00377         /* unsigned integers */
00378         char *prefix = 0; /* sign, "0x" or "0X" */
00379         unsigned int prefix_len = 0;
00380         char buffer[MAXCHARS];
00381         char *conv_pos = buffer + MAXCHARS;
00382         unsigned int conv_len = 0;
00383         unsigned int width = 0;
00384         unsigned int precision_fill;
00385         unsigned int field_fill;
00386         LARGEST_UNSIGNED uvalue = 0;
00387         int negative = 0;
00388       
00389         if (precision < 0) precision = 1;
00390         else flags &= ~PAD_ZERO;
00391       
00392         if (flags & SIGNED_YES) {
00393           /* signed integers */
00394           LARGEST_SIGNED value = 0;
00395           switch(flags & SIZE_MASK) {
00396           case SIZE_CHAR:
00397             value = (signed char)va_arg(ap, int);
00398             break;
00399           case SIZE_SHORT:
00400             value = (short)va_arg(ap, int);
00401             break;
00402           case SIZE_INT:
00403             value = va_arg(ap, int);
00404             break;
00405 #ifndef HAVE_LONGLONG
00406           case SIZE_LONGLONG:   /* Treat long long the same as long */
00407 #endif
00408           case SIZE_LONG:
00409             value = va_arg(ap, long);
00410             break;
00411 #ifdef HAVE_LONGLONG
00412           case SIZE_LONGLONG:
00413             value = va_arg(ap, long long);
00414             break;
00415 #endif
00416           }
00417           if (value < 0) {
00418             uvalue = -value;
00419             negative = 1;
00420           } else {
00421             uvalue = value;
00422           }
00423         } else {
00424         
00425           switch(flags & SIZE_MASK) {
00426           case SIZE_CHAR:
00427             uvalue = (unsigned char)va_arg(ap,unsigned int);
00428             break;
00429           case SIZE_SHORT:
00430             uvalue = (unsigned short)va_arg(ap,unsigned int);
00431             break;
00432           case SIZE_INT:
00433             uvalue = va_arg(ap,unsigned int);
00434             break;
00435 #ifndef HAVE_LONGLONG
00436           case SIZE_LONGLONG:   /* Treat long long the same as long */
00437 #endif
00438           case SIZE_LONG:
00439             uvalue = va_arg(ap,unsigned long);
00440             break;
00441 #ifdef HAVE_LONGLONG
00442           case SIZE_LONGLONG:
00443             uvalue = va_arg(ap,unsigned long long);
00444             break;
00445 #endif
00446           }
00447         }
00448         
00449         switch(flags & (RADIX_MASK)) {
00450         case RADIX_DECIMAL:
00451           conv_len = output_uint_decimal(&conv_pos,uvalue);
00452           break;
00453         case RADIX_OCTAL:
00454           conv_len = output_uint_octal(&conv_pos,uvalue);
00455           break;
00456         case RADIX_HEX:
00457           conv_len = output_uint_hex(&conv_pos,uvalue, flags);
00458           break;
00459         }
00460 
00461         width += conv_len;
00462         precision_fill = (precision > conv_len) ? precision - conv_len : 0;
00463         if ((flags & (RADIX_MASK | ALTERNATE_FORM))
00464             == (RADIX_OCTAL | ALTERNATE_FORM)) {
00465           if (precision_fill < 1) precision_fill = 1;
00466         }
00467 
00468         width += precision_fill;
00469         
00470         if ((flags & (RADIX_MASK | ALTERNATE_FORM))
00471             == (RADIX_HEX | ALTERNATE_FORM) && uvalue != 0) {
00472           prefix_len = 2;
00473           if (flags & CAPS_YES) {
00474             prefix = "0X";
00475           } else {
00476             prefix = "0x";
00477           }
00478         }
00479 
00480         if (flags & SIGNED_YES) {
00481           if (negative) {
00482             prefix = "-";
00483             prefix_len = 1;
00484           } else {
00485             switch(flags & POSITIVE_MASK) {
00486             case POSITIVE_SPACE:
00487               prefix = " ";
00488               prefix_len = 1;
00489               break;
00490             case POSITIVE_PLUS:
00491               prefix = "+";
00492               prefix_len = 1;
00493               break;
00494             }
00495           }
00496         }
00497 
00498         width += prefix_len;
00499 
00500         field_fill = (minwidth > width) ? minwidth - width : 0;
00501 
00502         if ((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
00503           if (flags & PAD_ZERO) {
00504             precision_fill += field_fill;
00505           } else {
00506             CHECKCB(fill_space(ctxt,field_fill));
00507           }
00508         }
00509 
00510         if (prefix_len > 0)
00511           CHECKCB(ctxt->write_str(ctxt->user_data, prefix, prefix_len));
00512         written += prefix_len;
00513         
00514         CHECKCB(fill_zero(ctxt,precision_fill));
00515         written += precision_fill;
00516         
00517         CHECKCB(ctxt->write_str(ctxt->user_data, conv_pos,conv_len));
00518         written += conv_len;
00519         
00520         if ((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
00521           CHECKCB(fill_space(ctxt,field_fill));
00522         }
00523         written += field_fill;
00524       }
00525       break;
00526     case CONV_STRING:
00527       {
00528         unsigned int field_fill;
00529         unsigned int len;
00530         char *str = va_arg(ap,char *);
00531         if (str) {
00532           char *pos = str;
00533           while(*pos != '\0') pos++;
00534           len = pos - str;
00535         } else {
00536           str = "(null)";
00537           len = 6;
00538         }
00539         if (precision >= 0 && precision < len) len = precision;
00540         field_fill = (minwidth > len) ? minwidth - len : 0;
00541         if ((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
00542           CHECKCB(fill_space(ctxt,field_fill));
00543         }
00544         CHECKCB(ctxt->write_str(ctxt->user_data, str,len));
00545         written += len;
00546         if ((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
00547           CHECKCB(fill_space(ctxt,field_fill));
00548         }
00549         written += field_fill;
00550       }
00551       break;
00552     case CONV_POINTER:
00553       {
00554         LARGEST_UNSIGNED uvalue =
00555           (LARGEST_UNSIGNED)(POINTER_INT)va_arg(ap,void *);
00556         char buffer[MAXCHARS_HEX + 3];
00557         char *conv_pos = buffer + MAXCHARS_HEX+3;
00558         unsigned int conv_len;
00559         unsigned int field_fill;
00560         
00561         conv_len = output_uint_hex(&conv_pos,uvalue,flags);
00562         if (conv_len == 0) {
00563           *--conv_pos = '0';
00564           conv_len++;
00565         }
00566         *--conv_pos = 'x';
00567         *--conv_pos = '0';
00568         *--conv_pos = '#';
00569         conv_len += 3;
00570 
00571         field_fill = (minwidth > conv_len) ? minwidth - conv_len : 0;
00572         
00573         if ((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
00574           CHECKCB(fill_space(ctxt,field_fill));
00575         }
00576         
00577         CHECKCB(ctxt->write_str(ctxt->user_data, conv_pos,conv_len));
00578         written += conv_len;
00579         
00580         if ((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
00581           CHECKCB(fill_space(ctxt,field_fill));
00582         }
00583         written += field_fill;
00584       }
00585       break;
00586     case CONV_CHAR:
00587       {
00588         char ch = va_arg(ap,int);
00589         unsigned int field_fill = (minwidth > 1) ? minwidth - 1 : 0;
00590         if ((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
00591           CHECKCB(fill_space(ctxt,field_fill));
00592           written += field_fill;
00593         }
00594         
00595         CHECKCB(ctxt->write_str(ctxt->user_data, &ch, 1));
00596         written++;
00597         
00598         if ((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
00599           CHECKCB(fill_space(ctxt,field_fill));
00600         }
00601         written+= field_fill;
00602       }
00603       break;
00604     case CONV_WRITTEN:
00605       {
00606         int *p = va_arg(ap,int*);
00607         *p = written;
00608       }
00609       break;
00610 
00611     }
00612   }
00613   
00614   return written;
00615 }

Generated on Mon Apr 11 14:23:35 2011 for Contiki 2.5 by  doxygen 1.6.1