goutils/cmd/zsearch/main.go

137 lines
2.4 KiB
Go
Raw Permalink Normal View History

2024-05-20 03:41:50 +00:00
// zsearch is a utility for searching zlib-compressed files for a
// search string. It was really designed for use with the Git object
// store, i.e. to aid in the recovery of files after Git does what Git
// do.
package main
import (
"bufio"
"bytes"
"compress/zlib"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
)
const defaultDirectory = ".git/objects"
func errorf(format string, a ...interface{}) {
fmt.Fprintf(os.Stderr, format, a...)
if format[len(format)-1] != '\n' {
fmt.Fprintf(os.Stderr, "\n")
}
}
func isDir(path string) bool {
fi, err := os.Stat(path)
if err != nil {
return false
}
return fi.IsDir()
}
func loadFile(path string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
buf := new(bytes.Buffer)
zread, err := zlib.NewReader(file)
if err != nil {
return nil, err
}
defer zread.Close()
_, err = io.Copy(buf, zread)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func showFile(path string) {
fileData, err := loadFile(path)
if err != nil {
errorf("%v", err)
return
}
fmt.Printf("%s\n", fileData)
}
func searchFile(path string, search *regexp.Regexp) error {
file, err := os.Open(path)
if err != nil {
2024-05-20 03:51:35 +00:00
errorf("%v", err)
2024-05-20 03:41:50 +00:00
return err
}
defer file.Close()
zread, err := zlib.NewReader(file)
if err != nil {
errorf("%v", err)
return err
}
defer zread.Close()
zbuf := bufio.NewReader(zread)
if search.MatchReader(zbuf) {
fileData, err := loadFile(path)
if err != nil {
errorf("%v", err)
return err
}
fmt.Printf("%s:\n%s\n", path, fileData)
}
return nil
}
func buildWalker(searchExpr *regexp.Regexp) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if info.Mode().IsRegular() {
return searchFile(path, searchExpr)
}
return nil
}
}
func main() {
flSearch := flag.String("s", "", "search string (should be an RE2 regular expression)")
flag.Parse()
if *flSearch == "" {
for _, path := range flag.Args() {
showFile(path)
}
} else {
search, err := regexp.Compile(*flSearch)
if err != nil {
errorf("Bad regexp: %v", err)
return
}
pathList := flag.Args()
if len(pathList) == 0 {
pathList = []string{defaultDirectory}
}
for _, path := range pathList {
if isDir(path) {
err := filepath.Walk(path, buildWalker(search))
if err != nil {
errorf("%v", err)
return
}
} else {
searchFile(path, search)
}
}
}
}