Home | History | Annotate | Download | only in bpfmt
      1 // 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 
      6 package main
      7 
      8 import (
      9 	"bytes"
     10 	"flag"
     11 	"fmt"
     12 	"io"
     13 	"io/ioutil"
     14 	"os"
     15 	"os/exec"
     16 	"path/filepath"
     17 
     18 	"github.com/google/blueprint/parser"
     19 )
     20 
     21 var (
     22 	// main operation modes
     23 	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")
     28 )
     29 
     30 var (
     31 	exitCode = 0
     32 )
     33 
     34 func report(err error) {
     35 	fmt.Fprintln(os.Stderr, err)
     36 	exitCode = 2
     37 }
     38 
     39 func usage() {
     40 	usageViolation("")
     41 }
     42 
     43 func usageViolation(violation string) {
     44 	fmt.Fprintln(os.Stderr, violation)
     45 	fmt.Fprintln(os.Stderr, "usage: bpfmt [flags] [path ...]")
     46 	flag.PrintDefaults()
     47 	os.Exit(2)
     48 }
     49 
     50 func processFile(filename string, out io.Writer) error {
     51 	f, err := os.Open(filename)
     52 	if err != nil {
     53 		return err
     54 	}
     55 	defer f.Close()
     56 
     57 	return processReader(filename, f, out)
     58 }
     59 
     60 func processReader(filename string, in io.Reader, out io.Writer) error {
     61 	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 		}
     90 		if *overwriteSourceFile {
     91 			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 
    106 	if !*list && !*overwriteSourceFile && !*doDiff {
    107 		_, err = out.Write(res)
    108 	}
    109 
    110 	return err
    111 }
    112 
    113 func walkDir(path string) {
    114 	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 
    124 	filepath.Walk(path, visitFile)
    125 }
    126 
    127 func main() {
    128 	flag.Parse()
    129 
    130 	if !*writeToStout && !*overwriteSourceFile && !*doDiff && !*list {
    131 		usageViolation("one of -d, -l, -o, or -w is required")
    132 	}
    133 
    134 	if flag.NArg() == 0 {
    135 		// file to parse is stdin
    136 		if *overwriteSourceFile {
    137 			fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
    138 			exitCode = 2
    139 			return
    140 		}
    141 		if err := processReader("<standard input>", os.Stdin, os.Stdout); err != nil {
    142 			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:
    155 			if err := processFile(path, os.Stdout); err != nil {
    156 				report(err)
    157 			}
    158 		}
    159 	}
    160 }
    161 
    162 func diff(b1, b2 []byte) (data []byte, err error) {
    163 	f1, err := ioutil.TempFile("", "bpfmt")
    164 	if err != nil {
    165 		return
    166 	}
    167 	defer os.Remove(f1.Name())
    168 	defer f1.Close()
    169 
    170 	f2, err := ioutil.TempFile("", "bpfmt")
    171 	if err != nil {
    172 		return
    173 	}
    174 	defer os.Remove(f2.Name())
    175 	defer f2.Close()
    176 
    177 	f1.Write(b1)
    178 	f2.Write(b2)
    179 
    180 	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
    181 	if len(data) > 0 {
    182 		// diff exits with a non-zero status when the files don't match.
    183 		// Ignore that failure as long as we get output.
    184 		err = nil
    185 	}
    186 	return
    187 
    188 }
    189