url.go - hugo - [fork] hugo port for 9front
 (HTM) git clone https://git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       url.go (6563B)
       ---
            1 // Copyright 2024 The Hugo Authors. All rights reserved.
            2 //
            3 // Licensed under the Apache License, Version 2.0 (the "License");
            4 // you may not use this file except in compliance with the License.
            5 // You may obtain a copy of the License at
            6 // http://www.apache.org/licenses/LICENSE-2.0
            7 //
            8 // Unless required by applicable law or agreed to in writing, software
            9 // distributed under the License is distributed on an "AS IS" BASIS,
           10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           11 // See the License for the specific language governing permissions and
           12 // limitations under the License.
           13 
           14 package paths
           15 
           16 import (
           17         "fmt"
           18         "net/url"
           19         "path"
           20         "path/filepath"
           21         "runtime"
           22         "strings"
           23 )
           24 
           25 type pathBridge struct{}
           26 
           27 func (pathBridge) Base(in string) string {
           28         return path.Base(in)
           29 }
           30 
           31 func (pathBridge) Clean(in string) string {
           32         return path.Clean(in)
           33 }
           34 
           35 func (pathBridge) Dir(in string) string {
           36         return path.Dir(in)
           37 }
           38 
           39 func (pathBridge) Ext(in string) string {
           40         return path.Ext(in)
           41 }
           42 
           43 func (pathBridge) Join(elem ...string) string {
           44         return path.Join(elem...)
           45 }
           46 
           47 func (pathBridge) Separator() string {
           48         return "/"
           49 }
           50 
           51 var pb pathBridge
           52 
           53 // MakePermalink combines base URL with content path to create full URL paths.
           54 // Example
           55 //
           56 //        base:   http://spf13.com/
           57 //        path:   post/how-i-blog
           58 //        result: http://spf13.com/post/how-i-blog
           59 func MakePermalink(host, plink string) *url.URL {
           60         base, err := url.Parse(host)
           61         if err != nil {
           62                 panic(err)
           63         }
           64 
           65         p, err := url.Parse(plink)
           66         if err != nil {
           67                 panic(err)
           68         }
           69 
           70         if p.Host != "" {
           71                 panic(fmt.Errorf("can't make permalink from absolute link %q", plink))
           72         }
           73 
           74         base.Path = path.Join(base.Path, p.Path)
           75         base.Fragment = p.Fragment
           76         base.RawQuery = p.RawQuery
           77 
           78         // path.Join will strip off the last /, so put it back if it was there.
           79         hadTrailingSlash := (plink == "" && strings.HasSuffix(host, "/")) || strings.HasSuffix(p.Path, "/")
           80         if hadTrailingSlash && !strings.HasSuffix(base.Path, "/") {
           81                 base.Path = base.Path + "/"
           82         }
           83 
           84         return base
           85 }
           86 
           87 // AddContextRoot adds the context root to an URL if it's not already set.
           88 // For relative URL entries on sites with a base url with a context root set (i.e. http://example.com/mysite),
           89 // relative URLs must not include the context root if canonifyURLs is enabled. But if it's disabled, it must be set.
           90 func AddContextRoot(baseURL, relativePath string) string {
           91         url, err := url.Parse(baseURL)
           92         if err != nil {
           93                 panic(err)
           94         }
           95 
           96         newPath := path.Join(url.Path, relativePath)
           97 
           98         // path strips trailing slash, ignore root path.
           99         if newPath != "/" && strings.HasSuffix(relativePath, "/") {
          100                 newPath += "/"
          101         }
          102         return newPath
          103 }
          104 
          105 // URLizeAn
          106 
          107 // PrettifyURL takes a URL string and returns a semantic, clean URL.
          108 func PrettifyURL(in string) string {
          109         x := PrettifyURLPath(in)
          110 
          111         if path.Base(x) == "index.html" {
          112                 return path.Dir(x)
          113         }
          114 
          115         if in == "" {
          116                 return "/"
          117         }
          118 
          119         return x
          120 }
          121 
          122 // PrettifyURLPath takes a URL path to a content and converts it
          123 // to enable pretty URLs.
          124 //
          125 //        /section/name.html       becomes /section/name/index.html
          126 //        /section/name/           becomes /section/name/index.html
          127 //        /section/name/index.html becomes /section/name/index.html
          128 func PrettifyURLPath(in string) string {
          129         return prettifyPath(in, pb)
          130 }
          131 
          132 // Uglify does the opposite of PrettifyURLPath().
          133 //
          134 //        /section/name/index.html becomes /section/name.html
          135 //        /section/name/           becomes /section/name.html
          136 //        /section/name.html       becomes /section/name.html
          137 func Uglify(in string) string {
          138         if path.Ext(in) == "" {
          139                 if len(in) < 2 {
          140                         return "/"
          141                 }
          142                 // /section/name/  -> /section/name.html
          143                 return path.Clean(in) + ".html"
          144         }
          145 
          146         name, ext := fileAndExt(in, pb)
          147         if name == "index" {
          148                 // /section/name/index.html -> /section/name.html
          149                 d := path.Dir(in)
          150                 if len(d) > 1 {
          151                         return d + ext
          152                 }
          153                 return in
          154         }
          155         // /.xml -> /index.xml
          156         if name == "" {
          157                 return path.Dir(in) + "index" + ext
          158         }
          159         // /section/name.html -> /section/name.html
          160         return path.Clean(in)
          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 
          231 // UrlStringToFilename converts the URL s to a filename.
          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)
          235         if err != nil {
          236                 return filepath.FromSlash(s), false
          237         }
          238 
          239         p := u.Path
          240 
          241         if p == "" {
          242                 p, _ = url.QueryUnescape(u.Opaque)
          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
          252         }
          253 
          254         p = filepath.FromSlash(p)
          255 
          256         if len(u.Host) == 1 {
          257                 // file://c/Users/...
          258                 return strings.ToUpper(u.Host) + ":" + p, true
          259         }
          260 
          261         if u.Host != "" && u.Host != "localhost" {
          262                 if filepath.VolumeName(u.Host) != "" {
          263                         return "", false
          264                 }
          265                 return `\\` + u.Host + p, true
          266         }
          267 
          268         if vol := filepath.VolumeName(p[1:]); vol == "" || strings.HasPrefix(vol, `\\`) {
          269                 return "", false
          270         }
          271 
          272         return p[1:], true
          273 }