]> Sergey Matveev's repositories - glocate.git/blob - main.go
Unify copyright comment format
[glocate.git] / main.go
1 // glocate -- ZFS-diff-friendly locate-like utility
2 // Copyright (C) 2022-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package main
17
18 import (
19         "flag"
20         "log"
21         "os"
22         "path"
23         "path/filepath"
24         "strings"
25         "syscall"
26 )
27
28 var TmpDir string
29
30 type Ent struct {
31         name  []string
32         mtime int64
33         size  int64
34 }
35
36 func (ent *Ent) IsDir() bool {
37         return IsDir(ent.name[len(ent.name)-1])
38 }
39
40 func dbCommit(dbPath string, tmp *os.File) {
41         umask := syscall.Umask(0)
42         syscall.Umask(umask)
43         if err := os.Chmod(tmp.Name(), os.FileMode(0666&^umask)); err != nil {
44                 log.Fatalln(err)
45         }
46         if err := os.Rename(tmp.Name(), dbPath); err != nil {
47                 log.Fatalln(err)
48         }
49 }
50
51 func main() {
52         dbPath := flag.String("db", "glocate.db", "Path to database")
53         doIndex := flag.Bool("index", false, "Perform indexing")
54         doUpdate := flag.String("update", "", "Update database")
55         showMachine := flag.Bool("machine", false, "Show machine friendly")
56         showTree := flag.Bool("tree", false, "Show human-friendly tree")
57         dryRun := flag.Bool("n", false, "Dry run, do not overwrite database")
58         rootPath := flag.String("root", "", "Search only that part of tree")
59         flag.Parse()
60         log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
61
62         var err error
63         TmpDir, err = filepath.Abs(*dbPath)
64         if err != nil {
65                 panic(err)
66         }
67         TmpDir = path.Dir(TmpDir)
68
69         if *doIndex {
70                 tmp := index()
71                 tmp.Close()
72                 if !*dryRun {
73                         dbCommit(*dbPath, tmp)
74                 }
75                 return
76         }
77
78         if *doUpdate != "" {
79                 tmp := updateWithDiff(*dbPath, *doUpdate)
80                 tmp.Close()
81                 if !*dryRun {
82                         dbCommit(*dbPath, tmp)
83                 }
84                 return
85         }
86
87         db, err := os.Open(*dbPath)
88         if err != nil {
89                 log.Fatalln(err)
90         }
91         entsReader := make(chan Ent, 1<<10)
92         go reader(db, entsReader)
93
94         entsPrinter := make(chan Ent, 1<<10)
95         printerJob := make(chan struct{})
96         go func() {
97                 if *showMachine {
98                         printerMachine(entsPrinter)
99                 } else if *showTree {
100                         printerTree(entsPrinter)
101                 } else {
102                         printerSimple(entsPrinter)
103                 }
104                 close(printerJob)
105         }()
106
107         var root []string
108         if *rootPath != "" {
109                 root = strings.Split("./"+*rootPath, "/")
110         }
111
112         var pat string
113         if len(flag.Args()) > 0 {
114                 pat = "*" + flag.Arg(0) + "*"
115         }
116
117         rootMet := false
118         var matched bool
119         var namePrev []string
120         var i int
121         for ent := range entsReader {
122                 if hasPrefix(ent.name, root) {
123                         rootMet = true
124                         if pat == "" {
125                                 entsPrinter <- ent
126                                 continue
127                         }
128                         for i = 0; i < len(ent.name); i++ {
129                                 if i == len(namePrev) || ent.name[i] != namePrev[i] {
130                                         break
131                                 }
132                         }
133                         for ; i < len(ent.name); i++ {
134                                 matched, err = path.Match(pat,
135                                         strings.ToLower(strings.TrimSuffix(ent.name[i], "/")))
136                                 if err != nil {
137                                         log.Fatalln(err)
138                                 }
139                         }
140                         if matched {
141                                 entsPrinter <- ent
142                         }
143                         namePrev = ent.name
144                 } else if rootMet {
145                         break
146                 }
147         }
148         close(entsPrinter)
149         <-printerJob
150 }