OSSP CVS Repository

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

Check-in Number: 1964
Date: 2002-Mar-07 13:11:09 (local)
2002-Mar-07 12:11:09 (UTC)
User:rse
Branch:
Comment: New API functions var_format and var_formatv which provide a convinience interface to printf-style expansion and variable expansion. var_format is like a combination of snprintf(3) and var_expand().

Example: Assume the variable value context gives "bar" for ${ARRAY[7]}, then the call..

    var_format(var, &tmp, 1, "foo${ARRAY[%d]}quux", 7);o

..results in tmp containing the malloc(3)'ed string "foobarquux". Thanks to Thomas L. for providing the hint to this functionality.

Tickets:
Inspections:
Files:
ossp-pkg/var/TODO      1.27 -> 1.28     0 inserted, 7 deleted
ossp-pkg/var/configure.ac      1.7 -> 1.8     1 inserted, 0 deleted
ossp-pkg/var/var.c      1.85 -> 1.86     195 inserted, 1 deleted
ossp-pkg/var/var.h      1.27 -> 1.28     4 inserted, 0 deleted
ossp-pkg/var/var.pod      1.28 -> 1.29     22 inserted, 0 deleted
ossp-pkg/var/var_test.c      1.42 -> 1.43     37 inserted, 0 deleted

ossp-pkg/var/TODO 1.27 -> 1.28

--- TODO 2002/03/07 09:42:43     1.27
+++ TODO 2002/03/07 12:11:09     1.28
@@ -1,11 +1,4 @@
                                OSSP var
 
- o New feature: allow var_expand (or with a new function
-   based on var_expand like var_printf) to first substitute printf-style
-   %x constructs and then the OSSP var constructs. This allow more
-   flexibility in expanding templates, because one can expand indices
-   and other dynamic stuff into the template inside variable constructs.
-   Think about var_printf("${array[%d]}", i); 
-
  o document exception handling
 


ossp-pkg/var/configure.ac 1.7 -> 1.8

--- configure.ac 2002/02/28 08:48:44     1.7
+++ configure.ac 2002/03/07 12:11:09     1.8
@@ -43,6 +43,7 @@
 sinclude(libtool.m4)
 AC_PROG_LIBTOOL
 
+AC_CHECK_FUNCS(snprintf)
 AC_CHECK_EXTLIB([OSSP ex], ex, __ex_ctx, ex.h, [AC_DEFINE(WITH_EX)])
 
 AC_CONFIG_HEADERS(config.h)


ossp-pkg/var/var.c 1.85 -> 1.86

--- var.c        2002/03/07 09:14:05     1.85
+++ var.c        2002/03/07 12:11:09     1.86
@@ -104,6 +104,134 @@
 
 /*
 **
+**  ==== FORMATTING FUNCTIONS ====
+**
+*/
+
+/* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
+static int 
+var_mvxprintf(
+    int (*output)(void *ctx, const char *buffer, size_t bufsize), void *ctx, 
+    const char *format, va_list ap)
+{
+    /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
+    char ibuf[((sizeof(int)*8)/3)+10]; 
+    char *cp;
+    char c;
+    int d;
+    int n;
+    int bytes;
+
+    if (format == NULL || ap == NULL)
+        return -1;
+    bytes = 0;
+    while (*format != '\0') {
+        if (*format == '%') {
+            c = *(format+1);
+            if (c == '%') {
+                /* expand "%%" */
+                cp = &c;
+                n = sizeof(char);
+            }
+            else if (c == 'c') {
+                /* expand "%c" */
+                c = (char)va_arg(ap, int);
+                cp = &c;
+                n = sizeof(char);
+            }
+            else if (c == 's') {
+                /* expand "%s" */
+                if ((cp = (char *)va_arg(ap, char *)) == NULL)
+                    cp = "(null)";
+                n = strlen(cp);
+            }
+            else if (c == 'd') {
+                /* expand "%d" */
+                d = (int)va_arg(ap, int);
+#ifdef HAVE_SNPRINTF
+                snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
+#else
+                sprintf(ibuf, "%d", d);                /* implicitly secure */
+#endif
+                cp = ibuf;
+                n = strlen(cp);
+            }
+            else {
+                /* any other "%X" */
+                cp = (char *)format;
+                n  = 2;
+            }
+            format += 2;
+        }
+        else {
+            /* plain text */
+            cp = (char *)format;
+            if ((format = strchr(cp, '%')) == NULL)
+                format = strchr(cp, '\0');
+            n = format - cp;
+        }
+        /* perform output operation */
+        if (output != NULL)
+            if ((n = output(ctx, cp, n)) == -1)
+                break;
+        bytes += n;
+    }
+    return bytes;
+}
+
+/* output callback function context for var_mvsnprintf() */
+typedef struct {
+    char *bufptr;
+    size_t buflen;
+} var_mvsnprintf_cb_t;
+
+/* output callback function for var_mvsnprintf() */
+static int 
+var_mvsnprintf_cb(
+    void *_ctx, 
+    const char *buffer, size_t bufsize)
+{
+    var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx;
+
+    if (bufsize > ctx->buflen)
+        return -1;
+    memcpy(ctx->bufptr, buffer, bufsize);
+    ctx->bufptr += bufsize;
+    ctx->buflen -= bufsize;
+    return bufsize;
+}
+
+/* minimal vsnprintf(3) variant which supports %{c,s,d} only */
+static int 
+var_mvsnprintf(
+    char *buffer, size_t bufsize, 
+    const char *format, va_list ap)
+{
+    int n;
+    var_mvsnprintf_cb_t ctx;
+
+    if (format == NULL || ap == NULL)
+        return -1;
+    if (buffer != NULL && bufsize == 0)
+        return -1;
+    if (buffer == NULL) 
+        /* just determine output length */
+        n = var_mvxprintf(NULL, NULL, format, ap);
+    else {
+        /* perform real output */
+        ctx.bufptr = buffer;
+        ctx.buflen = bufsize;
+        n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
+        if (n != -1 && ctx.buflen == 0)
+            n = -1;
+        if (n != -1)
+            *(ctx.bufptr) = '\0';
+    }
+    return n;
+}
+
+/*
+**
 **  ==== PARSE CONTEXT FUNCTIONS ====
 **
 */
@@ -2476,6 +2604,71 @@
     return VAR_RC(rc);
 }
 
+/* format and expand a string */
+var_rc_t 
+var_formatv(
+    var_t *var, 
+    char **dst_ptr, int force_expand, 
+    const char *fmt, va_list ap)
+{
+    var_rc_t rc;
+    va_list apbak;
+    char *cpBuf;
+    int nBuf;
+
+    /* argument sanity checks */
+    if (var == NULL || dst_ptr == NULL || fmt == NULL)
+        return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+
+    /* determine formatting buffer length */
+    apbak = ap;
+    nBuf = var_mvsnprintf(NULL, 0, fmt, ap);
+    ap = apbak;
+    if (nBuf == -1)
+        return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
+
+    /* perform formatting */
+    if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
+        return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
+    nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
+    if (nBuf == -1) {
+        free(cpBuf);
+        return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
+    }
+
+    /* perform expansion */
+    if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
+        free(cpBuf);
+        return VAR_RC(rc);
+    }
+
+    /* cleanup */
+    free(cpBuf);
+
+    return VAR_OK;
+}
+
+/* format and expand a string */
+var_rc_t 
+var_format(
+    var_t *var, 
+    char **dst_ptr, int force_expand, 
+    const char *fmt, ...)
+{
+    var_rc_t rc;
+    va_list ap;
+
+    /* argument sanity checks */
+    if (var == NULL || dst_ptr == NULL || fmt == NULL)
+        return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+
+    va_start(ap, fmt);
+    rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
+    va_end(ap);
+
+    return VAR_RC(rc);
+}
+
 /* var_rc_t to string mapping table */
 static const char *var_errors[] = {
     "everything ok",                                           /* VAR_OK = 0 */
@@ -2522,7 +2715,8 @@
     "unterminated loop construct",                             /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
     "invalid character in loop limits",                        /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
     "malformed operation argument list",                       /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
-    "undefined operation"                                      /* VAR_ERR_UNDEFINED_OPERATION */
+    "undefined operation",                                     /* VAR_ERR_UNDEFINED_OPERATION */
+    "formatting failure"                                       /* VAR_ERR_FORMATTING_FAILURE */
 };
 
 /* transslate a return code into its corresponding descriptive text */


ossp-pkg/var/var.h 1.27 -> 1.28

--- var.h        2002/03/04 11:53:27     1.27
+++ var.h        2002/03/07 12:11:09     1.28
@@ -31,10 +31,12 @@
 #define __VAR_H__
 
 #include <stdlib.h>
+#include <stdarg.h>
 
 /* Error codes */
 typedef enum {
     VAR_ERR_CALLBACK                        = -64,
+    VAR_ERR_FORMATTING_FAILURE              = -45,
     VAR_ERR_UNDEFINED_OPERATION             = -44,
     VAR_ERR_MALFORMED_OPERATION_ARGUMENTS   = -43,
     VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS     = -42,
@@ -123,6 +125,8 @@
 var_rc_t  var_config    (var_t  *var, var_config_t mode, ...);
 var_rc_t  var_unescape  (var_t  *var, const char *src_ptr, size_t src_len, char  *dst_ptr, size_t  dst_len, int all);
 var_rc_t  var_expand    (var_t  *var, const char *src_ptr, size_t src_len, char **dst_ptr, size_t *dst_len, int force_expand);
+var_rc_t  var_formatv   (var_t  *var, char **dst_ptr, int force_expand, const char *fmt, va_list ap);
+var_rc_t  var_format    (var_t  *var, char **dst_ptr, int force_expand, const char *fmt, ...);
 var_rc_t  var_strerror  (var_t  *var, var_rc_t rc, char **str);
 
 #endif /* __VAR_H__ */


ossp-pkg/var/var.pod 1.28 -> 1.29

--- var.pod      2002/03/07 09:14:05     1.28
+++ var.pod      2002/03/07 12:11:09     1.29
@@ -53,6 +53,8 @@
 B<var_config>,
 B<var_unescape>,
 B<var_expand>, 
+B<var_formatv>, 
+B<var_format>, 
 B<var_strerror>.
 
 =item Variables:
@@ -624,6 +626,26 @@
 and C<VAR_ERR_OUT_OF_MEMORY> errors, I<dst_ptr> and I<dst_len> are
 undefined.
 
+=item var_rc_t B<var_formatv>(var_t *I<var>, char **I<dst_ptr>, int I<force_expand>, const char *I<fmt>, va_list I<ap>);
+
+This is a high-level function on top of B<var_expand> which expands
+simple printf(3)-style constructs before expanding the complex variable
+constructs. So, this is something of a combination between sprintf(3)
+and B<var_expand>.
+
+It expands simple "C<%s>" (string, type "C<char *>"), "C<%d>" (integer
+number, type "C<int>") and "C<%c>" (character, type "C<int>") constructs
+in I<fmt>. The values are taken from the variable argument vector I<ap>.
+After this expansion the result is passed through B<var_expand> by
+passing through the I<var>, I<dst_ptr> and I<force_expand> arguments.
+The final result is a malloc(3)'ed buffer provided in I<dst_ptr> which
+the caller has to free(3) later.
+
+=item var_rc_t B<var_format>(var_t *I<var>, char **I<dst_ptr>, int I<force_expand>, const char *I<fmt>, ...);
+
+This is just a wrapper around B<var_formatv> which translates the
+variable argument list into C<va_list>.
+
 =item var_rc_t B<var_strerror>(var_t *I<var>, var_rc_t I<rc>, char **I<str>);
 
 This can be used to map any B<var_rc_t> return codes (as returned by all


ossp-pkg/var/var_test.c 1.42 -> 1.43

--- var_test.c   2002/03/07 09:14:05     1.42
+++ var_test.c   2002/03/07 12:11:09     1.43
@@ -295,6 +295,43 @@
         }
         free(tmp);
     }
+
+    fprintf(stderr, "Test case: formatting \"foo\"\n");
+    if ((rc = var_format(var, &tmp, 1, "foo")) != VAR_OK) {
+        var_strerror(var, rc, &err);
+        fprintf(stderr, "Formatting failed: %s (%d)\n", err, rc);
+        return 1;
+    }
+    if (strcmp(tmp, "foo") != 0) {
+        fprintf(stderr, "Formatting failed: expected \"foo\", got \"%s\"\n", tmp);
+        return 1;
+    }
+    free(tmp);
+
+    fprintf(stderr, "Test case: formatting \"foo<bar><123><x>\"\n");
+    if ((rc = var_format(var, &tmp, 1, "foo<%s><%d><%c>", "bar", 123, 'x')) != VAR_OK) {
+        var_strerror(var, rc, &err);
+        fprintf(stderr, "Formatting failed: %s (%d)\n", err, rc);
+        return 1;
+    }
+    if (strcmp(tmp, "foo<bar><123><x>") != 0) {
+        fprintf(stderr, "Formatting failed: expected \"foo<bar><123><x>\", got \"%s\"\n", tmp);
+        return 1;
+    }
+    free(tmp);
+
+    fprintf(stderr, "Test case: formatting \"foo${ARRAY[1]}bar\"\n");
+    if ((rc = var_format(var, &tmp, 1, "foo${ARRAY[%d]}bar", 1)) != VAR_OK) {
+        var_strerror(var, rc, &err);
+        fprintf(stderr, "Formatting failed: %s (%d)\n", err, rc);
+        return 1;
+    }
+    if (strcmp(tmp, "fooentry1bar") != 0) {
+        fprintf(stderr, "Formatting failed: expected \"fooentry1bar\", got \"%s\"\n", tmp);
+        return 1;
+    }
+    free(tmp);
+
     var_destroy(var);
     return 0;
 }

CVSTrac 2.0.1