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
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
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;
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
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
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
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
00378 char *prefix = 0;
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
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:
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:
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 }