]> git.decadent.org.uk Git - ion3.git/blob - build/mkman.lua
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[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 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             if not v.invalid then
210                 if v.kcb then
211                     v.kcb=string.gsub(v.kcb, "AnyModifier%+", "")
212                 end
213                 if v.action=="doc" then
214                     if outi==0 or #out[outi].bindings>0 then
215                         outi=outi+1
216                     end
217                     out[outi]={doc=v.text, bindings={}}
218                 elseif v.submap then
219                     parsetable(v.submap, prefix..v.kcb.." ")
220                 else
221                     assert(out[outi])
222                     v.kcb=prefix..v.kcb
223                     table.insert(out[outi].bindings, v)
224                 end
225             end
226         end
227     end
228     
229     if outi~=0 and #out[outi].bindings==0 then
230         out[outi]=nil
231     end
232     
233     parsetable(bindings, "")
234     
235     return out
236 end
237
238
239 local function combine_bindings(v)    
240     local nact={
241         ["mpress"]=TR("press"),
242         ["mclick"]=TR("click"),
243         ["mdrag"]=TR("drag"),
244         ["mdblclick"]=TR("double click"),
245     }
246     local first=true
247     local s=""
248     for _, b in ipairs(v.bindings) do
249         if not first then
250             s=s..', '
251         end
252         first=false
253         if b.action=="kpress" or b.action=="kpress_wait" then
254             s=s..b.kcb
255         else
256             if not b.area then
257                 s=s..TR("%s %s", b.kcb, nact[b.action])
258             else
259                 s=s..TR("%s %s at %s", b.kcb, nact[b.action], b.area)
260             end
261         end
262     end
263     
264     return s
265 end    
266
267 local function write_bindings_man(db)
268     local function write_binding_man(v)
269         return '.TP\n.B '..combine_bindings(v)..'\n'..gettext(v.doc or "?")..'\n'
270     end
271     
272     local s=""
273     
274     for _, v in ipairs(db) do
275         if #(v.bindings)>0 then
276             s=s..write_binding_man(v)
277         end
278     end
279     
280     return s
281 end
282
283 -- }}}
284
285 -- Main {{{
286
287 local infile
288 local outfile
289 local bindings={}
290 local replaces={}
291
292 local function doargs(a)
293     local i=1
294     while i<=#a do
295         if a[i]=='-o' then
296             outfile=a[i+1]
297             i=i+2
298         elseif a[i]=='-i' then
299             infile=a[i+1]
300             i=i+2
301         elseif a[i]=='-D' then
302             replaces[a[i+1]]=a[i+2]
303             i=i+3
304         elseif a[i]=='-po' then
305             read_translations(a[i+1])
306             i=i+2
307         else
308             parsefile(a[i], bindings)
309             i=i+1
310         end
311     end
312 end
313
314 doargs(arg)
315
316 local f, err=io.open(infile)
317 if not f then
318     error(err)
319 end
320
321 local of, oerr=io.open(outfile, 'w+')
322 if not of then
323     error(oerr)
324 end
325
326 for l in f:lines() do
327     l=string.gsub(l, '%s*BINDINGS:([%w%.%-]+)%s*', 
328                   function(s)
329                       if not bindings[s] then
330                           --error('No bindings for '..s)
331                           return "?"
332                       end
333                       local db=docgroup_bindings(bindings[s])
334                       return write_bindings_man(db)
335                   end)
336     
337     for pat, rep in pairs(replaces) do
338         l=string.gsub(l, pat, rep)
339     end
340     
341     of:write(l..'\n')
342 end
343
344 -- }}}
345