Index: ossp-pkg/var/TODO RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/TODO,v rcsdiff -q -kk '-r1.27' '-r1.28' -u '/v/ossp/cvs/ossp-pkg/var/Attic/TODO,v' 2>/dev/null --- 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 Index: ossp-pkg/var/configure.ac RCS File: /v/ossp/cvs/ossp-pkg/var/configure.ac,v rcsdiff -q -kk '-r1.7' '-r1.8' -u '/v/ossp/cvs/ossp-pkg/var/configure.ac,v' 2>/dev/null --- 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) Index: ossp-pkg/var/var.c RCS File: /v/ossp/cvs/ossp-pkg/var/var.c,v rcsdiff -q -kk '-r1.85' '-r1.86' -u '/v/ossp/cvs/ossp-pkg/var/var.c,v' 2>/dev/null --- 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: 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 */ Index: ossp-pkg/var/var.h RCS File: /v/ossp/cvs/ossp-pkg/var/var.h,v rcsdiff -q -kk '-r1.27' '-r1.28' -u '/v/ossp/cvs/ossp-pkg/var/var.h,v' 2>/dev/null --- 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 +#include /* 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__ */ Index: ossp-pkg/var/var.pod RCS File: /v/ossp/cvs/ossp-pkg/var/var.pod,v rcsdiff -q -kk '-r1.28' '-r1.29' -u '/v/ossp/cvs/ossp-pkg/var/var.pod,v' 2>/dev/null --- 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, B, B, +B, +B, B. =item Variables: @@ -624,6 +626,26 @@ and C errors, I and I are undefined. +=item var_rc_t B(var_t *I, char **I, int I, const char *I, va_list I); + +This is a high-level function on top of B 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. + +It expands simple "C<%s>" (string, type "C"), "C<%d>" (integer +number, type "C") and "C<%c>" (character, type "C") constructs +in I. The values are taken from the variable argument vector I. +After this expansion the result is passed through B by +passing through the I, I and I arguments. +The final result is a malloc(3)'ed buffer provided in I which +the caller has to free(3) later. + +=item var_rc_t B(var_t *I, char **I, int I, const char *I, ...); + +This is just a wrapper around B which translates the +variable argument list into C. + =item var_rc_t B(var_t *I, var_rc_t I, char **I); This can be used to map any B return codes (as returned by all Index: ossp-pkg/var/var_test.c RCS File: /v/ossp/cvs/ossp-pkg/var/var_test.c,v rcsdiff -q -kk '-r1.42' '-r1.43' -u '/v/ossp/cvs/ossp-pkg/var/var_test.c,v' 2>/dev/null --- 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<123>\"\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<123>") != 0) { + fprintf(stderr, "Formatting failed: expected \"foo<123>\", 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; }