| // Copyright 2012 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 get |
| |
| import ( |
| "encoding/xml" |
| "fmt" |
| "io" |
| "strings" |
| ) |
| |
| // charsetReader returns a reader for the given charset. Currently |
| // it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful |
| // error which is printed by go get, so the user can find why the package |
| // wasn't downloaded if the encoding is not supported. Note that, in |
| // order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters |
| // greater than 0x7f are not rejected). |
| func charsetReader(charset string, input io.Reader) (io.Reader, error) { |
| switch strings.ToLower(charset) { |
| case "ascii": |
| return input, nil |
| default: |
| return nil, fmt.Errorf("can't decode XML document using charset %q", charset) |
| } |
| } |
| |
| // parseMetaGoImports returns meta imports from the HTML in r. |
| // Parsing ends at the end of the <head> section or the beginning of the <body>. |
| func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { |
| d := xml.NewDecoder(r) |
| d.CharsetReader = charsetReader |
| d.Strict = false |
| var t xml.Token |
| for { |
| t, err = d.RawToken() |
| if err != nil { |
| if err == io.EOF || len(imports) > 0 { |
| err = nil |
| } |
| return |
| } |
| if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { |
| return |
| } |
| if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { |
| return |
| } |
| e, ok := t.(xml.StartElement) |
| if !ok || !strings.EqualFold(e.Name.Local, "meta") { |
| continue |
| } |
| if attrValue(e.Attr, "name") != "go-import" { |
| continue |
| } |
| if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { |
| // Ignore VCS type "mod", which is new Go modules. |
| // This code is for old go get and must ignore the new mod lines. |
| // Otherwise matchGoImport will complain about two |
| // different metaImport lines for the same Prefix. |
| if f[1] == "mod" { |
| continue |
| } |
| imports = append(imports, metaImport{ |
| Prefix: f[0], |
| VCS: f[1], |
| RepoRoot: f[2], |
| }) |
| } |
| } |
| } |
| |
| // attrValue returns the attribute value for the case-insensitive key |
| // `name', or the empty string if nothing is found. |
| func attrValue(attrs []xml.Attr, name string) string { |
| for _, a := range attrs { |
| if strings.EqualFold(a.Name.Local, name) { |
| return a.Value |
| } |
| } |
| return "" |
| } |