OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [path/] [filepath/] [path.go] - Rev 848

Go to most recent revision | Compare with Previous | Blame | View Log

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package filepath implements utility routines for manipulating filename paths
// in a way compatible with the target operating system-defined file paths.
package filepath

import (
        "bytes"
        "errors"
        "os"
        "runtime"
        "sort"
        "strings"
)

const (
        Separator     = os.PathSeparator
        ListSeparator = os.PathListSeparator
)

// Clean returns the shortest path name equivalent to path
// by purely lexical processing.  It applies the following rules
// iteratively until no further processing can be done:
//
//      1. Replace multiple Separator elements with a single one.
//      2. Eliminate each . path name element (the current directory).
//      3. Eliminate each inner .. path name element (the parent directory)
//         along with the non-.. element that precedes it.
//      4. Eliminate .. elements that begin a rooted path:
//         that is, replace "/.." by "/" at the beginning of a path,
//         assuming Separator is '/'.
//
// If the result of this process is an empty string, Clean
// returns the string ".".
//
// See also Rob Pike, ``Lexical File Names in Plan 9 or
// Getting Dot-Dot right,''
// http://plan9.bell-labs.com/sys/doc/lexnames.html
func Clean(path string) string {
        vol := VolumeName(path)
        path = path[len(vol):]
        if path == "" {
                if len(vol) > 1 && vol[1] != ':' {
                        // should be UNC
                        return FromSlash(vol)
                }
                return vol + "."
        }
        rooted := os.IsPathSeparator(path[0])

        // Invariants:
        //      reading from path; r is index of next byte to process.
        //      writing to buf; w is index of next byte to write.
        //      dotdot is index in buf where .. must stop, either because
        //              it is the leading slash or it is a leading ../../.. prefix.
        n := len(path)
        buf := []byte(path)
        r, w, dotdot := 0, 0, 0
        if rooted {
                buf[0] = Separator
                r, w, dotdot = 1, 1, 1
        }

        for r < n {
                switch {
                case os.IsPathSeparator(path[r]):
                        // empty path element
                        r++
                case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
                        // . element
                        r++
                case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
                        // .. element: remove to last separator
                        r += 2
                        switch {
                        case w > dotdot:
                                // can backtrack
                                w--
                                for w > dotdot && !os.IsPathSeparator(buf[w]) {
                                        w--
                                }
                        case !rooted:
                                // cannot backtrack, but not rooted, so append .. element.
                                if w > 0 {
                                        buf[w] = Separator
                                        w++
                                }
                                buf[w] = '.'
                                w++
                                buf[w] = '.'
                                w++
                                dotdot = w
                        }
                default:
                        // real path element.
                        // add slash if needed
                        if rooted && w != 1 || !rooted && w != 0 {
                                buf[w] = Separator
                                w++
                        }
                        // copy element
                        for ; r < n && !os.IsPathSeparator(path[r]); r++ {
                                buf[w] = path[r]
                                w++
                        }
                }
        }

        // Turn empty string into "."
        if w == 0 {
                buf[w] = '.'
                w++
        }

        return FromSlash(vol + string(buf[0:w]))
}

// ToSlash returns the result of replacing each separator character
// in path with a slash ('/') character.
func ToSlash(path string) string {
        if Separator == '/' {
                return path
        }
        return strings.Replace(path, string(Separator), "/", -1)
}

// FromSlash returns the result of replacing each slash ('/') character
// in path with a separator character.
func FromSlash(path string) string {
        if Separator == '/' {
                return path
        }
        return strings.Replace(path, "/", string(Separator), -1)
}

// SplitList splits a list of paths joined by the OS-specific ListSeparator.
func SplitList(path string) []string {
        if path == "" {
                return []string{}
        }
        return strings.Split(path, string(ListSeparator))
}

// Split splits path immediately following the final Separator,
// separating it into a directory and file name component.
// If there is no Separator in path, Split returns an empty dir
// and file set to path.
// The returned values have the property that path = dir+file.
func Split(path string) (dir, file string) {
        vol := VolumeName(path)
        i := len(path) - 1
        for i >= len(vol) && !os.IsPathSeparator(path[i]) {
                i--
        }
        return path[:i+1], path[i+1:]
}

// Join joins any number of path elements into a single path, adding
// a Separator if necessary.  All empty strings are ignored.
func Join(elem ...string) string {
        for i, e := range elem {
                if e != "" {
                        return Clean(strings.Join(elem[i:], string(Separator)))
                }
        }
        return ""
}

// Ext returns the file name extension used by path.
// The extension is the suffix beginning at the final dot
// in the final element of path; it is empty if there is
// no dot.
func Ext(path string) string {
        for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
                if path[i] == '.' {
                        return path[i:]
                }
        }
        return ""
}

// EvalSymlinks returns the path name after the evaluation of any symbolic
// links.
// If path is relative it will be evaluated relative to the current directory.
func EvalSymlinks(path string) (string, error) {
        if runtime.GOOS == "windows" {
                // Symlinks are not supported under windows.
                _, err := os.Lstat(path)
                if err != nil {
                        return "", err
                }
                return Clean(path), nil
        }
        const maxIter = 255
        originalPath := path
        // consume path by taking each frontmost path element,
        // expanding it if it's a symlink, and appending it to b
        var b bytes.Buffer
        for n := 0; path != ""; n++ {
                if n > maxIter {
                        return "", errors.New("EvalSymlinks: too many links in " + originalPath)
                }

                // find next path component, p
                i := strings.IndexRune(path, Separator)
                var p string
                if i == -1 {
                        p, path = path, ""
                } else {
                        p, path = path[:i], path[i+1:]
                }

                if p == "" {
                        if b.Len() == 0 {
                                // must be absolute path
                                b.WriteRune(Separator)
                        }
                        continue
                }

                fi, err := os.Lstat(b.String() + p)
                if err != nil {
                        return "", err
                }
                if fi.Mode()&os.ModeSymlink == 0 {
                        b.WriteString(p)
                        if path != "" {
                                b.WriteRune(Separator)
                        }
                        continue
                }

                // it's a symlink, put it at the front of path
                dest, err := os.Readlink(b.String() + p)
                if err != nil {
                        return "", err
                }
                if IsAbs(dest) {
                        b.Reset()
                }
                path = dest + string(Separator) + path
        }
        return Clean(b.String()), nil
}

// Abs returns an absolute representation of path.
// If the path is not absolute it will be joined with the current
// working directory to turn it into an absolute path.  The absolute
// path name for a given file is not guaranteed to be unique.
func Abs(path string) (string, error) {
        if IsAbs(path) {
                return Clean(path), nil
        }
        wd, err := os.Getwd()
        if err != nil {
                return "", err
        }
        return Join(wd, path), nil
}

// Rel returns a relative path that is lexically equivalent to targpath when
// joined to basepath with an intervening separator. That is,
// Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
// On success, the returned path will always be relative to basepath,
// even if basepath and targpath share no elements.
// An error is returned if targpath can't be made relative to basepath or if
// knowing the current working directory would be necessary to compute it.
func Rel(basepath, targpath string) (string, error) {
        baseVol := VolumeName(basepath)
        targVol := VolumeName(targpath)
        base := Clean(basepath)
        targ := Clean(targpath)
        if targ == base {
                return ".", nil
        }
        base = base[len(baseVol):]
        targ = targ[len(targVol):]
        if base == "." {
                base = ""
        }
        // Can't use IsAbs - `\a` and `a` are both relative in Windows.
        baseSlashed := len(base) > 0 && base[0] == Separator
        targSlashed := len(targ) > 0 && targ[0] == Separator
        if baseSlashed != targSlashed || baseVol != targVol {
                return "", errors.New("Rel: can't make " + targ + " relative to " + base)
        }
        // Position base[b0:bi] and targ[t0:ti] at the first differing elements.
        bl := len(base)
        tl := len(targ)
        var b0, bi, t0, ti int
        for {
                for bi < bl && base[bi] != Separator {
                        bi++
                }
                for ti < tl && targ[ti] != Separator {
                        ti++
                }
                if targ[t0:ti] != base[b0:bi] {
                        break
                }
                if bi < bl {
                        bi++
                }
                if ti < tl {
                        ti++
                }
                b0 = bi
                t0 = ti
        }
        if base[b0:bi] == ".." {
                return "", errors.New("Rel: can't make " + targ + " relative to " + base)
        }
        if b0 != bl {
                // Base elements left. Must go up before going down.
                seps := strings.Count(base[b0:bl], string(Separator))
                size := 2 + seps*3
                if tl != t0 {
                        size += 1 + tl - t0
                }
                buf := make([]byte, size)
                n := copy(buf, "..")
                for i := 0; i < seps; i++ {
                        buf[n] = Separator
                        copy(buf[n+1:], "..")
                        n += 3
                }
                if t0 != tl {
                        buf[n] = Separator
                        copy(buf[n+1:], targ[t0:])
                }
                return string(buf), nil
        }
        return targ[t0:], nil
}

// SkipDir is used as a return value from WalkFuncs to indicate that
// the directory named in the call is to be skipped. It is not returned
// as an error by any function.
var SkipDir = errors.New("skip this directory")

// WalkFunc is the type of the function called for each file or directory
// visited by Walk.  If there was a problem walking to the file or directory
// named by path, the incoming error will describe the problem and the
// function can decide how to handle that error (and Walk will not descend
// into that directory).  If an error is returned, processing stops.  The
// sole exception is that if path is a directory and the function returns the
// special value SkipDir, the contents of the directory are skipped
// and processing continues as usual on the next file.
type WalkFunc func(path string, info os.FileInfo, err error) error

// walk recursively descends path, calling w.
func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
        err := walkFn(path, info, nil)
        if err != nil {
                if info.IsDir() && err == SkipDir {
                        return nil
                }
                return err
        }

        if !info.IsDir() {
                return nil
        }

        list, err := readDir(path)
        if err != nil {
                return walkFn(path, info, err)
        }

        for _, fileInfo := range list {
                if err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn); err != nil {
                        return err
                }
        }
        return nil
}

// Walk walks the file tree rooted at root, calling walkFn for each file or
// directory in the tree, including root. All errors that arise visiting files
// and directories are filtered by walkFn. The files are walked in lexical
// order, which makes the output deterministic but means that for very
// large directories Walk can be inefficient.
func Walk(root string, walkFn WalkFunc) error {
        info, err := os.Lstat(root)
        if err != nil {
                return walkFn(root, nil, err)
        }
        return walk(root, info, walkFn)
}

// readDir reads the directory named by dirname and returns
// a sorted list of directory entries.
// Copied from io/ioutil to avoid the circular import.
func readDir(dirname string) ([]os.FileInfo, error) {
        f, err := os.Open(dirname)
        if err != nil {
                return nil, err
        }
        list, err := f.Readdir(-1)
        f.Close()
        if err != nil {
                return nil, err
        }
        sort.Sort(byName(list))
        return list, nil
}

// byName implements sort.Interface.
type byName []os.FileInfo

func (f byName) Len() int           { return len(f) }
func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
func (f byName) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }

// Base returns the last element of path.
// Trailing path separators are removed before extracting the last element.
// If the path is empty, Base returns ".".
// If the path consists entirely of separators, Base returns a single separator.
func Base(path string) string {
        if path == "" {
                return "."
        }
        // Strip trailing slashes.
        for len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) {
                path = path[0 : len(path)-1]
        }
        // Throw away volume name
        path = path[len(VolumeName(path)):]
        // Find the last element
        i := len(path) - 1
        for i >= 0 && !os.IsPathSeparator(path[i]) {
                i--
        }
        if i >= 0 {
                path = path[i+1:]
        }
        // If empty now, it had only slashes.
        if path == "" {
                return string(Separator)
        }
        return path
}

// Dir returns the all but the last element of path, typically the path's directory.
// Trailing path separators are removed before processing.
// If the path is empty, Dir returns ".".
// If the path consists entirely of separators, Dir returns a single separator.
// The returned path does not end in a separator unless it is the root directory.
func Dir(path string) string {
        vol := VolumeName(path)
        i := len(path) - 1
        for i >= len(vol) && !os.IsPathSeparator(path[i]) {
                i--
        }
        dir := Clean(path[len(vol) : i+1])
        last := len(dir) - 1
        if last > 0 && os.IsPathSeparator(dir[last]) {
                dir = dir[:last]
        }
        if dir == "" {
                dir = "."
        }
        return vol + dir
}

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.