diff options
Diffstat (limited to 'textproto')
-rw-r--r-- | textproto/catblob.go (renamed from textproto/io.go) | 100 | ||||
-rw-r--r-- | textproto/doc.go | 24 | ||||
-rw-r--r-- | textproto/fastimport.go | 108 | ||||
-rw-r--r-- | textproto/types.go | 144 |
4 files changed, 136 insertions, 240 deletions
diff --git a/textproto/io.go b/textproto/catblob.go index dde5470..6836689 100644 --- a/textproto/io.go +++ b/textproto/catblob.go @@ -13,14 +13,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. -// Package textproto implements low-level details of the fast-import -// format. -// -// This package deals with parsing and marshalling idiosyncratic -// datatypes used by the format (Ident tuples, 18-bit Mode numbers, -// oddly-quoted Path strings), and abstracting over special-case -// commands that break the "line-based" nature of the format (the -// "data" command, responses to the "cat-blob" command). package textproto import ( @@ -28,93 +20,8 @@ import ( "fmt" "io" "strconv" - "strings" ) -// FIReader is a low-level parser of a fast-import stream. -type FIReader struct { - r *bufio.Reader - - line *string - err error -} - -// NewFIReader creates a new FIReader parser. -func NewFIReader(r io.Reader) *FIReader { - return &FIReader{ - r: bufio.NewReader(r), - } -} - -// ReadLine reads a "line" from the stream; with special handling for -// the "data" command, which isn't really a single line, but rather -// contains arbitrary binary data. -func (fir *FIReader) ReadLine() (line string, err error) { - for len(line) <= 1 { - line, err = fir.r.ReadString('\n') - if err != nil { - return - } - } - - if strings.HasPrefix(line, "data ") { - if line[5:7] == "<<" { - // Delimited format - delim := line[7 : len(line)-1] - suffix := "\n" + delim + "\n" - - for !strings.HasSuffix(line, suffix) { - var _line string - _line, err = fir.r.ReadString('\n') - line += _line - if err != nil { - return - } - } - } else { - // Exact byte count format - var size int - size, err = strconv.Atoi(line[5 : len(line)-1]) - if err != nil { - return - } - data := make([]byte, size) - _, err = io.ReadFull(fir.r, data) - line += string(data) - } - } - return -} - -// FIWriter is a low-level marshaller of a fast-import stream. -type FIWriter struct { - w io.Writer -} - -// NewFIWriter creates a new FIWriter marshaller. -func NewFIWriter(w io.Writer) *FIWriter { - return &FIWriter{ - w: w, - } -} - -// WriteLine writes an ordinary line to the stream; arguments are -// handled similarly to fmt.Println. -func (fiw *FIWriter) WriteLine(a ...interface{}) error { - _, err := fmt.Fprintln(fiw.w, a...) - return err -} - -// WriteData writes a 'data' command to the stream. -func (fiw *FIWriter) WriteData(data string) error { - err := fiw.WriteLine("data", len(data)) - if err != nil { - return err - } - _, err = io.WriteString(fiw.w, data) - return err -} - // CatBlobReader is a low-level parser of an fast-import auxiliary // "cat-blob" stream. type CatBlobReader struct { @@ -140,11 +47,12 @@ func (cbr *CatBlobReader) ReadLine() (line string, err error) { } // get-mark : <sha1> LF - // cat-blob : <sha1> SP 'blob' SP <size> LF <data> LF + // cat-blob : <sha1> SP 'blob' SP <size> LF + // <data> LF // ls : <mode> SP ('blob' | 'tree' | 'commit') SP <dataref> HT <path> LF // ls : 'missing' SP <path> LF - // decide if we have a cat-blob result + // decide if we have a cat-blob result (return early if we don't) if len(line) <= 46 || line[40:46] != " blob " { return } @@ -161,7 +69,7 @@ func (cbr *CatBlobReader) ReadLine() (line string, err error) { } data := make([]byte, size+1) _, err = io.ReadFull(cbr.r, data) - line += string(data[:size]) + line += string(data) return } diff --git a/textproto/doc.go b/textproto/doc.go new file mode 100644 index 0000000..82154a1 --- /dev/null +++ b/textproto/doc.go @@ -0,0 +1,24 @@ +// Copyright (C) 2017-2018 Luke Shumaker <lukeshu@lukeshu.com> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +// Package textproto implements low-level details of the fast-import +// format. +// +// This package deals with parsing and marshalling idiosyncratic +// datatypes used by the format (Ident tuples, 18-bit Mode numbers, +// oddly-quoted Path strings), and abstracting over special-case +// commands that break the "line-based" nature of the format (the +// "data" command, responses to the "cat-blob" command). +package textproto diff --git a/textproto/fastimport.go b/textproto/fastimport.go new file mode 100644 index 0000000..b43c378 --- /dev/null +++ b/textproto/fastimport.go @@ -0,0 +1,108 @@ +// Copyright (C) 2017-2018 Luke Shumaker <lukeshu@lukeshu.com> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +package textproto + +import ( + "bufio" + "fmt" + "io" + "strconv" + "strings" +) + +// FIReader is a low-level parser of a fast-import stream. +type FIReader struct { + r *bufio.Reader + + line *string + err error +} + +// NewFIReader creates a new FIReader parser. +func NewFIReader(r io.Reader) *FIReader { + return &FIReader{ + r: bufio.NewReader(r), + } +} + +// ReadLine reads a "line" from the stream; with special handling for +// the "data" command, which isn't really a single line, but rather +// contains arbitrary binary data. +func (fir *FIReader) ReadLine() (line string, err error) { + for len(line) <= 1 { + line, err = fir.r.ReadString('\n') + if err != nil { + return + } + } + + if strings.HasPrefix(line, "data ") { + if line[5:7] == "<<" { + // Delimited format + delim := line[7 : len(line)-1] + suffix := "\n" + delim + "\n" + + for !strings.HasSuffix(line, suffix) { + var _line string + _line, err = fir.r.ReadString('\n') + line += _line + if err != nil { + return + } + } + } else { + // Exact byte count format + var size int + size, err = strconv.Atoi(line[5 : len(line)-1]) + if err != nil { + return + } + data := make([]byte, size) + _, err = io.ReadFull(fir.r, data) + line += string(data) + } + } + return +} + +// FIWriter is a low-level marshaller of a fast-import stream. +type FIWriter struct { + w io.Writer +} + +// NewFIWriter creates a new FIWriter marshaller. +func NewFIWriter(w io.Writer) *FIWriter { + return &FIWriter{ + w: w, + } +} + +// WriteLine writes an ordinary line to the stream; arguments are +// handled similarly to fmt.Println. +func (fiw *FIWriter) WriteLine(a ...interface{}) error { + _, err := fmt.Fprintln(fiw.w, a...) + return err +} + +// WriteData writes a 'data' command to the stream. +func (fiw *FIWriter) WriteData(data string) error { + err := fiw.WriteLine("data", len(data)) + if err != nil { + return err + } + _, err = io.WriteString(fiw.w, data) + return err +} diff --git a/textproto/types.go b/textproto/types.go deleted file mode 100644 index 1ff21a8..0000000 --- a/textproto/types.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (C) 2017-2018 Luke Shumaker <lukeshu@lukeshu.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package textproto - -import ( - "fmt" - "strconv" - "strings" - "time" -) - -// Ident is a tuple of a commiter's (or author's) name, email, and a -// timestamp with timezone. -// -// BUG(lukeshu): Ident (and ParseIdent) only supports the "raw" date -// format (not "rfc2822" or "now") -type Ident struct { - Name string - Email string - Time time.Time -} - -func (ut Ident) String() string { - if ut.Name == "" { - return fmt.Sprintf("<%s> %d %s", - ut.Name, - ut.Email, - ut.Time.Unix(), - ut.Time.Format("-0700")) - } else { - return fmt.Sprintf("%s <%s> %d %s", - ut.Name, - ut.Email, - ut.Time.Unix(), - ut.Time.Format("-0700")) - } -} - -// ParseIdent parses a string containing an Ident. -// -// The format of this string is -// -// <name> SP LT <email> GT SP <time> SP <offutc> -// -// Where <name> may contain a space, but not "<" or ">"; <time> is an -// integer number of seconds since the UNIX epoch (UTC); <offutc> is -// positive or negative 4-digit offset from UTC (for example, EST -// would be "-0500"). -func ParseIdent(str string) (Ident, error) { - ret := Ident{} - lt := strings.IndexAny(str, "<>") - if lt < 0 || str[lt] != '<' { - return ret, fmt.Errorf("Missing < in ident string: %v", str) - } - if lt > 0 { - if str[lt-1] != ' ' { - return ret, fmt.Errorf("Missing space before < in ident string: %v", str) - } - ret.Name = str[:lt-1] - } - gt := lt + 1 + strings.IndexAny(str[lt+1:], "<>") - if gt < lt+1 || str[gt] != '>' { - return ret, fmt.Errorf("Missing > in ident string: %v", str) - } - if str[gt+1] != ' ' { - return ret, fmt.Errorf("Missing space after > in ident string: %v", str) - } - ret.Email = str[lt+1 : gt] - - strWhen := str[gt+2:] - sp := strings.IndexByte(strWhen, ' ') - if sp < 0 { - return ret, fmt.Errorf("missing time zone in when: %v", str) - } - sec, err := strconv.ParseInt(strWhen[:sp], 10, 64) - if err != nil { - return ret, err - } - tzt, err := time.Parse("-0700", strWhen[sp+1:]) - if err != nil { - return ret, err - } - ret.Time = time.Unix(sec, 0).In(tzt.Location()) - - return ret, nil -} - -// Mode is a file mode as seen by git. -type Mode uint32 // 18 bits - -const ( - ModeFil = Mode(0100644) // A regular file - ModeExe = Mode(0100755) // An executable file - ModeSym = Mode(0120000) // A symbolic link - ModeGit = Mode(0160000) // A nested git repository (e.g. submodule) - ModeDir = Mode(0040000) // A directory -) - -func (m Mode) String() string { - return fmt.Sprintf("%06o", m) -} - -func (m Mode) GoString() string { - return fmt.Sprintf("%07o", m) -} - -// Path is a string storing a git path. -type Path string - -// PathEscape escapes a path in case it contains special characters. -func PathEscape(path Path) string { - if strings.HasPrefix(string(path), "\"") || strings.ContainsRune(string(path), '\n') { - return "\"" + strings.Replace(strings.Replace(strings.Replace(string(path), "\\", "\\\\", -1), "\"", "\\\"", -1), "\n", "\\n", -1) + "\"" - } else { - return string(path) - } -} - -// PathUnescape unescapes a quoted path. -func PathUnescape(epath string) Path { - if strings.HasPrefix(epath, "\"") { - return Path(strings.Replace(strings.Replace(strings.Replace(epath[1:len(epath)-1], "\\n", "\n", -1), "\\\"", "\"", -1), "\\\\", "\\", -1)) - } else { - return Path(epath) - } -} - -// String calls PathEscape on the Path. -func (p Path) String() string { - return PathEscape(p) -} |