Index: ossp-pkg/var/var.c RCS File: /v/ossp/cvs/ossp-pkg/var/var.c,v rcsdiff -q -kk '-r1.73' '-r1.74' -u '/v/ossp/cvs/ossp-pkg/var/var.c,v' 2>/dev/null --- 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(