Skip to content

Commit fd22755

Browse files
committed
path-utils: Check directory still exists & handle Windows length limits
1 parent 2bc59f5 commit fd22755

File tree

1 file changed

+57
-10
lines changed

1 file changed

+57
-10
lines changed

gui-lib/framework/private/path-utils.rkt

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
#lang racket/unit
22

33
(require "sig.rkt"
4+
racket/list
5+
openssl/md5
46
"../preferences.rkt")
57

68
(import)
79
(export framework:path-utils^)
810

911
;; preferences initialized in main.rkt
1012

13+
(define (make-getter/ensure-exists pref-sym)
14+
(λ ()
15+
(let ([maybe-dir (preferences:get pref-sym)])
16+
(and maybe-dir
17+
(directory-exists? maybe-dir)
18+
maybe-dir))))
19+
1120
(define current-backup-dir
12-
(preferences:get/set 'path-utils:backup-dir))
21+
(make-getter/ensure-exists 'path-utils:backup-dir))
1322

1423
(define current-autosave-dir
15-
(preferences:get/set 'path-utils:autosave-dir))
24+
(make-getter/ensure-exists 'path-utils:autosave-dir))
1625

1726
; generate-autosave-name : (or/c #f path-string? path-for-some-system?) -> path?
1827
(define (generate-autosave-name maybe-old-path)
@@ -62,7 +71,9 @@
6271
(if (file-exists? new-name)
6372
(loop (add1 n))
6473
new-name))))
65-
74+
75+
76+
;; generate-backup-name : path? -> path?
6677
(define (generate-backup-name full-name)
6778
(define-values (pre-base old-name dir?)
6879
(split-path full-name))
@@ -92,13 +103,49 @@
92103
; we should always use a complete one.
93104
; Using simplify-path does that and ensures no 'up or 'same
94105
; Using ! is not completely robust, but works well enough for Emacs.
106+
; Windows has limitations on path lengths. Racket handles MAX_PATH
107+
; by using "\\?\" paths when necessary, but individual elements must
108+
; be shorter than lpMaximumComponentLength. If necessary, we avoid
109+
; this by hashing the path.
95110
(define (encode-as-path-element base-maybe-relative name)
96-
(bytes->path-element
97-
(regexp-replace* (case (system-path-convention-type)
98-
[(windows) #rx#"\\\\"]
99-
[else #rx#"/"])
100-
(path->bytes
101-
(simplify-path (build-path base-maybe-relative name)))
102-
#"!")))
111+
(define windows?
112+
(eq? 'windows (system-path-convention-type)))
113+
(define illegal-rx
114+
(if windows?
115+
#rx#"\\\\"
116+
#rx#"/"))
117+
(define pth
118+
(simplify-path (build-path base-maybe-relative name)))
119+
(define legible-name-bytes
120+
(apply
121+
bytes-append
122+
(add-between
123+
(for/list ([elem (in-list (explode-path pth))])
124+
(regexp-replace* illegal-rx
125+
(path-element->bytes elem)
126+
#"!"))
127+
#"!")))
128+
(cond
129+
[(or (not windows?)
130+
(< (bytes-length legible-name-bytes)
131+
(lpMaximumComponentLength)))
132+
(bytes->path-element legible-name-bytes)]
133+
[else
134+
(string->path-element
135+
(regexp-replace*
136+
#rx"\\\\" ; NOT illegal-rx : this is a string regexp
137+
(md5 (open-input-bytes (path->bytes pth)))
138+
"!"))]))
139+
140+
141+
142+
;; lpMaximumComponentLength : -> real?
143+
;; Returns the maximum length of an element of a "\\?\" path on Windows.
144+
;; For now, assuming 255, but really this should be
145+
;; "the value returned in the lpMaximumComponentLength parameter
146+
;; of the GetVolumeInformation function".
147+
(define (lpMaximumComponentLength)
148+
255)
103149

104150

151+

0 commit comments

Comments
 (0)