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