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