2 libcore: library for basic data structures and algorithms.
3 Copyright (C) 2005 IPD Goos, Universit"at Karlsruhe, Germany
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 * A customizable printf clone.
23 * @author Sebastian Hack
37 #include "lc_printf.h"
42 /* printf implementation */
44 typedef struct lc_arg_t {
45 struct lc_arg_t *next;
49 const lc_arg_handler_t *handler;
53 set *args; /**< Map for named arguments. */
54 lc_arg_t *lower[26]; /**< Map for lower conversion specifiers. */
55 lc_arg_t *upper[26]; /**< Map for upper conversion specifiers. */
58 /** The default argument environment. */
59 static lc_arg_env_t *default_env = NULL;
61 static inline lc_arg_env_t *_lc_arg_get_default_env(void)
64 default_env = lc_arg_add_std(lc_arg_new_env());
69 lc_arg_env_t *lc_arg_get_default_env(void)
71 return _lc_arg_get_default_env();
74 static int lc_arg_cmp(const void *p1, const void *p2, size_t size)
76 const lc_arg_t *a1 = (const lc_arg_t*)p1;
77 const lc_arg_t *a2 = (const lc_arg_t*)p2;
79 return strcmp(a1->name, a2->name);
83 lc_arg_env_t *lc_arg_new_env(void)
85 lc_arg_env_t *env = XMALLOCZ(lc_arg_env_t);
86 env->args = new_set(lc_arg_cmp, 16);
90 void lc_arg_free_env(lc_arg_env_t *env)
96 int lc_arg_register(lc_arg_env_t *env, const char *name, char letter, const lc_arg_handler_t *handler)
101 lc_arg_t **map = NULL;
105 arg.handler = handler;
107 if (isupper((unsigned char)letter)) {
111 else if (islower((unsigned char)letter)) {
116 ent = set_insert(lc_arg_t, env->args, &arg, sizeof(arg), hash_str(name));
118 if (ent && base != 0)
119 map[letter - base] = ent;
124 void lc_arg_unregister(lc_arg_env_t *env, const char *name)
130 int lc_arg_append(lc_appendable_t *app, const lc_arg_occ_t *occ, const char *str, size_t len)
134 /* Set the padding to zero, if the zero is given and we are not left
135 * justified. (A minus overrides the zero). See printf(3). */
136 if (!occ->flag_minus && occ->flag_zero)
139 return lc_appendable_snwadd(app, str, len, MAX(0, occ->width), occ->flag_minus, pad);
143 static int dispatch_snprintf(char *buf, size_t len, const char *fmt,
144 int lc_arg_type, const lc_arg_value_t *val)
148 switch (lc_arg_type) {
149 #define LC_ARG_TYPE(type,name,va_type) \
150 case lc_arg_type_ ## name: res = snprintf(buf, len, fmt, val->v_ ## name); break;
151 #include "lc_printf_arg_types.def"
158 static char *make_fmt(char *buf, size_t len, const lc_arg_occ_t *occ)
167 if (occ->precision > 0)
168 snprintf(prec, sizeof(prec), ".%d", occ->precision);
171 snprintf(width, sizeof(width), "%d", occ->width);
173 assert(occ->modifier && "modifier must not be NULL");
174 strncpy(mod, occ->modifier, sizeof(mod) - 1);
175 mod[MIN(sizeof(mod) - 1, occ->modifier_length)] = '\0';
178 /* work-around for buggy mscrt not supporting z, j, and t modifier */
179 if (occ->modifier_length == 1) {
181 if (sizeof(size_t) == sizeof(int))
183 if (sizeof(size_t) == sizeof(__int64)) {
189 } else if (mod[0] == 't') {
190 if (sizeof(ptrdiff_t) == sizeof(int))
192 if (sizeof(ptrdiff_t) == sizeof(__int64)) {
198 } else if (mod[0] == 'j') {
199 if (sizeof(intmax_t) == sizeof(int))
201 if (sizeof(intmax_t) == sizeof(__int64)) {
208 } else if (occ->modifier_length == 2) {
209 if (mod[0] == 'h' && mod[1] == 'h') {
210 /* no support for char in mscrt, but we can safely ignore it
211 * because the size is handled by the argument reader code */
216 snprintf(buf, len, "%%%s%s%s%s%s%s%s%s%c",
217 occ->flag_space ? "#" : "",
218 occ->flag_hash ? "#" : "",
219 occ->flag_plus ? "+" : "",
220 occ->flag_minus ? "-" : "",
221 occ->flag_zero ? "0" : "",
223 mod, occ->conversion);
229 * Standard argument handler.
231 static int std_get_lc_arg_type(const lc_arg_occ_t *occ)
233 size_t modlen = occ->modifier_length;
235 /* check, if the type can be derived from the modifier */
237 const char *mod = occ->modifier;
240 return modlen > 1 && mod[1] == 'h' ? lc_arg_type_char : lc_arg_type_short;
242 return modlen > 1 && mod[1] == 'l' ? lc_arg_type_long_long : lc_arg_type_long;
243 #define TYPE_CASE(letter,type) case letter: return lc_arg_type_ ## type
244 TYPE_CASE('j', intmax_t);
245 TYPE_CASE('z', size_t);
246 TYPE_CASE('t', ptrdiff_t);
247 TYPE_CASE('L', long_double);
252 /* The type is given by the conversion specifier and cannot be
253 * determined from the modifier. */
254 switch (occ->conversion) {
261 return lc_arg_type_double;
265 return lc_arg_type_ptr;
267 return lc_arg_type_int;
271 static int std_emit(lc_appendable_t *app, const lc_arg_occ_t *occ, const lc_arg_value_t *val)
276 make_fmt(fmt, sizeof(fmt), occ);
278 switch (occ->conversion) {
280 /* Store the number of written characters in the given
281 * int pointer location */
284 int *num = (int*)val->v_ptr;
285 *num = (int)app->written;
289 /* strings are dumped directly, since they can get really big. A
290 * buffer of 128 letters for all other types should be enough. */
293 const char *str = (const char*)val->v_ptr;
294 res = lc_arg_append(app, occ, str, strlen(str));
300 int len = MAX(128, occ->width + 1);
301 char *buf = XMALLOCN(char, len);
302 res = dispatch_snprintf(buf, len, fmt, occ->lc_arg_type, val);
303 res = lc_appendable_snadd(app, buf, res);
311 static const lc_arg_handler_t std_handler = {
316 lc_arg_env_t *lc_arg_add_std(lc_arg_env_t *env)
318 lc_arg_register(env, "std:c", 'c', &std_handler);
319 lc_arg_register(env, "std:i", 'i', &std_handler);
320 lc_arg_register(env, "std:d", 'd', &std_handler);
321 lc_arg_register(env, "std:o", 'o', &std_handler);
322 lc_arg_register(env, "std:u", 'u', &std_handler);
323 lc_arg_register(env, "std:x", 'x', &std_handler);
324 lc_arg_register(env, "std:X", 'X', &std_handler);
326 lc_arg_register(env, "std:e", 'e', &std_handler);
327 lc_arg_register(env, "std:E", 'E', &std_handler);
328 lc_arg_register(env, "std:f", 'f', &std_handler);
329 lc_arg_register(env, "std:F", 'F', &std_handler);
330 lc_arg_register(env, "std:g", 'g', &std_handler);
331 lc_arg_register(env, "std:G", 'G', &std_handler);
333 lc_arg_register(env, "std:s", 's', &std_handler);
334 lc_arg_register(env, "std:p", 'p', &std_handler);
335 lc_arg_register(env, "std:n", 'n', &std_handler);
340 static char *read_int(const char *s, int *value)
343 int res = (int) strtol(s, &endptr, 10);
344 *value = endptr == s ? -1 : res;
348 /* Generic printf() function. */
350 int lc_evpprintf(const lc_arg_env_t *env, lc_appendable_t *app, const char *fmt, va_list args)
354 const char *last = fmt + strlen(fmt);
356 /* Find the first % */
357 s = strchr(fmt, '%');
359 /* Emit the text before the first % was found */
360 lc_appendable_snadd(app, fmt, (s ? s : last) - fmt);
365 const lc_arg_t *arg = NULL;
369 /* We must be at a '%' */
372 /* Reset the occurrence structure */
373 memset(&occ, 0, sizeof(occ));
375 /* Eat all flags and set the corresponding flags in the occ struct */
376 for (++s; strchr("#0-+", *s); ++s) {
396 /* Read the width if given */
397 s = read_int(s, &occ.width);
401 /* read the precision if given */
404 s = read_int(s + 1, &precision);
406 /* Negative or lacking precision after a '.' is treated as
408 occ.precision = MAX(0, precision);
412 * Now, we can either have:
413 * - a named argument like {node}
414 * - some modifiers followed by a conversion specifier
415 * - or some other character, which ends this format invalidly
421 res += lc_appendable_chadd(app, '%');
425 const char *named = ++s;
427 /* Read until the closing brace or end of the string. */
428 for (ch = *s; ch != '}' && ch != '\0'; ch = *++s) {
432 size_t n = s - named;
436 name = (char*) malloc(sizeof(char) * (n + 1));
437 memcpy(name, named, sizeof(char) * n);
441 arg = set_find(lc_arg_t, env->args, &tmp, sizeof(tmp), hash_str(named));
443 occ.modifier_length = 0;
445 /* Set the conversion specifier of the occurrence to the
446 * letter specified in the argument description. */
448 occ.conversion = arg->letter;
452 /* If we ended with a closing brace, move the current
453 * pointer after it, since it is not to be dumped. */
464 /* Read, as long there are letters */
465 while (isalpha((unsigned char)ch) && !arg) {
467 lc_arg_t * const *map = env->lower;
469 /* If uppercase, select the uppercase map from the environment */
470 if (isupper((unsigned char)ch)) {
475 if (map[ch - base] != NULL) {
477 occ.modifier_length = s - mod;
479 arg = map[ch - base];
487 /* Call the handler if an argument was determined */
488 if (arg != NULL && arg->handler != NULL) {
489 const lc_arg_handler_t *handler = arg->handler;
491 /* Let the handler determine the type of the argument based on the
492 * information gathered. */
493 occ.lc_arg_type = handler->get_lc_arg_type(&occ);
495 /* Store the value according to argument information */
496 switch (occ.lc_arg_type) {
497 #define LC_ARG_TYPE(type,name,va_type) \
498 case lc_arg_type_ ## name: val.v_ ## name = va_arg(args, va_type); break;
499 #include "lc_printf_arg_types.def"
503 /* Finally, call the handler. */
504 res += handler->emit(app, &occ, &val);
509 res += lc_appendable_snadd(app, old, (s ? s : last) - old);
515 /* Convenience implementations */
517 int lc_epprintf(const lc_arg_env_t *env, lc_appendable_t *app, const char *fmt, ...)
522 res = lc_evpprintf(env, app, fmt, args);
527 int lc_pprintf(lc_appendable_t *app, const char *fmt, ...)
532 res = lc_vpprintf(app, fmt, args);
537 int lc_vpprintf(lc_appendable_t *app, const char *fmt, va_list args)
539 return lc_evpprintf(_lc_arg_get_default_env(), app, fmt, args);
542 int lc_eprintf(const lc_arg_env_t *env, const char *fmt, ...)
547 res = lc_efprintf(env, stdout, fmt, args);
552 int lc_esnprintf(const lc_arg_env_t *env, char *buf, size_t len, const char *fmt, ...)
557 res = lc_evsnprintf(env, buf, len, fmt, args);
562 int lc_efprintf(const lc_arg_env_t *env, FILE *file, const char *fmt, ...)
567 res = lc_evfprintf(env, file, fmt, args);
572 int lc_eoprintf(const lc_arg_env_t *env, struct obstack *obst, const char *fmt, ...)
577 res = lc_evoprintf(env, obst, fmt, args);
582 int lc_evprintf(const lc_arg_env_t *env, const char *fmt, va_list args)
584 return lc_evfprintf(env, stdout, fmt, args);
587 int lc_evsnprintf(const lc_arg_env_t *env, char *buf, size_t len, const char *fmt, va_list args)
592 lc_appendable_init(&app, lc_appendable_string, buf, len);
593 res = lc_evpprintf(env, &app, fmt, args);
594 lc_appendable_finish(&app);
598 int lc_evfprintf(const lc_arg_env_t *env, FILE *f, const char *fmt, va_list args)
603 lc_appendable_init(&app, lc_appendable_file, f, 0);
604 res = lc_evpprintf(env, &app, fmt, args);
605 lc_appendable_finish(&app);
609 int lc_evoprintf(const lc_arg_env_t *env, struct obstack *obst, const char *fmt, va_list args)
614 lc_appendable_init(&app, lc_appendable_obstack, obst, 0);
615 res = lc_evpprintf(env, &app, fmt, args);
616 lc_appendable_finish(&app);
621 int lc_printf(const char *fmt, ...)
626 res = lc_vprintf(fmt, args);
631 int lc_snprintf(char *buf, size_t len, const char *fmt, ...)
636 res = lc_vsnprintf(buf, len, fmt, args);
641 int lc_fprintf(FILE *f, const char *fmt, ...)
646 res = lc_vfprintf(f, fmt, args);
651 int lc_oprintf(struct obstack *obst, const char *fmt, ...)
656 res = lc_voprintf(obst, fmt, args);
662 int lc_vprintf(const char *fmt, va_list args)
664 return lc_evprintf(_lc_arg_get_default_env(), fmt, args);
667 int lc_vsnprintf(char *buf, size_t len, const char *fmt, va_list args)
669 return lc_evsnprintf(_lc_arg_get_default_env(), buf, len, fmt, args);
672 int lc_vfprintf(FILE *f, const char *fmt, va_list args)
674 return lc_evfprintf(_lc_arg_get_default_env(), f, fmt, args);
677 int lc_voprintf(struct obstack *obst, const char *fmt, va_list args)
679 return lc_evoprintf(_lc_arg_get_default_env(), obst, fmt, args);