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
@@ -176,20 +245,28 @@ func UrlToFilename(s string) (string, bool) {
176245
177246 p = filepath .FromSlash (p )
178247
179- if u .Host != "" {
180- // C:\data\file.txt
181- p = strings .ToUpper (u .Host ) + ":" + p
248+ if runtime .GOOS == "windows" {
249+ return convertFileURLPathWindows (u .Host , p )
182250 }
183251
184252 return p , true
185253}
186254
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 )
255+ func convertFileURLPathWindows (host , path string ) (string , bool ) {
256+ if len (path ) == 0 || path [0 ] != '/' {
257+ return "" , false
193258 }
194- return u .String ()
259+
260+ if host != "" && host != "localhost" {
261+ if filepath .VolumeName (host ) != "" {
262+ return "" , false
263+ }
264+ return `\\` + host + path , true
265+ }
266+
267+ if vol := filepath .VolumeName (path [1 :]); vol == "" || strings .HasPrefix (vol , `\\` ) {
268+ fmt .Println ("HERE??" , path , filepath .VolumeName (path [1 :]))
269+ return "" , false
270+ }
271+ return path [1 :], true
195272}
0 commit comments