]> granicus.if.org Git - zziplib/blob - docs/make-doc.py
zzip64 support
[zziplib] / docs / make-doc.py
1 import sys
2 import re
3 import string
4 import commands
5 import warnings
6
7 errors = 0
8 def warn(msg, error=None):
9     global errors
10     errors += 1
11     if error is None:
12         warnings.warn("-- "+str(errors)+" --\n  "+msg, RuntimeWarning, 2)
13     else:
14         warnings.warn("-- "+str(errors)+" --\n  "+msg+
15                       "\n  error was "+str(error), RuntimeWarning, 2)
16 #fu
17
18 # beware, stupid python interprets backslashes in repl only partially!
19 def s(string, pattern, repl, count=0):
20     return re.sub(pattern, repl, string, count)
21 def m(string, pattern):
22     return re.match(pattern, string)
23 def sorted_keys(dict):
24     keys = dict.keys()
25     keys.sort()
26     return keys
27
28 # we make up a few formatter routines to help in the processing:
29 def html2docbook(text):
30     """ the C comment may contain html markup - simulate with docbook tags """
31     return (
32         s(s(s(s(s(s(s(s(s(s(s(text,
33                               r"<br\s*/?>",""),
34                             r"(</?)em>",r"\1emphasis>"),
35                           r"<code>","<userinput>"),
36                         r"</code>","</userinput>"),
37                       r"<link>","<function>"),
38                     r"</link>","</function>"),
39                   r"(?s)\s*</screen>","</screen>"),
40 #               r"<ul>","</para><itemizedlist>"),
41 #             r"</ul>","</itemizedlist><para>"),
42 #           r"<li>","<listitem><para>"),
43 #         r"</li>","</para></listitem>\n"),
44                 r"<ul>","</para><programlisting>\n"),
45               r"</ul>","</programlisting><para>"),
46             r"<li>",""),
47           r"</li>",""))
48 def paramdef2html(text):
49     return s(s(s(s(s(text,
50                      r"\s+<paramdef>", r"\n<nobr>"),
51                    r"<paramdef>",r"<nobr>"),
52                  r"</paramdef>",r"</nobr>"),
53                r"<parameters>",r"\n <code>"),
54              r"</parameters>",r"</code>\n")
55 def section2html(text):
56     mapping = { "<screen>" : "<pre>", "</screen>" : "</pre>",
57                 "<para>" : "<p>", "</para>" : "</p>" ,
58                 "<function>" : "<link>", "</function>" : "</link>" }
59     for str in mapping:
60         text = string.replace(text, str, mapping[str])
61     return text
62 def html(text):
63     return section2html(paramdef2html(text))
64 def cdata1(text):
65     return string.replace(text, "&",  "&amp;")
66 def cdata31(text):
67     return string.replace(string.replace(text, "<","&lt;"), ">","&gt;")
68 def cdata3(text):
69     return cdata31(cdata1(text))
70 def cdata43(text):
71     return string.replace(text,"\"", "&quot;")
72 def cdata41(text):
73     return cdata43(cdata31(text))
74 def cdata4(text):
75     return cdata43(cdata3(text))
76 def markup_as_screen41 (text):
77     """ used for non-star lines in comment blocks """
78     return " <screen> " + s(cdata41(text), r"(?m)^", r" ") +" </screen> "
79
80 def file_comment2section(text):
81     """ convert a C comment into a series of <para> and <screen> parts """
82     return ("<para>\n"+
83             s(s(s(s(s(s(s(text,
84                           r"(?s){<([\w\.\-]+\@[\w\.\-]+\w\w)>",
85                           r"&lt;\1&gt;"),
86                         r"(?mx) ^\s?\s?\s? ([^\*\s]+ .*) $",
87                         lambda x : markup_as_screen41 (x.group(1))),
88                       r"(?mx) ^\s*[*]\s* $", r" \n</para><para>\n"),
89                     r"(?mx) ^\s?\s?\s?\* (.*) $", r" \1 "),
90                   r"(?sx) </screen>(\s*)<screen> ", r"\1"),
91                 r"(?sx) <([^<>\;]+\@[^<>\;]+)> ", r"<email>\1</email>"),
92               r"(?sx) \&lt\;([^<>\&\;]+\@[^<>\&\;]+)\&gt\; ",
93               r"<email>\1</email>") + "\n</para>")
94 def func_comment2section(text):
95     """ convert a C comment into a series of <para> and <screen> parts
96         and sanitize a few markups already present in the comment text
97     """
98     return ("<para>\n"+
99             s(s(s(s(s(s(s(s(s(s(s(text,
100                                   r"<c>",r"<code>"),   r"</c>", r"</code>"),
101                               r"(?mx) ^\s?\s?\s? ([^\*\s]+.*)",
102                               lambda x: markup_as_screen41 (x.group(1))),
103                             r"(?mx) ^\s?\s?\s?\* (.*) $", r" <br /> \1"),
104                           r"(?mx) ^\s*<br\s*\/>\s* $", r"\n</para><para>\n"),
105                         r"<<",r"&lt;"),   r">>",r"&gt;"),
106                     r"(?sx) (</?para>\s*)<br\s*\/?>",r"\1"),
107                   r"(?sx) (</?para>\s*)<br\s*\/?>",r"\1"),
108                 r"(?sx) (<br\s*\/?>\s*)<br\s*\/?>",r"\1"),
109               r"(?sx) <\/screen>(\s*)<screen>",r"\1") + "\n</para>")
110 def markup_link_syntax(text):
111     """ markup the link-syntax ` => somewhere ` in the text block """
112     return (
113         s(s(s(s(text,
114                 r"(?mx) (^|\s)\=\>\"([^\"]*)\"", r"\1<link>\2</link>"),
115               r"(?mx) (^|\s)\=\>\'([^\"]*)\'", r"\1<link>\2</link>"),
116             r"(?mx) (^|\s)\=\>\s(\w[\w.]*\w)\b", r"\1<link>\2</link>"),
117           r"(?mx) (^|\s)\=\>\s([^\s\,\.\!\?\:\;\<\>\&\'\=\-]+)",
118           r"\1<link>\2</link>"))
119 def this_function_link(text, name):
120     return s(text, r"(?sx) (T|t)his \s (function|procedure) ", lambda x
121              : "<function>"+x.group(1)+"he "+name+" "+x.group(2)+"</function>")
122
123 # -----------------------------------------------------------------------
124 class Options:
125     var = {}
126     def __getattr__(self, name):
127         if not self.var.has_key(name): return None
128         return self.var[name]
129     def __setattr__(self, name, value):
130         self.var[name] = value
131 #end
132
133 o = Options()
134 o.verbose = 0
135
136 o.version = s( commands.getoutput(
137     """ grep -i "^version *:" *.spec 2>/dev/null |
138         sed -e "s/[Vv]ersion *: *//" """),  r"\s*",r"")
139 o.package = s(commands.getoutput(
140     """ grep -i "^name *:" *.spec 2>/dev/null |
141         sed -e "s/[Nn]ame *: *//" """),     r"\s*",r"")
142
143 if not len(o.version):
144     o.version = commands.getoutput(""" date +%Y.%m.%d """)
145 if not len(o.package):
146     o.package = "_project"
147
148 o.suffix = "-doc3"
149 o.mainheader = o.package+".h"
150
151 class File:
152     def __init__(self, filename):
153         self.name = filename
154         self.mainheader = o.mainheader
155         self.authors = ""
156         self.copyright = ""
157     def __getattr__(self, name):
158         """ defend against program to break on uninited members """
159         if self.__dict__.has_key(name): return self.__dict__[name]
160         warn("no such member: "+name); return None
161     def set_author(self, text):
162         if self.authors:
163             self.authors += "\n"
164         self.authors += text
165         return text
166     def set_copyright(self, text):
167         self.copyright = text
168         return text
169
170 class InputFiles:
171     """ for each set of input files we can create an object
172         it does correspond with a single html-output page and
173         a single docbook <reference> master page to be output
174     """
175     def __init__(self):
176         # the id will tell us in which order
177         # we did meet each function definition
178         self.id = 1000
179         self.files = [] # file_list
180         self.funcs = [] # func_list: of hidden class FuncDeclaration
181         self.file = None # current file
182     def new_File(self, name):
183         self.file = File(name)
184         self.files.append(self.file)
185         return self.file
186     def next_id(self):
187         id = self.id ; self.id += 1
188         return id
189     def add_function_declaration(self, comment, prototype):
190         class FuncDeclaration:    # note that both decl.comment and
191             pass                  # decl.prototype are in cdata1 format
192         func = FuncDeclaration()
193         func.file = self.file
194         func.comment = s(comment, # need to take out email-style markups
195                          r"<([\w\.\-]+\@[\w\.\-]+\w\w)>", r"&lt;\1&gt;")
196         func.prototype = prototype
197         func.id = all.next_id()
198         self.funcs.append(func)
199         # print id
200         return prototype
201
202 def scan_options (options, list):
203     def encode(text):
204         return s(s(text, r"¬",  r"&#AC;"), r"\*/",r"¬")
205     def decode(text):
206         return s(text, r"¬", r"*/")
207
208     for name in options:
209         found = m(name, r"^(\w+)=(.*)")
210         if found:
211             o.var[found.group(1)] = found.group(2)
212             continue
213         #else
214         try:
215             input = open(name, "r")
216         except IOError, error:
217             warn(#...... (scan_options) ...............
218                 "can not open input file: "+name, error)
219             continue
220         text = input.read() ; input.close()
221         text = encode (cdata1 (text))
222
223         file = list.new_File(name)
224         
225         # cut per-function comment block
226         text = s(text, r"(?x) [/][*][*](?=\s) ([^¬]+) Â¬ ([^\{\}\;\#]+) [\{\;]",
227                  lambda x : list.add_function_declaration(
228             decode(x.group(1)), decode(x.group(2))))
229
230         # cut per-file comment block
231         found = m(text, r"(?sx)  [/][*]+(?=\s) ([^¬]+) Â¬ "
232                   r"(?:\s*\#define\s*\S+)*"
233                   r"(\s*\#include\s*<[^<>]*>(?:\s*//[^\n]*)?)")
234         if found:
235             file.comment = decode(found.group(1))
236             file.include = cdata31(found.group(2))
237         else:
238             file.comment = None
239             file.include = None
240             found = m(text, r"(?sx)  ^ [/][*]+(?=\s) ([^¬]+) Â¬ ")
241             if found:
242                 file.comment = decode(found.group(1))
243         #fi
244         # throw away the rest - further processing on memorized strings only
245
246     return None
247
248 all = InputFiles()
249 scan_options (sys.argv[1:], all)
250
251 if not o.docbookfile:
252     o.docbookfile = o.package+o.suffix+".docbook"
253 if not o.libhtmlfile:
254     o.libhtmlfile = o.package+o.suffix+".html"
255 if not o.dumpdocfile:
256     o.dumpdocfile = o.package+o.suffix+".dxml"
257
258 # ...........................................................................
259 # check out information in the file.comment section
260
261 def all_files_comment2section(list):
262     for file in list:
263         if file.comment is None: continue
264         file.section = file_comment2section(file.comment)
265     
266         file.section = s(
267             file.section, r"(?sx) \b[Aa]uthor\s*:(.*</email>) ", lambda x
268             : "<author>" + file.set_author(x.group(1)) + "</author>")
269         file.section = s(
270             file.section, r"(?sx) \b[Cc]opyright\s*:([^<>]*)</para> ",lambda x
271             : "<copyright>" + file.set_copyright(x.group(1)) + "</copyright>")
272         # if "file" in file.name: print >> sys.stderr, file.comment # 2.3
273     #od
274 all_files_comment2section(all.files)
275
276 # -----------------------------------------------------------------------
277
278 class Function:
279     " <prespec>void* </><namespec>hello</><namespec> (int) const</callspec> "
280     def __init__(self):
281         self.prespec  = ""
282         self.namespec = ""
283         self.callspec = ""
284         self.name = ""
285 #    def set(self, **defines):
286 #        name = defines.keys()[0]
287 #        self.__dict__[name] = defines[name]
288 #        return defines[name]
289 #    def cut(self, **defines):
290 #        name = defines.keys()[0]
291 #        self.__dict__[name] += defines[name]
292 #        return ""
293     def __getattr__(self, name):
294         """ defend against program exit on members being not inited """
295         if self.__dict__.has_key(name): return self.__dict__[name]
296         warn("no such member: "+name); return None
297     def dict(self):
298         return self.__dict__
299     def dict_sorted_keys(self):
300         keys = self.__dict__.keys()
301         keys.sort()
302         return keys
303     def parse(self, prototype):
304         found = m(prototype, r"(?sx) ^(.*[^.]) \b(\w[\w.]*\w)\b (\s*\(.*) $ ")
305         if found:
306             self.prespec = found.group(1).lstrip()
307             self.namespec = found.group(2)
308             self.callspec = found.group(3).lstrip()
309             self.name = self.namespec.strip()
310             return self.name
311         return None
312
313 # pass 1 of per-func strings ...............................................
314 # (a) cut prototype into prespec/namespec/callspec
315 # (b) cut out first line of comment as headline information
316 # (c) sanitize rest of comment block into proper docbook formatted .body
317
318 # do this while copying strings from all.funcs to function_list
319 # and remember the original order in name_list
320
321 def markup_callspec(text):
322     return (
323         s(s(s(s(s(text,
324                   r"(?sx) ^([^\(\)]*)\(", r"\1<parameters>(<paramdef>",1),
325                 r"(?sx) \)([^\(\)]*)$", r"</paramdef>)</parameters>\1",1),
326               r"(?sx) , ", r"</paramdef>,<paramdef>"),
327             r"(?sx) <paramdef>(\s+) ", r"\1<paramdef>"),
328           r"(?sx) (\s+)</paramdef>", r"</paramdef>\1"))
329
330 def parse_all_functions(func_list): # list of FunctionDeclarations
331     """ parse all FunctionDeclarations and create a list of Functions """
332     list = []
333     for func in all.funcs:
334         function = Function()
335         if not function.parse (func.prototype): continue
336
337         list.append(function)
338
339         function.body = markup_link_syntax(func.comment)
340         if "\n" not in function.body: # single-line comment is the head
341             function.head = function.body
342             function.body = ""
343         else: # cut comment in first-line and only keep the rest as descr body
344             function.head = s(function.body,  r"(?sx) ^([^\n]*\n).*",r"\1",1)
345             function.body = s(function.body,  r"(?sx)  ^[^\n]*\n",   r"",  1)
346         #fi
347         if m(function.head, r"(?sx) ^\s*$ "): # empty head line, autofill here
348             function.head = s("("+func.file.name+")", r"[.][.][/]", r"")
349
350         function.body = func_comment2section(function.body)
351         function.src = func # keep a back reference
352
353         # add extra docbook markups to callspec in $fn-hash
354         function.callspec = markup_callspec (function.callspec)
355     #od
356     return list
357 function_list = parse_all_functions(all.funcs)
358
359 def examine_head_anchors(func_list):
360     """ .into tells later steps which func-name is the leader of a man 
361         page and that this func should add its descriptions over there. """
362     for function in func_list:
363         function.into = None
364         function.seealso = None
365         
366         found = m(function.head, r"(?sx) ^ \s* <link>(\w[\w.]*\w)<\/link>")
367         # if found and found.group(1) in func_list.names:
368         if found and found.group(1):
369             function.into = found.group(1)
370
371         def set_seealso(f, value):
372             f.seealso = value
373             return value
374         function.head = s(function.head, r"(.*)also:(.*)", lambda x
375                           : set_seealso(function, x.group(2)) and x.group(1))
376         if function.seealso and None:
377             print "function[",function.name,"].seealso=",function.seealso
378 examine_head_anchors(function_list)
379
380 # =============================================================== HTML =====
381
382 def find_by_name(func_list, name):
383     for func in func_list:
384         if func.name == name:
385             return func
386     #od
387     return None
388 #fu
389
390 class HtmlFunction:
391     def __init__(self, func):
392         self.src = func.src
393         self.into = func.into
394         self.name = func.name
395         self.toc_line = paramdef2html(
396             "  <td valign=\"top\"><code>"+func.prespec+"</code></td>\n"+
397             "  <td valign=\"top\">&nbsp;&nbsp;</td>\n"+
398             "  <td valign=\"top\"><a href=\"#"+func.name+"\">\n"+
399             "                       <code>"+func.namespec+"</code>"+
400             "  </a></td>\n"+
401             "  <td valign=\"top\">&nbsp;&nbsp;</td>\n"+
402             "  <td valign=\"top\">"+func.callspec+"</td>\n")
403         self.synopsis = paramdef2html(
404             "  <code>"+func.prespec+"</code>\n"+
405             "  <br /><b><code>"+func.namespec+"</code></b>\n"+
406             "   &nbsp; <code>"+func.callspec+"</code>\n")
407         self.anchor = "<a name=\""+func.name+"\" />"
408         self.section = "<para><em> &nbsp;"+func.head+"\n"+ \
409                        "\n</em></para>"+section2html(func.body)
410 #class
411
412 class HtmlFunctionFamily(HtmlFunction):
413     def __init__(page, func):
414         HtmlFunction.__init__(page, func)
415         page.toc_line_list = [ page.toc_line ]
416         # page.html_txt     = page.synopsis
417         page.synopsis_list = [ page.synopsis ]
418         page.anchor_list   = [ page.anchor ]
419         page.section_list  = [ this_function_link(page.section, func.name) ]
420
421 def ensure_name(text, name):
422     adds = "<small><code>"+name+"</code></small> -"
423     match = r"(?sx) .*>[^<>]*\b" + name + r"\b[^<>]*<.*"
424     found = m(text, match)
425     if found: return text
426     found = m(text, r".*<p(ara)?>.*")
427     if found: return s(text, r"(<p(ara)?>)", r"\1"+adds, 1)
428     return adds+text
429
430 def combined_html_pages(func_list):
431     """ and now add descriptions of non-leader entries (html-mode) """
432     combined = {}
433     
434     for func in func_list: # assemble leader pages
435         if func.into is not None: continue
436         combined[func.name] =  HtmlFunctionFamily(func)
437
438     for func in func_list: 
439         if func.into is None: continue
440         if func.into not in combined :
441             warn(#......... (combine_html_pages) ..............
442                 "function '"+func.name+"'s into => '"+func.into+
443                 "\n: no such target function: "+func.into)
444             combined[func.name] = HtmlFunctionFamily(func)
445             continue
446         #fi
447         page = HtmlFunction(func)
448         into = combined[func.into]
449         into.toc_line_list.append( page.toc_line )
450         into.anchor_list.append( page.anchor )
451         into.synopsis_list.append( page.synopsis )
452         into.section_list.append(
453             s(ensure_name(this_function_link(section2html( func.body ),
454                                              func.name), func.name),
455               r"(?sx) (</?para>\s*) <br\s*\/>", r"\1"))
456     return combined.values()
457 html_pages = combined_html_pages(function_list)
458
459 def html_resolve_links_on_page(text, list):
460     """ link ref-names of a page with its endpoint on the same html page"""
461     def html_link (name , extra):
462         """ make <link>s to <href> of correct target or make it <code> """
463         if find_by_name(list, name) is None:
464             return "<code>"+name+extra+"</code>"
465         else:
466             return "<a href=\"#"+name+"\"><code>"+name+extra+"</code></a>"
467     #fu html_link
468     return s(s(text, r"(?sx) <link>(\w+)([^<>]*)<\/link> ",
469                lambda x : html_link(x.group(1),x.group(2))),
470              r"(?sx) \-\> ", r"<small>-&gt;</small>") # just sanitize..
471 #fu html_resolve_links
472
473 class HtmlPage:
474     def __init__(self):
475         self.toc = ""
476         self.txt = ""
477         self.package = o.package
478         self.version = o.version
479     def page_text(self):
480         """ render .toc and .txt parts into proper <html> page """
481         T = ""
482         T += "<html><head>"
483         T += "<title>"+self.package+"autodoc documentation </title>"
484         T += "</head>\n<body>\n"
485         T += "\n<h1>"+self.package+" <small><small><i>- "+self.version
486         T += "</i></small></small></h1>"
487         T += "\n<table border=0 cellspacing=2 cellpadding=0>"
488         T +=  self.toc
489         T += "\n</table>"
490         T += "\n<h3>Documentation</h3>\n\n<dl>"
491         T += html_resolve_links_on_page(self.txt, function_list)
492         T += "\n</dl>\n</body></html>\n"
493         return T
494     def add_page_map(self, list):
495         """ generate the index-block at the start of the onepage-html file """
496         keys = list.keys()
497         keys.sort()
498         for name in keys:
499             self.toc += "<tr valign=\"top\">\n"+ \
500                         "\n</tr><tr valign=\"top\">\n".join(
501                 list[name].toc_line_list)+"</tr>\n"
502             self.txt += "\n<dt>"+" ".join(list[name].anchor_list)
503             self.txt += "\n"+"\n<br />".join(list[name].synopsis_list)+"<dt>"
504             self.txt += "\n<dd>\n"+"\n".join(list[name].section_list)
505             self.txt += ("\n<p align=\"right\">"+
506                          "<small>("+list[name].src.file.name+")</small>"+
507                          "</p></dd>")
508     def add_page_list(self, functions):
509         """ generate the index-block at the start of the onepage-html file """
510         mapp = {}
511         for func in functions:
512             mapp[func.name] = func
513         #od
514         self.add_page_map(mapp)
515 #end
516
517 html = HtmlPage()
518 # html.add_function_dict(Fn)
519 # html.add_function_list(Fn.sort.values())
520 html.add_page_list(html_pages)
521
522 # and finally print the html-formatted output
523 try:
524     F = open(o.libhtmlfile, "w")
525 except IOError, error:
526     warn(# ............. open(o.libhtmlfile, "w") ..............
527         "can not open html output file: "+o.libhtmlfile, error)
528 else:
529     print >> F, html.page_text()
530     F.close()
531 #fi
532
533 # ========================================================== DOCBOOK =====
534 # let's go for the pure docbook, a reference type master for all man pages
535
536 class RefPage:
537     def __init__(self, func):
538         """ initialize the fields needed for a man page entry - the fields are
539            named after the docbook-markup that encloses (!!) the text we store
540            the entries like X.refhint = "hello" will be printed therefore as
541            <refhint>hello</refhint>. Names with underscores are only used as
542            temporaries but they are memorized, perhaps for later usage. """
543         self.refhint = "\n<!--========= "+func.name+" (3) ===========-->\n"
544         self.refentry = None
545         self.refentry_date = o.version.strip()        # //refentryinfo/date
546         self.refentry_productname = o.package.strip() # //refentryinfo/prod*
547         self.refentry_title = None                    # //refentryinfo/title
548         self.refentryinfo = None                      # override
549         self.manvolnum = "3"                         # //refmeta/manvolnum
550         self.refentrytitle = None                    # //refmeta/refentrytitle
551         self.refmeta = None                          # override
552         self.refpurpose = None                       # //refnamediv/refpurpose
553         self.refname = None                          # //refnamediv/refname
554         self.refname_list = []
555         self.refnamediv = None                       # override
556         self.mainheader = func.src.file.mainheader
557         self.includes = func.src.file.include
558         self.funcsynopsisinfo = ""       # //funcsynopsisdiv/funcsynopsisinfo
559         self.funcsynopsis = None         # //funcsynopsisdiv/funcsynopsis
560         self.funcsynopsis_list = []
561         self.description = None
562         self.description_list = []
563         # optional sections
564         self.authors_list = []           # //sect1[authors]/listitem
565         self.authors = None              # override
566         self.copyright = None
567         self.copyright_list = []
568         self.seealso = None
569         self.seealso_list = []
570         if  func.seealso:
571             self.seealso_list.append(func.seealso)
572         # func.func references
573         self.func = func
574         self.file_authors = None
575         if  func.src.file.authors:
576             self.file_authors = func.src.file.authors
577         self.file_copyright = None
578         if  func.src.file.copyright:
579             self.file_copyright = func.src.file.copyright
580     #fu
581     def refentryinfo_text(page):
582         """ the manvol formatter wants to render a footer line and header line
583             on each manpage and such info is set in <refentryinfo> """
584         if page.refentryinfo:
585             return page.refentryinfo
586         if page.refentry_date and \
587            page.refentry_productname and \
588            page.refentry_title: return (
589             "\n <date>"+page.refentry_date+"</date>"+ 
590             "\n <productname>"+page.refentry_productname+"</productname>"+
591             "\n <title>"+page.refentry_title+"</title>")
592         if page.refentry_date and \
593            page.refentry_productname: return (
594             "\n <date>"+page.refentry_date+"</date>"+ 
595             "\n <productname>"+page.refentry_productname+"</productname>")
596         return ""
597     def refmeta_text(page):
598         """ the manvol formatter needs to know the filename of the manpage to
599             be made up and these parts are set in <refmeta> actually """
600         if page.refmeta:
601             return page.refmeta
602         if page.manvolnum and page.refentrytitle:
603             return (
604                 "\n <refentrytitle>"+page.refentrytitle+"</refentrytitle>"+
605                 "\n <manvolnum>"+page.manvolnum+"</manvolnum>")
606         if page.manvolnum and page.func.name:
607             return (
608                 "\n <refentrytitle>"+page.func.name+"</refentrytitle>"+
609                 "\n <manvolnum>"+page.manvolnum+"</manvolnum>")
610         return ""
611     def refnamediv_text(page):
612         """ the manvol formatter prints a header line with a <refpurpose> line
613             and <refname>'d functions that are described later. For each of
614             the <refname>s listed here, a mangpage is generated, and for each
615             of the <refname>!=<refentrytitle> then a symlink is created """
616         if page.refnamediv:
617             return page.refnamediv
618         if page.refpurpose and page.refname:
619             return ("\n <refname>"+page.refname+'</refname>'+
620                     "\n <refpurpose>"+page.refpurpose+" </refpurpose>")
621         if page.refpurpose and page.refname_list:
622             T = ""
623             for refname in page.refname_list:
624                 T += "\n <refname>"+refname+'</refname>'
625             T += "\n <refpurpose>"+page.refpurpose+" </refpurpose>"
626             return T
627         return ""
628     def funcsynopsisdiv_text(page):
629         """ refsynopsisdiv shall be between the manvol mangemaent information
630             and the reference page description blocks """
631         T=""
632         if page.funcsynopsis:
633             T += "\n<funcsynopsis>"
634             if page.funcsynopsisinfo:
635                 T += "\n<funcsynopsisinfo>"+    page.funcsynopsisinfo + \
636                      "\n</funcsynopsisinfo>\n"
637             T += page.funcsynopsis + \
638                  "\n</funcsynopsis>\n"
639         if page.funcsynopsis_list:
640             T += "\n<funcsynopsis>"
641             if page.funcsynopsisinfo:
642                 T += "\n<funcsynopsisinfo>"+    page.funcsynopsisinfo + \
643                      "\n</funcsynopsisinfo>\n"
644             for funcsynopsis in page.funcsynopsis_list:
645                 T += funcsynopsis
646             T += "\n</funcsynopsis>\n"
647         #fi
648         return T
649     def description_text(page):
650         """ the description section on a manpage is the main part. Here
651             it is generated from the per-function comment area. """
652         if page.description:
653             return page.description
654         if page.description_list:
655             T = ""
656             for description in page.description_list:
657                 if not description: continue
658                 T += description
659             if T: return T
660         return ""
661     def authors_text(page):
662         """ part of the footer sections on a manpage and a description of
663             original authors. We prever an itimizedlist to let the manvol
664             show a nice vertical aligment of authors of this ref item """
665         if page.authors:
666             return page.authors
667         if page.authors_list:
668             T = "<itemizedlist>"
669             previous=""
670             for authors in page.authors_list:
671                 if not authors: continue
672                 if previous == authors: continue
673                 T += "\n <listitem><para>"+authors+"</para></listitem>"
674                 previous = authors
675             T += "</itemizedlist>"
676             return T
677         if page.authors:
678             return page.authors
679         return ""
680     def copyright_text(page):
681         """ the copyright section is almost last on a manpage and purely
682             optional. We list the part of the per-file copyright info """
683         if page.copyright:
684             return page.copyright
685         """ we only return the first valid instead of merging them """
686         if page.copyright_list:
687             T = ""
688             for copyright in page.copyright_list:
689                 if not copyright: continue
690                 return copyright # !!!
691         return ""
692     def seealso_text(page):
693         """ the last section on a manpage is called 'SEE ALSO' usally and
694             contains a comma-separated list of references. Some manpage
695             viewers can parse these and convert them into hyperlinks """
696         if page.seealso:
697             return page.seealso
698         if page.seealso_list:
699             T = ""
700             for seealso in page.seealso_list:
701                 if not seealso: continue
702                 if T: T += ", "
703                 T += seealso
704             if T: return T
705         return ""
706     def refentry_text(page, id=None):
707         """ combine fields into a proper docbook refentry """
708         if id is None:
709             id = page.refentry
710         if id:
711             T = '<refentry id="'+id+'">'
712         else:
713             T = '<refentry>' # this is an error
714            
715         if page.refentryinfo_text():
716             T += "\n<refentryinfo>"+       page.refentryinfo_text()+ \
717                  "\n</refentryinfo>\n"
718         if page.refmeta_text():
719             T += "\n<refmeta>"+            page.refmeta_text() + \
720                  "\n</refmeta>\n" 
721         if page.refnamediv_text():
722             T += "\n<refnamediv>"+         page.refnamediv_text() + \
723                  "\n</refnamediv>\n"
724         if page.funcsynopsisdiv_text():     
725             T += "\n<refsynopsisdiv>\n"+   page.funcsynopsisdiv_text()+ \
726                  "\n</refsynopsisdiv>\n"
727         if page.description_text():
728             T += "\n<refsect1><title>Description</title> " + \
729                  page.description_text() + "\n</refsect1>"
730         if page.authors_text():
731             T += "\n<refsect1><title>Author</title> " + \
732                  page.authors_text() + "\n</refsect1>"
733         if page.copyright_text():
734             T += "\n<refsect1><title>Copyright</title> " + \
735                  page.copyright_text() + "\n</refsect1>\n"
736         if page.seealso_text():
737             T += "\n<refsect1><title>See Also</title><para> " + \
738                  page.seealso_text() + "\n</para></refsect1>\n"
739
740         T +=  "\n</refentry>\n"
741         return T
742     #fu
743 #end
744
745 # -----------------------------------------------------------------------
746 class FunctionRefPage(RefPage):
747     def reinit(page):
748         """ here we parse the input function for its values """
749         if page.func.into:
750             page.refhint = "\n              <!-- see "+page.func.into+" -->\n"
751         #fi
752         page.refentry = page.func.name               # //refentry@id
753         page.refentry_title = page.func.name.strip() # //refentryinfo/title
754         page.refentrytitle = page.func.name          # //refmeta/refentrytitle
755         if page.includes:
756             page.funcsynopsisinfo += "\n"+page.includes
757         if not page.funcsynopsisinfo:
758             page.funcsynopsisinfo="\n"+' #include &lt;'+page.mainheader+'&gt;'
759         page.refpurpose = page.func.head
760         page.refname = page.func.name
761
762         def funcsynopsis_of(func):
763             return (
764                 "\n <funcprototype>\n <funcdef>"+func.prespec+
765                 " <function>"+func.name+"</function></funcdef>"+
766                 "\n"+s(s(s(func.callspec,
767                            r"<parameters>\s*\(",r" "),
768                          r"\)\s*</parameters>",r" "),
769                        r"</paramdef>\s*,\s*",r"</paramdef>\n ")+
770                 " </funcprototype>")
771         page.funcsynopsis = funcsynopsis_of(page.func)
772
773         page.description = (
774             html2docbook(this_function_link(page.func.body, page.func.name)))
775
776         if page.file_authors:
777             def add_authors(page, ename, email):
778                 page.authors_list.append( ename+' '+email )
779                 return ename+email
780             s(page.file_authors,
781               r"(?sx) \s* ([^<>]*) (<email>[^<>]*</email>) ", lambda x
782               : add_authors(page, x.group(1), x.group(2)))
783         #fi
784
785         if page.file_copyright:
786             page.copyright = "<screen>\n"+page.file_copyright+"</screen>\n"
787         #fi
788         return page
789     def __init__(page,func):
790         RefPage.__init__(page, func)
791         FunctionRefPage.reinit(page)
792     
793 def refpage_list_from_function_list(funclist):
794     list = []
795     mapp = {}
796     for func in funclist:
797         mapp[func.name] = func
798     #od
799     for func in funclist:
800         page = FunctionRefPage(func)
801         if func.into and func.into not in mapp:
802             warn (# ............ (refpage_list_from_function_list) .......
803                 "page '"+page.func.name+"' has no target => "+
804                 "'"+page.func.into+"'"
805                 "\n: going to reset .into of Function '"+page.func.name+"'")
806             func.into = None
807         #fi
808         list.append(FunctionRefPage(func))
809     return list
810 #fu
811     
812 # ordered list of pages
813 refpage_list = refpage_list_from_function_list(function_list)
814
815 class FunctionFamilyRefPage(RefPage):
816     def __init__(self, page):
817         RefPage.__init__(self, page.func)
818         self.seealso_list = [] # reset
819         self.refhint_list = []
820     def refhint_list_text(page):
821         T = ""
822         for hint in page.refhint_list:
823             T += hint
824         return T
825     def refentry_text(page):
826         return page.refhint_list_text() + "\n" + \
827                RefPage.refentry_text(page)
828     pass
829
830 def docbook_pages_recombine(pagelist):
831     """ take a list of RefPages and create a new list where sections are
832         recombined in a way that their description is listed on the same
833         page and the manvol formatter creates symlinks to the combined
834         function description page - use the attribute 'into' to guide the
835         processing here as each of these will be removed from the output
836         list. If no into-pages are there then the returned list should
837         render to the very same output text like the input list would do """
838
839     list = []
840     combined = {}
841     for orig in pagelist:
842         if orig.func.into: continue
843         page = FunctionFamilyRefPage(orig)
844         combined[orig.func.name] = page ; list.append(page)
845
846         page.refentry = orig.refentry              # //refentry@id
847         page.refentry_title = orig.refentrytitle   # //refentryinfo/title
848         page.refentrytitle = orig.refentrytitle    # //refmeta/refentrytitle
849         page.includes = orig.includes
850         page.funcsynopsisinfo = orig.funcsynopsisinfo
851         page.refpurpose = orig.refpurpose
852         if orig.refhint:
853             page.refhint_list.append( orig.refhint )
854         if orig.refname:
855             page.refname_list.append( orig.refname )
856         elif orig.refname_list:
857             page.refname_list.extend( orig.refname_list )
858         if orig.funcsynopsis:
859             page.funcsynopsis_list.append( orig.funcsynopsis )
860         elif orig.refname_list:
861             page.funcsynopsis_list.extend( orig.funcsynopsis_list )
862         if orig.description:
863             page.description_list.append( orig.description )
864         elif orig.refname_list:
865             page.description_list.extend( orig.description_list )
866         if orig.seealso:
867             page.seealso_list.append( orig.seealso )
868         elif orig.seealso_list:
869             page.seealso_list.extend( orig.seealso_list )
870         if orig.authors:
871             page.authors_list.append( orig.authors )
872         elif orig.authors_list:
873             page.authors_list.extend( orig.authors_list )
874         if orig.copyright:
875             page.copyright_list.append( orig.copyright )
876         elif orig.refname_list:
877             page.copyright_list.extend( orig.copyright_list )
878     #od
879     for orig in pagelist:
880         if not orig.func.into: continue
881         if orig.func.into not in combined:
882             warn("page for '"+orig.func.name+
883                  "' has no target => '"+orig.func.into+"'")
884             page = FunctionFamilyRefPage(orig)
885         else:
886             page = combined[orig.func.into]
887
888         if orig.refname:
889             page.refname_list.append( orig.refname )
890         elif orig.refname_list:
891             page.refname_list.extend( orig.refname_list )
892         if orig.funcsynopsis:
893             page.funcsynopsis_list.append( orig.funcsynopsis )
894         elif orig.refname_list:
895             page.funcsynopsis_list.extend( orig.funcsynopsis_list )
896         if orig.description:
897             page.description_list.append( orig.description )
898         elif orig.refname_list:
899             page.description_list.extend( orig.description_list )
900         if orig.seealso:
901             page.seealso_list.append( orig.seealso )
902         elif orig.seealso_list:
903             page.seealso_list.extend( orig.seealso_list )
904         if orig.authors:
905             page.authors_list.append( orig.authors )
906         elif orig.authors_list:
907             page.authors_list.extend( orig.authors_list )
908         if orig.copyright:
909             page.copyright_list.append( orig.copyright )
910         elif orig.refname_list:
911             page.copyright_list.extend( orig.copyright_list )
912     #od
913     return list
914 #fu
915
916 combined_pages = docbook_pages_recombine(pagelist = refpage_list)
917
918 # -----------------------------------------------------------------------
919
920 class HeaderRefPage(RefPage):
921     pass
922
923 def docbook_refpages_perheader(page_list): # headerlist
924     " creating the per-header manpage - a combination of function man pages "
925     header = {}
926     for page in page_list:
927         assert not page.func.into
928         file = page.func.src.file.mainheader # short for the mainheader index
929         if file not in header:
930             header[file] = HeaderRefPage(page.func)
931             header[file].id = s(file, r"[^\w\.]","-")
932             header[file].refentry = header[file].id
933             header[file].refentryinfo = None
934             header[file].refentry_date = page.refentry_date
935             header[file].refentry_productname = (
936                 "the library "+page.refentry_productname)
937             header[file].manvolnum = page.manvolnum
938             header[file].refentrytitle = file
939             header[file].funcsynopsis = ""
940         if 1: # or += or if not header[file].refnamediv:
941             header[file].refpurpose = " library "
942             header[file].refname = header[file].id
943
944         if not header[file].funcsynopsisinfo and page.funcsynopsisinfo:
945             header[file].funcsynopsisinfo  = page.funcsynopsisinfo
946         if page.funcsynopsis:
947             header[file].funcsynopsis  += "\n"+page.funcsynopsis
948         if not header[file].copyright and page.copyright:
949             header[file].copyright = page.copyright
950         if not header[file].authors and page.authors:
951             header[file].authors = page.authors
952         if not header[file].authors and page.authors_list:
953             header[file].authors_list = page.authors_list
954         if not header[file].description:
955             found = m(commands.getoutput("cat "+o.package+".spec"),
956                       r"(?s)\%description\b([^\%]*)\%")
957             if found:
958                 header[file].description = found.group(1)
959             elif not header[file].description:
960                 header[file].description = "<para>" + (
961                     page.refentry_productname + " library") + "</para>";
962             #fi
963         #fi
964     #od
965     return header#list
966 #fu
967
968 def leaders(pagelist):
969     list = []
970     for page in pagelist:
971         if page.func.into : continue
972         list.append(page)
973     return list
974 header_refpages = docbook_refpages_perheader(leaders(refpage_list))
975
976 # -----------------------------------------------------------------------
977 # printing the docbook file is a two-phase process - we spit out the
978 # leader pages first - later we add more pages with _refstart pointing
979 # to the leader page, so that xmlto will add the functions there. Only the
980 # leader page contains some extra info needed for troff page processing.
981
982 doctype = '<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"'
983 doctype += "\n     "
984 doctype += '"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">'+"\n"
985
986 try:
987     F = open(o.docbookfile,"w")
988 except IOError, error:
989     warn("can not open docbook output file: "+o.docbookfile, error)
990 else:
991     print >> F, doctype, '<reference><title>Manual Pages</title>'
992
993     for page in combined_pages:
994         print >> F, page.refentry_text()
995     #od
996
997     for page in header_refpages.values():
998         if not page.refentry: continue
999         print >> F, "\n<!-- _______ "+page.id+" _______ -->",
1000         print >> F, page.refentry_text()
1001     #od
1002
1003     print >> F, "\n",'</reference>',"\n"
1004     F.close()
1005 #fi
1006
1007 # _____________________________________________________________________
1008 try:
1009     F = open( o.dumpdocfile, "w")
1010 except IOError, error:
1011     warn ("can not open"+o.dumpdocfile,error)
1012 else:
1013     for func in function_list:
1014         name = func.name
1015         print >> F, "<fn id=\""+name+"\">"+"<!-- FOR \""+name+"\" -->\n"
1016         for H in sorted_keys(func.dict()):
1017             print >> F, "<"+H+" name=\""+name+"\">",
1018             print >> F, str(func.dict()[H]),
1019             print >> F, "</"+H+">"
1020         #od
1021         print >> F, "</fn><!-- END \""+name+"\" -->\n\n";
1022     #od
1023     F.close();
1024 #fi
1025
1026 if errors: sys.exit(errors)