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