/
input.go
254 lines (212 loc) · 6.87 KB
/
input.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
// Copyright (c) 2014 Marcel Wouters
// Package config implements configuration and start-up.
// This source parses the command-line and reads additional input configuration.
package config
import (
"flag"
"fmt"
"github.com/marcelfw/mgit/command"
"github.com/marcelfw/mgit/repository"
go_ini "github.com/vaughan0/go-ini"
"io/ioutil"
"log"
"os"
"os/user"
"path"
"regexp"
"strconv"
)
type configFile struct {
file string
config go_ini.File
}
type configFiles []configFile
var localRegexp *regexp.Regexp
var shortcutRegexp *regexp.Regexp
var commandRegexp *regexp.Regexp
var globalConfigs configFiles
var parentConfigs configFiles
// init
func init() {
localRegexp = regexp.MustCompile("^local$")
shortcutRegexp = regexp.MustCompile("shortcut \"(.+)\"")
commandRegexp = regexp.MustCompile("command \"(.+)\"")
readConfigs()
}
// readConfigs finds all configuration files and loads them
func readConfigs() {
globalConfigs = make([]configFile, 0, 10)
parentConfigs = make([]configFile, 0, 10)
// Follow parent directories and add all configurations.
if wd, err := os.Getwd(); err == nil {
for {
filename := wd + "/.mgit"
if fi, err := os.Stat(filename); err == nil && !fi.IsDir() {
if config, err := go_ini.LoadFile(filename); err == nil {
parentConfigs = append(parentConfigs, configFile{filename, config})
}
}
nwd := path.Dir(wd)
if nwd == wd || nwd == "." {
break
}
wd = nwd
}
}
// Add configuration from user' directory.
if user, err := user.Current(); err == nil {
filename := user.HomeDir + "/.mgit"
if fi, err := os.Stat(filename); err == nil && !fi.IsDir() {
if config, err := go_ini.LoadFile(filename); err == nil {
globalConfigs = append(globalConfigs, configFile{filename, config})
}
}
}
if fi, err := os.Stat("/etc/mgit"); err == nil && !fi.IsDir() {
if config, err := go_ini.LoadFile("/etc/mgit"); err == nil {
globalConfigs = append(globalConfigs, configFile{"/etc/mgit", config})
}
}
}
func reduceConfigs(regexp regexp.Regexp, reduceFunc func(string, []string, map[string]string), configArrays ...configFiles) {
for _, configs := range configArrays {
for _, config := range configs {
for name, vars := range config.config {
match := regexp.FindStringSubmatch(name)
if len(match) >= 1 {
if value, ok := vars["root"]; ok {
if value == "." || (len(value) >= 2 && value[0:2] == "./") {
dir := path.Dir(config.file)
vars["root"] = path.Join(dir, value)
}
}
reduceFunc(config.file, match, vars)
}
}
}
}
}
// readShortcutFromConfiguration reads the configuration and return the filter for the shortcut.
// return bool false if something went wrong.
func readShortcutFromConfiguration(shortcut string) (map[string]string, bool) {
var filterMap map[string]string
var mapFunc = func(file string, match []string, vars map[string]string) {
if len(match) >= 2 && match[1] == shortcut {
if filterMap == nil {
log.Printf("reading shortcut \"%s\" from \"%s\"", shortcut, file)
filterMap = vars
}
}
}
reduceConfigs(*shortcutRegexp, mapFunc, parentConfigs, globalConfigs)
return filterMap, filterMap != nil
}
// readLocalConfiguration reads the configuration and return the first "local" section it finds.
// return bool false if something went wrong.
func readLocalConfiguration() (map[string]string, bool) {
var filterMap map[string]string
var mapFunc = func(file string, match []string, vars map[string]string) {
if len(match) >= 1 {
if filterMap == nil {
filterMap = vars
}
}
}
reduceConfigs(*localRegexp, mapFunc, parentConfigs, globalConfigs)
return filterMap, filterMap != nil
}
// ParseCommandline parses and validates the command-line and return useful structs to continue.
func ParseCommandline(osArgs []string, filterDefs []repository.FilterDefinition) (command string, cmdInteractive bool, args []string, repositoryFilter repository.RepositoryFilter, ok bool) {
var rootDirectory string
var depth int
var shortcut string
var interactive bool
var debug bool
mgitFlags := flag.NewFlagSet("mgitFlags", flag.ContinueOnError)
// These are truly hard-coded for now.
mgitFlags.StringVar(&shortcut, "s", "", "read settings with name from configuration file")
mgitFlags.StringVar(&rootDirectory, "root", "", "set root directory")
mgitFlags.IntVar(&depth, "depth", 0, "maximum depth to search in")
mgitFlags.BoolVar(&interactive, "i", false, "run command interactively")
mgitFlags.BoolVar(&debug, "debug", false, "show debug log")
filters := make([]repository.Filter, 0, len(filterDefs))
for _, filterDef := range filterDefs {
filters = append(filters, filterDef.AddFlags(mgitFlags))
}
mgitFlags.Parse(osArgs)
if !debug {
log.SetOutput(ioutil.Discard)
}
var filterMap map[string]string
if shortcut != "" {
filterMap, ok = readShortcutFromConfiguration(shortcut)
if !ok {
fmt.Printf("Shortcut \"%s\" not found.\n", shortcut)
return command, false, args, repositoryFilter, false
}
} else {
filterMap, ok = readLocalConfiguration()
}
if filterMap == nil {
filterMap = make(map[string]string)
}
if mgitFlags.NArg() == 0 {
fmt.Print("Could not find command to execute.\n")
return command, false, args, repositoryFilter, false
}
mgitFlags.VisitAll(func(flag *flag.Flag) {
if value, ok := filterMap[flag.Name]; ok {
if flag.Value.String() == "" {
flag.Value.Set(value)
}
}
if flag.Value.String() != "" {
log.Printf("Using flag \"%s\" with value \"%s\"", flag.Name, flag.Value.String())
}
})
if rootDirectory == "" {
if value, ok := filterMap["root"]; ok {
rootDirectory = value
}
if rootDirectory == "" {
rootDirectory = "."
}
}
if depth == 0 {
if value, ok := filterMap["depth"]; ok {
if ivalue, err := strconv.ParseInt(value, 10, 0); err == nil {
depth = int(ivalue)
}
}
}
if interactive {
cmdInteractive = true
}
log.Printf("Using root directory and depth \"%s\", \"%d\"", rootDirectory, depth)
repositoryFilter = repository.NewRepositoryFilter(rootDirectory, depth, filters)
args = mgitFlags.Args()
command = args[0]
args = args[1:]
return command, cmdInteractive, args, repositoryFilter, true
}
// createCommand creates a command based on a configuration section.
// returns _, false if command could not be created
func createCommand(vars map[string]string) (repository.Command, bool) {
if value, ok := vars["git"]; ok {
// add Git command
return command.NewGitProxyCommand(value, vars), true
}
return nil, false
}
// AddConfigCommands add commands from the configuration files to the command list.
func AddConfigCommands(commands map[string]repository.Command) map[string]repository.Command {
var cmdFunc = func(file string, match []string, vars map[string]string) {
if len(match) >= 2 {
if command, ok := createCommand(vars); ok {
commands[match[1]] = command
}
}
}
reduceConfigs(*commandRegexp, cmdFunc, parentConfigs, globalConfigs)
return commands
}