1- // Copyright 2021 The Hugo Authors. All rights reserved.
1+ // Copyright 2024 The Hugo Authors. All rights reserved.
22//
33// Licensed under the Apache License, Version 2.0 (the "License");
44// you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ import (
1818 "net/url"
1919 "path"
2020 "path/filepath"
21+ "runtime"
2122 "strings"
2223)
2324
@@ -159,9 +160,77 @@ func Uglify(in string) string {
159160 return path .Clean (in )
160161}
161162
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+
162231// UrlToFilename converts the URL s to a filename.
163232// 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 ) {
165234 u , err := url .ParseRequestURI (s )
166235 if err != nil {
167236 return filepath .FromSlash (s ), false
@@ -171,25 +240,34 @@ func UrlToFilename(s string) (string, bool) {
171240
172241 if p == "" {
173242 p , _ = url .QueryUnescape (u .Opaque )
174- return filepath .FromSlash (p ), true
243+ return filepath .FromSlash (p ), false
244+ }
245+
246+ if runtime .GOOS != "windows" {
247+ return p , true
248+ }
249+
250+ if len (p ) == 0 || p [0 ] != '/' {
251+ return filepath .FromSlash (p ), false
175252 }
176253
177254 p = filepath .FromSlash (p )
178255
179- if u .Host != "" {
180- // C:\data\ file.txt
181- p = strings .ToUpper (u .Host ) + ":" + p
256+ if len ( u .Host ) == 1 {
257+ // file://c/Users/...
258+ return strings .ToUpper (u .Host ) + ":" + p , true
182259 }
183260
184- return p , true
185- }
261+ if u .Host != "" && u .Host != "localhost" {
262+ if filepath .VolumeName (u .Host ) != "" {
263+ return "" , false
264+ }
265+ return `\\` + u .Host + p , true
266+ }
186267
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 )
268+ if vol := filepath .VolumeName (p [1 :]); vol == "" || strings .HasPrefix (vol , `\\` ) {
269+ return "" , false
193270 }
194- return u .String ()
271+
272+ return p [1 :], true
195273}
0 commit comments