|
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 elem separator-byte)) |
| 135 | + separator-byte))) |
| 136 | + (define num-legible-bytes |
| 137 | + (bytes-length legible-name-bytes)) |
| 138 | + (bytes->path-element |
| 139 | + (cond |
| 140 | + [(< num-legible-bytes |
| 141 | + (lpMaximumComponentLength)) |
| 142 | + legible-name-bytes] |
| 143 | + [else |
| 144 | + (define replacement |
| 145 | + (bytes-append separator-byte #"..." separator-byte)) |
| 146 | + (define num-excess-bytes |
| 147 | + (+ (- num-legible-bytes |
| 148 | + (lpMaximumComponentLength)) |
| 149 | + 5 ; extra margin of safety |
| 150 | + (bytes-length replacement))) |
| 151 | + (define num-bytes-to-keep-per-side |
| 152 | + (floor (/ (- num-legible-bytes num-excess-bytes) |
| 153 | + 2))) |
| 154 | + (bytes-append |
| 155 | + (subbytes legible-name-bytes 0 num-bytes-to-keep-per-side) |
| 156 | + replacement |
| 157 | + (subbytes legible-name-bytes (- num-legible-bytes |
| 158 | + num-bytes-to-keep-per-side)))]))) |
| 159 | + |
| 160 | + |
| 161 | +;; simplify+explode-path->bytes : path? -> (listof bytes?) |
| 162 | +;; Useful because path-element->bytes doesn't work on root paths. |
| 163 | +;; Using simplify-path ensures no 'up or 'same. |
| 164 | +(define (simplify+explode-path->bytes pth) |
| 165 | + (define elems |
| 166 | + (explode-path (simplify-path pth))) |
| 167 | + (cons (path->bytes (car elems)) |
| 168 | + (map path-element->bytes (cdr elems)))) |
145 | 169 |
|
146 | 170 | ;; lpMaximumComponentLength : -> real? |
147 | 171 | ;; Returns the maximum length of an element of a "\\?\" path on Windows. |
148 | 172 | ;; For now, assuming 255, but really this should be |
149 | 173 | ;; "the value returned in the lpMaximumComponentLength parameter |
150 | 174 | ;; of the GetVolumeInformation function". |
| 175 | +;; See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath |
151 | 176 | (define (lpMaximumComponentLength) |
152 | 177 | 255) |
153 | 178 |
|
|
0 commit comments