ref: 1d43ec7a65b71a0eb5b1ee7ea5c5773800076941
parent: 6f89679a2fdbe2fc0b0b26c08d04284d2ec54736
author: Matthew Flatt <mflatt@racket-lang.org>
date: Sun Jul 3 02:04:10 CDT 2022
Zuo: fix `{file,directory,link}-exists?` Treat failure at the `stat` level as non-existence. Closes #4337
--- a/lib/zuo/private/more.zuo
+++ b/lib/zuo/private/more.zuo
@@ -131,17 +131,17 @@
(define (file-exists? p)
(unless (path-string? p) (arg-error 'file-exists? "path string" p))
- (let ([s (stat p)])
+ (let ([s (stat p #t #t)])
(and s (eq? (hash-ref s 'type) 'file))))
(define (directory-exists? p)
(unless (path-string? p) (arg-error 'directory-exists?: "path string" p))
- (let ([s (stat p)])
+ (let ([s (stat p #t #t)])
(and s (eq? (hash-ref s 'type) 'dir))))
(define (link-exists? p)
(unless (path-string? p) (arg-error 'link-exists? "path string" p))
- (let ([s (stat p #f)])
+ (let ([s (stat p #f #t)])
(and s (eq? (hash-ref s 'type) 'link))))
(define (explode-path p)
--- a/zuo-doc/lang-zuo.scrbl
+++ b/zuo-doc/lang-zuo.scrbl
@@ -1149,7 +1149,10 @@
@section{Filesystem}
-@defproc[(stat [name path-string?] [follow-links? any/c #t]) (or/c hash? #f)]{
+@defproc[(stat [name path-string?]
+ [follow-links? any/c #t]
+ [false-on-error? any/c #f])
+ (or/c hash? #f)]{
Returns information about the file, directory, or link referenced by
@racket[name]. If @racket[follow-links?] is @racket[#f], then when
@@ -1156,7 +1159,10 @@
@racket[name] refers to a link, information is reported about the
link; otherwise, information is reported about the target of a link.
-If no such file, directory, or link exists, the result is @racket[#f].
+If @racket[name] is a valid path but such file, directory, or link exists,
+the result is @racket[#f]. If accessing @racket[name] encounters an error
+(e.g., @racket[name] uses a file as a directory or permission is denied),
+then @racket[#f] is reported instead of an error if @racket[false-on-error?].
Otherwise, the hash table has similar keys and values as
@realracket[file-or-directory-stat] from @racketmodname[racket], but
with only certain keys per platform:
@@ -1256,7 +1262,9 @@
)]{
Uses @racket[stat] to check for a file, directory, or link,
-respectively.}
+respectively. If @racket[stat] encounters an error (e.g., access
+permission), then the file, directory, or link is treated as
+non-existent.}
@section{Run Time Configuration}
--- a/zuo.c
+++ b/zuo.c
@@ -4788,11 +4788,12 @@
# define TO_INT64(a, b) (((zuo_int_t)(a) << 32) | (((zuo_int_t)b) & (zuo_int_t)0xFFFFFFFF))
#endif
-static zuo_t *zuo_stat(zuo_t *path, zuo_t *follow_links) {
+static zuo_t *zuo_stat(zuo_t *path, zuo_t *follow_links, zuo_t *false_on_error) {
const char *who = "stat";
zuo_t *result = z.o_empty_hash;
if (follow_links == z.o_undefined) follow_links = z.o_true;
+ if (false_on_error == z.o_undefined) false_on_error = z.o_false;
check_path_string(who, path);
@@ -4807,7 +4808,7 @@
stat_result = stat(ZUO_STRING_PTR(path), &stat_buf);
if (stat_result != 0) {
- if (errno != ENOENT)
+ if ((errno != ENOENT) && (false_on_error == z.o_false))
zuo_fail1w_errno(who, "failed", path);
return z.o_false;
}
@@ -4853,13 +4854,17 @@
if (fdh == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
- if ((err != ERROR_FILE_NOT_FOUND) && (err != ERROR_PATH_NOT_FOUND))
+ if ((err != ERROR_FILE_NOT_FOUND) && (err != ERROR_PATH_NOT_FOUND)
+ && (false_on_error == z.o_false))
zuo_fail1w(who, "failed", path);
return z.o_false;
}
- if (!GetFileInformationByHandle(fdh, &info))
+ if (!GetFileInformationByHandle(fdh, &info)) {
+ if (false_on_error != z.o_false)
+ return z.o_false;
zuo_fail1w(who, "failed", path);
+ }
CloseHandle(fdh);
@@ -6925,7 +6930,7 @@
ZUO_TOP_ENV_SET_PRIMITIVEb("fd-terminal?", zuo_fd_terminal_p);
ZUO_TOP_ENV_SET_PRIMITIVE1("fd-valid?", zuo_fd_valid_p);
- ZUO_TOP_ENV_SET_PRIMITIVEb("stat", zuo_stat);
+ ZUO_TOP_ENV_SET_PRIMITIVEC("stat", zuo_stat);
ZUO_TOP_ENV_SET_PRIMITIVE1("rm", zuo_rm);
ZUO_TOP_ENV_SET_PRIMITIVE2("mv", zuo_mv);
ZUO_TOP_ENV_SET_PRIMITIVE1("mkdir", zuo_mkdir);
@@ -7152,7 +7157,7 @@
if (load_file == NULL) {
load_file = "main.zuo";
load_path = zuo_string(load_file);
- if (zuo_stat(load_path, z.o_true) == z.o_false) {
+ if (zuo_stat(load_path, z.o_true, z.o_true) == z.o_false) {
zuo_error_color();
fprintf(stderr, "%s: no file specified, and no \"main.zuo\" found", argv0);
zuo_fail("");
@@ -7161,7 +7166,7 @@
zuo_t *st;
load_path = zuo_string(load_file);
load_path = zuo_normalize_input_path(load_path);
- st = zuo_stat(load_path, z.o_true);
+ st = zuo_stat(load_path, z.o_true, z.o_true);
if ((st != z.o_false)
&& (zuo_trie_lookup(st, zuo_symbol("type")) == zuo_symbol("dir"))) {
if (!strcmp(load_file, "."))