Contiki-NG
Loading...
Searching...
No Matches
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/*---------------------------------------------------------------------------*/
60typedef uint32_t 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 (((uint32_t)0x0000) << FLOAT_SHIFT)
130#define FLOAT_EXPONENT (((uint32_t)0x0001) << FLOAT_SHIFT)
131#define FLOAT_DEPENDANT (((uint32_t)0x0002) << FLOAT_SHIFT)
132#define FLOAT_HEX (((uint32_t)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/*---------------------------------------------------------------------------*/
142static FormatFlags
143parse_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/*---------------------------------------------------------------------------*/
174static unsigned int
175parse_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/*---------------------------------------------------------------------------*/
191static unsigned int
192output_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/*---------------------------------------------------------------------------*/
208static unsigned int
209output_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/*---------------------------------------------------------------------------*/
226static unsigned int
227output_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/*---------------------------------------------------------------------------*/
243static strformat_result
244fill_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/*---------------------------------------------------------------------------*/
264static strformat_result
265fill_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/*---------------------------------------------------------------------------*/
284int
285format_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/*---------------------------------------------------------------------------*/
295int
296format_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.