1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Chris Torek.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #if defined(LIBC_SCCS) && !defined(lint)
36 static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93";
37 #endif /* LIBC_SCCS and not lint */
38 /*
39 * This is the code responsible for handling positional arguments
40 * (%m$ and %m$.n$) for vfprintf() and vfwprintf().
41 */
42
43 #include "namespace.h"
44 #include <sys/types.h>
45
46 #include <limits.h>
47 #include <stdarg.h>
48 #include <stddef.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <wchar.h>
54
55 #include "un-namespace.h"
56 #include "printflocal.h"
57
58 #ifdef NL_ARGMAX
59 #define MAX_POSARG NL_ARGMAX
60 #else
61 #define MAX_POSARG 65536
62 #endif
63
64 /*
65 * Type ids for argument type table.
66 */
67 enum typeid {
68 T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT,
69 T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG,
70 T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SSIZET,
71 T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR,
72 T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR
73 };
74
75 /* An expandable array of types. */
76 struct typetable {
77 enum typeid *table; /* table of types */
78 enum typeid stattable[STATIC_ARG_TBL_SIZE];
79 u_int tablesize; /* current size of type table */
80 u_int tablemax; /* largest used index in table */
81 u_int nextarg; /* 1-based argument index */
82 };
83
84 static int __grow_type_table(struct typetable *);
85 static void build_arg_table (struct typetable *, va_list, union arg **);
86
87 /*
88 * Initialize a struct typetable.
89 */
90 static inline void
inittypes(struct typetable * types)91 inittypes(struct typetable *types)
92 {
93 u_int n;
94
95 types->table = types->stattable;
96 types->tablesize = STATIC_ARG_TBL_SIZE;
97 types->tablemax = 0;
98 types->nextarg = 1;
99 for (n = 0; n < STATIC_ARG_TBL_SIZE; n++)
100 types->table[n] = T_UNUSED;
101 }
102
103 /*
104 * struct typetable destructor.
105 */
106 static inline void
freetypes(struct typetable * types)107 freetypes(struct typetable *types)
108 {
109
110 if (types->table != types->stattable)
111 free (types->table);
112 }
113
114 /*
115 * Ensure that there is space to add a new argument type to the type table.
116 * Expand the table if necessary. Returns 0 on success.
117 */
118 static inline int
_ensurespace(struct typetable * types)119 _ensurespace(struct typetable *types)
120 {
121
122 if (types->nextarg >= types->tablesize) {
123 if (__grow_type_table(types))
124 return (-1);
125 }
126 if (types->nextarg > types->tablemax)
127 types->tablemax = types->nextarg;
128 return (0);
129 }
130
131 /*
132 * Add an argument type to the table, expanding if necessary.
133 * Returns 0 on success.
134 */
135 static inline int
addtype(struct typetable * types,enum typeid type)136 addtype(struct typetable *types, enum typeid type)
137 {
138
139 if (_ensurespace(types))
140 return (-1);
141 types->table[types->nextarg++] = type;
142 return (0);
143 }
144
145 static inline int
addsarg(struct typetable * types,int flags)146 addsarg(struct typetable *types, int flags)
147 {
148
149 if (_ensurespace(types))
150 return (-1);
151 if (flags & INTMAXT)
152 types->table[types->nextarg++] = T_INTMAXT;
153 else if (flags & SIZET)
154 types->table[types->nextarg++] = T_SSIZET;
155 else if (flags & PTRDIFFT)
156 types->table[types->nextarg++] = T_PTRDIFFT;
157 else if (flags & LLONGINT)
158 types->table[types->nextarg++] = T_LLONG;
159 else if (flags & LONGINT)
160 types->table[types->nextarg++] = T_LONG;
161 else
162 types->table[types->nextarg++] = T_INT;
163 return (0);
164 }
165
166 static inline int
adduarg(struct typetable * types,int flags)167 adduarg(struct typetable *types, int flags)
168 {
169
170 if (_ensurespace(types))
171 return (-1);
172 if (flags & INTMAXT)
173 types->table[types->nextarg++] = T_UINTMAXT;
174 else if (flags & SIZET)
175 types->table[types->nextarg++] = T_SIZET;
176 else if (flags & PTRDIFFT)
177 types->table[types->nextarg++] = T_SIZET;
178 else if (flags & LLONGINT)
179 types->table[types->nextarg++] = T_U_LLONG;
180 else if (flags & LONGINT)
181 types->table[types->nextarg++] = T_U_LONG;
182 else
183 types->table[types->nextarg++] = T_U_INT;
184 return (0);
185 }
186
187 /*
188 * Add * arguments to the type array.
189 */
190 static inline int
addaster(struct typetable * types,char ** fmtp)191 addaster(struct typetable *types, char **fmtp)
192 {
193 char *cp;
194 u_int n2;
195
196 n2 = 0;
197 cp = *fmtp;
198 while (is_digit(*cp)) {
199 n2 = 10 * n2 + to_digit(*cp);
200 cp++;
201 }
202 if (*cp == '$') {
203 u_int hold = types->nextarg;
204 types->nextarg = n2;
205 if (addtype(types, T_INT))
206 return (-1);
207 types->nextarg = hold;
208 *fmtp = ++cp;
209 } else {
210 if (addtype(types, T_INT))
211 return (-1);
212 }
213 return (0);
214 }
215
216 static inline int
addwaster(struct typetable * types,wchar_t ** fmtp)217 addwaster(struct typetable *types, wchar_t **fmtp)
218 {
219 wchar_t *cp;
220 u_int n2;
221
222 n2 = 0;
223 cp = *fmtp;
224 while (is_digit(*cp)) {
225 n2 = 10 * n2 + to_digit(*cp);
226 cp++;
227 }
228 if (*cp == '$') {
229 u_int hold = types->nextarg;
230 types->nextarg = n2;
231 if (addtype(types, T_INT))
232 return (-1);
233 types->nextarg = hold;
234 *fmtp = ++cp;
235 } else {
236 if (addtype(types, T_INT))
237 return (-1);
238 }
239 return (0);
240 }
241
242 /*
243 * Find all arguments when a positional parameter is encountered. Returns a
244 * table, indexed by argument number, of pointers to each arguments. The
245 * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
246 * It will be replaces with a malloc-ed one if it overflows.
247 * Returns 0 on success. On failure, returns nonzero and sets errno.
248 */
249 int
__find_arguments(const char * fmt0,va_list ap,union arg ** argtable)250 __find_arguments (const char *fmt0, va_list ap, union arg **argtable)
251 {
252 char *fmt; /* format string */
253 int ch; /* character from fmt */
254 u_int n; /* handy integer (short term usage) */
255 int error;
256 int flags; /* flags as above */
257 struct typetable types; /* table of types */
258
259 fmt = (char *)fmt0;
260 inittypes(&types);
261 error = 0;
262
263 /*
264 * Scan the format for conversions (`%' character).
265 */
266 for (;;) {
267 while ((ch = *fmt) != '\0' && ch != '%')
268 fmt++;
269 if (ch == '\0')
270 goto done;
271 fmt++; /* skip over '%' */
272
273 flags = 0;
274
275 rflag: ch = *fmt++;
276 reswitch: switch (ch) {
277 case ' ':
278 case '#':
279 goto rflag;
280 case '*':
281 if ((error = addaster(&types, &fmt)))
282 goto error;
283 goto rflag;
284 case '-':
285 case '+':
286 case '\'':
287 goto rflag;
288 case '.':
289 if ((ch = *fmt++) == '*') {
290 if ((error = addaster(&types, &fmt)))
291 goto error;
292 goto rflag;
293 }
294 while (is_digit(ch)) {
295 ch = *fmt++;
296 }
297 goto reswitch;
298 case '0':
299 goto rflag;
300 case '1': case '2': case '3': case '4':
301 case '5': case '6': case '7': case '8': case '9':
302 n = 0;
303 do {
304 n = 10 * n + to_digit(ch);
305 /* Detect overflow */
306 if (n > MAX_POSARG) {
307 error = -1;
308 goto error;
309 }
310 ch = *fmt++;
311 } while (is_digit(ch));
312 if (ch == '$') {
313 types.nextarg = n;
314 goto rflag;
315 }
316 goto reswitch;
317 #ifndef NO_FLOATING_POINT
318 case 'L':
319 flags |= LONGDBL;
320 goto rflag;
321 #endif
322 case 'h':
323 if (flags & SHORTINT) {
324 flags &= ~SHORTINT;
325 flags |= CHARINT;
326 } else
327 flags |= SHORTINT;
328 goto rflag;
329 case 'j':
330 flags |= INTMAXT;
331 goto rflag;
332 case 'l':
333 if (flags & LONGINT) {
334 flags &= ~LONGINT;
335 flags |= LLONGINT;
336 } else
337 flags |= LONGINT;
338 goto rflag;
339 case 'q':
340 flags |= LLONGINT; /* not necessarily */
341 goto rflag;
342 case 't':
343 flags |= PTRDIFFT;
344 goto rflag;
345 case 'z':
346 flags |= SIZET;
347 goto rflag;
348 case 'C':
349 flags |= LONGINT;
350 /*FALLTHROUGH*/
351 case 'c':
352 error = addtype(&types,
353 (flags & LONGINT) ? T_WINT : T_INT);
354 if (error)
355 goto error;
356 break;
357 case 'D':
358 flags |= LONGINT;
359 /*FALLTHROUGH*/
360 case 'd':
361 case 'i':
362 if ((error = addsarg(&types, flags)))
363 goto error;
364 break;
365 #ifndef NO_FLOATING_POINT
366 case 'a':
367 case 'A':
368 case 'e':
369 case 'E':
370 case 'f':
371 case 'g':
372 case 'G':
373 error = addtype(&types,
374 (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE);
375 if (error)
376 goto error;
377 break;
378 #endif /* !NO_FLOATING_POINT */
379 case 'n':
380 if (flags & INTMAXT)
381 error = addtype(&types, TP_INTMAXT);
382 else if (flags & PTRDIFFT)
383 error = addtype(&types, TP_PTRDIFFT);
384 else if (flags & SIZET)
385 error = addtype(&types, TP_SSIZET);
386 else if (flags & LLONGINT)
387 error = addtype(&types, TP_LLONG);
388 else if (flags & LONGINT)
389 error = addtype(&types, TP_LONG);
390 else if (flags & SHORTINT)
391 error = addtype(&types, TP_SHORT);
392 else if (flags & CHARINT)
393 error = addtype(&types, TP_SCHAR);
394 else
395 error = addtype(&types, TP_INT);
396 if (error)
397 goto error;
398 continue; /* no output */
399 case 'O':
400 flags |= LONGINT;
401 /*FALLTHROUGH*/
402 case 'o':
403 if ((error = adduarg(&types, flags)))
404 goto error;
405 break;
406 case 'p':
407 if ((error = addtype(&types, TP_VOID)))
408 goto error;
409 break;
410 case 'S':
411 flags |= LONGINT;
412 /*FALLTHROUGH*/
413 case 's':
414 error = addtype(&types,
415 (flags & LONGINT) ? TP_WCHAR : TP_CHAR);
416 if (error)
417 goto error;
418 break;
419 case 'U':
420 flags |= LONGINT;
421 /*FALLTHROUGH*/
422 case 'u':
423 case 'X':
424 case 'x':
425 if ((error = adduarg(&types, flags)))
426 goto error;
427 break;
428 default: /* "%?" prints ?, unless ? is NUL */
429 if (ch == '\0')
430 goto done;
431 break;
432 }
433 }
434 done:
435 build_arg_table(&types, ap, argtable);
436 error:
437 freetypes(&types);
438 return (error || *argtable == NULL);
439 }
440
441 /* wchar version of __find_arguments. */
442 int
__find_warguments(const wchar_t * fmt0,va_list ap,union arg ** argtable)443 __find_warguments (const wchar_t *fmt0, va_list ap, union arg **argtable)
444 {
445 wchar_t *fmt; /* format string */
446 wchar_t ch; /* character from fmt */
447 u_int n; /* handy integer (short term usage) */
448 int error;
449 int flags; /* flags as above */
450 struct typetable types; /* table of types */
451
452 fmt = (wchar_t *)fmt0;
453 inittypes(&types);
454 error = 0;
455
456 /*
457 * Scan the format for conversions (`%' character).
458 */
459 for (;;) {
460 while ((ch = *fmt) != '\0' && ch != '%')
461 fmt++;
462 if (ch == '\0')
463 goto done;
464 fmt++; /* skip over '%' */
465
466 flags = 0;
467
468 rflag: ch = *fmt++;
469 reswitch: switch (ch) {
470 case ' ':
471 case '#':
472 goto rflag;
473 case '*':
474 if ((error = addwaster(&types, &fmt)))
475 goto error;
476 goto rflag;
477 case '-':
478 case '+':
479 case '\'':
480 goto rflag;
481 case '.':
482 if ((ch = *fmt++) == '*') {
483 if ((error = addwaster(&types, &fmt)))
484 goto error;
485 goto rflag;
486 }
487 while (is_digit(ch)) {
488 ch = *fmt++;
489 }
490 goto reswitch;
491 case '0':
492 goto rflag;
493 case '1': case '2': case '3': case '4':
494 case '5': case '6': case '7': case '8': case '9':
495 n = 0;
496 do {
497 n = 10 * n + to_digit(ch);
498 /* Detect overflow */
499 if (n > MAX_POSARG) {
500 error = -1;
501 goto error;
502 }
503 ch = *fmt++;
504 } while (is_digit(ch));
505 if (ch == '$') {
506 types.nextarg = n;
507 goto rflag;
508 }
509 goto reswitch;
510 #ifndef NO_FLOATING_POINT
511 case 'L':
512 flags |= LONGDBL;
513 goto rflag;
514 #endif
515 case 'h':
516 if (flags & SHORTINT) {
517 flags &= ~SHORTINT;
518 flags |= CHARINT;
519 } else
520 flags |= SHORTINT;
521 goto rflag;
522 case 'j':
523 flags |= INTMAXT;
524 goto rflag;
525 case 'l':
526 if (flags & LONGINT) {
527 flags &= ~LONGINT;
528 flags |= LLONGINT;
529 } else
530 flags |= LONGINT;
531 goto rflag;
532 case 'q':
533 flags |= LLONGINT; /* not necessarily */
534 goto rflag;
535 case 't':
536 flags |= PTRDIFFT;
537 goto rflag;
538 case 'z':
539 flags |= SIZET;
540 goto rflag;
541 case 'C':
542 flags |= LONGINT;
543 /*FALLTHROUGH*/
544 case 'c':
545 error = addtype(&types,
546 (flags & LONGINT) ? T_WINT : T_INT);
547 if (error)
548 goto error;
549 break;
550 case 'D':
551 flags |= LONGINT;
552 /*FALLTHROUGH*/
553 case 'd':
554 case 'i':
555 if ((error = addsarg(&types, flags)))
556 goto error;
557 break;
558 #ifndef NO_FLOATING_POINT
559 case 'a':
560 case 'A':
561 case 'e':
562 case 'E':
563 case 'f':
564 case 'g':
565 case 'G':
566 error = addtype(&types,
567 (flags & LONGDBL) ? T_LONG_DOUBLE : T_DOUBLE);
568 if (error)
569 goto error;
570 break;
571 #endif /* !NO_FLOATING_POINT */
572 case 'n':
573 if (flags & INTMAXT)
574 error = addtype(&types, TP_INTMAXT);
575 else if (flags & PTRDIFFT)
576 error = addtype(&types, TP_PTRDIFFT);
577 else if (flags & SIZET)
578 error = addtype(&types, TP_SSIZET);
579 else if (flags & LLONGINT)
580 error = addtype(&types, TP_LLONG);
581 else if (flags & LONGINT)
582 error = addtype(&types, TP_LONG);
583 else if (flags & SHORTINT)
584 error = addtype(&types, TP_SHORT);
585 else if (flags & CHARINT)
586 error = addtype(&types, TP_SCHAR);
587 else
588 error = addtype(&types, TP_INT);
589 if (error)
590 goto error;
591 continue; /* no output */
592 case 'O':
593 flags |= LONGINT;
594 /*FALLTHROUGH*/
595 case 'o':
596 if ((error = adduarg(&types, flags)))
597 goto error;
598 break;
599 case 'p':
600 if ((error = addtype(&types, TP_VOID)))
601 goto error;
602 break;
603 case 'S':
604 flags |= LONGINT;
605 /*FALLTHROUGH*/
606 case 's':
607 error = addtype(&types,
608 (flags & LONGINT) ? TP_WCHAR : TP_CHAR);
609 if (error)
610 goto error;
611 break;
612 case 'U':
613 flags |= LONGINT;
614 /*FALLTHROUGH*/
615 case 'u':
616 case 'X':
617 case 'x':
618 if ((error = adduarg(&types, flags)))
619 goto error;
620 break;
621 default: /* "%?" prints ?, unless ? is NUL */
622 if (ch == '\0')
623 goto done;
624 break;
625 }
626 }
627 done:
628 build_arg_table(&types, ap, argtable);
629 error:
630 freetypes(&types);
631 return (error || *argtable == NULL);
632 }
633
634 /*
635 * Increase the size of the type table. Returns 0 on success.
636 */
637 static int
__grow_type_table(struct typetable * types)638 __grow_type_table(struct typetable *types)
639 {
640 enum typeid *const oldtable = types->table;
641 const int oldsize = types->tablesize;
642 enum typeid *newtable;
643 u_int n, newsize;
644
645 /* Detect overflow */
646 if (types->nextarg > NL_ARGMAX)
647 return (-1);
648
649 newsize = oldsize * 2;
650 if (newsize < types->nextarg + 1)
651 newsize = types->nextarg + 1;
652 if (oldsize == STATIC_ARG_TBL_SIZE) {
653 if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL)
654 return (-1);
655 bcopy(oldtable, newtable, oldsize * sizeof(enum typeid));
656 } else {
657 newtable = reallocarray(oldtable, newsize, sizeof(enum typeid));
658 if (newtable == NULL)
659 return (-1);
660 }
661 for (n = oldsize; n < newsize; n++)
662 newtable[n] = T_UNUSED;
663
664 types->table = newtable;
665 types->tablesize = newsize;
666
667 return (0);
668 }
669
670 /*
671 * Build the argument table from the completed type table.
672 * On malloc failure, *argtable is set to NULL.
673 */
674 static void
build_arg_table(struct typetable * types,va_list ap,union arg ** argtable)675 build_arg_table(struct typetable *types, va_list ap, union arg **argtable)
676 {
677 u_int n;
678
679 if (types->tablemax >= STATIC_ARG_TBL_SIZE) {
680 *argtable = (union arg *)
681 malloc (sizeof (union arg) * (types->tablemax + 1));
682 if (*argtable == NULL)
683 return;
684 }
685
686 (*argtable) [0].intarg = 0;
687 for (n = 1; n <= types->tablemax; n++) {
688 switch (types->table[n]) {
689 case T_UNUSED: /* whoops! */
690 (*argtable) [n].intarg = va_arg (ap, int);
691 break;
692 case TP_SCHAR:
693 (*argtable) [n].pschararg = va_arg (ap, signed char *);
694 break;
695 case TP_SHORT:
696 (*argtable) [n].pshortarg = va_arg (ap, short *);
697 break;
698 case T_INT:
699 (*argtable) [n].intarg = va_arg (ap, int);
700 break;
701 case T_U_INT:
702 (*argtable) [n].uintarg = va_arg (ap, unsigned int);
703 break;
704 case TP_INT:
705 (*argtable) [n].pintarg = va_arg (ap, int *);
706 break;
707 case T_LONG:
708 (*argtable) [n].longarg = va_arg (ap, long);
709 break;
710 case T_U_LONG:
711 (*argtable) [n].ulongarg = va_arg (ap, unsigned long);
712 break;
713 case TP_LONG:
714 (*argtable) [n].plongarg = va_arg (ap, long *);
715 break;
716 case T_LLONG:
717 (*argtable) [n].longlongarg = va_arg (ap, long long);
718 break;
719 case T_U_LLONG:
720 (*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long);
721 break;
722 case TP_LLONG:
723 (*argtable) [n].plonglongarg = va_arg (ap, long long *);
724 break;
725 case T_PTRDIFFT:
726 (*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t);
727 break;
728 case TP_PTRDIFFT:
729 (*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *);
730 break;
731 case T_SIZET:
732 (*argtable) [n].sizearg = va_arg (ap, size_t);
733 break;
734 case T_SSIZET:
735 (*argtable) [n].sizearg = va_arg (ap, ssize_t);
736 break;
737 case TP_SSIZET:
738 (*argtable) [n].pssizearg = va_arg (ap, ssize_t *);
739 break;
740 case T_INTMAXT:
741 (*argtable) [n].intmaxarg = va_arg (ap, intmax_t);
742 break;
743 case T_UINTMAXT:
744 (*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t);
745 break;
746 case TP_INTMAXT:
747 (*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *);
748 break;
749 case T_DOUBLE:
750 #ifndef NO_FLOATING_POINT
751 (*argtable) [n].doublearg = va_arg (ap, double);
752 #endif
753 break;
754 case T_LONG_DOUBLE:
755 #ifndef NO_FLOATING_POINT
756 (*argtable) [n].longdoublearg = va_arg (ap, long double);
757 #endif
758 break;
759 case TP_CHAR:
760 (*argtable) [n].pchararg = va_arg (ap, char *);
761 break;
762 case TP_VOID:
763 (*argtable) [n].pvoidarg = va_arg (ap, void *);
764 break;
765 case T_WINT:
766 (*argtable) [n].wintarg = va_arg (ap, wint_t);
767 break;
768 case TP_WCHAR:
769 (*argtable) [n].pwchararg = va_arg (ap, wchar_t *);
770 break;
771 }
772 }
773 }
774