package gophermap import ( "bufio" "bytes" "errors" "fmt" "io" "strconv" sr "tildegit.org/tjp/sliderule" "tildegit.org/tjp/sliderule/gopher" ) // Parse reads a gophermap document from a reader. func Parse(input io.Reader) (gopher.MapDocument, error) { rdr := bufio.NewReader(input) doc := gopher.MapDocument{} for num := 1; ; num += 1 { line, err := rdr.ReadBytes('\n') isEOF := errors.Is(err, io.EOF) if err != nil && !isEOF { return nil, err } if len(line) > 2 && !bytes.Equal(line, []byte(".\r\n")) { if line[len(line)-2] != '\r' || line[len(line)-1] != '\n' { return nil, InvalidLine(num) } item := gopher.MapItem{Type: sr.Status(line[0])} spl := bytes.Split(line[1:len(line)-2], []byte{'\t'}) if item.Type != gopher.InfoMessageType && len(spl) != 4 { return nil, InvalidLine(num) } item.Display = string(spl[0]) if len(spl) > 1 { item.Selector = string(spl[1]) } if len(spl) > 2 { item.Hostname = string(spl[2]) } if len(spl) > 3 { item.Port = string(spl[3]) if _, err = strconv.Atoi(item.Port); err != nil { return nil, InvalidLine(num) } } doc = append(doc, item) } if isEOF { break } } return doc, nil } // InvalidLine is returned from Parse when the reader contains a line which is invalid gophermap. type InvalidLine int // Error implements the error interface. func (il InvalidLine) Error() string { return fmt.Sprintf("Invalid gophermap on line %d.", il) }