-
-
Notifications
You must be signed in to change notification settings - Fork 260
/
detector.go
96 lines (80 loc) · 2.01 KB
/
detector.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
package processor
import (
"errors"
"strings"
)
// Given some content attempt to determine if it has a #! that maps to a known language and return the language
func DetectSheBang(content string) (string, error) {
if !strings.HasPrefix(content, "#!") {
return "", errors.New("Missing #!")
}
index := strings.Index(content, "\n")
if index != -1 {
content = content[:index]
}
cmd, err := scanForSheBang([]byte(content))
if err != nil {
return "", err
}
for k, v := range ShebangLookup {
for _, x := range v {
// detects both full path and env usage
if x == cmd {
return k, nil
}
}
}
return "", errors.New("Unknown #!")
}
func scanForSheBang(content []byte) (string, error) {
state := 0
lastSlash := 0
candidate1 := ""
candidate2 := ""
for i := range content {
switch state {
case 0: // Deals with whitespace after #! and before first /
if content[i] == '/' {
lastSlash = i
state = 1
}
case 1: // Once we found the first / keep going till we hit whitespace
if content[i] == '/' {
lastSlash = i
}
// when at the end pull out the candidate
if i == len(content)-1 {
candidate1 = string(content[lastSlash+1 : i+1])
}
// between last slash and here is the first candidate which is either env or perl/php/python etc..
if isWhitespace(content[i]) {
// mark from lastSlash to here as first argument
candidate1 = string(content[lastSlash+1 : i])
state = 2
}
case 2: // We have the first candidate, see if there is another
// go till end of whitespace, mark that spot as new start
if !isWhitespace(content[i]) {
lastSlash = i
state = 3
}
case 3:
if i == len(content)-1 {
candidate2 = string(content[lastSlash : i+1])
}
if isWhitespace(content[i]) {
candidate2 = string(content[lastSlash:i])
state = 4
}
case 4:
break
}
}
switch {
case candidate1 == "env":
return candidate2, nil
case candidate1 != "":
return candidate1, nil
}
return "", errors.New("Unable to determine #! command")
}