]> git.saurik.com Git - apple/libc.git/blobdiff - stdio/FreeBSD/xprintf.c
Libc-997.1.1.tar.gz
[apple/libc.git] / stdio / FreeBSD / xprintf.c
diff --git a/stdio/FreeBSD/xprintf.c b/stdio/FreeBSD/xprintf.c
new file mode 100644 (file)
index 0000000..772155d
--- /dev/null
@@ -0,0 +1,1199 @@
+/*-
+ * Copyright (c) 2005 Poul-Henning Kamp
+ * Copyright (c) 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libc/stdio/xprintf.c,v 1.9 2010/03/11 17:03:32 jhb Exp $
+ */
+
+#include "namespace.h"
+#include <err.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <stdint.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <namespace.h>
+#include <string.h>
+#include <wchar.h>
+#include <errno.h>
+#include "un-namespace.h"
+
+//#define MACHTIME
+#ifdef MACHTIME
+#include <mach/mach_time.h>
+#endif // MACHTIME
+
+#ifdef XPRINTF_PERF
+#include <libkern/OSAtomic.h>
+#endif /* XPRINTF_PERF */
+
+#include "local.h"
+#include "xprintf_private.h"
+#include "xprintf_domain.h"
+#include "fvwrite.h"
+
+/*
+ * Defining XPRINTF_DEBUG allows the __private_extern__ variable __use_xprintf
+ * to be set so that regular printf variants will use the extensible printf
+ * code path.  This is normally off, and is only used to test the extensible
+ * printf code in the conformance tests.
+ */
+#ifdef XPRINTF_DEBUG
+#include <unistd.h>
+int __use_xprintf = 0;
+#endif
+
+/* private stuff -----------------------------------------------------*/
+
+union arg {
+       int                     intarg;
+       long                    longarg;
+       intmax_t                intmaxarg;
+#ifndef NO_FLOATING_POINT
+       double                  doublearg;
+       long double             longdoublearg;
+#endif
+       wint_t                  wintarg;
+       char                    *pchararg;
+       wchar_t                 *pwchararg;
+       void                    *pvoidarg;
+#ifdef VECTORS
+       VECTORTYPE              vectorarg;
+       unsigned char           vuchararg[16];
+       signed char             vchararg[16];
+       unsigned short          vushortarg[8];
+       signed short            vshortarg[8];
+       unsigned int            vuintarg[4];
+       signed int              vintarg[4];
+       float                   vfloatarg[4];
+#ifdef V64TYPE
+       double                  vdoublearg[2];
+       unsigned long long      vulonglongarg[2];
+       long long               vlonglongarg[2];
+#endif /* V64TYPE */
+#endif /* VECTORS */
+};
+
+/*
+ * Macros for converting digits to letters and vice versa
+ */
+#define        to_digit(c)     ((c) - '0')
+#define is_digit(c)    (((unsigned)to_digit(c)) <= 9)
+
+/* various globals ---------------------------------------------------*/
+
+__private_extern__ const char __lowercase_hex[17] = "0123456789abcdef?";       /*lint !e784 */
+__private_extern__ const char __uppercase_hex[17] = "0123456789ABCDEF?";       /*lint !e784 */
+
+#define PADSIZE 16
+static char blanks[PADSIZE] =
+        {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
+static char zeroes[PADSIZE] =
+        {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
+
+/* printing and padding functions ------------------------------------*/
+
+#define NIOV 8
+
+struct __printf_io {
+       FILE            *fp;
+       struct __suio   uio;
+       struct __siov   iov[NIOV];
+       struct __siov   *iovp;
+};
+
+static void
+__printf_init(struct __printf_io *io)
+{
+
+       io->uio.uio_iov = io->iovp = &io->iov[0];
+       io->uio.uio_resid = 0;
+       io->uio.uio_iovcnt = 0;
+}
+
+__private_extern__ void
+__printf_flush(struct __printf_io *io)
+{
+
+       __sfvwrite(io->fp, &io->uio);
+       __printf_init(io);
+}
+
+__private_extern__ int
+__printf_puts(struct __printf_io *io, const void *ptr, int len)
+{
+
+
+#if 0
+       if (io->fp->_flags & __SERR)
+               return (0);
+#endif
+       if (len == 0)
+               return (0);
+       io->iovp->iov_base = __DECONST(void *, ptr);
+       io->iovp->iov_len = len;
+       io->uio.uio_resid += len;
+       io->iovp++;
+       io->uio.uio_iovcnt++;
+       if (io->uio.uio_iovcnt >= NIOV)
+               __printf_flush(io);
+       return (len);
+}
+
+__private_extern__ int
+__printf_pad(struct __printf_io *io, int howmany, int zero)
+{
+       int n;
+       const char *with;
+       int ret = 0;
+
+       if (zero)
+               with = zeroes;
+       else
+               with = blanks;
+
+       if ((n = (howmany)) > 0) {
+               while (n > PADSIZE) { 
+                       ret += __printf_puts(io, with, PADSIZE);
+                       n -= PADSIZE;
+               }
+               ret += __printf_puts(io, with, n);
+       }
+       return (ret);
+}
+
+__private_extern__ int
+__printf_out(struct __printf_io *io, const struct printf_info *pi, const void *ptr, int len)
+{
+       int ret = 0;
+
+       if ((!pi->left) && pi->width > len)
+               ret += __printf_pad(io, pi->width - len, pi->pad == '0');
+       ret += __printf_puts(io, ptr, len);
+       if (pi->left && pi->width > len)
+               ret += __printf_pad(io, pi->width - len, pi->pad == '0');
+       return (ret);
+}
+
+
+/* percent handling  -------------------------------------------------*/
+
+__private_extern__ int
+__printf_arginfo_pct(const struct printf_info *pi __unused, size_t n __unused, int *argt __unused)
+{
+
+       return (0);
+}
+
+__private_extern__ int
+__printf_render_pct(struct __printf_io *io, const struct printf_info *pi __unused, const void *const *arg __unused)
+{
+
+       return (__printf_puts(io, "%", 1));
+}
+
+/* 'n' ---------------------------------------------------------------*/
+
+__private_extern__ int
+__printf_arginfo_n(const struct printf_info *pi, size_t n, int *argt)
+{
+
+       assert(n >= 1);
+       argt[0] = PA_POINTER;
+       return (1);
+}
+
+/*
+ * This is a printf_render so that all output has been flushed before it
+ * gets called.
+ */
+
+__private_extern__ int
+__printf_render_n(FILE *io __unused, const struct printf_info *pi, const void *const *arg)
+{
+
+       if (pi->is_char)
+               **((signed char **)arg[0]) = (signed char)pi->sofar;
+       else if (pi->is_short)
+               **((short **)arg[0]) = (short)pi->sofar;
+       else if (pi->is_long)
+               **((long **)arg[0]) = pi->sofar;
+       else if (pi->is_long_double)
+               **((long long **)arg[0]) = pi->sofar;
+       else if (pi->is_intmax)
+               **((intmax_t **)arg[0]) = pi->sofar;
+       else if (pi->is_ptrdiff)
+               **((ptrdiff_t **)arg[0]) = pi->sofar;
+       else if (pi->is_quad)
+               **((quad_t **)arg[0]) = pi->sofar;
+       else if (pi->is_size)
+               **((size_t **)arg[0]) = pi->sofar;
+       else
+               **((int **)arg[0]) = pi->sofar;
+
+       return (0);
+}
+
+/* dynamic array handling  -------------------------------------------------*/
+#define ARRAYDELTA 8
+
+struct array {
+#ifdef XPRINTF_PERF
+       struct array *next;
+#endif /* XPRINTF_PERF */
+       void *data;
+       int itemsize;
+       int max;
+};
+
+#ifdef XPRINTF_PERF
+__private_extern__
+#else /* !XPRINTF_PERF */
+static
+#endif /* !XPRINTF_PERF */
+void
+arrayfree(struct array *a)
+{
+       if(a) free(a->data);
+}
+
+static void *
+arrayget(struct array *a, int i)
+{
+       if (i >= a->max) {
+               int oldsize = a->max * a->itemsize;
+               int newmax = i + ARRAYDELTA;
+               int newsize = newmax * a->itemsize;
+               void *newdata = realloc(a->data, newsize);
+               if(!newdata) return NULL;
+               bzero(newdata + oldsize, newsize - oldsize);
+               a->data = newdata;
+               a->max = newmax;
+       }
+       return a->data + i * a->itemsize;
+}
+
+static struct array *
+arrayinit(struct array *a, int itemsize)
+{
+       a->data = CALLOC(ARRAYDELTA, itemsize);
+       if(!a->data) return NULL;
+       a->itemsize = itemsize;
+       a->max = ARRAYDELTA;
+       return a;
+}
+
+/* dynamic array caching  -------------------------------------------------*/
+/*
+ * Normally, dynamic array structures are created on the stack, and array
+ * itself is freshly allocated, and then freed when no longer needed.  When
+ * the XPRINTF_PERF macro is defined, the dynamic array structures associated
+ * with all-in-one printf variants are not freed, but store in a cache for
+ * later use (dynamic array structures used for compile/execute continue to
+ * be freed after they are no longer needed).  This means there should be
+ * at most one structure in the cached per thread that actually used the
+ * all-in-one printf variant.
+ *
+ * The amount of memory that is cached is fairly small, totally about 1K
+ * for three structures used by a format string using ten conversion
+ * specifiers.  This is too small for purgeable memory.
+ *
+ * However, we do flush these caches in case we every are unable to allocate
+ * memory, and retry the allocation, just in case.
+ */
+#ifdef XPRINTF_PERF
+static OSQueueHead arg_type_queue = OS_ATOMIC_QUEUE_INIT;
+static OSQueueHead printf_info_queue = OS_ATOMIC_QUEUE_INIT;
+static OSQueueHead union_arg_queue = OS_ATOMIC_QUEUE_INIT;
+
+#define DEFINE_DEQUEUE(which, type) \
+static struct array * \
+which ## _dequeue(void) \
+{ \
+       struct array *a = (struct array *)OSAtomicDequeue(&which ## _queue, offsetof(struct array, next)); \
+ \
+       if (a) { \
+               bzero(a->data, a->max * a->itemsize); \
+               return a; \
+       } \
+       a = (struct array *)MALLOC(sizeof(*a)); \
+       if (!a) return NULL; \
+       if (!arrayinit(a, sizeof(type))) { \
+               free(a); \
+               return NULL; \
+       } \
+       return a; \
+}
+
+#define DEFINE_ENQUEUE(which) \
+__private_extern__ void \
+which ## _enqueue(struct array *a) \
+{ \
+       if (!a) return; \
+       OSAtomicEnqueue(&which ## _queue, a, offsetof(struct array, next)); \
+}
+
+#define DEFINE_FLUSH(which) \
+static void \
+which ## _flush(void) \
+{ \
+       struct array *a; \
+       while((a = (struct array *)OSAtomicDequeue(&which ## _queue, offsetof(struct array, next))) != NULL) { \
+               arrayfree(a); \
+               free(a); \
+       } \
+}
+
+DEFINE_DEQUEUE(arg_type, int)
+DEFINE_ENQUEUE(arg_type)
+DEFINE_FLUSH(arg_type)
+DEFINE_DEQUEUE(printf_info, struct printf_info)
+DEFINE_ENQUEUE(printf_info)
+DEFINE_FLUSH(printf_info)
+DEFINE_DEQUEUE(union_arg, union arg)
+DEFINE_ENQUEUE(union_arg)
+DEFINE_FLUSH(union_arg)
+
+static void
+flush_queues(void)
+{
+    arg_type_flush();
+    printf_info_flush();
+    union_arg_flush();
+}
+
+__private_extern__ void *
+xprintf_calloc(size_t count, size_t size)
+{
+    void *x = calloc(count, size);
+    if(!x) {
+       flush_queues();
+       x = calloc(count, size);
+    }
+    return x;
+}
+
+__private_extern__ void *
+xprintf_malloc(size_t size)
+{
+    void *x = malloc(size);
+    if(!x) {
+       flush_queues();
+       x = malloc(size);
+    }
+    return x;
+}
+
+#if 0
+void
+show_queues(void)
+{
+    struct array *a;
+    printf("arg_type:");
+    while((a = (struct array *)OSAtomicDequeue(&arg_type_queue, offsetof(struct array, next))) != NULL) printf("\n%p", a);
+    printf("\nprintf_info:");
+    while((a = (struct array *)OSAtomicDequeue(&printf_info_queue, offsetof(struct array, next))) != NULL) printf("\n%p", a);
+    printf("\nunion_arg:");
+    while((a = (struct array *)OSAtomicDequeue(&union_arg_queue, offsetof(struct array, next))) != NULL) printf("\n%p", a);
+    printf("\n");
+}
+#endif
+#endif /* XPRINTF_PERF */
+
+/* -------------------------------------------------------------------------*/
+
+__private_extern__ int
+__printf_comp(printf_comp_t restrict pc, printf_domain_t restrict domain)
+{
+       struct printf_info      *pi, *pil;
+       const char              *fmt;
+       int                     ch, pii;
+       int                     *argt;
+       int                     nextarg;
+       int                     maxarg;
+       int                     ret = 0;
+       int                     n;
+#ifndef XPRINTF_PERF
+       struct array            piarr, argtarr;
+#endif /* XPRINTF_PERF */
+       struct array            *pa, *aa;
+
+       fmt = pc->fmt;
+       maxarg = 0;
+       nextarg = 1;
+#ifdef XPRINTF_PERF
+       pa = printf_info_dequeue();
+#else /* !XPRINTF_PERF */
+       pa = arrayinit(&piarr, sizeof(*pi));
+#endif /* !XPRINTF_PERF */
+       if (!pa) {
+#ifdef XPRINTF_PERF
+           flush_queues();
+#endif /* XPRINTF_PERF */
+           return EOF;
+       }
+#ifdef XPRINTF_PERF
+       pc->pa = pa;
+       aa = arg_type_dequeue();
+#else /* !XPRINTF_PERF */
+       aa = arrayinit(&argtarr, sizeof(*argt));
+#endif /* !XPRINTF_PERF */
+       if (!aa) {
+               arrayfree(pa);
+#ifdef XPRINTF_PERF
+               free(pa);
+               flush_queues();
+#endif /* XPRINTF_PERF */
+               return EOF;
+       }
+#ifdef XPRINTF_PERF
+       pc->aa = aa;
+#endif /* XPRINTF_PERF */
+       for (pii = 0; ; pii++) {
+               pi = arrayget(pa, pii);
+               if (!pi) {
+                       ret = EOF;
+                       goto error;
+               }
+               pil = pi;
+               if (*fmt == '\0')
+                       break;
+               pil = pi + 1;
+               pi->prec = -1;
+               pi->pad = ' ';
+#ifdef VECTORS
+               pi->vsep = 'X'; /* Illegal value, changed to defaults later. */
+#endif /* VECTORS */
+               pi->begin = pi->end = fmt;
+               while (*fmt != '\0' && *fmt != '%')
+                       pi->end = ++fmt;
+               if (*fmt == '\0') 
+                       break;
+               fmt++;
+               for (;;) {
+                       pi->spec = *fmt;
+                       switch (pi->spec) {
+                       case ' ':
+                               /*-
+                                * ``If the space and + flags both appear, the space
+                                * flag will be ignored.''
+                                *      -- ANSI X3J11
+                                */
+                               if (pi->showsign == 0) {
+                                       pi->space = 1;
+                                       pi->signchar = ' ';
+                               }
+                               fmt++;
+                               continue;
+                       case '#':
+                               pi->alt = 1;
+                               fmt++;
+                               continue;
+#ifdef VECTORS
+                       case ',': case ';': case ':': case '_':
+                               pi->vsep = pi->spec;
+                               fmt++;
+                               continue;
+#endif /* VECTORS */
+                       case '.':
+                               pi->prec = 0;
+                               fmt++;
+                               if (*fmt == '*') {
+                                       fmt++;
+                                       /* Look for *nn$ and deal with it */
+                                       n = 0;
+                                       while (*fmt != '\0' && is_digit(*fmt)) {
+                                               n *= 10;
+                                               n += to_digit(*fmt);
+                                               fmt++;
+                                       }
+                                       if (*fmt == '$') {
+                                               if ((n + 1) > maxarg)
+                                                       maxarg = (n + 1);
+                                               fmt++;
+                                       } else n = nextarg++;
+                                       pi->get_prec = n;
+                                       argt = (int *)arrayget(aa, n);
+                                       if (!argt) {
+                                               ret = EOF;
+                                               goto error;
+                                       }
+                                       *argt = PA_INT;
+                                       continue;
+                               }
+                               while (*fmt != '\0' && is_digit(*fmt)) {
+                                       pi->prec *= 10;
+                                       pi->prec += to_digit(*fmt);
+                                       fmt++;
+                               }
+                               continue;
+                       case '-':
+                               pi->left = 1;
+                               fmt++;
+                               continue;
+                       case '+':
+                               pi->showsign = 1;
+                               pi->signchar = '+';
+                               fmt++;
+                               continue;
+                       case '*':
+                               fmt++;
+                               /* Look for *nn$ and deal with it */
+                               n = 0;
+                               while (*fmt != '\0' && is_digit(*fmt)) {
+                                       n *= 10;
+                                       n += to_digit(*fmt);
+                                       fmt++;
+                               }
+                               if (*fmt == '$') {
+                                       if ((n + 1) > maxarg)
+                                               maxarg = (n + 1);
+                                       fmt++;
+                               } else n = nextarg++;
+                               pi->get_width = n;
+                               argt = (int *)arrayget(aa, n);
+                               if (!argt) {
+                                       ret = EOF;
+                                       goto error;
+                               }
+                               *argt = PA_INT;
+                               continue;
+                       case '%':
+                               fmt++;
+                               break;
+                       case '\'':
+                               pi->group = 1;
+                               fmt++;
+                               continue;
+                       case '0':
+                               /*-
+                                * ``Note that 0 is taken as a flag, not as the
+                                * beginning of a field width.''
+                                *      -- ANSI X3J11
+                                */
+                               pi->pad = '0';
+                               fmt++;
+                               continue;
+                       case '1': case '2': case '3':
+                       case '4': case '5': case '6':
+                       case '7': case '8': case '9':
+                               n = 0;
+                               while (*fmt != '\0' && is_digit(*fmt)) {
+                                       n *= 10;
+                                       n += to_digit(*fmt);
+                                       fmt++;
+                               }
+                               if (*fmt == '$') {
+                                       if (nextarg > maxarg)
+                                               maxarg = nextarg;
+                                       nextarg = n;
+                                       fmt++;
+                               } else 
+                                       pi->width = n;
+                               continue;
+#if 0
+                       case 'D':
+                       case 'O':
+                       case 'U':
+                               pi->spec += ('a' - 'A');
+                               pi->is_intmax = 0;
+                               if (pi->is_long_double || pi->is_quad) {
+                                       pi->is_long = 0;
+                                       pi->is_long_double = 1;
+                               } else {
+                                       pi->is_long = 1;
+                                       pi->is_long_double = 0;
+                               }
+                               fmt++;
+                               break;
+#endif
+                       case 'j':
+                               pi->is_intmax = 1;
+                               fmt++;
+                               continue;
+                       case 'q':
+                               pi->is_long = 0;
+                               pi->is_quad = 1;
+                               fmt++;
+                               continue;
+                       case 'L':
+                               pi->is_long_double = 1;
+                               fmt++;
+                               continue;
+                       case 'h':
+                               fmt++;
+                               if (*fmt == 'h') {
+                                       fmt++;
+                                       pi->is_char = 1;
+                               } else {
+                                       pi->is_short = 1;
+                               }
+                               continue;
+                       case 'l':
+                               fmt++;
+                               if (*fmt == 'l') {
+                                       fmt++;
+                                       pi->is_long_double = 1;
+                                       pi->is_quad = 0;
+                               } else {
+                                       pi->is_quad = 0;
+                                       pi->is_long = 1;
+                               }
+                               continue;
+                       case 't':
+                               pi->is_ptrdiff = 1;
+                               fmt++;
+                               continue;
+                       case 'v':
+#ifdef VECTORS
+                               pi->is_vec = 1;
+#endif /* VECTORS */
+                               fmt++;
+                               continue;
+                       case 'z':
+                               pi->is_size = 1;
+                               fmt++;
+                               continue;
+                       default:
+                               fmt++;
+                               break;
+                       }
+                       if (printf_tbl_in_range(pi->spec)) {
+                               switch(domain->type[printf_tbl_index(pi->spec)]) {
+                               /* ignore PRINTF_DOMAIN_UNUSED until later */
+                               case PRINTF_DOMAIN_FLAG:
+                                       errx(1, "Unexpected flag: %c", pi->spec);
+                               case PRINTF_DOMAIN_GLIBC_API:
+                               case PRINTF_DOMAIN_FBSD_API:
+                                       /*
+                                        * Insure that there are always
+                                        * __PRINTFMAXARG available.
+                                        */
+                                       if (!arrayget(aa, nextarg + __PRINTFMAXARG - 1)) {
+                                               ret = EOF;
+                                               goto error;
+                                       }
+                                       pi->context = domain->tbl[printf_tbl_index(pi->spec)].context;
+                                       pi->loc = pc->loc;
+                                       ch = domain->tbl[printf_tbl_index(pi->spec)].arginfo(
+                                           pi, __PRINTFMAXARG, arrayget(aa, nextarg));
+                                       if (ch > 0)
+                                               pi->arg[0] = (void *)(long)nextarg;
+                                       if (ch > 1)
+                                               pi->arg[1] = (void *)(long)(nextarg + 1);
+                                       nextarg += ch;
+                                       break;
+                               }
+                       }
+                       break;
+               }
+       }
+       if (nextarg > maxarg)
+               maxarg = nextarg;
+       pc->argt = aa->data;
+       pc->pi = pa->data;
+       pc->pil = pil;
+       pc->maxarg = ch = maxarg;
+       if (ch < 1) ch = 1;
+#ifdef XPRINTF_PERF
+       pc->ua = union_arg_dequeue();
+       if (!pc->ua) {
+           ret = EOF;
+           goto error;
+       }
+       if (!arrayget(pc->ua, ch)) {
+           union_arg_enqueue(pc->ua);
+           ret = EOF;
+           goto error;
+       }
+       pc->args = pc->ua->data;
+#else /* !XPRINTF_PERF */
+       pc->args = (union arg *)malloc(ch * sizeof(*pc->args));
+       if (!pc->args) {
+               ret = EOF;
+               goto error;
+       }
+#endif /* !XPRINTF_PERF */
+       for (pi = pc->pi; pi < pil; pi++) {
+               if (pi->arg[0]) pi->arg[0] = &pc->args[(long)pi->arg[0]];
+               if (pi->arg[1]) pi->arg[1] = &pc->args[(long)pi->arg[1]];
+       }
+#if 0
+       fprintf(stderr, "fmt0 <%s>\n", fmt0);
+       fprintf(stderr, "pil %p\n", pil);
+#endif
+       pc->domain = domain;
+
+       return (ret);
+error:
+       arrayfree(pa);
+       arrayfree(aa);
+#ifdef XPRINTF_PERF
+       free(pa);
+       free(aa);
+       flush_queues();
+#endif /* XPRINTF_PERF */
+       return (ret);
+}
+
+__private_extern__ int
+__printf_exec(printf_comp_t restrict pc, FILE * restrict fp, va_list ap)
+{
+       struct printf_info      *pi;
+       int                     ch;
+       int                     ret = 0;
+       int                     n;
+       struct __printf_io      io;
+
+       __printf_init(&io);
+       io.fp = fp;
+
+       for (ch = 1; ch < pc->maxarg; ch++) {
+#if 0
+               fprintf(stderr, "arg %d %x\n", ch, pc->argt[ch]);
+#endif
+               switch(pc->argt[ch]) {
+               case PA_CHAR:
+                       pc->args[ch].intarg = (char)va_arg (ap, int);
+                       break;
+               case PA_INT:
+                       pc->args[ch].intarg = va_arg (ap, int);
+                       break;
+               case PA_INT | PA_FLAG_SHORT:
+                       pc->args[ch].intarg = (short)va_arg (ap, int);
+                       break;
+               case PA_INT | PA_FLAG_LONG:
+                       pc->args[ch].longarg = va_arg (ap, long);
+                       break;
+               case PA_INT | PA_FLAG_INTMAX:
+                       pc->args[ch].intmaxarg = va_arg (ap, intmax_t);
+                       break;
+               case PA_INT | PA_FLAG_QUAD:
+                       pc->args[ch].intmaxarg = va_arg (ap, quad_t);
+                       break;
+               case PA_INT | PA_FLAG_LONG_LONG:
+                       pc->args[ch].intmaxarg = va_arg (ap, long long);
+                       break;
+               case PA_INT | PA_FLAG_SIZE:
+                       pc->args[ch].intmaxarg = va_arg (ap, size_t);
+                       break;
+               case PA_INT | PA_FLAG_PTRDIFF:
+                       pc->args[ch].intmaxarg = (unsigned long)va_arg (ap, ptrdiff_t);
+                       break;
+               case PA_WCHAR:
+                       pc->args[ch].wintarg = va_arg (ap, wint_t);
+                       break;
+               case PA_POINTER:
+                       pc->args[ch].pvoidarg = va_arg (ap, void *);
+                       break;
+               case PA_STRING:
+                       pc->args[ch].pchararg = va_arg (ap, char *);
+                       break;
+               case PA_WSTRING:
+                       pc->args[ch].pwchararg = va_arg (ap, wchar_t *);
+                       break;
+               case PA_DOUBLE:
+#ifndef NO_FLOATING_POINT
+                       pc->args[ch].doublearg = va_arg (ap, double);
+#endif
+                       break;
+               case PA_DOUBLE | PA_FLAG_LONG_DOUBLE:
+#ifndef NO_FLOATING_POINT
+                       pc->args[ch].longdoublearg = va_arg (ap, long double);
+#endif
+                       break;
+#ifdef VECTORS
+               case PA_VECTOR:
+                       pc->args[ch].vectorarg = va_arg (ap, VECTORTYPE);
+                       break;
+#endif /* VECTORS */
+               default:
+                       errx(1, "argtype = %x (fmt = \"%s\")\n",
+                           pc->argt[ch], pc->fmt);
+               }
+       }
+       for (pi = pc->pi; pi < pc->pil; pi++) {
+#if 0
+               fprintf(stderr, "pi %p", pi);
+               fprintf(stderr, " spec '%c'", pi->spec);
+               fprintf(stderr, " args %d",
+                   ((uintptr_t)pi->arg[0] - (uintptr_t)pc->args) / sizeof pc->args[0]);
+               if (pi->width) fprintf(stderr, " width %d", pi->width);
+               if (pi->pad) fprintf(stderr, " pad 0x%x", pi->pad);
+               if (pi->left) fprintf(stderr, " left");
+               if (pi->showsign) fprintf(stderr, " showsign");
+               if (pi->signchar) fprintf(stderr, " signchar 0x%x", pi->signchar);
+               if (pi->prec != -1) fprintf(stderr, " prec %d", pi->prec);
+               if (pi->is_char) fprintf(stderr, " char");
+               if (pi->is_short) fprintf(stderr, " short");
+               if (pi->is_long) fprintf(stderr, " long");
+               if (pi->is_long_double) fprintf(stderr, " long_double");
+               fprintf(stderr, "\n");
+               fprintf(stderr, "\t\"%.*s\"\n", pi->end - pi->begin, pi->begin);
+#endif
+               if (pi->get_width) {
+                       pi->width = pc->args[pi->get_width].intarg;
+                       /*-
+                        * ``A negative field width argument is taken as a
+                        * - flag followed by a positive field width.''
+                        *      -- ANSI X3J11
+                        * They don't exclude field widths read from args.
+                        */
+                       if (pi->width < 0) {
+                               pi->left = 1;
+                               pi->width = -pi->width;
+                       }
+               }
+               if (pi->get_prec) 
+                       pi->prec = pc->args[pi->get_prec].intarg;
+               ret += __printf_puts(&io, pi->begin, pi->end - pi->begin);
+               if (pi->spec) {
+                       if (!printf_tbl_in_range(pi->spec)) goto unused;
+                       switch(pc->domain->type[printf_tbl_index(pi->spec)]) {
+                       case PRINTF_DOMAIN_UNUSED:
+               unused:
+                       {
+                               char unknown = pi->spec;
+                               ret += __printf_out(&io, pi, &unknown, 1);
+                               break;
+                       }
+                       case PRINTF_DOMAIN_GLIBC_API:
+                               __printf_flush(&io);
+                               pi->sofar = ret;
+                               ret += ((printf_function *)pc->domain->tbl[printf_tbl_index(pi->spec)].render)(
+                                   fp, pi, (const void *)pi->arg);
+                               break;
+                       case PRINTF_DOMAIN_FBSD_API:
+                               pi->sofar = ret;
+                               n = ((printf_render *)pc->domain->tbl[printf_tbl_index(pi->spec)].render)(
+                                   &io, pi, (const void *)pi->arg);
+                               if (n < 0)
+                                       io.fp->_flags |= __SERR;
+                               else
+                                       ret += n;
+                               break;
+                       }
+               }
+       }
+       __printf_flush(&io);
+       return (ret);
+}
+
+__private_extern__ int
+__v2printf(printf_comp_t restrict pc, printf_domain_t restrict domain, FILE * restrict fp, locale_t restrict loc, const char * restrict fmt, va_list ap)
+{
+       struct _printf_compiled spc;
+       int ret, saverrno;
+
+       /*
+        * All the printf family (including extensible printf variants) funnel
+        * down to this point.  So we can do common work here, and then fork
+        * out to the appropriate handler.
+        */
+       /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
+       if (prepwrite(fp) != 0) {
+               errno = EBADF;
+               return (EOF);
+       }
+       ORIENT(fp, -1);
+
+       if (pc == XPRINTF_PLAIN) {
+               NORMALIZE_LOCALE(loc);
+#ifdef XPRINTF_DEBUG
+               if (!__use_xprintf)
+#endif
+                                   return __vfprintf(fp, loc, fmt, ap);
+#ifdef XPRINTF_DEBUG
+               xprintf_domain_init();
+               domain = xprintf_domain_global;
+#endif
+       } else if (pc) {
+           pthread_mutex_lock(&pc->mutex);
+           pthread_rwlock_rdlock(&pc->domain->rwlock);
+           ret = __printf_exec(pc, fp, ap);
+           saverrno = errno;
+           pthread_rwlock_unlock(&pc->domain->rwlock);
+           pthread_mutex_unlock(&pc->mutex);
+           errno = saverrno;
+           return ret;
+       }
+       if (!domain) {
+               errno = EINVAL;
+               return EOF;
+       }
+       xprintf_domain_init();
+       bzero(&spc, sizeof(spc));
+       spc.fmt = fmt;
+       DEFAULT_CURRENT_LOCALE(loc);
+       XL_RETAIN(loc);
+       spc.loc = loc;
+       /*
+        * We don't need to lock the printf_comp_t mutex, since the
+        * printf_comp_t was just created on the stack, and is private.
+        */
+       pthread_rwlock_rdlock(&domain->rwlock);
+       if (__printf_comp(&spc, domain) < 0) {
+           saverrno = errno;
+           pthread_rwlock_unlock(&domain->rwlock);
+           XL_RELEASE(loc);
+           errno = saverrno;
+           return EOF;
+       }
+       ret = __printf_exec(&spc, fp, ap);
+       saverrno = errno;
+       pthread_rwlock_unlock(&domain->rwlock);
+       XL_RELEASE(loc);
+
+#ifdef XPRINTF_PERF
+       printf_info_enqueue(spc.pa);
+       arg_type_enqueue(spc.aa);
+       union_arg_enqueue(spc.ua);
+#else /* !XPRINTF_PERF */
+       free(spc.pi);
+       free(spc.argt);
+       free(spc.args);
+#endif /* !XPRINTF_PERF */
+       errno = saverrno;
+       return ret;
+}
+
+extern int      __fflush(FILE *fp);
+
+/*
+ * Helper function for `fprintf to unbuffered unix file': creates a
+ * temporary buffer.  We only work on write-only files; this avoids
+ * worries about ungetc buffers and so forth.
+ */
+static int
+__v3printf(printf_comp_t restrict pc, printf_domain_t restrict domain, FILE * restrict fp, locale_t restrict loc, const char * restrict fmt, va_list ap)
+{
+       int ret;
+       FILE fake;
+       struct __sFILEX extra;
+       unsigned char buf[BUFSIZ];
+
+       fake._extra = &extra;
+       INITEXTRA(&fake);
+
+       /* copy the important variables */
+       fake._flags = fp->_flags & ~__SNBF;
+       fake._file = fp->_file;
+       fake._cookie = fp->_cookie;
+       fake._write = fp->_write;
+       fake._orientation = fp->_orientation;
+       fake._mbstate = fp->_mbstate;
+
+       /* set up the buffer */
+       fake._bf._base = fake._p = buf;
+       fake._bf._size = fake._w = sizeof(buf);
+       fake._lbfsize = 0;      /* not actually used, but Just In Case */
+
+       /* do the work, then copy any error status */
+       ret = __v2printf(pc, domain, &fake, loc, fmt, ap);
+       if (ret >= 0 && __fflush(&fake))
+               ret = EOF;
+       if (fake._flags & __SERR)
+               fp->_flags |= __SERR;
+       return (ret);
+}
+
+__private_extern__ int
+__xvprintf(printf_comp_t restrict pc, printf_domain_t restrict domain, FILE * restrict fp, locale_t restrict loc, const char * restrict fmt0, va_list ap)
+{
+       int ret;
+
+       /* optimise fprintf(stderr) (and other unbuffered Unix files) */
+       if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
+           fp->_file >= 0)
+               ret = __v3printf(pc, domain, fp, loc, fmt0, ap);
+       else
+               ret = __v2printf(pc, domain, fp, loc, fmt0, ap);
+       return ret;
+}
+
+/* extending ---------------------------------------------------------*/
+
+// No global domain support
+#if 0
+int
+register_printf_function(int spec, printf_function *render, printf_arginfo_function *arginfo)
+{
+       return register_printf_domain_function(NULL, spec, render, arginfo);
+}
+
+__private_extern__ int
+register_printf_render(int spec, printf_render *render, printf_arginfo_function *arginfo)
+{
+       return register_printf_domain_render(NULL, spec, render, arginfo);
+}
+
+int
+register_printf_render_std(const char *specs)
+{
+       return register_printf_domain_render_std(NULL, specs);
+}
+#endif
+
+#ifdef VECTORS
+/* vector support ----------------------------------------------------*/
+
+#define PRINTVECTOR(_io, _pi, _arg, _cnt, _type, _elem, _render, _ret) { \
+       int i; \
+       _type a, *ap; \
+       a = (_type)(_arg)->_elem[0]; \
+       ap = &a; \
+       (_ret) += _render((_io), (_pi), (const void *)&ap); \
+       for(i = 1; i < (_cnt); i++) { \
+               (_ret) += __printf_puts((_io), (_pi)->begin, (_pi)->end - (_pi)->begin); \
+               a = (_type)(_arg)->_elem[i]; \
+               (_ret) += _render((_io), (_pi), (const void *)&ap); \
+       } \
+}
+
+#define PRINTVECTOR_P(_io, _pi, _arg, _cnt, _elem, _render, _ret) { \
+       int i; \
+       void * a, *ap; \
+       a = (void *)(uintptr_t)(_arg)->_elem[0]; \
+       ap = &a; \
+       (_ret) += _render((_io), (_pi), (const void *)&ap); \
+       for(i = 1; i < (_cnt); i++) { \
+               (_ret) += __printf_puts((_io), (_pi)->begin, (_pi)->end - (_pi)->begin); \
+               a = (void *)(uintptr_t)(_arg)->_elem[i]; \
+               (_ret) += _render((_io), (_pi), (const void *)&ap); \
+       } \
+}
+
+__private_extern__ int
+__xprintf_vector(struct __printf_io *io, const struct printf_info *pi, const void *const *arg)
+{
+       char vsep;      /* Vector separator character. */
+       const union arg *argp;
+       int ret = 0;
+       struct printf_info info = *pi;
+
+       argp = arg[0];
+       vsep = pi->vsep;
+       if (vsep == 'X') {
+               if (pi->spec == 'c')
+                       vsep = '\0';
+               else
+                       vsep = ' ';
+       }
+       info.begin = info.end = &vsep;
+       if (vsep) info.end++;
+       info.is_vec = 0;
+
+       if (pi->is_short) {
+               if (pi->spec == 'p') {
+                       PRINTVECTOR_P(io, &info, argp, 8, vushortarg, __printf_render_ptr, ret);
+               } else {
+                       PRINTVECTOR(io, &info, argp, 8, unsigned int, vushortarg, __printf_render_int, ret);
+               }
+       } else if (pi->is_long) {
+               info.is_long = 0;
+               if (pi->spec == 'p') {
+                       PRINTVECTOR_P(io, &info, argp, 4, vuintarg, __printf_render_ptr, ret);
+               } else {
+                       PRINTVECTOR(io, &info, argp, 4, unsigned int, vuintarg, __printf_render_int, ret);
+               }
+#ifdef V64TYPE
+       } else if (pi->is_long_double) {
+               switch (pi->spec) {
+               case 'a':
+               case 'A':
+               case 'e':
+               case 'E':
+               case 'f':
+               case 'g':
+               case 'G':
+                       info.is_long_double = 0;
+                       PRINTVECTOR(io, &info, argp, 2, double, vdoublearg, __printf_render_float, ret);
+                       break;
+               case 'p':
+                       info.is_long_double = 0;
+                       PRINTVECTOR_P(io, &info, argp, 2, vulonglongarg, __printf_render_ptr, ret);
+                       break;
+               case 'd':
+               case 'i':
+               case 'u':
+               case 'o':
+               case 'x':
+               case 'X':
+                       PRINTVECTOR(io, &info, argp, 2, unsigned long long, vulonglongarg, __printf_render_int, ret);
+                       break;
+               default:
+                       /*
+                        * The default case should never
+                        * happen.
+                        */
+               case 'c':
+                       info.is_long_double = 0;
+                       PRINTVECTOR(io, &info, argp, 16, unsigned int, vuchararg, __printf_render_chr, ret);
+               }
+#endif /* V64TYPE */
+       } else {
+               switch (pi->spec) {
+               case 'a':
+               case 'A':
+               case 'e':
+               case 'E':
+               case 'f':
+               case 'g':
+               case 'G':
+                       PRINTVECTOR(io, &info, argp, 4, double, vfloatarg, __printf_render_float, ret);
+                       break;
+               default:
+                       /*
+                        * The default case should never
+                        * happen.
+                        */
+               case 'p':
+                       PRINTVECTOR_P(io, &info, argp, 16, vuchararg, __printf_render_ptr, ret);
+                       break;
+               case 'd':
+               case 'i':
+               case 'u':
+               case 'o':
+               case 'x':
+               case 'X':
+                       info.is_char = 1;
+                       PRINTVECTOR(io, &info, argp, 16, unsigned int, vuchararg, __printf_render_int, ret);
+                       break;
+               case 'c':
+                       PRINTVECTOR(io, &info, argp, 16, unsigned int, vuchararg, __printf_render_chr, ret);
+               }
+       }
+       return ret;
+}
+#endif /* VECTORS */