1
- // Copyright 2021 The Hugo Authors. All rights reserved.
1
+ // Copyright 2024 The Hugo Authors. All rights reserved.
2
2
//
3
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
4
// you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ import (
18
18
"net/url"
19
19
"path"
20
20
"path/filepath"
21
+ "runtime"
21
22
"strings"
22
23
)
23
24
@@ -159,37 +160,111 @@ func Uglify(in string) string {
159
160
return path .Clean (in )
160
161
}
161
162
163
+ // URLEscape escapes unicode letters.
164
+ func URLEscape (uri string ) string {
165
+ // escape unicode letters
166
+ u , err := url .Parse (uri )
167
+ if err != nil {
168
+ panic (err )
169
+ }
170
+ return u .String ()
171
+ }
172
+
173
+ // TrimExt trims the extension from a path..
174
+ func TrimExt (in string ) string {
175
+ return strings .TrimSuffix (in , path .Ext (in ))
176
+ }
177
+
178
+ // From https://github.com/golang/go/blob/e0c76d95abfc1621259864adb3d101cf6f1f90fc/src/cmd/go/internal/web/url.go#L45
179
+ func UrlFromFilename (filename string ) (* url.URL , error ) {
180
+ if ! filepath .IsAbs (filename ) {
181
+ return nil , fmt .Errorf ("filepath must be absolute" )
182
+ }
183
+
184
+ // If filename has a Windows volume name, convert the volume to a host and prefix
185
+ // per https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/.
186
+ if vol := filepath .VolumeName (filename ); vol != "" {
187
+ if strings .HasPrefix (vol , `\\` ) {
188
+ filename = filepath .ToSlash (filename [2 :])
189
+ i := strings .IndexByte (filename , '/' )
190
+
191
+ if i < 0 {
192
+ // A degenerate case.
193
+ // \\host.example.com (without a share name)
194
+ // becomes
195
+ // file://host.example.com/
196
+ return & url.URL {
197
+ Scheme : "file" ,
198
+ Host : filename ,
199
+ Path : "/" ,
200
+ }, nil
201
+ }
202
+
203
+ // \\host.example.com\Share\path\to\file
204
+ // becomes
205
+ // file://host.example.com/Share/path/to/file
206
+ return & url.URL {
207
+ Scheme : "file" ,
208
+ Host : filename [:i ],
209
+ Path : filepath .ToSlash (filename [i :]),
210
+ }, nil
211
+ }
212
+
213
+ // C:\path\to\file
214
+ // becomes
215
+ // file:///C:/path/to/file
216
+ return & url.URL {
217
+ Scheme : "file" ,
218
+ Path : "/" + filepath .ToSlash (filename ),
219
+ }, nil
220
+ }
221
+
222
+ // /path/to/file
223
+ // becomes
224
+ // file:///path/to/file
225
+ return & url.URL {
226
+ Scheme : "file" ,
227
+ Path : filepath .ToSlash (filename ),
228
+ }, nil
229
+ }
230
+
162
231
// UrlToFilename converts the URL s to a filename.
163
232
// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
164
- func UrlToFilename (s string ) (string , bool ) {
233
+ func UrlStringToFilename (s string ) (string , bool ) {
165
234
u , err := url .ParseRequestURI (s )
166
235
if err != nil {
167
236
return filepath .FromSlash (s ), false
168
237
}
169
238
170
239
p := u .Path
171
240
172
- if p == "" {
173
- p , _ = url .QueryUnescape (u .Opaque )
174
- return filepath .FromSlash (p ), true
175
- }
176
-
177
- p = filepath .FromSlash (p )
241
+ isWindows := runtime .GOOS == "windows"
178
242
179
- if u .Host != "" {
180
- // C:\data\file.txt
181
- p = strings .ToUpper (u .Host ) + ":" + p
243
+ if isWindows {
244
+ if len (p ) == 0 || p [0 ] != '/' {
245
+ return "" , false
246
+ }
247
+ p = filepath .FromSlash (p )
248
+ if len (u .Host ) == 1 {
249
+ // file://c/Users/...
250
+ return strings .ToUpper (u .Host ) + ":" + p , true
251
+ }
252
+ return convertFileURLPathWindows (u .Host , p )
182
253
}
183
254
184
255
return p , true
185
256
}
186
257
187
- // URLEscape escapes unicode letters.
188
- func URLEscape ( uri string ) string {
189
- // escape unicode letters
190
- u , err := url . Parse ( uri )
191
- if err != nil {
192
- panic ( err )
258
+ func convertFileURLPathWindows ( host , path string ) ( string , bool ) {
259
+ if host != "" && host != "localhost" {
260
+ if filepath . VolumeName ( host ) != "" {
261
+ return "" , false
262
+ }
263
+ return `\\` + host + path , true
193
264
}
194
- return u .String ()
265
+
266
+ if vol := filepath .VolumeName (path [1 :]); vol == "" || strings .HasPrefix (vol , `\\` ) {
267
+ return "" , false
268
+ }
269
+ return path [1 :], true
195
270
}
0 commit comments