1if not modules then modules = { } end modules ['mtx-tools'] = {
2 version = 1.002,
3 comment = "companion to mtxrun.lua",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local find, format, sub, rep, gsub, lower = string.find, string.format, string.sub, string.rep, string.gsub, string.lower
10
11local helpinfo = [[
12<?xml version="1.0"?>
13<application>
14 <metadata>
15 <entry name="name">mtx-tools</entry>
16 <entry name="detail">Some File Related Goodies</entry>
17 <entry name="version">1.01</entry>
18 </metadata>
19 <flags>
20 <category name="basic">
21 <subcategory>
22 <flag name="disarmutfbomb"><short>remove utf bomb if present</short></flag>
23 <flag name="force"><short>remove indeed</short></flag>
24 </subcategory>
25 <subcategory>
26 <flag name="dirtoxml"><short>glob directory into xml</short></flag>
27 <flag name="pattern"><short>glob pattern (default: *)</short></flag>
28 <flag name="url"><short>url attribute (no processing)</short></flag>
29 <flag name="root"><short>the root of the globbed path (default: .)</short></flag>
30 <flag name="output"><short>output filename (console by default)</short></flag>
31 <flag name="recurse"><short>recurse into subdirecories</short></flag>
32 <flag name="stripname"><short>take pathpart of given pattern</short></flag>
33 <flag name="longname"><short>set name attributes to full path name</short></flag>
34 <flag name="downcase"><short>lowercase names</short></flag>
35 </subcategory>
36 <subcategory>
37 <flag name="showstring"><short>show unicode characters in given string</short></flag>
38 <flag name="showfile"><short>show unicode characters in given file</short></flag>
39 </subcategory>
40 <subcategory>
41 <flag name="pattern"><short>glob pattern (default: *)</short></flag>
42 <flag name="recurse"><short>recurse into subdirecories</short></flag>
43 <flag name="force"><short>downcase indeed</short></flag>
44 </subcategory>
45 </category>
46 </flags>
47</application>
48]]
49
50local application = logs.application {
51 name = "mtx-tools",
52 banner = "Some File Related Goodies 1.01",
53 helpinfo = helpinfo,
54}
55
56local report = application.report
57local writeln = (logs and logs.writer) or (texio and texio.write_nl) or print
58
59scripts = scripts or { }
60scripts.tools = scripts.tools or { }
61
62local bomb_1, bomb_2 = "^\254\255", "^\239\187\191"
63
64function scripts.tools.disarmutfbomb()
65 local force, done = environment.argument("force"), false
66 local files = environment.files
67 for i=1,#files do
68 local name = files[i]
69 if lfs.isfile(name) then
70 local data = io.loaddata(name)
71 if not data then
72
73 elseif find(data,bomb_1) then
74 report("file '%s' has a 2 character utf bomb",name)
75 if force then
76 io.savedata(name,(gsub(data,bomb_1,"")))
77 end
78 done = true
79 elseif find(data,bomb_2) then
80 report("file '%s' has a 3 character utf bomb",name)
81 if force then
82 io.savedata(name,(gsub(data,bomb_2,"")))
83 end
84 done = true
85 else
86
87 end
88 end
89 end
90 if done and not force then
91 report("use --force to do a real disarming")
92 end
93end
94
95function scripts.tools.downcase()
96 local pattern = environment.argument('pattern') or "*"
97 local recurse = environment.argument('recurse')
98 local force = environment.argument('force')
99 local n = 0
100 if recurse and not find(pattern,"^%*%*%/") then
101 pattern = "**/*" .. pattern
102 end
103 dir.glob(pattern,function(name)
104 local basename = file.basename(name)
105 if lower(basename) ~= basename then
106 n = n + 1
107 local low = lower(name)
108 if n == 1 then
109 report()
110 end
111 report("%a renamed to %a",name,low)
112 if force then
113 os.rename(name,low)
114 end
115 end
116 end)
117 if n > 0 then
118 report()
119 if force then
120 report("%s files renamed",n)
121 else
122 report("use --force to do a real rename (%s files involved)",n)
123 end
124 else
125 report("nothing to do")
126 end
127end
128
129function scripts.tools.dirtoxml()
130
131 local join, removesuffix, suffixonly, date = file.join, file.removesuffix, file.suffixonly, os.date
132
133 local xmlns = "http://www.pragma-ade.com/rlg/xmldir.rng"
134 local timestamp = "%Y-%m-%d %H:%M"
135
136 local pattern = environment.argument('pattern') or ".*"
137 local url = environment.argument('url') or "no-url"
138 local root = environment.argument('root') or "."
139 local outputfile = environment.argument('output')
140
141 local recurse = environment.argument('recurse') or false
142 local stripname = environment.argument('stripname')
143 local longname = environment.argument('longname')
144
145 local function flush(list,result,n,path)
146 n, result = n or 1, result or { }
147 local d = rep(" ",n)
148 for name, attr in table.sortedhash(list) do
149 local mode = attr.mode
150 if mode == "file" then
151 result[#result+1] = format("%s<file name='%s'>",d,(longname and path and join(path,name)) or name)
152 result[#result+1] = format("%s <base>%s</base>",d,removesuffix(name))
153 result[#result+1] = format("%s <type>%s</type>",d,suffixonly(name))
154 result[#result+1] = format("%s <size>%s</size>",d,attr.size)
155 result[#result+1] = format("%s <permissions>%s</permissions>",d,sub(attr.permissions,7,9))
156 result[#result+1] = format("%s <date>%s</date>",d,date(timestamp,attr.modification))
157 result[#result+1] = format("%s</file>",d)
158 elseif mode == "directory" then
159 result[#result+1] = format("%s<directory name='%s'>",d,name)
160 flush(attr.list,result,n+1,(path and join(path,name)) or name)
161 result[#result+1] = format("%s</directory>",d)
162 end
163 end
164 end
165
166 if not pattern or pattern == "" then
167 report('provide --pattern=')
168 return
169 end
170
171 if stripname then
172 pattern = file.dirname(pattern)
173 end
174
175 local luapattern = string.topattern(pattern,true)
176
177 lfs.chdir(root)
178
179 local list = dir.collectpattern(root,luapattern,recurse)
180
181 if list[outputfile] then
182 list[outputfile] = nil
183 end
184
185 local result = { "<?xml version='1.0'?>" }
186 result[#result+1] = format("<files url=%q root=%q pattern=%q luapattern=%q xmlns='%s' timestamp='%s'>",url,root,pattern,luapattern,xmlns,date(timestamp))
187 flush(list,result)
188 result[#result+1] = "</files>"
189
190 result = table.concat(result,"\n")
191
192 if not outputfile or outputfile == "" then
193 writeln(result)
194 else
195 io.savedata(outputfile,result)
196 end
197
198end
199
200local function showstring(s)
201 if not characters or not characters.data then
202 require("char-def")
203 end
204 local d = characters.data
205 local f = string.formatters["%U %s %-30s %c"]
206 for c in string.utfvalues(s) do
207 local cs = d[c]
208 print(f(c,cs.category or "",cs.description or "",c))
209 end
210end
211
212function scripts.tools.showstring()
213 local files = environment.files
214 for i=1,#files do
215 showstring(files[i])
216 end
217end
218
219function scripts.tools.showfile()
220 local files = environment.files
221 for i=1,#files do
222 showstring(io.loaddata(files[i]) or "")
223 end
224end
225
226if environment.argument("disarmutfbomb") then
227 scripts.tools.disarmutfbomb()
228elseif environment.argument("dirtoxml") then
229 scripts.tools.dirtoxml()
230elseif environment.argument("downcase") then
231 scripts.tools.downcase()
232elseif environment.argument("exporthelp") then
233 application.export(environment.argument("exporthelp"),environment.files[1])
234elseif environment.argument("showstring") then
235 scripts.tools.showstring()
236elseif environment.argument("showfile") then
237 scripts.tools.showfile()
238else
239 application.help()
240end
241 |