blob: c287ea2a510d8ba4c248aa5e7f8dd6c5b3e00a78 [file] [log] [blame]
Colin Cross5ad47f42015-01-08 19:35:10 -08001// Mostly copied from Go's src/cmd/gofmt:
2// Copyright 2009 The Go Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6package main
7
8import (
Colin Cross5ad47f42015-01-08 19:35:10 -08009 "bytes"
10 "flag"
11 "fmt"
Colin Cross5ad47f42015-01-08 19:35:10 -080012 "io"
13 "io/ioutil"
14 "os"
15 "os/exec"
16 "path/filepath"
Jeff Gaston6cd6ebc2017-10-05 19:46:15 -070017
18 "github.com/google/blueprint/parser"
Colin Cross5ad47f42015-01-08 19:35:10 -080019)
20
21var (
22 // main operation modes
Jeff Gaston2ca94322017-10-06 16:50:47 -070023 list = flag.Bool("l", false, "list files whose formatting differs from bpfmt's")
24 overwriteSourceFile = flag.Bool("w", false, "write result to (source) file")
25 writeToStout = flag.Bool("o", false, "write result to stdout")
26 doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
27 sortLists = flag.Bool("s", false, "sort arrays")
Colin Cross5ad47f42015-01-08 19:35:10 -080028)
29
30var (
31 exitCode = 0
32)
33
34func report(err error) {
35 fmt.Fprintln(os.Stderr, err)
36 exitCode = 2
37}
38
39func usage() {
Jeff Gaston2ca94322017-10-06 16:50:47 -070040 usageViolation("")
41}
42
43func usageViolation(violation string) {
44 fmt.Fprintln(os.Stderr, violation)
Colin Crossc1d87812018-02-23 10:55:25 -080045 fmt.Fprintln(os.Stderr, "usage: bpfmt [flags] [path ...]")
Colin Cross5ad47f42015-01-08 19:35:10 -080046 flag.PrintDefaults()
47 os.Exit(2)
48}
49
Jeff Gaston6cd6ebc2017-10-05 19:46:15 -070050func processFile(filename string, out io.Writer) error {
51 f, err := os.Open(filename)
52 if err != nil {
53 return err
Colin Cross5ad47f42015-01-08 19:35:10 -080054 }
Jeff Gaston6cd6ebc2017-10-05 19:46:15 -070055 defer f.Close()
Colin Cross5ad47f42015-01-08 19:35:10 -080056
Jeff Gaston6cd6ebc2017-10-05 19:46:15 -070057 return processReader(filename, f, out)
58}
59
60func processReader(filename string, in io.Reader, out io.Writer) error {
Colin Cross5ad47f42015-01-08 19:35:10 -080061 src, err := ioutil.ReadAll(in)
62 if err != nil {
63 return err
64 }
65
66 r := bytes.NewBuffer(src)
67
68 file, errs := parser.Parse(filename, r, parser.NewScope(nil))
69 if len(errs) > 0 {
70 for _, err := range errs {
71 fmt.Fprintln(os.Stderr, err)
72 }
73 return fmt.Errorf("%d parsing errors", len(errs))
74 }
75
76 if *sortLists {
77 parser.SortLists(file)
78 }
79
80 res, err := parser.Print(file)
81 if err != nil {
82 return err
83 }
84
85 if !bytes.Equal(src, res) {
86 // formatting has changed
87 if *list {
88 fmt.Fprintln(out, filename)
89 }
Jeff Gaston2ca94322017-10-06 16:50:47 -070090 if *overwriteSourceFile {
Colin Cross5ad47f42015-01-08 19:35:10 -080091 err = ioutil.WriteFile(filename, res, 0644)
92 if err != nil {
93 return err
94 }
95 }
96 if *doDiff {
97 data, err := diff(src, res)
98 if err != nil {
99 return fmt.Errorf("computing diff: %s", err)
100 }
101 fmt.Printf("diff %s bpfmt/%s\n", filename, filename)
102 out.Write(data)
103 }
104 }
105
Jeff Gaston2ca94322017-10-06 16:50:47 -0700106 if !*list && !*overwriteSourceFile && !*doDiff {
Colin Cross5ad47f42015-01-08 19:35:10 -0800107 _, err = out.Write(res)
108 }
109
110 return err
111}
112
Colin Cross5ad47f42015-01-08 19:35:10 -0800113func walkDir(path string) {
Jeff Gaston6cd6ebc2017-10-05 19:46:15 -0700114 visitFile := func(path string, f os.FileInfo, err error) error {
115 if err == nil && f.Name() == "Blueprints" {
116 err = processFile(path, os.Stdout)
117 }
118 if err != nil {
119 report(err)
120 }
121 return nil
122 }
123
Colin Cross5ad47f42015-01-08 19:35:10 -0800124 filepath.Walk(path, visitFile)
125}
126
127func main() {
Colin Crosse52c33c2019-05-21 10:22:28 -0700128 flag.Usage = usage
Colin Cross5ad47f42015-01-08 19:35:10 -0800129 flag.Parse()
130
Jeff Gaston2ca94322017-10-06 16:50:47 -0700131 if !*writeToStout && !*overwriteSourceFile && !*doDiff && !*list {
132 usageViolation("one of -d, -l, -o, or -w is required")
133 }
134
Colin Cross5ad47f42015-01-08 19:35:10 -0800135 if flag.NArg() == 0 {
Jeff Gaston2ca94322017-10-06 16:50:47 -0700136 // file to parse is stdin
137 if *overwriteSourceFile {
Colin Cross5ad47f42015-01-08 19:35:10 -0800138 fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
Colin Crosse52c33c2019-05-21 10:22:28 -0700139 os.Exit(2)
Colin Cross5ad47f42015-01-08 19:35:10 -0800140 }
Jeff Gaston6cd6ebc2017-10-05 19:46:15 -0700141 if err := processReader("<standard input>", os.Stdin, os.Stdout); err != nil {
Colin Cross5ad47f42015-01-08 19:35:10 -0800142 report(err)
143 }
144 return
145 }
146
147 for i := 0; i < flag.NArg(); i++ {
148 path := flag.Arg(i)
149 switch dir, err := os.Stat(path); {
150 case err != nil:
151 report(err)
152 case dir.IsDir():
153 walkDir(path)
154 default:
Jeff Gaston6cd6ebc2017-10-05 19:46:15 -0700155 if err := processFile(path, os.Stdout); err != nil {
Colin Cross5ad47f42015-01-08 19:35:10 -0800156 report(err)
157 }
158 }
159 }
Colin Crosse52c33c2019-05-21 10:22:28 -0700160
161 os.Exit(exitCode)
Colin Cross5ad47f42015-01-08 19:35:10 -0800162}
163
164func diff(b1, b2 []byte) (data []byte, err error) {
165 f1, err := ioutil.TempFile("", "bpfmt")
166 if err != nil {
167 return
168 }
169 defer os.Remove(f1.Name())
170 defer f1.Close()
171
172 f2, err := ioutil.TempFile("", "bpfmt")
173 if err != nil {
174 return
175 }
176 defer os.Remove(f2.Name())
177 defer f2.Close()
178
179 f1.Write(b1)
180 f2.Write(b2)
181
182 data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
183 if len(data) > 0 {
184 // diff exits with a non-zero status when the files don't match.
185 // Ignore that failure as long as we get output.
186 err = nil
187 }
188 return
189
190}