github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/dredge/dredge.go (about)

     1  // Copyright 2015 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"log"
    12  	"os"
    13  	"os/exec"
    14  	"regexp"
    15  	"strings"
    16  )
    17  
    18  var chop = flag.Bool("chop", false, "show pieces on stdout and exit")
    19  var cmdstr = flag.String("cmd", "", "command to run")
    20  var cmd []string
    21  
    22  func usage() {
    23  	fmt.Fprintf(os.Stderr, "usage: dredge -cmd 'command args' files...\n")
    24  	os.Exit(2)
    25  }
    26  
    27  func main() {
    28  	log.SetFlags(0)
    29  	log.SetPrefix("dredge: ")
    30  
    31  	flag.Usage = usage
    32  	flag.Parse()
    33  
    34  	if !*chop {
    35  		cmd = strings.Fields(*cmdstr)
    36  		if len(cmd) == 0 || flag.NArg() == 0 {
    37  			usage()
    38  		}
    39  
    40  		out, err := runCmd()
    41  		if err != nil {
    42  			log.Fatalf("command failed before any changes: %s\n%s", err, out)
    43  		}
    44  	}
    45  
    46  	for {
    47  		progress := false
    48  		for _, file := range flag.Args() {
    49  			progress = dredgeFile(file) || progress
    50  		}
    51  		if !progress {
    52  			break
    53  		}
    54  	}
    55  }
    56  
    57  var cblockRE = regexp.MustCompile(`(?m)^([^/{ \t\n][^/{\n]*\n)*([^/{ \t\n][^/{\n]*)?{[ \t]*\n(([^}\n].*)?\n)*};?[ \t]*\n`)
    58  
    59  func dredgeFile(file string) bool {
    60  	fmt.Printf("%s\n", file)
    61  	data, err := ioutil.ReadFile(file)
    62  	if err != nil {
    63  		log.Fatal(err)
    64  	}
    65  
    66  	matches := cblockRE.FindAllIndex(data, -1)
    67  	if *chop {
    68  		for _, m := range matches {
    69  			fmt.Printf("«\n%s»\n", data[m[0]:m[1]])
    70  		}
    71  		return false
    72  	}
    73  
    74  	progress := false
    75  	for {
    76  		loopProgress := false
    77  		for i := len(matches) - 1; i >= 0; i-- {
    78  			m := matches[i]
    79  			new := append(data[:m[0]:m[0]], data[m[1]:]...)
    80  			if err := ioutil.WriteFile(file, new, 0666); err != nil {
    81  				log.Fatal(err)
    82  			}
    83  			if _, err := runCmd(); err == nil {
    84  				fmt.Printf("+")
    85  				loopProgress = true
    86  				progress = true
    87  				data = new
    88  			}
    89  		}
    90  		if !loopProgress {
    91  			break
    92  		}
    93  		matches = cblockRE.FindAllIndex(data, -1)
    94  	}
    95  	ioutil.WriteFile(file, data, 0666) // always; must at least restore original
    96  	return progress
    97  }
    98  
    99  func runCmd() (out []byte, err error) {
   100  	fmt.Printf(".")
   101  	return exec.Command(cmd[0], cmd[1:]...).CombinedOutput()
   102  }