/* ** OSSP var -- Variable Expansion ** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/) ** ** This file is part of OSSP var, a variable expansion ** library which can be found at http://www.ossp.org/pkg/lib/var/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** var_test.c: library regression test. */ #include #include #include #include #include "config.h" #if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC) #include "dmalloc.h" #endif #include "var.h" #include "ts.h" /* ** ** ==== VARIABLE LOOKUP CALLBACK ==== ** */ struct variable { const char *name; const unsigned int idx; const char *data; }; static const struct variable lookup_vars[] = { { "ARRAY", 0, "entry0" }, { "ARRAY", 1, "entry1" }, { "ARRAY", 2, "entry2" }, { "ARRAY", 3, "entry3" }, { "BAR", 0, "type" }, { "EMPTY", 0, "" }, { "FOO", 0, "os" }, { "HEINZ", 0, "heinz0" }, { "HEINZ", 1, "heinz1" }, { "HOME", 0, "/home/regression-tests" }, { "MULTILINE", 0, "line1\nline2\n" }, { "NUMBER", 0, "+2" }, { "NUMEXP", 0, "((16)%5)" }, { "OSTYPE", 0, "regression-os" }, { "TERM", 0, "regression-term" }, { NULL, 0, NULL } }; static var_rc_t lookup_cb( var_t *var, void *context, const char *varname, size_t name_len, int idx, const char **data, size_t *data_len, size_t *buffer_size) { const struct variable *vars = context; size_t i, counter, length; static char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */ if (idx >= 0) { for (i = 0; vars[i].name; ++i) { if (strncmp(varname, vars[i].name, name_len) == 0 && vars[i].idx == idx) { *data = vars[i].data; *data_len = strlen(*data); *buffer_size = 0; return VAR_OK; } } } else { for (i = 0; vars[i].name; ++i) { if (strncmp(varname, vars[i].name, name_len) == 0) { counter = 1; length = strlen(vars[i].data); while ( vars[i + counter].data && strncmp(varname, vars[i + counter].name, name_len) == 0) counter++; if (counter == 1) sprintf(buf, "%d", length); else sprintf(buf, "%d", counter); *data = buf; *data_len = strlen(buf); *buffer_size = 0; return VAR_OK; } } } return VAR_ERR_UNDEFINED_VARIABLE; } /* ** ** ==== OPERATION CALLBACK ==== ** */ static var_rc_t operate_cb( 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; 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; } /* ** ** ==== TEST CASES ==== ** */ TS_TEST(test_expand) { struct test_case { const char *input; const char *expected; }; const struct test_case tests[] = { { "${HOME}${!!}", "/home/regression-tests${!!}" }, { "$HOME", "/home/regression-tests" }, { "${FOO}", "os" }, { "${BAR}", "type" }, { "${${FOO:u}${BAR:u}:l:u}", "REGRESSION-OS" }, { "${UNDEFINED}", "${UNDEFINED}" }, { "${OSTYPE:#}", "13" }, { "${EMPTY:-test${FOO}test}", "testostest" }, { "${EMPTY:-test${FOO:u}test}", "testOStest" }, { "${TERM:-test${FOO}test}", "regression-term" }, { "${EMPTY:+FOO}", "" }, { "${HOME:+test${FOO}test}", "testostest" }, { "${HOME:+${OS${BAR:u}}}", "regression-os" }, { "${HOME:+OS${UNDEFINED:u}}", "OS${UNDEFINED:u}" }, { "${UNDEFINED:+OS${BAR:u}}", "${UNDEFINED:+OS${BAR:u}}" }, { "${HOME:*heinz}", "" }, { "${EMPTY:*claus}", "claus" }, { "${TERM}", "regression-term" }, { "${HOME:s/reg/bla/}", "/home/blaression-tests" }, { "${HOME:s/e/bla/}", "/hombla/regression-tests" }, { "${HOME:s/e/bla/g}", "/hombla/rblagrblassion-tblasts" }, { "${HOME:s/\\//_/g}", "_home_regression-tests" }, { "${HOME:s/[eso]/_/g}", "/h_m_/r_gr___i_n-t__t_" }, { "${HOME:s/[esO]/_/g}", "/hom_/r_gr___ion-t__t_" }, { "${HOME:s/[esO]/_/gi}", "/h_m_/r_gr___i_n-t__t_" }, { "${OSTYPE:s/^[re]/_/g}", "_egression-os" }, { "${EMPTY:s/^[re]/_/g}", "" }, { "${HOME:s/.*\\x{}/heinz/}", "heinz" }, { "${HOME:s/e/bla/t}", "/hombla/regression-tests" }, { "${HOME:s/E/bla/t}", "/home/regression-tests" }, { "${HOME:s/E/bla/ti}", "/hombla/regression-tests" }, { "${HOME:s/E/bla/tig}", "/hombla/rblagrblassion-tblasts" }, { "${HOME:s/^(.*)$/<\\1>/}", "" }, { "${HOME:o1-5}", "home/" }, { "${HOME:o1,5}", "home/" }, { "${HOME:o5,}", "/regression-tests" }, { "${HOME:o5-}", "/regression-tests" }, { "${HOME:o7,13}", "egressi" }, { "${HOME:y/a-z/A-YZ/}", "/HOME/REGRESSION-TESTS" }, { "${HOME:y/e-g/a-c/}", "/homa/racrassion-tasts" }, { "${FOO:p/15/../l}", "os............." }, { "${FOO:p/15/12345/l}", "os1234512345123" }, { "${FOO:p/15/../r}", ".............os" }, { "${FOO:p/15/12345/r}", "1234512345123os" }, { "${FOO:p/15/../c}", "......os......." }, { "${FOO:p/15/12345/c}", "123451os1234512" }, { "${FOO:s/os/\\x{4F}\\123/g}", "OS" }, { "${FOO:s/os/\\1\\x4F\\123/t}", "\\1OS" }, { "${HOME:s/g(res)s/x\\1x/g}", "/home/rexresxion-tests" }, { "${HOME:s/(s+)/_\\1_/g}", "/home/regre_ss_ion-te_s_t_s_" }, { "${HOME:s/\\x65/\\x45/g}", "/homE/rEgrEssion-tEsts" }, { "${HOME:s/(s*)/x\\1X/g}", "xX/xXhxXoxXmxXexX/xXrxXexXgxXrxXexssXxXixXoxXnxX-xXtxXexsXxXtxsX" }, { "${HOME:s/./\\\\/g}", "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\" }, { "${ARRAY[1]}", "entry1", }, { "${ARRAY[5+4*2-1]}", "${ARRAY[5+4*2-1]}", }, { "${ARRAY[(5+(3+4)*2)%16]}", "entry3", }, { "${ARRAY[(5+(3+4)*2)/9]}", "entry2", }, { "${ARRAY[+1--2]}", "entry3" }, { "${ARRAY[-1]}", "4" }, { "${HOME[-1]}", "22" }, { "${ARRAY[$NUMBER]}", "entry2" }, { "${ARRAY[$NUMEXP]}", "entry1" }, { "${ARRAY[$NUMEXP-1]}", "entry0" }, { "${ARRAY[${UNDEFINED}-1]}", "${ARRAY[${UNDEFINED}-1]}" }, { "${ARRAY[5/(${UNDEFINED})]}", "${ARRAY[5/(${UNDEFINED})]}" }, { "[${ARRAY[#]}-]", "entry0-entry1-entry2-entry3-" }, { "[${ARRAY[#+1]}-]", "entry1-entry2-entry3-" }, { "-[${ARRAY[#]}:]{1,$NUMBER}-", "-entry1:entry2:-" }, { "-[${ARRAY[#]}:]{1,3,5}-", "-entry1::-" }, { "${MULTILINE:s/^/ | /gm}", " | 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-" }, { "[${HEINZ[#]}:${ARRAY[#]}-]", "heinz0:entry0-heinz1:entry1-:entry2-:entry3-" }, { "[${HEINZ[#]}:[${ARRAY[#]}] ]", "heinz0:entry0entry1entry2entry3 heinz1:entry0entry1entry2entry3 " }, { "[${HEINZ[#]}: [${ARRAY[#]}${ARRAY[#+1]:+, }]${HEINZ[#+1]:+; }]", "heinz0: entry0, entry1, entry2, entry3; heinz1: entry0, entry1, entry2, entry3" }, { "[${ARRAY[#]}:[${ARRAY[#]},]{1,2,} ]{0,2,}", "entry0:entry1,entry3, entry2:entry1,entry3, " }, }; char *tmp; size_t tmp_len; var_rc_t rc; size_t i; char buffer[1024]; var_t *var; char *err; ts_test_check(TS_CTX, "create environment"); if ((rc = var_create(&var)) != VAR_OK) { var_strerror(NULL, rc, &err); ts_test_fail(TS_CTX, "var_create: %s (%d)", err, rc); return; } if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup_cb, lookup_vars)) != VAR_OK) { var_strerror(NULL, rc, &err); ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc); return; } if ((rc = var_config(var, VAR_CONFIG_CB_OPERATION, operate_cb, NULL)) != VAR_OK) { var_strerror(NULL, rc, &err); ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc); return; } for (i = 0; i < sizeof(tests)/sizeof(struct test_case); i++) { ts_test_check(TS_CTX, "expansion (#%d): raw input \"%s\"", i, tests[i].input); rc = var_unescape(var, tests[i].input, strlen(tests[i].input), buffer, sizeof(buffer), 0); if (rc != VAR_OK) { var_strerror(var, rc, &err); ts_test_fail(TS_CTX, "var_unescape: %s (%d)", err, rc); continue; } ts_test_check(TS_CTX, "expansion (#%d): unescaped input \"%s\"", i, buffer); rc = var_expand(var, buffer, strlen(buffer), &tmp, &tmp_len, 0); if (rc != VAR_OK) { var_strerror(var, rc, &err); ts_test_fail(TS_CTX, "var_expand: %s (%d)", err, rc); continue; } ts_test_check(TS_CTX, "expansion (#%d): expanded output \"%s\"", i, tmp); if ( tmp_len != strlen(tests[i].expected) || memcmp(tests[i].expected, tmp, tmp_len) != 0) { ts_test_fail(TS_CTX, "expected \"%s\", got \"%s\"", tests[i].expected, tmp); free(tmp); continue; } free(tmp); } var_destroy(var); return; } TS_TEST(test_format) { var_rc_t rc; var_t *var; char *err; char *fmt; char *exp; char *got; ts_test_check(TS_CTX, "create environment"); if ((rc = var_create(&var)) != VAR_OK) { var_strerror(NULL, rc, &err); ts_test_fail(TS_CTX, "var_create: %s (%d)", err, rc); return; } if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup_cb, lookup_vars)) != VAR_OK) { var_strerror(NULL, rc, &err); ts_test_fail(TS_CTX, "var_config: %s (%d)", err, rc); return; } /* check trivial formatting */ fmt = "foo"; exp = "foo"; ts_test_check(TS_CTX, "formatting \"%s\"", fmt); if ((rc = var_format(var, &got, 1, fmt)) != VAR_OK) { var_strerror(var, rc, &err); ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc); return; } if (strcmp(got, exp) != 0) { ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", exp, got); return; } free(got); /* check real formatting */ fmt = "foo<%s><%d><%c>quux"; exp = "foo<123>quux"; ts_test_check(TS_CTX, "formatting \"%s\"", fmt); if ((rc = var_format(var, &got, 1, fmt, "bar", 123, 'x')) != VAR_OK) { var_strerror(var, rc, &err); ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc); return; } if (strcmp(got, exp) != 0) { ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", exp, got); return; } free(got); /* check combined formatting and expansion */ fmt = "foo<${ARRAY[%d]}>bar"; exp = "foobar"; ts_test_check(TS_CTX, "formatting \"%s\"", fmt); if ((rc = var_format(var, &got, 1, fmt, 1)) != VAR_OK) { var_strerror(var, rc, &err); ts_test_fail(TS_CTX, "var_format: %s (%d)", err, rc); return; } if (strcmp(got, exp) != 0) { ts_test_fail(TS_CTX, "var_format: expected \"%s\", got \"%s\"\n", exp, got); return; } free(got); var_destroy(var); } int main(int argc, char *argv[]) { ts_suite_t *ts; int n; ts = ts_suite_new("OSSP var (Variable Expansion)"); ts_suite_test(ts, test_expand, "expansion"); ts_suite_test(ts, test_format, "formatting"); n = ts_suite_run(ts); ts_suite_free(ts); return n; }