1/*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <printf.h>
25#include <pthread.h>
26#include <stdlib.h>
27#include <errno.h>
28#include "xprintf_domain.h"
29#include "xprintf_private.h"
30
31/* These are flag characters and can never be used as conversion specifiers */
32static const char _printf_tbl_flags[] = "#$'*+,-.0123456789:;L_hjlqtvz";
33
34struct _printf_tbl_defaults_fbsd {
35    const char *spec;
36    printf_arginfo_function *arginfo;
37    printf_render *render;
38};
39static struct _printf_tbl_defaults_fbsd _printf_tbl_defaults_fbsd[] = {
40    {"%",		__printf_arginfo_pct,	__printf_render_pct},
41    {"AEFGaefg",	__printf_arginfo_float,	__printf_render_float},
42    {"Cc",		__printf_arginfo_chr,	__printf_render_chr},
43    {"DOUXdioux",	__printf_arginfo_int,	__printf_render_int},
44    {"Ss",		__printf_arginfo_str,	__printf_render_str},
45    {"p",		__printf_arginfo_ptr,	__printf_render_ptr},
46};
47struct _printf_tbl_defaults_glibc {
48    const char *spec;
49    printf_arginfo_function *arginfo;
50    printf_function *render;
51};
52static struct _printf_tbl_defaults_glibc _printf_tbl_defaults_glibc[] = {
53    {"n",		__printf_arginfo_n,	__printf_render_n},
54};
55
56static printf_domain_t xprintf_domain_default;
57#ifdef XPRINTF_DEBUG
58__private_extern__ printf_domain_t xprintf_domain_global = NULL;
59#endif
60
61__private_extern__ pthread_once_t __xprintf_domain_once = PTHREAD_ONCE_INIT;
62
63__private_extern__ void
64__xprintf_domain_init(void)
65{
66    xprintf_domain_default = (printf_domain_t)calloc(
67#ifdef XPRINTF_DEBUG
68						     2,
69#else
70						     1,
71#endif
72						     sizeof(*xprintf_domain_default));
73    if(xprintf_domain_default == NULL)
74	LIBC_ABORT("No memory");
75
76    xprintf_domain_default->rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER;
77    {
78	const char *cp;
79	for(cp = _printf_tbl_flags; *cp; cp++)
80	    xprintf_domain_default->type[printf_tbl_index(*cp)] = PRINTF_DOMAIN_FLAG;
81    }
82    {
83	struct _printf_tbl_defaults_fbsd *d = _printf_tbl_defaults_fbsd;
84	int n = sizeof(_printf_tbl_defaults_fbsd) / sizeof(*_printf_tbl_defaults_fbsd);
85	for(; n > 0; d++, n--) {
86	    for(const char *cp = d->spec; *cp; cp++) {
87		xprintf_domain_default->type[printf_tbl_index(*cp)] = PRINTF_DOMAIN_FBSD_API;
88		xprintf_domain_default->tbl[printf_tbl_index(*cp)] = (struct _printf_tbl){d->arginfo, d->render, NULL};
89	    }
90	}
91    }
92    {
93	struct _printf_tbl_defaults_glibc *d = _printf_tbl_defaults_glibc;
94	int n = sizeof(_printf_tbl_defaults_glibc) / sizeof(*_printf_tbl_defaults_glibc);
95	for(; n > 0; d++, n--) {
96	    for(const char *cp = d->spec; *cp; cp++) {
97		xprintf_domain_default->type[printf_tbl_index(*cp)] = PRINTF_DOMAIN_GLIBC_API;
98		xprintf_domain_default->tbl[printf_tbl_index(*cp)] = (struct _printf_tbl){d->arginfo, d->render, NULL};
99	    }
100	}
101    }
102#ifdef XPRINTF_DEBUG
103    xprintf_domain_global = xprintf_domain_default + 1;
104    *xprintf_domain_global = *xprintf_domain_default;
105#endif
106}
107
108printf_domain_t
109copy_printf_domain(printf_domain_t src)
110{
111    printf_domain_t restrict copy;
112
113    if(!src) {
114	errno = EINVAL;
115	return NULL;
116    }
117    copy = (printf_domain_t)MALLOC(sizeof(*copy));
118    if(!copy) return NULL;
119    xprintf_domain_init();
120    pthread_rwlock_rdlock(&src->rwlock);
121    *copy = *src;
122    pthread_rwlock_unlock(&src->rwlock);
123    copy->rwlock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER;
124    return copy;
125}
126
127void
128free_printf_domain(printf_domain_t d)
129{
130    if(!d) return;
131    pthread_rwlock_destroy(&d->rwlock);
132    free(d);
133}
134
135printf_domain_t
136new_printf_domain(void)
137{
138    printf_domain_t restrict d;
139
140    xprintf_domain_init();
141
142    d = (printf_domain_t)MALLOC(sizeof(*d));
143    if(!d) return NULL;
144    *d = *xprintf_domain_default;
145    return d;
146}
147
148int
149register_printf_domain_function(printf_domain_t d, int spec, printf_function *render, printf_arginfo_function *arginfo, void *context)
150{
151    xprintf_domain_init();
152
153    if(!d || !printf_tbl_in_range(spec)) {
154	errno = EINVAL;
155	return -1;
156    }
157    xprintf_domain_init();
158
159    switch(d->type[printf_tbl_index(spec)]) {
160    case PRINTF_DOMAIN_FLAG:
161	errno = EINVAL;
162	return -1;
163    default:
164	pthread_rwlock_wrlock(&d->rwlock);
165	if(!render || !arginfo) {
166	    d->type[printf_tbl_index(spec)] = PRINTF_DOMAIN_UNUSED;
167	} else {
168	    d->type[printf_tbl_index(spec)] = PRINTF_DOMAIN_GLIBC_API;
169	    d->tbl[printf_tbl_index(spec)] = (struct _printf_tbl){arginfo, render, context};
170	}
171	pthread_rwlock_unlock(&d->rwlock);
172    }
173
174    return 0;
175}
176
177__private_extern__ int
178register_printf_domain_render(printf_domain_t d, int spec, printf_render *render, printf_arginfo_function *arginfo)
179{
180    xprintf_domain_init();
181
182    if(!d || !printf_tbl_in_range(spec)) {
183	errno = EINVAL;
184	return -1;
185    }
186    xprintf_domain_init();
187
188    switch(d->type[printf_tbl_index(spec)]) {
189    case PRINTF_DOMAIN_FLAG:
190	errno = EINVAL;
191	return -1;
192    default:
193	pthread_rwlock_wrlock(&d->rwlock);
194	if(!render || !arginfo) {
195	    d->type[printf_tbl_index(spec)] = PRINTF_DOMAIN_UNUSED;
196	} else {
197	    d->type[printf_tbl_index(spec)] = PRINTF_DOMAIN_FBSD_API;
198	    d->tbl[printf_tbl_index(spec)] = (struct _printf_tbl){arginfo, render, NULL};
199	}
200	pthread_rwlock_unlock(&d->rwlock);
201    }
202
203    return 0;
204}
205
206int
207register_printf_domain_render_std(printf_domain_t d, const char *specs)
208{
209    int ret = 0;
210
211    for (; *specs != '\0'; specs++) {
212	switch (*specs) {
213	case 'H':
214	    ret = register_printf_domain_render(d, *specs,
215		__printf_render_hexdump,
216		__printf_arginfo_hexdump);
217	    break;
218	case 'M':
219	    ret = register_printf_domain_render(d, *specs,
220		__printf_render_errno,
221		__printf_arginfo_errno);
222	    break;
223	case 'Q':
224	    ret = register_printf_domain_render(d, *specs,
225		__printf_render_quote,
226		__printf_arginfo_quote);
227	    break;
228	case 'T':
229	    ret = register_printf_domain_render(d, *specs,
230		__printf_render_time,
231		__printf_arginfo_time);
232	    break;
233	case 'V':
234	    ret = register_printf_domain_render(d, *specs,
235		__printf_render_vis,
236		__printf_arginfo_vis);
237	    break;
238	default:
239	    errno = EINVAL;
240	    return (-1);
241	}
242	if(ret < 0) return ret;
243    }
244    return (0);
245}
246