Index: ossp-pkg/l2/Makefile.in RCS File: /v/ossp/cvs/ossp-pkg/l2/Makefile.in,v rcsdiff -q -kk '-r1.10' '-r1.11' -u '/v/ossp/cvs/ossp-pkg/l2/Makefile.in,v' 2>/dev/null --- Makefile.in 2001/09/03 17:23:48 1.10 +++ Makefile.in 2001/09/04 13:52:59 1.11 @@ -73,7 +73,9 @@ l2_ch_null.lo \ l2_ch_filter.lo \ l2_ch_prefix.lo \ - l2_ch_buffer.lo + l2_ch_buffer.lo \ + l2_ut_format.lo \ + l2_ut_param.lo # file containing the official version information _VERSION_FILE = \ @@ -215,6 +217,7 @@ $(RM) ltmain.sh libtool.m4 $(RM) shtool $(RM) l2-config.1 + $(RM) l2_config.h.in $(RM) l2.3 $(RM) l2++.3 @@ -242,7 +245,7 @@ cp Makefile.in Makefile.in.bak \ && sed -ne '1,/^# DO NOT REMOVE/p' Makefile.in > Makefile.new \ && $(CC) -MM $(CPPFLAGS) $(CFLAGS) *.c |\ - sed -e 's/^\(pth_.*\)\.o:/\1.lo:/' >> Makefile.new \ + sed -e 's/^\(l2_.*\)\.o:/\1.lo:/' >> Makefile.new \ && cp Makefile.new Makefile.in && rm -f Makefile.new @@ -266,3 +269,35 @@ l2_stream.o: l2_stream.c l2.h l2_p.h l2_test.o: l2_test.c l2.h l2_version.o: l2_version.c l2_version.c +l2_ch_buffer.o: l2_ch_buffer.c l2.h l2_p.h +l2_ch_fd.o: l2_ch_fd.c l2.h l2_p.h +l2_ch_file.o: l2_ch_file.c l2.h l2_p.h +l2_ch_filter.o: l2_ch_filter.c l2.h l2_p.h +l2_ch_null.o: l2_ch_null.c l2.h l2_p.h +l2_ch_pipe.o: l2_ch_pipe.c l2.h l2_p.h +l2_ch_prefix.o: l2_ch_prefix.c l2.h l2_p.h +l2_ch_socket.o: l2_ch_socket.c l2.h l2_p.h +l2_ch_syslog.o: l2_ch_syslog.c l2.h l2_p.h +l2_channel.o: l2_channel.c l2.h l2_p.h +l2_epreuve.o: l2_epreuve.c l2.h l2_p.h +l2_stream.o: l2_stream.c l2.h l2_p.h +l2_test.o: l2_test.c l2.h +l2_ut_format.o: l2_ut_format.c l2.h l2_p.h +l2_ut_param.o: l2_ut_param.c l2.h l2_p.h +l2_version.o: l2_version.c l2_version.c +l2_ch_buffer.lo: l2_ch_buffer.c l2.h l2_p.h +l2_ch_fd.lo: l2_ch_fd.c l2.h l2_p.h +l2_ch_file.lo: l2_ch_file.c l2.h l2_p.h +l2_ch_filter.lo: l2_ch_filter.c l2.h l2_p.h +l2_ch_null.lo: l2_ch_null.c l2.h l2_p.h +l2_ch_pipe.lo: l2_ch_pipe.c l2.h l2_p.h +l2_ch_prefix.lo: l2_ch_prefix.c l2.h l2_p.h +l2_ch_socket.lo: l2_ch_socket.c l2.h l2_p.h +l2_ch_syslog.lo: l2_ch_syslog.c l2.h l2_p.h +l2_channel.lo: l2_channel.c l2.h l2_p.h +l2_epreuve.lo: l2_epreuve.c l2.h l2_p.h +l2_stream.lo: l2_stream.c l2.h l2_p.h +l2_test.lo: l2_test.c l2.h +l2_ut_format.lo: l2_ut_format.c l2.h l2_p.h +l2_ut_param.lo: l2_ut_param.c l2.h l2_p.h +l2_version.lo: l2_version.c l2_version.c Index: ossp-pkg/l2/l2.h RCS File: /v/ossp/cvs/ossp-pkg/l2/Attic/l2.h,v rcsdiff -q -kk '-r1.12' '-r1.13' -u '/v/ossp/cvs/ossp-pkg/l2/Attic/l2.h,v' 2>/dev/null --- l2.h 2001/09/03 13:43:33 1.12 +++ l2.h 2001/09/04 13:52:59 1.13 @@ -132,13 +132,14 @@ }; /* type of formatter callback function */ -typedef int (*l2_formatter_t)( - l2_context_t *context, - const char *name, - const char *param, - char *buf, - size_t bufsize, - va_list ap +typedef l2_result_t (*l2_formatter_t)( + l2_context_t *ctx, /* application context */ + const char id, /* input arg: format string id ('x' of '%x') */ + const char *param, /* input arg: format string parameter ('foo' of '%{foo}x') */ + char *bufptr, /* input arg: pointer to output buffer */ + size_t bufsize, /* input arg: maximum size of output buffer */ + size_t *buflen, /* ouput arg: written characters in output buffer */ + va_list *ap /* in/out arg: variable argument pointer */ ); /* list of shipped (output) channel handlers */ @@ -154,8 +155,6 @@ extern l2_handler_t l2_handler_prefix; extern l2_handler_t l2_handler_buffer; -/* parameter operations */ - /* channel operations */ l2_channel_t *l2_channel_create (l2_handler_t *h); l2_result_t l2_channel_configure(l2_channel_t *ch, const char *fmt, ...); @@ -165,16 +164,18 @@ l2_result_t l2_channel_close (l2_channel_t *ch); l2_result_t l2_channel_destroy (l2_channel_t *ch); l2_result_t l2_channel_stack (l2_channel_t *ch, l2_channel_t *chTop); -l2_result_t l2_channel_setparams(l2_param_t p[], const char *fmt, va_list ap); /* stream operations */ l2_stream_t *l2_stream_create (void); l2_result_t l2_stream_channel (l2_stream_t *st, l2_channel_t *ch, unsigned int levelmask); -l2_result_t l2_stream_formatter (l2_stream_t *st, const char *name, l2_formatter_t cb, l2_context_t *ctx); +l2_result_t l2_stream_formatter (l2_stream_t *st, char id, l2_formatter_t cb, l2_context_t *ctx); l2_result_t l2_stream_levels (l2_stream_t *st, unsigned int levelmask, unsigned int *levelmaskold); l2_result_t l2_stream_log (l2_stream_t *st, unsigned int log_level, const char *fmt, ...); l2_result_t l2_stream_vlog (l2_stream_t *st, unsigned int log_level, const char *fmt, va_list ap); l2_result_t l2_stream_destroy (l2_stream_t *st); +/* utility operations */ +l2_result_t l2_util_setparams(l2_param_t p[], const char *fmt, va_list ap); + #endif /* __L2_H__ */ Index: ossp-pkg/l2/l2_ch_buffer.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_buffer.c,v rcsdiff -q -kk '-r1.9' '-r1.10' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_buffer.c,v' 2>/dev/null --- l2_ch_buffer.c 2001/09/02 15:37:48 1.9 +++ l2_ch_buffer.c 2001/09/04 13:52:59 1.10 @@ -73,7 +73,7 @@ /* feed and call generic parameter parsing engine */ L2_PARAM_SET(pa[0], size, INT, &cfg->bufsize); L2_PARAM_END(pa[2]); - rv = l2_channel_setparams(pa, fmt, ap); + rv = l2_util_setparams(pa, fmt, ap); if (cfg->bufsize < 0) return L2_ERROR; return rv; Index: ossp-pkg/l2/l2_ch_file.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_file.c,v rcsdiff -q -kk '-r1.8' '-r1.9' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_file.c,v' 2>/dev/null --- l2_ch_file.c 2001/09/03 13:37:00 1.8 +++ l2_ch_file.c 2001/09/04 13:52:59 1.9 @@ -79,7 +79,7 @@ L2_PARAM_SET(pa[1], append, INT, &cfg->append); L2_PARAM_SET(pa[2], perm, LONG, &cfg->perm); L2_PARAM_END(pa[3]); - rv = l2_channel_setparams(pa, fmt, ap); + rv = l2_util_setparams(pa, fmt, ap); return rv; } Index: ossp-pkg/l2/l2_ch_socket.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_socket.c,v rcsdiff -q -kk '-r1.6' '-r1.7' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_socket.c,v' 2>/dev/null --- l2_ch_socket.c 2001/09/02 14:38:51 1.6 +++ l2_ch_socket.c 2001/09/04 13:52:59 1.7 @@ -129,7 +129,7 @@ L2_PARAM_SET(pa[0], host, CHARPTR, &cfg->pszHost); L2_PARAM_SET(pa[1], port, INT, &cfg->iPort); L2_PARAM_END(pa[2]); - rv = l2_channel_setparams(pa, fmt, ap); + rv = l2_util_setparams(pa, fmt, ap); return rv; } Index: ossp-pkg/l2/l2_ch_syslog.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_syslog.c,v rcsdiff -q -kk '-r1.8' '-r1.9' -u '/v/ossp/cvs/ossp-pkg/l2/l2_ch_syslog.c,v' 2>/dev/null --- l2_ch_syslog.c 2001/08/26 13:05:41 1.8 +++ l2_ch_syslog.c 2001/09/04 13:52:59 1.9 @@ -81,7 +81,7 @@ L2_PARAM_SET(pa[1], priority, INT, &cfg->iPriority); L2_PARAM_SET(pa[1], maskpriority, INT, &cfg->iMaskpri); L2_PARAM_END(pa[2]); - rv = l2_channel_setparams(pa, fmt, ap); + rv = l2_util_setparams(pa, fmt, ap); return rv; } Index: ossp-pkg/l2/l2_channel.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_channel.c,v rcsdiff -q -kk '-r1.10' '-r1.11' -u '/v/ossp/cvs/ossp-pkg/l2/l2_channel.c,v' 2>/dev/null --- l2_channel.c 2001/09/03 13:43:33 1.10 +++ l2_channel.c 2001/09/04 13:52:59 1.11 @@ -169,73 +169,3 @@ return rv; } -l2_result_t l2_channel_setparams(l2_param_t pa[], const char *fmt, va_list ap) -{ - const char *cpB, *cpE; - const char *cpC, *cpG; - int ok; - int i; - - if (pa == NULL || fmt == NULL || ap == NULL) - return L2_ERROR; - cpE = fmt; - while (*cpE != '\0') { - /* determine begin of parameter name */ - cpB = cpE; - while (*cpB == ',') - cpB++; - - /* determine end of parameter name */ - cpE = cpB; - while (*cpE != ',' && *cpE != '\0') - cpE++; - - /* try to match with configured parameters */ - ok = FALSE; - for (i = 0; pa[i].name != NULL; i++) { - cpC = pa[i].name; - cpG = cpB; - while (*cpC != '\0' && cpG < cpE) { - if (*cpC != *cpG) - break; - cpC++; - cpG++; - } - if (*cpC == '\0' && cpG == cpE) { - /* parameter matched, so store value */ - switch (pa[i].type) { - case L2_TYPE_CHAR: - *(char *)(pa[i].store) = va_get(ap, char); - break; - case L2_TYPE_SHORT: - *(short *)(pa[i].store) = va_get(ap, short); - break; - case L2_TYPE_INT: - *(int *)(pa[i].store) = va_get(ap, int); - break; - case L2_TYPE_LONG: - *(long *)(pa[i].store) = va_get(ap, long); - break; - case L2_TYPE_FLOAT: - *(float *)(pa[i].store) = va_get(ap, float); - break; - case L2_TYPE_DOUBLE: - *(double *)(pa[i].store) = va_get(ap, double); - break; - case L2_TYPE_CHARPTR: - *(char **)(pa[i].store) = va_get(ap, charptr); - break; - case L2_TYPE_VOIDPTR: - *(void **)(pa[i].store) = va_get(ap, voidptr); - break; - } - ok = TRUE; - break; - } - } - if (!ok) - return L2_ERROR; - } - return L2_OK; -} - Index: ossp-pkg/l2/l2_p.h RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_p.h,v rcsdiff -q -kk '-r1.9' '-r1.10' -u '/v/ossp/cvs/ossp-pkg/l2/l2_p.h,v' 2>/dev/null --- l2_p.h 2001/09/03 13:43:33 1.9 +++ l2_p.h 2001/09/04 13:52:59 1.10 @@ -57,7 +57,7 @@ typedef struct { l2_formatter_t cb; void *ctx; - char *name; + char id; } l2_formatter_entry_t; struct l2_stream_st { @@ -86,5 +86,46 @@ #define _va_type_cast_voidptr void * #define va_get(ap,type) (_va_type_cast_##type)va_arg((ap),_va_type_recv_##type) +struct l2_util_format_st { + + /* the output buffer */ + char *curpos; /* start of output buffer (first pos) */ + char *endpos; /* end of output buffer (last pos) */ + + /* callback for flushing the output buffer */ + int (*flush)( + struct l2_util_format_st *spec /* this l2_util_format_t specification */ + ); + + /* callback for formatting unknown %-constructs */ + void (*format)( + struct l2_util_format_st *spec, /* this l2_util_format_t specification */ + char *prefix_char, /* output arg: prefix character */ + char *pad_char, /* output arg: padding character */ + char **s_buf, /* output arg: string buffer */ + size_t *s_len, /* output arg: string len */ + char *num_buf, /* input arg: temporary buffer */ + int num_buf_size, /* input arg: temporary buffer len */ + char *extinfo, /* input arg: extension information */ + char fmt_char, /* input arg: current formatting character */ + va_list *ap /* in/out arg: variable argument pointer */ + ); + + /* arbitrary passed-through application data */ + union { + int i; + long l; + double d; + void *vp; + } data[6]; +}; +typedef struct l2_util_format_st l2_util_format_t; + +int l2_util_format (l2_util_format_t *vbuff, const char *fmt, va_list ap); +int l2_util_vsprintf (char *s, size_t n, const char *fmt, va_list ap); +char *l2_util_vasprintf (const char *fmt, va_list ap); +int l2_util_sprintf (char *s, size_t n, const char *fmt, ...); +char *l2_util_asprintf (const char *fmt, ...); + #endif /* __L2_P_H__ */ Index: ossp-pkg/l2/l2_stream.c RCS File: /v/ossp/cvs/ossp-pkg/l2/Attic/l2_stream.c,v rcsdiff -q -kk '-r1.7' '-r1.8' -u '/v/ossp/cvs/ossp-pkg/l2/Attic/l2_stream.c,v' 2>/dev/null --- l2_stream.c 2001/09/03 13:43:33 1.7 +++ l2_stream.c 2001/09/04 13:52:59 1.8 @@ -61,17 +61,17 @@ return L2_OK; } -l2_result_t l2_stream_formatter(l2_stream_t *st, const char *name, l2_formatter_t cb, l2_context_t *ctx) +l2_result_t l2_stream_formatter(l2_stream_t *st, char id, l2_formatter_t cb, l2_context_t *ctx) { int i; - if (st == NULL || name == NULL || cb != NULL) + if (st == NULL || id == '\0' || cb != NULL) return L2_ERROR; for (i = 0; i < L2_MAX_FORMATTERS && st->formatters[i].cb != NULL; i++) ; if (i == L2_MAX_FORMATTERS) return L2_ERROR; - st->formatters[i].name = strdup(name); + st->formatters[i].id = id; st->formatters[i].ctx = ctx; st->formatters[i].cb = cb; return L2_OK; @@ -100,12 +100,45 @@ return rv; } +static int l2_stream_vlog_flush(l2_util_format_t *vfmt) +{ + return -1; +} + +static void l2_stream_vlog_format( + l2_util_format_t *vfmt, + char *cPrefix, char *cPad, char **cppOut, size_t *npOutLen, + char *cpBuf, int nBufLenMax, char *cpParam, char cId, va_list *apArgs) +{ + l2_stream_t *st = (l2_stream_t *)(vfmt->data[0].vp); + int i; + + /* init result */ + *cPrefix = '\0'; + *cPad = ' '; + *cppOut = NULL; + *npOutLen = 0; + + /* iterate over all configured formatters */ + for (i = 0; i < L2_MAX_FORMATTERS && st->formatters[i].cb != NULL; i++) { + if (st->formatters[i].id == cId) { + if (st->formatters[i].cb(st->formatters[i].ctx, cId, cpParam, + cpBuf, nBufLenMax, npOutLen, apArgs) == L2_OK) { + *cppOut = cpBuf; + break; + } + } + } + return; +} + l2_result_t l2_stream_vlog(l2_stream_t *st, unsigned int level, const char *fmt, va_list ap) { int i; int l, j; size_t len; l2_result_t rv; + l2_util_format_t vfmt; if (st == NULL || fmt == NULL || ap == NULL) return L2_ERROR; @@ -119,9 +152,15 @@ /* check whether level is globally enabled */ if (!(st->levelmask & level)) return L2_OK; - - /* XXX use st->formatter!! XXX */ - len = vsnprintf(st->message, L2_MAX_MSGSIZE, fmt, ap); + + /* format message */ + vfmt.curpos = st->message; + vfmt.endpos = st->message+ L2_MAX_MSGSIZE; + vfmt.data[0].vp = st; + vfmt.flush = l2_stream_vlog_flush; + vfmt.format = l2_stream_vlog_format; + if ((len = l2_util_format(&vfmt, fmt, ap)) == -1) + return L2_ERROR; rv = L2_OK; for (i = 0; i < L2_MAX_CHANNELS && st->channels[i].ch != NULL; i++) { @@ -141,8 +180,6 @@ return L2_ERROR; for (i = 0; i < L2_MAX_CHANNELS && st->channels[i].ch != NULL; i++) l2_channel_destroy(st->channels[i].ch); - for (i = 0; i < L2_MAX_FORMATTERS && st->formatters[i].cb != NULL; i++) - free(st->formatters[i].name); free(st); return L2_OK; } Index: ossp-pkg/l2/l2_ut_format.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ut_format.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/l2/l2_ut_format.c,v' | diff -u /dev/null - -L'ossp-pkg/l2/l2_ut_format.c' 2>/dev/null --- ossp-pkg/l2/l2_ut_format.c +++ - 2024-04-23 09:54:57.038237205 +0200 @@ -0,0 +1,1165 @@ +/* +** L2 - OSSP Logging Library +** Copyright (c) 2001 The OSSP Project (http://www.ossp.org/) +** Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/) +** +** This file is part of OSSP L2, a flexible logging library which +** can be found at http://www.ossp.org/pkg/l2/. +** +** Permission to use, copy, modify, and distribute this software for +** any purpose with or without fee is hereby granted, provided that +** the above copyright notice and this permission notice appear in all +** copies. +** +** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 AUTHORS AND COPYRIGHT HOLDERS AND THEIR +** 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. +** +** l2_ut_format.c: printf(3)-style formatting +*/ + +/* + * This is a generic printf-style formatting code which is based on + * Apache's ap_snprintf which it turn is based on and used with the + * permission of, the SIO stdio-replacement strx_* functions by Panos + * Tsirigotis for xinetd. The IEEE + * floating point formatting routines are derived from an anchient + * FreeBSD version which took it from GNU libc-4.6.27 and modified it + * to be thread safe. The whole code was finally cleaned up, stripped + * and extended by Ralf S. Engelschall for use inside the Str library. + * Especially any Apache and network specific kludges were removed again + * and instead the formatting engine now can be extended by the caller + * on-the-fly. It was then finally adjusted to be stand-alone for use + * inside OSSP L2. + */ + +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 The Apache Software Foundation. All rights + * reserved. + * + * 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. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR + * ITS 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "l2.h" +#include "l2_p.h" + +/* types which are locally use */ +typedef long long_int; +typedef unsigned long u_long_int; +#if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG > 0) +typedef long long quad_int; +typedef unsigned long long u_quad_int; +#else +typedef long quad_int; +typedef unsigned long u_quad_int; +#endif + +/* a few handy defines */ +#ifndef NUL +#define NUL '\0' +#endif +#ifndef NULL +#define NULL ((void *)0) +#endif +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif +#define S_NULL "(NULL)" +#define S_NULL_LEN 6 +#define FLOAT_DIGITS 6 +#define EXPONENT_LENGTH 10 + +/* NUM_BUF_SIZE is the size of the buffer used for arithmetic + conversions. This is a magic number; do NOT decrease it! */ +#define NUM_BUF_SIZE 512 +#define NDIG 80 + +/* compatibility */ +#if !defined(HAVE_ISNAN) +#define isnan(d) (0) +#endif +#if !defined(HAVE_ISINF) +#define isinf(d) (0) +#endif + +/* explicit support for unsigned char based ctype stuff */ +#define l2_util_isalpha(c) (isalpha(((unsigned char)(c)))) +#define l2_util_isdigit(c) (isdigit(((unsigned char)(c)))) +#define l2_util_isxdigit(c) (isxdigit(((unsigned char)(c)))) +#define l2_util_islower(c) (islower(((unsigned char)(c)))) +#define l2_util_tolower(c) (tolower((unsigned char)(c))) + +/* + * Convert decimal number to its string representation. The number of + * digits is specified by ndigit decpt is set to the position of the + * decimal point sign is set to 0 for positive, 1 for negative. buf must + * have at least NDIG bytes. + */ + +#define l2_util_ecvt(arg,ndigits,decpt,sign,buf) \ + l2_util_cvt((arg), (ndigits), (decpt), (sign), 1, (buf)) + +#define l2_util_fcvt(arg,ndigits,decpt,sign,buf) \ + l2_util_cvt((arg), (ndigits), (decpt), (sign), 0, (buf)) + +static char * +l2_util_cvt( + double arg, + int ndigits, + int *decpt, + int *sign, + int eflag, + char *buf) +{ + register int r2; + double fi, fj; + register char *p, *p1; + + if (ndigits >= NDIG - 1) + ndigits = NDIG - 2; + r2 = 0; + *sign = FALSE; + p = &buf[0]; + if (arg < 0) { + *sign = TRUE; + arg = -arg; + } + arg = modf(arg, &fi); + p1 = &buf[NDIG]; + + /* Do integer part */ + if (fi != 0) { + p1 = &buf[NDIG]; + while (fi != 0 && p1 > &buf[0]) { + fj = modf(fi / 10, &fi); + *--p1 = (int)((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < &buf[NDIG]) + *p++ = *p1++; + } + else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + p1 = &buf[ndigits]; + if (eflag == 0) + p1 += r2; + *decpt = r2; + if (p1 < &buf[0]) { + buf[0] = NUL; + return (buf); + } + while (p <= p1 && p < &buf[NDIG]) { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int) fj + '0'; + } + if (p1 >= &buf[NDIG]) { + buf[NDIG - 1] = NUL; + return (buf); + } + p = p1; + *p1 += 5; + while (*p1 > '9') { + *p1 = '0'; + if (p1 > buf) + ++ * --p1; + else { + *p1 = '1'; + (*decpt)++; + if (eflag == 0) { + if (p > buf) + *p = '0'; + p++; + } + } + } + *p = NUL; + return buf; +} + +static char * +l2_util_gcvt( + double number, + int ndigit, + char *buf, + int altform) +{ + int sign; + int decpt; + register char *p1, *p2; + register int i; + char buf1[NDIG]; + + p1 = l2_util_ecvt(number, ndigit, &decpt, &sign, buf1); + p2 = buf; + if (sign) + *p2++ = '-'; + for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) + ndigit--; + if ((decpt >= 0 && decpt - ndigit > 4) + || (decpt < 0 && decpt < -3)) { /* use E-style */ + decpt--; + *p2++ = *p1++; + *p2++ = '.'; + for (i = 1; i < ndigit; i++) + *p2++ = *p1++; + *p2++ = 'e'; + if (decpt < 0) { + decpt = -decpt; + *p2++ = '-'; + } + else + *p2++ = '+'; + if (decpt / 100 > 0) + *p2++ = decpt / 100 + '0'; + if (decpt / 10 > 0) + *p2++ = (decpt % 100) / 10 + '0'; + *p2++ = decpt % 10 + '0'; + } + else { + if (decpt <= 0) { + if (*p1 != '0') + *p2++ = '.'; + while (decpt < 0) { + decpt++; + *p2++ = '0'; + } + } + for (i = 1; i <= ndigit; i++) { + *p2++ = *p1++; + if (i == decpt) + *p2++ = '.'; + } + if (ndigit < decpt) { + while (ndigit++ < decpt) + *p2++ = '0'; + *p2++ = '.'; + } + } + if (p2[-1] == '.' && !altform) + p2--; + *p2 = NUL; + return buf; +} + +/* + * The INS_CHAR macro inserts a character in the buffer and flushes the + * buffer if necessary. It uses the char pointers sp and bep: sp points + * to the next available character in the buffer, bep points to the + * end-of-buffer+1. While using this macro, note that the nextb pointer + * is NOT updated. NOTE: Evaluation of the c argument should not have + * any side-effects + */ +#define INS_CHAR(c, sp, bep, cc) { \ + if (sp >= bep) { \ + vbuff->curpos = sp; \ + if (vbuff->flush(vbuff)) \ + return -1; \ + sp = vbuff->curpos; \ + bep = vbuff->endpos; \ + } \ + *sp++ = (c); \ + cc++; \ +} + +/* + * Convert a string to decimal value + */ +#define NUM(c) ((c) - '0') +#define STR_TO_DEC(str, num) { \ + num = NUM(*str++); \ + while (l2_util_isdigit(*(str))) { \ + num *= 10 ; \ + num += NUM(*str++) ; \ + } \ +} + +/* + * This macro does zero padding so that the precision requirement is + * satisfied. The padding is done by adding '0's to the left of the + * string that is going to be printed. + */ +#define FIX_PRECISION(adjust, precision, s, s_len) \ + if (adjust) { \ + while (s_len < precision) { \ + *--s = '0'; \ + s_len++; \ + } \ + } + +/* + * This macro does padding. + * The padding is done by printing the character ch. + */ +#define PAD(width, len, ch) \ + do { \ + INS_CHAR(ch, sp, bep, cc); \ + width-- ; \ + } while (width > len) + +/* + * Prefix the character ch to the string str + * Increase length. Set the has_prefix flag. + */ +#define PREFIX(str, length, ch) \ + *--str = ch; \ + length++; \ + has_prefix = TRUE + +/* + * Convert num to its decimal format. + * Return value: + * - a pointer to a string containing the number (no sign) + * - len contains the length of the string + * - is_negative is set to TRUE or FALSE depending on the sign + * of the number (always set to FALSE if is_unsigned is TRUE) + * The caller provides a buffer for the string: that is the buf_end argument + * which is a pointer to the END of the buffer + 1 (i.e. if the buffer + * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) + * Note: we have 2 versions. One is used when we need to use quads + * (conv_10_quad), the other when we don't (conv_10). We're assuming the + * latter is faster. + */ +static char * +conv_10( + register long_int num, + register int is_unsigned, + register int *is_negative, + char *buf_end, + register size_t *len) +{ + register char *p = buf_end; + register u_long_int magnitude; + + if (is_unsigned) { + magnitude = (u_long_int)num; + *is_negative = FALSE; + } + else { + *is_negative = (num < 0); + /* On a 2's complement machine, negating the most negative integer + results in a number that cannot be represented as a signed integer. + Here is what we do to obtain the number's magnitude: + a. add 1 to the number + b. negate it (becomes positive) + c. convert it to unsigned + d. add 1 */ + if (*is_negative) { + long_int t = num + 1; + + magnitude = ((u_long_int) - t) + 1; + } + else + magnitude = (u_long_int)num; + } + /* We use a do-while loop so that we write at least 1 digit */ + do { + register u_long_int new_magnitude = magnitude / 10; + *--p = (char) (magnitude - new_magnitude * 10 + '0'); + magnitude = new_magnitude; + } while (magnitude); + *len = (int)(buf_end - p); + return p; +} + +static char * +conv_10_quad( + quad_int num, + register int is_unsigned, + register int *is_negative, + char *buf_end, + register size_t *len) +{ + register char *p = buf_end; + u_quad_int magnitude; + + if (is_unsigned) { + magnitude = (u_quad_int)num; + *is_negative = FALSE; + } + else { + *is_negative = (num < 0); + /* On a 2's complement machine, negating the most negative integer + result in a number that cannot be represented as a signed integer. + Here is what we do to obtain the number's magnitude: + a. add 1 to the number + b. negate it (becomes positive) + c. convert it to unsigned + d. add 1 */ + if (*is_negative) { + quad_int t = num + 1; + magnitude = ((u_quad_int) - t) + 1; + } + else + magnitude = (u_quad_int)num; + } + /* We use a do-while loop so that we write at least 1 digit */ + do { + u_quad_int new_magnitude = magnitude / 10; + *--p = (char)(magnitude - new_magnitude * 10 + '0'); + magnitude = new_magnitude; + } while (magnitude); + *len = (int)(buf_end - p); + return p; +} + +/* + * Convert a floating point number to a string formats 'f', 'e' or 'E'. + * The result is placed in buf, and len denotes the length of the string + * The sign is returned in the is_negative argument (and is not placed + * in buf). + */ +static char * +conv_fp( + register char format, + register double num, + int add_dp, + int precision, + int *is_negative, + char *buf, + size_t *len) +{ + register char *s = buf; + register char *p; + int decimal_point; + char buf1[NDIG]; + + if (format == 'f') + p = l2_util_fcvt(num, precision, &decimal_point, is_negative, buf1); + else /* either e or E format */ + p = l2_util_ecvt(num, precision + 1, &decimal_point, is_negative, buf1); + + /* Check for Infinity and NaN */ + if (l2_util_isalpha(*p)) { + *len = strlen(strcpy(buf, p)); + *is_negative = FALSE; + return (buf); + } + + if (format == 'f') { + if (decimal_point <= 0) { + *s++ = '0'; + if (precision > 0) { + *s++ = '.'; + while (decimal_point++ < 0) + *s++ = '0'; + } + else if (add_dp) + *s++ = '.'; + } + else { + while (decimal_point-- > 0) + *s++ = *p++; + if (precision > 0 || add_dp) + *s++ = '.'; + } + } + else { + *s++ = *p++; + if (precision > 0 || add_dp) + *s++ = '.'; + } + + /* copy the rest of p, the NUL is NOT copied */ + while (*p) + *s++ = *p++; + + if (format != 'f') { + char temp[EXPONENT_LENGTH]; /* for exponent conversion */ + size_t t_len; + int exponent_is_negative; + + *s++ = format; /* either e or E */ + decimal_point--; + if (decimal_point != 0) { + p = conv_10((long_int) decimal_point, FALSE, &exponent_is_negative, + &temp[EXPONENT_LENGTH], &t_len); + *s++ = exponent_is_negative ? '-' : '+'; + /* Make sure the exponent has at least 2 digits */ + if (t_len == 1) + *s++ = '0'; + while (t_len--) + *s++ = *p++; + } + else { + *s++ = '+'; + *s++ = '0'; + *s++ = '0'; + } + } + + *len = (int)(s - buf); + return buf; +} + +/* + * Convert num to a base X number where X is a power of 2. nbits determines X. + * For example, if nbits is 3, we do base 8 conversion + * Return value: + * a pointer to a string containing the number + * The caller provides a buffer for the string: that is the buf_end + * argument which is a pointer to the END of the buffer + 1 (i.e. if the + * buffer is declared as buf[100], buf_end should be &buf[100]) As with + * conv_10, we have a faster version which is used when the number isn't + * quad size. + */ + +static const char low_digits[] = "0123456789abcdef"; +static const char upper_digits[] = "0123456789ABCDEF"; + +static char * +conv_p2( + register u_long_int num, + register int nbits, + char format, + char *buf_end, + register size_t *len) +{ + register int mask = (1 << nbits) - 1; + register char *p = buf_end; + register const char *digits = (format == 'X') ? upper_digits : low_digits; + + do { + *--p = digits[num & mask]; + num >>= nbits; + } while (num); + *len = (int)(buf_end - p); + return p; +} + +static char * +conv_p2_quad( + u_quad_int num, + register int nbits, + char format, + char *buf_end, + register size_t *len) +{ + register int mask = (1 << nbits) - 1; + register char *p = buf_end; + register const char *digits = (format == 'X') ? upper_digits : low_digits; + + do { + *--p = digits[num & mask]; + num >>= nbits; + } while (num); + *len = (size_t)(buf_end - p); + return p; +} + +/* + * l2_util_format(), the generic printf-style formatting routine + * and heart of this piece of source. + */ +int +l2_util_format( + l2_util_format_t *vbuff, + const char *fmt, + va_list ap) +{ + register char *sp; + register char *bep; + register int cc = 0; + register int i; + + char *s = NULL; + char *q; + size_t s_len; + + register int min_width = 0; + int precision = 0; + enum { LEFT, RIGHT } adjust; + char pad_char; + char prefix_char; + + double fp_num; + quad_int i_quad = (quad_int)0; + u_quad_int ui_quad; + long_int i_num = (long_int)0; + u_long_int ui_num; + + char num_buf[NUM_BUF_SIZE]; + char char_buf[2]; /* for printing %% and % */ + + enum var_type_enum { IS_QUAD, IS_LONG, IS_SHORT, IS_INT }; + enum var_type_enum var_type = IS_INT; + + int alternate_form; + int print_sign; + int print_blank; + int adjust_precision; + int adjust_width; + int is_negative; + + char extinfo[20]; + + sp = vbuff->curpos; + bep = vbuff->endpos; + + while (*fmt != NUL) { + if (*fmt != '%') { + INS_CHAR(*fmt, sp, bep, cc); + } + else { + /* + * Default variable settings + */ + adjust = RIGHT; + alternate_form = print_sign = print_blank = FALSE; + pad_char = ' '; + prefix_char = NUL; + extinfo[0] = NUL; + + fmt++; + + /* + * Try to avoid checking for flags, width or precision + */ + if (!l2_util_islower(*fmt)) { + /* + * Recognize flags: -, #, BLANK, + + */ + for (;; fmt++) { + if (*fmt == '{') { + i = 0; + for (fmt++; *fmt != '}' && *fmt != NUL; fmt++) { + if (i < sizeof(extinfo)-1) + extinfo[i++] = *fmt; + } + extinfo[i] = NUL; + } + else if (*fmt == '-') + adjust = LEFT; + else if (*fmt == '+') + print_sign = TRUE; + else if (*fmt == '#') + alternate_form = TRUE; + else if (*fmt == ' ') + print_blank = TRUE; + else if (*fmt == '0') + pad_char = '0'; + else + break; + } + + /* + * Check if a width was specified + */ + if (l2_util_isdigit(*fmt)) { + STR_TO_DEC(fmt, min_width); + adjust_width = TRUE; + } + else if (*fmt == '*') { + min_width = va_arg(ap, int); + fmt++; + adjust_width = TRUE; + if (min_width < 0) { + adjust = LEFT; + min_width = -min_width; + } + } + else + adjust_width = FALSE; + + /* + * Check if a precision was specified + * + * XXX: an unreasonable amount of precision may be specified + * resulting in overflow of num_buf. Currently we + * ignore this possibility. + */ + if (*fmt == '.') { + adjust_precision = TRUE; + fmt++; + if (l2_util_isdigit(*fmt)) { + STR_TO_DEC(fmt, precision); + } + else if (*fmt == '*') { + precision = va_arg(ap, int); + fmt++; + if (precision < 0) + precision = 0; + } + else + precision = 0; + } + else + adjust_precision = FALSE; + } + else + adjust_precision = adjust_width = FALSE; + + /* + * Modifier check + */ + if (*fmt == 'q') { + var_type = IS_QUAD; + fmt++; + } + else if (*fmt == 'l') { + var_type = IS_LONG; + fmt++; + } + else if (*fmt == 'h') { + var_type = IS_SHORT; + fmt++; + } + else { + var_type = IS_INT; + } + + /* + * Argument extraction and printing. + * First we determine the argument type. + * Then, we convert the argument to a string. + * On exit from the switch, s points to the string that + * must be printed, s_len has the length of the string + * The precision requirements, if any, are reflected in s_len. + * + * NOTE: pad_char may be set to '0' because of the 0 flag. + * It is reset to ' ' by non-numeric formats + */ + switch (*fmt) { + + /* Unsigned Decimal Integer */ + case 'u': + if (var_type == IS_QUAD) { + i_quad = va_arg(ap, u_quad_int); + s = conv_10_quad(i_quad, 1, &is_negative, + &num_buf[NUM_BUF_SIZE], &s_len); + } + else { + if (var_type == IS_LONG) + i_num = (long_int)va_arg(ap, u_long_int); + else if (var_type == IS_SHORT) + i_num = (long_int)(unsigned short)va_arg(ap, unsigned int); + else + i_num = (long_int)va_arg(ap, unsigned int); + s = conv_10(i_num, 1, &is_negative, + &num_buf[NUM_BUF_SIZE], &s_len); + } + FIX_PRECISION(adjust_precision, precision, s, s_len); + break; + + /* Signed Decimal Integer */ + case 'd': + case 'i': + if (var_type == IS_QUAD) { + i_quad = va_arg(ap, quad_int); + s = conv_10_quad(i_quad, 0, &is_negative, + &num_buf[NUM_BUF_SIZE], &s_len); + } + else { + if (var_type == IS_LONG) + i_num = (long_int)va_arg(ap, long_int); + else if (var_type == IS_SHORT) + i_num = (long_int)(short)va_arg(ap, int); + else + i_num = (long_int)va_arg(ap, int); + s = conv_10(i_num, 0, &is_negative, + &num_buf[NUM_BUF_SIZE], &s_len); + } + FIX_PRECISION(adjust_precision, precision, s, s_len); + + if (is_negative) + prefix_char = '-'; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + break; + + /* Unsigned Octal Integer */ + case 'o': + if (var_type == IS_QUAD) { + ui_quad = va_arg(ap, u_quad_int); + s = conv_p2_quad(ui_quad, 3, *fmt, + &num_buf[NUM_BUF_SIZE], &s_len); + } + else { + if (var_type == IS_LONG) + ui_num = (u_long_int) va_arg(ap, u_long_int); + else if (var_type == IS_SHORT) + ui_num = (u_long_int)(unsigned short)va_arg(ap, unsigned int); + else + ui_num = (u_long_int)va_arg(ap, unsigned int); + s = conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); + } + FIX_PRECISION(adjust_precision, precision, s, s_len); + if (alternate_form && *s != '0') { + *--s = '0'; + s_len++; + } + break; + + /* Unsigned Hexadecimal Integer */ + case 'x': + case 'X': + if (var_type == IS_QUAD) { + ui_quad = va_arg(ap, u_quad_int); + s = conv_p2_quad(ui_quad, 4, *fmt, + &num_buf[NUM_BUF_SIZE], &s_len); + } + else { + if (var_type == IS_LONG) + ui_num = (u_long_int)va_arg(ap, u_long_int); + else if (var_type == IS_SHORT) + ui_num = (u_long_int)(unsigned short)va_arg(ap, unsigned int); + else + ui_num = (u_long_int)va_arg(ap, unsigned int); + s = conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); + } + FIX_PRECISION(adjust_precision, precision, s, s_len); + if (alternate_form && i_num != 0) { + *--s = *fmt; /* 'x' or 'X' */ + *--s = '0'; + s_len += 2; + } + break; + + /* String */ + case 's': + s = va_arg(ap, char *); + if (s != NULL) { + s_len = strlen(s); + if (adjust_precision && precision < s_len) + s_len = precision; + } + else { + s = S_NULL; + s_len = S_NULL_LEN; + } + pad_char = ' '; + break; + + /* Double Floating Point (style 1) */ + case 'f': + case 'e': + case 'E': + fp_num = va_arg(ap, double); + if (isnan(fp_num)) { + s = "NaN"; + s_len = 3; + } + else if (isinf(fp_num)) { + s = "Inf"; + s_len = 3; + } + else { + /* use &num_buf[1], so that we have room for the sign */ + s = conv_fp(*fmt, fp_num, alternate_form, + (adjust_precision == FALSE) ? FLOAT_DIGITS : precision, + &is_negative, &num_buf[1], &s_len); + if (is_negative) + prefix_char = '-'; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + } + break; + + /* Double Floating Point (style 2) */ + case 'g': + case 'G': + fp_num = va_arg(ap, double); + if (isnan(fp_num)) { + s = "NaN"; + s_len = 3; + } + else if (isinf(fp_num)) { + s = "Inf"; + s_len = 3; + } + else { + if (adjust_precision == FALSE) + precision = FLOAT_DIGITS; + else if (precision == 0) + precision = 1; + /* use &num_buf[1], so that we have room for the sign */ + s = l2_util_gcvt(fp_num, precision, &num_buf[1], alternate_form); + if (*s == '-') + prefix_char = *s++; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + s_len = strlen(s); + if (alternate_form && (q = strchr(s, '.')) == NULL) { + s[s_len++] = '.'; + s[s_len] = NUL; /* delimit for following strchr() */ + } + if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) + *q = 'E'; + } + break; + + /* Single Character */ + case 'c': + char_buf[0] = (char) (va_arg(ap, int)); + s = &char_buf[0]; + s_len = 1; + pad_char = ' '; + break; + + /* The '%' Character */ + case '%': + char_buf[0] = '%'; + s = &char_buf[0]; + s_len = 1; + pad_char = ' '; + break; + + /* Special: Number of already written characters */ + case 'n': + if (var_type == IS_QUAD) + *(va_arg(ap, quad_int *)) = cc; + else if (var_type == IS_LONG) + *(va_arg(ap, long *)) = cc; + else if (var_type == IS_SHORT) + *(va_arg(ap, short *)) = cc; + else + *(va_arg(ap, int *)) = cc; + break; + + /* + * Pointer argument type. + */ + case 'p': +#if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == SIZEOF_VOID_P) + ui_quad = (u_quad_int) va_arg(ap, void *); + s = conv_p2_quad(ui_quad, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); +#else + ui_num = (u_long_int) va_arg(ap, void *); + s = conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); +#endif + pad_char = ' '; + break; + + case NUL: + /* + * The last character of the format string was %. + * We ignore it. + */ + continue; + + /* + * The default case is for unrecognized %'s. We print + * % to help the user identify what option is not + * understood. This is also useful in case the user + * wants to pass the output of format_converter to + * another function that understands some other % + * (like syslog). Note that we can't point s inside fmt + * because the unknown could be preceded by width + * etc. + */ + default: + s = NULL; + if (vbuff->format != NULL) { + vbuff->format(vbuff, + &prefix_char, &pad_char, &s, &s_len, + num_buf, NUM_BUF_SIZE, extinfo, *fmt, &ap); + } + if (s == NULL) { + char_buf[0] = '%'; + char_buf[1] = *fmt; + s = char_buf; + s_len = 2; + pad_char = ' '; + } + break; + } + + if (prefix_char != NUL && s != S_NULL && s != char_buf) { + *--s = prefix_char; + s_len++; + } + + if (adjust_width && adjust == RIGHT && min_width > s_len) { + if (pad_char == '0' && prefix_char != NUL) { + INS_CHAR(*s, sp, bep, cc); + s++; + s_len--; + min_width--; + } + PAD(min_width, s_len, pad_char); + } + + /* + * Print the string s. + */ + for (i = s_len; i != 0; i--) { + INS_CHAR(*s, sp, bep, cc); + s++; + } + + if (adjust_width && adjust == LEFT && min_width > s_len) + PAD(min_width, s_len, pad_char); + } + fmt++; + } + vbuff->curpos = sp; + return cc; +} + +/* + * l2_util_format -- format a new string. + * This is inspired by POSIX sprintf(3), but mainly provides the + * following differences: first it is actually a snprintf(3) style, i.e. + * it allows one to specify the maximum number of characters which are + * allowed to write. Second, it allows one to just count the number of + * characters which have to be written. + */ + +#define STR_FORMAT_BUFLEN 20 + +static int l2_util_flush_fake(l2_util_format_t *out_handle) +{ + out_handle->data[1].i += out_handle->data[2].i; + out_handle->curpos = (char *)out_handle->data[0].vp; + return 0; +} + +static int l2_util_flush_real(l2_util_format_t *out_handle) +{ + return -1; +} + +int l2_util_vsprintf(char *s, size_t n, const char *fmt, va_list ap) +{ + l2_util_format_t handle; + char buf[STR_FORMAT_BUFLEN]; + int rv; + + if (n == 0) + return 0; + if (s == NULL) { + /* fake formatting, i.e., calculate output length only */ + handle.curpos = buf; + handle.endpos = buf + sizeof(buf) - 1; + handle.flush = l2_util_flush_fake; + handle.format = NULL; + handle.data[0].vp = buf; + handle.data[1].i = 0; + handle.data[2].i = sizeof(buf); + rv = l2_util_format(&handle, fmt, ap); + if (rv == -1) + rv = (int)n; + } + else { + /* real formatting, i.e., create output */ + handle.curpos = s; + handle.endpos = s + n - 1; + handle.flush = l2_util_flush_real; + handle.format = NULL; + rv = l2_util_format(&handle, fmt, ap); + *(handle.curpos) = NUL; + if (rv == -1) + rv = (int)n; + } + return rv; +} + +char *l2_util_vasprintf(const char *fmt, va_list ap) +{ + va_list apbak; + char *s; + int rv; + + apbak = ap; + if ((rv = l2_util_vsprintf(NULL, -1, fmt, ap)) == -1) + return NULL; + if ((s = malloc(rv+1)) == NULL) + return NULL; + ap = apbak; + if ((rv = l2_util_vsprintf(s, rv+1, fmt, ap)) == -1) + return NULL; + return s; +} + +int l2_util_sprintf(char *s, size_t n, const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = l2_util_vsprintf(s, n, fmt, ap); + va_end(ap); + return rv; +} + +char *l2_util_asprintf(const char *fmt, ...) +{ + va_list ap; + char *rv; + + va_start(ap, fmt); + rv = l2_util_asprintf(fmt, ap); + va_end(ap); + return rv; +} + Index: ossp-pkg/l2/l2_ut_param.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ut_param.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/l2/l2_ut_param.c,v' | diff -u /dev/null - -L'ossp-pkg/l2/l2_ut_param.c' 2>/dev/null --- ossp-pkg/l2/l2_ut_param.c +++ - 2024-04-23 09:54:57.041593845 +0200 @@ -0,0 +1,104 @@ +/* +** L2 - OSSP Logging Library +** Copyright (c) 2001 The OSSP Project (http://www.ossp.org/) +** Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/) +** +** This file is part of OSSP L2, a flexible logging library which +** can be found at http://www.ossp.org/pkg/l2/. +** +** Permission to use, copy, modify, and distribute this software for +** any purpose with or without fee is hereby granted, provided that +** the above copyright notice and this permission notice appear in all +** copies. +** +** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 AUTHORS AND COPYRIGHT HOLDERS AND THEIR +** 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. +** +** l2_ut_param.c: parameter parsing support +*/ + +#include "l2.h" +#include "l2_p.h" + +#include + +l2_result_t l2_util_setparams(l2_param_t pa[], const char *fmt, va_list ap) +{ + const char *cpB, *cpE; + const char *cpC, *cpG; + int ok; + int i; + + if (pa == NULL || fmt == NULL || ap == NULL) + return L2_ERROR; + cpE = fmt; + while (*cpE != '\0') { + /* determine begin of parameter name */ + cpB = cpE; + while (*cpB == ',') + cpB++; + + /* determine end of parameter name */ + cpE = cpB; + while (*cpE != ',' && *cpE != '\0') + cpE++; + + /* try to match with configured parameters */ + ok = FALSE; + for (i = 0; pa[i].name != NULL; i++) { + cpC = pa[i].name; + cpG = cpB; + while (*cpC != '\0' && cpG < cpE) { + if (*cpC != *cpG) + break; + cpC++; + cpG++; + } + if (*cpC == '\0' && cpG == cpE) { + /* parameter matched, so store value */ + switch (pa[i].type) { + case L2_TYPE_CHAR: + *(char *)(pa[i].store) = va_get(ap, char); + break; + case L2_TYPE_SHORT: + *(short *)(pa[i].store) = va_get(ap, short); + break; + case L2_TYPE_INT: + *(int *)(pa[i].store) = va_get(ap, int); + break; + case L2_TYPE_LONG: + *(long *)(pa[i].store) = va_get(ap, long); + break; + case L2_TYPE_FLOAT: + *(float *)(pa[i].store) = va_get(ap, float); + break; + case L2_TYPE_DOUBLE: + *(double *)(pa[i].store) = va_get(ap, double); + break; + case L2_TYPE_CHARPTR: + *(char **)(pa[i].store) = va_get(ap, charptr); + break; + case L2_TYPE_VOIDPTR: + *(void **)(pa[i].store) = va_get(ap, voidptr); + break; + } + ok = TRUE; + break; + } + } + if (!ok) + return L2_ERROR; + } + return L2_OK; +} +