package filepathlite
import (
"errors"
"internal/stringslite"
"io/fs"
"slices"
)
var errInvalidPath = errors .New ("invalid path" )
type lazybuf struct {
path string
buf []byte
w int
volAndPath string
volLen int
}
func (b *lazybuf ) index (i int ) byte {
if b .buf != nil {
return b .buf [i ]
}
return b .path [i ]
}
func (b *lazybuf ) append (c byte ) {
if b .buf == nil {
if b .w < len (b .path ) && b .path [b .w ] == c {
b .w ++
return
}
b .buf = make ([]byte , len (b .path ))
copy (b .buf , b .path [:b .w ])
}
b .buf [b .w ] = c
b .w ++
}
func (b *lazybuf ) prepend (prefix ...byte ) {
b .buf = slices .Insert (b .buf , 0 , prefix ...)
b .w += len (prefix )
}
func (b *lazybuf ) string () string {
if b .buf == nil {
return b .volAndPath [:b .volLen +b .w ]
}
return b .volAndPath [:b .volLen ] + string (b .buf [:b .w ])
}
func Clean (path string ) string {
originalPath := path
volLen := volumeNameLen (path )
path = path [volLen :]
if path == "" {
if volLen > 1 && IsPathSeparator (originalPath [0 ]) && IsPathSeparator (originalPath [1 ]) {
return FromSlash (originalPath )
}
return originalPath + "."
}
rooted := IsPathSeparator (path [0 ])
n := len (path )
out := lazybuf {path : path , volAndPath : originalPath , volLen : volLen }
r , dotdot := 0 , 0
if rooted {
out .append (Separator )
r , dotdot = 1 , 1
}
for r < n {
switch {
case IsPathSeparator (path [r ]):
r ++
case path [r ] == '.' && (r +1 == n || IsPathSeparator (path [r +1 ])):
r ++
case path [r ] == '.' && path [r +1 ] == '.' && (r +2 == n || IsPathSeparator (path [r +2 ])):
r += 2
switch {
case out .w > dotdot :
out .w --
for out .w > dotdot && !IsPathSeparator (out .index (out .w )) {
out .w --
}
case !rooted :
if out .w > 0 {
out .append (Separator )
}
out .append ('.' )
out .append ('.' )
dotdot = out .w
}
default :
if rooted && out .w != 1 || !rooted && out .w != 0 {
out .append (Separator )
}
for ; r < n && !IsPathSeparator (path [r ]); r ++ {
out .append (path [r ])
}
}
}
if out .w == 0 {
out .append ('.' )
}
postClean (&out )
return FromSlash (out .string ())
}
func IsLocal (path string ) bool {
return isLocal (path )
}
func unixIsLocal(path string ) bool {
if IsAbs (path ) || path == "" {
return false
}
hasDots := false
for p := path ; p != "" ; {
var part string
part , p , _ = stringslite .Cut (p , "/" )
if part == "." || part == ".." {
hasDots = true
break
}
}
if hasDots {
path = Clean (path )
}
if path == ".." || stringslite .HasPrefix (path , "../" ) {
return false
}
return true
}
func Localize (path string ) (string , error ) {
if !fs .ValidPath (path ) {
return "" , errInvalidPath
}
return localize (path )
}
func ToSlash (path string ) string {
if Separator == '/' {
return path
}
return replaceStringByte (path , Separator , '/' )
}
func FromSlash (path string ) string {
if Separator == '/' {
return path
}
return replaceStringByte (path , '/' , Separator )
}
func replaceStringByte(s string , old , new byte ) string {
if stringslite .IndexByte (s , old ) == -1 {
return s
}
n := []byte (s )
for i := range n {
if n [i ] == old {
n [i ] = new
}
}
return string (n )
}
func Split (path string ) (dir , file string ) {
vol := VolumeName (path )
i := len (path ) - 1
for i >= len (vol ) && !IsPathSeparator (path [i ]) {
i --
}
return path [:i +1 ], path [i +1 :]
}
func Ext (path string ) string {
for i := len (path ) - 1 ; i >= 0 && !IsPathSeparator (path [i ]); i -- {
if path [i ] == '.' {
return path [i :]
}
}
return ""
}
func Base (path string ) string {
if path == "" {
return "."
}
for len (path ) > 0 && IsPathSeparator (path [len (path )-1 ]) {
path = path [0 : len (path )-1 ]
}
path = path [len (VolumeName (path )):]
i := len (path ) - 1
for i >= 0 && !IsPathSeparator (path [i ]) {
i --
}
if i >= 0 {
path = path [i +1 :]
}
if path == "" {
return string (Separator )
}
return path
}
func Dir (path string ) string {
vol := VolumeName (path )
i := len (path ) - 1
for i >= len (vol ) && !IsPathSeparator (path [i ]) {
i --
}
dir := Clean (path [len (vol ) : i +1 ])
if dir == "." && len (vol ) > 2 {
return vol
}
return vol + dir
}
func VolumeName (path string ) string {
return FromSlash (path [:volumeNameLen (path )])
}
func VolumeNameLen (path string ) int {
return volumeNameLen (path )
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .