--- 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(
|