]> git.decadent.org.uk Git - ion3.git/blob - build/mkman.lua
[svn-upgrade] Integrating new upstream version, ion3 (20080411)
[ion3.git] / build / mkman.lua
1 --
2 -- build/mkman.lua
3 --
4 -- Translates bindings from Ion configuration into a listing for
5 -- manual pages.
6 --
7
8
9 -- Translations {{{
10
11 local translations={}
12
13 local function gettext(x)
14     local t=translations[x]
15     if not t or t=="" then
16         return x
17     else
18         return t
19     end
20 end
21
22 local function TR(x, ...)
23     return string.format(gettext(x), unpack(arg))
24 end
25
26 local function read_translations(pofile)
27     local f, err=io.open(pofile)
28     if not f then 
29         error(err) 
30     end
31     
32     local msgid, msgstr, st, en
33     
34     for l in f:lines() do
35         if string.find(l, "^msgid") then
36             if msgid then
37                 assert(msgstr)
38                 translations[msgid]=msgstr
39                 msgstr=nil
40             end
41             st, en, msgid=string.find(l, '^msgid%s*"(.*)"%s*$')
42         elseif string.find(l, "^msgstr") then
43             assert(msgid and not msgstr)
44             st, en, msgstr=string.find(l, '^msgstr%s*"(.*)"%s*$')
45         elseif not (string.find(l, "^%s*#") or string.find(l, "^%s*$")) then
46             local st, en, str=string.find(l, '^%s*"(.*)"%s*$')
47             assert(msgid or msgstr)
48             if not msgstr then
49                 msgid=msgid..str
50             else
51                 msgstr=msgstr..str
52             end
53         end
54     end
55
56     if msgid then
57         assert(msgstr)
58         translations[msgid]=msgstr
59     end
60     
61     f:close()
62 end
63
64 -- }}}
65
66
67 -- File parsing {{{
68
69 local function dobindings(fn, bindings)
70     local p={}
71     local dummy = function() end
72     
73     p.META="Mod1+"
74     p.ALTMETA=""
75     
76     p.dopath=dummy
77     p.defmenu=dummy
78     p.defctxmenu=dummy
79     p.menuentry=dummy
80     p.submenu=dummy
81     p.submap_enter=dummy
82     p.submap_leave=dummy
83     p.submap_wait=dummy
84     
85     p.ioncore={
86         set=dummy,
87     }
88     
89     function p.bdoc(text)
90         return {action = "doc", text = text}
91     end
92     
93     function p.submap(kcb_, list)
94         if not list then
95             return function(lst)
96                        return p.submap(kcb_, lst)
97                    end
98         end
99         return {action = "kpress", kcb = kcb_, submap = list}
100     end
101     
102     local function putcmd(cmd, guard, t)
103         t.cmd=cmd
104         t.guard=guard
105         return t
106     end
107     
108     function p.kpress(keyspec, cmd, guard)
109         return putcmd(cmd, guard, {action = "kpress", kcb = keyspec})
110     end
111     
112     function p.kpress_wait(keyspec, cmd, guard)
113         return putcmd(cmd, guard, {action = "kpress_wait", kcb = keyspec})
114     end
115     
116     local function mact(act_, kcb_, cmd, guard)
117         local st, en, kcb2_, area_=string.find(kcb_, "([^@]*)@(.*)")
118         return putcmd(cmd, guard, {
119             action = act_,
120             kcb = (kcb2_ or kcb_),
121             area = area_,
122         })
123     end
124     
125     function p.mclick(buttonspec, cmd, guard)
126         return mact("mclick", buttonspec, cmd, guard)
127     end
128     
129     function p.mdblclick(buttonspec, cmd, guard)
130         return mact("mdblclick", buttonspec, cmd, guard)
131     end
132     
133     function p.mpress(buttonspec, cmd, guard)
134         return mact("mpress", buttonspec, cmd, guard)
135     end
136
137     function p.mdrag(buttonspec, cmd, guard)
138         return mact("mdrag", buttonspec, cmd, guard)
139     end
140     
141     function ins(t, v)
142         if not t.seen then
143             t.seen={}
144         end
145         
146         if (not v.kcb) or v.submap then
147             -- Submap rebinds are not presently handled
148             table.insert(t, v)
149         else
150             local id=v.action..":"..v.kcb..":"..(v.area or "")
151             local i=t.seen[id]
152             if i then
153                 t[i].invalid=true
154             end
155             if v.cmd then
156                 table.insert(t, v)
157                 t.seen[id]=#t
158             else
159                 -- Unbind only
160                 t.seen[id]=nil
161             end
162         end
163     end
164     
165     function p.defbindings(context, bnd)
166         if not bindings[context] then
167             bindings[context]={}
168         else
169             -- Reset documentation
170             table.insert(bindings[context], { action = "doc", text = nil })
171         end
172         
173         for _, v in ipairs(bnd) do
174             ins(bindings[context], v)
175         end
176     end
177
178     local env=setmetatable({}, {
179         __index=p, 
180         __newindex=function(x) 
181                        error("Setting global "..tostring(x))
182                    end,
183     })
184     setfenv(fn, env)
185     fn()
186     return bindings
187 end
188
189 local function parsefile(f, bindings)
190     local fn, err=loadfile(f)
191     if not fn then
192         error(err)
193     end
194     
195     return dobindings(fn, bindings)
196 end
197     
198 -- }}}
199
200
201 -- Binding output {{{
202
203 local function docgroup_bindings(bindings)
204     local out={}
205     local outi=0
206     
207     local function parsetable(t, prefix)
208         --for _, v in ipairs(t) do
209         -- ipairs doesn't like nil values, that e.g. submap_wait dummy might generate
210         for i=1,#t do
211             local v=t[i]
212             if v and not v.invalid then
213                 if v.kcb then
214                     v.kcb=string.gsub(v.kcb, "AnyModifier%+", "")
215                 end
216                 if v.action=="doc" then
217                     if outi==0 or #out[outi].bindings>0 then
218                         outi=outi+1
219                     end
220                     out[outi]={doc=v.text, bindings={}}
221                 elseif v.submap then
222                     parsetable(v.submap, prefix..v.kcb.." ")
223                 else
224                     assert(out[outi])
225                     v.kcb=prefix..v.kcb
226                     table.insert(out[outi].bindings, v)
227                 end
228             end
229         end
230     end
231     
232     if outi~=0 and #out[outi].bindings==0 then
233         out[outi]=nil
234     end
235     
236     parsetable(bindings, "")
237     
238     return out
239 end
240
241
242 local function combine_bindings(v)    
243     local nact={
244         ["mpress"]=TR("press"),
245         ["mclick"]=TR("click"),
246         ["mdrag"]=TR("drag"),
247         ["mdblclick"]=TR("double click"),
248     }
249     local first=true
250     local s=""
251     for _, b in ipairs(v.bindings) do
252         if not first then
253             s=s..', '
254         end
255         first=false
256         if b.action=="kpress" or b.action=="kpress_wait" then
257             s=s..b.kcb
258         else
259             if not b.area then
260                 s=s..TR("%s %s", b.kcb, nact[b.action])
261             else
262                 s=s..TR("%s %s at %s", b.kcb, nact[b.action], b.area)
263             end
264         end
265     end
266     
267     return s
268 end    
269
270 local function write_bindings_man(db)
271     local function write_binding_man(v)
272         return '.TP\n.B '..combine_bindings(v)..'\n'..gettext(v.doc or "?")..'\n'
273     end
274     
275     local s=""
276     
277     for _, v in ipairs(db) do
278         if #(v.bindings)>0 then
279             s=s..write_binding_man(v)
280         end
281     end
282     
283     return s
284 end
285
286 -- }}}
287
288 -- Main {{{
289
290 local infile
291 local outfile
292 local bindings={}
293 local replaces={}
294
295 local function doargs(a)
296     local i=1
297     while i<=#a do
298         if a[i]=='-o' then
299             outfile=a[i+1]
300             i=i+2
301         elseif a[i]=='-i' then
302             infile=a[i+1]
303             i=i+2
304         elseif a[i]=='-D' then
305             replaces[a[i+1]]=a[i+2]
306             i=i+3
307         elseif a[i]=='-po' then
308             read_translations(a[i+1])
309             i=i+2
310         else
311             parsefile(a[i], bindings)
312             i=i+1
313         end
314     end
315 end
316
317 doargs(arg)
318
319 local f, err=io.open(infile)
320 if not f then
321     error(err)
322 end
323
324 local of, oerr=io.open(outfile, 'w+')
325 if not of then
326     error(oerr)
327 end
328
329 for l in f:lines() do
330     l=string.gsub(l, '%s*BINDINGS:([%w%.%-]+)%s*', 
331                   function(s)
332                       if not bindings[s] then
333                           --error('No bindings for '..s)
334                           return "?"
335                       end
336                       local db=docgroup_bindings(bindings[s])
337                       return write_bindings_man(db)
338                   end)
339     
340     for pat, rep in pairs(replaces) do
341         l=string.gsub(l, pat, rep)
342     end
343     
344     of:write(l..'\n')
345 end
346
347 -- }}}
348