OSSP CVS Repository

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

Check-in Number: 1908
Date: 2002-Feb-28 09:08:16 (local)
2002-Feb-28 08:08:16 (UTC)
User:rse
Branch:
Comment: HEADS UPS: Mega-commit, revamping the whole library! - completely new API - completely new manual page
Tickets:
Inspections:
Files:
ossp-pkg/var/.cvsignore      1.3 -> 1.4     9 inserted, 8 deleted
ossp-pkg/var/Makefile.in      1.4 -> 1.5     9 inserted, 1 deleted
ossp-pkg/var/TODO      1.12 -> 1.13     6 inserted, 3 deleted
ossp-pkg/var/VERSION      1.2 -> 1.3     2 inserted, 2 deleted
ossp-pkg/var/var.c      1.61 -> 1.62     325 inserted, 219 deleted
ossp-pkg/var/var.h      1.23 -> 1.24     29 inserted, 33 deleted
ossp-pkg/var/var.pod      1.20 -> 1.21     539 inserted, 738 deleted
ossp-pkg/var/var_play.c      added-> 1.1
ossp-pkg/var/var_qref.pod      1.3->removed
ossp-pkg/var/var_test.c      1.33 -> 1.34     22 inserted, 9 deleted

ossp-pkg/var/.cvsignore 1.3 -> 1.4

--- .cvsignore   2001/11/20 20:02:21     1.3
+++ .cvsignore   2002/02/28 08:08:16     1.4
@@ -1,13 +1,14 @@
-var_test
-var.3
-shtool
+Makefile
 config.guess
+config.h
+config.h.in
 config.sub
-ltmain.sh
-libtool.m4
 configure
-config.h.in
-config.h
-Makefile
+libtool.m4
+ltmain.sh
+shtool
 var-config
 var-config.1
+var.3
+var_play
+var_test


ossp-pkg/var/Makefile.in 1.4 -> 1.5

--- Makefile.in  2002/02/27 13:44:16     1.4
+++ Makefile.in  2002/02/28 08:08:16     1.5
@@ -53,6 +53,9 @@
 TST_NAME    = var_test
 TST_OBJS    = var_test.o
 
+PLY_NAME    = var_play
+PLY_OBJS    = var_play.o
+
 MAN_NAME    = var-config.1 var.3
 
 #   helper macro for generating a Unix manual page
@@ -68,7 +71,7 @@
 .SUFFIXES:
 .SUFFIXES: .c .o .lo
 
-all: $(LIB_NAME) $(TST_NAME) $(MAN_NAME)
+all: $(LIB_NAME) $(TST_NAME) $(PLY_NAME) $(MAN_NAME)
 
 .c.o:
         $(CC) $(CPPFLAGS) $(CFLAGS) -c $<
@@ -77,6 +80,7 @@
 
 $(LIB_OBJS): Makefile
 $(TST_OBJS): Makefile
+$(PLY_OBJS): Makefile
 
 $(LIB_NAME): $(LIB_OBJS)
         @$(LIBTOOL) --mode=link $(CC) -o $(LIB_NAME) $(LIB_OBJS) -rpath $(libdir) \
@@ -85,6 +89,9 @@
 $(TST_NAME): $(TST_OBJS) $(LIB_NAME)
         @$(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -o $(TST_NAME) $(TST_OBJS) $(LIB_NAME) $(LIBS)
 
+$(PLY_NAME): $(PLY_OBJS) $(LIB_NAME)
+        @$(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -o $(PLY_NAME) $(PLY_OBJS) $(LIB_NAME) $(LIBS)
+
 var-config.1: var-config.pod VERSION
         BASENAME="var-config"; SEC=1; \
         NAME="VAR"; ONELINE="Variable Expansion Library"; \
@@ -112,6 +119,7 @@
 clean:
         -$(RM) $(LIB_NAME) $(LIB_OBJS)
         -$(RM) $(TST_NAME) $(TST_OBJS)
+        -$(RM) $(PLY_NAME) $(PLY_OBJS)
 
 distclean: clean
         -$(RM) config.log config.status config.cache


ossp-pkg/var/TODO 1.12 -> 1.13

--- TODO 2002/02/27 11:56:01     1.12
+++ TODO 2002/02/28 08:08:16     1.13
@@ -1,5 +1,10 @@
                                OSSP var
 
+ o add README
+ o generate internals into context
+ o use internal context to carry information
+ o document exception handling
+ o document arithmetic expressions
  o document loop construct
 
  ---------------------------------------------------------------------
@@ -116,9 +121,7 @@
         ${foo[$#{foo}]}
  ---------------------------------------------------------------------
 
- o manual page style cleanup and perhaps merge with var_qref.pod?
-
- o add library context and move initialization and configuration
+ o add internal library context and move initialization and configuration
    into this context.
 
  o bugfix?: (thl)


ossp-pkg/var/VERSION 1.2 -> 1.3

--- VERSION      2002/02/27 13:44:16     1.2
+++ VERSION      2002/02/28 08:08:16     1.3
@@ -1,6 +1,6 @@
 
-  VERSION -- Version Information for OSSP var (syntax: Text)
+  VERSION -- Version Information for OSSP VAR (syntax: Text)
   [automatically generated and maintained by GNU shtool]
 
-  This is OSSP var, Version 0.1.0 (20-Nov-2001)
+  This is OSSP VAR, Version 0.9.0 (28-Feb-2002)
 


ossp-pkg/var/var.c 1.61 -> 1.62

--- var.c        2002/02/27 13:44:16     1.61
+++ var.c        2002/02/28 08:08:16     1.62
@@ -31,6 +31,8 @@
 #include "config.h"
 #endif
 
+#include <stdlib.h>
+#include <stdarg.h>
 #include <ctype.h>
 #include <string.h>
 #include <stdio.h>
@@ -56,75 +58,29 @@
 #define VAR_RC(rv) (rv)
 #endif /* WITH_EX */
 
-/* The default configuration for the parser. */
-
-const var_config_t var_config_default = {
-    '$',                        /* varinit */
-    '{',                        /* startdelim */
-    '}',                        /* enddelim */
-    '[',                        /* startindex */
-    ']',                        /* endindex */
-    '#',                        /* current_index */
-    '\\',                       /* escape */
-    "a-zA-Z0-9_"                /* namechars */
+/* the external context structure */
+struct var_st {
+    var_syntax_t   syntax;
+    var_cb_value_t cb_value_fct;
+    void          *cb_value_ctx;
 };
 
-/* The var_strerror() routine will map a var_rc_t into a text message. */
-
-const char *var_strerror(var_rc_t rc)
-{
-    static char *var_errors[] = {
-        "everything ok",                                                        /* VAR_OK = 0 */
-        "incomplete named character",                                           /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
-        "incomplete hexadecimal value",                                         /* VAR_ERR_INCOMPLETE_HEX */
-        "invalid hexadecimal value",                                            /* VAR_ERR_INVALID_HEX */
-        "octal value too large",                                                /* VAR_ERR_OCTAL_TOO_LARGE */
-        "invalid octal value",                                                  /* VAR_ERR_INVALID_OCTAL */
-        "incomplete octal value",                                               /* VAR_ERR_INCOMPLETE_OCTAL */
-        "incomplete grouped hexadecimal value",                                 /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
-        "incorrect character class specification",                              /* VAR_ERR_INCORRECT_CLASS_SPEC */
-        "invalid expansion configuration",                                      /* VAR_ERR_INVALID_CONFIGURATION */
-        "out of memory",                                                        /* VAR_ERR_OUT_OF_MEMORY */
-        "incomplete variable specification",                                    /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
-        "undefined variable",                                                   /* VAR_ERR_UNDEFINED_VARIABLE */
-        "input is neither text nor variable",                                   /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
-        "unknown command character in variable",                                /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
-        "malformated search and replace operation",                             /* VAR_ERR_MALFORMATTED_REPLACE */
-        "unknown flag in search and replace operation",                         /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
-        "invalid regular expression in search and replace operation",           /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
-        "missing parameter in command",                                         /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
-        "empty search string in search and replace operation",                  /* VAR_ERR_EMPTY_SEARCH_STRING */
-        "start offset missing in cut operation",                                /* VAR_ERR_MISSING_START_OFFSET */
-        "offsets in cut operation delimited by unknown character",              /* VAR_ERR_INVALID_OFFSET_DELIMITER */
-        "range out of bounds in cut operation",                                 /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
-        "offset out of bounds in cut operation",                                /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
-        "logic error in cut operation",                                         /* VAR_ERR_OFFSET_LOGIC */
-        "malformatted transpose operation",                                     /* VAR_ERR_MALFORMATTED_TRANSPOSE */
-        "source and destination classes do not match in transpose operation",   /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
-        "empty character class in transpose operation",                         /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
-        "incorrect character class in transpose operation",                     /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
-        "malformatted padding operation",                                       /* VAR_ERR_MALFORMATTED_PADDING */
-        "width parameter missing in padding operation",                         /* VAR_ERR_MISSING_PADDING_WIDTH */
-        "fill string missing in padding operation",                             /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
-        "unknown quoted pair in search and replace operation",                  /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
-        "sub-matching reference out of range",                                  /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
-        "invalid argument",                                                     /* VAR_ERR_INVALID_ARGUMENT */
-        "incomplete quoted pair",                                               /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
-        "lookup function does not support variable arrays",                     /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
-        "index specification of array variable contains an invalid character",  /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
-        "index specification of array variable is incomplete",                  /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
-        "bracket expression in array variable's index is not closed",           /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
-        "division by zero error in index specification",                        /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
-        "unterminated loop construct",                                          /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
-        "invalid character in loop limits"                                      /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
-    };
-
-    rc = 0 - rc;
-    if (rc < 0 || rc >= sizeof(var_errors) / sizeof(char *))
-        return "unknown error";
+/* the internal expansion context structure */
+typedef struct {
+    int            force_expand;
+} var_expand_t;
 
-    return var_errors[rc];
-}
+/* the default syntax configuration */
+static const var_syntax_t var_syntax_default = {
+    '\\',         /* escape */
+    '$',          /* delim_init */
+    '{',          /* delim_open */
+    '}',          /* delim_close */
+    '[',          /* index_open */
+    ']',          /* index_close */
+    '#',          /* index_mark */
+    "a-zA-Z0-9_"  /* name_chars */
+};
 
 /* Routines for manipulation of token buffers. */
 
@@ -384,85 +340,25 @@
         return expand_simple_hex(src, dst, end);
 }
 
-var_rc_t var_unescape(const char *src, size_t len, char *dst,
-                      int unescape_all)
-{
-    const char *end = src + len;
-    var_rc_t rc;
-
-    while (src < end) {
-        if (*src == '\\') {
-            if (++src == end)
-                return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
-            switch (*src) {
-            case '\\':
-                if (!unescape_all) {
-                    *dst++ = '\\';
-                }
-                *dst++ = '\\';
-                break;
-            case 'n':
-                *dst++ = '\n';
-                break;
-            case 't':
-                *dst++ = '\t';
-                break;
-            case 'r':
-                *dst++ = '\r';
-                break;
-            case 'x':
-                ++src;
-                if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
-                    return VAR_RC(rc);
-                break;
-            case '0':
-            case '1':
-            case '2':
-            case '3':
-            case '4':
-            case '5':
-            case '6':
-            case '7':
-            case '8':
-            case '9':
-                if (end - src >= 3 && isdigit((int)src[1]) && isdigit((int)src[2])) {
-                    if ((rc = expand_octal(&src, &dst, end)) != 0)
-                        return VAR_RC(rc);
-                    break;
-                }
-            default:
-                if (!unescape_all) {
-                    *dst++ = '\\';
-                }
-                *dst++ = *src;
-            }
-            ++src;
-        } else
-            *dst++ = *src++;
-    }
-    *dst = '\0';
-    return VAR_OK;
-}
-
 /* The recursive-descent parser for variable expressions. */
 
 static int variable(const char *begin, const char *end,
-                    const var_config_t *config, const char_class_t nameclass,
-                    var_cb_t lookup, void *lookup_context,
-                    int force_expand, tokenbuf_t *result, int current_index,
+                    const var_syntax_t *config, const char_class_t nameclass,
+                    var_cb_value_t lookup, void *lookup_context,
+                    int force_expand, tokenbuf_t *result, int index_mark,
                     int* rel_lookup_flag);
 static int command(const char *begin, const char *end,
-                   const var_config_t *config, const char_class_t nameclass,
-                   var_cb_t lookup, void *lookup_context, int force_expand,
-                   tokenbuf_t *data, int current_index, int* rel_lookup_flag);
-static int num_exp(const char *begin, const char *end, int current_index,
+                   const var_syntax_t *config, const char_class_t nameclass,
+                   var_cb_value_t lookup, void *lookup_context, int force_expand,
+                   tokenbuf_t *data, int index_mark, int* rel_lookup_flag);
+static int num_exp(const char *begin, const char *end, int index_mark,
                    int* result, int* failed, int* rel_lookup_flag,
-                   const var_config_t *config,
+                   const var_syntax_t *config,
                    const char_class_t nameclass,
-                   var_cb_t lookup, void* lookup_context);
+                   var_cb_value_t lookup, void* lookup_context);
 
-static int text(const char *begin, const char *end, char varinit,
-                char startindex, char endindex, char escape)
+static int text(const char *begin, const char *end, char delim_init,
+                char index_open, char index_close, char escape)
 {
     const char *p;
 
@@ -471,9 +367,9 @@
             if (++p == end)
                 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
         }
-        else if (*p == varinit)
+        else if (*p == delim_init)
             break;
-        else if (startindex && (*p == startindex || *p == endindex))
+        else if (index_open && (*p == index_open || *p == index_close))
             break;
     }
     return p - begin;
@@ -499,11 +395,11 @@
 }
 
 static int substext(const char *begin, const char *end,
-                    const var_config_t *config)
+                    const var_syntax_t *config)
 {
     const char *p;
 
-    for (p = begin; p != end && *p != config->varinit && *p != '/'; p++) {
+    for (p = begin; p != end && *p != config->delim_init && *p != '/'; p++) {
         if (*p == config->escape) {
             if (p + 1 == end)
                 return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
@@ -514,14 +410,14 @@
 }
 
 static int exptext(const char *begin, const char *end,
-                   const var_config_t *config)
+                   const var_syntax_t *config)
 {
     const char *p;
 
     for (p = begin;
             p != end
-         && *p != config->varinit
-         && *p != config->enddelim
+         && *p != config->delim_init
+         && *p != config->delim_close
          && *p != ':'; p++) {
         if (*p == config->escape) {
             if (p + 1 == end)
@@ -544,11 +440,11 @@
     return num;
 }
 
-static int num_exp_read_operand(const char *begin, const char *end, int current_index,
+static int num_exp_read_operand(const char *begin, const char *end, int index_mark,
                                 int *result, int *failed, int *rel_lookup_flag,
-                                const var_config_t *config,
+                                const var_syntax_t *config,
                                 const char_class_t nameclass,
-                                var_cb_t lookup, void *lookup_context)
+                                var_cb_value_t lookup, void *lookup_context)
 {
     const char* p = begin;
     tokenbuf_t tmp;
@@ -560,7 +456,7 @@
         return VAR_ERR_INCOMPLETE_INDEX_SPEC;
 
     if (*p == '(') {
-        rc = num_exp(++p, end, current_index, result, failed,
+        rc = num_exp(++p, end, index_mark, result, failed,
                      rel_lookup_flag, config, nameclass, lookup, lookup_context);
         if (rc < 0)
             return rc;
@@ -571,13 +467,13 @@
             return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
         ++p;
     }
-    else if (*p == config->varinit) {
+    else if (*p == config->delim_init) {
         rc = variable(p, end, config, nameclass, lookup,
-                      lookup_context, 1, &tmp, current_index, rel_lookup_flag);
+                      lookup_context, 1, &tmp, index_mark, rel_lookup_flag);
         if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
             *failed = 1;
             rc = variable(p, end, config, nameclass, lookup,
-                          lookup_context, 0, &tmp, current_index, rel_lookup_flag);
+                          lookup_context, 0, &tmp, index_mark, rel_lookup_flag);
             if (rc < 0)
                 return rc;
             p += rc;
@@ -587,16 +483,16 @@
             if (rc < 0)
                 return rc;
             p += rc;
-            rc = num_exp(tmp.begin, tmp.end, current_index, result,
+            rc = num_exp(tmp.begin, tmp.end, index_mark, result,
                          failed, rel_lookup_flag, config, nameclass, lookup, lookup_context);
             tokenbuf_free(&tmp);
             if (rc < 0)
                 return rc;
         }
     }
-    else if (config->current_index && *p == config->current_index) {
+    else if (config->index_mark && *p == config->index_mark) {
         p++;
-        *result = current_index;
+        *result = index_mark;
         (*rel_lookup_flag)++;
     }
     else if (isdigit(*p)) {
@@ -625,11 +521,11 @@
     return p - begin;
 }
 
-static int num_exp(const char *begin, const char *end, int current_index,
+static int num_exp(const char *begin, const char *end, int index_mark,
                    int *result, int *failed, int *rel_lookup_flag,
-                   const var_config_t *config,
+                   const var_syntax_t *config,
                    const char_class_t nameclass,
-                   var_cb_t lookup, void *lookup_context)
+                   var_cb_value_t lookup, void *lookup_context)
 {
     const char *p = begin;
     char operator;
@@ -639,7 +535,7 @@
     if (begin == end)
         return VAR_ERR_INCOMPLETE_INDEX_SPEC;
 
-    rc = num_exp_read_operand(p, end, current_index, result,
+    rc = num_exp_read_operand(p, end, index_mark, result,
                               failed, rel_lookup_flag, config, nameclass,
                               lookup, lookup_context);
     if (rc < 0)
@@ -649,7 +545,7 @@
     while (p != end) {
         if (*p == '+' || *p == '-') {
             operator = *p++;
-            rc = num_exp(p, end, current_index, &right, failed,
+            rc = num_exp(p, end, index_mark, &right, failed,
                          rel_lookup_flag, config, nameclass, lookup, lookup_context);
             if (rc < 0)
                 return rc;
@@ -661,7 +557,7 @@
         }
         else if (*p == '*' || *p == '/' || *p == '%') {
             operator = *p++;
-            rc = num_exp_read_operand(p, end, current_index, &right, failed,
+            rc = num_exp_read_operand(p, end, index_mark, &right, failed,
                                       rel_lookup_flag, config, nameclass, lookup, lookup_context);
             if (rc < 0)
                 return rc;
@@ -697,10 +593,10 @@
 }
 
 static int expression(const char *begin, const char *end,
-                      const var_config_t *config,
-                      const char_class_t nameclass, var_cb_t lookup,
+                      const var_syntax_t *config,
+                      const char_class_t nameclass, var_cb_value_t lookup,
                       void *lookup_context, int force_expand,
-                      tokenbuf_t *result, int current_index, int *rel_lookup_flag)
+                      tokenbuf_t *result, int index_mark, int *rel_lookup_flag)
     {
     const char *p = begin;
     const char *data;
@@ -719,7 +615,7 @@
 
     /* Expect STARTDELIM. */
 
-    if (p == end || *p != config->startdelim)
+    if (p == end || *p != config->delim_open)
         return 0;
 
     if (++p == end)
@@ -741,7 +637,7 @@
         }
 
         rc = variable(p, end, config, nameclass, lookup, lookup_context,
-                      force_expand, &tmp, current_index, rel_lookup_flag);
+                      force_expand, &tmp, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         if (rc > 0) {
@@ -763,8 +659,8 @@
 
     /* If the next token is START-INDEX, read the index specification. */
 
-    if (config->startindex && *p == config->startindex) {
-        rc = num_exp(++p, end, current_index, &idx, &failed,
+    if (config->index_open && *p == config->index_open) {
+        rc = num_exp(++p, end, index_mark, &idx, &failed,
                      rel_lookup_flag, config, nameclass, lookup, lookup_context);
         if (rc < 0)
             goto error_return;
@@ -778,7 +674,7 @@
             rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
             goto error_return;
         }
-        if (*p != config->endindex) {
+        if (*p != config->index_close) {
             rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
             goto error_return;
         }
@@ -788,7 +684,7 @@
     /* Now we have the name of the variable stored in "name". The next
        token here must either be an END-DELIM or a ':'. */
 
-    if (p == end || (*p != config->enddelim && *p != ':')) {
+    if (p == end || (*p != config->delim_close && *p != ':')) {
         rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
         goto error_return;
     }
@@ -839,11 +735,11 @@
             if (!failed)
                 rc = command(p, end, config, nameclass, lookup,
                              lookup_context, force_expand, result,
-                             current_index, rel_lookup_flag);
+                             index_mark, rel_lookup_flag);
             else
                 rc = command(p, end, config, nameclass, lookup,
                              lookup_context, force_expand, &tmp,
-                             current_index, rel_lookup_flag);
+                             index_mark, rel_lookup_flag);
             if (rc < 0)
                 goto error_return;
             p += rc;
@@ -851,7 +747,7 @@
                 result->end += rc;
         }
 
-        if (p == end || *p != config->enddelim) {
+        if (p == end || *p != config->delim_close) {
             rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
             goto error_return;
         }
@@ -876,9 +772,9 @@
 }
 
 static int variable(const char *begin, const char *end,
-                    const var_config_t *config, const char_class_t nameclass,
-                    var_cb_t lookup, void *lookup_context,
-                    int force_expand, tokenbuf_t *result, int current_index,
+                    const var_syntax_t *config, const char_class_t nameclass,
+                    var_cb_value_t lookup, void *lookup_context,
+                    int force_expand, tokenbuf_t *result, int index_mark,
                     int *rel_lookup_flag)
 {
     const char *p = begin;
@@ -893,7 +789,7 @@
 
     /* Expect VARINIT. */
 
-    if (p == end || *p != config->varinit)
+    if (p == end || *p != config->delim_init)
         return 0;
 
     if (++p == end)
@@ -924,17 +820,17 @@
     /* OK, we're dealing with a complex expression here. */
 
     rc = expression(p, end, config, nameclass, lookup, lookup_context,
-                    force_expand, result, current_index, rel_lookup_flag);
+                    force_expand, result, index_mark, rel_lookup_flag);
     if (rc > 0)
         rc++;
     return rc;
 }
 
 static int exptext_or_variable(const char *begin, const char *end,
-                               const var_config_t *config,
-                               const char_class_t nameclass, var_cb_t lookup,
+                               const var_syntax_t *config,
+                               const char_class_t nameclass, var_cb_value_t lookup,
                                void *lookup_context, int force_expand,
-                               tokenbuf_t *result, int current_index,
+                               tokenbuf_t *result, int index_mark,
                                int *rel_lookup_flag)
 {
     const char *p = begin;
@@ -960,7 +856,7 @@
         }
 
         rc = variable(p, end, config, nameclass, lookup, lookup_context,
-                      force_expand, &tmp, current_index, rel_lookup_flag);
+                      force_expand, &tmp, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         if (rc > 0) {
@@ -983,10 +879,10 @@
 }
 
 static int substext_or_variable(const char *begin, const char *end,
-                                const var_config_t *config,
-                                const char_class_t nameclass, var_cb_t lookup,
+                                const var_syntax_t *config,
+                                const char_class_t nameclass, var_cb_value_t lookup,
                                 void *lookup_context, int force_expand,
-                                tokenbuf_t *result, int current_index,
+                                tokenbuf_t *result, int index_mark,
                                 int *rel_lookup_flag)
 {
     const char *p = begin;
@@ -1012,7 +908,7 @@
         }
 
         rc = variable(p, end, config, nameclass, lookup, lookup_context,
-                      force_expand, &tmp, current_index, rel_lookup_flag);
+                      force_expand, &tmp, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         if (rc > 0) {
@@ -1469,9 +1365,9 @@
 }
 
 static int command(const char *begin, const char *end,
-                   const var_config_t *config, const char_class_t nameclass,
-                   var_cb_t lookup, void *lookup_context, int force_expand,
-                   tokenbuf_t *data, int current_index, int *rel_lookup_flag)
+                   const var_syntax_t *config, const char_class_t nameclass,
+                   var_cb_value_t lookup, void *lookup_context, int force_expand,
+                   tokenbuf_t *data, int index_mark, int *rel_lookup_flag)
 {
     const char *p = begin;
     tokenbuf_t tmptokbuf;
@@ -1577,7 +1473,7 @@
         p++;
         rc = exptext_or_variable(p, end, config, nameclass, lookup,
                                  lookup_context, force_expand, &tmptokbuf,
-                                 current_index, rel_lookup_flag);
+                                 index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         if (rc == 0) {
@@ -1594,7 +1490,7 @@
     case '*':                   /* Return "" if data is not empty, parameter otherwise. */
         p++;
         rc = exptext_or_variable(p, end, config, nameclass, lookup,
-                                 lookup_context, force_expand, &tmptokbuf, current_index, rel_lookup_flag);
+                                 lookup_context, force_expand, &tmptokbuf, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         if (rc == 0) {
@@ -1617,7 +1513,7 @@
     case '+':                   /* Substitute parameter if data is not empty. */
         p++;
         rc = exptext_or_variable(p, end, config, nameclass, lookup,
-                                 lookup_context, force_expand, &tmptokbuf, current_index, rel_lookup_flag);
+                                 lookup_context, force_expand, &tmptokbuf, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         if (rc == 0) {
@@ -1639,7 +1535,7 @@
         p++;
 
         rc = substext_or_variable(p, end, config, nameclass, lookup,
-                                  lookup_context, force_expand, &search, current_index, rel_lookup_flag);
+                                  lookup_context, force_expand, &search, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         p += rc;
@@ -1651,7 +1547,7 @@
         p++;
 
         rc = substext_or_variable(p, end, config, nameclass, lookup,
-                                  lookup_context, force_expand, &replace, current_index, rel_lookup_flag);
+                                  lookup_context, force_expand, &replace, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         p += rc;
@@ -1685,7 +1581,7 @@
         p++;
 
         rc = substext_or_variable(p, end, config, nameclass, lookup,
-                                  lookup_context, force_expand, &search, current_index, rel_lookup_flag);
+                                  lookup_context, force_expand, &search, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         p += rc;
@@ -1697,7 +1593,7 @@
         p++;
 
         rc = substext_or_variable(p, end, config, nameclass, lookup,
-                                  lookup_context, force_expand, &replace, current_index, rel_lookup_flag);
+                                  lookup_context, force_expand, &replace, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         p += rc;
@@ -1740,7 +1636,7 @@
         p++;
 
         rc = substext_or_variable(p, end, config, nameclass, lookup,
-                                  lookup_context, force_expand, &replace, current_index, rel_lookup_flag);
+                                  lookup_context, force_expand, &replace, index_mark, rel_lookup_flag);
         if (rc < 0)
             goto error_return;
         p += rc;
@@ -1790,7 +1686,7 @@
 }
 
 struct wrapper_context {
-    var_cb_t lookup;
+    var_cb_value_t lookup;
     void    *context;
     int     *rel_lookup_flag;
 };
@@ -1817,9 +1713,9 @@
 }
 
 static var_rc_t loop_limits(const char *begin, const char *end,
-                            const var_config_t *config,
+                            const var_syntax_t *config,
                             const char_class_t nameclass,
-                            var_cb_t lookup, void* lookup_context,
+                            var_cb_value_t lookup, void* lookup_context,
                             int* start, int* step, int* stop, int* open_end)
     {
     const char* p = begin;
@@ -1830,7 +1726,7 @@
     if (begin == end)
         return 0;
 
-    if (*p != config->startdelim)
+    if (*p != config->delim_open)
         return 0;
     else
         ++p;
@@ -1870,7 +1766,7 @@
 
     if (*p != ',')
         {
-        if (*p != config->enddelim)
+        if (*p != config->delim_close)
             return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
         else
             {
@@ -1907,17 +1803,17 @@
     if (failed)
         return VAR_ERR_UNDEFINED_VARIABLE;
 
-    if (*p != config->enddelim)
+    if (*p != config->delim_close)
         return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
 
     return ++p - begin;
     }
 
 static var_rc_t input(const char *begin, const char *end,
-                      const var_config_t *config,
-                      const char_class_t nameclass, var_cb_t lookup,
+                      const var_syntax_t *config,
+                      const char_class_t nameclass, var_cb_value_t lookup,
                       void *lookup_context, int force_expand,
-                      tokenbuf_t *output, int current_index,
+                      tokenbuf_t *output, int index_mark,
                       size_t recursion_level, int *rel_lookup_flag)
 {
     const char *p = begin;
@@ -1939,7 +1835,7 @@
     }
 
     do {
-        if (begin != end && config->startindex && *begin == config->startindex) {
+        if (begin != end && config->index_open && *begin == config->index_open) {
             original_rel_lookup_state = *rel_lookup_flag;
             loop_limit_length = -1;
             wcon.lookup = lookup;
@@ -1963,7 +1859,7 @@
                            &wcon, 1, output, i, recursion_level+1, rel_lookup_flag);
                 if (rc < 0)
                     goto error_return;
-                if (begin[rc] != config->endindex) {
+                if (begin[rc] != config->index_close) {
                     rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
                     goto error_return;
                 }
@@ -1998,8 +1894,8 @@
             continue;
         }
 
-        rc = text(begin, end, config->varinit, config->startindex,
-                  config->endindex, config->escape);
+        rc = text(begin, end, config->delim_init, config->index_open,
+                  config->index_close, config->escape);
         if (rc > 0) {
             if (!tokenbuf_append(output, begin, rc)) {
                 rc = VAR_ERR_OUT_OF_MEMORY;
@@ -2012,7 +1908,7 @@
 
         rc = variable(begin, end, config, nameclass, lookup,
                       lookup_context, force_expand, &result,
-                      current_index, rel_lookup_flag);
+                      index_mark, rel_lookup_flag);
         if (rc > 0) {
             if (!tokenbuf_append(output, result.begin, result.end - result.begin)) {
                 rc = VAR_ERR_OUT_OF_MEMORY;
@@ -2041,10 +1937,11 @@
     return rc;
 }
 
-var_rc_t var_expand(const char *input_buf, size_t input_len,
-                    char **result, size_t *result_len,
-                    var_cb_t lookup, void *lookup_context,
-                    const var_config_t *config, int force_expand)
+static var_rc_t internal_expand(
+    const char *input_buf, size_t input_len,
+    char **result, size_t *result_len,
+    var_cb_value_t lookup, void *lookup_context,
+    const var_syntax_t *config, int force_expand)
 {
     char_class_t nameclass;
     var_rc_t rc;
@@ -2058,21 +1955,21 @@
 
     /* Optionally use default configuration */
     if (config == NULL)
-        config = &var_config_default;
+        config = &var_syntax_default;
 
     /* Set the result pointer to the begining of the input buffer so
        that it is correctly initialized in case we fail with an error. */
     *result = (char *)input_buf;
 
     /* Expand the class description for valid variable names. */
-    if ((rc = expand_character_class(config->namechars, nameclass)) != VAR_OK)
+    if ((rc = expand_character_class(config->name_chars, nameclass)) != VAR_OK)
         return VAR_RC(rc);
 
     /* Make sure that the specials defined in the configuration do not
        appear in the character name class. */
-    if (nameclass[(int)config->varinit] ||
-        nameclass[(int)config->startdelim] ||
-        nameclass[(int)config->enddelim] ||
+    if (nameclass[(int)config->delim_init] ||
+        nameclass[(int)config->delim_open] ||
+        nameclass[(int)config->delim_close] ||
         nameclass[(int)config->escape])
         return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
 
@@ -2108,3 +2005,212 @@
     return VAR_RC(rc);
 }
 
+/* ------------------------------------------------------------------ */
+
+var_rc_t 
+var_create(
+    var_t **pvar)
+{
+    var_t *var;
+
+    if (pvar == NULL)
+        return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
+        return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
+    memset(var, 0, sizeof(var));
+    var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
+    *pvar = var;
+    return VAR_OK;
+}
+
+var_rc_t 
+var_destroy(
+    var_t *var)
+{
+    if (var == NULL)
+        return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    free(var);
+    return VAR_OK;
+}
+
+var_rc_t 
+var_config(
+    var_t *var, 
+    var_config_t mode, 
+    ...)
+{
+    va_list ap;
+
+    if (var == NULL)
+        return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    va_start(ap, mode);
+    switch (mode) {
+        case VAR_CONFIG_SYNTAX: {
+            var_syntax_t *s;
+            s = (var_syntax_t *)va_arg(ap, void *);
+            if (s == NULL)
+                return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+            var->syntax.escape      = s->escape;
+            var->syntax.delim_init  = s->delim_init;
+            var->syntax.delim_open  = s->delim_open;
+            var->syntax.delim_close = s->delim_close;
+            var->syntax.index_open  = s->index_open;
+            var->syntax.index_close = s->index_close;
+            var->syntax.index_mark  = s->index_mark;
+            if (var->syntax.name_chars != NULL)
+                free(var->syntax.name_chars);
+            var->syntax.name_chars = strdup(s->name_chars);
+            break;
+        }
+        case VAR_CONFIG_CB_VALUE: {
+            var_cb_value_t fct;
+            void *ctx;
+            fct = (var_cb_value_t)va_arg(ap, void *);
+            ctx = (void *)va_arg(ap, void *);
+            var->cb_value_fct = fct;
+            var->cb_value_ctx = ctx;
+            break;
+        }
+        default:
+            return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    }
+    va_end(ap);
+    return VAR_OK;
+}
+
+var_rc_t 
+var_unescape(
+    var_t *var, 
+    const char *src, size_t srclen, 
+    char *dst, size_t dstlen, 
+    int all)
+{
+    const char *end;
+    var_rc_t rc;
+
+    if (var == NULL || src == NULL || dst == NULL)
+        return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    end = src + srclen;
+    while (src < end) {
+        if (*src == '\\') {
+            if (++src == end)
+                return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
+            switch (*src) {
+                case '\\':
+                    if (!all) {
+                        *dst++ = '\\';
+                    }
+                    *dst++ = '\\';
+                    break;
+                case 'n':
+                    *dst++ = '\n';
+                    break;
+                case 't':
+                    *dst++ = '\t';
+                    break;
+                case 'r':
+                    *dst++ = '\r';
+                    break;
+                case 'x':
+                    ++src;
+                    if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
+                        return VAR_RC(rc);
+                    break;
+                case '0': case '1': case '2': case '3': case '4':
+                case '5': case '6': case '7': case '8': case '9':
+                    if (   end - src >= 3 
+                        && isdigit((int)src[1]) 
+                        && isdigit((int)src[2])) {
+                        if ((rc = expand_octal(&src, &dst, end)) != 0)
+                            return VAR_RC(rc);
+                        break;
+                    }
+                default:
+                    if (!all) {
+                        *dst++ = '\\';
+                    }
+                    *dst++ = *src;
+            }
+            ++src;
+        } else
+            *dst++ = *src++;
+    }
+    *dst = '\0';
+    return VAR_OK;
+}
+
+var_rc_t 
+var_expand(
+    var_t *var, 
+    const char *src, size_t srclen, 
+    char **dst, size_t *dstlen, 
+    int force_expand)
+{
+    /* var_expand_t ctx; */
+    var_rc_t rc;
+
+    /* ctx.force_expand = force_expand; */
+    rc = internal_expand(src, srclen, dst, dstlen, 
+                         var->cb_value_fct, var->cb_value_ctx,
+                         &var->syntax, force_expand);
+    return rc;
+}
+
+var_rc_t var_strerror(var_t *var, var_rc_t rc, char **str)
+{
+    static char *errors[] = {
+        "everything ok",                                                        /* VAR_OK = 0 */
+        "incomplete named character",                                           /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
+        "incomplete hexadecimal value",                                         /* VAR_ERR_INCOMPLETE_HEX */
+        "invalid hexadecimal value",                                            /* VAR_ERR_INVALID_HEX */
+        "octal value too large",                                                /* VAR_ERR_OCTAL_TOO_LARGE */
+        "invalid octal value",                                                  /* VAR_ERR_INVALID_OCTAL */
+        "incomplete octal value",                                               /* VAR_ERR_INCOMPLETE_OCTAL */
+        "incomplete grouped hexadecimal value",                                 /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
+        "incorrect character class specification",                              /* VAR_ERR_INCORRECT_CLASS_SPEC */
+        "invalid expansion configuration",                                      /* VAR_ERR_INVALID_CONFIGURATION */
+        "out of memory",                                                        /* VAR_ERR_OUT_OF_MEMORY */
+        "incomplete variable specification",                                    /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
+        "undefined variable",                                                   /* VAR_ERR_UNDEFINED_VARIABLE */
+        "input is neither text nor variable",                                   /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
+        "unknown command character in variable",                                /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
+        "malformated search and replace operation",                             /* VAR_ERR_MALFORMATTED_REPLACE */
+        "unknown flag in search and replace operation",                         /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
+        "invalid regular expression in search and replace operation",           /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
+        "missing parameter in command",                                         /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
+        "empty search string in search and replace operation",                  /* VAR_ERR_EMPTY_SEARCH_STRING */
+        "start offset missing in cut operation",                                /* VAR_ERR_MISSING_START_OFFSET */
+        "offsets in cut operation delimited by unknown character",              /* VAR_ERR_INVALID_OFFSET_DELIMITER */
+        "range out of bounds in cut operation",                                 /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
+        "offset out of bounds in cut operation",                                /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
+        "logic error in cut operation",                                         /* VAR_ERR_OFFSET_LOGIC */
+        "malformatted transpose operation",                                     /* VAR_ERR_MALFORMATTED_TRANSPOSE */
+        "source and destination classes do not match in transpose operation",   /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
+        "empty character class in transpose operation",                         /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
+        "incorrect character class in transpose operation",                     /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
+        "malformatted padding operation",                                       /* VAR_ERR_MALFORMATTED_PADDING */
+        "width parameter missing in padding operation",                         /* VAR_ERR_MISSING_PADDING_WIDTH */
+        "fill string missing in padding operation",                             /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
+        "unknown quoted pair in search and replace operation",                  /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
+        "sub-matching reference out of range",                                  /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
+        "invalid argument",                                                     /* VAR_ERR_INVALID_ARGUMENT */
+        "incomplete quoted pair",                                               /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
+        "lookup function does not support variable arrays",                     /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
+        "index specification of array variable contains an invalid character",  /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
+        "index specification of array variable is incomplete",                  /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
+        "bracket expression in array variable's index is not closed",           /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
+        "division by zero error in index specification",                        /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
+        "unterminated loop construct",                                          /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
+        "invalid character in loop limits"                                      /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
+    };
+
+    if (str == NULL)
+        return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    rc = 0 - rc;
+    if (rc < 0 || rc >= sizeof(errors) / sizeof(char *))
+        *str = "unknown error";
+    else
+        *str = errors[rc];
+    return VAR_OK;
+}
+


ossp-pkg/var/var.h 1.23 -> 1.24

--- var.h        2002/02/27 13:44:16     1.23
+++ var.h        2002/02/28 08:08:16     1.24
@@ -77,46 +77,42 @@
     VAR_ERR_INVALID_HEX                     =  -3,
     VAR_ERR_INCOMPLETE_HEX                  =  -2,
     VAR_ERR_INCOMPLETE_NAMED_CHARACTER      =  -1,
-    VAR_OK = 0
+    VAR_OK                                  =   0
 } var_rc_t;
 
-/* Expand quoted pairs to their binary representation. */
-var_rc_t var_unescape(const char *src, size_t len, char *dst, int unescape_all);
+struct var_st;
+typedef struct var_st var_t;
 
-/* Prototype for the lookup callback used in var_expand(). */
-typedef var_rc_t (*var_cb_t)(
-    void *context,
-    const char *varname, size_t name_len, int idx,
-    const char **data, size_t *data_len,
-    size_t *buffer_size
-);
-
-/* Configure the var_expand() parser's tokens. */
-typedef struct {
-    char varinit;        /* '$' */
-    char startdelim;     /* '{' */
-    char enddelim;       /* '}' */
-    char startindex;     /* '[' */
-    char endindex;       /* ']' */
-    char current_index;  /* '#' */
-    char escape;         /* '\' */
-    char *namechars;     /* 'a-zA-Z0-9_' */
+typedef enum {
+    VAR_CONFIG_SYNTAX,
+    VAR_CONFIG_CB_VALUE
 } var_config_t;
 
-/* The default configuration */
-extern const var_config_t var_config_default;
-
-/* Expand variable expressions in a text buffer. */
-var_rc_t var_expand(const char *input, size_t input_len,
-                    char **result, size_t *result_len,
-                    var_cb_t lookup, void *lookup_context,
-                    const var_config_t *config, int force_expand);
-
-/* Map an error code to a text message. */
-const char *var_strerror(var_rc_t rc);
+typedef struct {
+    char  escape;       /* default: '\' */
+    char  delim_init;   /* default: '$' */
+    char  delim_open;   /* default: '{' */
+    char  delim_close;  /* default: '}' */
+    char  index_open;   /* default: '[' */
+    char  index_close;  /* default: ']' */
+    char  index_mark;   /* default: '#' */
+    char *name_chars;   /* default: "a-zA-Z0-9_" */
+} var_syntax_t;
+
+typedef var_rc_t (*var_cb_value_t)(
+    void *ctx,
+    const char  *var_ptr, size_t  var_len, int var_idx,
+    const char **val_ptr, size_t *val_len, size_t *val_size
+);
 
-/* Unique library identifier. */
 extern const char var_id[];
 
+var_rc_t  var_create    (var_t **var);
+var_rc_t  var_destroy   (var_t  *var);
+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_strerror  (var_t  *var, var_rc_t rc, char **str);
+
 #endif /* __VAR_H__ */
 


ossp-pkg/var/var.pod 1.20 -> 1.21

--- var.pod      2002/02/27 13:44:16     1.20
+++ var.pod      2002/02/28 08:08:16     1.21
@@ -35,841 +35,642 @@
 
 =head1 SYNOPSIS
 
-var_rc_t var_unescape(const char *src, size_t len, char *dst, int unescape_all);
+=over 2
 
-var_rc_t var_expand(const char *input, size_t input_len, char **result, size_t *result_len, var_cb_t lookup, void *lookup_context, const var_config_t *config, int force_expand);
+=item Types:
 
-const char *var_strerror(var_rc_t rc);
+B<var_rc_t>,
+B<var_t>,
+B<var_config_t>,
+B<var_syntax_t>,
+B<var_cb_value_t>.
 
-=head1 DESCRIPTION
-
-The routines included in this library, var_unescape() and
-var_expand(), enable application developers to make use of variable
-expansion in an arbitrary text buffer. Assume, for instance, your
-application provided the data items
-
-    HOST   = peti.example.org
-    OSTYPE = FreeBSD
-    HOME   = /home/my-application
-
-and you had obtained the following message from the user via means of
-a configuration file, the system environment, or the command line:
-
-    My-application starting up with the following configuration:
-
-        HOST   = $HOST
-        OSTYPE = $OSTYPE
-        HOME   = $HOME
-
-Then you could use var_expand() to replace the variables in that
-message by their actual contents.
-
-The good thing about this kind of approach is that you separate the
-actual information in a message from the message's layout, language,
-or content encoding. If the user doesn't like messages as long the one
-above, he'd simply provide the template
-
-    my-application: $HOST ($OSTYPE), $HOME
-
-to get the result, he's interested in. Maybe all he's interested in
-is:
-
-    my-application's home is $HOME
-
-To take things one step further, OSSP var provides the user -- the
-person providing the template file, that is -- with powerful
-mechanisms to modify the presentation of the variable's contents. For
-example, the expression ${HOME:l} would yield the contents of the
-variable HOME converted to all lower-case. Or, to show on even more
-powerful construct: The expression ${HOME:y/ \t/__/} would turn all
-blanks or tabs in the contents of HOME to underscores, before
-inserting it into the output text.
-
-Similarly, the function var_unescape() will expand "quoted pairs",
-thus allowing the user to speficy non-printable characters is a
-template, such as the carrige return ('\r'), the newline ('\n'), or --
-as seen above -- the tab ('\t').
-
-Using these two routines in combination, gives the application
-developer the power to have the user custumize the application's
-messages as he or she sees fit at virtually no extra cost;
-incorporating a template mechanism into your own applications with
-OSSP var is extremely simple.
-
-=head1 THE VAR_UNESCAPE FUNCTION
-
-The purpose of var_unescape() is to expand any quoted pairs found in
-the input buffer. Its prototype is:
-
-    var_rc_t var_unescape(const char *src, size_t len,
-                          char *dst, int unescape_all);
-
-The first parameter, "src", is a pointer to the input buffer, which
-should be processed. Unlike most C library functions, var_unescape()
-does not expect the buffer to be terminated by a null byte ('\0'),
-instead, it expects the length of the buffer's contents as parameter
-"len". The third parameter, "dst" is a pointer to a buffer, into which
-the expanded buffer is copied if processing is successful. The size of
-this buffer must be at least len+1 characters. The reason the result
-buffer has to be one byte longer than the input buffer is that
-var_unescape() always adds a terminating null byte at the end of the
-output buffer, so that you can use the result comfortably with other C
-library routines.
-
-The last parameter, "unescape_all", is flag that modifies the behavior
-of var_unescape(). If is set to TRUE (any value except zero, that is),
-var_unescape() will expand B<any> quoted pair it sees, even those that
-it does not know about. Hence, a "\1" will become a "1", even though
-"\1" has no special meaning to var_unescape(). If "unescape_all" is
-set to FALSE (zero), such quoted pairs will be copied verbatimly to
-the output buffer.
-
-The quoted pairs supported by var_unescape() are '\t', '\r', '\n',
-'\abc' (octal), '\xAB' (hexadecimal), and '\x{...}' (grouped
-hexadecimal). Please refer to section "SUPPORTED NAMED CHARACTERS" for
-a more comprehensive description of these quoted pairs.
-
-If var_unescape() encounters any syntax errors, it will return an
-error code denoting exactly what kind of error occured; please refer
-to section "CODES RETURNED BY THE LIBRARY" for a complete list of
-possible return codes. If no error occured, VAR_OK is returned.
-
-=head1 THE VAR_EXPAND FUNCTION
-
-The second function of OSSP var is var_expand(), the routine that will
-expand any variables found in a text buffer to their actual contents.
-Its prototype is:
-
-    var_rc_t var_expand(const char *input, size_t input_len,
-                        char **result, size_t *result_len,
-                        var_cb_t lookup, void *lookup_context,
-                        const var_config_t *config,
-                        int force_expand);
-
-Don't be scared, the usage is way simpler than it looks at first
-glance. The parameters of var_expand() are:
+=item Functions:
 
-=over 4
+B<var_create>,
+B<var_destroy>,
+B<var_config>,
+B<var_unescape>,
+B<var_expand>, 
+B<var_strerror>.
 
-=item const char *input
+=item Variables:
 
-A pointer to the text buffer containing the variable expressions to be
-expanded. Unlike in most other C library functions, this text buffer
-need not be terminated by a null byte.
-
-=item size_t input_len
-
-The length of the text buffer's contents.
-
-=item char **result
-
-A pointer to a character pointer in which the location of the expanded
-text buffer will be stored. The result buffer will be allocated by
-var_expand() using the system call malloc(3), thus it is the caller's
-responsibility to free(3) that buffer once it is not used anymore. The
-result buffer will be terminated by null byte, which is not included
-in the "result_len" counter.
-
-If var_expand() fails with an error, "result" will point to "input".
-The only exceptions are the VAR_ERR_INVALID_ARGUMENT
-VAR_ERR_OUT_OF_MEMORY errors, in which case "result" is undefined.
-
-=item size_t *result_len
-
-A pointer to the location in which the length of the expanded text
-buffer will be stored. If var_expand() fails with an error -- with the
-exception of VAR_ERR_INVALID_ARGUMENT and VAR_ERR_OUT_OF_MEMORY --,
-"result_len" will contain the number of characters that have been
-consumed from "input" before the error occured. This argument can be
-NULL if the application is not interested in this information.
-
-=item var_cb_t lookup
-
-A pointer to the function used to perform the actual variable lookup.
-Please refer to section "THE LOOKUP CALLBACK" for a comprehensive
-description.
-
-=item void *lookup_context
-
-An arbitrary value passed through to "lookup" every time it is called.
-Please refer to section "THE LOOKUP CALLBACK" for a comprehensive
-description.
-
-=item const var_config_t *config
-
-The configuration of var_expand(). The var_config_t structure is
-defined as follows:
-
-    typedef struct {
-        char varinit;
-        char startdelim;
-        char enddelim;
-        char startindex;
-        char endindex;
-        char current_index;
-        char escape;
-        char *namechars;
-    } var_config_t;
-
-Using this structure, you can modify the parser to use different
-tokens to find variable constructs. If "config" is NULL, the default
-configuration will be used, which you can access through the
-declaration
-
-    extern const var_config_t var_config_default;
-
-in var.h. The default configuration for var_init looks like this:
-
-    const var_config_t var_config_default = {
-        '$',              /* varinit */
-        '{',              /* startdelim */
-        '}',              /* enddelim */
-        '[',              /* startindex */
-        ']',              /* endindex */
-        '#',              /* current_index */
-        '\\',             /* escape */
-        "a-zA-Z0-9_"      /* namechars */
-    };
-
-Please note that the setting of var_config_t.escape is actually a
-single backslash; the quote above has been taken from the C source
-code, which is why the backslash had to be escaped with another
-backslash for the C pre-processor.
-
-=item int force_expand
-
-This flag determines how var_expand() deals with undefined variables.
-If it is set to TRUE (any value but zero), var_expand() will fail with
-error code VAR_ERR_UNDEFINED_VARIABLE whenever an undefined variable
-is encountered. If set to FALSE (zero), var_expand() will copy the
-expression it failed to expand verbatimly into the output buffer, in
-order to enable you to go over the buffer with an additional pass.
-
-Generally, if you do not plan to use muli-pass expansion, you should
-set "force_expand" to TRUE in order to make sure no unexpanded
-variables are left over in the buffer.
+B<var_id>.
 
 =back
 
-var_expand() returns VAR_OK if everything went fine, and one of the
-error codes described in section "CODES RETURNED BY THE LIBRARY" if
-the function call failed.
-
-=head1 THE VAR_STRERROR FUNCTION
-
-In order to make life for application developers easier, the helper
-function var_strerror() has been provided, which can be used to map
-any of the error codes returned by the OSSP var library into a
-clear-text message describing the reason for failure. Please note that
-errors coming from the callback, such as VAR_ERR_CALLBACK and those
-based on it, cannot be mapped and will yield the message "unknown
-error".
-
-=head1 COMBINING VAR_UNESCAPE AND VAR_EXPAND
-
-For maximum power and flexibility, you will want to use both routines
-provided by this library in combination. That is, you will want to use
-var_unescape() to turn all quoted pairs into their real
-representation, before you call var_expand(), because then the user
-can safely use specials like "\n" or "\t" throughout the template and
-achieve the desired effect. These quoted pairs are particularly useful
-if search-and-replace or transpose actions are performed on variables
-before they're expanded. Be sure, though, to make the first
-var_unescape() pass with "expand_all" set to FALSE, or the routine
-will also expand quoted pairs like "\1", which might have a special
-meaning in the var_expand() pass to follow.
-
-Once, all known quoted pairs are expanded, expand the variables with
-var_expand(). After that, you will want to have a second pass with
-var_unescape() and "expand_all" set to TRUE, to make sure all
-remaining quoted pairs are expanded. Also, the var_expand() pass might
-have introduced now quoted pairs into the output text, which you need
-to expand to get the desired effect.
-
-Take a look at this code snipped, to see how to combine var_unescape()
-und var_expand() properly:
-
-    var_rc_t rc;
-    char* result;
-    size_t result_len;
-
-    if ((rc = var_unescape(input, strlen(input), output, 0)) != VAR_OK ||
-        (rc = var_expand(output, strlen(output),
-                         &result, &result_len,
-                         &lookup, NULL,
-                         NULL, 1)) != VAR_OK ||
-        (rc = var_unescape(input, strlen(input), output, 1)) != VAR_OK)
-    {
-        printf("Expanding the template failed with error %d.\n", rc);
-        exit(1);
-    }
-    [...]
-    free(result);
-
-=head1 THE LOOKUP CALLBACK
-
-The function var_expand() does not know how to look the contents of a
-variable up itself. Instead, it relies on a caller-supplied callback
-function, which adheres to the var_cb_t function interface:
-
- var_rc_t lookup(void *context,
-                 const char *varname, size_t name_len, int index,
-                 const char **data, size_t *data_len,
-                 size_t *buffer_size);
-
-This function will be called by var_expand() whenever it has to
-retrieve the contents of, say, the variable $name, using the following
-parameters:
-
-=over 4
-
-=item void *context
-
-The contents of context is passed through from the var_expand()'s
-"lookup_context" parameter to the callback. This parameter can be used
-by the programmer to provide internal data to the callback function
-through var_expand().
-
-=item const char *varname
-
-This is a pointer to the name of the variable which's contents
-var_expand() wishes to retrieve. In our example of looking up $name,
-varname would point to the string "name". Please note that the string
-is NOT necessarily terminated by a '\0' character! If the callback
-function needs to pass the string to the standard C library string
-manipulation functions during the lookup, it will have to copy the
-string into a buffer of its own to ensure it is null-terminated.
-
-=item size_t name_len
-
-The "name_len" parameter contains the length of the variable name
-"varname" points to.
-
-=item int index
-
-The contents of this interger determines which entry of a variable
-array to look-up. If the variable specification that led to the
-execution of the lookup function did not contain an index, zero is
-provided per default. If "index" is negative, the callback must return
-the number of entries of the variable array.
-
-=item const char **data
-
-This is a pointer to the location where the callback function should
-store the pointer to the contents of the variable.
-
-=item size_t *data_len
-
-This is a pointer to the location where the callback function should
-store the length of the contents of the variable.
-
-=item size_t *buffer_size
-
-This is a pointer to the location where the callback function should
-store the size of the buffer that has been allocated to hold the
-contents of the looked-up variable. If no buffer has been allocated at
-all, because the variable uses some other means of storing the
-contents -- as in the case of getenv(3), where the system provides the
-buffer for the string --, this should be zero (0).
-
-In case a buffer size greater than zero is returned by the callback
-function, var_expand() will make use of that buffer internally if
-possible. If will also free(3) the buffer when it is not needed
-anymore.
-
-=back
-
-The return code of the lookup function is interpreted by var_expand()
-according to the following convention: VAR_OK means success, that
-is, the contents of the variable has been looked-up successfully and
-the "data", "data_len", and "buffer_size" locations have been filled
-with appropriate values. A return code VAR_ERR_XXX means that the lookup
-failed, such as a system error or lack of resources. In the latter two
-cases, the contents of "data", "data_len" and "buffer_size" is assumed
-to be undefined. Hence, var_expand() will not free(3) any possibly
-allocated buffers, the callback must take care of that itself.
-
-If a callback returns the special VAR_ERR_UNDEFINED_VARIABLE return
-code, the behaviour of var_expand() depends on the setting of
-the "force_expand" parameter. If force-expand mode has been set,
-var_expand() will fail with this error. If force-expand mode has
-not been set, var_expand() will copy the expression that caused the
-lookup to fail verbatimly into the output buffer so that an additional
-expanding pass may expand it later.
-
-If the callback returns an VAR_ERR_XXX, var_expand() will fail with the return
-code it got from the callback.  If the cause for the error can not be denoted
-by an error code defined in var.h, callback implementors should use the error
-code VAR_ERR_CALLBACK, which is currently defined to -64. It is guaranteed
-that no error code smaller than VAR_ERR_CALLBACK is ever used by var_expand()
-or VAR_UNESCAPE(), so if the callback implementor wishes to distinguish
-between different reasons for failure, he can define his own set of errors
-
-    typedef enum {
-        LOOKUP_ERROR_ONE   = -3,
-        LOOKUP_ERROR_TWO   = -2,
-        LOOKUP_ERROR_THREE = -1,
-    } lookup_error_t;
-
-and return, say, "(VAR_ERR_CALLBACK - LOOKUP_ERROR_TWO)".
-
-To illustrate the implementation of a proper callback, take a look at
-the following expamle that accesses the system environment via
-getenv(3) to lookup variables and to return them to var_expand():
-
-    var_rc_t env_lookup(
-        void *context,
-        const char *varname, size_t name_len, int idx,
-        const char **data, size_t *data_len,
-        size_t *buffer_size)
-    {
-        char tmp[256];
-
-        if (idx != 0)
-            return VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED;
-        if (name_len > sizeof(tmp) - 1)
-            return VAR_ERR_CALLBACK;
-        memcpy(tmp, varname, name_len);
-        tmp[name_len] = '\0';
-        if ((*data = getenv(tmp)) == NULL)
-            return VAR_ERR_UNDEFINED_VARIABLE;
-        *data_len = strlen(*data);
-        *buffer_size = 0;
-        return VAR_OK;
-    }
-
-=head1 SUPPORTED NAMED CHARACTERS
-
-The var_unescape() function knows the following constructs:
-
-=over 4
-
-=item \t, \r, \n
-
-These expressions are replaced by the appropriate binary
-representation of a tab, a carrige return and a newline respectively.
-
-=item \abc
-
-This expression is replaced by the value of the octal number "abc".
-Valid digits of "a" are in the range of '0' to '3', for digits "b" and
-"c" in the range of '0' to '7'. Please note that an octal expression
-is recognized only if the backslash is followed by three digits! The
-expression "\1a7", for example, is interpreted as the quoted pair "\1"
-followed by the verbatim text "a7".
-
-=item \xAB
-
-This expression is replaced by the value of the hexadecimal number
-$AB. Both characters "A" and "B" must be in the range of '0' to '9',
-'a' to 'f', or 'A' to 'F'.
-
-=item \x{...}
-
-This expression denotes a set of grouped hexadecimal numbers. The
-"..." part may consist of an arbitrary number of hexadecimal pairs,
-such as in "\x{}", "\x{ff}", or "\x{55ffab04}". The empty expression
-"\x{}" is a no-op; it will not produce any output.
-
-This construct may be useful to specify multi-byte characters (as in
-Unicode). Even though "\x{0102}" is effectively equivalent to
-"\x01\x02", the grouping of values may be useful in other contexts,
-even though var_unescape() or var_expand() make no direct use of it.
-
-=back
+=head1 DESCRIPTION
 
-=head1 SUPPORTED VARIABLE EXPRESSIONS
+B<OSSP var> is a flexible, full-featured and fast variable construct
+expansion library.
 
-Additionally to the ordinary variable expansion of $name or ${name},
-var_expand() supports a number of operations that can be performed on
-the contents of "name" before it is copied to the output buffer. Such
-operations are always denoted by appending the a colon and a command
-character to the variable name, for expample: ${name:l} or
-${name:s/foo/bar/}. You can specify multiple operations, which are
-executed from the left to the right, for expample:
-${name:l:s/foo/bar/:u}.
-
-Also, you can nest variable expansion and command execution pretty
-much anywhere in the construct, for example: ${name:s/$foo/$bar/g}. In
-that context is probably useful to have a look at the formal
-expression grammar provided in section "EBNF GRAMMAR OF SUPPORTED
-EXPRESSIONS".
-
-Generally, all operations described below do not modify the contents
-of any variable -- var_expand() generally can't set variables, it will
-only read them. If the description says that an operation "replaces
-the contents of variable $foo", it is meant that rather than expanding
-the expression the the contents of $foo, it will expand to the
-modified string instead. The contents of $foo is left untouched in any
-case.
+It supports a configurable variable construct syntax very similar
+to the style found in many scripting languages (like C<@>I<name>,
+C<${>I<name>C<}>, C<$(>I<name>C<)>, etc.) and provides both simple
+scalar (C<${>I<name>C<}>) and array (C<${>I<name>C<[>I<index>C<]}>)
+expansion, plus optionally one or more post-operations on the expanded
+value (C<${>I<name>C<:>I<op>[C<:>I<op>...]]C<}>).
+
+The supported post-operations are length determination, case
+conversion, defaults, postive and negative alternatives, sub-strings,
+regular expression based substitutions, character translations, and
+padding. Additionally, a meta-construct plus arithmetic expressions for
+index and range calculations allow (even nested) iterations over array
+variable expansions (..C<[>..C<${>I<name>C<[#+1]}>..C<]>..).
+
+The actual variable value lookup is performed through a callback
+function, so B<OSSP var> can expand arbitrary values.
+
+=head1 SYNTAX CONSTRUCTS
+
+A string expanded through B<OSSP var> can consist of arbitrary text
+characters plus one or more of the following special syntax constructs
+which are expanded by B<OSSP var>.
 
 =over 4
 
-=item ${name:#}
-
-This operation will expand to the length of the contents of $name. If,
-for example, "$FOO" is "foobar", then "${FOO:#}" will result in "6".
-
-=item ${name:l}
-
-This operation will turn the contents of $name to all lower-case,
-using the system routine tolower(3), thereby possibly using the
-system's localization settings.
-
-=item ${name:u}
-
-This operation will turn the contents of $name to all upper-case,
-using the system routine toupper(3), thereby possibly using the
-system's localization settings.
-
-=item ${name:*<word>}
-
-This operation will replace the contents of $name with the empty
-string ("") if $name is not empty. Otherwise, it will replace it by
-"word".
-
-=item ${name:-<word>}
-
-This operation will replace the contents of $name with "word" if $name
-is empty. Otherwise, it will expand to the contents of $name.
-
-=item ${name:+<word>}
-
-This operation will replace the contents of $name with "word" if $name
-is not empty. Otherwise, it will expand to the contents of $name.
-
-=item ${name:o<start>-<end>}
-
-This operation will cut the string starting at position "start" to
-ending position "end" out of the contents of $name and return that.
-Please note that the character at position "end" is included in the
-result; ${name:o3-4} for instance, will return a two-character string.
-Also, please note that start positions begin at zero (0)! If the "end"
-parameter is left out, as in ${name:o3-}, the operation will return the
-string starting at position 3 until the end.
-
-=item ${name:o<start>,<length>}
-
-This operation will cut the string starting at position "start"
-of length "length" out of the contents of $name and return that.
-${name:o3,4} means, for instance, to return the next 4 characters
-starting at position 3 in the string. Please note that start positions
-begin at zero (0)! If "length" is left out, as in ${name:o3,}, the
-operation will return the string starting at position 3 until the end.
-
-=item ${name:s/<pattern>/<string>/[gti]}
-
-This operation will perform a search-and-replace operation on the
-contents of $name and return the result. The behavior of the
-search-and-replace is modified by the following flags parameter: If a
-'t' flag has been provided, a plain text search-and-replace is
-performed, otherwise, the default is to a regular expression
-search-and-replace as in the system utility sed(1). If the 'g' flag
-has been provided, the search-and-replace will replace all instances
-of "pattern" by "replace", otherwise, the default is to replace only
-the first instance. If the 'i' flag has been provided, the
-search-and-replace will take place case-insensitively, otherwise, the
-default is to distinguish character case.
-
-=item ${name:y/<ochars>/<nchars>/}
-
-This operation will translate all characters in the contents of $name
-that are found in the "ochars" class to the corresponding character in
-the "nchars" class, just like the system utility tr(1) does. Both
-"ochars" and "nchars" may contain character range specifications, for
-example "a-z0-9". A hyphon as the first or last character of the class
-specification is interpreted literally. Both the "ochars" and the
-"nchars" class must contain the same number of characters after all
-ranges are expanded, or var_expand() will abort with an error.
-
-If, for example, "$FOO" would contain "foobar", then
-"${FOO:y/a-z/A-Z/} would yield "FOOBAR". Another goodie is to use that
-operation to ROT13-encrypt or decrypt a string with the expression
-"${FOO:y/a-z/n-za-m/}".
-
-=item ${name:p/<width>/<string>/<align>}
-
-This operation will pad the contents of $name with "string" according
-to the "align" parameter, so that the result is at least "width"
-characters long. Valid parameters for align are 'l' (left), 'r'
-(right), or 'c' (center). The "string" parameter may contain multiple
-characters, if you see any use for that.
-
-If, for example, "$FOO" is "foobar", then "${FOO:p/20/./c}" would
-yield ".......foobar......."; "${FOO:p/20/./l}" would yield
-"foobar.............."; and "${FOO:p/20/./r}" would yield
-"..............foobar";
-
-=back
-
-=head1 EBNF GRAMMAR OF SUPPORTED EXPRESSIONS
-
- input      : ( TEXT | variable | START-INDEX input END-INDEX ( loop-limits )? )*
-
- loop-limits: START-DELIM (numexp)? ',' (numexp)? ( ',' (numexp)? )? END-DELIM
-
- variable   : '$' (name|expression)
-
- expression : START-DELIM (name|variable)+ (START-INDEX num-exp END-INDEX)? (':' command)* END-DELIM
-
- name       : (VARNAME)+
-
- command    : '-' (EXPTEXT|variable)+
-            | '+' (EXPTEXT|variable)+
-            | 'o' (NUMBER ('-'|',') (NUMBER)?)
-            | '#'
-            | '*' (EXPTEXT|variable)+
-            | 's' '/' (variable|SUBSTTEXT)+ '/' (variable|SUBSTTEXT)* '/' ('g'|'i'|'t')*
-            | 'y' '/' (variable|SUBSTTEXT)+ '/' (variable|SUBSTTEXT)* '/'
-            | 'p' '/' NUMBER '/' (variable|SUBSTTEXT)* '/' ('r'|'l'|'c')
-            | 'l'
-            | 'u'
-
- num-exp    : operand
-            | operand ('+'|'-'|'*'|'/'|'%') num-exp
-
- operand    : ('+'|'-')? NUMBER
-            | CURR-INDEX
-            | '(' num-exp ')'
-            | variable
-
- START-DELIM: '{'
-
- END-DELIM  : '}'
-
- START-INDEX: '['
-
- END-INDEX  : ']'
-
- CURR-INDEX : '#'
-
- VARNAME    : '[a-zA-Z0-9_]+'
+=item C<\>I<NNN>
 
- NUMBER     : '[0-9]+'
+Character with the octal value I<NNN> 
+(I<N>: C<0>,...,C<7>).
 
- SUBSTTEXT  : '[^$/]'
+=item C<\x>I<NN>, C<\x{>I<NNMM..>C<}>
 
- EXPTEXT    : '[^$}:]+'
+Character with the hexadecimal value I<NN> or the characters
+denoted by grouped hexadecimal numbers I<NNMM..>. (I<N>, I<M>:
+C<0>,...,C<9>,[C<aA>],...,[C<fF>]).
 
- TEXT       : '[^$[\]]+'
+=item C<\t>, C<\r>, C<\n>
 
-Please note that the descriptions of START-DELIM, END-DELIM, VARNAME,
-SUBSTEXT, and EXPTEXT shown here assume that var_expand() has been
-called in the default configuration. In thruth, the contents of
-VARNAME corresponds directly to the setting of "namechars" in the
-var_config_t structure. Similarly, the dollar ('$') corresponds
-directly to the setting of "varinit", and the '{' and '}' characters
-to "startdelim" and "enddelim" respectively.
+Tabulator, Carriage Return and Newline character.
 
-=head1 CODES RETURNED BY THE LIBRARY
+=item C<\\>, C<\>I<x>
 
-Generally, all routines part of that library follow the convention
-that a return code of zero or greater denotes success and a return
-code of less than zero denotes failure. (This is slightly different
-for the callbacks, please see section "THE LOOKUP CALLBACK" for
-further details.) In order to distinguish the various causes of
-failure, the following set of defines is provided in var.h:
+Ordinary character C<\> and I<x>.
 
-=over 4
-
-=item VAR_OK
+=item C<$>I<name>, C<${>I<name>C<}>
 
-No errors; everything went fine.
+Contents of scalar variable I<name>.
 
-=item VAR_ERR_INCOMPLETE_QUOTED_PAIR
+=item C<${>I<name>C<[>I<index>C<]>C<}>
 
-The configured escape character as the last character in the input
-buffer.
+Contents of array variable I<name> at position I<index>.
+For I<index> full arithmetic expressions are allowed.
 
-=item VAR_ERR_INVALID_ARGUMENT
+=item C<${>I<name>C<:#}>
 
-Any of the provided arguments is invalid, for expample: the pointer to
-the input buffer is NULL.
+Length of C<$>I<name>.
 
-=item VAR_ERR_SUBMATCH_OUT_OF_RANGE
+=item C<${>I<name>C<:l}>, C<${>I<name>C<:u}>
 
-During execution of a ${name:s/pattern/replace/flags} operation, a
-submatch has been referenced in the "replace" part, which's number is
-greater than the number of submatches encountered in the "pattern"
-part, for expample: ${name:s/foo(bar)/\2/}.
+C<$>I<name>, converted to all lower-case or all upper-case.
 
-=item VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE
+=item C<${>I<name>C<:->I<word>C<}>
 
-During execution of a ${name:s/pattern/replace/flags} operation, the
-parser encountered an unknown quoted pair in the "replace" part. Valid
-quoted pairs are "\\", "\0", "\1", ... , "\9" only.
+If C<$>I<name> is not empty string, then C<$>I<name>, else I<word>
+(default value).
 
-=item VAR_ERR_EMPTY_PADDING_FILL_STRING
+=item C<${>I<name>C<:+>I<word>C<}>
 
-The "fill" part in an ${name:p/width/fill/pos/} expression was found
-to be empty.
+If C<$>I<name> is empty string, then empty string, else I<word>
+(positive alternative).
 
-=item VAR_ERR_MISSING_PADDING_WIDTH
+=item C<${>I<name>C<:*>I<word>C<}>
 
-The "width" part in an ${name:p/width/fill/pos/} expression was found
-to be empty.
+If C<$>I<name> is not empty string, then empty string, else I<word>
+(negative alternative).
 
-=item VAR_ERR_MALFORMATTED_PADDING
+=item C<${>I<name>C<:o>I<start>C<,>[I<length>]C<}>
 
-Any of the "/" delimiters was missing while parsing a
-${name:p/width/fill/pos/} expression.
+Substring of C<$>I<name> starting at position I<start> with I<length>
+characters.
 
-=item VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC
+=item C<${>I<name>C<:o>I<start>C<->[I<end>]C<}>
 
-While parsing a ${name:y/old-class/new-class/} expression, any of the
-character class specifications had a start-of-range character that was
-greater (in terms of ASCII encoding) than the end-of-range character,
-for expample: "[z-a]".
+Substring of C<$>I<name> starting at position I<start> and ending at
+position I<end> (inclusive).
 
-=item VAR_ERR_EMPTY_TRANSPOSE_CLASS
+=item C<${>I<name>C<:s/>I<pattern>C</>I<string>C</>[C<itg>]C<}>
 
-While parsing a ${name:y/old-class/new-class/} expression, any of the
-character class specifications was found to be empty.
+C<$>I<name> after replacing characters matching I<pattern> with
+I<string>. By default, case-sensitive regular expression matching is
+performed and only the first occurance of I<pattern> is replaced. Flag
+"C<i>" switches to case insensitive matching; flag "C<t>" switches
+to plain text pattern; flag "C<g>" switches to replacements of all
+occurances.
 
-=item VAR_ERR_TRANSPOSE_CLASSES_MISMATCH
+=item C<${>I<name>C<:y/>I<ochars>C</>I<nchars>C</}>
 
-While parsing a ${name:y/old-class/new-class/} expression, the number
-of characters found in the expanded "old-class" was different than the
-number of characters in new-class".
+C<$>I<name> after replacing all characters found in the I<ochars>
+character class by the corresponding character in the I<nchars>
+character class.
 
-=item VAR_ERR_MALFORMATTED_TRANSPOSE
+=item C<${>I<name>C<:p/>I<width>C</>I<string>C</>{C<l>,C<c>,C<r>}C<}>
 
-Any of the "/" delimiters was missing while parsing a
-${name:y/old-class/new-class/} expression.
+C<$>I<name> after padding to I<width> with I<string>. Original contents
+of I<name> is either left justified (flag "C<l>"), centered (flag
+"C<c>"), or right justified (flag "C<r>").
 
-=item VAR_ERR_OFFSET_LOGIC
 
-The "end" offset in a ${name:o<start>,<end>} expression is smaller
-than the "start" offset.
+=item C<[>I<body>C<]>, C<[>I<body>C<]>C<{>I<start>C<,>I<step>C<,>I<end>C<}>
 
-=item VAR_ERR_OFFSET_OUT_OF_BOUNDS
+Repeat expansion of I<body> as long as at least one array variable
+does not expand to the empty string (first variant) or exactly
+(I<end>-I<start>)/I<step> times (second variant). In both cases the
+character "C<#>" is expanded in C<body> as the current loop index
+(C<0>,... for first variant and I<start>,...,I<end> with stepping
+I<step> for second variant). I<index> of array variable lookups. For
+I<start>, I<step> and I<end>, full arithmetic expressions are allowed.
 
-The "start" offset in a ${name:o<start>,<end>} expression is greater
-than the number of characters found in $name.
+=back
 
-=item VAR_ERR_RANGE_OUT_OF_BOUNDS
+=head1 SYNTAX CONSTRUCTS (GRAMMAR)
 
-The end-of-range in a ${name:o<start>,<end>} or ${name:o<start>-<end>}
-expression would be greater than the number of characters found in
-$name.
+All the variable syntax constructs supported by B<OSSP var> follow the
+same grammatical form. For completeness and reference reasons, the
+corresponding grammar is given in an extended BNF:
+
+ input       ::= ( TEXT
+                 | variable 
+                 | INDEX_OPEN input INDEX_CLOSE (loop_limits)?
+                 )*
+
+ variable    ::= DELIM_INIT (name|expression)
+
+ name        ::= (NAME_CHARS)+
+
+ expression  ::= DELIM_OPEN 
+                 (name|variable)+
+                 (INDEX_OPEN num_exp INDEX_CLOSE)? 
+                 (':' command)*
+                 DELIM_CLOSE
+
+ command     ::= '-' (TEXT_EXP|variable)+
+               | '+' (TEXT_EXP|variable)+
+               | 'o' NUMBER ('-'|',') (NUMBER)?
+               | '#'
+               | '*' (TEXT_EXP|variable)+
+               | 's' '/' (variable|TEXT_SUBST)+ 
+                     '/' (variable|TEXT_SUBST)* 
+                     '/' ('g'|'i'|'t')*
+               | 'y' '/' (variable|TEXT_SUBST)+ 
+                     '/' (variable|TEXT_SUBST)* 
+                     '/'
+               | 'p' '/' NUMBER 
+                     '/' (variable|TEXT_SUBST)* 
+                     '/' ('r'|'l'|'c')
+               | 'l'
+               | 'u'
+
+ num_exp     ::= operand
+               | operand ('+'|'-'|'*'|'/'|'%') num_exp
+
+ operand     ::= ('+'|'-')? NUMBER
+               | INDEX_MARK
+               | '(' num_exp ')'
+               | variable
+
+ loop_limits ::= DELIM_OPEN 
+                 (num_exp)? ',' (num_exp)? (',' (num_exp)?)? 
+                 DELIM_CLOSE
+
+ NUMBER      ::= ('0'|...|'9')+
+
+ TEXT_SUBST  ::= ^(DELIM_INIT|'/')
+ TEXT_EXP    ::= (^(DELIM_INIT|DELIM_CLOSE|':'))+
+ TEXT        ::= (^(DELIM_INIT|INDEX_OPEN|INDEX_CLOSE))+
+
+ DELIM_INIT  ::= '$'                    
+ DELIM_OPEN  ::= '{'   
+ DELIM_CLOSE ::= '}' 
+ INDEX_OPEN  ::= '['    
+ INDEX_CLOSE ::= ']'
+ INDEX_MARK  ::= '#'
+ NAME_CHARS  ::= 'a'|...|'z'|'A'|...|'Z'|'0'|...|'9'
+
+Notice that the grammar definitions of DELIM_INIT, DELIM_OPEN,
+DELIM_CLOSE, INDEX_OPEN, INDEX_CLOSE, INDEX_MARK and NAME_CHARS
+correspond to the default syntax configuration only. They can be changed
+through the API (see B<var_syntax_t>).
 
-=item VAR_ERR_INVALID_OFFSET_DELIMITER
+=head1 APPLICATION PROGRAMMING INTERFACE (API)
 
-The two numbers in an offset operation are delimited by a character
-different from "," or "-".
+The following is a detailed description of the B<OSSP var> B<ISO-C>
+language Application Programming Interface (API):
 
-=item VAR_ERR_MISSING_START_OFFSET
+=head2 TYPES
 
-The "start" offset in a ${name:o<start>,<end>} or
-${name:o<start>-<end>} expression was found to be empty.
+The B<OSSP var> API consists of the following B<ISO-C> data types:
 
-=item VAR_ERR_EMPTY_SEARCH_STRING
+=over 4
 
-The "pattern" part of a ${name:s/pattern/replace/flags} expression was
-found to be empty.
+=item B<var_rc_t>
 
-=item VAR_ERR_MISSING_PARAMETER_IN_COMMAND
+This is an exported enumerated integer type describing the return
+code of all API functions. On success, every API functions returns
+C<VAR_OK>. On error, they return C<VAR_ERR_XXX>. For a list of all
+possible return codes see F<var.h>. Their corresponding describing text
+can be determined with var_strerror().
+
+=item B<var_t>
+
+This is an opaque data type representing a variable expansion context.
+Only pointers to this abstract data type are used in the API.
+
+=item B<var_config_t>
+
+This is an exported enumerated integer type describing configuration
+parameters for var_config(). Currently C<VAR_CONFIG_SYNTAX> (for
+configuring the syntax via B<var_syntax_t>) and C<VAR_CONFIG_CB_VALUE>
+(for configuring the callback for value lookups via B<var_cb_value_t>)
+are defined.
+
+=item B<var_syntax_t>
+
+This is an exported structural data type describing the variable
+construct syntax. It is passed to var_config() on C<VAR_CONFIG_SYNTAX>
+and consists of the following members (directly corresponding to the
+upper-case non-terminals in the grammar above):
+
+ char  escape;       /* default: '\\' */
+ char  delim_init;   /* default: '$' */
+ char  delim_open;   /* default: '{' */
+ char  delim_close;  /* default: '}' */
+ char  index_open;   /* default: '[' */
+ char  index_close;  /* default: ']' */
+ char  index_mark;   /* default: '#' */
+ char *name_chars;   /* default: "a-zA-Z0-9_" */
+
+All members are single character constants, except for I<name_chars>
+which is a character class listing all valid characters. As an
+abbreviation the construct "I<x>C<->I<y>" is supported which means all
+characters from I<x> to I<y> (both included) in the underlying character
+set.
+
+=item B<var_cb_value_t>
+
+This is an exported function pointer type for variable value lookup
+functions. Such a callback function B<cb> has to be of the following
+prototype:
+
+var_rc_t *B<cb>(void *I<ctx>, const char *I<var_ptr>, size_t
+I<var_len>, int I<var_idx>, const char **I<val_ptr>, size_t *I<val_len>,
+size_t *I<val_size>);
 
-In a ${name:+word}, ${name:-word}, or ${name:*word} expression, the
-"word" part was missing -- that means empty.
+This function will be called by var_expand() internally whenever it has
+to resolve the contents of a variable. Its parameters are:
 
-=item VAR_ERR_INVALID_REGEX_IN_REPLACE
+=over 4
 
-While compiling the "pattern" part of a
-${name:s/pattern/replace/flags} expression, regcomp(3) failed with an
-error.
+=item void *I<ctx>
 
-=item VAR_ERR_UNKNOWN_REPLACE_FLAG
+This is the passed-through argument as passed to var_config() on
+C<VAR_CONFIG_CB_VALUE> as the forth argument. This can be used
+to provide an internal context to the callback function through
+var_expand().
 
-In a ${name:s/pattern/replace/flags} expression, a flag other that
-"t", "i", or "g" was found.
+=item const char *I<var_ptr>
 
-=item VAR_ERR_MALFORMATTED_REPLACE
+This is a pointer to the name of the variable which's contents
+var_expand() wishes to resolve. Please note that the string is NOT
+necessarily terminated by a C<NUL> ('C<\0>') character. If the callback
+function needs it C<NUL>-terminated, it has to copy the string into an a
+temporary buffer of its own and C<NUL>-terminate it there.
+
+=item size_t I<var_len>
+
+This is the length of the variable name at I<var_ptr>.
+
+=item int I<var_idx>
+
+This determines which entry of an array variable to lookup. If the
+variable specification that led to the execution of the lookup function
+did not contain an index, zero (C<0>) is provided by default as
+I<var_idx>. If I<var_idx> is less than zero, the callback should return
+the number of entries in the array variable. If I<var_idx> is greater or
+equal zero, it should return the specified particular entry. It is up to
+the callback to decide what to return for an index not equal to zero if
+the underlying variable is a scalar.
 
-Any of the "/" delimiters was missing while parsing a
-${name:s/pattern/replace/flags} expression.
+=item const char **I<val_ptr>
 
-=item VAR_ERR_UNKNOWN_COMMAND_CHAR
+This is a pointer to the location where the callback function should
+store the pointer to the resolved value of the variable.
 
-In a ${name:<char>} expression, "char" did not specify any of the
-supported operations.
+=item size_t *I<val_len>
 
-=item VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE
+This is a pointer to the location where the callback function should
+store the length of the resolved value of the variable.
 
-At one point during parsing of the input buffer, an expression was
-found that was neither verbatim text nor a variable expression. This
-usually is the result of a inconsistent configuration of var_expand()
-via the var_config_t paramater.
+=item size_t *I<val_size>
 
-=item VAR_ERR_UNDEFINED_VARIABLE
+This is a pointer to the location where the callback function should
+store the size of the buffer that has been allocated to hold the
+value of the resolved variable. 
 
-Looking up a variable's contents failed and var_expand() was running
-in "force expand" mode.
+If no buffer has been allocated by the callback at all, because the
+variable uses some other means of storing the contents -- as in the case
+of getenv(3), where the system provides the buffer for the string --,
+this should be set to zero (C<0>).
+
+In case a buffer size greater than zero is returned by the callback,
+var_expand() will make use of that buffer internally if possible. It
+will also free(3) the buffer when it is not needed anymore, so it
+is important that it was previously allocated with malloc(3) by the
+callback.
 
-=item VAR_ERR_INCOMPLETE_VARIABLE_SPEC
+=back
 
-The input buffer ended in the middle of a ${name} expression, or the
-configured variable initializer character was found to be the last
-character of the input buffer.
+The return code of the lookup function B<cb> is interpreted by
+var_expand() according to the following convention: C<VAR_OK> means
+success, that is, the contents of the variable has been resolved
+successfully and the I<val_ptr>, I<val_len>, and I<val_size> variables
+have been filled with appropriate values. A return code C<VAR_ERR_XXX>
+means that the resolving failed, such as a system error or lack of
+resources. In the latter two cases, the contents of I<val_ptr>,
+I<val_len> and I<val_size> is assumed to be undefined. Hence,
+var_expand() will not free(3) any possibly allocated buffers, the
+callback must take care of this itself.
+
+If a callback returns the special C<VAR_ERR_UNDEFINED_VARIABLE> return
+code, the behaviour of var_expand() depends on the setting of its
+I<force_expand> parameter. If I<force_expand> has been set, var_expand()
+will pass-through this error to the caller. If I<force_expand> has
+not been set, var_expand() will copy the expression that caused the
+lookup to fail verbatimly into the output buffer so that an additional
+expanding pass may expand it later.
 
-=item VAR_ERR_OUT_OF_MEMORY
+If the callback returns an C<VAR_ERR_XXX>, var_expand() will fail with
+this return code. If the cause for the error can not be denoted by an
+error code defined in F<var.h>, callback implementors should use the
+error code C<VAR_ERR_CALLBACK> (which is currently defined to -64).
+It is guaranteed that no error code smaller than C<VAR_ERR_CALLBACK>
+is ever used by any B<OSSP var> API function, so if the callback
+implementor wishes to distinguish between different reasons for failure,
+he subtract own callback return codes from this value, i.e., return
+(C<VAR_ERR_CALLBACK> - I<n>) (I<n> E<gt>= 0) from the callback function.
 
-var_expand() failed while malloc(3)ing internally needed buffers.
+=back
 
-=item VAR_ERR_INVALID_CONFIGURATION
+=head2 FUNCTIONS
 
-Any of the characters configured in the var_config_t structure as a
-special ("varinit", "startdelim", "enddelim", and "escape") was found
-to be a member of the "namechars" class.
+The B<OSSP var> API consists of the following B<ISO-C> functions:
 
-=item VAR_ERR_INCORRECT_CLASS_SPEC
+=over 4
 
-The character class specification "namechars" of the var_config_t
-structure provided to var_expand was syntactically incorrect, that is,
-the start-of-range was greater than end-of-range. (See also
-VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC.)
+=item var_rc_t B<var_create>(var_t **I<var>);
 
-=item VAR_ERR_INCOMPLETE_GROUPED_HEX
+Create a new variable expansion context and store it into I<var>.
 
-var_unescape() encountered the end of the input buffer in the middle
-of a grouped-hex "\x{...}" expression.
+=item var_rc_t B<var_destroy>(var_t *I<var>);
 
-=item VAR_ERR_INCOMPLETE_OCTAL
+Destroy the variable expansion context I<var>.
 
-var_unescape() encountered the end of the input buffer in the middle
-of an octal "\000" expression.
+=item var_rc_t B<var_config>(var_t *I<var>, var_config_t I<mode>, ...);
 
-=item VAR_ERR_INVALID_OCTAL
+Configure the variable expansion context I<var>. The variable argument
+list depends on the I<mode> identifier:
 
-The second of third digit of an octal "\000" expression was found not
-be in the range of '0' to '7'.
+=over 4
 
-=item VAR_ERR_OCTAL_TOO_LARGE
+=item C<VAR_CONFIG_SYNTAX>, var_syntax_t *I<syntax>
 
-The value specified via an octal "\000" expression was larger than
-0377.
+This overrides the syntax configuration in I<var> with the one provided
+in I<syntax>. The complete structure contents is copied, so the caller
+is allowed to immediately destroy I<syntax> after the var_config() call.
+The default is the contents as shown above under the type description of
+B<var_syntax_t>.
+
+=item C<VAR_CONFIG_CB_VALUE>, var_cb_value_t I<cb>, void *I<ctx>
+
+This overrides the syntax configuration in I<var> with the one provided
+The default is C<NULL> for I<cb> and I<ctx>. At least C<NULL> for I<cb>
+is not valid for proper operation of var_expand(), so the caller has to
+configure the callback before variable expansions can be successfully
+performed.
 
-=item VAR_ERR_INVALID_HEX
+=back
 
-Any of the digits of a hex "\x00" expression was found not be in the
-range of '0' to '9' or 'a' to 'b'.
+=item var_rc_t B<var_unescape>(var_t *I<var>, const char *I<src_ptr>, size_t I<src_len>, char *I<dst_ptr>, size_t  I<dst_len>, int I<all>);
 
-=item VAR_ERR_INCOMPLETE_HEX
+This expands escape sequences found in the input buffer
+I<src_ptr>/I<src_len>. The I<dst_ptr>/I<dst_len> point to a output
+buffer, into which the expanded data is copied if processing is
+successful. The size of this buffer must be at least I<src_len>+1
+characters. The reason is that var_unescape() always adds a terminating
+C<NUL> ('C<\0>') character at the end of the output buffer, so that
+you can use the result comfortably with other C library routines. The
+supplied I<dst_ptr> either has to be point to a pre-allocated buffer or
+is allowed to point to I<src_ptr> (because the unescaping operation is
+guarrantied to either keep the size or reduce the size of the input).
+
+The parameter I<all> is a boolean flag that modifies the behavior
+of var_unescape(). If is set to true (any value except zero),
+var_unescape() will expand B<any> escape sequences it sees, even those that
+it does not know about. This means that "C<\1>" will become "C<1>",
+even though "C<\1>" has no special meaning to var_unescape(). If I<all>
+is set to false (the value zero), such escape sequences will be copied
+verbatimly to the output buffer.
+
+The quoted pairs supported by var_unescape() are "C<\t>" (tabulator),
+"C<\r>" (carriage return), "C<\n>" (line feed), "C<\NNN>" (octal value),
+"C<\xNN>" (hexadecimal value), and "C<\x{NNMM..}>" (grouped hexadecimal
+values).
+
+=item var_rc_t B<var_expand>(var_t *I<var>, const char *I<src_ptr>, size_t I<src_len>, char **I<dst_ptr>, size_t *I<dst_len>, int I<force_expand>);
+
+This is the heart of B<OSSP var>. It expands all syntax constructs in
+I<src_ptr>/I<src_len> and stores them in an allocated buffer returned
+in I<dst_ptr>/I<dst_len>.
+
+The output buffer I<dst_ptr>/I<dst_len> is allocated by var_expand()
+using the system call malloc(3), thus it is the caller's responsibility
+to free(3) that buffer once it is no longer used anymore. The output
+buffer for convinience reasons is always C<NUL>-terminated by
+var_expand(), but this C<NUL> character is not counted for I<dst_len>.
+The I<dst_len> pointer can be specified as C<NULL> if you are not
+interested in the output buffer length.
+
+The I<force_expand> flag determines how var_expand() deals with
+undefined variables (indicated by the callback function through the
+return code C<VAR_ERR_UNDEFINED_VARIABLE>). If it is set to true
+(any value except zero), var_expand() will fail with error code
+C<VAR_ERR_UNDEFINED_VARIABLE> whenever an undefined variable is
+encountered. That is, it just passes-through the return code of the
+callback function. If set to false (value zero), var_expand() will copy
+the expression it failed to expand verbatimly into the output buffer,
+in order to enable you to go over the buffer with an additional pass.
+Generally, if you do not plan to use muli-pass expansion, you should set
+I<force_expand> to true in order to make sure no unexpanded variable
+constructs are left over in the buffer.
+
+If var_expand() fails with an error, I<dst_ptr> will point to I<src_ptr>
+and I<dst_len> will contain the number of characters that have been
+consumed from I<src_ptr> before the error occured. In other words,
+if an error occurs, I<dst_ptr>/I<dst_len> point to the last parsing
+location in I<src_ptr>/I<src_len> before the error occured. The only
+exceptions for this error semantics are: on C<VAR_ERR_INVALID_ARGUMENT>
+and C<VAR_ERR_OUT_OF_MEMORY> errors, I<dst_ptr> and I<dst_len> are
+undefined.
+
+=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
+the B<OSSP var> API functions) into a clear-text message describing the
+reason for failure in prose. Please note that errors coming from the
+callback, such as C<VAR_ERR_CALLBACK> and those based on it, cannot be
+mapped and will yield the message "C<unknown error>".
 
-var_unescape() encountered the end of the input buffer in the middle
-of a hex "\x00" expression.
+=back
 
-=item VAR_ERR_INCOMPLETE_NAMED_CHARACTER
+=head2 VARIABLES
 
-var_unescape() encountered the backslash ('\') as the last character
-of the input buffer.
+=over 4
 
-=item VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED
+=item B<var_id>
 
 =back
 
-=head1 SO ALSO
+=head1 COMBINING UNESCAPING AND EXPANSION
 
-regex(7)
+For maximum power and flexibility, you usually want to combine
+var_unescape() and var_expand(). That is, you will want to use
+var_unescape() to turn all escape sequences into their real
+representation before you call var_expand() for expanding variable
+constructs. This way the user can safely use specials like "C<\n>" or
+"C<\t>" throughout the template and achieve the desired effect. These
+escape sequences are particularly useful if search-and-replace or
+transpose actions are performed on variables before they are expanded.
+Be sure, though, to make the first var_unescape() pass with the I<all>
+flag set to false, or the routine will also expand escape sequences like
+"C<\1>", which might have a special meaning (regular expression
+back-references) in the var_expand() pass to follow.
+
+Once, all known escape sequences are expanded, expand the variables
+with var_expand(). After that, you will want to have a second pass
+with var_unescape() and the flag I<all> set to true, to make sure all
+remaining escape sequences are expanded. Also, the var_expand() pass
+might have introduced now quoted pairs into the output text, which you
+need to expand to get the desired effect.
+
+=head1 EXAMPLE
+
+The following simple but complete program illustrates the full usage
+of B<OSSP var>. It accepts a single argument on the command line
+and expands this in three steps (unescaping known escape sequences,
+expanding variable constructs, unescaping new and unknown escape
+sequences). The value lookup callback uses the process environment to
+resolve variables.
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ 
+ #include "var.h"
+ 
+ static var_rc_t lookup(
+     void *ctx,
+     const char  *var_ptr, size_t  var_len, int     var_idx,
+     const char **val_ptr, size_t *val_len, size_t *val_size)
+ {
+     char tmp[256];
+ 
+     if (var_idx != 0)
+         return VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED;
+     if (var_len > sizeof(tmp) - 1)
+         return VAR_ERR_OUT_OF_MEMORY;
+     memcpy(tmp, var_ptr, var_len);
+     tmp[var_len] = '\0';
+     if ((*val_ptr = getenv(tmp)) == NULL)
+         return VAR_ERR_UNDEFINED_VARIABLE;
+     *val_len = strlen(*val_ptr);
+     *val_size = 0;
+     return VAR_OK;
+ }
+ 
+ static void die(const char *context, var_t *var, var_rc_t rc)
+ {
+     char *error;
+ 
+     var_strerror(var, rc, &error);
+     fprintf(stderr, "ERROR: %s: %s (%d)\n", context, error, rc);
+     exit(1);
+ }
+ 
+ int main(int argc, char *argv[])
+ {
+     var_t *var;
+     var_rc_t rc;
+     char *src_ptr;
+     char *dst_ptr;
+     size_t src_len;
+     size_t dst_len;
+     var_syntax_t syntax = { '\\', '$', '{', '}', '[', ']', '#', "a-zA-Z0-9_" };
+ 
+     /* command line handling */
+     if (argc != 2)
+         die("command line", NULL, VAR_ERR_INVALID_ARGUMENT);
+     src_ptr = argv[1];
+     src_len = strlen(src_ptr);
+     fprintf(stdout, "input:     \"%s\"\n", src_ptr);
+ 
+     /* establish variable expansion context */
+     if ((rc = var_create(&var)) != VAR_OK)
+         die("create context", NULL, rc);
+     if ((rc = var_config(var, VAR_CONFIG_SYNTAX, &syntax)) != VAR_OK)
+         die("configure syntax", var, rc);
+     if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup, NULL)) != VAR_OK)
+         die("configure callback", var, rc);
+ 
+     /* unescape known escape sequences (in place) */
+     if ((rc = var_unescape(var, src_ptr, src_len, src_ptr, src_len+1, 0)) != VAR_OK)
+         die("unescape known escape sequences", var, rc);
+     src_len = strlen(src_ptr);
+     fprintf(stdout, "unescaped: \"%s\"\n", src_ptr);
+ 
+     /* expand variable constructs (force expansion) */
+     if ((rc = var_expand(var, src_ptr, src_len, &dst_ptr, &dst_len, 1)) != VAR_OK) {
+         if (rc != VAR_ERR_INVALID_ARGUMENT && rc != VAR_ERR_OUT_OF_MEMORY) {
+             fprintf(stdout, "parsing:   \"%s\"\n", dst_ptr); 
+             fprintf(stdout, "             %*s\n", dst_len, "^"); 
+         }
+         die("variable expansion", var, rc);
+     }
+     fprintf(stdout, "expanded:  \"%s\"\n", dst_ptr);
+ 
+     /* unescape new and unknown escape sequences (in place) */
+     if ((rc = var_unescape(var, dst_ptr, dst_len, dst_ptr, dst_len+1, 1)) != VAR_OK)
+         die("unescape new and unknown escape sequences", var, rc);
+     fprintf(stdout, "output:    \"%s\"\n", dst_ptr);
+     free(dst_ptr);
+ 
+     /* destroy variable expansion context */
+     if ((rc = var_destroy(var)) != VAR_OK)
+         die("destroy context", var, rc);
+ 
+     return 0;
+ }
+
+Copy & paste the source code it into F<var_play.c> (or use the version
+already shipped with the B<OSSP var> source distribution), compile it
+with
+
+ $ cc `var-config --cflags` -o var_play var_play.c `var-config --ldflags --libs`
+
+and use it to play with the various B<OSSP var> variable expansion
+possibilities.
+
+=head1 SEE ALSO
+
+pcre(3), regex(7), B<OSSP val> (Value Access).
+
+=head1 HISTORY
+
+B<OSSP var> was written by Peter Simons E<lt>simons@crypt.toE<gt> in
+November 2001. Its API and internal code structure was revamped in
+February 2002 by Ralf S. Engelschall E<lt>rse@engelschall.comE<gt> to
+fully conform to the B<OSSP> library standards.
 
 =cut
+


ossp-pkg/var/var_play.c -> 1.1

*** /dev/null    Sat Nov 23 01:28:35 2024
--- -    Sat Nov 23 01:28:39 2024
***************
*** 0 ****
--- 1,90 ----
+ 
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ 
+ #include "var.h"
+ 
+ static var_rc_t lookup(
+     void *ctx,
+     const char  *var_ptr, size_t  var_len, int     var_idx,
+     const char **val_ptr, size_t *val_len, size_t *val_size)
+ {
+     char tmp[256];
+ 
+     if (var_idx != 0)
+         return VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED;
+     if (var_len > sizeof(tmp) - 1)
+         return VAR_ERR_OUT_OF_MEMORY;
+     memcpy(tmp, var_ptr, var_len);
+     tmp[var_len] = '\0';
+     if ((*val_ptr = getenv(tmp)) == NULL)
+         return VAR_ERR_UNDEFINED_VARIABLE;
+     *val_len = strlen(*val_ptr);
+     *val_size = 0;
+     return VAR_OK;
+ }
+ 
+ static void die(const char *context, var_t *var, var_rc_t rc)
+ {
+     char *error;
+ 
+     var_strerror(var, rc, &error);
+     fprintf(stderr, "ERROR: %s: %s (%d)\n", context, error, rc);
+     exit(1);
+ }
+ 
+ int main(int argc, char *argv[])
+ {
+     var_t *var;
+     var_rc_t rc;
+     char *src_ptr;
+     char *dst_ptr;
+     size_t src_len;
+     size_t dst_len;
+     var_syntax_t syntax = { '\\', '$', '{', '}', '[', ']', '#', "a-zA-Z0-9_" };
+ 
+     /* command line handling */
+     if (argc != 2)
+         die("command line", NULL, VAR_ERR_INVALID_ARGUMENT);
+     src_ptr = argv[1];
+     src_len = strlen(src_ptr);
+     fprintf(stdout, "input:     \"%s\"\n", src_ptr);
+ 
+     /* establish variable expansion context */
+     if ((rc = var_create(&var)) != VAR_OK)
+         die("create context", NULL, rc);
+     if ((rc = var_config(var, VAR_CONFIG_SYNTAX, &syntax)) != VAR_OK)
+         die("configure syntax", var, rc);
+     if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup, NULL)) != VAR_OK)
+         die("configure callback", var, rc);
+ 
+     /* unescape known escape sequences (in place) */
+     if ((rc = var_unescape(var, src_ptr, src_len, src_ptr, src_len+1, 0)) != VAR_OK)
+         die("unescape known escape sequences", var, rc);
+     src_len = strlen(src_ptr);
+     fprintf(stdout, "unescaped: \"%s\"\n", src_ptr);
+ 
+     /* expand variable constructs (force expansion) */
+     if ((rc = var_expand(var, src_ptr, src_len, &dst_ptr, &dst_len, 1)) != VAR_OK) {
+         if (rc != VAR_ERR_INVALID_ARGUMENT && rc != VAR_ERR_OUT_OF_MEMORY) {
+             fprintf(stdout, "parsing:   \"%s\"\n", dst_ptr); 
+             fprintf(stdout, "             %*s\n", dst_len, "^"); 
+         }
+         die("variable expansion", var, rc);
+     }
+     fprintf(stdout, "expanded:  \"%s\"\n", dst_ptr);
+ 
+     /* unescape new and unknown escape sequences (in place) */
+     if ((rc = var_unescape(var, dst_ptr, dst_len, dst_ptr, dst_len+1, 1)) != VAR_OK)
+         die("unescape new and unknown escape sequences", var, rc);
+     fprintf(stdout, "output:    \"%s\"\n", dst_ptr);
+     free(dst_ptr);
+ 
+     /* destroy variable expansion context */
+     if ((rc = var_destroy(var)) != VAR_OK)
+         die("destroy context", var, rc);
+ 
+     return 0;
+ }
+ 


ossp-pkg/var/var_qref.pod 1.3 -> 1.4



ossp-pkg/var/var_test.c 1.33 -> 1.34

--- var_test.c   2002/02/27 13:44:16     1.33
+++ var_test.c   2002/02/28 08:08:16     1.34
@@ -205,26 +205,38 @@
     var_rc_t rc;
     size_t i;
     char buffer[1024];
+    var_t *var;
+    char *err;
 
-    for (i = 0; i < sizeof(tests) / sizeof(struct test_case); ++i) {
+    if ((rc = var_create(&var)) != VAR_OK) {
+        var_strerror(NULL, rc, &err);
+        printf("unable to create variable expansion context: %s (%d)\n", err, rc);
+        return 1;
+    }
+    if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, var_lookup, vars)) != VAR_OK) {
+        var_strerror(NULL, rc, &err);
+        printf("unable to configure variable expansion context: %s (%d)\n", err, rc);
+        return 1;
+    }
+
+    for (i = 0; i < sizeof(tests)/sizeof(struct test_case); ++i) {
 #ifdef DEBUG
         printf("Test case #%02d: Original input is '%s'.\n", i,
                tests[i].input);
 #endif
-        rc = var_unescape(tests[i].input, strlen(tests[i].input), buffer, 0);
+        rc = var_unescape(var, tests[i].input, strlen(tests[i].input), buffer, sizeof(buffer), 0);
         if (rc != VAR_OK) {
-            printf ("Test case #%d: First var_unescape() failed with return code %d ('%s').\n",
-                    i, rc, var_strerror(rc));
+            var_strerror(var, rc, &err);
+            printf("Test case #%d: var_unescape() failed: %s (%d)\n", i, err, rc);
             return 1;
         }
 #ifdef DEBUG
         printf("Test case #%02d: Unescaped input is '%s'.\n", i, buffer);
 #endif
-        rc = var_expand(buffer, strlen(buffer), &tmp, &tmp_len,
-            &var_lookup, (void *)vars, NULL, 0);
+        rc = var_expand(var, buffer, strlen(buffer), &tmp, &tmp_len, 0);
         if (rc != VAR_OK) {
-            printf ("Test case #%d: var_expand() failed with return code %d ('%s').\n",
-                    i, rc, var_strerror(rc));
+            var_strerror(var, rc, &err);
+            printf("Test case #%d: var_expand() failed: %s (%d).\n", i, err, rc);
             return 1;
         }
 #ifdef DEBUG
@@ -234,11 +246,12 @@
             || tmp == NULL
             || memcmp(tests[i].expected, tmp, tmp_len) != 0) {
             printf("Test case #%d: Expected result '%s' but got '%s'.\n",
-                i, tests[i].expected, tmp);
+                   i, tests[i].expected, tmp);
             return 1;
         }
         free(tmp);
     }
+    var_destroy(var);
     return 0;
 }
 

CVSTrac 2.0.1