Skip to content

Commit 012eb39

Browse files
committed
path-utils: Uniform maximum length on all platforms.
When necessary, replace some bytes from the middle rather than hashing the path. (This means it is still human-readable, mostly.)
1 parent 2c3f613 commit 012eb39

File tree

1 file changed

+64
-37
lines changed

1 file changed

+64
-37
lines changed

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

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
(require "sig.rkt"
44
racket/list
5-
openssl/md5
65
"../preferences.rkt")
76

87
(import)
@@ -97,57 +96,85 @@
9796
(build-path base name-element)]))
9897

9998

99+
(define candidate-separators
100+
`(#"!" #"%" #"_" #"|" #":" #">" #"^" #"$" #"@" #"*" #"?"))
101+
102+
(define separator-regexps
103+
(map (compose1 byte-regexp regexp-quote) candidate-separators))
100104

101105
; encode-as-path-element : dir-path path-element -> path-element
102106
; N.B. generate-backup-name may supply a relative directory, but
103107
; we should always use a complete one.
104-
; Using simplify-path does that and ensures no 'up or 'same
105-
; Using ! is not completely robust, but works well enough for Emacs.
108+
; That is handled by simplify+explode-path->bytes.
106109
; Windows has limitations on path lengths. Racket handles MAX_PATH
107110
; by using "\\?\" paths when necessary, but individual elements must
108-
; be shorter than lpMaximumComponentLength. If necessary, we avoid
109-
; this by hashing the path.
111+
; be shorter than lpMaximumComponentLength.
112+
; We respect this limit (on all platforms, for consistency)
113+
; by replacing some bytes from the middle if necessary.
110114
(define (encode-as-path-element base-maybe-relative name)
111-
(define windows?
112-
(eq? 'windows (system-path-convention-type)))
113115
(define illegal-rx
114-
(if windows?
115-
#rx#"\\\\"
116-
#rx#"/"))
117-
(define pth
118-
(simplify-path (build-path base-maybe-relative name)))
116+
(case (system-path-convention-type)
117+
[(windows) #rx#"\\\\"]
118+
[else #rx#"/"]))
119+
(define l-bytes
120+
(simplify+explode-path->bytes (build-path base-maybe-relative name)))
121+
(define separator-byte
122+
(or (let ([all-components (apply bytes-append l-bytes)])
123+
(for/first ([sep (in-list candidate-separators)]
124+
[rx (in-list separator-regexps)]
125+
#:unless (regexp-match? rx all-components))
126+
sep))
127+
#"!"))
119128
(define legible-name-bytes
120-
(let ([elements (explode-path pth)])
121-
(apply
122-
bytes-append
123-
(add-between
124-
(cons (regexp-replace* illegal-rx
125-
(path->bytes (car elements))
126-
#"!")
127-
(for/list ([elem (in-list (cdr elements))])
128-
(regexp-replace* illegal-rx
129-
(path-element->bytes elem)
130-
#"!")))
131-
#"!"))))
132-
(cond
133-
[(or (not windows?)
134-
(< (bytes-length legible-name-bytes)
135-
(lpMaximumComponentLength)))
136-
(bytes->path-element legible-name-bytes)]
137-
[else
138-
(string->path-element
139-
(regexp-replace*
140-
#rx"\\\\" ; NOT illegal-rx : this is a string regexp
141-
(md5 (open-input-bytes (path->bytes pth)))
142-
"!"))]))
143-
144-
129+
(apply
130+
bytes-append
131+
separator-byte
132+
(add-between
133+
(for/list ([elem (in-list l-bytes)])
134+
(regexp-replace* illegal-rx
135+
(path-element->bytes elem)
136+
separator-byte))
137+
separator-byte)))
138+
(define num-legible-bytes
139+
(bytes-length legible-name-bytes))
140+
(bytes->path-element
141+
(cond
142+
[(< num-legible-bytes
143+
(lpMaximumComponentLength))
144+
legible-name-bytes]
145+
[else
146+
(define replacement
147+
(bytes-append separator-byte #"..." separator-byte))
148+
(define num-excess-bytes
149+
(+ (- num-legible-bytes
150+
(lpMaximumComponentLength))
151+
5 ; extra margin of safety
152+
(bytes-length replacement)))
153+
(define num-bytes-to-keep-per-side
154+
(floor (/ (- num-legible-bytes num-excess-bytes)
155+
2)))
156+
(bytes-append
157+
(subbytes legible-name-bytes 0 num-bytes-to-keep-per-side)
158+
replacement
159+
(subbytes legible-name-bytes (- num-legible-bytes
160+
num-bytes-to-keep-per-side)))])))
161+
162+
163+
;; simplify+explode-path->bytes : path? -> (listof bytes?)
164+
;; Useful because path-element->bytes doesn't work on root paths.
165+
;; Using simplify-path ensures no 'up or 'same.
166+
(define (simplify+explode-path->bytes pth)
167+
(define elems
168+
(explode-path (simplify-path pth)))
169+
(cons (path->bytes (car elems))
170+
(map path-element->bytes (cdr elems))))
145171

146172
;; lpMaximumComponentLength : -> real?
147173
;; Returns the maximum length of an element of a "\\?\" path on Windows.
148174
;; For now, assuming 255, but really this should be
149175
;; "the value returned in the lpMaximumComponentLength parameter
150176
;; of the GetVolumeInformation function".
177+
;; See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
151178
(define (lpMaximumComponentLength)
152179
255)
153180

0 commit comments

Comments
 (0)