-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
274 lines (257 loc) · 9.04 KB
/
main.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
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package main
import (
"errors"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"unicode/utf8"
"gopkg.in/yaml.v2"
)
// Config structure of configuration from a yaml settings file.
type Config struct {
Linenum string `yaml:"linenum"`
Match string `yaml:"match"`
IgnoredItems []string `yaml:"ignore"`
}
// ListedFiles returns a string of all files in a directory.
var ListedFiles []string
// Cfg returns the user configurations from a file.
var Cfg Config
var Debug int
// @todo excluded regex not working when -f is specified because -f value is part of the path
func CheckExclude(path string, outfile string, folderFlag string) (string, bool) {
regpath := strings.TrimPrefix(path, folderFlag)
m := Cfg.IgnoredItems
reg := []bool{}
//If we are outputting to a file ignore the output file by default if it is in the project path
if outfile != "" {
if strings.Contains(outfile, folderFlag) {
m = append(m, outfile)
}
}
for _, i := range m {
v, _ := regexp.Compile(i)
regCheck := v.MatchString(strings.TrimSpace(regpath))
if regCheck {
reg = append(reg, true)
} else {
reg = append(reg, false)
}
}
for _, r := range reg {
if r {
return path, true
}
}
return path, false
}
func initSettings() error {
logger := GetLoggerType()
dirname, err := os.UserHomeDir()
if err != nil {
logger.Err.Println("Could not get the users home directory", err.Error())
}
f, err := os.Open(dirname + "/.flowcat/config")
f.Close()
if err != nil {
var SetFile *os.File
SetFile, err = os.OpenFile(dirname+"/.flowcatconfig", os.O_WRONLY|io.SeekStart|os.O_CREATE, 0755)
if err != nil {
logger.Err.Println("Could not create settings file when running init", err.Error())
return errors.New("ERROR: could not create settings file")
}
defer SetFile.Close()
_, err = SetFile.WriteString("# Settings\n")
if err != nil {
logger.Err.Println("Could not write to settings file during init", err.Error())
return errors.New("ERROR: could not create settings file")
}
_, err = SetFile.WriteString("match: \"@todo\"\n\n")
if err != nil {
logger.Err.Println("Could not write to settings file during init", err.Error())
return errors.New("ERROR: could not create settings file")
}
_, err = SetFile.WriteString("# File patterns to ignore\n")
if err != nil {
logger.Err.Println("Could not write to settings file during init", err.Error())
return errors.New("ERROR: could not create settings file")
}
_, err = SetFile.WriteString("ignore:\n")
if err != nil {
logger.Err.Println("Could not write to settings file during init", err.Error())
return errors.New("ERROR: could not create settings file")
}
_, err = SetFile.WriteString(" - \"^\\\\..*\"\n")
if err != nil {
logger.Err.Println("Could not write to settings file during init", err.Error())
return errors.New("ERROR: could not create settings file")
}
SetFile.Close()
logger.Info.Println("Settings file created at ~/.flowcat/config")
return nil
}
logger.Err.Println("User ran init but the config file already exists")
fmt.Println("setting file already exists consider editing the file ~/.flowcat/config or delete it before running init if you want to refresh it")
return errors.New("setting file already exists")
}
func init() {
logger := GetLoggerType()
homedir, err := os.UserHomeDir()
if err != nil {
logger.Err.Println("Could not get the users home directory", err.Error())
}
//Make sure the user directory has a folder called .flowcat and there is a logs folder in it
err = os.MkdirAll(homedir+"/.flowcat/logs", 0775)
if err != nil {
logger.Err.Println("Could not create flowcat directories in user folder", homedir+"/.flowcat/logs", err.Error())
}
//Delete the logs each time so we dont fill the harddisk
err = os.Truncate(homedir+"/.flowcat/logs/info.log", 0)
if err != nil {
logger.Err.Println("Could not trucate info.log file", err.Error())
}
err = os.Truncate(homedir+"/.flowcat/logs/error.log", 0)
if err != nil {
logger.Err.Println("Could not trucate error.log file", err.Error())
}
}
func main() {
logger := GetLoggerType()
var F *os.File
var Showlines bool = false
var matchexp string
var outputFile string
//@todo add --dfa flag and pass to lexer file to change lexer compiling to dfa if wanted
folderFlag := flag.String("f", "./", "The project top level directory, where flowcat should start recursing from.")
outputFlag := flag.String("o", "", "Optional output file to dump results to, note output will still be shown on terminal.")
matchFlag := flag.String("m", "", "The string to match to do items on.")
lineFlag := flag.Bool("l", false, "If line numbers should be shown with todo items in output.") //@todo change this to string so we can override the default in the configuration file if needed
helpFlag := flag.Bool("h", false, "Shows the help menu.")
flag.Parse()
//Helpflag implemented because the default help flag from the flag package returns status code 2
if *helpFlag {
fmt.Println("Flowcat version 4.0.0")
fmt.Println("")
fmt.Println("Options for Flowcat:")
fmt.Println("init")
fmt.Println("using flowcat init creates a settings file for the current user, settings can be changed later in the ~/.flowcat/config file")
fmt.Println("-f string")
fmt.Println(" The project top level directory, where flowcat should start recursing from or a specific file (default Current Directory)")
fmt.Println("-l")
fmt.Println(" Display line numbers in the output.")
fmt.Println("-m string")
fmt.Println(" The string to match to do items on. (default 'TODO')")
fmt.Println("-o string")
fmt.Println(" Optional output file to dump results to, note output will still be shown on terminal.")
fmt.Println("-h")
fmt.Println(" This help menu.")
os.Exit(0)
}
//if we are using the init argument then run init function
if len(os.Args) > 1 && os.Args[1] == "init" {
err := initSettings()
if err != nil {
logger.Warn.Println("Could not init settings when running flowcat with init argument", err.Error())
}
//always exit without running if we were using init argument
os.Exit(0)
}
//Get settings from .flowcat file in users home directory
dirname, err := os.UserHomeDir()
if err != nil {
logger.Warn.Println("Could not find user home directory", dirname, err.Error())
}
//@todo check if the folder .flowcat exists in the user dir
//@todo if it does not exist create it
//@todo if there is no .flowcat/settings file then create it with defaults
settings, err := os.OpenFile(dirname+"/.flowcat/config", os.O_RDONLY, 0600)
if err != nil {
logger.Warn.Println("Could not open user configuration file", dirname+"/.flowcat/config", err.Error())
}
defer settings.Close()
configuration := yaml.NewDecoder(settings)
err = configuration.Decode(&Cfg)
if err != nil {
logger.Warn.Println("Unable to get settings from configuration file.", err.Error())
}
// else {
// _ = yaml.Unmarshal(settings, &Cfg)
// // //Ignore errors
// _ = yaml.Unmarshal(settings, &Cfg.IgnoredItems)
// // //Ignore errors
// }
if Cfg.Linenum == "true" {
Showlines = true
}
if *lineFlag {
Showlines = *lineFlag
}
if *matchFlag != "" {
matchexp = *matchFlag
} else if Cfg.Match != "" {
matchexp = Cfg.Match
} else {
matchexp = "TODO"
}
if *outputFlag != "" {
if *folderFlag != "" {
//If the user only supplied a filename without a path then use the filepath supplied at -f
dir, _ := filepath.Split(*outputFlag)
if dir == "" {
dir, _ := filepath.Split(*folderFlag)
outputFile = dir + *outputFlag
} else {
outputFile = *outputFlag
}
} else {
outputFile = *outputFlag
}
//Truncate the output file once before we scan
err := os.Truncate(outputFile, 0)
if err != nil {
logger.Err.Println("Could not trucate output file", outputFile, err.Error())
}
}
//Todo make this its own function and pass in relative information
parseFiles := func(path string, info os.FileInfo, _ error) (err error) {
if outputFile != "" {
F, err = os.OpenFile(outputFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
if err != nil {
logger.Err.Println("could not open specified output file", outputFile, err.Error())
fmt.Println("WARNING could not create output file", err.Error())
}
defer F.Close()
if err != nil && F != nil {
logger.Err.Println("could not write to the specified output file", outputFile, err.Error())
}
}
if info.Mode().IsRegular() {
file, exc := CheckExclude(path, outputFile, *folderFlag)
//If the file does not match our exclusion regex then use it.
if !exc {
logger.Info.Println("Checking file", path)
contents, err := os.ReadFile(path)
if err != nil {
logger.Err.Println("could not read file", file, err)
}
contentbytes := []byte(contents)
if utf8.Valid(contentbytes) {
GetComments(contentbytes, matchexp, path, Showlines, outputFile)
}
}
return nil
}
return nil
}
//Start crawling the base directory
//@todo change below to use filepath.WalkDir instead
err = filepath.Walk(*folderFlag, parseFiles)
if err != nil {
logger.Err.Println("An error occurred while walking directory", err.Error())
}
os.Exit(0)
}