package nginx import ( "bufio" "fmt" "io" "regexp" "strings" ) var ErrNullReceiver = fmt.Errorf("decoder receiver must not be null") var ErrInvalidReceiver = fmt.Errorf("decoder receiver must be compatible with *[]*Line") // decoder implements parser.Decoder type decoder struct{ reader io.Reader } func (d *decoder) Decode(v interface{}) error { // check 'v' if v == nil { return ErrNullReceiver } vcast, ok := v.(*[]*Line) if !ok { return ErrInvalidReceiver } r := bufio.NewReader(d.reader) n := -1 // line number // regexes reSection := regexp.MustCompile(`(?m)^([a-z0-9_-]+)\s+\{$`) reAssign := regexp.MustCompile(`(?m)^([a-z0-9_-]+)\s+([^;#]+);$`) reInclude := regexp.MustCompile(`(?m)^include\s+([^;#]+);$`) // actual section tree // tree := []Line{} for { n++ l := &Line{Number: n, Type: NONE} // 1. read line notrim, err := r.ReadString('\n') if err == io.EOF { break } else if err != nil { return err } // 2. ignore empty line := strings.Trim(notrim, " \t\r\n") if len(line) < 1 { continue } // 3. get indentation firstChar := 0 for ; !in_array(" \t\r\n", notrim[firstChar:][0]); firstChar++ { } l.Indent = notrim[0:firstChar] // 3. comment if line[0] == '#' { l.Type = COMMENT l.Components = []string{strings.Trim(line[1:], " \t")} } else if line[0] == ';' { l.Type = COLONCOMMENT l.Components = []string{strings.Trim(line[1:], " \t")} } // 4. section if l.Type == NONE { match := reSection.FindStringSubmatch(line) if match != nil { l.Type = SECTION l.Components = match[1:] } } // 5. include if l.Type == NONE { match := reInclude.FindStringSubmatch(line) if match != nil { l.Type = INCLUDE l.Components = []string{match[1]} } } // 6. assignment if l.Type == NONE { match := reAssign.FindStringSubmatch(line) if match != nil { l.Type = ASSIGNMENT l.Components = match[1:] } } *vcast = append(*vcast, l) } return nil } func in_array(haystack string, needle byte) bool { for i, l := 0, len(haystack); i < l; i++ { if haystack[i] == needle { return true } } return false }