aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile24
-rw-r--r--bindtemp_un.c6
-rw-r--r--common.h4
-rw-r--r--config.mk2
-rw-r--r--execlat.c27
-rw-r--r--execleat.c27
-rw-r--r--execlpe.c27
-rw-r--r--execvat.c22
-rw-r--r--execveat.c56
-rw-r--r--execvpe.c22
-rw-r--r--fexecl.c27
-rw-r--r--fexecle.c27
-rw-r--r--fexecv.c22
-rw-r--r--libsimple.c2
-rw-r--r--libsimple.h2
-rw-r--r--libsimple/ascii.h657
-rw-r--r--libsimple/env.h7
-rw-r--r--libsimple/exec.h99
-rw-r--r--vexecl.c22
-rw-r--r--vexeclat.c28
-rw-r--r--vexecle.c22
-rw-r--r--vexecleat.c30
-rw-r--r--vexeclp.c22
-rw-r--r--vexeclpe.c22
-rw-r--r--vfexecl.c22
-rw-r--r--vfexecle.c28
-rw-r--r--vxexecl.c35
-rw-r--r--vxexecle.c28
-rw-r--r--which.c92
-rw-r--r--xexecl.c27
-rw-r--r--xexecv.c31
31 files changed, 1464 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index d9c2b5a..84fb58c 100644
--- a/Makefile
+++ b/Makefile
@@ -28,9 +28,11 @@ SUBHDR =\
libsimple/aligned_wcsndup.h\
libsimple/aligned_wmemdup.h\
libsimple/array.h\
+ libsimple/ascii.h\
libsimple/calloc.h\
libsimple/definitions.h\
libsimple/env.h\
+ libsimple/exec.h\
libsimple/malloc.h\
libsimple/mallocz.h\
libsimple/mem.h\
@@ -236,6 +238,15 @@ OBJ =\
ewcsdup.o\
ewcsndup.o\
ewmemdup.o\
+ execlat.o\
+ execleat.o\
+ execlpe.o\
+ execvat.o\
+ execveat.o\
+ execvpe.o\
+ fexecl.o\
+ fexecle.o\
+ fexecv.o\
generate_seed.o\
getcwd.o\
getenv_e.o\
@@ -501,6 +512,14 @@ OBJ =\
vcallocn.o\
venprintf.o\
veprintf.o\
+ vexecl.o\
+ vexeclat.o\
+ vexecle.o\
+ vexecleat.o\
+ vexeclp.o\
+ vexeclpe.o\
+ vfexecl.o\
+ vfexecle.o\
vmallocn.o\
vmemalignn.o\
vmemalignzn.o\
@@ -513,9 +532,14 @@ OBJ =\
vvallocn.o\
vvalloczn.o\
vweprintf.o\
+ vxexecl.o\
+ vxexecle.o\
wcsndup.o\
weprintf.o\
+ which.o\
wmemdup.o\
+ xexecl.o\
+ xexecv.o\
zabs.o\
zdiff.o\
libsimple.o
diff --git a/bindtemp_un.c b/bindtemp_un.c
index 0362e47..5f0de05 100644
--- a/bindtemp_un.c
+++ b/bindtemp_un.c
@@ -57,8 +57,8 @@ libsimple_bindtemp_un(int fd, int dir_fd, struct sockaddr_un *addr, socklen_t *a
len = snprintf(addr->sun_path, sizeof(addr->sun_path), "/dev/fd/%i/tmp", dir_fd);
if (len < 0 || (size_t)len >= sizeof(addr->sun_path) || sizeof(addr->sun_path) - (size_t)len < 6)
abort();
- rem = sizeof(addr->sun_path) - 1 - len;
- addr->sun_path[sizeof(addr->sun_path) - 1] = 0;
+ rem = sizeof(addr->sun_path) - 1U - (size_t)len;
+ addr->sun_path[sizeof(addr->sun_path) - 1U] = 0;
while (try_limit--) {
again:
@@ -67,7 +67,7 @@ libsimple_bindtemp_un(int fd, int dir_fd, struct sockaddr_un *addr, socklen_t *a
errno = saved_errno;
return 0;
} else if (errno == ENAMETOOLONG && rem > 5) {
- addr->sun_path[len + --rem] = 0;
+ addr->sun_path[(size_t)len + --rem] = 0;
goto again;
} else if (errno != EEXIST && errno != EADDRINUSE) {
return -1;
diff --git a/common.h b/common.h
index 7f84f59..81ad053 100644
--- a/common.h
+++ b/common.h
@@ -9,3 +9,7 @@
# pragma clang diagnostic ignored "-Wc++98-compat"
# pragma clang diagnostic ignored "-Wcovered-switch-default"
#endif
+
+#ifdef execveat
+# undef execveat
+#endif
diff --git a/config.mk b/config.mk
index 9bafe24..c7eb5ca 100644
--- a/config.mk
+++ b/config.mk
@@ -3,6 +3,6 @@ MANPREFIX = $(PREFIX)/share/man
CC = cc
-CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
CFLAGS = -std=c11 -Wall -Wextra -O2
LDFLAGS = -s -lm
diff --git a/execlat.c b/execlat.c
new file mode 100644
index 0000000..37f7fbb
--- /dev/null
+++ b/execlat.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_execlat(int dirfd, const char *pathname, ... /* argv, NULL, int flags */)
+{
+ int ret;
+ va_list args;
+ va_start(args, pathname);
+ ret = libsimple_vexeclat(dirfd, pathname, args);
+ va_end(args);
+ return ret;
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/execleat.c b/execleat.c
new file mode 100644
index 0000000..c72e1de
--- /dev/null
+++ b/execleat.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_execleat(int dirfd, const char *pathname, ... /* argv, NULL, char *const envp[], int flags */)
+{
+ int ret;
+ va_list args;
+ va_start(args, pathname);
+ ret = libsimple_vexecleat(dirfd, pathname, args);
+ va_end(args);
+ return ret;
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/execlpe.c b/execlpe.c
new file mode 100644
index 0000000..034eb6e
--- /dev/null
+++ b/execlpe.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_execlpe(const char *file, ... /* argv, NULL, char *const envp[] */)
+{
+ int ret;
+ va_list args;
+ va_start(args, file);
+ ret = libsimple_vexeclpe(file, args);
+ va_end(args);
+ return ret;
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/execvat.c b/execvat.c
new file mode 100644
index 0000000..ea5bb75
--- /dev/null
+++ b/execvat.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_execvat(int dirfd, const char *pathname, char *const argv[], int flags)
+{
+ return execveat(dirfd, pathname, argv, environ, flags);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/execveat.c b/execveat.c
new file mode 100644
index 0000000..ed7b11e
--- /dev/null
+++ b/execveat.c
@@ -0,0 +1,56 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags)
+{
+ struct stat st;
+ int fd, saved_errno;
+
+#if defined(__linux__)
+ execveat(dirfd, pathname, argv, envp, flags);
+ if (errno != ENOSYS)
+ return -1;
+#endif
+
+ if (flags & AT_EMPTY_PATH)
+ return fexecve(dirfd, argv, envp);
+
+#ifndef O_PATH
+# define O_PATH O_RDONLY
+#endif
+
+ fd = openat(dirfd, pathname, O_PATH | ((flags & AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
+ if (fd < 0)
+ return -1;
+
+ if (flags & AT_SYMLINK_NOFOLLOW) {
+ if (fstat(fd, &st)) {
+ saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ return -1;
+ }
+ if (S_ISLNK(st.st_mode)) {
+ close(fd);
+ errno = ELOOP;
+ return -1;
+ }
+ }
+
+ return fexecve(fd, argv, envp);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/execvpe.c b/execvpe.c
new file mode 100644
index 0000000..fe117a9
--- /dev/null
+++ b/execvpe.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_execvpe(const char *file, char *const argv[], char *const envp[])
+{
+ return libsimple_xexecv(-1, file, -1, NULL, envp, argv);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/fexecl.c b/fexecl.c
new file mode 100644
index 0000000..4767fa4
--- /dev/null
+++ b/fexecl.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_fexecl(int fd, ... /* argv, NULL */)
+{
+ int ret;
+ va_list args;
+ va_start(args, fd);
+ ret = libsimple_vfexecl(fd, args);
+ va_end(args);
+ return ret;
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/fexecle.c b/fexecle.c
new file mode 100644
index 0000000..e70fbc2
--- /dev/null
+++ b/fexecle.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_fexecle(int fd, ... /* argv, NULL, char *const envp[] */)
+{
+ int ret;
+ va_list args;
+ va_start(args, fd);
+ ret = libsimple_vfexecle(fd, args);
+ va_end(args);
+ return ret;
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/fexecv.c b/fexecv.c
new file mode 100644
index 0000000..955ae58
--- /dev/null
+++ b/fexecv.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_fexecv(int fd, char *const argv[])
+{
+ return fexecve(fd, argv, environ);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/libsimple.c b/libsimple.c
index 2bb5139..100a22c 100644
--- a/libsimple.c
+++ b/libsimple.c
@@ -70,7 +70,7 @@ test_timeval(double d, time_t sec, long int usec, double rd, const char *s, cons
}
#ifdef libsimple_vasprintfa
-LIBSIMPLE_GCC_ONLY__(__attribute__((__format__(__printf__, 2, 0))))
+LIBSIMPLE_GCC_ONLY__(__attribute__((__format__(__printf__, 2, 3))))
static int
test_vasprintfa(const char *expected, const char *format, ...)
{
diff --git a/libsimple.h b/libsimple.h
index 2b45dfa..f1e479c 100644
--- a/libsimple.h
+++ b/libsimple.h
@@ -169,6 +169,8 @@
#include "libsimple/abs.h"
#include "libsimple/net.h"
#include "libsimple/path.h"
+#include "libsimple/ascii.h"
+#include "libsimple/exec.h"
/**
diff --git a/libsimple/ascii.h b/libsimple/ascii.h
new file mode 100644
index 0000000..faaa99e
--- /dev/null
+++ b/libsimple/ascii.h
@@ -0,0 +1,657 @@
+/* See LICENSE file for copyright and license details. */
+
+/* TODO man, doc, test */
+
+
+#define LIBSIMPLE_CHAR_NUL '\x00'
+#ifndef CHAR_NUL
+# define CHAR_NUL LIBSIMPLE_CHAR_NUL
+#endif
+#define LIBSIMPLE_CHAR_NULL LIBSIMPLE_CHAR_NUL
+#ifndef CHAR_NULL
+# define CHAR_NULL LIBSIMPLE_CHAR_NULL
+#endif
+
+#define LIBSIMPLE_CHAR_SOH '\x01'
+#ifndef CHAR_SOH
+# define CHAR_SOH LIBSIMPLE_CHAR_SOH
+#endif
+#define LIBSIMPLE_CHAR_START_OF_HEADING LIBSIMPLE_CHAR_SOH
+#ifndef CHAR_START_OF_HEADING
+# define CHAR_START_OF_HEADING LIBSIMPLE_CHAR_START_OF_HEADING
+#endif
+
+#define LIBSIMPLE_CHAR_STX '\x02'
+#ifndef CHAR_STX
+# define CHAR_STX LIBSIMPLE_CHAR_STX
+#endif
+#define LIBSIMPLE_CHAR_START_OF_TEXT LIBSIMPLE_CHAR_STX
+#ifndef CHAR_START_OF_TEXT
+# define CHAR_START_OF_TEXT LIBSIMPLE_CHAR_START_OF_TEXT
+#endif
+
+#define LIBSIMPLE_CHAR_ETX '\x03'
+#ifndef CHAR_ETX
+# define CHAR_ETX LIBSIMPLE_CHAR_ETX
+#endif
+#define LIBSIMPLE_CHAR_END_OF_TEXT LIBSIMPLE_CHAR_ETX
+#ifndef CHAR_END_OF_TEXT
+# define CHAR_END_OF_TEXT LIBSIMPLE_CHAR_END_OF_TEXT
+#endif
+
+#define LIBSIMPLE_CHAR_EOT '\x04'
+#ifndef CHAR_EOT
+# define CHAR_EOT LIBSIMPLE_CHAR_EOT
+#endif
+#define LIBSIMPLE_CHAR_END_OF_TRANSMISSION LIBSIMPLE_CHAR_EOT
+#ifndef CHAR_END_OF_TRANSMISSION
+# define CHAR_END_OF_TRANSMISSION LIBSIMPLE_CHAR_END_OF_TRANSMISSION
+#endif
+
+#define LIBSIMPLE_CHAR_ENQ '\x05'
+#ifndef CHAR_ENQ
+# define CHAR_ENQ LIBSIMPLE_CHAR_ENQ
+#endif
+#define LIBSIMPLE_CHAR_ENQUIRY LIBSIMPLE_CHAR_ENQ
+#ifndef CHAR_ENQUIRY
+# define CHAR_ENQUIRY LIBSIMPLE_CHAR_ENQUIRY
+#endif
+
+#define LIBSIMPLE_CHAR_ACK '\x06'
+#ifndef CHAR_ACK
+# define CHAR_ACK LIBSIMPLE_CHAR_ACK
+#endif
+#define LIBSIMPLE_CHAR_ACKNOWLEDGE LIBSIMPLE_CHAR_ACK
+#ifndef CHAR_ACKNOWLEDGE
+# define CHAR_ACKNOWLEDGE LIBSIMPLE_CHAR_ACKNOWLEDGE
+#endif
+
+#define LIBSIMPLE_CHAR_BEL '\x07'
+#ifndef CHAR_BEL
+# define CHAR_BEL LIBSIMPLE_CHAR_BEL
+#endif
+#define LIBSIMPLE_CHAR_BELL LIBSIMPLE_CHAR_BEL
+#ifndef CHAR_BELL
+# define CHAR_BELL LIBSIMPLE_CHAR_BELL
+#endif
+
+#define LIBSIMPLE_CHAR_BS '\x08'
+#ifndef CHAR_BS
+# define CHAR_BS LIBSIMPLE_CHAR_BS
+#endif
+#define LIBSIMPLE_CHAR_BACKSPACE LIBSIMPLE_CHAR_BS
+#ifndef CHAR_BACKSPACE
+# define CHAR_BACKSPACE LIBSIMPLE_CHAR_BACKSPACE
+#endif
+
+#define LIBSIMPLE_CHAR_HT '\x09'
+#ifndef CHAR_HT
+# define CHAR_HT LIBSIMPLE_CHAR_HT
+#endif
+#define LIBSIMPLE_CHAR_HORIZONTAL_TABULATION LIBSIMPLE_CHAR_HT
+#ifndef CHAR_HORIZONTAL_TABULATION
+# define CHAR_HORIZONTAL_TABULATION LIBSIMPLE_CHAR_HORIZONTAL_TABULATION
+#endif
+
+#define LIBSIMPLE_CHAR_LF '\x0A'
+#ifndef CHAR_LF
+# define CHAR_LF LIBSIMPLE_CHAR_LF
+#endif
+#define LIBSIMPLE_CHAR_LINE_FEED LIBSIMPLE_CHAR_LF
+#ifndef CHAR_LINE_FEED
+# define CHAR_LINE_FEED LIBSIMPLE_CHAR_LINE_FEED
+#endif
+
+#define LIBSIMPLE_CHAR_VT '\x0B'
+#ifndef CHAR_VT
+# define CHAR_VT LIBSIMPLE_CHAR_VT
+#endif
+#define LIBSIMPLE_CHAR_VERTICAL_TABULATION LIBSIMPLE_CHAR_VT
+#ifndef CHAR_VERTICAL_TABULATION
+# define CHAR_VERTICAL_TABULATION LIBSIMPLE_CHAR_VERTICAL_TABULATION
+#endif
+
+#define LIBSIMPLE_CHAR_FF '\x0C'
+#ifndef CHAR_FF
+# define CHAR_FF LIBSIMPLE_CHAR_FF
+#endif
+#define LIBSIMPLE_CHAR_FORM_FEED LIBSIMPLE_CHAR_FF
+#ifndef CHAR_FORM_FEED
+# define CHAR_FORM_FEED LIBSIMPLE_CHAR_FORM_FEED
+#endif
+
+#define LIBSIMPLE_CHAR_CR '\x0D'
+#ifndef CHAR_CR
+# define CHAR_CR LIBSIMPLE_CHAR_CR
+#endif
+#define LIBSIMPLE_CHAR_CARRIAGE_RETURN LIBSIMPLE_CHAR_CR
+#ifndef CHAR_CARRIAGE_RETURN
+# define CHAR_CARRIAGE_RETURN LIBSIMPLE_CHAR_CARRIAGE_RETURN
+#endif
+
+#define LIBSIMPLE_CHAR_SO '\x0E'
+#ifndef CHAR_SO
+# define CHAR_SO LIBSIMPLE_CHAR_SO
+#endif
+#define LIBSIMPLE_CHAR_SHIFT_OUT LIBSIMPLE_CHAR_SO
+#ifndef CHAR_SHIFT_OUT
+# define CHAR_SHIFT_OUT LIBSIMPLE_CHAR_SHIFT_OUT
+#endif
+
+#define LIBSIMPLE_CHAR_SI '\x0F'
+#ifndef CHAR_SI
+# define CHAR_SI LIBSIMPLE_CHAR_SI
+#endif
+#define LIBSIMPLE_CHAR_SHIFT_IN LIBSIMPLE_CHAR_SI
+#ifndef CHAR_SHIFT_IN
+# define CHAR_SHIFT_IN LIBSIMPLE_CHAR_SHIFT_IN
+#endif
+
+#define LIBSIMPLE_CHAR_DLE '\x10'
+#ifndef CHAR_DLE
+# define CHAR_DLE LIBSIMPLE_CHAR_DLE
+#endif
+#define LIBSIMPLE_CHAR_DATA_LINK_ESCAPE LIBSIMPLE_CHAR_DLE
+#ifndef CHAR_DATA_LINK_ESCAPE
+# define CHAR_DATA_LINK_ESCAPE LIBSIMPLE_CHAR_DATA_LINK_ESCAPE
+#endif
+
+#define LIBSIMPLE_CHAR_DC1 '\x11'
+#ifndef CHAR_DC1
+# define CHAR_DC1 LIBSIMPLE_CHAR_DC1
+#endif
+#define LIBSIMPLE_CHAR_DEVICE_CONTROL_1 LIBSIMPLE_CHAR_DC1
+#ifndef CHAR_DEVICE_CONTROL_1
+# define CHAR_DEVICE_CONTROL_1 LIBSIMPLE_CHAR_DEVICE_CONTROL_1
+#endif
+
+#define LIBSIMPLE_CHAR_DC2 '\x12'
+#ifndef CHAR_DC2
+# define CHAR_DC2 LIBSIMPLE_CHAR_DC2
+#endif
+#define LIBSIMPLE_CHAR_DEVICE_CONTROL_2 LIBSIMPLE_CHAR_DC2
+#ifndef CHAR_DEVICE_CONTROL_2
+# define CHAR_DEVICE_CONTROL_2 LIBSIMPLE_CHAR_DEVICE_CONTROL_2
+#endif
+
+#define LIBSIMPLE_CHAR_DC3 '\x13'
+#ifndef CHAR_DC3
+# define CHAR_DC3 LIBSIMPLE_CHAR_DC3
+#endif
+#define LIBSIMPLE_CHAR_DEVICE_CONTROL_3 LIBSIMPLE_CHAR_DC3
+#ifndef CHAR_DEVICE_CONTROL_3
+# define CHAR_DEVICE_CONTROL_3 LIBSIMPLE_CHAR_DEVICE_CONTROL_3
+#endif
+
+#define LIBSIMPLE_CHAR_DC4 '\x14'
+#ifndef CHAR_DC4
+# define CHAR_DC4 LIBSIMPLE_CHAR_DC4
+#endif
+#define LIBSIMPLE_CHAR_DEVICE_CONTROL_4 LIBSIMPLE_CHAR_DC4
+#ifndef CHAR_DEVICE_CONTROL_4
+# define CHAR_DEVICE_CONTROL_4 LIBSIMPLE_CHAR_DEVICE_CONTROL_4
+#endif
+
+#define LIBSIMPLE_CHAR_NAK '\x15'
+#ifndef CHAR_NAK
+# define CHAR_NAK LIBSIMPLE_CHAR_NAK
+#endif
+#define LIBSIMPLE_CHAR_NEGATIVE_ACKNOWLEDGE LIBSIMPLE_CHAR_NAK
+#ifndef CHAR_NEGATIVE_ACKNOWLEDGE
+# define CHAR_NEGATIVE_ACKNOWLEDGE LIBSIMPLE_CHAR_NEGATIVE_ACKNOWLEDGE
+#endif
+
+#define LIBSIMPLE_CHAR_SYN '\x16'
+#ifndef CHAR_SYN
+# define CHAR_SYN LIBSIMPLE_CHAR_SYN
+#endif
+#define LIBSIMPLE_CHAR_SYNCHRONOUS_IDLE LIBSIMPLE_CHAR_SYN
+#ifndef CHAR_SYNCHRONOUS_IDLE
+# define CHAR_SYNCHRONOUS_IDLE LIBSIMPLE_CHAR_SYNCHRONOUS_IDLE
+#endif
+
+#define LIBSIMPLE_CHAR_ETB '\x17'
+#ifndef CHAR_ETB
+# define CHAR_ETB LIBSIMPLE_CHAR_ETB
+#endif
+#define LIBSIMPLE_CHAR_END_OF_TRANSMISSION_BLOCK LIBSIMPLE_CHAR_ETB
+#ifndef CHAR_END_OF_TRANSMISSION_BLOCK
+# define CHAR_END_OF_TRANSMISSION_BLOCK LIBSIMPLE_CHAR_END_OF_TRANSMISSION_BLOCK
+#endif
+
+#define LIBSIMPLE_CHAR_CAN '\x18'
+#ifndef CHAR_CAN
+# define CHAR_CAN LIBSIMPLE_CHAR_CAN
+#endif
+#define LIBSIMPLE_CHAR_CANCEL LIBSIMPLE_CHAR_CAN
+#ifndef CHAR_CANCEL
+# define CHAR_CANCEL LIBSIMPLE_CHAR_CANCEL
+#endif
+
+#define LIBSIMPLE_CHAR_EM '\x19'
+#ifndef CHAR_EM
+# define CHAR_EM LIBSIMPLE_CHAR_EM
+#endif
+#define LIBSIMPLE_CHAR_END_OF_MEDIUM LIBSIMPLE_CHAR_EM
+#ifndef CHAR_END_OF_MEDIUM
+# define CHAR_END_OF_MEDIUM LIBSIMPLE_CHAR_END_OF_MEDIUM
+#endif
+
+#define LIBSIMPLE_CHAR_SUB '\x1A'
+#ifndef CHAR_SUB
+# define CHAR_SUB LIBSIMPLE_CHAR_SUB
+#endif
+#define LIBSIMPLE_CHAR_SUBSTITUTE LIBSIMPLE_CHAR_SUB
+#ifndef CHAR_SUBSTITUTE
+# define CHAR_SUBSTITUTE LIBSIMPLE_CHAR_SUBSTITUTE
+#endif
+
+#define LIBSIMPLE_CHAR_ESC '\x1B'
+#ifndef CHAR_ESC
+# define CHAR_ESC LIBSIMPLE_CHAR_ESC
+#endif
+#define LIBSIMPLE_CHAR_ESCAPE LIBSIMPLE_CHAR_ESC
+#ifndef CHAR_ESCAPE
+# define CHAR_ESCAPE LIBSIMPLE_CHAR_ESCAPE
+#endif
+
+#define LIBSIMPLE_CHAR_FS '\x1C'
+#ifndef CHAR_FS
+# define CHAR_FS LIBSIMPLE_CHAR_FS
+#endif
+#define LIBSIMPLE_CHAR_FILE_SEPARATOR LIBSIMPLE_CHAR_FS
+#ifndef CHAR_FILE_SEPARATOR
+# define CHAR_FILE_SEPARATOR LIBSIMPLE_CHAR_FILE_SEPARATOR
+#endif
+
+#define LIBSIMPLE_CHAR_GS '\x1D'
+#ifndef CHAR_GS
+# define CHAR_GS LIBSIMPLE_CHAR_GS
+#endif
+#define LIBSIMPLE_CHAR_GROUP_SEPARATOR LIBSIMPLE_CHAR_GS
+#ifndef CHAR_GROUP_SEPARATOR
+# define CHAR_GROUP_SEPARATOR LIBSIMPLE_CHAR_GROUP_SEPARATOR
+#endif
+
+#define LIBSIMPLE_CHAR_RS '\x1E'
+#ifndef CHAR_RS
+# define CHAR_RS LIBSIMPLE_CHAR_RS
+#endif
+#define LIBSIMPLE_CHAR_RECORD_SEPARATOR LIBSIMPLE_CHAR_RS
+#ifndef CHAR_RECORD_SEPARATOR
+# define CHAR_RECORD_SEPARATOR LIBSIMPLE_CHAR_RECORD_SEPARATOR
+#endif
+
+#define LIBSIMPLE_CHAR_US '\x1F'
+#ifndef CHAR_US
+# define CHAR_US LIBSIMPLE_CHAR_US
+#endif
+#define LIBSIMPLE_CHAR_UNIT_SEPARATOR LIBSIMPLE_CHAR_US
+#ifndef CHAR_UNIT_SEPARATOR
+# define CHAR_UNIT_SEPARATOR LIBSIMPLE_CHAR_UNIT_SEPARATOR
+#endif
+
+#define LIBSIMPLE_CHAR_SP '\x20'
+#ifndef CHAR_SP
+# define CHAR_SP LIBSIMPLE_CHAR_SP
+#endif
+#define LIBSIMPLE_CHAR_SPACE LIBSIMPLE_CHAR_SP
+#ifndef CHAR_SPACE
+# define CHAR_SPACE LIBSIMPLE_CHAR_SPACE
+#endif
+
+#define LIBSIMPLE_CHAR_DEL '\x7F'
+#ifndef CHAR_DEL
+# define CHAR_DEL LIBSIMPLE_CHAR_DEL
+#endif
+#define LIBSIMPLE_CHAR_DELETE LIBSIMPLE_CHAR_DEL
+#ifndef CHAR_DELETE
+# define CHAR_DELETE LIBSIMPLE_CHAR_DELETE
+#endif
+
+
+#define LIBSIMPLE_STR_CHAR_NUL "\x00"
+#ifndef STR_CHAR_NUL
+# define STR_CHAR_NUL LIBSIMPLE_STR_CHAR_NUL
+#endif
+#define LIBSIMPLE_STR_CHAR_NULL LIBSIMPLE_STR_CHAR_NUL
+#ifndef STR_CHAR_NULL
+# define STR_CHAR_NULL LIBSIMPLE_STR_CHAR_NULL
+#endif
+
+#define LIBSIMPLE_STR_CHAR_SOH "\x01"
+#ifndef STR_CHAR_SOH
+# define STR_CHAR_SOH LIBSIMPLE_STR_CHAR_SOH
+#endif
+#define LIBSIMPLE_STR_CHAR_START_OF_HEADING LIBSIMPLE_STR_CHAR_SOH
+#ifndef STR_CHAR_START_OF_HEADING
+# define STR_CHAR_START_OF_HEADING LIBSIMPLE_STR_CHAR_START_OF_HEADING
+#endif
+
+#define LIBSIMPLE_STR_CHAR_STX "\x02"
+#ifndef STR_CHAR_STX
+# define STR_CHAR_STX LIBSIMPLE_STR_CHAR_STX
+#endif
+#define LIBSIMPLE_STR_CHAR_START_OF_TEXT LIBSIMPLE_STR_CHAR_STX
+#ifndef STR_CHAR_START_OF_TEXT
+# define STR_CHAR_START_OF_TEXT LIBSIMPLE_STR_CHAR_START_OF_TEXT
+#endif
+
+#define LIBSIMPLE_STR_CHAR_ETX "\x03"
+#ifndef STR_CHAR_ETX
+# define STR_CHAR_ETX LIBSIMPLE_STR_CHAR_ETX
+#endif
+#define LIBSIMPLE_STR_CHAR_END_OF_TEXT LIBSIMPLE_STR_CHAR_ETX
+#ifndef STR_CHAR_END_OF_TEXT
+# define STR_CHAR_END_OF_TEXT LIBSIMPLE_STR_CHAR_END_OF_TEXT
+#endif
+
+#define LIBSIMPLE_STR_CHAR_EOT "\x04"
+#ifndef STR_CHAR_EOT
+# define STR_CHAR_EOT LIBSIMPLE_STR_CHAR_EOT
+#endif
+#define LIBSIMPLE_STR_CHAR_END_OF_TRANSMISSION LIBSIMPLE_STR_CHAR_EOT
+#ifndef STR_CHAR_END_OF_TRANSMISSION
+# define STR_CHAR_END_OF_TRANSMISSION LIBSIMPLE_STR_CHAR_END_OF_TRANSMISSION
+#endif
+
+#define LIBSIMPLE_STR_CHAR_ENQ "\x05"
+#ifndef STR_CHAR_ENQ
+# define STR_CHAR_ENQ LIBSIMPLE_STR_CHAR_ENQ
+#endif
+#define LIBSIMPLE_STR_CHAR_ENQUIRY LIBSIMPLE_STR_CHAR_ENQ
+#ifndef STR_CHAR_ENQUIRY
+# define STR_CHAR_ENQUIRY LIBSIMPLE_STR_CHAR_ENQUIRY
+#endif
+
+#define LIBSIMPLE_STR_CHAR_ACK "\x06"
+#ifndef STR_CHAR_ACK
+# define STR_CHAR_ACK LIBSIMPLE_STR_CHAR_ACK
+#endif
+#define LIBSIMPLE_STR_CHAR_ACKNOWLEDGE LIBSIMPLE_STR_CHAR_ACK
+#ifndef STR_CHAR_ACKNOWLEDGE
+# define STR_CHAR_ACKNOWLEDGE LIBSIMPLE_STR_CHAR_ACKNOWLEDGE
+#endif
+
+#define LIBSIMPLE_STR_CHAR_BEL "\x07"
+#ifndef STR_CHAR_BEL
+# define STR_CHAR_BEL LIBSIMPLE_STR_CHAR_BEL
+#endif
+#define LIBSIMPLE_STR_CHAR_BELL LIBSIMPLE_STR_CHAR_BEL
+#ifndef STR_CHAR_BELL
+# define STR_CHAR_BELL LIBSIMPLE_STR_CHAR_BELL
+#endif
+
+#define LIBSIMPLE_STR_CHAR_BS "\x08"
+#ifndef STR_CHAR_BS
+# define STR_CHAR_BS LIBSIMPLE_STR_CHAR_BS
+#endif
+#define LIBSIMPLE_STR_CHAR_BACKSPACE LIBSIMPLE_STR_CHAR_BS
+#ifndef STR_CHAR_BACKSPACE
+# define STR_CHAR_BACKSPACE LIBSIMPLE_STR_CHAR_BACKSPACE
+#endif
+
+#define LIBSIMPLE_STR_CHAR_HT "\x09"
+#ifndef STR_CHAR_HT
+# define STR_CHAR_HT LIBSIMPLE_STR_CHAR_HT
+#endif
+#define LIBSIMPLE_STR_CHAR_HORIZONTAL_TABULATION LIBSIMPLE_STR_CHAR_HT
+#ifndef STR_CHAR_HORIZONTAL_TABULATION
+# define STR_CHAR_HORIZONTAL_TABULATION LIBSIMPLE_STR_CHAR_HORIZONTAL_TABULATION
+#endif
+
+#define LIBSIMPLE_STR_CHAR_LF "\x0A"
+#ifndef STR_CHAR_LF
+# define STR_CHAR_LF LIBSIMPLE_STR_CHAR_LF
+#endif
+#define LIBSIMPLE_STR_CHAR_LINE_FEED LIBSIMPLE_STR_CHAR_LF
+#ifndef STR_CHAR_LINE_FEED
+# define STR_CHAR_LINE_FEED LIBSIMPLE_STR_CHAR_LINE_FEED
+#endif
+
+#define LIBSIMPLE_STR_CHAR_VT "\x0B"
+#ifndef STR_CHAR_VT
+# define STR_CHAR_VT LIBSIMPLE_STR_CHAR_VT
+#endif
+#define LIBSIMPLE_STR_CHAR_VERTICAL_TABULATION LIBSIMPLE_STR_CHAR_VT
+#ifndef STR_CHAR_VERTICAL_TABULATION
+# define STR_CHAR_VERTICAL_TABULATION LIBSIMPLE_STR_CHAR_VERTICAL_TABULATION
+#endif
+
+#define LIBSIMPLE_STR_CHAR_FF "\x0C"
+#ifndef STR_CHAR_FF
+# define STR_CHAR_FF LIBSIMPLE_STR_CHAR_FF
+#endif
+#define LIBSIMPLE_STR_CHAR_FORM_FEED LIBSIMPLE_STR_CHAR_FF
+#ifndef STR_CHAR_FORM_FEED
+# define STR_CHAR_FORM_FEED LIBSIMPLE_STR_CHAR_FORM_FEED
+#endif
+
+#define LIBSIMPLE_STR_CHAR_CR "\x0D"
+#ifndef STR_CHAR_CR
+# define STR_CHAR_CR LIBSIMPLE_STR_CHAR_CR
+#endif
+#define LIBSIMPLE_STR_CHAR_CARRIAGE_RETURN LIBSIMPLE_STR_CHAR_CR
+#ifndef STR_CHAR_CARRIAGE_RETURN
+# define STR_CHAR_CARRIAGE_RETURN LIBSIMPLE_STR_CHAR_CARRIAGE_RETURN
+#endif
+
+#define LIBSIMPLE_STR_CHAR_SO "\x0E"
+#ifndef STR_CHAR_SO
+# define STR_CHAR_SO LIBSIMPLE_STR_CHAR_SO
+#endif
+#define LIBSIMPLE_STR_CHAR_SHIFT_OUT LIBSIMPLE_STR_CHAR_SO
+#ifndef STR_CHAR_SHIFT_OUT
+# define STR_CHAR_SHIFT_OUT LIBSIMPLE_STR_CHAR_SHIFT_OUT
+#endif
+
+#define LIBSIMPLE_STR_CHAR_SI "\x0F"
+#ifndef STR_CHAR_SI
+# define STR_CHAR_SI LIBSIMPLE_STR_CHAR_SI
+#endif
+#define LIBSIMPLE_STR_CHAR_SHIFT_IN LIBSIMPLE_STR_CHAR_SI
+#ifndef STR_CHAR_SHIFT_IN
+# define STR_CHAR_SHIFT_IN LIBSIMPLE_STR_CHAR_SHIFT_IN
+#endif
+
+#define LIBSIMPLE_STR_CHAR_DLE "\x10"
+#ifndef STR_CHAR_DLE
+# define STR_CHAR_DLE LIBSIMPLE_STR_CHAR_DLE
+#endif
+#define LIBSIMPLE_STR_CHAR_DATA_LINK_ESCAPE LIBSIMPLE_STR_CHAR_DLE
+#ifndef STR_CHAR_DATA_LINK_ESCAPE
+# define STR_CHAR_DATA_LINK_ESCAPE LIBSIMPLE_STR_CHAR_DATA_LINK_ESCAPE
+#endif
+
+#define LIBSIMPLE_STR_CHAR_DC1 "\x11"
+#ifndef STR_CHAR_DC1
+# define STR_CHAR_DC1 LIBSIMPLE_STR_CHAR_DC1
+#endif
+#define LIBSIMPLE_STR_CHAR_DEVICE_CONTROL_1 LIBSIMPLE_STR_CHAR_DC1
+#ifndef STR_CHAR_DEVICE_CONTROL_1
+# define STR_CHAR_DEVICE_CONTROL_1 LIBSIMPLE_STR_CHAR_DEVICE_CONTROL_1
+#endif
+
+#define LIBSIMPLE_STR_CHAR_DC2 "\x12"
+#ifndef STR_CHAR_DC2
+# define STR_CHAR_DC2 LIBSIMPLE_STR_CHAR_DC2
+#endif
+#define LIBSIMPLE_STR_CHAR_DEVICE_CONTROL_2 LIBSIMPLE_STR_CHAR_DC2
+#ifndef STR_CHAR_DEVICE_CONTROL_2
+# define STR_CHAR_DEVICE_CONTROL_2 LIBSIMPLE_STR_CHAR_DEVICE_CONTROL_2
+#endif
+
+#define LIBSIMPLE_STR_CHAR_DC3 "\x13"
+#ifndef STR_CHAR_DC3
+# define STR_CHAR_DC3 LIBSIMPLE_STR_CHAR_DC3
+#endif
+#define LIBSIMPLE_STR_CHAR_DEVICE_CONTROL_3 LIBSIMPLE_STR_CHAR_DC3
+#ifndef STR_CHAR_DEVICE_CONTROL_3
+# define STR_CHAR_DEVICE_CONTROL_3 LIBSIMPLE_STR_CHAR_DEVICE_CONTROL_3
+#endif
+
+#define LIBSIMPLE_STR_CHAR_DC4 "\x14"
+#ifndef STR_CHAR_DC4
+# define STR_CHAR_DC4 LIBSIMPLE_STR_CHAR_DC4
+#endif
+#define LIBSIMPLE_STR_CHAR_DEVICE_CONTROL_4 LIBSIMPLE_STR_CHAR_DC4
+#ifndef STR_CHAR_DEVICE_CONTROL_4
+# define STR_CHAR_DEVICE_CONTROL_4 LIBSIMPLE_STR_CHAR_DEVICE_CONTROL_4
+#endif
+
+#define LIBSIMPLE_STR_CHAR_NAK "\x15"
+#ifndef STR_CHAR_NAK
+# define STR_CHAR_NAK LIBSIMPLE_STR_CHAR_NAK
+#endif
+#define LIBSIMPLE_STR_CHAR_NEGATIVE_ACKNOWLEDGE LIBSIMPLE_STR_CHAR_NAK
+#ifndef STR_CHAR_NEGATIVE_ACKNOWLEDGE
+# define STR_CHAR_NEGATIVE_ACKNOWLEDGE LIBSIMPLE_STR_CHAR_NEGATIVE_ACKNOWLEDGE
+#endif
+
+#define LIBSIMPLE_STR_CHAR_SYN "\x16"
+#ifndef STR_CHAR_SYN
+# define STR_CHAR_SYN LIBSIMPLE_STR_CHAR_SYN
+#endif
+#define LIBSIMPLE_STR_CHAR_SYNCHRONOUS_IDLE LIBSIMPLE_STR_CHAR_SYN
+#ifndef STR_CHAR_SYNCHRONOUS_IDLE
+# define STR_CHAR_SYNCHRONOUS_IDLE LIBSIMPLE_STR_CHAR_SYNCHRONOUS_IDLE
+#endif
+
+#define LIBSIMPLE_STR_CHAR_ETB "\x17"
+#ifndef STR_CHAR_ETB
+# define STR_CHAR_ETB LIBSIMPLE_STR_CHAR_ETB
+#endif
+#define LIBSIMPLE_STR_CHAR_END_OF_TRANSMISSION_BLOCK LIBSIMPLE_STR_CHAR_ETB
+#ifndef STR_CHAR_END_OF_TRANSMISSION_BLOCK
+# define STR_CHAR_END_OF_TRANSMISSION_BLOCK LIBSIMPLE_STR_CHAR_END_OF_TRANSMISSION_BLOCK
+#endif
+
+#define LIBSIMPLE_STR_CHAR_CAN "\x18"
+#ifndef STR_CHAR_CAN
+# define STR_CHAR_CAN LIBSIMPLE_STR_CHAR_CAN
+#endif
+#define LIBSIMPLE_STR_CHAR_CANCEL LIBSIMPLE_STR_CHAR_CAN
+#ifndef STR_CHAR_CANCEL
+# define STR_CHAR_CANCEL LIBSIMPLE_STR_CHAR_CANCEL
+#endif
+
+#define LIBSIMPLE_STR_CHAR_EM "\x19"
+#ifndef STR_CHAR_EM
+# define STR_CHAR_EM LIBSIMPLE_STR_CHAR_EM
+#endif
+#define LIBSIMPLE_STR_CHAR_END_OF_MEDIUM LIBSIMPLE_STR_CHAR_EM
+#ifndef STR_CHAR_END_OF_MEDIUM
+# define STR_CHAR_END_OF_MEDIUM LIBSIMPLE_STR_CHAR_END_OF_MEDIUM
+#endif
+
+#define LIBSIMPLE_STR_CHAR_SUB "\x1A"
+#ifndef STR_CHAR_SUB
+# define STR_CHAR_SUB LIBSIMPLE_STR_CHAR_SUB
+#endif
+#define LIBSIMPLE_STR_CHAR_SUBSTITUTE LIBSIMPLE_STR_CHAR_SUB
+#ifndef STR_CHAR_SUBSTITUTE
+# define STR_CHAR_SUBSTITUTE LIBSIMPLE_STR_CHAR_SUBSTITUTE
+#endif
+
+#define LIBSIMPLE_STR_CHAR_ESC "\x1B"
+#ifndef STR_CHAR_ESC
+# define STR_CHAR_ESC LIBSIMPLE_STR_CHAR_ESC
+#endif
+#define LIBSIMPLE_STR_CHAR_ESCAPE LIBSIMPLE_STR_CHAR_ESC
+#ifndef STR_CHAR_ESCAPE
+# define STR_CHAR_ESCAPE LIBSIMPLE_STR_CHAR_ESCAPE
+#endif
+
+#define LIBSIMPLE_STR_CHAR_FS "\x1C"
+#ifndef STR_CHAR_FS
+# define STR_CHAR_FS LIBSIMPLE_STR_CHAR_FS
+#endif
+#define LIBSIMPLE_STR_CHAR_FILE_SEPARATOR LIBSIMPLE_STR_CHAR_FS
+#ifndef STR_CHAR_FILE_SEPARATOR
+# define STR_CHAR_FILE_SEPARATOR LIBSIMPLE_STR_CHAR_FILE_SEPARATOR
+#endif
+
+#define LIBSIMPLE_STR_CHAR_GS "\x1D"
+#ifndef STR_CHAR_GS
+# define STR_CHAR_GS LIBSIMPLE_STR_CHAR_GS
+#endif
+#define LIBSIMPLE_STR_CHAR_GROUP_SEPARATOR LIBSIMPLE_STR_CHAR_GS
+#ifndef STR_CHAR_GROUP_SEPARATOR
+# define STR_CHAR_GROUP_SEPARATOR LIBSIMPLE_STR_CHAR_GROUP_SEPARATOR
+#endif
+
+#define LIBSIMPLE_STR_CHAR_RS "\x1E"
+#ifndef STR_CHAR_RS
+# define STR_CHAR_RS LIBSIMPLE_STR_CHAR_RS
+#endif
+#define LIBSIMPLE_STR_CHAR_RECORD_SEPARATOR LIBSIMPLE_STR_CHAR_RS
+#ifndef STR_CHAR_RECORD_SEPARATOR
+# define STR_CHAR_RECORD_SEPARATOR LIBSIMPLE_STR_CHAR_RECORD_SEPARATOR
+#endif
+
+#define LIBSIMPLE_STR_CHAR_US "\x1F"
+#ifndef STR_CHAR_US
+# define STR_CHAR_US LIBSIMPLE_STR_CHAR_US
+#endif
+#define LIBSIMPLE_STR_CHAR_UNIT_SEPARATOR LIBSIMPLE_STR_CHAR_US
+#ifndef STR_CHAR_UNIT_SEPARATOR
+# define STR_CHAR_UNIT_SEPARATOR LIBSIMPLE_STR_CHAR_UNIT_SEPARATOR
+#endif
+
+#define LIBSIMPLE_STR_CHAR_SP "\x20"
+#ifndef STR_CHAR_SP
+# define STR_CHAR_SP LIBSIMPLE_STR_CHAR_SP
+#endif
+#define LIBSIMPLE_STR_CHAR_SPACE LIBSIMPLE_STR_CHAR_SP
+#ifndef STR_CHAR_SPACE
+# define STR_CHAR_SPACE LIBSIMPLE_STR_CHAR_SPACE
+#endif
+
+#define LIBSIMPLE_STR_CHAR_DEL "\x7F"
+#ifndef STR_CHAR_DEL
+# define STR_CHAR_DEL LIBSIMPLE_STR_CHAR_DEL
+#endif
+#define LIBSIMPLE_STR_CHAR_DELETE LIBSIMPLE_STR_CHAR_DEL
+#ifndef STR_CHAR_DELETE
+# define STR_CHAR_DELETE LIBSIMPLE_STR_CHAR_DELETE
+#endif
+
+
+#define LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, ABBR, FULL, NAME)\
+ X(LIBSIMPLE_CHAR_##ABBR, LIBSIMPLE_STR_CHAR_##ABBR, #ABBR, ABBR, #FULL, FULL, NAME)
+
+#define LIBSIMPLE_LIST_ASCII_CONTROL_CHARS(X, D)\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, NUL, NULL, "null") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, SOH, START_OF_HEADING, "start of heading") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, STX, START_OF_TEXT, "start of text") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, ETX, END_OF_TEXT, "end of text") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, EOT, END_OF_TRANSMISSION, "end of transmission") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, ENQ, ENQUIRY, "enquiry") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, ACK, ACKNOWLEDGE, "acknowledge") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, BEL, BELL, "bell") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, BS, BACKSPACE, "backspace") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, HT, HORIZONTAL_TABULATION, "horizontal tabulation") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, LF, LINE_FEED, "line feed") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, VT, VERTICAL_TABULATION, "vertical tabulation") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, FF, FORM_FEED, "form feed") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, CR, CARRIAGE_RETURN, "carriage return") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, SO, SHIFT_OUT, "shift out") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, SI, SHIFT_IN, "shift in") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, DLE, DATA_LINK_ESCAPE, "data link escape") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, DC1, DEVICE_CONTROL_1, "device control 1") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, DC2, DEVICE_CONTROL_2, "device control 2") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, DC3, DEVICE_CONTROL_3, "device control 3") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, DC4, DEVICE_CONTROL_4, "device control 4") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, NAK, NEGATIVE_ACKNOWLEDGE, "negative acknowledge") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, SYN, SYNCHRONOUS_IDLE, "synchronous idle") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, ETB, END_OF_TRANSMISSION_BLOCK, "end of transmission block") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, CAN, CANCEL, "cancel") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, EM, END_OF_MEDIUM, "end of medium") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, SUB, SUBSTITUTE, "substitute") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, ESC, ESCAPE, "escape") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, FS, FILE_SEPARATOR, "file separator") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, GS, GROUP_SEPARATOR, "group separator") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, RS, RECORD_SEPARATOR, "record separator") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, US, UNIT_SEPARATOR, "unit separator") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, SP, SPACE, "space") D\
+ LIBSIMPLE_LIST_ASCII_CONTROL_CHARS__(X, DEL, DELETE, "delete")
diff --git a/libsimple/env.h b/libsimple/env.h
index 98a3d9f..a39ae2a 100644
--- a/libsimple/env.h
+++ b/libsimple/env.h
@@ -194,3 +194,10 @@ libsimple_eputenvf(const char *fmt__, ...)
#ifndef eputenvf
# define eputenvf libsimple_eputenvf
#endif
+
+
+/*
+ * TODO add getenv_first, getenv_first_ne, getenv_first_e
+ * TODO add getenv_last, getenv_last_ne, getenv_last_e
+ * TODO add unsetenv_first, unsetenv_last, unsetenv_each
+ */
diff --git a/libsimple/exec.h b/libsimple/exec.h
new file mode 100644
index 0000000..bc61628
--- /dev/null
+++ b/libsimple/exec.h
@@ -0,0 +1,99 @@
+/* See LICENSE file for copyright and license details. */
+
+/* TODO man, doc, test */
+
+
+const char *libsimple_which(const char *file, int cwdfd, const char *path, char **free_this_out);
+
+int libsimple_xexecv(int dirfd, const char *file, int atflags, const char *path, char *const *envp, char *const *argv);
+
+int libsimple_vxexecl(int dirfd, const char *file, int atflags, const char *path, char *const *envp, va_list argv_null);
+
+int libsimple_xexecl(int dirfd, const char *file, int atflags, const char *path, char *const *envp, ... /* argv, NULL */);
+
+int libsimple_vxexecle(int dirfd, const char *file, int atflags, const char *path, va_list argv_null_envp);
+
+int libsimple_execlpe(const char *file, ... /* argv, NULL, char *const envp[] */);
+#ifndef execlpe
+# define execlpe libsimple_execlpe
+#endif
+
+int libsimple_vexecl(const char *pathname, va_list argv_null);
+#ifndef vexecl
+# define vexecl libsimple_vexecl
+#endif
+
+int libsimple_vexecle(const char *pathname, va_list argv_null_envp);
+#ifndef vexecle
+# define vexecle libsimple_vexecle
+#endif
+
+int libsimple_vexeclp(const char *file, va_list argv_null);
+#ifndef vexeclp
+# define vexeclp libsimple_vexeclp
+#endif
+
+int libsimple_vexeclpe(const char *file, va_list argv_null_envp);
+#ifndef vexeclpe
+# define vexeclpe libsimple_vexeclpe
+#endif
+
+int libsimple_execvpe(const char *file, char *const argv[], char *const envp[]);
+#ifndef execvpe
+# define execvpe libsimple_execvpe
+#endif
+
+int libsimple_vfexecl(int fd, va_list argv_null);
+#ifndef vfexecl
+# define vfexecl libsimple_vfexecl
+#endif
+
+int libsimple_vfexecle(int fd, va_list argv_null_envp);
+#ifndef vfexecle
+# define vfexecle libsimple_vfexecle
+#endif
+
+int libsimple_fexecl(int fd, ... /* argv, NULL */);
+#ifndef fexecl
+# define fexecl libsimple_fexecl
+#endif
+
+int libsimple_fexecle(int fd, ... /* argv, NULL, char *const envp[] */);
+#ifndef fexecle
+# define fexecle libsimple_fexecle
+#endif
+
+int libsimple_fexecv(int fd, char *const argv[]);
+#ifndef fexecv
+# define fexecv libsimple_fexecv
+#endif
+
+int libsimple_execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags);
+#ifndef execveat
+# define execveat libsimple_execveat
+#endif
+
+int libsimple_execvat(int dirfd, const char *pathname, char *const argv[], int flags);
+#ifndef execvat
+# define execvat libsimple_execvat
+#endif
+
+int libsimple_execleat(int dirfd, const char *pathname, ... /* argv, NULL, char *const envp[], int flags */);
+#ifndef execleat
+# define execleat libsimple_execleat
+#endif
+
+int libsimple_execlat(int dirfd, const char *pathname, ... /* argv, NULL, int flags */);
+#ifndef execlat
+# define execlat libsimple_execlat
+#endif
+
+int libsimple_vexecleat(int dirfd, const char *pathname, va_list argv_null_envp_flags);
+#ifndef vexecleat
+# define vexecleat libsimple_execlpe
+#endif
+
+int libsimple_vexeclat(int dirfd, const char *pathname, va_list argv_null_flags);
+#ifndef vexeclat
+# define vexeclat libsimple_vexeclat
+#endif
diff --git a/vexecl.c b/vexecl.c
new file mode 100644
index 0000000..50649ed
--- /dev/null
+++ b/vexecl.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vexecl(const char *pathname, va_list argv_null)
+{
+ return libsimple_vxexecl(-1, pathname, -1, "", environ, argv_null);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vexeclat.c b/vexeclat.c
new file mode 100644
index 0000000..97983d1
--- /dev/null
+++ b/vexeclat.c
@@ -0,0 +1,28 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vexeclat(int dirfd, const char *pathname, va_list argv_null_flags)
+{
+ int flags;
+ va_list args;
+ va_copy(args, argv_null_flags);
+ while (va_arg(args, char *));
+ flags = va_arg(args, int);
+ va_end(args);
+ return libsimple_vxexecl(dirfd, pathname, flags, NULL, environ, argv_null_flags);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vexecle.c b/vexecle.c
new file mode 100644
index 0000000..2094bc1
--- /dev/null
+++ b/vexecle.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vexecle(const char *pathname, va_list argv_null_envp)
+{
+ return libsimple_vxexecle(-1, pathname, -1, "", argv_null_envp);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vexecleat.c b/vexecleat.c
new file mode 100644
index 0000000..f9bda0e
--- /dev/null
+++ b/vexecleat.c
@@ -0,0 +1,30 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vexecleat(int dirfd, const char *pathname, va_list argv_null_envp_flags)
+{
+ char *const *envp;
+ int flags;
+ va_list args;
+ va_copy(args, argv_null_envp_flags);
+ while (va_arg(args, char *));
+ envp = va_arg(args, char *const *);
+ flags = va_arg(args, int);
+ va_end(args);
+ return libsimple_vxexecl(dirfd, pathname, flags, NULL, envp, argv_null_envp_flags);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vexeclp.c b/vexeclp.c
new file mode 100644
index 0000000..a1d6f1f
--- /dev/null
+++ b/vexeclp.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vexeclp(const char *file, va_list argv_null)
+{
+ return libsimple_vxexecl(-1, file, -1, NULL, environ, argv_null);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vexeclpe.c b/vexeclpe.c
new file mode 100644
index 0000000..5951fc9
--- /dev/null
+++ b/vexeclpe.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vexeclpe(const char *file, va_list argv_null_envp)
+{
+ return libsimple_vxexecle(-1, file, -1, NULL, argv_null_envp);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vfexecl.c b/vfexecl.c
new file mode 100644
index 0000000..dd66b87
--- /dev/null
+++ b/vfexecl.c
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vfexecl(int fd, va_list argv_null)
+{
+ return libsimple_vxexecl(fd, "", AT_EMPTY_PATH, "", environ, argv_null);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vfexecle.c b/vfexecle.c
new file mode 100644
index 0000000..31ee602
--- /dev/null
+++ b/vfexecle.c
@@ -0,0 +1,28 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vfexecle(int fd, va_list argv_null_envp)
+{
+ char *const *envp;
+ va_list args;
+ va_copy(args, argv_null_envp);
+ while (va_arg(args, char *));
+ envp = va_arg(args, char *const *);
+ va_end(args);
+ return libsimple_vxexecl(fd, "", AT_EMPTY_PATH, "", envp, argv_null_envp);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vxexecl.c b/vxexecl.c
new file mode 100644
index 0000000..c8282a6
--- /dev/null
+++ b/vxexecl.c
@@ -0,0 +1,35 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vxexecl(int dirfd, const char *file, int atflags, const char *path, char *const *envp, va_list argv_null)
+{
+ char **argv;
+ size_t argc = 0;
+ va_list args;
+ va_copy(args, argv_null);
+ do {
+ argc += 1;
+ } while (va_arg(args, char *));
+ va_end(args);
+ argv = alloca((argc + 1) * sizeof(*argv));
+ argc = 0;
+ do {
+ argv[argc] = va_arg(args, char *);
+ } while (argv[argc++]);
+ return libsimple_xexecv(dirfd, file, atflags, path, envp, argv);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/vxexecle.c b/vxexecle.c
new file mode 100644
index 0000000..61415ef
--- /dev/null
+++ b/vxexecle.c
@@ -0,0 +1,28 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_vxexecle(int dirfd, const char *file, int atflags, const char *path, va_list argv_null_envp)
+{
+ char *const *envp;
+ va_list args;
+ va_copy(args, argv_null_envp);
+ while (va_arg(args, char *));
+ envp = va_arg(args, char *const *);
+ va_end(args);
+ return libsimple_vxexecl(dirfd, file, atflags, path, envp, argv_null_envp);
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/which.c b/which.c
new file mode 100644
index 0000000..96a3ece
--- /dev/null
+++ b/which.c
@@ -0,0 +1,92 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+const char *
+libsimple_which(const char *file, int cwdfd, const char *path, char **free_this_out)
+{
+ const char *p, *q;
+ char *buf = NULL;
+ size_t bufsize, len, filelen;
+ int got_eacces = 0, saved_errno = errno;
+
+ free_this_out = NULL;
+
+ if (!file || !free_this_out) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (strchr(file, '/'))
+ return file;
+
+ if (!path) {
+ path = getenv("PATH");
+ if (!path)
+ path = "";
+ }
+ if (!*path) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ bufsize = 0;
+ for (p = path; p; p = q) {
+ q = strchr(p, ':');
+ if (q)
+ len = (size_t)(q++ - p);
+ else
+ len = strlen(p);
+ if (len > bufsize)
+ bufsize = len;
+ }
+ if (bufsize) {
+ filelen = strlen(file) + 1U;
+ bufsize += 1U + filelen;
+ buf = malloc(bufsize);
+ if (!buf)
+ return NULL;
+ }
+
+ for (p = path; p; p = q) {
+ q = strchr(p, ':');
+ if (q)
+ len = (size_t)(q++ - p);
+ else
+ len = strlen(p);
+ if (len) {
+ memcpy(buf, p, len);
+ buf[len] = '/';
+ memcpy(&buf[len + 1], file, filelen);
+ if (!faccessat(cwdfd, buf, X_OK, AT_EACCESS)) {
+ *free_this_out = buf;
+ errno = saved_errno;
+ return buf;
+ }
+ } else {
+ if (!faccessat(cwdfd, file, X_OK, AT_EACCESS)) {
+ errno = saved_errno;
+ return file;
+ }
+ }
+ if (errno == EACCES)
+ got_eacces = 1;
+ }
+
+ free(buf);
+ errno = got_eacces ? EACCES : ENOENT;
+ return NULL;
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/xexecl.c b/xexecl.c
new file mode 100644
index 0000000..fa46725
--- /dev/null
+++ b/xexecl.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_xexecl(int dirfd, const char *file, int atflags, const char *path, char *const *envp, ... /* argv, NULL */)
+{
+ int ret;
+ va_list args;
+ va_start(args, envp);
+ ret = libsimple_vxexecl(dirfd, file, atflags, path, envp, args);
+ va_end(args);
+ return ret;
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/xexecv.c b/xexecv.c
new file mode 100644
index 0000000..68fd264
--- /dev/null
+++ b/xexecv.c
@@ -0,0 +1,31 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+int
+libsimple_xexecv(int dirfd, const char *file, int atflags, const char *path, char *const *envp, char *const *argv)
+{
+ char *free_this;
+ int ret;
+ if (dirfd == -1 && atflags == -1) {
+ file = libsimple_which(file, AT_FDCWD, path, &free_this);
+ ret = execve(file, argv, envp);
+ free(free_this);
+ return ret;
+ } else {
+ return execveat(dirfd, file, argv, envp, atflags);
+ }
+}
+
+
+#else
+#include "test.h"
+
+int
+main(void)
+{
+ return 0;
+}
+
+#endif