summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisplib.c2
-rw-r--r--stdlib/path-test.tl13
-rw-r--r--stream.c32
-rw-r--r--stream.h2
-rw-r--r--tests/018/path-test.tl6
-rw-r--r--txr.1107
6 files changed, 93 insertions, 69 deletions
diff --git a/lisplib.c b/lisplib.c
index 17485812..63ef9b1d 100644
--- a/lisplib.c
+++ b/lisplib.c
@@ -190,7 +190,7 @@ static val path_test_set_entries(val dlt, val fun)
lit("path-newer"), lit("path-older"),
lit("path-same-object"), lit("path-private-to-me-p"),
lit("path-strictly-private-to-me-p"),
- lit("path-dir-empty"), lit("path-search"),
+ lit("path-dir-empty"),
nil
};
diff --git a/stdlib/path-test.tl b/stdlib/path-test.tl
index 4d700167..04ed2055 100644
--- a/stdlib/path-test.tl
+++ b/stdlib/path-test.tl
@@ -185,16 +185,3 @@
(casequal ent
(("." ".."))
(t (return nil))))))))
-
-(defun path-search (name : (path (getenv "PATH")))
- (if (empty name)
- nil
- (let ((pcomp (if (listp path)
- path
- (spl (if (starts-with "CYGNAL" (uname).sysname) #\; #\:)
- path))))
- (each ((pc pcomp))
- (if (nequal pc "")
- (let ((st (ignerr (stat (path-cat pc name)))))
- (if (and st (path-executable-to-me-p st) (path-file-p st))
- (return st.path))))))))
diff --git a/stream.c b/stream.c
index 0b6897cb..10511ca7 100644
--- a/stream.c
+++ b/stream.c
@@ -103,6 +103,7 @@ val socket_error_s;
struct cobj_class *stream_cls, *stdio_stream_cls;
const wchli_t *path_sep_chars = wli("/");
+wchar_t path_var_sep_char = ':';
val top_stderr;
@@ -4383,6 +4384,31 @@ static void fds_clobber(struct save_fds *fds, int flags)
fds_subst_nosave(fds->suberr, STDERR_FILENO);
}
+val path_search(val name, val path_in)
+{
+ val self = lit("path-search");
+ val ps = static_str(path_sep_chars);
+
+ if (empty(name) || equal(name, lit(".")) || equal(name, lit(".."))) {
+ return nil;
+ } else if (break_str(name, ps)) {
+ return name;
+ } else {
+ val path = default_arg_strict(path_in, getenv_wrap(lit("PATH")));
+ val spath = if3(listp(path), path, split_str(path, chr(path_var_sep_char)));
+ for (; spath; spath = cdr(spath)) {
+ val dir = car(spath);
+ val full = path_cat(dir, name);
+ char *full8 = utf8_dup_to(c_str(full, self));
+ int res = access(full8, F_OK);
+ free(full8);
+ if (res == 0)
+ return full;
+ }
+ return nil;
+ }
+}
+
#if HAVE_FORK_STUFF
static val open_subprocess(val name, val mode_str, val args, val fun)
{
@@ -4998,9 +5024,10 @@ static void detect_path_separators(void)
struct utsname un;
if (uname(&un) >= 0) {
- if (strncmp(un.sysname, "CYGNAL", 6) == 0)
+ if (strncmp(un.sysname, "CYGNAL", 6) == 0) {
path_sep_chars = wli("\\/");
- return;
+ path_var_sep_char = ';';
+ }
}
#endif
}
@@ -5528,6 +5555,7 @@ void stream_init(void)
reg_fun(intern(lit("open-file"), user_package), func_n2o(open_file, 1));
reg_fun(intern(lit("open-fileno"), user_package), func_n2o(open_fileno, 1));
reg_fun(intern(lit("open-tail"), user_package), func_n3o(open_tail, 1));
+ reg_fun(intern(lit("path-search"), user_package), func_n2o(path_search, 1));
reg_fun(intern(lit("open-command"), user_package), func_n2o(open_command, 1));
reg_fun(intern(lit("open-pipe"), user_package), func_n2(open_command));
reg_fun(intern(lit("open-process"), user_package), func_n3o(open_process, 2));
diff --git a/stream.h b/stream.h
index 8454a6a7..dc1d0c4d 100644
--- a/stream.h
+++ b/stream.h
@@ -152,6 +152,7 @@ extern val socket_error_s;
#endif
extern const wchli_t *path_sep_chars;
+extern wchar_t path_var_sep_char;
extern val top_stderr;
@@ -245,6 +246,7 @@ val open_file(val path, val mode_str);
val open_fileno(val fd, val mode_str);
val open_tail(val path, val mode_str, val seek_end_p);
val open_command(val path, val mode_str);
+val path_search(val name, val path_in);
val open_process(val path, val mode_str, val args);
val make_catenated_stream(val stream_list);
val make_catenated_stream_v(struct args *streams);
diff --git a/tests/018/path-test.tl b/tests/018/path-test.tl
index 919186ac..d3aa6dce 100644
--- a/tests/018/path-test.tl
+++ b/tests/018/path-test.tl
@@ -15,4 +15,8 @@
(path-search "sh" "AlMoStCeRtAiNlLyNoNeXisTenT:/bin") "/bin/sh"
(path-search "sh" "/bin") "/bin/sh"
(path-search "sh" "/bin/") "/bin/sh"
- (path-search "sh" ":/bin/") "/bin/sh")
+ (path-search "sh" ":/bin/") "/bin/sh"
+ (path-search "" "/bin") nil
+ (path-search "." "/bin") nil
+ (path-search ".." "/bin") nil
+ (path-search "foo/bar" "/bin") "foo/bar")
diff --git a/txr.1 b/txr.1
index 230c20f7..5156b86b 100644
--- a/txr.1
+++ b/txr.1
@@ -68630,76 +68630,79 @@ device.
.coNP Function @ path-search
.synb
-.mets (path-search < program-name <> [ search-path ])
+.mets (path-search < name <> [ search-path ])
.syne
.desc
The
.code path-search
-function searches for an executable program whose name is given by the
-.meta program-name
-string argument. If the program is found, then the full path to that
-program is returned, otherwise
+function searches for the existence of a filesystem object named by
+.meta name
+in the directories specified
+.metn search-path .
+
+If
+.meta name
+is the empty string or one of the two strings
+.str .
+(dot)
+or
+.str ..
+(dotdot),
+then
.code nil
-is returned.
+is returned. If
+.meta name
+contains any path separator characters (any of the set of characters
+found in the
+.code path-sep-chars
+string) then the function returns
+.meta name
+without performing any search. In all these trivial cases, the
+.meta search-path
+argument is ignored.
-If the
+The
.meta search-path
-argument is omitted, then
-.code path-search
-uses the search path given in the
+argument, if present, may be a string or a list of strings.
+If omitted, then it takes on the value of the
.code PATH
-environment variable. This path is decomposed into components according
-to the separator character, which may be
+environment variable if that variable exists, or else takes on
+the value
+.code nil
+indicating an empty search path.
+
+If
+.meta search-path
+is a string, it is converted to a list of directories by splitting on the
+separator character, which may be
.code :
(colon)
or
.code ;
-(semicolon) depending on the system. Then, for each component of the path,
+(semicolon) depending on the system. Then, for each directory in the list,
.code path-search
affixes the
-.meta program-name
+.meta name
to that component, as if using the
.code path-cat
-function, and tests whether the resulting path is an existing file object
-according to
-.code path-file-p
-and which is executable according to
-.codn path-executable-to-me-p .
-
-The
-.code search-path
-argument may be specified as a string, which is taken instead of the value
-of the
-.code PATH
-environment variable, and decomposed into components in the same way.
-The argument may also be specified as a list of strings, which are taken
-to be components of a search path, and used as-is.
-
-If
-.meta program-name
-is the empty string or the search path is empty, the function returns
-.codn nil .
-
-Components of the search path which are empty strings are ignored.
-
-If
-.meta program-name
-is a string which includes path-separator characters, or if
-.meta program-name
-is one of the special names
-.str .
-or
-.strn .. ,
-the behavior may produce a different result from the host platform's native
-path-search strategy.
+function, and tests whether the resulting path refers to an existing filesystem
+object.
+If so, then the search terminates and that resulting path is returned.
+If the entire list is traversed without finding a filesystem object, then
+.code nil
+is returned.
+If any error whatsoever occurs while determining whether the resulting path
+exists, the situation is treated as nonexistence, and the search continues.
-The behavior of
+Note: subtle discrepancies may exist between
+.code path-search
+and the host platform's mechanisms for searching for an executable program.
+For instance, since
.code path-search
-may also deviate from the host platform's native path-search strategy
-due to issues of permissions. If an executable file is found in the path,
-but is not executable to the caller due to permissions, it is treated
-as nonexistent, which means that the search can find a same-named file
-later in the search path.
+is interested in existence only, it may return a path which exists, but is
+not executable. Whereas a path searching implementation which tests for
+executability will in that case continue searching, and not return that
+path.
.SS* Unix Credentials