OSSP CVS Repository

ossp - Difference in ossp-pkg/var/var.c versions 1.73 and 1.74
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [History

ossp-pkg/var/var.c 1.73 -> 1.74

--- var.c        2002/03/01 22:02:40     1.73
+++ var.c        2002/03/02 12:29:05     1.74
@@ -140,7 +140,9 @@
     size_t buffer_size;
 } tokenbuf_t;
 
-static void tokenbuf_init(tokenbuf_t *buf)
+static void 
+tokenbuf_init(
+    tokenbuf_t *buf)
 {
     buf->begin = NULL;
     buf->end = NULL;
@@ -148,7 +150,9 @@
     return;
 }
 
-static void tokenbuf_set(tokenbuf_t *buf, const char *begin, const char *end, size_t buffer_size)
+static void 
+tokenbuf_set(
+    tokenbuf_t *buf, const char *begin, const char *end, size_t buffer_size)
 {
     buf->begin = begin;
     buf->end = end;
@@ -156,7 +160,9 @@
     return;
 }
 
-static void tokenbuf_copy(tokenbuf_t *src, tokenbuf_t *dst)
+static void 
+tokenbuf_copy(
+    tokenbuf_t *src, tokenbuf_t *dst)
 {
     dst->begin = src->begin;
     dst->end = src->end;
@@ -164,7 +170,9 @@
     return;
 }
 
-static void tokenbuf_move(tokenbuf_t *src, tokenbuf_t *dst)
+static void 
+tokenbuf_move(
+    tokenbuf_t *src, tokenbuf_t *dst)
 {
     dst->begin = src->begin;
     dst->end = src->end;
@@ -173,7 +181,9 @@
     return;
 }
 
-static int tokenbuf_assign(tokenbuf_t *buf, const char *data, size_t len)
+static int 
+tokenbuf_assign(
+    tokenbuf_t *buf, const char *data, size_t len)
 {
     char *p;
 
@@ -187,7 +197,9 @@
     return 1;
 }
 
-static int tokenbuf_append(tokenbuf_t *output, const char *data, size_t len)
+static int 
+tokenbuf_append(
+    tokenbuf_t *output, const char *data, size_t len)
 {
     char *new_buffer;
     size_t new_size;
@@ -241,12 +253,16 @@
     return 1;
 }
 
-static int tokenbuf_merge(tokenbuf_t *output, tokenbuf_t *input)
+static int 
+tokenbuf_merge(
+    tokenbuf_t *output, tokenbuf_t *input)
 {
     return tokenbuf_append(output, input->begin, input->end - input->begin);
 }
 
-static void tokenbuf_free(tokenbuf_t *buf)
+static void 
+tokenbuf_free(
+    tokenbuf_t *buf)
 {
     if (buf->begin != NULL && buf->buffer_size > 0)
         free((char *)buf->begin);
@@ -255,7 +271,9 @@
     return;
 }
 
-static size_t tokenbuf_toint(tokenbuf_t *number)
+static size_t 
+tokenbuf_toint(
+    tokenbuf_t *number)
 {
     const char *p;
     size_t num;
@@ -420,48 +438,7 @@
 
 /* forward declarations */
 static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result);
-static int parse_command (var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *data);
-static int parse_num_exp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
-
-/* parse plain text */
-static int 
-parse_text(
-    var_t *var, var_parse_t *ctx,
-    const char *begin, const char *end)
-{
-    const char *p;
-
-    /* parse until delim_init (variable construct) 
-       or index_open (loop construct) is found */
-    for (p = begin; p != end; p++) {
-        if (*p == var->syntax.escape) {
-            p++; /* skip next character */
-            if (p == end)
-                return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
-        }
-        else if (*p == var->syntax.delim_init)
-            break;
-        else if (   var->syntax.index_open != NUL
-                 && (   *p == var->syntax.index_open 
-                     || *p == var->syntax.index_close))
-            break;
-    }
-    return (p - begin);
-}
-
-/* parse variable name */
-static int 
-parse_varname(
-    var_t *var, var_parse_t *ctx,
-    const char *begin, const char *end)
-{
-    const char *p;
-
-    /* parse as long as name class characters are found */
-    for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
-        ;
-    return (p - begin);
-}
+static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
 
 /* parse number */
 static int 
@@ -518,451 +495,6 @@
     return (p - begin);
 }
 
-/* convert a string into a decimal number */
-static int 
-convert_num_exp_read_int(const char **begin, const char *end)
-{
-    int num = 0;
-
-    do {
-        num *= 10;
-        num += **begin - '0';
-        ++(*begin);
-    } while (isdigit(**begin) && *begin != end);
-    return num;
-}
-
-/* parse numerical expression operand */
-static int 
-parse_num_exp_operand(
-    var_t *var, var_parse_t *ctx,
-    const char *begin, const char *end, 
-    int *result, 
-    int *failed)
-{
-    const char *p;
-    tokenbuf_t tmp;
-    int rc;
-    var_parse_t myctx;
-
-    p = begin;
-    tokenbuf_init(&tmp);
-
-    if (begin == end)
-        return VAR_ERR_INCOMPLETE_INDEX_SPEC;
-
-    if (*p == '(') {
-        rc = parse_num_exp(var, ctx, ++p, end, result, failed);
-        if (rc < 0)
-            return rc;
-        p += rc;
-        if (p == end)
-            return VAR_ERR_INCOMPLETE_INDEX_SPEC;
-        if (*p != ')')
-            return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
-        ++p;
-    }
-    else if (*p == var->syntax.delim_init) {
-        ctx = var_parse_push(ctx, &myctx);
-        ctx->force_expand = 1;
-        rc = parse_variable(var, ctx, p, end, &tmp);
-        ctx = var_parse_pop(ctx);
-        if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
-            *failed = 1;
-            ctx = var_parse_push(ctx, &myctx);
-            ctx->force_expand = 0;
-            rc = parse_variable(var, ctx, p, end, &tmp);
-            ctx = var_parse_pop(ctx);
-            if (rc < 0)
-                return rc;
-            p += rc;
-            *result = 0;
-        }
-        else {
-            if (rc < 0)
-                return rc;
-            p += rc;
-            rc = parse_num_exp(var, ctx, tmp.begin, tmp.end, result, failed);
-            tokenbuf_free(&tmp);
-            if (rc < 0)
-                return rc;
-        }
-    }
-    else if (var->syntax.index_mark && *p == var->syntax.index_mark) {
-        p++;
-        *result = ctx->index_this;
-        if (ctx->rel_lookup_flag)
-            ctx->rel_lookup_cnt++;
-    }
-    else if (isdigit(*p)) {
-        *result = convert_num_exp_read_int(&p, end);
-    }
-    else if (*p == '+') {
-        if (end - p > 1 && isdigit(p[1])) {
-            p++;
-            *result = convert_num_exp_read_int(&p, end);
-        }
-        else
-            return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
-    }
-    else if (*p == '-') {
-        if (end - p > 1 && isdigit(p[1])) {
-            p++;
-            *result = convert_num_exp_read_int(&p, end);
-            *result = 0 - *result;
-        }
-        else
-            return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
-    }
-    else
-        return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
-
-    return (p - begin);
-}
-
-/* parse numerical expression */
-static int 
-parse_num_exp(
-    var_t *var, var_parse_t *ctx,
-    const char *begin, const char *end, 
-    int *result, int *failed)
-{
-    const char *p;
-    char operator;
-    int right;
-    int rc;
-
-    p = begin;
-    if (p == end)
-        return VAR_ERR_INCOMPLETE_INDEX_SPEC;
-
-    rc = parse_num_exp_operand(var, ctx, p, end, result, failed);
-    if (rc < 0)
-        return rc;
-    p += rc;
-
-    while (p != end) {
-        if (*p == '+' || *p == '-') {
-            operator = *p++;
-            rc = parse_num_exp(var, ctx, p, end, &right, failed);
-            if (rc < 0)
-                return rc;
-            p += rc;
-            if (operator == '+')
-                *result = *result + right;
-            else
-                *result = *result - right;
-        }
-        else if (*p == '*' || *p == '/' || *p == '%') {
-            operator = *p++;
-            rc = parse_num_exp_operand(var, ctx, p, end, &right, failed);
-            if (rc < 0)
-                return rc;
-            p += rc;
-            if (operator == '*') {
-                *result = *result * right;
-            }
-            else if (operator == '/') {
-                if (right == 0) {
-                    if (*failed)
-                        *result = 0;
-                    else
-                        return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
-                }
-                else
-                    *result = *result / right;
-            }
-            else if (operator == '%') {
-                if (right == 0) {
-                    if (*failed)
-                        *result = 0;
-                    else
-                        return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
-                }
-                else
-                    *result = *result % right;
-            }
-        }
-        else
-            break;
-    }
-    return p - begin;
-}
-
-/* lookup a variable value by callin the callback function */
-static int 
-lookup_value(
-    var_t *var, var_parse_t *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 buf[1];
-    int rc;
-
-    /* pass through to original callback */
-    rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
-                              var_ptr, var_len, var_idx, 
-                              val_ptr, val_len, val_size);
-
-    /* convert undefined variable into empty variable if relative
-       lookups are counted. This is the case inside an active loop
-       construct if no limits are given. There the parse_input()
-       has to proceed until all variables have undefined values. 
-       This trick here allows it to determine this case. */
-    if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
-        ctx->rel_lookup_cnt--;
-        buf[0] = NUL;
-        *val_ptr  = buf;
-        *val_len  = 0;
-        *val_size = 0;
-        return VAR_OK;
-    }
-
-    return rc;
-}
-
-static int 
-parse_expression(
-    var_t *var, var_parse_t *ctx,
-    const char *begin, const char *end,
-    tokenbuf_t *result)
-{
-    const char *p = begin;
-    const char *data;
-    size_t len, buffer_size;
-    int failed = 0;
-    int rc;
-    int idx = 0;
-    tokenbuf_t name;
-    tokenbuf_t tmp;
-
-    /* Clear the tokenbufs to make sure we have a defined state. */
-
-    tokenbuf_init(&name);
-    tokenbuf_init(&tmp);
-    tokenbuf_init(result);
-
-    /* Expect STARTDELIM. */
-
-    if (p == end || *p != var->syntax.delim_open)
-        return 0;
-
-    if (++p == end)
-        return VAR_ERR_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 = parse_varname(var, ctx, p, end);
-        if (rc < 0)
-            goto error_return;
-        if (rc > 0) {
-            if (!tokenbuf_append(&name, p, rc)) {
-                rc = VAR_ERR_OUT_OF_MEMORY;
-                goto error_return;
-            }
-            p += rc;
-        }
-
-        rc = parse_variable(var, ctx, p, end, &tmp);
-        if (rc < 0)
-            goto error_return;
-        if (rc > 0) {
-            if (!tokenbuf_append(&name, tmp.begin, tmp.end - tmp.begin)) {
-                rc = VAR_ERR_OUT_OF_MEMORY;
-                goto error_return;
-            }
-            p += rc;
-        }
-    } while (rc > 0);
-
-    /* We must have the complete variable name now, so make sure we
-       do. */
-
-    if (name.begin == name.end) {
-        if (ctx->force_expand) {
-            rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
-            goto error_return;
-        }
-        else {
-            /* If no force_expand is requested, we have to back-off.
-               We're not sure whether our approach here is 100% correct,
-               because it _could_ have side-effects according to Peter
-               Simons, but as far as we know and tried it, it is
-               correct. But be warned -- RSE */
-            result->begin = begin - 1;
-            result->end = p;
-            result->buffer_size = 0;
-            goto goahead;
-        }
-    }
-
-    /* If the next token is START-INDEX, read the index specification. */
-
-    if (var->syntax.index_open && *p == var->syntax.index_open) {
-        rc = parse_num_exp(var, ctx, ++p, end, &idx, &failed);
-        if (rc < 0)
-            goto error_return;
-        if (rc == 0) {
-            rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
-            goto error_return;
-        }
-        p += rc;
-
-        if (p == end) {
-            rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
-            goto error_return;
-        }
-        if (*p != var->syntax.index_close) {
-            rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
-            goto error_return;
-        }
-        p++;
-    }
-
-    /* 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 != var->syntax.delim_close && *p != ':')) {
-        rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
-        goto error_return;
-    }
-    p++;
-
-    /* Use the lookup callback to get the variable's contents. */
-
-    if (failed) {
-        result->begin = begin - 1;
-        result->end = p;
-        result->buffer_size = 0;
-    }
-    else {
-        rc = lookup_value(var, ctx, name.begin, name.end - name.begin, idx,
-                          &data, &len, &buffer_size);
-        if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
-            /* The variable is undefined. What we'll do now depends on the
-               force_expand flag. */
-            if (ctx->force_expand)
-                goto error_return;
-
-            /* 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 if (rc < 0 /* != VAR_OK */) {
-            goto error_return;
-        }
-        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;
-        }
-    }
-
-    goahead:
-    if (p[-1] == ':') {
-        /* Parse and execute commands. */
-
-        tokenbuf_free(&tmp);
-        p--;
-        while (p != end && *p == ':') {
-            p++;
-            if (!failed)
-                rc = parse_command(var, ctx, p, end, result);
-            else
-                rc = parse_command(var, ctx, p, end, &tmp);
-            if (rc < 0)
-                goto error_return;
-            p += rc;
-            if (failed)
-                result->end += rc;
-        }
-
-        if (p == end || *p != var->syntax.delim_close) {
-            rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
-            goto error_return;
-        }
-        p++;
-        if (failed)
-            result->end++;
-    }
-
-    /* Exit gracefully. */
-
-    tokenbuf_free(&name);
-    tokenbuf_free(&tmp);
-    return p - begin;
-
-    /* Exit in case of an error. */
-
-  error_return:
-    tokenbuf_free(&name);
-    tokenbuf_free(&tmp);
-    tokenbuf_free(result);
-    return rc;
-}
-
-static int 
-parse_variable(
-    var_t *var, 
-    var_parse_t *ctx,
-    const char *begin, const char *end,
-    tokenbuf_t *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. */
-
-    tokenbuf_init(result);
-
-    /* Expect VARINIT. */
-
-    if (p == end || *p != var->syntax.delim_init)
-        return 0;
-
-    if (++p == end)
-        return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
-
-    /* Try to read the variable name. If that fails, we're parsing a
-       complex expression. */
-
-    rc = parse_varname(var, ctx, p, end);
-    if (rc < 0)
-        return rc;
-    if (rc > 0) {
-        rc2 = lookup_value(var, ctx, p, rc, 0, &data, &len, &buffer_size);
-        if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
-            result->begin = begin;
-            result->end = begin + 1 + rc;
-            result->buffer_size = 0;
-            return 1 + rc;
-        }
-        if (rc2 < 0 /* != VAR_OK */)
-            return rc2;
-        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 = parse_expression(var, ctx, p, end, result);
-    if (rc > 0)
-        rc++;
-    return rc;
-}
-
 static int 
 parse_exptext_or_variable(
     var_t *var, var_parse_t *ctx,
@@ -1161,14 +693,12 @@
     num2 = tokenbuf_toint(number2);
 
     /* Determine begin of result string. */
-
     if ((data->end - data->begin) < num1)
         return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
     else
         p = data->begin + num1;
 
     /* If num2 is zero, we copy the rest from there. */
-
     if (num2 == 0) {
         if (!tokenbuf_assign(&res, p, data->end - p))
             return VAR_ERR_OUT_OF_MEMORY;
@@ -1323,7 +853,6 @@
 
         /* Copy the pattern and the data to our own buffer to make
            sure they're terminated with a null byte. */
-
         if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
             return VAR_ERR_OUT_OF_MEMORY;
         if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
@@ -1332,7 +861,6 @@
         }
 
         /* Compile the pattern. */
-
         rc = regcomp(&preg, tmp.begin, REG_NEWLINE | REG_EXTENDED|((case_insensitive)?REG_ICASE:0));
         tokenbuf_free(&tmp);
         if (rc != 0) {
@@ -1340,9 +868,7 @@
             return VAR_ERR_INVALID_REGEX_IN_REPLACE;
         }
 
-        /* Match the pattern and create the result string in the tmp
-           buffer. */
-
+        /* 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;
@@ -1405,6 +931,7 @@
     return VAR_OK;
 }
 
+/* operation: padding */
 static int 
 op_padding(
     tokenbuf_t *data, 
@@ -1413,9 +940,10 @@
     char position)
 {
     tokenbuf_t result;
-    size_t width = tokenbuf_toint(widthstr);
+    size_t width;
     int i;
 
+    width = tokenbuf_toint(widthstr);
     if (fill->begin == fill->end)
         return VAR_ERR_EMPTY_PADDING_FILL_STRING;
 
@@ -1466,7 +994,6 @@
         i = (width - (data->end - data->begin)) / 2;
         if (i > 0) {
             /* Create the prefix. */
-
             i = i / (fill->end - fill->begin);
             while (i > 0) {
                 if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) {
@@ -1483,14 +1010,12 @@
             }
 
             /* Append the actual data string. */
-
             if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) {
                 tokenbuf_free(&result);
                 return VAR_ERR_OUT_OF_MEMORY;
             }
 
             /* Append the suffix. */
-
             i = width - (result.end - result.begin);
             i = i / (fill->end - fill->begin);
             while (i > 0) {
@@ -1508,7 +1033,6 @@
             }
 
             /* Move string from temporary buffer to data buffer. */
-
             tokenbuf_free(data);
             tokenbuf_move(&result, data);
         }
@@ -1517,101 +1041,105 @@
     return VAR_OK;
 }
 
+/* XXX */
+
+/* parse an operation (":x...") */
 static int 
-parse_command(
+parse_operation(
     var_t *var, var_parse_t *ctx,
     const char *begin, const char *end,
     tokenbuf_t *data)
 {
-    const char *p = begin;
+    const char *p;
     tokenbuf_t tmptokbuf;
     tokenbuf_t search, replace, flags;
     tokenbuf_t number1, number2;
     int isrange;
     int rc;
+    char *ptr;
 
+    /* initialization */
     tokenbuf_init(&tmptokbuf);
     tokenbuf_init(&search);
     tokenbuf_init(&replace);
     tokenbuf_init(&flags);
     tokenbuf_init(&number1);
     tokenbuf_init(&number2);
-
-    if (begin == end)
+    p = begin;
+    if (p == end)
         return 0;
 
+    /* dispatch through the first operation character */
     switch (tolower(*p)) {
-        case 'l':                   /* Turn data to lowercase. */
-            if (data->begin) {
-                char *ptr;
-                /* If the buffer does not live in an allocated buffer,
+        case 'l': {
+            /* turn value to lowercase. */
+            if (data->begin != NULL) {
+                /* if the buffer does not live in an allocated buffer,
                    we have to copy it before modifying the contents. */
-
                 if (data->buffer_size == 0) {
                     if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
                         rc = VAR_ERR_OUT_OF_MEMORY;
                         goto error_return;
                     }
                 }
-                for (ptr = (char *)data->begin; ptr != data->end; ++ptr)
-                    *ptr = tolower(*ptr);
+                /* convert value */
+                for (ptr = (char *)data->begin; ptr != data->end; ptr++)
+                    *ptr = (char)tolower((int)(*ptr));
             }
             p++;
             break;
-
-        case 'u':                   /* Turn data to uppercase. */
-            if (data->begin) {
-                char *ptr;
+        }
+        case 'u': {
+            /* turn value to uppercase. */
+            if (data->begin != NULL) {
+                /* if the buffer does not live in an allocated buffer,
+                   we have to copy it before modifying the contents. */
                 if (data->buffer_size == 0) {
-                    if (!tokenbuf_assign
-                        (data, data->begin, data->end - data->begin)) {
+                    if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
                         rc = VAR_ERR_OUT_OF_MEMORY;
                         goto error_return;
                     }
                 }
-                for (ptr = (char *) data->begin; ptr != data->end; ++ptr)
-                    *ptr = toupper(*ptr);
+                /* convert value */
+                for (ptr = (char *)data->begin; ptr != data->end; ptr++)
+                    *ptr = (char)toupper((int)(*ptr));
             }
-            ++p;
+            p++;
             break;
-
-        case 'o':                   /* Cut out substrings. */
-            ++p;
+        }
+        case 'o': {
+            /* cut out substring of value. */
+            p++;
             rc = parse_number(var, ctx, p, end);
             if (rc == 0) {
                 rc = VAR_ERR_MISSING_START_OFFSET;
                 goto error_return;
             }
-            number1.begin = p;
-            number1.end = p + rc;
-            number1.buffer_size = 0;
+            tokenbuf_set(&number1, p, p + rc, 0);
             p += rc;
-
             if (*p == ',') {
                 isrange = 0;
-                ++p;
+                p++;
             } else if (*p == '-') {
                 isrange = 1;
-                ++p;
+                p++;
             } else {
                 rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
                 goto error_return;
             }
-
             rc = parse_number(var, ctx, p, end);
-            number2.begin = p;
-            number2.end = p + rc;
-            number2.buffer_size = 0;
+            tokenbuf_set(&number2, p, p + rc, 0);
             p += rc;
-            if (data->begin) {
+            if (data->begin != NULL) {
                 rc = op_cut_out_offset(data, &number1, &number2, isrange);
                 if (rc < 0)
                     goto error_return;
             }
             break;
-
-        case '#':                   /* Substitute length of the string. */
-            if (data->begin) {
+        }
+        case '#': {
+            /* determine length of the value */
+            if (data->begin != NULL) {
                 char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
                 sprintf(buf, "%d", (int)(data->end - data->begin));
                 tokenbuf_free(data);
@@ -1620,10 +1148,11 @@
                     goto error_return;
                 }
             }
-            ++p;
+            p++;
             break;
-
-        case '-':                   /* Substitute parameter if data is empty. */
+        }
+        case '-': {
+            /* substitute parameter if data is empty */
             p++;
             rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
             if (rc < 0)
@@ -1638,8 +1167,9 @@
                 tokenbuf_move(&tmptokbuf, data);
             }
             break;
-
-        case '*':                   /* Return "" if data is not empty, parameter otherwise. */
+        }
+        case '*': {
+            /* substitute empty string if data is not empty, parameter otherwise. */
             p++;
             rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
             if (rc < 0)
@@ -1660,8 +1190,9 @@
                 }
             }
             break;
-
-        case '+':                   /* Substitute parameter if data is not empty. */
+        }
+        case '+': {
+            /* substitute parameter if data is not empty. */
             p++;
             rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
             if (rc < 0)
@@ -1676,150 +1207,128 @@
                 tokenbuf_move(&tmptokbuf, data);
             }
             break;
-
-        case 's':                   /* Search and replace. */
+        }
+        case 's': {
+            /* search and replace. */
             p++;
-
             if (*p != '/')
                 return VAR_ERR_MALFORMATTED_REPLACE;
             p++;
-
             rc = parse_substext_or_variable(var, ctx, p, end, &search);
             if (rc < 0)
                 goto error_return;
             p += rc;
-
             if (*p != '/') {
                 rc = VAR_ERR_MALFORMATTED_REPLACE;
                 goto error_return;
             }
             p++;
-
             rc = parse_substext_or_variable(var, ctx, p, end, &replace);
             if (rc < 0)
                 goto error_return;
             p += rc;
-
             if (*p != '/') {
                 rc = VAR_ERR_MALFORMATTED_REPLACE;
                 goto error_return;
             }
             p++;
-
             rc = parse_exptext(var, ctx, p, end);
             if (rc < 0)
                 goto error_return;
-            flags.begin = p;
-            flags.end = p + rc;
-            flags.buffer_size = 0;
+            tokenbuf_set(&flags, p, p + rc, 0);
             p += rc;
-
-            if (data->begin) {
+            if (data->begin != NULL) {
                 rc = op_search_and_replace(data, &search, &replace, &flags);
                 if (rc < 0)
                     goto error_return;
             }
             break;
-
-        case 'y':                   /* Transpose characters from class A to class B. */
+        }
+        case 'y': {
+            /* transpose characters from class A to class B. */
             p++;
-
             if (*p != '/')
                 return VAR_ERR_MALFORMATTED_TRANSPOSE;
             p++;
-
             rc = parse_substext_or_variable(var, ctx, p, end, &search);
             if (rc < 0)
                 goto error_return;
             p += rc;
-
             if (*p != '/') {
                 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
                 goto error_return;
             }
             p++;
-
             rc = parse_substext_or_variable(var, ctx, p, end, &replace);
             if (rc < 0)
                 goto error_return;
             p += rc;
-
             if (*p != '/') {
                 rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
                 goto error_return;
             } else
-                ++p;
-
+                p++;
             if (data->begin) {
                 rc = op_transpose(data, &search, &replace);
                 if (rc < 0)
                     goto error_return;
             }
             break;
-
-
-        case 'p':                   /* Padding. */
+        }
+        case 'p': {
+            /* padding. */
             p++;
-
             if (*p != '/')
                 return VAR_ERR_MALFORMATTED_PADDING;
             p++;
-
             rc = parse_number(var, ctx, p, end);
             if (rc == 0) {
                 rc = VAR_ERR_MISSING_PADDING_WIDTH;
                 goto error_return;
             }
-            number1.begin = p;
-            number1.end = p + rc;
-            number1.buffer_size = 0;
+            tokenbuf_set(&number1, p, p + rc, 0);
             p += rc;
-
             if (*p != '/') {
                 rc = VAR_ERR_MALFORMATTED_PADDING;
                 goto error_return;
             }
             p++;
-
             rc = parse_substext_or_variable(var, ctx, p, end, &replace);
             if (rc < 0)
                 goto error_return;
             p += rc;
-
             if (*p != '/') {
                 rc = VAR_ERR_MALFORMATTED_PADDING;
                 goto error_return;
             }
             p++;
-
             if (*p != 'l' && *p != 'c' && *p != 'r') {
                 rc = VAR_ERR_MALFORMATTED_PADDING;
                 goto error_return;
             }
             p++;
-
             if (data->begin) {
                 rc = op_padding(data, &number1, &replace, p[-1]);
                 if (rc < 0)
                     goto error_return;
             }
             break;
-
+        }
         default:
             return VAR_ERR_UNKNOWN_COMMAND_CHAR;
     }
 
-    /* Exit gracefully. */
-
+    /* return successfully */
     tokenbuf_free(&tmptokbuf);
     tokenbuf_free(&search);
     tokenbuf_free(&replace);
     tokenbuf_free(&flags);
     tokenbuf_free(&number1);
     tokenbuf_free(&number2);
-    return p - begin;
+    return (p - begin);
 
-  error_return:
+    /* return with an error */
+    error_return:
     tokenbuf_free(data);
     tokenbuf_free(&tmptokbuf);
     tokenbuf_free(&search);
@@ -1830,11 +1339,467 @@
     return rc;
 }
 
+/* parse an integer number ("123") */
+static int 
+parse_integer(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    int *result)
+{
+    const char *p;
+
+    p = begin;
+    (*result) = 0;
+    while (isdigit(*p) && p != end) {
+        (*result) *= 10;
+        (*result) += (*p - '0');
+        p++;
+    }
+    return (p - begin);
+}
+
+/* parse numerical expression operand */
+static int 
+parse_numexp_operand(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end, 
+    int *result, int *failed)
+{
+    const char *p;
+    tokenbuf_t tmp;
+    int rc;
+    var_parse_t myctx;
+
+    /* initialization */
+    p = begin;
+    tokenbuf_init(&tmp);
+    if (p == end)
+        return VAR_ERR_INCOMPLETE_INDEX_SPEC;
+
+    /* parse opening numerical expression */
+    if (*p == '(') {
+        /* parse inner numerical expression */
+        rc = parse_numexp(var, ctx, ++p, end, result, failed);
+        if (rc < 0)
+            return rc;
+        p += rc;
+        if (p == end)
+            return VAR_ERR_INCOMPLETE_INDEX_SPEC;
+        /* parse closing parenthesis */
+        if (*p != ')')
+            return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
+        p++;
+    }
+    /* parse contained variable */
+    else if (*p == var->syntax.delim_init) {
+        /* parse variable with forced expansion */
+        ctx = var_parse_push(ctx, &myctx);
+        ctx->force_expand = 1;
+        rc = parse_variable(var, ctx, p, end, &tmp);
+        ctx = var_parse_pop(ctx);
+
+        if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
+            *failed = 1;
+            /* parse variable without forced expansion */
+            ctx = var_parse_push(ctx, &myctx);
+            ctx->force_expand = 0;
+            rc = parse_variable(var, ctx, p, end, &tmp);
+            ctx = var_parse_pop(ctx);
+            if (rc < 0)
+                return rc;
+            p += rc;
+            *result = 0;
+        }
+        else if (rc < 0)
+            return rc;
+        else {
+            p += rc;
+            /* parse remaining numerical expression */
+            rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
+            tokenbuf_free(&tmp);
+            if (rc < 0)
+                return rc;
+        }
+    }
+    /* parse relative index mark ("#") */
+    else if (   var->syntax.index_mark != NUL 
+             && *p == var->syntax.index_mark) {
+        p++;
+        *result = ctx->index_this;
+        if (ctx->rel_lookup_flag)
+            ctx->rel_lookup_cnt++;
+    }
+    /* parse plain integer number */
+    else if (isdigit(*p)) {
+        rc = parse_integer(var, ctx, p, end, result);
+        p += rc;
+    }
+    /* parse signed positive integer number */
+    else if (*p == '+') {
+        if ((end - p) > 1 && isdigit(p[1])) {
+            p++;
+            rc = parse_integer(var, ctx, p, end, result);
+            p += rc;
+        }
+        else
+            return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
+    }
+    /* parse signed negative integer number */
+    else if (*p == '-') {
+        if (end - p > 1 && isdigit(p[1])) {
+            p++;
+            rc = parse_integer(var, ctx, p, end, result);
+            *result = -(*result);
+            p += rc;
+        }
+        else
+            return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
+    }
+    /* else we failed to parse anything reasonable */
+    else
+        return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
+
+    return (p - begin);
+}
+
+/* parse numerical expression ("x+y") */
+static int 
+parse_numexp(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end, 
+    int *result, int *failed)
+{
+    const char *p;
+    char operator;
+    int right;
+    int rc;
+
+    /* initialization */
+    p = begin;
+    if (p == end)
+        return VAR_ERR_INCOMPLETE_INDEX_SPEC;
+
+    /* parse left numerical operand */
+    rc = parse_numexp_operand(var, ctx, p, end, result, failed);
+    if (rc < 0)
+        return rc;
+    p += rc;
+
+    /* parse numerical operator */
+    while (p != end) {
+        if (*p == '+' || *p == '-') {
+            operator = *p++;
+            /* recursively parse right operand (light binding) */
+            rc = parse_numexp(var, ctx, p, end, &right, failed);
+            if (rc < 0)
+                return rc;
+            p += rc;
+            if (operator == '+')
+                *result = (*result + right);
+            else
+                *result = (*result - right);
+        }
+        else if (*p == '*' || *p == '/' || *p == '%') {
+            operator = *p++;
+            /* recursively parse right operand (string binding) */
+            rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
+            if (rc < 0)
+                return rc;
+            p += rc;
+            if (operator == '*')
+                *result = (*result * right);
+            else if (operator == '/') {
+                if (right == 0) {
+                    if (*failed)
+                        *result = 0;
+                    else
+                        return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
+                }
+                else
+                    *result = (*result / right);
+            }
+            else if (operator == '%') {
+                if (right == 0) {
+                    if (*failed)
+                        *result = 0;
+                    else
+                        return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
+                }
+                else
+                    *result = (*result % right);
+            }
+        }
+        else
+            break;
+    }
+
+    /* return amount of parsed input */
+    return (p - begin);
+}
+
+/* parse variable name ("abc") */
+static int 
+parse_name(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end)
+{
+    const char *p;
+
+    /* parse as long as name class characters are found */
+    for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
+        ;
+    return (p - begin);
+}
+
+/* lookup a variable value through the callback function */
+static int 
+lookup_value(
+    var_t *var, var_parse_t *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 buf[1];
+    int rc;
+
+    /* pass through to original callback */
+    rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
+                              var_ptr, var_len, var_idx, 
+                              val_ptr, val_len, val_size);
+
+    /* convert undefined variable into empty variable if relative
+       lookups are counted. This is the case inside an active loop
+       construct if no limits are given. There the parse_input()
+       has to proceed until all variables have undefined values. 
+       This trick here allows it to determine this case. */
+    if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
+        ctx->rel_lookup_cnt--;
+        buf[0] = NUL;
+        *val_ptr  = buf;
+        *val_len  = 0;
+        *val_size = 0;
+        return VAR_OK;
+    }
+
+    return rc;
+}
+
+/* parse complex variable construct ("${name...}") */
+static int 
+parse_variable_complex(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *result)
+{
+    const char *p;
+    const char *data;
+    size_t len, buffer_size;
+    int failed = 0;
+    int rc;
+    int idx = 0;
+    tokenbuf_t name;
+    tokenbuf_t tmp;
+
+    /* initializations */
+    p = begin;
+    tokenbuf_init(&name);
+    tokenbuf_init(&tmp);
+    tokenbuf_init(result);
+
+    /* parse open delimiter */
+    if (p == end || *p != var->syntax.delim_open)
+        return 0;
+    p++;
+    if (p == end)
+        return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+
+    /* parse name of variable to expand. The name may consist of an
+       arbitrary number of variable name character and contained variable
+       constructs. */
+    do {
+        /* parse a variable name */
+        rc = parse_name(var, ctx, p, end);
+        if (rc < 0)
+            goto error_return;
+        if (rc > 0) {
+            if (!tokenbuf_append(&name, p, rc)) {
+                rc = VAR_ERR_OUT_OF_MEMORY;
+                goto error_return;
+            }
+            p += rc;
+        }
+
+        /* parse an (embedded) variable */
+        rc = parse_variable(var, ctx, p, end, &tmp);
+        if (rc < 0)
+            goto error_return;
+        if (rc > 0) {
+            if (!tokenbuf_merge(&name, &tmp)) {
+                rc = VAR_ERR_OUT_OF_MEMORY;
+                goto error_return;
+            }
+            p += rc;
+        }
+    } while (rc > 0);
+
+    /* we must have the complete expanded variable name now, 
+       so make sure we really do. */
+    if (name.begin == name.end) {
+        if (ctx->force_expand) {
+            rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+            goto error_return;
+        }
+        else {
+            /* If no force_expand is requested, we have to back-off.
+               We're not sure whether our approach here is 100% correct,
+               because it _could_ have side-effects according to Peter
+               Simons, but as far as we know and tried it, it is
+               correct. But be warned -- RSE */
+            tokenbuf_set(result, begin - 1, p, 0);
+            goto goahead;
+        }
+    }
+
+    /* parse an optional index specification */
+    if (   var->syntax.index_open != NUL
+        && *p == var->syntax.index_open) {
+        p++;
+        rc = parse_numexp(var, ctx, p, end, &idx, &failed);
+        if (rc < 0)
+            goto error_return;
+        if (rc == 0) {
+            rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
+            goto error_return;
+        }
+        p += rc;
+        if (p == end) {
+            rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
+            goto error_return;
+        }
+        if (*p != var->syntax.index_close) {
+            rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
+            goto error_return;
+        }
+        p++;
+    }
+
+    /* parse end of variable construct or start of post-operations */
+    if (p == end || (*p != var->syntax.delim_close && *p != ':')) {
+        rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+        goto error_return;
+    }
+    p++;
+
+    /* lookup the variable value now */
+    if (failed)
+        tokenbuf_set(result, begin - 1, p, 0);
+    else {
+        rc = lookup_value(var, ctx,
+                          name.begin, name.end-name.begin, idx,
+                          &data, &len, &buffer_size);
+        if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
+            if (ctx->force_expand)
+                goto error_return;
+            tokenbuf_set(result, begin - 1, p, 0);
+            failed = 1;
+        }
+        else if (rc < 0)
+            goto error_return;
+        else
+            /* the preliminary result is the raw value of the variable.
+               This may be modified by the operations that may follow. */
+            tokenbuf_set(result, data, data + len, buffer_size);
+    }
+
+    /* parse optional post-operations */
+    goahead:
+    if (p[-1] == ':') {
+        tokenbuf_free(&tmp);
+        p--;
+        while (p != end && *p == ':') {
+            p++;
+            if (!failed)
+                rc = parse_operation(var, ctx, p, end, result);
+            else
+                rc = parse_operation(var, ctx, p, end, &tmp);
+            if (rc < 0)
+                goto error_return;
+            p += rc;
+            if (failed)
+                result->end += rc;
+        }
+        if (p == end || *p != var->syntax.delim_close) {
+            rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+            goto error_return;
+        }
+        p++;
+        if (failed)
+            result->end++;
+    }
+
+    /* return successfully */
+    tokenbuf_free(&name);
+    tokenbuf_free(&tmp);
+    return (p - begin);
+
+    /* return with an error */
+    error_return:
+    tokenbuf_free(&name);
+    tokenbuf_free(&tmp);
+    tokenbuf_free(result);
+    return rc;
+}
+
+/* parse variable construct ("$name" or "${name...}") */
+static int 
+parse_variable(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *result)
+{
+    const char *p;
+    const char *data;
+    size_t len, buffer_size;
+    int rc, rc2;
+
+    /* initialization */
+    p = begin;
+    tokenbuf_init(result);
+
+    /* parse init delimiter */
+    if (p == end || *p != var->syntax.delim_init)
+        return 0;
+    p++;
+    if (p == end)
+        return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+
+    /* parse a simple variable name. 
+       (if this fails, we're try to parse a complex variable construct) */
+    rc = parse_name(var, ctx, p, end);
+    if (rc < 0)
+        return rc;
+    if (rc > 0) {
+        rc2 = lookup_value(var, ctx, p, rc, 0, &data, &len, &buffer_size);
+        if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
+            tokenbuf_set(result, begin, begin + 1 + rc, 0);
+            return (1 + rc);
+        }
+        if (rc2 < 0)
+            return rc2;
+        tokenbuf_set(result, data, data + len, buffer_size);
+        return (1 + rc);
+    }
+
+    /* parse a complex variable construct (else case) */
+    rc = parse_variable_complex(var, ctx, p, end, result);
+    if (rc > 0)
+        rc++;
+    return rc;
+}
+
 /* parse loop construct limits ("[...]{b,s,e}") */
 static var_rc_t 
 parse_looplimits(
-    var_t *var,
-    var_parse_t *ctx,
+    var_t *var, var_parse_t *ctx,
     const char *begin, const char *end,
     int *start, int *step, int *stop, int *open_stop)
 {
@@ -1856,7 +1821,7 @@
 
     /* parse loop start value */
     failed = 0;
-    rc = parse_num_exp(var, ctx, p, end, start, &failed);
+    rc = parse_numexp(var, ctx, p, end, start, &failed);
     if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
         *start = 0; /* use default */
     else if (rc < 0)
@@ -1873,7 +1838,7 @@
 
     /* parse loop step value */
     failed = 0;
-    rc = parse_num_exp(var, ctx, p, end, step, &failed);
+    rc = parse_numexp(var, ctx, p, end, step, &failed);
     if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
         *step = 1; /* use default */
     else if (rc < 0)
@@ -1905,7 +1870,7 @@
 
     /* parse loop stop value */
     failed = 0;
-    rc = parse_num_exp(var, ctx, p, end, stop, &failed);
+    rc = parse_numexp(var, ctx, p, end, stop, &failed);
     if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
         *stop = 0; /* use default */
         *open_stop = 1;
@@ -1928,6 +1893,32 @@
     return (p - begin);
 }
 
+/* parse plain text */
+static int 
+parse_text(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end)
+{
+    const char *p;
+
+    /* parse until delim_init (variable construct) 
+       or index_open (loop construct) is found */
+    for (p = begin; p != end; p++) {
+        if (*p == var->syntax.escape) {
+            p++; /* skip next character */
+            if (p == end)
+                return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
+        }
+        else if (*p == var->syntax.delim_init)
+            break;
+        else if (   var->syntax.index_open != NUL
+                 && (   *p == var->syntax.index_open 
+                     || *p == var->syntax.index_close))
+            break;
+    }
+    return (p - begin);
+}
+
 /* expand input in general */
 static var_rc_t 
 parse_input(

CVSTrac 2.0.1