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  }
377 
378  /* parse conversion specifier */
379  switch(*pos) {
380  case 'd':
381  case 'i':
382  flags |= CONV_INTEGER | RADIX_DECIMAL | SIGNED_YES;
383  break;
384  case 'u':
385  flags |= CONV_INTEGER | RADIX_DECIMAL | SIGNED_NO;
386  break;
387  case 'o':
388  flags |= CONV_INTEGER | RADIX_OCTAL | SIGNED_NO;
389  break;
390  case 'x':
391  flags |= CONV_INTEGER | RADIX_HEX | SIGNED_NO;
392  break;
393  case 'X':
394  flags |= CONV_INTEGER | RADIX_HEX | SIGNED_NO | CAPS_YES;
395  break;
396 #ifdef HAVE_DOUBLE
397  case 'f':
398  flags |= CONV_FLOAT | FLOAT_NORMAL;
399  break;
400  case 'F':
401  flags |= CONV_FLOAT | FLOAT_NORMAL | CAPS_YES;
402  break;
403  case 'e':
404  flags |= CONV_FLOAT | FLOAT_EXPONENT;
405  break;
406  case 'E':
407  flags |= CONV_FLOAT | FLOAT_EXPONENT | CAPS_YES;
408  break;
409  case 'g':
410  flags |= CONV_FLOAT | FLOAT_DEPENDANT;
411  break;
412  case 'G':
413  flags |= CONV_FLOAT | FLOAT_DEPENDANT | CAPS_YES;
414  break;
415  case 'a':
416  flags |= CONV_FLOAT | FLOAT_HEX;
417  break;
418  case 'A':
419  flags |= CONV_FLOAT | FLOAT_HEX | CAPS_YES;
420  break;
421 #endif
422  case 'c':
423  flags |= CONV_CHAR;
424  break;
425  case 's':
426  flags |= CONV_STRING;
427  break;
428  case 'p':
429  flags |= CONV_POINTER;
430  break;
431  case 'n':
432  flags |= CONV_WRITTEN;
433  break;
434  case '%':
435  flags |= CONV_PERCENT;
436  break;
437  case '\0':
438  va_end(ap);
439  return written;
440  }
441  pos++;
442 
443  switch(flags & CONV_MASK) {
444  case CONV_PERCENT:
445  CHECKCB(ctxt->write_str(ctxt->user_data, "%", 1));
446  written++;
447  break;
448  case CONV_INTEGER:
449  {
450  /* unsigned integers */
451  char *prefix = 0; /* sign, "0x" or "0X" */
452  unsigned int prefix_len = 0;
453  char buffer[MAXCHARS];
454  char *conv_pos = buffer + MAXCHARS;
455  unsigned int conv_len = 0;
456  unsigned int width = 0;
457  unsigned int precision_fill;
458  unsigned int field_fill;
459  LARGEST_UNSIGNED uvalue = 0;
460  int negative = 0;
461 
462  if(precision < 0) {
463  precision = 1;
464  } else {
465  flags &= ~PAD_ZERO;
466  }
467 
468  if(flags & SIGNED_YES) {
469  /* signed integers */
470  LARGEST_SIGNED value = 0;
471  switch(flags & SIZE_MASK) {
472  case SIZE_CHAR:
473  value = (signed char)va_arg(ap, int);
474  break;
475  case SIZE_SHORT:
476  value = (short)va_arg(ap, int);
477  break;
478  case SIZE_INT:
479  value = va_arg(ap, int);
480  break;
481 #ifndef HAVE_LONGLONG
482  case SIZE_LONGLONG: /* Treat long long the same as long */
483 #endif
484  case SIZE_LONG:
485  value = va_arg(ap, long);
486  break;
487 #ifdef HAVE_LONGLONG
488  case SIZE_LONGLONG:
489  value = va_arg(ap, long long);
490  break;
491 #endif
492  }
493  if(value < 0) {
494  uvalue = -value;
495  negative = 1;
496  } else {
497  uvalue = value;
498  }
499  } else {
500 
501  switch(flags & SIZE_MASK) {
502  case SIZE_CHAR:
503  uvalue = (unsigned char)va_arg(ap, unsigned int);
504  break;
505  case SIZE_SHORT:
506  uvalue = (unsigned short)va_arg(ap, unsigned int);
507  break;
508  case SIZE_INT:
509  uvalue = va_arg(ap, unsigned int);
510  break;
511 #ifndef HAVE_LONGLONG
512  case SIZE_LONGLONG: /* Treat long long the same as long */
513 #endif
514  case SIZE_LONG:
515  uvalue = va_arg(ap, unsigned long);
516  break;
517 #ifdef HAVE_LONGLONG
518  case SIZE_LONGLONG:
519  uvalue = va_arg(ap, unsigned long long);
520  break;
521 #endif
522  }
523  }
524 
525  switch(flags & (RADIX_MASK)) {
526  case RADIX_DECIMAL:
527  conv_len = output_uint_decimal(&conv_pos, uvalue);
528  break;
529  case RADIX_OCTAL:
530  conv_len = output_uint_octal(&conv_pos, uvalue);
531  break;
532  case RADIX_HEX:
533  conv_len = output_uint_hex(&conv_pos, uvalue, flags);
534  break;
535  }
536 
537  width += conv_len;
538  precision_fill = (precision > conv_len) ? precision - conv_len : 0;
539  if((flags & (RADIX_MASK | ALTERNATE_FORM))
540  == (RADIX_OCTAL | ALTERNATE_FORM)) {
541  if(precision_fill < 1) {
542  precision_fill = 1;
543  }
544  }
545 
546  width += precision_fill;
547 
548  if((flags & (RADIX_MASK | ALTERNATE_FORM))
549  == (RADIX_HEX | ALTERNATE_FORM) && uvalue != 0) {
550  prefix_len = 2;
551  if(flags & CAPS_YES) {
552  prefix = "0X";
553  } else {
554  prefix = "0x";
555  }
556  }
557 
558  if(flags & SIGNED_YES) {
559  if(negative) {
560  prefix = "-";
561  prefix_len = 1;
562  } else {
563  switch(flags & POSITIVE_MASK) {
564  case POSITIVE_SPACE:
565  prefix = " ";
566  prefix_len = 1;
567  break;
568  case POSITIVE_PLUS:
569  prefix = "+";
570  prefix_len = 1;
571  break;
572  }
573  }
574  }
575 
576  width += prefix_len;
577 
578  field_fill = (minwidth > width) ? minwidth - width : 0;
579 
580  if((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
581  if(flags & PAD_ZERO) {
582  precision_fill += field_fill;
583  field_fill = 0; /* Do not double count padding */
584  } else {
585  CHECKCB(fill_space(ctxt, field_fill));
586  }
587  }
588 
589  if(prefix_len > 0) {
590  CHECKCB(ctxt->write_str(ctxt->user_data, prefix, prefix_len));
591  }
592  written += prefix_len;
593 
594  CHECKCB(fill_zero(ctxt, precision_fill));
595  written += precision_fill;
596 
597  CHECKCB(ctxt->write_str(ctxt->user_data, conv_pos, conv_len));
598  written += conv_len;
599 
600  if((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
601  CHECKCB(fill_space(ctxt, field_fill));
602  }
603  written += field_fill;
604  }
605  break;
606  case CONV_STRING:
607  {
608  unsigned int field_fill;
609  unsigned int len;
610  char *str = va_arg(ap, char *);
611 
612  if(str) {
613  char *pos = str;
614  while(*pos != '\0') pos++;
615  len = pos - str;
616  } else {
617  str = "(null)";
618  len = 6;
619  }
620 
621  if(precision >= 0 && precision < len) {
622  len = precision;
623  }
624 
625  field_fill = (minwidth > len) ? minwidth - len : 0;
626 
627  if((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
628  CHECKCB(fill_space(ctxt, field_fill));
629  }
630 
631  CHECKCB(ctxt->write_str(ctxt->user_data, str, len));
632  written += len;
633 
634  if((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
635  CHECKCB(fill_space(ctxt, field_fill));
636  }
637  written += field_fill;
638  }
639  break;
640  case CONV_POINTER:
641  {
642  LARGEST_UNSIGNED uvalue =
643  (LARGEST_UNSIGNED)(POINTER_INT)va_arg(ap, void *);
644  char buffer[MAXCHARS_HEX + 3];
645  char *conv_pos = buffer + MAXCHARS_HEX + 3;
646  unsigned int conv_len;
647  unsigned int field_fill;
648 
649  conv_len = output_uint_hex(&conv_pos, uvalue, flags);
650 
651  if(conv_len == 0) {
652  *--conv_pos = '0';
653  conv_len++;
654  }
655 
656  *--conv_pos = 'x';
657  *--conv_pos = '0';
658  *--conv_pos = '#';
659  conv_len += 3;
660 
661  field_fill = (minwidth > conv_len) ? minwidth - conv_len : 0;
662 
663  if((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
664  CHECKCB(fill_space(ctxt, field_fill));
665  }
666 
667  CHECKCB(ctxt->write_str(ctxt->user_data, conv_pos, conv_len));
668  written += conv_len;
669 
670  if((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
671  CHECKCB(fill_space(ctxt, field_fill));
672  }
673 
674  written += field_fill;
675  }
676  break;
677  case CONV_CHAR:
678  {
679  char ch = va_arg(ap, int);
680  unsigned int field_fill = (minwidth > 1) ? minwidth - 1 : 0;
681 
682  if((flags & JUSTIFY_MASK) == JUSTIFY_RIGHT) {
683  CHECKCB(fill_space(ctxt, field_fill));
684  written += field_fill;
685  }
686 
687  CHECKCB(ctxt->write_str(ctxt->user_data, &ch, 1));
688  written++;
689 
690  if((flags & JUSTIFY_MASK) == JUSTIFY_LEFT) {
691  CHECKCB(fill_space(ctxt, field_fill));
692  }
693  written += field_fill;
694  }
695  break;
696  case CONV_WRITTEN:
697  {
698  int *p = va_arg(ap, int *);
699  *p = written;
700  }
701  break;
702  }
703  }
704 
705  return written;
706 }
707 /*---------------------------------------------------------------------------*/
static void start(void)
Start measurement.