diff --git a/README.md b/README.md index db5ff56..f83b815 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,12 @@ Generic Functions Find matching string in an array of strings. Returns index in array on match, or `-1` on error or not found. +`systemf(fmt, ...)` + + Like `system()`, but takes a formatted string as argument. This + greatly simplifies operations that usually consist of composing a + command from parts into a dynamic buffer before calling it. + - `telnet_open(), telnet_close(), telnet_expect()` Poor mans telnet expect in C. Opens connection to a Telnet service; diff --git a/src/Makefile.am b/src/Makefile.am index 580ccc8..283f2d0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,8 @@ libite_la_SOURCES = chomp.c copyfile.c \ reallocarray.c rsync.c \ strlcpy.c strlcat.c strtonum.c \ strdupa.h strndupa.h strnlen.h \ - strmatch.c telnet.c tempfile.c \ + strmatch.c systemf.c \ + telnet.c tempfile.c \ yorn.c which.c \ lite.h strlite.h \ queue.h tree.h diff --git a/src/lite.h b/src/lite.h index e9ac6a9..555bdae 100644 --- a/src/lite.h +++ b/src/lite.h @@ -98,6 +98,8 @@ void *reallocarray(void *optr, size_t nmemb, size_t size); void progress (int percent, int max_width); void progress_simple(int percent); +int systemf (const char *fmt, ...); + sdbuf_t*telnet_open (int addr, short port); int telnet_close (sdbuf_t *ctx); int telnet_expect (sdbuf_t *ctx, char *script[], FILE *output); diff --git a/src/systemf.c b/src/systemf.c new file mode 100644 index 0000000..fbafe72 --- /dev/null +++ b/src/systemf.c @@ -0,0 +1,73 @@ +/* Formatted system() which returns actual return status of command + * + * Copyright (c) 2020 Joachim Nilsson + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +int systemf(const char *fmt, ...) +{ + va_list ap; + char *cmd; + int len, status; + int rc = -1; + + va_start(ap, fmt); + len = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + va_start(ap, fmt); + cmd = alloca(++len); + if (!cmd) { + va_end(ap); + errno = ENOMEM; + return -1; + } + + vsnprintf(cmd, len, fmt, ap); + va_end(ap); + + status = system(cmd); + + rc = WEXITSTATUS(status); + if (!WIFEXITED(status)) { + if (WIFSIGNALED(status) && + (WTERMSIG(status) == SIGINT || + WTERMSIG(status) == SIGQUIT)) { + errno = EINTR; + rc = -1; + } else if (!rc) { + /* + * Alert callee that command didn't complete + * successfully. Some programs don't change + * their exit code when signaled. + */ + rc = 1; + } + } + + return rc; +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/tests/.gitignore b/tests/.gitignore index b6e830e..cb90340 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -17,6 +17,7 @@ progress rsync str strmatch +systemf tempfile touch tree diff --git a/tests/Makefile.am b/tests/Makefile.am index dbd9d72..032dd3c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -17,6 +17,7 @@ TESTS += progress TESTS += rsync TESTS += str TESTS += strmatch +TESTS += systemf TESTS += touch TESTS += tempfile TESTS += which diff --git a/tests/systemf.c b/tests/systemf.c new file mode 100644 index 0000000..17bd768 --- /dev/null +++ b/tests/systemf.c @@ -0,0 +1,31 @@ +#include +#include "check.h" + +int main(void) +{ + struct { char *cmd; int rc; } list[] = { + { "false", 1 }, + { "true", 0 }, + { "kill -9 $$", 1 }, + { "kill -QUIT $$", -1 }, + { "kill -INT $$", -1 } + }; + + for (size_t i = 0; i < NELEMS(list); i++) { + int rc; + + rc = systemf("%s", list[i].cmd); + if (rc != list[i].rc) + err(rc, "Failed command %s", list[i].cmd); + } + + return 0; +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ +