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