diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 8 | ||||
| -rw-r--r-- | src/Makefile.in | 56 | ||||
| -rw-r--r-- | src/deque.c | 52 | ||||
| -rw-r--r-- | src/format.c | 38 | ||||
| -rw-r--r-- | src/internal.h | 2 | ||||
| -rw-r--r-- | src/io.c | 34 | ||||
| -rw-r--r-- | src/libHX.map | 58 | ||||
| -rw-r--r-- | src/map_int.h | 3 | ||||
| -rw-r--r-- | src/misc.c | 14 | ||||
| -rw-r--r-- | src/opt.c | 299 | ||||
| -rw-r--r-- | src/string.c | 370 | ||||
| -rw-r--r-- | src/tc-list.c | 2 | ||||
| -rw-r--r-- | src/tc-map.c | 22 | ||||
| -rw-r--r-- | src/tc-option.c | 207 | ||||
| -rw-r--r-- | src/tc-realpath.c | 52 | ||||
| -rw-r--r-- | src/tc-string.c | 68 | ||||
| -rw-r--r-- | src/tc-strquote.c | 5 | ||||
| -rw-r--r-- | src/tc-switchuser.c | 19 | ||||
| -rw-r--r-- | src/tc-time.c | 1 | ||||
| -rw-r--r-- | src/tx-option.cpp | 21 |
20 files changed, 755 insertions, 576 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index f28c0a6..2e26d7e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,8 +9,8 @@ libHX_la_SOURCES = deque.c dl.c format.c io.c map.c \ mc.c misc.c opt.c proc.c \ rand.c socket.c string.c time.c libHX_la_LIBADD = ${libdl_LIBS} -lm ${libpthread_LIBS} ${librt_LIBS} ${libsocket_LIBS} -libHX_la_LDFLAGS = -no-undefined -version-info 40:0:8 -if WITH_GNU_LD +libHX_la_LDFLAGS = -no-undefined -version-info 43:0:0 +if WITH_LDSYM libHX_la_LDFLAGS += -Wl,--version-script=${srcdir}/libHX.map endif if WITH_SUN_LD @@ -60,7 +60,7 @@ if HAVE_CXX check_PROGRAMS += tx-compile tx-cast tx-deque tx-dir \ tx-intdiff tx-list tx-list2 \ tx-misc tx-netio \ - tx-option tx-proc tx-rand tx-strchr2 tx-string \ + tx-proc tx-rand tx-strchr2 tx-string \ tx-strquote tx-time TESTS += tx-strchr2 tx-strquote tx_cast_SOURCES = tx-cast.cpp @@ -80,8 +80,6 @@ tx_misc_SOURCES = tx-misc.cpp tx_misc_LDADD = libHX.la tx_netio_SOURCES = tx-netio.cpp tx_netio_LDADD = libHX.la ${libsocket_LIBS} -tx_option_SOURCES = tx-option.cpp -tx_option_LDADD = libHX.la tx_proc_SOURCES = tx-proc.cpp tx_proc_LDADD = libHX.la tx_rand_SOURCES = tx-rand.cpp diff --git a/src/Makefile.in b/src/Makefile.in index 0768775..9cbd614 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.17 from Makefile.am. +# Makefile.in generated by automake 1.18.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2024 Free Software Foundation, Inc. +# Copyright (C) 1994-2025 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -92,7 +92,7 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -@WITH_GNU_LD_TRUE@am__append_1 = -Wl,--version-script=${srcdir}/libHX.map +@WITH_LDSYM_TRUE@am__append_1 = -Wl,--version-script=${srcdir}/libHX.map @WITH_SUN_LD_TRUE@am__append_2 = -Wl,-M,${srcdir}/libHX.map @MINGW32_TRUE@am__append_3 = ux-file.c ux-mmap.c @MINGW32_TRUE@am__append_4 = -lws2_32 @@ -110,7 +110,7 @@ TESTS = tc-format$(EXEEXT) tc-option$(EXEEXT) tc-strchr2$(EXEEXT) \ @HAVE_CXX_TRUE@am__append_5 = tx-compile tx-cast tx-deque tx-dir \ @HAVE_CXX_TRUE@ tx-intdiff tx-list tx-list2 \ @HAVE_CXX_TRUE@ tx-misc tx-netio \ -@HAVE_CXX_TRUE@ tx-option tx-proc tx-rand tx-strchr2 tx-string \ +@HAVE_CXX_TRUE@ tx-proc tx-rand tx-strchr2 tx-string \ @HAVE_CXX_TRUE@ tx-strquote tx-time @HAVE_CXX_TRUE@am__append_6 = tx-strchr2 tx-strquote @@ -131,10 +131,10 @@ CONFIG_CLEAN_VPATH_FILES = @HAVE_CXX_TRUE@ tx-deque$(EXEEXT) tx-dir$(EXEEXT) \ @HAVE_CXX_TRUE@ tx-intdiff$(EXEEXT) tx-list$(EXEEXT) \ @HAVE_CXX_TRUE@ tx-list2$(EXEEXT) tx-misc$(EXEEXT) \ -@HAVE_CXX_TRUE@ tx-netio$(EXEEXT) tx-option$(EXEEXT) \ -@HAVE_CXX_TRUE@ tx-proc$(EXEEXT) tx-rand$(EXEEXT) \ -@HAVE_CXX_TRUE@ tx-strchr2$(EXEEXT) tx-string$(EXEEXT) \ -@HAVE_CXX_TRUE@ tx-strquote$(EXEEXT) tx-time$(EXEEXT) +@HAVE_CXX_TRUE@ tx-netio$(EXEEXT) tx-proc$(EXEEXT) \ +@HAVE_CXX_TRUE@ tx-rand$(EXEEXT) tx-strchr2$(EXEEXT) \ +@HAVE_CXX_TRUE@ tx-string$(EXEEXT) tx-strquote$(EXEEXT) \ +@HAVE_CXX_TRUE@ tx-time$(EXEEXT) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -296,10 +296,6 @@ am__tx_netio_SOURCES_DIST = tx-netio.cpp @HAVE_CXX_TRUE@am_tx_netio_OBJECTS = tx-netio.$(OBJEXT) tx_netio_OBJECTS = $(am_tx_netio_OBJECTS) @HAVE_CXX_TRUE@tx_netio_DEPENDENCIES = libHX.la $(am__DEPENDENCIES_1) -am__tx_option_SOURCES_DIST = tx-option.cpp -@HAVE_CXX_TRUE@am_tx_option_OBJECTS = tx-option.$(OBJEXT) -tx_option_OBJECTS = $(am_tx_option_OBJECTS) -@HAVE_CXX_TRUE@tx_option_DEPENDENCIES = libHX.la am__tx_proc_SOURCES_DIST = tx-proc.cpp @HAVE_CXX_TRUE@am_tx_proc_OBJECTS = tx-proc.$(OBJEXT) tx_proc_OBJECTS = $(am_tx_proc_OBJECTS) @@ -360,11 +356,11 @@ am__depfiles_remade = ./$(DEPDIR)/deque.Plo ./$(DEPDIR)/dl.Plo \ ./$(DEPDIR)/tx-deque.Po ./$(DEPDIR)/tx-dir.Po \ ./$(DEPDIR)/tx-intdiff.Po ./$(DEPDIR)/tx-list.Po \ ./$(DEPDIR)/tx-misc.Po ./$(DEPDIR)/tx-netio.Po \ - ./$(DEPDIR)/tx-option.Po ./$(DEPDIR)/tx-proc.Po \ - ./$(DEPDIR)/tx-rand.Po ./$(DEPDIR)/tx-strchr2.Po \ - ./$(DEPDIR)/tx-string.Po ./$(DEPDIR)/tx-strquote.Po \ - ./$(DEPDIR)/tx-time.Po ./$(DEPDIR)/tx_list2-tx-list2.Po \ - ./$(DEPDIR)/ux-file.Plo ./$(DEPDIR)/ux-mmap.Plo + ./$(DEPDIR)/tx-proc.Po ./$(DEPDIR)/tx-rand.Po \ + ./$(DEPDIR)/tx-strchr2.Po ./$(DEPDIR)/tx-string.Po \ + ./$(DEPDIR)/tx-strquote.Po ./$(DEPDIR)/tx-time.Po \ + ./$(DEPDIR)/tx_list2-tx-list2.Po ./$(DEPDIR)/ux-file.Plo \ + ./$(DEPDIR)/ux-mmap.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -410,9 +406,8 @@ SOURCES = $(libHX_la_SOURCES) tc-cast.c tc-compile.c tc-deque.c \ $(tx_cast_SOURCES) $(tx_compile_SOURCES) $(tx_deque_SOURCES) \ $(tx_dir_SOURCES) $(tx_intdiff_SOURCES) $(tx_list_SOURCES) \ $(tx_list2_SOURCES) $(tx_misc_SOURCES) $(tx_netio_SOURCES) \ - $(tx_option_SOURCES) $(tx_proc_SOURCES) $(tx_rand_SOURCES) \ - $(tx_strchr2_SOURCES) $(tx_string_SOURCES) \ - $(tx_strquote_SOURCES) $(tx_time_SOURCES) + $(tx_proc_SOURCES) $(tx_rand_SOURCES) $(tx_strchr2_SOURCES) \ + $(tx_string_SOURCES) $(tx_strquote_SOURCES) $(tx_time_SOURCES) DIST_SOURCES = $(am__libHX_la_SOURCES_DIST) tc-cast.c tc-compile.c \ tc-deque.c tc-dir.c tc-format.c tc-io.c tc-list.c tc-list2.c \ tc-map.c tc-memmem.c tc-misc.c tc-netio.c tc-option.c \ @@ -423,10 +418,9 @@ DIST_SOURCES = $(am__libHX_la_SOURCES_DIST) tc-cast.c tc-compile.c \ $(am__tx_dir_SOURCES_DIST) $(am__tx_intdiff_SOURCES_DIST) \ $(am__tx_list_SOURCES_DIST) $(am__tx_list2_SOURCES_DIST) \ $(am__tx_misc_SOURCES_DIST) $(am__tx_netio_SOURCES_DIST) \ - $(am__tx_option_SOURCES_DIST) $(am__tx_proc_SOURCES_DIST) \ - $(am__tx_rand_SOURCES_DIST) $(am__tx_strchr2_SOURCES_DIST) \ - $(am__tx_string_SOURCES_DIST) $(am__tx_strquote_SOURCES_DIST) \ - $(am__tx_time_SOURCES_DIST) + $(am__tx_proc_SOURCES_DIST) $(am__tx_rand_SOURCES_DIST) \ + $(am__tx_strchr2_SOURCES_DIST) $(am__tx_string_SOURCES_DIST) \ + $(am__tx_strquote_SOURCES_DIST) $(am__tx_time_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -563,7 +557,7 @@ am__common_driver_flags = \ # To be inserted before the command running the test. Creates the # directory for the log if needed. Stores in $dir the directory # containing $f, in $tst the test, in $log the log. Executes the -# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# developer-defined test setup AM_TESTS_ENVIRONMENT (if any), and # passes TESTS_ENVIRONMENT. Set up options for the wrapper that # will run the test scripts (or their associated LOG_COMPILER, if # thy have one). @@ -780,7 +774,7 @@ libHX_la_SOURCES = deque.c dl.c format.c io.c map.c mc.c misc.c opt.c \ proc.c rand.c socket.c string.c time.c $(am__append_3) libHX_la_LIBADD = ${libdl_LIBS} -lm ${libpthread_LIBS} ${librt_LIBS} \ ${libsocket_LIBS} $(am__append_4) -libHX_la_LDFLAGS = -no-undefined -version-info 40:0:8 $(am__append_1) \ +libHX_la_LDFLAGS = -no-undefined -version-info 43:0:0 $(am__append_1) \ $(am__append_2) EXTRA_libHX_la_DEPENDENCIES = libHX.map EXTRA_DIST = internal.h map_int.h libHX.map uxcompat.h analyze.sh @@ -825,8 +819,6 @@ tc_time_LDADD = libHX.la @HAVE_CXX_TRUE@tx_misc_LDADD = libHX.la @HAVE_CXX_TRUE@tx_netio_SOURCES = tx-netio.cpp @HAVE_CXX_TRUE@tx_netio_LDADD = libHX.la ${libsocket_LIBS} -@HAVE_CXX_TRUE@tx_option_SOURCES = tx-option.cpp -@HAVE_CXX_TRUE@tx_option_LDADD = libHX.la @HAVE_CXX_TRUE@tx_proc_SOURCES = tx-proc.cpp @HAVE_CXX_TRUE@tx_proc_LDADD = libHX.la @HAVE_CXX_TRUE@tx_rand_SOURCES = tx-rand.cpp @@ -1041,10 +1033,6 @@ tx-netio$(EXEEXT): $(tx_netio_OBJECTS) $(tx_netio_DEPENDENCIES) $(EXTRA_tx_netio @rm -f tx-netio$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(tx_netio_OBJECTS) $(tx_netio_LDADD) $(LIBS) -tx-option$(EXEEXT): $(tx_option_OBJECTS) $(tx_option_DEPENDENCIES) $(EXTRA_tx_option_DEPENDENCIES) - @rm -f tx-option$(EXEEXT) - $(AM_V_CXXLD)$(CXXLINK) $(tx_option_OBJECTS) $(tx_option_LDADD) $(LIBS) - tx-proc$(EXEEXT): $(tx_proc_OBJECTS) $(tx_proc_DEPENDENCIES) $(EXTRA_tx_proc_DEPENDENCIES) @rm -f tx-proc$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(tx_proc_OBJECTS) $(tx_proc_LDADD) $(LIBS) @@ -1119,7 +1107,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-list.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-misc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-netio.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-option.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-proc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-rand.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tx-strchr2.Po@am__quote@ # am--include-marker @@ -1513,6 +1500,7 @@ tx-strquote.log: tx-strquote$(EXEEXT) @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -1640,7 +1628,6 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/tx-list.Po -rm -f ./$(DEPDIR)/tx-misc.Po -rm -f ./$(DEPDIR)/tx-netio.Po - -rm -f ./$(DEPDIR)/tx-option.Po -rm -f ./$(DEPDIR)/tx-proc.Po -rm -f ./$(DEPDIR)/tx-rand.Po -rm -f ./$(DEPDIR)/tx-strchr2.Po @@ -1739,7 +1726,6 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/tx-list.Po -rm -f ./$(DEPDIR)/tx-misc.Po -rm -f ./$(DEPDIR)/tx-netio.Po - -rm -f ./$(DEPDIR)/tx-option.Po -rm -f ./$(DEPDIR)/tx-proc.Po -rm -f ./$(DEPDIR)/tx-rand.Po -rm -f ./$(DEPDIR)/tx-strchr2.Po diff --git a/src/deque.c b/src/deque.c index bc39e0e..4124d1a 100644 --- a/src/deque.c +++ b/src/deque.c @@ -1,6 +1,6 @@ /* * Double-ended queues - * Copyright Jan Engelhardt, 2002-2008 + * Copyright Jan Engelhardt, 2025 * * This file is part of libHX. libHX is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser @@ -8,7 +8,9 @@ * either version 2.1 or (at your option) any later version. */ #include <errno.h> +#include <limits.h> #include <stdarg.h> +#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <libHX/deque.h> @@ -162,22 +164,62 @@ EXPORT_SYMBOL void HXdeque_genocide2(struct HXdeque *dq, void (*xfree)(void *)) free(dq); } -EXPORT_SYMBOL void ** -HXdeque_to_vec(const struct HXdeque *dq, unsigned int *num) +static void ** +HXdeque_to_veci(const struct HXdeque *dq, size_t *num, bool sentinel) { const struct HXdeque_node *trav; void **ret, **p; - ret = malloc((dq->items + 1) * sizeof(void *)); + ret = malloc((dq->items + sentinel) * sizeof(void *)); if (ret == NULL) return NULL; p = ret; for (trav = dq->first; trav != NULL; trav = trav->next) *p++ = trav->ptr; - *p = NULL; + if (sentinel) + *p = NULL; if (num != NULL) *num = dq->items; return ret; } + +EXPORT_SYMBOL void ** +HXdeque_to_vec(const struct HXdeque *dq, size_t *num) +{ + return HXdeque_to_veci(dq, num, true); +} + +EXPORT_SYMBOL void ** +HXdeque_to_vecx(const struct HXdeque *dq, size_t *num) +{ + return HXdeque_to_veci(dq, num, false); +} + +char **HXdeque_to_vec_strdup(const struct HXdeque *dq, size_t *num) +{ + const struct HXdeque_node *iter; + char **ret, **p; + int se; + + ret = malloc((dq->items + 1) * sizeof(char *)); + if (ret == nullptr) + return nullptr; + p = ret; + for (iter = dq->first; iter != nullptr; ++p, iter = iter->next) { + *p = strdup(iter->ptr); + if (*p == nullptr) + goto out; + } + *p = nullptr; + if (num != nullptr) + *num = dq->items; + return ret; + + out: + se = errno; + HX_zvecfree(ret); + errno = se; + return nullptr; +} diff --git a/src/format.c b/src/format.c index e924c6a..985a527 100644 --- a/src/format.c +++ b/src/format.c @@ -20,9 +20,6 @@ #undef HXformat_aprintf #undef HXformat_fprintf #undef HXformat_sprintf -extern int HXformat_aprintf(const struct HXformat_map *, hxmc_t **, const char *); -extern int HXformat_sprintf(const struct HXformat_map *, char *, size_t, const char *); -extern int HXformat_fprintf(const struct HXformat_map *, FILE *, const char *); /* To make it easier on the highlighter */ #define C_OPEN '(' @@ -380,7 +377,7 @@ static hxmc_t *HXformat2_xcall(const char *name, const char **pptr, hxmc_t *ret, *ret2, **argv; struct HXdeque *dq; const char *s, *delim; - int err = 0; + ssize_t err = 0; dq = HXdeque_init(); if (dq == NULL) @@ -563,13 +560,13 @@ HXformat2_xany(const char **pptr, const struct HXformat_map *blk) /* Closing parenthesis - variable */ const struct fmt_entry *entry; hxmc_t *new_name = NULL; - int eret; + ssize_t eret; *pptr = ++s; eret = HXformat_aprintf(blk, &new_name, name); if (eret <= 0) { ret = NULL; - } else if (*new_name == '\0') { + } else if (new_name == nullptr || *new_name == '\0') { ret = &HXformat2_nexp; } else { entry = HXmap_get(blk->vars, new_name); @@ -630,14 +627,7 @@ EXPORT_SYMBOL struct HXformat_map *HXformat_init(void) return NULL; } -EXPORT_SYMBOL int HXformat_aprintf(const struct HXformat_map *blk, - hxmc_t **resultp, const char *fmt) -{ - ssize_t ret = HXformat3_aprintf(blk, resultp, fmt); - return ret > INT_MAX ? INT_MAX : ret; -} - -EXPORT_SYMBOL ssize_t HXformat3_aprintf(const struct HXformat_map *blk, +EXPORT_SYMBOL ssize_t HXformat_aprintf(const struct HXformat_map *blk, hxmc_t **resultp, const char *fmt) { hxmc_t *ex, *ts, *out; @@ -686,20 +676,13 @@ EXPORT_SYMBOL ssize_t HXformat3_aprintf(const struct HXformat_map *blk, return ret; } -EXPORT_SYMBOL int HXformat_fprintf(const struct HXformat_map *ftable, - FILE *filp, const char *fmt) -{ - ssize_t ret = HXformat3_fprintf(ftable, filp, fmt); - return ret > INT_MAX ? INT_MAX : ret; -} - -EXPORT_SYMBOL ssize_t HXformat3_fprintf(const struct HXformat_map *ftable, +EXPORT_SYMBOL ssize_t HXformat_fprintf(const struct HXformat_map *ftable, FILE *filp, const char *fmt) { hxmc_t *str; ssize_t ret; - if ((ret = HXformat3_aprintf(ftable, &str, fmt)) <= 0) + if ((ret = HXformat_aprintf(ftable, &str, fmt)) <= 0) return ret; errno = 0; if (fputs(str, filp) < 0) @@ -708,14 +691,7 @@ EXPORT_SYMBOL ssize_t HXformat3_fprintf(const struct HXformat_map *ftable, return ret; } -EXPORT_SYMBOL int HXformat_sprintf(const struct HXformat_map *ftable, - char *dest, size_t size, const char *fmt) -{ - ssize_t ret = HXformat3_sprintf(ftable, dest, size, fmt); - return ret > INT_MAX ? INT_MAX : ret; -} - -EXPORT_SYMBOL ssize_t HXformat3_sprintf(const struct HXformat_map *ftable, +EXPORT_SYMBOL ssize_t HXformat_sprintf(const struct HXformat_map *ftable, char *dest, size_t size, const char *fmt) { hxmc_t *str = nullptr; diff --git a/src/internal.h b/src/internal.h index f7d83d8..2f5aa8f 100644 --- a/src/internal.h +++ b/src/internal.h @@ -50,9 +50,11 @@ struct memcont { char data[]; }; +struct HXdeque; struct timespec; struct timeval; +extern char **HXdeque_to_vec_strdup(const struct HXdeque *, size_t *); extern hxmc_t *HXparse_dequote_fmt(const char *, const char *, const char **); extern size_t HX_substr_helper(size_t, long, long, size_t *); @@ -65,8 +65,14 @@ static int mkdir_gen(const char *d, unsigned int mode) if (mkdir(d, mode) == 0) /* use umask() for permissions */ #endif return 1; +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) + /* Undocumented extra error codes */ + if (errno != EEXIST && errno != EISDIR) + return -errno; +#else if (errno != EEXIST) return -errno; +#endif if (stat(d, &sb) == 0) { #if defined(_WIN32) if (sb.st_mode & S_IFDIR) @@ -98,7 +104,7 @@ EXPORT_SYMBOL struct HXdir *HXdir_open(const char *s) * and bug-concealing "char d_name[256]", while on Solaris, it is a * proper "char d_name[]". */ - size_t size = sizeof(*d); + size_t size = offsetof(struct dirent, d_name); ssize_t name_max; DIR *tmp_dh = opendir(s); if (tmp_dh == NULL) @@ -110,7 +116,6 @@ EXPORT_SYMBOL struct HXdir *HXdir_open(const char *s) */ name_max = fpathconf(dirfd(tmp_dh), _PC_NAME_MAX); if (name_max > 0) { - size -= sizeof(struct dirent) - offsetof(struct dirent, d_name); size += name_max + 1; } else { #ifdef NAME_MAX @@ -378,6 +383,8 @@ EXPORT_SYMBOL int HX_readlink(hxmc_t **target, const char *path) HXmc_setlen(target, ret); // \0 set here anyway return ret; } + if (linkbuf_size > SIZE_MAX / 2) + return -E2BIG; linkbuf_size *= 2; if (HXmc_setlen(target, linkbuf_size) == NULL) { int saved_errno = errno; @@ -483,7 +490,10 @@ EXPORT_SYMBOL int HX_getcwd(hxmc_t **target) return 1; } if (errno == ERANGE) { - if (HXmc_setlen(target, linkbuf_size *= 2) != nullptr) + if (linkbuf_size > SIZE_MAX / 2) + return -E2BIG; + linkbuf_size *= 2; + if (HXmc_setlen(target, linkbuf_size) != nullptr) continue; /* errno already set by realloc, fall into next if block */ } @@ -777,16 +787,18 @@ EXPORT_SYMBOL char *HX_slurp_fd(int fd, size_t *outsize) char *buf = malloc(bufsize); if (buf == nullptr) return nullptr; - ssize_t rdret; - while ((rdret = read(fd, buf + offset, bufsize - 1 - offset)) > 0) { + do { + assert(offset < bufsize); + ssize_t rdret = read(fd, buf + offset, bufsize - 1 - offset); + if (rdret <= 0) + break; offset += rdret; - /* - * Make it so that the next read call is not called - * with an exceptionally small size. - */ if (bufsize - offset >= 4095) + /* 4K room still, just continue reading. */ continue; - if (bufsize > SSIZE_MAX) + + /* Less than 4K room, enlarge now */ + if (bufsize > SIZE_MAX / 2) /* No more doubling */ break; bufsize *= 2; @@ -798,7 +810,7 @@ EXPORT_SYMBOL char *HX_slurp_fd(int fd, size_t *outsize) return nullptr; } buf = nbuf; - } + } while (true); buf[offset] = '\0'; if (outsize != nullptr) *outsize = offset; diff --git a/src/libHX.map b/src/libHX.map index 922cc3c..1154304 100644 --- a/src/libHX.map +++ b/src/libHX.map @@ -1,4 +1,4 @@ -LIBHX_3.25 { +LIBHX_5.0 { global: HX_basename; HX_basename_exact; @@ -15,7 +15,6 @@ global: HX_ffs; HX_fls; HX_getl; - HX_getopt; HX_getopt_help; HX_getopt_help_cb; HX_getopt_usage; @@ -36,7 +35,6 @@ global: HX_split; HX_split_fixed; HX_split_inplace; - HX_stpltrim; HX_strbchr; HX_strchr2; HX_strclone; @@ -54,7 +52,6 @@ global: HX_strrev; HX_strrtrim; HX_strsep2; - HX_strsep; HX_strupper; HX_time_compare; HX_timespec_add; @@ -123,68 +120,35 @@ global: HXproc_run_async; HXproc_run_sync; HXproc_wait; -local: - *; -}; - -LIBHX_3.27 { -global: HX_socket_from_env; HX_slurp_fd; HX_slurp_file; HXproc_switch_user; HXproc_top_fd; -} LIBHX_3.25; - -LIBHX_4.2 { -global: HX_strtod_unit; HX_strtoull_unit; HX_unit_size; HX_unit_size_cu; -} LIBHX_3.27; - -LIBHX_4.3 { -global: HX_sendfile; HX_unit_seconds; HX_strtoull_sec; -} LIBHX_4.2; - -LIBHX_4.9 { -global: - HXformat3_aprintf; - HXformat3_fprintf; - HXformat3_sprintf; + HXformat_aprintf; + HXformat_fprintf; + HXformat_sprintf; HX_ipaddr_is_local; HX_sockaddr_is_local; -} LIBHX_4.3; - -LIBHX_4.11 { -global: HX_addrport_split; HX_inet_connect; HX_inet_listen; HX_local_listen; -} LIBHX_4.9; - -LIBHX_4.15 { -global: HX_flpr; HX_flprf; -} LIBHX_4.11; - -LIBHX_4.16 { -global: HX_strtoull_nsec; -} LIBHX_4.15; - -LIBHX_4.18 { -global: - HX_getopt5; -} LIBHX_4.16; - -LIBHX_4.24 { -global: HX_getcwd; -} LIBHX_4.18; + HX_strtoull8601p_sec; + HX_getopt6; + HX_getopt6_clean; + HXdeque_to_vecx; +local: + *; +}; diff --git a/src/map_int.h b/src/map_int.h index 6e95c69..b7d74f8 100644 --- a/src/map_int.h +++ b/src/map_int.h @@ -14,7 +14,8 @@ extern "C" { */ struct HXmap_private { /* from struct HXmap */ - unsigned int items, flags; + size_t items; + unsigned int flags; /* private: */ enum HXmap_type type; @@ -51,24 +51,24 @@ static __inline__ void hexdump_ascii(FILE *fp, unsigned char c, bool tty) fprintf(fp, "."); } -EXPORT_SYMBOL void HX_hexdump(FILE *fp, const void *vptr, unsigned int len) +EXPORT_SYMBOL void HX_hexdump(FILE *fp, const void *vptr, size_t len) { const unsigned char *ptr = vptr; - unsigned int i, j; + size_t i; bool tty = isatty(fileno(fp)); - fprintf(fp, "Dumping %u bytes\n", len); + fprintf(fp, "Dumping %zu bytes\n", len); for (i = 0; i < len / 16; ++i) { - fprintf(fp, "%04x | ", i * 16); - for (j = 0; j < 16; ++j) + fprintf(fp, "%04zx | ", i * 16); + for (unsigned int j = 0; j < 16; ++j) fprintf(fp, "%02x%c", *ptr++, (j == 7) ? '-' : ' '); ptr -= 16; fprintf(fp, "| "); - for (j = 0; j < 16; ++j) + for (unsigned int j = 0; j < 16; ++j) hexdump_ascii(fp, *ptr++, tty); fprintf(fp, "\n"); } - fprintf(fp, "%04x | ", i * 16); + fprintf(fp, "%04zx | ", i * 16); len -= i * 16; for (i = 0; i < len; ++i) fprintf(fp, "%02x%c", ptr[i], (i == 7) ? '-' : ' '); @@ -1,6 +1,6 @@ /* * libHX/opt.c - * Copyright Jan Engelhardt, 2002-2011 + * Copyright Jan Engelhardt, 2025 * * This file is part of libHX. libHX is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser @@ -8,6 +8,7 @@ * either version 2.1 or (at your option) any later version. */ #include <errno.h> +#include <limits.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -119,13 +120,15 @@ enum { /** * struct HX_getopt_vars - option parser working variable set * @arg0: saved program name - * @remaining: list of extracted non-options + * @desc: list of extracted options + * @oarg: list of extracted options + * @uarg: list of extracted non-options * @cbi: callback info * @flags: flags setting the behavior for HX_getopt */ struct HX_getopt_vars { const char *arg0; - struct HXdeque *remaining; + struct HXdeque *desc, *oarg, *uarg; struct HXoptcb cbi; unsigned int flags; }; @@ -218,6 +221,10 @@ static void do_assign(struct HXoptcb *cbi, const char *arg0) if (opt->ptr != NULL) *static_cast(char **, opt->ptr) = HX_strdup(cbi->data); break; + case HXTYPE_STRP: + if (opt->ptr != nullptr) + *static_cast(const char **, opt->ptr) = cbi->data; + break; case HXTYPE_STRDQ: HXdeque_push(opt->ptr, HX_strdup(cbi->data)); break; @@ -237,6 +244,15 @@ static void do_assign(struct HXoptcb *cbi, const char *arg0) opt->cb(cbi); } +static int do_assign_front(struct HX_getopt_vars *par) +{ + if (HXdeque_push(par->desc, par->cbi.current) == nullptr || + HXdeque_push(par->oarg, par->cbi.data) == nullptr) + return -errno; + do_assign(&par->cbi, par->arg0); + return 0; +} + static __inline__ const struct HXoption * lookup_short(const struct HXoption *table, char opt) { @@ -494,27 +510,14 @@ static int HX_getopt_error(int err, const char *key, unsigned int flags) return HXOPT_I_ERROR; } -static int HX_getopt_twolong(const char *const *opt, +static int HX_getopt_twolong(const char *key, const char *value, struct HX_getopt_vars *par) { - const char *key = opt[0], *value = opt[1]; - par->cbi.current = lookup_long_pfx(par->cbi.table, key + 2); if (par->cbi.current == &HXopt_ambig_prefix) return HX_getopt_error(HXOPT_E_AMBIG_PREFIX, key, par->flags); - if (par->cbi.current == NULL) { - if (par->flags & HXOPT_PTHRU) { - char *tmp = HX_strdup(key); - if (tmp == NULL) - return -errno; - if (HXdeque_push(par->remaining, tmp) == NULL) { - free(tmp); - return -errno; - } - return HXOPT_S_NORMAL | HXOPT_I_ADVARG; - } + if (par->cbi.current == nullptr) return HX_getopt_error(HXOPT_E_LONG_UNKNOWN, key, par->flags); - } par->cbi.flags = HXOPTCB_BY_LONG; if (takes_void(par->cbi.current->type)) { @@ -550,11 +553,21 @@ static int HX_getopt_long(const char *cur, struct HX_getopt_vars *par) return -errno; value = strchr(key, '='); if (value == nullptr) { - /* Cannot happen because state is always !S_TWOLONG */ + /* + * Cannot happen because state is always !S_TWOLONG, but make + * static analyzers happy. + */ free(key); return -EINVAL; } - *value++ = '\0'; + *value = '\0'; + value = strchr(cur, '='); + if (value == nullptr) { + /* Cannot happen either */ + free(key); + return -EINVAL; + } + ++value; par->cbi.current = lookup_long_pfx(par->cbi.table, key + 2); if (par->cbi.current == &HXopt_ambig_prefix) { ret = HX_getopt_error(HXOPT_E_AMBIG_PREFIX, key, par->flags); @@ -562,15 +575,6 @@ static int HX_getopt_long(const char *cur, struct HX_getopt_vars *par) return ret; } if (par->cbi.current == NULL) { - if (par->flags & HXOPT_PTHRU) { - /* Undo nuke of '=' and reuse alloc */ - value[-1] = '='; - if (HXdeque_push(par->remaining, key) == NULL) { - free(key); - return -errno; - } - return HXOPT_S_NORMAL | HXOPT_I_ADVARG; - } ret = HX_getopt_error(HXOPT_E_LONG_UNKNOWN, key, par->flags); free(key); return ret; @@ -585,58 +589,48 @@ static int HX_getopt_long(const char *cur, struct HX_getopt_vars *par) return ret; } + free(key); par->cbi.flags = HXOPTCB_BY_LONG; par->cbi.data = value; - /* Not possible to use %HXOPT_I_ASSIGN due to transience of @key. */ - do_assign(&par->cbi, par->arg0); - free(key); + /* + * Not possible to use %HXOPT_I_ASSIGN due to transience of @key. Thus + * manually call do_assign now rather than in the superordinate + * function. + */ + ret = do_assign_front(par); + if (ret < 0) + return ret; return HXOPT_S_NORMAL | HXOPT_I_ADVARG; } -static int HX_getopt_short(const char *const *opt, const char *cur, +static int HX_getopt_short(const char *key, const char *value, struct HX_getopt_vars *par) { - char op = *cur; + char op = *key; if (op == '\0') return HXOPT_S_NORMAL | HXOPT_I_ADVARG; par->cbi.current = lookup_short(par->cbi.table, op); - if (par->cbi.current == NULL) { - if (par->flags & HXOPT_PTHRU) { - /* - * @cur-1 is always valid: it is either the previous - * char, or it is '-'. - */ - char *buf = HX_strdup(cur - 1); - if (buf != NULL) - *buf = '-'; - if (HXdeque_push(par->remaining, buf) == NULL) { - free(buf); - return -errno; - } - return HXOPT_S_NORMAL | HXOPT_I_ADVARG; - } + if (par->cbi.current == nullptr) return HX_getopt_error(HXOPT_E_SHORT_UNKNOWN, &op, par->flags); - } par->cbi.flags = HXOPTCB_BY_SHORT; if (takes_void(par->cbi.current->type)) { /* -A */ par->cbi.data = NULL; return HXOPT_S_SHORT | HXOPT_I_ASSIGN | HXOPT_I_ADVCHAR; - } else if (cur[1] != '\0') { + } else if (key[1] != '\0') { /* -Avalue */ - par->cbi.data = cur + 1; + par->cbi.data = key + 1; return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG; } - cur = *++opt; if (par->cbi.current->type & HXOPT_OPTIONAL) { - if (cur == NULL || *cur != '-' || - (cur[0] == '-' && cur[1] == '\0')) { + if (value == nullptr || *value != '-' || + (value[0] == '-' && value[1] == '\0')) { /* -f - -f bla */ - par->cbi.data = cur; + par->cbi.data = value; return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG2; } else { /* -f -a-file --another --file -- endofoptions */ @@ -645,41 +639,28 @@ static int HX_getopt_short(const char *const *opt, const char *cur, } } else { /* -A value */ - if (cur == NULL) + if (value == nullptr) return HX_getopt_error(HXOPT_E_SHORT_MISSING, &op, par->flags); - par->cbi.data = cur; + par->cbi.data = value; return HXOPT_S_NORMAL | HXOPT_I_ASSIGN | HXOPT_I_ADVARG2; } } static int HX_getopt_term(const char *cur, const struct HX_getopt_vars *par) { - char *tmp = HX_strdup(cur); - if (tmp == NULL) - return -errno; - if (HXdeque_push(par->remaining, tmp) == NULL) { - free(tmp); + if (HXdeque_push(par->uarg, cur) == nullptr) return -errno; - } return HXOPT_S_TERMINATED | HXOPT_I_ADVARG; } static int HX_getopt_normal(const char *cur, const struct HX_getopt_vars *par) { if (cur[0] == '-' && cur[1] == '\0') { - /* Note to popt developers: A single dash is NOT an option! */ - HXdeque_push(par->remaining, HX_strdup(cur)); + HXdeque_push(par->uarg, cur); return HXOPT_S_NORMAL | HXOPT_I_ADVARG; } - if (cur[0] == '-' && cur[1] == '-' && cur[2] == '\0') { - /* - * Double dash. If passthrough is on, "--" must be copied into - * @remaining. This is done in the next round. - */ - if (!(par->flags & HXOPT_PTHRU)) - return HXOPT_S_TERMINATED | HXOPT_I_ADVARG; - return HXOPT_S_TERMINATED; - } + if (cur[0] == '-' && cur[1] == '-' && cur[2] == '\0') + return HXOPT_S_TERMINATED | HXOPT_I_ADVARG; if (cur[0] == '-' && cur[1] == '-') { /* long option */ if (strchr(cur + 2, '=') == NULL) return HXOPT_S_TWOLONG; @@ -692,62 +673,71 @@ static int HX_getopt_normal(const char *cur, const struct HX_getopt_vars *par) if (par->flags & HXOPT_RQ_ORDER) /* POSIX: first non-option implies option termination */ return HXOPT_S_TERMINATED; - cur = HX_strdup(cur); - if (cur == NULL || HXdeque_push(par->remaining, cur) == NULL) + if (HXdeque_push(par->uarg, cur) == nullptr) return -errno; return HXOPT_S_NORMAL | HXOPT_I_ADVARG; } -EXPORT_SYMBOL int HX_getopt5(const struct HXoption *table, char **orig_argv, - int *new_argc, char ***new_argv, unsigned int flags) +EXPORT_SYMBOL void HX_getopt6_clean(struct HXopt6_result *r) +{ + free(r->desc); + free(r->oarg); + free(r->uarg); + HX_zvecfree(r->dup_argv); + r->desc = nullptr; + r->oarg = nullptr; + r->uarg = nullptr; + r->dup_argv = nullptr; +} + +EXPORT_SYMBOL int HX_getopt6(const struct HXoption *table, int argc, + char **argv, struct HXopt6_result *result, unsigned int flags) { struct HX_getopt_vars ps; - const char **opt = const_cast(const char **, orig_argv); + const char **opt = const_cast(const char **, argv); int state = HXOPT_S_NORMAL; int ret = -ENOMEM; - unsigned int argk = 0; - if (new_argc != nullptr) - *new_argc = 0; - if (new_argv != nullptr) - *new_argv = nullptr; + if ((flags & (HXOPT_RQ_ORDER | HXOPT_ANY_ORDER)) == (HXOPT_RQ_ORDER | HXOPT_ANY_ORDER)) + return -EINVAL; + if (result == nullptr && flags & (HXOPT_ITER_OPTS | HXOPT_ITER_ARGS | HXOPT_DUP_ARGS)) + return -EINVAL; + if (result != nullptr) + memset(result, 0, sizeof(*result)); memset(&ps, 0, sizeof(ps)); - ps.remaining = HXdeque_init(); - if (ps.remaining == NULL) { + ps.desc = HXdeque_init(); + ps.oarg = HXdeque_init(); + ps.uarg = HXdeque_init(); + if (ps.desc == nullptr || ps.oarg == nullptr || ps.uarg == nullptr) { ret = -errno; goto out; } ps.flags = flags; - ps.arg0 = *opt; - ps.cbi.table = table; - - if (*opt != NULL) { - /* put argv[0] back */ - char *arg = HX_strdup(*opt++); - if (arg == NULL) { - ret = -errno; - goto out; - } - if (HXdeque_push(ps.remaining, arg) == NULL) { - free(arg); - ret = -errno; - goto out; - } + if (argc < 0) { + argc = 0; + for (const char **p = opt; *p != nullptr; ++p) + ++argc; } + if (argc > 0) { + ps.arg0 = *opt++; + --argc; + } + ps.cbi.table = table; - if (posix_me_harder()) + if (!(ps.flags & HXOPT_ANY_ORDER) && posix_me_harder()) ps.flags |= HXOPT_RQ_ORDER; - for (const char *cur = *opt; cur != NULL; ) { + const char *op0 = argc > 0 ? opt[0] : nullptr; + while (argc > 0) { if (state == HXOPT_S_TWOLONG) - state = HX_getopt_twolong(opt, &ps); + state = HX_getopt_twolong(op0, argc > 1 ? opt[1] : nullptr, &ps); else if (state == HXOPT_S_LONG) - state = HX_getopt_long(cur, &ps); + state = HX_getopt_long(op0, &ps); else if (state == HXOPT_S_SHORT) - state = HX_getopt_short(opt, cur, &ps); + state = HX_getopt_short(op0, argc > 1 ? opt[1] : nullptr, &ps); else if (state == HXOPT_S_TERMINATED) - state = HX_getopt_term(cur, &ps); + state = HX_getopt_term(op0, &ps); else if (state == HXOPT_S_NORMAL) - state = HX_getopt_normal(cur, &ps); + state = HX_getopt_normal(op0, &ps); if (state < 0) { ret = state; @@ -757,31 +747,62 @@ EXPORT_SYMBOL int HX_getopt5(const struct HXoption *table, char **orig_argv, ret = state & ~HXOPT_I_ERROR; goto out; } - if (state & HXOPT_I_ASSIGN) - do_assign(&ps.cbi, ps.arg0); - if (state & HXOPT_I_ADVARG) - cur = *++opt; - else if (state & HXOPT_I_ADVARG2) - cur = *(opt += 2); - else if (state & HXOPT_I_ADVCHAR) - ++cur; + if (state & HXOPT_I_ASSIGN) { + ret = do_assign_front(&ps); + if (ret < 0) + goto out; + } + if (state & HXOPT_I_ADVARG) { + --argc; + if (argc > 0) + op0 = *++opt; + } else if (state & HXOPT_I_ADVARG2) { + opt += 2; + argc -= 2; + if (argc > 0) + op0 = *opt; + } else if (state & HXOPT_I_ADVCHAR) { + ++op0; + } state &= ~HXOPT_I_MASK; } - if (new_argv != nullptr) { - *new_argv = reinterpret_cast(char **, HXdeque_to_vec(ps.remaining, &argk)); - if (*new_argv == nullptr) { + if (flags & HXOPT_ITER_OPTS) { + size_t nelem = 0; + result->desc = reinterpret_cast(const struct HXoption **, HXdeque_to_vec(ps.desc, &nelem)); + result->oarg = reinterpret_cast(char **, HXdeque_to_vec(ps.oarg, &nelem)); + if (result->desc == nullptr || result->oarg == nullptr) { + ret = -errno; + goto out; + } + result->nopts = nelem < INT_MAX ? nelem : INT_MAX; + } + if (flags & HXOPT_ITER_ARGS) { + size_t nelem = 0; + result->uarg = reinterpret_cast(char **, HXdeque_to_vecx(ps.uarg, &nelem)); + if (result->uarg == nullptr) { + ret = -errno; + goto out; + } + result->nargs = nelem < INT_MAX ? nelem : INT_MAX; + } + if (flags & HXOPT_DUP_ARGS) { + if (ps.arg0 != nullptr && HXdeque_unshift(ps.uarg, ps.arg0) == nullptr) { ret = -errno; goto out; } - if (new_argc != nullptr) - *new_argc = argk; - /* pointers are owned by new_argv now, so free only the deque head */ - HXdeque_free(ps.remaining); - ps.remaining = nullptr; + size_t nelem = 0; + result->dup_argv = HXdeque_to_vec_strdup(ps.uarg, &nelem); + if (result->dup_argv == nullptr) { + ret = -errno; + goto out; + } + result->dup_argc = nelem < INT_MAX ? nelem : INT_MAX; } ret = HXOPT_ERR_SUCCESS; out: + if (ret != HXOPT_ERR_SUCCESS && result != nullptr) + HX_getopt6_clean(result); if (ret == HXOPT_ERR_SUCCESS) { } else if (ret < 0) { if (!(ps.flags & HXOPT_QUIET)) @@ -793,28 +814,12 @@ EXPORT_SYMBOL int HX_getopt5(const struct HXoption *table, char **orig_argv, else if (ps.flags & HXOPT_USAGEONERR) HX_getopt_usage(&ps.cbi, stderr); } - if (ps.remaining != nullptr) - HXdeque_genocide2(ps.remaining, free); - return ret; -} - -EXPORT_SYMBOL int HX_getopt(const struct HXoption *table, int *argc, - char ***argv, unsigned int flags) -{ - int new_argc = 0; - char **new_argv = nullptr; - int ret = HX_getopt5(table, *argv, &new_argc, - flags & HXOPT_KEEP_ARGV ? nullptr : &new_argv, flags); - if (ret != HXOPT_ERR_SUCCESS) - return ret; - if (flags & HXOPT_KEEP_ARGV) - // NO_CREATE_NEW / DESTROY_NEW - new_argv = *argv; - else if (flags & HXOPT_DESTROY_OLD) - HX_zvecfree(*argv); - if (argc != nullptr) - *argc = new_argc; - *argv = new_argv; + if (ps.uarg != nullptr) + HXdeque_free(ps.uarg); + if (ps.oarg != nullptr) + HXdeque_free(ps.oarg); + if (ps.desc != nullptr) + HXdeque_free(ps.desc); return ret; } diff --git a/src/string.c b/src/string.c index 3a85fa8..e9c162d 100644 --- a/src/string.c +++ b/src/string.c @@ -41,6 +41,26 @@ struct HX_quote_rule { const char *chars; }; +/* years, months, weeks, days, hours, minutes, seconds, msecs, musecs, nsecs; */ +enum period_idx { + PERIDX_YEARS = 0, + PERIDX_MONTHS, + PERIDX_WEEKS, + PERIDX_DAYS, + PERIDX_HOURS, + PERIDX_MIN, + PERIDX_SEC, + PERIDX_MSEC, + PERIDX_MUSEC, + PERIDX_NSEC, + PERIDX_MAX, +}; + +struct HX_unit_desc { + const char name[8]; + uint8_t len, pidx; +}; + static const char HX_hexenc[] = "0123456789ABCDEF"; EXPORT_SYMBOL char *HX_basename(const char *s) @@ -217,13 +237,11 @@ EXPORT_SYMBOL char **HX_split(const char *str, const char *delim, ret[*cp] = NULL; { - char *seg, *wp = HX_strdup(str), *bg = wp; + char *wp = HX_strdup(str), *bg = wp; size_t i = 0; - while (--max > 0) { - seg = HX_strsep(&wp, delim); - ret[i++] = HX_strdup(seg); - } + while (--max > 0) + ret[i++] = HX_strdup(strtok_r(nullptr, delim, &wp)); ret[i++] = HX_strdup(wp); free(bg); @@ -374,13 +392,6 @@ EXPORT_SYMBOL size_t HX_strltrim(char *expr) return diff; } -EXPORT_SYMBOL char *HX_stpltrim(const char *p) -{ - while (HX_isspace(*p)) - ++p; - return const_cast1(char *, p); -} - /** * Helper for substr() function for dealing with negative off/len values * @z: total length of string @@ -442,14 +453,6 @@ EXPORT_SYMBOL char *HX_strndup(const char *src, size_t size) return ret; } -EXPORT_SYMBOL size_t HX_strnlen(const char *src, size_t size) -{ - const char *ptr = src; - for (; *ptr != '\0' && size > 0; --size, ++ptr) - ; - return ptr - src; -} - EXPORT_SYMBOL size_t HX_strrcspn(const char *s, const char *rej) { size_t n = strlen(s); @@ -483,35 +486,6 @@ EXPORT_SYMBOL size_t HX_strrtrim(char *expr) return s; } -EXPORT_SYMBOL char *HX_strsep(char **sp, const char *d) -{ - char *begin, *end; - - if (*sp == NULL || **sp == '\0') - return NULL; - begin = *sp; - - if (d[0] == '\0' || d[1] == '\0') { - if (*begin == *d) - end = begin; - else if (*begin == '\0') - end = NULL; - else - end = strchr(begin + 1, *d); - } else { - end = strpbrk(begin, d); - } - - if (end == NULL) { - *sp = NULL; - } else { - *end++ = '\0'; - *sp = end; - } - - return begin; -} - EXPORT_SYMBOL char *HX_strsep2(char **wp, const char *str) { char *ptr, *ret; @@ -534,8 +508,6 @@ static const struct HX_quote_rule HX_quote_rules[] = { [HXQUOTE_LDAPFLT] = {HXQUOTE_REJECT, "\n*()\\"}, [HXQUOTE_LDAPRDN] = {HXQUOTE_REJECT, "\n \"#+,;<=>\\"}, [HXQUOTE_URIENC] = {HXQUOTE_ACCEPT, "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"}, - [HXQUOTE_SQLSQUOTE] = {HXQUOTE_REJECT, "'"}, - [HXQUOTE_SQLBQUOTE] = {HXQUOTE_REJECT, "`"}, }; /** @@ -605,30 +577,6 @@ static char *HX_quote_backslash(char *dest, const char *src, const char *qc) return ret; } -static char * -HX_quote_sqlbackslash(char *dest, const char *src, const char *trm) -{ - char *ret = dest; - size_t len; - - while (*src != '\0') { - len = strcspn(src, trm); - if (len > 0) { - memcpy(dest, src, len); - dest += len; - src += len; - if (*src == '\0') - break; - } - *dest++ = *trm; - *dest++ = *trm; - ++src; - } - - *dest = '\0'; - return ret; -} - /** * Encode @src into BASE-64 according to RFC 4648 and write result to @dest, * which must be of appropriate size, plus one for a trailing NUL. @@ -782,8 +730,6 @@ static size_t HX_quoted_size(const char *s, unsigned int type) switch (type) { case HXQUOTE_SQUOTE: case HXQUOTE_DQUOTE: - case HXQUOTE_SQLSQUOTE: - case HXQUOTE_SQLBQUOTE: return HX_qsize_bsr(s, HX_quote_rules[type].chars, 1); case HXQUOTE_HTML: return HX_qsize_html(s); @@ -862,9 +808,6 @@ EXPORT_SYMBOL char *HX_strquote(const char *src, unsigned int type, return HX_quote_base64(*free_me, src, '+', ','); case HXQUOTE_URIENC: return HX_quote_urlenc(*free_me, src); - case HXQUOTE_SQLSQUOTE: - case HXQUOTE_SQLBQUOTE: - return HX_quote_sqlbackslash(*free_me, src, rule->chars); } return NULL; } @@ -1068,47 +1011,169 @@ EXPORT_SYMBOL unsigned long long HX_strtoull_unit(const char *s, return neg ? -ipart : ipart; } +static const struct HX_unit_desc txtperiod_utab[] = { + {"seconds", 7, PERIDX_SEC}, + {"second", 6, PERIDX_SEC}, + {"sec", 3, PERIDX_SEC}, + {"s", 1, PERIDX_SEC}, + {"minutes", 7, PERIDX_MIN}, + {"minute", 6, PERIDX_MIN}, + {"min", 3, PERIDX_MIN}, + {"hours", 5, PERIDX_HOURS}, + {"hour", 4, PERIDX_HOURS}, + {"h", 1, PERIDX_HOURS}, + {"days", 4, PERIDX_DAYS}, + {"day", 3, PERIDX_DAYS}, + {"d", 1, PERIDX_DAYS}, + {"weeks", 5, PERIDX_WEEKS}, + {"week", 4, PERIDX_WEEKS}, + {"months", 6, PERIDX_MONTHS}, + {"month", 5, PERIDX_MONTHS}, + {"years", 5, PERIDX_YEARS}, + {"year", 4, PERIDX_YEARS}, + {"y", 1, PERIDX_YEARS}, + {"msec", 4, PERIDX_MSEC}, + {"ms", 2, PERIDX_MSEC}, + {"µsec", 5, PERIDX_MUSEC}, + {"µs", 3, PERIDX_MUSEC}, + {"nsec", 4, PERIDX_NSEC}, + {"ns", 2, PERIDX_NSEC}, +}; + +static const struct HX_unit_desc iso8601p_dtab[] = { + {"D", 1, PERIDX_DAYS}, + {"M", 1, PERIDX_MONTHS}, + {"Y", 1, PERIDX_YEARS}, +}; + +static const struct HX_unit_desc iso8601p_ttab[] = { + {"S", 1, PERIDX_SEC}, + {"M", 1, PERIDX_MIN}, + {"H", 1, PERIDX_HOURS}, +}; + /* Numbers also used by systemd — the focus is on longterm averages */ #define SECONDS_PER_YEAR 31557600 /* 365.25 days */ #define SECONDS_PER_MONTH 2629800 /* 1/12th of that year = 30.4375 days */ #define NSEC_PER_SECOND 1000000000ULL -static const struct { - const char name[8]; - unsigned int len; - uint32_t s_mult; - uint64_t ns_mult; -} time_multiplier[] = { - {"seconds", 7, 1, 1 * NSEC_PER_SECOND}, - {"second", 6, 1, 1 * NSEC_PER_SECOND}, - {"sec", 3, 1, 1 * NSEC_PER_SECOND}, - {"s", 1, 1, 1 * NSEC_PER_SECOND}, - {"minutes", 7, 60, 60 * NSEC_PER_SECOND}, - {"minute", 6, 60, 60 * NSEC_PER_SECOND}, - {"min", 3, 60, 60 * NSEC_PER_SECOND}, - {"hours", 5, 3600, 3600 * NSEC_PER_SECOND}, - {"hour", 4, 3600, 3600 * NSEC_PER_SECOND}, - {"h", 1, 3600, 3600 * NSEC_PER_SECOND}, - {"days", 4, 86400, 86400 * NSEC_PER_SECOND}, - {"day", 3, 86400, 86400 * NSEC_PER_SECOND}, - {"d", 1, 86400, 86400 * NSEC_PER_SECOND}, - {"weeks", 5, 604800, 604800 * NSEC_PER_SECOND}, - {"week", 4, 604800, 604800 * NSEC_PER_SECOND}, - {"months", 6, SECONDS_PER_MONTH, SECONDS_PER_MONTH * NSEC_PER_SECOND}, - {"month", 5, SECONDS_PER_MONTH, SECONDS_PER_MONTH * NSEC_PER_SECOND}, - {"years", 5, SECONDS_PER_YEAR, SECONDS_PER_YEAR * NSEC_PER_SECOND}, - {"year", 4, SECONDS_PER_YEAR, SECONDS_PER_YEAR * NSEC_PER_SECOND}, - {"y", 1, SECONDS_PER_YEAR, SECONDS_PER_YEAR * NSEC_PER_SECOND}, - {"msec", 4, 0, 1000000}, - {"ms", 2, 0, 1000000}, - {"µsec", 5, 0, 1000}, - {"µs", 3, 0, 1000}, - {"nsec", 4, 0, 1}, - {"ns", 2, 0, 1}, +static const uint64_t sec_mult[PERIDX_MAX] = { + [PERIDX_YEARS] = SECONDS_PER_YEAR, + [PERIDX_MONTHS] = SECONDS_PER_MONTH, + [PERIDX_WEEKS] = 604800, + [PERIDX_DAYS] = 86400, + [PERIDX_HOURS] = 3600, + [PERIDX_MIN] = 60, + [PERIDX_SEC] = 1, + [PERIDX_MSEC] = 0, + [PERIDX_MUSEC] = 0, + [PERIDX_NSEC] = 0, +}; + +static const uint64_t nsec_mult[PERIDX_MAX] = { + [PERIDX_YEARS] = NSEC_PER_SECOND * SECONDS_PER_YEAR, + [PERIDX_MONTHS] = NSEC_PER_SECOND * SECONDS_PER_MONTH, + [PERIDX_WEEKS] = NSEC_PER_SECOND * 604800, + [PERIDX_DAYS] = NSEC_PER_SECOND * 86400, + [PERIDX_HOURS] = NSEC_PER_SECOND * 3600, + [PERIDX_MIN] = NSEC_PER_SECOND * 60, + [PERIDX_SEC] = NSEC_PER_SECOND, + [PERIDX_MSEC] = 1000000, + [PERIDX_MUSEC] = 1000, + [PERIDX_NSEC] = 1, }; -static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool nsec) +/** + * Take a textual period string ("1 minute 5 seconds") and break it down. + * @s: input string + * @utab: allowed unit suffixes + * @mtab: multiplication table + * @out_end: parsing stopping point + * + * Returns an errno. + */ +static unsigned long long HX_strtoull_period(const char *s, + const struct HX_unit_desc *utab, size_t usize, const uint64_t *mtab, + size_t msize, char **out_end) +{ + unsigned long long quant = 0; + + while (*s != '\0') { + while (HX_isspace(*s)) + ++s; + const char *numbegin = s; + if (*s == '-') + break; + char *end = nullptr; + unsigned long long num = strtoull(s, &end, 10); + if (num == ULLONG_MAX && errno == ERANGE) + return num; + double frac = 0; + bool have_frac = *end == '.'; + if (have_frac) + frac = strtod(s, &end); + if (end == s) + break; + s = end; + while (HX_isspace(*s)) + ++s; + unsigned int i; + for (i = 0; i < usize; ++i) + if (strncasecmp(s, utab[i].name, utab[i].len) == 0 && + /* Ensure a word boundary is present */ + !HX_isalpha(s[utab[i].len])) + break; + if (i == usize) { + if ((!have_frac && num == 0) || (have_frac && frac == 0)) + /* 0 is the same no matter what unit, take it */ + continue; + s = numbegin; + break; + } + unsigned long long mult = mtab[utab[i].pidx]; + if (have_frac) { + double v = frac * mult; + if (v >= static_cast(double, ULLONG_MAX)) { + /* + * As ULLONG_MAX gets promoted to double, its + * value may _increase_, therefore here we use + * >= and not >. + */ + if (out_end != nullptr) + *out_end = const_cast(char *, numbegin); + errno = ERANGE; + return ULLONG_MAX; + } + num = v; + } else { + if (mult > 0 && num > ULLONG_MAX / mult) { + if (out_end != nullptr) + *out_end = const_cast(char *, numbegin); + errno = ERANGE; + return ULLONG_MAX; + } + num *= mult; + } + if (num > ULLONG_MAX - quant) { + if (out_end != nullptr) + *out_end = const_cast(char *, numbegin); + errno = ERANGE; + return ULLONG_MAX; + } + quant += num; + s += utab[i].len; + } + if (out_end != nullptr) + *out_end = const_cast(char *, s); + errno = 0; + return quant; +} + +static unsigned long long HX_strtoull_iso8601p_2(const char *s, + const uint64_t *mtab, size_t msize, char **out_end) { + const struct HX_unit_desc *utab = iso8601p_dtab; + size_t usize = ARRAY_SIZE(iso8601p_dtab); unsigned long long quant = 0; while (*s != '\0') { @@ -1117,6 +1182,14 @@ static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool n const char *numbegin = s; if (*s == '-') break; + if (HX_toupper(*s) == 'T') { + if (utab != iso8601p_dtab) + break; + utab = iso8601p_ttab; + usize = ARRAY_SIZE(iso8601p_ttab); + ++s; + continue; + } char *end = nullptr; unsigned long long num = strtoull(s, &end, 10); if (num == ULLONG_MAX && errno == ERANGE) @@ -1131,45 +1204,92 @@ static unsigned long long HX_strtoull_time(const char *s, char **out_end, bool n while (HX_isspace(*s)) ++s; unsigned int i; - for (i = 0; i < ARRAY_SIZE(time_multiplier); ++i) - if (strncmp(s, time_multiplier[i].name, - time_multiplier[i].len) == 0 && - !HX_isalpha(s[time_multiplier[i].len])) + /* No word boundary check for 8601 mode */ + for (i = 0; i < usize; ++i) + if (strncmp(s, utab[i].name, utab[i].len) == 0) break; - if (i == ARRAY_SIZE(time_multiplier)) { + if (i == usize) { if ((!have_frac && num == 0) || (have_frac && frac == 0)) /* 0 is the same no matter what unit, take it */ continue; s = numbegin; break; } - unsigned long long mult = nsec ? time_multiplier[i].ns_mult : time_multiplier[i].s_mult; + unsigned long long mult = mtab[utab[i].pidx]; if (have_frac) { - quant += frac * mult; + double v = frac * mult; + if (v >= static_cast(double, ULLONG_MAX)) { + /* + * As ULLONG_MAX gets promoted to double, its + * value may _increase_, therefore here we use + * >= and not >. + */ + if (out_end != nullptr) + *out_end = const_cast(char *, numbegin); + errno = ERANGE; + return ULLONG_MAX; + } + num = v; } else { - if (mult > 0 && num >= ULLONG_MAX / mult) { + if (mult > 0 && num > ULLONG_MAX / mult) { if (out_end != nullptr) *out_end = const_cast(char *, numbegin); errno = ERANGE; return ULLONG_MAX; } - quant += num * mult; + num *= mult; } - s += time_multiplier[i].len; + if (num > ULLONG_MAX - quant) { + if (out_end != nullptr) + *out_end = const_cast(char *, numbegin); + errno = ERANGE; + return ULLONG_MAX; + } + quant += num; + s += utab[i].len; } if (out_end != nullptr) *out_end = const_cast(char *, s); + errno = 0; return quant; } +static bool looks_like_iso8601(const char *s) +{ + if (s[0] != 'P') + return false; + if (HX_isdigit(s[1])) + return true; + if (s[1] == 'T' && HX_isdigit(s[2])) + return true; + return false; +} + EXPORT_SYMBOL unsigned long long HX_strtoull_sec(const char *s, char **out_end) { - return HX_strtoull_time(s, out_end, false); + if (looks_like_iso8601(s)) + return HX_strtoull_iso8601p_2(&s[1], sec_mult, ARRAY_SIZE(sec_mult), out_end); + return HX_strtoull_period(s, + txtperiod_utab, ARRAY_SIZE(txtperiod_utab), + sec_mult, ARRAY_SIZE(sec_mult), out_end); } EXPORT_SYMBOL unsigned long long HX_strtoull_nsec(const char *s, char **out_end) { - return HX_strtoull_time(s, out_end, true); + if (looks_like_iso8601(s)) + return HX_strtoull_iso8601p_2(&s[1], nsec_mult, ARRAY_SIZE(nsec_mult), out_end); + return HX_strtoull_period(s, + txtperiod_utab, ARRAY_SIZE(txtperiod_utab), + nsec_mult, ARRAY_SIZE(nsec_mult), out_end); +} + +EXPORT_SYMBOL unsigned long long HX_strtoull8601p_sec(const char *s, char **out_end) +{ + if (*s == 'P') + return HX_strtoull_iso8601p_2(&s[1], sec_mult, ARRAY_SIZE(sec_mult), out_end); + if (out_end != nullptr) + *out_end = const_cast(char *, s); + return 0; } EXPORT_SYMBOL char *HX_unit_seconds(char *out, size_t outsize, diff --git a/src/tc-list.c b/src/tc-list.c index d31a5c7..abb0944 100644 --- a/src/tc-list.c +++ b/src/tc-list.c @@ -81,7 +81,7 @@ static void l_dump(bool pop) #endif } - printf("Remaining elements: %u\n", strings_ct.items); + printf("Remaining elements: %zu\n", strings_ct.items); } static void l_empty(void) diff --git a/src/tc-map.c b/src/tc-map.c index e9e896f..5ad7dfe 100644 --- a/src/tc-map.c +++ b/src/tc-map.c @@ -107,7 +107,7 @@ static void tmap_flush(struct HXmap *map, bool verbose) const struct HXmap_node *node; struct HXmap_trav *iter; - tmap_printf("Flushing %u elements (with traversal)\n", map->items); + tmap_printf("Flushing %zu elements (with traversal)\n", map->items); tmap_ipush(); while (map->items != 0) { /* May need to reload traverser due to deletion */ @@ -141,7 +141,7 @@ static void tmap_add_speed(struct HXmap *map) tmap_time(&stop); HX_timespec_sub(&delta, &stop, &start); } while (!(delta.tv_sec >= 1 || map->items >= 1000000)); - tmap_printf("%u elements in " HX_TIMESPEC_FMT + tmap_printf("%zu elements in " HX_TIMESPEC_FMT " (plus time measurement overhead)\n", map->items, HX_TIMESPEC_EXP(&delta)); threshold = map->items; @@ -151,7 +151,7 @@ static void tmap_add_speed(struct HXmap *map) tmap_add_rand(map, threshold); tmap_time(&stop); HX_timespec_sub(&delta, &stop, &start); - tmap_printf("%u elements in " HX_TIMESPEC_FMT " (w/o overhead)\n", + tmap_printf("%zu elements in " HX_TIMESPEC_FMT " (w/o overhead)\n", map->items, HX_TIMESPEC_EXP(&delta)); tmap_ipop(); } @@ -176,14 +176,14 @@ static void tmap_trav_speed(struct HXmap *map) tmap_time(&stop); HX_timespec_sub(&delta, &stop, &start); HXmap_travfree(iter); - tmap_printf("Open traversal of %u nodes: " HX_TIMESPEC_FMT "s\n", + tmap_printf("Open traversal of %zu nodes: " HX_TIMESPEC_FMT "s\n", map->items, HX_TIMESPEC_EXP(&delta)); tmap_time(&start); HXmap_qfe(map, tmap_each_fn, NULL); tmap_time(&stop); HX_timespec_sub(&delta, &stop, &start); - tmap_printf("QFE traversal of %u nodes: " HX_TIMESPEC_FMT "s\n", + tmap_printf("QFE traversal of %zu nodes: " HX_TIMESPEC_FMT "s\n", map->items, HX_TIMESPEC_EXP(&delta)); tmap_ipop(); @@ -200,7 +200,7 @@ static void tmap_trav_speed(struct HXmap *map) start = delta; stop = delta2; HX_timespec_sub(&delta, &stop, &start); - tmap_printf("Lookup of %u nodes: " HX_TIMESPEC_FMT "s\n", + tmap_printf("Lookup of %zu nodes: " HX_TIMESPEC_FMT "s\n", map->items, HX_TIMESPEC_EXP(&delta)); tmap_ipop(); } @@ -210,7 +210,7 @@ static int tmap_flat(const struct HXmap *map) struct HXmap_node *nodes; unsigned int i; - tmap_printf("Retrieving flattened list of %u elements:\n", map->items); + tmap_printf("Retrieving flattened list of %zu elements:\n", map->items); tmap_ipush(); nodes = HXmap_keysvalues(map); if (nodes == NULL) { @@ -430,7 +430,7 @@ static void tmap_hmap_test_1a(const char *map_type, u.map = HXmap_init5(HXMAPT_HASH, HXMAP_SCKEY, &intstr_ops, 0, 0); tmap_new_perfect_tree(u.map, power, 2); - tmap_printf("%s, intstr, %u items/%u buckets, " + tmap_printf("%s, intstr, %zu items/%u buckets, " "agglomeration: %.2f%%\n", map_type, u.map->items, HXhash_primes[u.hmap->power], hmap_agg_index(u.hmap, false)); @@ -441,13 +441,13 @@ static void tmap_hmap_test_1a(const char *map_type, while (u.map->items < 1 << max_power) { /* Fill up just right up to the maximum load */ tmap_add_rand(u.map, u.hmap->max_load - u.map->items); - tmap_printf("%s, words, %u items/%u buckets, " + tmap_printf("%s, words, %zu items/%u buckets, " "agglomeration: %.2f%%\n", map_type, u.map->items, HXhash_primes[u.hmap->power], hmap_agg_index(u.hmap, false)); /* trigger resize */ tmap_add_rand(u.map, 1); - tmap_printf("%s, words, %u items/%u buckets, " + tmap_printf("%s, words, %zu items/%u buckets, " "agglomeration: %.2f%%\n", map_type, u.map->items, HXhash_primes[u.hmap->power], hmap_agg_index(u.hmap, false)); @@ -535,7 +535,7 @@ static void rbt_height_check(const struct HXrbtree *tree) max = 2 * log(tree->super.items + 1) / log(2); avg = log((pow(2, min) + pow(2, max)) / 2) / log(2); tmap_ipush(); - tmap_printf("%u items; height %u; min/avg/max %.2f/%.2f/%.2f\n", + tmap_printf("%zu items; height %u; min/avg/max %.2f/%.2f/%.2f\n", tree->super.items, rbt_tree_height(tree->root), min, avg, max); tmap_ipop(); diff --git a/src/tc-option.c b/src/tc-option.c index 8e99b19..860f9d2 100644 --- a/src/tc-option.c +++ b/src/tc-option.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Jan Engelhardt +// SPDX-FileCopyrightText: 2025 Jan Engelhardt /* * option parser test program */ @@ -14,17 +14,22 @@ #include "internal.h" static int opt_v = 0, opt_mask = 0; -static char *opt_kstr = NULL; +static char *opt_kstr, *opt_strp; static long opt_klong = 0; static double opt_kdbl = 0; static int opt_kflag = 0, opt_kint = 0; static int opt_dst = 0; static hxmc_t *opt_mcstr = NULL; +static inline const char *znul(const char *s) +{ + return s != nullptr ? s : "(null)"; +} + static void opt_cbf(const struct HXoptcb *cbi) { printf("cbf was called... with \"%s\"/'%c'\n", - cbi->current->ln, cbi->current->sh); + znul(cbi->current->ln), cbi->current->sh); } static const char *opt_eitheror[] = {"neither", "either", "or"}; @@ -59,83 +64,159 @@ static struct HXoption table[] = { .cb = opt_cbf, .help = "XOR mask test", .htyp = "value"}, {.sh = 'G', .type = HXTYPE_NONE, .help = "Just a flag", .cb = opt_cbf}, {.sh = 'H', .type = HXTYPE_NONE, .help = "Just a flag", .cb = opt_cbf}, - {.sh = 'I', .type = HXTYPE_NONE, .help = "Just a flag", .cb = opt_cbf}, + {.sh = 'I', .type = HXTYPE_NONE, .ptr = nullptr, .help = "Just a flag", .cb = opt_cbf}, HXOPT_AUTOHELP, {.sh = 'J', .type = HXTYPE_NONE, .help = "Just a flag", .cb = opt_cbf}, + {.ln = "strp", .sh = 'Z', .type = HXTYPE_STRP, .ptr = &opt_strp, + .help = "String pointer", .cb = opt_cbf}, HXOPT_TABLEEND, }; -static void dump_argv(char **v) +static int t_empty_argv(void) { - while (*v != NULL) - printf("[%s] ", *v++); - printf("\n"); -} + char *zero_argv[] = {nullptr}; + struct HXopt6_result result; -static int t_pthru(void) -{ - const char *argv[] = { - "ARGV0", "-Zomg", "-GZfoo", "bar", - "--unknown-f=13.37", "--unknown-a", - "foo", "bar", NULL - }; - char **nargv = nullptr; - int nargc = 0; - - printf("PTHRU test:\n"); - if (HX_getopt5(table, const_cast(char **, argv), &nargc, &nargv, - HXOPT_USAGEONERR | HXOPT_PTHRU) != HXOPT_ERR_SUCCESS) + printf("...with argv={NULL}\n"); + if (HX_getopt6(table, 0, zero_argv, &result, + HXOPT_USAGEONERR | HXOPT_ITER_OA | HXOPT_DUP_ARGS) != HXOPT_ERR_SUCCESS) return EXIT_FAILURE; - printf("argc = %d\n", nargc); - dump_argv(nargv); - printf("\n"); - HX_zvecfree(nargv); - return EXIT_SUCCESS; + bool ok = result.nopts == 0 && result.nargs == 0 && result.dup_argc == 0; + HX_getopt6_clean(&result); + free(opt_kstr); + opt_kstr = nullptr; + return ok ? EXIT_SUCCESS : EXIT_FAILURE; } -static int t_empty_argv(void) +static int runner(int argc, char **argv) { - char *zero_argv[] = {nullptr}; - char **new_argv = nullptr; + printf("== HX_getopt6 ==\n"); + int ret = HX_getopt6(table, argc, argv, nullptr, HXOPT_USAGEONERR); + printf("Return value of HX_getopt: %d\n", ret); + printf("Either-or is: %s\n", opt_eitheror[opt_dst]); + printf("values: D=%lf I=%d L=%ld S=%p/%s strp=%p/%s\n", + opt_kdbl, opt_kint, opt_klong, + opt_kstr, znul(opt_kstr), opt_strp, znul(opt_strp)); + printf("Verbosity level: %d\n", opt_v); + printf("Mask: 0x%08X\n", opt_mask); + printf("mcstr: >%s<\n", znul(opt_mcstr)); + free(opt_kstr); + opt_kstr = nullptr; - printf("Testing argv={NULL}\n"); - if (HX_getopt5(table, zero_argv, nullptr, &new_argv, - HXOPT_USAGEONERR) != HXOPT_ERR_SUCCESS) - return EXIT_FAILURE; - HX_zvecfree(new_argv); + ret = t_empty_argv(); + if (ret != EXIT_SUCCESS) + return ret; return EXIT_SUCCESS; } -static int t_keep_argv(void) +static int t_getopt6_aflags(int unused_argc, char **unused_argv) { - static const char *const one_argv[] = {"what", nullptr}; - const char **argv = const_cast2(const char **, one_argv); - if (HX_getopt(table, nullptr, &argv, HXOPT_KEEP_ARGV) != HXOPT_ERR_SUCCESS) + struct HXopt6_result result; + char *argv[] = {"./prog", "-q", "foo", "-qS", "quux", "bar", "--NIL", nullptr}; + + printf("== ANY_ORDER ==\n"); + int ret = HX_getopt6(table, 6, argv, &result, HXOPT_ANY_ORDER); + if (ret != HXOPT_ERR_SUCCESS || + result.uarg != nullptr || result.dup_argv != nullptr) + return EXIT_FAILURE; + HX_getopt6_clean(&result); + free(opt_kstr); + opt_kstr = nullptr; + + /* T: Asking for DUP should produce data */ + /* T: Limit strings -- NIL must not be processed */ + printf("== ANY_ORDER/DUP_ARGS ==\n"); + ret = HX_getopt6(table, 6, argv, &result, HXOPT_ANY_ORDER | HXOPT_DUP_ARGS); + if (ret != HXOPT_ERR_SUCCESS || + result.uarg != nullptr || + result.dup_argv == nullptr || result.dup_argc != 3) + return EXIT_FAILURE; + if (strcmp(result.dup_argv[0], argv[0]) != 0 || + strcmp(result.dup_argv[1], argv[2]) != 0 || + strcmp(result.dup_argv[2], argv[5]) != 0) + return EXIT_FAILURE; + for (int i = 0; i < result.dup_argc; ++i) + printf(" %s", result.dup_argv[i]); + printf("\n"); + HX_getopt6_clean(&result); + free(opt_kstr); + opt_kstr = nullptr; + + /* T: POSIX order */ + printf("== RQ_ORDER/DUP_ARGS ==\n"); + ret = HX_getopt6(table, 6, argv, &result, HXOPT_RQ_ORDER | HXOPT_DUP_ARGS); + if (ret != HXOPT_ERR_SUCCESS || + result.uarg != nullptr || + result.dup_argv == nullptr || result.dup_argc != 5) + return EXIT_FAILURE; + if (strcmp(result.dup_argv[0], argv[0]) != 0 || + strcmp(result.dup_argv[1], argv[2]) != 0 || + strcmp(result.dup_argv[2], argv[3]) != 0 || + strcmp(result.dup_argv[3], argv[4]) != 0 || + strcmp(result.dup_argv[4], argv[5]) != 0) return EXIT_FAILURE; - return argv == one_argv ? EXIT_SUCCESS : EXIT_FAILURE; + for (int i = 0; i < result.dup_argc; ++i) + printf(" %s", result.dup_argv[i]); + printf("\n"); + HX_getopt6_clean(&result); + free(opt_kstr); + opt_kstr = nullptr; + + /* T: Asking for ITER should produce data */ + printf("== ANY_ORDER/ITER_ARGS ==\n"); + ret = HX_getopt6(table, 6, argv, &result, HXOPT_ANY_ORDER | HXOPT_ITER_ARGS); + if (ret != HXOPT_ERR_SUCCESS || result.dup_argv != nullptr || + result.nargs != 2 || result.uarg == nullptr) + return EXIT_FAILURE; + if (strcmp(result.uarg[0], argv[2]) != 0 || + strcmp(result.uarg[1], argv[5]) != 0) + return EXIT_FAILURE; + for (int i = 0; i < result.nargs; ++i) + printf(" %s", result.uarg[i]); + printf("\n"); + HX_getopt6_clean(&result); + free(opt_kstr); + opt_kstr = nullptr; + + /* T: Asking for ITER should produce data */ + printf("== ANY_ORDER/ITER_OPTS ==\n"); + ret = HX_getopt6(table, 6, argv, &result, HXOPT_ANY_ORDER | HXOPT_ITER_OPTS); + if (ret != HXOPT_ERR_SUCCESS || result.nopts != 3 || + result.desc == nullptr || result.oarg == nullptr) + return EXIT_FAILURE; + if (result.desc[0]->sh != 'q' || result.oarg[0] != nullptr || + result.desc[1]->sh != 'q' || result.oarg[1] != nullptr || + result.desc[2]->sh != 'S' || result.oarg[2] == nullptr || + strcmp(result.oarg[2], argv[4]) != 0) + return EXIT_FAILURE; + printf(" %s", argv[0]); + for (int i = 0; i < result.nopts; ++i) { + printf(" -%c", result.desc[i]->sh); + if (result.oarg[i] != nullptr) + printf(" %s", result.oarg[i]); + } + printf("\n"); + HX_getopt6_clean(&result); + free(opt_kstr); + opt_kstr = nullptr; + return EXIT_SUCCESS; } -static int runner(int argc, char **argv) +static int t_getopt6_eq(void) { - char **nargv = nullptr; - int ret = HX_getopt5(table, argv, &argc, &nargv, HXOPT_USAGEONERR); - printf("Return value of HX_getopt: %d\n", ret); - if (ret == EXIT_SUCCESS) - HX_zvecfree(nargv); - ret = t_empty_argv(); - if (ret != EXIT_SUCCESS) - return ret; - ret = t_keep_argv(); - if (ret != EXIT_SUCCESS) - return ret; - - printf("Either-or is: %s\n", opt_eitheror[opt_dst]); - printf("values: D=%lf I=%d L=%ld S=%s\n", - opt_kdbl, opt_kint, opt_klong, opt_kstr); - printf("Verbosity level: %d\n", opt_v); - printf("Mask: 0x%08X\n", opt_mask); - printf("mcstr: >%s<\n", opt_mcstr); - return t_pthru(); + printf("== getopt6_eq ==\n"); + char *argv[] = {"./prog", "--strp", "bar", "--strp=bar"}; + struct HXopt6_result result; + int ret = HX_getopt6(table, ARRAY_SIZE(argv), argv, &result, + HXOPT_ITER_OPTS | HXOPT_USAGEONERR); + if (ret != HXOPT_ERR_SUCCESS || result.nopts != 2) + return EXIT_FAILURE; + if (strcmp(result.oarg[0], result.oarg[1]) != 0) + return EXIT_FAILURE; + HX_getopt6_clean(&result); + free(opt_kstr); + opt_kstr = nullptr; + return EXIT_SUCCESS; } int main(int argc, char **argv) @@ -145,6 +226,12 @@ int main(int argc, char **argv) int ret = runner(argc, argv); if (ret != EXIT_SUCCESS) printf("FAILED\n"); + ret = t_getopt6_aflags(argc, argv); + if (ret != EXIT_SUCCESS) + printf("FAILED\n"); + ret = t_getopt6_eq(); + if (ret != EXIT_SUCCESS) + printf("FAILED\n"); HX_exit(); return ret; } diff --git a/src/tc-realpath.c b/src/tc-realpath.c index 4d6b79f..e4933a5 100644 --- a/src/tc-realpath.c +++ b/src/tc-realpath.c @@ -10,36 +10,14 @@ #include <libHX/string.h> #include "internal.h" -static unsigned int rp_flags; -static unsigned int rp_absolute; -static unsigned int rp_no_parent, rp_no_self; - static const struct HXoption rp_option_table[] = { - {.sh = 'a', .type = HXTYPE_NONE, .ptr = &rp_absolute, - .help = "Produce an absolute path"}, - {.sh = 'p', .type = HXTYPE_NONE, .ptr = &rp_no_parent, - .help = "Deactivate resolution of \"..\" entries"}, - {.sh = 's', .type = HXTYPE_NONE, .ptr = &rp_no_self, - .help = "Deactivate resolution of \".\" entries"}, + {.sh = 'a', .type = HXTYPE_NONE, .help = "Produce an absolute path"}, + {.sh = 'p', .type = HXTYPE_NONE, .help = "Deactivate resolution of \"..\" entries"}, + {.sh = 's', .type = HXTYPE_NONE, .help = "Deactivate resolution of \".\" entries"}, HXOPT_AUTOHELP, HXOPT_TABLEEND, }; -static bool rp_get_options(char **oargv, int *argc, char ***argv) -{ - if (HX_getopt5(rp_option_table, oargv, argc, argv, HXOPT_USAGEONERR) != - HXOPT_ERR_SUCCESS) - return false; - rp_flags = HX_REALPATH_DEFAULT; - if (rp_absolute) - rp_flags |= HX_REALPATH_ABSOLUTE; - if (rp_no_parent) - rp_flags &= ~HX_REALPATH_PARENT; - if (rp_no_self) - rp_flags &= ~HX_REALPATH_SELF; - return true; -} - static void t_1(void) { hxmc_t *tmp = HXmc_strinit(""); @@ -57,20 +35,28 @@ static void t_2(void) HXmc_free(tmp); } -int main(int argc, char **oargv) +int main(int argc, char **argv) { - char **argv = nullptr; hxmc_t *res; int ret; - - if (!rp_get_options(oargv, &argc, &argv)) - return EXIT_FAILURE; + struct HXopt6_result result; + if (HX_getopt6(rp_option_table, argc, argv, &result, + HXOPT_USAGEONERR | HXOPT_ITER_OA) != HXOPT_ERR_SUCCESS) + return false; + unsigned int rp_flags = HX_REALPATH_DEFAULT; + for (int i = 0; i < result.nopts; ++i) { + switch (result.desc[i]->sh) { + case 'a': rp_flags |= HX_REALPATH_ABSOLUTE; break; + case 'p': rp_flags &= ~HX_REALPATH_PARENT; break; + case 's': rp_flags &= ~HX_REALPATH_SELF; break; + } + } t_1(); t_2(); res = NULL; - for (int i = 1; i < argc; ++i) { - ret = HX_realpath(&res, argv[argc], rp_flags); + for (int i = 0; i < result.nargs; ++i) { + ret = HX_realpath(&res, result.uarg[i], rp_flags); if (ret < 0) { perror("HX_realpath"); printf("\n"); @@ -78,6 +64,6 @@ int main(int argc, char **oargv) printf("%s\n", res); } } - HX_zvecfree(argv); + HX_getopt6_clean(&result); return EXIT_SUCCESS; } diff --git a/src/tc-string.c b/src/tc-string.c index d777268..1c67ede 100644 --- a/src/tc-string.c +++ b/src/tc-string.c @@ -108,23 +108,22 @@ static void t_strncat(void) printf("String: >%s<\n", data); } -static void t_strnlen(void) -{ - static const char s[] = "Hello world"; - printf("# strnlen: %" HX_SIZET_FMT "u %" HX_SIZET_FMT "u " - "%" HX_SIZET_FMT "u %" HX_SIZET_FMT "u %" HX_SIZET_FMT "u\n", - HX_strnlen(s, -1), HX_strnlen(s, 0), HX_strnlen(s, 1), - HX_strnlen(s, strlen(s)), HX_strnlen(s, 999)); -} - static void t_strsep(void) { - char b[] = "jengelh:x:1500:100:Jan Engelhardt:/home/jengelh:/bin/bash"; + char orig[] = "jengelh:x:1500:100:Jan Engelhardt:/home/jengelh:/bin/bash"; + char b[sizeof(orig)]; char *wp = b, *ret; printf("# strsep\n"); + memcpy(b, orig, sizeof(orig)); while ((ret = HX_strsep2(&wp, ":")) != NULL) printf("%s\n", ret); + + printf("# strtok_r\n"); + memcpy(b, orig, sizeof(orig)); + wp = b; + while ((ret = strtok_r(nullptr, ":", &wp)) != nullptr) + printf("%s\n", ret); } static void t_strtrim(void) @@ -132,11 +131,6 @@ static void t_strtrim(void) char a[] = " a and b ", aexp[] = "a and b "; char b[] = " a and b ", bexp[] = " a and b"; char c[] = "a&b", cexp[] = "a&b"; - const char *r; - - r = HX_stpltrim(a); - printf("HX_stpltrim(\"%s\") = \"%s\"\n", a, r); - assert(strcmp(r, aexp) == 0); printf("HX_strltrim(\"%s\") = ", a); printf("\"%s\"\n", (HX_strltrim(a), a)); @@ -146,7 +140,6 @@ static void t_strtrim(void) printf("\"%s\"\n", (HX_strrtrim(b), b)); assert(strcmp(b, bexp) == 0); - assert(strcmp(cexp, HX_stpltrim(c)) == 0); assert(strcmp(cexp, (HX_strltrim(c), c)) == 0); assert(strcmp(cexp, (HX_strrtrim(c), c)) == 0); } @@ -399,14 +392,19 @@ static int t_time_units(void) static int t_time_strto(void) { #define NS_PER_S 1000000000ULL + #define S_PER_Y 31557600 + enum { + NO_NSEC = 0x2U, + }; static const struct { const char *input; unsigned long long expect_s, expect_ns; const char expect_rem[16]; + unsigned int flags; } vt[] = { {"29µs", 0, 29000, ""}, - {"1y", 31557600, NS_PER_S * 31557600, ""}, - {"1y1month1week1d1h1min1s ", 31557600+2629800+86400*8+3600+60+1, NS_PER_S * (31557600+2629800+86400*8+3600+60+1), ""}, + {"1y", S_PER_Y, NS_PER_S * S_PER_Y, ""}, + {"1y1month1week1d1h1min1s ", S_PER_Y+2629800+86400*8+3600+60+1, NS_PER_S * (S_PER_Y+2629800+86400*8+3600+60+1), ""}, {" -1d", 0, 0, "-1d"}, {"1 -", 0, 0, "1 -"}, {"12.5 hours .5 hours 240 minutes 25200 seconds", 86400, NS_PER_S * 86400, ""}, @@ -418,23 +416,42 @@ static int t_time_strto(void) {"1s0.0", 1, NS_PER_S, ""}, {"1s1s", 2, 2 * NS_PER_S, ""}, {"1s1", 1, 1 * NS_PER_S, "1"}, + {"584542046090y", 584542046090ULL * S_PER_Y, ULLONG_MAX, "", NO_NSEC}, + {"584542046090y19767615s", ULLONG_MAX, ULLONG_MAX, "", NO_NSEC}, + {"584542046090y19767616s", ULLONG_MAX, ULLONG_MAX, "19767616s", NO_NSEC}, + {"584542046090y", 584542046090ULL * S_PER_Y, ULLONG_MAX, "", NO_NSEC}, {"584542046091y", ULLONG_MAX, ULLONG_MAX, "584542046091y"}, + {"584542046091.0y", ULLONG_MAX, ULLONG_MAX, "584542046091.0y"}, + {"P1M", S_PER_Y / 12, S_PER_Y / 12 * NS_PER_S, ""}, + {"p1m", 0, 0, "p1m"}, + {"PT1M", 60, 60 * NS_PER_S, ""}, + {"P584542046090Y", 584542046090ULL * S_PER_Y, ULLONG_MAX, "", NO_NSEC}, + {"P0DT0H0M0.0S", 0, 0}, + {"P0DT12H45M0.0S", 12 * 3600 + 45 * 60, (12 * 3600 + 45 * 60) * NS_PER_S}, + {"P1D", 86400, 86400 * NS_PER_S}, }; - char *end; printf("===== t_time_strto\n"); for (size_t i = 0; i < ARRAY_SIZE(vt); ++i) { + char *end, *end_ns; unsigned long long q = HX_strtoull_sec(vt[i].input, &end); - unsigned long long qn = HX_strtoull_nsec(vt[i].input, &end); - printf("Observed: \"%s\" => %llus [%lluns] + \"%s\"\n", vt[i].input, q, qn, end); - if (q != vt[i].expect_s || qn != vt[i].expect_ns) { - printf("Expected: %llus [%lluns]\n", vt[i].expect_s, vt[i].expect_ns); + unsigned long long qn = HX_strtoull_nsec(vt[i].input, &end_ns); + printf("Observed: \"%s\" => %llus + \"%s\" [%lluns + \"%s\"]\n", + vt[i].input, q, end, qn, end_ns); + if (q != vt[i].expect_s || strcmp(end, vt[i].expect_rem) != 0) { + printf("-!- Expected: %llus + \"%s\"\n", vt[i].expect_s, vt[i].expect_rem); return EXIT_FAILURE; } - if (strcmp(end, vt[i].expect_rem) != 0) { - printf("Expected: remainder \"%s\"\n", vt[i].expect_rem); + if (vt[i].flags & NO_NSEC) + continue; + if (qn != vt[i].expect_ns || strcmp(end_ns, vt[i].expect_rem) != 0) { + printf("-!- Expected: %llus + \"%s\"\n", vt[i].expect_ns, vt[i].expect_rem); return EXIT_FAILURE; } } + if (HX_strtoull8601p_sec("P1D", nullptr) != 86400) { + printf("8601p_sec failed\n"); + return EXIT_FAILURE; + } return EXIT_SUCCESS; } @@ -496,7 +513,6 @@ static int runner(int argc, char **argv) if (ret != EXIT_SUCCESS) return EXIT_FAILURE; t_strncat(); - t_strnlen(); t_strdup(); t_strsep(); t_strtrim(); diff --git a/src/tc-strquote.c b/src/tc-strquote.c index ef357d1..c96aa19 100644 --- a/src/tc-strquote.c +++ b/src/tc-strquote.c @@ -10,7 +10,6 @@ static const char input1[] = "\"Good\" ol' \\'escaped\\' strings"; static const char output1a[] = "\"Good\" ol\\' \\\\\\'escaped\\\\\\' strings"; static const char output1b[] = "\\\"Good\\\" ol' \\\\'escaped\\\\' strings"; -static const char output1c[] = "\"Good\" ol'' \\''escaped\\'' strings"; static const char input2[] = "<p style=\"height: 1;\">Foo & \"bar\"</p>"; static const char output2[] = "<p style="height: 1;">Foo &amp; "bar"</p>"; @@ -20,8 +19,6 @@ static const char output3b[] = "\\20\\23o\\3Dfoo(*)\\2Cba\\5Cr\\20"; static const char output3c[] = "ICNvPWZvbygqKSxiYVxyIA=="; static const char input4[] = "http://user:pass@host.de/~path/file(msvc);stuff.php?query[phpindex]=value&another=one;stuff"; static const char output4[] = "http%3A%2F%2Fuser%3Apass%40host.de%2F~path%2Ffile%28msvc%29%3Bstuff.php%3Fquery%5Bphpindex%5D%3Dvalue%26another%3Done%3Bstuff"; -static const char input5[] = "echo hello `echo world`"; -static const char output5[] = "echo hello ``echo world``"; static const char input6[] = "\xfb\xef\xff"; static const char output6[] = "++//"; static const char input7[] = "\xfb\xef\xff"; @@ -58,13 +55,11 @@ int main(void) return EXIT_FAILURE; tst(input1, HXQUOTE_SQUOTE, output1a); tst(input1, HXQUOTE_DQUOTE, output1b); - tst(input1, HXQUOTE_SQLSQUOTE, output1c); tst(input2, HXQUOTE_HTML, output2); tst(input3, HXQUOTE_LDAPFLT, output3a); tst(input3, HXQUOTE_LDAPRDN, output3b); tst(input3, HXQUOTE_BASE64, output3c); tst(input4, HXQUOTE_URIENC, output4); - tst(input5, HXQUOTE_SQLBQUOTE, output5); tst(input6, HXQUOTE_BASE64, output6); tst(input7, HXQUOTE_BASE64URL, output7); return 0; diff --git a/src/tc-switchuser.c b/src/tc-switchuser.c index e5d2c25..379dfff 100644 --- a/src/tc-switchuser.c +++ b/src/tc-switchuser.c @@ -10,19 +10,28 @@ #include <libHX/defs.h> #include <libHX/option.h> #include <libHX/proc.h> +#include "internal.h" #if defined(HAVE_INITGROUPS) -static char *user_name, *group_name; static const struct HXoption options_table[] = { - {.sh = 'u', .type = HXTYPE_STRING, .ptr = &user_name}, - {.sh = 'g', .type = HXTYPE_STRING, .ptr= &group_name}, + {.sh = 'u', .type = HXTYPE_STRING}, + {.sh = 'g', .type = HXTYPE_STRING}, HXOPT_TABLEEND, }; static int runner(int argc, char **argv) { - if (HX_getopt(options_table, &argc, &argv, HXOPT_USAGEONERR) != HXOPT_ERR_SUCCESS) + char *user_name = nullptr, *group_name = nullptr; + struct HXopt6_result result; + + if (HX_getopt6(options_table, argc, argv, &result, + HXOPT_USAGEONERR | HXOPT_ITER_OPTS) != HXOPT_ERR_SUCCESS) return EXIT_FAILURE; + for (int i = 0; i < result.nopts; ++i) { + if (result.desc[i]->sh == 'u') user_name = result.oarg[i]; + if (result.desc[i]->sh == 'g') group_name = result.oarg[i]; + } + const char *user = user_name != NULL ? user_name : "-"; const char *group = group_name != NULL ? group_name : "-"; switch (HXproc_switch_user(user_name, group_name)) { @@ -63,7 +72,7 @@ static int runner(int argc, char **argv) break; } } - HX_zvecfree(argv); + HX_getopt6_clean(&result); return EXIT_SUCCESS; } diff --git a/src/tc-time.c b/src/tc-time.c index e810233..e1ec12b 100644 --- a/src/tc-time.c +++ b/src/tc-time.c @@ -5,6 +5,7 @@ #include <stdlib.h> #include <time.h> #include <unistd.h> +#include <sys/time.h> #include <libHX/defs.h> #include <libHX/init.h> #include <libHX/misc.h> diff --git a/src/tx-option.cpp b/src/tx-option.cpp deleted file mode 100644 index 248ab7c..0000000 --- a/src/tx-option.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __cplusplus -# include <stdlib.h> -#else -# include <cstdlib> -#endif -#include <libHX/option.h> - -static unsigned int g_verbose; -static const struct HXoption t[] = { - {nullptr, 'v', HXTYPE_NONE | HXOPT_INC, &g_verbose}, - HXOPT_AUTOHELP, - HXOPT_TABLEEND, -}; - -int main(int argc, char **argv) -{ - if (HX_getopt(t, &argc, &argv, HXOPT_USAGEONERR) != HXOPT_ERR_SUCCESS) - return EXIT_FAILURE; - HX_zvecfree(argv); - return EXIT_SUCCESS; -} |
