/
file.go
154 lines (126 loc) · 3.27 KB
/
file.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package ast
import (
"os"
"regexp"
"strconv"
"strings"
)
type File struct {
NodeBase
// write out before the script starts
Before bool
// trim the end
TrimEnd bool
// normal file stuff
Path string
Mode os.FileMode
Chown string
// content markers
BegContent int
EndContent int
}
func (P *Parser) parseFile() error {
N := P.node
S := P.script
// grab header line
line := stripTrailingWhitespace(S.Lines[N.BegLine()])
header := strings.Fields(line)
P.logger.Debugf("parseFile: %q %v", line, header)
if len(header) < 3 {
return NewScriptError("Invalid file header format", N, nil)
}
prefix, fparts, suffix := header[0], header[1:len(header)-1], header[len(header)-1]
if len(prefix) < 2 || len(suffix) < 2 {
return NewScriptError("Invalid file header delims", N, nil)
}
before := line[0] == '-'
fchar := line[0:1]
if strings.Count(prefix, fchar) != len(prefix) || strings.Count(suffix, fchar) != len(suffix) {
return NewScriptError("Invalid file header delims", N, nil)
}
fpath := fparts[0]
fargs := fparts[1:]
// create the file
F := &File{
NodeBase: P.node.CloneNodeBase(),
Before: before,
Path: fpath,
Mode: 0644,
Chown: "",
// set to header line so we can compare later
BegContent: P.lineno,
EndContent: P.lineno,
}
// loop over args
for _, arg := range fargs {
// check for trimming
if arg == "trim" || arg == "//" {
F.TrimEnd = true
continue
}
// check for filemode syntax
if b, err := regexp.MatchString("[[:digit:]]+", arg); b || err != nil {
// this should never happen at runtime
if err != nil {
panic(err)
}
fm, cerr := strconv.ParseUint(arg, 8, 32)
if cerr != nil {
return NewScriptError("Invalid file header filemode: "+arg, F, err)
}
F.Mode = os.FileMode(fm)
continue
}
if b, err := regexp.MatchString("[-xwr]+", arg); b || err != nil {
// this should never happen at runtime
if err != nil {
panic(err)
}
return NewScriptError("char based filemode is not supported yet", F, err)
}
// otherwise assume chown
F.Chown = arg
}
// overwrite self on Parser
P.node = Node(F)
//
////// Parse File
//
// inc lineno to first line of content
P.lineno++
// start consuming lines
for !P.EOF() {
l := S.Lines[P.lineno]
// does the line start with the file header prefix? (it should match to indicate transition)
if strings.HasPrefix(l, prefix) {
flds := strings.Fields(l)
// is the first field the same? and there are at least 2 fields? let's make a transition
if len(flds[0]) >= len(prefix) && len(flds) > 2 {
// if we have "<prefix> end .*", consume the line
// otherwise we are assuming the start of the next file and want to keep it
if strings.TrimSpace(strings.ToLower(flds[1])) == "end" {
P.IncLine()
}
// finalize the file and return to main loop
S.AddFile(F)
P.AppendNode(F)
return nil
}
}
// ok, no transition, so accum, inc, and try the next line
// accume and set endings
F.SetEndLine(P.lineno)
F.EndContent = P.lineno
// set initial the first time around
// we may never get here if a file has no content
if F.BegContent == F.BegLine() {
F.BegContent = P.lineno
}
// inc and loop around
P.IncLine()
}
// finalize this file
S.AddFile(F)
P.AppendNode(F)
return nil
}