Skip to content

Commit

Permalink
feat: add option to allow invalid characters
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Aug 12, 2024
1 parent 7ff5526 commit b862694
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ linters:
- makezero
- misspell
- nakedret
- nestif
- nilerr
- nilnil
- noctx
Expand Down Expand Up @@ -89,6 +88,7 @@ linters:
- lll
- maintidx
- musttag
- nestif
- nlreturn
- paralleltest
- tagliatelle
Expand Down
6 changes: 5 additions & 1 deletion cmd/parse-all/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import (
var igcExtensionRx = regexp.MustCompile(`(?i)\.igc\z`)

func run() error {
allowInvalidChars := flag.Bool("allow-invalid-chars", true, "allow invalid characters")
flag.Parse()
options := []igc.ParseOption{
igc.WithAllowInvalidChars(*allowInvalidChars),
}
for _, arg := range flag.Args() {
if err := fs.WalkDir(os.DirFS(arg), ".", func(path string, dirEntry fs.DirEntry, err error) error {
if err != nil {
Expand All @@ -32,7 +36,7 @@ func run() error {
return err
}
defer file.Close()
igcFile, err := igc.Parse(file)
igcFile, err := igc.Parse(file, options...)
if err != nil {
return err
}
Expand Down
8 changes: 4 additions & 4 deletions igc.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,11 @@ type IGC struct {
}

// Parse parses an IGC from r.
func Parse(r io.Reader) (*IGC, error) {
return newParser().parse(r)
func Parse(r io.Reader, options ...ParseOption) (*IGC, error) {
return newParser(options...).parse(r)
}

// Parse parses an IGC from lines.
func ParseLines(lines []string) (*IGC, error) {
return newParser().parseLines(lines)
func ParseLines(lines []string, options ...ParseOption) (*IGC, error) {
return newParser(options...).parseLines(lines)
}
6 changes: 4 additions & 2 deletions igc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ func TestParseLine(t *testing.T) {
{
name: "unknown_record_null",
line: "\x00",
expectedErr: `1: "\x00": unknown record type`,
expectedErr: "1: \"\\x00\": unknown record type\n'\\x00': invalid character",
},
{
name: "empty line",
Expand Down Expand Up @@ -953,7 +953,9 @@ func TestParseTestData(t *testing.T) {
file, err := os.Open(filepath.Join("testdata", name))
assert.NoError(t, err)
defer file.Close()
igc, err := igc.Parse(file)
igc, err := igc.Parse(file,
igc.WithAllowInvalidChars(true),
)
assert.NoError(t, err)
assertEqualErrors(t, expectedErrorsByName[name], igc.Errs)
})
Expand Down
35 changes: 33 additions & 2 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ func (e unknownRecordTypeError) Error() string {
}

var (
invalidCharsRx = regexp.MustCompile(`([^\x20\x22-\x23\x25-\x29\x2b-\x5b\x5d\x5f-\x7d])`)

aRecordRx = regexp.MustCompile(`\AA([A-Z]{3})(.*)\z`)
bRecordRx = regexp.MustCompile(`\AB(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])([0-9\-]\d{4})([0-9\-]\d{4})(.*)\z`)
cRecordRx = regexp.MustCompile(`\AC(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])(.*)\z`)
Expand All @@ -76,6 +78,7 @@ var (
)

type parser struct {
allowInvalidChars bool
date time.Time
prevTime time.Time
cRecords []Record
Expand All @@ -92,15 +95,27 @@ type parser struct {
kRecordAdditions []BKRecordAddition
}

func newParser() *parser {
return &parser{
type ParseOption func(*parser)

func WithAllowInvalidChars(allowInvalidChars bool) ParseOption {
return func(p *parser) {
p.allowInvalidChars = allowInvalidChars
}
}

func newParser(options ...ParseOption) *parser {
p := &parser{
bRecordsAdditionsByTLC: make(map[string]*BKRecordAddition),
latMinMul: 1,
latMinDiv: 6e4,
lonMinMul: 1,
lonMinDiv: 6e4,
fracSecondMul: 1e9,
}
for _, o := range options {
o(p)
}
return p
}

func (p *parser) parse(r io.Reader) (*IGC, error) {
Expand Down Expand Up @@ -158,6 +173,22 @@ func (p *parser) parseLines(lines []string) (*IGC, error) {
default:
err = unknownRecordTypeError(line[0])
}
if !p.allowInvalidChars {
if match := invalidCharsRx.FindStringSubmatch(lineStr); match != nil {
invalidChar := match[1][0]
var invalidCharErr error
if '\x20' <= invalidChar && invalidChar <= '\x7f' {
invalidCharErr = fmt.Errorf("'%c': invalid character", invalidChar)
} else {
invalidCharErr = fmt.Errorf("'\\x%02x': invalid character", invalidChar)
}
if err == nil {
err = invalidCharErr
} else {
err = errors.Join(err, invalidCharErr)
}
}
}
records = append(records, record)
if err != nil {
errs = append(errs, &Error{
Expand Down
47 changes: 47 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,50 @@ func TestIntPow(t *testing.T) {
})
}
}

func TestReservedCharacters(t *testing.T) {
for c, expected := range map[byte]bool{
'\r': true,
'\n': true,
' ': false,
'!': true,
'"': false,
'#': false,
'$': true,
'%': false,
'&': false,
'\'': false,
'(': false,
')': false,
'0': false,
'9': false,
':': false,
';': false,
'<': false,
'=': false,
'>': false,
'?': false,
'@': false,
'A': false,
'Z': false,
'[': false,
'\\': true,
']': false,
'^': true,
'_': false,
'`': false,
'a': false,
'z': false,
'{': false,
'|': false,
'}': false,
'~': true,
'*': true,
'\x80': true,
'\xff': true,
} {
t.Run(string(c), func(t *testing.T) {
assert.Equal(t, expected, invalidCharsRx.MatchString(string(c)))
})
}
}

0 comments on commit b862694

Please sign in to comment.