summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--sysif.c64
-rw-r--r--txr.110
3 files changed, 81 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index d9918c1f..0bfc6592 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2015-03-10 Kaz Kylheku <kaz@kylheku.com>
+
+ * sysif.c (mkdir_nothrow_exists): New static function.
+ (ensure_dir): New function.
+ (sysif_init): ensure_dir registered as intrinsic.
+
+ * txr.1: ensure_dir documented.
+
2015-02-25 Kaz Kylheku <kaz@kylheku.com>
* parser.c (open_txr_file, regex_parse, lisp_parse): Functions
diff --git a/sysif.c b/sysif.c
index c401b23a..04466896 100644
--- a/sysif.c
+++ b/sysif.c
@@ -58,6 +58,7 @@
#include "signal.h"
#include "utf8.h"
#include "unwind.h"
+#include "gc.h"
#include "eval.h"
#include "sysif.h"
@@ -180,6 +181,68 @@ static val mkdir_wrap(val path, val mode)
}
#endif
+#if HAVE_MKDIR || HAVE_WINDOWS_H
+static val mkdir_nothrow_exists(val path, val mode)
+{
+ val ret = t;
+
+ uw_catch_begin(cons(file_error_s, nil), esym, eobj);
+
+ ret = mkdir_wrap(path, mode);
+
+ uw_catch (esym, eobj) {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ ret = num(errno);
+ break;
+ case EEXIST:
+ break;
+ default:
+ uw_throw(esym, eobj);
+ }
+ }
+
+ uw_unwind;
+
+ uw_catch_end;
+
+ return ret;
+}
+
+static val ensure_dir(val path, val mode)
+{
+#if HAVE_WINDOWS_H
+ val sep = lit("\\");
+ val sep_set = lit("\\/");
+#else
+ val sep = lit("/");
+ val sep_set = lit("/");
+#endif
+ val split_path = split_str_set(path, sep_set);
+ val partial_path = pop(&split_path);
+ val ret = t;
+
+ for (;;) {
+ if (length(partial_path) != zero)
+ ret = mkdir_nothrow_exists(partial_path, mode);
+
+ if (!split_path)
+ break;
+
+ partial_path = format(nil, lit("~a~a~a"),
+ partial_path, sep, pop(&split_path), nao);
+ }
+
+ if (ret != t)
+ uw_throwf(file_error_s,
+ lit("ensure-dir: ~a: ~a/~s"), path, ret,
+ string_utf8(strerror(c_num(ret))), nao);
+
+ return ret;
+}
+#endif
+
#if HAVE_UNISTD_H
static val chdir_wrap(val path)
{
@@ -388,6 +451,7 @@ void sysif_init(void)
#if HAVE_MKDIR || HAVE_WINDOWS_H
reg_fun(intern(lit("mkdir"), user_package), func_n2o(mkdir_wrap, 1));
+ reg_fun(intern(lit("ensure-dir"), user_package), func_n2o(ensure_dir, 1));
#endif
#if HAVE_UNISTD_H
diff --git a/txr.1 b/txr.1
index 75794618..a8c10b07 100644
--- a/txr.1
+++ b/txr.1
@@ -23989,9 +23989,10 @@ of the system timer. Actual sleep times may be rounded up to the nearest 10
millisecond multiple on a system where timed suspensions are triggered by a 100
Hz tick.
-.coNP Function @ mkdir
+.coNP Functions @ mkdir and @ ensure-dir
.synb
.mets (mkdir < path <> [ mode ])
+.mets (ensure-dir < path <> [ mode ])
.syne
.desc
.code mkdir
@@ -24015,6 +24016,13 @@ for the newly created directory. If omitted, the requested permissions are
are subject to the system
.codn umask .
+The function
+.code ensure-dir
+is similar to
+.code mkdir
+except that it attempts to create all the missing parent directories
+as necessary, and does not throw an error if the directory exists.
+
.coNP Function @ chdir
.synb
.mets (chdir << path )