|
2 | 2 |
|
3 | 3 | (require "sig.rkt"
|
4 | 4 | racket/list
|
5 |
| - openssl/md5 |
6 | 5 | "../preferences.rkt")
|
7 | 6 |
|
8 | 7 | (import)
|
|
97 | 96 | (build-path base name-element)]))
|
98 | 97 |
|
99 | 98 |
|
| 99 | +(define candidate-separators |
| 100 | + `(#"!" #"%" #"_" #"|" #":" #">" #"^" #"$" #"@" #"*" #"?")) |
| 101 | + |
| 102 | +(define separator-regexps |
| 103 | + (map (compose1 byte-regexp regexp-quote) candidate-separators)) |
100 | 104 |
|
101 | 105 | ; encode-as-path-element : dir-path path-element -> path-element
|
102 | 106 | ; N.B. generate-backup-name may supply a relative directory, but
|
103 | 107 | ; 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. |
106 | 109 | ; Windows has limitations on path lengths. Racket handles MAX_PATH
|
107 | 110 | ; 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. |
110 | 114 | (define (encode-as-path-element base-maybe-relative name)
|
111 |
| - (define windows? |
112 |
| - (eq? 'windows (system-path-convention-type))) |
113 | 115 | (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 | + #"!")) |
119 | 128 | (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)))) |
145 | 171 |
|
146 | 172 | ;; lpMaximumComponentLength : -> real?
|
147 | 173 | ;; Returns the maximum length of an element of a "\\?\" path on Windows.
|
148 | 174 | ;; For now, assuming 255, but really this should be
|
149 | 175 | ;; "the value returned in the lpMaximumComponentLength parameter
|
150 | 176 | ;; of the GetVolumeInformation function".
|
| 177 | +;; See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath |
151 | 178 | (define (lpMaximumComponentLength)
|
152 | 179 | 255)
|
153 | 180 |
|
|
0 commit comments