From a91fa9d52ea0bbcf4fbb712eb522976a75a18121 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 11 Feb 2010 10:01:03 -0800 Subject: [PATCH] Steps towards more flexible godoc: The Mapping object implements a flexible mapping of relative to absolute paths and vice versa. R=rsc CC=golang-dev https://golang.org/cl/206067 --- src/cmd/godoc/mapping.go | 176 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/cmd/godoc/mapping.go diff --git a/src/cmd/godoc/mapping.go b/src/cmd/godoc/mapping.go new file mode 100644 index 00000000000..62f85a0747a --- /dev/null +++ b/src/cmd/godoc/mapping.go @@ -0,0 +1,176 @@ +// 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. + +// This file implements the Mapping data structure. + +package main + +import ( + "fmt" + "io" + "os" + pathutil "path" + "strings" +) + + +// A Mapping object maps relative paths (e.g. from URLs) +// to absolute paths (of the file system) and vice versa. +// +// A Mapping object consists of a list of individual mappings +// of the form: prefix -> path which are interpreted as follows: +// A relative path of the form prefix/tail is to be mapped to +// the absolute path/tail, if that absolute path exists in the file +// system. Given a Mapping object, a relative path is mapped to an +// absolute path by trying each of the individual mappings in order, +// until a valid mapping is found. For instance, for the mapping: +// +// user -> /home/user +// public -> /home/user/public +// public -> /home/build/public +// +// the relative paths below are mapped to absolute paths as follows: +// +// user/foo -> /home/user/foo +// public/net/rpc/file1.go -> /home/user/public/net/rpc/file1.go +// +// If there is no /home/user/public/net/rpc/file2.go, the next public +// mapping entry is used to map the relative path to: +// +// public/net/rpc/file2.go -> /home/build/public/net/rpc/file2.go +// +// (assuming that file exists). +// +type Mapping struct { + list []mapping +} + + +type mapping struct { + prefix, path string +} + + +// Init initializes the Mapping from a list of ':'-separated +// paths. Empty paths are ignored; relative paths are assumed +// to be relative to the current working directory and converted +// to absolute paths. For each path of the form: +// +// dirname/localname +// +// a mapping +// +// localname -> path +// +// is added to the Mapping object, in the order of occurrence. +// For instance, the argument: +// +// /home/user:/home/build/public +// +// leads to the following mapping: +// +// user -> /home/user +// public -> /home/build/public +// +func (m *Mapping) Init(paths string) { + cwd, _ := os.Getwd() // ignore errors + + pathlist := strings.Split(paths, ":", 0) + + list := make([]mapping, len(pathlist)) + n := 0 // number of mappings + + for _, path := range pathlist { + if len(path) == 0 { + // ignore empty paths (don't assume ".") + continue + } + + // len(path) > 0: normalize path + if path[0] != '/' { + path = pathutil.Join(cwd, path) + } else { + path = pathutil.Clean(path) + } + + // check if mapping exists already + var i int + for i = 0; i < n; i++ { + if path == list[i].path { + break + } + } + + // add mapping if it is new + if i >= n { + _, prefix := pathutil.Split(path) + list[i] = mapping{prefix, path} + n++ + } + } + + m.list = list[0:n] +} + + +// IsEmpty returns true if there are no mappings specified. +func (m *Mapping) IsEmpty() bool { return len(m.list) == 0 } + + +// Fprint prints the mapping. +func (m *Mapping) Fprint(w io.Writer) { + for _, e := range m.list { + fmt.Fprintf(w, "\t%s -> %s\n", e.prefix, e.path) + } +} + + +func split(path string) (head, tail string) { + i := strings.Index(path, "/") + if i > 0 { + // 0 < i < len(path) + return path[0:i], path[i+1:] + } + return "", path +} + + +// ToAbsolute maps a relative path to an absolute path using the Mapping +// specified by the receiver. If the path cannot be mapped, the empty +// string is returned. +// +func (m *Mapping) ToAbsolute(path string) string { + for _, e := range m.list { + if strings.HasPrefix(path, e.path) { + // /absolute/prefix/foo -> prefix/foo + return pathutil.Join(e.prefix, path[len(e.path):]) // Join will remove a trailing '/' + } + } + return "" // no match +} + + +// ToRelative maps an absolute path to a relative path using the Mapping +// specified by the receiver. If the path cannot be mapped, the empty +// string is returned. +// +func (m *Mapping) ToRelative(path string) string { + prefix, tail := split(path) + for _, e := range m.list { + switch { + case e.prefix == prefix: + // use tail + case e.prefix == "": + tail = path + default: + continue // no match + } + abspath := pathutil.Join(e.path, tail) + if _, err := os.Stat(abspath); err == nil { + return abspath + } + } + + return "" // no match +}