OSSP CVS Repository

ossp - Check-in [835]
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [Patchset]  [Tagging/Branching

Check-in Number: 835
Date: 2001-Sep-04 15:52:59 (local)
2001-Sep-04 13:52:59 (UTC)
User:rse
Branch:
Comment: Wohhooooo! Here comes the underlying message formatting support:

1. renamed l2_channel_setparam() to l2_util_setparam() because it is just a utility function and is not tied to any channel.

2. moved l2_util_setparam() to its own l2_ut_param.c source file.

3. added l2_ut_format.c which contains a slightly adjusted version of Str's str_format() stuff under the name l2_util_format().

4. use l2_util_format() in l2_stream.c instead of vsnprintf() and this way finally support l2_formatter_t callbacks.

5. cleanup adjustments to the l2_stream_formatter() API.

Let's rock...

Tickets:
Inspections:
Files:
ossp-pkg/l2/Makefile.in      1.10 -> 1.11     37 inserted, 2 deleted
ossp-pkg/l2/l2.h      1.12 -> 1.13     12 inserted, 11 deleted
ossp-pkg/l2/l2_ch_buffer.c      1.9 -> 1.10     1 inserted, 1 deleted
ossp-pkg/l2/l2_ch_file.c      1.8 -> 1.9     1 inserted, 1 deleted
ossp-pkg/l2/l2_ch_socket.c      1.6 -> 1.7     1 inserted, 1 deleted
ossp-pkg/l2/l2_ch_syslog.c      1.8 -> 1.9     1 inserted, 1 deleted
ossp-pkg/l2/l2_channel.c      1.10 -> 1.11     0 inserted, 70 deleted
ossp-pkg/l2/l2_p.h      1.9 -> 1.10     42 inserted, 1 deleted
ossp-pkg/l2/l2_stream.c      1.7 -> 1.8     45 inserted, 8 deleted
ossp-pkg/l2/l2_ut_format.c      added-> 1.1
ossp-pkg/l2/l2_ut_param.c      added-> 1.1

ossp-pkg/l2/Makefile.in 1.10 -> 1.11

--- 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


ossp-pkg/l2/l2.h 1.12 -> 1.13

--- 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__ */
 


ossp-pkg/l2/l2_ch_buffer.c 1.9 -> 1.10

--- 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;


ossp-pkg/l2/l2_ch_file.c 1.8 -> 1.9

--- 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;
 }


ossp-pkg/l2/l2_ch_socket.c 1.6 -> 1.7

--- 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;
 }


ossp-pkg/l2/l2_ch_syslog.c 1.8 -> 1.9

--- 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;
 }


ossp-pkg/l2/l2_channel.c 1.10 -> 1.11

--- 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;
-}
-


ossp-pkg/l2/l2_p.h 1.9 -> 1.10

--- 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__ */
 


ossp-pkg/l2/l2_stream.c 1.7 -> 1.8

--- 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;
 }


ossp-pkg/l2/l2_ut_format.c -> 1.1

*** /dev/null    Fri Apr 26 09:33:00 2024
--- -    Fri Apr 26 09:34:32 2024
***************
*** 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 <panos@alumni.cs.colorado.edu> 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 <stdio.h>
+ #include <stdlib.h>
+ #include <stdarg.h>
+ #include <string.h>
+ #include <ctype.h>
+ #include <math.h>
+ 
+ #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 %<unknown> */
+ 
+     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
+                  * %<char> 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 %<char>
+                  * (like syslog). Note that we can't point s inside fmt
+                  * because the unknown <char> 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;
+ }
+ 


ossp-pkg/l2/l2_ut_param.c -> 1.1

*** /dev/null    Fri Apr 26 09:33:00 2024
--- -    Fri Apr 26 09:34:32 2024
***************
*** 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 <string.h>
+ 
+ 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;
+ }
+ 

CVSTrac 2.0.1