diff options
Diffstat (limited to 'snprintfv/printf.c')
-rw-r--r-- | snprintfv/printf.c | 1568 |
1 files changed, 1568 insertions, 0 deletions
diff --git a/snprintfv/printf.c b/snprintfv/printf.c new file mode 100644 index 0000000..58927a9 --- /dev/null +++ b/snprintfv/printf.c @@ -0,0 +1,1568 @@ +/* -*- Mode: C -*- */ + +/* printf.c --- printf clone for argv arrays + * Copyright (C) 1998, 1999, 2000, 2002 Gary V. Vaughan + * Originally by Gary V. Vaughan, 1998 + * This file is part of Snprintfv + * + * Snprintfv is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Snprintfv program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception to the GNU General Public License, if you + * distribute this file as part of a program that also links with and + * uses the libopts library from AutoGen, you may include it under + * the same distribution terms used by the libopts library. + */ + +/* Code: */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <unistd.h> /* for the write(2) call */ + +#ifdef HAVE_ASSERT_H +# include <assert.h> +#else +# define assert(_e) +#endif + +#define COMPILING_PRINTF_C +#include "printf.h" + +#ifdef WITH_DMALLOC +#include <dmalloc.h> +#endif + +#include "filament.h" +#include "stream.h" +#include "mem.h" + +#ifdef SNV_LIBRARY_BUILD +# include "dl.h" +#else + +# ifndef HAVE_STRTOUL +# include "strtoul.c" +# endif +#endif /* SNV_LIBRARY_BUILD */ + +#define EOS '\0' +#define SNV_CHAR_SPEC '%' +#define SNV_ESC_SPEC '\\' + +/* Functions to manage mapping of spec chars to handlers. */ +SNV_INLINE unsigned spec_hash (unsigned spec); +SNV_INLINE void spec_init (void); +SNV_INLINE spec_entry *spec_lookup (unsigned spec); +static void spec_insert (spec_entry * pentry); +static int do_printfv (STREAM *stream, const char *format, + union printf_arg const args[]); + +/* FIXME: We are assuming an ASCII character set where all the + printable characters are between SPACE and DEL. */ +#define ASCII_DEL (int)'\177' +#define ASCII_SPACE (int)' ' + +#define IS_MODIFIER(spec) (!((spec)->fmt)) + +/* TODO: This is not thread-safe. Change the API to pass the spec_table + in as the first parameter to the functions which use it? */ +static spec_entry *spec_table[ASCII_DEL - ASCII_SPACE]; + +/* TODO: This is not thread-safe as well. */ +static char *printf_last_error; + +SNV_INLINE unsigned +spec_hash (unsigned spec) +{ + return (spec & ASCII_DEL) - ASCII_SPACE; +} + +/* Register all of the functions in INIT_SPEC_TABLE. */ +static void +spec_init (void) +{ + static bool is_init = false; + + if (!is_init) + { + extern spec_entry snv_default_spec_table[]; + unsigned ix; + + memset (spec_table, 0, sizeof (spec_table)); + for (ix = 0; snv_default_spec_table[ix].spec_key != EOS; ix++) + { + unsigned hash = spec_hash (snv_default_spec_table[ix].spec_key); + spec_table[hash] = snv_default_spec_table + ix; + } + + is_init = true; + } +} + +/* Insert PENTRY, a new handler, into SPEC_TABLE. */ +SNV_INLINE void +spec_insert (spec_entry *pentry) +{ + unsigned hash = spec_hash (pentry->spec_key); + spec_init (); + spec_table[hash] = pentry; +} + +/* Lookup and return the SPEC_TABLE entry for SPEC. */ +SNV_INLINE spec_entry * +spec_lookup (unsigned spec) +{ + unsigned hash = spec_hash (spec); + spec_init (); + return spec_table[hash]; +} + +/** + * register_printf_function: printf.h + * @spec: the character which will trigger @func, cast to an unsigned int. + * @fmt: the handler function to actually print the arguments to the specifier + * @arg: the handler function to tell %printf about the types of the arguments + * to the specifier + * + * Register the pair made of @fmt and @arg, so that it is called + * when @spec is encountered in a format string. + * + * Return value: + * Returns %NULL if @func was not successfully registered, a + * %spec_entry with the information on the function if it was. + **/ +spec_entry * +register_printf_function (unsigned spec, printf_function *fmt, + printf_arginfo_function *arg) +{ + spec_entry *new, *old; + old = spec_lookup (spec); + if (old && IS_MODIFIER (old)) + return NULL; + + if (!fmt || !spec) + return NULL; + + new = snv_new (spec_entry, 1); + new->spec_key = spec; + new->fmt = fmt; + new->arg = arg; + new->user = NULL; + + spec_insert (new); + + return new; +} + +static int +call_argtype_function ( + struct printf_info *const pinfo, + int **argtypes, + spec_entry *spec) +{ + int n; + int argindex = (pinfo->dollar && !IS_MODIFIER (spec)) + ? pinfo->dollar - 1 + : pinfo->argindex; + + int save_argindex = pinfo->argindex; + int save_state = pinfo->state; + char const *save_format = pinfo->format; + + if (!spec->arg) + { + n = 1; + if (pinfo->argc <= argindex) + { + /* + * "argtypes" points to a pointer of an array of int values. + * Here, we ensure that there are "argindex + 1" entries in + * that array. + */ + *argtypes = snv_renew (int, *argtypes, argindex + 1); + + /* + * IF there are more entries that follow the current argument + * index, then we will clobber all the entries that follow. + * The size of these entries is the size of the array elements, + * not the size of the pointer to the array elements. + */ + if (pinfo->argc < argindex) + memset(*argtypes + pinfo->argc, PA_UNKNOWN, + (argindex - pinfo->argc) * sizeof(**argtypes)); + + pinfo->argc = argindex + 1; + } + + (*argtypes) [argindex] = spec->type; + } + + else + { + pinfo->spec = (unsigned)*(pinfo->format); + pinfo->extra = spec->user; + pinfo->type = spec->type; + + if (pinfo->argc > argindex) + n = spec->arg(pinfo, (size_t) (pinfo->argc - argindex), + *argtypes + argindex); + else + n = spec->arg(pinfo, (size_t)0, NULL); + + if (n < 0) + return n; + if (argindex + n > pinfo->argc) + { + int new_ct = argindex + n; + *argtypes = snv_renew (int, *argtypes, new_ct); + memset(*argtypes + pinfo->argc, PA_UNKNOWN, + (new_ct - pinfo->argc) * sizeof(**argtypes)); + pinfo->argc = argindex + n; + /* Call again... */ + pinfo->argindex = save_argindex; + pinfo->format = save_format; + pinfo->state = save_state; + pinfo->spec = (unsigned)*(pinfo->format); + pinfo->extra = spec->user; + pinfo->type = spec->type; + n = spec->arg(pinfo, (size_t)n, *argtypes + argindex); + } + } + + if (!pinfo->dollar || !IS_MODIFIER (spec)) + pinfo->argindex += n; + + return n; +} + + +/** + * printf_strerror: printf.h + * + * Communicate information on the last error in a printf + * format string. + * + * Return value: + * A string describing the last error which occurred during the + * parsing of a printf format string. It is the responsibility + * of the caller to free the string. + */ +char * +printf_strerror (void) +{ + return snv_strdup(printf_last_error); +} + +/* (re)initialise the memory used by PPARSER. */ +static inline void +parser_init ( + struct printf_info *pinfo, + const char *format, + const union printf_arg *args) +{ + memset (pinfo, 0, sizeof (struct printf_info)); + pinfo->format = format; + pinfo->args = args; +} + +static inline struct printf_info * +parser_reset (struct printf_info *pinfo) +{ + pinfo->is_long_double = pinfo->is_char = pinfo->is_short = + pinfo->is_long = pinfo->alt = pinfo->space = pinfo->left = + pinfo->showsign = pinfo->group = pinfo->wide = + pinfo->width = pinfo->spec = 0; + + pinfo->state = SNV_STATE_BEGIN; + pinfo->prec = -1; + pinfo->dollar = 0; + pinfo->pad = ' '; + + return pinfo; +} + + +/** + * printf_error: printf.h + * @pinfo: pointer to the current parser state. + * @file: file where error was detected. + * @line: line where error was detected. + * @func1: " (" if function is supplied by compiler. + * @func2: function where error was detected, if supplied by compiler. + * @func3: ")" if function is supplied by compiler. + * @error_message: new error message to append to @pinfo. + * + * The contents of @error_message are appended to the @pinfo internal + * error string, so it is safe to pass static strings or recycle the + * original when this function returns. + * + * Return value: + * The address of the full accumulated error message in @pinfo is + * returned. + **/ +char * +printf_error (struct printf_info *pinfo, const char *file, int line, + const char *func1, const char *func2, const char *func3, + const char *error_message) +{ + int i; + char *result; + if (pinfo->error == NULL) + pinfo->error = filnew (NULL, (size_t)0); + else + filccat (pinfo->error, '\n'); + + /* Cannot use printf because a bug in it might trigger another + printf_error! */ + result = filcat (pinfo->error, "file "); + filcat (pinfo->error, file); + filcat (pinfo->error, ": line "); + for (i = 10; i <= line; i *= 10); + for (i /= 10; i >= 1; i /= 10) + filccat (pinfo->error, '0' + (line / i) % 10); + + filcat (pinfo->error, func1); + filcat (pinfo->error, func2); + filcat (pinfo->error, func3); + filcat (pinfo->error, ": "); + filcat (pinfo->error, error_message); + return result; +} + + + +/** + * parse_printf_format: printf.h + * @format: a % delimited format string. + * @n: the size of the @argtypes vector + * @argtypes: a vector of ints, to be filled with the argument types from @format + * + * Returns information about the number and types of + * arguments expected by the template string @format. + * The argument @n specifies the number of elements in the array + * @argtypes. This is the maximum number of elements that + * the function will try to write. + * + * Return value: + * The total number of arguments required by @format. If this + * number is greater than @n, then the information returned + * describes only the first @n arguments. If you want information + * about additional arguments, allocate a bigger array and call + * this function again. If there is an error, then %SNV_ERROR + * is returned instead. + **/ +size_t +parse_printf_format (const char *format, int n, int *argtypes) +{ + struct printf_info info; + + return_val_if_fail (format != NULL, (size_t)-1); + + parser_init (&info, format, NULL); + + while (*info.format != EOS) + { + int ch = (int) *info.format++; + spec_entry *spec; + int status; + int argindex; + + if (ch != SNV_CHAR_SPEC) + continue; + + if (*info.format == SNV_CHAR_SPEC) + { + /* An escaped CHAR_SPEC: ignore it (by falling through). */ + ++info.format; + continue; + } + + /* We found the start of a format specifier! */ + parser_reset (&info); + do + { + /* Until we fill the stream (or get some other + exception) or one of the handlers tells us + we have reached the end of the specifier... */ + + /* ...lookup the handler associated with the char + we are looking at in the format string... */ + spec = spec_lookup ((unsigned)*(info.format)); + if (spec == NULL) + { + PRINTF_ERROR (&info, "unregistered specifier"); + goto error; + } + + if (!IS_MODIFIER (spec) && + !(info.state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (&info, "invalid combination of flags"); + goto error; + } + + argindex = info.dollar && !IS_MODIFIER (spec) + ? info.dollar - 1 : info.argindex; + + /* ...and call the relevant handler. */ + if (spec->arg) + { + info.spec = (unsigned)*(info.format); + info.extra = spec->user; + info.type = spec->type; + status = (*spec->arg) (&info, (size_t) (n - argindex), + argtypes + argindex); + } + else + { + status = 1; + if (n > argindex) + argtypes[argindex] = spec->type; + } + + if (status < 0) + goto error; + + info.argc = MAX (info.argc, argindex + status); + if (!info.dollar || !IS_MODIFIER (spec)) + info.argindex += status; + + info.format++; + } + while (IS_MODIFIER (spec)); + + continue; + + error: + /* Get here on error */ + info.argc = -1; + break; + } + + if (printf_last_error) + snv_delete (printf_last_error); + + if (info.error) + printf_last_error = fildelete (info.error); + else + printf_last_error = NULL; + + return info.argc; +} + +static int +do_printfv (STREAM *stream, const char *format, union printf_arg const args[]) +{ + struct printf_info info; + + /* This is the parser driver. + + Here we scan through the format string and move bytes into the + stream and call handlers based on the parser state. */ + + parser_init (&info, format, args); + + /* Keep going until the format string runs out! */ + while (*info.format != EOS) + { + int ch = (int) *info.format++; + + switch (ch) + { + case SNV_CHAR_SPEC: + if (*info.format != SNV_CHAR_SPEC) + { + /* We found the start of a format specifier! */ + spec_entry *spec; + int status; + int argindex; + + parser_reset (&info); + do + { + /* Until we fill the stream (or get some other + exception) or one of the handlers tells us + we have reached the end of the specifier... */ + + /* ...lookup the handler associated with the char + we are looking at in the format string... */ + spec = spec_lookup ((unsigned)*(info.format)); + if (spec == NULL) + { + PRINTF_ERROR (&info, "unregistered specifier"); + goto error; + } + + if (!IS_MODIFIER (spec) && + !(info.state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (&info, "invalid combination of flags"); + goto error; + } + + /* ...and call the relevant handler. */ + info.spec = (unsigned)*(info.format); + info.extra = spec->user; + info.type = spec->type; + + status = spec->arg ? (*spec->arg) (&info, (size_t)0, NULL) : 1; + + if (status < 0) + goto error; + + argindex = info.dollar && !IS_MODIFIER (spec) + ? info.dollar - 1 : info.argindex; + + info.format++; + info.argc = MAX (info.argc, argindex + status); + if (!info.dollar && !IS_MODIFIER (spec)) + info.argindex += status; + } + while (info.count >= 0 && IS_MODIFIER (spec)); + + status = (*spec->fmt) (stream, &info, args + argindex); + + if (status < 0) + goto error; + + info.count += status; + continue; + } + + /* An escaped CHAR_SPEC: ignore it (by falling through). */ + ++info.format; + + /*FALLTHROUGH*/ + + default: + /* Just a character: copy it. */ + SNV_EMIT (ch, stream, info.count); + continue; + } + + error: + /* Get here on error */ + info.count = -1; + break; + } + + if (printf_last_error) + snv_delete (printf_last_error); + + if (info.error) + printf_last_error = fildelete (info.error); + else + printf_last_error = NULL; + + return info.count; +} + +/** + * stream_printfv: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +stream_printfv (STREAM *stream, const char *format, snv_constpointer const *ap) +{ + union printf_arg *args; + struct printf_info info; + int count_or_errorcode; + int *argtypes = NULL; + + return_val_if_fail (format != NULL, SNV_ERROR); + + parser_init (&info, format, NULL); + + /* Keep going until the format string runs out! */ + while (*info.format != EOS) + { + int ch = (int) *info.format++; + + switch (ch) + { + case SNV_CHAR_SPEC: + if (*info.format != SNV_CHAR_SPEC) + { + /* We found the start of a format specifier! */ + spec_entry *spec; + + parser_reset (&info); + do + { + /* Until we fill the stream (or get some other + exception) or one of the handlers tells us + we have reached the end of the specifier... */ + + /* ...lookup the handler associated with the char + we are looking at in the format string... */ + spec = spec_lookup ((unsigned)*(info.format)); + if (spec == NULL) + { + PRINTF_ERROR (&info, "unregistered specifier"); + goto error; + } + + if (!IS_MODIFIER (spec) && + !(info.state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (&info, "invalid combination of flags"); + goto error; + } + + /* ...and call the relevant handler. */ + if (call_argtype_function (&info, &argtypes, spec) < 0) + goto error; + + info.format++; + } + while (info.count >= 0 && IS_MODIFIER (spec)); + continue; + } + + /* An escaped CHAR_SPEC: ignore it (by falling through). */ + ++info.format; + + /*FALLTHROUGH*/ + + default: /* Just a character: ignore it. */ + continue; + } + + error: + /* Get here on error */ + info.argc = -1; + break; + } + + if (info.argc == 0) + { + args = NULL; + } + else + { + int idx; + + assert(argtypes != NULL); + args = snv_new (union printf_arg, info.argc); + + /* We scanned the format string to find the type of the arguments, + so we can now cast it and store it correctly. */ + for (idx = 0; idx < info.argc; idx++) + { + int tp = argtypes[idx]; + if ((tp & PA_TYPE_MASK) == PA_TYPE_MASK) + { + if (idx + 1 == info.argc) + { + info.argc--; + break; + } + continue; /* Ignore it. We allow skipping args, but the + * user is responsible for ensuring a void* sized + * spacer in the argument list. + */ + } + + switch (tp & ~PA_FLAG_UNSIGNED) + { + case PA_CHAR: + args[idx].pa_char = (char) *(const long int *)(ap + idx); + break; + + case PA_WCHAR: + args[idx].pa_wchar = + (snv_wchar_t) *(const long int *)(ap + idx); + break; + + case PA_INT|PA_FLAG_SHORT: + args[idx].pa_short_int = + (short int) *(const long int *)(ap + idx); + break; + + case PA_INT: + args[idx].pa_int = (int) *(const long int *)(ap + idx); + break; + + case PA_INT|PA_FLAG_LONG: + args[idx].pa_long_int = *(const long int *)(ap + idx); + break; + + case PA_INT|PA_FLAG_LONG_LONG: + args[idx].pa_long_long_int = **(const intmax_t **)(ap + idx); + break; + + case PA_FLOAT: + args[idx].pa_float = **(const float **)(ap + idx); + break; + + case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: +#ifdef HAVE_LONG_DOUBLE + args[idx].pa_long_double = **(const long double **)(ap + idx); + break; +#endif + /* else fall through */ + + case PA_DOUBLE: + args[idx].pa_double = **(const double **)(ap + idx); + break; + + /* Note that pointer types are dereferenced just once! */ + case PA_STRING: + args[idx].pa_string = *(const char **)(ap + idx); + break; + + case PA_WSTRING: + args[idx].pa_wstring = *(const snv_wchar_t **)(ap + idx); + break; + + case PA_POINTER: + args[idx].pa_pointer = *(snv_constpointer *)(ap + idx); + break; + + default: + if (argtypes[idx] & PA_FLAG_PTR) + args[idx].pa_pointer = *(snv_constpointer *)(ap + idx); + else + args[idx].pa_long_double = 0.0; + break; + } + } + } + + if (printf_last_error) + snv_delete (printf_last_error); + + if (info.error) + printf_last_error = fildelete (info.error); + else + printf_last_error = NULL; + + count_or_errorcode = do_printfv (stream, format, args); + + snv_delete (argtypes); + if (info.argc > 0) + snv_delete (args); + + return count_or_errorcode; +} + + +/** + * stream_vprintf: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +stream_vprintf (STREAM *stream, const char *format, va_list ap) +{ + union printf_arg *args = NULL; + struct printf_info info; + int count_or_errorcode; + int *argtypes = NULL; + + return_val_if_fail (format != NULL, SNV_ERROR); + + parser_init (&info, format, NULL); + + /* Keep going until the format string runs out! */ + while (*info.format != EOS) + { + int ch = (int) *info.format++; + + switch (ch) + { + case SNV_CHAR_SPEC: + if (*info.format != SNV_CHAR_SPEC) + { + /* We found the start of a format specifier! */ + spec_entry *spec; + + parser_reset (&info); + do + { + /* Until we fill the stream (or get some other + exception) or one of the handlers tells us + we have reached the end of the specifier... */ + /* ...lookup the handler associated with the char + we are looking at in the format string... */ + spec = spec_lookup ((unsigned)*(info.format)); + if (spec == NULL) + { + PRINTF_ERROR (&info, "unregistered specifier"); + goto error; + } + + if (!IS_MODIFIER (spec) && + !(info.state & (SNV_STATE_BEGIN | SNV_STATE_SPECIFIER))) + { + PRINTF_ERROR (&info, "invalid combination of flags"); + goto error; + } + + /* ...and call the relevant handler. */ + if (call_argtype_function (&info, &argtypes, spec) < 0) + goto error; + + info.format++; + } + while (info.count >= 0 && IS_MODIFIER (spec)); + continue; + } + /* An escaped CHAR_SPEC: ignore it (by falling through). */ + ++info.format; + + /*FALLTHROUGH*/ + + default: /* Just a character: ignore it. */ + continue; + } + + error: + /* Get here on error */ + info.argc = -1; + break; + } + + if (info.argc > 0) + { + int idx; + + assert(argtypes != NULL); + args = snv_new (union printf_arg, info.argc); + + /* Scan the format string to find the type of the argument + so we can cast it and store it correctly. + + Note that according to the ISO C standards, standard + type promotion takes place on any variadic arguments as + they are aligned on the call stack, and so it is these + promoted types that we must extract with the va_arg() + macro, or the alignment gets all messed up. + + Thanks to Robert Lipe <robertlipe@usa.net> for explaining all + this to me. */ + for (idx = 0; idx < info.argc; idx++) + switch (argtypes[idx] & ~PA_FLAG_UNSIGNED) + { + case PA_CHAR: + args[idx].pa_char = va_arg (ap, int); /* Promoted. */ + break; + + case PA_WCHAR: + args[idx].pa_wchar = va_arg (ap, snv_wint_t); /* Promoted. */ + break; + + case PA_INT|PA_FLAG_SHORT: + args[idx].pa_short_int = va_arg (ap, int); /* Promoted. */ + break; + + case PA_INT: + args[idx].pa_int = va_arg (ap, int); + break; + + case PA_INT|PA_FLAG_LONG: + args[idx].pa_long_int = va_arg (ap, long int); + break; + + case PA_INT|PA_FLAG_LONG_LONG: + args[idx].pa_long_long_int = va_arg (ap, intmax_t); + break; + + case PA_FLOAT: + args[idx].pa_float = va_arg (ap, double); /* Promoted. */ + break; + + case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: + args[idx].pa_long_double = va_arg (ap, long double); + break; + + case PA_DOUBLE: + args[idx].pa_double = va_arg (ap, double); + break; + + case PA_STRING: + args[idx].pa_string = va_arg (ap, const char *); + break; + + case PA_WSTRING: + args[idx].pa_wstring = va_arg (ap, const snv_wchar_t *); + break; + + case PA_POINTER: + args[idx].pa_pointer = va_arg (ap, void *); + break; + + default: + if (argtypes[idx] & PA_FLAG_PTR) + args[idx].pa_pointer = va_arg (ap, void *); + else + args[idx].pa_long_double = 0.0; + break; + } + } + + if (printf_last_error) + snv_delete (printf_last_error); + + if (info.error) + printf_last_error = fildelete (info.error); + else + printf_last_error = NULL; + + count_or_errorcode = do_printfv (stream, format, args); + + snv_delete (argtypes); + snv_delete (args); + return count_or_errorcode; +} + +/** + * stream_printf: printf.h + * @stream: an initialised stream structure. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to @stream. If @stream is %NULL, only count the + * number of characters needed to output the format. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +stream_printf (STREAM * stream, const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = stream_vprintf (stream, format, ap); + va_end (ap); + + return count_or_errorcode; +} + + +/* Finally... the main API implementation: */ + +/** + * snv_fdputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a file descriptor. + * + * Return value: + * The value of @ch that has been put in @stream, or -1 in case of + * an error (errno will be set to indicate the type of error). + **/ +int +snv_fdputc (int ch, STREAM *stream) +{ + static char buf[1] = { 0 }; + buf[0] = (char) ch; + return + write ((int) SNV_POINTER_TO_LONG (stream_details (stream)), buf, (size_t) 1) + ? ch : -1; +} + +/** + * snv_dprintf: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_dprintf (int fd, const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vdprintf (fd, format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vdprintf: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vdprintf (int fd, const char *format, va_list ap) +{ + int result; + STREAM *out = stream_new (SNV_LONG_TO_POINTER (fd), + SNV_UNLIMITED, NULL, snv_fdputc); + + result = stream_vprintf (out, format, ap); + stream_delete (out); + return result; +} + +/** + * snv_dprintfv: printf.h + * @fd: an open file descriptor. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to file descriptor @fd. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_dprintfv (int fd, const char *format, snv_constpointer const args[]) +{ + int result; + STREAM *out = stream_new (SNV_LONG_TO_POINTER (fd), + SNV_UNLIMITED, NULL, snv_fdputc); + + result = stream_printfv (out, format, args); + stream_delete (out); + return result; +} + + +/** + * snv_fileputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a FILE*. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +int +snv_fileputc (int ch, STREAM *stream) +{ + FILE *fp = (FILE *) stream_details (stream); + return putc (ch, fp); +} + +static int +snv_fileputc_unlocked (int ch, STREAM *stream) +{ + FILE *fp = (FILE *) stream_details (stream); + return SNV_PUTC_UNLOCKED (ch, fp); +} + +/** + * snv_printf: printf.h + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_printf (const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vprintf (format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vprintf: printf.h + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vprintf (const char *format, va_list ap) +{ + int result; + STREAM *out = stream_new (stdout, SNV_UNLIMITED, NULL, snv_fileputc_unlocked); + int tmp; + + SNV_WITH_LOCKED_FP (stdout, tmp) + result = stream_vprintf (out, format, ap); + + stream_delete (out); + return result; +} + +/** + * snv_printfv: printf.h + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to the string @format, + * and write the result to the standard output stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_printfv (const char *format, snv_constpointer const args[]) +{ + int result; + STREAM *out = stream_new (stdout, SNV_UNLIMITED, NULL, snv_fileputc_unlocked); + int tmp; + + SNV_WITH_LOCKED_FP (stdout, tmp) + result = stream_printfv (out, format, args); + stream_delete (out); + return result; +} + +/** + * snv_fprintf: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the @file stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_fprintf (FILE * file, const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vfprintf (file, format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vfprintf: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the @file stream. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vfprintf (FILE *file, const char *format, va_list ap) +{ + int result; + STREAM *out = stream_new (file, SNV_UNLIMITED, NULL, snv_fileputc_unlocked); + int tmp; + + SNV_WITH_LOCKED_FP (file, tmp) + result = stream_vprintf (out, format, ap); + stream_delete (out); + return result; +} + +/** + * snv_fprintfv: printf.h + * @file: a stdio.h FILE* stream. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to @file. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_fprintfv (FILE *file, const char *format, snv_constpointer const args[]) +{ + int result; + STREAM *out = stream_new (file, SNV_UNLIMITED, NULL, snv_fileputc_unlocked); + int tmp; + + SNV_WITH_LOCKED_FP (file, tmp) + result = stream_printfv (out, format, args); + + stream_delete (out); + return result; +} + + +/** + * snv_bufputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a char buffer. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +int +snv_bufputc (int ch, STREAM * stream) +{ + char **ppbuffer = (char **) stream_details (stream); + **ppbuffer = (char) ch; + (*ppbuffer)++; + return ch; +} + +/** + * snv_sprintf: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_sprintf (char buffer[], const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vsprintf (buffer, format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vsprintf: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vsprintf (char buffer[], const char *format, va_list ap) +{ + int count_or_errorcode; + STREAM *out = stream_new (&buffer, SNV_UNLIMITED, NULL, snv_bufputc); + count_or_errorcode = stream_vprintf (out, format, ap); + + /* Terminate with an EOS without incrementing the counter. */ + stream_put (EOS, out); + + stream_delete (out); + return count_or_errorcode; +} + +/** + * snv_sprintfv: printf.h + * @buffer: a preallocated char* buffer. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to the string @buffer. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_sprintfv (char buffer[], const char *format, snv_constpointer const args[]) +{ + int count_or_errorcode; + STREAM *out = stream_new (&buffer, SNV_UNLIMITED, NULL, snv_bufputc); + count_or_errorcode = stream_printfv (out, format, args); + + /* Terminate with an EOS without incrementing the counter. */ + stream_put (EOS, out); + + stream_delete (out); + return count_or_errorcode; +} + +/** + * snv_snprintf: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_snprintf (char buffer[], unsigned long limit, const char *format, ...) +{ + int count_or_errorcode; + va_list ap; + + va_start (ap, format); + count_or_errorcode = snv_vsnprintf (buffer, limit, format, ap); + va_end (ap); + + return count_or_errorcode; +} + +/** + * snv_vsnprintf: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vsnprintf (char buffer[], unsigned long limit, const char *format, va_list ap) +{ + int count_or_errorcode; + STREAM *out = stream_new (&buffer, limit - 1, NULL, snv_bufputc); + count_or_errorcode = stream_vprintf (out, format, ap); + *buffer = EOS; + + stream_delete (out); + return count_or_errorcode; +} + +/** + * snv_snprintfv: printf.h + * @buffer: a preallocated char* buffer. + * @limit: the maximum number of characters to write into @buffer. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to the string @buffer, truncating the formatted string + * if it reaches @limit characters in length. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_snprintfv (char buffer[], unsigned long limit, const char *format, + snv_constpointer const args[]) +{ + int count_or_errorcode; + STREAM *out = stream_new (&buffer, limit - 1, NULL, snv_bufputc); + count_or_errorcode = stream_printfv (out, format, args); + *buffer = EOS; + + stream_delete (out); + return count_or_errorcode; +} + + +/** + * snv_filputc: printf.h + * @ch: A single character to be added to @stream. + * @stream: The stream in which to write @ch. + * + * A StreamPut function for use in putting characters + * into STREAMs holding a Filament*. + * + * Return value: + * The value of @ch that has been put in @stream. + **/ +int +snv_filputc (int ch, STREAM * stream) +{ + return filccat ((Filament *) stream_details (stream), ch), ch; +} + +/** + * snv_asprintf: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @va_alist: a varargs/stdargs va_list. + * + * Format the elements of @va_alist according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Yes, this interface is cumbersome and totally useless. It would + * have been better to simply return the allocated address, but + * it turns out that somebody wasn't thinking much when adding + * asprintf to libiberty a few years ago. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_asprintf (char **result, const char *format, ...) +{ + int count; + va_list ap; + + va_start (ap, format); + count = snv_vasprintf (result, format, ap); + va_end (ap); + + return count; +} + +/** + * snv_vasprintf: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @ap: a varargs/stdargs va_list. + * + * Format the elements of @ap according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Above moaning for asprintf applies here too. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_vasprintf (char **result, const char *format, va_list ap) +{ + int count_or_errorcode; + char *base; + Filament *fil = filnew (NULL, (size_t)0); + STREAM *out = stream_new (fil, SNV_UNLIMITED, NULL, snv_filputc); + count_or_errorcode = stream_vprintf (out, format, ap); + + base = fildelete (fil); + stream_delete (out); + + *result = (count_or_errorcode < 0) ? NULL : base; + return count_or_errorcode; +} + +/** + * snv_asprintfv: printf.h + * @result: the address of a char * variable. + * @format: a % delimited format string. + * @args: a vector of argument addresses to match @format. + * + * Format the elements of @args according to @format, and write + * the results to an internally allocated buffer whose address is + * stored in @result (and should be freed by the caller) unless + * there is an error. + * + * Above moaning for asprintf applies here too. + * + * Return value: + * The number of characters written is returned, unless there is + * an error, when %SNV_ERROR is returned. + **/ +int +snv_asprintfv (char **result, const char *format, snv_constpointer const args[]) +{ + int count_or_errorcode; + char *base; + Filament *fil = filnew (NULL, (size_t)0); + STREAM *out = stream_new (fil, SNV_UNLIMITED, NULL, snv_filputc); + count_or_errorcode = stream_printfv (out, format, args); + + base = fildelete (fil); + stream_delete (out); + + *result = (count_or_errorcode < 0) ? NULL : base; + return count_or_errorcode; +} + +/* + * Local Variables: + * mode: C + * c-file-style: "gnu" + * indent-tabs-mode: nil + * End: + * end of snprintfv/printfv.c */ |