// CheckPath checks that a module path is valid.
// A valid module path is a valid import path, as checked by CheckImportPath,
-// with two additional constraints.
+// with three additional constraints.
// First, the leading path element (up to the first slash, if any),
// by convention a domain name, must contain only lower-case ASCII letters,
// ASCII digits, dots (U+002E), and dashes (U+002D);
// and must not contain any dots. For paths beginning with "gopkg.in/",
// this second requirement is replaced by a requirement that the path
// follow the gopkg.in server's conventions.
+// Third, no path element may begin with a dot.
func CheckPath(path string) error {
- if err := checkPath(path, false); err != nil {
+ if err := checkPath(path, modulePath); err != nil {
return fmt.Errorf("malformed module path %q: %v", path, err)
}
i := strings.Index(path, "/")
//
// A valid path element is a non-empty string made up of
// ASCII letters, ASCII digits, and limited ASCII punctuation: - . _ and ~.
-// It must not begin or end with a dot (U+002E), nor contain two dots in a row.
+// It must not end with a dot (U+002E), nor contain two dots in a row.
//
// The element prefix up to the first dot must not be a reserved file name
// on Windows, regardless of case (CON, com1, NuL, and so on). The element
// top-level package documentation for additional information about
// subtleties of Unicode.
func CheckImportPath(path string) error {
- if err := checkPath(path, false); err != nil {
+ if err := checkPath(path, importPath); err != nil {
return fmt.Errorf("malformed import path %q: %v", path, err)
}
return nil
}
+// pathKind indicates what kind of path we're checking. Module paths,
+// import paths, and file paths have different restrictions.
+type pathKind int
+
+const (
+ modulePath pathKind = iota
+ importPath
+ filePath
+)
+
// checkPath checks that a general path is valid.
// It returns an error describing why but not mentioning path.
// Because these checks apply to both module paths and import paths,
// the caller is expected to add the "malformed ___ path %q: " prefix.
// fileName indicates whether the final element of the path is a file name
// (as opposed to a directory name).
-func checkPath(path string, fileName bool) error {
+func checkPath(path string, kind pathKind) error {
if !utf8.ValidString(path) {
return fmt.Errorf("invalid UTF-8")
}
elemStart := 0
for i, r := range path {
if r == '/' {
- if err := checkElem(path[elemStart:i], fileName); err != nil {
+ if err := checkElem(path[elemStart:i], kind); err != nil {
return err
}
elemStart = i + 1
}
}
- if err := checkElem(path[elemStart:], fileName); err != nil {
+ if err := checkElem(path[elemStart:], kind); err != nil {
return err
}
return nil
}
// checkElem checks whether an individual path element is valid.
-// fileName indicates whether the element is a file name (not a directory name).
-func checkElem(elem string, fileName bool) error {
+func checkElem(elem string, kind pathKind) error {
if elem == "" {
return fmt.Errorf("empty path element")
}
if strings.Count(elem, ".") == len(elem) {
return fmt.Errorf("invalid path element %q", elem)
}
- if elem[0] == '.' && !fileName {
+ if elem[0] == '.' && kind == modulePath {
return fmt.Errorf("leading dot in path element")
}
if elem[len(elem)-1] == '.' {
return fmt.Errorf("trailing dot in path element")
}
charOK := pathOK
- if fileName {
+ if kind == filePath {
charOK = fileNameOK
}
for _, r := range elem {
}
}
- if fileName {
+ if kind == filePath {
// don't check for Windows short-names in file names. They're
// only an issue for import paths.
return nil
// top-level package documentation for additional information about
// subtleties of Unicode.
func CheckFilePath(path string) error {
- if err := checkPath(path, true); err != nil {
+ if err := checkPath(path, filePath); err != nil {
return fmt.Errorf("malformed file path %q: %v", path, err)
}
return nil
// Versions are allowed to be in non-semver form but must be valid file names
// and not contain exclamation marks.
func EscapeVersion(v string) (escaped string, err error) {
- if err := checkElem(v, true); err != nil || strings.Contains(v, "!") {
+ if err := checkElem(v, filePath); err != nil || strings.Contains(v, "!") {
return "", &InvalidVersionError{
Version: v,
Err: fmt.Errorf("disallowed version string"),
if !ok {
return "", fmt.Errorf("invalid escaped version %q", escaped)
}
- if err := checkElem(v, true); err != nil {
+ if err := checkElem(v, filePath); err != nil {
return "", fmt.Errorf("invalid escaped version %q: %v", v, err)
}
return v, nil