home: hub: zuo

Download patch

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, "."))