-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.lua
146 lines (118 loc) · 3.2 KB
/
main.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
-- shell escaping:
-- https://github.com/GUI/lua-shell-games/blob/master/lib/shell-games.lua#L219
-- https://github.com/python/cpython/blob/main/Lib/shlex.py#L323
-- but for now we just replace newlines
local function print_line(prefix, name)
just(io.write(prefix, " ", name:gsub("\n", "⏎"), "\n"))
end
local function print_line_0(prefix, name)
just(io.write(prefix, " ", name, "\0"))
end
-- display usage string and exit
local function usage()
just(io.stderr:write("Usage: ", program_name, [=[ [CMD] [OPTIONS]...
List all added, deleted, and modified files in the current directory and its subdirectories.
Without CMD argument the tool shows all changes made since the last reset.
Options:
-0 use ASCII null as output separator
The CMD argument, if given, must be one of the following:
init create empty change tracking database in the current directory
Options:
-f,--force discard any previous tracking data
reset accept current state as the reference for further change tracking
help,-h,--help display this help and exit
]=]))
os.exit(1)
end
-- check if there is exactly one command line option that matches any of the given names
local function one_option(args, ...)
if #args == 0 then
return false
end
if #args > 1 then
perror("too many options\n")
usage()
end
local opt = args[1]
for i = 1, select("#", ...) do
if opt == select(i, ...) then
return true
end
end
perror("unknown option %q\n", opt)
usage()
end
-- ensure no options supplied
local function no_options(args)
if #args > 0 then
perror("this command does not expect options\n")
usage()
end
end
-- listing of changes
local function do_diff(fname, db)
traverse(fname, function(name, kind, size, tag)
local stat = db[name]
if not stat then
print_line("+", name)
return
end
if kind ~= stat.kind or size ~= stat.size or ((kind == TYPE_LINK or size == 0) and tag ~= stat.tag) then
print_line("*", name)
db[name] = nil
return
end
if kind == TYPE_FILE and size > 0 then
return true -- request checksum
end
db[name] = nil -- this was either a link, or a file of zero size
end)
-- compare sums
pump_sums(fname, function(name, sum)
if sum ~= db[name].tag then
print_line("*", name)
end
db[name] = nil
end)
-- all remaining items must have been deleted
for name in pairs(db) do
print_line("-", name)
end
end
-- commands
local function reset(args)
no_options(args)
database_file_must_exist()
return save_database(build_database())
end
local function ls(args)
if one_option(args, "-0") then
print_line = print_line_0
end
database_file_must_exist()
with_temp_file(do_diff, load_database())
end
local function init(args)
if not one_option(args, "-f", "--force") and database_file_exists() then
fail("database file already exists")
end
create_empty_database()
end
-- main
local function main()
local cmd_map = {
["help"] = usage,
["--help"] = usage,
["-h"] = usage,
["reset"] = reset,
["init"] = init,
["dump"] = dump_database
}
local cmd = cmd_map[arg[1]]
if cmd then
cmd(table.move(arg, 2, #arg, 1, {}))
else
ls(table.move(arg, 1, #arg, 1, {}))
end
end
run(main)