OSSP CVS Repository

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

Check-in Number: 1297
Date: 2001-Nov-13 13:08:30 (local)
2001-Nov-13 12:08:30 (UTC)
User:simons
Branch:
Comment: - Moved all routines into a single source file var.c. - Renamed varexp.h to var.h. - Removed odin-based build system. - Moved test cases into var_test.c. - Adapted build system.
Tickets:
Inspections:
Files:
ossp-pkg/var/Makefile      1.1 -> 1.2     5 inserted, 23 deleted
ossp-pkg/var/Odinfile      1.1->removed
ossp-pkg/var/command.c      1.1->removed
ossp-pkg/var/cut-out-offset.c      1.1->removed
ossp-pkg/var/expand-character-class.c      1.1->removed
ossp-pkg/var/expand-named-characters.c      1.1->removed
ossp-pkg/var/expand.c      1.1->removed
ossp-pkg/var/expression.c      1.1->removed
ossp-pkg/var/input.c      1.1->removed
ossp-pkg/var/internal.h      1.1->removed
ossp-pkg/var/padding.c      1.1->removed
ossp-pkg/var/regression-tests/.run-tests      1.1->removed
ossp-pkg/var/regression-tests/Makefile      1.1->removed
ossp-pkg/var/regression-tests/Odinfile      1.1->removed
ossp-pkg/var/regression-tests/empty-search-pattern.c      1.1->removed
ossp-pkg/var/regression-tests/expand-character-class.c      1.1->removed
ossp-pkg/var/regression-tests/expand-named-characters.c      1.1->removed
ossp-pkg/var/regression-tests/expand1.c      1.1->removed
ossp-pkg/var/regression-tests/expand2.c      1.1->removed
ossp-pkg/var/regression-tests/expand3.c      1.1->removed
ossp-pkg/var/regression-tests/expand4.c      1.1->removed
ossp-pkg/var/regression-tests/expand5.c      1.1->removed
ossp-pkg/var/regression-tests/expand6.c      1.1->removed
ossp-pkg/var/regression-tests/force-expand.c      1.1->removed
ossp-pkg/var/regression-tests/offset-failure.c      1.1->removed
ossp-pkg/var/search-and-replace.c      1.1->removed
ossp-pkg/var/text.c      1.1->removed
ossp-pkg/var/tokenbuf.c      1.1->removed
ossp-pkg/var/transpose.c      1.1->removed
ossp-pkg/var/var.c      added-> 1.1
ossp-pkg/var/var.h      added-> 1.1
ossp-pkg/var/var_test.c      added-> 1.1
ossp-pkg/var/varexp.h      1.1->removed
ossp-pkg/var/variable.c      1.1->removed

ossp-pkg/var/Makefile 1.1 -> 1.2

--- Makefile     2001/11/09 17:01:37     1.1
+++ Makefile     2001/11/13 12:08:30     1.2
@@ -11,9 +11,7 @@
 CFLAGS          =
 LDFLAGS         =
 
-OBJS            = expand-named-characters.o expand-character-class.o command.o \
-                  expression.o variable.o text.o expand.o input.o tokenbuf.o   \
-                  search-and-replace.o cut-out-offset.o transpose.o padding.o
+OBJS            = var.o
 
 .c.o:
         $(CC) $(CPPFLAGS) $(WARNFLAGS) $(OPTFLAGS) $(CFLAGS) -c $<
@@ -25,26 +23,10 @@
         $(AR) cr $@ $(OBJS)
         $(RANLIB) $@
 
-clean::
-        @(cd regression-tests && $(MAKE) clean)
-        rm -f $(OBJS)
-        rm -f libvarexp.a
+var_test:       var_test.o libvarexp.a
+        $(CC) $(LDFLAGS) -o $@ var_test.o libvarexp.a
 
-check::
-        (cd regression-tests && $(MAKE) check)
+clean::
+        rm -f $(OBJS) libvarexp.a var_test
 
 # Dependencies
-
-command.o: internal.h varexp.h
-cut-out-offset.o: internal.h varexp.h
-expand-character-class.o: internal.h varexp.h
-expand-named-characters.o: internal.h varexp.h
-expand.o: internal.h varexp.h
-expression.o: internal.h varexp.h
-input.o: internal.h varexp.h
-padding.o: internal.h varexp.h
-search-and-replace.o: internal.h varexp.h
-text.o: internal.h varexp.h
-tokenbuf.o: internal.h varexp.h
-transpose.o: internal.h varexp.h
-variable.o: internal.h varexp.h


ossp-pkg/var/Odinfile 1.1 -> 1.2



ossp-pkg/var/command.c 1.1 -> 1.2



ossp-pkg/var/cut-out-offset.c 1.1 -> 1.2



ossp-pkg/var/expand-character-class.c 1.1 -> 1.2



ossp-pkg/var/expand-named-characters.c 1.1 -> 1.2



ossp-pkg/var/expand.c 1.1 -> 1.2



ossp-pkg/var/expression.c 1.1 -> 1.2



ossp-pkg/var/input.c 1.1 -> 1.2



ossp-pkg/var/internal.h 1.1 -> 1.2



ossp-pkg/var/padding.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/.run-tests 1.1 -> 1.2



ossp-pkg/var/regression-tests/Makefile 1.1 -> 1.2



ossp-pkg/var/regression-tests/Odinfile 1.1 -> 1.2



ossp-pkg/var/regression-tests/empty-search-pattern.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/expand-character-class.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/expand-named-characters.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/expand1.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/expand2.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/expand3.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/expand4.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/expand5.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/expand6.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/force-expand.c 1.1 -> 1.2



ossp-pkg/var/regression-tests/offset-failure.c 1.1 -> 1.2



ossp-pkg/var/search-and-replace.c 1.1 -> 1.2



ossp-pkg/var/text.c 1.1 -> 1.2



ossp-pkg/var/tokenbuf.c 1.1 -> 1.2



ossp-pkg/var/transpose.c 1.1 -> 1.2



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

*** /dev/null    Fri Apr 19 03:04:04 2024
--- -    Fri Apr 19 03:05:30 2024
***************
*** 0 ****
--- 1,1708 ----
+ /*
+ **  VAR - OSSP variable expression library.
+ **  Copyright (c) 2001 The OSSP Project (http://www.ossp.org/)
+ **  Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/)
+ **
+ **  This file is part of OSSP VAR, an extensible data serialization
+ **  library which can be found at http://www.ossp.org/pkg/var/.
+ **
+ **  Permission to use, copy, modify, and distribute this software for
+ **  any purpose with or without fee is hereby granted, provided that
+ **  the above copyright notice and this permission notice appear in all
+ **  copies.
+ **
+ **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+ **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ **  SUCH DAMAGE.
+ **
+ **  var.h: VAR library API
+ */
+ 
+ #include <assert.h>
+ #include <ctype.h>
+ #include <string.h>
+ #include <stdio.h>
+ #include <errno.h>
+ #include <sys/types.h>
+ #include <regex.h>
+ #include "var.h"
+ 
+ /* The default configuration for the parser. */
+ 
+ const var_config_t var_config_default =
+     {
+     '$',                        /* varinit */
+     '{',                        /* startdelim */
+     '}',                        /* enddelim */
+     '\\',                       /* escape */
+     "a-zA-Z0-9_"                /* namechars */
+     };
+ 
+ /* Routines for manipulation of tokenbufs. */
+ 
+ #define VAR_INITIAL_BUFFER_SIZE 64
+ 
+ typedef struct
+     {
+     const char*  begin;
+     const char*  end;
+     size_t buffer_size;
+     }
+ tokenbuf;
+ 
+ static void init_tokenbuf(tokenbuf* buf)
+     {
+     buf->begin = buf->end = NULL;
+     buf->buffer_size = 0;
+     }
+ 
+ static void move_tokenbuf(tokenbuf* src, tokenbuf* dst)
+     {
+     dst->begin = src->begin;
+     dst->end = src->end;
+     dst->buffer_size = src->buffer_size;
+     init_tokenbuf(src);
+     }
+ 
+ static int assign_to_tokenbuf(tokenbuf* buf, const char* data, size_t len)
+     {
+     char* p = malloc(len+1);
+     if (p)
+         {
+         memcpy(p, data, len);
+         buf->begin       = p;
+         buf->end         = p + len;
+         buf->buffer_size = len + 1;
+         *((char*)(buf->end)) = '\0';
+         return 1;
+         }
+     else
+         return 0;
+     }
+ 
+ static int append_to_tokenbuf(tokenbuf* output, const char* data, size_t len)
+     {
+     char*  new_buffer;
+     size_t new_size;
+ 
+     /* Is the tokenbuffer initialized at all? If not, allocate a
+        standard-sized buffer to begin with. */
+ 
+     if (output->begin == NULL)
+         {
+         if ((output->begin = output->end = malloc(VAR_INITIAL_BUFFER_SIZE)) == NULL)
+             return 0;
+         else
+             output->buffer_size = VAR_INITIAL_BUFFER_SIZE;
+         }
+ 
+     /* Does the token contain text, but no buffer has been allocated
+        yet? */
+ 
+     if (output->buffer_size == 0)
+         {
+         /* Check whether data borders to output. If, we can append
+            simly by increasing the end pointer. */
+ 
+         if (output->end == data)
+             {
+             output->end += len;
+             return 1;
+             }
+ 
+         /* OK, so copy the contents of output into an allocated buffer
+            so that we can append that way. */
+ 
+         else
+             {
+             char* tmp = malloc(output->end - output->begin + len + 1);
+             if (!tmp)
+                 return 0;
+             memcpy(tmp, output->begin, output->end - output->begin);
+             output->buffer_size = output->end - output->begin;
+             output->begin = tmp;
+             output->end   = tmp + output->buffer_size;
+             output->buffer_size += len + 1;
+             }
+         }
+ 
+     /* Does the token fit into the current buffer? If not, realloc a
+        larger buffer that fits. */
+ 
+     if ((output->buffer_size - (output->end - output->begin)) <= len)
+         {
+         new_size = output->buffer_size;
+         do
+             {
+             new_size *= 2;
+             }
+         while ((new_size - (output->end - output->begin)) <= len);
+         new_buffer = realloc((char*)output->begin, new_size);
+         if (new_buffer == NULL)
+             return 0;
+         output->end = new_buffer + (output->end - output->begin);
+         output->begin = new_buffer;
+         output->buffer_size = new_size;
+         }
+ 
+     /* Append the data at the end of the current buffer. */
+ 
+     memcpy((char*)output->end, data, len);
+     output->end += len;
+     *((char*)output->end) = '\0';
+     return 1;
+     }
+ 
+ static void free_tokenbuf(tokenbuf* buf)
+     {
+     if (buf->begin != NULL && buf->buffer_size > 0)
+         free((char*)buf->begin);
+     buf->begin = buf->end = NULL;
+     buf->buffer_size = 0;
+     }
+ 
+ static size_t tokenbuf2int(tokenbuf* number)
+     {
+     const char* p;
+     size_t num = 0;
+     for (p = number->begin; p != number->end; ++p)
+         {
+         num *= 10;
+         num += *p - '0';
+         }
+     return num;
+     }
+ 
+ /* Routines for the expansion of quoted-pair expressions. */
+ 
+ static void expand_range(char a, char b, char class[256])
+     {
+     assert(a <= b);
+     assert(class != NULL);
+ 
+     do
+         {
+         class[(int)a] = 1;
+         }
+     while (++a <= b);
+     }
+ 
+ static var_rc_t expand_character_class(const char* desc, char class[256])
+     {
+     size_t i;
+ 
+     assert(desc != NULL);
+     assert(class != NULL);
+ 
+     /* Clear the class array. */
+ 
+     for (i = 0; i < 256; ++i)
+         class[i] = 0;
+ 
+     /* Walk through the class description and set the appropriate
+        entries in the array. */
+ 
+     while(*desc != '\0')
+         {
+         if (desc[1] == '-' && desc[2] != '\0')
+             {
+             if (desc[0] > desc[2])
+                 return VAR_INCORRECT_CLASS_SPEC;
+             expand_range(desc[0], desc[2], class);
+             desc += 3;
+             }
+         else
+             {
+             class[(int)*desc] = 1;
+             ++desc;
+             }
+         }
+ 
+     return VAR_OK;
+     }
+ 
+ static int isoct(char c)
+     {
+     if (c >= '0' && c <= '7')
+         return 1;
+     else
+         return 0;
+     }
+ 
+ static var_rc_t expand_octal(const char** src, char** dst, const char* end)
+     {
+     unsigned char c;
+ 
+     if (end - *src < 3)
+         return VAR_INCOMPLETE_OCTAL;
+     if (!isoct(**src) || !isoct((*src)[1]) || !isoct((*src)[2]))
+         return VAR_INVALID_OCTAL;
+ 
+     c = **src - '0';
+     if (c > 3)
+         return VAR_OCTAL_TOO_LARGE;
+     c *= 8;
+     ++(*src);
+ 
+     c += **src - '0';
+     c *= 8;
+     ++(*src);
+ 
+     c += **src - '0';
+ 
+     **dst = (char)c;
+     ++(*dst);
+     return VAR_OK;
+     }
+ 
+ static int ishex(char c)
+     {
+     if ((c >= '0' && c <= '9') ||
+         (c >= 'a' && c <= 'f') ||
+         (c >= 'A' && c <= 'F'))
+         return 1;
+     else
+         return 0;
+     }
+ 
+ static var_rc_t expand_simple_hex(const char** src, char** dst, const char* end)
+     {
+     unsigned char c = 0;
+ 
+     if (end - *src < 2)
+         return VAR_INCOMPLETE_HEX;
+     if (!ishex(**src) || !ishex((*src)[1]))
+         return VAR_INVALID_HEX;
+ 
+     if (**src >= '0' && **src <= '9')
+         c = **src - '0';
+     else if (c >= 'a' && c <= 'f')
+         c = **src - 'a' + 10;
+     else if (c >= 'A' && c <= 'F')
+         c = **src - 'A' + 10;
+ 
+     c = c << 4;
+     ++(*src);
+ 
+     if (**src >= '0' && **src <= '9')
+         c += **src - '0';
+     else if (**src >= 'a' && **src <= 'f')
+         c += **src - 'a' + 10;
+     else if (**src >= 'A' && **src <= 'F')
+         c += **src - 'A' + 10;
+ 
+     **dst = (char)c;
+     ++(*dst);
+     return VAR_OK;
+     }
+ 
+ static var_rc_t expand_grouped_hex(const char** src, char** dst, const char* end)
+     {
+     var_rc_t rc;
+ 
+     while (*src < end && **src != '}')
+         {
+         if ((rc = expand_simple_hex(src, dst, end)) != 0)
+             return rc;
+         ++(*src);
+         }
+     if (*src == end)
+         return VAR_INCOMPLETE_GROUPED_HEX;
+ 
+     return VAR_OK;
+     }
+ 
+ static var_rc_t expand_hex(const char** src, char** dst, const char* end)
+     {
+     if (*src == end)
+         return VAR_INCOMPLETE_HEX;
+     if (**src == '{')
+         {
+         ++(*src);
+         return expand_grouped_hex(src, dst, end);
+         }
+     else
+         return expand_simple_hex(src, dst, end);
+     }
+ 
+ var_rc_t expand_named_characters(const char* src, size_t len, char* dst)
+     {
+     const char* end = src + len;
+     var_rc_t rc;
+ 
+     assert(src != NULL);
+     assert(dst != NULL);
+ 
+     while (src < end)
+         {
+         if (*src == '\\')
+             {
+             if (++src == end)
+                 return VAR_INCOMPLETE_NAMED_CHARACTER;
+             switch (*src)
+                 {
+                 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)) != 0)
+                         return rc;
+                     break;
+                 case '0':
+                 case '1':
+                 case '2':
+                 case '3':
+                 case '4':
+                 case '5':
+                 case '6':
+                 case '7':
+                 case '8':
+                 case '9':
+                     if ((rc = expand_octal(&src, &dst, end)) != 0)
+                         return rc;
+                     break;
+                 default:
+                     *dst++ = *src;
+                 }
+             ++src;
+             }
+         else
+             *dst++ = *src++;
+         }
+     *dst = '\0';
+     return VAR_OK;
+     }
+ 
+ /* The recursive-descent parser for variable expressions. */
+ 
+ static int variable(const char*, const char*, const var_config_t*,
+                     const char[256], var_cb_t, void*, int, tokenbuf*);
+ static int command(const char*, const char*, const var_config_t*,
+                    const char[256], var_cb_t, void*, int, tokenbuf*);
+ 
+ static int text(const char* begin, const char* end, char varinit, char escape)
+     {
+     const char* p;
+     for (p = begin; p != end && *p != varinit; ++p)
+         {
+         if (*p == escape)
+             {
+             if (p+1 == end)
+                 return VAR_INCOMPLETE_QUOTED_PAIR;
+             else
+                 ++p;
+             }
+         }
+     return p - begin;
+     }
+ 
+ static int varname(const char* begin, const char* end, const char nameclass[256])
+     {
+     const char* p;
+     for (p = begin; p != end && nameclass[(int)*p]; ++p)
+         ;
+     return p - begin;
+     }
+ 
+ static int number(const char* begin, const char* end)
+     {
+     const char* p;
+     for (p = begin; p != end && isdigit(*p); ++p)
+         ;
+     return p - begin;
+     }
+ 
+ static int substext(const char* begin, const char* end, const var_config_t* config)
+     {
+     const char* p;
+     for (p = begin; p != end && *p != config->varinit && *p != '/'; ++p)
+         {
+         if (*p == config->escape)
+             {
+             if (p+1 == end)
+                 return VAR_INCOMPLETE_QUOTED_PAIR;
+             else
+                 ++p;
+             }
+         }
+     return p - begin;
+     }
+ 
+ static int exptext(const char* begin, const char* end, const var_config_t* config)
+     {
+     const char* p;
+     for (p = begin; p != end && *p != config->varinit && *p != config->enddelim && *p != ':'; ++p)
+         {
+         if (*p == config->escape)
+             {
+             if (p+1 == end)
+                 return VAR_INCOMPLETE_QUOTED_PAIR;
+             else
+                 ++p;
+             }
+         }
+     return p - begin;
+     }
+ 
+ 
+ static int expression(const char* begin, const char* end, const var_config_t* config,
+                       const char nameclass[256], var_cb_t lookup, void* lookup_context,
+                       int force_expand, tokenbuf* result)
+     {
+     const char* p = begin;
+     const char* data;
+     size_t len, buffer_size;
+     int failed = 0;
+     int rc;
+     tokenbuf name;
+     tokenbuf tmp;
+ 
+     /* Clear the tokenbufs to make sure we have a defined state. */
+ 
+     init_tokenbuf(&name);
+     init_tokenbuf(&tmp);
+     init_tokenbuf(result);
+ 
+     /* Expect STARTDELIM. */
+ 
+     if (p == end || *p != config->startdelim)
+         return 0;
+ 
+     if (++p == end)
+         return VAR_INCOMPLETE_VARIABLE_SPEC;
+ 
+     /* Get the name of the variable to expand. The name may consist of
+        an arbitrary number of VARNAMEs and VARIABLEs. */
+ 
+     do
+         {
+         rc = varname(p, end, nameclass);
+         if (rc < 0)
+             goto error_return;
+         else if (rc > 0)
+             {
+             if (!append_to_tokenbuf(&name, p, rc))
+                 {
+                 rc = VAR_OUT_OF_MEMORY;
+                 goto error_return;
+                 }
+             else
+                 p += rc;
+             }
+ 
+         rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp);
+         if (rc < 0)
+             goto error_return;
+         else if (rc > 0)
+             {
+             if (!append_to_tokenbuf(&name, tmp.begin, tmp.end - tmp.begin))
+                 {
+                 rc = VAR_OUT_OF_MEMORY;
+                 goto error_return;
+                 }
+             else
+                 p += rc;
+             }
+         }
+     while (rc > 0);
+ 
+     /* We must have the complete variable name now, so make sure we
+        do. */
+ 
+     if (name.begin == name.end)
+         {
+         rc = VAR_INCOMPLETE_VARIABLE_SPEC;
+         goto error_return;
+         }
+ 
+     /* Now we have the name of the variable stored in "name". We
+        expect an ENDDELIM here. */
+ 
+     if (p == end || (*p != config->enddelim && *p != ':'))
+         {
+         rc = VAR_INCOMPLETE_VARIABLE_SPEC;
+         goto error_return;
+         }
+     else
+         ++p;
+ 
+     /* Use the lookup callback to get the variable's contents. */
+ 
+     rc = (*lookup)(lookup_context, name.begin, name.end - name.begin, &data, &len, &buffer_size);
+     if (rc < 0)
+         goto error_return;
+     else if (rc == 0)
+         {
+         /* The variable is undefined. What we'll do now depends on the
+            force_expand flag. */
+ 
+         if (force_expand)
+             {
+             rc = VAR_UNDEFINED_VARIABLE;
+             goto error_return;
+             }
+         else
+             {
+             /* Initialize result to point back to the original text in
+                the buffer. */
+ 
+             result->begin       = begin-1;
+             result->end         = p;
+             result->buffer_size = 0;
+             failed              = 1;
+             }
+         }
+     else
+         {
+         /* The preliminary result is the contents of the variable.
+            This may be modified by the commands that may follow. */
+ 
+         result->begin       = data;
+         result->end         = data + len;
+         result->buffer_size = buffer_size;
+         }
+ 
+     if (p[-1] == ':')
+         {
+         /* Parse and execute commands. */
+ 
+         free_tokenbuf(&tmp);
+         --p;
+         while (p != end && *p == ':')
+             {
+             ++p;
+             if (!failed)
+                 rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, result);
+             else
+                 rc = command(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp);
+             if (rc < 0)
+                 goto error_return;
+             p += rc;
+             if (failed)
+                 result->end += rc;
+             }
+ 
+         if (p == end || *p != config->enddelim)
+             {
+             rc = VAR_INCOMPLETE_VARIABLE_SPEC;
+             goto error_return;
+             }
+         ++p;
+         if (failed)
+             ++result->end;
+         }
+ 
+     /* Exit gracefully. */
+ 
+     free_tokenbuf(&name);
+     free_tokenbuf(&tmp);
+     return p - begin;
+ 
+     /* Exit in case of an error. */
+ 
+   error_return:
+     free_tokenbuf(&name);
+     free_tokenbuf(&tmp);
+     free_tokenbuf(result);
+     return rc;
+     }
+ 
+ static int variable(const char* begin, const char* end, const var_config_t* config,
+                     const char nameclass[256], var_cb_t lookup, void* lookup_context,
+                     int force_expand, tokenbuf* result)
+     {
+     const char* p = begin;
+     const char* data;
+     size_t len, buffer_size;
+     int rc, rc2;
+ 
+     /* Clear the result tokenbuf to make sure we're in a defined
+        state. */
+ 
+     init_tokenbuf(result);
+ 
+     /* Expect VARINIT. */
+ 
+     if (p == end || *p != config->varinit)
+         return 0;
+ 
+     if (++p == end)
+         return VAR_INCOMPLETE_VARIABLE_SPEC;
+ 
+     /* Try to read the variable name. If that fails, we're parsing a
+        complex expression. */
+ 
+     rc = varname(p, end, nameclass);
+     if (rc < 0)
+         return rc;
+     else if (rc > 0)
+         {
+         rc2 = (*lookup)(lookup_context, p, rc, &data, &len, &buffer_size);
+         if (rc2 < 0)
+             return rc2;
+         else if (rc2 == 0)
+             {
+             if (force_expand)
+                 return VAR_UNDEFINED_VARIABLE;
+             else
+                 {
+                 result->begin       = begin;
+                 result->end         = begin + 1 + rc;
+                 result->buffer_size = 0;
+                 return 1 + rc;
+                 }
+             }
+         else
+             {
+             result->begin       = data;
+             result->end         = data + len;
+             result->buffer_size = buffer_size;
+             return 1 + rc;
+             }
+         }
+ 
+     /* OK, we're dealing with a complex expression here. */
+ 
+     rc = expression(p, end, config, nameclass, lookup, lookup_context, force_expand, result);
+     if (rc > 0)
+         ++rc;
+     return rc;
+     }
+ 
+ static int exptext_or_variable(const char* begin, const char* end, const var_config_t* config,
+                         const char nameclass[256], var_cb_t lookup, void* lookup_context,
+                         int force_expand, tokenbuf* result)
+     {
+     const char* p = begin;
+     tokenbuf tmp;
+     int rc;
+ 
+     init_tokenbuf(result);
+     init_tokenbuf(&tmp);
+ 
+     if (begin == end)
+         return 0;
+ 
+     do
+         {
+         rc = exptext(p, end, config);
+         if (rc < 0)
+             goto error_return;
+         else if (rc > 0)
+             {
+             if (!append_to_tokenbuf(result, p, rc))
+                 {
+                 rc = VAR_OUT_OF_MEMORY;
+                 goto error_return;
+                 }
+             else
+                 p += rc;
+             }
+ 
+         rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp);
+         if (rc < 0)
+             goto error_return;
+         else if (rc > 0)
+             {
+             p += rc;
+             if (!append_to_tokenbuf(result, tmp.begin, tmp.end - tmp.begin))
+                 {
+                 rc = VAR_OUT_OF_MEMORY;
+                 goto error_return;
+                 }
+             }
+         }
+     while (rc > 0);
+ 
+     free_tokenbuf(&tmp);
+     return p - begin;
+ 
+   error_return:
+     free_tokenbuf(&tmp);
+     free_tokenbuf(result);
+     return rc;
+     }
+ 
+ static int substext_or_variable(const char* begin, const char* end, const var_config_t* config,
+                                 const char nameclass[256], var_cb_t lookup, void* lookup_context,
+                                 int force_expand, tokenbuf* result)
+     {
+     const char* p = begin;
+     tokenbuf tmp;
+     int rc;
+ 
+     init_tokenbuf(result);
+     init_tokenbuf(&tmp);
+ 
+     if (begin == end)
+         return 0;
+ 
+     do
+         {
+         rc = substext(p, end, config);
+         if (rc < 0)
+             goto error_return;
+         else if (rc > 0)
+             {
+             if (!append_to_tokenbuf(result, p, rc))
+                 {
+                 rc = VAR_OUT_OF_MEMORY;
+                 goto error_return;
+                 }
+             else
+                 p += rc;
+             }
+ 
+         rc = variable(p, end, config, nameclass, lookup, lookup_context, force_expand, &tmp);
+         if (rc < 0)
+             goto error_return;
+         else if (rc > 0)
+             {
+             p += rc;
+             if (!append_to_tokenbuf(result, tmp.begin, tmp.end - tmp.begin))
+                 {
+                 rc = VAR_OUT_OF_MEMORY;
+                 goto error_return;
+                 }
+             }
+         }
+     while (rc > 0);
+ 
+     free_tokenbuf(&tmp);
+     return p - begin;
+ 
+   error_return:
+     free_tokenbuf(&tmp);
+     free_tokenbuf(result);
+     return rc;
+     }
+ 
+ 
+ static int expand_class_description(tokenbuf* src, tokenbuf* dst)
+     {
+     unsigned char c, d;
+     const char* p = src->begin;
+     while(p != src->end)
+         {
+         if ((src->end - p) >= 3 && p[1] == '-')
+             {
+             printf("Expand class.\n");
+             if (*p > p[2])
+                 return VAR_INCORRECT_TRANSPOSE_CLASS_SPEC;
+             for (c = *p, d = p[2]; c <= d; ++c)
+                 {
+                 if (!append_to_tokenbuf(dst, (char*)&c, 1))
+                     return VAR_OUT_OF_MEMORY;
+                 }
+             p += 3;
+             }
+         else
+             {
+             printf("Copy verbatim.\n");
+             if (!append_to_tokenbuf(dst, p, 1))
+                 return VAR_OUT_OF_MEMORY;
+             else
+                 ++p;
+             }
+         }
+     return VAR_OK;
+     }
+ 
+ static int transpose(tokenbuf* data, tokenbuf* search, tokenbuf* replace)
+     {
+     tokenbuf srcclass, dstclass;
+     const char* p;
+     int rc;
+     size_t i;
+ 
+     init_tokenbuf(&srcclass);
+     init_tokenbuf(&dstclass);
+ 
+     if ((rc = expand_class_description(search, &srcclass)) != VAR_OK)
+         goto error_return;
+     if ((rc = expand_class_description(replace, &dstclass)) != VAR_OK)
+         goto error_return;
+ 
+     printf("Transpose from '%s' to '%s'.\n",
+            srcclass.begin, dstclass.begin);
+ 
+     if (srcclass.begin == srcclass.end)
+         {
+         rc = VAR_EMPTY_TRANSPOSE_CLASS;
+         goto error_return;
+         }
+     if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin))
+         {
+         rc = VAR_TRANSPOSE_CLASSES_MISMATCH;
+         goto error_return;
+         }
+ 
+     if (data->buffer_size == 0)
+         {
+         tokenbuf tmp;
+         if (!assign_to_tokenbuf(&tmp, data->begin, data->end - data->begin))
+             {
+             rc = VAR_OUT_OF_MEMORY;
+             goto error_return;
+             }
+         move_tokenbuf(&tmp, data);
+         }
+ 
+     for (p = data->begin; p != data->end; ++p)
+         {
+         for (i = 0; i <= (srcclass.end - srcclass.begin); ++i)
+             {
+             if (*p == srcclass.begin[i])
+                 {
+                 *((char*)p) = dstclass.begin[i];
+                 break;
+                 }
+             }
+         }
+ 
+     free_tokenbuf(&srcclass);
+     free_tokenbuf(&dstclass);
+     return VAR_OK;
+ 
+   error_return:
+     free_tokenbuf(search);
+     free_tokenbuf(replace);
+     free_tokenbuf(&srcclass);
+     free_tokenbuf(&dstclass);
+     return rc;
+     }
+ 
+ static int cut_out_offset(tokenbuf* data, tokenbuf* number1, tokenbuf* number2, int isrange)
+     {
+     tokenbuf res;
+     const char* p;
+     size_t num1 = tokenbuf2int(number1);
+     size_t num2 = tokenbuf2int(number2);
+ 
+     /* Determine begin of result string. */
+ 
+     if ((data->end - data->begin) < num1)
+         return VAR_OFFSET_OUT_OF_BOUNDS;
+     else
+         p = data->begin + num1;
+ 
+     /* If num2 is zero, we copy the rest from there. */
+ 
+     if (num2 == 0)
+         {
+         if (!assign_to_tokenbuf(&res, p, data->end - p))
+             return VAR_OUT_OF_MEMORY;
+         }
+     else                        /* OK, then use num2. */
+         {
+         if (isrange)
+             {
+             if ((p + num2) > data->end)
+                 return VAR_RANGE_OUT_OF_BOUNDS;
+             if (!assign_to_tokenbuf(&res, p, num2))
+                 return VAR_OUT_OF_MEMORY;
+             }
+         else
+             {
+             if (num2 < num1)
+                 return VAR_OFFSET_LOGIC_ERROR;
+             if ((data->begin + num2) > data->end)
+                 return VAR_RANGE_OUT_OF_BOUNDS;
+             if (!assign_to_tokenbuf(&res, p, (data->begin + num2) - p))
+                 return VAR_OUT_OF_MEMORY;
+             }
+         }
+     free_tokenbuf(data);
+     move_tokenbuf(&res, data);
+     return VAR_OK;
+     }
+ 
+ static int search_and_replace(tokenbuf* data, tokenbuf* search, tokenbuf* replace, tokenbuf* flags)
+     {
+     const char* p;
+     int case_insensitive = 0;
+     int global = 0;
+     int no_regex = 0;
+     int rc;
+ 
+     if (search->begin == search->end)
+         return VAR_EMPTY_SEARCH_STRING;
+ 
+     printf("Search '%s' in '%s' and replace it with '%s'.\n",
+            search->begin, data->begin, replace->begin);
+ 
+     for (p = flags->begin; p != flags->end; ++p)
+         {
+         switch (tolower(*p))
+             {
+             case 'i':
+                 case_insensitive = 1;
+                 printf("case_insensitive = 1;\n");
+                 break;
+             case 'g':
+                 global = 1;
+                 printf("global = 1;\n");
+                 break;
+             case 't':
+                 no_regex = 1;
+                 printf("no_regex = 1;\n");
+                 break;
+             default:
+                 return VAR_UNKNOWN_REPLACE_FLAG;
+             }
+         }
+ 
+     if (no_regex)
+         {
+         tokenbuf tmp;
+         init_tokenbuf(&tmp);
+ 
+         for (p = data->begin; p != data->end; )
+             {
+             if (case_insensitive)
+                 rc = strncasecmp(p, search->begin, search->end - search->begin);
+             else
+                 rc = strncmp(p, search->begin, search->end - search->begin);
+             if (rc != 0)
+                 {               /* no match, copy character */
+                 if (!append_to_tokenbuf(&tmp, p, 1))
+                     {
+                     free_tokenbuf(&tmp);
+                     return VAR_OUT_OF_MEMORY;
+                     }
+                 ++p;
+                 }
+             else
+                 {
+                 append_to_tokenbuf(&tmp, replace->begin, replace->end - replace->begin);
+                 p += search->end - search->begin;
+                 if (!global)
+                     {
+                     if (!append_to_tokenbuf(&tmp, p, data->end - p))
+                         {
+                         free_tokenbuf(&tmp);
+                         return VAR_OUT_OF_MEMORY;
+                         }
+                     break;
+                     }
+                 }
+             }
+ 
+         free_tokenbuf(data);
+         move_tokenbuf(&tmp, data);
+         }
+     else
+         {
+         tokenbuf tmp;
+         tokenbuf mydata;
+         regex_t preg;
+         regmatch_t pmatch[33];
+         int regexec_flag;
+ 
+         /* Copy the pattern and the data to our own buffer to make
+            sure they're terminated with a null byte. */
+ 
+         if (!assign_to_tokenbuf(&tmp, search->begin, search->end - search->begin))
+             return VAR_OUT_OF_MEMORY;
+         if (!assign_to_tokenbuf(&mydata, data->begin, data->end - data->begin))
+             {
+             free_tokenbuf(&tmp);
+             return VAR_OUT_OF_MEMORY;
+             }
+ 
+         /* Compile the pattern. */
+ 
+         printf("data is.................: '%s'\n", mydata.begin);
+         printf("regex search pattern is.: '%s'\n", tmp.begin);
+         printf("regex replace pattern is: '%s'\n", replace->begin);
+         rc = regcomp(&preg, tmp.begin, REG_EXTENDED | ((case_insensitive) ? REG_ICASE : 0));
+         free_tokenbuf(&tmp);
+         if (rc != 0)
+             {
+             free_tokenbuf(&mydata);
+             return VAR_INVALID_REGEX_IN_REPLACE;
+             }
+         printf("Subexpression in pattern: '%d'\n", preg.re_nsub);
+ 
+         /* Match the pattern and create the result string in the tmp
+            buffer. */
+ 
+         for (p = mydata.begin; p != mydata.end; )
+             {
+             if (p == mydata.begin || p[-1] == '\n')
+                 regexec_flag = 0;
+             else
+                 regexec_flag = REG_NOTBOL;
+             if (regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag) == REG_NOMATCH)
+                 {
+                 printf("No match; appending remainder ('%s') to output string.\n", p);
+                 append_to_tokenbuf(&tmp, p, mydata.end - p);
+                 break;
+                 }
+             else
+                 {
+                 if (!append_to_tokenbuf(&tmp, p, pmatch[0].rm_so) ||
+                     !append_to_tokenbuf(&tmp, replace->begin, replace->end - replace->begin))
+                     {
+                     regfree(&preg);
+                     free_tokenbuf(&tmp);
+                     free_tokenbuf(&mydata);
+                     return VAR_OUT_OF_MEMORY;
+                     }
+                 else
+                     p += pmatch[0].rm_eo;
+                 if (!global)
+                     {
+                     append_to_tokenbuf(&tmp, p, mydata.end - p);
+                     break;
+                     }
+                 }
+             }
+ 
+         regfree(&preg);
+         free_tokenbuf(data);
+         move_tokenbuf(&tmp, data);
+         free_tokenbuf(&mydata);
+         }
+ 
+     return VAR_OK;
+     }
+ 
+ static int padding(tokenbuf* data, tokenbuf* widthstr, tokenbuf* fill, char position)
+     {
+     tokenbuf result;
+     size_t width = tokenbuf2int(widthstr);
+     int i;
+ 
+     printf("Padding data '%s' to width '%d' by filling in '%s' to position '%c'.\n",
+            data->begin, width, fill->begin, position);
+ 
+     if (fill->begin == fill->end)
+         return VAR_EMPTY_PADDING_FILL_STRING;
+ 
+     init_tokenbuf(&result);
+ 
+     if (position == 'l')
+         {
+         i = width - (data->end - data->begin);
+         if (i > 0)
+             {
+             printf("Missing %d characters at the end of the data string.\n", i);
+             i = i / (fill->end - fill->begin);
+             printf("That's %d times the padding string.\n", i);
+             while(i > 0)
+                 {
+                 if (!append_to_tokenbuf(data, fill->begin, fill->end - fill->begin))
+                     return VAR_OUT_OF_MEMORY;
+                 --i;
+                 }
+             i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
+             printf("Plus a remainder of %d characters.\n", i);
+             if (!append_to_tokenbuf(data, fill->begin, i))
+                 return VAR_OUT_OF_MEMORY;
+             }
+         }
+     else if (position == 'r')
+         {
+         i = width - (data->end - data->begin);
+         if (i > 0)
+             {
+             printf("Missing %d characters at the beginning of the data string.\n", i);
+             i = i / (fill->end - fill->begin);
+             printf("That's %d times the padding string.\n", i);
+             while(i > 0)
+                 {
+                 if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin))
+                     {
+                     free_tokenbuf(&result);
+                     return VAR_OUT_OF_MEMORY;
+                     }
+                 --i;
+                 }
+             i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
+             printf("Plus a remainder of %d characters.\n", i);
+             if (!append_to_tokenbuf(&result, fill->begin, i))
+                 {
+                 free_tokenbuf(&result);
+                 return VAR_OUT_OF_MEMORY;
+                 }
+             if (!append_to_tokenbuf(&result, data->begin, data->end - data->begin))
+                 {
+                 free_tokenbuf(&result);
+                 return VAR_OUT_OF_MEMORY;
+                 }
+ 
+             free_tokenbuf(data);
+             move_tokenbuf(&result, data);
+             }
+         }
+      else if (position == 'c')
+         {
+         i = (width - (data->end - data->begin)) / 2;
+         if (i > 0)
+             {
+             /* Create the prefix. */
+ 
+             printf("Missing %d characters at the beginning of the data string.\n", i);
+             i = i / (fill->end - fill->begin);
+             printf("That's %d times the padding string.\n", i);
+             while(i > 0)
+                 {
+                 if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin))
+                     {
+                     free_tokenbuf(&result);
+                     return VAR_OUT_OF_MEMORY;
+                     }
+                 --i;
+                 }
+             i = ((width - (data->end - data->begin)) / 2) % (fill->end - fill->begin);
+             printf("Plus a remainder of %d characters.\n", i);
+             if (!append_to_tokenbuf(&result, fill->begin, i))
+                 {
+                 free_tokenbuf(&result);
+                 return VAR_OUT_OF_MEMORY;
+                 }
+ 
+             /* Append the actual data string. */
+ 
+             if (!append_to_tokenbuf(&result, data->begin, data->end - data->begin))
+                 {
+                 free_tokenbuf(&result);
+                 return VAR_OUT_OF_MEMORY;
+                 }
+ 
+             /* Append the suffix. */
+ 
+             i = width - (result.end - result.begin);
+             printf("Missing %d characters at the end of the data string.\n", i);
+             i = i / (fill->end - fill->begin);
+             printf("That's %d times the padding string.\n", i);
+             while(i > 0)
+                 {
+                 if (!append_to_tokenbuf(&result, fill->begin, fill->end - fill->begin))
+                     {
+                     free_tokenbuf(&result);
+                     return VAR_OUT_OF_MEMORY;
+                     }
+                 --i;
+                 }
+             i = width - (result.end - result.begin);
+             printf("Plus a remainder of %d characters.\n", i);
+             if (!append_to_tokenbuf(&result, fill->begin, i))
+                 {
+                 free_tokenbuf(&result);
+                 return VAR_OUT_OF_MEMORY;
+                 }
+ 
+             /* Move string from temporary buffer to data buffer. */
+ 
+             free_tokenbuf(data);
+             move_tokenbuf(&result, data);
+             }
+         }
+ 
+     return VAR_OK;
+     }
+ 
+ static int command(const char* begin, const char* end, const var_config_t* config,
+                    const char nameclass[256], var_cb_t lookup, void* lookup_context,
+                    int force_expand, tokenbuf* data)
+     {
+     const char* p = begin;
+     tokenbuf tmptokbuf;
+     tokenbuf search, replace, flags;
+     tokenbuf number1, number2;
+     int isrange;
+     int rc;
+ 
+     init_tokenbuf(&tmptokbuf);
+     init_tokenbuf(&search);
+     init_tokenbuf(&replace);
+     init_tokenbuf(&flags);
+     init_tokenbuf(&number1);
+     init_tokenbuf(&number2);
+ 
+     if (begin == end)
+         return 0;
+ 
+     switch (tolower(*p))
+         {
+         case 'l':               /* Turn data to lowercase. */
+             if (data->begin)
+                 {
+                 char* ptr;
+                 /* If the buffer does not life in an allocated buffer,
+                    we have to copy it before modifying the contents. */
+ 
+                 if (data->buffer_size == 0)
+                     {
+                     if (!assign_to_tokenbuf(data, data->begin, data->end - data->begin))
+                         {
+                         rc = VAR_OUT_OF_MEMORY;
+                         goto error_return;
+                         }
+                     }
+                 for (ptr = (char*)data->begin; ptr != data->end; ++ptr)
+                     *ptr = tolower(*ptr);
+                 }
+             ++p;
+             break;
+ 
+         case 'u':               /* Turn data to uppercase. */
+             if (data->begin)
+                 {
+                 char* ptr;
+                 if (data->buffer_size == 0)
+                     {
+                     if (!assign_to_tokenbuf(data, data->begin, data->end - data->begin))
+                         {
+                         rc = VAR_OUT_OF_MEMORY;
+                         goto error_return;
+                         }
+                     }
+                 for (ptr = (char*)data->begin; ptr != data->end; ++ptr)
+                     *ptr = toupper(*ptr);
+                 }
+             ++p;
+             break;
+ 
+         case 'o':               /* Cut out substrings. */
+             ++p;
+             rc = number(p, end);
+             if (rc == 0)
+                 {
+                 rc = VAR_MISSING_START_OFFSET;
+                 goto error_return;
+                 }
+             else
+                 {
+                 number1.begin = p;
+                 number1.end   = p + rc;
+                 number1.buffer_size = 0;
+                 p += rc;
+                 }
+ 
+             if (*p == ',')
+                 {
+                 isrange = 0;
+                 ++p;
+                 }
+             else if (*p == '-')
+                 {
+                 isrange = 1;
+                 ++p;
+                 }
+             else
+                 {
+                 rc = VAR_INVALID_OFFSET_DELIMITER;
+                 goto error_return;
+                 }
+ 
+             rc = number(p, end);
+             number2.begin = p;
+             number2.end   = p + rc;
+             number2.buffer_size = 0;
+             p += rc;
+             if (data->begin)
+                 {
+                 rc = cut_out_offset(data, &number1, &number2, isrange);
+                 if (rc < 0)
+                     goto error_return;
+                 }
+             break;
+ 
+         case '#':               /* Substitute length of the string. */
+             if (data->begin)
+                 {
+                 char buf[1024];
+                 sprintf(buf, "%d", data->end - data->begin);
+                 free_tokenbuf(data);
+                 if (!assign_to_tokenbuf(data, buf, strlen(buf)))
+                     {
+                     rc = VAR_OUT_OF_MEMORY;
+                     goto error_return;
+                     }
+                 }
+             ++p;
+             break;
+ 
+         case '-':               /* Substitute parameter if data is empty. */
+             ++p;
+             rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context,
+                                      force_expand, &tmptokbuf);
+             if (rc < 0)
+                 goto error_return;
+             else if (rc == 0)
+                 {
+                 rc = VAR_MISSING_PARAMETER_IN_COMMAND;
+                 goto error_return;
+                 }
+             else
+                 p += rc;
+             if (data->begin != NULL && data->begin == data->end)
+                 {
+                 free_tokenbuf(data);
+                 move_tokenbuf(&tmptokbuf, data);
+                 }
+             break;
+ 
+         case '*':               /* Return "" if data is not empty, parameter otherwise. */
+             ++p;
+             rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context,
+                                      force_expand, &tmptokbuf);
+             if (rc < 0)
+                 goto error_return;
+             else if (rc == 0)
+                 {
+                 rc = VAR_MISSING_PARAMETER_IN_COMMAND;
+                 goto error_return;
+                 }
+             else
+                 p += rc;
+             if (data->begin != NULL)
+                 {
+                 if (data->begin == data->end)
+                     {
+                     free_tokenbuf(data);
+                     move_tokenbuf(&tmptokbuf, data);
+                     }
+                 else
+                     {
+                     free_tokenbuf(data);
+                     data->begin = data->end = "";
+                     data->buffer_size = 0;
+                     }
+                 }
+             break;
+ 
+         case '+':               /* Substitute parameter if data is not empty. */
+             ++p;
+             rc = exptext_or_variable(p, end, config, nameclass, lookup, lookup_context,
+                                      force_expand, &tmptokbuf);
+             if (rc < 0)
+                 goto error_return;
+             else if (rc == 0)
+                 {
+                 rc = VAR_MISSING_PARAMETER_IN_COMMAND;
+                 goto error_return;
+                 }
+             else
+                 p += rc;
+             if (data->begin != NULL)
+                 {
+                 if (data->begin != data->end)
+                     {
+                     free_tokenbuf(data);
+                     move_tokenbuf(&tmptokbuf, data);
+                     }
+                 }
+             break;
+ 
+         case 's':               /* Search and replace. */
+             ++p;
+ 
+             if (*p != '/')
+                 return VAR_MALFORMATTED_REPLACE;
+             else
+                 ++p;
+ 
+             rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context,
+                                       force_expand, &search);
+             if (rc < 0)
+                 goto error_return;
+             else
+                 p += rc;
+ 
+             if (*p != '/')
+                 {
+                 rc = VAR_MALFORMATTED_REPLACE;
+                 goto error_return;
+                 }
+             else
+                 ++p;
+ 
+             rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context,
+                                       force_expand, &replace);
+             if (rc < 0)
+                 goto error_return;
+             else
+                 p += rc;
+ 
+             if (*p != '/')
+                 {
+                 rc = VAR_MALFORMATTED_REPLACE;
+                 goto error_return;
+                 }
+             else
+                 ++p;
+ 
+             rc = exptext(p, end, config);
+             if (rc < 0)
+                 goto error_return;
+             else
+                 {
+                 flags.begin = p;
+                 flags.end = p + rc;
+                 flags.buffer_size = 0;
+                 p += rc;
+                 }
+ 
+             if (data->begin)
+                 {
+                 rc = search_and_replace(data, &search, &replace, &flags);
+                 if (rc < 0)
+                     goto error_return;
+                 }
+             break;
+ 
+         case 'y':               /* Transpose characters from class A to class B. */
+             ++p;
+ 
+             if (*p != '/')
+                 return VAR_MALFORMATTED_TRANSPOSE;
+             else
+                 ++p;
+ 
+             rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context,
+                                       force_expand, &search);
+             if (rc < 0)
+                 goto error_return;
+             else
+                 p += rc;
+ 
+             if (*p != '/')
+                 {
+                 rc = VAR_MALFORMATTED_TRANSPOSE;
+                 goto error_return;
+                 }
+             else
+                 ++p;
+ 
+             rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context,
+                                       force_expand, &replace);
+             if (rc < 0)
+                 goto error_return;
+             else
+                 p += rc;
+ 
+             if (*p != '/')
+                 {
+                 rc = VAR_MALFORMATTED_TRANSPOSE;
+                 goto error_return;
+                 }
+             else
+                 ++p;
+ 
+             if (data->begin)
+                 {
+                 rc = transpose(data, &search, &replace);
+                 if (rc < 0)
+                     goto error_return;
+                 }
+             break;
+ 
+ 
+         case 'p':               /* Padding. */
+             ++p;
+ 
+             if (*p != '/')
+                 return VAR_MALFORMATTED_PADDING;
+             else
+                 ++p;
+ 
+             rc = number(p, end);
+             if (rc == 0)
+                 {
+                 rc = VAR_MISSING_PADDING_WIDTH;
+                 goto error_return;
+                 }
+             else
+                 {
+                 number1.begin = p;
+                 number1.end   = p + rc;
+                 number1.buffer_size = 0;
+                 p += rc;
+                 }
+ 
+             if (*p != '/')
+                 {
+                 rc = VAR_MALFORMATTED_PADDING;
+                 goto error_return;
+                 }
+             else
+                 ++p;
+ 
+             rc = substext_or_variable(p, end, config, nameclass, lookup, lookup_context,
+                                       force_expand, &replace);
+             if (rc < 0)
+                 goto error_return;
+             else
+                 p += rc;
+ 
+             if (*p != '/')
+                 {
+                 rc = VAR_MALFORMATTED_PADDING;
+                 goto error_return;
+                 }
+             else
+                 ++p;
+ 
+             if (*p != 'l' && *p != 'c' && *p != 'r')
+                 {
+                 rc = VAR_MALFORMATTED_PADDING;
+                 goto error_return;
+                 }
+             else
+                 ++p;
+ 
+             if (data->begin)
+                 {
+                 rc = padding(data, &number1, &replace, p[-1]);
+                 if (rc < 0)
+                     goto error_return;
+                 }
+             break;
+ 
+         default:
+             return VAR_UNKNOWN_COMMAND_CHAR;
+         }
+ 
+     /* Exit gracefully. */
+ 
+     free_tokenbuf(&tmptokbuf);
+     free_tokenbuf(&search);
+     free_tokenbuf(&replace);
+     free_tokenbuf(&flags);
+     free_tokenbuf(&number1);
+     free_tokenbuf(&number2);
+     return p - begin;
+ 
+   error_return:
+     free_tokenbuf(data);
+     free_tokenbuf(&tmptokbuf);
+     free_tokenbuf(&search);
+     free_tokenbuf(&replace);
+     free_tokenbuf(&flags);
+     free_tokenbuf(&number1);
+     free_tokenbuf(&number2);
+     return rc;
+     }
+ 
+ static var_rc_t input(const char* begin, const char* end, const var_config_t* config,
+                       const char nameclass[256], var_cb_t lookup, void* lookup_context,
+                       int force_expand, tokenbuf* output)
+     {
+     int rc;
+     tokenbuf result;
+ 
+     init_tokenbuf(&result);
+ 
+     do
+         {
+         rc = text(begin, end, config->varinit, config->escape);
+         if (rc > 0)
+             {
+             if (!append_to_tokenbuf(output, begin, rc))
+                 {
+                 rc = VAR_OUT_OF_MEMORY;
+                 goto error_return;
+                 }
+             begin += rc;
+             }
+         else if (rc < 0)
+             goto error_return;
+ 
+         rc = variable(begin, end, config, nameclass, lookup, lookup_context, force_expand, &result);
+         if (rc > 0)
+             {
+             if (!append_to_tokenbuf(output, result.begin, result.end - result.begin))
+                 {
+                 rc = VAR_OUT_OF_MEMORY;
+                 goto error_return;
+                 }
+             else
+                 begin += rc;
+             }
+         else if (rc < 0)
+             goto error_return;
+         }
+     while (rc > 0);
+ 
+     if (begin != end)
+         {
+         rc = VAR_INPUT_ISNT_TEXT_NOR_VARIABLE;
+         goto error_return;
+         }
+ 
+     return VAR_OK;
+ 
+   error_return:
+     free_tokenbuf(&result);
+     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)
+     {
+     char nameclass[256];
+     var_rc_t rc;
+     tokenbuf output;
+ 
+     /* Assert everything is as we expect it. */
+ 
+     assert(input_buf != NULL);
+     assert(result != NULL);
+     assert(result_len != NULL);
+     assert(lookup != NULL);
+ 
+     /* Expand the class description for valid variable names. */
+ 
+     if (config == NULL)
+         config = &var_config_default;
+     rc = expand_character_class(config->namechars, nameclass);
+     if (rc != VAR_OK)
+         return 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] ||
+         nameclass[(int)config->escape])
+         return VAR_INVALID_CONFIGURATION;
+ 
+     /* Call the parser. */
+ 
+     output.begin = output.end = NULL;
+     output.buffer_size = 0;
+     rc = input(input_buf, input_buf + input_len, config, nameclass,
+                lookup, lookup_context, force_expand, &output);
+     if (rc != VAR_OK)
+         {
+         free_tokenbuf(&output);
+         return rc;
+         }
+     *result     = (char*)output.begin;
+     *result_len = output.end - output.begin;
+ 
+     return VAR_OK;
+     }


ossp-pkg/var/var.h -> 1.1

*** /dev/null    Fri Apr 19 03:04:04 2024
--- -    Fri Apr 19 03:05:30 2024
***************
*** 0 ****
--- 1,127 ----
+ #ifndef LIB_VARIABLE_EXPAND_H
+ #define LIB_VARIABLE_EXPAND_H
+ 
+ #include <stdlib.h>
+ 
+ /* Error codes returned by the varexp library. */
+ 
+ typedef enum
+     {
+     VAR_EMPTY_PADDING_FILL_STRING      = -32,
+     VAR_MISSING_PADDING_WIDTH          = -31,
+     VAR_MALFORMATTED_PADDING           = -30,
+     VAR_INCORRECT_TRANSPOSE_CLASS_SPEC = -29,
+     VAR_EMPTY_TRANSPOSE_CLASS          = -28,
+     VAR_TRANSPOSE_CLASSES_MISMATCH     = -27,
+     VAR_MALFORMATTED_TRANSPOSE         = -26,
+     VAR_OFFSET_LOGIC_ERROR             = -25,
+     VAR_OFFSET_OUT_OF_BOUNDS           = -24,
+     VAR_RANGE_OUT_OF_BOUNDS            = -23,
+     VAR_INVALID_OFFSET_DELIMITER       = -22,
+     VAR_MISSING_START_OFFSET           = -21,
+     VAR_EMPTY_SEARCH_STRING            = -20,
+     VAR_MISSING_PARAMETER_IN_COMMAND   = -19,
+     VAR_INVALID_REGEX_IN_REPLACE       = -18,
+     VAR_UNKNOWN_REPLACE_FLAG           = -17,
+     VAR_MALFORMATTED_REPLACE           = -16,
+     VAR_UNKNOWN_COMMAND_CHAR           = -14,
+     VAR_INPUT_ISNT_TEXT_NOR_VARIABLE   = -13,
+     VAR_UNDEFINED_VARIABLE             = -12,
+     VAR_INCOMPLETE_VARIABLE_SPEC       = -11,
+     VAR_OUT_OF_MEMORY                  = -10,
+     VAR_INVALID_CONFIGURATION          = -9,
+     VAR_INCORRECT_CLASS_SPEC           = -8,
+     VAR_INCOMPLETE_GROUPED_HEX         = -7,
+     VAR_INCOMPLETE_OCTAL               = -6,
+     VAR_INVALID_OCTAL                  = -5,
+     VAR_OCTAL_TOO_LARGE                = -4,
+     VAR_INVALID_HEX                    = -3,
+     VAR_INCOMPLETE_HEX                 = -2,
+     VAR_INCOMPLETE_NAMED_CHARACTER     = -1,
+     VAR_INCOMPLETE_QUOTED_PAIR         = -1,
+     VAR_OK                             = 0
+     }
+ var_rc_t;
+ 
+ /*
+    Expand the following named characters to their binary
+    representation:
+ 
+        \t          tab
+        \n          newline
+        \r          return
+        \033        octal char
+        \x1B        hex char
+        \x{263a}    wide hex char
+ 
+   Any other character quoted by a backslash is copied verbatim.
+ */
+ 
+ var_rc_t expand_named_characters(const char* src, size_t len, char* dst);
+ 
+ /*
+    The callback will be called by variable_expand(), providing the
+    following parameterns:
+ 
+         context         - passed through from variable_expand()'s
+                           parameters
+         varname         - pointer to the name of the variable to
+                           expand
+         name_len        - length of the string starting at varname
+         data            - location, where the callback should store
+                           the pointer to the contents of the looked-up
+                           variable
+         data_len        - location, where the callback should store
+                           the length of the data
+         malloced_buffer - location, where the callback should store
+                           either TRUE or FALSE, telling the framework
+                           whether the buffer must be free(3)ed.
+ 
+    The return code is interpreted as follows:
+        >0               - OK
+         0               - undefined variable
+        <0 - error
+ */
+ 
+ typedef int (*var_cb_t)(void* context,
+                         const char* varname, size_t name_len,
+                         const char** data, size_t* data_len, size_t* buffer_size);
+ 
+ /*
+    This structure configures the parser's specials. I think, the fields
+    are pretty self-explanatory. The only one worth mentioning is
+    force_expand, which is a boolean. If set to TRUE, variable_expand()
+    will fail with an error if the lookup callback returns "undefined
+    variable". If set to FALSE, variable_expand() will copy the
+    expression that failed verbatimly to the output so that another pass
+    may expand it.
+ 
+    The comments after each field show the default configuration.
+ */
+ 
+ typedef struct
+     {
+     char  varinit;               /* '$' */
+     char  startdelim;            /* '{' */
+     char  enddelim;              /* '}' */
+     char  escape;                /* '\' */
+     char* namechars;             /* 'a-zA-Z0-9_' */
+     }
+ var_config_t;
+ extern const var_config_t var_config_default;
+ 
+ /*
+    variable_expand() will parse the contents of input for variable
+    expressions and expand them using the provided lookup callback. The
+    pointer to the resulting buffer is stored in result, its length in
+    result_len. The buffer is always terminated by a '\0' byte, which is
+    not included in the result_len count. The buffer must be free(3)ed
+    by the caller.
+ */
+ 
+ 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);
+ 
+ #endif /* !defined(LIB_VARIABLE_EXPAND_H) */


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

*** /dev/null    Fri Apr 19 03:04:04 2024
--- -    Fri Apr 19 03:05:30 2024
***************
*** 0 ****
--- 1,124 ----
+ #include <stdio.h>
+ #include <string.h>
+ #include <errno.h>
+ #include "var.h"
+ 
+ int env_lookup(void* context,
+           const char* varname, size_t name_len,
+           const char** data, size_t* data_len, size_t* buffer_size)
+     {
+     char tmp[256];
+ 
+     if (name_len > sizeof(tmp)-1)
+         {
+         printf("Callback can't expand variable names longer than %d characters.\n", sizeof(tmp-1));
+         exit(1);
+         }
+     memcpy(tmp, varname, name_len);
+     tmp[name_len] = '\0';
+     *data = getenv(tmp);
+     if (*data == NULL)
+         return 0;
+     *data_len = strlen(*data);
+     *buffer_size = 0;
+     return 1;
+     }
+ 
+ struct test_case
+     {
+     const char* input;
+     const char* expected;
+     };
+ 
+ int main(int argc, char** argv)
+     {
+     const struct test_case tests[] =
+         {
+         { "$HOME",                      "/home/regression-tests"          },
+         { "${FOO}",                     "os"                              },
+         { "${BAR}",                     "type"                            },
+         { "${${FOO:u}${BAR:u}:l:u}",    "REGRESSION-OS"                   },
+         { "${UNDEFINED}",               "${UNDEFINED}"                    },
+         { "${OSTYPE:#}",                "13"                              },
+         { "${EMPTY:-test${FOO}test}",   "testostest"                      },
+         { "${EMPTY:-test${FOO:u}test}", "testOStest"                      },
+         { "${TERM:-test${FOO}test}",    "regression-term"                 },
+         { "${EMPTY:+FOO}",              ""                                },
+         { "${HOME:+test${FOO}test}",    "testostest"                      },
+         { "${HOME:+${OS${BAR:u}}}",     "regression-os"                   },
+         { "${HOME:+OS${UNDEFINED:u}}",  "OS${UNDEFINED:u}"                },
+         { "${UNDEFINED:+OS${BAR:u}}",   "${UNDEFINED:+OS${BAR:u}}"        },
+         { "${HOME:*heinz}",             ""                                },
+         { "${EMPTY:*claus}",            "claus"                           },
+         { "${TERM}",                    "regression-term"                 },
+         { "${HOME:s/reg/bla/}",         "/home/blaression-tests"          },
+         { "${HOME:s/e/bla/}",           "/hombla/regression-tests"        },
+         { "${HOME:s/e/bla/g}",          "/hombla/rblagrblassion-tblasts"  },
+         { "${HOME:s/\\//_/g}",          "_home_regression-tests"          },
+         { "${HOME:s/[eso]/_/g}",        "/h_m_/r_gr___i_n-t__t_"          },
+         { "${HOME:s/[esO]/_/g}",        "/hom_/r_gr___ion-t__t_"          },
+         { "${HOME:s/[esO]/_/gi}",       "/h_m_/r_gr___i_n-t__t_"          },
+         { "${OSTYPE:s/^[re]/_/g}",      "_egression-os"                   },
+         { "${EMPTY:s/^[re]/_/g}",       ""                                },
+         { "${HOME:s/.*/heinz/}",        "heinz"                           },
+         { "${HOME:s/e/bla/t}",          "/hombla/regression-tests"        },
+         { "${HOME:s/E/bla/t}",          "/home/regression-tests"          },
+         { "${HOME:s/E/bla/ti}",         "/hombla/regression-tests"        },
+         { "${HOME:s/E/bla/tig}",        "/hombla/rblagrblassion-tblasts"  },
+         { "${HOME:o1-5}",               "home/"                           },
+         { "${HOME:o1,5}",               "home"                            },
+         { "${HOME:o5,}",                "/regression-tests"               },
+         { "${HOME:o5-}",                "/regression-tests"               },
+         { "${HOME:o7,13}",              "egress"                          },
+         { "${HOME:y/a-z/A-YZ/}",        "/HOME/REGRESSION-TESTS"          },
+         { "${HOME:y/e-g/a-c/}",         "/homa/racrassion-tasts"          },
+         { "${FOO:p/15/../l}",           "os............."                 },
+         { "${FOO:p/15/12345/l}",        "os1234512345123"                 },
+         { "${FOO:p/15/../r}",           ".............os"                 },
+         { "${FOO:p/15/12345/r}",        "1234512345123os"                 },
+         { "${FOO:p/15/../c}",           "......os......."                 },
+         { "${FOO:p/15/12345/c}",        "123451os1234512"                 }
+         };
+     /*
+         { "${HOME:s/g(res)s/x\\\\1x/g}","/homE/rEgrEssion-tEsts"          }
+         { "${HOME:s/\\x65/\\x45/g}",    "/home/regression-tests"          }
+     */
+     char*    tmp;
+     size_t   tmp_len;
+     var_rc_t rc;
+     size_t   i;
+ 
+     if (setenv("HOME", "/home/regression-tests", 1) != 0 ||
+         setenv("OSTYPE", "regression-os", 1) != 0 ||
+         setenv("TERM", "regression-term", 1) != 0 ||
+         setenv("FOO", "os", 1) != 0 ||
+         setenv("BAR", "type", 1) != 0 ||
+         setenv("EMPTY", "", 1) != 0)
+         {
+         printf("Failed to set the environment: %s.\n", strerror(errno));
+         return 1;
+         }
+     unsetenv("UNDEFINED");
+ 
+     for (i = 0; i < sizeof(tests) / sizeof(struct test_case); ++i)
+         {
+         rc = var_expand(tests[i].input, strlen(tests[i].input),
+                         &tmp, &tmp_len,
+                         &env_lookup, NULL,
+                         NULL, 0);
+         if (rc != VAR_OK)
+             {
+             printf("Test case #%d: var_expand() failed with return code %d.\n", i, rc);
+             return 1;
+             }
+         if (tmp_len != strlen(tests[i].expected) || 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);
+             return 1;
+             }
+         printf("Test case #%02d: '%s' --> '%s'.\n", i, tests[i].input, tmp);
+         free(tmp);
+         }
+ 
+     return 0;
+     }


ossp-pkg/var/varexp.h 1.1 -> 1.2



ossp-pkg/var/variable.c 1.1 -> 1.2


CVSTrac 2.0.1