-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinclude.go
More file actions
181 lines (160 loc) · 4.1 KB
/
Copy pathinclude.go
File metadata and controls
181 lines (160 loc) · 4.1 KB
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package folie
// This file contains the machinery for the forth include directive that pulls files in.
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path"
"strings"
"time"
)
var callCount int
// includeFile sends out one file onto tx, line by line, expanding embedded includes as needed.
func includeFile(tx io.Writer, rx <-chan []byte, name string, level int) bool {
f, err := os.Open(name)
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot open %s: %s\n", name, err)
return false
}
defer f.Close()
currDir := path.Dir(name)
currFile := path.Base(name)
currLine := 0
if level == 0 {
callCount = 0
}
callCount++
prefix := fmt.Sprintf("%d>", callCount)
lastMsg := ""
defer func() {
statusMsg(lastMsg, "")
}()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
currLine++
lastMsg = statusMsg(lastMsg, "%s %s %d: ", prefix, currFile, currLine)
line := scanner.Text()
s := strings.TrimLeft(line, " ")
if s == "" || s == "\\" || strings.HasPrefix(s, "\\ ") {
continue // don't send empty or comment-only lines
}
if strings.HasPrefix(line, "include ") {
for _, fname := range strings.Fields(line)[1:] {
statusMsg(lastMsg, "")
if !includeFile(tx, rx, path.Join(currDir, fname), level+1) {
return false
}
}
} else {
buf := make([]byte, len(line)+1)
copy(buf, line)
buf[len(line)] = '\r'
tx.Write(buf)
if !match(line, rx) {
return false
}
}
}
return true
}
// statusMsg prints a formatted string and returns it. It takes the previous
// string to be able to clear it before outputting the new message.
func statusMsg(prev string, desc string, args ...interface{}) string {
msg := fmt.Sprintf(desc, args...)
n := len(msg)
// FIXME this optimisation is incorrect, it sometimes eats up first 3 chars
if false && n > 3 && n == len(prev) && msg[:n-3] == prev[:n-3] {
fmt.Print("\b\b\b", msg[n-3:]) // optimise if only end changes
} else {
if len(msg) < len(prev) {
fmt.Print("\r", strings.Repeat(" ", len(prev)))
}
fmt.Print("\r", msg)
}
return msg
}
func match(expect string, rx <-chan []byte) bool {
timer := time.NewTimer(3 * time.Second)
var pending []byte
for {
select {
case data := <-rx:
pending = append(pending, data...)
if !timer.Stop() {
<-timer.C
}
timer.Reset(time.Second)
/* case <-commandSend:
return false // abort include
*/
case <-time.After(10 * time.Millisecond):
if !bytes.Contains(pending, []byte{'\n'}) {
continue
}
lines := bytes.Split(pending, []byte{'\n'})
// Print and then discard all but the last complete line and the possible
// start of an incomplete line.
n := len(lines)
for i := 0; i < n-2; i++ {
fmt.Printf("%s\n", lines[i])
}
lines = lines[n-2:]
last := string(lines[0]) // line[0] is the last complete line
if len(lines[1]) == 0 {
// There is no partial line, so mecrisp may have caught up.
hasExpected := strings.HasPrefix(last, expect+" ")
if hasExpected || strings.HasSuffix(last, " ok.") {
if last != expect+" ok." {
msg := last
// only show output if source does not start with "("
// ... in that case, show just the comment up to ")"
if hasExpected {
msg = last[len(expect)+1:]
if last[0] == '(' {
if n := strings.Index(expect, ")"); n > 0 {
msg = last[:n+1] + last[len(expect):]
}
}
}
if msg == "" {
return true // don't show empty [if]-skipped lines
}
fmt.Printf("%s\n", msg)
if hasFatalError(last) {
return false // no point in keeping going
}
}
return true
}
}
fmt.Printf("%s\n", last)
pending = lines[1]
case <-timer.C:
if len(pending) == 0 {
return true
}
fmt.Printf("%s (timeout)\n", pending)
return string(pending) == expect+" "
}
}
}
func hasFatalError(s string) bool {
for _, match := range []string{
" not found.",
" is compile-only.",
" Stack not balanced.",
" Stack underflow",
" Stack overflow",
" Flash full",
" Ram full",
" Structures don't match",
" Jump too far",
} {
if strings.HasSuffix(s, match) {
return true
}
}
return false
}