From 3dfd6c11ed5599ab8baf4a6114f4ccb34150de6e Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sun, 5 May 2024 10:24:40 +0200 Subject: First commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 16 + LICENSE | 15 + Makefile | 177 +++++ README | 14 + TODO | 13 + common.h | 271 +++++++ config.mk | 8 + libexec.h | 1633 +++++++++++++++++++++++++++++++++++++++++ libexec_add_output_document.c | 45 ++ libexec_add_output_fd.c | 15 + libexec_add_pluming.c | 25 + libexec_clear_environ.c | 72 ++ libexec_close.c | 23 + libexec_construct_command.c | 20 + libexec_copy_environ.c | 51 ++ libexec_destroy_command.c | 36 + libexec_destroy_document.c | 28 + libexec_destroy_pluming.c | 31 + libexec_destroy_result.c | 20 + libexec_dup.c | 24 + libexec_exec.c | 200 +++++ libexec_get_documents.c | 83 +++ libexec_getenv.c | 122 +++ libexec_getenv_last.c | 123 ++++ libexec_init_command.c | 43 ++ libexec_input_data_copy.c | 23 + libexec_input_data_gift.c | 25 + libexec_input_text_copy.c | 19 + libexec_input_text_gift.c | 19 + libexec_open.c | 15 + libexec_openat.c | 36 + libexec_openat2.c | 54 ++ libexec_pipe_commands.c | 20 + libexec_pipe_commandsv.c | 22 + libexec_pipe_commandsvn.c | 106 +++ libexec_put_arguments.c | 22 + libexec_put_argumentsn.c | 45 ++ libexec_putenv.c | 144 ++++ libexec_putenv_append.c | 235 ++++++ libexec_putenv_noclobber.c | 175 +++++ libexec_putenv_noreplace.c | 171 +++++ libexec_putenv_prepend.c | 235 ++++++ libexec_putenv_replace.c | 171 +++++ libexec_putenvf.c | 61 ++ libexec_putenvf_append.c | 240 ++++++ libexec_putenvf_noclobber.c | 180 +++++ libexec_putenvf_noreplace.c | 176 +++++ libexec_putenvf_prepend.c | 240 ++++++ libexec_putenvf_replace.c | 176 +++++ libexec_recv_document.c | 53 ++ libexec_renumber_fd.c | 32 + libexec_run.c | 20 + libexec_run_pipeline.c | 343 +++++++++ libexec_send_document.c | 26 + libexec_set_environ.c | 26 + libexec_set_exec_fd.c | 21 + libexec_set_exec_path.c | 29 + libexec_set_executable.c | 27 + libexec_set_require_path.c | 18 + libexec_setenv.c | 73 ++ libexec_setenv_append.c | 212 ++++++ libexec_setenv_noclobber.c | 180 +++++ libexec_setenv_noreplace.c | 176 +++++ libexec_setenv_prepend.c | 212 ++++++ libexec_setenv_replace.c | 176 +++++ libexec_setenvf.c | 61 ++ libexec_setenvf_append.c | 217 ++++++ libexec_setenvf_noclobber.c | 185 +++++ libexec_setenvf_noreplace.c | 181 +++++ libexec_setenvf_prepend.c | 217 ++++++ libexec_setenvf_replace.c | 181 +++++ libexec_spawn.c | 89 +++ libexec_unsetenv.c | 48 ++ libexec_unsetenv_first.c | 44 ++ libexec_unsetenv_last.c | 46 ++ libexec_vconstruct_command.c | 882 ++++++++++++++++++++++ libexec_vpipe_commands.c | 30 + libexec_vputenvf.c | 39 + libexec_vputenvf_append.c | 15 + libexec_vputenvf_noclobber.c | 15 + libexec_vputenvf_noreplace.c | 15 + libexec_vputenvf_prepend.c | 15 + libexec_vputenvf_replace.c | 15 + libexec_vrun.c | 307 ++++++++ libexec_vsetenvf.c | 39 + libexec_vsetenvf_append.c | 15 + libexec_vsetenvf_noclobber.c | 15 + libexec_vsetenvf_noreplace.c | 15 + libexec_vsetenvf_prepend.c | 15 + libexec_vsetenvf_replace.c | 15 + mk/linux.mk | 6 + mk/macos.mk | 6 + mk/windows.mk | 6 + testhelp.c | 366 +++++++++ 94 files changed, 10442 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 common.h create mode 100644 config.mk create mode 100644 libexec.h create mode 100644 libexec_add_output_document.c create mode 100644 libexec_add_output_fd.c create mode 100644 libexec_add_pluming.c create mode 100644 libexec_clear_environ.c create mode 100644 libexec_close.c create mode 100644 libexec_construct_command.c create mode 100644 libexec_copy_environ.c create mode 100644 libexec_destroy_command.c create mode 100644 libexec_destroy_document.c create mode 100644 libexec_destroy_pluming.c create mode 100644 libexec_destroy_result.c create mode 100644 libexec_dup.c create mode 100644 libexec_exec.c create mode 100644 libexec_get_documents.c create mode 100644 libexec_getenv.c create mode 100644 libexec_getenv_last.c create mode 100644 libexec_init_command.c create mode 100644 libexec_input_data_copy.c create mode 100644 libexec_input_data_gift.c create mode 100644 libexec_input_text_copy.c create mode 100644 libexec_input_text_gift.c create mode 100644 libexec_open.c create mode 100644 libexec_openat.c create mode 100644 libexec_openat2.c create mode 100644 libexec_pipe_commands.c create mode 100644 libexec_pipe_commandsv.c create mode 100644 libexec_pipe_commandsvn.c create mode 100644 libexec_put_arguments.c create mode 100644 libexec_put_argumentsn.c create mode 100644 libexec_putenv.c create mode 100644 libexec_putenv_append.c create mode 100644 libexec_putenv_noclobber.c create mode 100644 libexec_putenv_noreplace.c create mode 100644 libexec_putenv_prepend.c create mode 100644 libexec_putenv_replace.c create mode 100644 libexec_putenvf.c create mode 100644 libexec_putenvf_append.c create mode 100644 libexec_putenvf_noclobber.c create mode 100644 libexec_putenvf_noreplace.c create mode 100644 libexec_putenvf_prepend.c create mode 100644 libexec_putenvf_replace.c create mode 100644 libexec_recv_document.c create mode 100644 libexec_renumber_fd.c create mode 100644 libexec_run.c create mode 100644 libexec_run_pipeline.c create mode 100644 libexec_send_document.c create mode 100644 libexec_set_environ.c create mode 100644 libexec_set_exec_fd.c create mode 100644 libexec_set_exec_path.c create mode 100644 libexec_set_executable.c create mode 100644 libexec_set_require_path.c create mode 100644 libexec_setenv.c create mode 100644 libexec_setenv_append.c create mode 100644 libexec_setenv_noclobber.c create mode 100644 libexec_setenv_noreplace.c create mode 100644 libexec_setenv_prepend.c create mode 100644 libexec_setenv_replace.c create mode 100644 libexec_setenvf.c create mode 100644 libexec_setenvf_append.c create mode 100644 libexec_setenvf_noclobber.c create mode 100644 libexec_setenvf_noreplace.c create mode 100644 libexec_setenvf_prepend.c create mode 100644 libexec_setenvf_replace.c create mode 100644 libexec_spawn.c create mode 100644 libexec_unsetenv.c create mode 100644 libexec_unsetenv_first.c create mode 100644 libexec_unsetenv_last.c create mode 100644 libexec_vconstruct_command.c create mode 100644 libexec_vpipe_commands.c create mode 100644 libexec_vputenvf.c create mode 100644 libexec_vputenvf_append.c create mode 100644 libexec_vputenvf_noclobber.c create mode 100644 libexec_vputenvf_noreplace.c create mode 100644 libexec_vputenvf_prepend.c create mode 100644 libexec_vputenvf_replace.c create mode 100644 libexec_vrun.c create mode 100644 libexec_vsetenvf.c create mode 100644 libexec_vsetenvf_append.c create mode 100644 libexec_vsetenvf_noclobber.c create mode 100644 libexec_vsetenvf_noreplace.c create mode 100644 libexec_vsetenvf_prepend.c create mode 100644 libexec_vsetenvf_replace.c create mode 100644 mk/linux.mk create mode 100644 mk/macos.mk create mode 100644 mk/windows.mk create mode 100644 testhelp.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..942ebc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*\#* +*~ +*.o +*.a +*.lo +*.to +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda +*.test diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fccd785 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +© 2024 Mattias Andrée + +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d159bef --- /dev/null +++ b/Makefile @@ -0,0 +1,177 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OS = linux +# Linux: linux +# Mac OS: macos +# Windows: windows +include mk/$(OS).mk + + +LIB_MAJOR = 1 +LIB_MINOR = 0 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) +LIB_NAME = exec + + +OBJ =\ + libexec_add_pluming.o\ + libexec_add_output_document.o\ + libexec_add_output_fd.o\ + libexec_clear_environ.o\ + libexec_close.o\ + libexec_construct_command.o\ + libexec_copy_environ.o\ + libexec_destroy_command.o\ + libexec_destroy_document.o\ + libexec_destroy_pluming.o\ + libexec_destroy_result.o\ + libexec_dup.o\ + libexec_exec.o\ + libexec_get_documents.o\ + libexec_getenv.o\ + libexec_getenv_last.o\ + libexec_init_command.o\ + libexec_input_data_copy.o\ + libexec_input_data_gift.o\ + libexec_input_text_copy.o\ + libexec_input_text_gift.o\ + libexec_open.o\ + libexec_openat.o\ + libexec_openat2.o\ + libexec_pipe_commands.o\ + libexec_pipe_commandsv.o\ + libexec_pipe_commandsvn.o\ + libexec_put_arguments.o\ + libexec_put_argumentsn.o\ + libexec_putenv.o\ + libexec_putenv_append.o\ + libexec_putenv_noclobber.o\ + libexec_putenv_noreplace.o\ + libexec_putenv_prepend.o\ + libexec_putenv_replace.o\ + libexec_putenvf.o\ + libexec_putenvf_append.o\ + libexec_putenvf_noclobber.o\ + libexec_putenvf_noreplace.o\ + libexec_putenvf_prepend.o\ + libexec_putenvf_replace.o\ + libexec_recv_document.o\ + libexec_renumber_fd.o\ + libexec_run.o\ + libexec_run_pipeline.o\ + libexec_send_document.o\ + libexec_set_environ.o\ + libexec_set_exec_fd.o\ + libexec_set_exec_path.o\ + libexec_set_executable.o\ + libexec_set_require_path.o\ + libexec_setenv.o\ + libexec_setenv_append.o\ + libexec_setenv_noclobber.o\ + libexec_setenv_noreplace.o\ + libexec_setenv_prepend.o\ + libexec_setenv_replace.o\ + libexec_setenvf.o\ + libexec_setenvf_append.o\ + libexec_setenvf_noclobber.o\ + libexec_setenvf_noreplace.o\ + libexec_setenvf_prepend.o\ + libexec_setenvf_replace.o\ + libexec_spawn.o\ + libexec_unsetenv.o\ + libexec_unsetenv_first.o\ + libexec_unsetenv_last.o\ + libexec_vconstruct_command.o\ + libexec_vpipe_commands.o\ + libexec_vputenvf.o\ + libexec_vputenvf_append.o\ + libexec_vputenvf_noclobber.o\ + libexec_vputenvf_noreplace.o\ + libexec_vputenvf_prepend.o\ + libexec_vputenvf_replace.o\ + libexec_vrun.o\ + libexec_vsetenvf.o\ + libexec_vsetenvf_append.o\ + libexec_vsetenvf_noclobber.o\ + libexec_vsetenvf_noreplace.o\ + libexec_vsetenvf_prepend.o\ + libexec_vsetenvf_replace.o + +HDR =\ + libexec.h\ + common.h + +LOBJ = $(OBJ:.o=.lo) +TOBJ = $(OBJ:.o=.to) +TEST = $(OBJ:.o=.test) + + +all: libexec.a libexec.$(LIBEXT) $(TEST) +$(OBJ): $(HDR) +$(LOBJ): $(HDR) +$(TOBJ): $(HDR) +$(TEST): testhelp.o libexec.a + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.to: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) -DTEST + +.to.test: + $(CC) -o $@ $< testhelp.o libexec.a $(LDFLAGS) + +libexec.a: $(OBJ) + @rm -f -- $@ + $(AR) rc $@ $(OBJ) + $(AR) ts $@ > /dev/null + +libexec.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) + +check: $(TEST) + @status=0;\ + for t in $(TEST); do\ + if test -z "$(CHECK_PREFIX)"; then\ + printf './%s\n' "$$t";\ + else\ + printf '%s ./%s\n' "$(CHECK_PREFIX)" "$$t";\ + fi;\ + if ! $(CHECK_PREFIX) "./$$t"; then\ + printf '\033[1;31m%s failed!\033[m\n' "$$t";\ + status=1;\ + fi;\ + done;\ + exit $$status + +install: libexec.a libexec.$(LIBEXT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libexec.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libexec.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMINOREXT)" + $(FIX_INSTALL_NAME) "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMINOREXT)" + ln -sf -- libexec.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMAJOREXT)" + ln -sf -- libexec.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBEXT)" + cp -- libexec.h "$(DESTDIR)$(PREFIX)/include/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libexec.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMAJOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMINOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBEXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/libexec.h" + +clean: + -rm -f -- *.o *.a *.lo *.to *.su *.so *.so.* *.dll *.dylib + -rm -f -- *.gch *.gcov *.gcno *.gcda *.test *.$(LIBEXT) + +.SUFFIXES: +.SUFFIXES: .lo .o .c .to .test + +.PHONY: all check install uninstall clean diff --git a/README b/README new file mode 100644 index 0000000..ac082e5 --- /dev/null +++ b/README @@ -0,0 +1,14 @@ +NAME + libexec - Library for running complex external utilities + +DESCRIPTION + libexec is a library that is designed to make it easy + to safely and correctly run external utilities, including + creating pipe lines. + + libexec is both designed to aid shell implementations and + to make it easier to write programs run a lot of commands + but could not easily be written as a shell script. + +SEE ALSO + None. diff --git a/TODO b/TODO new file mode 100644 index 0000000..ec3a255 --- /dev/null +++ b/TODO @@ -0,0 +1,13 @@ +Test that LIBEXEC_NOREPLACE first copies the calling process's environment, +even if nothing is modified, but also test that LIBEXEC_NOCLOBBER does not +do this. + +add LIBEXEC_RUN_ADD_SHARED_OUTPUT_FD +add LIBEXEC_RUN_ADD_SHARED_OUTPUT +add LIBEXEC_RUN_GET_STDOUT +add LIBEXEC_RUN_GET_STDERR +add LIBEXEC_RUN_SET_USER_TAG +add a way to create process substitutions in libexec_run +add a way to add a fork of the process into a pipeline + +add man pages diff --git a/common.h b/common.h new file mode 100644 index 0000000..66a8ded --- /dev/null +++ b/common.h @@ -0,0 +1,271 @@ +/* See LICENSE file for copyright and license details. */ +#include "libexec.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__linux__) +int libexec_openat2(struct libexec_command *cmd, int fd, int dirfd, const char *file, void *how, size_t size); +#endif + + +#ifdef TEST +# define TESTED_ELSEWHERE LIBEXEC_CONST__ int main(void) { return 0; } + +enum assert_type { + ASSERT_ENUM, + ASSERT_UINT, + ASSERT_INT, + ASSERT_NULL, + ASSERT_PTR, + ASSERT_STR, + ASSERT_STR_REV +}; + +enum assert_how { + ASSERT_EQ, + ASSERT_NE, + ASSERT_LT, + ASSERT_LE, + ASSERT_GT, + ASSERT_GE, + ASSERT_CONTAINS_ALL, + ASSERT_CONTAINS_ANY, + ASSERT_CONTAINS_NOT_ALL, + ASSERT_CONTAINS_NOT_ANY, + ASSERT_IS_IN, + ASSERT_NOT_IN, + ASSERT_NOT_LT, + ASSERT_NOT_LE, + ASSERT_NOT_GT, + ASSERT_NOT_GE +}; + +void test_assert(const char *file, int line, enum assert_type type, enum assert_how how, + const char *have_string, const char *expect_string, const void *have, const void *expect); + +#define ASSERT_EQ_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_EQ, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_NE_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_NE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_LT_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_LT, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_GT_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_GT, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_LE_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_LE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_GE_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_GE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_ALL_OF_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_CONTAINS_ALL, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_SOME_OF_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_CONTAINS_ANY, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_NONE_OF_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_CONTAINS_NOT_ANY, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_NOT_ALL_OF_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_CONTAINS_NOT_ALL, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_IS_IN_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_IS_IN, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_NOT_IN_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_NOT_IN, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_EQ_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_EQ, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_NE_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_NE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_LT_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_LT, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_GT_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_GT, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_LE_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_LE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_GE_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_GE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_ALL_OF_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_CONTAINS_ALL, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_SOME_OF_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_CONTAINS_ANY, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_NONE_OF_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_CONTAINS_NOT_ANY, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_NOT_ALL_OF_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_CONTAINS_NOT_ALL, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_IS_IN_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_IS_IN, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_EQ_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_EQ, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_NE_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_NE, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_LT_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_LT, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_GT_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_GT, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_LE_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_LE, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_GE_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_GE, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_EQ_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_EQ, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_NE_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_STARTS_WITH_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_EXTENDS_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_TRUNCATES_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_BEGINNING_BOUNDED_TO_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_ENDS_WITH_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_EXTENDS_END_OF_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_TRUNCATES_END_OF_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_ENDING_BOUNDED_TO_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_STARTS_WITH_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_EXTENDS_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_TRUNCATES_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_BEGINNING_NOT_BOUNDED_TO_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_ENDS_WITH_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_NOT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_EXTENDS_END_OF_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_NOT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_TRUNCATES_END_OF_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_NOT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_ENDING_NOT_BOUNDED_TO_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_NOT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_CONTAINS_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_CONTAINS_ALL, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_CONTAIN_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_CONTAINS_NOT_ALL, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_IS_IN_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_IS_IN, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_NOT_IN_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_IN, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_IS_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_EQ, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_NOT_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_NE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_BEFORE_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_AFTER_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_BEFORE_OR_IS_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_AFTER_OR_IS_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_IS_NULL(HAVE)\ + test_assert(__FILE__, __LINE__, ASSERT_NULL, ASSERT_EQ, #HAVE, "NULL", (HAVE), NULL) + +#define ASSERT_NOT_NULL(HAVE)\ + test_assert(__FILE__, __LINE__, ASSERT_NULL, ASSERT_NE, #HAVE, "NULL", (HAVE), NULL) + +#define ASSERT_IS_TRUE(HAVE)\ + ASSERT_NE_INT(HAVE, 0) + +#define ASSERT_IS_FALSE(HAVE)\ + ASSERT_EQ_INT(HAVE, 0) + +#define ASSERT_ZERO(HAVE)\ + ASSERT_EQ_INT(HAVE, 0) + + +#endif diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..f4adf12 --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = c99 + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = +LDFLAGS = diff --git a/libexec.h b/libexec.h new file mode 100644 index 0000000..5f7b2d0 --- /dev/null +++ b/libexec.h @@ -0,0 +1,1633 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBEXEC_H +#define LIBEXEC_H + + +#if defined(__linux__) +# include +#endif +#include +#include +#include +#include +#include + + +/* For internal use only { */ +#if defined(__GNUC__) +# define LIBEXEC_PURE__ __attribute__((__pure__)) +# define LIBEXEC_CONST__ __attribute__((__const__)) +# define LIBEXEC_PRINTF__(FMTIDX) __attribute__((__format__(__gnu_printf__, (FMTIDX), (FMTIDX) + 1))) +# define LIBEXEC_VPRINTF__(FMTIDX) __attribute__((__format__(__gnu_printf__, (FMTIDX), 0))) +#else +# define LIBEXEC_PURE__ +# define LIBEXEC_CONST__ +# define LIBEXEC_PRINTF__(FMTIDX) +# define LIBEXEC_VPRINTF__(FMTIDX) +#endif +/* } */ + + +#define LIBEXEC_VERSION 0U + + +enum libexec_insert_mode { + LIBEXEC_REPLACE, /* replace existing definition if any */ + LIBEXEC_NOREPLACE, /* do nothing if definition already exists */ + LIBEXEC_NOCLOBBER, /* fail if definition already exists an differs */ + LIBEXEC_APPEND, /* add, to end of list, even if definition already exists, but also keep old */ + LIBEXEC_PREPEND /* add, to beginning of list, even if definition already exists, but also keep old */ + /* Either LIBEXEC_APPEND or LIBEXEC_PREPEND is required to put something missing '=' in the environment */ +#define LIBEXEC_INSERT_MODE__COUNT__ 5 +}; + +enum libexec_pluming_type { + LIBEXEC_PLUMING_OPENAT, + LIBEXEC_PLUMING_OPENAT2, + LIBEXEC_PLUMING_CLOSE, + LIBEXEC_PLUMING_DUP2, + LIBEXEC_PLUMING_DOCUMENT, + LIBEXEC_PLUMING_PIPE +#define LIBEXEC_PLUMING_TYPE__COUNT__ 6 +}; + +enum libexec_pipe { + LIBEXEC_PIPE = 0, + LIBEXEC_SOCK_STREAM = 1, + LIBEXEC_SOCK_SEQPACKET = 2, + LIBEXEC_SOCK_DGRAM = 3, + LIBEXEC_DIRECT_PIPE = 4, + LIBEXEC_PIPE_CIRCULARLY = 0x8000 +#define LIBEXEC_PIPE__COUNT__ 5 +}; + +enum libexec_locate_method { + LIBEXEC_ALLOW_NAME, + LIBEXEC_REQUIRE_PATH, + LIBEXEC_EXEC_FD, + LIBEXEC_EXEC_AT +#define LIBEXEC_LOCATE_METHOD__COUNT__ 4 +}; + +enum libexec_run_instruction { + LIBEXEC_RUN_END, /* required to mark end if arguments for libexec_run */ + LIBEXEC_RUN_END_CIRCULAR_PIPE, /* alternative to LIBEXEC_RUN_END */ + LIBEXEC_RUN_PIPE, + LIBEXEC_RUN_SET_EXECUTABLE, + LIBEXEC_RUN_SET_REQUIRE_PATH, + LIBEXEC_RUN_SET_EXEC_FD, + LIBEXEC_RUN_SET_EXEC_PATH, + LIBEXEC_RUN_COPY_ENVIRON, + LIBEXEC_RUN_SET_ENVIRON, + LIBEXEC_RUN_CLEAR_ENVIRON, + LIBEXEC_RUN_UNSETENV, + LIBEXEC_RUN_PUTENV, + LIBEXEC_RUN_SETENV, + LIBEXEC_RUN_PUTENVF, + LIBEXEC_RUN_SETENVF, + LIBEXEC_RUN_OPEN, + LIBEXEC_RUN_OPENAT, + LIBEXEC_RUN_OPENAT2, /* generates ENOSYS unless running on Linux */ + LIBEXEC_RUN_DUP, + LIBEXEC_RUN_CLOSE, + LIBEXEC_RUN_RENUMBER_FD, + LIBEXEC_RUN_INPUT_COPY, + LIBEXEC_RUN_INPUT_GIFT, + LIBEXEC_RUN_ADD_OUTPUT_FD, /* use LIBEXEC_RUN_SET_FD_CALLBACK first */ + LIBEXEC_RUN_ADD_OUTPUT, + LIBEXEC_RUN_SET_SIGMASK, /* use once to configure how libexec_run_pipeline is invoked behind the scenes */ + LIBEXEC_RUN_SET_FD_CALLBACK, /* ditto */ + LIBEXEC_RUN_SET_REAPER, /* ditto */ + LIBEXEC_RUN_SET_ATFORK, /* ditto */ + LIBEXEC_RUN_SET_MUTEX, /* ditto */ + LIBEXEC_RUN_SET_INTERRUPT_CALLBACK /* ditto */ +#define LIBEXEC_RUN_INSTRUCTION__COUNT__ 31 +}; +#define LIBEXEC_RUN_END() LIBEXEC_RUN_END +#define LIBEXEC_RUN_END_CIRCULAR_PIPE(TYPE) LIBEXEC_RUN_END_CIRCULAR_PIPE, TYPE +#define LIBEXEC_RUN_PIPE(TYPE) LIBEXEC_RUN_PIPE, TYPE +#define LIBEXEC_RUN_SET_EXECUTABLE(FILE) LIBEXEC_RUN_SET_EXECUTABLE, FILE +#define LIBEXEC_RUN_SET_REQUIRE_PATH(REQUIRE) LIBEXEC_RUN_SET_REQUIRE_PATH, REQUIRE +#define LIBEXEC_RUN_SET_EXEC_FD(FD) LIBEXEC_RUN_SET_EXEC_FD, FD +#define LIBEXEC_RUN_SET_EXEC_PATH(DIRFD, PATH) LIBEXEC_RUN_SET_EXEC_PATH, DIRFD, PATH +#define LIBEXEC_RUN_COPY_ENVIRON(ENVIRON) LIBEXEC_RUN_COPY_ENVIRON, ENVIRON +#define LIBEXEC_RUN_SET_ENVIRON(ENVIRON) LIBEXEC_RUN_SET_ENVIRON, ENVIRON +#define LIBEXEC_RUN_CLEAR_ENVIRON(ENVIRON) LIBEXEC_RUN_CLEAR_ENVIRON +#define LIBEXEC_RUN_UNSETENV(ENV) LIBEXEC_RUN_UNSETENV, ENV +#define LIBEXEC_RUN_PUTENV(... /* STR, [HOW] */) LIBEXEC_RUN_PUTENV__(__VA_ARGS__, LIBEXEC_REPLACE,) +#define LIBEXEC_RUN_SETENV(... /* ENV, VAL, [HOW] */) LIBEXEC_RUN_PUTENV__(__VA_ARGS__, LIBEXEC_REPLACE,) +#define LIBEXEC_RUN_PUTENV_SAFE(HOW, STR) LIBEXEC_RUN_PUTENV, HOW, STR +#define LIBEXEC_RUN_SETENV_SAFE(HOW, ENV, VAL) LIBEXEC_RUN_SETENV, HOW, ENV, VAL +#define LIBEXEC_RUN_PUTENVF_SAFE(HOW, ...) LIBEXEC_RUN_PUTENV, HOW, __VA_ARGS__ +#define LIBEXEC_RUN_SETENVF_SAFE(HOW, ENV, ...) LIBEXEC_RUN_SETENV, HOW, ENV, __VA_ARGS__ +#define LIBEXEC_RUN_PUTENVF(...) LIBEXEC_RUN_PUTENVF_SAFE(LIBEXEC_REPLACE, __VA_ARGS__) +#define LIBEXEC_RUN_SETENVF(ENV, ...) LIBEXEC_RUN_SETENVF_SAFE(LIBEXEC_REPLACE, ENV, __VA_ARGS__) +#define LIBEXEC_RUN_OPEN(FD, FILE, ... /* FLAGS, [MODE] */) LIBEXEC_RUN_OPEN__(FD, FILE, __VA_ARGS__, 0666,) +#define LIBEXEC_RUN_OPENAT(FD, DIRFD, FILE, ... /* FLAGS, [MODE] */) LIBEXEC_RUN_OPENAT__(FD, DIRFD, FILE, __VA_ARGS__, 0666,) +#define LIBEXEC_RUN_OPENAT2(FD, DIRFD, FILE, ... /* HOW, [SIZE] */)\ + LIBEXEC_RUN_OPENAT2__(FD, DIRFD, FILE, __VA_ARGS__, sizeof(LIBEXEC__FIRST__(__VA_ARGS__,)),) +#define LIBEXEC_RUN_DUP(NEW_FD, OLD_FD) LIBEXEC_RUN_DUP, NEW_FD, OLD_FD +#define LIBEXEC_RUN_CLOSE(FD) LIBEXEC_RUN_CLOSE, FD +#define LIBEXEC_RUN_RENUMBER_FD(NEW_FD, OLD_FD) LIBEXEC_RUN_RENUMBER_FD, NEW_FD, OLD_FD +#define LIBEXEC_RUN_INPUT_COPY(FD, ... /* DATA, [LEN] */)\ + LIBEXEC_RUN_INPUT__(COPY, FD, __VA_ARGS__, strlen(LIBEXEC__FIRST__(__VA_ARGS__)),) +#define LIBEXEC_RUN_INPUT_GIFT(FD, ... /* DATA, [LEN] */)\ + LIBEXEC_RUN_INPUT__(GIFT, FD, __VA_ARGS__, strlen(LIBEXEC__FIRST__(__VA_ARGS__)),) +#define LIBEXEC_RUN_ADD_OUTPUT_FD(FD, WR_FD) LIBEXEC_RUN_ADD_OUTPUT_FD, FD, WR_FD +#define LIBEXEC_RUN_ADD_OUTPUT(FD, OUT) LIBEXEC_RUN_ADD_OUTPUT, FD, OUT +#define LIBEXEC_RUN_SET_SIGMASK(SIGMASK) LIBEXEC_RUN_SET_SIGMASK, SIGMASK +#define LIBEXEC_RUN_SET_FD_CALLBACK(CB, USER) LIBEXEC_RUN_SET_FD_CALLBACK, CB, USER +#define LIBEXEC_RUN_SET_REAPER(CB, USER) LIBEXEC_RUN_SET_REAPER, CB, USER +#define LIBEXEC_RUN_SET_ATFORK(CB, USER) LIBEXEC_RUN_SET_ATFORK, CB, USER +#define LIBEXEC_RUN_SET_MUTEX(CB, USER) LIBEXEC_RUN_SET_MUTEX, CB, USER +#define LIBEXEC_RUN_SET_INTERRUPT_CALLBACK(CB, USER) LIBEXEC_RUN_SET_INTERRUPT_CALLBACK, CB, USER + +#define LIBEXEC__FIRST__(X, ...) X +#define LIBEXEC_RUN_PUTENV__(STR, HOW, ...) LIBEXEC_RUN_PUTENV, HOW, STR +#define LIBEXEC_RUN_SETENV__(ENV, VAL, HOW, ...) LIBEXEC_RUN_SETENV, HOW, ENV, VAL +#define LIBEXEC_RUN_OPEN__(FD, FILE, FLAGS, MODE, ...) LIBEXEC_RUN_OPEN, FD, FILE, FLAGS, MODE +#define LIBEXEC_RUN_OPENAT__(FD, DIRFD, FILE, FLAGS, MODE, ...) LIBEXEC_RUN_OPENAT, FD, DIRFD, FILE, FLAGS, MODE +#define LIBEXEC_RUN_INPUT__(INSTR, FD, DATA, LEN, ...) LIBEXEC_RUN_INPUT_##INSTR, FD, DATA, LEN + +struct libexec_pluming { + int fd; + enum libexec_pluming_type type; + union { + int fd; + struct { + char *file; + int dirfd; + union { + struct { + mode_t mode; + int flags; + }; +#if defined(__linux__) + struct { + struct open_how *how; + size_t how_size; + }; +#endif + }; + }; + struct { + char *text; + size_t len; + }; + } target; +}; + +#define LIBEXEC_COMMAND_INIT\ + ((struct libexec_command){LIBEXEC_VERSION, LIBEXEC_ALLOW_NAME, -1, NULL, NULL, 0, NULL, 0, NULL}) +struct libexec_command { + unsigned library_version; + enum libexec_locate_method exec_how; + int exec_fd; + char *executable; + char **arguments; + size_t narguments; + struct libexec_pluming *plumings; + size_t nplumings; + char **environ; +}; + +struct libexec_document { + unsigned long long int user; + int fd; + char *text; + size_t length; + union { + size_t offset; /* when used for input */ + size_t alloc_size; /* when used for output */ + }; +}; + +struct libexec_result { + int *exit_statuses; + size_t proc_count; + int all_successful; +}; + + +void libexec_init_command(struct libexec_command *cmd); +void libexec_destroy_command(struct libexec_command *cmd); +void libexec_destroy_pluming(struct libexec_pluming *pluming); +int libexec_destroy_document(struct libexec_document *doc); +void libexec_destroy_result(struct libexec_result *result); + +int libexec_vconstruct_command(struct libexec_command *cmd, const char *fmt, va_list args); +int libexec_construct_command(struct libexec_command *cmd, const char *fmt, ...); +int libexec_put_argumentsn(struct libexec_command *cmd, const char *const *args, size_t nargs); +int libexec_put_arguments(struct libexec_command *cmd, const char *const *args); +int libexec_set_executable(struct libexec_command *cmd, const char *executable /* NULL = use first argument in command */); +void libexec_set_require_path(struct libexec_command *cmd, int require); +int libexec_set_exec_fd(struct libexec_command *cmd, int fd); +int libexec_set_exec_path(struct libexec_command *cmd, int dirfd, const char *path /* NULL = use first argument in command */); + +int libexec_add_pluming(struct libexec_command *cmd, const struct libexec_pluming *pluming); +int libexec_open(struct libexec_command *cmd, int fd, const char *file, int flags, mode_t mode); +int libexec_openat(struct libexec_command *cmd, int fd, int dirfd, const char *file, int flags, mode_t mode); +#if defined(__linux__) +int libexec_openat2(struct libexec_command *cmd, int fd, int dirfd, const char *file, struct open_how *how, size_t size); +#endif +int libexec_dup(struct libexec_command *cmd, int fd, int old_fd); +int libexec_close(struct libexec_command *cmd, int fd); +int libexec_renumber_fd(struct libexec_command *cmd, int fd, int old_fd); +int libexec_input_data_copy(struct libexec_command *cmd, int fd, const char *data, size_t len); +int libexec_input_data_gift(struct libexec_command *cmd, int fd, char *data, size_t len); +int libexec_input_text_copy(struct libexec_command *cmd, int fd, const char *text); +int libexec_input_text_gift(struct libexec_command *cmd, int fd, char *text); +int libexec_add_output_fd(struct libexec_command *cmd, int fd, int wr_fd); +int libexec_add_output_document(struct libexec_command *cmd, int fd, struct libexec_document *doc /* .fd will be set */, int flags); + + +/** + * The the value of an environment variable in a command + * + * If command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment is inspected + * + * In the event that the environment contains multiple + * definitions of the same variable, the first definition + * is used + * + * @param cmd The command whose environment shall be inspected + * @param name The name of the environment variable to read + * @return The value of the environment variable, or NULL + * on failure or if the environment variable was + * not found + * + * @throws (unmodified) The environment variable does not exist + * @throws EINVAL Invalid argument input + */ +LIBEXEC_PURE__ const char *libexec_getenv(struct libexec_command *, const char *); + +/** + * The the value of an environment variable in a command + * + * If command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment is inspected + * + * In the event that the environment contains multiple + * definitions of the same variable, the last definition + * is used + * + * @param cmd The command whose environment shall be inspected + * @param name The name of the environment variable to read + * @return The value of the environment variable, or NULL + * on failure or if the environment variable was + * not found + * + * @throws (unmodified) The environment variable does not exist + * @throws EINVAL Invalid argument input + */ +LIBEXEC_PURE__ const char *libexec_getenv_last(struct libexec_command *, const char *); + + +/** + * Copy a list of environment variables and used it for a command + * + * @param cmd The command whose environment shall be set + * @param env The environment (will be copied), if NULL, the + * calling process's environment will be (copied + * and) used for the command + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_copy_environ(struct libexec_command *, const char *const *); + +/** + * Set the command's environment to a prebuilt list of + * environment variables + * + * @param cmd The command whose environment shall be set + * @param env The environment; the ownership of the input + * pointer and all pointers stored in it will be + * transfered to `cmd`, meaning that they will be + * deallocated using free(3) when they are removed + * or when `cmd` is destructed. If NULL, the + * commands environment will be reset to its default + * state, meaning that the calling process's environment + * (it will not be copied into `cmd`) will be used + * when running the command + */ +void libexec_set_environ(struct libexec_command *, char **); + +/** + * Clear a command's environment, removing all it's environment variables + * + * Even if the command's environment is in its default state (to + * use the calling process's environment), and calling process's + * environment is void of variables, the command's environment + * will be put in a non-default state, meaning that future changes + * to the calling process's environments variable will not affect + * the command's environment + * + * @param cmd The command whose environment shall be modified + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_clear_environ(struct libexec_command *); + +/** + * Remove all occurences of an environment variable + * in a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will be copied and set as the + * command's environment once the variable is found; however + * if the variable is not found within the environment, the + * command's environment will remain in its default state + * + * @param cmd The command whose environment shall be modified + * @param name The name of the variable to remove from the environment + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_unsetenv(struct libexec_command *, const char *); + +/** + * Remove the first occurence of an environment variable + * in a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will be copied and set as the + * command's environment once the variable is found; however + * if the variable is not found within the environment, the + * command's environment will remain in its default state + * + * @param cmd The command whose environment shall be modified + * @param name The name of the variable to remove from the environment + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_unsetenv_first(struct libexec_command *, const char *); + +/** + * Remove the last occurence of an environment variable + * in a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will be copied and set as the + * command's environment once the variable is found; however + * if the variable is not found within the environment, the + * command's environment will remain in its default state + * + * @param cmd The command whose environment shall be modified + * @param name The name of the variable to remove from the environment + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_unsetenv_last(struct libexec_command *, const char *); + + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * @param cmd The command whose environment shall be modified + * @param how Must be one of the following values: + * - LIBEXEC_REPLACE: + * If the environment already contains the + * variable, replace its first definition in + * the environment + * - LIBEXEC_NOREPLACE: + * Do not replacing already existing definition + * of the environment variable + * - LIBEXEC_NOCLOBBER: + * Fail if the environment already contains the + * variable, but has a different value + * - LIBEXEC_APPEND: + * Always the string to the end of the environment + * - LIBEXEC_PREPEND: + * Always the string to the beginning of the environment + * @param string The environment variable that should be put into + * the environment, unless `how` is `LIBEXEC_APPEND` + * or `LIBEXEC_PREPEND`, it must be in the form + * `"%s=%s", name, value` (where `name` does not + * contain an equal sign) + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * (Only if `how` is `LIBEXEC_NOCLOBBER`) + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_putenv(struct libexec_command *, enum libexec_insert_mode, const char *); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * @param cmd The command whose environment shall be modified + * @param how Must be one of the following values: + * - LIBEXEC_REPLACE: + * If the environment already contains the + * variable, replace its first definition in + * the environment + * - LIBEXEC_NOREPLACE: + * Do not replacing already existing definition + * of the environment variable + * - LIBEXEC_NOCLOBBER: + * Fail if the environment already contains the + * variable, but has a different value + * - LIBEXEC_APPEND: + * Always the string to the end of the environment + * - LIBEXEC_PREPEND: + * Always the string to the beginning of the environment + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment, unless `how` is `LIBEXEC_APPEND` + * or `LIBEXEC_PREPEND`, the resulting string must + * be in the form `"%s=%s", name, value` (where `name` + * does not contain an equal sign) + * @param args Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * (Only if `how` is `LIBEXEC_NOCLOBBER`) + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(3) int libexec_vputenvf(struct libexec_command *, enum libexec_insert_mode, const char *, va_list); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * @param cmd The command whose environment shall be modified + * @param how Must be one of the following values: + * - LIBEXEC_REPLACE: + * If the environment already contains the + * variable, replace its first definition in + * the environment + * - LIBEXEC_NOREPLACE: + * Do not replacing already existing definition + * of the environment variable + * - LIBEXEC_NOCLOBBER: + * Fail if the environment already contains the + * variable, but has a different value + * - LIBEXEC_APPEND: + * Always the string to the end of the environment + * - LIBEXEC_PREPEND: + * Always the string to the beginning of the environment + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment, unless `how` is `LIBEXEC_APPEND` + * or `LIBEXEC_PREPEND`, the resulting string must + * be in the form `"%s=%s", name, value` (where `name` + * does not contain an equal sign) + * @param ... Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * (Only if `how` is `LIBEXEC_NOCLOBBER`) + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(3) int libexec_putenvf(struct libexec_command *, enum libexec_insert_mode, const char *, ...); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * @param cmd The command whose environment shall be modified + * @param how Must be one of the following values: + * - LIBEXEC_REPLACE: + * If the environment already contains the + * variable, replace its first definition in + * the environment + * - LIBEXEC_NOREPLACE: + * Do not replacing already existing definition + * of the environment variable + * - LIBEXEC_NOCLOBBER: + * Fail if the environment already contains the + * variable, but has a different value + * - LIBEXEC_APPEND: + * Always the string to the end of the environment + * - LIBEXEC_PREPEND: + * Always the string to the beginning of the environment + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value The value that shall be associated with the + * environment variable + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * (Only if `how` is `LIBEXEC_NOCLOBBER`) + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_setenv(struct libexec_command *, enum libexec_insert_mode, const char *, const char *); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * @param cmd The command whose environment shall be modified + * @param how Must be one of the following values: + * - LIBEXEC_REPLACE: + * If the environment already contains the + * variable, replace its first definition in + * the environment + * - LIBEXEC_NOREPLACE: + * Do not replacing already existing definition + * of the environment variable + * - LIBEXEC_NOCLOBBER: + * Fail if the environment already contains the + * variable, but has a different value + * - LIBEXEC_APPEND: + * Always the string to the end of the environment + * - LIBEXEC_PREPEND: + * Always the string to the beginning of the environment + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param args Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * (Only if `how` is `LIBEXEC_NOCLOBBER`) + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(4) int libexec_vsetenvf(struct libexec_command *, enum libexec_insert_mode, const char *, const char *, va_list); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * @param cmd The command whose environment shall be modified + * @param how Must be one of the following values: + * - LIBEXEC_REPLACE: + * If the environment already contains the + * variable, replace its first definition in + * the environment + * - LIBEXEC_NOREPLACE: + * Do not replacing already existing definition + * of the environment variable + * - LIBEXEC_NOCLOBBER: + * Fail if the environment already contains the + * variable, but has a different value + * - LIBEXEC_APPEND: + * Always the string to the end of the environment + * - LIBEXEC_PREPEND: + * Always the string to the beginning of the environment + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param ... Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * (Only if `how` is `LIBEXEC_NOCLOBBER`) + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(4) int libexec_setenvf(struct libexec_command *, enum libexec_insert_mode, const char *, const char *, ...); + + +/** + * Put an environment variable into a command's environment, + * replacing the first already existing definition of the + * variable if there is one + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_putenv(cmd, LIBEXEC_REPLACE, string)` + * + * @param cmd The command whose environment shall be modified + * @param string The environment variable that should be put into the + * environment, it must be in the form `"%s=%s", name, value` + * (where `name` does not contain an equal sign) + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_putenv_replace(struct libexec_command *, const char *); + +/** + * Put an environment variable into a command's environment, + * replacing the first already existing definition of the + * variable if there is one + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_REPLACE, fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment, the resulting string must + * be in the form `"%s=%s", name, value` (where `name` + * does not contain an equal sign) + * @param args Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(2) int libexec_vputenvf_replace(struct libexec_command *, const char *, va_list); + +/** + * Put an environment variable into a command's environment, + * replacing the first already existing definition of the + * variable if there is one + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_putenvf(cmd, LIBEXEC_REPLACE, fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment, the resulting string must + * be in the form `"%s=%s", name, value` (where `name` + * does not contain an equal sign) + * @param ... Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(2) int libexec_putenvf_replace(struct libexec_command *, const char *, ...); + +/** + * Put an environment variable into a command's environment, + * replacing the first already existing definition of the + * variable if there is one + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_setenv(cmd, LIBEXEC_REPLACE, name, value)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value The value that shall be associated with the + * environment variable + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_setenv_replace(struct libexec_command *, const char *, const char *); + +/** + * Put an environment variable into a command's environment, + * replacing the first already existing definition of the + * variable if there is one + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_REPLACE, name, value_fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param args Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(3) int libexec_vsetenvf_replace(struct libexec_command *, const char *, const char *, va_list); + +/** + * Put an environment variable into a command's environment, + * replacing the first already existing definition of the + * variable if there is one + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_setenvf(cmd, LIBEXEC_REPLACE, name, value_fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param ... Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(3) int libexec_setenvf_replace(struct libexec_command *, const char *, const char *, ...); + + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, the command's environment variable will not + * be modified, except that initialisation from default state + * as describe above will take place + * + * Equivalent to `libexec_putenv(cmd, LIBEXEC_NOREPLACE, string)` + * + * @param cmd The command whose environment shall be modified + * @param string The environment variable that should be put into the + * environment, it must be in the form `"%s=%s", name, value` + * (where `name` does not contain an equal sign) + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_putenv_noreplace(struct libexec_command *, const char *); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, the command's environment variable will not + * be modified, except that initialisation from default state + * as describe above will take place + * + * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_NOREPLACE, fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment, the resulting string must + * be in the form `"%s=%s", name, value` (where `name` + * does not contain an equal sign) + * @param args Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(2) int libexec_vputenvf_noreplace(struct libexec_command *, const char *, va_list); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, the command's environment variable will not + * be modified, except that initialisation from default state + * as describe above will take place + * + * Equivalent to `libexec_putenvf(cmd, LIBEXEC_NOREPLACE, fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment, the resulting string must + * be in the form `"%s=%s", name, value` (where `name` + * does not contain an equal sign) + * @param ... Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(2) int libexec_putenvf_noreplace(struct libexec_command *, const char *, ...); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, the command's environment variable will not + * be modified, except that initialisation from default state + * as describe above will take place + * + * Equivalent to `libexec_setenv(cmd, LIBEXEC_NOREPLACE, name, value)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value The value that shall be associated with the + * environment variable + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_setenv_noreplace(struct libexec_command *, const char *, const char *); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, the command's environment variable will not + * be modified, except that initialisation from default state + * as describe above will take place + * + * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_NOREPLACE, name, value_fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param args Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(3) int libexec_vsetenvf_noreplace(struct libexec_command *, const char *, const char *, va_list); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, the command's environment variable will not + * be modified, except that initialisation from default state + * as describe above will take place + * + * Equivalent to `libexec_setenvf(cmd, LIBEXEC_NOREPLACE, name, value_fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param ... Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(3) int libexec_setenvf_noreplace(struct libexec_command *, const char *, const char *, ...); + + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, but has a different value, the function will + * fail without side effects + * + * Equivalent to `libexec_putenv(cmd, LIBEXEC_NOCLOBBER, string)` + * + * @param cmd The command whose environment shall be modified + * @param string The environment variable that should be put into the + * environment, it must be in the form `"%s=%s", name, value` + * (where `name` does not contain an equal sign) + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_putenv_noclobber(struct libexec_command *, const char *); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, but has a different value, the function will + * fail without side effects + * + * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_NOCLOBBER, fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment, the resulting string must + * be in the form `"%s=%s", name, value` (where `name` + * does not contain an equal sign) + * @param args Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(2) int libexec_vputenvf_noclobber(struct libexec_command *, const char *, va_list); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, but has a different value, the function will + * fail without side effects + * + * Equivalent to `libexec_putenvf(cmd, LIBEXEC_NOCLOBBER, fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment, the resulting string must + * be in the form `"%s=%s", name, value` (where `name` + * does not contain an equal sign) + * @param ... Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(2) int libexec_putenvf_noclobber(struct libexec_command *, const char *, ...); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, but has a different value, the function will + * fail without side effects + * + * Equivalent to `libexec_setenv(cmd, LIBEXEC_NOCLOBBER, name, value)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value The value that shall be associated with the + * environment variable + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_setenv_noclobber(struct libexec_command *, const char *, const char *); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, but has a different value, the function will + * fail without side effects + * + * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_NOCLOBBER, name, value_fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param args Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(3) int libexec_vsetenvf_noclobber(struct libexec_command *, const char *, const char *, va_list); + +/** + * Put an environment variable into a command's environment + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * If the environment variable already exists in the command's + * environment, but has a different value, the function will + * fail without side effects + * + * Equivalent to `libexec_setenvf(cmd, LIBEXEC_NOCLOBBER, name, value_fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param ... Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws (unmodified) The environment variable already exists + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(3) int libexec_setenvf_noclobber(struct libexec_command *, const char *, const char *, ...); + + +/** + * Put an environment variable into a command's environment, + * adding it to the end of the environment regardless of + * whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_putenv(cmd, LIBEXEC_APPEND, string)` + * + * @param cmd The command whose environment shall be modified + * @param string The environment variable that should be put into the + * environment; there is no requirement that it contains + * an equal sign + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_putenv_append(struct libexec_command *, const char *); + +/** + * Put an environment variable into a command's environment, + * adding it to the end of the environment regardless of + * whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_APPEND, fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment; there is no requirement that + * the resulting string contains an equal sign + * @param args Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(2) int libexec_vputenvf_append(struct libexec_command *, const char *, va_list); + +/** + * Put an environment variable into a command's environment, + * adding it to the end of the environment regardless of + * whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_putenvf(cmd, LIBEXEC_APPEND, fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment; there is no requirement that + * the resulting string contains an equal sign + * @param ... Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(2) int libexec_putenvf_append(struct libexec_command *, const char *, ...); + +/** + * Put an environment variable into a command's environment, + * adding it to the end of the environment regardless of + * whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_setenv(cmd, LIBEXEC_APPEND, name, value)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value The value that shall be associated with the + * environment variable + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_setenv_append(struct libexec_command *, const char *, const char *); + +/** + * Put an environment variable into a command's environment, + * adding it to the end of the environment regardless of + * whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_APPEND, name, value_fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param args Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(3) int libexec_vsetenvf_append(struct libexec_command *, const char *, const char *, va_list); + +/** + * Put an environment variable into a command's environment, + * adding it to the end of the environment regardless of + * whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_setenvf(cmd, LIBEXEC_APPEND, name, value_fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param ... Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(3) int libexec_setenvf_append(struct libexec_command *, const char *, const char *, ...); + + +/** + * Put an environment variable into a command's environment, + * adding it to the beginning of the environment regardless + * of whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_putenv(cmd, LIBEXEC_PREPEND, string)` + * + * @param cmd The command whose environment shall be modified + * @param string The environment variable that should be put into the + * environment; there is no requirement that it contains + * an equal sign + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_putenv_prepend(struct libexec_command *, const char *); + +/** + * Put an environment variable into a command's environment, + * adding it to the beginning of the environment regardless + * of whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_PREPEND, fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment; there is no requirement that + * the resulting string contains an equal sign + * @param args Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(2) int libexec_vputenvf_prepend(struct libexec_command *, const char *, va_list); + +/** + * Put an environment variable into a command's environment, + * adding it to the beginning of the environment regardless + * of whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_putenvf(cmd, LIBEXEC_PREPEND, fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param fmt Formatting string, following the rules of printf(3), + * for the environment variable that should be put + * into the environment; there is no requirement that + * the resulting string contains an equal sign + * @param ... Arguments for `fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(2) int libexec_putenvf_prepend(struct libexec_command *, const char *, ...); + +/** + * Put an environment variable into a command's environment, + * adding it to the beginning of the environment regardless + * of whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_setenv(cmd, LIBEXEC_PREPEND, name, value)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value The value that shall be associated with the + * environment variable + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + */ +int libexec_setenv_prepend(struct libexec_command *, const char *, const char *); + +/** + * Put an environment variable into a command's environment, + * adding it to the beginning of the environment regardless + * of whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_PREPEND, name, value_fmt, args)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param args Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_VPRINTF__(3) int libexec_vsetenvf_prepend(struct libexec_command *, const char *, const char *, va_list); + +/** + * Put an environment variable into a command's environment, + * adding it to the beginning of the environment regardless + * of whether the variable already exists + * + * If the command's environment is in its default state (to + * use the calling process's environment), the calling + * process's environment will first be copied and set as the + * command's environment + * + * Equivalent to `libexec_setenvf(cmd, LIBEXEC_PREPEND, name, value_fmt, ...)` + * + * @param cmd The command whose environment shall be modified + * @param name The name of the environment variable, must not + * contain an equal sign + * @param value_fmt Formatting string, following the rules of printf(3), + * for the environment variable's value + * @param ... Arguments for `value_fmt` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory to modify the command's environment + * @throws EILSEQ According to printf(3) + * @throws EOVERFLOW According to printf(3) + */ +LIBEXEC_PRINTF__(3) int libexec_setenvf_prepend(struct libexec_command *, const char *, const char *, ...); + + +/** + * Put a series of commands in a pipeline, piping each's + * standard output to the next's (when there is one) + * standard input + * + * Pluming data added to the commands will prefix any + * already existing pluming information + * + * @param how Which type of file should be used to pipe + * together commands, and additional options. + * Must be one of the following values: + * - LIBEXEC_PIPE: + * Use a normal pipe(7) + * - LIBEXEC_SOCK_STREAM + * Use a unix(7) socketpair(3) with the type SOCK_STREAM + * - LIBEXEC_SOCK_SEQPACKET + * Use a unix(7) socketpair(3) with the type SOCK_SEQPACKET + * - LIBEXEC_SOCK_DGRAM + * Use a unix(7) socketpair(3) with the type SOCK_DGRAM + * - LIBEXEC_DIRECT_PIPE + * Use an O_DIRECT pipe(7) (see pipe2(2)) + * Optionally, the file type may be OR'ed with + * LIBEXEC_PIPE_CIRCULARLY to pipe the last + * command's standard output to the first command's + * standard input. + * @param cmds List of commands to put in a pipeline + * @param n The number of elements in `cmds` + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory + */ +int libexec_pipe_commandsvn(enum libexec_pipe how, struct libexec_command *const *cmds, size_t n); + +/** + * Put a series of commands in a pipeline, piping each's + * standard output to the next's (when there is one) + * standard input + * + * Pluming data added to the commands will prefix any + * already existing pluming information + * + * @param how Which type of file should be used to pipe + * together commands, and additional options. + * Must be one of the following values: + * - LIBEXEC_PIPE: + * Use a normal pipe(7) + * - LIBEXEC_SOCK_STREAM + * Use a unix(7) socketpair(3) with the type SOCK_STREAM + * - LIBEXEC_SOCK_SEQPACKET + * Use a unix(7) socketpair(3) with the type SOCK_SEQPACKET + * - LIBEXEC_SOCK_DGRAM + * Use a unix(7) socketpair(3) with the type SOCK_DGRAM + * - LIBEXEC_DIRECT_PIPE + * Use an O_DIRECT pipe(7) (see pipe2(2)) + * Optionally, the file type may be OR'ed with + * LIBEXEC_PIPE_CIRCULARLY to pipe the last + * command's standard output to the first command's + * standard input. + * @param cmds `NULL` terminated list of commands to put in a pipeline + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory + */ +int libexec_pipe_commandsv(enum libexec_pipe how, struct libexec_command *const *cmds); + +/** + * Put a series of commands in a pipeline, piping each's + * standard output to the next's (when there is one) + * standard input + * + * Pluming data added to the commands will prefix any + * already existing pluming information + * + * @param how Which type of file should be used to pipe + * together commands, and additional options. + * Must be one of the following values: + * - LIBEXEC_PIPE: + * Use a normal pipe(7) + * - LIBEXEC_SOCK_STREAM + * Use a unix(7) socketpair(3) with the type SOCK_STREAM + * - LIBEXEC_SOCK_SEQPACKET + * Use a unix(7) socketpair(3) with the type SOCK_SEQPACKET + * - LIBEXEC_SOCK_DGRAM + * Use a unix(7) socketpair(3) with the type SOCK_DGRAM + * - LIBEXEC_DIRECT_PIPE + * Use an O_DIRECT pipe(7) (see pipe2(2)) + * Optionally, the file type may be OR'ed with + * LIBEXEC_PIPE_CIRCULARLY to pipe the last + * command's standard output to the first command's + * standard input. + * @param args `NULL` terminated list of commands to put in a pipeline + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory + */ +int libexec_vpipe_commands(enum libexec_pipe how, va_list args); + +/** + * Put a series of commands in a pipeline, piping each's + * standard output to the next's (when there is one) + * standard input + * + * Pluming data added to the commands will prefix any + * already existing pluming information + * + * @param how Which type of file should be used to pipe + * together commands, and additional options. + * Must be one of the following values: + * - LIBEXEC_PIPE: + * Use a normal pipe(7) + * - LIBEXEC_SOCK_STREAM + * Use a unix(7) socketpair(3) with the type SOCK_STREAM + * - LIBEXEC_SOCK_SEQPACKET + * Use a unix(7) socketpair(3) with the type SOCK_SEQPACKET + * - LIBEXEC_SOCK_DGRAM + * Use a unix(7) socketpair(3) with the type SOCK_DGRAM + * - LIBEXEC_DIRECT_PIPE + * Use an O_DIRECT pipe(7) (see pipe2(2)) + * Optionally, the file type may be OR'ed with + * LIBEXEC_PIPE_CIRCULARLY to pipe the last + * command's standard output to the first command's + * standard input. + * @param ... `NULL` terminated list of commands to put in a pipeline + * @return 0 on success, -1 on failure + * + * @throws EINVAL Invalid argument input + * @throws ENOMEM Failed to allocate enough memory + */ +int libexec_pipe_commands(enum libexec_pipe how, ...); + + +int libexec_get_documents(struct libexec_command *cmd, struct libexec_document **docsp, size_t *ndocsp, int flags); +int libexec_exec(struct libexec_command *cmd); +int libexec_spawn(pid_t *out, struct libexec_command *cmd, + int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user), + void *user, struct libexec_document **docsp, size_t *ndocsp, int doc_fd_flags); +int libexec_send_document(struct libexec_document *doc); +int libexec_recv_document(struct libexec_document *doc); + +/* TODO maybe its better to have `struct libexec_run_how` */ +int libexec_run_pipeline(int (*on_alien_epoll)(int alien_epoll, uint32_t events, void *user1), int alien_epoll, void *user1, + int (*on_alien_child_death)(pid_t pid, void *user2), void *user2, + int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user3), void *user3, + int (*reap_mutex_control)(int action /* -1 = acquire, +1 = release */, void *user4), void *user4, + int (*on_interrupt)(void *user5), void *user5, + const sigset_t *sigmask /* nullable */, + struct libexec_document *const *docs, size_t ndocs, int docs_1_level, + struct libexec_command *const *cmds, int *exit_statuses_out /* nullable */, size_t ncmds); +#define LIBEXEC_RUN_PIPELINE_NO_EPOLL NULL, -1, NULL +#define LIBEXEC_RUN_PIPELINE_NO_CHILDREN NULL, NULL +#define LIBEXEC_RUN_PIPELINE_NO_AFTER_FORK NULL, NULL +#define LIBEXEC_RUN_PIPELINE_NO_THREADING NULL, NULL +#define LIBEXEC_RUN_PIPELINE_NO_INTERRUPT NULL, NULL +#define LIBEXEC_RUN_PIPELINE_NO_SIGMASK NULL +#define LIBEXEC_RUN_PIPELINE_NO_OUTPUT NULL, 0, 0 +#define LIBEXEC_RUN_PIPELINE_ONLY_OUTPUT\ + LIBEXEC_RUN_PIPELINE_NO_EPOLL,\ + LIBEXEC_RUN_PIPELINE_NO_CHILDREN,\ + LIBEXEC_RUN_PIPELINE_NO_AFTER_FORK,\ + LIBEXEC_RUN_PIPELINE_NO_THREADING,\ + LIBEXEC_RUN_PIPELINE_NO_INTERRUPT +#define LIBEXEC_RUN_PIPELINE_NOTHING\ + LIBEXEC_RUN_PIPELINE_ONLY_OUTPUT,\ + LIBEXEC_RUN_PIPELINE_NO_OUTPUT + +int libexec_vrun(struct libexec_result *out, va_list args); +int libexec_run(struct libexec_result *out, ...); + + +#endif diff --git a/libexec_add_output_document.c b/libexec_add_output_document.c new file mode 100644 index 0000000..9eb1f8d --- /dev/null +++ b/libexec_add_output_document.c @@ -0,0 +1,45 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_add_output_document(struct libexec_command *cmd, int fd, struct libexec_document *doc, int flags) +{ + int fd_flags, fl_flags, fds[2]; + + if (!cmd || fd < 0 || !doc) { + errno = EINVAL; + return -1; + } + + fd_flags = (flags & O_CLOEXEC); + fl_flags = (flags ^ fd_flags); + if (fd_flags) + fd_flags = FD_CLOEXEC; + + if (pipe(fds)) + return -1; + if (fd_flags && fcntl(fds[0], F_SETFD, fd_flags)) + goto fail; + if (fl_flags && fcntl(fds[0], F_SETFL, fl_flags)) + goto fail; + + doc->fd = fds[0]; + + if (libexec_dup(cmd, fd, fds[1])) + goto fail; + cmd->plumings[cmd->nplumings - 1].type = LIBEXEC_PLUMING_PIPE; + + return 0; + +fail: + close(fds[0]); + close(fds[1]); + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_add_output_fd.c b/libexec_add_output_fd.c new file mode 100644 index 0000000..6eae580 --- /dev/null +++ b/libexec_add_output_fd.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_add_output_fd(struct libexec_command *cmd, int fd, int wr_fd) +{ + return libexec_renumber_fd(cmd, fd, wr_fd); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_add_pluming.c b/libexec_add_pluming.c new file mode 100644 index 0000000..108e8e8 --- /dev/null +++ b/libexec_add_pluming.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_add_pluming(struct libexec_command *cmd, const struct libexec_pluming *pluming) +{ + void *new; + if (!cmd || !pluming) { + errno = EINVAL; + return -1; + } + new = realloc(cmd->plumings, (cmd->nplumings + 1) * sizeof(*cmd->plumings)); + if (!new) + return -1; + cmd->plumings = new; + memcpy(&cmd->plumings[cmd->nplumings++], pluming, sizeof(*pluming)); + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_clear_environ.c b/libexec_clear_environ.c new file mode 100644 index 0000000..9f09561 --- /dev/null +++ b/libexec_clear_environ.c @@ -0,0 +1,72 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_clear_environ(struct libexec_command *cmd) +{ + size_t i; + char **new_env; + + if (!cmd) { + errno = EINVAL; + return -1; + } + + new_env = calloc(1, sizeof(*cmd->environ)); + if (!new_env) + return -1; + + if (cmd->environ) { + for (i = 0; cmd->environ[i]; i++) + free(cmd->environ[i]); + free(cmd->environ); + } + + cmd->environ = new_env; + return 0; +} + + +#else + + +static void +check_clear_from_default(void) +{ + struct libexec_command cmd, ref; + char **old_env; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + ASSERT_IS_NULL(cmd.environ); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + old_env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = old_env; + + libexec_destroy_command(&cmd); +} + + +int +main(void) +{ + errno = 0; + ASSERT_EQ_INT(libexec_clear_environ(NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + + check_clear_from_default(); + /* XXX test malloc failure */ + /* XXX test that function deallocates */ + return 0; +} + + +#endif diff --git a/libexec_close.c b/libexec_close.c new file mode 100644 index 0000000..b1af338 --- /dev/null +++ b/libexec_close.c @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_close(struct libexec_command *cmd, int fd) +{ + struct libexec_pluming pluming; + if (fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_CLOSE; + return libexec_add_pluming(cmd, &pluming); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_construct_command.c b/libexec_construct_command.c new file mode 100644 index 0000000..36b18f9 --- /dev/null +++ b/libexec_construct_command.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_construct_command(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vconstruct_command(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_copy_environ.c b/libexec_copy_environ.c new file mode 100644 index 0000000..2c7e19f --- /dev/null +++ b/libexec_copy_environ.c @@ -0,0 +1,51 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_copy_environ(struct libexec_command *cmd, const char *const *env) +{ + size_t i, n; + char **new_env; + + if (!cmd) { + errno = EINVAL; + return -1; + } + + if (!env) + env = *(const char *const **)(void *)&environ; + + n = 0; + while (env[n]) + n++; + + new_env = calloc((n + 1), sizeof(*cmd->environ)); + if (!new_env) + return -1; + + for (i = 0; i < n; i++) { + new_env[i] = strdup(env[i]); + if (!new_env[i]) { + while (i--) + free(new_env[i]); + free(new_env); + return -1; + } + } + + if (cmd->environ) { + for (i = 0; cmd->environ[i]; i++) + free(cmd->environ[i]); + free(cmd->environ); + } + + cmd->environ = new_env; + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_destroy_command.c b/libexec_destroy_command.c new file mode 100644 index 0000000..d5382fb --- /dev/null +++ b/libexec_destroy_command.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_destroy_command(struct libexec_command *cmd) +{ + size_t i; + + if (!cmd) + return; + + free(cmd->executable); + + for (i = 0; i < cmd->narguments; i++) + free(cmd->arguments[i]); + free(cmd->arguments); + + for (i = 0; i < cmd->nplumings; i++) + libexec_destroy_pluming(&cmd->plumings[i]); + free(cmd->plumings); + + if (cmd->environ) { + for (i = 0; cmd->environ[i]; i++) + free(cmd->environ[i]); + free(cmd->environ); + } + + *cmd = LIBEXEC_COMMAND_INIT; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_destroy_document.c b/libexec_destroy_document.c new file mode 100644 index 0000000..aee6298 --- /dev/null +++ b/libexec_destroy_document.c @@ -0,0 +1,28 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_destroy_document(struct libexec_document *doc) +{ + int ret = 0; + + if (!doc) + return 0; + + free(doc->text); + doc->text = NULL; + doc->length = 0; + doc->offset = 0; + if (doc->fd >= 0) { + ret = close(doc->fd); + doc->fd = -1; + } + return ret; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_destroy_pluming.c b/libexec_destroy_pluming.c new file mode 100644 index 0000000..6aae3c7 --- /dev/null +++ b/libexec_destroy_pluming.c @@ -0,0 +1,31 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_destroy_pluming(struct libexec_pluming *pluming) +{ + if (!pluming) + return; + + if (pluming->type == LIBEXEC_PLUMING_OPENAT || + pluming->type == LIBEXEC_PLUMING_OPENAT2) { + free(pluming->target.file); + pluming->target.file = NULL; + + } else if (pluming->type == LIBEXEC_PLUMING_DOCUMENT) { + free(pluming->target.text); + pluming->target.text = NULL; + + } else if (pluming->type == LIBEXEC_PLUMING_PIPE) { + if (pluming->target.fd >= 0) + close(pluming->target.fd); + pluming->target.fd = -1; + } +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_destroy_result.c b/libexec_destroy_result.c new file mode 100644 index 0000000..0587c3b --- /dev/null +++ b/libexec_destroy_result.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_destroy_result(struct libexec_result *result) +{ + if (!result) + return; + + free(result->exit_statuses); + result->exit_statuses = NULL; + result->proc_count = 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_dup.c b/libexec_dup.c new file mode 100644 index 0000000..674acb7 --- /dev/null +++ b/libexec_dup.c @@ -0,0 +1,24 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_dup(struct libexec_command *cmd, int fd, int old_fd) +{ + struct libexec_pluming pluming; + if (fd < 0 || old_fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_DUP2; + pluming.target.fd = old_fd; + return libexec_add_pluming(cmd, &pluming); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_exec.c b/libexec_exec.c new file mode 100644 index 0000000..3f306d0 --- /dev/null +++ b/libexec_exec.c @@ -0,0 +1,200 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +static char * +which(const char *name, char *path, char **end_colon_out) +{ + size_t namelen_nul, dirlen, maxlen; + char *p, *q, *buf; + int eacces_encountered = 0; + + if (!path || !*path) + goto enoent; + + maxlen = 0; + for (p = path; p; p = q) { + q = strchr(p, ':'); + if (q) + dirlen = (size_t)(q++ - p); + else + dirlen = strlen(p); + if (dirlen > maxlen) + dirlen = maxlen; + } + + namelen_nul = strlen(name) + 1; + + buf = NULL; + if (maxlen) { + buf = malloc(maxlen + 1 + namelen_nul); + if (!buf) + return NULL; + } + + for (; path; path = q) { + q = strchr(p, ':'); + if (q) + dirlen = (size_t)(q++ - path); + else + dirlen = strlen(path); + p = buf; + if (dirlen) { + memcpy(p, path, dirlen); + p = &p[dirlen]; + *p++ = '/'; + } + memcpy(p, name, namelen_nul); + if (!faccessat(AT_FDCWD, buf, X_OK, AT_EACCESS)) { + *end_colon_out = q; + return buf; + } + if (errno == EACCES) + eacces_encountered = 1; + } + + free(buf); + + if (eacces_encountered) { + errno = EACCES; + return NULL; + } + +enoent: + errno = ENOENT; + return NULL; +} + + +static void +exec_path(struct libexec_command *cmd) +{ + const char *file = cmd->executable ? cmd->executable : cmd->arguments[0]; + execve(file, cmd->arguments, cmd->environ ? cmd->environ : environ); +} + + +static void +exec_name(struct libexec_command *cmd) +{ + /* execvpe is non-standard */ + const char *file = cmd->executable ? cmd->executable : cmd->arguments[0]; + char *end_colon = NULL, *file_free = NULL; + if (!strchr(file, '/')) { + file = file_free = which(file, getenv("PATH"), &end_colon); + if (!file) + return; + } + execve(file, cmd->arguments, cmd->environ ? cmd->environ : environ); + free(file_free); + if (end_colon) + *end_colon = ':'; +} + + +static void +exec_fd(struct libexec_command *cmd) +{ + fexecve(cmd->exec_fd, cmd->arguments, cmd->environ ? cmd->environ : environ); +} + + +static void +exec_at(struct libexec_command *cmd) +{ + const char *file = cmd->executable ? cmd->executable : cmd->arguments[0]; + execveat(cmd->exec_fd, file, cmd->arguments, cmd->environ ? cmd->environ : environ, 0); +} + + +int +libexec_exec(struct libexec_command *cmd) +{ + void (*exec_function)(struct libexec_command *cmd); + size_t i; + int fd; + + if (!cmd) + goto einval; + + if (cmd->library_version > LIBEXEC_VERSION) + abort(); + + if (!cmd->narguments) + goto einval; + + if (cmd->exec_how == LIBEXEC_REQUIRE_PATH) + exec_function = &exec_path; + else if (cmd->exec_how == LIBEXEC_ALLOW_NAME) + exec_function = &exec_name; + else if (cmd->exec_how == LIBEXEC_EXEC_FD) + exec_function = &exec_fd; + else if (cmd->exec_how == LIBEXEC_EXEC_AT) + exec_function = &exec_at; + else + goto einval; + + for (i = 0; i < cmd->nplumings; i++) { + switch (cmd->plumings[i].type) { + case LIBEXEC_PLUMING_CLOSE: + if (cmd->plumings[i].fd >= 0) { + close(cmd->plumings[i].fd); + cmd->plumings[i].fd = -1; + } + break; + +#if defined(__linux__) + case LIBEXEC_PLUMING_OPENAT2: + fd = (int)syscall(SYS_openat2, + cmd->plumings[i].target.dirfd, cmd->plumings[i].target.file, + cmd->plumings[i].target.how, cmd->plumings[i].target.how_size); + if (fd < 0) + return -1; + free(cmd->plumings[i].target.file); + free(cmd->plumings[i].target.how); + goto openned_fd; +#endif + + case LIBEXEC_PLUMING_OPENAT: + fd = openat(cmd->plumings[i].target.dirfd, cmd->plumings[i].target.file, + cmd->plumings[i].target.flags, cmd->plumings[i].target.mode); + if (fd < 0) + return -1; + free(cmd->plumings[i].target.file); +#if defined(__linux__) + openned_fd: +#endif + cmd->plumings[i].type = LIBEXEC_PLUMING_PIPE; + cmd->plumings[i].target.fd = fd; + /* fall through */ + + case LIBEXEC_PLUMING_DUP2: + case LIBEXEC_PLUMING_PIPE: + if (cmd->plumings[i].fd == cmd->plumings[i].target.fd) + break; + if (dup2(cmd->plumings[i].target.fd, cmd->plumings[i].fd) == -1) + return -1; + if (cmd->plumings[i].type == LIBEXEC_PLUMING_PIPE) + close(cmd->plumings[i].target.fd); + cmd->plumings[i].target.fd = cmd->plumings[i].fd; + break; + + default: + case LIBEXEC_PLUMING_DOCUMENT: + goto einval; + } + } + + (*exec_function)(cmd); + return -1; + +einval: + errno = EINVAL; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_get_documents.c b/libexec_get_documents.c new file mode 100644 index 0000000..2bdadf8 --- /dev/null +++ b/libexec_get_documents.c @@ -0,0 +1,83 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_get_documents(struct libexec_command *cmd, struct libexec_document **docsp, size_t *ndocsp, int flags) +{ + typedef int fdpair[2]; + size_t i, j, n; + void *new; + fdpair *pipes; + int error; + int fd_flags, fl_flags; + + if (!cmd || !docsp || !ndocsp) { + errno = EINVAL; + return -1; + } + + fd_flags = (flags & O_CLOEXEC); + fl_flags = (flags ^ fd_flags); + if (fd_flags) + fd_flags = FD_CLOEXEC; + + n = 0; + for (i = 0; i < cmd->nplumings; i++) + if (cmd->plumings[i].type == LIBEXEC_PLUMING_DOCUMENT) + n += 1; + + pipes = malloc(n * sizeof(*pipes)); + if (!pipes) + return -1; + + j = 0; + for (i = 0; i < cmd->nplumings; i++) { + if (cmd->plumings[i].type == LIBEXEC_PLUMING_DOCUMENT) { + if (pipe(pipes[j])) + goto fail; + j++; + if (fd_flags && fcntl(pipes[j - 1][1], F_SETFD, fd_flags)) + goto fail; + if (fl_flags && fcntl(pipes[j - 1][1], F_SETFL, fl_flags)) + goto fail; + } + } + + new = realloc(*docsp, (*ndocsp + n) * sizeof(**docsp)); + if (!new) + goto fail; + *docsp = new; + + for (i = 0, j = 0; i < cmd->nplumings; i++) { + if (cmd->plumings[i].type != LIBEXEC_PLUMING_DOCUMENT) + continue; + (*docsp)[j].user = 0; + (*docsp)[j].fd = pipes[j][1]; + (*docsp)[j].text = cmd->plumings[i].target.text; + (*docsp)[j].length = cmd->plumings[i].target.len; + (*docsp)[j].offset = 0; + cmd->plumings[i].target.fd = pipes[j][0]; + cmd->plumings[i].type = LIBEXEC_PLUMING_PIPE; + j++; + } + + free(pipes); + return 0; + +fail: + error = errno; + while (j--) { + close(pipes[j][0]); + close(pipes[j][1]); + } + free(pipes); + errno = error; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_getenv.c b/libexec_getenv.c new file mode 100644 index 0000000..5099b28 --- /dev/null +++ b/libexec_getenv.c @@ -0,0 +1,122 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +const char * +libexec_getenv(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, len; + + if (!cmd || !name) { + errno = EINVAL; + return NULL; + } + + if (strchr(name, '=')) + return NULL; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + for (i = 0; env[i]; i++) + if (!strncmp(env[i], name, len) && env[i][len] == '=') + return &env[i][len + 1]; + + return NULL; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + env = environ; + environ = calloc(12, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("AA=aa"); + environ[1] = strdup("A=a0"); + environ[2] = strdup("B=b0"); + environ[3] = strdup("A=a1"); + environ[4] = strdup("B=b1"); + environ[5] = strdup("A=a2"); + environ[6] = strdup("B=b2"); + environ[7] = strdup("A=a3"); + environ[8] = strdup("B=b3"); + environ[9] = strdup("BB=bb"); + environ[10] = strdup("X=Y=Z"); + ASSERT_NOT_NULL(environ[0]); + ASSERT_NOT_NULL(environ[1]); + ASSERT_NOT_NULL(environ[2]); + ASSERT_NOT_NULL(environ[3]); + ASSERT_NOT_NULL(environ[4]); + ASSERT_NOT_NULL(environ[5]); + ASSERT_NOT_NULL(environ[6]); + ASSERT_NOT_NULL(environ[7]); + ASSERT_NOT_NULL(environ[8]); + ASSERT_NOT_NULL(environ[9]); + ASSERT_NOT_NULL(environ[10]); + ASSERT_IS_NULL(environ[11]); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, "PATH")); + ASSERT_IS_TRUE(errno == 0); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(NULL, "x")); + ASSERT_EQ_INT(errno, EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, "x")); + ASSERT_IS_TRUE(errno == 0); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, NULL)); + ASSERT_EQ_INT(errno, EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, "X=Y")); + ASSERT_IS_TRUE(errno == 0 || errno == EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, "")); + ASSERT_IS_TRUE(errno == 0); + + ASSERT_EQ_STR(libexec_getenv(&cmd, "X"), "Y=Z"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "AA"), "aa"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "BB"), "bb"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "A"), "a0"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "B"), "b0"); + + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + libexec_destroy_command(&cmd); + + ASSERT_IS_NULL(libexec_getenv(&cmd, "PATH")); + ASSERT_IS_NULL(libexec_getenv(NULL, "x")); + ASSERT_IS_NULL(libexec_getenv(&cmd, "x")); + ASSERT_IS_NULL(libexec_getenv(&cmd, NULL)); + ASSERT_IS_NULL(libexec_getenv(&cmd, "X=Y")); + ASSERT_IS_NULL(libexec_getenv(&cmd, "")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "X"), "Y=Z"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "AA"), "aa"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "BB"), "bb"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "A"), "a0"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "B"), "b0"); + + libexec_set_environ(&cmd, environ); + environ = env; + + return 0; +} + + +#endif diff --git a/libexec_getenv_last.c b/libexec_getenv_last.c new file mode 100644 index 0000000..1de318e --- /dev/null +++ b/libexec_getenv_last.c @@ -0,0 +1,123 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +const char * +libexec_getenv_last(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, len; + const char *ret = NULL; + + if (!cmd || !name) { + errno = EINVAL; + return NULL; + } + + if (strchr(name, '=')) + return NULL; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + for (i = 0; env[i]; i++) + if (!strncmp(env[i], name, len) && env[i][len] == '=') + ret = &env[i][len + 1]; + + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + env = environ; + environ = calloc(12, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("AA=aa"); + environ[1] = strdup("A=a0"); + environ[2] = strdup("B=b0"); + environ[3] = strdup("A=a1"); + environ[4] = strdup("B=b1"); + environ[5] = strdup("A=a2"); + environ[6] = strdup("B=b2"); + environ[7] = strdup("A=a3"); + environ[8] = strdup("B=b3"); + environ[9] = strdup("BB=bb"); + environ[10] = strdup("X=Y=Z"); + ASSERT_NOT_NULL(environ[0]); + ASSERT_NOT_NULL(environ[1]); + ASSERT_NOT_NULL(environ[2]); + ASSERT_NOT_NULL(environ[3]); + ASSERT_NOT_NULL(environ[4]); + ASSERT_NOT_NULL(environ[5]); + ASSERT_NOT_NULL(environ[6]); + ASSERT_NOT_NULL(environ[7]); + ASSERT_NOT_NULL(environ[8]); + ASSERT_NOT_NULL(environ[9]); + ASSERT_NOT_NULL(environ[10]); + ASSERT_IS_NULL(environ[11]); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "PATH")); + ASSERT_IS_TRUE(errno == 0); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(NULL, "x")); + ASSERT_EQ_INT(errno, EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "x")); + ASSERT_IS_TRUE(errno == 0); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, NULL)); + ASSERT_EQ_INT(errno, EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "X=Y")); + ASSERT_IS_TRUE(errno == 0 || errno == EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "")); + ASSERT_IS_TRUE(errno == 0); + + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "X"), "Y=Z"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "AA"), "aa"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "BB"), "bb"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "A"), "a3"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "B"), "b3"); + + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + libexec_destroy_command(&cmd); + + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "PATH")); + ASSERT_IS_NULL(libexec_getenv_last(NULL, "x")); + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "x")); + ASSERT_IS_NULL(libexec_getenv_last(&cmd, NULL)); + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "X=Y")); + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "")); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "X"), "Y=Z"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "AA"), "aa"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "BB"), "bb"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "A"), "a3"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "B"), "b3"); + + libexec_set_environ(&cmd, environ); + environ = env; + + return 0; +} + + +#endif diff --git a/libexec_init_command.c b/libexec_init_command.c new file mode 100644 index 0000000..c49af27 --- /dev/null +++ b/libexec_init_command.c @@ -0,0 +1,43 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_init_command(struct libexec_command *cmd) +{ + if (!cmd) + return; + + *cmd = LIBEXEC_COMMAND_INIT; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd; + + memset(&cmd, 127, sizeof(cmd)); + libexec_init_command(&cmd); + + ASSERT_EQ_INT(cmd.library_version, LIBEXEC_VERSION); + ASSERT_EQ_ENUM(cmd.exec_how, LIBEXEC_ALLOW_NAME); + ASSERT_EQ_INT(cmd.exec_fd, -1); + ASSERT_IS_NULL(cmd.executable); + ASSERT_IS_NULL(cmd.arguments); + ASSERT_EQ_UINT(cmd.narguments, 0); + ASSERT_IS_NULL(cmd.plumings); + ASSERT_EQ_UINT(cmd.nplumings, 0); + ASSERT_IS_NULL(cmd.environ); + + libexec_init_command(NULL); + + return 0; +} + + +#endif diff --git a/libexec_input_data_copy.c b/libexec_input_data_copy.c new file mode 100644 index 0000000..bb401e0 --- /dev/null +++ b/libexec_input_data_copy.c @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_input_data_copy(struct libexec_command *cmd, int fd, const char *data, size_t len) +{ + char *copy = malloc(len); + if (!copy) + return -1; + memcpy(copy, data, len); + if (libexec_input_data_gift(cmd, fd, copy, len)) { + free(copy); + return -1; + } + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_input_data_gift.c b/libexec_input_data_gift.c new file mode 100644 index 0000000..77ee80f --- /dev/null +++ b/libexec_input_data_gift.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_input_data_gift(struct libexec_command *cmd, int fd, char *data, size_t len) +{ + struct libexec_pluming pluming; + if (fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_DOCUMENT; + pluming.target.text = data; + pluming.target.len = len; + return libexec_add_pluming(cmd, &pluming); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_input_text_copy.c b/libexec_input_text_copy.c new file mode 100644 index 0000000..da12bfd --- /dev/null +++ b/libexec_input_text_copy.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_input_text_copy(struct libexec_command *cmd, int fd, const char *text) +{ + if (!text) { + errno = EINVAL; + return -1; + } + return libexec_input_data_copy(cmd, fd, text, strlen(text)); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_input_text_gift.c b/libexec_input_text_gift.c new file mode 100644 index 0000000..94b09c3 --- /dev/null +++ b/libexec_input_text_gift.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_input_text_gift(struct libexec_command *cmd, int fd, char *text) +{ + if (!text) { + errno = EINVAL; + return -1; + } + return libexec_input_data_gift(cmd, fd, text, strlen(text)); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_open.c b/libexec_open.c new file mode 100644 index 0000000..7aa2698 --- /dev/null +++ b/libexec_open.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_open(struct libexec_command *cmd, int fd, const char *file, int flags, mode_t mode) +{ + return libexec_openat(cmd, fd, AT_FDCWD, file, flags, mode); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_openat.c b/libexec_openat.c new file mode 100644 index 0000000..41498ba --- /dev/null +++ b/libexec_openat.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_openat(struct libexec_command *cmd, int fd, int dirfd, const char *file, int flags, mode_t mode) +{ + struct libexec_pluming pluming; + if (fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_OPENAT; + pluming.target.dirfd = dirfd; + pluming.target.flags = flags; + pluming.target.mode = mode; + pluming.target.file = NULL; + if (file) { + pluming.target.file = strdup(file); + if (!pluming.target.file) + return -1; + } + if (libexec_add_pluming(cmd, &pluming)) { + free(pluming.target.file); + return -1; + } + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_openat2.c b/libexec_openat2.c new file mode 100644 index 0000000..ff97bff --- /dev/null +++ b/libexec_openat2.c @@ -0,0 +1,54 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_openat2(struct libexec_command *cmd, int fd, int dirfd, const char *file, struct open_how *how, size_t size) +{ +#if defined(__linux__) + + struct libexec_pluming pluming; + if (fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_OPENAT2; + pluming.target.dirfd = dirfd; + pluming.target.how = NULL; + pluming.target.file = NULL; + if (file) { + pluming.target.file = strdup(file); + if (!pluming.target.file) + return -1; + } + if (size) { + pluming.target.how = malloc(size); + if (!pluming.target.how) { + free(pluming.target.file); + return -1; + } + memcpy(pluming.target.how, how, size); + pluming.target.how_size = size; + } + if (libexec_add_pluming(cmd, &pluming)) { + free(pluming.target.file); + free(pluming.target.how); + return -1; + } + return 0; + +#else + + errno = ENOSYS; + return -1; + +#endif +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_pipe_commands.c b/libexec_pipe_commands.c new file mode 100644 index 0000000..580bf86 --- /dev/null +++ b/libexec_pipe_commands.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_pipe_commands(enum libexec_pipe how, ...) +{ + int ret; + va_list args; + va_start(args, how); + ret = libexec_vpipe_commands(how, args); + va_end(args); + return ret; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_pipe_commandsv.c b/libexec_pipe_commandsv.c new file mode 100644 index 0000000..ee96b7d --- /dev/null +++ b/libexec_pipe_commandsv.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_pipe_commandsv(enum libexec_pipe how, struct libexec_command *const *cmds) +{ + size_t n = 0; + if (!cmds) { + errno = EINVAL; + return -1; + } + while (cmds[n]) + n++; + return libexec_pipe_commandsvn(how, cmds, n); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_pipe_commandsvn.c b/libexec_pipe_commandsvn.c new file mode 100644 index 0000000..d6aa1d5 --- /dev/null +++ b/libexec_pipe_commandsvn.c @@ -0,0 +1,106 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +static void +remove_last_pluming(struct libexec_command *cmd) +{ + if (!cmd->nplumings || !cmd->plumings) + abort(); + if (cmd->plumings[--cmd->nplumings].type != LIBEXEC_PLUMING_PIPE) + abort(); + close(cmd->plumings[cmd->nplumings].target.fd); + if (!cmd->nplumings) { + free(cmd->plumings); + cmd->plumings = NULL; + } +} + + +static int +pipe_two_commands(enum libexec_pipe how, struct libexec_command *left, struct libexec_command *right) +{ + int fds[2], r; + struct libexec_pluming tmp; + + if (!left || !right) { + einval: + errno = EINVAL; + return -1; + } + + if (how == LIBEXEC_PIPE) + r = pipe(fds); + else if (how == LIBEXEC_SOCK_STREAM) + r = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); + else if (how == LIBEXEC_SOCK_SEQPACKET) + r = socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds); + else if (how == LIBEXEC_SOCK_DGRAM) + r = socketpair(PF_UNIX, SOCK_DGRAM, 0, fds); + else if (how == LIBEXEC_DIRECT_PIPE) + r = pipe2(fds, O_DIRECT); + else + goto einval; + if (r) + return -1; + + if (libexec_dup(left, STDOUT_FILENO, fds[1])) { + close(fds[0]); + close(fds[1]); + return -1; + } + left->plumings[left->nplumings - 1].type = LIBEXEC_PLUMING_PIPE; + + if (libexec_dup(right, STDIN_FILENO, fds[0])) { + remove_last_pluming(left); + close(fds[0]); + return -1; + } + right->plumings[right->nplumings - 1].type = LIBEXEC_PLUMING_PIPE; + + /* pluming shall be at the front */ + tmp = left->plumings[left->nplumings - 1]; + memcpy(&left->plumings[1], &left->plumings[0], (left->nplumings - 1) * sizeof(*left->plumings)); + left->plumings[0] = tmp; + tmp = right->plumings[right->nplumings - 1]; + memcpy(&right->plumings[1], &right->plumings[0], (right->nplumings - 1) * sizeof(*right->plumings)); + right->plumings[0] = tmp; + + return 0; +} + + +int +libexec_pipe_commandsvn(enum libexec_pipe how, struct libexec_command *const *cmds, size_t n) +{ + enum libexec_pipe circular; + size_t i; + + circular = how; + circular &= LIBEXEC_PIPE_CIRCULARLY; + how ^= circular; + + for (i = 1; i < n; i++) + if (pipe_two_commands(how, cmds[i - 1], cmds[i])) + goto fail; + + if (circular && n) + if (pipe_two_commands(how, cmds[n - 1], cmds[0])) + goto fail; + + return 0; + +fail: + i -= 1; + while (i) { + remove_last_pluming(cmds[i--]); + remove_last_pluming(cmds[i]); + } + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_put_arguments.c b/libexec_put_arguments.c new file mode 100644 index 0000000..45f7f1a --- /dev/null +++ b/libexec_put_arguments.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_put_arguments(struct libexec_command *cmd, const char *const *args) +{ + size_t n = 0; + if (!args) { + errno = EINVAL; + return -1; + } + while (args[n]) + n += 1; + return libexec_put_argumentsn(cmd, args, n); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_put_argumentsn.c b/libexec_put_argumentsn.c new file mode 100644 index 0000000..9d09ec0 --- /dev/null +++ b/libexec_put_argumentsn.c @@ -0,0 +1,45 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_put_argumentsn(struct libexec_command *cmd, const char *const *args, size_t nargs) +{ + size_t i; + void *new; + + if (!cmd) { + errno = EINVAL; + return -1; + } + + new = realloc(cmd->arguments, (cmd->narguments + nargs + 1) * sizeof(*cmd->arguments)); + if (!new) + return -1; + cmd->arguments = new; + + for (i = 0; i < nargs; i++) { + cmd->arguments[cmd->narguments + i] = strdup(args[i]); + if (!cmd->arguments[cmd->narguments + i]) { + while (i) + free(cmd->arguments[cmd->narguments + --i]); + if (!cmd->narguments) { + free(cmd->arguments); + cmd->arguments = NULL; + } + return -1; + } + } + + cmd->arguments[cmd->narguments + nargs] = NULL; + cmd->narguments += nargs; + + return 0; + +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_putenv.c b/libexec_putenv.c new file mode 100644 index 0000000..366f5fb --- /dev/null +++ b/libexec_putenv.c @@ -0,0 +1,144 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv(struct libexec_command *cmd, enum libexec_insert_mode how, const char *string) +{ + char **env; + char *copy, *eqsign; + size_t i, n, len; + int environ_copied = 0; + void *new; + + if (!cmd || !string) { + errno = EINVAL; + return -1; + } + + env = cmd->environ ? cmd->environ : environ; + + if (how == LIBEXEC_APPEND || how == LIBEXEC_PREPEND) { + n = 0; + while (env[n]) + n++; + + just_add: + copy = strdup(string); + if (!copy) + return -1; + + if (!cmd->environ) { + if (libexec_copy_environ(cmd, NULL)) { + free(copy); + return -1; + } + environ_copied = 1; + } + + new = realloc(cmd->environ, (n + 2) * sizeof(*cmd->environ)); + if (!new) { + if (environ_copied) { + while (n) + free(cmd->environ[--n]); + free(cmd->environ); + cmd->environ = NULL; + } + free(copy); + return -1; + } + cmd->environ = new; + + if (how == LIBEXEC_APPEND) { + cmd->environ[n] = copy; + cmd->environ[n + 1] = NULL; + } else { + memmove(&cmd->environ[1], cmd->environ, (n + 1) * sizeof(*cmd->environ)); + cmd->environ[0] = copy; + } + + + } else { + eqsign = strchr(string, '='); + + if (!eqsign || (how != LIBEXEC_REPLACE && how != LIBEXEC_NOCLOBBER && how != LIBEXEC_NOREPLACE)) { + errno = EINVAL; + return -1; + } + + len = (size_t)(eqsign - string) + 1; + for (i = 0; env[i]; i++) + if (!strncmp(env[i], string, len)) + break; + + if (!env[i]) { + n = i; + how = LIBEXEC_APPEND; + goto just_add; + } + + if (how == LIBEXEC_NOREPLACE || !strcmp(env[i], string)) { + if (!cmd->environ) { + if (libexec_copy_environ(cmd, NULL)) { + free(copy); + return -1; + } + } + return 0; + } + if (how == LIBEXEC_NOCLOBBER) + return -1; /* do not change errno */ + + copy = strdup(string); + if (!copy) + return -1; + + if (!cmd->environ) { + if (libexec_copy_environ(cmd, NULL)) { + free(copy); + return -1; + } + } + + free(cmd->environ[i]); + cmd->environ[i] = copy; + + } + return 0; +} + + +#else + + +int +main(void) +{ + /* Correct usage is tested via + * libexec_putenv_append.c, + * libexec_putenv_noclobber.c, + * libexec_putenv_noreplace.c, + * libexec_putenv_prepend.c, and + * libexec_putenv_replace.c */ + + struct libexec_command cmd, ref; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv(&cmd, (enum libexec_insert_mode)-1, "A=B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv(&cmd, (enum libexec_insert_mode)LIBEXEC_INSERT_MODE__COUNT__, "A=B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + return 0; +} + + +#endif diff --git a/libexec_putenv_append.c b/libexec_putenv_append.c new file mode 100644 index 0000000..bb45e1c --- /dev/null +++ b/libexec_putenv_append.c @@ -0,0 +1,235 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_append(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_APPEND, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_append(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "B=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "BB=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[7]); + ASSERT_EQ_STR(cmd.environ[7], "B=F"); + ASSERT_IS_NULL(cmd.environ[8]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenv_noclobber.c b/libexec_putenv_noclobber.c new file mode 100644 index 0000000..e9cf3ed --- /dev/null +++ b/libexec_putenv_noclobber.c @@ -0,0 +1,175 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_noclobber(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_NOCLOBBER, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(&cmd, "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(&cmd, "A=D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(&cmd, "A=D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenv_noreplace.c b/libexec_putenv_noreplace.c new file mode 100644 index 0000000..b33708a --- /dev/null +++ b/libexec_putenv_noreplace.c @@ -0,0 +1,171 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_noreplace(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_NOREPLACE, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noreplace(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noreplace(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noreplace(&cmd, "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenv_prepend.c b/libexec_putenv_prepend.c new file mode 100644 index 0000000..840978d --- /dev/null +++ b/libexec_putenv_prepend.c @@ -0,0 +1,235 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_prepend(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_PREPEND, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_prepend(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=D"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[7]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X=0"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X=0"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "X=0"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X=0"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X=0"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "X=0"); + ASSERT_IS_NULL(cmd.environ[7]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=D"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[7]); + ASSERT_EQ_STR(cmd.environ[7], "X=0"); + ASSERT_IS_NULL(cmd.environ[8]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenv_replace.c b/libexec_putenv_replace.c new file mode 100644 index 0000000..c62ad01 --- /dev/null +++ b/libexec_putenv_replace.c @@ -0,0 +1,171 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_replace(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_REPLACE, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_replace(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_replace(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_replace(&cmd, "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf.c b/libexec_putenvf.c new file mode 100644 index 0000000..7729fc4 --- /dev/null +++ b/libexec_putenvf.c @@ -0,0 +1,61 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf(cmd, how, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + /* Correct usage is tested, indirectly, via + * libexec_putenvf_append.c, + * libexec_putenvf_noclobber.c, + * libexec_putenvf_noreplace.c, + * libexec_putenvf_prepend.c, and + * libexec_putenvf_replace.c */ + + struct libexec_command cmd, ref; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf(&cmd, (enum libexec_insert_mode)-1, "A=%s", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf(&cmd, (enum libexec_insert_mode)LIBEXEC_INSERT_MODE__COUNT__, "A=%s", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + libexec_clear_environ(&cmd); + ASSERT_ZERO(libexec_putenvf(&cmd, LIBEXEC_NOCLOBBER, "PATH=%s:%s", "test", "path")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "test:path"); + errno = 0; + ASSERT_EQ_INT(libexec_putenvf(&cmd, LIBEXEC_NOCLOBBER, "PATH=%s", "x"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "test:path"); + ASSERT_ZERO(libexec_putenvf(&cmd, LIBEXEC_REPLACE, "PATH=%s%s", "my", "path")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "mypath"); + libexec_destroy_command(&cmd); + + return 0; +} + + +#endif diff --git a/libexec_putenvf_append.c b/libexec_putenvf_append.c new file mode 100644 index 0000000..8ceb806 --- /dev/null +++ b/libexec_putenvf_append.c @@ -0,0 +1,240 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_append(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_append(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_append(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s", "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s%s", "A=", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "AA%sE", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s%s%s%s", "B", "B", "=", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "B=%s", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "B=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s=%s=%s", "A", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "A%sD", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s", "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s%s%s", "A", "A=", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "BB=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[7]); + ASSERT_EQ_STR(cmd.environ[7], "B=F"); + ASSERT_IS_NULL(cmd.environ[8]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf_noclobber.c b/libexec_putenvf_noclobber.c new file mode 100644 index 0000000..988e381 --- /dev/null +++ b/libexec_putenvf_noclobber.c @@ -0,0 +1,180 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_noclobber(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_noclobber(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(&cmd, "%s", "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "%s%s%s", "A", "=", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "%s=%s", "A=B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(&cmd, "%s", "A=D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "AA%sE", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "%sB=F", "B")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "%sB=F", "")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(&cmd, "A=D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf_noreplace.c b/libexec_putenvf_noreplace.c new file mode 100644 index 0000000..bcde8a2 --- /dev/null +++ b/libexec_putenvf_noreplace.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_noreplace(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_noreplace(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noreplace(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noreplace(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noreplace(&cmd, "%s", "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "%s", "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "%s=%s", "A=B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "A=%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "AA%sE", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "%s", "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf_prepend.c b/libexec_putenvf_prepend.c new file mode 100644 index 0000000..2e51a72 --- /dev/null +++ b/libexec_putenvf_prepend.c @@ -0,0 +1,240 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_prepend(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_prepend(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_prepend(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s", "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s%s", "A=", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "AA%sE", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s%s%s%s", "B", "B", "=", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "B=%s", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=D"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[7]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s=%s=%s", "A", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X=0"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "A%sD", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X=0"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s", "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "X=0"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s%s%s", "A", "A=", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X=0"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X=0"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "X=0"); + ASSERT_IS_NULL(cmd.environ[7]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=D"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[7]); + ASSERT_EQ_STR(cmd.environ[7], "X=0"); + ASSERT_IS_NULL(cmd.environ[8]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf_replace.c b/libexec_putenvf_replace.c new file mode 100644 index 0000000..7cfd773 --- /dev/null +++ b/libexec_putenvf_replace.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_replace(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_replace(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_replace(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_replace(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_replace(&cmd, "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "%s", "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "%s%s", "A=B", "=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "%s=%s", "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "%s=E", "AA")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "BB%sF", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_recv_document.c b/libexec_recv_document.c new file mode 100644 index 0000000..8eb0be6 --- /dev/null +++ b/libexec_recv_document.c @@ -0,0 +1,53 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_recv_document(struct libexec_document *doc) +{ + ssize_t r; + void *new; + size_t new_size; + + if (!doc) { + errno = EINVAL; + return -1; + } + + for (;;) { + if (doc->length == doc->alloc_size) { + new_size = doc->alloc_size + 8096; + new = realloc(doc->text, new_size); + if (!new) + return -1; + doc->text = new; + doc->alloc_size = new_size; + } + r = read(doc->fd, &doc->text[doc->length], doc->alloc_size - doc->length); + if (r <= 0) { + if (!r) + goto done; + return -1; + } + doc->length += (size_t)r; + } + return 0; + +done: + if (doc->length == doc->alloc_size) { + new_size = doc->alloc_size + 1; + new = realloc(doc->text, new_size); + if (!new) + return -1; + doc->text = new; + doc->alloc_size = new_size; + } + doc->text[doc->length] = '\0'; + return 1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_renumber_fd.c b/libexec_renumber_fd.c new file mode 100644 index 0000000..d9350fa --- /dev/null +++ b/libexec_renumber_fd.c @@ -0,0 +1,32 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_renumber_fd(struct libexec_command *cmd, int fd, int old_fd) +{ + if (fd == old_fd) { + if (!cmd || fd < 0) { + errno = EINVAL; + return -1; + } + return 0; + } + if (libexec_dup(cmd, fd, old_fd)) + return -1; + if (libexec_close(cmd, old_fd)) { + cmd->nplumings -= 1; + if (!cmd->nplumings) { + free(cmd->plumings); + cmd->plumings = NULL; + } + return -1; + } + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_run.c b/libexec_run.c new file mode 100644 index 0000000..ca1f629 --- /dev/null +++ b/libexec_run.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_run(struct libexec_result *out, ...) +{ + int ret; + va_list args; + va_start(args, out); + ret = libexec_vrun(out, args); + va_end(args); + return ret; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_run_pipeline.c b/libexec_run_pipeline.c new file mode 100644 index 0000000..616f172 --- /dev/null +++ b/libexec_run_pipeline.c @@ -0,0 +1,343 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +struct reap_context { + pid_t *pids; + size_t npids; + size_t procs_rem; + int *exit_statuses_out; /* nullable */ +}; + + +struct reaped { + pid_t pid; + int status; +}; + + +static struct reaped *reaped_elsewhere = NULL; +static size_t nreaped_elsewhere = 0; +static size_t reaped_elsewhere_size = 0; + + +static void +sigchld_handler(int signo) +{ + (void) signo; +} + + +static int +unlist_reap(pid_t pid, int *status) +{ + size_t i; + + for (i = 0; i < nreaped_elsewhere; i++) + if (reaped_elsewhere[i].pid == pid) + goto found; + return 0; + +found: + *status = reaped_elsewhere[i].status; + reaped_elsewhere[i] = reaped_elsewhere[--nreaped_elsewhere]; + + if (nreaped_elsewhere <= reaped_elsewhere_size / 4) { + free(reaped_elsewhere); + reaped_elsewhere = NULL; + nreaped_elsewhere = 0; + reaped_elsewhere_size = 0; + } + + return 1; +} + + +static int +reap_child(struct reap_context *ctx, pid_t pid) +{ + size_t i; + int status; + + if (!ctx->procs_rem) + return 0; + + for (i = 0; i < ctx->npids; i++) { + if (ctx->pids[i] == pid) { + if (waitpid(pid, &status, WNOHANG) != pid) + return -1; + if (ctx->exit_statuses_out) + ctx->exit_statuses_out[i] = status; + ctx->procs_rem -= 1; + ctx->pids[i] = -1; + while (ctx->npids && ctx->pids[ctx->npids - 1] == -1) + ctx->npids -= 1; + return 1; + } + } + + return 0; +} + + +static int +reap_children(struct reap_context *reapctx, int flags, + int (*reap_mutex_control)(int action /* -1 = acquire, +1 = release */, void *user4), void *user4, + int (*on_alien_child_death)(pid_t pid, void *user2), void *user2) +{ + siginfo_t info; + int r, status; + void *new; + size_t new_size; + size_t i; + + if (reap_mutex_control) + if ((*reap_mutex_control)(-1, user4)) + goto fail; + + if (nreaped_elsewhere) { + for (i = 0; i < reapctx->npids; i++) { + if (unlist_reap(reapctx->pids[i], &status)) { + if (reapctx->exit_statuses_out) + reapctx->exit_statuses_out[i] = status; + reapctx->procs_rem -= 1; + reapctx->pids[i] = -1; + } + } + while (reapctx->npids && reapctx->pids[reapctx->npids - 1] == -1) + reapctx->npids -= 1; + } + + for (;;) { + info.si_pid = 0; + if (waitid(P_ALL, 0, &info, flags | WNOWAIT)) + goto fail; + if (info.si_pid < 1) + break; + + r = reap_child(reapctx, info.si_pid); + if (r < 0) + goto fail; + if (r > 0) + continue; + + if (on_alien_child_death) { + r = (*on_alien_child_death)(info.si_pid, user2); + if (r < 0) + goto fail; + if (r > 0) + continue; + } + + if (nreaped_elsewhere == reaped_elsewhere_size) { + new_size = reaped_elsewhere_size + 4; + new = realloc(reaped_elsewhere, new_size * sizeof(*reaped_elsewhere)); + if (!new) + goto fail; + reaped_elsewhere = new; + reaped_elsewhere_size = new_size; + } + + if (waitpid(info.si_pid, &status, WNOHANG) != info.si_pid) { + if (!nreaped_elsewhere) { + free(reaped_elsewhere); + reaped_elsewhere = NULL; + reaped_elsewhere_size = 0; + } + goto fail; + } + + reaped_elsewhere[nreaped_elsewhere].pid = info.si_pid; + reaped_elsewhere[nreaped_elsewhere].status = status; + nreaped_elsewhere++; + } + + if (reap_mutex_control) + if ((*reap_mutex_control)(+1, user4)) + goto fail; + return 0; + +fail: + if (reap_mutex_control) + if ((*reap_mutex_control)(+1, user4)) + goto fail; + return -1; +} + + +int +libexec_run_pipeline(int (*on_alien_epoll)(int alien_epoll, uint32_t events, void *user1), int alien_epoll, void *user1, + int (*on_alien_child_death)(pid_t pid, void *user2), void *user2, + int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user3), void *user3, + int (*reap_mutex_control)(int action /* -1 = acquire, +1 = release */, void *user4), void *user4, + int (*on_interrupt)(void *user5), void *user5, + const sigset_t *sigmask /* nullable */, + struct libexec_document *const *docs, size_t ndocs, int docs_1_level, + struct libexec_command *const *cmds, int *exit_statuses_out /* nullable */, size_t ncmds) +{ + struct libexec_document *input_docs = NULL; + size_t i, ninput_docs = 0, files_open; + struct epoll_event evs[8]; + int epfd, n, sigchld_set = 0, error, r; + struct libexec_document *doc; + struct sigaction sigchld, old_sigchld; + struct reap_context reapctx = { + .pids = NULL, + .npids = 0, + .procs_rem = 0, + .exit_statuses_out = exit_statuses_out + }; + + if (!ncmds) + return 0; + + memset(evs, 0, sizeof(evs)); + + epfd = epoll_create1(EPOLL_CLOEXEC); + if (epfd < 0) + return -1; + + if (sigaction(SIGCHLD, NULL, &old_sigchld)) + goto fail; + if (!(old_sigchld.sa_flags & SA_SIGINFO)) { + if (old_sigchld.sa_handler == SIG_IGN || old_sigchld.sa_handler == SIG_DFL) { + memset(&sigchld, 0, sizeof(sigchld)); + sigchld.sa_handler = &sigchld_handler; + sigemptyset(&sigchld.sa_mask); + if (sigaction(SIGCHLD, &sigchld, NULL)) + goto fail; + sigchld_set = 1; + } + } + + if (alien_epoll >= 0) { + evs[0].events = EPOLLIN; + evs[0].data.ptr = NULL; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, alien_epoll, &evs[0])) + goto fail; + } + + reapctx.pids = calloc(ncmds, sizeof(*reapctx.pids)); + if (!reapctx.pids) + goto fail; + + for (i = 0; i < ncmds; i++) + if (libexec_spawn(&reapctx.pids[reapctx.npids++ /* may be set on failure */], cmds[i], + after_fork, user3, &input_docs, &ninput_docs, O_CLOEXEC | O_NONBLOCK)) + goto fail; + + for (i = 0; i < ninput_docs; i++) { + evs[0].events = EPOLLOUT; + evs[0].data.ptr = &input_docs[i]; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, input_docs[i].fd, &evs[0])) + goto fail; + } + + for (i = 0; i < ndocs; i++) { + doc = docs_1_level ? &(*docs)[i] : docs[i]; + evs[0].events = EPOLLIN; + evs[0].data.ptr = doc; + doc->user = 1; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, doc->fd, &evs[0])) + goto fail; + } + + reapctx.procs_rem = ncmds; + + files_open = ninput_docs + ndocs + (size_t)(alien_epoll >= 0); + while (files_open) { + if (reap_children(&reapctx, WNOHANG, reap_mutex_control, user4, on_alien_child_death, user2)) + goto fail; + + n = epoll_pwait(epfd, evs, sizeof(evs) / sizeof(*evs), -1, sigmask); + if (n < 0) { + if (errno != EINTR) + goto fail; + if (on_interrupt) + if ((*on_interrupt)(user5)) + goto fail; + continue; + } + + for (i = 0; i < (size_t)n; i++) { + if (evs[i].data.ptr == NULL) { + r = (*on_alien_epoll)(alien_epoll, evs[i].events, user1); + if (r < 0) { + goto fail; + } else if (r > 0) { + if (epoll_ctl(epfd, EPOLL_CTL_DEL, alien_epoll, &evs[i])) + goto fail; + files_open -= 1; + } + } else { + doc = evs[i].data.ptr; + send_recv_again: + if (doc->user ? libexec_recv_document(doc) : libexec_send_document(doc)) { + if (errno == EAGAIN) + continue; + if (errno == EINTR) { + if (on_interrupt) + if ((*on_interrupt)(user5)) + goto fail; + goto send_recv_again; + } + } else { + files_open -= 1; + if (doc->user) { + close(doc->fd); + doc->fd = -1; + } else { + libexec_destroy_document(doc); + } + } + } + } + } + free(input_docs); + close(epfd); + + while (reapctx.procs_rem) + if (reap_children(&reapctx, 0, reap_mutex_control, user4, on_alien_child_death, user2)) + goto fail; + + free(reapctx.pids); + if (sigchld_set) + if (sigaction(SIGCHLD, &old_sigchld, NULL)) + abort(); + + return 0; + +fail: + error = errno; + while (ninput_docs) + libexec_destroy_document(&input_docs[--ninput_docs]); + free(input_docs); + close(epfd); + if (reapctx.npids) { + if (reap_mutex_control) + if ((*reap_mutex_control)(+1, user4)) + abort(); + for (i = 0; i < reapctx.npids; i++) + if (reapctx.pids[i] >= 1) + if (unlist_reap(reapctx.pids[i], &(int){0}) || kill(reapctx.pids[i], SIGKILL)) + reapctx.pids[i] = -1; + for (i = 0; i < reapctx.npids; i++) + if (reapctx.pids[i] >= 1) + waitpid(reapctx.pids[i], &(int){0}, 0); + if (reap_mutex_control) + if ((*reap_mutex_control)(-1, user4)) + abort(); + } + free(reapctx.pids); + if (sigchld_set) + if (sigaction(SIGCHLD, &old_sigchld, NULL)) + abort(); + errno = error; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_send_document.c b/libexec_send_document.c new file mode 100644 index 0000000..e69ad72 --- /dev/null +++ b/libexec_send_document.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_send_document(struct libexec_document *doc) +{ + ssize_t r; + if (!doc) { + errno = EINVAL; + return -1; + } + while (doc->offset < doc->length) { + r = write(doc->fd, &doc->text[doc->offset], doc->length - doc->offset); + if (r < 0) + return -1; + doc->offset += (size_t)r; + } + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_environ.c b/libexec_set_environ.c new file mode 100644 index 0000000..1d344d0 --- /dev/null +++ b/libexec_set_environ.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_set_environ(struct libexec_command *cmd, char **env) +{ + size_t i; + + if (!cmd) + return; + + if (cmd->environ) { + for (i = 0; cmd->environ[i]; i++) + free(cmd->environ[i]); + free(cmd->environ); + } + + cmd->environ = env; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_exec_fd.c b/libexec_set_exec_fd.c new file mode 100644 index 0000000..c185a9d --- /dev/null +++ b/libexec_set_exec_fd.c @@ -0,0 +1,21 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_set_exec_fd(struct libexec_command *cmd, int fd) +{ + if (!cmd || fd < 0) { + errno = EINVAL; + return -1; + } + cmd->exec_how = LIBEXEC_EXEC_FD; + cmd->exec_fd = fd; + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_exec_path.c b/libexec_set_exec_path.c new file mode 100644 index 0000000..2b5a7af --- /dev/null +++ b/libexec_set_exec_path.c @@ -0,0 +1,29 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_set_exec_path(struct libexec_command *cmd, int dirfd, const char *path) +{ + char *s = NULL; + if (!cmd) { + errno = EINVAL; + return -1; + } + if (path) { + s = strdup(path); + if (!s) + return -1; + } + free(cmd->executable); + cmd->executable = s; + cmd->exec_fd = dirfd; + cmd->exec_how = LIBEXEC_EXEC_AT; + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_executable.c b/libexec_set_executable.c new file mode 100644 index 0000000..0f9b7c9 --- /dev/null +++ b/libexec_set_executable.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_set_executable(struct libexec_command *cmd, const char *executable) +{ + char *s = NULL; + if (!cmd) { + errno = EINVAL; + return -1; + } + if (executable) { + s = strdup(executable); + if (!s) + return -1; + } + free(cmd->executable); + cmd->executable = s; + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_require_path.c b/libexec_set_require_path.c new file mode 100644 index 0000000..55dd369 --- /dev/null +++ b/libexec_set_require_path.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_set_require_path(struct libexec_command *cmd, int require) +{ + if (!cmd) + return; + cmd->exec_how = require ? LIBEXEC_REQUIRE_PATH : LIBEXEC_ALLOW_NAME; + cmd->exec_fd = -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_setenv.c b/libexec_setenv.c new file mode 100644 index 0000000..47bc8e9 --- /dev/null +++ b/libexec_setenv.c @@ -0,0 +1,73 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv(struct libexec_command *cmd, enum libexec_insert_mode how, const char *name, const char *value) +{ + int ret; + char *buf; + if (!name || !value || strchr(name, '=')) { + errno = EINVAL; + return -1; + } + buf = malloc(strlen(name) + strlen(value) + 2); + stpcpy(stpcpy(stpcpy(buf, name), "="), value); + ret = libexec_putenv(cmd, how, buf); + free(buf); + return ret; +} + + +#else + + +int +main(void) +{ + /* Correct usage is tested via + * libexec_setenv_append.c, + * libexec_setenv_noclobber.c, + * libexec_setenv_noreplace.c, + * libexec_setenv_prepend.c, and + * libexec_setenv_replace.c */ + + struct libexec_command cmd, ref; + int i; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)-1, "A", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)LIBEXEC_INSERT_MODE__COUNT__, "A", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + for (i = 0; i < LIBEXEC_INSERT_MODE__COUNT__; i++) { + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)i, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)i, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)i, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + } + + return 0; +} + + +#endif diff --git a/libexec_setenv_append.c b/libexec_setenv_append.c new file mode 100644 index 0000000..a35207d --- /dev/null +++ b/libexec_setenv_append.c @@ -0,0 +1,212 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_append(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_APPEND, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "BB=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "B=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "B=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenv_noclobber.c b/libexec_setenv_noclobber.c new file mode 100644 index 0000000..6478a9e --- /dev/null +++ b/libexec_setenv_noclobber.c @@ -0,0 +1,180 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_noclobber(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_NOCLOBBER, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, "A", "D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, "A", "D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenv_noreplace.c b/libexec_setenv_noreplace.c new file mode 100644 index 0000000..26339e8 --- /dev/null +++ b/libexec_setenv_noreplace.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_noreplace(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_NOREPLACE, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noreplace(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noreplace(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noreplace(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noreplace(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenv_prepend.c b/libexec_setenv_prepend.c new file mode 100644 index 0000000..9c7a02b --- /dev/null +++ b/libexec_setenv_prepend.c @@ -0,0 +1,212 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_prepend(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_PREPEND, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_prepend(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[6]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X=0"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X=0"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "X=0"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X=0"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X=0"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "X=0"); + ASSERT_IS_NULL(cmd.environ[7]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenv_replace.c b/libexec_setenv_replace.c new file mode 100644 index 0000000..f8191c1 --- /dev/null +++ b/libexec_setenv_replace.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_replace(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_REPLACE, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_replace(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_replace(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_replace(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_replace(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf.c b/libexec_setenvf.c new file mode 100644 index 0000000..7589dca --- /dev/null +++ b/libexec_setenvf.c @@ -0,0 +1,61 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf(cmd, how, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + /* Correct usage is tested, indirectly, via + * libexec_setenvf_append.c, + * libexec_setenvf_noclobber.c, + * libexec_setenvf_noreplace.c, + * libexec_setenvf_prepend.c, and + * libexec_setenvf_replace.c */ + + struct libexec_command cmd, ref; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf(&cmd, (enum libexec_insert_mode)-1, "A", "%s", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf(&cmd, (enum libexec_insert_mode)LIBEXEC_INSERT_MODE__COUNT__, "A", "%s", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + libexec_clear_environ(&cmd); + ASSERT_ZERO(libexec_setenvf(&cmd, LIBEXEC_NOCLOBBER, "PATH", "%s:%s", "test", "path")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "test:path"); + errno = 0; + ASSERT_EQ_INT(libexec_setenvf(&cmd, LIBEXEC_NOCLOBBER, "PATH", "%s", "x"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "test:path"); + ASSERT_ZERO(libexec_setenvf(&cmd, LIBEXEC_REPLACE, "PATH", "%s%s", "my", "path")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "mypath"); + libexec_destroy_command(&cmd); + + return 0; +} + + +#endif diff --git a/libexec_setenvf_append.c b/libexec_setenvf_append.c new file mode 100644 index 0000000..b0b6a4e --- /dev/null +++ b/libexec_setenvf_append.c @@ -0,0 +1,217 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_append(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_append(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "%s=%s", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "%c", 'D')); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "BB=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "B=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "B=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf_noclobber.c b/libexec_setenvf_noclobber.c new file mode 100644 index 0000000..f40b367 --- /dev/null +++ b/libexec_setenvf_noclobber.c @@ -0,0 +1,185 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_noclobber(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_noclobber(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "A", "%s%s", "B", "=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "A", "B%sC", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, "A", "D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "A", "%s=%s", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, "A", "D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf_noreplace.c b/libexec_setenvf_noreplace.c new file mode 100644 index 0000000..9480c7f --- /dev/null +++ b/libexec_setenvf_noreplace.c @@ -0,0 +1,181 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_noreplace(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_noreplace(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noreplace(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noreplace(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noreplace(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noreplace(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "%s%s", "B", "=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "%c", 'D')); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf_prepend.c b/libexec_setenvf_prepend.c new file mode 100644 index 0000000..3a8a197 --- /dev/null +++ b/libexec_setenvf_prepend.c @@ -0,0 +1,217 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_prepend(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_prepend(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_prepend(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%s=%s", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%c", 'D')); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "AA", "%i", 5)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=5"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=5"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "B", "F%s", "")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=5"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[6]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X=0"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X=0"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "X=0"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X=0"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "BB", "%s%s", "", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X=0"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "X=0"); + ASSERT_IS_NULL(cmd.environ[7]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf_replace.c b/libexec_setenvf_replace.c new file mode 100644 index 0000000..9be28db --- /dev/null +++ b/libexec_setenvf_replace.c @@ -0,0 +1,181 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_replace(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_replace(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_replace(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_replace(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_replace(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_replace(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "%s=%s", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "%c", 'D')); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_spawn.c b/libexec_spawn.c new file mode 100644 index 0000000..f0b46e6 --- /dev/null +++ b/libexec_spawn.c @@ -0,0 +1,89 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_spawn(pid_t *out, struct libexec_command *cmd, + int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user), + void *user, struct libexec_document **docsp, size_t *ndocsp, int doc_fd_flags) +{ + int fds[2], error, new_fd; + size_t i; + ssize_t r; + + if (!docsp || !ndocsp || !out || !cmd) { + errno = EINVAL; + return -1; + } + + *out = -1; + + if (pipe2(fds, O_CLOEXEC)) + return -1; + + new_fd = fds[1]; + for (i = 0; i < cmd->nplumings; i++) + if (cmd->plumings[i].fd >= new_fd) + new_fd = cmd->plumings[i].fd + 1; + + if (new_fd != fds[1]) { + new_fd = fcntl(fds[1], F_DUPFD_CLOEXEC, new_fd); + if (new_fd == -1) + goto fail; + fds[1] = new_fd; + } + + if (libexec_get_documents(cmd, docsp, ndocsp, doc_fd_flags)) + goto fail; + + *out = fork(); + switch (*out) { + case -1: + goto fail; + + case 0: + close(fds[0]); + if (after_fork) + if ((*after_fork)(cmd, fds[1], user)) + goto child_fail; + libexec_exec(cmd); + child_fail: + error = errno; + write_again: + r = write(fds[1], &error, sizeof(error)); + if (r <= 0) { + if (errno == EINTR) + goto write_again; + abort(); + } + _exit(1); + + default: + close(fds[1]); + read_again: + r = read(fds[0], &error, sizeof(error)); + if (r < 0) { + if (errno == EINTR) + goto read_again; + } else if (r == 0) { + return 0; + } else if ((size_t)r >= sizeof(error)) { + errno = error; + } + + return -1; + } + +fail: + error = errno; + close(fds[0]); + close(fds[1]); + errno = error; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_unsetenv.c b/libexec_unsetenv.c new file mode 100644 index 0000000..48ec645 --- /dev/null +++ b/libexec_unsetenv.c @@ -0,0 +1,48 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_unsetenv(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, n, len; + + if (!cmd || !name) { + errno = EINVAL; + return -1; + } + + if (strchr(name, '=')) + return 0; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + n = 0; + while (env[n]) + n++; + + for (i = 0; i < n;) { + if (!strncmp(env[i], name, len) && env[i][len] == '=') { + if (!cmd->environ) { + if (libexec_copy_environ(cmd, NULL)) + return -1; + env = cmd->environ; + } + free(env); + memmove(&env[i], &env[i + 1], (n - i - 1) * sizeof(*env)); + n--; + } else { + i++; + } + } + + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_unsetenv_first.c b/libexec_unsetenv_first.c new file mode 100644 index 0000000..9c5ea12 --- /dev/null +++ b/libexec_unsetenv_first.c @@ -0,0 +1,44 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_unsetenv_first(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, n, len; + + if (!cmd || !name) { + errno = EINVAL; + return -1; + } + + if (strchr(name, '=')) + return 0; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + for (i = 0; env[i]; i++) + if (!strncmp(env[i], name, len) && env[i][len] == '=') + break; + + if (env[i]) { + n = i; + while (env[n]) + n++; + if (!cmd->environ) + if (libexec_copy_environ(cmd, NULL)) + return -1; + free(cmd->environ[i]); + memmove(&cmd->environ[i], &cmd->environ[i + 1], (n - i - 1) * sizeof(*cmd->environ)); + } + + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_unsetenv_last.c b/libexec_unsetenv_last.c new file mode 100644 index 0000000..e3fc263 --- /dev/null +++ b/libexec_unsetenv_last.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_unsetenv_last(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, n, len; + + if (!cmd || !name) { + errno = EINVAL; + return -1; + } + + if (strchr(name, '=')) + return 0; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + n = 0; + while (env[n]) + n++; + + for (i = n; i--;) + if (!strncmp(env[i], name, len) && env[i][len] == '=') + goto found; + + return 0; + +found: + if (!cmd->environ) + if (libexec_copy_environ(cmd, NULL)) + return -1; + free(cmd->environ[i]); + memmove(&cmd->environ[i], &cmd->environ[i + 1], (n - i - 1) * sizeof(*cmd->environ)); + + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_vconstruct_command.c b/libexec_vconstruct_command.c new file mode 100644 index 0000000..bb3626f --- /dev/null +++ b/libexec_vconstruct_command.c @@ -0,0 +1,882 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#define FLAG_MINUS 0x01 +#define FLAG_PLUS 0x02 +#define FLAG_HASH 0x04 +#define FLAG_ZERO 0x08 +#define FLAG_SPACE 0x10 + + +#define TYPESIZE_CHAR -2 +#define TYPESIZE_SHORT -1 +#define TYPESIZE_INT 0 +#define TYPESIZE_LONG +1 +#define TYPESIZE_LONG_LONG +2 +#define TYPESIZE_SIZE 3 +#define TYPESIZE_PTRDIFF 4 +#define TYPESIZE_INTPTR 5 +#define TYPESIZE_INTMAX 6 + + +union anyarray_const { + const void *v; + const signed char *hhi; + const signed short int *hi; + const signed int *i; + const signed long int *li; + const signed long long int *lli; + const ssize_t *zi; + const ptrdiff_t *ti; + const intptr_t *pi; + const intmax_t *ji; + const unsigned char *hhu; + const unsigned short int *hu; + const unsigned int *u; + const unsigned long int *lu; + const unsigned long long int *llu; + const size_t *zu; + const ptrdiff_t *tu; + const uintptr_t *pu; + const uintmax_t *ju; + const char *c; + const char *const *s; +}; + +union anyvalue { + intmax_t i; + uintmax_t u; + char c[2]; + const char *s; +}; + +struct partial_string { + char *s; + size_t len; + size_t size; + int started; +}; + +struct argument_format { + size_t array_size; + size_t width1; + size_t width2; + int array_input; + int array_null_terminated; + int typesize; + int has_dot; + char typeclass; + unsigned flags; +}; + +struct ignore_stack { + char *stack; + size_t top; + size_t size; + size_t active; +}; + + +static int +resize_partial_string(struct partial_string *str, size_t req_size) +{ + void *new = realloc(str->s, req_size); + if (!new) + return -1; + str->s = new; + str->size = req_size; + return 0; +} + + +static int +resize_partial_string_if_full(struct partial_string *str, size_t extra) +{ + if (str->len == str->size) + if (resize_partial_string(str, str->len + extra)) + return -1; + return 0; +} + + +static char * +make_unsigned(uintmax_t value, char fmt, const char *plus, const char *prefix, size_t minwidth) +{ +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + + const char *fmtstr; + char *ret, *p; + size_t len, zeroes, totlen; + + if (fmt == 'd' || fmt == 'i' || fmt == 'u') + fmtstr = "%ju"; + else if (fmt == 'o') + fmtstr = "%jo"; + else if (fmt == 'x') + fmtstr = "%jx"; + else if (fmt == 'X') + fmtstr = "%jX"; + else + abort(); + + len = (size_t)snprintf(NULL, 0, fmtstr, value); + zeroes = len < minwidth ? minwidth - len : 0; + + totlen = strlen(plus) + 1; + if (strlen(prefix) > SIZE_MAX - totlen) + goto enomem; + totlen += strlen(prefix); + if (zeroes + len > SIZE_MAX - totlen) + goto enomem; + totlen += zeroes + len; + + ret = malloc(totlen); + if (!ret) + goto enomem; + + p = stpcpy(stpcpy(ret, plus), prefix); + while (zeroes--) + *p++ = '0'; + sprintf(p, fmtstr, value); + + return ret; + +enomem: + errno = ENOMEM; + return NULL; + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +} + + +static char * +make_signed(intmax_t value, char fmt, const char *plus, const char *prefix, size_t minwidth) +{ + if (value < 0) + return make_unsigned((uintmax_t)-(value + 1) + 1U, fmt, "-", prefix, minwidth); + else + return make_unsigned((uintmax_t)value, fmt, plus, prefix, minwidth); +} + + +static unsigned +get_flag(char c) +{ + return c == '-' ? FLAG_MINUS : + c == '+' ? FLAG_PLUS : + c == '#' ? FLAG_HASH : + c == '0' ? FLAG_ZERO : + c == ' ' ? FLAG_SPACE : 0U; +} + + +static unsigned +remove_ignored_flags(unsigned flags, char fmt) +{ + if (fmt == 's' || fmt == 'c') { + flags &= FLAG_MINUS; + } else if (fmt == 'i' || fmt == 'd') { + if (flags & FLAG_PLUS) + flags &= (unsigned)~FLAG_SPACE; + } else { + flags &= (unsigned)~(FLAG_PLUS | FLAG_SPACE); + } + return flags; +} + + +static const char * +read_size(size_t *out, const char *fmt, va_list args, int optional) +{ + size_t digit; + + if (*fmt == '*') { + fmt++; + *out = va_arg(args, size_t); + } else if (!optional && !isdigit(*fmt)) { + goto einval; + } else { + *out = 0; + while (isdigit(*fmt)) { + digit = (size_t)(*fmt++ - '0'); + if (*out > (SIZE_MAX - digit) / 10U) + goto einval; + *out = *out * 10U + digit; + } + } + + return fmt; + +einval: + errno = EINVAL; + return NULL; +} + + +static const char * +get_typesize(int *out, const char *fmt) +{ + *out = 0; + + for (; *fmt == 'h'; fmt++) + if (--*out < -2) + goto einval; + + for (; *fmt == 'l'; fmt++) + if (*out < 0 || ++*out > +2) + goto einval; + + if (*fmt == 'z' || *fmt == 't' || *fmt == 'T' || *fmt == 'j') { + if (*out) + goto einval; + *out = *fmt == 'z' ? TYPESIZE_SIZE : + *fmt == 't' ? TYPESIZE_PTRDIFF : + *fmt == 'T' ? TYPESIZE_INTPTR : /* non-standard */ + TYPESIZE_INTMAX; + fmt++; + } + + /* + * TODO add =N for intN_t/uintN_t (arbitrary N) + * TODO add =+N for int_leastN_t/uint_leastN_t (arbitrary N) + * TODO add =/N for int_fastN_t/uint_fastN_t (arbitrary N) + * non-standard + */ + + if (*fmt == 's' || *fmt == 'c') { + if (*out) { + /* TODO %ls should be `const wchar_t *` */ + /* TODO %lc should be `wint_t` */ + goto einval; + } + } + + return fmt; + +einval: + errno = EINVAL; + return NULL; +} + + +static intmax_t +get_signed_va(va_list args, int typesize) +{ + if (typesize == TYPESIZE_CHAR) + return (intmax_t)(signed char)va_arg(args, signed int); + else if (typesize == TYPESIZE_SHORT) + return (intmax_t)(signed short int)va_arg(args, signed int); + else if (typesize == TYPESIZE_INT) + return (intmax_t)va_arg(args, signed int); + else if (typesize == TYPESIZE_LONG) + return (intmax_t)va_arg(args, signed long int); + else if (typesize == TYPESIZE_LONG_LONG) + return (intmax_t)va_arg(args, signed long long int); + else if (typesize == TYPESIZE_SIZE) /* TODO since ssize_t may be bounded to -1, may it be bounded above INTMAX_MAX? */ + return (intmax_t)va_arg(args, ssize_t); + else if (typesize == TYPESIZE_PTRDIFF) + return (intmax_t)va_arg(args, ptrdiff_t); + else if (typesize == TYPESIZE_INTPTR) + return (intmax_t)va_arg(args, intptr_t); + else if (typesize == TYPESIZE_INTMAX) + return (intmax_t)va_arg(args, intmax_t); + else + abort(); +} + + +static uintmax_t +get_unsigned_va(va_list args, int typesize) +{ + if (typesize == TYPESIZE_CHAR) + return (uintmax_t)(unsigned char)va_arg(args, unsigned int); + else if (typesize == TYPESIZE_SHORT) + return (uintmax_t)(unsigned short int)va_arg(args, unsigned int); + else if (typesize == TYPESIZE_INT) + return (uintmax_t)va_arg(args, unsigned int); + else if (typesize == TYPESIZE_LONG) + return (uintmax_t)va_arg(args, unsigned long int); + else if (typesize == TYPESIZE_LONG_LONG) + return (uintmax_t)va_arg(args, unsigned long long int); + else if (typesize == TYPESIZE_SIZE) + return (uintmax_t)va_arg(args, size_t); + else if (typesize == TYPESIZE_PTRDIFF) + return (uintmax_t)va_arg(args, ptrdiff_t); /* TODO ptrdiff_t is signed */ + else if (typesize == TYPESIZE_INTPTR) + return (uintmax_t)va_arg(args, uintptr_t); + else if (typesize == TYPESIZE_INTMAX) + return (uintmax_t)va_arg(args, uintmax_t); + else + abort(); +} + + +static intmax_t +get_signed_array(union anyarray_const *arrayp, int typesize) +{ + if (typesize == TYPESIZE_CHAR) + return (intmax_t)*arrayp->hhi++; + else if (typesize == TYPESIZE_SHORT) + return (intmax_t)*arrayp->hi++; + else if (typesize == TYPESIZE_INT) + return (intmax_t)*arrayp->i++; + else if (typesize == TYPESIZE_LONG) + return (intmax_t)*arrayp->li++; + else if (typesize == TYPESIZE_LONG_LONG) + return (intmax_t)*arrayp->lli++; + else if (typesize == TYPESIZE_SIZE) + return (intmax_t)*arrayp->zi++; /* TODO since ssize_t may be bounded to -1, may it be bounded above INTMAX_MAX? */ + else if (typesize == TYPESIZE_PTRDIFF) + return (intmax_t)*arrayp->ti++; + else if (typesize == TYPESIZE_INTPTR) + return (intmax_t)*arrayp->pi++; + else if (typesize == TYPESIZE_INTMAX) + return (intmax_t)*arrayp->ji++; + else + abort(); +} + + +static uintmax_t +get_unsigned_array(union anyarray_const *arrayp, int typesize) +{ + if (typesize == TYPESIZE_CHAR) + return (uintmax_t)*arrayp->hhu++; + else if (typesize == TYPESIZE_SHORT) + return (uintmax_t)*arrayp->hu++; + else if (typesize == TYPESIZE_INT) + return (uintmax_t)*arrayp->u++; + else if (typesize == TYPESIZE_LONG) + return (uintmax_t)*arrayp->lu++; + else if (typesize == TYPESIZE_LONG_LONG) + return (uintmax_t)*arrayp->llu++; + else if (typesize == TYPESIZE_SIZE) + return (uintmax_t)*arrayp->zu++; + else if (typesize == TYPESIZE_PTRDIFF) + return (uintmax_t)*arrayp->tu++; /* TODO ptrdiff_t is signed */ + else if (typesize == TYPESIZE_INTPTR) + return (uintmax_t)*arrayp->pu++; + else if (typesize == TYPESIZE_INTMAX) + return (uintmax_t)*arrayp->ju++; + else + abort(); +} + + +static int +get_value_va(union anyvalue *out, va_list args, char fmt, int typesize) +{ + if (fmt == 'i' || fmt == 'd') { + out->i = get_signed_va(args, typesize); + + } else if (fmt == 'u' || fmt == 'o' || fmt == 'x' || fmt == 'X') { + out->u = get_unsigned_va(args, typesize); + + } else if (fmt == 's') { + if (typesize) + abort(); + out->s = va_arg(args, const char *); + + } else if (fmt == 'c') { + if (typesize) + abort(); + out->c[0] = (char)va_arg(args, int); + out->c[1] = '\0'; + + } else { + errno = EINVAL; + return -1; + } + + return 0; +} + + +static int +get_value_array(union anyvalue *out, union anyarray_const *arrayp, char fmt, int typesize) +{ + if (fmt == 'i' || fmt == 'd') { + out->i = get_signed_array(arrayp, typesize); + return !!out->i; + + } else if (fmt == 'u' || fmt == 'o' || fmt == 'x' || fmt == 'X') { + out->u = get_unsigned_array(arrayp, typesize); + return !!out->u; + + } else if (fmt == 's') { + if (typesize) + abort(); + out->s = *arrayp->s++; + return !!out->s; + + } else if (fmt == 'c') { + if (typesize) + abort(); + out->c[0] = *arrayp->c++; + out->c[1] = '\0'; + return !!out->c[0]; + + } else { + errno = EINVAL; + return -1; + } +} + + +static char * +create_string(const union anyvalue *value, char fmt, const char *plus, const char *prefix, int has_dot, size_t width) +{ + if (fmt == 'i' || fmt == 'd') { + return make_signed(value->i, fmt, plus, value->i ? prefix : "", width); + + } else if (fmt == 'u' || fmt == 'o' || fmt == 'x' || fmt == 'X') { + return make_unsigned(value->u, fmt, "", value->u ? prefix : "", width); + + } else if (fmt == 's') { + return strndup(value->s, has_dot ? width : strlen(value->s)); + + } else if (fmt == 'c') { + return strndup(value->c, has_dot ? width : 1); + + } else { + errno = EINVAL; + return NULL; + } +} + + +static int +pad_and_insert(struct partial_string *arg, const char *substring, size_t min_width, char pad_char, unsigned pad_before) +{ + size_t pad_len, substring_len; + + substring_len = strlen(substring); + pad_len = substring_len < min_width ? min_width - substring_len : 0; + if (substring_len + pad_len > SIZE_MAX - arg->len) { + errno = ENOMEM; + return -1; + } + + if (substring_len + pad_len + arg->len > arg->size) + if (resize_partial_string(arg, substring_len + pad_len + arg->len)) + return -1; + + if (pad_before) { + memset(&arg->s[arg->len], pad_char, pad_len); + arg->len += pad_len; + } + + memcpy(&arg->s[arg->len], substring, substring_len); + arg->len += substring_len; + + if (!pad_before) { + memset(&arg->s[arg->len], pad_char, pad_len); + arg->len += pad_len; + } + + return 0; +} + + +static int +insert_argument(struct partial_string *arg, const union anyvalue *value, char fmt, + unsigned flags, size_t width1, int has_dot, size_t width2) +{ + const char *plus, *prefix; + char *substring; + + plus = (flags & FLAG_PLUS) ? "+" : (flags & FLAG_SPACE) ? " " : ""; + + prefix = !(flags & FLAG_HASH) ? "" : + fmt == 'o' ? "0" : + fmt == 'x' ? "0x" : + fmt == 'X' ? "0X" : ""; + + if (fmt != 's' && fmt != 'c') { + if (!has_dot) { + width2 = width1; + width1 = 0; + } + } + + substring = create_string(value, fmt, plus, prefix, has_dot, width2); + if (!substring) + return -1; + + if (pad_and_insert(arg, substring, width1, (flags & FLAG_ZERO) ? '0' : ' ', flags & FLAG_MINUS)) { + free(substring); + return -1; + } + + free(substring); + return 0; +} + + +static int +get_and_insert_arguments(struct partial_string *arg, va_list args, const struct argument_format *arg_fmt, int ignoring) +{ + union anyarray_const array; + union anyvalue value; + unsigned int flags; + size_t n; + int r; + + flags = remove_ignored_flags(arg_fmt->flags, arg_fmt->typeclass); + + if (arg_fmt->array_input) { + array.v = va_arg(args, const void *); + n = arg_fmt->array_size; + while (arg_fmt->array_null_terminated || n--) { + r = get_value_array(&value, &array, arg_fmt->typeclass, arg_fmt->typesize); + if (r < 0) + return -1; + else if (!r && arg_fmt->array_null_terminated) + break; + if (!ignoring) + if (insert_argument(arg, &value, arg_fmt->typeclass, flags, + arg_fmt->width1, arg_fmt->has_dot, arg_fmt->width2)) + return -1; + } + + } else { + if (get_value_va(&value, args, arg_fmt->typeclass, arg_fmt->typesize)) + return -1; + if (!ignoring) + if (insert_argument(arg, &value, arg_fmt->typeclass, flags, + arg_fmt->width1, arg_fmt->has_dot, arg_fmt->width2)) + return -1; + } + + return 0; +} + + +static const char * +get_argument_format(struct argument_format *out, const char *fmt, va_list args) +{ + unsigned flag; + + memset(out, 0, sizeof(*out)); + + if (*fmt == '[') { + fmt++; + out->array_input = 1; + if (*fmt == ']') { + out->array_null_terminated = 1; + } else { + fmt = read_size(&out->array_size, fmt, args, 1); + if (!fmt) + goto fail; + } + if (*fmt != ']') + goto einval; + fmt++; + } + + while ((flag = get_flag(*fmt))) { + out->flags |= flag; + fmt++; + } + + fmt = read_size(&out->width1, fmt, args, 0); + if (!fmt) + goto fail; + if (*fmt == '.') { + out->has_dot = 1; + out->flags &= (unsigned)~FLAG_ZERO; + fmt++; + fmt = read_size(&out->width2, fmt, args, 0); + if (!fmt) + goto fail; + } + + fmt = get_typesize(&out->typesize, fmt); + if (!fmt) + goto fail; + + out->typeclass = *fmt++; + + return fmt; + +einval: + errno = EINVAL; +fail: + return NULL; +} + + +static void +destroy_ignore_stack(struct ignore_stack *stack) +{ + free(stack->stack); + stack->stack = NULL; +} + + +static int +push_ignore_stack(struct ignore_stack *stack, va_list args) +{ + int show; + void *new; + size_t new_size; + + if (stack->top == stack->size) { + new_size = stack->size + 8; + new = realloc(stack->stack, new_size * sizeof(*stack->stack)); + if (!new) + return -1; + stack->stack = new; + stack->size = new_size; + } + + show = va_arg(args, int); + stack->stack[stack->top++] = (char)!show; + stack->active += (size_t)!show; + return 0; +} + + +static int +pop_ignore_stack(struct ignore_stack *stack) +{ + if (!stack->top) { + errno = EINVAL; + return -1; + } + if (stack->stack[--stack->top]) + stack->active -= 1; + return 0; +} + + +static int +is_ignoring(struct ignore_stack *stack) +{ + return stack->active > 0; +} + + +static const char * +format(const char *fmt, struct partial_string *arg, struct ignore_stack *ignore_stack, va_list args) +{ + + struct argument_format arg_fmt; + + /* TODO $< should work as <(), * or number before < to select fd (default: STDOUT_FILENO) */ + /* TODO $> should work as >(), * or number before > to select fd (default: STDIN_FILENO) */ + /* TODO add support for embedding via %S and %C (do not set arg->started) */ + /* TODO add support for floating point values */ + + if (*fmt == '$' || *fmt == '\'' || *fmt == '"' || *fmt == '_') { + if (!is_ignoring(ignore_stack)) { + if (arg->len == arg->size) + if (resize_partial_string(arg, arg->size + 32)) + goto fail; + arg->s[arg->len++] = *fmt == '_' ? ' ' : *fmt; + arg->started = 1; + } + fmt++; + + } else if (*fmt == '(') { + fmt++; + if (push_ignore_stack(ignore_stack, args)) + goto fail; + + } else if (*fmt == ')') { + fmt++; + if (pop_ignore_stack(ignore_stack)) + goto fail; + + } else { + fmt = get_argument_format(&arg_fmt, fmt, args); + if (!fmt) + goto fail; + if (get_and_insert_arguments(arg, args, &arg_fmt, is_ignoring(ignore_stack))) + goto fail; + arg->started = 1; + } + + return fmt; + +fail: + return NULL; +} + + +static char ** +format_command(const char *fmt, va_list args, size_t *narguments_out) +{ + size_t command_size = 0; + char **command = NULL; + struct ignore_stack ignore_stack; + struct partial_string arg; + void *new; + + *narguments_out = 0; + + memset(&ignore_stack, 0, sizeof(ignore_stack)); + ignore_stack.stack = NULL; + + memset(&arg, 0, sizeof(arg)); + arg.s = NULL; + +next_arg: + arg.started = 0; + arg.len = 0; + while (*fmt) { + if (*fmt == '$') { + fmt++; + fmt = format(fmt, &arg, &ignore_stack, args); + if (!fmt) + goto fail; + + } else if (isspace(*fmt)) { + fmt++; + if (!arg.started || is_ignoring(&ignore_stack)) + continue; + new = realloc(command, (command_size + 1) * sizeof(*command)); + if (!new) + goto fail; + command = new; + if (resize_partial_string_if_full(&arg, 32)) + goto fail; + arg.s[arg.len] = '\0'; + command[command_size] = strdup(arg.s); + if (!command[command_size]) + goto fail; + command_size++; + goto next_arg; + + } else if (*fmt == '\'' || *fmt == '"') { + arg.started = 1; + /* TODO add support for quotes */ + errno = EINVAL; + goto fail; + + } else { + if (!is_ignoring(&ignore_stack)) { + if (resize_partial_string_if_full(&arg, 32)) + goto fail; + arg.s[arg.len++] = *fmt; + arg.started = 1; + } + fmt++; + } + } + + new = realloc(command, (command_size + 2) * sizeof(*command)); + if (!new) + goto fail; + command = new; + if (arg.started) { + if (resize_partial_string_if_full(&arg, 1)) + goto fail; + arg.s[arg.len] = '\0'; + command[command_size++] = arg.s; + } else { + free(arg.s); + } + command[command_size] = NULL; + destroy_ignore_stack(&ignore_stack); + + *narguments_out = command_size; + return command; + +fail: + free(arg.s); + destroy_ignore_stack(&ignore_stack); + while (command_size) + free(command[--command_size]); + free(command); + return NULL; +} + + +static int +append_arguments(struct libexec_command *cmd, va_list args) +{ + size_t n = 0, i; + va_list args2; + void *new; + + va_copy(args2, args); + while (va_arg(args2, const char *)) + n += 1; + va_end(args2); + + new = realloc(cmd->arguments, (cmd->narguments + n + 1) * sizeof(*cmd->arguments)); + if (!new) + return -1; + cmd->arguments = new; + + for (i = 0; i < n; i++) { + cmd->arguments[cmd->narguments + i] = strdup(va_arg(args, const char *)); + if (!cmd->arguments[cmd->narguments + i]) { + while (i) + free(cmd->arguments[cmd->narguments + --i]); + if (!cmd->narguments) { + free(cmd->arguments); + cmd->arguments = NULL; + } + return -1; + } + } + + cmd->arguments[cmd->narguments + n] = va_arg(args, char *); /* that's a NULL */ + cmd->narguments += n; + + return 0; +} + + +int +libexec_vconstruct_command(struct libexec_command *cmd, const char *fmt, va_list args) +{ + size_t n; + char **command = NULL; + void *new; + + if (!cmd) { + errno = EINVAL; + return -1; + } + + if (!fmt) { + return append_arguments(cmd, args); + + } else { + command = format_command(fmt, args, &n); + if (!command) + return -1; + if (!cmd->narguments) { + cmd->arguments = command; + cmd->narguments = n; + } else { + new = realloc(cmd->arguments, (cmd->narguments + n + 1) * sizeof(*cmd->arguments)); + if (!new) { + while (n) + free(command[--n]); + free(command); + return -1; + } + cmd->arguments = new; + memcpy(&cmd->arguments[cmd->narguments], command, (n + 1) * sizeof(*command)); + cmd->narguments += n; + free(command); + } + return 0; + } +} + + +#else +TESTED_ELSEWHERE /* libexec_construct_command.c */ +#endif diff --git a/libexec_vpipe_commands.c b/libexec_vpipe_commands.c new file mode 100644 index 0000000..6f1eb3f --- /dev/null +++ b/libexec_vpipe_commands.c @@ -0,0 +1,30 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vpipe_commands(enum libexec_pipe how, va_list args) +{ + struct libexec_command **cmds; + size_t n = 0, i; + int ret; + va_list args2; + va_copy(args2, args); + while (va_arg(args2, struct libexec_command *)) + n += 1; + va_end(args2); + cmds = calloc(n + 1, sizeof(*cmds)); + if (!cmds) + return -1; + for (i = 0; i < n; i++) + cmds[i] = va_arg(args, struct libexec_command *); + ret = libexec_pipe_commandsvn(how, cmds, n); + free(cmds); + return ret; +} + + +#else +TESTED_ELSEWHERE /* libexec_pipe_commands.c */ +#endif diff --git a/libexec_vputenvf.c b/libexec_vputenvf.c new file mode 100644 index 0000000..3d8b7f9 --- /dev/null +++ b/libexec_vputenvf.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + + +int +libexec_vputenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *fmt, va_list args) +{ + int len, ret; + char *buf; + va_list args2; + if (!fmt) { + errno = EINVAL; + return -1; + } + va_copy(args2, args); + len = vsnprintf(NULL, 0, fmt, args2); + va_end(args2); + if (len < 0) + return -1; + buf = malloc((size_t)len + 1U); + if (!buf) + return -1; + if (vsprintf(buf, fmt, args) != len) + abort(); + ret = libexec_putenv(cmd, how, buf); + free(buf); + return ret; +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf.c */ +#endif diff --git a/libexec_vputenvf_append.c b/libexec_vputenvf_append.c new file mode 100644 index 0000000..ca0d564 --- /dev/null +++ b/libexec_vputenvf_append.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_append(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_APPEND, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_append.c */ +#endif diff --git a/libexec_vputenvf_noclobber.c b/libexec_vputenvf_noclobber.c new file mode 100644 index 0000000..61a1e74 --- /dev/null +++ b/libexec_vputenvf_noclobber.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_noclobber(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_NOCLOBBER, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_noclobber.c */ +#endif diff --git a/libexec_vputenvf_noreplace.c b/libexec_vputenvf_noreplace.c new file mode 100644 index 0000000..6998742 --- /dev/null +++ b/libexec_vputenvf_noreplace.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_noreplace(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_NOREPLACE, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_noreplace.c */ +#endif diff --git a/libexec_vputenvf_prepend.c b/libexec_vputenvf_prepend.c new file mode 100644 index 0000000..e1b9614 --- /dev/null +++ b/libexec_vputenvf_prepend.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_prepend(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_PREPEND, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_prepend.c */ +#endif diff --git a/libexec_vputenvf_replace.c b/libexec_vputenvf_replace.c new file mode 100644 index 0000000..10c22d2 --- /dev/null +++ b/libexec_vputenvf_replace.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_replace(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_REPLACE, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_replace.c */ +#endif diff --git a/libexec_vrun.c b/libexec_vrun.c new file mode 100644 index 0000000..99822c2 --- /dev/null +++ b/libexec_vrun.c @@ -0,0 +1,307 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vrun(struct libexec_result *out, va_list args) +{ +#define CMD (cmds[ncmds - 1]) + + struct libexec_document *doc, **docs = NULL; + struct libexec_command **cmds = NULL; + size_t ncmds = 0, ndocs = 0, i; + const char *fmt, *arg_str1, *arg_str2; + char *arg_str_nc; + int arg_int1, arg_int2, arg_int3, fd, *exit_statuses = NULL; + enum libexec_insert_mode insert_mode; + void *new, *arg_voidp1; + enum libexec_pipe pipe_how = 0; + struct epoll_event ev; + int alien_epoll = -1; + const sigset_t *sigmask; + int (*on_alien_epoll)(int alien_epoll, uint32_t events, void *user); + int (*on_alien_child_death)(pid_t pid, void *user); + int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user); + int (*reap_mutex_control)(int action, void *user); + int (*on_interrupt)(void *user); + void *on_alien_epoll_user = NULL; + void *on_alien_child_death_user = NULL; + void *after_fork_user = NULL; + void *reap_mutex_control_user = NULL; + void *on_interrupt_user = NULL; + int ret = -1; + + if (!out) { + errno = EINVAL; + return -1; + } + + memset(&ev, 0, sizeof(ev)); + memset(out, 0, sizeof(*out)); + out->exit_statuses = NULL; + +new_command: + new = realloc(cmds, (ncmds + 1) * sizeof(*cmds)); + if (!new) + goto fail; + cmds = new; + ncmds += 1; + CMD = malloc(sizeof(CMD)); + if (!CMD) + goto fail; + libexec_init_command(CMD); + fmt = va_arg(args, const char *); + if (libexec_vconstruct_command(CMD, fmt, args)) + goto fail; + + for (;;) { + switch (va_arg(args, enum libexec_run_instruction)) { + case LIBEXEC_RUN_END: + if (ncmds > 1) + if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1])) + goto fail; + goto constructed; + + case LIBEXEC_RUN_END_CIRCULAR_PIPE: + if (ncmds > 1) + if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1])) + goto fail; + pipe_how = va_arg(args, enum libexec_pipe); + if (libexec_pipe_commands(pipe_how, cmds[ncmds - 1], cmds[0])) + goto fail; + goto constructed; + + case LIBEXEC_RUN_PIPE: + if (ncmds > 1) + if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1])) + goto fail; + pipe_how = va_arg(args, enum libexec_pipe); + goto new_command; + + case LIBEXEC_RUN_SET_EXECUTABLE: + if (libexec_set_executable(CMD, va_arg(args, const char *))) + goto fail; + break; + + case LIBEXEC_RUN_SET_REQUIRE_PATH: + libexec_set_require_path(CMD, va_arg(args, int)); + break; + + case LIBEXEC_RUN_SET_EXEC_FD: + if (libexec_set_exec_fd(CMD, va_arg(args, int))) + goto fail; + break; + + case LIBEXEC_RUN_SET_EXEC_PATH: + arg_int1 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + if (libexec_set_exec_path(CMD, arg_int1, arg_str1)) + goto fail; + break; + + case LIBEXEC_RUN_COPY_ENVIRON: + if (libexec_copy_environ(CMD, va_arg(args, const char *const *))) + goto fail; + break; + + case LIBEXEC_RUN_SET_ENVIRON: + libexec_set_environ(CMD, va_arg(args, char **)); + break; + + case LIBEXEC_RUN_CLEAR_ENVIRON: + if (libexec_clear_environ(CMD)) + goto fail; + break; + + case LIBEXEC_RUN_UNSETENV: + if (libexec_unsetenv(CMD, va_arg(args, const char *))) + goto fail; + break; + + case LIBEXEC_RUN_PUTENV: + insert_mode = va_arg(args, enum libexec_insert_mode); + arg_str1 = va_arg(args, const char *); + if (libexec_putenv(CMD, insert_mode, arg_str1)) + goto fail; + break; + + case LIBEXEC_RUN_SETENV: + insert_mode = va_arg(args, enum libexec_insert_mode); + arg_str1 = va_arg(args, const char *); + arg_str2 = va_arg(args, const char *); + if (libexec_setenv(CMD, insert_mode, arg_str1, arg_str2)) + goto fail; + break; + + case LIBEXEC_RUN_PUTENVF: + insert_mode = va_arg(args, enum libexec_insert_mode); + arg_str1 = va_arg(args, const char *); + if (libexec_vputenvf(CMD, insert_mode, arg_str1, args)) + goto fail; + break; + + case LIBEXEC_RUN_SETENVF: + insert_mode = va_arg(args, enum libexec_insert_mode); + arg_str1 = va_arg(args, const char *); + arg_str2 = va_arg(args, const char *); + if (libexec_vsetenvf(CMD, insert_mode, arg_str1, arg_str2, args)) + goto fail; + break; + + case LIBEXEC_RUN_OPEN: + arg_int1 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + arg_int2 = va_arg(args, int); + if (libexec_open(CMD, arg_int1, arg_str1, arg_int2, va_arg(args, mode_t))) + goto fail; + break; + + case LIBEXEC_RUN_OPENAT: + arg_int1 = va_arg(args, int); + arg_int2 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + arg_int3 = va_arg(args, int); + if (libexec_openat(CMD, arg_int1, arg_int2, arg_str1, arg_int3, va_arg(args, mode_t))) + goto fail; + break; + + case LIBEXEC_RUN_OPENAT2: + arg_int1 = va_arg(args, int); + arg_int2 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + arg_voidp1 = va_arg(args, void *); + if (libexec_openat2(CMD, arg_int1, arg_int2, arg_str1, arg_voidp1, va_arg(args, size_t))) + goto fail; + break; + + case LIBEXEC_RUN_DUP: + arg_int1 = va_arg(args, int); + if (libexec_dup(CMD, arg_int1, va_arg(args, int))) + goto fail; + break; + + case LIBEXEC_RUN_CLOSE: + if (libexec_close(CMD, va_arg(args, int))) + goto fail; + break; + + case LIBEXEC_RUN_RENUMBER_FD: + arg_int1 = va_arg(args, int); + if (libexec_renumber_fd(CMD, arg_int1, va_arg(args, int))) + goto fail; + break; + + case LIBEXEC_RUN_INPUT_COPY: + arg_int1 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + if (libexec_input_data_copy(CMD, arg_int1, arg_str1, va_arg(args, size_t))) + goto fail; + break; + + case LIBEXEC_RUN_INPUT_GIFT: + arg_int1 = va_arg(args, int); + arg_str_nc = va_arg(args, char *); + if (libexec_input_data_gift(CMD, arg_int1, arg_str_nc, va_arg(args, size_t))) + goto fail; + break; + + case LIBEXEC_RUN_ADD_OUTPUT_FD: + arg_int1 = va_arg(args, int); + fd = va_arg(args, int); + if (libexec_add_output_fd(CMD, arg_int1, fd)) + goto fail; + if (alien_epoll >= 0) { + ev.events = EPOLLIN; + ev.data.fd = fd; + if (epoll_ctl(alien_epoll, EPOLL_CTL_ADD, fd, &ev)) + goto fail; + } + break; + + case LIBEXEC_RUN_ADD_OUTPUT: + arg_int1 = va_arg(args, int); + doc = va_arg(args, struct libexec_document *); + new = realloc(docs, (ndocs + 1) * sizeof(*docs)); + if (!new) + goto fail; + docs = new; + docs[ndocs++] = doc; + if (libexec_add_output_document(CMD, arg_int1, doc, O_CLOEXEC | O_NONBLOCK)) + goto fail; + break; + + case LIBEXEC_RUN_SET_SIGMASK: + sigmask = va_arg(args, const sigset_t *); + break; + + case LIBEXEC_RUN_SET_FD_CALLBACK: + on_alien_epoll = va_arg(args, int (*)(int, uint32_t, void *)); + on_alien_epoll_user = va_arg(args, void *); + alien_epoll = epoll_create1(EPOLL_CLOEXEC); + if (alien_epoll < 0) + goto fail; + break; + + case LIBEXEC_RUN_SET_REAPER: + on_alien_child_death = va_arg(args, int (*)(pid_t, void *)); + on_alien_child_death_user = va_arg(args, void *); + break; + + case LIBEXEC_RUN_SET_ATFORK: + after_fork = va_arg(args, int (*)(struct libexec_command *, int, void *)); + after_fork_user = va_arg(args, void *); + break; + + case LIBEXEC_RUN_SET_MUTEX: + reap_mutex_control = va_arg(args, int (*)(int, void *)); + reap_mutex_control_user = va_arg(args, void *); + break; + + case LIBEXEC_RUN_SET_INTERRUPT_CALLBACK: + on_interrupt = va_arg(args, int (*)(void *)); + on_interrupt_user = va_arg(args, void *); + break; + + default: + errno = EINVAL; + goto fail; + } + } + +constructed: + ret = libexec_run_pipeline(on_alien_epoll, alien_epoll, on_alien_epoll_user, + on_alien_child_death, on_alien_child_death_user, + after_fork, after_fork_user, + reap_mutex_control, reap_mutex_control_user, + on_interrupt, on_interrupt_user, + sigmask, docs, ndocs, 0, cmds, exit_statuses, ncmds); + if (!ret) { + out->exit_statuses = exit_statuses; + out->proc_count = ncmds; + for (i = 0; i < ncmds; i++) + if (exit_statuses[i]) + break; + out->all_successful = (i == ncmds); + exit_statuses = NULL; + } + +fail: + while (ncmds--) { + if (cmds[ncmds]) { + libexec_destroy_command(cmds[ncmds]); + free(cmds[ncmds]); + } + } + free(cmds); + free(docs); + if (alien_epoll >= 0) + close(alien_epoll); + free(exit_statuses); + return ret; +} + + +#else +TESTED_ELSEWHERE /* libexec_run.c */ +#endif diff --git a/libexec_vsetenvf.c b/libexec_vsetenvf.c new file mode 100644 index 0000000..b4d35c5 --- /dev/null +++ b/libexec_vsetenvf.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + + +int +libexec_vsetenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *name, const char *value_fmt, va_list args) +{ + int len, ret; + char *buf; + va_list args2; + if (!name || !value_fmt) { + errno = EINVAL; + return -1; + } + va_copy(args2, args); + len = vsnprintf(NULL, 0, value_fmt, args2); + va_end(args2); + if (len < 0) + return -1; + buf = malloc((size_t)len + 1U); + if (!buf) + return -1; + if (vsprintf(buf, value_fmt, args) != len) + abort(); + ret = libexec_setenv(cmd, how, name, buf); + free(buf); + return ret; +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf.c */ +#endif diff --git a/libexec_vsetenvf_append.c b/libexec_vsetenvf_append.c new file mode 100644 index 0000000..c00f8b6 --- /dev/null +++ b/libexec_vsetenvf_append.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vsetenvf_append(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args) +{ + return libexec_vsetenvf(cmd, LIBEXEC_APPEND, name, value_fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_append.c */ +#endif diff --git a/libexec_vsetenvf_noclobber.c b/libexec_vsetenvf_noclobber.c new file mode 100644 index 0000000..5164e8b --- /dev/null +++ b/libexec_vsetenvf_noclobber.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vsetenvf_noclobber(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args) +{ + return libexec_vsetenvf(cmd, LIBEXEC_NOCLOBBER, name, value_fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_noclobber.c */ +#endif diff --git a/libexec_vsetenvf_noreplace.c b/libexec_vsetenvf_noreplace.c new file mode 100644 index 0000000..1fb1476 --- /dev/null +++ b/libexec_vsetenvf_noreplace.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vsetenvf_noreplace(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args) +{ + return libexec_vsetenvf(cmd, LIBEXEC_NOREPLACE, name, value_fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_noreplace.c */ +#endif diff --git a/libexec_vsetenvf_prepend.c b/libexec_vsetenvf_prepend.c new file mode 100644 index 0000000..63daaae --- /dev/null +++ b/libexec_vsetenvf_prepend.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vsetenvf_prepend(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args) +{ + return libexec_vsetenvf(cmd, LIBEXEC_PREPEND, name, value_fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_prepend.c */ +#endif diff --git a/libexec_vsetenvf_replace.c b/libexec_vsetenvf_replace.c new file mode 100644 index 0000000..d1e8788 --- /dev/null +++ b/libexec_vsetenvf_replace.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vsetenvf_replace(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args) +{ + return libexec_vsetenvf(cmd, LIBEXEC_REPLACE, name, value_fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_replace.c */ +#endif diff --git a/mk/linux.mk b/mk/linux.mk new file mode 100644 index 0000000..ad58f69 --- /dev/null +++ b/mk/linux.mk @@ -0,0 +1,6 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,lib$(LIB_NAME).$(LIBEXT).$(LIB_MAJOR) +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) + +FIX_INSTALL_NAME = : diff --git a/mk/macos.mk b/mk/macos.mk new file mode 100644 index 0000000..55d3eaa --- /dev/null +++ b/mk/macos.mk @@ -0,0 +1,6 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib -Wl,-compatibility_version,$(LIB_MAJOR) -Wl,-current_version,$(LIB_VERSION) +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = install_name_tool -id "$(PREFIX)/lib/libexec.$(LIBMAJOREXT)" diff --git a/mk/windows.mk b/mk/windows.mk new file mode 100644 index 0000000..ed5ec8d --- /dev/null +++ b/mk/windows.mk @@ -0,0 +1,6 @@ +LIBEXT = dll +LIBFLAGS = -shared +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = : diff --git a/testhelp.c b/testhelp.c new file mode 100644 index 0000000..74997eb --- /dev/null +++ b/testhelp.c @@ -0,0 +1,366 @@ +/* See LICENSE file for copyright and license details. */ +#define TEST +#include "common.h" + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wswitch" +# pragma GCC diagnostic ignored "-Wswitch-enum" +#endif + + +static char * +quote(const char *s, char **out) +{ + char *ret, *p; + int safe = 1; + *out = p = ret = malloc(strlen(s) * 4 + 3); + if (!ret) { + fprintf(stderr, "ran out of memory during printing of assertion failure\n"); + exit(1); + } + *p++ = '"'; + for (; *s; s++) { + if (*s == '\r') + p = stpcpy(p, "\\r"), safe = 1; + else if (*s == '\t') + p = stpcpy(p, "\\t"), safe = 1; + else if (*s == '\a') + p = stpcpy(p, "\\a"), safe = 1; + else if (*s == '\f') + p = stpcpy(p, "\\f"), safe = 1; + else if (*s == '\v') + p = stpcpy(p, "\\v"), safe = 1; + else if (*s == '\b') + p = stpcpy(p, "\\b"), safe = 1; + else if (*s == '\n') + p = stpcpy(p, "\\n"), safe = 1; + else if (*s == '\"') + p = stpcpy(p, "\\\""), safe = 1; + else if (*s == '\\') + p = stpcpy(p, "\\\\"), safe = 1; + else if (*s < ' ' || *s >= 127) + p += sprintf(p, "\\x%02X", (unsigned char)*s), safe = 0; + else if (*s == '?' && p[-1] == '?') + p += sprintf(p, "\"\"?"), safe = 1; + else if (isxdigit(*s) && !safe) + p += sprintf(p, "\"\"%c", *s), safe = 1; + else + *p++ = *s, safe = 1; + } + *p++ = '"'; + *p++ = '\0'; + return ret; +} + + +#define CASE(HOW, OP)\ + case HOW:\ + if ((*have) OP (*expect))\ + return;\ + symbol = #OP;\ + break + + +#define CASE_LOGIC(HOW, OP1, OP2, OPERAND3_IS_EXPECT)\ + case HOW:\ + if (((*have) OP1 (*expect)) OP2 (OPERAND3_IS_EXPECT ? (*expect) : 0))\ + return;\ + symbol = #OP1;\ + symbol2 = #OP2;\ + operand3_is_expect = (OPERAND3_IS_EXPECT);\ + break + + +static void +test_assert_uint(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const uintmax_t *have, const uintmax_t *expect) +{ + const char *symbol; + const char *symbol2 = NULL; + int operand3_is_expect; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + CASE(ASSERT_EQ, ==); + CASE(ASSERT_NE, !=); + CASE(ASSERT_LT, <); + CASE(ASSERT_LE, <=); + CASE(ASSERT_GT, >); + CASE(ASSERT_GE, >=); + CASE_LOGIC(ASSERT_CONTAINS_ALL, &, ==, 1); + CASE_LOGIC(ASSERT_CONTAINS_ANY, &, !=, 0); + CASE_LOGIC(ASSERT_CONTAINS_NOT_ALL, &, !=, 1); + CASE_LOGIC(ASSERT_CONTAINS_NOT_ANY, &, ==, 0); + CASE_LOGIC(ASSERT_IS_IN, &~, ==, 0); + CASE_LOGIC(ASSERT_NOT_IN, &~, !=, 0); + default: + abort(); + } + if (!symbol2) + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %ju %s %ju\n", + file, line, have_string, symbol, expect_string, *have, symbol, *expect); + else if (!operand3_is_expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting ((%s) %s (%s)) %s 0, actualised as (%#jx %s %#jx) %s 0\n", + file, line, have_string, symbol, expect_string, symbol2, *have, symbol, *expect, symbol2); + else + fprintf(stderr, "TEST FAILED at %s:%i: asserting ((%s) %s (%s)) %s (%s), actualised as (%#jx %s %#jx) %s %#jx\n", + file, line, have_string, symbol, expect_string, symbol2, expect_string, + *have, symbol, *expect, symbol2, *expect); + exit(1); +} + + +static void +test_assert_int(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const intmax_t *have, const intmax_t *expect) +{ + const char *symbol; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + CASE(ASSERT_EQ, ==); + CASE(ASSERT_NE, !=); + CASE(ASSERT_LT, <); + CASE(ASSERT_LE, <=); + CASE(ASSERT_GT, >); + CASE(ASSERT_GE, >=); + default: + test_assert_uint(file, line, how, have_string, expect_string, + (const uintmax_t *)have, (const uintmax_t *)expect); + return; + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %ji %s %ji\n", + file, line, have_string, symbol, expect_string, *have, symbol, *expect); + exit(1); +} + + +#define PTR_CASE(HOW, OP)\ + case HOW:\ + if ((have) OP (expect))\ + return;\ + symbol = #OP;\ + break + + +static void +test_assert_ptr(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const char *have, const char *expect) +{ + const char *symbol; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + PTR_CASE(ASSERT_EQ, ==); + PTR_CASE(ASSERT_NE, !=); + PTR_CASE(ASSERT_LT, <); + PTR_CASE(ASSERT_LE, <=); + PTR_CASE(ASSERT_GT, >); + PTR_CASE(ASSERT_GE, >=); + default: + abort(); + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %p %s %p\n", + file, line, have_string, symbol, expect_string, have, symbol, expect); + exit(1); +} + + +#define STR_CASE(HOW, FUNC)\ + case HOW:\ + if (FUNC(have, expect))\ + return;\ + function = #FUNC;\ + break + +#define SWAP_STR_CASE(HOW, NEW_HOW)\ + case HOW:\ + test_assert_str(file, line, how, expect_string, have_string, expect, have, rev);\ + return + + +static void +test_assert_str(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const char *have, const char *expect, int rev) +{ + const char *function; + size_t have_len; + size_t expect_len; + char *have_quote = NULL; + char *expect_quote = NULL; + + if (!have && !expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) and (%s) are NULL\n", + file, line, have_string, expect_string); + else if (!have) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) was NULL\n", + file, line, have_string); + else if (!expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) was NULL\n", + file, line, expect_string); + if (!have || !expect) + exit(1); + + switch (how) { + STR_CASE(ASSERT_EQ, !strcmp); + STR_CASE(ASSERT_NE, strcmp); + STR_CASE(ASSERT_IS_IN, strstr); + STR_CASE(ASSERT_NOT_IN, !strstr); + SWAP_STR_CASE(ASSERT_CONTAINS_ALL, ASSERT_IS_IN); + SWAP_STR_CASE(ASSERT_CONTAINS_NOT_ALL, ASSERT_NOT_INT); + SWAP_STR_CASE(ASSERT_LE, ASSERT_GE); + SWAP_STR_CASE(ASSERT_LT, ASSERT_GT); + SWAP_STR_CASE(ASSERT_NOT_LE, ASSERT_NOT_GE); + SWAP_STR_CASE(ASSERT_NOT_LT, ASSERT_NOT_GT); + default: + have_len = strlen(have); + expect_len = strlen(expect); + if (rev) + goto assert_end; + else + goto assert_start; + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting %s(%s, %s), actualised as %s(%s, %s)\n", + file, line, function, have_string, expect_string, + function, quote(have, &have_quote), quote(expect, &expect_quote)); +free_and_exit: + free(have_quote); + free(expect_quote); + exit(1); + +assert_start: + switch (how) { + case ASSERT_GE: + if (have_len >= expect_len && !strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) >= strlen(%s) && !strncmp(%s, %s, strlen(%s)), " + "actualised as %zu >= %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_NOT_GE: + if (have_len < expect_len || strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) < strlen(%s) || strncmp(%s, %s, strlen(%s)), " + "actualised as %zu < %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_GT: + if (have_len > expect_len && !strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) > strlen(%s) && !strncmp(%s, %s, strlen(%s)), " + "actualised as %zu > %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_NOT_GT: + if (have_len <= expect_len || strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) <= strlen(%s) || strncmp(%s, %s, strlen(%s)), " + "actualised as %zu <= %zu || strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + default: + abort(); + } + goto free_and_exit; + +assert_end: + switch (how) { + case ASSERT_GE: + if (have_len >= expect_len && !strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) >= strlen(%s) && !strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu >= %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_NOT_GE: + if (have_len > expect_len || strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) < strlen(%s) || strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu < %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_GT: + if (have_len > expect_len && !strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) > strlen(%s) && !strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu > %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_NOT_GT: + if (have_len <= expect_len || strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) <= strlen(%s) || strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu <= %zu || strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + default: + abort(); + } + goto free_and_exit; +} + + +void +test_assert(const char *file, int line, enum assert_type type, enum assert_how how, + const char *have_string, const char *expect_string, const void *have, const void *expect) +{ + switch (type) { + case ASSERT_ENUM: + case ASSERT_UINT: + test_assert_uint(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_INT: + test_assert_int(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_NULL: + case ASSERT_PTR: + test_assert_ptr(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_STR: + test_assert_str(file, line, how, have_string, expect_string, have, expect, 0); + break; + + case ASSERT_STR_REV: + test_assert_str(file, line, how, have_string, expect_string, have, expect, 1); + break; + + default: + abort(); + } +} -- cgit v1.2.3-70-g09d2