summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/util.h9
-rw-r--r--lib/Makemodule.am1
-rw-r--r--lib/util/dirstack.c74
3 files changed, 84 insertions, 0 deletions
diff --git a/include/util.h b/include/util.h
index f618f81..b496039 100644
--- a/include/util.h
+++ b/include/util.h
@@ -41,4 +41,13 @@ void print_version(void);
*/
int mkdir_p(const char *path);
+/* Returns 0 on success. On failure, prints error message to stderr. */
+int pushd(const char *path);
+
+/* Same as pushd, but the string doesn't have to be null-terminated. */
+int pushdn(const char *path, size_t len);
+
+/* Returns 0 on success. On failure, prints error message to stderr. */
+int popd(void);
+
#endif /* UTIL_H */
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 447acfb..0120347 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -23,6 +23,7 @@ libutil_a_SOURCES = lib/util/canonicalize_name.c lib/util/write_retry.c
libutil_a_SOURCES += lib/util/read_retry.c include/util.h
libutil_a_SOURCES += lib/util/print_version.c lib/util/mkdir_p.c
libutil_a_SOURCES += lib/util/str_table.c include/str_table.h
+libutil_a_SOURCES += lib/util/dirstack.c
if WITH_GZIP
libcompress_a_SOURCES += lib/comp/gzip.c
diff --git a/lib/util/dirstack.c b/lib/util/dirstack.c
new file mode 100644
index 0000000..7bfd1a2
--- /dev/null
+++ b/lib/util/dirstack.c
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "util.h"
+
+#define STACK_DEPTH 128
+
+static int dirstack[STACK_DEPTH];
+static int stacktop = 0;
+
+int pushd(const char *path)
+{
+ int fd;
+
+ assert(stacktop < STACK_DEPTH);
+
+ fd = open(".", O_DIRECTORY | O_PATH | O_RDONLY | O_CLOEXEC);
+
+ if (fd < 0) {
+ perror("open ./");
+ return -1;
+ }
+
+ if (chdir(path)) {
+ perror(path);
+ close(fd);
+ return -1;
+ }
+
+ dirstack[stacktop++] = fd;
+ return 0;
+}
+
+int pushdn(const char *path, size_t len)
+{
+ char *temp;
+ int ret;
+
+ temp = strndup(path, len);
+ if (temp == NULL) {
+ perror("pushd");
+ return -1;
+ }
+
+ ret = pushd(temp);
+
+ free(temp);
+ return ret;
+}
+
+int popd(void)
+{
+ int fd;
+
+ assert(stacktop > 0);
+
+ fd = dirstack[stacktop - 1];
+
+ if (fchdir(fd)) {
+ perror("popd");
+ return -1;
+ }
+
+ --stacktop;
+ close(fd);
+ return 0;
+}