diff --git a/test/clitoris.c b/test/clitoris.c index d87c237..8502a18 100644 --- a/test/clitoris.c +++ b/test/clitoris.c @@ -73,6 +73,10 @@ # define countof(x) (sizeof(x) / sizeof(*x)) #endif /* !countof */ +#if !defined strlenof +# define strlenof(x) (sizeof(x) - 1U) +#endif /* !strlenof */ + #if !defined with # define with(args...) for (args, *__ep__ = (void*)1; __ep__; __ep__ = 0) #endif /* !with */ @@ -81,6 +85,17 @@ # define PATH_MAX 256U #endif /* !PATH_MAX */ +#if defined HAVE_SPLICE +# ifdef __INTEL_COMPILER +# pragma warning(disable:1419) +# endif /* __INTEL_COMPILER */ +extern ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, + size_t len, unsigned int flags); +# ifdef __INTEL_COMPILER +# pragma warning(default:1419) +# endif /* __INTEL_COMPILER */ +#endif /* HAVE_SPLICE */ + typedef struct clitf_s clitf_t; typedef struct clit_buf_s clit_buf_t; typedef struct clit_bit_s clit_bit_t; @@ -111,6 +126,9 @@ struct clit_opt_s { unsigned int ptyp:1; unsigned int keep_going_p:1; unsigned int shcmdp:1; + /* just some padding to start afresh on an 8-bit boundary */ + unsigned int:4; + unsigned int xcod:8; /* use this instead of /bin/sh */ char *shcmd; @@ -310,18 +328,80 @@ cmdify(char *restrict cmd) { /* prep for about 16 params */ char **v = calloc(16U, sizeof(*v)); - size_t i = 0U; - const char *ifs = getenv("IFS") ?: " \t\n"; - v[0U] = strtok(cmd, ifs); - do { - if (UNLIKELY((i % 16U) == 15U)) { - v = realloc(v, (i + 1U + 16U) * sizeof(*v)); - } - } while ((v[++i] = strtok(NULL, ifs)) != NULL); + if (LIKELY(v != NULL)) { + const char *ifs = getenv("IFS") ?: " \t\n"; + size_t i = 0U; + + v[0U] = strtok(cmd, ifs); + do { + if (UNLIKELY((i % 16U) == 15U)) { + v = realloc(v, (i + 1U + 16U) * sizeof(*v)); + } + } while ((v[++i] = strtok(NULL, ifs)) != NULL); + } return v; } +/* takes ideas from Gregory Pakosz's whereami */ +static char* +get_argv0dir(const char *argv0) +{ +/* return current executable */ + char *res; + + if (0) { +#if 0 +#elif defined __linux__ +/* don't rely on argv0 at all */ + } else if (1) { + if ((res = realpath("/proc/self/exe", NULL)) == NULL) { + /* we've got a plan B */ + goto planb; + } +#elif defined __APPLE__ + } else if (1) { + char buf[PATH_MAX]; + uint32_t bsz = strlenof(buf); + + if (_NSGetExecutablePath(buf, &bsz) < 0) { + /* plan B again */ + goto planb; + } + /* strdup BUF quickly */ + res = strdup(buf); +#endif /* OS */ + } else { + planb: + /* backup plan, massage argv0 */ + if (argv0 == NULL) { + return NULL; + } + /* otherwise simply copy ARGV0 */ + res = strdup(argv0); + } + + /* path extraction aka dirname'ing, absolute or otherwise */ + if (res == NULL) { + return NULL; + } + with (char *dir0 = strrchr(res, '/')) { + if (dir0 == NULL) { + free(res); + return NULL; + } + *dir0 = '\0'; + } + return res; +} + +static void +free_argv0dir(char *a0) +{ + free(a0); + return; +} + /* clit bit handling */ #define CLIT_BIT_FD(x) (clit_bit_fd_p(x) ? (int)(x).z : -1) @@ -559,12 +639,12 @@ find_ignore(struct clit_tst_s tst[static 1]) static char tok_out[] = "output"; static char tok_ret[] = "return"; - if (strncmp(cmd, tok_ign, sizeof(tok_ign) - 1U)) { + if (strncmp(cmd, tok_ign, strlenof(tok_ign))) { /* don't bother */ break; } /* fast-forward a little */ - cmd += sizeof(tok_ign) - 1U; + cmd += strlenof(tok_ign); if (isspace(*cmd)) { /* it's our famous ignore token it seems */ @@ -572,14 +652,14 @@ find_ignore(struct clit_tst_s tst[static 1]) } else if (*cmd++ != '-') { /* unknown token then */ break; - } else if (!strncmp(cmd, tok_out, sizeof(tok_out) - 1U)) { + } else if (!strncmp(cmd, tok_out, strlenof(tok_out))) { /* ignore-output it is */ tst->ign_out = 1U; - cmd += sizeof(tok_out) - 1U; - } else if (!strncmp(cmd, tok_ret, sizeof(tok_ret) - 1U)) { + cmd += strlenof(tok_out); + } else if (!strncmp(cmd, tok_ret, strlenof(tok_ret))) { /* ignore-return it is */ tst->ign_ret = 1U; - cmd += sizeof(tok_ret) - 1U; + cmd += strlenof(tok_ret); } else { /* don't know what's going on */ break; @@ -727,7 +807,7 @@ find_opt(struct clit_opt_s options, const char *bp, size_t bz) static const char magic[] = "setopt "; for (const char *mp; - (mp = xmemmem(bp, bz, magic, sizeof(magic) - 1U)) != NULL; + (mp = xmemmem(bp, bz, magic, strlenof(magic))) != NULL; bz -= (mp + 1U) - bp, bp = mp + 1U) { unsigned int opt; @@ -741,12 +821,14 @@ find_opt(struct clit_opt_s options, const char *bp, size_t bz) opt = 0U; } else { /* found rubbish then */ - mp += sizeof(magic) - 1U; + mp += strlenof(magic); continue; } -#define CMP(x, lit) (strncmp((x), (lit), sizeof(lit) - 1)) +#define CMP(x, lit) (strncmp((x), (lit), strlenof(lit))) /* parse the option value */ - if ((mp += sizeof(magic) - 1U) == NULL) { + mp += strlenof(magic); + if (NULL) { + /* not reached */ ; } else if (CMP(mp, "verbose\n") == 0) { options.verbosep = opt; @@ -772,6 +854,14 @@ find_opt(struct clit_opt_s options, const char *bp, size_t bz) } options.shcmd = xstrndup(arg, eol - arg); options.shcmdp = 1U; + } else if (CMP(mp, "exit-code") == 0) { + const char *arg = mp + sizeof("exit-code"); + char *p; + long unsigned int xc; + + if ((xc = strtoul(arg, &p, 0), *p == '\n')) { + options.xcod = (unsigned int)xc; + } } #undef CMP } @@ -1067,9 +1157,6 @@ differ(struct clit_chld_s ctx[static 1], clit_bit_t exp, bool xpnd_proto_p) if (expfd >= 0) { close(expfd); } - if (actfd >= 0) { - close(actfd); - } kill(difftool, SIGTERM); difftool = -1; break; @@ -1223,6 +1310,8 @@ run_tst(struct clit_chld_s ctx[static 1], struct clit_tst_s tst[static 1]) goto wait; } if (ctx->options.timeo > 0 && (ctx->feed > 0 || ctx->diff > 0)) { + alrm_handler_closure.feed = ctx->feed; + alrm_handler_closure.diff = ctx->diff; alrm_handler_closure.old_hdl = signal(SIGALRM, alrm_handler); } with (const char *p = tst->cmd.d, *const ep = tst->cmd.d + tst->cmd.z) { @@ -1238,6 +1327,7 @@ run_tst(struct clit_chld_s ctx[static 1], struct clit_tst_s tst[static 1]) * or in case of a pty, send exit command and keep fingers * crossed the pty will close itself */ close(ctx->pin); + ctx->pin = -1; } /* wait for the beef child */ @@ -1249,8 +1339,8 @@ run_tst(struct clit_chld_s ctx[static 1], struct clit_tst_s tst[static 1]) rc = 0; } else if (tst->exp_ret == 255U && rc) { rc = 0; - } else { - rc = 1; + } else if (ctx->options.xcod) { + rc = ctx->options.xcod; } } else { rc = 1; @@ -1289,7 +1379,7 @@ run_tst(struct clit_chld_s ctx[static 1], struct clit_tst_s tst[static 1]) } #if defined HAVE_PTY_H - if (UNLIKELY(ctx->options.ptyp)) { + if (UNLIKELY(ctx->options.ptyp && ctx->pin >= 0)) { /* also close child's stdin here */ close(ctx->pin); } @@ -1527,15 +1617,33 @@ main(int argc, char *argv[]) } else if (getenv("DIFF") != NULL) { cmd_diff = getenv("DIFF"); } - - /* prepend our current directory and our argv[0] directory */ - with (char *arg0 = argv[0]) { - char *dir0; - if ((dir0 = strrchr(arg0, '/')) != NULL) { - *dir0 = '\0'; + if (argi->exit_code_arg == (const char*)0x1U) { + options.xcod = 0U; + } else if (argi->exit_code_arg) { + options.xcod = strtoul(argi->exit_code_arg, NULL, 0); + } else { + options.xcod = 1U; + } + + /* Although I cannot support my claim with a hard survey, I would + * say in 99.9 cases out of a hundred the cli tool in question + * has not been installed at the time of testing it, so somehow + * we must make sure to test the version in the build directory + * rather than a globally installed one. + * + * On the other hand, in general we won't know where the build + * directory is, we've got --builddir for that, however we can + * assist our users by prepending the current working directory + * and the directory we're run from to PATH. + * + * So let's prepend our argv[0] directory */ + with (char *arg0 = get_argv0dir(argv[0])) { + if (LIKELY(arg0 != NULL)) { prepend_path(arg0); + free_argv0dir(arg0); } } + /* ... and our current directory */ prepend_path("."); /* also bang builddir to path */ with (char *blddir = getenv("builddir")) { @@ -1543,8 +1651,8 @@ main(int argc, char *argv[]) /* use at most 256U bytes for blddir */ char _blddir[256U]; - memccpy(_blddir, blddir, '\0', sizeof(_blddir) - 1U); - _blddir[sizeof(_blddir) - 1U] = '\0'; + memccpy(_blddir, blddir, '\0', strlenof(_blddir)); + _blddir[strlenof(_blddir)] = '\0'; prepend_path(_blddir); } } diff --git a/test/clitoris.yuck b/test/clitoris.yuck index dda57f4..a20adc6 100644 --- a/test/clitoris.yuck +++ b/test/clitoris.yuck @@ -14,3 +14,6 @@ Run .clit regression test files. -k, --keep-going Don't break on the first failure or differing output --diff=CMD Use specified diff command instead of the one in PATH. + -x, --exit-code[=N] Map any non-0 exit code from the commands to N, + (default: 1), or, if N is omitted, pass the exit + code through as is.