Skip to content

Commit 3e0d644

Browse files
committed
Added validate-xml sub-command
1 parent 9fe0fac commit 3e0d644

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed

Diff for: README.md

+5
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ Extract URLs from the named files, or STDIN. URLs are parsed naively with a sim
294294
Validate JSON files for correctness and syntax-errors.
295295

296296

297+
## validate-xml
298+
299+
Validate XML files for correctness and syntax-errors.
300+
301+
297302
## validate-yaml
298303

299304
Validate YAML files for correctness and syntax-errors.

Diff for: cmd_validate_xml.go

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/xml"
6+
"flag"
7+
"fmt"
8+
"io"
9+
"io/ioutil"
10+
"os"
11+
"strings"
12+
)
13+
14+
// Structure for our options and state.
15+
type validateXMLCommand struct {
16+
17+
// comma-separated list of files to exclude, as set by the
18+
// command-line flag.
19+
exclude string
20+
21+
// an array of patterns to exclude, calculated from the
22+
// exclude setting above.
23+
excluded []string
24+
25+
// Should we report on what we're testing.
26+
verbose bool
27+
}
28+
29+
// identReader is a hack which allows us to ignore character-conversion
30+
// issues, depending on the encoded-characterset of the XML input.
31+
//
32+
// We use this because we care little for the attributes/values, instead
33+
// wanting to check for tag-validity.
34+
func identReader(encoding string, input io.Reader) (io.Reader, error) {
35+
return input, nil
36+
}
37+
38+
// Arguments adds per-command args to the object.
39+
func (vx *validateXMLCommand) Arguments(f *flag.FlagSet) {
40+
f.BoolVar(&vx.verbose, "verbose", false, "Should we be verbose")
41+
f.StringVar(&vx.exclude, "exclude", "", "Comma-separated list of patterns to exclude files from the check")
42+
43+
}
44+
45+
// Info returns the name of this subcommand.
46+
func (vx *validateXMLCommand) Info() (string, string) {
47+
return "validate-xml", `Validate all XML files for syntax.
48+
49+
Details:
50+
51+
This command allows you to validate XML files, by default searching
52+
recursively beneath the current directory for all files which match
53+
the pattern '*.xml'.
54+
55+
If you prefer you may specify a number of directories or files:
56+
57+
- Any file specified will be checked.
58+
- Any directory specified will be recursively scanned for matching files.
59+
- Files that do not have a '.xml' suffix will be ignored.
60+
61+
Example:
62+
63+
$ sysbox validate-xml -verbose file1.xml file2.xml ..
64+
$ sysbox validate-xml -exclude=foo /dir/1/path /file/1/path ..
65+
`
66+
}
67+
68+
// Validate a single file
69+
func (vx *validateXMLCommand) validateFile(path string) error {
70+
71+
// Exclude this file? Based on the supplied list though?
72+
for _, ex := range vx.excluded {
73+
if strings.Contains(path, ex) {
74+
if vx.verbose {
75+
fmt.Printf("SKIPPED\t%s - matched '%s'\n", path, ex)
76+
}
77+
return nil
78+
}
79+
}
80+
81+
// Read the file-contents
82+
data, err := ioutil.ReadFile(path)
83+
if err != nil {
84+
return err
85+
}
86+
87+
// Store the results here.
88+
var result interface{}
89+
90+
// Decode into the results, taking care that we
91+
// wire up some magic to avoid caring about the
92+
// encoding/character-set issues.
93+
r := bytes.NewReader(data)
94+
decoder := xml.NewDecoder(r)
95+
decoder.CharsetReader = identReader
96+
err = decoder.Decode(&result)
97+
98+
// Show the error if there was one, but otherwise only show
99+
// the success if running verbosely.
100+
if err != nil {
101+
fmt.Printf("ERROR\t%s - %s\n", path, err.Error())
102+
} else {
103+
if vx.verbose {
104+
fmt.Printf("OK\t%s\n", path)
105+
}
106+
}
107+
return err
108+
}
109+
110+
// Execute is invoked if the user specifies `validate-xml` as the subcommand.
111+
func (vx *validateXMLCommand) Execute(args []string) int {
112+
113+
// Did we find at least one file with an error?
114+
failed := false
115+
116+
// Create our array of excluded patterns if something
117+
// should be excluded.
118+
if vx.exclude != "" {
119+
vx.excluded = strings.Split(vx.exclude, ",")
120+
}
121+
122+
// Add a fake argument if nothing is present, because we
123+
// want to process the current directory (recursively) by default.
124+
if len(args) < 1 {
125+
args = append(args, ".")
126+
}
127+
128+
// We can handle file/directory names as arguments. If a
129+
// directory is specified then we process it recursively.
130+
//
131+
// We'll start by building up a list of all the files to test,
132+
// before we begin the process of testing. We'll make sure
133+
// our list is unique to cut down on any unnecessary I/O.
134+
todo := make(map[string]bool)
135+
136+
// For each argument ..
137+
for _, arg := range args {
138+
139+
// Check that it actually exists.
140+
info, err := os.Stat(arg)
141+
if os.IsNotExist(err) {
142+
fmt.Printf("The path does not exist: %s\n", arg)
143+
continue
144+
}
145+
146+
// Error?
147+
if err != nil {
148+
fmt.Printf("Failed to stat(%s): %s\n", arg, err.Error())
149+
continue
150+
}
151+
152+
// A directory?
153+
if info.Mode().IsDir() {
154+
155+
// Find suitable entries in the directory
156+
files, err := FindFiles(arg, []string{".xml"})
157+
if err != nil {
158+
fmt.Printf("Error finding files in %s: %s\n", arg, err.Error())
159+
continue
160+
}
161+
162+
// Then record each one.
163+
for _, ent := range files {
164+
todo[ent] = true
165+
}
166+
} else {
167+
168+
// OK the entry we were given is just a file,
169+
// so we'll save the path away.
170+
todo[arg] = true
171+
}
172+
}
173+
174+
//
175+
// Now we have a list of files to process.
176+
//
177+
for file := range todo {
178+
179+
// Run the validation, and note the result
180+
err := vx.validateFile(file)
181+
if err != nil {
182+
failed = true
183+
}
184+
}
185+
186+
// Setup a suitable exit-code
187+
if failed {
188+
return 1
189+
}
190+
191+
return 0
192+
}

Diff for: main.go

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ func main() {
5454
subcommands.Register(&treeCommand{})
5555
subcommands.Register(&urlsCommand{})
5656
subcommands.Register(&validateJSONCommand{})
57+
subcommands.Register(&validateXMLCommand{})
5758
subcommands.Register(&validateYAMLCommand{})
5859
subcommands.Register(&withLockCommand{})
5960

0 commit comments

Comments
 (0)