summaryrefslogtreecommitdiffstats
path: root/unit/atf-src/atf-c/detail
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
commitea648e70a989cca190cd7403fe892fd2dcc290b4 (patch)
treee2b6b1c647da68b0d4d66082835e256eb30970e8 /unit/atf-src/atf-c/detail
parentInitial commit. (diff)
downloadbind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.tar.xz
bind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.zip
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'unit/atf-src/atf-c/detail')
-rw-r--r--unit/atf-src/atf-c/detail/Atffile13
-rw-r--r--unit/atf-src/atf-c/detail/Kyuafile13
-rw-r--r--unit/atf-src/atf-c/detail/Makefile.am.inc99
-rw-r--r--unit/atf-src/atf-c/detail/dynstr.c394
-rw-r--r--unit/atf-src/atf-c/detail/dynstr.h77
-rw-r--r--unit/atf-src/atf-c/detail/dynstr_test.c634
-rw-r--r--unit/atf-src/atf-c/detail/env.c114
-rw-r--r--unit/atf-src/atf-c/detail/env.h39
-rw-r--r--unit/atf-src/atf-c/detail/env_test.c133
-rw-r--r--unit/atf-src/atf-c/detail/fs.c884
-rw-r--r--unit/atf-src/atf-c/detail/fs.h128
-rw-r--r--unit/atf-src/atf-c/detail/fs_test.c1079
-rw-r--r--unit/atf-src/atf-c/detail/list.c388
-rw-r--r--unit/atf-src/atf-c/detail/list.h111
-rw-r--r--unit/atf-src/atf-c/detail/list_test.c365
-rw-r--r--unit/atf-src/atf-c/detail/map.c380
-rw-r--r--unit/atf-src/atf-c/detail/map.h114
-rw-r--r--unit/atf-src/atf-c/detail/map_test.c421
-rw-r--r--unit/atf-src/atf-c/detail/process.c670
-rw-r--r--unit/atf-src/atf-c/detail/process.h131
-rw-r--r--unit/atf-src/atf-c/detail/process_helpers.c113
-rw-r--r--unit/atf-src/atf-c/detail/process_test.c1159
-rw-r--r--unit/atf-src/atf-c/detail/sanity.c74
-rw-r--r--unit/atf-src/atf-c/detail/sanity.h72
-rw-r--r--unit/atf-src/atf-c/detail/sanity_test.c230
-rw-r--r--unit/atf-src/atf-c/detail/test_helpers.c147
-rw-r--r--unit/atf-src/atf-c/detail/test_helpers.h75
-rw-r--r--unit/atf-src/atf-c/detail/text.c180
-rw-r--r--unit/atf-src/atf-c/detail/text.h44
-rw-r--r--unit/atf-src/atf-c/detail/text_test.c421
-rw-r--r--unit/atf-src/atf-c/detail/tp_main.c615
-rw-r--r--unit/atf-src/atf-c/detail/user.c75
-rw-r--r--unit/atf-src/atf-c/detail/user.h45
-rw-r--r--unit/atf-src/atf-c/detail/user_test.c146
-rw-r--r--unit/atf-src/atf-c/detail/version_helper.c43
35 files changed, 9626 insertions, 0 deletions
diff --git a/unit/atf-src/atf-c/detail/Atffile b/unit/atf-src/atf-c/detail/Atffile
new file mode 100644
index 0000000..5fd8593
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/Atffile
@@ -0,0 +1,13 @@
+Content-Type: application/X-atf-atffile; version="1"
+
+prop: test-suite = atf
+
+tp: dynstr_test
+tp: env_test
+tp: fs_test
+tp: list_test
+tp: map_test
+tp: process_test
+tp: sanity_test
+tp: text_test
+tp: user_test
diff --git a/unit/atf-src/atf-c/detail/Kyuafile b/unit/atf-src/atf-c/detail/Kyuafile
new file mode 100644
index 0000000..bb741da
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/Kyuafile
@@ -0,0 +1,13 @@
+syntax("kyuafile", 1)
+
+test_suite("atf")
+
+atf_test_program{name="dynstr_test"}
+atf_test_program{name="env_test"}
+atf_test_program{name="fs_test"}
+atf_test_program{name="list_test"}
+atf_test_program{name="map_test"}
+atf_test_program{name="process_test"}
+atf_test_program{name="sanity_test"}
+atf_test_program{name="text_test"}
+atf_test_program{name="user_test"}
diff --git a/unit/atf-src/atf-c/detail/Makefile.am.inc b/unit/atf-src/atf-c/detail/Makefile.am.inc
new file mode 100644
index 0000000..ad45602
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/Makefile.am.inc
@@ -0,0 +1,99 @@
+# Copyright (c) 2007 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+libatf_c_la_SOURCES += atf-c/detail/dynstr.c \
+ atf-c/detail/dynstr.h \
+ atf-c/detail/env.c \
+ atf-c/detail/env.h \
+ atf-c/detail/fs.c \
+ atf-c/detail/fs.h \
+ atf-c/detail/list.c \
+ atf-c/detail/list.h \
+ atf-c/detail/map.c \
+ atf-c/detail/map.h \
+ atf-c/detail/process.c \
+ atf-c/detail/process.h \
+ atf-c/detail/sanity.c \
+ atf-c/detail/sanity.h \
+ atf-c/detail/text.c \
+ atf-c/detail/text.h \
+ atf-c/detail/tp_main.c \
+ atf-c/detail/user.c \
+ atf-c/detail/user.h
+
+tests_atf_c_detail_DATA = atf-c/detail/Atffile \
+ atf-c/detail/Kyuafile
+tests_atf_c_detaildir = $(pkgtestsdir)/atf-c/detail
+EXTRA_DIST += $(tests_atf_c_detail_DATA)
+
+noinst_LTLIBRARIES += atf-c/detail/libtest_helpers.la
+atf_c_detail_libtest_helpers_la_SOURCES = atf-c/detail/test_helpers.c \
+ atf-c/detail/test_helpers.h
+atf_c_detail_libtest_helpers_la_CPPFLAGS = -I$(srcdir)/atf-c \
+ -DATF_INCLUDEDIR=\"$(includedir)\"
+
+tests_atf_c_detail_PROGRAMS = atf-c/detail/dynstr_test
+atf_c_detail_dynstr_test_SOURCES = atf-c/detail/dynstr_test.c
+atf_c_detail_dynstr_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/env_test
+atf_c_detail_env_test_SOURCES = atf-c/detail/env_test.c
+atf_c_detail_env_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/fs_test
+atf_c_detail_fs_test_SOURCES = atf-c/detail/fs_test.c
+atf_c_detail_fs_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/list_test
+atf_c_detail_list_test_SOURCES = atf-c/detail/list_test.c
+atf_c_detail_list_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/map_test
+atf_c_detail_map_test_SOURCES = atf-c/detail/map_test.c
+atf_c_detail_map_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/process_helpers
+atf_c_detail_process_helpers_SOURCES = atf-c/detail/process_helpers.c
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/process_test
+atf_c_detail_process_test_SOURCES = atf-c/detail/process_test.c
+atf_c_detail_process_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/sanity_test
+atf_c_detail_sanity_test_SOURCES = atf-c/detail/sanity_test.c
+atf_c_detail_sanity_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/text_test
+atf_c_detail_text_test_SOURCES = atf-c/detail/text_test.c
+atf_c_detail_text_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/user_test
+atf_c_detail_user_test_SOURCES = atf-c/detail/user_test.c
+atf_c_detail_user_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/version_helper
+atf_c_detail_version_helper_SOURCES = atf-c/detail/version_helper.c
+
+# vim: syntax=make:noexpandtab:shiftwidth=8:softtabstop=8
diff --git a/unit/atf-src/atf-c/detail/dynstr.c b/unit/atf-src/atf-c/detail/dynstr.c
new file mode 100644
index 0000000..424a1c9
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/dynstr.c
@@ -0,0 +1,394 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/dynstr.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/error.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+resize(atf_dynstr_t *ad, size_t newsize)
+{
+ char *newdata;
+ atf_error_t err;
+
+ PRE(newsize > ad->m_datasize);
+
+ newdata = (char *)malloc(newsize);
+ if (newdata == NULL) {
+ err = atf_no_memory_error();
+ } else {
+ strcpy(newdata, ad->m_data);
+ free(ad->m_data);
+ ad->m_data = newdata;
+ ad->m_datasize = newsize;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+static
+atf_error_t
+prepend_or_append(atf_dynstr_t *ad, const char *fmt, va_list ap,
+ bool prepend)
+{
+ char *aux;
+ atf_error_t err;
+ size_t newlen;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = atf_text_format_ap(&aux, fmt, ap2);
+ va_end(ap2);
+ if (atf_is_error(err))
+ goto out;
+ newlen = ad->m_length + strlen(aux);
+
+ if (newlen + sizeof(char) > ad->m_datasize) {
+ err = resize(ad, newlen + sizeof(char));
+ if (atf_is_error(err))
+ goto out_free;
+ }
+
+ if (prepend) {
+ memmove(ad->m_data + strlen(aux), ad->m_data, ad->m_length + 1);
+ memcpy(ad->m_data, aux, strlen(aux));
+ } else
+ strcpy(ad->m_data + ad->m_length, aux);
+ ad->m_length = newlen;
+ err = atf_no_error();
+
+out_free:
+ free(aux);
+out:
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_dynstr" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constants.
+ */
+
+const size_t atf_dynstr_npos = SIZE_MAX;
+
+/*
+ * Constructors and destructors.
+ */
+
+atf_error_t
+atf_dynstr_init(atf_dynstr_t *ad)
+{
+ atf_error_t err;
+
+ ad->m_data = (char *)malloc(sizeof(char));
+ if (ad->m_data == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ ad->m_data[0] = '\0';
+ ad->m_datasize = 1;
+ ad->m_length = 0;
+ err = atf_no_error();
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+
+ ad->m_datasize = strlen(fmt) + 1;
+ ad->m_length = 0;
+
+ do {
+ va_list ap2;
+ int ret;
+
+ ad->m_datasize *= 2;
+ ad->m_data = (char *)malloc(ad->m_datasize);
+ if (ad->m_data == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ va_copy(ap2, ap);
+ ret = vsnprintf(ad->m_data, ad->m_datasize, fmt, ap2);
+ va_end(ap2);
+ if (ret < 0) {
+ free(ad->m_data);
+ err = atf_libc_error(errno, "Cannot format string");
+ goto out;
+ }
+
+ INV(ret >= 0);
+ if ((size_t)ret >= ad->m_datasize) {
+ free(ad->m_data);
+ ad->m_data = NULL;
+ }
+ ad->m_length = ret;
+ } while (ad->m_length >= ad->m_datasize);
+
+ err = atf_no_error();
+out:
+ POST(atf_is_error(err) || ad->m_data != NULL);
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_fmt(atf_dynstr_t *ad, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_dynstr_init_ap(ad, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_raw(atf_dynstr_t *ad, const void *mem, size_t memlen)
+{
+ atf_error_t err;
+
+ if (memlen >= SIZE_MAX - 1) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ ad->m_data = (char *)malloc(memlen + 1);
+ if (ad->m_data == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ ad->m_datasize = memlen + 1;
+ memcpy(ad->m_data, mem, memlen);
+ ad->m_data[memlen] = '\0';
+ ad->m_length = strlen(ad->m_data);
+ INV(ad->m_length <= memlen);
+ err = atf_no_error();
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_rep(atf_dynstr_t *ad, size_t len, char ch)
+{
+ atf_error_t err;
+
+ if (len == SIZE_MAX) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ ad->m_datasize = (len + 1) * sizeof(char);
+ ad->m_data = (char *)malloc(ad->m_datasize);
+ if (ad->m_data == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ memset(ad->m_data, ch, len);
+ ad->m_data[len] = '\0';
+ ad->m_length = len;
+ err = atf_no_error();
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_substr(atf_dynstr_t *ad, const atf_dynstr_t *src,
+ size_t beg, size_t end)
+{
+ if (beg > src->m_length)
+ beg = src->m_length;
+
+ if (end == atf_dynstr_npos || end > src->m_length)
+ end = src->m_length;
+
+ return atf_dynstr_init_raw(ad, src->m_data + beg, end - beg);
+}
+
+atf_error_t
+atf_dynstr_copy(atf_dynstr_t *dest, const atf_dynstr_t *src)
+{
+ atf_error_t err;
+
+ dest->m_data = (char *)malloc(src->m_datasize);
+ if (dest->m_data == NULL)
+ err = atf_no_memory_error();
+ else {
+ memcpy(dest->m_data, src->m_data, src->m_datasize);
+ dest->m_datasize = src->m_datasize;
+ dest->m_length = src->m_length;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+void
+atf_dynstr_fini(atf_dynstr_t *ad)
+{
+ INV(ad->m_data != NULL);
+ free(ad->m_data);
+}
+
+char *
+atf_dynstr_fini_disown(atf_dynstr_t *ad)
+{
+ INV(ad->m_data != NULL);
+ return ad->m_data;
+}
+
+/*
+ * Getters.
+ */
+
+const char *
+atf_dynstr_cstring(const atf_dynstr_t *ad)
+{
+ return ad->m_data;
+}
+
+size_t
+atf_dynstr_length(const atf_dynstr_t *ad)
+{
+ return ad->m_length;
+}
+
+size_t
+atf_dynstr_rfind_ch(const atf_dynstr_t *ad, char ch)
+{
+ size_t pos;
+
+ for (pos = ad->m_length; pos > 0 && ad->m_data[pos - 1] != ch; pos--)
+ ;
+
+ return pos == 0 ? atf_dynstr_npos : pos - 1;
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_dynstr_append_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = prepend_or_append(ad, fmt, ap2, false);
+ va_end(ap2);
+
+ return err;
+}
+
+atf_error_t
+atf_dynstr_append_fmt(atf_dynstr_t *ad, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = prepend_or_append(ad, fmt, ap, false);
+ va_end(ap);
+
+ return err;
+}
+
+void
+atf_dynstr_clear(atf_dynstr_t *ad)
+{
+ ad->m_data[0] = '\0';
+ ad->m_length = 0;
+}
+
+atf_error_t
+atf_dynstr_prepend_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = prepend_or_append(ad, fmt, ap2, true);
+ va_end(ap2);
+
+ return err;
+}
+
+atf_error_t
+atf_dynstr_prepend_fmt(atf_dynstr_t *ad, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = prepend_or_append(ad, fmt, ap, true);
+ va_end(ap);
+
+ return err;
+}
+
+/*
+ * Operators.
+ */
+
+bool
+atf_equal_dynstr_cstring(const atf_dynstr_t *ad, const char *str)
+{
+ return strcmp(ad->m_data, str) == 0;
+}
+
+bool
+atf_equal_dynstr_dynstr(const atf_dynstr_t *s1, const atf_dynstr_t *s2)
+{
+ return strcmp(s1->m_data, s2->m_data) == 0;
+}
diff --git a/unit/atf-src/atf-c/detail/dynstr.h b/unit/atf-src/atf-c/detail/dynstr.h
new file mode 100644
index 0000000..60b8e42
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/dynstr.h
@@ -0,0 +1,77 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_DYNSTR_H)
+#define ATF_C_DETAIL_DYNSTR_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_dynstr" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_dynstr {
+ char *m_data;
+ size_t m_datasize;
+ size_t m_length;
+};
+typedef struct atf_dynstr atf_dynstr_t;
+
+/* Constants */
+extern const size_t atf_dynstr_npos;
+
+/* Constructors and destructors */
+atf_error_t atf_dynstr_init(atf_dynstr_t *);
+atf_error_t atf_dynstr_init_ap(atf_dynstr_t *, const char *, va_list);
+atf_error_t atf_dynstr_init_fmt(atf_dynstr_t *, const char *, ...);
+atf_error_t atf_dynstr_init_raw(atf_dynstr_t *, const void *, size_t);
+atf_error_t atf_dynstr_init_rep(atf_dynstr_t *, size_t, char);
+atf_error_t atf_dynstr_init_substr(atf_dynstr_t *, const atf_dynstr_t *,
+ size_t, size_t);
+atf_error_t atf_dynstr_copy(atf_dynstr_t *, const atf_dynstr_t *);
+void atf_dynstr_fini(atf_dynstr_t *);
+char *atf_dynstr_fini_disown(atf_dynstr_t *);
+
+/* Getters */
+const char *atf_dynstr_cstring(const atf_dynstr_t *);
+size_t atf_dynstr_length(const atf_dynstr_t *);
+size_t atf_dynstr_rfind_ch(const atf_dynstr_t *, char);
+
+/* Modifiers */
+atf_error_t atf_dynstr_append_ap(atf_dynstr_t *, const char *, va_list);
+atf_error_t atf_dynstr_append_fmt(atf_dynstr_t *, const char *, ...);
+void atf_dynstr_clear(atf_dynstr_t *);
+atf_error_t atf_dynstr_prepend_ap(atf_dynstr_t *, const char *, va_list);
+atf_error_t atf_dynstr_prepend_fmt(atf_dynstr_t *, const char *, ...);
+
+/* Operators */
+bool atf_equal_dynstr_cstring(const atf_dynstr_t *, const char *);
+bool atf_equal_dynstr_dynstr(const atf_dynstr_t *, const atf_dynstr_t *);
+
+#endif /* !defined(ATF_C_DETAIL_DYNSTR_H) */
diff --git a/unit/atf-src/atf-c/detail/dynstr_test.c b/unit/atf-src/atf-c/detail/dynstr_test.c
new file mode 100644
index 0000000..2512752
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/dynstr_test.c
@@ -0,0 +1,634 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/dynstr.h"
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Tests for the "atf_dynstr" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+ATF_TC(init);
+ATF_TC_HEAD(init, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the empty constructor");
+}
+ATF_TC_BODY(init, tc)
+{
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init(&str));
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+}
+
+static
+void
+init_fmt(atf_dynstr_t *str, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ RE(atf_dynstr_init_ap(str, fmt, ap));
+ va_end(ap);
+}
+
+ATF_TC(init_ap);
+ATF_TC_HEAD(init_ap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the formatted constructor using "
+ "a va_list argument");
+}
+ATF_TC_BODY(init_ap, tc)
+{
+ atf_dynstr_t str;
+
+ init_fmt(&str, "String 1");
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0);
+ atf_dynstr_fini(&str);
+
+ init_fmt(&str, "String %d", 2);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0);
+ atf_dynstr_fini(&str);
+
+ init_fmt(&str, "%s %d", "String", 3);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 3") == 0);
+ atf_dynstr_fini(&str);
+
+ init_fmt(&str, "%s%s%s%s%s%s%s", "This ", "should ", "be ", "a ",
+ "large ", "string ", "aaaabbbbccccdddd");
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str),
+ "This should be a large string "
+ "aaaabbbbccccdddd") == 0);
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(init_fmt);
+ATF_TC_HEAD(init_fmt, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the formatted constructor using "
+ "a variable list of parameters");
+}
+ATF_TC_BODY(init_fmt, tc)
+{
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&str, "String 1"));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "String %d", 2));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "%s %d", "String", 3));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 3") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "%s%s%s%s%s%s%s", "This ", "should ",
+ "be ", "a ", "large ", "string ",
+ "aaaabbbbccccdddd"));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str),
+ "This should be a large string "
+ "aaaabbbbccccdddd") == 0);
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(init_raw);
+ATF_TC_HEAD(init_raw, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of a string "
+ "using a raw memory pointer");
+}
+ATF_TC_BODY(init_raw, tc)
+{
+ const char *src = "String 1, String 2";
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_raw(&str, src, 0));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_raw(&str, src, 8));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_raw(&str, src + 10, 8));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_raw(&str, "String\0Lost", 11));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String") == 0);
+ atf_dynstr_fini(&str);
+
+ {
+ atf_error_t err = atf_dynstr_init_raw(&str, "NULL", SIZE_MAX - 1);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ atf_error_free(err);
+ }
+}
+
+ATF_TC(init_rep);
+ATF_TC_HEAD(init_rep, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of a string by "
+ "repeating characters");
+}
+ATF_TC_BODY(init_rep, tc)
+{
+ const size_t maxlen = 8192;
+ char buf[maxlen + 1];
+ size_t i;
+
+ buf[0] = '\0';
+
+ for (i = 0; i < maxlen; i++) {
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_rep(&str, i, 'a'));
+
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to construct dynstr by repeating %zd "
+ "times the '%c' character", i, 'a');
+ }
+
+ atf_dynstr_fini(&str);
+
+ strcat(buf, "a");
+ }
+
+ {
+ atf_dynstr_t str;
+ atf_error_t err;
+
+ err = atf_dynstr_init_rep(&str, SIZE_MAX, 'a');
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ atf_error_free(err);
+
+ err = atf_dynstr_init_rep(&str, SIZE_MAX - 1, 'a');
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ atf_error_free(err);
+ }
+}
+
+ATF_TC(init_substr);
+ATF_TC_HEAD(init_substr, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of a string "
+ "using a substring of another one");
+}
+ATF_TC_BODY(init_substr, tc)
+{
+ atf_dynstr_t src;
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&src, "Str 1, Str 2"));
+
+ RE(atf_dynstr_init_substr(&str, &src, 0, 0));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 0, atf_dynstr_npos));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1, Str 2") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 0, 100));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1, Str 2") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 0, 5));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 100, atf_dynstr_npos));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 7, atf_dynstr_npos));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 2") == 0);
+ atf_dynstr_fini(&str);
+
+ atf_dynstr_fini(&src);
+}
+
+ATF_TC(copy);
+ATF_TC_HEAD(copy, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_dynstr_copy constructor");
+}
+ATF_TC_BODY(copy, tc)
+{
+ atf_dynstr_t str, str2;
+
+ RE(atf_dynstr_init_fmt(&str, "Test string"));
+ RE(atf_dynstr_copy(&str2, &str));
+
+ ATF_REQUIRE(atf_equal_dynstr_dynstr(&str, &str2));
+
+ RE(atf_dynstr_append_fmt(&str2, " non-shared text"));
+
+ ATF_REQUIRE(!atf_equal_dynstr_dynstr(&str, &str2));
+
+ atf_dynstr_fini(&str2);
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(fini_disown);
+ATF_TC_HEAD(fini_disown, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks grabbing ownership of the "
+ "internal plain C string");
+}
+ATF_TC_BODY(fini_disown, tc)
+{
+ const char *cstr;
+ char *cstr2;
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&str, "Test string 1"));
+ cstr = atf_dynstr_cstring(&str);
+ cstr2 = atf_dynstr_fini_disown(&str);
+
+ ATF_REQUIRE_EQ(cstr, cstr2);
+ free(cstr2);
+}
+
+/*
+ * Getters.
+ */
+
+ATF_TC(cstring);
+ATF_TC_HEAD(cstring, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the method to obtain a plain C "
+ "string");
+}
+ATF_TC_BODY(cstring, tc)
+{
+ const char *cstr;
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&str, "Test string 1"));
+ cstr = atf_dynstr_cstring(&str);
+ ATF_REQUIRE(cstr != NULL);
+ ATF_REQUIRE(strcmp(cstr, "Test string 1") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "Test string 2"));
+ cstr = atf_dynstr_cstring(&str);
+ ATF_REQUIRE(cstr != NULL);
+ ATF_REQUIRE(strcmp(cstr, "Test string 2") == 0);
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(length);
+ATF_TC_HEAD(length, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the method to obtain the length");
+}
+ATF_TC_BODY(length, tc)
+{
+ size_t i;
+
+ for (i = 0; i < 8192; i++) {
+ atf_dynstr_t str;
+ RE(atf_dynstr_init_rep(&str, i, 'a'));
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), i);
+ atf_dynstr_fini(&str);
+ }
+}
+
+ATF_TC(rfind_ch);
+ATF_TC_HEAD(rfind_ch, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the method to locate the first "
+ "occurrence of a character starting from the end");
+}
+ATF_TC_BODY(rfind_ch, tc)
+{
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&str, "Foo1/Bar2/,.Baz"));
+
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '\0'), atf_dynstr_npos);
+
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '0'), atf_dynstr_npos);
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'b'), atf_dynstr_npos);
+
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'F'), 0);
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '/'), 9);
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'a'), 13);
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'z'), 14);
+
+ atf_dynstr_fini(&str);
+}
+
+/*
+ * Modifiers.
+ */
+
+static
+void
+check_append(atf_error_t (*append)(atf_dynstr_t *, const char *, ...))
+{
+ const size_t maxlen = 8192;
+ char buf[maxlen + 1];
+ size_t i;
+ atf_dynstr_t str;
+
+ printf("Appending with plain string\n");
+ buf[0] = '\0';
+ RE(atf_dynstr_init(&str));
+ for (i = 0; i < maxlen; i++) {
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to append character at iteration %zd", i);
+ }
+
+ RE(append(&str, "a"));
+ strcat(buf, "a");
+ }
+ atf_dynstr_fini(&str);
+
+ printf("Appending with formatted string\n");
+ buf[0] = '\0';
+ RE(atf_dynstr_init(&str));
+ for (i = 0; i < maxlen; i++) {
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to append character at iteration %zd", i);
+ }
+
+ RE(append(&str, "%s", "a"));
+ strcat(buf, "a");
+ }
+ atf_dynstr_fini(&str);
+}
+
+static
+atf_error_t
+append_ap_aux(atf_dynstr_t *str, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_dynstr_append_ap(str, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+ATF_TC(append_ap);
+ATF_TC_HEAD(append_ap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that appending a string to "
+ "another one works");
+}
+ATF_TC_BODY(append_ap, tc)
+{
+ check_append(append_ap_aux);
+}
+
+ATF_TC(append_fmt);
+ATF_TC_HEAD(append_fmt, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that appending a string to "
+ "another one works");
+}
+ATF_TC_BODY(append_fmt, tc)
+{
+ check_append(atf_dynstr_append_fmt);
+}
+
+ATF_TC(clear);
+ATF_TC_HEAD(clear, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clearing a string");
+}
+ATF_TC_BODY(clear, tc)
+{
+ atf_dynstr_t str;
+
+ printf("Clear an empty string\n");
+ RE(atf_dynstr_init(&str));
+ atf_dynstr_clear(&str);
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+
+ printf("Clear a non-empty string\n");
+ RE(atf_dynstr_init_fmt(&str, "Not empty"));
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), strlen("Not empty"));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Not empty") == 0);
+ atf_dynstr_clear(&str);
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+}
+
+static
+void
+check_prepend(atf_error_t (*prepend)(atf_dynstr_t *, const char *, ...))
+{
+ const size_t maxlen = 8192;
+ char buf[maxlen + 1];
+ size_t i;
+ atf_dynstr_t str;
+
+ printf("Prepending with plain string\n");
+ buf[0] = '\0';
+ RE(atf_dynstr_init(&str));
+ for (i = 0; i < maxlen; i++) {
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to prepend character at iteration %zd", i);
+ }
+
+ memmove(buf + 1, buf, i + 1);
+ if (i % 2 == 0) {
+ RE(prepend(&str, "%s", "a"));
+ buf[0] = 'a';
+ } else {
+ RE(prepend(&str, "%s", "b"));
+ buf[0] = 'b';
+ }
+ }
+ atf_dynstr_fini(&str);
+
+ printf("Prepending with formatted string\n");
+ buf[0] = '\0';
+ RE(atf_dynstr_init(&str));
+ for (i = 0; i < maxlen; i++) {
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to prepend character at iteration %zd", i);
+ }
+
+ memmove(buf + 1, buf, i + 1);
+ if (i % 2 == 0) {
+ RE(prepend(&str, "%s", "a"));
+ buf[0] = 'a';
+ } else {
+ RE(prepend(&str, "%s", "b"));
+ buf[0] = 'b';
+ }
+ }
+ atf_dynstr_fini(&str);
+}
+
+static
+atf_error_t
+prepend_ap_aux(atf_dynstr_t *str, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_dynstr_prepend_ap(str, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+ATF_TC(prepend_ap);
+ATF_TC_HEAD(prepend_ap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that prepending a string to "
+ "another one works");
+}
+ATF_TC_BODY(prepend_ap, tc)
+{
+ check_prepend(prepend_ap_aux);
+}
+
+ATF_TC(prepend_fmt);
+ATF_TC_HEAD(prepend_fmt, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that prepending a string to "
+ "another one works");
+}
+ATF_TC_BODY(prepend_fmt, tc)
+{
+ check_prepend(atf_dynstr_prepend_fmt);
+}
+
+/*
+ * Operators.
+ */
+
+ATF_TC(equal_cstring);
+ATF_TC_HEAD(equal_cstring, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_equal_dynstr_cstring "
+ "function");
+}
+ATF_TC_BODY(equal_cstring, tc)
+{
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init(&str));
+ ATF_REQUIRE( atf_equal_dynstr_cstring(&str, ""));
+ ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Test"));
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "Test"));
+ ATF_REQUIRE( atf_equal_dynstr_cstring(&str, "Test"));
+ ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, ""));
+ ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Tes"));
+ ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Test "));
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(equal_dynstr);
+ATF_TC_HEAD(equal_dynstr, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_equal_dynstr_dynstr "
+ "function");
+}
+ATF_TC_BODY(equal_dynstr, tc)
+{
+ atf_dynstr_t str, str2;
+
+ RE(atf_dynstr_init(&str));
+ RE(atf_dynstr_init_fmt(&str2, "Test"));
+ ATF_REQUIRE( atf_equal_dynstr_dynstr(&str, &str));
+ ATF_REQUIRE(!atf_equal_dynstr_dynstr(&str, &str2));
+ atf_dynstr_fini(&str2);
+ atf_dynstr_fini(&str);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Constructors and destructors. */
+ ATF_TP_ADD_TC(tp, init);
+ ATF_TP_ADD_TC(tp, init_ap);
+ ATF_TP_ADD_TC(tp, init_fmt);
+ ATF_TP_ADD_TC(tp, init_raw);
+ ATF_TP_ADD_TC(tp, init_rep);
+ ATF_TP_ADD_TC(tp, init_substr);
+ ATF_TP_ADD_TC(tp, copy);
+ ATF_TP_ADD_TC(tp, fini_disown);
+
+ /* Getters. */
+ ATF_TP_ADD_TC(tp, cstring);
+ ATF_TP_ADD_TC(tp, length);
+ ATF_TP_ADD_TC(tp, rfind_ch);
+
+ /* Modifiers. */
+ ATF_TP_ADD_TC(tp, append_ap);
+ ATF_TP_ADD_TC(tp, append_fmt);
+ ATF_TP_ADD_TC(tp, clear);
+ ATF_TP_ADD_TC(tp, prepend_ap);
+ ATF_TP_ADD_TC(tp, prepend_fmt);
+
+ /* Operators. */
+ ATF_TP_ADD_TC(tp, equal_cstring);
+ ATF_TP_ADD_TC(tp, equal_dynstr);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/env.c b/unit/atf-src/atf-c/detail/env.c
new file mode 100644
index 0000000..8ee3d69
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/env.c
@@ -0,0 +1,114 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/env.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/error.h"
+
+const char *
+atf_env_get(const char *name)
+{
+ const char* val = getenv(name);
+ PRE(val != NULL);
+ return val;
+}
+
+const char *
+atf_env_get_with_default(const char *name, const char *default_value)
+{
+ const char* val = getenv(name);
+ if (val == NULL)
+ return default_value;
+ else
+ return val;
+}
+
+bool
+atf_env_has(const char *name)
+{
+ return getenv(name) != NULL;
+}
+
+atf_error_t
+atf_env_set(const char *name, const char *val)
+{
+ atf_error_t err;
+
+#if defined(HAVE_SETENV)
+ if (setenv(name, val, 1) == -1)
+ err = atf_libc_error(errno, "Cannot set environment variable "
+ "'%s' to '%s'", name, val);
+ else
+ err = atf_no_error();
+#elif defined(HAVE_PUTENV)
+ char *buf;
+
+ err = atf_text_format(&buf, "%s=%s", name, val);
+ if (!atf_is_error(err)) {
+ if (putenv(buf) == -1)
+ err = atf_libc_error(errno, "Cannot set environment variable "
+ "'%s' to '%s'", name, val);
+ free(buf);
+ }
+#else
+# error "Don't know how to set an environment variable."
+#endif
+
+ return err;
+}
+
+atf_error_t
+atf_env_unset(const char *name)
+{
+ atf_error_t err;
+
+#if defined(HAVE_UNSETENV)
+ unsetenv(name);
+ err = atf_no_error();
+#elif defined(HAVE_PUTENV)
+ char *buf;
+
+ err = atf_text_format(&buf, "%s=", name);
+ if (!atf_is_error(err)) {
+ if (putenv(buf) == -1)
+ err = atf_libc_error(errno, "Cannot unset environment variable"
+ " '%s'", name);
+ free(buf);
+ }
+#else
+# error "Don't know how to unset an environment variable."
+#endif
+
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/env.h b/unit/atf-src/atf-c/detail/env.h
new file mode 100644
index 0000000..5d29440
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/env.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_ENV_H)
+#define ATF_C_DETAIL_ENV_H
+
+#include <stdbool.h>
+
+#include <atf-c/error_fwd.h>
+
+const char *atf_env_get(const char *);
+const char *atf_env_get_with_default(const char *, const char *);
+bool atf_env_has(const char *);
+atf_error_t atf_env_set(const char *, const char *);
+atf_error_t atf_env_unset(const char *);
+
+#endif /* !defined(ATF_C_DETAIL_ENV_H) */
diff --git a/unit/atf-src/atf-c/detail/env_test.c b/unit/atf-src/atf-c/detail/env_test.c
new file mode 100644
index 0000000..3909c29
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/env_test.c
@@ -0,0 +1,133 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/env.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/detail/text.h"
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(has);
+ATF_TC_HEAD(has, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_has function");
+}
+ATF_TC_BODY(has, tc)
+{
+ ATF_REQUIRE(atf_env_has("PATH"));
+ ATF_REQUIRE(!atf_env_has("_UNDEFINED_VARIABLE_"));
+}
+
+ATF_TC(get);
+ATF_TC_HEAD(get, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_get function");
+}
+ATF_TC_BODY(get, tc)
+{
+ const char *val;
+
+ ATF_REQUIRE(atf_env_has("PATH"));
+
+ val = atf_env_get("PATH");
+ ATF_REQUIRE(strlen(val) > 0);
+ ATF_REQUIRE(strchr(val, ':') != NULL);
+}
+
+ATF_TC(get_with_default);
+ATF_TC_HEAD(get_with_default, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_get_with_default "
+ "function");
+}
+ATF_TC_BODY(get_with_default, tc)
+{
+ const char *val;
+
+ ATF_REQUIRE(atf_env_has("PATH"));
+
+ val = atf_env_get_with_default("PATH", "unknown");
+ ATF_REQUIRE(strcmp(val, "unknown") != 0);
+
+ val = atf_env_get_with_default("_UNKNOWN_VARIABLE_", "foo bar");
+ ATF_REQUIRE(strcmp(val, "foo bar") == 0);
+}
+
+ATF_TC(set);
+ATF_TC_HEAD(set, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_set function");
+}
+ATF_TC_BODY(set, tc)
+{
+ char *oldval;
+
+ ATF_REQUIRE(atf_env_has("PATH"));
+ RE(atf_text_format(&oldval, "%s", atf_env_get("PATH")));
+ RE(atf_env_set("PATH", "foo-bar"));
+ ATF_REQUIRE(strcmp(atf_env_get("PATH"), oldval) != 0);
+ ATF_REQUIRE(strcmp(atf_env_get("PATH"), "foo-bar") == 0);
+ free(oldval);
+
+ ATF_REQUIRE(!atf_env_has("_UNDEFINED_VARIABLE_"));
+ RE(atf_env_set("_UNDEFINED_VARIABLE_", "foo2-bar2"));
+ ATF_REQUIRE(strcmp(atf_env_get("_UNDEFINED_VARIABLE_"),
+ "foo2-bar2") == 0);
+}
+
+ATF_TC(unset);
+ATF_TC_HEAD(unset, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_unset function");
+}
+ATF_TC_BODY(unset, tc)
+{
+ ATF_REQUIRE(atf_env_has("PATH"));
+ RE(atf_env_unset("PATH"));
+ ATF_REQUIRE(!atf_env_has("PATH"));
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, has);
+ ATF_TP_ADD_TC(tp, get);
+ ATF_TP_ADD_TC(tp, get_with_default);
+ ATF_TP_ADD_TC(tp, set);
+ ATF_TP_ADD_TC(tp, unset);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/fs.c b/unit/atf-src/atf-c/detail/fs.c
new file mode 100644
index 0000000..5ff7648
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/fs.c
@@ -0,0 +1,884 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/fs.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/detail/user.h"
+#include "atf-c/error.h"
+
+/* ---------------------------------------------------------------------
+ * Prototypes for auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static bool check_umask(const mode_t, const mode_t);
+static atf_error_t copy_contents(const atf_fs_path_t *, char **);
+static mode_t current_umask(void);
+static atf_error_t do_mkdtemp(char *);
+static atf_error_t normalize(atf_dynstr_t *, char *);
+static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list);
+static void replace_contents(atf_fs_path_t *, const char *);
+static const char *stat_type_to_string(const int);
+
+/* ---------------------------------------------------------------------
+ * The "invalid_umask" error type.
+ * --------------------------------------------------------------------- */
+
+struct invalid_umask_error_data {
+ /* One of atf_fs_stat_*_type. */
+ int m_type;
+
+ /* The original path causing the error. */
+ /* XXX: Ideally this would be an atf_fs_path_t, but if we create it
+ * from the error constructor, we cannot delete the path later on.
+ * Can't remember why atf_error_new does not take a hook for
+ * deletion. */
+ char m_path[1024];
+
+ /* The umask that caused the error. */
+ mode_t m_umask;
+};
+typedef struct invalid_umask_error_data invalid_umask_error_data_t;
+
+static
+void
+invalid_umask_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ const invalid_umask_error_data_t *data;
+
+ PRE(atf_error_is(err, "invalid_umask"));
+
+ data = atf_error_data(err);
+ snprintf(buf, buflen, "Could not create the temporary %s %s because "
+ "it will not have enough access rights due to the current "
+ "umask %05o", stat_type_to_string(data->m_type),
+ data->m_path, (unsigned int)data->m_umask);
+}
+
+static
+atf_error_t
+invalid_umask_error(const atf_fs_path_t *path, const int type,
+ const mode_t failing_mask)
+{
+ atf_error_t err;
+ invalid_umask_error_data_t data;
+
+ data.m_type = type;
+
+ strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path));
+ data.m_path[sizeof(data.m_path) - 1] = '\0';
+
+ data.m_umask = failing_mask;
+
+ err = atf_error_new("invalid_umask", &data, sizeof(data),
+ invalid_umask_format);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * The "unknown_file_type" error type.
+ * --------------------------------------------------------------------- */
+
+struct unknown_type_error_data {
+ const char *m_path;
+ int m_type;
+};
+typedef struct unknown_type_error_data unknown_type_error_data_t;
+
+static
+void
+unknown_type_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ const unknown_type_error_data_t *data;
+
+ PRE(atf_error_is(err, "unknown_type"));
+
+ data = atf_error_data(err);
+ snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type,
+ data->m_path);
+}
+
+static
+atf_error_t
+unknown_type_error(const char *path, int type)
+{
+ atf_error_t err;
+ unknown_type_error_data_t data;
+
+ data.m_path = path;
+ data.m_type = type;
+
+ err = atf_error_new("unknown_type", &data, sizeof(data),
+ unknown_type_format);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+bool
+check_umask(const mode_t exp_mode, const mode_t min_mode)
+{
+ const mode_t actual_mode = (~current_umask() & exp_mode);
+ return (actual_mode & min_mode) == min_mode;
+}
+
+static
+atf_error_t
+copy_contents(const atf_fs_path_t *p, char **buf)
+{
+ atf_error_t err;
+ char *str;
+
+ str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1);
+ if (str == NULL)
+ err = atf_no_memory_error();
+ else {
+ strcpy(str, atf_dynstr_cstring(&p->m_data));
+ *buf = str;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+static
+mode_t
+current_umask(void)
+{
+ const mode_t current = umask(0);
+ (void)umask(current);
+ return current;
+}
+
+static
+atf_error_t
+do_mkdtemp(char *tmpl)
+{
+ atf_error_t err;
+
+ PRE(strstr(tmpl, "XXXXXX") != NULL);
+
+ if (mkdtemp(tmpl) == NULL)
+ err = atf_libc_error(errno, "Cannot create temporary directory "
+ "with template '%s'", tmpl);
+ else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+do_mkstemp(char *tmpl, int *fdout)
+{
+ atf_error_t err;
+
+ PRE(strstr(tmpl, "XXXXXX") != NULL);
+
+ *fdout = mkstemp(tmpl);
+ if (*fdout == -1)
+ err = atf_libc_error(errno, "Cannot create temporary file "
+ "with template '%s'", tmpl);
+
+ else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+normalize(atf_dynstr_t *d, char *p)
+{
+ const char *ptr;
+ char *last;
+ atf_error_t err;
+ bool first;
+
+ PRE(strlen(p) > 0);
+ PRE(atf_dynstr_length(d) == 0);
+
+ if (p[0] == '/')
+ err = atf_dynstr_append_fmt(d, "/");
+ else
+ err = atf_no_error();
+
+ first = true;
+ last = NULL; /* Silence GCC warning. */
+ ptr = strtok_r(p, "/", &last);
+ while (!atf_is_error(err) && ptr != NULL) {
+ if (strlen(ptr) > 0) {
+ err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr);
+ first = false;
+ }
+
+ ptr = strtok_r(NULL, "/", &last);
+ }
+
+ return err;
+}
+
+static
+atf_error_t
+normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
+{
+ char *str;
+ atf_error_t err;
+ va_list ap2;
+
+ err = atf_dynstr_init(d);
+ if (atf_is_error(err))
+ goto out;
+
+ va_copy(ap2, ap);
+ err = atf_text_format_ap(&str, p, ap2);
+ va_end(ap2);
+ if (atf_is_error(err))
+ atf_dynstr_fini(d);
+ else {
+ err = normalize(d, str);
+ free(str);
+ }
+
+out:
+ return err;
+}
+
+static
+void
+replace_contents(atf_fs_path_t *p, const char *buf)
+{
+ atf_error_t err;
+
+ PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
+
+ atf_dynstr_clear(&p->m_data);
+ err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
+
+ INV(!atf_is_error(err));
+}
+
+static
+const char *
+stat_type_to_string(const int type)
+{
+ const char *str;
+
+ if (type == atf_fs_stat_blk_type)
+ str = "block device";
+ else if (type == atf_fs_stat_chr_type)
+ str = "character device";
+ else if (type == atf_fs_stat_dir_type)
+ str = "directory";
+ else if (type == atf_fs_stat_fifo_type)
+ str = "named pipe";
+ else if (type == atf_fs_stat_lnk_type)
+ str = "symbolic link";
+ else if (type == atf_fs_stat_reg_type)
+ str = "regular file";
+ else if (type == atf_fs_stat_sock_type)
+ str = "socket";
+ else if (type == atf_fs_stat_wht_type)
+ str = "whiteout";
+ else {
+ UNREACHABLE;
+ str = NULL;
+ }
+
+ return str;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors/destructors.
+ */
+
+atf_error_t
+atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = normalize_ap(&p->m_data, fmt, ap2);
+ va_end(ap2);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_fs_path_init_ap(p, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
+{
+ return atf_dynstr_copy(&dest->m_data, &src->m_data);
+}
+
+void
+atf_fs_path_fini(atf_fs_path_t *p)
+{
+ atf_dynstr_fini(&p->m_data);
+}
+
+/*
+ * Getters.
+ */
+
+atf_error_t
+atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
+{
+ const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
+ atf_error_t err;
+
+ if (endpos == atf_dynstr_npos)
+ err = atf_fs_path_init_fmt(bp, ".");
+ else if (endpos == 0)
+ err = atf_fs_path_init_fmt(bp, "/");
+ else
+ err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos);
+
+#if defined(HAVE_CONST_DIRNAME)
+ INV(atf_equal_dynstr_cstring(&bp->m_data,
+ dirname(atf_dynstr_cstring(&p->m_data))));
+#endif /* defined(HAVE_CONST_DIRNAME) */
+
+ return err;
+}
+
+const char *
+atf_fs_path_cstring(const atf_fs_path_t *p)
+{
+ return atf_dynstr_cstring(&p->m_data);
+}
+
+atf_error_t
+atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
+{
+ size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
+ atf_error_t err;
+
+ if (begpos == atf_dynstr_npos)
+ begpos = 0;
+ else
+ begpos++;
+
+ err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
+
+#if defined(HAVE_CONST_BASENAME)
+ INV(atf_equal_dynstr_cstring(ln,
+ basename(atf_dynstr_cstring(&p->m_data))));
+#endif /* defined(HAVE_CONST_BASENAME) */
+
+ return err;
+}
+
+bool
+atf_fs_path_is_absolute(const atf_fs_path_t *p)
+{
+ return atf_dynstr_cstring(&p->m_data)[0] == '/';
+}
+
+bool
+atf_fs_path_is_root(const atf_fs_path_t *p)
+{
+ return atf_equal_dynstr_cstring(&p->m_data, "/");
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
+{
+ atf_dynstr_t aux;
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = normalize_ap(&aux, fmt, ap2);
+ va_end(ap2);
+ if (!atf_is_error(err)) {
+ const char *auxstr = atf_dynstr_cstring(&aux);
+ const bool needslash = auxstr[0] != '/';
+
+ err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
+ needslash ? "/" : "", auxstr);
+
+ atf_dynstr_fini(&aux);
+ }
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_fs_path_append_ap(p, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
+{
+ return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
+}
+
+atf_error_t
+atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
+{
+ atf_error_t err;
+
+ PRE(!atf_fs_path_is_absolute(p));
+
+ err = atf_fs_getcwd(pa);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_fs_path_append_path(pa, p);
+ if (atf_is_error(err))
+ atf_fs_path_fini(pa);
+
+out:
+ return err;
+}
+
+/*
+ * Operators.
+ */
+
+bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
+ const atf_fs_path_t *p2)
+{
+ return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constants.
+ */
+
+const int atf_fs_stat_blk_type = 1;
+const int atf_fs_stat_chr_type = 2;
+const int atf_fs_stat_dir_type = 3;
+const int atf_fs_stat_fifo_type = 4;
+const int atf_fs_stat_lnk_type = 5;
+const int atf_fs_stat_reg_type = 6;
+const int atf_fs_stat_sock_type = 7;
+const int atf_fs_stat_wht_type = 8;
+
+/*
+ * Constructors/destructors.
+ */
+
+atf_error_t
+atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
+{
+ atf_error_t err;
+ const char *pstr = atf_fs_path_cstring(p);
+
+ if (lstat(pstr, &st->m_sb) == -1) {
+ err = atf_libc_error(errno, "Cannot get information of %s; "
+ "lstat(2) failed", pstr);
+ } else {
+ int type = st->m_sb.st_mode & S_IFMT;
+ err = atf_no_error();
+ switch (type) {
+ case S_IFBLK: st->m_type = atf_fs_stat_blk_type; break;
+ case S_IFCHR: st->m_type = atf_fs_stat_chr_type; break;
+ case S_IFDIR: st->m_type = atf_fs_stat_dir_type; break;
+ case S_IFIFO: st->m_type = atf_fs_stat_fifo_type; break;
+ case S_IFLNK: st->m_type = atf_fs_stat_lnk_type; break;
+ case S_IFREG: st->m_type = atf_fs_stat_reg_type; break;
+ case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break;
+#if defined(S_IFWHT)
+ case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break;
+#endif
+ default:
+ err = unknown_type_error(pstr, type);
+ }
+ }
+
+ return err;
+}
+
+void
+atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
+{
+ dest->m_type = src->m_type;
+ dest->m_sb = src->m_sb;
+}
+
+void
+atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+/*
+ * Getters.
+ */
+
+dev_t
+atf_fs_stat_get_device(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_dev;
+}
+
+ino_t
+atf_fs_stat_get_inode(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_ino;
+}
+
+mode_t
+atf_fs_stat_get_mode(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & ~S_IFMT;
+}
+
+off_t
+atf_fs_stat_get_size(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_size;
+}
+
+int
+atf_fs_stat_get_type(const atf_fs_stat_t *st)
+{
+ return st->m_type;
+}
+
+bool
+atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IRUSR;
+}
+
+bool
+atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWUSR;
+}
+
+bool
+atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXUSR;
+}
+
+bool
+atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IRGRP;
+}
+
+bool
+atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWGRP;
+}
+
+bool
+atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXGRP;
+}
+
+bool
+atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IROTH;
+}
+
+bool
+atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWOTH;
+}
+
+bool
+atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXOTH;
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+const int atf_fs_access_f = 1 << 0;
+const int atf_fs_access_r = 1 << 1;
+const int atf_fs_access_w = 1 << 2;
+const int atf_fs_access_x = 1 << 3;
+
+/*
+ * An implementation of access(2) but using the effective user value
+ * instead of the real one. Also avoids false positives for root when
+ * asking for execute permissions, which appear in SunOS.
+ */
+atf_error_t
+atf_fs_eaccess(const atf_fs_path_t *p, int mode)
+{
+ atf_error_t err;
+ struct stat st;
+ bool ok;
+
+ PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
+ mode & atf_fs_access_w || mode & atf_fs_access_x);
+
+ if (lstat(atf_fs_path_cstring(p), &st) == -1) {
+ err = atf_libc_error(errno, "Cannot get information from file %s",
+ atf_fs_path_cstring(p));
+ goto out;
+ }
+
+ err = atf_no_error();
+
+ /* Early return if we are only checking for existence and the file
+ * exists (stat call returned). */
+ if (mode & atf_fs_access_f)
+ goto out;
+
+ ok = false;
+ if (atf_user_is_root()) {
+ if (!ok && !(mode & atf_fs_access_x)) {
+ /* Allow root to read/write any file. */
+ ok = true;
+ }
+
+ if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+ /* Allow root to execute the file if any of its execution bits
+ * are set. */
+ ok = true;
+ }
+ } else {
+ if (!ok && (atf_user_euid() == st.st_uid)) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR));
+ }
+ if (!ok && atf_user_is_member_of_group(st.st_gid)) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP));
+ }
+ if (!ok && ((atf_user_euid() != st.st_uid) &&
+ !atf_user_is_member_of_group(st.st_gid))) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH));
+ }
+ }
+
+ if (!ok)
+ err = atf_libc_error(EACCES, "Access check failed");
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_exists(const atf_fs_path_t *p, bool *b)
+{
+ atf_error_t err;
+
+ err = atf_fs_eaccess(p, atf_fs_access_f);
+ if (atf_is_error(err)) {
+ if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) {
+ atf_error_free(err);
+ err = atf_no_error();
+ *b = false;
+ }
+ } else
+ *b = true;
+
+ return err;
+}
+
+atf_error_t
+atf_fs_getcwd(atf_fs_path_t *p)
+{
+ atf_error_t err;
+ char *cwd;
+
+#if defined(HAVE_GETCWD_DYN)
+ cwd = getcwd(NULL, 0);
+#else
+ cwd = getcwd(NULL, MAXPATHLEN);
+#endif
+ if (cwd == NULL) {
+ err = atf_libc_error(errno, "Cannot determine current directory");
+ goto out;
+ }
+
+ err = atf_fs_path_init_fmt(p, "%s", cwd);
+ free(cwd);
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_mkdtemp(atf_fs_path_t *p)
+{
+ atf_error_t err;
+ char *buf;
+
+ if (!check_umask(S_IRWXU, S_IRWXU)) {
+ err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
+ goto out;
+ }
+
+ err = copy_contents(p, &buf);
+ if (atf_is_error(err))
+ goto out;
+
+ err = do_mkdtemp(buf);
+ if (atf_is_error(err))
+ goto out_buf;
+
+ replace_contents(p, buf);
+
+ INV(!atf_is_error(err));
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
+{
+ atf_error_t err;
+ char *buf;
+ int fd;
+
+ if (!check_umask(S_IRWXU, S_IRWXU)) {
+ err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
+ goto out;
+ }
+
+ err = copy_contents(p, &buf);
+ if (atf_is_error(err))
+ goto out;
+
+ err = do_mkstemp(buf, &fd);
+ if (atf_is_error(err))
+ goto out_buf;
+
+ replace_contents(p, buf);
+ *fdout = fd;
+
+ INV(!atf_is_error(err));
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_rmdir(const atf_fs_path_t *p)
+{
+ atf_error_t err;
+
+ if (rmdir(atf_fs_path_cstring(p))) {
+ if (errno == EEXIST) {
+ /* Some operating systems (e.g. OpenSolaris 200906) return
+ * EEXIST instead of ENOTEMPTY for non-empty directories.
+ * Homogenize the return value so that callers don't need
+ * to bother about differences in operating systems. */
+ errno = ENOTEMPTY;
+ }
+ err = atf_libc_error(errno, "Cannot remove directory");
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+atf_error_t
+atf_fs_unlink(const atf_fs_path_t *p)
+{
+ atf_error_t err;
+ const char *path;
+
+ path = atf_fs_path_cstring(p);
+
+ if (unlink(path) != 0)
+ err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
+ else
+ err = atf_no_error();
+
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/fs.h b/unit/atf-src/atf-c/detail/fs.h
new file mode 100644
index 0000000..1c6c0eb
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/fs.h
@@ -0,0 +1,128 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_FS_H)
+#define ATF_C_DETAIL_FS_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <atf-c/detail/dynstr.h>
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_fs_path {
+ atf_dynstr_t m_data;
+};
+typedef struct atf_fs_path atf_fs_path_t;
+
+/* Constructors/destructors. */
+atf_error_t atf_fs_path_init_ap(atf_fs_path_t *, const char *, va_list);
+atf_error_t atf_fs_path_init_fmt(atf_fs_path_t *, const char *, ...);
+atf_error_t atf_fs_path_copy(atf_fs_path_t *, const atf_fs_path_t *);
+void atf_fs_path_fini(atf_fs_path_t *);
+
+/* Getters. */
+atf_error_t atf_fs_path_branch_path(const atf_fs_path_t *, atf_fs_path_t *);
+const char *atf_fs_path_cstring(const atf_fs_path_t *);
+atf_error_t atf_fs_path_leaf_name(const atf_fs_path_t *, atf_dynstr_t *);
+bool atf_fs_path_is_absolute(const atf_fs_path_t *);
+bool atf_fs_path_is_root(const atf_fs_path_t *);
+
+/* Modifiers. */
+atf_error_t atf_fs_path_append_ap(atf_fs_path_t *, const char *, va_list);
+atf_error_t atf_fs_path_append_fmt(atf_fs_path_t *, const char *, ...);
+atf_error_t atf_fs_path_append_path(atf_fs_path_t *, const atf_fs_path_t *);
+atf_error_t atf_fs_path_to_absolute(const atf_fs_path_t *, atf_fs_path_t *);
+
+/* Operators. */
+bool atf_equal_fs_path_fs_path(const atf_fs_path_t *,
+ const atf_fs_path_t *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_stat" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_fs_stat {
+ int m_type;
+ struct stat m_sb;
+};
+typedef struct atf_fs_stat atf_fs_stat_t;
+
+/* Constants. */
+extern const int atf_fs_stat_blk_type;
+extern const int atf_fs_stat_chr_type;
+extern const int atf_fs_stat_dir_type;
+extern const int atf_fs_stat_fifo_type;
+extern const int atf_fs_stat_lnk_type;
+extern const int atf_fs_stat_reg_type;
+extern const int atf_fs_stat_sock_type;
+extern const int atf_fs_stat_wht_type;
+
+/* Constructors/destructors. */
+atf_error_t atf_fs_stat_init(atf_fs_stat_t *, const atf_fs_path_t *);
+void atf_fs_stat_copy(atf_fs_stat_t *, const atf_fs_stat_t *);
+void atf_fs_stat_fini(atf_fs_stat_t *);
+
+/* Getters. */
+dev_t atf_fs_stat_get_device(const atf_fs_stat_t *);
+ino_t atf_fs_stat_get_inode(const atf_fs_stat_t *);
+mode_t atf_fs_stat_get_mode(const atf_fs_stat_t *);
+off_t atf_fs_stat_get_size(const atf_fs_stat_t *);
+int atf_fs_stat_get_type(const atf_fs_stat_t *);
+bool atf_fs_stat_is_owner_readable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_owner_writable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_owner_executable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_group_readable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_group_writable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_group_executable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_other_readable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_other_writable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_other_executable(const atf_fs_stat_t *);
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+extern const int atf_fs_access_f;
+extern const int atf_fs_access_r;
+extern const int atf_fs_access_w;
+extern const int atf_fs_access_x;
+
+atf_error_t atf_fs_eaccess(const atf_fs_path_t *, int);
+atf_error_t atf_fs_exists(const atf_fs_path_t *, bool *);
+atf_error_t atf_fs_getcwd(atf_fs_path_t *);
+atf_error_t atf_fs_mkdtemp(atf_fs_path_t *);
+atf_error_t atf_fs_mkstemp(atf_fs_path_t *, int *);
+atf_error_t atf_fs_rmdir(const atf_fs_path_t *);
+atf_error_t atf_fs_unlink(const atf_fs_path_t *);
+
+#endif /* !defined(ATF_C_DETAIL_FS_H) */
diff --git a/unit/atf-src/atf-c/detail/fs_test.c b/unit/atf-src/atf-c/detail/fs_test.c
new file mode 100644
index 0000000..3dbc4d3
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/fs_test.c
@@ -0,0 +1,1079 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/fs.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/detail/user.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+create_dir(const char *p, int mode)
+{
+ int ret;
+
+ ret = mkdir(p, mode);
+ if (ret == -1)
+ atf_tc_fail("Could not create helper directory %s", p);
+}
+
+static
+void
+create_file(const char *p, int mode)
+{
+ int fd;
+
+ fd = open(p, O_CREAT | O_WRONLY | O_TRUNC, mode);
+ if (fd == -1)
+ atf_tc_fail("Could not create helper file %s", p);
+ close(fd);
+}
+
+static
+bool
+exists(const atf_fs_path_t *p)
+{
+ return access(atf_fs_path_cstring(p), F_OK) == 0;
+}
+
+static
+atf_error_t
+mkstemp_discard_fd(atf_fs_path_t *p)
+{
+ int fd;
+ atf_error_t err = atf_fs_mkstemp(p, &fd);
+ if (!atf_is_error(err))
+ close(fd);
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(path_normalize);
+ATF_TC_HEAD(path_normalize, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the path's normalization");
+}
+ATF_TC_BODY(path_normalize, tc)
+{
+ struct test {
+ const char *in;
+ const char *out;
+ } tests[] = {
+ { ".", ".", },
+ { "..", "..", },
+
+ { "/", "/", },
+ { "//", "/", }, /* NO_CHECK_STYLE */
+ { "///", "/", }, /* NO_CHECK_STYLE */
+
+ { "foo", "foo", },
+ { "foo/", "foo", },
+ { "foo/bar", "foo/bar", },
+ { "foo/bar/", "foo/bar", },
+
+ { "/foo", "/foo", },
+ { "/foo/bar", "/foo/bar", },
+ { "/foo/bar/", "/foo/bar", },
+
+ { "///foo", "/foo", }, /* NO_CHECK_STYLE */
+ { "///foo///bar", "/foo/bar", }, /* NO_CHECK_STYLE */
+ { "///foo///bar///", "/foo/bar", }, /* NO_CHECK_STYLE */
+
+ { NULL, NULL }
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+
+ printf("Input : >%s<\n", t->in);
+ printf("Expected output: >%s<\n", t->out);
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ printf("Output : >%s<\n", atf_fs_path_cstring(&p));
+ ATF_REQUIRE(strcmp(atf_fs_path_cstring(&p), t->out) == 0);
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_copy);
+ATF_TC_HEAD(path_copy, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_copy constructor");
+}
+ATF_TC_BODY(path_copy, tc)
+{
+ atf_fs_path_t str, str2;
+
+ RE(atf_fs_path_init_fmt(&str, "foo"));
+ RE(atf_fs_path_copy(&str2, &str));
+
+ ATF_REQUIRE(atf_equal_fs_path_fs_path(&str, &str2));
+
+ RE(atf_fs_path_append_fmt(&str2, "bar"));
+
+ ATF_REQUIRE(!atf_equal_fs_path_fs_path(&str, &str2));
+
+ atf_fs_path_fini(&str2);
+ atf_fs_path_fini(&str);
+}
+
+ATF_TC(path_is_absolute);
+ATF_TC_HEAD(path_is_absolute, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the path::is_absolute function");
+}
+ATF_TC_BODY(path_is_absolute, tc)
+{
+ struct test {
+ const char *in;
+ bool abs;
+ } tests[] = {
+ { "/", true },
+ { "////", true }, /* NO_CHECK_STYLE */
+ { "////a", true }, /* NO_CHECK_STYLE */
+ { "//a//", true }, /* NO_CHECK_STYLE */
+ { "a////", false }, /* NO_CHECK_STYLE */
+ { "../foo", false },
+ { NULL, false },
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+
+ printf("Input : %s\n", t->in);
+ printf("Expected result: %s\n", t->abs ? "true" : "false");
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ printf("Result : %s\n",
+ atf_fs_path_is_absolute(&p) ? "true" : "false");
+ if (t->abs)
+ ATF_REQUIRE(atf_fs_path_is_absolute(&p));
+ else
+ ATF_REQUIRE(!atf_fs_path_is_absolute(&p));
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_is_root);
+ATF_TC_HEAD(path_is_root, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the path::is_root function");
+}
+ATF_TC_BODY(path_is_root, tc)
+{
+ struct test {
+ const char *in;
+ bool root;
+ } tests[] = {
+ { "/", true },
+ { "////", true }, /* NO_CHECK_STYLE */
+ { "////a", false }, /* NO_CHECK_STYLE */
+ { "//a//", false }, /* NO_CHECK_STYLE */
+ { "a////", false }, /* NO_CHECK_STYLE */
+ { "../foo", false },
+ { NULL, false },
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+
+ printf("Input : %s\n", t->in);
+ printf("Expected result: %s\n", t->root ? "true" : "false");
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ printf("Result : %s\n",
+ atf_fs_path_is_root(&p) ? "true" : "false");
+ if (t->root)
+ ATF_REQUIRE(atf_fs_path_is_root(&p));
+ else
+ ATF_REQUIRE(!atf_fs_path_is_root(&p));
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_branch_path);
+ATF_TC_HEAD(path_branch_path, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_branch_path "
+ "function");
+}
+ATF_TC_BODY(path_branch_path, tc)
+{
+ struct test {
+ const char *in;
+ const char *branch;
+ } tests[] = {
+ { ".", "." },
+ { "foo", "." },
+ { "foo/bar", "foo" },
+ { "/foo", "/" },
+ { "/foo/bar", "/foo" },
+ { NULL, NULL },
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p, bp;
+
+ printf("Input : %s\n", t->in);
+ printf("Expected output: %s\n", t->branch);
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ RE(atf_fs_path_branch_path(&p, &bp));
+ printf("Output : %s\n", atf_fs_path_cstring(&bp));
+ ATF_REQUIRE(strcmp(atf_fs_path_cstring(&bp), t->branch) == 0);
+ atf_fs_path_fini(&bp);
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_leaf_name);
+ATF_TC_HEAD(path_leaf_name, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_leaf_name "
+ "function");
+}
+ATF_TC_BODY(path_leaf_name, tc)
+{
+ struct test {
+ const char *in;
+ const char *leaf;
+ } tests[] = {
+ { ".", "." },
+ { "foo", "foo" },
+ { "foo/bar", "bar" },
+ { "/foo", "foo" },
+ { "/foo/bar", "bar" },
+ { NULL, NULL },
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+ atf_dynstr_t ln;
+
+ printf("Input : %s\n", t->in);
+ printf("Expected output: %s\n", t->leaf);
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ RE(atf_fs_path_leaf_name(&p, &ln));
+ printf("Output : %s\n", atf_dynstr_cstring(&ln));
+ ATF_REQUIRE(atf_equal_dynstr_cstring(&ln, t->leaf));
+ atf_dynstr_fini(&ln);
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_append);
+ATF_TC_HEAD(path_append, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the concatenation of multiple "
+ "paths");
+}
+ATF_TC_BODY(path_append, tc)
+{
+ struct test {
+ const char *in;
+ const char *ap;
+ const char *out;
+ } tests[] = {
+ { "foo", "bar", "foo/bar" },
+ { "foo/", "/bar", "foo/bar" },
+ { "foo/", "/bar/baz", "foo/bar/baz" },
+ { "foo/", "///bar///baz", "foo/bar/baz" }, /* NO_CHECK_STYLE */
+
+ { NULL, NULL, NULL }
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+
+ printf("Input : >%s<\n", t->in);
+ printf("Append : >%s<\n", t->ap);
+ printf("Expected output: >%s<\n", t->out);
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+
+ RE(atf_fs_path_append_fmt(&p, "%s", t->ap));
+
+ printf("Output : >%s<\n", atf_fs_path_cstring(&p));
+ ATF_REQUIRE(strcmp(atf_fs_path_cstring(&p), t->out) == 0);
+
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_to_absolute);
+ATF_TC_HEAD(path_to_absolute, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_to_absolute "
+ "function");
+}
+ATF_TC_BODY(path_to_absolute, tc)
+{
+ const char *names[] = { ".", "dir", NULL };
+ const char **n;
+
+ ATF_REQUIRE(mkdir("dir", 0755) != -1);
+
+ for (n = names; *n != NULL; n++) {
+ atf_fs_path_t p, p2;
+ atf_fs_stat_t st1, st2;
+
+ RE(atf_fs_path_init_fmt(&p, "%s", *n));
+ RE(atf_fs_stat_init(&st1, &p));
+ printf("Relative path: %s\n", atf_fs_path_cstring(&p));
+
+ RE(atf_fs_path_to_absolute(&p, &p2));
+ printf("Absolute path: %s\n", atf_fs_path_cstring(&p2));
+
+ ATF_REQUIRE(atf_fs_path_is_absolute(&p2));
+ RE(atf_fs_stat_init(&st2, &p2));
+
+ ATF_REQUIRE_EQ(atf_fs_stat_get_device(&st1),
+ atf_fs_stat_get_device(&st2));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_inode(&st1),
+ atf_fs_stat_get_inode(&st2));
+
+ atf_fs_stat_fini(&st2);
+ atf_fs_stat_fini(&st1);
+ atf_fs_path_fini(&p2);
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_equal);
+ATF_TC_HEAD(path_equal, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the equality operators for paths");
+}
+ATF_TC_BODY(path_equal, tc)
+{
+ atf_fs_path_t p1, p2;
+
+ RE(atf_fs_path_init_fmt(&p1, "foo"));
+
+ RE(atf_fs_path_init_fmt(&p2, "foo"));
+ ATF_REQUIRE(atf_equal_fs_path_fs_path(&p1, &p2));
+ atf_fs_path_fini(&p2);
+
+ RE(atf_fs_path_init_fmt(&p2, "bar"));
+ ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2));
+ atf_fs_path_fini(&p2);
+
+ atf_fs_path_fini(&p1);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "atf_fs_stat" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(stat_mode);
+ATF_TC_HEAD(stat_mode, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_get_mode function "
+ "and, indirectly, the constructor");
+}
+ATF_TC_BODY(stat_mode, tc)
+{
+ atf_fs_path_t p;
+ atf_fs_stat_t st;
+
+ create_file("f1", 0400);
+ create_file("f2", 0644);
+
+ RE(atf_fs_path_init_fmt(&p, "f1"));
+ RE(atf_fs_stat_init(&st, &p));
+ ATF_CHECK_EQ(0400, atf_fs_stat_get_mode(&st));
+ atf_fs_stat_fini(&st);
+ atf_fs_path_fini(&p);
+
+ RE(atf_fs_path_init_fmt(&p, "f2"));
+ RE(atf_fs_stat_init(&st, &p));
+ ATF_CHECK_EQ(0644, atf_fs_stat_get_mode(&st));
+ atf_fs_stat_fini(&st);
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(stat_type);
+ATF_TC_HEAD(stat_type, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_get_type function "
+ "and, indirectly, the constructor");
+}
+ATF_TC_BODY(stat_type, tc)
+{
+ atf_fs_path_t p;
+ atf_fs_stat_t st;
+
+ create_dir("dir", 0755);
+ create_file("reg", 0644);
+
+ RE(atf_fs_path_init_fmt(&p, "dir"));
+ RE(atf_fs_stat_init(&st, &p));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_type(&st), atf_fs_stat_dir_type);
+ atf_fs_stat_fini(&st);
+ atf_fs_path_fini(&p);
+
+ RE(atf_fs_path_init_fmt(&p, "reg"));
+ RE(atf_fs_stat_init(&st, &p));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_type(&st), atf_fs_stat_reg_type);
+ atf_fs_stat_fini(&st);
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(stat_perms);
+ATF_TC_HEAD(stat_perms, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_is_* functions");
+}
+ATF_TC_BODY(stat_perms, tc)
+{
+ atf_fs_path_t p;
+ atf_fs_stat_t st;
+
+ create_file("reg", 0);
+
+ RE(atf_fs_path_init_fmt(&p, "reg"));
+
+#define perms(ur, uw, ux, gr, gw, gx, othr, othw, othx) \
+ { \
+ RE(atf_fs_stat_init(&st, &p)); \
+ ATF_REQUIRE(atf_fs_stat_is_owner_readable(&st) == ur); \
+ ATF_REQUIRE(atf_fs_stat_is_owner_writable(&st) == uw); \
+ ATF_REQUIRE(atf_fs_stat_is_owner_executable(&st) == ux); \
+ ATF_REQUIRE(atf_fs_stat_is_group_readable(&st) == gr); \
+ ATF_REQUIRE(atf_fs_stat_is_group_writable(&st) == gw); \
+ ATF_REQUIRE(atf_fs_stat_is_group_executable(&st) == gx); \
+ ATF_REQUIRE(atf_fs_stat_is_other_readable(&st) == othr); \
+ ATF_REQUIRE(atf_fs_stat_is_other_writable(&st) == othw); \
+ ATF_REQUIRE(atf_fs_stat_is_other_executable(&st) == othx); \
+ atf_fs_stat_fini(&st); \
+ }
+
+ chmod("reg", 0000);
+ perms(false, false, false, false, false, false, false, false, false);
+
+ chmod("reg", 0001);
+ perms(false, false, false, false, false, false, false, false, true);
+
+ chmod("reg", 0010);
+ perms(false, false, false, false, false, true, false, false, false);
+
+ chmod("reg", 0100);
+ perms(false, false, true, false, false, false, false, false, false);
+
+ chmod("reg", 0002);
+ perms(false, false, false, false, false, false, false, true, false);
+
+ chmod("reg", 0020);
+ perms(false, false, false, false, true, false, false, false, false);
+
+ chmod("reg", 0200);
+ perms(false, true, false, false, false, false, false, false, false);
+
+ chmod("reg", 0004);
+ perms(false, false, false, false, false, false, true, false, false);
+
+ chmod("reg", 0040);
+ perms(false, false, false, true, false, false, false, false, false);
+
+ chmod("reg", 0400);
+ perms(true, false, false, false, false, false, false, false, false);
+
+ chmod("reg", 0644);
+ perms(true, true, false, true, false, false, true, false, false);
+
+ chmod("reg", 0755);
+ perms(true, true, true, true, false, true, true, false, true);
+
+ chmod("reg", 0777);
+ perms(true, true, true, true, true, true, true, true, true);
+
+#undef perms
+
+ atf_fs_path_fini(&p);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(exists);
+ATF_TC_HEAD(exists, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_exists function");
+}
+ATF_TC_BODY(exists, tc)
+{
+ atf_error_t err;
+ atf_fs_path_t pdir, pfile;
+ bool b;
+
+ RE(atf_fs_path_init_fmt(&pdir, "dir"));
+ RE(atf_fs_path_init_fmt(&pfile, "dir/file"));
+
+ create_dir(atf_fs_path_cstring(&pdir), 0755);
+ create_file(atf_fs_path_cstring(&pfile), 0644);
+
+ printf("Checking existence of a directory\n");
+ RE(atf_fs_exists(&pdir, &b));
+ ATF_REQUIRE(b);
+
+ printf("Checking existence of a file\n");
+ RE(atf_fs_exists(&pfile, &b));
+ ATF_REQUIRE(b);
+
+ /* XXX: This should probably be a separate test case to let the user
+ * be aware that some tests were skipped because privileges were not
+ * correct. */
+ if (!atf_user_is_root()) {
+ printf("Checking existence of a file inside a directory without "
+ "permissions\n");
+ ATF_REQUIRE(chmod(atf_fs_path_cstring(&pdir), 0000) != -1);
+ err = atf_fs_exists(&pfile, &b);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE(chmod(atf_fs_path_cstring(&pdir), 0755) != -1);
+ atf_error_free(err);
+ }
+
+ printf("Checking existence of a non-existent file\n");
+ ATF_REQUIRE(unlink(atf_fs_path_cstring(&pfile)) != -1);
+ RE(atf_fs_exists(&pfile, &b));
+ ATF_REQUIRE(!b);
+
+ atf_fs_path_fini(&pfile);
+ atf_fs_path_fini(&pdir);
+}
+
+ATF_TC(eaccess);
+ATF_TC_HEAD(eaccess, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_eaccess function");
+}
+ATF_TC_BODY(eaccess, tc)
+{
+ const int modes[] = { atf_fs_access_f, atf_fs_access_r, atf_fs_access_w,
+ atf_fs_access_x, 0 };
+ const int *m;
+ struct tests {
+ mode_t fmode;
+ int amode;
+ int uerror;
+ int rerror;
+ } tests[] = {
+ { 0000, atf_fs_access_r, EACCES, 0 },
+ { 0000, atf_fs_access_w, EACCES, 0 },
+ { 0000, atf_fs_access_x, EACCES, EACCES },
+
+ { 0001, atf_fs_access_r, EACCES, 0 },
+ { 0001, atf_fs_access_w, EACCES, 0 },
+ { 0001, atf_fs_access_x, EACCES, 0 },
+ { 0002, atf_fs_access_r, EACCES, 0 },
+ { 0002, atf_fs_access_w, EACCES, 0 },
+ { 0002, atf_fs_access_x, EACCES, EACCES },
+ { 0004, atf_fs_access_r, EACCES, 0 },
+ { 0004, atf_fs_access_w, EACCES, 0 },
+ { 0004, atf_fs_access_x, EACCES, EACCES },
+
+ { 0010, atf_fs_access_r, EACCES, 0 },
+ { 0010, atf_fs_access_w, EACCES, 0 },
+ { 0010, atf_fs_access_x, 0, 0 },
+ { 0020, atf_fs_access_r, EACCES, 0 },
+ { 0020, atf_fs_access_w, 0, 0 },
+ { 0020, atf_fs_access_x, EACCES, EACCES },
+ { 0040, atf_fs_access_r, 0, 0 },
+ { 0040, atf_fs_access_w, EACCES, 0 },
+ { 0040, atf_fs_access_x, EACCES, EACCES },
+
+ { 0100, atf_fs_access_r, EACCES, 0 },
+ { 0100, atf_fs_access_w, EACCES, 0 },
+ { 0100, atf_fs_access_x, 0, 0 },
+ { 0200, atf_fs_access_r, EACCES, 0 },
+ { 0200, atf_fs_access_w, 0, 0 },
+ { 0200, atf_fs_access_x, EACCES, EACCES },
+ { 0400, atf_fs_access_r, 0, 0 },
+ { 0400, atf_fs_access_w, EACCES, 0 },
+ { 0400, atf_fs_access_x, EACCES, EACCES },
+
+ { 0, 0, 0, 0 }
+ };
+ struct tests *t;
+ atf_fs_path_t p;
+ atf_error_t err;
+
+ RE(atf_fs_path_init_fmt(&p, "the-file"));
+
+ printf("Non-existent file checks\n");
+ for (m = &modes[0]; *m != 0; m++) {
+ err = atf_fs_eaccess(&p, *m);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOENT);
+ atf_error_free(err);
+ }
+
+ create_file(atf_fs_path_cstring(&p), 0000);
+ ATF_REQUIRE(chown(atf_fs_path_cstring(&p), geteuid(), getegid()) != -1);
+
+ for (t = &tests[0]; t->amode != 0; t++) {
+ const int experr = atf_user_is_root() ? t->rerror : t->uerror;
+
+ printf("\n");
+ printf("File mode : %04o\n", (unsigned int)t->fmode);
+ printf("Access mode : 0x%02x\n", t->amode);
+
+ ATF_REQUIRE(chmod(atf_fs_path_cstring(&p), t->fmode) != -1);
+
+ /* First, existence check. */
+ err = atf_fs_eaccess(&p, atf_fs_access_f);
+ ATF_REQUIRE(!atf_is_error(err));
+
+ /* Now do the specific test case. */
+ printf("Expected error: %d\n", experr);
+ err = atf_fs_eaccess(&p, t->amode);
+ if (atf_is_error(err)) {
+ if (atf_error_is(err, "libc"))
+ printf("Error : %d\n", atf_libc_error_code(err));
+ else
+ printf("Error : Non-libc error\n");
+ } else
+ printf("Error : None\n");
+ if (experr == 0) {
+ ATF_REQUIRE(!atf_is_error(err));
+ } else {
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), experr);
+ atf_error_free(err);
+ }
+ }
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(getcwd);
+ATF_TC_HEAD(getcwd, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_getcwd function");
+}
+ATF_TC_BODY(getcwd, tc)
+{
+ atf_fs_path_t cwd1, cwd2;
+
+ create_dir ("root", 0755);
+
+ RE(atf_fs_getcwd(&cwd1));
+ ATF_REQUIRE(chdir("root") != -1);
+ RE(atf_fs_getcwd(&cwd2));
+
+ RE(atf_fs_path_append_fmt(&cwd1, "root"));
+
+ ATF_REQUIRE(atf_equal_fs_path_fs_path(&cwd1, &cwd2));
+
+ atf_fs_path_fini(&cwd2);
+ atf_fs_path_fini(&cwd1);
+}
+
+ATF_TC(rmdir_empty);
+ATF_TC_HEAD(rmdir_empty, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function");
+}
+ATF_TC_BODY(rmdir_empty, tc)
+{
+ atf_fs_path_t p;
+
+ RE(atf_fs_path_init_fmt(&p, "test-dir"));
+
+ ATF_REQUIRE(mkdir("test-dir", 0755) != -1);
+ ATF_REQUIRE(exists(&p));
+ RE(atf_fs_rmdir(&p));
+ ATF_REQUIRE(!exists(&p));
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(rmdir_enotempty);
+ATF_TC_HEAD(rmdir_enotempty, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function");
+}
+ATF_TC_BODY(rmdir_enotempty, tc)
+{
+ atf_fs_path_t p;
+ atf_error_t err;
+
+ RE(atf_fs_path_init_fmt(&p, "test-dir"));
+
+ ATF_REQUIRE(mkdir("test-dir", 0755) != -1);
+ ATF_REQUIRE(exists(&p));
+ create_file("test-dir/foo", 0644);
+
+ err = atf_fs_rmdir(&p);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOTEMPTY);
+ atf_error_free(err);
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(rmdir_eperm);
+ATF_TC_HEAD(rmdir_eperm, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function");
+}
+ATF_TC_BODY(rmdir_eperm, tc)
+{
+ atf_fs_path_t p;
+ atf_error_t err;
+
+ RE(atf_fs_path_init_fmt(&p, "test-dir/foo"));
+
+ ATF_REQUIRE(mkdir("test-dir", 0755) != -1);
+ ATF_REQUIRE(mkdir("test-dir/foo", 0755) != -1);
+ ATF_REQUIRE(chmod("test-dir", 0555) != -1);
+ ATF_REQUIRE(exists(&p));
+
+ err = atf_fs_rmdir(&p);
+ if (atf_user_is_root()) {
+ ATF_REQUIRE(!atf_is_error(err));
+ } else {
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), EACCES);
+ atf_error_free(err);
+ }
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(mkdtemp_ok);
+ATF_TC_HEAD(mkdtemp_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function, "
+ "successful execution");
+}
+ATF_TC_BODY(mkdtemp_ok, tc)
+{
+ atf_fs_path_t p1, p2;
+ atf_fs_stat_t s1, s2;
+
+ RE(atf_fs_path_init_fmt(&p1, "testdir.XXXXXX"));
+ RE(atf_fs_path_init_fmt(&p2, "testdir.XXXXXX"));
+ RE(atf_fs_mkdtemp(&p1));
+ RE(atf_fs_mkdtemp(&p2));
+ ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2));
+ ATF_REQUIRE(exists(&p1));
+ ATF_REQUIRE(exists(&p2));
+
+ RE(atf_fs_stat_init(&s1, &p1));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_type(&s1), atf_fs_stat_dir_type);
+ ATF_REQUIRE( atf_fs_stat_is_owner_readable(&s1));
+ ATF_REQUIRE( atf_fs_stat_is_owner_writable(&s1));
+ ATF_REQUIRE( atf_fs_stat_is_owner_executable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_group_readable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_group_writable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_group_executable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_other_readable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_other_writable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_other_executable(&s1));
+
+ RE(atf_fs_stat_init(&s2, &p2));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_type(&s2), atf_fs_stat_dir_type);
+ ATF_REQUIRE( atf_fs_stat_is_owner_readable(&s2));
+ ATF_REQUIRE( atf_fs_stat_is_owner_writable(&s2));
+ ATF_REQUIRE( atf_fs_stat_is_owner_executable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_group_readable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_group_writable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_group_executable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_other_readable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_other_writable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_other_executable(&s2));
+
+ atf_fs_stat_fini(&s2);
+ atf_fs_stat_fini(&s1);
+ atf_fs_path_fini(&p2);
+ atf_fs_path_fini(&p1);
+}
+
+ATF_TC(mkdtemp_err);
+ATF_TC_HEAD(mkdtemp_err, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function, "
+ "error conditions");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(mkdtemp_err, tc)
+{
+ atf_error_t err;
+ atf_fs_path_t p;
+
+ ATF_REQUIRE(mkdir("dir", 0555) != -1);
+
+ RE(atf_fs_path_init_fmt(&p, "dir/testdir.XXXXXX"));
+
+ err = atf_fs_mkdtemp(&p);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_CHECK_EQ(atf_libc_error_code(err), EACCES);
+ atf_error_free(err);
+
+ ATF_CHECK(!exists(&p));
+ ATF_CHECK(strcmp(atf_fs_path_cstring(&p), "dir/testdir.XXXXXX") == 0);
+
+ atf_fs_path_fini(&p);
+}
+
+static
+void
+do_umask_check(atf_error_t (*const mk_func)(atf_fs_path_t *),
+ atf_fs_path_t *path, const mode_t test_mask,
+ const char *str_mask, const char *exp_name)
+{
+ char buf[1024];
+ int old_umask;
+ atf_error_t err;
+
+ printf("Creating temporary %s with umask %s\n", exp_name, str_mask);
+
+ old_umask = umask(test_mask);
+ err = mk_func(path);
+ (void)umask(old_umask);
+
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "invalid_umask"));
+ atf_error_format(err, buf, sizeof(buf));
+ ATF_CHECK(strstr(buf, exp_name) != NULL);
+ ATF_CHECK(strstr(buf, str_mask) != NULL);
+ atf_error_free(err);
+}
+
+ATF_TC(mkdtemp_umask);
+ATF_TC_HEAD(mkdtemp_umask, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function "
+ "causing an error due to a too strict umask");
+}
+ATF_TC_BODY(mkdtemp_umask, tc)
+{
+ atf_fs_path_t p;
+
+ RE(atf_fs_path_init_fmt(&p, "testdir.XXXXXX"));
+
+ do_umask_check(atf_fs_mkdtemp, &p, 00100, "00100", "directory");
+ do_umask_check(atf_fs_mkdtemp, &p, 00200, "00200", "directory");
+ do_umask_check(atf_fs_mkdtemp, &p, 00400, "00400", "directory");
+ do_umask_check(atf_fs_mkdtemp, &p, 00500, "00500", "directory");
+ do_umask_check(atf_fs_mkdtemp, &p, 00600, "00600", "directory");
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(mkstemp_ok);
+ATF_TC_HEAD(mkstemp_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function, "
+ "successful execution");
+}
+ATF_TC_BODY(mkstemp_ok, tc)
+{
+ int fd1, fd2;
+ atf_fs_path_t p1, p2;
+ atf_fs_stat_t s1, s2;
+
+ RE(atf_fs_path_init_fmt(&p1, "testfile.XXXXXX"));
+ RE(atf_fs_path_init_fmt(&p2, "testfile.XXXXXX"));
+ fd1 = fd2 = -1;
+ RE(atf_fs_mkstemp(&p1, &fd1));
+ RE(atf_fs_mkstemp(&p2, &fd2));
+ ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2));
+ ATF_REQUIRE(exists(&p1));
+ ATF_REQUIRE(exists(&p2));
+
+ ATF_CHECK(fd1 != -1);
+ ATF_CHECK(fd2 != -1);
+ ATF_CHECK(write(fd1, "foo", 3) == 3);
+ ATF_CHECK(write(fd2, "bar", 3) == 3);
+ close(fd1);
+ close(fd2);
+
+ RE(atf_fs_stat_init(&s1, &p1));
+ ATF_CHECK_EQ(atf_fs_stat_get_type(&s1), atf_fs_stat_reg_type);
+ ATF_CHECK( atf_fs_stat_is_owner_readable(&s1));
+ ATF_CHECK( atf_fs_stat_is_owner_writable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_owner_executable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_group_readable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_group_writable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_group_executable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_other_readable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_other_writable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_other_executable(&s1));
+
+ RE(atf_fs_stat_init(&s2, &p2));
+ ATF_CHECK_EQ(atf_fs_stat_get_type(&s2), atf_fs_stat_reg_type);
+ ATF_CHECK( atf_fs_stat_is_owner_readable(&s2));
+ ATF_CHECK( atf_fs_stat_is_owner_writable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_owner_executable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_group_readable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_group_writable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_group_executable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_other_readable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_other_writable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_other_executable(&s2));
+
+ atf_fs_stat_fini(&s2);
+ atf_fs_stat_fini(&s1);
+ atf_fs_path_fini(&p2);
+ atf_fs_path_fini(&p1);
+}
+
+ATF_TC(mkstemp_err);
+ATF_TC_HEAD(mkstemp_err, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function, "
+ "error conditions");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(mkstemp_err, tc)
+{
+ int fd;
+ atf_error_t err;
+ atf_fs_path_t p;
+
+ ATF_REQUIRE(mkdir("dir", 0555) != -1);
+
+ RE(atf_fs_path_init_fmt(&p, "dir/testfile.XXXXXX"));
+ fd = 1234;
+
+ err = atf_fs_mkstemp(&p, &fd);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_CHECK_EQ(atf_libc_error_code(err), EACCES);
+ atf_error_free(err);
+
+ ATF_CHECK(!exists(&p));
+ ATF_CHECK(strcmp(atf_fs_path_cstring(&p), "dir/testfile.XXXXXX") == 0);
+ ATF_CHECK_EQ(fd, 1234);
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(mkstemp_umask);
+ATF_TC_HEAD(mkstemp_umask, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function "
+ "causing an error due to a too strict umask");
+}
+ATF_TC_BODY(mkstemp_umask, tc)
+{
+ atf_fs_path_t p;
+
+ RE(atf_fs_path_init_fmt(&p, "testfile.XXXXXX"));
+
+ do_umask_check(mkstemp_discard_fd, &p, 00100, "00100", "regular file");
+ do_umask_check(mkstemp_discard_fd, &p, 00200, "00200", "regular file");
+ do_umask_check(mkstemp_discard_fd, &p, 00400, "00400", "regular file");
+
+ atf_fs_path_fini(&p);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the tests for the "atf_fs_path" type. */
+ ATF_TP_ADD_TC(tp, path_normalize);
+ ATF_TP_ADD_TC(tp, path_copy);
+ ATF_TP_ADD_TC(tp, path_is_absolute);
+ ATF_TP_ADD_TC(tp, path_is_root);
+ ATF_TP_ADD_TC(tp, path_branch_path);
+ ATF_TP_ADD_TC(tp, path_leaf_name);
+ ATF_TP_ADD_TC(tp, path_append);
+ ATF_TP_ADD_TC(tp, path_to_absolute);
+ ATF_TP_ADD_TC(tp, path_equal);
+
+ /* Add the tests for the "atf_fs_stat" type. */
+ ATF_TP_ADD_TC(tp, stat_mode);
+ ATF_TP_ADD_TC(tp, stat_type);
+ ATF_TP_ADD_TC(tp, stat_perms);
+
+ /* Add the tests for the free functions. */
+ ATF_TP_ADD_TC(tp, eaccess);
+ ATF_TP_ADD_TC(tp, exists);
+ ATF_TP_ADD_TC(tp, getcwd);
+ ATF_TP_ADD_TC(tp, rmdir_empty);
+ ATF_TP_ADD_TC(tp, rmdir_enotempty);
+ ATF_TP_ADD_TC(tp, rmdir_eperm);
+ ATF_TP_ADD_TC(tp, mkdtemp_ok);
+ ATF_TP_ADD_TC(tp, mkdtemp_err);
+ ATF_TP_ADD_TC(tp, mkdtemp_umask);
+ ATF_TP_ADD_TC(tp, mkstemp_ok);
+ ATF_TP_ADD_TC(tp, mkstemp_err);
+ ATF_TP_ADD_TC(tp, mkstemp_umask);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/list.c b/unit/atf-src/atf-c/detail/list.c
new file mode 100644
index 0000000..d14216e
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/list.c
@@ -0,0 +1,388 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/list.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+struct list_entry {
+ struct list_entry *m_prev;
+ struct list_entry *m_next;
+ void *m_object;
+ bool m_managed;
+};
+
+static
+atf_list_citer_t
+entry_to_citer(const atf_list_t *l, const struct list_entry *le)
+{
+ atf_list_citer_t iter;
+ iter.m_list = l;
+ iter.m_entry = le;
+ return iter;
+}
+
+static
+atf_list_iter_t
+entry_to_iter(atf_list_t *l, struct list_entry *le)
+{
+ atf_list_iter_t iter;
+ iter.m_list = l;
+ iter.m_entry = le;
+ return iter;
+}
+
+static
+struct list_entry *
+new_entry(void *object, bool managed)
+{
+ struct list_entry *le;
+
+ le = (struct list_entry *)malloc(sizeof(*le));
+ if (le != NULL) {
+ le->m_prev = le->m_next = NULL;
+ le->m_object = object;
+ le->m_managed = managed;
+ } else
+ free(object);
+
+ return le;
+}
+
+static
+void
+delete_entry(struct list_entry *le)
+{
+ if (le->m_managed)
+ free(le->m_object);
+
+ free(le);
+}
+
+static
+struct list_entry *
+new_entry_and_link(void *object, bool managed, struct list_entry *prev,
+ struct list_entry *next)
+{
+ struct list_entry *le;
+
+ le = new_entry(object, managed);
+ if (le != NULL) {
+ le->m_prev = prev;
+ le->m_next = next;
+
+ prev->m_next = le;
+ next->m_prev = le;
+ }
+
+ return le;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_list_citer" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Getters.
+ */
+
+const void *
+atf_list_citer_data(const atf_list_citer_t citer)
+{
+ const struct list_entry *le = citer.m_entry;
+ PRE(le != NULL);
+ return le->m_object;
+}
+
+atf_list_citer_t
+atf_list_citer_next(const atf_list_citer_t citer)
+{
+ const struct list_entry *le = citer.m_entry;
+ atf_list_citer_t newciter;
+
+ PRE(le != NULL);
+
+ newciter = citer;
+ newciter.m_entry = le->m_next;
+
+ return newciter;
+}
+
+bool
+atf_equal_list_citer_list_citer(const atf_list_citer_t i1,
+ const atf_list_citer_t i2)
+{
+ return i1.m_list == i2.m_list && i1.m_entry == i2.m_entry;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_list_iter" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Getters.
+ */
+
+void *
+atf_list_iter_data(const atf_list_iter_t iter)
+{
+ const struct list_entry *le = iter.m_entry;
+ PRE(le != NULL);
+ return le->m_object;
+}
+
+atf_list_iter_t
+atf_list_iter_next(const atf_list_iter_t iter)
+{
+ const struct list_entry *le = iter.m_entry;
+ atf_list_iter_t newiter;
+
+ PRE(le != NULL);
+
+ newiter = iter;
+ newiter.m_entry = le->m_next;
+
+ return newiter;
+}
+
+bool
+atf_equal_list_iter_list_iter(const atf_list_iter_t i1,
+ const atf_list_iter_t i2)
+{
+ return i1.m_list == i2.m_list && i1.m_entry == i2.m_entry;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_list" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+atf_error_t
+atf_list_init(atf_list_t *l)
+{
+ struct list_entry *lebeg, *leend;
+
+ lebeg = new_entry(NULL, false);
+ if (lebeg == NULL) {
+ return atf_no_memory_error();
+ }
+
+ leend = new_entry(NULL, false);
+ if (leend == NULL) {
+ free(lebeg);
+ return atf_no_memory_error();
+ }
+
+ lebeg->m_next = leend;
+ lebeg->m_prev = NULL;
+
+ leend->m_next = NULL;
+ leend->m_prev = lebeg;
+
+ l->m_size = 0;
+ l->m_begin = lebeg;
+ l->m_end = leend;
+
+ return atf_no_error();
+}
+
+void
+atf_list_fini(atf_list_t *l)
+{
+ struct list_entry *le;
+ size_t freed;
+
+ le = (struct list_entry *)l->m_begin;
+ freed = 0;
+ while (le != NULL) {
+ struct list_entry *lenext;
+
+ lenext = le->m_next;
+ delete_entry(le);
+ le = lenext;
+
+ freed++;
+ }
+ INV(freed == l->m_size + 2);
+}
+
+/*
+ * Getters.
+ */
+
+atf_list_iter_t
+atf_list_begin(atf_list_t *l)
+{
+ struct list_entry *le = l->m_begin;
+ return entry_to_iter(l, le->m_next);
+}
+
+atf_list_citer_t
+atf_list_begin_c(const atf_list_t *l)
+{
+ const struct list_entry *le = l->m_begin;
+ return entry_to_citer(l, le->m_next);
+}
+
+atf_list_iter_t
+atf_list_end(atf_list_t *l)
+{
+ return entry_to_iter(l, l->m_end);
+}
+
+atf_list_citer_t
+atf_list_end_c(const atf_list_t *l)
+{
+ return entry_to_citer(l, l->m_end);
+}
+
+void *
+atf_list_index(atf_list_t *list, const size_t idx)
+{
+ atf_list_iter_t iter;
+
+ PRE(idx < atf_list_size(list));
+
+ iter = atf_list_begin(list);
+ {
+ size_t pos = 0;
+ while (pos < idx &&
+ !atf_equal_list_iter_list_iter((iter), atf_list_end(list))) {
+ iter = atf_list_iter_next(iter);
+ pos++;
+ }
+ }
+ return atf_list_iter_data(iter);
+}
+
+const void *
+atf_list_index_c(const atf_list_t *list, const size_t idx)
+{
+ atf_list_citer_t iter;
+
+ PRE(idx < atf_list_size(list));
+
+ iter = atf_list_begin_c(list);
+ {
+ size_t pos = 0;
+ while (pos < idx &&
+ !atf_equal_list_citer_list_citer((iter),
+ atf_list_end_c(list))) {
+ iter = atf_list_citer_next(iter);
+ pos++;
+ }
+ }
+ return atf_list_citer_data(iter);
+}
+
+size_t
+atf_list_size(const atf_list_t *l)
+{
+ return l->m_size;
+}
+
+char **
+atf_list_to_charpp(const atf_list_t *l)
+{
+ char **array;
+ atf_list_citer_t iter;
+ size_t i;
+
+ array = malloc(sizeof(char *) * (atf_list_size(l) + 1));
+ if (array == NULL)
+ goto out;
+
+ i = 0;
+ atf_list_for_each_c(iter, l) {
+ array[i] = strdup((const char *)atf_list_citer_data(iter));
+ if (array[i] == NULL) {
+ atf_utils_free_charpp(array);
+ array = NULL;
+ goto out;
+ }
+
+ i++;
+ }
+ array[i] = NULL;
+
+out:
+ return array;
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_list_append(atf_list_t *l, void *data, bool managed)
+{
+ struct list_entry *le, *next, *prev;
+ atf_error_t err;
+
+ next = (struct list_entry *)l->m_end;
+ prev = next->m_prev;
+ le = new_entry_and_link(data, managed, prev, next);
+ if (le == NULL)
+ err = atf_no_memory_error();
+ else {
+ l->m_size++;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+void
+atf_list_append_list(atf_list_t *l, atf_list_t *src)
+{
+ struct list_entry *e1, *e2, *ghost1, *ghost2;
+
+ ghost1 = (struct list_entry *)l->m_end;
+ ghost2 = (struct list_entry *)src->m_begin;
+
+ e1 = ghost1->m_prev;
+ e2 = ghost2->m_next;
+
+ delete_entry(ghost1);
+ delete_entry(ghost2);
+
+ e1->m_next = e2;
+ e2->m_prev = e1;
+
+ l->m_end = src->m_end;
+ l->m_size += src->m_size;
+}
diff --git a/unit/atf-src/atf-c/detail/list.h b/unit/atf-src/atf-c/detail/list.h
new file mode 100644
index 0000000..b250066
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/list.h
@@ -0,0 +1,111 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_LIST_H)
+#define ATF_C_DETAIL_LIST_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_list_citer" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_list_citer {
+ const struct atf_list *m_list;
+ const void *m_entry;
+};
+typedef struct atf_list_citer atf_list_citer_t;
+
+/* Getters. */
+const void *atf_list_citer_data(const atf_list_citer_t);
+atf_list_citer_t atf_list_citer_next(const atf_list_citer_t);
+
+/* Operators. */
+bool atf_equal_list_citer_list_citer(const atf_list_citer_t,
+ const atf_list_citer_t);
+
+/* ---------------------------------------------------------------------
+ * The "atf_list_iter" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_list_iter {
+ struct atf_list *m_list;
+ void *m_entry;
+};
+typedef struct atf_list_iter atf_list_iter_t;
+
+/* Getters. */
+void *atf_list_iter_data(const atf_list_iter_t);
+atf_list_iter_t atf_list_iter_next(const atf_list_iter_t);
+
+/* Operators. */
+bool atf_equal_list_iter_list_iter(const atf_list_iter_t,
+ const atf_list_iter_t);
+
+/* ---------------------------------------------------------------------
+ * The "atf_list" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_list {
+ void *m_begin;
+ void *m_end;
+
+ size_t m_size;
+};
+typedef struct atf_list atf_list_t;
+
+/* Constructors and destructors */
+atf_error_t atf_list_init(atf_list_t *);
+void atf_list_fini(atf_list_t *);
+
+/* Getters. */
+atf_list_iter_t atf_list_begin(atf_list_t *);
+atf_list_citer_t atf_list_begin_c(const atf_list_t *);
+atf_list_iter_t atf_list_end(atf_list_t *);
+atf_list_citer_t atf_list_end_c(const atf_list_t *);
+void *atf_list_index(atf_list_t *, const size_t);
+const void *atf_list_index_c(const atf_list_t *, const size_t);
+size_t atf_list_size(const atf_list_t *);
+char **atf_list_to_charpp(const atf_list_t *);
+
+/* Modifiers. */
+atf_error_t atf_list_append(atf_list_t *, void *, bool);
+void atf_list_append_list(atf_list_t *, atf_list_t *);
+
+/* Macros. */
+#define atf_list_for_each(iter, list) \
+ for (iter = atf_list_begin(list); \
+ !atf_equal_list_iter_list_iter((iter), atf_list_end(list)); \
+ iter = atf_list_iter_next(iter))
+#define atf_list_for_each_c(iter, list) \
+ for (iter = atf_list_begin_c(list); \
+ !atf_equal_list_citer_list_citer((iter), atf_list_end_c(list)); \
+ iter = atf_list_citer_next(iter))
+
+#endif /* !defined(ATF_C_DETAIL_LIST_H) */
diff --git a/unit/atf-src/atf-c/detail/list_test.c b/unit/atf-src/atf-c/detail/list_test.c
new file mode 100644
index 0000000..82a715d
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/list_test.c
@@ -0,0 +1,365 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/list.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Tests for the "atf_list" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+ATF_TC(list_init);
+ATF_TC_HEAD(list_init, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_init function");
+}
+ATF_TC_BODY(list_init, tc)
+{
+ atf_list_t list;
+
+ RE(atf_list_init(&list));
+ ATF_REQUIRE_EQ(atf_list_size(&list), 0);
+ atf_list_fini(&list);
+}
+
+/*
+ * Getters.
+ */
+
+ATF_TC(list_index);
+ATF_TC_HEAD(list_index, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_index function");
+}
+ATF_TC_BODY(list_index, tc)
+{
+ atf_list_t list;
+ int i1 = 1;
+ int i2 = 5;
+ int i3 = 9;
+
+ RE(atf_list_init(&list));
+ RE(atf_list_append(&list, &i1, false));
+ RE(atf_list_append(&list, &i2, false));
+ RE(atf_list_append(&list, &i3, false));
+
+ ATF_CHECK_EQ(*(int *)atf_list_index(&list, 0), 1);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&list, 1), 5);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&list, 2), 9);
+
+ atf_list_fini(&list);
+}
+
+ATF_TC(list_index_c);
+ATF_TC_HEAD(list_index_c, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_index_c function");
+}
+ATF_TC_BODY(list_index_c, tc)
+{
+ atf_list_t list;
+ int i1 = 1;
+ int i2 = 5;
+ int i3 = 9;
+
+ RE(atf_list_init(&list));
+ RE(atf_list_append(&list, &i1, false));
+ RE(atf_list_append(&list, &i2, false));
+ RE(atf_list_append(&list, &i3, false));
+
+ ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 0), 1);
+ ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 1), 5);
+ ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 2), 9);
+
+ atf_list_fini(&list);
+}
+
+ATF_TC_WITHOUT_HEAD(list_to_charpp_empty);
+ATF_TC_BODY(list_to_charpp_empty, tc)
+{
+ atf_list_t list;
+ char **array;
+
+ RE(atf_list_init(&list));
+ ATF_REQUIRE((array = atf_list_to_charpp(&list)) != NULL);
+ atf_list_fini(&list);
+
+ ATF_CHECK_EQ(NULL, array[0]);
+ atf_utils_free_charpp(array);
+}
+
+ATF_TC_WITHOUT_HEAD(list_to_charpp_some);
+ATF_TC_BODY(list_to_charpp_some, tc)
+{
+ atf_list_t list;
+ char **array;
+
+ char s1[] = "one";
+ char s2[] = "two";
+ char s3[] = "three";
+
+ RE(atf_list_init(&list));
+ RE(atf_list_append(&list, s1, false));
+ RE(atf_list_append(&list, s2, false));
+ RE(atf_list_append(&list, s3, false));
+ ATF_REQUIRE((array = atf_list_to_charpp(&list)) != NULL);
+ atf_list_fini(&list);
+
+ ATF_CHECK_STREQ("one", array[0]);
+ ATF_CHECK_STREQ("two", array[1]);
+ ATF_CHECK_STREQ("three", array[2]);
+ ATF_CHECK_EQ(NULL, array[3]);
+ atf_utils_free_charpp(array);
+}
+
+/*
+ * Modifiers.
+ */
+
+ATF_TC(list_append);
+ATF_TC_HEAD(list_append, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_append function");
+}
+ATF_TC_BODY(list_append, tc)
+{
+ atf_list_t list;
+ size_t i;
+ char buf[] = "Test string";
+
+ RE(atf_list_init(&list));
+ for (i = 0; i < 1024; i++) {
+ ATF_REQUIRE_EQ(atf_list_size(&list), i);
+ RE(atf_list_append(&list, buf, false));
+ }
+ atf_list_fini(&list);
+}
+
+ATF_TC(list_append_list);
+ATF_TC_HEAD(list_append_list, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_append_list "
+ "function");
+}
+ATF_TC_BODY(list_append_list, tc)
+{
+ {
+ atf_list_t l1, l2;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_init(&l2));
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK_EQ(atf_list_size(&l1), 0);
+
+ atf_list_fini(&l1);
+ }
+
+ {
+ atf_list_t l1, l2;
+ int item = 5;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_append(&l1, &item, false));
+ RE(atf_list_init(&l2));
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK_EQ(atf_list_size(&l1), 1);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item);
+
+ atf_list_fini(&l1);
+ }
+
+ {
+ atf_list_t l1, l2;
+ int item = 5;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_init(&l2));
+ RE(atf_list_append(&l2, &item, false));
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK_EQ(atf_list_size(&l1), 1);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item);
+
+ atf_list_fini(&l1);
+ }
+
+ {
+ atf_list_t l1, l2;
+ int item1 = 5;
+ int item2 = 9;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_append(&l1, &item1, false));
+ RE(atf_list_init(&l2));
+ RE(atf_list_append(&l2, &item2, false));
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK_EQ(atf_list_size(&l1), 2);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item1);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 1), item2);
+
+ atf_list_fini(&l1);
+ }
+
+ {
+ atf_list_t l1, l2;
+ atf_list_citer_t end1, end2;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_init(&l2));
+
+ end1 = atf_list_end_c(&l1);
+ end2 = atf_list_end_c(&l2);
+ /* XXX Shouldn't query m_entry here. */
+ ATF_CHECK(end1.m_entry != end2.m_entry);
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK(atf_list_end_c(&l1).m_entry == end2.m_entry);
+
+ atf_list_fini(&l1);
+ }
+}
+
+/*
+ * Macros.
+ */
+
+ATF_TC(list_for_each);
+ATF_TC_HEAD(list_for_each, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_for_each macro");
+}
+ATF_TC_BODY(list_for_each, tc)
+{
+ atf_list_t list;
+ atf_list_iter_t iter;
+ size_t count, i, size;
+ int nums[10];
+
+ printf("Iterating over empty list\n");
+ RE(atf_list_init(&list));
+ count = 0;
+ atf_list_for_each(iter, &list) {
+ count++;
+ printf("Item count is now %zd\n", count);
+ }
+ ATF_REQUIRE_EQ(count, 0);
+ atf_list_fini(&list);
+
+ for (size = 0; size <= 10; size++) {
+ printf("Iterating over list of %zd elements\n", size);
+ RE(atf_list_init(&list));
+ for (i = 0; i < size; i++) {
+ nums[i] = i + 1;
+ RE(atf_list_append(&list, &nums[i], false));
+ }
+ count = 0;
+ atf_list_for_each(iter, &list) {
+ printf("Retrieved item: %d\n", *(int *)atf_list_iter_data(iter));
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, size);
+ atf_list_fini(&list);
+ }
+}
+
+ATF_TC(list_for_each_c);
+ATF_TC_HEAD(list_for_each_c, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_for_each_c macro");
+}
+ATF_TC_BODY(list_for_each_c, tc)
+{
+ atf_list_t list;
+ atf_list_citer_t iter;
+ size_t count, i, size;
+ int nums[10];
+
+ printf("Iterating over empty list\n");
+ RE(atf_list_init(&list));
+ count = 0;
+ atf_list_for_each_c(iter, &list) {
+ count++;
+ printf("Item count is now %zd\n", count);
+ }
+ ATF_REQUIRE_EQ(count, 0);
+ atf_list_fini(&list);
+
+ for (size = 0; size <= 10; size++) {
+ printf("Iterating over list of %zd elements\n", size);
+ RE(atf_list_init(&list));
+ for (i = 0; i < size; i++) {
+ nums[i] = i + 1;
+ RE(atf_list_append(&list, &nums[i], false));
+ }
+ count = 0;
+ atf_list_for_each_c(iter, &list) {
+ printf("Retrieved item: %d\n",
+ *(const int *)atf_list_citer_data(iter));
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, size);
+ atf_list_fini(&list);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Constructors and destructors. */
+ ATF_TP_ADD_TC(tp, list_init);
+
+ /* Getters. */
+ ATF_TP_ADD_TC(tp, list_index);
+ ATF_TP_ADD_TC(tp, list_index_c);
+ ATF_TP_ADD_TC(tp, list_to_charpp_empty);
+ ATF_TP_ADD_TC(tp, list_to_charpp_some);
+
+ /* Modifiers. */
+ ATF_TP_ADD_TC(tp, list_append);
+ ATF_TP_ADD_TC(tp, list_append_list);
+
+ /* Macros. */
+ ATF_TP_ADD_TC(tp, list_for_each);
+ ATF_TP_ADD_TC(tp, list_for_each_c);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/map.c b/unit/atf-src/atf-c/detail/map.c
new file mode 100644
index 0000000..45de47f
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/map.c
@@ -0,0 +1,380 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/map.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+struct map_entry {
+ char *m_key;
+ void *m_value;
+ bool m_managed;
+};
+
+static
+struct map_entry *
+new_entry(const char *key, void *value, bool managed)
+{
+ struct map_entry *me;
+
+ me = (struct map_entry *)malloc(sizeof(*me));
+ if (me != NULL) {
+ me->m_key = strdup(key);
+ if (me->m_key == NULL) {
+ free(me);
+ me = NULL;
+ } else {
+ me->m_value = value;
+ me->m_managed = managed;
+ }
+ }
+
+ return me;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_map_citer" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Getters.
+ */
+
+const char *
+atf_map_citer_key(const atf_map_citer_t citer)
+{
+ const struct map_entry *me = citer.m_entry;
+ PRE(me != NULL);
+ return me->m_key;
+}
+
+const void *
+atf_map_citer_data(const atf_map_citer_t citer)
+{
+ const struct map_entry *me = citer.m_entry;
+ PRE(me != NULL);
+ return me->m_value;
+}
+
+atf_map_citer_t
+atf_map_citer_next(const atf_map_citer_t citer)
+{
+ atf_map_citer_t newciter;
+
+ newciter = citer;
+ newciter.m_listiter = atf_list_citer_next(citer.m_listiter);
+ newciter.m_entry = ((const struct map_entry *)
+ atf_list_citer_data(newciter.m_listiter));
+
+ return newciter;
+}
+
+bool
+atf_equal_map_citer_map_citer(const atf_map_citer_t i1,
+ const atf_map_citer_t i2)
+{
+ return i1.m_map == i2.m_map && i1.m_entry == i2.m_entry;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_map_iter" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Getters.
+ */
+
+const char *
+atf_map_iter_key(const atf_map_iter_t iter)
+{
+ const struct map_entry *me = iter.m_entry;
+ PRE(me != NULL);
+ return me->m_key;
+}
+
+void *
+atf_map_iter_data(const atf_map_iter_t iter)
+{
+ const struct map_entry *me = iter.m_entry;
+ PRE(me != NULL);
+ return me->m_value;
+}
+
+atf_map_iter_t
+atf_map_iter_next(const atf_map_iter_t iter)
+{
+ atf_map_iter_t newiter;
+
+ newiter = iter;
+ newiter.m_listiter = atf_list_iter_next(iter.m_listiter);
+ newiter.m_entry = ((struct map_entry *)
+ atf_list_iter_data(newiter.m_listiter));
+
+ return newiter;
+}
+
+bool
+atf_equal_map_iter_map_iter(const atf_map_iter_t i1,
+ const atf_map_iter_t i2)
+{
+ return i1.m_map == i2.m_map && i1.m_entry == i2.m_entry;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_map" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+atf_error_t
+atf_map_init(atf_map_t *m)
+{
+ return atf_list_init(&m->m_list);
+}
+
+atf_error_t
+atf_map_init_charpp(atf_map_t *m, const char *const *array)
+{
+ atf_error_t err;
+ const char *const *ptr = array;
+
+ err = atf_map_init(m);
+ if (array != NULL) {
+ while (!atf_is_error(err) && *ptr != NULL) {
+ const char *key, *value;
+
+ key = *ptr;
+ INV(key != NULL);
+ ptr++;
+
+ if ((value = *ptr) == NULL) {
+ err = atf_libc_error(EINVAL, "List too short; no value for "
+ "key '%s' provided", key); /* XXX: Not really libc_error */
+ break;
+ }
+ ptr++;
+
+ err = atf_map_insert(m, key, strdup(value), true);
+ }
+ }
+
+ if (atf_is_error(err))
+ atf_map_fini(m);
+
+ return err;
+}
+
+void
+atf_map_fini(atf_map_t *m)
+{
+ atf_list_iter_t iter;
+
+ atf_list_for_each(iter, &m->m_list) {
+ struct map_entry *me = atf_list_iter_data(iter);
+
+ if (me->m_managed)
+ free(me->m_value);
+ free(me->m_key);
+ free(me);
+ }
+ atf_list_fini(&m->m_list);
+}
+
+/*
+ * Getters.
+ */
+
+atf_map_iter_t
+atf_map_begin(atf_map_t *m)
+{
+ atf_map_iter_t iter;
+ iter.m_map = m;
+ iter.m_listiter = atf_list_begin(&m->m_list);
+ iter.m_entry = atf_list_iter_data(iter.m_listiter);
+ return iter;
+}
+
+atf_map_citer_t
+atf_map_begin_c(const atf_map_t *m)
+{
+ atf_map_citer_t citer;
+ citer.m_map = m;
+ citer.m_listiter = atf_list_begin_c(&m->m_list);
+ citer.m_entry = atf_list_citer_data(citer.m_listiter);
+ return citer;
+}
+
+atf_map_iter_t
+atf_map_end(atf_map_t *m)
+{
+ atf_map_iter_t iter;
+ iter.m_map = m;
+ iter.m_entry = NULL;
+ iter.m_listiter = atf_list_end(&m->m_list);
+ return iter;
+}
+
+atf_map_citer_t
+atf_map_end_c(const atf_map_t *m)
+{
+ atf_map_citer_t iter;
+ iter.m_map = m;
+ iter.m_entry = NULL;
+ iter.m_listiter = atf_list_end_c(&m->m_list);
+ return iter;
+}
+
+atf_map_iter_t
+atf_map_find(atf_map_t *m, const char *key)
+{
+ atf_list_iter_t iter;
+
+ atf_list_for_each(iter, &m->m_list) {
+ struct map_entry *me = atf_list_iter_data(iter);
+
+ if (strcmp(me->m_key, key) == 0) {
+ atf_map_iter_t i;
+ i.m_map = m;
+ i.m_entry = me;
+ i.m_listiter = iter;
+ return i;
+ }
+ }
+
+ return atf_map_end(m);
+}
+
+atf_map_citer_t
+atf_map_find_c(const atf_map_t *m, const char *key)
+{
+ atf_list_citer_t iter;
+
+ atf_list_for_each_c(iter, &m->m_list) {
+ const struct map_entry *me = atf_list_citer_data(iter);
+
+ if (strcmp(me->m_key, key) == 0) {
+ atf_map_citer_t i;
+ i.m_map = m;
+ i.m_entry = me;
+ i.m_listiter = iter;
+ return i;
+ }
+ }
+
+ return atf_map_end_c(m);
+}
+
+size_t
+atf_map_size(const atf_map_t *m)
+{
+ return atf_list_size(&m->m_list);
+}
+
+char **
+atf_map_to_charpp(const atf_map_t *l)
+{
+ char **array;
+ atf_map_citer_t iter;
+ size_t i;
+
+ array = malloc(sizeof(char *) * (atf_map_size(l) * 2 + 1));
+ if (array == NULL)
+ goto out;
+
+ i = 0;
+ atf_map_for_each_c(iter, l) {
+ array[i] = strdup(atf_map_citer_key(iter));
+ if (array[i] == NULL) {
+ atf_utils_free_charpp(array);
+ array = NULL;
+ goto out;
+ }
+
+ array[i + 1] = strdup((const char *)atf_map_citer_data(iter));
+ if (array[i + 1] == NULL) {
+ atf_utils_free_charpp(array);
+ array = NULL;
+ goto out;
+ }
+
+ i += 2;
+ }
+ array[i] = NULL;
+
+out:
+ return array;
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_map_insert(atf_map_t *m, const char *key, void *value, bool managed)
+{
+ struct map_entry *me;
+ atf_error_t err;
+ atf_map_iter_t iter;
+
+ iter = atf_map_find(m, key);
+ if (atf_equal_map_iter_map_iter(iter, atf_map_end(m))) {
+ me = new_entry(key, value, managed);
+ if (me == NULL)
+ err = atf_no_memory_error();
+ else {
+ err = atf_list_append(&m->m_list, me, false);
+ if (atf_is_error(err)) {
+ if (managed)
+ free(value);
+ free(me->m_key);
+ free(me);
+ }
+ }
+ } else {
+ me = iter.m_entry;
+ if (me->m_managed)
+ free(me->m_value);
+
+ INV(strcmp(me->m_key, key) == 0);
+ me->m_value = value;
+ me->m_managed = managed;
+
+ err = atf_no_error();
+ }
+
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/map.h b/unit/atf-src/atf-c/detail/map.h
new file mode 100644
index 0000000..7ca8e8a
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/map.h
@@ -0,0 +1,114 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_MAP_H)
+#define ATF_C_DETAIL_MAP_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <atf-c/detail/list.h>
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_map_citer" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_map_citer {
+ const struct atf_map *m_map;
+ const void *m_entry;
+ atf_list_citer_t m_listiter;
+};
+typedef struct atf_map_citer atf_map_citer_t;
+
+/* Getters. */
+const char *atf_map_citer_key(const atf_map_citer_t);
+const void *atf_map_citer_data(const atf_map_citer_t);
+atf_map_citer_t atf_map_citer_next(const atf_map_citer_t);
+
+/* Operators. */
+bool atf_equal_map_citer_map_citer(const atf_map_citer_t,
+ const atf_map_citer_t);
+
+/* ---------------------------------------------------------------------
+ * The "atf_map_iter" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_map_iter {
+ struct atf_map *m_map;
+ void *m_entry;
+ atf_list_iter_t m_listiter;
+};
+typedef struct atf_map_iter atf_map_iter_t;
+
+/* Getters. */
+const char *atf_map_iter_key(const atf_map_iter_t);
+void *atf_map_iter_data(const atf_map_iter_t);
+atf_map_iter_t atf_map_iter_next(const atf_map_iter_t);
+
+/* Operators. */
+bool atf_equal_map_iter_map_iter(const atf_map_iter_t,
+ const atf_map_iter_t);
+
+/* ---------------------------------------------------------------------
+ * The "atf_map" type.
+ * --------------------------------------------------------------------- */
+
+/* A list-based map. Typically very inefficient, but our maps are small
+ * enough. */
+struct atf_map {
+ atf_list_t m_list;
+};
+typedef struct atf_map atf_map_t;
+
+/* Constructors and destructors */
+atf_error_t atf_map_init(atf_map_t *);
+atf_error_t atf_map_init_charpp(atf_map_t *, const char *const *);
+void atf_map_fini(atf_map_t *);
+
+/* Getters. */
+atf_map_iter_t atf_map_begin(atf_map_t *);
+atf_map_citer_t atf_map_begin_c(const atf_map_t *);
+atf_map_iter_t atf_map_end(atf_map_t *);
+atf_map_citer_t atf_map_end_c(const atf_map_t *);
+atf_map_iter_t atf_map_find(atf_map_t *, const char *);
+atf_map_citer_t atf_map_find_c(const atf_map_t *, const char *);
+size_t atf_map_size(const atf_map_t *);
+char **atf_map_to_charpp(const atf_map_t *);
+
+/* Modifiers. */
+atf_error_t atf_map_insert(atf_map_t *, const char *, void *, bool);
+
+/* Macros. */
+#define atf_map_for_each(iter, map) \
+ for (iter = atf_map_begin(map); \
+ !atf_equal_map_iter_map_iter((iter), atf_map_end(map)); \
+ iter = atf_map_iter_next(iter))
+#define atf_map_for_each_c(iter, map) \
+ for (iter = atf_map_begin_c(map); \
+ !atf_equal_map_citer_map_citer((iter), atf_map_end_c(map)); \
+ iter = atf_map_citer_next(iter))
+
+#endif /* !defined(ATF_C_DETAIL_MAP_H) */
diff --git a/unit/atf-src/atf-c/detail/map_test.c b/unit/atf-src/atf-c/detail/map_test.c
new file mode 100644
index 0000000..0982e20
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/map_test.c
@@ -0,0 +1,421 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/map.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Tests for the "atf_map" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+ATF_TC(map_init);
+ATF_TC_HEAD(map_init, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_init function");
+}
+ATF_TC_BODY(map_init, tc)
+{
+ atf_map_t map;
+
+ RE(atf_map_init(&map));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 0);
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(map_init_charpp_null);
+ATF_TC_BODY(map_init_charpp_null, tc)
+{
+ atf_map_t map;
+
+ RE(atf_map_init_charpp(&map, NULL));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 0);
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(map_init_charpp_empty);
+ATF_TC_BODY(map_init_charpp_empty, tc)
+{
+ const char *const array[] = { NULL };
+ atf_map_t map;
+
+ RE(atf_map_init_charpp(&map, array));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 0);
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(map_init_charpp_some);
+ATF_TC_BODY(map_init_charpp_some, tc)
+{
+ const char *const array[] = { "K1", "V1", "K2", "V2", NULL };
+ atf_map_t map;
+ atf_map_citer_t iter;
+
+ RE(atf_map_init_charpp(&map, array));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 2);
+
+ iter = atf_map_find_c(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K1") == 0);
+ ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V1") == 0);
+
+ iter = atf_map_find_c(&map, "K2");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K2") == 0);
+ ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V2") == 0);
+
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(map_init_charpp_short);
+ATF_TC_BODY(map_init_charpp_short, tc)
+{
+ const char *const array[] = { "K1", "V1", "K2", NULL };
+ atf_map_t map;
+
+ atf_error_t err = atf_map_init_charpp(&map, array);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+}
+
+/*
+ * Getters.
+ */
+
+ATF_TC(find);
+ATF_TC_HEAD(find, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_find function");
+}
+ATF_TC_BODY(find, tc)
+{
+ atf_map_t map;
+ char val1[] = "V1";
+ char val2[] = "V2";
+ atf_map_iter_t iter;
+
+ RE(atf_map_init(&map));
+ RE(atf_map_insert(&map, "K1", val1, false));
+ RE(atf_map_insert(&map, "K2", val2, false));
+
+ iter = atf_map_find(&map, "K0");
+ ATF_REQUIRE(atf_equal_map_iter_map_iter(iter, atf_map_end(&map)));
+
+ iter = atf_map_find(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map)));
+ ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K1") == 0);
+ ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "V1") == 0);
+ strcpy(atf_map_iter_data(iter), "Z1");
+
+ iter = atf_map_find(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map)));
+ ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K1") == 0);
+ ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "Z1") == 0);
+
+ iter = atf_map_find(&map, "K2");
+ ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map)));
+ ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K2") == 0);
+ ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "V2") == 0);
+
+ atf_map_fini(&map);
+}
+
+ATF_TC(find_c);
+ATF_TC_HEAD(find_c, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_find_c function");
+}
+ATF_TC_BODY(find_c, tc)
+{
+ atf_map_t map;
+ char val1[] = "V1";
+ char val2[] = "V2";
+ atf_map_citer_t iter;
+
+ RE(atf_map_init(&map));
+ RE(atf_map_insert(&map, "K1", val1, false));
+ RE(atf_map_insert(&map, "K2", val2, false));
+
+ iter = atf_map_find_c(&map, "K0");
+ ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+
+ iter = atf_map_find_c(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K1") == 0);
+ ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V1") == 0);
+
+ iter = atf_map_find_c(&map, "K2");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K2") == 0);
+ ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V2") == 0);
+
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(to_charpp_empty);
+ATF_TC_BODY(to_charpp_empty, tc)
+{
+ atf_map_t map;
+ char **array;
+
+ RE(atf_map_init(&map));
+ ATF_REQUIRE((array = atf_map_to_charpp(&map)) != NULL);
+ atf_map_fini(&map);
+
+ ATF_CHECK_EQ(NULL, array[0]);
+ atf_utils_free_charpp(array);
+}
+
+ATF_TC_WITHOUT_HEAD(to_charpp_some);
+ATF_TC_BODY(to_charpp_some, tc)
+{
+ atf_map_t map;
+ char **array;
+
+ char s1[] = "one";
+ char s2[] = "two";
+ char s3[] = "three";
+
+ RE(atf_map_init(&map));
+ RE(atf_map_insert(&map, "K1", s1, false));
+ RE(atf_map_insert(&map, "K2", s2, false));
+ RE(atf_map_insert(&map, "K3", s3, false));
+ ATF_REQUIRE((array = atf_map_to_charpp(&map)) != NULL);
+ atf_map_fini(&map);
+
+ ATF_CHECK_STREQ("K1", array[0]);
+ ATF_CHECK_STREQ("one", array[1]);
+ ATF_CHECK_STREQ("K2", array[2]);
+ ATF_CHECK_STREQ("two", array[3]);
+ ATF_CHECK_STREQ("K3", array[4]);
+ ATF_CHECK_STREQ("three", array[5]);
+ ATF_CHECK_EQ(NULL, array[6]);
+ atf_utils_free_charpp(array);
+}
+
+/*
+ * Modifiers.
+ */
+
+ATF_TC(map_insert);
+ATF_TC_HEAD(map_insert, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_insert function");
+}
+ATF_TC_BODY(map_insert, tc)
+{
+ atf_map_t map;
+ char buf[] = "1st test string";
+ char buf2[] = "2nd test string";
+ const char *ptr;
+ atf_map_citer_t iter;
+
+ RE(atf_map_init(&map));
+
+ printf("Inserting some values\n");
+ ATF_REQUIRE_EQ(atf_map_size(&map), 0);
+ RE(atf_map_insert(&map, "K1", buf, false));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 1);
+ RE(atf_map_insert(&map, "K2", buf, false));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 2);
+ RE(atf_map_insert(&map, "K3", buf, false));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 3);
+
+ printf("Replacing a value\n");
+ iter = atf_map_find_c(&map, "K3");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ptr = atf_map_citer_data(iter);
+ ATF_REQUIRE_EQ(ptr, buf);
+ RE(atf_map_insert(&map, "K3", buf2, false));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 3);
+ iter = atf_map_find_c(&map, "K3");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ptr = atf_map_citer_data(iter);
+ ATF_REQUIRE_EQ(ptr, buf2);
+
+ atf_map_fini(&map);
+}
+
+/*
+ * Macros.
+ */
+
+ATF_TC(map_for_each);
+ATF_TC_HEAD(map_for_each, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_for_each macro");
+}
+ATF_TC_BODY(map_for_each, tc)
+{
+ atf_map_t map;
+ atf_map_iter_t iter;
+ size_t count, i, size;
+ char keys[10][5];
+ int nums[10];
+
+ printf("Iterating over empty map\n");
+ RE(atf_map_init(&map));
+ count = 0;
+ atf_map_for_each(iter, &map) {
+ count++;
+ printf("Item count is now %zd\n", count);
+ }
+ ATF_REQUIRE_EQ(count, 0);
+ atf_map_fini(&map);
+
+ for (size = 0; size <= 10; size++) {
+ printf("Iterating over map of %zd elements\n", size);
+ RE(atf_map_init(&map));
+ for (i = 0; i < size; i++) {
+ nums[i] = i + 1;
+ snprintf(keys[i], sizeof(keys[i]), "%d", nums[i]);
+ RE(atf_map_insert(&map, keys[i], &nums[i], false));
+ }
+ count = 0;
+ atf_map_for_each(iter, &map) {
+ printf("Retrieved item: %d\n", *(int *)atf_map_iter_data(iter));
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, size);
+ atf_map_fini(&map);
+ }
+}
+
+ATF_TC(map_for_each_c);
+ATF_TC_HEAD(map_for_each_c, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_for_each_c macro");
+}
+ATF_TC_BODY(map_for_each_c, tc)
+{
+ atf_map_t map;
+ atf_map_citer_t iter;
+ size_t count, i, size;
+ char keys[10][5];
+ int nums[10];
+
+ printf("Iterating over empty map\n");
+ RE(atf_map_init(&map));
+ count = 0;
+ atf_map_for_each_c(iter, &map) {
+ count++;
+ printf("Item count is now %zd\n", count);
+ }
+ ATF_REQUIRE_EQ(count, 0);
+ atf_map_fini(&map);
+
+ for (size = 0; size <= 10; size++) {
+ printf("Iterating over map of %zd elements\n", size);
+ RE(atf_map_init(&map));
+ for (i = 0; i < size; i++) {
+ nums[i] = i + 1;
+ snprintf(keys[i], sizeof(keys[i]), "%d", nums[i]);
+ RE(atf_map_insert(&map, keys[i], &nums[i], false));
+ }
+ count = 0;
+ atf_map_for_each_c(iter, &map) {
+ printf("Retrieved item: %d\n",
+ *(const int *)atf_map_citer_data(iter));
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, size);
+ atf_map_fini(&map);
+ }
+}
+
+/*
+ * Other.
+ */
+
+ATF_TC(stable_keys);
+ATF_TC_HEAD(stable_keys, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that the keys do not change "
+ "even if their original values do");
+}
+ATF_TC_BODY(stable_keys, tc)
+{
+ atf_map_t map;
+ atf_map_citer_t iter;
+ char key[] = "K1";
+
+ RE(atf_map_init(&map));
+
+ RE(atf_map_insert(&map, key, strdup("test-value"), true));
+ iter = atf_map_find_c(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ iter = atf_map_find_c(&map, "K2");
+ ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+
+ strcpy(key, "K2");
+ iter = atf_map_find_c(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ iter = atf_map_find_c(&map, "K2");
+ ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+
+ atf_map_fini(&map);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Constructors and destructors. */
+ ATF_TP_ADD_TC(tp, map_init);
+ ATF_TP_ADD_TC(tp, map_init_charpp_null);
+ ATF_TP_ADD_TC(tp, map_init_charpp_empty);
+ ATF_TP_ADD_TC(tp, map_init_charpp_some);
+ ATF_TP_ADD_TC(tp, map_init_charpp_short);
+
+ /* Getters. */
+ ATF_TP_ADD_TC(tp, find);
+ ATF_TP_ADD_TC(tp, find_c);
+ ATF_TP_ADD_TC(tp, to_charpp_empty);
+ ATF_TP_ADD_TC(tp, to_charpp_some);
+
+ /* Modifiers. */
+ ATF_TP_ADD_TC(tp, map_insert);
+
+ /* Macros. */
+ ATF_TP_ADD_TC(tp, map_for_each);
+ ATF_TP_ADD_TC(tp, map_for_each_c);
+
+ /* Other. */
+ ATF_TP_ADD_TC(tp, stable_keys);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/process.c b/unit/atf-src/atf-c/detail/process.c
new file mode 100644
index 0000000..8e08b6c
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process.c
@@ -0,0 +1,670 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/process.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+
+/* This prototype is not in the header file because this is a private
+ * function; however, we need to access it during testing. */
+atf_error_t atf_process_status_init(atf_process_status_t *, int);
+
+/* ---------------------------------------------------------------------
+ * The "stream_prepare" auxiliary type.
+ * --------------------------------------------------------------------- */
+
+struct stream_prepare {
+ const atf_process_stream_t *m_sb;
+
+ bool m_pipefds_ok;
+ int m_pipefds[2];
+};
+typedef struct stream_prepare stream_prepare_t;
+
+static
+atf_error_t
+stream_prepare_init(stream_prepare_t *sp, const atf_process_stream_t *sb)
+{
+ atf_error_t err;
+
+ const int type = atf_process_stream_type(sb);
+
+ sp->m_sb = sb;
+ sp->m_pipefds_ok = false;
+
+ if (type == atf_process_stream_type_capture) {
+ if (pipe(sp->m_pipefds) == -1)
+ err = atf_libc_error(errno, "Failed to create pipe");
+ else {
+ err = atf_no_error();
+ sp->m_pipefds_ok = true;
+ }
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+void
+stream_prepare_fini(stream_prepare_t *sp)
+{
+ if (sp->m_pipefds_ok) {
+ close(sp->m_pipefds[0]);
+ close(sp->m_pipefds[1]);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_stream" type.
+ * --------------------------------------------------------------------- */
+
+const int atf_process_stream_type_capture = 1;
+const int atf_process_stream_type_connect = 2;
+const int atf_process_stream_type_inherit = 3;
+const int atf_process_stream_type_redirect_fd = 4;
+const int atf_process_stream_type_redirect_path = 5;
+
+static
+bool
+stream_is_valid(const atf_process_stream_t *sb)
+{
+ return (sb->m_type == atf_process_stream_type_capture) ||
+ (sb->m_type == atf_process_stream_type_connect) ||
+ (sb->m_type == atf_process_stream_type_inherit) ||
+ (sb->m_type == atf_process_stream_type_redirect_fd) ||
+ (sb->m_type == atf_process_stream_type_redirect_path);
+}
+
+atf_error_t
+atf_process_stream_init_capture(atf_process_stream_t *sb)
+{
+ sb->m_type = atf_process_stream_type_capture;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_connect(atf_process_stream_t *sb,
+ const int src_fd, const int tgt_fd)
+{
+ PRE(src_fd >= 0);
+ PRE(tgt_fd >= 0);
+ PRE(src_fd != tgt_fd);
+
+ sb->m_type = atf_process_stream_type_connect;
+ sb->m_src_fd = src_fd;
+ sb->m_tgt_fd = tgt_fd;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_inherit(atf_process_stream_t *sb)
+{
+ sb->m_type = atf_process_stream_type_inherit;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_redirect_fd(atf_process_stream_t *sb,
+ const int fd)
+{
+ sb->m_type = atf_process_stream_type_redirect_fd;
+ sb->m_fd = fd;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_redirect_path(atf_process_stream_t *sb,
+ const atf_fs_path_t *path)
+{
+ sb->m_type = atf_process_stream_type_redirect_path;
+ sb->m_path = path;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+void
+atf_process_stream_fini(atf_process_stream_t *sb)
+{
+ PRE(stream_is_valid(sb));
+}
+
+int
+atf_process_stream_type(const atf_process_stream_t *sb)
+{
+ PRE(stream_is_valid(sb));
+
+ return sb->m_type;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_status" type.
+ * --------------------------------------------------------------------- */
+
+atf_error_t
+atf_process_status_init(atf_process_status_t *s, int status)
+{
+ s->m_status = status;
+
+ return atf_no_error();
+}
+
+void
+atf_process_status_fini(atf_process_status_t *s ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+bool
+atf_process_status_exited(const atf_process_status_t *s)
+{
+ int mutable_status = s->m_status;
+ return WIFEXITED(mutable_status);
+}
+
+int
+atf_process_status_exitstatus(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_exited(s));
+ int mutable_status = s->m_status;
+ return WEXITSTATUS(mutable_status);
+}
+
+bool
+atf_process_status_signaled(const atf_process_status_t *s)
+{
+ int mutable_status = s->m_status;
+ return WIFSIGNALED(mutable_status);
+}
+
+int
+atf_process_status_termsig(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_signaled(s));
+ int mutable_status = s->m_status;
+ return WTERMSIG(mutable_status);
+}
+
+bool
+atf_process_status_coredump(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_signaled(s));
+#if defined(WCOREDUMP)
+ int mutable_status = s->m_status;
+ return WCOREDUMP(mutable_status);
+#else
+ return false;
+#endif
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_child" type.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+atf_process_child_init(atf_process_child_t *c)
+{
+ c->m_pid = 0;
+ c->m_stdout = -1;
+ c->m_stderr = -1;
+
+ return atf_no_error();
+}
+
+static
+void
+atf_process_child_fini(atf_process_child_t *c)
+{
+ if (c->m_stdout != -1)
+ close(c->m_stdout);
+ if (c->m_stderr != -1)
+ close(c->m_stderr);
+}
+
+atf_error_t
+atf_process_child_wait(atf_process_child_t *c, atf_process_status_t *s)
+{
+ atf_error_t err;
+ int status;
+
+ if (waitpid(c->m_pid, &status, 0) == -1)
+ err = atf_libc_error(errno, "Failed waiting for process %d",
+ c->m_pid);
+ else {
+ atf_process_child_fini(c);
+ err = atf_process_status_init(s, status);
+ }
+
+ return err;
+}
+
+pid_t
+atf_process_child_pid(const atf_process_child_t *c)
+{
+ return c->m_pid;
+}
+
+int
+atf_process_child_stdout(atf_process_child_t *c)
+{
+ PRE(c->m_stdout != -1);
+ return c->m_stdout;
+}
+
+int
+atf_process_child_stderr(atf_process_child_t *c)
+{
+ PRE(c->m_stderr != -1);
+ return c->m_stderr;
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+safe_dup(const int oldfd, const int newfd)
+{
+ atf_error_t err;
+
+ if (oldfd != newfd) {
+ if (dup2(oldfd, newfd) == -1) {
+ err = atf_libc_error(errno, "Could not allocate file descriptor");
+ } else {
+ close(oldfd);
+ err = atf_no_error();
+ }
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+child_connect(const stream_prepare_t *sp, int procfd)
+{
+ atf_error_t err;
+ const int type = atf_process_stream_type(sp->m_sb);
+
+ if (type == atf_process_stream_type_capture) {
+ close(sp->m_pipefds[0]);
+ err = safe_dup(sp->m_pipefds[1], procfd);
+ } else if (type == atf_process_stream_type_connect) {
+ if (dup2(sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd) == -1)
+ err = atf_libc_error(errno, "Cannot connect descriptor %d to %d",
+ sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd);
+ else
+ err = atf_no_error();
+ } else if (type == atf_process_stream_type_inherit) {
+ err = atf_no_error();
+ } else if (type == atf_process_stream_type_redirect_fd) {
+ err = safe_dup(sp->m_sb->m_fd, procfd);
+ } else if (type == atf_process_stream_type_redirect_path) {
+ int aux = open(atf_fs_path_cstring(sp->m_sb->m_path),
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (aux == -1)
+ err = atf_libc_error(errno, "Could not create %s",
+ atf_fs_path_cstring(sp->m_sb->m_path));
+ else {
+ err = safe_dup(aux, procfd);
+ if (atf_is_error(err))
+ close(aux);
+ }
+ } else {
+ UNREACHABLE;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+static
+void
+parent_connect(const stream_prepare_t *sp, int *fd)
+{
+ const int type = atf_process_stream_type(sp->m_sb);
+
+ if (type == atf_process_stream_type_capture) {
+ close(sp->m_pipefds[1]);
+ *fd = sp->m_pipefds[0];
+ } else if (type == atf_process_stream_type_connect) {
+ /* Do nothing. */
+ } else if (type == atf_process_stream_type_inherit) {
+ /* Do nothing. */
+ } else if (type == atf_process_stream_type_redirect_fd) {
+ /* Do nothing. */
+ } else if (type == atf_process_stream_type_redirect_path) {
+ /* Do nothing. */
+ } else {
+ UNREACHABLE;
+ }
+}
+
+static
+atf_error_t
+do_parent(atf_process_child_t *c,
+ const pid_t pid,
+ const stream_prepare_t *outsp,
+ const stream_prepare_t *errsp)
+{
+ atf_error_t err;
+
+ err = atf_process_child_init(c);
+ if (atf_is_error(err))
+ goto out;
+
+ c->m_pid = pid;
+
+ parent_connect(outsp, &c->m_stdout);
+ parent_connect(errsp, &c->m_stderr);
+
+out:
+ return err;
+}
+
+static
+void
+do_child(void (*)(void *),
+ void *,
+ const stream_prepare_t *,
+ const stream_prepare_t *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+do_child(void (*start)(void *),
+ void *v,
+ const stream_prepare_t *outsp,
+ const stream_prepare_t *errsp)
+{
+ atf_error_t err;
+
+ err = child_connect(outsp, STDOUT_FILENO);
+ if (atf_is_error(err))
+ goto out;
+
+ err = child_connect(errsp, STDERR_FILENO);
+ if (atf_is_error(err))
+ goto out;
+
+ start(v);
+ UNREACHABLE;
+
+out:
+ if (atf_is_error(err)) {
+ char buf[1024];
+
+ atf_error_format(err, buf, sizeof(buf));
+ fprintf(stderr, "Unhandled error: %s\n", buf);
+ atf_error_free(err);
+
+ exit(EXIT_FAILURE);
+ } else
+ exit(EXIT_SUCCESS);
+}
+
+static
+atf_error_t
+fork_with_streams(atf_process_child_t *c,
+ void (*start)(void *),
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void *v)
+{
+ atf_error_t err;
+ stream_prepare_t outsp;
+ stream_prepare_t errsp;
+ pid_t pid;
+
+ err = stream_prepare_init(&outsp, outsb);
+ if (atf_is_error(err))
+ goto out;
+
+ err = stream_prepare_init(&errsp, errsb);
+ if (atf_is_error(err))
+ goto err_outpipe;
+
+ pid = fork();
+ if (pid == -1) {
+ err = atf_libc_error(errno, "Failed to fork");
+ goto err_errpipe;
+ }
+
+ if (pid == 0) {
+ do_child(start, v, &outsp, &errsp);
+ UNREACHABLE;
+ abort();
+ err = atf_no_error();
+ } else {
+ err = do_parent(c, pid, &outsp, &errsp);
+ if (atf_is_error(err))
+ goto err_errpipe;
+ }
+
+ goto out;
+
+err_errpipe:
+ stream_prepare_fini(&errsp);
+err_outpipe:
+ stream_prepare_fini(&outsp);
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+init_stream_w_default(const atf_process_stream_t *usersb,
+ atf_process_stream_t *inheritsb,
+ const atf_process_stream_t **realsb)
+{
+ atf_error_t err;
+
+ if (usersb == NULL) {
+ err = atf_process_stream_init_inherit(inheritsb);
+ if (!atf_is_error(err))
+ *realsb = inheritsb;
+ } else {
+ err = atf_no_error();
+ *realsb = usersb;
+ }
+
+ return err;
+}
+
+atf_error_t
+atf_process_fork(atf_process_child_t *c,
+ void (*start)(void *),
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void *v)
+{
+ atf_error_t err;
+ atf_process_stream_t inherit_outsb, inherit_errsb;
+ const atf_process_stream_t *real_outsb, *real_errsb;
+
+ real_outsb = NULL; /* Shut up GCC warning. */
+ err = init_stream_w_default(outsb, &inherit_outsb, &real_outsb);
+ if (atf_is_error(err))
+ goto out;
+
+ real_errsb = NULL; /* Shut up GCC warning. */
+ err = init_stream_w_default(errsb, &inherit_errsb, &real_errsb);
+ if (atf_is_error(err))
+ goto out_out;
+
+ err = fork_with_streams(c, start, real_outsb, real_errsb, v);
+
+ if (errsb == NULL)
+ atf_process_stream_fini(&inherit_errsb);
+out_out:
+ if (outsb == NULL)
+ atf_process_stream_fini(&inherit_outsb);
+out:
+ return err;
+}
+
+static
+int
+const_execvp(const char *file, const char *const *argv)
+{
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+ return execvp(file, UNCONST(argv));
+#undef UNCONST
+}
+
+static
+atf_error_t
+list_to_array(const atf_list_t *l, const char ***ap)
+{
+ atf_error_t err;
+ const char **a;
+
+ a = (const char **)malloc((atf_list_size(l) + 1) * sizeof(const char *));
+ if (a == NULL)
+ err = atf_no_memory_error();
+ else {
+ const char **aiter;
+ atf_list_citer_t liter;
+
+ aiter = a;
+ atf_list_for_each_c(liter, l) {
+ *aiter = (const char *)atf_list_citer_data(liter);
+ aiter++;
+ }
+ *aiter = NULL;
+
+ err = atf_no_error();
+ *ap = a;
+ }
+
+ return err;
+}
+
+struct exec_args {
+ const atf_fs_path_t *m_prog;
+ const char *const *m_argv;
+ void (*m_prehook)(void);
+};
+
+static
+void
+do_exec(void *v)
+{
+ struct exec_args *ea = v;
+
+ if (ea->m_prehook != NULL)
+ ea->m_prehook();
+
+ const int ret = const_execvp(atf_fs_path_cstring(ea->m_prog), ea->m_argv);
+ const int errnocopy = errno;
+ INV(ret == -1);
+ fprintf(stderr, "exec(%s) failed: %s\n",
+ atf_fs_path_cstring(ea->m_prog), strerror(errnocopy));
+ exit(EXIT_FAILURE);
+}
+
+atf_error_t
+atf_process_exec_array(atf_process_status_t *s,
+ const atf_fs_path_t *prog,
+ const char *const *argv,
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void (*prehook)(void))
+{
+ atf_error_t err;
+ atf_process_child_t c;
+ struct exec_args ea = { prog, argv, prehook };
+
+ PRE(outsb == NULL ||
+ atf_process_stream_type(outsb) != atf_process_stream_type_capture);
+ PRE(errsb == NULL ||
+ atf_process_stream_type(errsb) != atf_process_stream_type_capture);
+
+ err = atf_process_fork(&c, do_exec, outsb, errsb, &ea);
+ if (atf_is_error(err))
+ goto out;
+
+again:
+ err = atf_process_child_wait(&c, s);
+ if (atf_is_error(err)) {
+ INV(atf_error_is(err, "libc") && atf_libc_error_code(err) == EINTR);
+ atf_error_free(err);
+ goto again;
+ }
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_process_exec_list(atf_process_status_t *s,
+ const atf_fs_path_t *prog,
+ const atf_list_t *argv,
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void (*prehook)(void))
+{
+ atf_error_t err;
+ const char **argv2;
+
+ PRE(outsb == NULL ||
+ atf_process_stream_type(outsb) != atf_process_stream_type_capture);
+ PRE(errsb == NULL ||
+ atf_process_stream_type(errsb) != atf_process_stream_type_capture);
+
+ argv2 = NULL; /* Silence GCC warning. */
+ err = list_to_array(argv, &argv2);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_process_exec_array(s, prog, argv2, outsb, errsb, prehook);
+
+ free(argv2);
+out:
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/process.h b/unit/atf-src/atf-c/detail/process.h
new file mode 100644
index 0000000..81158d0
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process.h
@@ -0,0 +1,131 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_PROCESS_H)
+#define ATF_C_DETAIL_PROCESS_H
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+
+#include <atf-c/detail/fs.h>
+#include <atf-c/detail/list.h>
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_stream" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_stream {
+ int m_type;
+
+ /* Valid if m_type == connect. */
+ int m_src_fd;
+ int m_tgt_fd;
+
+ /* Valid if m_type == redirect_fd. */
+ int m_fd;
+
+ /* Valid if m_type == redirect_path. */
+ const atf_fs_path_t *m_path;
+};
+typedef struct atf_process_stream atf_process_stream_t;
+
+extern const int atf_process_stream_type_capture;
+extern const int atf_process_stream_type_connect;
+extern const int atf_process_stream_type_inherit;
+extern const int atf_process_stream_type_redirect_fd;
+extern const int atf_process_stream_type_redirect_path;
+
+atf_error_t atf_process_stream_init_capture(atf_process_stream_t *);
+atf_error_t atf_process_stream_init_connect(atf_process_stream_t *,
+ const int, const int);
+atf_error_t atf_process_stream_init_inherit(atf_process_stream_t *);
+atf_error_t atf_process_stream_init_redirect_fd(atf_process_stream_t *,
+ const int fd);
+atf_error_t atf_process_stream_init_redirect_path(atf_process_stream_t *,
+ const atf_fs_path_t *);
+void atf_process_stream_fini(atf_process_stream_t *);
+
+int atf_process_stream_type(const atf_process_stream_t *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_status" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_status {
+ int m_status;
+};
+typedef struct atf_process_status atf_process_status_t;
+
+void atf_process_status_fini(atf_process_status_t *);
+
+bool atf_process_status_exited(const atf_process_status_t *);
+int atf_process_status_exitstatus(const atf_process_status_t *);
+bool atf_process_status_signaled(const atf_process_status_t *);
+int atf_process_status_termsig(const atf_process_status_t *);
+bool atf_process_status_coredump(const atf_process_status_t *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_child" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_child {
+ pid_t m_pid;
+
+ int m_stdout;
+ int m_stderr;
+};
+typedef struct atf_process_child atf_process_child_t;
+
+atf_error_t atf_process_child_wait(atf_process_child_t *,
+ atf_process_status_t *);
+pid_t atf_process_child_pid(const atf_process_child_t *);
+int atf_process_child_stdout(atf_process_child_t *);
+int atf_process_child_stderr(atf_process_child_t *);
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+atf_error_t atf_process_fork(atf_process_child_t *,
+ void (*)(void *),
+ const atf_process_stream_t *,
+ const atf_process_stream_t *,
+ void *);
+atf_error_t atf_process_exec_array(atf_process_status_t *,
+ const atf_fs_path_t *,
+ const char *const *,
+ const atf_process_stream_t *,
+ const atf_process_stream_t *,
+ void (*)(void));
+atf_error_t atf_process_exec_list(atf_process_status_t *,
+ const atf_fs_path_t *,
+ const atf_list_t *,
+ const atf_process_stream_t *,
+ const atf_process_stream_t *,
+ void (*)(void));
+
+#endif /* !defined(ATF_C_DETAIL_PROCESS_H) */
diff --git a/unit/atf-src/atf-c/detail/process_helpers.c b/unit/atf-src/atf-c/detail/process_helpers.c
new file mode 100644
index 0000000..47b90bc
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process_helpers.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include <sys/types.h>
+
+#include <assert.h> /* NO_CHECK_STYLE */
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static
+int
+h_echo(const char *msg)
+{
+ printf("%s\n", msg);
+ return EXIT_SUCCESS;
+}
+
+static
+int
+h_exit_failure(void)
+{
+ return EXIT_FAILURE;
+}
+
+static
+int
+h_exit_signal(void)
+{
+ kill(getpid(), SIGKILL);
+ assert(0); /* NO_CHECK_STYLE */
+ return EXIT_FAILURE;
+}
+
+static
+int
+h_exit_success(void)
+{
+ return EXIT_SUCCESS;
+}
+
+static
+int
+h_stdout_stderr(const char *id)
+{
+ fprintf(stdout, "Line 1 to stdout for %s\n", id);
+ fprintf(stdout, "Line 2 to stdout for %s\n", id);
+ fprintf(stderr, "Line 1 to stderr for %s\n", id);
+ fprintf(stderr, "Line 2 to stderr for %s\n", id);
+
+ return EXIT_SUCCESS;
+}
+
+static
+void
+check_args(const int argc, const char *const argv[], const int required)
+{
+ if (argc < required) {
+ fprintf(stderr, "Usage: %s helper-name [args]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int
+main(int argc, const char *const argv[])
+{
+ int exitcode;
+
+ check_args(argc, argv, 2);
+
+ if (strcmp(argv[1], "echo") == 0) {
+ check_args(argc, argv, 3);
+ exitcode = h_echo(argv[2]);
+ } else if (strcmp(argv[1], "exit-failure") == 0)
+ exitcode = h_exit_failure();
+ else if (strcmp(argv[1], "exit-signal") == 0)
+ exitcode = h_exit_signal();
+ else if (strcmp(argv[1], "exit-success") == 0)
+ exitcode = h_exit_success();
+ else if (strcmp(argv[1], "stdout-stderr") == 0) {
+ check_args(argc, argv, 3);
+ exitcode = h_stdout_stderr(argv[2]);
+ } else {
+ fprintf(stderr, "%s: Unknown helper %s\n", argv[0], argv[1]);
+ exitcode = EXIT_FAILURE;
+ }
+
+ return exitcode;
+}
diff --git a/unit/atf-src/atf-c/detail/process_test.c b/unit/atf-src/atf-c/detail/process_test.c
new file mode 100644
index 0000000..5ae5565
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process_test.c
@@ -0,0 +1,1159 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/process.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/test_helpers.h"
+
+atf_error_t atf_process_status_init(atf_process_status_t *, int);
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions for testing of 'atf_process_fork'.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Testing of atf_process_fork is quite messy. We want to be able to test
+ * all the possible combinations of stdout and stderr behavior to ensure
+ * that the streams are manipulated correctly.
+ *
+ * To do this, the do_fork function is a wrapper for atf_process_fork that
+ * issues stream-specific hooks before fork, while the child is running and
+ * after the child terminates. We then provide test cases that just call
+ * do_fork with different hooks.
+ *
+ * The hooks are described by base_stream, and we then have one *_stream
+ * type for ever possible stream behavior.
+ */
+
+enum out_type { stdout_type, stderr_type };
+
+struct base_stream {
+ void (*init)(void *);
+ void (*process)(void *, atf_process_child_t *);
+ void (*fini)(void *);
+
+ /* m_sb is initialized by subclasses that need it, but all consumers
+ * must use m_sb_ptr, which may or may not point to m_sb. This allows
+ * us to test the interface with a NULL value, which triggers a
+ * default behavior. */
+ atf_process_stream_t m_sb;
+ atf_process_stream_t *m_sb_ptr;
+ enum out_type m_type;
+};
+#define BASE_STREAM(ihook, phook, fhook, type) \
+ { .init = ihook, \
+ .process = phook, \
+ .fini = fhook, \
+ .m_type = type }
+
+static
+void
+check_file(const enum out_type type)
+{
+ switch (type) {
+ case stdout_type:
+ ATF_CHECK(atf_utils_grep_file("stdout: msg", "stdout"));
+ ATF_CHECK(!atf_utils_grep_file("stderr: msg", "stdout"));
+ break;
+ case stderr_type:
+ ATF_CHECK(atf_utils_grep_file("stderr: msg", "stderr"));
+ ATF_CHECK(!atf_utils_grep_file("stdout: msg", "stderr"));
+ break;
+ default:
+ UNREACHABLE;
+ }
+}
+
+struct capture_stream {
+ struct base_stream m_base;
+
+ char *m_msg;
+};
+#define CAPTURE_STREAM(type) \
+ { .m_base = BASE_STREAM(capture_stream_init, \
+ capture_stream_process, \
+ capture_stream_fini, \
+ type) }
+
+static
+void
+capture_stream_init(void *v)
+{
+ struct capture_stream *s = v;
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_capture(&s->m_base.m_sb));
+ s->m_msg = NULL;
+}
+
+static
+void
+capture_stream_process(void *v, atf_process_child_t *c)
+{
+ struct capture_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ s->m_msg = atf_utils_readline(atf_process_child_stdout(c));
+ break;
+ case stderr_type:
+ s->m_msg = atf_utils_readline(atf_process_child_stderr(c));
+ break;
+ default:
+ UNREACHABLE;
+ }
+}
+
+static
+void
+capture_stream_fini(void *v)
+{
+ struct capture_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ ATF_CHECK(atf_utils_grep_string("stdout: msg", s->m_msg));
+ ATF_CHECK(!atf_utils_grep_string("stderr: msg", s->m_msg));
+ break;
+ case stderr_type:
+ ATF_CHECK(!atf_utils_grep_string("stdout: msg", s->m_msg));
+ ATF_CHECK(atf_utils_grep_string("stderr: msg", s->m_msg));
+ break;
+ default:
+ UNREACHABLE;
+ }
+
+ free(s->m_msg);
+ atf_process_stream_fini(&s->m_base.m_sb);
+}
+
+struct connect_stream {
+ struct base_stream m_base;
+
+ int m_fd;
+};
+#define CONNECT_STREAM(type) \
+ { .m_base = BASE_STREAM(connect_stream_init, \
+ NULL, \
+ connect_stream_fini, \
+ type) }
+
+static
+void
+connect_stream_init(void *v)
+{
+ struct connect_stream *s = v;
+ int src_fd;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ src_fd = STDOUT_FILENO;
+ s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ case stderr_type:
+ src_fd = STDERR_FILENO;
+ s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ default:
+ UNREACHABLE;
+ src_fd = -1;
+ }
+ ATF_REQUIRE(s->m_fd != -1);
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_connect(&s->m_base.m_sb, src_fd, s->m_fd));
+}
+
+static
+void
+connect_stream_fini(void *v)
+{
+ struct connect_stream *s = v;
+
+ ATF_REQUIRE(close(s->m_fd) != -1);
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ check_file(s->m_base.m_type);
+}
+
+struct inherit_stream {
+ struct base_stream m_base;
+ int m_fd;
+
+ int m_old_fd;
+};
+#define INHERIT_STREAM(type) \
+ { .m_base = BASE_STREAM(inherit_stream_init, \
+ NULL, \
+ inherit_stream_fini, \
+ type) }
+
+static
+void
+inherit_stream_init(void *v)
+{
+ struct inherit_stream *s = v;
+ const char *name;
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_inherit(&s->m_base.m_sb));
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ s->m_fd = STDOUT_FILENO;
+ name = "stdout";
+ break;
+ case stderr_type:
+ s->m_fd = STDERR_FILENO;
+ name = "stderr";
+ break;
+ default:
+ UNREACHABLE;
+ name = NULL;
+ }
+
+ s->m_old_fd = dup(s->m_fd);
+ ATF_REQUIRE(s->m_old_fd != -1);
+ ATF_REQUIRE(close(s->m_fd) != -1);
+ ATF_REQUIRE_EQ(open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644),
+ s->m_fd);
+}
+
+static
+void
+inherit_stream_fini(void *v)
+{
+ struct inherit_stream *s = v;
+
+ ATF_REQUIRE(dup2(s->m_old_fd, s->m_fd) != -1);
+ ATF_REQUIRE(close(s->m_old_fd) != -1);
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ check_file(s->m_base.m_type);
+}
+
+#define default_stream inherit_stream
+#define DEFAULT_STREAM(type) \
+ { .m_base = BASE_STREAM(default_stream_init, \
+ NULL, \
+ default_stream_fini, \
+ type) }
+
+static
+void
+default_stream_init(void *v)
+{
+ struct inherit_stream *s = v;
+
+ inherit_stream_init(v);
+ s->m_base.m_sb_ptr = NULL;
+}
+
+static
+void
+default_stream_fini(void *v)
+{
+ inherit_stream_fini(v);
+}
+
+struct redirect_fd_stream {
+ struct base_stream m_base;
+
+ int m_fd;
+};
+#define REDIRECT_FD_STREAM(type) \
+ { .m_base = BASE_STREAM(redirect_fd_stream_init, \
+ NULL, \
+ redirect_fd_stream_fini, \
+ type) }
+
+static
+void
+redirect_fd_stream_init(void *v)
+{
+ struct redirect_fd_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ case stderr_type:
+ s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ default:
+ UNREACHABLE;
+ }
+ ATF_REQUIRE(s->m_fd != -1);
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_redirect_fd(&s->m_base.m_sb, s->m_fd));
+}
+
+static
+void
+redirect_fd_stream_fini(void *v)
+{
+ struct redirect_fd_stream *s = v;
+
+ ATF_REQUIRE(close(s->m_fd) != -1);
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ check_file(s->m_base.m_type);
+}
+
+struct redirect_path_stream {
+ struct base_stream m_base;
+
+ atf_fs_path_t m_path;
+};
+#define REDIRECT_PATH_STREAM(type) \
+ { .m_base = BASE_STREAM(redirect_path_stream_init, \
+ NULL, \
+ redirect_path_stream_fini, \
+ type) }
+
+static
+void
+redirect_path_stream_init(void *v)
+{
+ struct redirect_path_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ RE(atf_fs_path_init_fmt(&s->m_path, "stdout"));
+ break;
+ case stderr_type:
+ RE(atf_fs_path_init_fmt(&s->m_path, "stderr"));
+ break;
+ default:
+ UNREACHABLE;
+ }
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_redirect_path(&s->m_base.m_sb, &s->m_path));
+}
+
+static
+void
+redirect_path_stream_fini(void *v)
+{
+ struct redirect_path_stream *s = v;
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ atf_fs_path_fini(&s->m_path);
+
+ check_file(s->m_base.m_type);
+}
+
+static void child_print(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+struct child_print_data {
+ const char *m_msg;
+};
+
+static
+void
+child_print(void *v)
+{
+ struct child_print_data *cpd = v;
+
+ fprintf(stdout, "stdout: %s\n", cpd->m_msg);
+ fprintf(stderr, "stderr: %s\n", cpd->m_msg);
+
+ exit(EXIT_SUCCESS);
+}
+
+static
+void
+do_fork(const struct base_stream *outfs, void *out,
+ const struct base_stream *errfs, void *err)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+ struct child_print_data cpd = { "msg" };
+
+ outfs->init(out);
+ errfs->init(err);
+
+ RE(atf_process_fork(&child, child_print, outfs->m_sb_ptr,
+ errfs->m_sb_ptr, &cpd));
+ if (outfs->process != NULL)
+ outfs->process(out, &child);
+ if (errfs->process != NULL)
+ errfs->process(err, &child);
+ RE(atf_process_child_wait(&child, &status));
+
+ outfs->fini(out);
+ errfs->fini(err);
+
+ atf_process_status_fini(&status);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "stream" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(stream_init_capture);
+ATF_TC_HEAD(stream_init_capture, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_capture function");
+}
+ATF_TC_BODY(stream_init_capture, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_capture(&sb));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_capture);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_connect);
+ATF_TC_HEAD(stream_init_connect, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_connect function");
+}
+ATF_TC_BODY(stream_init_connect, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_connect(&sb, 1, 2));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_connect);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_inherit);
+ATF_TC_HEAD(stream_init_inherit, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_inherit function");
+}
+ATF_TC_BODY(stream_init_inherit, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_inherit(&sb));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_inherit);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_redirect_fd);
+ATF_TC_HEAD(stream_init_redirect_fd, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_redirect_fd function");
+}
+ATF_TC_BODY(stream_init_redirect_fd, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_redirect_fd(&sb, 1));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_redirect_fd);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_redirect_path);
+ATF_TC_HEAD(stream_init_redirect_path, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_redirect_path function");
+}
+ATF_TC_BODY(stream_init_redirect_path, tc)
+{
+ atf_process_stream_t sb;
+ atf_fs_path_t path;
+
+ RE(atf_fs_path_init_fmt(&path, "foo"));
+ RE(atf_process_stream_init_redirect_path(&sb, &path));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_redirect_path);
+
+ atf_process_stream_fini(&sb);
+ atf_fs_path_fini(&path);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "status" type.
+ * --------------------------------------------------------------------- */
+
+static void child_exit_success(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_exit_failure(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigkill(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigquit(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigterm(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+void
+child_exit_success(void)
+{
+ exit(EXIT_SUCCESS);
+}
+
+void
+child_exit_failure(void)
+{
+ exit(EXIT_FAILURE);
+}
+
+void
+child_sigkill(void)
+{
+ kill(getpid(), SIGKILL);
+ abort();
+}
+
+void
+child_sigquit(void)
+{
+ kill(getpid(), SIGQUIT);
+ abort();
+}
+
+void
+child_sigterm(void)
+{
+ kill(getpid(), SIGTERM);
+ abort();
+}
+
+static
+int
+fork_and_wait_child(void (*child_func)(void))
+{
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ ATF_REQUIRE(pid != -1);
+ if (pid == 0) {
+ status = 0; /* Silence compiler warnings */
+ child_func();
+ UNREACHABLE;
+ } else {
+ ATF_REQUIRE(waitpid(pid, &status, 0) != 0);
+ }
+
+ return status;
+}
+
+ATF_TC(status_exited);
+ATF_TC_HEAD(status_exited, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that exit cleanly");
+}
+ATF_TC_BODY(status_exited, tc)
+{
+ {
+ const int rawstatus = fork_and_wait_child(child_exit_success);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(atf_process_status_exited(&s));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_SUCCESS);
+ ATF_CHECK(!atf_process_status_signaled(&s));
+ atf_process_status_fini(&s);
+ }
+
+ {
+ const int rawstatus = fork_and_wait_child(child_exit_failure);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(atf_process_status_exited(&s));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_FAILURE);
+ ATF_CHECK(!atf_process_status_signaled(&s));
+ atf_process_status_fini(&s);
+ }
+}
+
+ATF_TC(status_signaled);
+ATF_TC_HEAD(status_signaled, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that end due to a signal");
+}
+ATF_TC_BODY(status_signaled, tc)
+{
+ {
+ const int rawstatus = fork_and_wait_child(child_sigkill);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGKILL);
+ ATF_CHECK(!atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+ }
+
+ {
+ const int rawstatus = fork_and_wait_child(child_sigterm);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGTERM);
+ ATF_CHECK(!atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+ }
+}
+
+ATF_TC(status_coredump);
+ATF_TC_HEAD(status_coredump, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that crash");
+}
+ATF_TC_BODY(status_coredump, tc)
+{
+ struct rlimit rl;
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ atf_tc_skip("Cannot unlimit the core file size; check limits "
+ "manually");
+
+ const int rawstatus = fork_and_wait_child(child_sigquit);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGQUIT);
+ ATF_CHECK(atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "child" type.
+ * --------------------------------------------------------------------- */
+
+static void child_report_pid(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+child_report_pid(void *v ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ const pid_t pid = getpid();
+ if (write(STDOUT_FILENO, &pid, sizeof(pid)) != sizeof(pid))
+ abort();
+ fprintf(stderr, "Reporting %d to parent\n", (int)getpid());
+ exit(EXIT_SUCCESS);
+}
+
+ATF_TC(child_pid);
+ATF_TC_HEAD(child_pid, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the correctness of the pid "
+ "stored in the child type");
+}
+ATF_TC_BODY(child_pid, tc)
+{
+ atf_process_stream_t outsb, errsb;
+ atf_process_child_t child;
+ atf_process_status_t status;
+ pid_t pid;
+
+ RE(atf_process_stream_init_capture(&outsb));
+ RE(atf_process_stream_init_inherit(&errsb));
+
+ RE(atf_process_fork(&child, child_report_pid, &outsb, &errsb, NULL));
+ ATF_CHECK_EQ(read(atf_process_child_stdout(&child), &pid, sizeof(pid)),
+ sizeof(pid));
+ printf("Expected PID: %d\n", (int)atf_process_child_pid(&child));
+ printf("Actual PID: %d\n", (int)pid);
+ ATF_CHECK_EQ(atf_process_child_pid(&child), pid);
+
+ RE(atf_process_child_wait(&child, &status));
+ atf_process_status_fini(&status);
+
+ atf_process_stream_fini(&outsb);
+ atf_process_stream_fini(&errsb);
+}
+
+static
+void
+child_loop(void *v ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ for (;;)
+ sleep(1);
+}
+
+static
+void
+nop_signal(int sig ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+static
+void
+child_spawn_loop_and_wait_eintr(void *v ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+ struct sigaction sighup, old_sighup;
+
+#define RE_ABORT(expr) \
+ do { \
+ atf_error_t _aux_err = expr; \
+ if (atf_is_error(_aux_err)) { \
+ atf_error_free(_aux_err); \
+ abort(); \
+ } \
+ } while (0)
+
+ {
+ atf_process_stream_t outsb, errsb;
+
+ RE_ABORT(atf_process_stream_init_capture(&outsb));
+ RE_ABORT(atf_process_stream_init_inherit(&errsb));
+ RE_ABORT(atf_process_fork(&child, child_loop, &outsb, &errsb, NULL));
+ atf_process_stream_fini(&outsb);
+ atf_process_stream_fini(&errsb);
+ }
+
+ sighup.sa_handler = nop_signal;
+ sigemptyset(&sighup.sa_mask);
+ sighup.sa_flags = 0;
+ if (sigaction(SIGHUP, &sighup, &old_sighup) == -1)
+ abort();
+
+ printf("waiting\n");
+ fflush(stdout);
+
+ fprintf(stderr, "Child entering wait(2)\n");
+ atf_error_t err = atf_process_child_wait(&child, &status);
+ fprintf(stderr, "Child's wait(2) terminated\n");
+ if (!atf_is_error(err)) {
+ fprintf(stderr, "wait completed successfully (not interrupted)\n");
+ abort();
+ }
+ if (!atf_error_is(err, "libc")) {
+ fprintf(stderr, "wait did not raise libc_error\n");
+ abort();
+ }
+ if (atf_libc_error_code(err) != EINTR) {
+ fprintf(stderr, "libc_error is not EINTR\n");
+ abort();
+ }
+ atf_error_free(err);
+
+ sigaction(SIGHUP, &old_sighup, NULL);
+
+ fprintf(stderr, "Child is killing subchild\n");
+ kill(atf_process_child_pid(&child), SIGTERM);
+
+ RE_ABORT(atf_process_child_wait(&child, &status));
+ atf_process_status_fini(&status);
+
+#undef RE_ABORT
+
+ exit(EXIT_SUCCESS);
+}
+
+ATF_TC(child_wait_eintr);
+ATF_TC_HEAD(child_wait_eintr, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the interruption of the wait "
+ "method by an external signal, and the return of "
+ "an EINTR error");
+ atf_tc_set_md_var(tc, "timeout", "30");
+}
+ATF_TC_BODY(child_wait_eintr, tc)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ {
+ atf_process_stream_t outsb, errsb;
+
+ RE(atf_process_stream_init_capture(&outsb));
+ RE(atf_process_stream_init_inherit(&errsb));
+ RE(atf_process_fork(&child, child_spawn_loop_and_wait_eintr,
+ &outsb, &errsb, NULL));
+ atf_process_stream_fini(&outsb);
+ atf_process_stream_fini(&errsb);
+ }
+
+ {
+ /* Wait until the child process performs the wait call. This is
+ * racy, because the message we get from it is sent *before*
+ * doing the real system call... but I can't figure any other way
+ * to do this. */
+ char buf[16];
+ printf("Waiting for child to issue wait(2)\n");
+ ATF_REQUIRE(read(atf_process_child_stdout(&child), buf,
+ sizeof(buf)) > 0);
+ sleep(1);
+ }
+
+ printf("Interrupting child's wait(2) call\n");
+ kill(atf_process_child_pid(&child), SIGHUP);
+
+ printf("Waiting for child's completion\n");
+ RE(atf_process_child_wait(&child, &status));
+ ATF_REQUIRE(atf_process_status_exited(&status));
+ ATF_REQUIRE_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS);
+ atf_process_status_fini(&status);
+}
+
+/* ---------------------------------------------------------------------
+ * Tests cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+do_exec(const atf_tc_t *tc, const char *helper_name, atf_process_status_t *s,
+ void (*prehook)(void))
+{
+ atf_fs_path_t process_helpers;
+ const char *argv[3];
+
+ get_process_helpers_path(tc, true, &process_helpers);
+
+ argv[0] = atf_fs_path_cstring(&process_helpers);
+ argv[1] = helper_name;
+ argv[2] = NULL;
+ printf("Executing %s %s\n", argv[0], argv[1]);
+
+ RE(atf_process_exec_array(s, &process_helpers, argv, NULL, NULL, prehook));
+ atf_fs_path_fini(&process_helpers);
+}
+
+static
+void
+check_line(int fd, const char *exp)
+{
+ char *line = atf_utils_readline(fd);
+ ATF_CHECK(line != NULL);
+ ATF_CHECK_STREQ_MSG(exp, line, "read: '%s', expected: '%s'", line, exp);
+ free(line);
+}
+
+ATF_TC(exec_failure);
+ATF_TC_HEAD(exec_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests execing a command");
+}
+ATF_TC_BODY(exec_failure, tc)
+{
+ atf_process_status_t status;
+
+ do_exec(tc, "exit-failure", &status, NULL);
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_FAILURE);
+ atf_process_status_fini(&status);
+}
+
+ATF_TC(exec_list);
+ATF_TC_HEAD(exec_list, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests execing a command");
+}
+ATF_TC_BODY(exec_list, tc)
+{
+ atf_fs_path_t process_helpers;
+ atf_list_t argv;
+ atf_process_status_t status;
+
+ RE(atf_list_init(&argv));
+
+ get_process_helpers_path(tc, true, &process_helpers);
+ atf_list_append(&argv, strdup(atf_fs_path_cstring(&process_helpers)), true);
+ atf_list_append(&argv, strdup("echo"), true);
+ atf_list_append(&argv, strdup("test-message"), true);
+ {
+ atf_fs_path_t outpath;
+ atf_process_stream_t outsb;
+
+ RE(atf_fs_path_init_fmt(&outpath, "stdout"));
+ RE(atf_process_stream_init_redirect_path(&outsb, &outpath));
+ RE(atf_process_exec_list(&status, &process_helpers, &argv, &outsb,
+ NULL, NULL));
+ atf_process_stream_fini(&outsb);
+ atf_fs_path_fini(&outpath);
+ }
+ atf_list_fini(&argv);
+
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS);
+
+ {
+ int fd = open("stdout", O_RDONLY);
+ ATF_CHECK(fd != -1);
+ check_line(fd, "test-message");
+ close(fd);
+ }
+
+ atf_process_status_fini(&status);
+ atf_fs_path_fini(&process_helpers);
+}
+
+static void
+exit_early(void)
+{
+ exit(80);
+}
+
+ATF_TC(exec_prehook);
+ATF_TC_HEAD(exec_prehook, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests execing a command with a prehook");
+}
+ATF_TC_BODY(exec_prehook, tc)
+{
+ atf_process_status_t status;
+
+ do_exec(tc, "exit-success", &status, exit_early);
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), 80);
+ atf_process_status_fini(&status);
+}
+
+ATF_TC(exec_success);
+ATF_TC_HEAD(exec_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests execing a command");
+}
+ATF_TC_BODY(exec_success, tc)
+{
+ atf_process_status_t status;
+
+ do_exec(tc, "exit-success", &status, NULL);
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS);
+ atf_process_status_fini(&status);
+}
+
+static const int exit_v_null = 1;
+static const int exit_v_notnull = 2;
+
+static
+void
+child_cookie(void *v)
+{
+ if (v == NULL)
+ exit(exit_v_null);
+ else
+ exit(exit_v_notnull);
+
+ UNREACHABLE;
+}
+
+ATF_TC(fork_cookie);
+ATF_TC_HEAD(fork_cookie, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with "
+ "a null and non-null data cookie");
+}
+ATF_TC_BODY(fork_cookie, tc)
+{
+ atf_process_stream_t outsb, errsb;
+
+ RE(atf_process_stream_init_inherit(&outsb));
+ RE(atf_process_stream_init_inherit(&errsb));
+
+ {
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, NULL));
+ RE(atf_process_child_wait(&child, &status));
+
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_null);
+
+ atf_process_status_fini(&status);
+ }
+
+ {
+ atf_process_child_t child;
+ atf_process_status_t status;
+ int dummy_int;
+
+ RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, &dummy_int));
+ RE(atf_process_child_wait(&child, &status));
+
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_notnull);
+
+ atf_process_status_fini(&status);
+ }
+
+ atf_process_stream_fini(&errsb);
+ atf_process_stream_fini(&outsb);
+}
+
+#define TC_FORK_STREAMS(outlc, outuc, errlc, erruc) \
+ ATF_TC(fork_out_ ## outlc ## _err_ ## errlc); \
+ ATF_TC_HEAD(fork_out_ ## outlc ## _err_ ## errlc, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with " \
+ "stdout " #outlc " and stderr " #errlc); \
+ } \
+ ATF_TC_BODY(fork_out_ ## outlc ## _err_ ## errlc, tc) \
+ { \
+ struct outlc ## _stream out = outuc ## _STREAM(stdout_type); \
+ struct errlc ## _stream err = erruc ## _STREAM(stderr_type); \
+ do_fork(&out.m_base, &out, &err.m_base, &err); \
+ }
+
+TC_FORK_STREAMS(capture, CAPTURE, capture, CAPTURE);
+TC_FORK_STREAMS(capture, CAPTURE, connect, CONNECT);
+TC_FORK_STREAMS(capture, CAPTURE, default, DEFAULT);
+TC_FORK_STREAMS(capture, CAPTURE, inherit, INHERIT);
+TC_FORK_STREAMS(capture, CAPTURE, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(capture, CAPTURE, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(connect, CONNECT, capture, CAPTURE);
+TC_FORK_STREAMS(connect, CONNECT, connect, CONNECT);
+TC_FORK_STREAMS(connect, CONNECT, default, DEFAULT);
+TC_FORK_STREAMS(connect, CONNECT, inherit, INHERIT);
+TC_FORK_STREAMS(connect, CONNECT, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(connect, CONNECT, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(default, DEFAULT, capture, CAPTURE);
+TC_FORK_STREAMS(default, DEFAULT, connect, CONNECT);
+TC_FORK_STREAMS(default, DEFAULT, default, DEFAULT);
+TC_FORK_STREAMS(default, DEFAULT, inherit, INHERIT);
+TC_FORK_STREAMS(default, DEFAULT, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(default, DEFAULT, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(inherit, INHERIT, capture, CAPTURE);
+TC_FORK_STREAMS(inherit, INHERIT, connect, CONNECT);
+TC_FORK_STREAMS(inherit, INHERIT, default, DEFAULT);
+TC_FORK_STREAMS(inherit, INHERIT, inherit, INHERIT);
+TC_FORK_STREAMS(inherit, INHERIT, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(inherit, INHERIT, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, capture, CAPTURE);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, connect, CONNECT);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, default, DEFAULT);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, inherit, INHERIT);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, capture, CAPTURE);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, connect, CONNECT);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, default, DEFAULT);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, inherit, INHERIT);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, redirect_path, REDIRECT_PATH);
+
+#undef TC_FORK_STREAMS
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the tests for the "stream" type. */
+ ATF_TP_ADD_TC(tp, stream_init_capture);
+ ATF_TP_ADD_TC(tp, stream_init_connect);
+ ATF_TP_ADD_TC(tp, stream_init_inherit);
+ ATF_TP_ADD_TC(tp, stream_init_redirect_fd);
+ ATF_TP_ADD_TC(tp, stream_init_redirect_path);
+
+ /* Add the tests for the "status" type. */
+ ATF_TP_ADD_TC(tp, status_exited);
+ ATF_TP_ADD_TC(tp, status_signaled);
+ ATF_TP_ADD_TC(tp, status_coredump);
+
+ /* Add the tests for the "child" type. */
+ ATF_TP_ADD_TC(tp, child_pid);
+ ATF_TP_ADD_TC(tp, child_wait_eintr);
+
+ /* Add the tests for the free functions. */
+ ATF_TP_ADD_TC(tp, exec_failure);
+ ATF_TP_ADD_TC(tp, exec_list);
+ ATF_TP_ADD_TC(tp, exec_prehook);
+ ATF_TP_ADD_TC(tp, exec_success);
+ ATF_TP_ADD_TC(tp, fork_cookie);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_redirect_path);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/sanity.c b/unit/atf-src/atf-c/detail/sanity.c
new file mode 100644
index 0000000..cfc8676
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/sanity.c
@@ -0,0 +1,74 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/sanity.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <err.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static
+void
+fail(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[4096];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ warnx("%s", buf);
+ warnx("%s", "");
+ warnx("This is probably a bug in this application or one of the "
+ "libraries it uses. If you believe this problem is caused "
+ "by, or is related to " PACKAGE_STRING ", please report it "
+ "to " PACKAGE_BUGREPORT " and provide as many details as "
+ "possible describing how you got to this condition.");
+
+ abort();
+}
+
+void
+atf_sanity_inv(const char *file, int line, const char *cond)
+{
+ fail("Invariant check failed at %s:%d: %s", file, line, cond);
+}
+
+void
+atf_sanity_pre(const char *file, int line, const char *cond)
+{
+ fail("Precondition check failed at %s:%d: %s", file, line, cond);
+}
+
+void
+atf_sanity_post(const char *file, int line, const char *cond)
+{
+ fail("Postcondition check failed at %s:%d: %s", file, line, cond);
+}
diff --git a/unit/atf-src/atf-c/detail/sanity.h b/unit/atf-src/atf-c/detail/sanity.h
new file mode 100644
index 0000000..33b5528
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/sanity.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_SANITY_H)
+#define ATF_C_DETAIL_SANITY_H
+
+void atf_sanity_inv(const char *, int, const char *);
+void atf_sanity_pre(const char *, int, const char *);
+void atf_sanity_post(const char *, int, const char *);
+
+#if !defined(NDEBUG)
+
+#define INV(x) \
+ do { \
+ if (!(x)) \
+ atf_sanity_inv(__FILE__, __LINE__, #x); \
+ } while (0)
+#define PRE(x) \
+ do { \
+ if (!(x)) \
+ atf_sanity_pre(__FILE__, __LINE__, #x); \
+ } while (0)
+#define POST(x) \
+ do { \
+ if (!(x)) \
+ atf_sanity_post(__FILE__, __LINE__, #x); \
+ } while (0)
+
+#else /* defined(NDEBUG) */
+
+#define INV(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+
+#define PRE(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+
+#define POST(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+
+#endif /* !defined(NDEBUG) */
+
+#define UNREACHABLE INV(0)
+
+#endif /* !defined(ATF_C_DETAIL_SANITY_H) */
diff --git a/unit/atf-src/atf-c/detail/sanity_test.c b/unit/atf-src/atf-c/detail/sanity_test.c
new file mode 100644
index 0000000..a986c32
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/sanity_test.c
@@ -0,0 +1,230 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/sanity.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/process.h"
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+enum type { inv, pre, post, unreachable };
+
+struct test_data {
+ enum type m_type;
+ bool m_cond;
+};
+
+static void do_test_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+do_test_child(void *v)
+{
+ struct test_data *td = v;
+
+ switch (td->m_type) {
+ case inv:
+ INV(td->m_cond);
+ break;
+
+ case pre:
+ PRE(td->m_cond);
+ break;
+
+ case post:
+ POST(td->m_cond);
+ break;
+
+ case unreachable:
+ if (!td->m_cond)
+ UNREACHABLE;
+ break;
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static
+void
+do_test(enum type t, bool cond)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+ int nlines;
+ char *lines[3];
+
+ {
+ atf_process_stream_t outsb, errsb;
+ struct test_data td = { t, cond };
+
+ RE(atf_process_stream_init_inherit(&outsb));
+ RE(atf_process_stream_init_capture(&errsb));
+ RE(atf_process_fork(&child, do_test_child, &outsb, &errsb, &td));
+ atf_process_stream_fini(&errsb);
+ atf_process_stream_fini(&outsb);
+ }
+
+ nlines = 0;
+ while (nlines < 3 && (lines[nlines] =
+ atf_utils_readline(atf_process_child_stderr(&child))) != NULL)
+ nlines++;
+ ATF_REQUIRE(nlines == 0 || nlines == 3);
+
+ RE(atf_process_child_wait(&child, &status));
+ if (!cond) {
+ ATF_REQUIRE(atf_process_status_signaled(&status));
+ ATF_REQUIRE(atf_process_status_termsig(&status) == SIGABRT);
+ } else {
+ ATF_REQUIRE(atf_process_status_exited(&status));
+ ATF_REQUIRE(atf_process_status_exitstatus(&status) == EXIT_SUCCESS);
+ }
+ atf_process_status_fini(&status);
+
+ if (!cond) {
+ switch (t) {
+ case inv:
+ ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0]));
+ break;
+
+ case pre:
+ ATF_REQUIRE(atf_utils_grep_string("Precondition", lines[0]));
+ break;
+
+ case post:
+ ATF_REQUIRE(atf_utils_grep_string("Postcondition", lines[0]));
+ break;
+
+ case unreachable:
+ ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0]));
+ break;
+ }
+
+ ATF_REQUIRE(atf_utils_grep_string(__FILE__, lines[0]));
+ ATF_REQUIRE(atf_utils_grep_string(PACKAGE_BUGREPORT, lines[2]));
+ }
+
+ while (nlines > 0) {
+ nlines--;
+ free(lines[nlines]);
+ }
+}
+
+static
+void
+require_ndebug(void)
+{
+#if defined(NDEBUG)
+ atf_tc_skip("Sanity checks not available; code built with -DNDEBUG");
+#endif
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(inv);
+ATF_TC_HEAD(inv, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the INV macro");
+}
+ATF_TC_BODY(inv, tc)
+{
+ require_ndebug();
+
+ do_test(inv, false);
+ do_test(inv, true);
+}
+
+ATF_TC(pre);
+ATF_TC_HEAD(pre, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the PRE macro");
+}
+ATF_TC_BODY(pre, tc)
+{
+ require_ndebug();
+
+ do_test(pre, false);
+ do_test(pre, true);
+}
+
+ATF_TC(post);
+ATF_TC_HEAD(post, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the POST macro");
+}
+ATF_TC_BODY(post, tc)
+{
+ require_ndebug();
+
+ do_test(post, false);
+ do_test(post, true);
+}
+
+ATF_TC(unreachable);
+ATF_TC_HEAD(unreachable, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the UNREACHABLE macro");
+}
+ATF_TC_BODY(unreachable, tc)
+{
+ require_ndebug();
+
+ do_test(unreachable, false);
+ do_test(unreachable, true);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, inv);
+ ATF_TP_ADD_TC(tp, pre);
+ ATF_TP_ADD_TC(tp, post);
+ ATF_TP_ADD_TC(tp, unreachable);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/test_helpers.c b/unit/atf-src/atf-c/detail/test_helpers.c
new file mode 100644
index 0000000..827d46e
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/test_helpers.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/test_helpers.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/build.h"
+#include "atf-c/check.h"
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/env.h"
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/process.h"
+#include "atf-c/error.h"
+
+bool
+build_check_c_o(const char *path)
+{
+ bool success;
+ atf_dynstr_t iflag;
+ const char *optargs[4];
+
+ RE(atf_dynstr_init_fmt(&iflag, "-I%s", atf_env_get_with_default(
+ "ATF_INCLUDEDIR", ATF_INCLUDEDIR)));
+
+ optargs[0] = atf_dynstr_cstring(&iflag);
+ optargs[1] = "-Wall";
+ optargs[2] = "-Werror";
+ optargs[3] = NULL;
+
+ RE(atf_check_build_c_o(path, "test.o", optargs, &success));
+
+ atf_dynstr_fini(&iflag);
+
+ return success;
+}
+
+bool
+build_check_c_o_srcdir(const atf_tc_t *tc, const char *sfile)
+{
+ atf_fs_path_t path;
+
+ RE(atf_fs_path_init_fmt(&path, "%s/%s",
+ atf_tc_get_config_var(tc, "srcdir"), sfile));
+ const bool result = build_check_c_o(atf_fs_path_cstring(&path));
+ atf_fs_path_fini(&path);
+ return result;
+}
+
+void
+header_check(const char *hdrname)
+{
+ FILE *srcfile;
+ char failmsg[128];
+
+ srcfile = fopen("test.c", "w");
+ ATF_REQUIRE(srcfile != NULL);
+ fprintf(srcfile, "#include <%s>\n", hdrname);
+ fclose(srcfile);
+
+ snprintf(failmsg, sizeof(failmsg),
+ "Header check failed; %s is not self-contained", hdrname);
+
+ if (!build_check_c_o("test.c"))
+ atf_tc_fail("%s", failmsg);
+}
+
+void
+get_process_helpers_path(const atf_tc_t *tc, const bool is_detail,
+ atf_fs_path_t *path)
+{
+ RE(atf_fs_path_init_fmt(path, "%s/%sprocess_helpers",
+ atf_tc_get_config_var(tc, "srcdir"),
+ is_detail ? "" : "detail/"));
+}
+
+struct run_h_tc_data {
+ atf_tc_t *m_tc;
+ const char *m_resname;
+};
+
+static
+void
+run_h_tc_child(void *v)
+{
+ struct run_h_tc_data *data = (struct run_h_tc_data *)v;
+
+ RE(atf_tc_run(data->m_tc, data->m_resname));
+}
+
+/* TODO: Investigate if it's worth to add this functionality as part of
+ * the public API. I.e. a function to easily run a test case body in a
+ * subprocess. */
+void
+run_h_tc(atf_tc_t *tc, const char *outname, const char *errname,
+ const char *resname)
+{
+ atf_fs_path_t outpath, errpath;
+ atf_process_stream_t outb, errb;
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ RE(atf_fs_path_init_fmt(&outpath, outname));
+ RE(atf_fs_path_init_fmt(&errpath, errname));
+
+ struct run_h_tc_data data = { tc, resname };
+
+ RE(atf_process_stream_init_redirect_path(&outb, &outpath));
+ RE(atf_process_stream_init_redirect_path(&errb, &errpath));
+ RE(atf_process_fork(&child, run_h_tc_child, &outb, &errb, &data));
+ atf_process_stream_fini(&errb);
+ atf_process_stream_fini(&outb);
+
+ RE(atf_process_child_wait(&child, &status));
+ ATF_CHECK(atf_process_status_exited(&status));
+ atf_process_status_fini(&status);
+
+ atf_fs_path_fini(&errpath);
+ atf_fs_path_fini(&outpath);
+}
diff --git a/unit/atf-src/atf-c/detail/test_helpers.h b/unit/atf-src/atf-c/detail/test_helpers.h
new file mode 100644
index 0000000..a601c29
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/test_helpers.h
@@ -0,0 +1,75 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if defined(ATF_C_DETAIL_TEST_HELPERS_H)
+# error "Cannot include test_helpers.h more than once."
+#else
+# define ATF_C_DETAIL_TEST_HELPERS_H
+#endif
+
+#include <stdbool.h>
+
+#include <atf-c.h>
+
+#include <atf-c/error_fwd.h>
+#include <atf-c/tc.h>
+
+struct atf_dynstr;
+struct atf_fs_path;
+
+#define CE(stm) ATF_CHECK(!atf_is_error(stm))
+#define RE(stm) ATF_REQUIRE(!atf_is_error(stm))
+
+#define HEADER_TC(name, hdrname) \
+ ATF_TC(name); \
+ ATF_TC_HEAD(name, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", "Tests that the " hdrname " file can " \
+ "be included on its own, without any prerequisites"); \
+ } \
+ ATF_TC_BODY(name, tc) \
+ { \
+ header_check(hdrname); \
+ }
+
+#define BUILD_TC(name, sfile, descr, failmsg) \
+ ATF_TC(name); \
+ ATF_TC_HEAD(name, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", descr); \
+ } \
+ ATF_TC_BODY(name, tc) \
+ { \
+ if (!build_check_c_o_srcdir(tc, sfile)) \
+ atf_tc_fail("%s", failmsg); \
+ }
+
+bool build_check_c_o(const char *);
+bool build_check_c_o_srcdir(const atf_tc_t *, const char *);
+void header_check(const char *);
+void get_process_helpers_path(const atf_tc_t *, const bool,
+ struct atf_fs_path *);
+bool read_line(int, struct atf_dynstr *);
+void run_h_tc(atf_tc_t *, const char *, const char *, const char *);
diff --git a/unit/atf-src/atf-c/detail/text.c b/unit/atf-src/atf-c/detail/text.c
new file mode 100644
index 0000000..3b1d7e7
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/text.c
@@ -0,0 +1,180 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/text.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+
+atf_error_t
+atf_text_for_each_word(const char *instr, const char *sep,
+ atf_error_t (*func)(const char *, void *),
+ void *data)
+{
+ atf_error_t err;
+ char *str, *str2, *last;
+
+ str = strdup(instr);
+ if (str == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ err = atf_no_error();
+ str2 = strtok_r(str, sep, &last);
+ while (str2 != NULL && !atf_is_error(err)) {
+ err = func(str2, data);
+ str2 = strtok_r(NULL, sep, &last);
+ }
+
+ free(str);
+out:
+ return err;
+}
+
+atf_error_t
+atf_text_format(char **dest, const char *fmt, ...)
+{
+ atf_error_t err;
+ va_list ap;
+
+ va_start(ap, fmt);
+ err = atf_text_format_ap(dest, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_text_format_ap(char **dest, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ atf_dynstr_t tmp;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = atf_dynstr_init_ap(&tmp, fmt, ap2);
+ va_end(ap2);
+ if (!atf_is_error(err))
+ *dest = atf_dynstr_fini_disown(&tmp);
+
+ return err;
+}
+
+atf_error_t
+atf_text_split(const char *str, const char *delim, atf_list_t *words)
+{
+ atf_error_t err;
+ const char *end;
+ const char *iter;
+
+ err = atf_list_init(words);
+ if (atf_is_error(err))
+ goto err;
+
+ end = str + strlen(str);
+ INV(*end == '\0');
+ iter = str;
+ while (iter < end) {
+ const char *ptr;
+
+ INV(iter != NULL);
+ ptr = strstr(iter, delim);
+ if (ptr == NULL)
+ ptr = end;
+
+ INV(ptr >= iter);
+ if (ptr > iter) {
+ atf_dynstr_t word;
+
+ err = atf_dynstr_init_raw(&word, iter, ptr - iter);
+ if (atf_is_error(err))
+ goto err_list;
+
+ err = atf_list_append(words, atf_dynstr_fini_disown(&word), true);
+ if (atf_is_error(err))
+ goto err_list;
+ }
+
+ iter = ptr + strlen(delim);
+ }
+
+ INV(!atf_is_error(err));
+ return err;
+
+err_list:
+ atf_list_fini(words);
+err:
+ return err;
+}
+
+atf_error_t
+atf_text_to_bool(const char *str, bool *b)
+{
+ atf_error_t err;
+
+ if (strcasecmp(str, "yes") == 0 ||
+ strcasecmp(str, "true") == 0) {
+ *b = true;
+ err = atf_no_error();
+ } else if (strcasecmp(str, "no") == 0 ||
+ strcasecmp(str, "false") == 0) {
+ *b = false;
+ err = atf_no_error();
+ } else {
+ /* XXX Not really a libc error. */
+ err = atf_libc_error(EINVAL, "Cannot convert string '%s' "
+ "to boolean", str);
+ }
+
+ return err;
+}
+
+atf_error_t
+atf_text_to_long(const char *str, long *l)
+{
+ atf_error_t err;
+ char *endptr;
+ long tmp;
+
+ errno = 0;
+ tmp = strtol(str, &endptr, 10);
+ if (str[0] == '\0' || *endptr != '\0')
+ err = atf_libc_error(EINVAL, "'%s' is not a number", str);
+ else if (errno == ERANGE || (tmp == LONG_MAX || tmp == LONG_MIN))
+ err = atf_libc_error(ERANGE, "'%s' is out of range", str);
+ else {
+ *l = tmp;
+ err = atf_no_error();
+ }
+
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/text.h b/unit/atf-src/atf-c/detail/text.h
new file mode 100644
index 0000000..b2820b6
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/text.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_TEXT_H)
+#define ATF_C_DETAIL_TEXT_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <atf-c/detail/list.h>
+#include <atf-c/error_fwd.h>
+
+atf_error_t atf_text_for_each_word(const char *, const char *,
+ atf_error_t (*)(const char *, void *),
+ void *);
+atf_error_t atf_text_format(char **, const char *, ...);
+atf_error_t atf_text_format_ap(char **, const char *, va_list);
+atf_error_t atf_text_split(const char *, const char *, atf_list_t *);
+atf_error_t atf_text_to_bool(const char *, bool *);
+atf_error_t atf_text_to_long(const char *, long *);
+
+#endif /* !defined(ATF_C_DETAIL_TEXT_H) */
diff --git a/unit/atf-src/atf-c/detail/text_test.c b/unit/atf-src/atf-c/detail/text_test.c
new file mode 100644
index 0000000..5d6c8e5
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/text_test.c
@@ -0,0 +1,421 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/text.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+#define REQUIRE_ERROR(exp) \
+ do { \
+ atf_error_t err = exp; \
+ ATF_REQUIRE(atf_is_error(err)); \
+ atf_error_free(err); \
+ } while (0)
+
+static
+size_t
+array_size(const char *words[])
+{
+ size_t count;
+ const char **word;
+
+ count = 0;
+ for (word = words; *word != NULL; word++)
+ count++;
+
+ return count;
+}
+
+static
+void
+check_split(const char *str, const char *delim, const char *words[])
+{
+ atf_list_t list;
+ const char **word;
+ size_t i;
+
+ printf("Splitting '%s' with delimiter '%s'\n", str, delim);
+ CE(atf_text_split(str, delim, &list));
+
+ printf("Expecting %zd words\n", array_size(words));
+ ATF_CHECK_EQ(atf_list_size(&list), array_size(words));
+
+ for (word = words, i = 0; *word != NULL; word++, i++) {
+ printf("Word at position %zd should be '%s'\n", i, words[i]);
+ ATF_CHECK_STREQ((const char *)atf_list_index_c(&list, i), words[i]);
+ }
+
+ atf_list_fini(&list);
+}
+
+static
+atf_error_t
+word_acum(const char *word, void *data)
+{
+ char *acum = data;
+
+ strcat(acum, word);
+
+ return atf_no_error();
+}
+
+static
+atf_error_t
+word_count(const char *word ATF_DEFS_ATTRIBUTE_UNUSED, void *data)
+{
+ size_t *counter = data;
+
+ (*counter)++;
+
+ return atf_no_error();
+}
+
+struct fail_at {
+ int failpos;
+ int curpos;
+};
+
+static
+atf_error_t
+word_fail_at(const char *word ATF_DEFS_ATTRIBUTE_UNUSED, void *data)
+{
+ struct fail_at *fa = data;
+ atf_error_t err;
+
+ if (fa->failpos == fa->curpos)
+ err = atf_no_memory_error(); /* Just a random error. */
+ else {
+ fa->curpos++;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(for_each_word);
+ATF_TC_HEAD(for_each_word, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_text_for_each_word"
+ "function");
+}
+ATF_TC_BODY(for_each_word, tc)
+{
+ size_t cnt;
+ char acum[1024];
+
+ cnt = 0;
+ strcpy(acum, "");
+ RE(atf_text_for_each_word("1 2 3", " ", word_count, &cnt));
+ RE(atf_text_for_each_word("1 2 3", " ", word_acum, acum));
+ ATF_REQUIRE(cnt == 3);
+ ATF_REQUIRE(strcmp(acum, "123") == 0);
+
+ cnt = 0;
+ strcpy(acum, "");
+ RE(atf_text_for_each_word("1 2 3", ".", word_count, &cnt));
+ RE(atf_text_for_each_word("1 2 3", ".", word_acum, acum));
+ ATF_REQUIRE(cnt == 1);
+ ATF_REQUIRE(strcmp(acum, "1 2 3") == 0);
+
+ cnt = 0;
+ strcpy(acum, "");
+ RE(atf_text_for_each_word("1 2 3 4 5", " ", word_count, &cnt));
+ RE(atf_text_for_each_word("1 2 3 4 5", " ", word_acum, acum));
+ ATF_REQUIRE(cnt == 5);
+ ATF_REQUIRE(strcmp(acum, "12345") == 0);
+
+ cnt = 0;
+ strcpy(acum, "");
+ RE(atf_text_for_each_word("1 2.3.4 5", " .", word_count, &cnt));
+ RE(atf_text_for_each_word("1 2.3.4 5", " .", word_acum, acum));
+ ATF_REQUIRE(cnt == 5);
+ ATF_REQUIRE(strcmp(acum, "12345") == 0);
+
+ {
+ struct fail_at fa;
+ fa.failpos = 3;
+ fa.curpos = 0;
+ atf_error_t err = atf_text_for_each_word("a b c d e", " ",
+ word_fail_at, &fa);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ ATF_REQUIRE(fa.curpos == 3);
+ atf_error_free(err);
+ }
+}
+
+ATF_TC(format);
+ATF_TC_HEAD(format, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of free-form "
+ "strings using a variable parameters list");
+}
+ATF_TC_BODY(format, tc)
+{
+ char *str;
+ atf_error_t err;
+
+ err = atf_text_format(&str, "%s %s %d", "Test", "string", 1);
+ ATF_REQUIRE(!atf_is_error(err));
+ ATF_REQUIRE(strcmp(str, "Test string 1") == 0);
+ free(str);
+}
+
+static
+void
+format_ap(char **dest, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_text_format_ap(dest, fmt, ap);
+ va_end(ap);
+
+ ATF_REQUIRE(!atf_is_error(err));
+}
+
+ATF_TC(format_ap);
+ATF_TC_HEAD(format_ap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of free-form "
+ "strings using a va_list argument");
+}
+ATF_TC_BODY(format_ap, tc)
+{
+ char *str;
+
+ format_ap(&str, "%s %s %d", "Test", "string", 1);
+ ATF_REQUIRE(strcmp(str, "Test string 1") == 0);
+ free(str);
+}
+
+ATF_TC(split);
+ATF_TC_HEAD(split, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the split function");
+}
+ATF_TC_BODY(split, tc)
+{
+ {
+ const char *words[] = { NULL };
+ check_split("", " ", words);
+ }
+
+ {
+ const char *words[] = { NULL };
+ check_split(" ", " ", words);
+ }
+
+ {
+ const char *words[] = { NULL };
+ check_split(" ", " ", words);
+ }
+
+ {
+ const char *words[] = { "a", "b", NULL };
+ check_split("a b", " ", words);
+ }
+
+ {
+ const char *words[] = { "a", "b", "c", "d", NULL };
+ check_split("a b c d", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split("foo bar", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", "baz", "foobar", NULL };
+ check_split("foo bar baz foobar", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split(" foo bar", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split("foo bar", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split("foo bar ", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split(" foo bar ", " ", words);
+ }
+}
+
+ATF_TC(split_delims);
+ATF_TC_HEAD(split_delims, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the split function using "
+ "different delimiters");
+}
+ATF_TC_BODY(split_delims, tc)
+{
+
+ {
+ const char *words[] = { NULL };
+ check_split("", "/", words);
+ }
+
+ {
+ const char *words[] = { " ", NULL };
+ check_split(" ", "/", words);
+ }
+
+ {
+ const char *words[] = { " ", NULL };
+ check_split(" ", "/", words);
+ }
+
+ {
+ const char *words[] = { "a", "b", NULL };
+ check_split("a/b", "/", words);
+ }
+
+ {
+ const char *words[] = { "a", "bcd", "ef", NULL };
+ check_split("aLONGDELIMbcdLONGDELIMef", "LONGDELIM", words);
+ }
+}
+
+ATF_TC(to_bool);
+ATF_TC_HEAD(to_bool, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_text_to_bool function");
+}
+ATF_TC_BODY(to_bool, tc)
+{
+ bool b;
+
+ RE(atf_text_to_bool("true", &b)); ATF_REQUIRE(b);
+ RE(atf_text_to_bool("TRUE", &b)); ATF_REQUIRE(b);
+ RE(atf_text_to_bool("yes", &b)); ATF_REQUIRE(b);
+ RE(atf_text_to_bool("YES", &b)); ATF_REQUIRE(b);
+
+ RE(atf_text_to_bool("false", &b)); ATF_REQUIRE(!b);
+ RE(atf_text_to_bool("FALSE", &b)); ATF_REQUIRE(!b);
+ RE(atf_text_to_bool("no", &b)); ATF_REQUIRE(!b);
+ RE(atf_text_to_bool("NO", &b)); ATF_REQUIRE(!b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("", &b));
+ ATF_REQUIRE(b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("tru", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("tru", &b));
+ ATF_REQUIRE(b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("true2", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("true2", &b));
+ ATF_REQUIRE(b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("fals", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("fals", &b));
+ ATF_REQUIRE(b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("false2", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("false2", &b));
+ ATF_REQUIRE(b);
+}
+
+ATF_TC(to_long);
+ATF_TC_HEAD(to_long, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_text_to_long function");
+}
+ATF_TC_BODY(to_long, tc)
+{
+ long l;
+
+ RE(atf_text_to_long("0", &l)); ATF_REQUIRE_EQ(l, 0);
+ RE(atf_text_to_long("-5", &l)); ATF_REQUIRE_EQ(l, -5);
+ RE(atf_text_to_long("5", &l)); ATF_REQUIRE_EQ(l, 5);
+ RE(atf_text_to_long("123456789", &l)); ATF_REQUIRE_EQ(l, 123456789);
+
+ l = 1212;
+ REQUIRE_ERROR(atf_text_to_long("", &l));
+ ATF_REQUIRE_EQ(l, 1212);
+ REQUIRE_ERROR(atf_text_to_long("foo", &l));
+ ATF_REQUIRE_EQ(l, 1212);
+ REQUIRE_ERROR(atf_text_to_long("1234x", &l));
+ ATF_REQUIRE_EQ(l, 1212);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, for_each_word);
+ ATF_TP_ADD_TC(tp, format);
+ ATF_TP_ADD_TC(tp, format_ap);
+ ATF_TP_ADD_TC(tp, split);
+ ATF_TP_ADD_TC(tp, split_delims);
+ ATF_TP_ADD_TC(tp, to_bool);
+ ATF_TP_ADD_TC(tp, to_long);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/tp_main.c b/unit/atf-src/atf-c/detail/tp_main.c
new file mode 100644
index 0000000..229615b
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/tp_main.c
@@ -0,0 +1,615 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/env.h"
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/map.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+#include "atf-c/tc.h"
+#include "atf-c/tp.h"
+#include "atf-c/utils.h"
+
+#if defined(HAVE_GNU_GETOPT)
+# define GETOPT_POSIX "+"
+#else
+# define GETOPT_POSIX ""
+#endif
+
+static const char *progname = NULL;
+
+/* This prototype is provided by macros.h during instantiation of the test
+ * program, so it can be kept private. Don't know if that's the best idea
+ * though. */
+int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *));
+
+enum tc_part {
+ BODY,
+ CLEANUP,
+};
+
+/* ---------------------------------------------------------------------
+ * The "usage" and "user" error types.
+ * --------------------------------------------------------------------- */
+
+#define FREE_FORM_ERROR(name) \
+ struct name ## _error_data { \
+ char m_what[2048]; \
+ }; \
+ \
+ static \
+ void \
+ name ## _format(const atf_error_t err, char *buf, size_t buflen) \
+ { \
+ const struct name ## _error_data *data; \
+ \
+ PRE(atf_error_is(err, #name)); \
+ \
+ data = atf_error_data(err); \
+ snprintf(buf, buflen, "%s", data->m_what); \
+ } \
+ \
+ static \
+ atf_error_t \
+ name ## _error(const char *fmt, ...) \
+ { \
+ atf_error_t err; \
+ struct name ## _error_data data; \
+ va_list ap; \
+ \
+ va_start(ap, fmt); \
+ vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \
+ va_end(ap); \
+ \
+ err = atf_error_new(#name, &data, sizeof(data), name ## _format); \
+ \
+ return err; \
+ }
+
+FREE_FORM_ERROR(usage);
+FREE_FORM_ERROR(user);
+
+/* ---------------------------------------------------------------------
+ * Printing functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+print_error(const atf_error_t err)
+{
+ char buf[4096];
+
+ PRE(atf_is_error(err));
+
+ atf_error_format(err, buf, sizeof(buf));
+ fprintf(stderr, "%s: ERROR: %s\n", progname, buf);
+
+ if (atf_error_is(err, "usage"))
+ fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n",
+ progname);
+}
+
+static
+void
+print_warning(const char *message)
+{
+ fprintf(stderr, "%s: WARNING: %s\n", progname, message);
+}
+
+/* ---------------------------------------------------------------------
+ * Options handling.
+ * --------------------------------------------------------------------- */
+
+struct params {
+ bool m_do_list;
+ atf_fs_path_t m_srcdir;
+ char *m_tcname;
+ enum tc_part m_tcpart;
+ atf_fs_path_t m_resfile;
+ atf_map_t m_config;
+};
+
+static
+atf_error_t
+argv0_to_dir(const char *argv0, atf_fs_path_t *dir)
+{
+ atf_error_t err;
+ atf_fs_path_t temp;
+
+ err = atf_fs_path_init_fmt(&temp, "%s", argv0);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_fs_path_branch_path(&temp, dir);
+
+ atf_fs_path_fini(&temp);
+out:
+ return err;
+}
+
+static
+atf_error_t
+params_init(struct params *p, const char *argv0)
+{
+ atf_error_t err;
+
+ p->m_do_list = false;
+ p->m_tcname = NULL;
+ p->m_tcpart = BODY;
+
+ err = argv0_to_dir(argv0, &p->m_srcdir);
+ if (atf_is_error(err))
+ return err;
+
+ err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout");
+ if (atf_is_error(err)) {
+ atf_fs_path_fini(&p->m_srcdir);
+ return err;
+ }
+
+ err = atf_map_init(&p->m_config);
+ if (atf_is_error(err)) {
+ atf_fs_path_fini(&p->m_resfile);
+ atf_fs_path_fini(&p->m_srcdir);
+ return err;
+ }
+
+ return err;
+}
+
+static
+void
+params_fini(struct params *p)
+{
+ atf_map_fini(&p->m_config);
+ atf_fs_path_fini(&p->m_resfile);
+ atf_fs_path_fini(&p->m_srcdir);
+ if (p->m_tcname != NULL)
+ free(p->m_tcname);
+}
+
+static
+atf_error_t
+parse_vflag(char *arg, atf_map_t *config)
+{
+ atf_error_t err;
+ char *split;
+
+ split = strchr(arg, '=');
+ if (split == NULL) {
+ err = usage_error("-v requires an argument of the form var=value");
+ goto out;
+ }
+
+ *split = '\0';
+ split++;
+
+ err = atf_map_insert(config, arg, split, false);
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+replace_path_param(atf_fs_path_t *param, const char *value)
+{
+ atf_error_t err;
+ atf_fs_path_t temp;
+
+ err = atf_fs_path_init_fmt(&temp, "%s", value);
+ if (!atf_is_error(err)) {
+ atf_fs_path_fini(param);
+ *param = temp;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Test case listing.
+ * --------------------------------------------------------------------- */
+
+static
+void
+list_tcs(const atf_tp_t *tp)
+{
+ const atf_tc_t *const *tcs;
+ const atf_tc_t *const *tcsptr;
+
+ printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n");
+
+ tcs = atf_tp_get_tcs(tp);
+ INV(tcs != NULL); /* Should be checked. */
+ for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) {
+ const atf_tc_t *tc = *tcsptr;
+ char **vars = atf_tc_get_md_vars(tc);
+ char **ptr;
+
+ INV(vars != NULL); /* Should be checked. */
+
+ if (tcsptr != tcs) /* Not first. */
+ printf("\n");
+
+ for (ptr = vars; *ptr != NULL; ptr += 2) {
+ if (strcmp(*ptr, "ident") == 0) {
+ printf("ident: %s\n", *(ptr + 1));
+ break;
+ }
+ }
+
+ for (ptr = vars; *ptr != NULL; ptr += 2) {
+ if (strcmp(*ptr, "ident") != 0) {
+ printf("%s: %s\n", *ptr, *(ptr + 1));
+ }
+ }
+
+ atf_utils_free_charpp(vars);
+ }
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+ free(UNCONST(tcs));
+#undef UNCONST
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+handle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart)
+{
+ atf_error_t err;
+
+ err = atf_no_error();
+
+ *tcname = strdup(tcarg);
+ if (*tcname == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ char *delim = strchr(*tcname, ':');
+ if (delim != NULL) {
+ *delim = '\0';
+
+ delim++;
+ if (strcmp(delim, "body") == 0) {
+ *tcpart = BODY;
+ } else if (strcmp(delim, "cleanup") == 0) {
+ *tcpart = CLEANUP;
+ } else {
+ err = usage_error("Invalid test case part `%s'", delim);
+ goto out;
+ }
+ }
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+process_params(int argc, char **argv, struct params *p)
+{
+ atf_error_t err;
+ int ch;
+ int old_opterr;
+
+ err = params_init(p, argv[0]);
+ if (atf_is_error(err))
+ goto out;
+
+ old_opterr = opterr;
+ opterr = 0;
+ while (!atf_is_error(err) &&
+ (ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {
+ switch (ch) {
+ case 'l':
+ p->m_do_list = true;
+ break;
+
+ case 'r':
+ err = replace_path_param(&p->m_resfile, optarg);
+ break;
+
+ case 's':
+ err = replace_path_param(&p->m_srcdir, optarg);
+ break;
+
+ case 'v':
+ err = parse_vflag(optarg, &p->m_config);
+ break;
+
+ case ':':
+ err = usage_error("Option -%c requires an argument.", optopt);
+ break;
+
+ case '?':
+ default:
+ err = usage_error("Unknown option -%c.", optopt);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Clear getopt state just in case the test wants to use it. */
+ opterr = old_opterr;
+ optind = 1;
+#if defined(HAVE_OPTRESET)
+ optreset = 1;
+#endif
+
+ if (!atf_is_error(err)) {
+ if (p->m_do_list) {
+ if (argc > 0)
+ err = usage_error("Cannot provide test case names with -l");
+ } else {
+ if (argc == 0)
+ err = usage_error("Must provide a test case name");
+ else if (argc == 1)
+ err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart);
+ else if (argc > 1) {
+ err = usage_error("Cannot provide more than one test case "
+ "name");
+ }
+ }
+ }
+
+ if (atf_is_error(err))
+ params_fini(p);
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+srcdir_strip_libtool(atf_fs_path_t *srcdir)
+{
+ atf_error_t err;
+ atf_fs_path_t parent;
+
+ err = atf_fs_path_branch_path(srcdir, &parent);
+ if (atf_is_error(err))
+ goto out;
+
+ atf_fs_path_fini(srcdir);
+ *srcdir = parent;
+
+ INV(!atf_is_error(err));
+out:
+ return err;
+}
+
+static
+atf_error_t
+handle_srcdir(struct params *p)
+{
+ atf_error_t err;
+ atf_dynstr_t leafname;
+ atf_fs_path_t exe, srcdir;
+ bool b;
+
+ err = atf_fs_path_copy(&srcdir, &p->m_srcdir);
+ if (atf_is_error(err))
+ goto out;
+
+ if (!atf_fs_path_is_absolute(&srcdir)) {
+ atf_fs_path_t srcdirabs;
+
+ err = atf_fs_path_to_absolute(&srcdir, &srcdirabs);
+ if (atf_is_error(err))
+ goto out_srcdir;
+
+ atf_fs_path_fini(&srcdir);
+ srcdir = srcdirabs;
+ }
+
+ err = atf_fs_path_leaf_name(&srcdir, &leafname);
+ if (atf_is_error(err))
+ goto out_srcdir;
+ else {
+ const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs");
+ atf_dynstr_fini(&leafname);
+
+ if (libs) {
+ err = srcdir_strip_libtool(&srcdir);
+ if (atf_is_error(err))
+ goto out;
+ }
+ }
+
+ err = atf_fs_path_copy(&exe, &srcdir);
+ if (atf_is_error(err))
+ goto out_srcdir;
+
+ err = atf_fs_path_append_fmt(&exe, "%s", progname);
+ if (atf_is_error(err))
+ goto out_exe;
+
+ err = atf_fs_exists(&exe, &b);
+ if (!atf_is_error(err)) {
+ if (b) {
+ err = atf_map_insert(&p->m_config, "srcdir",
+ strdup(atf_fs_path_cstring(&srcdir)), true);
+ } else {
+ err = user_error("Cannot find the test program in the source "
+ "directory `%s'", atf_fs_path_cstring(&srcdir));
+ }
+ }
+
+out_exe:
+ atf_fs_path_fini(&exe);
+out_srcdir:
+ atf_fs_path_fini(&srcdir);
+out:
+ return err;
+}
+
+static
+atf_error_t
+run_tc(const atf_tp_t *tp, struct params *p, int *exitcode)
+{
+ atf_error_t err;
+
+ err = atf_no_error();
+
+ if (!atf_tp_has_tc(tp, p->m_tcname)) {
+ err = usage_error("Unknown test case `%s'", p->m_tcname);
+ goto out;
+ }
+
+ if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get(
+ "__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0)
+ {
+ print_warning("Running test cases outside of kyua(1) is unsupported");
+ print_warning("No isolation nor timeout control is being applied; you "
+ "may get unexpected failures; see atf-test-case(4)");
+ }
+
+ switch (p->m_tcpart) {
+ case BODY:
+ err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile));
+ if (atf_is_error(err)) {
+ /* TODO: Handle error */
+ *exitcode = EXIT_FAILURE;
+ atf_error_free(err);
+ } else {
+ *exitcode = EXIT_SUCCESS;
+ }
+
+ break;
+
+ case CLEANUP:
+ err = atf_tp_cleanup(tp, p->m_tcname);
+ if (atf_is_error(err)) {
+ /* TODO: Handle error */
+ *exitcode = EXIT_FAILURE;
+ atf_error_free(err);
+ } else {
+ *exitcode = EXIT_SUCCESS;
+ }
+
+ break;
+
+ default:
+ UNREACHABLE;
+ }
+
+ INV(!atf_is_error(err));
+out:
+ return err;
+}
+
+static
+atf_error_t
+controlled_main(int argc, char **argv,
+ atf_error_t (*add_tcs_hook)(atf_tp_t *),
+ int *exitcode)
+{
+ atf_error_t err;
+ struct params p;
+ atf_tp_t tp;
+ char **raw_config;
+
+ err = process_params(argc, argv, &p);
+ if (atf_is_error(err))
+ goto out;
+
+ err = handle_srcdir(&p);
+ if (atf_is_error(err))
+ goto out_p;
+
+ raw_config = atf_map_to_charpp(&p.m_config);
+ if (raw_config == NULL) {
+ err = atf_no_memory_error();
+ goto out_p;
+ }
+ err = atf_tp_init(&tp, (const char* const*)raw_config);
+ atf_utils_free_charpp(raw_config);
+ if (atf_is_error(err))
+ goto out_p;
+
+ err = add_tcs_hook(&tp);
+ if (atf_is_error(err))
+ goto out_tp;
+
+ if (p.m_do_list) {
+ list_tcs(&tp);
+ INV(!atf_is_error(err));
+ *exitcode = EXIT_SUCCESS;
+ } else {
+ err = run_tc(&tp, &p, exitcode);
+ }
+
+out_tp:
+ atf_tp_fini(&tp);
+out_p:
+ params_fini(&p);
+out:
+ return err;
+}
+
+int
+atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *))
+{
+ atf_error_t err;
+ int exitcode;
+
+ progname = strrchr(argv[0], '/');
+ if (progname == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ /* Libtool workaround: if running from within the source tree (binaries
+ * that are not installed yet), skip the "lt-" prefix added to files in
+ * the ".libs" directory to show the real (not temporary) name. */
+ if (strncmp(progname, "lt-", 3) == 0)
+ progname += 3;
+
+ exitcode = EXIT_FAILURE; /* Silence GCC warning. */
+ err = controlled_main(argc, argv, add_tcs_hook, &exitcode);
+ if (atf_is_error(err)) {
+ print_error(err);
+ atf_error_free(err);
+ exitcode = EXIT_FAILURE;
+ }
+
+ return exitcode;
+}
diff --git a/unit/atf-src/atf-c/detail/user.c b/unit/atf-src/atf-c/detail/user.c
new file mode 100644
index 0000000..bbb4ded
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/user.c
@@ -0,0 +1,75 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/user.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "atf-c/detail/sanity.h"
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+uid_t
+atf_user_euid(void)
+{
+ return geteuid();
+}
+
+bool
+atf_user_is_member_of_group(gid_t gid)
+{
+ static gid_t groups[NGROUPS_MAX];
+ static int ngroups = -1;
+ bool found;
+ int i;
+
+ if (ngroups == -1) {
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ INV(ngroups >= 0);
+ }
+
+ found = false;
+ for (i = 0; !found && i < ngroups; i++)
+ if (groups[i] == gid)
+ found = true;
+ return found;
+}
+
+bool
+atf_user_is_root(void)
+{
+ return geteuid() == 0;
+}
+
+bool
+atf_user_is_unprivileged(void)
+{
+ return geteuid() != 0;
+}
diff --git a/unit/atf-src/atf-c/detail/user.h b/unit/atf-src/atf-c/detail/user.h
new file mode 100644
index 0000000..14a3e96
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/user.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#if !defined(ATF_C_DETAIL_USER_H)
+#define ATF_C_DETAIL_USER_H
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+
+/* TODO: Would be nice to have an atf_user_t type and transform all of
+ * the functions below to methods. */
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+uid_t atf_user_euid(void);
+bool atf_user_is_member_of_group(gid_t);
+bool atf_user_is_root(void);
+bool atf_user_is_unprivileged(void);
+
+#endif /* !defined(ATF_C_DETAIL_USER_H) */
diff --git a/unit/atf-src/atf-c/detail/user_test.c b/unit/atf-src/atf-c/detail/user_test.c
new file mode 100644
index 0000000..6a0f6e1
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/user_test.c
@@ -0,0 +1,146 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "atf-c/detail/user.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(euid);
+ATF_TC_HEAD(euid, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_user_euid function");
+}
+ATF_TC_BODY(euid, tc)
+{
+ ATF_REQUIRE_EQ(atf_user_euid(), geteuid());
+}
+
+ATF_TC(is_member_of_group);
+ATF_TC_HEAD(is_member_of_group, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_member_of_group "
+ "function");
+}
+ATF_TC_BODY(is_member_of_group, tc)
+{
+ gid_t gids[NGROUPS_MAX];
+ gid_t g, maxgid;
+ int ngids;
+ const gid_t maxgid_limit = 1 << 16;
+
+ {
+ int i;
+
+ ngids = getgroups(NGROUPS_MAX, gids);
+ if (ngids == -1)
+ atf_tc_fail("Call to getgroups failed");
+ maxgid = 0;
+ for (i = 0; i < ngids; i++) {
+ printf("User group %d is %u\n", i, gids[i]);
+ if (maxgid < gids[i])
+ maxgid = gids[i];
+ }
+ printf("User belongs to %d groups\n", ngids);
+ printf("Last GID is %u\n", maxgid);
+ }
+
+ if (maxgid > maxgid_limit) {
+ printf("Test truncated from %u groups to %u to keep the run time "
+ "reasonable enough\n", maxgid, maxgid_limit);
+ maxgid = maxgid_limit;
+ }
+
+ for (g = 0; g < maxgid; g++) {
+ bool found = false;
+ int i;
+
+ for (i = 0; !found && i < ngids; i++) {
+ if (gids[i] == g)
+ found = true;
+ }
+
+ if (found) {
+ printf("Checking if user belongs to group %d\n", g);
+ ATF_REQUIRE(atf_user_is_member_of_group(g));
+ } else {
+ printf("Checking if user does not belong to group %d\n", g);
+ ATF_REQUIRE(!atf_user_is_member_of_group(g));
+ }
+ }
+}
+
+ATF_TC(is_root);
+ATF_TC_HEAD(is_root, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_root function");
+}
+ATF_TC_BODY(is_root, tc)
+{
+ if (geteuid() == 0)
+ ATF_REQUIRE(atf_user_is_root());
+ else
+ ATF_REQUIRE(!atf_user_is_root());
+}
+
+ATF_TC(is_unprivileged);
+ATF_TC_HEAD(is_unprivileged, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_unprivileged "
+ "function");
+}
+ATF_TC_BODY(is_unprivileged, tc)
+{
+ if (geteuid() != 0)
+ ATF_REQUIRE(atf_user_is_unprivileged());
+ else
+ ATF_REQUIRE(!atf_user_is_unprivileged());
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, euid);
+ ATF_TP_ADD_TC(tp, is_member_of_group);
+ ATF_TP_ADD_TC(tp, is_root);
+ ATF_TP_ADD_TC(tp, is_unprivileged);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/version_helper.c b/unit/atf-src/atf-c/detail/version_helper.c
new file mode 100644
index 0000000..fd48466
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/version_helper.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+ printf("%s\n", PACKAGE_VERSION);
+ return EXIT_SUCCESS;
+}