func (fp goFilepath) volumeName(path string) string {
	return path[:fp.volumeNameLen(path)]
}
VolumeName returns leading volume name.
Given "C:\foo\bar" it returns "C:" on Windows.
Given "\\host\share\foo" it returns "\\host\share".
On other platforms it returns "".
package fs
import (
	"errors"
	"os"
	"strings"
	"syscall"
)
type goFilepath struct {
	cwd           string
	isWindows     bool
	pathSeparator byte
}
func isSlash(c uint8) bool {
	return c == '\\' || c == '/'
}
var reservedNames = []string{
	"CON", "PRN", "AUX", "NUL",
	"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
	"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
}
func isReservedName(path string) bool {
	if len(path) == 0 {
		return false
	}
	for _, reserved := range reservedNames {
		if strings.EqualFold(path, reserved) {
			return true
		}
	}
	return false
}
func (fp goFilepath) isAbs(path string) bool {
	if !fp.isWindows {
		return strings.HasPrefix(path, "/")
	}
	if isReservedName(path) {
		return true
	}
	l := fp.volumeNameLen(path)
	if l == 0 {
		return false
	}
	path = path[l:]
	if path == "" {
		return false
	}
	return isSlash(path[0])
}
func (fp goFilepath) abs(path string) (string, error) {
	if fp.isAbs(path) {
		return fp.clean(path), nil
	}
	return fp.join([]string{fp.cwd, path}), nil
}
func (fp goFilepath) isPathSeparator(c uint8) bool {
	return c == '/' || (fp.isWindows && c == '\\')
}
func (fp goFilepath) volumeNameLen(path string) int {
	if !fp.isWindows {
		return 0
	}
	if len(path) < 2 {
		return 0
	}
	c := path[0]
	if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
		return 2
	}
	if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
		!isSlash(path[2]) && path[2] != '.' {
		for n := 3; n < l-1; n++ {
			if isSlash(path[n]) {
				n++
				if !isSlash(path[n]) {
					if path[n] == '.' {
						break
					}
					for ; n < l; n++ {
						if isSlash(path[n]) {
							break
						}
					}
					return n
				}
				break
			}
		}
	}
	return 0
}
func (fp goFilepath) evalSymlinks(path string) (string, error) {
	volLen := fp.volumeNameLen(path)
	pathSeparator := string(fp.pathSeparator)
	if volLen < len(path) && fp.isPathSeparator(path[volLen]) {
		volLen++
	}
	vol := path[:volLen]
	dest := vol
	linksWalked := 0
	for start, end := volLen, volLen; start < len(path); start = end {
		for start < len(path) && fp.isPathSeparator(path[start]) {
			start++
		}
		end = start
		for end < len(path) && !fp.isPathSeparator(path[end]) {
			end++
		}
		isWindowsDot := fp.isWindows && path[fp.volumeNameLen(path):] == "."
		if end == start {
			break
		} else if path[start:end] == "." && !isWindowsDot {
			continue
		} else if path[start:end] == ".." {
			var r int
			for r = len(dest) - 1; r >= volLen; r-- {
				if fp.isPathSeparator(dest[r]) {
					break
				}
			}
			if r < volLen || dest[r+1:] == ".." {
				if len(dest) > volLen {
					dest += pathSeparator
				}
				dest += ".."
			} else {
				dest = dest[:r]
			}
			continue
		}
		if len(dest) > fp.volumeNameLen(dest) && !fp.isPathSeparator(dest[len(dest)-1]) {
			dest += pathSeparator
		}
		dest += path[start:end]
		fi, err := os.Lstat(dest)
		if err != nil {
			return "", err
		}
		if fi.Mode()&os.ModeSymlink == 0 {
			if !fi.Mode().IsDir() && end < len(path) {
				return "", syscall.ENOTDIR
			}
			continue
		}
		linksWalked++
		if linksWalked > 255 {
			return "", errors.New("EvalSymlinks: too many links")
		}
		link, err := os.Readlink(dest)
		if err != nil {
			return "", err
		}
		if isWindowsDot && !fp.isAbs(link) {
			break
		}
		path = link + path[end:]
		v := fp.volumeNameLen(link)
		if v > 0 {
			if v < len(link) && fp.isPathSeparator(link[v]) {
				v++
			}
			vol = link[:v]
			dest = vol
			end = len(vol)
		} else if len(link) > 0 && fp.isPathSeparator(link[0]) {
			dest = link[:1]
			end = 1
		} else {
			var r int
			for r = len(dest) - 1; r >= volLen; r-- {
				if fp.isPathSeparator(dest[r]) {
					break
				}
			}
			if r < volLen {
				dest = vol
			} else {
				dest = dest[:r]
			}
			end = 0
		}
	}
	return fp.clean(dest), nil
}
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) string() string {
	if b.buf == nil {
		return b.volAndPath[:b.volLen+b.w]
	}
	return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
}
func (fp goFilepath) fromSlash(path string) string {
	if !fp.isWindows {
		return path
	}
	return strings.ReplaceAll(path, "/", "\\")
}
func (fp goFilepath) clean(path string) string {
	originalPath := path
	volLen := fp.volumeNameLen(path)
	path = path[volLen:]
	if path == "" {
		if volLen > 1 && originalPath[1] != ':' {
			return fp.fromSlash(originalPath)
		}
		return originalPath + "."
	}
	rooted := fp.isPathSeparator(path[0])
	n := len(path)
	out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
	r, dotdot := 0, 0
	if rooted {
		out.append(fp.pathSeparator)
		r, dotdot = 1, 1
	}
	for r < n {
		switch {
		case fp.isPathSeparator(path[r]):
			r++
		case path[r] == '.' && (r+1 == n || fp.isPathSeparator(path[r+1])):
			r++
		case path[r] == '.' && path[r+1] == '.' && (r+2 == n || fp.isPathSeparator(path[r+2])):
			r += 2
			switch {
			case out.w > dotdot:
				out.w--
				for out.w > dotdot && !fp.isPathSeparator(out.index(out.w)) {
					out.w--
				}
			case !rooted:
				if out.w > 0 {
					out.append(fp.pathSeparator)
				}
				out.append('.')
				out.append('.')
				dotdot = out.w
			}
		default:
			if rooted && out.w != 1 || !rooted && out.w != 0 {
				out.append(fp.pathSeparator)
			}
			for ; r < n && !fp.isPathSeparator(path[r]); r++ {
				out.append(path[r])
			}
		}
	}
	if out.w == 0 {
		out.append('.')
	}
	return fp.fromSlash(out.string())
}