package main import ( "bufio" "crypto/sha256" "fmt" "io/fs" "log" "net/url" "os" "path/filepath" "time" ) type metadata struct { size, mtime, mode int64 // sum [sha256.Size]byte sum []byte } type metamap map[string]metadata func getIndex(root string, mm metamap) (index string, err error) { filesystem := os.DirFS(root) err = fs.WalkDir(filesystem, ".", func(path string, d fs.DirEntry, err error) error { if err != nil { return err } info, err := d.Info() if err != nil { return err } if info.IsDir() { if d.Name() == ".bb" { return fs.SkipDir } return nil } b, err := os.ReadFile(path) if err != nil { return err } // md := metadata{info.Size(), info.ModTime().Unix(), int64(info.Mode()), sha256.Sum256(b)} a := sha256.Sum256(b) md := metadata{info.Size(), info.ModTime().Unix(), int64(info.Mode()), a[:]} log.Println(path, md, mm[path]) index += fmt.Sprintf("%s %d %d %o %x\n", url.PathEscape(path), md.size, md.mtime, md.mode, md.sum) return nil }) return index, err } func readIndex(path string) (md metamap, err error) { f, err := os.Open(path) if err != nil { return md, err } defer f.Close() md = metamap{} scan := bufio.NewScanner(f) for scan.Scan() { var p string var d metadata n, err := fmt.Sscanf(scan.Text(), "%s %d %d %o %64x", &p, &d.size, &d.mtime, &d.mode, &d.sum) if err != nil || n != 5 { return md, err } path, err := url.PathUnescape(p) if err != nil { return md, err } md[path] = d } if err := scan.Err(); err != nil { return md, err } return md, nil } func initBB(root string) (current, previous string, err error) { if err = os.MkdirAll(filepath.Join(root, ".bb", "data"), 0o750); err != nil { return "", "", err } prevs, _ := fs.Glob(os.DirFS(root), filepath.Join(".bb", "index-*")) if len(prevs) > 0 { previous = prevs[len(prevs)-1] } now := time.Now() y, m, d := now.Date() h, mn, s := now.Clock() current = filepath.Join(root, ".bb", fmt.Sprintf("index-%d-%02d%02d-%02d%02d%02d", y, m, d, h, mn, s)) return current, previous, nil } func main() { log.SetFlags(log.Lshortfile) wd, err := os.Getwd() if err != nil { log.Fatal(err) } index, oldindex, err := initBB(wd) if err != nil { log.Fatal(err) } md := metamap{} if oldindex != "" { if md, err = readIndex(oldindex); err != nil { log.Fatal(err) } } data, err := getIndex(wd, md) if err != nil { log.Fatal(err) } err = os.WriteFile(index, []byte(data), 0o644) if err != nil { log.Fatal(err) } }