EVE 1.0
strformat.c
1 #include <strformat.h>
2 
3 #define HAVE_DOUBLE
4 
5 #define HAVE_LONGLONG
6 #ifndef LARGEST_SIGNED
7 #ifdef HAVE_LONGLONG
8 #define LARGEST_SIGNED long long int
9 #else
10 #define LARGEST_UNSIGNED long int
11 #endif
12 #endif
13 
14 #ifndef LARGEST_UNSIGNED
15 #ifdef HAVE_LONGLONG
16 #define LARGEST_UNSIGNED unsigned long long int
17 #else
18 #define LARGEST_UNSIGNED unsigned long int
19 #endif
20 #endif
21 
22 #ifndef POINTER_INT
23 #define POINTER_INT unsigned long
24 #endif
25 
26 typedef unsigned int FormatFlags;
27 
28 #define MAKE_MASK(shift,size) (((1 << size) - 1) << (shift))
29 
30 #define JUSTIFY_SHIFT 0
31 #define JUSTIFY_SIZE 1
32 #define JUSTIFY_RIGHT 0x0000
33 #define JUSTIFY_LEFT 0x0001
34 #define JUSTIFY_MASK MAKE_MASK(JUSTIFY_SHIFT,JUSTIFY_SIZE)
35 
36 
37 /* How a positive number is prefixed */
38 #define POSITIVE_SHIFT (JUSTIFY_SHIFT + JUSTIFY_SIZE)
39 #define POSITIVE_NONE (0x0000 << POSITIVE_SHIFT)
40 #define POSITIVE_SPACE (0x0001 << POSITIVE_SHIFT)
41 #define POSITIVE_PLUS (0x0003 << POSITIVE_SHIFT)
42 #define POSITIVE_MASK MAKE_MASK(POSITIVE_SHIFT, POSITIVE_SIZE)
43 
44 #define POSITIVE_SIZE 2
45 
46 #define ALTERNATE_FORM_SHIFT (POSITIVE_SHIFT + POSITIVE_SIZE)
47 #define ALTERNATE_FORM_SIZE 1
48 #define ALTERNATE_FORM (0x0001 << ALTERNATE_FORM_SHIFT)
49 
50 
51 #define PAD_SHIFT (ALTERNATE_FORM_SHIFT + ALTERNATE_FORM_SIZE)
52 #define PAD_SIZE 1
53 #define PAD_SPACE (0x0000 << PAD_SHIFT)
54 #define PAD_ZERO (0x0001 << PAD_SHIFT)
55 
56 #define SIZE_SHIFT (PAD_SHIFT + PAD_SIZE)
57 #define SIZE_SIZE 3
58 #define SIZE_CHAR (0x0001 << SIZE_SHIFT)
59 #define SIZE_SHORT (0x0002 << SIZE_SHIFT)
60 #define SIZE_INT (0x0000 << SIZE_SHIFT)
61 #define SIZE_LONG (0x0003 << SIZE_SHIFT)
62 #define SIZE_LONGLONG (0x0004 << SIZE_SHIFT)
63 #define SIZE_MASK MAKE_MASK(SIZE_SHIFT,SIZE_SIZE)
64 
65 #define CONV_SHIFT (SIZE_SHIFT + SIZE_SIZE)
66 #define CONV_SIZE 3
67 #define CONV_INTEGER (0x0001 << CONV_SHIFT)
68 #define CONV_FLOAT (0x0002 << CONV_SHIFT)
69 #define CONV_POINTER (0x0003 << CONV_SHIFT)
70 #define CONV_STRING (0x0004 << CONV_SHIFT)
71 #define CONV_CHAR (0x0005 << CONV_SHIFT)
72 #define CONV_PERCENT (0x0006 << CONV_SHIFT)
73 #define CONV_WRITTEN (0x0007 << CONV_SHIFT)
74 #define CONV_MASK MAKE_MASK(CONV_SHIFT, CONV_SIZE)
75 
76 #define RADIX_SHIFT (CONV_SHIFT + CONV_SIZE)
77 #define RADIX_SIZE 2
78 #define RADIX_DECIMAL (0x0001 << RADIX_SHIFT)
79 #define RADIX_OCTAL (0x0002 << RADIX_SHIFT)
80 #define RADIX_HEX (0x0003 << RADIX_SHIFT)
81 #define RADIX_MASK MAKE_MASK(RADIX_SHIFT,RADIX_SIZE)
82 
83 #define SIGNED_SHIFT (RADIX_SHIFT + RADIX_SIZE)
84 #define SIGNED_SIZE 1
85 #define SIGNED_NO (0x0000 << SIGNED_SHIFT)
86 #define SIGNED_YES (0x0001 << SIGNED_SHIFT)
87 #define SIGNED_MASK MAKE_MASK(SIGNED_SHIFT,SIGNED_SIZE)
88 
89 #define CAPS_SHIFT (SIGNED_SHIFT + SIGNED_SIZE)
90 #define CAPS_SIZE 1
91 #define CAPS_NO (0x0000 << CAPS_SHIFT)
92 #define CAPS_YES (0x0001 << CAPS_SHIFT)
93 #define CAPS_MASK MAKE_MASK(CAPS_SHIFT,CAPS_SIZE)
94 
95 #define FLOAT_SHIFT (CAPS_SHIFT + CAPS_SIZE)
96 #define FLOAT_SIZE 2
97 #define FLOAT_NORMAL (0x0000 << FLOAT_SHIFT)
98 #define FLOAT_EXPONENT (0x0001 << FLOAT_SHIFT)
99 #define FLOAT_DEPENDANT (0x0002 << FLOAT_SHIFT)
100 #define FLOAT_HEX (0x0003 << FLOAT_SHIFT)
101 #define FLOAT_MASK MAKE_MASK(FLOAT_SHIFT, FLOAT_SIZE)
102 
103 static FormatFlags
104 parse_flags(const char **posp)
105 {
106  FormatFlags flags = 0;
107  const char *pos = *posp;
108  while (1) {
109  switch(*pos) {
110  case '-':
111  flags |= JUSTIFY_LEFT;
112  break;
113  case '+':
114  flags |= POSITIVE_PLUS;
115  break;
116  case ' ':
117  flags |= POSITIVE_SPACE;
118  break;
119  case '#':
120  flags |= ALTERNATE_FORM;
121  break;
122  case '0':
123  flags |= PAD_ZERO;
124  break;
125  default:
126  *posp = pos;
127  return flags;
128  }
129  pos++;
130  }
131 #ifdef ECLIPSE_STUB_CODE_ANALYSE
132  assert(0);
133  return 0;
134 #endif /* ECLIPSE_STUB_CODE_ANALYSE */
135 }
136 
137 static unsigned int
138 parse_uint(const char **posp)
139 {
140  unsigned v = 0;
141  const char *pos = *posp;
142  char ch;
143  while((ch = *pos) >= '0' && ch <= '9') {
144  v = v * 10 + (ch - '0');
145  pos++;
146  }
147  *posp = pos;
148  return v;
149 }
150 
151 #define MAXCHARS_HEX ((sizeof(LARGEST_UNSIGNED) * 8) / 4 )
152 
153 /* Largest number of characters needed for converting an unsigned integer.
154  */
155 #define MAXCHARS ((sizeof(LARGEST_UNSIGNED) * 8 + 2) / 3 )
156 
157 static unsigned int
158 output_uint_decimal(char **posp, LARGEST_UNSIGNED v)
159 {
160  unsigned int len;
161  char *pos = *posp;
162  while (v > 0) {
163  *--pos = (v % 10) + '0';
164  v /= 10;
165  }
166  len = *posp - pos;
167  *posp = pos;
168  return len;
169 }
170 
171 static unsigned int
172 output_uint_hex(char **posp, LARGEST_UNSIGNED v, unsigned int flags)
173 {
174  unsigned int len;
175  const char *hex = (flags & CAPS_YES) ?"0123456789ABCDEF":"0123456789abcdef";
176  char *pos = *posp;
177  while (v > 0) {
178  *--pos = hex[(v % 16)];
179  v /= 16;
180  }
181  len = *posp - pos;
182  *posp = pos;
183  return len;
184 }
185 
186 static unsigned int
187 output_uint_octal(char **posp, LARGEST_UNSIGNED v)
188 {
189  unsigned int len;
190  char *pos = *posp;
191  while (v > 0) {
192  *--pos = (v % 8) + '0';
193  v /= 8;
194  }
195  len = *posp - pos;
196  *posp = pos;
197  return len;
198 }
199 
200 static StrFormatResult
201 fill_space(const StrFormatContext *ctxt, unsigned int len)
202 {
203  StrFormatResult res;
204  static const char buffer[16] = " ";
205  while(len > 16) {
206  res = ctxt->write_str(ctxt->user_data, buffer, 16);
207  if (res != STRFORMAT_OK) return res;
208  len -= 16;
209  }
210  if (len == 0) return STRFORMAT_OK;
211  return ctxt->write_str(ctxt->user_data, buffer, len);
212 }
213 
214 static StrFormatResult
215 fill_zero(const StrFormatContext *ctxt, unsigned int len)
216 {
217  StrFormatResult res;
218  static const char buffer[16] = "0000000000000000";
219  while(len > 16) {
220  res = ctxt->write_str(ctxt->user_data, buffer, 16);
221  if (res != STRFORMAT_OK) return res;
222  len -= 16;
223  }
224  if (len == 0) return STRFORMAT_OK;
225  return ctxt->write_str(ctxt->user_data, buffer, len);
226 }
227 
228 #define CHECKCB(res) {if ((res) != STRFORMAT_OK) {va_end(ap); return -1;}}
229 
230 int
231 format_str(const StrFormatContext *ctxt, const char *format, ...)
232 {
233  int ret;
234  va_list ap;
235  va_start(ap, format);
236  ret = format_str_v(ctxt, format, ap);
237  va_end(ap);
238  return ret;
239 }
240 
241 int
242 format_str_v(const StrFormatContext *ctxt, const char *format, va_list ap)
243 {
244  unsigned int written = 0;
245  const char *pos = format;
246  while(*pos != '\0') {
247  FormatFlags flags;
248  unsigned int minwidth = 0;
249  int precision = -1; /* Negative means no precision */
250  char ch;
251  const char *start = pos;
252  while( (ch = *pos) != '\0' && ch != '%') pos++;
253  if (pos != start) {
254  CHECKCB(ctxt->write_str(ctxt->user_data, start, pos - start));
255  written += pos - start;
256  }
257  if (*pos == '\0') {
258  va_end(ap);
259  return written;
260  }
261  pos++;
262  if (*pos == '\0') {
263  va_end(ap);
264  return written;
265  }
266  flags = parse_flags(&pos);
267 
268  /* parse width */
269  if (*pos >= '1' && *pos <= '9') {
270  minwidth = parse_uint(&pos);
271  } else if (*pos == '*') {
272  int w = va_arg(ap,int);
273  if (w < 0) {
274  flags |= JUSTIFY_LEFT;
275  minwidth = w;
276  } else {
277  minwidth = w;
278  }
279  pos ++;
280  }
281 
282  /* parse precision */
283  if (*pos == '.') {
284  pos++;
285  if (*pos >= '0' && *pos <= '9') {
286  precision = parse_uint(&pos);
287  } else if (*pos == '*') {
288  pos++;
289  precision = va_arg(ap,int);
290  }
291  }
292  if (*pos == 'l') {
293  pos++;
294  if (*pos == 'l') {
295  flags |= SIZE_LONGLONG;
296  pos++;
297  } else {
298  flags |= SIZE_LONG;
299  }
300  } else if (*pos == 'h') {
301  pos++;
302  if (*pos == 'h') {
303  flags |= SIZE_CHAR;
304  pos++;
305  } else {
306  flags |= SIZE_SHORT;
307  }
308  }
309 
310  /* parse conversion specifier */
311  switch(*pos) {
312  case 'd':
313  case 'i':
314  flags |= CONV_INTEGER | RADIX_DECIMAL | SIGNED_YES;
315  break;
316  case 'u':
317  flags |= CONV_INTEGER | RADIX_DECIMAL | SIGNED_NO;
318  break;
319  case 'o':
320  flags |= CONV_INTEGER | RADIX_OCTAL | SIGNED_NO;
321  break;
322  case 'x':
323  flags |= CONV_INTEGER | RADIX_HEX | SIGNED_NO;
324  break;
325  case 'X':
326  flags |= CONV_INTEGER | RADIX_HEX | SIGNED_NO | CAPS_YES;
327  break;
328 #ifdef HAVE_DOUBLE
329  case 'f':
330  flags |= CONV_FLOAT | FLOAT_NORMAL;
331  break;
332  case 'F':
333  flags |= CONV_FLOAT | FLOAT_NORMAL | CAPS_YES;
334  break;
335  case 'e':
336  flags |= CONV_FLOAT | FLOAT_EXPONENT;
337  break;
338  case 'E':
339  flags |= CONV_FLOAT | FLOAT_EXPONENT | CAPS_YES;
340  break;
341  case 'g':
342  flags |= CONV_FLOAT | FLOAT_DEPENDANT;
343  break;
344  case 'G':
345  flags |= CONV_FLOAT | FLOAT_DEPENDANT | CAPS_YES;
346  break;
347  case 'a':
348  flags |= CONV_FLOAT | FLOAT_HEX;
349  break;
350  case 'A':
351  flags |= CONV_FLOAT | FLOAT_HEX | CAPS_YES;
352  break;
353 #endif
354  case 'c':
355  flags |= CONV_CHAR;
356  break;
357  case 's':
358  flags |= CONV_STRING;
359  break;
360  case 'p':
361  flags |= CONV_POINTER;
362  break;
363  case 'n':
364  flags |= CONV_WRITTEN;
365  break;
366  case '%':
367  flags |= CONV_PERCENT;
368  break;
369  case '\0':
370  va_end(ap);
371  return written;
372  }
373  pos++;
374  switch(flags & CONV_MASK) {
375  case CONV_PERCENT:
376  CHECKCB(ctxt->write_str(ctxt->user_data, "%", 1));
377  written++;
378  break;
379  case CONV_INTEGER:
380  {
381  /* unsigned integers */
382  char *prefix = 0; /* sign, "0x" or "0X" */
383  unsigned int prefix_len = 0;
384  char buffer[MAXCHARS];
385  char *conv_pos = buffer + MAXCHARS;
386  unsigned int conv_len = 0;
387  unsigned int width = 0;
388  unsigned int precision_fill;
389  unsigned int field_fill;
390  LARGEST_UNSIGNED uvalue = 0;
391  int negative = 0;
392 
393  if (precision < 0) precision = 1;
394  else flags &= ~PAD_ZERO;
395 
396  if (flags & SIGNED_YES) {
397  /* signed integers */
398  LARGEST_SIGNED value = 0;
399  switch(flags & SIZE_MASK) {
400  case SIZE_CHAR:
401  value = (signed char)va_arg(ap, int);
402  break;
403  case SIZE_SHORT:
404  value = (short)va_arg(ap, int);
405  break;
406  case SIZE_INT:
407  value = va_arg(ap, int);
408  break;
409 #ifndef HAVE_LONGLONG
410  case SIZE_LONGLONG: /* Treat long long the same as long */
411 #endif
412  case SIZE_LONG:
413  value = va_arg(ap, long);
414  break;
415 #ifdef HAVE_LONGLONG
416  case SIZE_LONGLONG:
417  value = va_arg(ap, long long);
418  break;
419 #endif
420  }
421  if (value < 0) {
422  uvalue = -value;
423  negative = 1;
424  } else {
425  uvalue = value;
426  }
427  } else {
428 
429  switch(flags & SIZE_MASK) {
430  case SIZE_CHAR:
431  uvalue = (unsigned char)va_arg(ap,unsigned int);
432  break;
433  case SIZE_SHORT:
434  uvalue = (unsigned short)va_arg(ap,unsigned int);
435  break;
436  case SIZE_INT:
437  uvalue = va_arg(ap,unsigned int);
438  break;
439 #ifndef HAVE_LONGLONG
440  case SIZE_LONGLONG: /* Treat long long the same as long */
441 #endif
442  case SIZE_LONG:
443  uvalue = va_arg(ap,unsigned long);
444  break;
445 #ifdef HAVE_LONGLONG
446  case SIZE_LONGLONG:
447  uvalue = va_arg(ap,unsigned long long);
448  break;
449 #endif
450  }
451  }
452 
453  switch(flags & (RADIX_MASK)) {
454  case RADIX_DECIMAL:
455  conv_len = output_uint_decimal(&conv_pos,uvalue);
456  break;
457  case RADIX_OCTAL:
458  conv_len = output_uint_octal(&conv_pos,uvalue);
459  break;
460  case RADIX_HEX:
461  conv_len = output_uint_hex(&conv_pos,uvalue, flags);
462  break;
463  }
464 
465  width += conv_len;
466  precision_fill = (precision > conv_len) ? precision - conv_len : 0;
467  if ((flags & (RADIX_MASK | ALTERNATE_FORM))
468  == (RADIX_OCTAL | ALTERNATE_FORM)) {
469  if (precision_fill < 1) precision_fill = 1;
470  }
471 
472  width += precision_fill;
473 
474  if ((flags & (RADIX_MASK | ALTERNATE_FORM))
475  == (RADIX_HEX | ALTERNATE_FORM) && uvalue != 0) {
476  prefix_len = 2;
477  if (flags & CAPS_YES) {
478  prefix = "0X";
479  } else {
480  prefix = "0x";
481  }
482  }
483 
484  if (flags & SIGNED_YES) {
485  if (negative) {
486  prefix = "-";
487  prefix_len = 1;
488  } else {
489  switch(flags & POSITIVE_MASK) {
490  case POSITIVE_SPACE:
491  prefix = " ";
492  prefix_len = 1;
493  break;
494  case POSITIVE_PLUS:
495  prefix = "+";
496  prefix_len = 1;
497  break;
498  }
499  }
500  }
501 
502  width += prefix_len;
503 
504  field_fill = (minwidth > width) ? minwidth - width : 0;
505 
506  if ((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
507  if (flags & PAD_ZERO) {
508  precision_fill += field_fill;
509  field_fill = 0; /* Do not double count padding */
510  } else {
511  CHECKCB(fill_space(ctxt,field_fill));
512  }
513  }
514 
515  if (prefix_len > 0)
516  CHECKCB(ctxt->write_str(ctxt->user_data, prefix, prefix_len));
517  written += prefix_len;
518 
519  CHECKCB(fill_zero(ctxt,precision_fill));
520  written += precision_fill;
521 
522  CHECKCB(ctxt->write_str(ctxt->user_data, conv_pos,conv_len));
523  written += conv_len;
524 
525  if ((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
526  CHECKCB(fill_space(ctxt,field_fill));
527  }
528  written += field_fill;
529  }
530  break;
531  case CONV_STRING:
532  {
533  unsigned int field_fill;
534  unsigned int len;
535  char *str = va_arg(ap,char *);
536  if (str) {
537  char *pos = str;
538  while(*pos != '\0') pos++;
539  len = pos - str;
540  } else {
541  str = "(null)";
542  len = 6;
543  }
544  if (precision >= 0 && precision < len) len = precision;
545  field_fill = (minwidth > len) ? minwidth - len : 0;
546  if ((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
547  CHECKCB(fill_space(ctxt,field_fill));
548  }
549  CHECKCB(ctxt->write_str(ctxt->user_data, str,len));
550  written += len;
551  if ((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
552  CHECKCB(fill_space(ctxt,field_fill));
553  }
554  written += field_fill;
555  }
556  break;
557  case CONV_POINTER:
558  {
559  LARGEST_UNSIGNED uvalue =
560  (LARGEST_UNSIGNED)(POINTER_INT)va_arg(ap,void *);
561  char buffer[MAXCHARS_HEX + 3];
562  char *conv_pos = buffer + MAXCHARS_HEX+3;
563  unsigned int conv_len;
564  unsigned int field_fill;
565 
566  conv_len = output_uint_hex(&conv_pos,uvalue,flags);
567  if (conv_len == 0) {
568  *--conv_pos = '0';
569  conv_len++;
570  }
571  *--conv_pos = 'x';
572  *--conv_pos = '0';
573  *--conv_pos = '#';
574  conv_len += 3;
575 
576  field_fill = (minwidth > conv_len) ? minwidth - conv_len : 0;
577 
578  if ((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
579  CHECKCB(fill_space(ctxt,field_fill));
580  }
581 
582  CHECKCB(ctxt->write_str(ctxt->user_data, conv_pos,conv_len));
583  written += conv_len;
584 
585  if ((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
586  CHECKCB(fill_space(ctxt,field_fill));
587  }
588  written += field_fill;
589  }
590  break;
591  case CONV_CHAR:
592  {
593  char ch = va_arg(ap,int);
594  unsigned int field_fill = (minwidth > 1) ? minwidth - 1 : 0;
595  if ((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
596  CHECKCB(fill_space(ctxt,field_fill));
597  written += field_fill;
598  }
599 
600  CHECKCB(ctxt->write_str(ctxt->user_data, &ch, 1));
601  written++;
602 
603  if ((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
604  CHECKCB(fill_space(ctxt,field_fill));
605  }
606  written+= field_fill;
607  }
608  break;
609  case CONV_WRITTEN:
610  {
611  int *p = va_arg(ap,int*);
612  *p = written;
613  }
614  break;
615 
616  }
617  }
618 
619  return written;
620 }