--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * noerr - routines for cleaning up without blatting errno
+ *
+ * It is a good idea to follow the standard C convention of setting errno in
+ * your own helper functions. Unfortunately, care must be taken in the error
+ * paths as most standard functions can (and do) overwrite errno, even if they
+ * succeed.
+ *
+ * Example:
+ * #include <sys/types.h>
+ * #include <sys/stat.h>
+ * #include <fcntl.h>
+ *
+ * bool write_string_to_file(const char *file, const char *string)
+ * {
+ * int ret, fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ * if (fd < 0)
+ * return false;
+ * ret = write(fd, string, strlen(string));
+ * if (ret < 0) {
+ * // Preserve errno from write above.
+ * close_noerr(fd);
+ * unlink_noerr(file);
+ * return false;
+ * }
+ * if (close(fd) != 0) {
+ * // Again, preserve errno.
+ * unlink_noerr(file);
+ * return false;
+ * }
+ * // A short write means out of space.
+ * if (ret < strlen(string)) {
+ * unlink(file);
+ * errno = ENOSPC;
+ * return false;
+ * }
+ * return true;
+ * }
+ */
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0)
+ /* Nothing. */
+ return 0;
+
+ return 1;
+}
--- /dev/null
+#include "noerr.h"
+#include <unistd.h>
+#include <errno.h>
+
+int close_noerr(int fd)
+{
+ int saved_errno = errno, ret;
+
+ if (close(fd) != 0)
+ ret = errno;
+ else
+ ret = 0;
+
+ errno = saved_errno;
+ return ret;
+}
+
+int unlink_noerr(const char *pathname)
+{
+ int saved_errno = errno, ret;
+
+ if (unlink(pathname) != 0)
+ ret = errno;
+ else
+ ret = 0;
+
+ errno = saved_errno;
+ return ret;
+}
--- /dev/null
+#ifndef NOERR_H
+#define NOERR_H
+
+/**
+ * close_noerr - close without stomping errno.
+ * @fd: the file descriptor to close.
+ *
+ * errno is saved and restored across the call to close: if an error occurs,
+ * the resulting (non-zero) errno is returned.
+ */
+int close_noerr(int fd);
+
+/**
+ * unlink_noerr - unlink a file without stomping errno.
+ * @pathname: the path to unlink.
+ *
+ * errno is saved and restored across the call to unlink: if an error occurs,
+ * the resulting (non-zero) errno is returned.
+ */
+int unlink_noerr(const char *pathname);
+
+#endif /* NOERR_H */
--- /dev/null
+#include "noerr/noerr.h"
+#include "tap.h"
+#include "noerr/noerr.c"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <assert.h>
+
+int main(int argc, char *argv[])
+{
+ /* tempnam(3) is generally a bad idea, but OK here. */
+ char *name = tempnam(NULL, "noerr");
+ int fd;
+
+ plan_tests(12);
+ /* Should fail to unlink. */
+ ok1(unlink(name) != 0);
+ ok1(errno == ENOENT);
+
+ /* This one should not set errno. */
+ errno = 100;
+ ok1(unlink_noerr(name) == ENOENT);
+ ok1(errno == 100);
+
+ /* Should fail to close. */
+ ok1(close(-1) != 0);
+ ok1(errno == EBADF);
+
+ /* This one should not set errno. */
+ errno = 100;
+ ok1(close_noerr(-1) == EBADF);
+ ok1(errno == 100);
+
+ /* Test successful close/unlink doesn't hit errno either. */
+ fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ assert(fd >= 0);
+
+ errno = 100;
+ ok1(close_noerr(fd) == 0);
+ ok1(errno == 100);
+
+ errno = 100;
+ ok1(unlink_noerr(name) == 0);
+ ok1(errno == 100);
+
+ return exit_status();
+}