Index: ossp-pkg/var/TODO RCS File: /v/ossp/cvs/ossp-pkg/var/Attic/TODO,v rcsdiff -q -kk '-r1.19' '-r1.20' -u '/v/ossp/cvs/ossp-pkg/var/Attic/TODO,v' 2>/dev/null --- TODO 2002/02/28 12:10:33 1.19 +++ TODO 2002/03/04 11:53:27 1.20 @@ -19,8 +19,3 @@ o document exception handling - o additional feature: functions via callback. - ${foo:F:F} calls callback for bar with result of foo - expansion and then callback for quux with the result returned from - bar. - Index: ossp-pkg/var/var.c RCS File: /v/ossp/cvs/ossp-pkg/var/var.c,v rcsdiff -q -kk '-r1.75' '-r1.76' -u '/v/ossp/cvs/ossp-pkg/var/var.c,v' 2>/dev/null --- var.c 2002/03/04 11:37:34 1.75 +++ var.c 2002/03/04 11:53:27 1.76 @@ -72,19 +72,21 @@ /* the external context structure */ struct var_st { - var_syntax_t syntax; - char_class_t syntax_nameclass; - var_cb_value_t cb_value_fct; - void *cb_value_ctx; + var_syntax_t syntax; + char_class_t syntax_nameclass; + var_cb_value_t cb_value_fct; + void *cb_value_ctx; + var_cb_operation_t cb_operation_fct; + void *cb_operation_ctx; }; /* the internal expansion context structure */ struct var_parse_st { struct var_parse_st *lower; - int force_expand; - int rel_lookup_flag; - int rel_lookup_cnt; - int index_this; + int force_expand; + int rel_lookup_flag; + int rel_lookup_cnt; + int index_this; }; typedef struct var_parse_st var_parse_t; @@ -443,6 +445,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_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed); +static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end); /* parse substitution text */ static int @@ -485,6 +488,71 @@ return (p - begin); } +/* parse opertion argument text */ +static int +parse_opargtext( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end) +{ + const char *p; + + /* parse until delim_init or ')' */ + for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) { + if (*p == var->syntax.escape) { + if (p + 1 == end) + return VAR_ERR_INCOMPLETE_QUOTED_PAIR; + p++; + } + } + return (p - begin); +} + +static int +parse_opargtext_or_variable( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + tokenbuf_t *result) +{ + const char *p; + tokenbuf_t tmp; + int rc; + + tokenbuf_init(result); + tokenbuf_init(&tmp); + p = begin; + if (p == end) + return 0; + do { + rc = parse_opargtext(var, ctx, p, end); + if (rc < 0) + goto error_return; + if (rc > 0) { + if (!tokenbuf_append(result, 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) { + p += rc; + if (!tokenbuf_merge(result, &tmp)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + } + } while (rc > 0); + tokenbuf_free(&tmp); + return (p - begin); + + error_return: + tokenbuf_free(&tmp); + tokenbuf_free(result); + return rc; +} + /* parse expression or variable */ static int parse_exptext_or_variable( @@ -1314,6 +1382,62 @@ } break; } + case '%': { + /* operation callback function */ + const char *op_ptr; + size_t op_len; + const char *arg_ptr; + size_t arg_len; + const char *val_ptr; + size_t val_len; + const char *out_ptr; + size_t out_len; + size_t out_size; + tokenbuf_t args; + + p++; + rc = parse_name(var, ctx, p, end); + if (rc < 0) + goto error_return; + op_ptr = p; + op_len = rc; + p += rc; + if (*p == '(') { + p++; + tokenbuf_init(&args); + rc = parse_opargtext_or_variable(var, ctx, p, end, &args); + if (rc < 0) + goto error_return; + p += rc; + arg_ptr = args.begin; + arg_len = args.end - args.begin; + if (*p != ')') { + rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS; + goto error_return; + } + p++; + } + else { + arg_ptr = NULL; + arg_len = 0; + } + val_ptr = data->begin; + val_len = data->end - data->begin; + + if (data->begin != NULL && var->cb_operation_fct != NULL) { + /* call operation callback function */ + rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx, + op_ptr, op_len, + arg_ptr, arg_len, + val_ptr, val_len, + &out_ptr, &out_len, &out_size); + if (rc < 0) + goto error_return; + tokenbuf_free(data); + tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size); + } + break; + } default: return VAR_ERR_UNKNOWN_COMMAND_CHAR; } @@ -2141,6 +2265,15 @@ var->cb_value_ctx = ctx; break; } + case VAR_CONFIG_CB_OPERATION: { + var_cb_operation_t fct; + void *ctx; + fct = (var_cb_operation_t)va_arg(ap, void *); + ctx = (void *)va_arg(ap, void *); + var->cb_operation_fct = fct; + var->cb_operation_ctx = ctx; + break; + } default: return VAR_RC(VAR_ERR_INVALID_ARGUMENT); } @@ -2266,6 +2399,8 @@ /* var_rc_t to string mapping table */ static const char *var_errors[] = { "everything ok", /* VAR_OK = 0 */ + "undefined operation", /* VAR_ERR_UNDEFINED_OPERATION */ + "malformed operation argument list", /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */ "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */ "incomplete hexadecimal value", /* VAR_ERR_INCOMPLETE_HEX */ "invalid hexadecimal value", /* VAR_ERR_INVALID_HEX */ Index: ossp-pkg/var/var.h RCS File: /v/ossp/cvs/ossp-pkg/var/var.h,v rcsdiff -q -kk '-r1.26' '-r1.27' -u '/v/ossp/cvs/ossp-pkg/var/var.h,v' 2>/dev/null --- var.h 2002/02/28 12:40:01 1.26 +++ var.h 2002/03/04 11:53:27 1.27 @@ -35,6 +35,8 @@ /* Error codes */ typedef enum { VAR_ERR_CALLBACK = -64, + VAR_ERR_UNDEFINED_OPERATION = -44, + VAR_ERR_MALFORMED_OPERATION_ARGUMENTS = -43, VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS = -42, VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT = -41, VAR_ERR_DIVISION_BY_ZERO_IN_INDEX = -40, @@ -85,7 +87,8 @@ typedef enum { VAR_CONFIG_SYNTAX, - VAR_CONFIG_CB_VALUE + VAR_CONFIG_CB_VALUE, + VAR_CONFIG_CB_OPERATION } var_config_t; typedef struct { @@ -105,6 +108,14 @@ const char **val_ptr, size_t *val_len, size_t *val_size ); +typedef var_rc_t (*var_cb_operation_t)( + var_t *var, void *ctx, + const char *op_ptr, size_t op_len, + const char *arg_ptr, size_t arg_len, + const char *val_ptr, size_t val_len, + const char **out_ptr, size_t *out_len, size_t *out_size +); + extern const char var_id[]; var_rc_t var_create (var_t **var); Index: ossp-pkg/var/var.pod RCS File: /v/ossp/cvs/ossp-pkg/var/var.pod,v rcsdiff -q -kk '-r1.24' '-r1.25' -u '/v/ossp/cvs/ossp-pkg/var/var.pod,v' 2>/dev/null --- var.pod 2002/02/28 14:58:07 1.24 +++ var.pod 2002/03/04 11:53:27 1.25 @@ -43,7 +43,8 @@ B, B, B, -B. +B, +B. =item Functions: @@ -172,6 +173,11 @@ of I is either left justified (flag "C"), centered (flag "C"), or right justified (flag "C"). +=item C<${>IC<:%>I[C<(>IC<)>]C<}> + +C<$>I after passing it to an application-supplied function I. +The optional argument I is passed to the function, too. +By default no such functions are defined. =item C<[>IC<]>, C<[>IC<]>C<{>IC<,>IC<,>IC<}> @@ -224,6 +230,8 @@ | 'p' '/' NUMBER '/' (variable|TEXT_SUBST)* '/' ('r'|'l'|'c') + | '%' (name|variable)+ + ('(' (TEXT_ARGS)? ')')? | 'l' | 'u' @@ -241,7 +249,8 @@ NUMBER ::= ('0'|...|'9')+ - TEXT_SUBST ::= ^(DELIM_INIT|'/') + TEXT_SUBST ::= (^(DELIM_INIT|'/'))+ + TEXT_ARGS ::= (^(DELIM_INIT|')'))+ TEXT_EXP ::= (^(DELIM_INIT|DELIM_CLOSE|':'))+ TEXT ::= (^(DELIM_INIT|INDEX_OPEN|INDEX_CLOSE))+ @@ -421,6 +430,94 @@ he subtract own callback return codes from this value, i.e., return (C - I) (I E= 0) from the callback function. +=item B + +This is an exported function pointer type for variable value operation +functions. Such a callback function B has to be of the following +prototype: + +var_rc_t *B(var_t *I, void *I, const char *I, +size_t I, const char *I, size_t I, const +char *I, size_t I, const char **I, size_t +*I, size_t *I); + +This function will be called by var_expand() internally whenever a +custom operation is used. Its parameters are: + +=over 4 + +=item var_t *I + +This is the passed-through argument as passed to var_expand() as the +first argument. This can be used in the callback function to distinguish +the expansion context or to resolve return codes, etc. + +=item void *I + +This is the passed-through argument as passed to var_config() on +C as the forth argument. This can be used +to provide an internal context to the callback function through +var_expand(). + +=item const char *I + +This is a pointer to the name of the operation which var_expand() wishes +to perform. Please note that the string is NOT necessarily terminated +by a C ('C<\0>') character. If the callback function needs it +C-terminated, it has to copy the string into an a temporary buffer +of its own and C-terminate it there. + +=item size_t I + +This is the length of the variable name at I. + +=item const char *I + +This is a pointer to the optional argument string to the operation. If +no argument string or an empty argument string was supplied this is +C. + +=item size_t I + +This is the length of the I. + +=item const char *I + +This is a pointer to the value of the variable which the +operation wants to adjust. + +=item size_t I + +This is the length of the I. + +=item const char **I + +This is a pointer to the location where the callback function should +store the pointer to the adjusted value. + +=item size_t *I + +This is a pointer to the location where the callback function should +store the length of the adjusted value of the variable. + +=item size_t *I + +This is a pointer to the location where the callback function should +store the size of the buffer that has been allocated to hold the +adjusted value of the variable. + +If no buffer has been allocated by the callback at all, because the +variable uses some other means of storing the contents, this should be +set to zero (C<0>). + +In case a buffer size greater than zero is returned by the callback, +var_expand() will make use of that buffer internally if possible. It +will also free(3) the buffer when it is not needed anymore, so it +is important that it was previously allocated with malloc(3) by the +callback. + +=back + =back =head2 FUNCTIONS Index: ossp-pkg/var/var_test.c RCS File: /v/ossp/cvs/ossp-pkg/var/var_test.c,v rcsdiff -q -kk '-r1.38' '-r1.39' -u '/v/ossp/cvs/ossp-pkg/var/var_test.c,v' 2>/dev/null --- var_test.c 2002/02/28 20:27:05 1.38 +++ var_test.c 2002/03/04 11:53:27 1.39 @@ -30,9 +30,55 @@ #include #include #include +#include #include "var.h" +static var_rc_t +var_operation( + var_t *var, void *ctx, + const char *op_ptr, size_t op_len, + const char *arg_ptr, size_t arg_len, + const char *val_ptr, size_t val_len, + char **out_ptr, size_t *out_len, size_t *out_size) +{ + int i; + + fprintf(stderr, "op=<%s>(%d) arg=<%s>(%d) val=<%s>(%d)\n", + op_ptr, op_len, arg_ptr, arg_len, val_ptr, val_len); + if (val_ptr == NULL) { + *out_ptr = ""; + *out_len = 0; + *out_size = 0; + return VAR_OK; + } + if (op_len == 6 && strncmp(op_ptr, "return", 6) == 0) { + *out_ptr = malloc(arg_len); + *out_len = arg_len; + *out_size = arg_len; + memcpy(*out_ptr, arg_ptr, arg_len); + return VAR_OK; + } + else if (op_len == 5 && strncmp(op_ptr, "upper", 5) == 0) { + *out_ptr = malloc(val_len); + *out_len = val_len; + *out_size = val_len; + for (i = 0; i < val_len; i++) + (*out_ptr)[i] = (char)toupper((int)(val_ptr[i])); + return VAR_OK; + } + else if (op_len == 5 && strncmp(op_ptr, "lower", 5) == 0) { + *out_ptr = malloc(val_len); + *out_len = val_len; + *out_size = val_len; + for (i = 0; i < val_len; i++) + (*out_ptr)[i] = (char)tolower((int)(val_ptr[i])); + return VAR_OK; + } + else + return VAR_ERR_UNDEFINED_OPERATION; +} + struct variable { const char *name; const unsigned int idx; @@ -177,6 +223,9 @@ { "-[${ARRAY[#]}:]{1,$NUMBER}-", "-entry1:entry2:-" }, { "-[${ARRAY[#]}:]{1,3,5}-", "-entry1::-" }, { "${MULTILINE:s/^/ | /g}", " | line1\n | line2\n" }, + { "${HOME:%upper}", "/HOME/REGRESSION-TESTS" }, + { "${HOME:%upper:%lower}", "/home/regression-tests" }, + { "${EMPTY:%return($HOME)}", "/home/regression-tests" }, { "[${ARRAY}:${ARRAY[#]}-]", "entry0:entry0-entry0:entry1-entry0:entry2-entry0:entry3-" @@ -215,6 +264,11 @@ var_strerror(NULL, rc, &err); printf("unable to configure variable expansion context: %s (%d)\n", err, rc); return 1; + } + if ((rc = var_config(var, VAR_CONFIG_CB_OPERATION, var_operation, NULL)) != VAR_OK) { + var_strerror(NULL, rc, &err); + printf("unable to configure variable expansion context: %s (%d)\n", err, rc); + return 1; } for (i = 0; i < sizeof(tests)/sizeof(struct test_case); ++i) {