From e804a6f981faebd95504dcf1792b1f5664de3424 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 14 Dec 2023 12:46:17 +0000 Subject: [PATCH 1/7] tests: upgrade to latest version of clar --- tests/clar.c | 327 +++++++++++++++++++++++++++++++++--------- tests/clar.h | 62 ++++---- tests/clar/fixtures.h | 28 ++-- tests/clar/fs.h | 319 +++++++++++++++++++++++++++++++--------- tests/clar/print.h | 177 ++++++++++++++++++++--- tests/clar/sandbox.h | 23 ++- tests/clar/summary.h | 143 ++++++++++++++++++ tests/ntlm_tests.h | 11 +- 8 files changed, 892 insertions(+), 198 deletions(-) create mode 100644 tests/clar/summary.h diff --git a/tests/clar.c b/tests/clar.c index 2adaef0..3fc2c76 100644 --- a/tests/clar.c +++ b/tests/clar.c @@ -12,6 +12,7 @@ #include #include #include +#include /* required for sandboxing */ #include @@ -67,7 +68,7 @@ # define PRIxZ "Ix" # endif -# ifdef _MSC_VER +# if defined(_MSC_VER) || defined(__MINGW32__) typedef struct stat STAT_T; # else typedef struct _stat STAT_T; @@ -86,34 +87,58 @@ typedef struct stat STAT_T; #endif +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + #include "clar.h" -__attribute__((unused)) static void fs_rm(const char *_source); -__attribute__((unused)) static void fs_copy(const char *_source, const char *dest); -__attribute__((unused)) +#ifdef CLAR_FIXTURE_PATH static const char * fixture_path(const char *base, const char *fixture_name); +#endif struct clar_error { - const char *test; - int test_number; - const char *suite; const char *file; - int line_number; + const char *function; + size_t line_number; const char *error_msg; char *description; struct clar_error *next; }; -static struct { - int argc; - char **argv; +struct clar_explicit { + size_t suite_idx; + const char *filter; + + struct clar_explicit *next; +}; + +struct clar_report { + const char *test; + int test_number; + const char *suite; + + enum cl_test_status status; + time_t start; + double elapsed; + struct clar_error *errors; + struct clar_error *last_error; + + struct clar_report *next; +}; + +struct clar_summary { + const char *filename; + FILE *fp; +}; + +static struct { enum cl_test_status test_status; + const char *active_test; const char *active_suite; @@ -123,12 +148,21 @@ static struct { int tests_ran; int suites_ran; + enum cl_output_format output_format; + int report_errors_only; int exit_on_error; - int report_suite_names; + int verbosity; - struct clar_error *errors; - struct clar_error *last_error; + int write_summary; + char *summary_filename; + struct clar_summary *summary; + + struct clar_explicit *explicit; + struct clar_explicit *last_explicit; + + struct clar_report *reports; + struct clar_report *last_report; void (*local_cleanup)(void *); void *local_cleanup_payload; @@ -158,8 +192,8 @@ struct clar_suite { /* From clar_print_*.c */ static void clar_print_init(int test_count, int suite_count, const char *suite_names); static void clar_print_shutdown(int test_count, int suite_count, int error_count); -static void clar_print_error(int num, const struct clar_error *error); -static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status failed); +static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error); +static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed); static void clar_print_onsuite(const char *suite_name, int suite_index); static void clar_print_onabort(const char *msg, ...); @@ -167,6 +201,10 @@ static void clar_print_onabort(const char *msg, ...); static void clar_unsandbox(void); static int clar_sandbox(void); +/* From summary.h */ +static struct clar_summary *clar_summary_init(const char *filename); +static int clar_summary_shutdown(struct clar_summary *fp); + /* Load the declarations for the test suite */ #include "clar.suite" @@ -189,34 +227,78 @@ void cl_trace_register(cl_trace_cb *cb, void *payload) /* Core test functions */ static void -clar_report_errors(void) +clar_report_errors(struct clar_report *report) { + struct clar_error *error; int i = 1; - struct clar_error *error, *next; - - error = _clar.errors; - while (error != NULL) { - next = error->next; - clar_print_error(i++, error); - free(error->description); - free(error); - error = next; + + for (error = report->errors; error; error = error->next) + clar_print_error(i++, _clar.last_report, error); +} + +static void +clar_report_all(void) +{ + struct clar_report *report; + struct clar_error *error; + int i = 1; + + for (report = _clar.reports; report; report = report->next) { + if (report->status != CL_TEST_FAILURE) + continue; + + for (error = report->errors; error; error = error->next) + clar_print_error(i++, report, error); } +} + +#ifdef WIN32 +# define clar_time DWORD + +static void clar_time_now(clar_time *out) +{ + *out = GetTickCount(); +} + +static double clar_time_diff(clar_time *start, clar_time *end) +{ + return ((double)*end - (double)*start) / 1000; +} +#else +# include + +# define clar_time struct timeval + +static void clar_time_now(clar_time *out) +{ + struct timezone tz; + + gettimeofday(out, &tz); +} - _clar.errors = _clar.last_error = NULL; +static double clar_time_diff(clar_time *start, clar_time *end) +{ + return ((double)end->tv_sec + (double)end->tv_usec / 1.0E6) - + ((double)start->tv_sec + (double)start->tv_usec / 1.0E6); } +#endif static void clar_run_test( + const struct clar_suite *suite, const struct clar_func *test, const struct clar_func *initialize, const struct clar_func *cleanup) { - _clar.test_status = CL_TEST_OK; + clar_time start, end; + _clar.trampoline_enabled = 1; CL_TRACE(CL_TRACE__TEST__BEGIN); + _clar.last_report->start = time(NULL); + clar_time_now(&start); + if (setjmp(_clar.trampoline) == 0) { if (initialize->ptr != NULL) initialize->ptr(); @@ -226,8 +308,15 @@ clar_run_test( CL_TRACE(CL_TRACE__TEST__RUN_END); } + clar_time_now(&end); + _clar.trampoline_enabled = 0; + if (_clar.last_report->status == CL_TEST_NOTRUN) + _clar.last_report->status = CL_TEST_OK; + + _clar.last_report->elapsed = clar_time_diff(&start, &end); + if (_clar.local_cleanup != NULL) _clar.local_cleanup(_clar.local_cleanup_payload); @@ -243,9 +332,9 @@ clar_run_test( _clar.local_cleanup_payload = NULL; if (_clar.report_errors_only) { - clar_report_errors(); + clar_report_errors(_clar.last_report); } else { - clar_print_ontest(test->name, _clar.tests_ran, _clar.test_status); + clar_print_ontest(suite->name, test->name, _clar.tests_ran, _clar.last_report->status); } } @@ -254,6 +343,8 @@ clar_run_suite(const struct clar_suite *suite, const char *filter) { const struct clar_func *test = suite->tests; size_t i, matchlen; + struct clar_report *report; + int exact = 0; if (!suite->enabled) return; @@ -278,6 +369,11 @@ clar_run_suite(const struct clar_suite *suite, const char *filter) while (*filter == ':') ++filter; matchlen = strlen(filter); + + if (matchlen && filter[matchlen - 1] == '$') { + exact = 1; + matchlen--; + } } } @@ -285,8 +381,26 @@ clar_run_suite(const struct clar_suite *suite, const char *filter) if (filter && strncmp(test[i].name, filter, matchlen)) continue; + if (exact && strlen(test[i].name) != matchlen) + continue; + _clar.active_test = test[i].name; - clar_run_test(&test[i], &suite->initialize, &suite->cleanup); + + report = calloc(1, sizeof(struct clar_report)); + report->suite = _clar.active_suite; + report->test = _clar.active_test; + report->test_number = _clar.tests_ran; + report->status = CL_TEST_NOTRUN; + + if (_clar.reports == NULL) + _clar.reports = report; + + if (_clar.last_report != NULL) + _clar.last_report->next = report; + + _clar.last_report = report; + + clar_run_test(suite, &test[i], &suite->initialize, &suite->cleanup); if (_clar.exit_on_error && _clar.total_errors) return; @@ -301,13 +415,15 @@ clar_usage(const char *arg) { printf("Usage: %s [options]\n\n", arg); printf("Options:\n"); - printf(" -sname\tRun only the suite with `name` (can go to individual test name)\n"); - printf(" -iname\tInclude the suite with `name`\n"); - printf(" -xname\tExclude the suite with `name`\n"); - printf(" -v \tIncrease verbosity (show suite names)\n"); - printf(" -q \tOnly report tests that had an error\n"); - printf(" -Q \tQuit as soon as a test fails\n"); - printf(" -l \tPrint suite names\n"); + printf(" -sname Run only the suite with `name` (can go to individual test name)\n"); + printf(" -iname Include the suite with `name`\n"); + printf(" -xname Exclude the suite with `name`\n"); + printf(" -v Increase verbosity (show suite names)\n"); + printf(" -q Only report tests that had an error\n"); + printf(" -Q Quit as soon as a test fails\n"); + printf(" -t Display results in tap format\n"); + printf(" -l Print suite names\n"); + printf(" -r[filename] Write summary file (to the optional filename)\n"); exit(-1); } @@ -316,11 +432,18 @@ clar_parse_args(int argc, char **argv) { int i; + /* Verify options before execute */ for (i = 1; i < argc; ++i) { char *argument = argv[i]; - if (argument[0] != '-') + if (argument[0] != '-' || argument[1] == '\0' + || strchr("sixvqQtlr", argument[1]) == NULL) { clar_usage(argv[0]); + } + } + + for (i = 1; i < argc; ++i) { + char *argument = argv[i]; switch (argument[1]) { case 's': @@ -352,10 +475,27 @@ clar_parse_args(int argc, char **argv) ++found; if (!exact) - _clar.report_suite_names = 1; + _clar.verbosity = MAX(_clar.verbosity, 1); switch (action) { - case 's': _clar_suites[j].enabled = 1; clar_run_suite(&_clar_suites[j], argument); break; + case 's': { + struct clar_explicit *explicit = + calloc(1, sizeof(struct clar_explicit)); + assert(explicit); + + explicit->suite_idx = j; + explicit->filter = argument; + + if (_clar.explicit == NULL) + _clar.explicit = explicit; + + if (_clar.last_explicit != NULL) + _clar.last_explicit->next = explicit; + + _clar_suites[j].enabled = 1; + _clar.last_explicit = explicit; + break; + } case 'i': _clar_suites[j].enabled = 1; break; case 'x': _clar_suites[j].enabled = 0; break; } @@ -380,6 +520,10 @@ clar_parse_args(int argc, char **argv) _clar.exit_on_error = 1; break; + case 't': + _clar.output_format = CL_OUTPUT_TAP; + break; + case 'l': { size_t j; printf("Test suites (use -s to run just one):\n"); @@ -390,11 +534,17 @@ clar_parse_args(int argc, char **argv) } case 'v': - _clar.report_suite_names = 1; + _clar.verbosity++; + break; + + case 'r': + _clar.write_summary = 1; + free(_clar.summary_filename); + _clar.summary_filename = *(argument + 2) ? strdup(argument + 2) : NULL; break; default: - clar_usage(argv[0]); + assert(!"Unexpected commandline argument!"); } } } @@ -402,29 +552,48 @@ clar_parse_args(int argc, char **argv) void clar_test_init(int argc, char **argv) { + const char *summary_env; + + if (argc > 1) + clar_parse_args(argc, argv); + clar_print_init( (int)_clar_callback_count, (int)_clar_suite_count, "" ); + if (!_clar.summary_filename && + (summary_env = getenv("CLAR_SUMMARY")) != NULL) { + _clar.write_summary = 1; + _clar.summary_filename = strdup(summary_env); + } + + if (_clar.write_summary && !_clar.summary_filename) + _clar.summary_filename = strdup("summary.xml"); + + if (_clar.write_summary && + !(_clar.summary = clar_summary_init(_clar.summary_filename))) { + clar_print_onabort("Failed to open the summary file\n"); + exit(-1); + } + if (clar_sandbox() < 0) { clar_print_onabort("Failed to sandbox the test runner.\n"); exit(-1); } - - _clar.argc = argc; - _clar.argv = argv; } int -clar_test_run() +clar_test_run(void) { - if (_clar.argc > 1) - clar_parse_args(_clar.argc, _clar.argv); + size_t i; + struct clar_explicit *explicit; - if (!_clar.suites_ran) { - size_t i; + if (_clar.explicit) { + for (explicit = _clar.explicit; explicit; explicit = explicit->next) + clar_run_suite(&_clar_suites[explicit->suite_idx], explicit->filter); + } else { for (i = 0; i < _clar_suite_count; ++i) clar_run_suite(&_clar_suites[i], NULL); } @@ -433,8 +602,11 @@ clar_test_run() } void -clar_test_shutdown() +clar_test_shutdown(void) { + struct clar_explicit *explicit, *explicit_next; + struct clar_report *report, *report_next; + clar_print_shutdown( _clar.tests_ran, (int)_clar_suite_count, @@ -442,6 +614,23 @@ clar_test_shutdown() ); clar_unsandbox(); + + if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0) { + clar_print_onabort("Failed to write the summary file\n"); + exit(-1); + } + + for (explicit = _clar.explicit; explicit; explicit = explicit_next) { + explicit_next = explicit->next; + free(explicit); + } + + for (report = _clar.reports; report; report = report_next) { + report_next = report->next; + free(report); + } + + free(_clar.summary_filename); } int @@ -461,7 +650,7 @@ static void abort_test(void) if (!_clar.trampoline_enabled) { clar_print_onabort( "Fatal error: a cleanup method raised an exception."); - clar_report_errors(); + clar_report_errors(_clar.last_report); exit(-1); } @@ -471,32 +660,31 @@ static void abort_test(void) void clar__skip(void) { - _clar.test_status = CL_TEST_SKIP; + _clar.last_report->status = CL_TEST_SKIP; _clar.total_skipped++; abort_test(); } void clar__fail( const char *file, - int line, + const char *function, + size_t line, const char *error_msg, const char *description, int should_abort) { struct clar_error *error = calloc(1, sizeof(struct clar_error)); - if (_clar.errors == NULL) - _clar.errors = error; + if (_clar.last_report->errors == NULL) + _clar.last_report->errors = error; - if (_clar.last_error != NULL) - _clar.last_error->next = error; + if (_clar.last_report->last_error != NULL) + _clar.last_report->last_error->next = error; - _clar.last_error = error; + _clar.last_report->last_error = error; - error->test = _clar.active_test; - error->test_number = _clar.tests_ran; - error->suite = _clar.active_suite; error->file = file; + error->function = function; error->line_number = line; error->error_msg = error_msg; @@ -504,7 +692,7 @@ void clar__fail( error->description = strdup(description); _clar.total_errors++; - _clar.test_status = CL_TEST_FAILURE; + _clar.last_report->status = CL_TEST_FAILURE; if (should_abort) abort_test(); @@ -513,7 +701,8 @@ void clar__fail( void clar__assert( int condition, const char *file, - int line, + const char *function, + size_t line, const char *error_msg, const char *description, int should_abort) @@ -521,12 +710,13 @@ void clar__assert( if (condition) return; - clar__fail(file, line, error_msg, description, should_abort); + clar__fail(file, function, line, error_msg, description, should_abort); } void clar__assert_equal( const char *file, - int line, + const char *function, + size_t line, const char *err, int should_abort, const char *fmt, @@ -636,7 +826,7 @@ void clar__assert_equal( va_end(args); if (!is_equal) - clar__fail(file, line, err, buf, should_abort); + clar__fail(file, function, line, err, buf, should_abort); } void cl_set_cleanup(void (*cleanup)(void *), void *opaque) @@ -649,3 +839,4 @@ void cl_set_cleanup(void (*cleanup)(void *), void *opaque) #include "clar/fixtures.h" #include "clar/fs.h" #include "clar/print.h" +#include "clar/summary.h" diff --git a/tests/clar.h b/tests/clar.h index 5c674d7..8c22382 100644 --- a/tests/clar.h +++ b/tests/clar.h @@ -12,13 +12,21 @@ enum cl_test_status { CL_TEST_OK, CL_TEST_FAILURE, - CL_TEST_SKIP + CL_TEST_SKIP, + CL_TEST_NOTRUN, }; +enum cl_output_format { + CL_OUTPUT_CLAP, + CL_OUTPUT_TAP, +}; + +/** Setup clar environment */ void clar_test_init(int argc, char *argv[]); int clar_test_run(void); void clar_test_shutdown(void); +/** One shot setup & run */ int clar_test(int argc, char *argv[]); const char *clar_sandbox_path(void); @@ -72,21 +80,22 @@ void cl_trace_register(cl_trace_cb *cb, void *payload); const char *cl_fixture(const char *fixture_name); void cl_fixture_sandbox(const char *fixture_name); void cl_fixture_cleanup(const char *fixture_name); +const char *cl_fixture_basename(const char *fixture_name); #endif /** * Assertion macros with explicit error message */ -#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __LINE__, "Function call failed: " #expr, desc, 1) -#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __LINE__, "Expected function call to fail: " #expr, desc, 1) -#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __LINE__, "Expression is not true: " #expr, desc, 1) +#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 1) +#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 1) +#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 1) /** * Check macros with explicit error message */ -#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __LINE__, "Function call failed: " #expr, desc, 0) -#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __LINE__, "Expected function call to fail: " #expr, desc, 0) -#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __LINE__, "Expression is not true: " #expr, desc, 0) +#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 0) +#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 0) +#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 0) /** * Assertion macros with no error message @@ -105,39 +114,40 @@ void cl_fixture_cleanup(const char *fixture_name); /** * Forced failure/warning */ -#define cl_fail(desc) clar__fail(__FILE__, __LINE__, "Test failed.", desc, 1) -#define cl_warning(desc) clar__fail(__FILE__, __LINE__, "Warning during test execution:", desc, 0) +#define cl_fail(desc) clar__fail(__FILE__, __func__, __LINE__, "Test failed.", desc, 1) +#define cl_warning(desc) clar__fail(__FILE__, __func__, __LINE__, "Warning during test execution:", desc, 0) #define cl_skip() clar__skip() /** * Typed assertion macros */ -#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2)) -#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2)) +#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2)) +#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2)) -#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2)) -#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2)) +#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2)) +#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2)) -#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len)) -#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len)) +#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len)) +#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len)) -#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len)) -#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len)) +#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len)) +#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len)) -#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) -#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) -#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) +#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) +#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) +#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) -#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0)) +#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__func__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0)) -#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) +#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__func__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) void clar__skip(void); void clar__fail( const char *file, - int line, + const char *func, + size_t line, const char *error, const char *description, int should_abort); @@ -145,14 +155,16 @@ void clar__fail( void clar__assert( int condition, const char *file, - int line, + const char *func, + size_t line, const char *error, const char *description, int should_abort); void clar__assert_equal( const char *file, - int line, + const char *func, + size_t line, const char *err, int should_abort, const char *fmt, diff --git a/tests/clar/fixtures.h b/tests/clar/fixtures.h index 1955afe..6ec6423 100644 --- a/tests/clar/fixtures.h +++ b/tests/clar/fixtures.h @@ -1,3 +1,4 @@ +#ifdef CLAR_FIXTURE_PATH static const char * fixture_path(const char *base, const char *fixture_name) { @@ -20,9 +21,17 @@ fixture_path(const char *base, const char *fixture_name) return _path; } -__attribute__((unused)) -static const char * -fixture_basename(const char *fixture_name) +const char *cl_fixture(const char *fixture_name) +{ + return fixture_path(CLAR_FIXTURE_PATH, fixture_name); +} + +void cl_fixture_sandbox(const char *fixture_name) +{ + fs_copy(cl_fixture(fixture_name), _clar_path); +} + +const char *cl_fixture_basename(const char *fixture_name) { const char *p; @@ -34,19 +43,8 @@ fixture_basename(const char *fixture_name) return fixture_name; } -#ifdef CLAR_FIXTURE_PATH -const char *cl_fixture(const char *fixture_name) -{ - return fixture_path(CLAR_FIXTURE_PATH, fixture_name); -} - -void cl_fixture_sandbox(const char *fixture_name) -{ - fs_copy(cl_fixture(fixture_name), _clar_path); -} - void cl_fixture_cleanup(const char *fixture_name) { - fs_rm(fixture_path(_clar_path, fixture_basename(fixture_name))); + fs_rm(fixture_path(_clar_path, cl_fixture_basename(fixture_name))); } #endif diff --git a/tests/clar/fs.h b/tests/clar/fs.h index 7c7dde6..3e39890 100644 --- a/tests/clar/fs.h +++ b/tests/clar/fs.h @@ -1,5 +1,19 @@ +/* + * By default, use a read/write loop to copy files on POSIX systems. + * On Linux, use sendfile by default as it's slightly faster. On + * macOS, we avoid fcopyfile by default because it's slightly slower. + */ +#undef USE_FCOPYFILE +#define USE_SENDFILE 1 + #ifdef _WIN32 +#ifdef CLAR_WIN32_LONGPATHS +# define CLAR_MAX_PATH 4096 +#else +# define CLAR_MAX_PATH MAX_PATH +#endif + #define RM_RETRY_COUNT 5 #define RM_RETRY_DELAY 10 @@ -40,21 +54,48 @@ fs_rmdir_rmdir(WCHAR *_wpath) return 0; } +static void translate_path(WCHAR *path, size_t path_size) +{ + size_t path_len, i; + + if (wcsncmp(path, L"\\\\?\\", 4) == 0) + return; + + path_len = wcslen(path); + cl_assert(path_size > path_len + 4); + + for (i = path_len; i > 0; i--) { + WCHAR c = path[i - 1]; + + if (c == L'/') + path[i + 3] = L'\\'; + else + path[i + 3] = path[i - 1]; + } + + path[0] = L'\\'; + path[1] = L'\\'; + path[2] = L'?'; + path[3] = L'\\'; + path[path_len + 4] = L'\0'; +} + static void fs_rmdir_helper(WCHAR *_wsource) { - WCHAR buffer[MAX_PATH]; + WCHAR buffer[CLAR_MAX_PATH]; HANDLE find_handle; WIN32_FIND_DATAW find_data; size_t buffer_prefix_len; /* Set up the buffer and capture the length */ - wcscpy_s(buffer, MAX_PATH, _wsource); - wcscat_s(buffer, MAX_PATH, L"\\"); + wcscpy_s(buffer, CLAR_MAX_PATH, _wsource); + translate_path(buffer, CLAR_MAX_PATH); + wcscat_s(buffer, CLAR_MAX_PATH, L"\\"); buffer_prefix_len = wcslen(buffer); /* FindFirstFile needs a wildcard to match multiple items */ - wcscat_s(buffer, MAX_PATH, L"*"); + wcscat_s(buffer, CLAR_MAX_PATH, L"*"); find_handle = FindFirstFileW(buffer, &find_data); cl_assert(INVALID_HANDLE_VALUE != find_handle); @@ -64,7 +105,7 @@ fs_rmdir_helper(WCHAR *_wsource) if (fs__dotordotdot(find_data.cFileName)) continue; - wcscpy_s(buffer + buffer_prefix_len, MAX_PATH - buffer_prefix_len, find_data.cFileName); + wcscpy_s(buffer + buffer_prefix_len, CLAR_MAX_PATH - buffer_prefix_len, find_data.cFileName); if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) fs_rmdir_helper(buffer); @@ -105,7 +146,7 @@ fs_rm_wait(WCHAR *_wpath) ERROR_PATH_NOT_FOUND == last_error) return 0; - Sleep(RM_RETRY_DELAY * retries * retries); + Sleep(RM_RETRY_DELAY * retries * retries); } while (retries++ <= RM_RETRY_COUNT); @@ -115,7 +156,7 @@ fs_rm_wait(WCHAR *_wpath) static void fs_rm(const char *_source) { - WCHAR wsource[MAX_PATH]; + WCHAR wsource[CLAR_MAX_PATH]; DWORD attrs; /* The input path is UTF-8. Convert it to wide characters @@ -125,7 +166,9 @@ fs_rm(const char *_source) _source, -1, /* Indicates NULL termination */ wsource, - MAX_PATH)); + CLAR_MAX_PATH)); + + translate_path(wsource, CLAR_MAX_PATH); /* Does the item exist? If not, we have no work to do */ attrs = GetFileAttributesW(wsource); @@ -150,21 +193,23 @@ fs_rm(const char *_source) static void fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest) { - WCHAR buf_source[MAX_PATH], buf_dest[MAX_PATH]; + WCHAR buf_source[CLAR_MAX_PATH], buf_dest[CLAR_MAX_PATH]; HANDLE find_handle; WIN32_FIND_DATAW find_data; size_t buf_source_prefix_len, buf_dest_prefix_len; - wcscpy_s(buf_source, MAX_PATH, _wsource); - wcscat_s(buf_source, MAX_PATH, L"\\"); + wcscpy_s(buf_source, CLAR_MAX_PATH, _wsource); + wcscat_s(buf_source, CLAR_MAX_PATH, L"\\"); + translate_path(buf_source, CLAR_MAX_PATH); buf_source_prefix_len = wcslen(buf_source); - wcscpy_s(buf_dest, MAX_PATH, _wdest); - wcscat_s(buf_dest, MAX_PATH, L"\\"); + wcscpy_s(buf_dest, CLAR_MAX_PATH, _wdest); + wcscat_s(buf_dest, CLAR_MAX_PATH, L"\\"); + translate_path(buf_dest, CLAR_MAX_PATH); buf_dest_prefix_len = wcslen(buf_dest); /* Get an enumerator for the items in the source. */ - wcscat_s(buf_source, MAX_PATH, L"*"); + wcscat_s(buf_source, CLAR_MAX_PATH, L"*"); find_handle = FindFirstFileW(buf_source, &find_data); cl_assert(INVALID_HANDLE_VALUE != find_handle); @@ -177,8 +222,8 @@ fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest) if (fs__dotordotdot(find_data.cFileName)) continue; - wcscpy_s(buf_source + buf_source_prefix_len, MAX_PATH - buf_source_prefix_len, find_data.cFileName); - wcscpy_s(buf_dest + buf_dest_prefix_len, MAX_PATH - buf_dest_prefix_len, find_data.cFileName); + wcscpy_s(buf_source + buf_source_prefix_len, CLAR_MAX_PATH - buf_source_prefix_len, find_data.cFileName); + wcscpy_s(buf_dest + buf_dest_prefix_len, CLAR_MAX_PATH - buf_dest_prefix_len, find_data.cFileName); if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes) fs_copydir_helper(buf_source, buf_dest); @@ -197,7 +242,7 @@ fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest) static void fs_copy(const char *_source, const char *_dest) { - WCHAR wsource[MAX_PATH], wdest[MAX_PATH]; + WCHAR wsource[CLAR_MAX_PATH], wdest[CLAR_MAX_PATH]; DWORD source_attrs, dest_attrs; HANDLE find_handle; WIN32_FIND_DATAW find_data; @@ -209,14 +254,17 @@ fs_copy(const char *_source, const char *_dest) _source, -1, wsource, - MAX_PATH)); + CLAR_MAX_PATH)); cl_assert(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, _dest, -1, wdest, - MAX_PATH)); + CLAR_MAX_PATH)); + + translate_path(wsource, CLAR_MAX_PATH); + translate_path(wdest, CLAR_MAX_PATH); /* Check the source for existence */ source_attrs = GetFileAttributesW(wsource); @@ -230,8 +278,8 @@ fs_copy(const char *_source, const char *_dest) * Use FindFirstFile to parse the path */ find_handle = FindFirstFileW(wsource, &find_data); cl_assert(INVALID_HANDLE_VALUE != find_handle); - wcscat_s(wdest, MAX_PATH, L"\\"); - wcscat_s(wdest, MAX_PATH, find_data.cFileName); + wcscat_s(wdest, CLAR_MAX_PATH, L"\\"); + wcscat_s(wdest, CLAR_MAX_PATH, find_data.cFileName); FindClose(find_handle); /* Check the new target for existence */ @@ -247,81 +295,222 @@ fs_copy(const char *_source, const char *_dest) void cl_fs_cleanup(void) { +#ifdef CLAR_FIXTURE_PATH fs_rm(fixture_path(_clar_path, "*")); +#endif } #else #include #include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) +# include +#endif -static int -shell_out(char * const argv[]) +#if defined(__APPLE__) +# include +#endif + +static void basename_r(const char **out, int *out_len, const char *in) { - int status, piderr; - pid_t pid; + size_t in_len = strlen(in), start_pos; - pid = fork(); + for (in_len = strlen(in); in_len; in_len--) { + if (in[in_len - 1] != '/') + break; + } - if (pid < 0) { - fprintf(stderr, - "System error: `fork()` call failed (%d) - %s\n", - errno, strerror(errno)); - exit(-1); + for (start_pos = in_len; start_pos; start_pos--) { + if (in[start_pos - 1] == '/') + break; } - if (pid == 0) { - execv(argv[0], argv); + cl_assert(in_len - start_pos < INT_MAX); + + if (in_len - start_pos > 0) { + *out = &in[start_pos]; + *out_len = (in_len - start_pos); + } else { + *out = "/"; + *out_len = 1; } +} - do { - piderr = waitpid(pid, &status, WUNTRACED); - } while (piderr < 0 && (errno == EAGAIN || errno == EINTR)); +static char *joinpath(const char *dir, const char *base, int base_len) +{ + char *out; + int len; + + if (base_len == -1) { + size_t bl = strlen(base); + + cl_assert(bl < INT_MAX); + base_len = (int)bl; + } + + len = strlen(dir) + base_len + 2; + cl_assert(len > 0); + + cl_assert(out = malloc(len)); + cl_assert(snprintf(out, len, "%s/%.*s", dir, base_len, base) < len); - return WEXITSTATUS(status); + return out; } static void -fs_copy(const char *_source, const char *dest) +fs_copydir_helper(const char *source, const char *dest, int dest_mode) { - char *argv[5]; - char *source; - size_t source_len; + DIR *source_dir; + struct dirent *d; - source = strdup(_source); - source_len = strlen(source); + mkdir(dest, dest_mode); - if (source[source_len - 1] == '/') - source[source_len - 1] = 0; + cl_assert_(source_dir = opendir(source), "Could not open source dir"); + while ((d = (errno = 0, readdir(source_dir))) != NULL) { + char *child; - argv[0] = "/bin/cp"; - argv[1] = "-R"; - argv[2] = source; - argv[3] = (char *)dest; - argv[4] = NULL; + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + child = joinpath(source, d->d_name, -1); + fs_copy(child, dest); + free(child); + } - cl_must_pass_( - shell_out(argv), - "Failed to copy test fixtures to sandbox" - ); + cl_assert_(errno == 0, "Failed to iterate source dir"); - free(source); + closedir(source_dir); } static void -fs_rm(const char *source) +fs_copyfile_helper(const char *source, size_t source_len, const char *dest, int dest_mode) { - char *argv[4]; + int in, out; + + cl_must_pass((in = open(source, O_RDONLY))); + cl_must_pass((out = open(dest, O_WRONLY|O_CREAT|O_TRUNC, dest_mode))); + +#if USE_FCOPYFILE && defined(__APPLE__) + ((void)(source_len)); /* unused */ + cl_must_pass(fcopyfile(in, out, 0, COPYFILE_DATA)); +#elif USE_SENDFILE && defined(__linux__) + { + ssize_t ret = 0; + + while (source_len && (ret = sendfile(out, in, NULL, source_len)) > 0) { + source_len -= (size_t)ret; + } + cl_assert(ret >= 0); + } +#else + { + char buf[131072]; + ssize_t ret; - argv[0] = "/bin/rm"; - argv[1] = "-Rf"; - argv[2] = (char *)source; - argv[3] = NULL; + ((void)(source_len)); /* unused */ - cl_must_pass_( - shell_out(argv), - "Failed to cleanup the sandbox" - ); + while ((ret = read(in, buf, sizeof(buf))) > 0) { + size_t len = (size_t)ret; + + while (len && (ret = write(out, buf, len)) > 0) { + cl_assert(ret <= (ssize_t)len); + len -= ret; + } + cl_assert(ret >= 0); + } + cl_assert(ret == 0); + } +#endif + + close(in); + close(out); +} + +static void +fs_copy(const char *source, const char *_dest) +{ + char *dbuf = NULL; + const char *dest = NULL; + struct stat source_st, dest_st; + + cl_must_pass_(lstat(source, &source_st), "Failed to stat copy source"); + + if (lstat(_dest, &dest_st) == 0) { + const char *base; + int base_len; + + /* Target exists and is directory; append basename */ + cl_assert(S_ISDIR(dest_st.st_mode)); + + basename_r(&base, &base_len, source); + cl_assert(base_len < INT_MAX); + + dbuf = joinpath(_dest, base, base_len); + dest = dbuf; + } else if (errno != ENOENT) { + cl_fail("Cannot copy; cannot stat destination"); + } else { + dest = _dest; + } + + if (S_ISDIR(source_st.st_mode)) { + fs_copydir_helper(source, dest, source_st.st_mode); + } else { + fs_copyfile_helper(source, source_st.st_size, dest, source_st.st_mode); + } + + free(dbuf); +} + +static void +fs_rmdir_helper(const char *path) +{ + DIR *dir; + struct dirent *d; + + cl_assert_(dir = opendir(path), "Could not open dir"); + while ((d = (errno = 0, readdir(dir))) != NULL) { + char *child; + + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + child = joinpath(path, d->d_name, -1); + fs_rm(child); + free(child); + } + + cl_assert_(errno == 0, "Failed to iterate source dir"); + closedir(dir); + + cl_must_pass_(rmdir(path), "Could not remove directory"); +} + +static void +fs_rm(const char *path) +{ + struct stat st; + + if (lstat(path, &st)) { + if (errno == ENOENT) + return; + + cl_fail("Cannot copy; cannot stat destination"); + } + + if (S_ISDIR(st.st_mode)) { + fs_rmdir_helper(path); + } else { + cl_must_pass(unlink(path)); + } } void diff --git a/tests/clar/print.h b/tests/clar/print.h index 916d807..c17e2f6 100644 --- a/tests/clar/print.h +++ b/tests/clar/print.h @@ -1,28 +1,29 @@ +/* clap: clar protocol, the traditional clar output format */ -static void clar_print_init(int test_count, int suite_count, const char *suite_names) +static void clar_print_clap_init(int test_count, int suite_count, const char *suite_names) { (void)test_count; printf("Loaded %d suites: %s\n", (int)suite_count, suite_names); printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n"); } -static void clar_print_shutdown(int test_count, int suite_count, int error_count) +static void clar_print_clap_shutdown(int test_count, int suite_count, int error_count) { (void)test_count; (void)suite_count; (void)error_count; printf("\n\n"); - clar_report_errors(); + clar_report_all(); } -static void clar_print_error(int num, const struct clar_error *error) +static void clar_print_clap_error(int num, const struct clar_report *report, const struct clar_error *error) { printf(" %d) Failure:\n", num); - printf("%s::%s [%s:%d]\n", - error->suite, - error->test, + printf("%s::%s [%s:%"PRIuZ"]\n", + report->suite, + report->test, error->file, error->line_number); @@ -35,32 +36,176 @@ static void clar_print_error(int num, const struct clar_error *error) fflush(stdout); } -static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status status) +static void clar_print_clap_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status) +{ + (void)test_name; + (void)test_number; + + if (_clar.verbosity > 1) { + printf("%s::%s: ", suite_name, test_name); + + switch (status) { + case CL_TEST_OK: printf("ok\n"); break; + case CL_TEST_FAILURE: printf("fail\n"); break; + case CL_TEST_SKIP: printf("skipped"); break; + case CL_TEST_NOTRUN: printf("notrun"); break; + } + } else { + switch (status) { + case CL_TEST_OK: printf("."); break; + case CL_TEST_FAILURE: printf("F"); break; + case CL_TEST_SKIP: printf("S"); break; + case CL_TEST_NOTRUN: printf("N"); break; + } + + fflush(stdout); + } +} + +static void clar_print_clap_onsuite(const char *suite_name, int suite_index) +{ + if (_clar.verbosity == 1) + printf("\n%s", suite_name); + + (void)suite_index; +} + +static void clar_print_clap_onabort(const char *fmt, va_list arg) +{ + vfprintf(stderr, fmt, arg); +} + +/* tap: test anywhere protocol format */ + +static void clar_print_tap_init(int test_count, int suite_count, const char *suite_names) +{ + (void)test_count; + (void)suite_count; + (void)suite_names; + printf("TAP version 13\n"); +} + +static void clar_print_tap_shutdown(int test_count, int suite_count, int error_count) +{ + (void)suite_count; + (void)error_count; + + printf("1..%d\n", test_count); +} + +static void clar_print_tap_error(int num, const struct clar_report *report, const struct clar_error *error) +{ + (void)num; + (void)report; + (void)error; +} + +static void print_escaped(const char *str) +{ + char *c; + + while ((c = strchr(str, '\'')) != NULL) { + printf("%.*s", (int)(c - str), str); + printf("''"); + str = c + 1; + } + + printf("%s", str); +} + +static void clar_print_tap_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status) { + const struct clar_error *error = _clar.last_report->errors; + (void)test_name; (void)test_number; switch(status) { - case CL_TEST_OK: printf("."); break; - case CL_TEST_FAILURE: printf("F"); break; - case CL_TEST_SKIP: printf("S"); break; + case CL_TEST_OK: + printf("ok %d - %s::%s\n", test_number, suite_name, test_name); + break; + case CL_TEST_FAILURE: + printf("not ok %d - %s::%s\n", test_number, suite_name, test_name); + + printf(" ---\n"); + printf(" reason: |\n"); + printf(" %s\n", error->error_msg); + + if (error->description) + printf(" %s\n", error->description); + + printf(" at:\n"); + printf(" file: '"); print_escaped(error->file); printf("'\n"); + printf(" line: %" PRIuZ "\n", error->line_number); + printf(" function: '%s'\n", error->function); + printf(" ---\n"); + + break; + case CL_TEST_SKIP: + case CL_TEST_NOTRUN: + printf("ok %d - # SKIP %s::%s\n", test_number, suite_name, test_name); + break; } fflush(stdout); } -static void clar_print_onsuite(const char *suite_name, int suite_index) +static void clar_print_tap_onsuite(const char *suite_name, int suite_index) { - if (_clar.report_suite_names) - printf("\n%s", suite_name); + printf("# start of suite %d: %s\n", suite_index, suite_name); +} - (void)suite_index; +static void clar_print_tap_onabort(const char *fmt, va_list arg) +{ + printf("Bail out! "); + vprintf(fmt, arg); + fflush(stdout); +} + +/* indirection between protocol output selection */ + +#define PRINT(FN, ...) do { \ + switch (_clar.output_format) { \ + case CL_OUTPUT_CLAP: \ + clar_print_clap_##FN (__VA_ARGS__); \ + break; \ + case CL_OUTPUT_TAP: \ + clar_print_tap_##FN (__VA_ARGS__); \ + break; \ + default: \ + abort(); \ + } \ + } while (0) + +static void clar_print_init(int test_count, int suite_count, const char *suite_names) +{ + PRINT(init, test_count, suite_count, suite_names); +} + +static void clar_print_shutdown(int test_count, int suite_count, int error_count) +{ + PRINT(shutdown, test_count, suite_count, error_count); +} + +static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error) +{ + PRINT(error, num, report, error); +} + +static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status) +{ + PRINT(ontest, suite_name, test_name, test_number, status); +} + +static void clar_print_onsuite(const char *suite_name, int suite_index) +{ + PRINT(onsuite, suite_name, suite_index); } static void clar_print_onabort(const char *msg, ...) { va_list argp; va_start(argp, msg); - vfprintf(stderr, msg, argp); + PRINT(onabort, msg, argp); va_end(argp); } diff --git a/tests/clar/sandbox.h b/tests/clar/sandbox.h index 4b83bf3..0ba1479 100644 --- a/tests/clar/sandbox.h +++ b/tests/clar/sandbox.h @@ -1,4 +1,8 @@ -static char _clar_path[4096]; +#ifdef __APPLE__ +#include +#endif + +static char _clar_path[4096 + 1]; static int is_valid_tmp_path(const char *path) @@ -31,14 +35,24 @@ find_tmp_path(char *buffer, size_t length) continue; if (is_valid_tmp_path(env)) { - strncpy(buffer, env, length); +#ifdef __APPLE__ + if (length >= PATH_MAX && realpath(env, buffer) != NULL) + return 0; +#endif + strncpy(buffer, env, length - 1); + buffer[length - 1] = '\0'; return 0; } } /* If the environment doesn't say anything, try to use /tmp */ if (is_valid_tmp_path("/tmp")) { - strncpy(buffer, "/tmp", length); +#ifdef __APPLE__ + if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL) + return 0; +#endif + strncpy(buffer, "/tmp", length - 1); + buffer[length - 1] = '\0'; return 0; } @@ -53,7 +67,8 @@ find_tmp_path(char *buffer, size_t length) /* This system doesn't like us, try to use the current directory */ if (is_valid_tmp_path(".")) { - strncpy(buffer, ".", length); + strncpy(buffer, ".", length - 1); + buffer[length - 1] = '\0'; return 0; } diff --git a/tests/clar/summary.h b/tests/clar/summary.h new file mode 100644 index 0000000..4dd352e --- /dev/null +++ b/tests/clar/summary.h @@ -0,0 +1,143 @@ + +#include +#include + +static int clar_summary_close_tag( + struct clar_summary *summary, const char *tag, int indent) +{ + const char *indt; + + if (indent == 0) indt = ""; + else if (indent == 1) indt = "\t"; + else indt = "\t\t"; + + return fprintf(summary->fp, "%s\n", indt, tag); +} + +static int clar_summary_testsuites(struct clar_summary *summary) +{ + return fprintf(summary->fp, "\n"); +} + +static int clar_summary_testsuite(struct clar_summary *summary, + int idn, const char *name, time_t timestamp, + int test_count, int fail_count, int error_count) +{ + struct tm *tm = localtime(×tamp); + char iso_dt[20]; + + if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", tm) == 0) + return -1; + + return fprintf(summary->fp, "\t\n", + idn, name, iso_dt, test_count, fail_count, error_count); +} + +static int clar_summary_testcase(struct clar_summary *summary, + const char *name, const char *classname, double elapsed) +{ + return fprintf(summary->fp, + "\t\t\n", + name, classname, elapsed); +} + +static int clar_summary_failure(struct clar_summary *summary, + const char *type, const char *message, const char *desc) +{ + return fprintf(summary->fp, + "\t\t\t\n", + type, message, desc); +} + +static int clar_summary_skipped(struct clar_summary *summary) +{ + return fprintf(summary->fp, "\t\t\t\n"); +} + +struct clar_summary *clar_summary_init(const char *filename) +{ + struct clar_summary *summary; + FILE *fp; + + if ((fp = fopen(filename, "w")) == NULL) { + perror("fopen"); + return NULL; + } + + if ((summary = malloc(sizeof(struct clar_summary))) == NULL) { + perror("malloc"); + fclose(fp); + return NULL; + } + + summary->filename = filename; + summary->fp = fp; + + return summary; +} + +int clar_summary_shutdown(struct clar_summary *summary) +{ + struct clar_report *report; + const char *last_suite = NULL; + + if (clar_summary_testsuites(summary) < 0) + goto on_error; + + report = _clar.reports; + while (report != NULL) { + struct clar_error *error = report->errors; + + if (last_suite == NULL || strcmp(last_suite, report->suite) != 0) { + if (clar_summary_testsuite(summary, 0, report->suite, + report->start, _clar.tests_ran, _clar.total_errors, 0) < 0) + goto on_error; + } + + last_suite = report->suite; + + clar_summary_testcase(summary, report->test, report->suite, report->elapsed); + + while (error != NULL) { + if (clar_summary_failure(summary, "assert", + error->error_msg, error->description) < 0) + goto on_error; + + error = error->next; + } + + if (report->status == CL_TEST_SKIP) + clar_summary_skipped(summary); + + if (clar_summary_close_tag(summary, "testcase", 2) < 0) + goto on_error; + + report = report->next; + + if (!report || strcmp(last_suite, report->suite) != 0) { + if (clar_summary_close_tag(summary, "testsuite", 1) < 0) + goto on_error; + } + } + + if (clar_summary_close_tag(summary, "testsuites", 0) < 0 || + fclose(summary->fp) != 0) + goto on_error; + + printf("written summary file to %s\n", summary->filename); + + free(summary); + return 0; + +on_error: + fclose(summary->fp); + free(summary); + return -1; +} diff --git a/tests/ntlm_tests.h b/tests/ntlm_tests.h index 6344bc4..42c770a 100644 --- a/tests/ntlm_tests.h +++ b/tests/ntlm_tests.h @@ -5,26 +5,27 @@ #include "ntlm.h" #include "util.h" -#define cl_must_fail_with_(val, expr, desc) clar__assert((expr) == (val), __FILE__, __LINE__, "Expected function call to fail with " #val ": " #expr, desc, 0) +#define cl_must_fail_with_(val, expr, desc) clar__assert((expr) == (val), __FILE__, __func__, __LINE__, "Expected function call to fail with " #val ": " #expr, desc, 0) #define cl_must_fail_with(val, expr) cl_must_fail_with_(val, expr, NULL) -#define cl_ntlm_pass(ntlm, expr) cl_ntlm_expect((ntlm), (expr), 0, __FILE__, __LINE__) +#define cl_ntlm_pass(ntlm, expr) cl_ntlm_expect((ntlm), (expr), 0, __FILE__, __func__, __LINE__) -#define cl_ntlm_expect(ntlm, expr, expected, file, line) do { \ +#define cl_ntlm_expect(ntlm, expr, expected, file, func, line) do { \ int _ntlm_error; \ if ((_ntlm_error = (expr)) != expected) \ - cl_ntlm_report_failure(ntlm, file, line, "Function call failed: " #expr); \ + cl_ntlm_report_failure(ntlm, file, func, line, "Function call failed: " #expr); \ } while (0) __attribute__((unused)) static void cl_ntlm_report_failure( ntlm_client *ntlm, const char *file, + const char *func, int line, const char *message) { - clar__fail(file, line, message, ntlm_client_errmsg(ntlm), 1); + clar__fail(file, func, line, message, ntlm_client_errmsg(ntlm), 1); } #endif /* PRIVATE_TESTS_NTLM_H__ */ From 2ae5dd9b6901051c8d75909cbd0f4bdc4a149669 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 14 Dec 2023 12:49:57 +0000 Subject: [PATCH 2/7] cli: update to latest version of adopt --- cli/ntlm_cli.c | 70 +++--- cli/ntlm_cli_opt.c | 576 +++++++++++++++++++++++++++++++++++++++------ cli/ntlm_cli_opt.h | 230 ++++++++++++++---- 3 files changed, 711 insertions(+), 165 deletions(-) diff --git a/cli/ntlm_cli.c b/cli/ntlm_cli.c index b492b49..7e73e00 100644 --- a/cli/ntlm_cli.c +++ b/cli/ntlm_cli.c @@ -89,51 +89,41 @@ int main(int argc, char **argv) ntlm_opt opt; ntlm_opt_spec opt_specs[] = { - { NTLM_OPT_SWITCH, "negotiate", 'n', &type, NTLM_CLI_NEGOTIATE, - NULL, "produce a negotiate package", - NTLM_OPT_USAGE_SHOW_LONG }, - { NTLM_OPT_SWITCH, "response", 'r', &type, NTLM_CLI_RESPONSE, - NULL, "produce a response to a challenge (required)", - NTLM_OPT_USAGE_CHOICE | NTLM_OPT_USAGE_SHOW_LONG }, - - { NTLM_OPT_VALUE, "target", 't', &target, 0, - "target", "sets the target (the remote hostname)", - 0 }, - { NTLM_OPT_VALUE, "username", 'u', &username, 0, - "username", "sets the username for authentication", - 0 }, - { NTLM_OPT_VALUE, "domain", 'd', &domain, 0, - "domain", "sets the user's domain", - 0 }, - { NTLM_OPT_VALUE, "password", 'p', &password, 0, - "password", "sets the user's password", - 0 }, - - { NTLM_OPT_BOOL, "enable-lm", 0, &enable_lm, 0, - NULL, "enable LM authentication", - 0 }, - { NTLM_OPT_BOOL, "enable-ntlm", 0, &enable_ntlm, 0, - NULL, "enable NTLM authentication", - 0 }, - { NTLM_OPT_BOOL, "disable-ntlm2", 0, &disable_ntlm2, 0, - NULL, "disable NTLM2 authentication", - 0 }, - - { NTLM_OPT_BOOL, "raw", 0, &raw, 0, - NULL, "read and write raw binary (instead of base64)", - 0 }, - { NTLM_OPT_SWITCH, "help", 0, &help, 0, - NULL, "display help", - 0 }, - - { NTLM_OPT_NONE, NULL, 0, NULL, 0, NULL, NULL, 0 } + { NTLM_OPT_TYPE_SWITCH, "negotiate", 'n', &type, NTLM_CLI_NEGOTIATE, + NTLM_OPT_USAGE_SHOW_LONG, NULL, "produce a negotiate package" }, + { NTLM_OPT_TYPE_SWITCH, "response", 'r', &type, NTLM_CLI_RESPONSE, + NTLM_OPT_USAGE_CHOICE | NTLM_OPT_USAGE_SHOW_LONG, + NULL, "produce a response to a challenge (required)" }, + + { NTLM_OPT_TYPE_VALUE, "target", 't', &target, 0, + 0, "target", "sets the target (the remote hostname)" }, + { NTLM_OPT_TYPE_VALUE, "username", 'u', &username, 0, + 0, "username", "sets the username for authentication" }, + { NTLM_OPT_TYPE_VALUE, "domain", 'd', &domain, 0, + 0, "domain", "sets the user's domain" }, + { NTLM_OPT_TYPE_VALUE, "password", 'p', &password, 0, + 0, "password", "sets the user's password" }, + + { NTLM_OPT_TYPE_BOOL, "enable-lm", 0, &enable_lm, 0, + 0, NULL, "enable LM authentication" }, + { NTLM_OPT_TYPE_BOOL, "enable-ntlm", 0, &enable_ntlm, 0, + 0, NULL, "enable NTLM authentication" }, + { NTLM_OPT_TYPE_BOOL, "disable-ntlm2", 0, &disable_ntlm2, 0, + 0, NULL, "disable NTLM2 authentication" }, + + { NTLM_OPT_TYPE_BOOL, "raw", 0, &raw, 0, + 0, NULL, "read and write raw binary (instead of base64)" }, + { NTLM_OPT_TYPE_SWITCH, "help", 0, &help, 0, + 0, NULL, "display help" }, + + { NTLM_OPT_TYPE_NONE, NULL, 0, NULL, 0, 0, NULL, NULL } }; - ntlm_opt_parser_init(&opt_parser, opt_specs, argv + 1, argc - 1); + ntlm_opt_parser_init(&opt_parser, opt_specs, argv + 1, argc - 1, 0); while (ntlm_opt_parser_next(&opt, &opt_parser)) { if (!opt.spec) { - ntlm_opt_status_fprint(stderr, &opt); + ntlm_opt_status_fprint(stderr, NULL, &opt); ntlm_opt_usage_fprint(stderr, argv[0], opt_specs); return 129; } diff --git a/cli/ntlm_cli_opt.c b/cli/ntlm_cli_opt.c index 79c1b11..6c6d961 100644 --- a/cli/ntlm_cli_opt.c +++ b/cli/ntlm_cli_opt.c @@ -4,11 +4,19 @@ * * This file is part of adopt, distributed under the MIT license. * For full terms and conditions, see the included LICENSE file. + * + * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT. + * + * This file was produced by using the `rename.pl` script included with + * adopt. The command-line specified was: + * + * ./rename.pl --out=../ntlmclient/cli/ --filename=ntlm_cli_opt ntlm_opt */ #include #include #include +#include #include #include "ntlm_cli_opt.h" @@ -26,50 +34,95 @@ # define INLINE(type) static inline type #endif -#define spec_is_named_type(x) \ - ((x)->type == NTLM_OPT_BOOL || \ - (x)->type == NTLM_OPT_SWITCH || \ - (x)->type == NTLM_OPT_VALUE || \ - (x)->type == NTLM_OPT_VALUE_OPTIONAL) +#ifdef _MSC_VER +# define alloca _alloca +#endif -INLINE(const ntlm_opt_spec *) spec_byname( - ntlm_opt_parser *parser, const char *name, size_t namelen) +#define spec_is_option_type(x) \ + ((x)->type == NTLM_OPT_TYPE_BOOL || \ + (x)->type == NTLM_OPT_TYPE_SWITCH || \ + (x)->type == NTLM_OPT_TYPE_VALUE) + +INLINE(const ntlm_opt_spec *) spec_for_long( + int *is_negated, + int *has_value, + const char **value, + const ntlm_opt_parser *parser, + const char *arg) { const ntlm_opt_spec *spec; + char *eql; + size_t eql_pos; + + eql = strchr(arg, '='); + eql_pos = (eql = strchr(arg, '=')) ? (size_t)(eql - arg) : strlen(arg); for (spec = parser->specs; spec->type; ++spec) { - if (spec->type == NTLM_OPT_LITERAL && namelen == 0) + /* Handle -- (everything after this is literal) */ + if (spec->type == NTLM_OPT_TYPE_LITERAL && arg[0] == '\0') + return spec; + + /* Handle --no-option arguments for bool types */ + if (spec->type == NTLM_OPT_TYPE_BOOL && + strncmp(arg, "no-", 3) == 0 && + strcmp(arg + 3, spec->name) == 0) { + *is_negated = 1; + return spec; + } + + /* Handle the typical --option arguments */ + if (spec_is_option_type(spec) && + spec->name && + strcmp(arg, spec->name) == 0) return spec; - if (spec_is_named_type(spec) && - spec->name && - strlen(spec->name) == namelen && - strncmp(name, spec->name, namelen) == 0) + /* Handle --option=value arguments */ + if (spec->type == NTLM_OPT_TYPE_VALUE && + eql && + strncmp(arg, spec->name, eql_pos) == 0 && + spec->name[eql_pos] == '\0') { + *has_value = 1; + *value = arg[eql_pos + 1] ? &arg[eql_pos + 1] : NULL; return spec; + } } return NULL; } -INLINE(const ntlm_opt_spec *) spec_byalias(ntlm_opt_parser *parser, char alias) +INLINE(const ntlm_opt_spec *) spec_for_short( + const char **value, + const ntlm_opt_parser *parser, + const char *arg) { const ntlm_opt_spec *spec; for (spec = parser->specs; spec->type; ++spec) { - if (spec_is_named_type(spec) && alias == spec->alias) + /* Handle -svalue short options with a value */ + if (spec->type == NTLM_OPT_TYPE_VALUE && + arg[0] == spec->alias && + arg[1] != '\0') { + *value = &arg[1]; return spec; + } + + /* Handle typical -s short options */ + if (arg[0] == spec->alias) { + *value = NULL; + return spec; + } } return NULL; } -INLINE(const ntlm_opt_spec *) spec_nextarg(ntlm_opt_parser *parser) +INLINE(const ntlm_opt_spec *) spec_for_arg(ntlm_opt_parser *parser) { const ntlm_opt_spec *spec; size_t args = 0; for (spec = parser->specs; spec->type; ++spec) { - if (spec->type == NTLM_OPT_ARG) { + if (spec->type == NTLM_OPT_TYPE_ARG) { if (args == parser->arg_idx) { parser->arg_idx++; return spec; @@ -78,24 +131,49 @@ INLINE(const ntlm_opt_spec *) spec_nextarg(ntlm_opt_parser *parser) args++; } - if (spec->type == NTLM_OPT_ARGS && args == parser->arg_idx) + if (spec->type == NTLM_OPT_TYPE_ARGS && args == parser->arg_idx) return spec; } return NULL; } +INLINE(int) spec_is_choice(const ntlm_opt_spec *spec) +{ + return ((spec + 1)->type && + ((spec + 1)->usage & NTLM_OPT_USAGE_CHOICE)); +} + +/* + * If we have a choice with switches and bare arguments, and we see + * the switch, then we no longer expect the bare argument. + */ +INLINE(void) consume_choices(const ntlm_opt_spec *spec, ntlm_opt_parser *parser) +{ + /* back up to the beginning of the choices */ + while (spec->type && (spec->usage & NTLM_OPT_USAGE_CHOICE)) + --spec; + + if (!spec_is_choice(spec)) + return; + + do { + if (spec->type == NTLM_OPT_TYPE_ARG) + parser->arg_idx++; + ++spec; + } while(spec->type && (spec->usage & NTLM_OPT_USAGE_CHOICE)); +} + static ntlm_opt_status_t parse_long(ntlm_opt *opt, ntlm_opt_parser *parser) { const ntlm_opt_spec *spec; - char *arg = parser->args[parser->idx++], *name = arg + 2, *eql; - size_t namelen; - - namelen = (eql = strrchr(arg, '=')) ? (size_t)(eql - name) : strlen(name); + char *arg = parser->args[parser->idx++]; + const char *value = NULL; + int is_negated = 0, has_value = 0; opt->arg = arg; - if ((spec = spec_byname(parser, name, namelen)) == NULL) { + if ((spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2])) == NULL) { opt->spec = NULL; opt->status = NTLM_OPT_STATUS_UNKNOWN_OPTION; goto done; @@ -104,19 +182,25 @@ static ntlm_opt_status_t parse_long(ntlm_opt *opt, ntlm_opt_parser *parser) opt->spec = spec; /* Future options parsed as literal */ - if (spec->type == NTLM_OPT_LITERAL) + if (spec->type == NTLM_OPT_TYPE_LITERAL) parser->in_literal = 1; - if (spec->type == NTLM_OPT_BOOL && spec->value) - *((int *)spec->value) = 1; + /* --bool or --no-bool */ + else if (spec->type == NTLM_OPT_TYPE_BOOL && spec->value) + *((int *)spec->value) = !is_negated; + + /* --accumulate */ + else if (spec->type == NTLM_OPT_TYPE_ACCUMULATOR && spec->value) + *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1; - if (spec->type == NTLM_OPT_SWITCH && spec->value) + /* --switch */ + else if (spec->type == NTLM_OPT_TYPE_SWITCH && spec->value) *((int *)spec->value) = spec->switch_value; /* Parse values as "--foo=bar" or "--foo bar" */ - if (spec->type == NTLM_OPT_VALUE || spec->type == NTLM_OPT_VALUE_OPTIONAL) { - if (eql && *(eql+1)) - opt->value = eql + 1; + else if (spec->type == NTLM_OPT_TYPE_VALUE) { + if (has_value) + opt->value = (char *)value; else if ((parser->idx + 1) <= parser->args_len) opt->value = parser->args[parser->idx++]; @@ -125,11 +209,15 @@ static ntlm_opt_status_t parse_long(ntlm_opt *opt, ntlm_opt_parser *parser) } /* Required argument was not provided */ - if (spec->type == NTLM_OPT_VALUE && !opt->value) + if (spec->type == NTLM_OPT_TYPE_VALUE && + !opt->value && + !(spec->usage & NTLM_OPT_USAGE_VALUE_OPTIONAL)) opt->status = NTLM_OPT_STATUS_MISSING_VALUE; else opt->status = NTLM_OPT_STATUS_OK; + consume_choices(opt->spec, parser); + done: return opt->status; } @@ -137,11 +225,12 @@ static ntlm_opt_status_t parse_long(ntlm_opt *opt, ntlm_opt_parser *parser) static ntlm_opt_status_t parse_short(ntlm_opt *opt, ntlm_opt_parser *parser) { const ntlm_opt_spec *spec; - char *arg = parser->args[parser->idx++], alias = *(arg + 1); + char *arg = parser->args[parser->idx++]; + const char *value; opt->arg = arg; - if ((spec = spec_byalias(parser, alias)) == NULL) { + if ((spec = spec_for_short(&value, parser, &arg[1 + parser->in_short])) == NULL) { opt->spec = NULL; opt->status = NTLM_OPT_STATUS_UNKNOWN_OPTION; goto done; @@ -149,16 +238,19 @@ static ntlm_opt_status_t parse_short(ntlm_opt *opt, ntlm_opt_parser *parser) opt->spec = spec; - if (spec->type == NTLM_OPT_BOOL && spec->value) + if (spec->type == NTLM_OPT_TYPE_BOOL && spec->value) *((int *)spec->value) = 1; - if (spec->type == NTLM_OPT_SWITCH && spec->value) + else if (spec->type == NTLM_OPT_TYPE_ACCUMULATOR && spec->value) + *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1; + + else if (spec->type == NTLM_OPT_TYPE_SWITCH && spec->value) *((int *)spec->value) = spec->switch_value; /* Parse values as "-ifoo" or "-i foo" */ - if (spec->type == NTLM_OPT_VALUE || spec->type == NTLM_OPT_VALUE_OPTIONAL) { - if (strlen(arg) > 2) - opt->value = arg + 2; + else if (spec->type == NTLM_OPT_TYPE_VALUE) { + if (value) + opt->value = (char *)value; else if ((parser->idx + 1) <= parser->args_len) opt->value = parser->args[parser->idx++]; @@ -166,35 +258,89 @@ static ntlm_opt_status_t parse_short(ntlm_opt *opt, ntlm_opt_parser *parser) *((char **)spec->value) = opt->value; } + /* + * Handle compressed short arguments, like "-fbcd"; see if there's + * another character after the one we processed. If not, advance + * the parser index. + */ + if (spec->type != NTLM_OPT_TYPE_VALUE && arg[2 + parser->in_short] != '\0') { + parser->in_short++; + parser->idx--; + } else { + parser->in_short = 0; + } + /* Required argument was not provided */ - if (spec->type == NTLM_OPT_VALUE && !opt->value) + if (spec->type == NTLM_OPT_TYPE_VALUE && !opt->value) opt->status = NTLM_OPT_STATUS_MISSING_VALUE; else opt->status = NTLM_OPT_STATUS_OK; + consume_choices(opt->spec, parser); + done: return opt->status; } static ntlm_opt_status_t parse_arg(ntlm_opt *opt, ntlm_opt_parser *parser) { - const ntlm_opt_spec *spec = spec_nextarg(parser); + const ntlm_opt_spec *spec = spec_for_arg(parser); opt->spec = spec; - opt->arg = parser->args[parser->idx++]; + opt->arg = parser->args[parser->idx]; - if (spec && spec->value) - *((char **)spec->value) = opt->arg; + if (!spec) { + parser->idx++; + opt->status = NTLM_OPT_STATUS_UNKNOWN_OPTION; + } else if (spec->type == NTLM_OPT_TYPE_ARGS) { + if (spec->value) + *((char ***)spec->value) = &parser->args[parser->idx]; + + /* + * We have started a list of arguments; the remainder of + * given arguments need not be examined. + */ + parser->in_args = (parser->args_len - parser->idx); + parser->idx = parser->args_len; + opt->args_len = parser->in_args; + opt->status = NTLM_OPT_STATUS_OK; + } else { + if (spec->value) + *((char **)spec->value) = parser->args[parser->idx]; + + parser->idx++; + opt->status = NTLM_OPT_STATUS_OK; + } - opt->status = spec ? NTLM_OPT_STATUS_OK : NTLM_OPT_STATUS_UNKNOWN_OPTION; return opt->status; } +static int support_gnu_style(unsigned int flags) +{ + if ((flags & NTLM_OPT_PARSE_FORCE_GNU) != 0) + return 1; + + if ((flags & NTLM_OPT_PARSE_GNU) == 0) + return 0; + + /* TODO: Windows */ +#if defined(_WIN32) && defined(UNICODE) + if (_wgetenv(L"POSIXLY_CORRECT") != NULL) + return 0; +#else + if (getenv("POSIXLY_CORRECT") != NULL) + return 0; +#endif + + return 1; +} + void ntlm_opt_parser_init( ntlm_opt_parser *parser, const ntlm_opt_spec specs[], char **args, - size_t args_len) + size_t args_len, + unsigned int flags) { assert(parser); @@ -203,6 +349,112 @@ void ntlm_opt_parser_init( parser->specs = specs; parser->args = args; parser->args_len = args_len; + parser->flags = flags; + + parser->needs_sort = support_gnu_style(flags); +} + +INLINE(const ntlm_opt_spec *) spec_for_sort( + int *needs_value, + const ntlm_opt_parser *parser, + const char *arg) +{ + int is_negated, has_value = 0; + const char *value; + const ntlm_opt_spec *spec = NULL; + size_t idx = 0; + + *needs_value = 0; + + if (strncmp(arg, "--", 2) == 0) { + spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2]); + *needs_value = !has_value; + } + + else if (strncmp(arg, "-", 1) == 0) { + spec = spec_for_short(&value, parser, &arg[1]); + + /* + * Advance through compressed short arguments to see if + * the last one has a value, eg "-xvffilename". + */ + while (spec && !value && arg[1 + ++idx] != '\0') + spec = spec_for_short(&value, parser, &arg[1 + idx]); + + *needs_value = (value == NULL); + } + + return spec; +} + +/* + * Some parsers allow for handling arguments like "file1 --help file2"; + * this is done by re-sorting the arguments in-place; emulate that. + */ +static int sort_gnu_style(ntlm_opt_parser *parser) +{ + size_t i, j, insert_idx = parser->idx, offset; + const ntlm_opt_spec *spec; + char *option, *value; + int needs_value, changed = 0; + + parser->needs_sort = 0; + + for (i = parser->idx; i < parser->args_len; i++) { + spec = spec_for_sort(&needs_value, parser, parser->args[i]); + + /* Not a "-" or "--" prefixed option. No change. */ + if (!spec) + continue; + + /* A "--" alone means remaining args are literal. */ + if (spec->type == NTLM_OPT_TYPE_LITERAL) + break; + + option = parser->args[i]; + + /* + * If the argument is a value type and doesn't already + * have a value (eg "--foo=bar" or "-fbar") then we need + * to copy the next argument as its value. + */ + if (spec->type == NTLM_OPT_TYPE_VALUE && needs_value) { + /* + * A required value is not provided; set parser + * index to this value so that we fail on it. + */ + if (i + 1 >= parser->args_len) { + parser->idx = i; + return 1; + } + + value = parser->args[i + 1]; + offset = 1; + } else { + value = NULL; + offset = 0; + } + + /* Caller error if args[0] is an option. */ + if (i == 0) + return 0; + + /* Shift args up one (or two) and insert the option */ + for (j = i; j > insert_idx; j--) + parser->args[j + offset] = parser->args[j - 1]; + + parser->args[insert_idx] = option; + + if (value) + parser->args[insert_idx + 1] = value; + + insert_idx += (1 + offset); + i += offset; + + changed = 1; + } + + return changed; } ntlm_opt_status_t ntlm_opt_parser_next(ntlm_opt *opt, ntlm_opt_parser *parser) @@ -211,47 +463,206 @@ ntlm_opt_status_t ntlm_opt_parser_next(ntlm_opt *opt, ntlm_opt_parser *parser) memset(opt, 0x0, sizeof(ntlm_opt)); - if (parser->idx >= parser->args_len) + if (parser->idx >= parser->args_len) { + opt->args_len = parser->in_args; return NTLM_OPT_STATUS_DONE; + } - /* Handle arguments in long form, those beginning with "--" */ + /* Handle options in long form, those beginning with "--" */ if (strncmp(parser->args[parser->idx], "--", 2) == 0 && - !parser->in_literal) + !parser->in_short && + !parser->in_literal) return parse_long(opt, parser); - /* Handle arguments in short form, those beginning with "-" */ - else if (strncmp(parser->args[parser->idx], "-", 1) == 0 && - !parser->in_literal) + /* Handle options in short form, those beginning with "-" */ + else if (parser->in_short || + (strncmp(parser->args[parser->idx], "-", 1) == 0 && + !parser->in_literal)) return parse_short(opt, parser); - /* Handle "free" arguments, those without a dash */ + /* + * We've reached the first "bare" argument. In POSIX mode, all + * remaining items on the command line are arguments. In GNU + * mode, there may be long or short options after this. Sort any + * options up to this position then re-parse the current position. + */ + if (parser->needs_sort && sort_gnu_style(parser)) + return ntlm_opt_parser_next(opt, parser); + + return parse_arg(opt, parser); +} + +INLINE(int) spec_included(const ntlm_opt_spec **specs, const ntlm_opt_spec *spec) +{ + const ntlm_opt_spec **i; + + for (i = specs; *i; ++i) { + if (spec == *i) + return 1; + } + + return 0; +} + +static ntlm_opt_status_t validate_required( + ntlm_opt *opt, + const ntlm_opt_spec specs[], + const ntlm_opt_spec **given_specs) +{ + const ntlm_opt_spec *spec, *required; + int given; + + /* + * Iterate over the possible specs to identify requirements and + * ensure that those have been given on the command-line. + * Note that we can have required *choices*, where one in a + * list of choices must be specified. + */ + for (spec = specs, required = NULL, given = 0; spec->type; ++spec) { + if (!required && (spec->usage & NTLM_OPT_USAGE_REQUIRED)) { + required = spec; + given = 0; + } else if (!required) { + continue; + } + + if (!given) + given = spec_included(given_specs, spec); + + /* + * Validate the requirement unless we're in a required + * choice. In that case, keep the required state and + * validate at the end of the choice list. + */ + if (!spec_is_choice(spec)) { + if (!given) { + opt->spec = required; + opt->status = NTLM_OPT_STATUS_MISSING_ARGUMENT; + break; + } + + required = NULL; + given = 0; + } + } + + return opt->status; +} + +ntlm_opt_status_t ntlm_opt_parse( + ntlm_opt *opt, + const ntlm_opt_spec specs[], + char **args, + size_t args_len, + unsigned int flags) +{ + ntlm_opt_parser parser; + const ntlm_opt_spec **given_specs; + size_t given_idx = 0; + + ntlm_opt_parser_init(&parser, specs, args, args_len, flags); + + given_specs = alloca(sizeof(const ntlm_opt_spec *) * (args_len + 1)); + + while (ntlm_opt_parser_next(opt, &parser)) { + if (opt->status != NTLM_OPT_STATUS_OK && + opt->status != NTLM_OPT_STATUS_DONE) + return opt->status; + + if ((opt->spec->usage & NTLM_OPT_USAGE_STOP_PARSING)) + return (opt->status = NTLM_OPT_STATUS_DONE); + + given_specs[given_idx++] = opt->spec; + } + + given_specs[given_idx] = NULL; + + return validate_required(opt, specs, given_specs); +} + +static int spec_name_fprint(FILE *file, const ntlm_opt_spec *spec) +{ + int error; + + if (spec->type == NTLM_OPT_TYPE_ARG) + error = fprintf(file, "%s", spec->value_name); + else if (spec->type == NTLM_OPT_TYPE_ARGS) + error = fprintf(file, "%s", spec->value_name); + else if (spec->alias && !(spec->usage & NTLM_OPT_USAGE_SHOW_LONG)) + error = fprintf(file, "-%c", spec->alias); else - return parse_arg(opt, parser); + error = fprintf(file, "--%s", spec->name); + + return error; } int ntlm_opt_status_fprint( FILE *file, + const char *command, const ntlm_opt *opt) { + const ntlm_opt_spec *choice; int error; + if (command && (error = fprintf(file, "%s: ", command)) < 0) + return error; + switch (opt->status) { case NTLM_OPT_STATUS_DONE: - error = fprintf(file, "Finished processing arguments (no error)\n"); + error = fprintf(file, "finished processing arguments (no error)\n"); break; case NTLM_OPT_STATUS_OK: - error = fprintf(file, "No error\n"); + error = fprintf(file, "no error\n"); break; case NTLM_OPT_STATUS_UNKNOWN_OPTION: - error = fprintf(file, "Unknown option: %s\n", opt->arg); + error = fprintf(file, "unknown option: %s\n", opt->arg); break; case NTLM_OPT_STATUS_MISSING_VALUE: - if (strncmp(opt->arg, "--", 2) == 0) - error = fprintf(file, "Option '%s' requires a value.\n", - opt->spec->name); - else - error = fprintf(file, "Switch '%c' requires a value.\n", - opt->spec->alias); + if ((error = fprintf(file, "argument '")) < 0 || + (error = spec_name_fprint(file, opt->spec)) < 0 || + (error = fprintf(file, "' requires a value.\n")) < 0) + ; + break; + case NTLM_OPT_STATUS_MISSING_ARGUMENT: + if (spec_is_choice(opt->spec)) { + int is_choice = 1; + + if (spec_is_choice((opt->spec)+1)) + error = fprintf(file, "one of"); + else + error = fprintf(file, "either"); + + if (error < 0) + break; + + for (choice = opt->spec; is_choice; ++choice) { + is_choice = spec_is_choice(choice); + + if (!is_choice) + error = fprintf(file, " or"); + else if (choice != opt->spec) + error = fprintf(file, ","); + + if ((error < 0) || + (error = fprintf(file, " '")) < 0 || + (error = spec_name_fprint(file, choice)) < 0 || + (error = fprintf(file, "'")) < 0) + break; + + if (!spec_is_choice(choice)) + break; + } + + if ((error < 0) || + (error = fprintf(file, " is required.\n")) < 0) + break; + } else { + if ((error = fprintf(file, "argument '")) < 0 || + (error = spec_name_fprint(file, opt->spec)) < 0 || + (error = fprintf(file, "' is required.\n")) < 0) + break; + } + break; default: error = fprintf(file, "Unknown status: %d\n", opt->status); @@ -267,14 +678,17 @@ int ntlm_opt_usage_fprint( const ntlm_opt_spec specs[]) { const ntlm_opt_spec *spec; - int choice = 0; + int choice = 0, next_choice = 0, optional = 0; int error; if ((error = fprintf(file, "usage: %s", command)) < 0) goto done; for (spec = specs; spec->type; ++spec) { - int optional = !(spec->usage & NTLM_OPT_USAGE_REQUIRED); + if (!choice) + optional = !(spec->usage & NTLM_OPT_USAGE_REQUIRED); + + next_choice = !!((spec + 1)->usage & NTLM_OPT_USAGE_CHOICE); if (spec->usage & NTLM_OPT_USAGE_HIDDEN) continue; @@ -288,21 +702,30 @@ int ntlm_opt_usage_fprint( goto done; if (optional && !choice && (error = fprintf(file, "[")) < 0) + error = fprintf(file, "["); + if (!optional && !choice && next_choice) + error = fprintf(file, "("); + + if (error < 0) goto done; - if (spec->type == NTLM_OPT_VALUE && spec->alias) + if (spec->type == NTLM_OPT_TYPE_VALUE && spec->alias && + !(spec->usage & NTLM_OPT_USAGE_VALUE_OPTIONAL) && + !(spec->usage & NTLM_OPT_USAGE_SHOW_LONG)) error = fprintf(file, "-%c <%s>", spec->alias, spec->value_name); - else if (spec->type == NTLM_OPT_VALUE) - error = fprintf(file, "--%s=<%s>", spec->name, spec->value_name); - else if (spec->type == NTLM_OPT_VALUE_OPTIONAL && spec->alias) + else if (spec->type == NTLM_OPT_TYPE_VALUE && spec->alias && + !(spec->usage & NTLM_OPT_USAGE_SHOW_LONG)) error = fprintf(file, "-%c [<%s>]", spec->alias, spec->value_name); - else if (spec->type == NTLM_OPT_VALUE_OPTIONAL) + else if (spec->type == NTLM_OPT_TYPE_VALUE && + !(spec->usage & NTLM_OPT_USAGE_VALUE_OPTIONAL)) error = fprintf(file, "--%s[=<%s>]", spec->name, spec->value_name); - else if (spec->type == NTLM_OPT_ARG) + else if (spec->type == NTLM_OPT_TYPE_VALUE) + error = fprintf(file, "--%s=<%s>", spec->name, spec->value_name); + else if (spec->type == NTLM_OPT_TYPE_ARG) error = fprintf(file, "<%s>", spec->value_name); - else if (spec->type == NTLM_OPT_ARGS) - error = fprintf(file, "<%s...>", spec->value_name); - else if (spec->type == NTLM_OPT_LITERAL) + else if (spec->type == NTLM_OPT_TYPE_ARGS) + error = fprintf(file, "<%s>...", spec->value_name); + else if (spec->type == NTLM_OPT_TYPE_LITERAL) error = fprintf(file, "--"); else if (spec->alias && !(spec->usage & NTLM_OPT_USAGE_SHOW_LONG)) error = fprintf(file, "-%c", spec->alias); @@ -312,10 +735,15 @@ int ntlm_opt_usage_fprint( if (error < 0) goto done; - choice = !!((spec+1)->usage & NTLM_OPT_USAGE_CHOICE); + if (!optional && choice && !next_choice) + error = fprintf(file, ")"); + else if (optional && !next_choice) + error = fprintf(file, "]"); - if (optional && !choice && (error = fprintf(file, "]")) < 0) + if (error < 0) goto done; + + choice = next_choice; } error = fprintf(file, "\n"); diff --git a/cli/ntlm_cli_opt.h b/cli/ntlm_cli_opt.h index dfccffa..a41c9b0 100644 --- a/cli/ntlm_cli_opt.h +++ b/cli/ntlm_cli_opt.h @@ -4,10 +4,17 @@ * * This file is part of adopt, distributed under the MIT license. * For full terms and conditions, see the included LICENSE file. + * + * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT. + * + * This file was produced by using the `rename.pl` script included with + * adopt. The command-line specified was: + * + * ./rename.pl --out=../ntlmclient/cli/ --filename=ntlm_cli_opt ntlm_opt */ -#ifndef NTLM_OPT_H -#define NTLM_OPT_H +#ifndef NTLM_CLI_OPT_H +#define NTLM_CLI_OPT_H #include #include @@ -16,55 +23,123 @@ * The type of argument to be parsed. */ typedef enum { - NTLM_OPT_NONE = 0, + NTLM_OPT_TYPE_NONE = 0, /** - * An argument that, when specified, sets a given value to true. - * This is useful for arguments like "--debug". The `value` pointer - * in the returned option will be set to `1` when this is set. + * An option that, when specified, sets a given value to true. + * This is useful for options like "--debug". A negation + * option (beginning with "no-") is implicitly specified; for + * example "--no-debug". The `value` pointer in the returned + * option will be set to `1` when this is specified, and set to + * `0` when the negation "no-" option is specified. */ - NTLM_OPT_BOOL, + NTLM_OPT_TYPE_BOOL, /** - * An argument that, when specified, sets the given `value_ptr` - * to the given `value`. + * An option that, when specified, sets the given `value` pointer + * to the specified `switch_value`. This is useful for booleans + * where you do not want the implicit negation that comes with an + * `NTLM_OPT_TYPE_BOOL`, or for switches that multiplex a value, like + * setting a mode. For example, `--read` may set the `value` to + * `MODE_READ` and `--write` may set the `value` to `MODE_WRITE`. */ - NTLM_OPT_SWITCH, + NTLM_OPT_TYPE_SWITCH, - /** An argument that has a value ("-nvalue" or "--name value") */ - NTLM_OPT_VALUE, + /** + * An option that, when specified, increments the given + * `value` by the given `switch_value`. This can be specified + * multiple times to continue to increment the `value`. + * (For example, "-vvv" to set verbosity to 3.) + */ + NTLM_OPT_TYPE_ACCUMULATOR, - /** An argument that has an optional value ("-n" or "-n foo") */ - NTLM_OPT_VALUE_OPTIONAL, + /** + * An option that takes a value, for example `-n value`, + * `-nvalue`, `--name value` or `--name=value`. + */ + NTLM_OPT_TYPE_VALUE, - /** The literal arguments follow specifier, bare "--" */ - NTLM_OPT_LITERAL, + /** + * A bare "--" that indicates that arguments following this are + * literal. This allows callers to specify things that might + * otherwise look like options, for example to operate on a file + * named "-rf" then you can invoke "program -- -rf" to treat + * "-rf" as an argument not an option. + */ + NTLM_OPT_TYPE_LITERAL, - /** A single "free" argument ("path") */ - NTLM_OPT_ARG, + /** + * A single argument, not an option. When options are exhausted, + * arguments will be matches in the order that they're specified + * in the spec list. For example, if two `NTLM_OPT_TYPE_ARGS` are + * specified, `input_file` and `output_file`, then the first bare + * argument on the command line will be `input_file` and the + * second will be `output_file`. + */ + NTLM_OPT_TYPE_ARG, - /** Unmatched arguments, a collection of "free" arguments ("paths...") */ - NTLM_OPT_ARGS, + /** + * A collection of arguments. This is useful when you want to take + * a list of arguments, for example, multiple paths. When specified, + * the value will be set to the first argument in the list. + */ + NTLM_OPT_TYPE_ARGS, } ntlm_opt_type_t; /** - * Usage information for an argument, to be displayed to the end-user. - * This is only for display, the parser ignores this usage information. + * Additional information about an option, including parsing + * restrictions and usage information to be displayed to the end-user. */ typedef enum { + /** Defaults for the argument. */ + NTLM_OPT_USAGE_DEFAULT = 0, + /** This argument is required. */ NTLM_OPT_USAGE_REQUIRED = (1u << 0), - /** This argument should not be displayed in usage. */ - NTLM_OPT_USAGE_HIDDEN = (1u << 1), + /** + * This is a multiple choice argument, combined with the previous + * argument. For example, when the previous argument is `-f` and + * this optional is applied to an argument of type `-b` then one + * of `-f` or `-b` may be specified. + */ + NTLM_OPT_USAGE_CHOICE = (1u << 1), + + /** + * This argument short-circuits the remainder of parsing. + * Useful for arguments like `--help`. + */ + NTLM_OPT_USAGE_STOP_PARSING = (1u << 2), - /** This is a multiple choice argument, combined with the previous arg. */ - NTLM_OPT_USAGE_CHOICE = (1u << 2), + /** The argument's value is optional ("-n" or "-n foo") */ + NTLM_OPT_USAGE_VALUE_OPTIONAL = (1u << 3), + + /** This argument should not be displayed in usage. */ + NTLM_OPT_USAGE_HIDDEN = (1u << 4), /** In usage, show the long format instead of the abbreviated format. */ - NTLM_OPT_USAGE_SHOW_LONG = (1u << 3), + NTLM_OPT_USAGE_SHOW_LONG = (1u << 5), } ntlm_opt_usage_t; +typedef enum { + /** Default parsing behavior. */ + NTLM_OPT_PARSE_DEFAULT = 0, + + /** + * Parse with GNU `getopt_long` style behavior, where options can + * be intermixed with arguments at any position (for example, + * "file1 --help file2".) Like `getopt_long`, this can mutate the + * arguments given. + */ + NTLM_OPT_PARSE_GNU = (1u << 0), + + /** + * Force GNU `getopt_long` style behavior; the `POSIXLY_CORRECT` + * environment variable is ignored. + */ + NTLM_OPT_PARSE_FORCE_GNU = (1u << 1), +} ntlm_opt_flag_t; + /** Specification for an available option. */ typedef struct ntlm_opt_spec { /** Type of option expected. */ @@ -77,43 +152,57 @@ typedef struct ntlm_opt_spec { const char alias; /** - * If this spec is of type `NTLM_OPT_BOOL`, this is a pointer to - * an `int` that will be set to `1` if the option is specified. + * If this spec is of type `NTLM_OPT_TYPE_BOOL`, this is a pointer + * to an `int` that will be set to `1` if the option is specified. * - * If this spec is of type `NTLM_OPT_SWITCH`, this is a pointer to - * an `int` that will be set to the opt's `value` (below) when - * this option is specified. + * If this spec is of type `NTLM_OPT_TYPE_SWITCH`, this is a pointer + * to an `int` that will be set to the opt's `switch_value` (below) + * when this option is specified. + * + * If this spec is of type `NTLM_OPT_TYPE_ACCUMULATOR`, this is a + * pointer to an `int` that will be incremented by the opt's + * `switch_value` (below). If no `switch_value` is provided then + * the value will be incremented by 1. + * + * If this spec is of type `NTLM_OPT_TYPE_VALUE`, + * `NTLM_OPT_TYPE_VALUE_OPTIONAL`, or `NTLM_OPT_TYPE_ARG`, this is + * a pointer to a `char *` that will be set to the value + * specified on the command line. * - * If this spec is of type `NTLM_OPT_VALUE` or `NTLM_OPT_VALUE_OPTIONAL`, - * this is a pointer to a `char *`, that will be set to the value + * If this spec is of type `NTLM_OPT_TYPE_ARGS`, this is a pointer + * to a `char **` that will be set to the remaining values * specified on the command line. */ void *value; /** - * If this spec is of type `NTLM_OPT_SWITCH`, this is the value to - * set in the option's `value_ptr` pointer when it is specified. - * This is ignored for other opt types. + * If this spec is of type `NTLM_OPT_TYPE_SWITCH`, this is the value + * to set in the option's `value` pointer when it is specified. If + * this spec is of type `NTLM_OPT_TYPE_ACCUMULATOR`, this is the value + * to increment in the option's `value` pointer when it is + * specified. This is ignored for other opt types. */ int switch_value; + /** + * Optional usage flags that change parsing behavior and how + * usage information is shown to the end-user. + */ + uint32_t usage; + /** * The name of the value, provided when creating usage information. * This is required only for the functions that display usage - * information and only when a spec is of type `NTLM_OPT_VALUE`. + * information and only when a spec is of type `NTLM_OPT_TYPE_VALUE, + * `NTLM_OPT_TYPE_ARG` or `NTLM_OPT_TYPE_ARGS``. */ const char *value_name; /** - * Short description of the option, used when creating usage - * information. This is only used when creating usage information. + * Optional short description of the option to display to the + * end-user. This is only used when creating usage information. */ const char *help; - - /** - * Optional `ntlm_opt_usage_t`, used when creating usage information. - */ - ntlm_opt_usage_t usage; } ntlm_opt_spec; /** Return value for `ntlm_opt_parser_next`. */ @@ -138,6 +227,9 @@ typedef enum { * was provided. */ NTLM_OPT_STATUS_MISSING_VALUE = 3, + + /** A required argument was not provided. */ + NTLM_OPT_STATUS_MISSING_ARGUMENT = 4, } ntlm_opt_status_t; /** An option provided on the command-line. */ @@ -162,6 +254,13 @@ typedef struct ntlm_opt { * this is the value provided to the argument. */ char *value; + + /** + * If the argument is of type `NTLM_OPT_ARGS`, this is the number of + * arguments remaining. This value is persisted even when parsing + * is complete and `status` == `NTLM_OPT_STATUS_DONE`. + */ + size_t args_len; } ntlm_opt; /* The internal parser state. Callers should not modify this structure. */ @@ -169,27 +268,54 @@ typedef struct ntlm_opt_parser { const ntlm_opt_spec *specs; char **args; size_t args_len; + unsigned int flags; + /* Parser state */ size_t idx; size_t arg_idx; - int in_literal : 1, - in_short : 1; + size_t in_args; + size_t in_short; + unsigned int needs_sort : 1, + in_literal : 1; } ntlm_opt_parser; +/** + * Parses all the command-line arguments and updates all the options using + * the pointers provided. Parsing stops on any invalid argument and + * information about the failure will be provided in the opt argument. + * + * This is the simplest way to parse options; it handles the initialization + * (`parser_init`) and looping (`parser_next`). + * + * @param opt The The `ntlm_opt` information that failed parsing + * @param specs A NULL-terminated array of `ntlm_opt_spec`s that can be parsed + * @param args The arguments that will be parsed + * @param args_len The length of arguments to be parsed + * @param flags The `ntlm_opt_flag_t flags for parsing + */ +ntlm_opt_status_t ntlm_opt_parse( + ntlm_opt *opt, + const ntlm_opt_spec specs[], + char **args, + size_t args_len, + unsigned int flags); + /** * Initializes a parser that parses the given arguments according to the * given specifications. * * @param parser The `ntlm_opt_parser` that will be initialized * @param specs A NULL-terminated array of `ntlm_opt_spec`s that can be parsed - * @param argv The arguments that will be parsed + * @param args The arguments that will be parsed * @param args_len The length of arguments to be parsed + * @param flags The `ntlm_opt_flag_t flags for parsing */ void ntlm_opt_parser_init( ntlm_opt_parser *parser, const ntlm_opt_spec specs[], - char **argv, - size_t args_len); + char **args, + size_t args_len, + unsigned int flags); /** * Parses the next command-line argument and places the information about @@ -211,11 +337,13 @@ ntlm_opt_status_t ntlm_opt_parser_next( * specified, or when an argument was specified without a value. * * @param file The file to print information to + * @param command The name of the command to use when printing (optional) * @param opt The option that failed to parse * @return 0 on success, -1 on failure */ int ntlm_opt_status_fprint( FILE *file, + const char *command, const ntlm_opt *opt); /** @@ -231,4 +359,4 @@ int ntlm_opt_usage_fprint( const char *command, const ntlm_opt_spec specs[]); -#endif /* NTLM_OPT_H */ +#endif /* NTLM_CLI_OPT_H */ From 36f2e361594059c4d4ab0153d79aee9185f7406e Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 14 Dec 2023 13:22:54 +0000 Subject: [PATCH 3/7] compat: keep declaration at top of block --- src/crypt_commoncrypto.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/crypt_commoncrypto.c b/src/crypt_commoncrypto.c index 4ff57ed..3c20469 100644 --- a/src/crypt_commoncrypto.c +++ b/src/crypt_commoncrypto.c @@ -59,11 +59,12 @@ bool ntlm_des_encrypt( ntlm_des_block *plaintext, ntlm_des_block *key) { + CCCryptorStatus result; size_t written; NTLM_UNUSED(ntlm); - CCCryptorStatus result = CCCrypt(kCCEncrypt, + result = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionECBMode, key, sizeof(ntlm_des_block), NULL, plaintext, sizeof(ntlm_des_block), From da3cc96f2aadae1f067d34666d75d36e196c4a0d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 14 Dec 2023 13:37:02 +0000 Subject: [PATCH 4/7] compat: include ntlm headers --- src/crypt.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/crypt.h b/src/crypt.h index b7dc1a3..4ad543e 100644 --- a/src/crypt.h +++ b/src/crypt.h @@ -9,6 +9,9 @@ #ifndef PRIVATE_CRYPT_COMMON_H__ #define PRIVATE_CRYPT_COMMON_H__ +#include "ntlmclient.h" +#include "ntlm.h" + #if defined(CRYPT_OPENSSL) # include "crypt_openssl.h" #elif defined(CRYPT_MBEDTLS) From 0627bda011e209f8379a618204e4abc7e53cea15 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 14 Dec 2023 13:37:18 +0000 Subject: [PATCH 5/7] openssl: more dynamic loading support Support more variants for dynamic loading of OpenSSL libraries. --- src/crypt_openssl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/crypt_openssl.c b/src/crypt_openssl.c index dfcd9de..c4be129 100644 --- a/src/crypt_openssl.c +++ b/src/crypt_openssl.c @@ -44,7 +44,9 @@ static inline void HMAC_CTX_free(HMAC_CTX *ctx) #endif -#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(CRYPT_OPENSSL_DYNAMIC) +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x03050000fL) || \ + defined(CRYPT_OPENSSL_DYNAMIC) static inline void HMAC_CTX_cleanup(HMAC_CTX *ctx) { @@ -61,7 +63,9 @@ static bool ntlm_crypt_init_functions(ntlm_client *ntlm) void *handle; if ((handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL && + (handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL && (handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL && + (handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL && (handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL) { ntlm_client_set_errmsg(ntlm, "could not open libssl"); return false; From 7545a954a21ac384af2f0b299832084efd54a979 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 14 Dec 2023 13:39:42 +0000 Subject: [PATCH 6/7] compat: use INT64_C macro --- src/ntlm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ntlm.c b/src/ntlm.c index 3393be9..ad4de5d 100644 --- a/src/ntlm.c +++ b/src/ntlm.c @@ -930,10 +930,10 @@ const char *ntlm_client_target_domain_dns(ntlm_client *ntlm) } #define EVEN_PARITY(a) \ - (!!((a) & 0x01ll) ^ !!((a) & 0x02ll) ^ \ - !!((a) & 0x04ll) ^ !!((a) & 0x08ll) ^ \ - !!((a) & 0x10ll) ^ !!((a) & 0x20ll) ^ \ - !!((a) & 0x40ll) ^ !!((a) & 0x80ll)) + (!!((a) & INT64_C(0x01)) ^ !!((a) & INT64_C(0x02)) ^ \ + !!((a) & INT64_C(0x04)) ^ !!((a) & INT64_C(0x08)) ^ \ + !!((a) & INT64_C(0x10)) ^ !!((a) & INT64_C(0x20)) ^ \ + !!((a) & INT64_C(0x40)) ^ !!((a) & INT64_C(0x80))) static void generate_odd_parity(ntlm_des_block *block) { From 1e44d323f229d258bba0a6a76ba0162fb7c6cd01 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 14 Dec 2023 13:39:53 +0000 Subject: [PATCH 7/7] compat: avoid trailing commas on last list item --- src/ntlm.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ntlm.h b/src/ntlm.h index 227f5bc..4abfd7a 100644 --- a/src/ntlm.h +++ b/src/ntlm.h @@ -30,7 +30,7 @@ typedef enum { NTLM_STATE_CHALLENGE = 1, NTLM_STATE_RESPONSE = 2, NTLM_STATE_ERROR = 3, - NTLM_STATE_COMPLETE = 4, + NTLM_STATE_COMPLETE = 4 } ntlm_state; typedef struct { @@ -122,7 +122,7 @@ struct ntlm_client { }; typedef enum { - NTLM_ENABLE_HOSTVERSION = (1 << 31), + NTLM_ENABLE_HOSTVERSION = (1 << 31) } ntlm_client_internal_flags; typedef enum { @@ -130,7 +130,7 @@ typedef enum { NTLM_TARGET_INFO_SERVER = 1, NTLM_TARGET_INFO_DOMAIN = 2, NTLM_TARGET_INFO_SERVER_DNS = 3, - NTLM_TARGET_INFO_DOMAIN_DNS = 4, + NTLM_TARGET_INFO_DOMAIN_DNS = 4 } ntlm_target_info_type_t; typedef enum { @@ -168,7 +168,7 @@ typedef enum { NTLM_NEGOTIATE_TARGET_INFO = 0x00800000, /* Version information should be provided */ - NTLM_NEGOTIATE_VERSION = 0x01000000, + NTLM_NEGOTIATE_VERSION = 0x01000000 } ntlm_negotiate_t; extern int ntlm_client_set_nonce(ntlm_client *ntlm, uint64_t nonce);