/*++ /* NAME /* hfrom_format 3 /* SUMMARY /* Parse a header_from_format setting /* SYNOPSIS /* #include /* /* int hfrom_format_parse( /* const char *name, /* const char *value) /* /* const char *str_hfrom_format_code(int code) /* DESCRIPTION /* hfrom_format_parse() takes a parameter name (used for /* diagnostics) and value, and maps it to the corresponding /* code: HFROM_FORMAT_NAME_STD maps to HFROM_FORMAT_CODE_STD, /* and HFROM_FORMAT_NAME_OBS maps to HFROM_FORMAT_CODE_OBS. /* /* str_hfrom_format_code() does the reverse mapping. /* DIAGNOSTICS /* All input errors are fatal. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA /*--*/ /* * System library. */ #include /* * Utility library. */ #include #include /* * Global library. */ #include /* * Application-specific. */ #include /* * Primitive dependency injection. */ #ifdef TEST extern NORETURN PRINTFLIKE(1, 2) test_msg_fatal(const char *,...); #define msg_fatal test_msg_fatal #endif /* * The name-to-code mapping. */ static const NAME_CODE hfrom_format_table[] = { HFROM_FORMAT_NAME_STD, HFROM_FORMAT_CODE_STD, HFROM_FORMAT_NAME_OBS, HFROM_FORMAT_CODE_OBS, 0, -1, }; /* hfrom_format_parse - parse header_from_format setting */ int hfrom_format_parse(const char *name, const char *value) { int code; if ((code = name_code(hfrom_format_table, NAME_CODE_FLAG_NONE, value)) < 0) msg_fatal("invalid setting: \"%s = %s\"", name, value); return (code); } /* str_hfrom_format_code - convert code to string */ const char *str_hfrom_format_code(int code) { const char *name; if ((name = str_name_code(hfrom_format_table, code)) == 0) msg_fatal("invalid header format code: %d", code); return (name); } #ifdef TEST #include #include #include #include #include #include #define STR(x) vstring_str(x) /* * TODO(wietse) make this a proper VSTREAM interface. Instead of temporarily * swapping streams, we could temporarily swap the stream's write function. */ /* vstream_swap - kludge to capture output for testing */ static void vstream_swap(VSTREAM *one, VSTREAM *two) { VSTREAM save; save = *one; *one = *two; *two = save; } jmp_buf test_fatal_jbuf; #undef msg_fatal /* test_msg_fatal - does not return, and does not terminate */ void test_msg_fatal(const char *fmt,...) { va_list ap; va_start(ap, fmt); vmsg_warn(fmt, ap); va_end(ap); longjmp(test_fatal_jbuf, 1); } struct name_test_case { const char *label; /* identifies test case */ const char *config; /* configuration under test */ const char *exp_warning; /* expected warning or empty */ const int exp_code; /* expected code */ }; static struct name_test_case name_test_cases[] = { {"hfrom_format_parse good-standard", /* config */ HFROM_FORMAT_NAME_STD, /* warning */ "", /* exp_code */ HFROM_FORMAT_CODE_STD }, {"hfrom_format_parse good-obsolete", /* config */ HFROM_FORMAT_NAME_OBS, /* warning */ "", /* exp_code */ HFROM_FORMAT_CODE_OBS }, {"hfrom_format_parse bad", /* config */ "does-not-exist", /* warning */ "hfrom_format: warning: invalid setting: \"hfrom_format_parse bad = does-not-exist\"\n", /* code */ 0, }, {"hfrom_format_parse empty", /* config */ "", /* warning */ "hfrom_format: warning: invalid setting: \"hfrom_format_parse empty = \"\n", /* code */ 0, }, 0, }; struct code_test_case { const char *label; /* identifies test case */ int code; /* code under test */ const char *exp_warning; /* expected warning or empty */ const char *exp_name; /* expected namme */ }; static struct code_test_case code_test_cases[] = { {"str_hfrom_format_code good-standard", /* code */ HFROM_FORMAT_CODE_STD, /* warning */ "", /* exp_name */ HFROM_FORMAT_NAME_STD }, {"str_hfrom_format_code good-obsolete", /* code */ HFROM_FORMAT_CODE_OBS, /* warning */ "", /* exp_name */ HFROM_FORMAT_NAME_OBS }, {"str_hfrom_format_code bad", /* config */ 12345, /* warning */ "hfrom_format: warning: invalid header format code: 12345\n", /* exp_name */ 0 }, 0, }; int main(int argc, char **argv) { struct name_test_case *np; int code; struct code_test_case *cp; const char *name; int pass = 0; int fail = 0; int test_failed; VSTRING *msg_buf; VSTREAM *memory_stream; msg_vstream_init("hfrom_format", VSTREAM_ERR); msg_buf = vstring_alloc(100); for (np = name_test_cases; np->label != 0; np++) { VSTRING_RESET(msg_buf); VSTRING_TERMINATE(msg_buf); test_failed = 0; if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0) msg_fatal("open memory stream: %m"); vstream_swap(VSTREAM_ERR, memory_stream); if (setjmp(test_fatal_jbuf) == 0) code = hfrom_format_parse(np->label, np->config); vstream_swap(memory_stream, VSTREAM_ERR); if (vstream_fclose(memory_stream)) msg_fatal("close memory stream: %m"); if (strcmp(STR(msg_buf), np->exp_warning) != 0) { msg_warn("test case %s: got error: \"%s\", want: \"%s\"", np->label, STR(msg_buf), np->exp_warning); test_failed = 1; } if (*np->exp_warning == 0) { if (code != np->exp_code) { msg_warn("test case %s: got code: \"%d\", want: \"%d\"(%s)", np->label, code, np->exp_code, str_hfrom_format_code(np->exp_code)); test_failed = 1; } } if (test_failed) { msg_info("%s: FAIL", np->label); fail++; } else { msg_info("%s: PASS", np->label); pass++; } } for (cp = code_test_cases; cp->label != 0; cp++) { VSTRING_RESET(msg_buf); VSTRING_TERMINATE(msg_buf); test_failed = 0; if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0) msg_fatal("open memory stream: %m"); vstream_swap(VSTREAM_ERR, memory_stream); if (setjmp(test_fatal_jbuf) == 0) name = str_hfrom_format_code(cp->code); vstream_swap(memory_stream, VSTREAM_ERR); if (vstream_fclose(memory_stream)) msg_fatal("close memory stream: %m"); if (strcmp(STR(msg_buf), cp->exp_warning) != 0) { msg_warn("test case %s: got error: \"%s\", want: \"%s\"", cp->label, STR(msg_buf), cp->exp_warning); test_failed = 1; } else if (*cp->exp_warning == 0) { if (strcmp(name, cp->exp_name)) { msg_warn("test case %s: got name: \"%s\", want: \"%s\"", cp->label, name, cp->exp_name); test_failed = 1; } } if (test_failed) { msg_info("%s: FAIL", cp->label); fail++; } else { msg_info("%s: PASS", cp->label); pass++; } } msg_info("PASS=%d FAIL=%d", pass, fail); vstring_free(msg_buf); exit(fail != 0); } #endif