Contiki-NG
strformat.c
1 /*
2  * Copyright (c) 2009, Simon Berg
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the copyright holder nor the names of its
15  * contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*---------------------------------------------------------------------------*/
32 #include "contiki.h"
33 
34 #include <strformat.h>
35 /*---------------------------------------------------------------------------*/
36 #define HAVE_DOUBLE
37 #define HAVE_LONGLONG
38 
39 #ifndef LARGEST_SIGNED
40 #ifdef HAVE_LONGLONG
41 #define LARGEST_SIGNED long long int
42 #else
43 #define LARGEST_UNSIGNED long int
44 #endif /* HAVE_LONGLONG */
45 #endif /* LARGEST_SIGNED */
46 
47 #ifndef LARGEST_UNSIGNED
48 #ifdef HAVE_LONGLONG
49 #define LARGEST_UNSIGNED unsigned long long int
50 #else
51 #define LARGEST_UNSIGNED unsigned long int
52 #endif /* HAVE_LONGLONG */
53 #endif /* LARGEST_UNSIGNED */
54 
55 #ifndef POINTER_INT
56 #define POINTER_INT unsigned long
57 #endif
58 /*---------------------------------------------------------------------------*/
59 typedef unsigned int FormatFlags;
60 /*---------------------------------------------------------------------------*/
61 #define MAKE_MASK(shift, size) (((1 << size) - 1) << (shift))
62 /*---------------------------------------------------------------------------*/
63 #define JUSTIFY_SHIFT 0
64 #define JUSTIFY_SIZE 1
65 #define JUSTIFY_RIGHT 0x0000
66 #define JUSTIFY_LEFT 0x0001
67 #define JUSTIFY_MASK MAKE_MASK(JUSTIFY_SHIFT, JUSTIFY_SIZE)
68 /*---------------------------------------------------------------------------*/
69 /* How a positive number is prefixed */
70 #define POSITIVE_SHIFT (JUSTIFY_SHIFT + JUSTIFY_SIZE)
71 #define POSITIVE_NONE (0x0000 << POSITIVE_SHIFT)
72 #define POSITIVE_SPACE (0x0001 << POSITIVE_SHIFT)
73 #define POSITIVE_PLUS (0x0003 << POSITIVE_SHIFT)
74 #define POSITIVE_MASK MAKE_MASK(POSITIVE_SHIFT, POSITIVE_SIZE)
75 
76 #define POSITIVE_SIZE 2
77 /*---------------------------------------------------------------------------*/
78 #define ALTERNATE_FORM_SHIFT (POSITIVE_SHIFT + POSITIVE_SIZE)
79 #define ALTERNATE_FORM_SIZE 1
80 #define ALTERNATE_FORM (0x0001 << ALTERNATE_FORM_SHIFT)
81 /*---------------------------------------------------------------------------*/
82 #define PAD_SHIFT (ALTERNATE_FORM_SHIFT + ALTERNATE_FORM_SIZE)
83 #define PAD_SIZE 1
84 #define PAD_SPACE (0x0000 << PAD_SHIFT)
85 #define PAD_ZERO (0x0001 << PAD_SHIFT)
86 /*---------------------------------------------------------------------------*/
87 #define SIZE_SHIFT (PAD_SHIFT + PAD_SIZE)
88 #define SIZE_SIZE 3
89 #define SIZE_CHAR (0x0001 << SIZE_SHIFT)
90 #define SIZE_SHORT (0x0002 << SIZE_SHIFT)
91 #define SIZE_INT (0x0000 << SIZE_SHIFT)
92 #define SIZE_LONG (0x0003 << SIZE_SHIFT)
93 #define SIZE_LONGLONG (0x0004 << SIZE_SHIFT)
94 #define SIZE_MASK MAKE_MASK(SIZE_SHIFT, SIZE_SIZE)
95 /*---------------------------------------------------------------------------*/
96 #define CONV_SHIFT (SIZE_SHIFT + SIZE_SIZE)
97 #define CONV_SIZE 3
98 #define CONV_INTEGER (0x0001 << CONV_SHIFT)
99 #define CONV_FLOAT (0x0002 << CONV_SHIFT)
100 #define CONV_POINTER (0x0003 << CONV_SHIFT)
101 #define CONV_STRING (0x0004 << CONV_SHIFT)
102 #define CONV_CHAR (0x0005 << CONV_SHIFT)
103 #define CONV_PERCENT (0x0006 << CONV_SHIFT)
104 #define CONV_WRITTEN (0x0007 << CONV_SHIFT)
105 #define CONV_MASK MAKE_MASK(CONV_SHIFT, CONV_SIZE)
106 /*---------------------------------------------------------------------------*/
107 #define RADIX_SHIFT (CONV_SHIFT + CONV_SIZE)
108 #define RADIX_SIZE 2
109 #define RADIX_DECIMAL (0x0001 << RADIX_SHIFT)
110 #define RADIX_OCTAL (0x0002 << RADIX_SHIFT)
111 #define RADIX_HEX (0x0003 << RADIX_SHIFT)
112 #define RADIX_MASK MAKE_MASK(RADIX_SHIFT, RADIX_SIZE)
113 /*---------------------------------------------------------------------------*/
114 #define SIGNED_SHIFT (RADIX_SHIFT + RADIX_SIZE)
115 #define SIGNED_SIZE 1
116 #define SIGNED_NO (0x0000 << SIGNED_SHIFT)
117 #define SIGNED_YES (0x0001 << SIGNED_SHIFT)
118 #define SIGNED_MASK MAKE_MASK(SIGNED_SHIFT, SIGNED_SIZE)
119 /*---------------------------------------------------------------------------*/
120 #define CAPS_SHIFT (SIGNED_SHIFT + SIGNED_SIZE)
121 #define CAPS_SIZE 1
122 #define CAPS_NO (0x0000 << CAPS_SHIFT)
123 #define CAPS_YES (0x0001 << CAPS_SHIFT)
124 #define CAPS_MASK MAKE_MASK(CAPS_SHIFT, CAPS_SIZE)
125 /*---------------------------------------------------------------------------*/
126 #define FLOAT_SHIFT (CAPS_SHIFT + CAPS_SIZE)
127 #define FLOAT_SIZE 2
128 #define FLOAT_NORMAL (0x0000 << FLOAT_SHIFT)
129 #define FLOAT_EXPONENT (0x0001 << FLOAT_SHIFT)
130 #define FLOAT_DEPENDANT (0x0002 << FLOAT_SHIFT)
131 #define FLOAT_HEX (0x0003 << FLOAT_SHIFT)
132 #define FLOAT_MASK MAKE_MASK(FLOAT_SHIFT, FLOAT_SIZE)
133 /*---------------------------------------------------------------------------*/
134 #define CHECKCB(res) { if((res) != STRFORMAT_OK) { va_end(ap); return -1; } }
135 /*---------------------------------------------------------------------------*/
136 #define MAXCHARS_HEX ((sizeof(LARGEST_UNSIGNED) * 8) / 4)
137 
138 /* Largest number of characters needed for converting an unsigned integer. */
139 #define MAXCHARS ((sizeof(LARGEST_UNSIGNED) * 8 + 2) / 3)
140 /*---------------------------------------------------------------------------*/
141 static FormatFlags
142 parse_flags(const char **posp)
143 {
144  FormatFlags flags = 0;
145  const char *pos = *posp;
146 
147  while(1) {
148  switch(*pos) {
149  case '-':
150  flags |= JUSTIFY_LEFT;
151  break;
152  case '+':
153  flags |= POSITIVE_PLUS;
154  break;
155  case ' ':
156  flags |= POSITIVE_SPACE;
157  break;
158  case '#':
159  flags |= ALTERNATE_FORM;
160  break;
161  case '0':
162  flags |= PAD_ZERO;
163  break;
164  default:
165  *posp = pos;
166  return flags;
167  }
168 
169  pos++;
170  }
171 }
172 /*---------------------------------------------------------------------------*/
173 static unsigned int
174 parse_uint(const char **posp)
175 {
176  unsigned v = 0;
177  const char *pos = *posp;
178  char ch;
179 
180  while((ch = *pos) >= '0' && ch <= '9') {
181  v = v * 10 + (ch - '0');
182  pos++;
183  }
184 
185  *posp = pos;
186 
187  return v;
188 }
189 /*---------------------------------------------------------------------------*/
190 static unsigned int
191 output_uint_decimal(char **posp, LARGEST_UNSIGNED v)
192 {
193  unsigned int len;
194  char *pos = *posp;
195 
196  while(v > 0) {
197  *--pos = (v % 10) + '0';
198  v /= 10;
199  }
200 
201  len = *posp - pos;
202  *posp = pos;
203 
204  return len;
205 }
206 /*---------------------------------------------------------------------------*/
207 static unsigned int
208 output_uint_hex(char **posp, LARGEST_UNSIGNED v, unsigned int flags)
209 {
210  unsigned int len;
211  const char *hex = (flags & CAPS_YES) ? "0123456789ABCDEF" : "0123456789abcdef";
212  char *pos = *posp;
213 
214  while(v > 0) {
215  *--pos = hex[(v % 16)];
216  v /= 16;
217  }
218 
219  len = *posp - pos;
220  *posp = pos;
221 
222  return len;
223 }
224 /*---------------------------------------------------------------------------*/
225 static unsigned int
226 output_uint_octal(char **posp, LARGEST_UNSIGNED v)
227 {
228  unsigned int len;
229  char *pos = *posp;
230 
231  while(v > 0) {
232  *--pos = (v % 8) + '0';
233  v /= 8;
234  }
235 
236  len = *posp - pos;
237  *posp = pos;
238 
239  return len;
240 }
241 /*---------------------------------------------------------------------------*/
242 static strformat_result
243 fill_space(const strformat_context_t *ctxt, unsigned int len)
244 {
245  strformat_result res;
246  static const char buffer[16] = " ";
247 
248  while(len > 16) {
249  res = ctxt->write_str(ctxt->user_data, buffer, 16);
250  if(res != STRFORMAT_OK) {
251  return res;
252  }
253  len -= 16;
254  }
255 
256  if(len == 0) {
257  return STRFORMAT_OK;
258  }
259 
260  return ctxt->write_str(ctxt->user_data, buffer, len);
261 }
262 /*---------------------------------------------------------------------------*/
263 static strformat_result
264 fill_zero(const strformat_context_t *ctxt, unsigned int len)
265 {
266  strformat_result res;
267  static const char buffer[16] = "0000000000000000";
268 
269  while(len > 16) {
270  res = ctxt->write_str(ctxt->user_data, buffer, 16);
271  if(res != STRFORMAT_OK) {
272  return res;
273  }
274  len -= 16;
275  }
276 
277  if(len == 0) {
278  return STRFORMAT_OK;
279  }
280  return ctxt->write_str(ctxt->user_data, buffer, len);
281 }
282 /*---------------------------------------------------------------------------*/
283 int
284 format_str(const strformat_context_t *ctxt, const char *format, ...)
285 {
286  int ret;
287  va_list ap;
288  va_start(ap, format);
289  ret = format_str_v(ctxt, format, ap);
290  va_end(ap);
291  return ret;
292 }
293 /*---------------------------------------------------------------------------*/
294 int
295 format_str_v(const strformat_context_t *ctxt, const char *format, va_list ap)
296 {
297  unsigned int written = 0;
298  const char *pos = format;
299 
300  while(*pos != '\0') {
301  FormatFlags flags;
302  unsigned int minwidth = 0;
303  int precision = -1; /* Negative means no precision */
304  char ch;
305  const char *start = pos;
306 
307  while((ch = *pos) != '\0' && ch != '%') {
308  pos++;
309  }
310 
311  if(pos != start) {
312  CHECKCB(ctxt->write_str(ctxt->user_data, start, pos - start));
313  written += pos - start;
314  }
315 
316  if(*pos == '\0') {
317  va_end(ap);
318  return written;
319  }
320 
321  pos++;
322 
323  if(*pos == '\0') {
324  va_end(ap);
325  return written;
326  }
327 
328  flags = parse_flags(&pos);
329 
330  /* parse width */
331  if(*pos >= '1' && *pos <= '9') {
332  minwidth = parse_uint(&pos);
333  } else if(*pos == '*') {
334  int w = va_arg(ap, int);
335 
336  if(w < 0) {
337  flags |= JUSTIFY_LEFT;
338  minwidth = w;
339  } else {
340  minwidth = w;
341  }
342 
343  pos++;
344  }
345 
346  /* parse precision */
347  if(*pos == '.') {
348  pos++;
349 
350  if(*pos >= '0' && *pos <= '9') {
351  precision = parse_uint(&pos);
352  } else if(*pos == '*') {
353  pos++;
354  precision = va_arg(ap, int);
355  }
356  }
357 
358  if(*pos == 'l') {
359  pos++;
360 
361  if(*pos == 'l') {
362  flags |= SIZE_LONGLONG;
363  pos++;
364  } else {
365  flags |= SIZE_LONG;
366  }
367  } else if(*pos == 'h') {
368  pos++;
369 
370  if(*pos == 'h') {
371  flags |= SIZE_CHAR;
372  pos++;
373  } else {
374  flags |= SIZE_SHORT;
375  }
376  } else if(*pos == 'z') {
377  if(sizeof(size_t) == sizeof(short)) {
378  flags |= SIZE_SHORT;
379  } else if(sizeof(size_t) == sizeof(long)) {
380  flags |= SIZE_LONG;
381 #ifdef HAVE_LONGLONG
382  } else if(sizeof(size_t) == sizeof(long long)) {
383  flags |= SIZE_LONGLONG;
384  }
385 #endif
386  pos++;
387  }
388 
389  /* parse conversion specifier */
390  switch(*pos) {
391  case 'd':
392  case 'i':
393  flags |= CONV_INTEGER | RADIX_DECIMAL | SIGNED_YES;
394  break;
395  case 'u':
396  flags |= CONV_INTEGER | RADIX_DECIMAL | SIGNED_NO;
397  break;
398  case 'o':
399  flags |= CONV_INTEGER | RADIX_OCTAL | SIGNED_NO;
400  break;
401  case 'x':
402  flags |= CONV_INTEGER | RADIX_HEX | SIGNED_NO;
403  break;
404  case 'X':
405  flags |= CONV_INTEGER | RADIX_HEX | SIGNED_NO | CAPS_YES;
406  break;
407 #ifdef HAVE_DOUBLE
408  case 'f':
409  flags |= CONV_FLOAT | FLOAT_NORMAL;
410  break;
411  case 'F':
412  flags |= CONV_FLOAT | FLOAT_NORMAL | CAPS_YES;
413  break;
414  case 'e':
415  flags |= CONV_FLOAT | FLOAT_EXPONENT;
416  break;
417  case 'E':
418  flags |= CONV_FLOAT | FLOAT_EXPONENT | CAPS_YES;
419  break;
420  case 'g':
421  flags |= CONV_FLOAT | FLOAT_DEPENDANT;
422  break;
423  case 'G':
424  flags |= CONV_FLOAT | FLOAT_DEPENDANT | CAPS_YES;
425  break;
426  case 'a':
427  flags |= CONV_FLOAT | FLOAT_HEX;
428  break;
429  case 'A':
430  flags |= CONV_FLOAT | FLOAT_HEX | CAPS_YES;
431  break;
432 #endif
433  case 'c':
434  flags |= CONV_CHAR;
435  break;
436  case 's':
437  flags |= CONV_STRING;
438  break;
439  case 'p':
440  flags |= CONV_POINTER;
441  break;
442  case 'n':
443  flags |= CONV_WRITTEN;
444  break;
445  case '%':
446  flags |= CONV_PERCENT;
447  break;
448  case '\0':
449  va_end(ap);
450  return written;
451  }
452  pos++;
453 
454  switch(flags & CONV_MASK) {
455  case CONV_PERCENT:
456  CHECKCB(ctxt->write_str(ctxt->user_data, "%", 1));
457  written++;
458  break;
459  case CONV_INTEGER:
460  {
461  /* unsigned integers */
462  char *prefix = 0; /* sign, "0x" or "0X" */
463  unsigned int prefix_len = 0;
464  char buffer[MAXCHARS];
465  char *conv_pos = buffer + MAXCHARS;
466  unsigned int conv_len = 0;
467  unsigned int width = 0;
468  unsigned int precision_fill;
469  unsigned int field_fill;
470  LARGEST_UNSIGNED uvalue = 0;
471  int negative = 0;
472 
473  if(precision < 0) {
474  precision = 1;
475  } else {
476  flags &= ~PAD_ZERO;
477  }
478 
479  if(flags & SIGNED_YES) {
480  /* signed integers */
481  LARGEST_SIGNED value = 0;
482  switch(flags & SIZE_MASK) {
483  case SIZE_CHAR:
484  value = (signed char)va_arg(ap, int);
485  break;
486  case SIZE_SHORT:
487  value = (short)va_arg(ap, int);
488  break;
489  case SIZE_INT:
490  value = va_arg(ap, int);
491  break;
492 #ifndef HAVE_LONGLONG
493  case SIZE_LONGLONG: /* Treat long long the same as long */
494 #endif
495  case SIZE_LONG:
496  value = va_arg(ap, long);
497  break;
498 #ifdef HAVE_LONGLONG
499  case SIZE_LONGLONG:
500  value = va_arg(ap, long long);
501  break;
502 #endif
503  }
504  if(value < 0) {
505  uvalue = -value;
506  negative = 1;
507  } else {
508  uvalue = value;
509  }
510  } else {
511 
512  switch(flags & SIZE_MASK) {
513  case SIZE_CHAR:
514  uvalue = (unsigned char)va_arg(ap, unsigned int);
515  break;
516  case SIZE_SHORT:
517  uvalue = (unsigned short)va_arg(ap, unsigned int);
518  break;
519  case SIZE_INT:
520  uvalue = va_arg(ap, unsigned int);
521  break;
522 #ifndef HAVE_LONGLONG
523  case SIZE_LONGLONG: /* Treat long long the same as long */
524 #endif
525  case SIZE_LONG:
526  uvalue = va_arg(ap, unsigned long);
527  break;
528 #ifdef HAVE_LONGLONG
529  case SIZE_LONGLONG:
530  uvalue = va_arg(ap, unsigned long long);
531  break;
532 #endif
533  }
534  }
535 
536  switch(flags & (RADIX_MASK)) {
537  case RADIX_DECIMAL:
538  conv_len = output_uint_decimal(&conv_pos, uvalue);
539  break;
540  case RADIX_OCTAL:
541  conv_len = output_uint_octal(&conv_pos, uvalue);
542  break;
543  case RADIX_HEX:
544  conv_len = output_uint_hex(&conv_pos, uvalue, flags);
545  break;
546  }
547 
548  width += conv_len;
549  precision_fill = (precision > conv_len) ? precision - conv_len : 0;
550  if((flags & (RADIX_MASK | ALTERNATE_FORM))
551  == (RADIX_OCTAL | ALTERNATE_FORM)) {
552  if(precision_fill < 1) {
553  precision_fill = 1;
554  }
555  }
556 
557  width += precision_fill;
558 
559  if((flags & (RADIX_MASK | ALTERNATE_FORM))
560  == (RADIX_HEX | ALTERNATE_FORM) && uvalue != 0) {
561  prefix_len = 2;
562  if(flags & CAPS_YES) {
563  prefix = "0X";
564  } else {
565  prefix = "0x";
566  }
567  }
568 
569  if(flags & SIGNED_YES) {
570  if(negative) {
571  prefix = "-";
572  prefix_len = 1;
573  } else {
574  switch(flags & POSITIVE_MASK) {
575  case POSITIVE_SPACE:
576  prefix = " ";
577  prefix_len = 1;
578  break;
579  case POSITIVE_PLUS:
580  prefix = "+";
581  prefix_len = 1;
582  break;
583  }
584  }
585  }
586 
587  width += prefix_len;
588 
589  field_fill = (minwidth > width) ? minwidth - width : 0;
590 
591  if((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
592  if(flags & PAD_ZERO) {
593  precision_fill += field_fill;
594  field_fill = 0; /* Do not double count padding */
595  } else {
596  CHECKCB(fill_space(ctxt, field_fill));
597  }
598  }
599 
600  if(prefix_len > 0) {
601  CHECKCB(ctxt->write_str(ctxt->user_data, prefix, prefix_len));
602  }
603  written += prefix_len;
604 
605  CHECKCB(fill_zero(ctxt, precision_fill));
606  written += precision_fill;
607 
608  CHECKCB(ctxt->write_str(ctxt->user_data, conv_pos, conv_len));
609  written += conv_len;
610 
611  if((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
612  CHECKCB(fill_space(ctxt, field_fill));
613  }
614  written += field_fill;
615  }
616  break;
617  case CONV_STRING:
618  {
619  unsigned int field_fill;
620  unsigned int len;
621  char *str = va_arg(ap, char *);
622 
623  if(str) {
624  char *pos = str;
625  while(*pos != '\0') pos++;
626  len = pos - str;
627  } else {
628  str = "(null)";
629  len = 6;
630  }
631 
632  if(precision >= 0 && precision < len) {
633  len = precision;
634  }
635 
636  field_fill = (minwidth > len) ? minwidth - len : 0;
637 
638  if((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
639  CHECKCB(fill_space(ctxt, field_fill));
640  }
641 
642  CHECKCB(ctxt->write_str(ctxt->user_data, str, len));
643  written += len;
644 
645  if((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
646  CHECKCB(fill_space(ctxt, field_fill));
647  }
648  written += field_fill;
649  }
650  break;
651  case CONV_POINTER:
652  {
653  LARGEST_UNSIGNED uvalue =
654  (LARGEST_UNSIGNED)(POINTER_INT)va_arg(ap, void *);
655  char buffer[MAXCHARS_HEX + 3];
656  char *conv_pos = buffer + MAXCHARS_HEX + 3;
657  unsigned int conv_len;
658  unsigned int field_fill;
659 
660  conv_len = output_uint_hex(&conv_pos, uvalue, flags);
661 
662  if(conv_len == 0) {
663  *--conv_pos = '0';
664  conv_len++;
665  }
666 
667  *--conv_pos = 'x';
668  *--conv_pos = '0';
669  *--conv_pos = '#';
670  conv_len += 3;
671 
672  field_fill = (minwidth > conv_len) ? minwidth - conv_len : 0;
673 
674  if((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
675  CHECKCB(fill_space(ctxt, field_fill));
676  }
677 
678  CHECKCB(ctxt->write_str(ctxt->user_data, conv_pos, conv_len));
679  written += conv_len;
680 
681  if((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
682  CHECKCB(fill_space(ctxt, field_fill));
683  }
684 
685  written += field_fill;
686  }
687  break;
688  case CONV_CHAR:
689  {
690  char ch = va_arg(ap, int);
691  unsigned int field_fill = (minwidth > 1) ? minwidth - 1 : 0;
692 
693  if((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
694  CHECKCB(fill_space(ctxt, field_fill));
695  written += field_fill;
696  }
697 
698  CHECKCB(ctxt->write_str(ctxt->user_data, &ch, 1));
699  written++;
700 
701  if((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
702  CHECKCB(fill_space(ctxt, field_fill));
703  }
704  written += field_fill;
705  }
706  break;
707  case CONV_WRITTEN:
708  {
709  int *p = va_arg(ap, int *);
710  *p = written;
711  }
712  break;
713  }
714  }
715 
716  return written;
717 }
718 /*---------------------------------------------------------------------------*/
static void start(void)
Start measurement.