|
1 | 1 | #lang racket/unit |
2 | 2 |
|
3 | 3 | (require "sig.rkt" |
| 4 | + racket/list |
| 5 | + openssl/md5 |
4 | 6 | "../preferences.rkt") |
5 | 7 |
|
6 | 8 | (import) |
7 | 9 | (export framework:path-utils^) |
8 | 10 |
|
9 | 11 | ;; preferences initialized in main.rkt |
10 | 12 |
|
| 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 | + |
11 | 20 | (define current-backup-dir |
12 | | - (preferences:get/set 'path-utils:backup-dir)) |
| 21 | + (make-getter/ensure-exists 'path-utils:backup-dir)) |
13 | 22 |
|
14 | 23 | (define current-autosave-dir |
15 | | - (preferences:get/set 'path-utils:autosave-dir)) |
| 24 | + (make-getter/ensure-exists 'path-utils:autosave-dir)) |
16 | 25 |
|
17 | 26 | ; generate-autosave-name : (or/c #f path-string? path-for-some-system?) -> path? |
18 | 27 | (define (generate-autosave-name maybe-old-path) |
|
62 | 71 | (if (file-exists? new-name) |
63 | 72 | (loop (add1 n)) |
64 | 73 | new-name)))) |
65 | | - |
| 74 | + |
| 75 | + |
| 76 | +;; generate-backup-name : path? -> path? |
66 | 77 | (define (generate-backup-name full-name) |
67 | 78 | (define-values (pre-base old-name dir?) |
68 | 79 | (split-path full-name)) |
|
92 | 103 | ; we should always use a complete one. |
93 | 104 | ; Using simplify-path does that and ensures no 'up or 'same |
94 | 105 | ; 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. |
95 | 110 | (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 | + (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 | + |
| 145 | + |
| 146 | +;; lpMaximumComponentLength : -> real? |
| 147 | +;; Returns the maximum length of an element of a "\\?\" path on Windows. |
| 148 | +;; For now, assuming 255, but really this should be |
| 149 | +;; "the value returned in the lpMaximumComponentLength parameter |
| 150 | +;; of the GetVolumeInformation function". |
| 151 | +(define (lpMaximumComponentLength) |
| 152 | + 255) |
103 | 153 |
|
104 | 154 |
|
0 commit comments