docbook2man-de-spec.pl 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304
  1. =head1 NAME
  2. docbook2man-spec - convert DocBook RefEntries to Unix manpages
  3. =head1 SYNOPSIS
  4. The SGMLSpm package from CPAN. This contains the sgmlspl script which
  5. is used to grok this file. Use it like this:
  6. nsgmls some-docbook-document.sgml | sgmlspl sgmlspl-specs/docbook2man-spec.pl
  7. =head1 OPTIONS
  8. =over 4
  9. =item --section <label>
  10. Use the specified manpage section number,
  11. if not specified in <MANVOLNUM>. Default is one (1).
  12. =item --date <string>
  13. Use the specified date in the .TH header.
  14. Default is today.
  15. =item --lowercase | --preserve-case
  16. Convert output file names and cross-references to lower case.
  17. Default is to convert to upper case.
  18. =head1 DESCRIPTION
  19. This is a sgmlspl spec file that produces Unix-style
  20. manpages from RefEntry markup.
  21. See the accompanying RefEntry man page for 'plain new' documentation. :)
  22. =head1 LIMITATIONS
  23. Trying docbook2man on non-DocBook or non-conformant SGML results in
  24. undefined behavior. :-)
  25. This program is a slow, dodgy Perl script.
  26. This program does not come close to supporting all the possible markup
  27. in DocBook, and will produce wrong output in some cases with supported
  28. markup.
  29. =head1 TODO
  30. Add new element handling and fix existing handling. Be robust.
  31. Produce cleanest, readable man output as possible (unlike some
  32. other converters). Follow Linux man(7) convention.
  33. If this results in added logic in this script,
  34. that's okay. The code should still be reasonably organized.
  35. Make it faster. If Perl sucks port it to another language.
  36. =head1 COPYRIGHT
  37. Copyright (C) 1998-1999 Steve Cheng <steve@ggi-project.org>
  38. Copyright (C) 1999 Thomas Lockhart <lockhart@alumni.caltech.edu>
  39. This program is free software; you can redistribute it and/or modify it
  40. under the terms of the GNU General Public License as published by the Free
  41. Software Foundation; either version 2, or (at your option) any later
  42. version.
  43. You should have received a copy of the GNU General Public License along with
  44. this program; see the file COPYING. If not, please write to the Free
  45. Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  46. =cut
  47. # $Id: docbook2man-de-spec.pl,v 1.1 2004/07/05 09:32:10 kreutzm-guest Exp $
  48. use SGMLS; # Use the SGMLS package.
  49. use SGMLS::Output; # Use stack-based output.
  50. use SGMLS::Refs;
  51. ########################################################################
  52. # SGMLSPL script produced automatically by the script sgmlspl.pl
  53. #
  54. # Document Type: any, but processes only RefEntries
  55. # Edited by: me :)
  56. ########################################################################
  57. $write_manpages = 0;
  58. $blank_xrefs = 0;
  59. $default_sect = "1";
  60. $default_date = `date "+%d %B %Y"`; # L10N
  61. while (@ARGV) {
  62. my $arg = shift @ARGV;
  63. if ($arg eq "--section") {
  64. $default_sect = shift @ARGV || die "$arg requires an argument\n"; # L10N
  65. } elsif ($arg eq "--date") {
  66. $default_date = shift @ARGV || die "$arg requires an argument\n"; # L10N
  67. } elsif ($arg eq "--lowercase") {
  68. $lowercase_names = 1;
  69. } elsif ($arg eq "--preserve-case") {
  70. $lowercase_names = 0;
  71. } elsif ($arg eq "--help") {
  72. print "Usage: $0",
  73. " [ --section <label> ]",
  74. " [ --date <string> ]",
  75. " [ --lowercase | --preserve-case ]",
  76. "\n";
  77. exit;
  78. } else {
  79. die "unrecognized switch $arg; try $0 --help\n"; # L10N
  80. }
  81. }
  82. sgml('start', sub {
  83. push_output('nul');
  84. $raw_cdata = 1; # Makes it a bit faster.
  85. # Links file
  86. open(LINKSFILE, ">manpage.links");
  87. $Refs = new SGMLS::Refs("manpage.refs", "manpage.log");
  88. });
  89. sgml('end', sub {
  90. close(LINKSFILE);
  91. # Explicitly invoke destructor,
  92. # otherwise cache file may not get written!
  93. # Thomas Lockhart, 1999-08-03, perl-5.004, RedHat5.2
  94. undef $Refs;
  95. if($blank_xrefs) {
  96. warn "Warning: output contains unresolved XRefs\n"; # L10N
  97. }
  98. });
  99. ########################################################################
  100. #
  101. # Output helpers
  102. #
  103. ########################################################################
  104. # Remove leading and trailing blanks.
  105. sub StripString
  106. {
  107. my $str = shift;
  108. $str = $1 if ($str =~ m#^\s*(\S.*)#);
  109. $str = $1 if ($str =~ m#^(.*\S)\s*$#);
  110. return $str;
  111. }
  112. # Generate a good file name, for given manpage title and manvolnum
  113. # (cdata content).
  114. # Cleanup whitespace and convert to lower case if required.
  115. sub FileInfo
  116. {
  117. my $title = StripString(shift);
  118. my $volnum = StripString(shift);
  119. $title = lc $title if $lowercase_names;
  120. $title =~ tr/ /_/;
  121. $volnum =~ tr/ /_/;
  122. # The 'package name' part of the section should
  123. # not be used when citing it.
  124. my $sectcite = $1 if ($volnum =~ /([0-9]*)/);
  125. return ("$title.$volnum", "$title($sectcite)");
  126. }
  127. # Our own version of sgml() and output() to allow simple string output
  128. # to play well with roff's stupid whitespace rules.
  129. sub man_sgml
  130. {
  131. if(ref($_[1]) eq 'CODE') {
  132. return &sgml;
  133. }
  134. my $s = $_[1];
  135. $s =~ s/\\/\\\\/g;
  136. $s =~ s/'/\\'/g;
  137. sgml($_[0], eval("sub { man_output '$s' }"));
  138. }
  139. sub man_output
  140. {
  141. if($separator eq 'full') {
  142. output "\n" unless $newline_last++;
  143. output ".PP\n";
  144. $separator = '';
  145. }
  146. $_ = shift;
  147. if(s/^\n//) {
  148. output "\n" unless $newline_last++;
  149. }
  150. return if $_ eq '';
  151. output $_;
  152. if(@_) {
  153. output @_;
  154. $newline_last = (pop(@_) =~ /\n$/);
  155. } else {
  156. $newline_last = ($_ =~ /\n$/)
  157. }
  158. }
  159. # Fold lines into one, quote some characters
  160. sub fold_string
  161. {
  162. $_ = shift;
  163. s/\\/\\\\/g;
  164. s/"/\\\&"/g;
  165. # Change tabs and newlines to spaces
  166. # The newlines will be swallowed later while trimming
  167. tr/[\t\n]/ /;
  168. # Trim whitespace from beginning and end.
  169. s/^ +//;
  170. s/ +$//;
  171. return $_;
  172. }
  173. sub save_cdata()
  174. {
  175. $raw_cdata++;
  176. push_output('string');
  177. }
  178. sub bold_on()
  179. {
  180. # If the last font is also bold, don't change anything.
  181. # Basically this is to just get more readable man output.
  182. if($fontstack[$#fontstack] ne 'bold') {
  183. if(!$raw_cdata) {
  184. output '\fB';
  185. #$newline_last = 0;
  186. }
  187. }
  188. push(@fontstack, 'bold');
  189. }
  190. sub italic_on()
  191. {
  192. # If the last font is also italic, don't change anything.
  193. if($fontstack[$#fontstack] ne 'italic') {
  194. if(!$raw_cdata) {
  195. output '\fI';
  196. #$newline_last = 0;
  197. }
  198. }
  199. push(@fontstack, 'italic');
  200. }
  201. sub font_off()
  202. {
  203. my $thisfont = pop(@fontstack);
  204. my $lastfont = $fontstack[$#fontstack];
  205. # Only output font change if it is different
  206. if($thisfont ne $lastfont) {
  207. if($raw_cdata) { return; }
  208. elsif($lastfont eq 'bold') { output '\fB'; }
  209. elsif($lastfont eq 'italic') { output '\fI'; }
  210. else { output '\fR'; }
  211. #$newline_last = 0;
  212. }
  213. }
  214. ########################################################################
  215. #
  216. # Manpage management
  217. #
  218. ########################################################################
  219. sgml('<REFENTRY>', sub {
  220. # This will be overwritten at end of REFMETA, when we know the name of the page.
  221. pop_output();
  222. $write_manpages = 1; # Currently writing manpage.
  223. $nocollapse_whitespace = 0; # Current whitespace collapse counter.
  224. $newline_last = 1; # At beginning of line?
  225. # Just a bit of warning, you will see this variable manipulated
  226. # manually a lot. It makes the code harder to follow but it
  227. # saves you from having to worry about collapsing at the end of
  228. # parse, stopping at verbatims, etc.
  229. $raw_cdata = 0; # Instructs certain output functions to
  230. # leave CDATA alone, so we can assign
  231. # it to a string and process it, etc.
  232. @fontstack = (); # Fonts being activated.
  233. $list_nestlevel = 0; # Indent certain nested content.
  234. # Separator to use between 'elements' in the content of a
  235. # paragraph (usually). This makes sure that PCDATA after a list
  236. # in a PARA gets a break in between and not become part of the
  237. # last listitem. Note that we can't do it after the list ends,
  238. # because often the list ends the paragraph and we'll get an
  239. # extra break. Anything that changes the separator status from
  240. # the default should also save its last state in the parent
  241. # element's ext, but I'm not going to explain further. It's a
  242. # gross hack and almost guaranteed to fail in unforseen cases.
  243. # The only way to avoid all this is to use a tree/grove model, which
  244. # we're _not_ doing.
  245. $separator = '';
  246. $manpage_title = ''; # Needed for indexing.
  247. $manpage_sect = '';
  248. @manpage_names = ();
  249. $manpage_misc = '';
  250. });
  251. sgml('</REFENTRY>', sub {
  252. if(!$newline_last) {
  253. output "\n";
  254. }
  255. $raw_cdata = 1;
  256. push_output('nul');
  257. $write_manpages = 0;
  258. });
  259. sgml('</REFMETA>', sub {
  260. my ($filename, $citation) =
  261. FileInfo($manpage_title, $manpage_sect || $default_sect);
  262. push_output('file', $filename);
  263. output <<_END_BANNER;
  264. .\\" This manpage has been automatically generated by docbook2man-spec
  265. .\\" from a DocBook document. docbook2man-spec can be found at:
  266. .\\" <http://shell.ipoline.com/~elmert/hacks/docbook2X/>
  267. .\\" Please send any bug reports, improvements, comments, patches,
  268. .\\" etc. to Steve Cheng <steve\@ggi-project.org>.
  269. _END_BANNER
  270. my $manpage_date = $_[0]->parent->ext->{'date'} || $default_date;
  271. output '.TH "';
  272. # If the title is not mixed-case, convention says to
  273. # uppercase the whole title. (The canonical title is
  274. # lowercase.)
  275. if($manpage_title =~ /[A-Z]/) {
  276. output fold_string($manpage_title);
  277. } else {
  278. output uc(fold_string($manpage_title));
  279. }
  280. output '" "', fold_string($manpage_sect),
  281. '" "', fold_string($manpage_date),
  282. '" "', $manpage_misc,
  283. '" "', $manpage_manual,
  284. "\"\n";
  285. $newline_last = 1;
  286. # References to this RefEntry.
  287. if(defined($_[0]->parent->attribute('ID')->value)) {
  288. my $id = $_[0]->parent->attribute('ID')->value;
  289. # Append XREFLABEL content to citations.
  290. if(defined($_[0]->parent->attribute('XREFLABEL')->value)) {
  291. $citation = $_[0]->parent->attribute('XREFLABEL')->value .
  292. ' [' . $citation . ']';
  293. }
  294. $Refs->put("refentry:$id", $citation);
  295. }
  296. });
  297. sgml('<REFENTRYTITLE>', sub {
  298. if($_[0]->in('REFMETA')) {
  299. save_cdata();
  300. } else {
  301. # Manpage citations are in bold.
  302. bold_on();
  303. }
  304. });
  305. sgml('</REFENTRYTITLE>', sub {
  306. if($_[0]->in('REFMETA')) {
  307. $raw_cdata--;
  308. $manpage_title = pop_output();
  309. }
  310. else { font_off(); }
  311. if (defined($_[0]->attribute('ID')->value)) {
  312. my $id = $_[0]->attribute('ID')->value;
  313. my ($name, $citation) = FileInfo($manpage_title, $default_sect);
  314. $Refs->put("refentrytitle:$id", $citation);
  315. }
  316. });
  317. sgml('<MANVOLNUM>', sub {
  318. if($_[0]->in('REFMETA')) {
  319. save_cdata();
  320. } else {
  321. # Manpage citations use ().
  322. output '(';
  323. }
  324. });
  325. sgml('</MANVOLNUM>', sub {
  326. if($_[0]->in('REFMETA')) {
  327. $raw_cdata--;
  328. $manpage_sect = pop_output();
  329. }
  330. else { output ')' }
  331. });
  332. sgml('<REFMISCINFO>', \&save_cdata);
  333. sgml('</REFMISCINFO>', sub {
  334. $raw_cdata--;
  335. $manpage_misc = fold_string(pop_output());
  336. });
  337. # NAME section
  338. man_sgml('<REFNAMEDIV>', "\n.SH NAME\n"); # L10N
  339. sgml('<REFNAME>', \&save_cdata);
  340. sgml('</REFNAME>', sub {
  341. $raw_cdata--;
  342. push(@manpage_names, pop_output());
  343. });
  344. sgml('<REFPURPOSE>', \&save_cdata);
  345. sgml('</REFPURPOSE>', sub {
  346. $raw_cdata--;
  347. my $manpage_purpose = fold_string(pop_output());
  348. for(my $i = 0; $i < $#manpage_names; $i++) {
  349. output fold_string($manpage_names[$i]), ', ';
  350. }
  351. output fold_string($manpage_names[$#manpage_names]);
  352. output " \\- $manpage_purpose\n";
  353. $newline_last = 1;
  354. foreach(@manpage_names) {
  355. # Don't link to itself
  356. if($_ ne $manpage_title) {
  357. print LINKSFILE "$manpage_title.$manpage_sect $_.$manpage_sect\n";
  358. }
  359. }
  360. });
  361. man_sgml('<REFCLASS>', "\n.sp\n");
  362. #RefDescriptor
  363. ########################################################################
  364. #
  365. # SYNOPSIS section and synopses
  366. #
  367. ########################################################################
  368. man_sgml('<REFSYNOPSISDIV>', "\n.SH ÜBERSICHT\n"); # L10N
  369. man_sgml('</REFSYNOPSISDIV>', "\n");
  370. ## FIXME! Must be made into block elements!!
  371. #sgml('<FUNCSYNOPSIS>', \&bold_on);
  372. #sgml('</FUNCSYNOPSIS>', \&font_off);
  373. #sgml('<CMDSYNOPSIS>', \&bold_on);
  374. #sgml('</CMDSYNOPSIS>', \&font_off);
  375. man_sgml('<FUNCSYNOPSIS>', sub {
  376. #man_output("\n.sp\n");
  377. bold_on();
  378. });
  379. man_sgml('</FUNCSYNOPSIS>', sub {
  380. font_off();
  381. man_output "\n";
  382. });
  383. man_sgml('<CMDSYNOPSIS>', "\n.sp\n");
  384. man_sgml('</CMDSYNOPSIS>', "\n");
  385. man_sgml('<FUNCPROTOTYPE>', "\n.sp\n");
  386. # Arguments to functions. This is C convention.
  387. man_sgml('<PARAMDEF>', '(');
  388. man_sgml('</PARAMDEF>', ");\n");
  389. man_sgml('<VOID>', "(void);\n");
  390. sub arg_start
  391. {
  392. # my $choice = $_[0]->attribute('CHOICE')->value;
  393. # The content model for CmdSynopsis doesn't include #PCDATA,
  394. # so we won't see any of the whitespace in the source file,
  395. # so we have to add it after each component.
  396. man_output ' ';
  397. if($_[0]->attribute('CHOICE')->value =~ /opt/i) {
  398. man_output '[ ';
  399. }
  400. bold_on();
  401. }
  402. sub arg_end
  403. {
  404. font_off();
  405. if($_[0]->attribute('REP')->value =~ /^Repeat/i) {
  406. italic_on();
  407. man_output '...';
  408. font_off();
  409. }
  410. if($_[0]->attribute('CHOICE')->value =~ /opt/i) {
  411. man_output ' ] ';
  412. }
  413. }
  414. sgml('<ARG>', \&arg_start);
  415. sgml('</ARG>', \&arg_end);
  416. sgml('<GROUP>', \&arg_start);
  417. sgml('</GROUP>', \&arg_end);
  418. sgml('<OPTION>', \&bold_on);
  419. sgml('</OPTION>', \&font_off);
  420. # FIXME: This is one _blank_ line.
  421. man_sgml('<SBR>', "\n\n");
  422. ########################################################################
  423. #
  424. # General sections
  425. #
  426. ########################################################################
  427. # The name of the section is handled by TITLE. This just sets
  428. # up the roff markup.
  429. man_sgml('<REFSECT1>', sub { $separator = ''; man_output "\n.SH "});
  430. man_sgml('<REFSECT2>', sub { $separator = ''; man_output "\n.SS "});
  431. man_sgml('<REFSECT3>', sub { $separator = ''; man_output "\n.SS "});
  432. ########################################################################
  433. #
  434. # Titles, metadata.
  435. #
  436. ########################################################################
  437. sgml('<TITLE>', sub {
  438. if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) {
  439. $write_manpages = 1;
  440. }
  441. save_cdata();
  442. });
  443. sgml('</TITLE>', sub {
  444. my $title = fold_string(pop_output());
  445. $raw_cdata--;
  446. if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) {
  447. # We use TITLE of enclosing Reference or Book as manual name
  448. $manpage_manual = $title;
  449. $write_manpages = 0;
  450. }
  451. elsif(exists $_[0]->parent->ext->{'title'}) {
  452. # By far the easiest case. Just fold the string as
  453. # above, and then set the parent element's variable.
  454. $_[0]->parent->ext->{'title'} = $title;
  455. }
  456. else {
  457. # If the parent element's handlers are lazy,
  458. # output the folded string for them :)
  459. # We assume they want uppercase and a newline.
  460. man_output '"', uc($title), "\"\n";
  461. }
  462. if (defined($_[0]->attribute('ID')->value)) {
  463. my $id = $_[0]->attribute('ID')->value;
  464. my ($name, $citation) = FileInfo($manpage_title, $default_sect);
  465. $Refs->put("title:$id", $citation);
  466. }
  467. });
  468. sgml('<ATTRIBUTION>', sub {
  469. if($_[0]->in('BLOCKQUOTE')) {
  470. push_output('string');
  471. }
  472. });
  473. sgml('</ATTRIBUTION>', sub {
  474. if($_[0]->in('BLOCKQUOTE')) {
  475. $_[0]->parent->ext->{'attribution'} = pop_output();
  476. } else {
  477. # For an Epigraph.
  478. man_output "\n\n";
  479. }
  480. });
  481. sgml('<DATE>', sub {
  482. save_cdata();
  483. });
  484. sgml('</DATE>', sub {
  485. $_[0]->parent->parent->ext->{'date'} = fold_string(pop_output());
  486. $raw_cdata--;
  487. });
  488. sub ignore_content { push_output 'nul'; }
  489. sub restore_content { pop_output(); }
  490. sgml('<DOCINFO>', \&ignore_content);
  491. sgml('</DOCINFO>', \&restore_content);
  492. sgml('<REFSYNOPSISDIVINFO>', \&ignore_content);
  493. sgml('</REFSYNOPSISDIVINFO>', \&restore_content);
  494. sgml('<REFSECT1INFO>', \&ignore_content);
  495. sgml('</REFSECT1INFO>', \&restore_content);
  496. sgml('<REFSECT2INFO>', \&ignore_content);
  497. sgml('</REFSECT2INFO>', \&restore_content);
  498. sgml('<REFSECT3INFO>', \&ignore_content);
  499. sgml('</REFSECT3INFO>', \&restore_content);
  500. sgml('<INDEXTERM>', \&ignore_content);
  501. sgml('</INDEXTERM>', \&restore_content);
  502. sgml('<AUTHORBLURB>', \&ignore_content);
  503. sgml('</AUTHORBLURB>', \&restore_content);
  504. ########################################################################
  505. #
  506. # Set bold on enclosed content
  507. #
  508. ########################################################################
  509. sgml('<APPLICATION>', \&bold_on);
  510. sgml('</APPLICATION>', \&font_off);
  511. sgml('<CLASSNAME>', \&bold_on); sgml('</CLASSNAME>', \&font_off);
  512. sgml('<STRUCTNAME>', \&bold_on); sgml('</STRUCTNAME>', \&font_off);
  513. sgml('<STRUCTFIELD>', \&bold_on); sgml('</STRUCTFIELD>', \&font_off);
  514. sgml('<SYMBOL>', \&bold_on); sgml('</SYMBOL>', \&font_off);
  515. sgml('<TYPE>', \&bold_on); sgml('</TYPE>', \&font_off);
  516. sgml('<ENVAR>', \&bold_on); sgml('</ENVAR>', \&font_off);
  517. sgml('<FUNCTION>', \&bold_on); sgml('</FUNCTION>', \&font_off);
  518. sgml('<EMPHASIS>', \&bold_on); sgml('</EMPHASIS>', \&font_off);
  519. sgml('<ERRORNAME>', \&bold_on); sgml('</ERRORNAME>', \&font_off);
  520. # ERRORTYPE
  521. sgml('<COMMAND>', \&bold_on); sgml('</COMMAND>', \&font_off);
  522. sgml('<GUIBUTTON>', \&bold_on); sgml('</GUIBUTTON>', \&font_off);
  523. sgml('<GUIICON>', \&bold_on); sgml('</GUIICON>', \&font_off);
  524. # GUILABEL
  525. # GUIMENU
  526. # GUIMENUITEM
  527. # GUISUBMENU
  528. # MENUCHOICE
  529. sgml('<ACCEL>', \&bold_on); sgml('</ACCEL>', \&font_off);
  530. # KEYCODE
  531. # SHORTCUT
  532. sgml('<KEYCOMBO>', sub {
  533. $separator = 'none';
  534. $_[0]->ext->{'separator'} = 'none';
  535. });
  536. sgml('</KEYCOMBO>', sub { $separator = $_[0]->parent->ext->{'separator'}; });
  537. sub _keycombo {
  538. if($_[0]->in('KEYCOMBO')) {
  539. if($separator eq 'none') { $separator = '' }
  540. else { man_output "+"; }
  541. }
  542. bold_on();
  543. }
  544. sgml('<KEYCAP>', \&_keycombo); sgml('</KEYCAP>', \&font_off);
  545. sgml('<KEYSYM>', \&_keycombo); sgml('</KEYSYM>', \&font_off);
  546. sgml('<MOUSEBUTTON>', \&_keycombo); sgml('</MOUSEBUTTON>', \&font_off);
  547. sgml('<USERINPUT>', \&bold_on); sgml('</USERINPUT>', \&font_off);
  548. sgml('<INTERFACEDEFINITION>', \&bold_on);
  549. sgml('</INTERFACEDEFINITION>', \&font_off);
  550. # May need to look at the CLASS
  551. sgml('<SYSTEMITEM>', \&bold_on);
  552. sgml('</SYSTEMITEM>', \&font_off);
  553. ########################################################################
  554. #
  555. # Set italic on enclosed content
  556. #
  557. ########################################################################
  558. sgml('<FIRSTTERM>', \&italic_on); sgml('</FIRSTTERM>', \&font_off);
  559. sgml('<FILENAME>', \&italic_on); sgml('</FILENAME>', \&font_off);
  560. sgml('<PARAMETER>', \&italic_on); sgml('</PARAMETER>', \&font_off);
  561. sgml('<PROPERTY>', \&italic_on); sgml('</PROPERTY>', \&font_off);
  562. sgml('<REPLACEABLE>', sub {
  563. italic_on();
  564. if($_[0]->in('TOKEN')) {
  565. # When tokenizing, follow more 'intuitive' convention
  566. output "<";
  567. }
  568. });
  569. sgml('</REPLACEABLE>', sub {
  570. if($_[0]->in('TOKEN')) {
  571. output ">";
  572. }
  573. font_off();
  574. });
  575. sgml('<CITETITLE>', \&italic_on); sgml('</CITETITLE>', \&font_off);
  576. sgml('<FOREIGNPHRASE>', \&italic_on); sgml('</FOREIGNPHRASE>', \&font_off);
  577. sgml('<LINEANNOTATION>', \&italic_on); sgml('</LINEANNOTATION>', \&font_off);
  578. ########################################################################
  579. #
  580. # Other 'inline' elements
  581. #
  582. ########################################################################
  583. man_sgml('<EMAIL>', '<');
  584. man_sgml('</EMAIL>', '>');
  585. man_sgml('<OPTIONAL>', '[');
  586. man_sgml('</OPTIONAL>', ']');
  587. man_sgml('</TRADEMARK>', "\\u\\s-2TM\\s+2\\d"); # L10N
  588. man_sgml('<COMMENT>', "[Kommentar: "); # L10N
  589. man_sgml('</COMMENT>', "]");
  590. man_sgml('<QUOTE>', "»"); # L10N
  591. man_sgml('</QUOTE>', "«"); # L10N
  592. #man_sgml('<LITERAL>', '"');
  593. #man_sgml('</LITERAL>', '"');
  594. # There doesn't seem to be a good way to represent LITERAL in -man
  595. # ComputerOutput, SGMLTag, Markup are the same thing.
  596. # These create spaces between content in special elements
  597. # without PCDATA content.
  598. man_sgml('</HONORIFIC>', " ");
  599. man_sgml('</FIRSTNAME>', " ");
  600. man_sgml('</SURNAME>', " ");
  601. man_sgml('</LINEAGE>', " ");
  602. man_sgml('</OTHERNAME>', " ");
  603. man_sgml('<AFFILIATION>', "(");
  604. man_sgml('</AFFILIATION>', ") ");
  605. man_sgml('<CONTRIB>', "(");
  606. man_sgml('</CONTRIB>', ") ");
  607. man_sgml('</STREET>', " ");
  608. man_sgml('</POB>', " ");
  609. man_sgml('</POSTCODE>', " ");
  610. man_sgml('</CITY>', " ");
  611. man_sgml('</STATE>', " ");
  612. man_sgml('</COUNTRY>', " ");
  613. man_sgml('</PHONE>', " ");
  614. man_sgml('</FAX>', " ");
  615. man_sgml('</OTHERADDRESS>', " ");
  616. man_sgml('</ALT>', ": ");
  617. man_sgml('<GRAPHIC>', " [GRAPHIK] "); # L10N
  618. # No special presentation:
  619. # AUTHORINITIALS
  620. # ABBREV
  621. # ACTION
  622. # ACRONYM
  623. # CITATION
  624. # PHRASE
  625. # QUOTE
  626. # WORDASWORD
  627. # PROMPT
  628. # RETURNVALUE
  629. # TOKEN
  630. # DATABASE
  631. # HARDWARE
  632. # INTERFACE
  633. # MEDIALABEL
  634. ########################################################################
  635. #
  636. # Paragraph and paragraph-like elements
  637. #
  638. ########################################################################
  639. sub para_start {
  640. if($separator eq '' or $separator eq 'full') {
  641. $separator = '';
  642. man_output "\n.PP\n";
  643. } elsif($separator eq 'blank') {
  644. man_output "\n\n";
  645. } elsif($separator eq 'none' ) {
  646. $_[0]->parent->ext->{'separator'} = 'blank';
  647. $separator = 'blank';
  648. }
  649. }
  650. # Actually applies to a few other block elements as well
  651. sub para_end {
  652. $separator = $_[0]->parent->ext->{'separator'};
  653. man_output "\n";
  654. }
  655. sgml('<PARA>', \&para_start);
  656. sgml('</PARA>', \&para_end);
  657. sgml('<SIMPARA>', \&para_start);
  658. sgml('</SIMPARA>', \&para_end);
  659. # Nothing special, except maybe FIXME set nobreak.
  660. sgml('<INFORMALEXAMPLE>', \&para_start);
  661. sgml('</INFORMALEXAMPLE>', \&para_end);
  662. ########################################################################
  663. #
  664. # Blocks using SS sections
  665. #
  666. ########################################################################
  667. # FIXME: We need to consider the effects of SS
  668. # in a hanging tag :(
  669. # Complete with the optional-title dilemma (again).
  670. sgml('<ABSTRACT>', sub {
  671. $_[0]->ext->{'title'} = 'ABSTRAKT'; # L10N
  672. output "\n" unless $newline_last++;
  673. push_output('string');
  674. });
  675. sgml('</ABSTRACT>', sub {
  676. my $content = pop_output();
  677. # As ABSTRACT is never on the same level as RefSect1,
  678. # this leaves us with only .SS in terms of -man macros.
  679. output ".SS \"", uc($_[0]->ext->{'title'}), "\"\n";
  680. output $content;
  681. output "\n" unless $newline_last++;
  682. });
  683. # Ah, I needed a break. Example always has a title.
  684. sgml('<EXAMPLE>', sub { $separator = ''; man_output "\n.SS "});
  685. sgml('</EXAMPLE>', \&para_end);
  686. # Same with sidebar.
  687. sgml('<SIDEBAR>', sub { $separator = ''; man_output "\n.SS "});
  688. sgml('</SIDEBAR>', \&para_end);
  689. sgml('<FORMALPARA>', sub { $separator = ''; man_output "\n.SS "});
  690. sgml('</FORMALPARA>', \&para_end);
  691. sgml('<FIGURE>', sub { $separator = ''; man_output "\n.SS "});
  692. sgml('</FIGURE>', \&para_end);
  693. # NO title. # L10N
  694. sgml('<HIGHLIGHTS>', sub { $separator = ''; man_output "\n.SS HIGHLIGHTS\n"});
  695. sgml('</HIGHLIGHTS>', \&para_end);
  696. ########################################################################
  697. #
  698. # Indented 'Block' elements
  699. #
  700. ########################################################################
  701. sub indent_block_start
  702. {
  703. $separator = '';
  704. man_output "\n.sp\n.RS\n";
  705. }
  706. sub indent_block_end
  707. {
  708. $separator = $_[0]->parent->ext->{'separator'};
  709. man_output "\n.RE\n.sp\n";
  710. }
  711. sgml('<ADDRESS>', sub {
  712. &indent_block_start;
  713. if($_[0]->attribute('FORMAT')->type eq 'NOTATION'
  714. and $_[0]->attribute('FORMAT')->value->name eq 'LINESPECIFIC') {
  715. &verbatim_start;
  716. }
  717. });
  718. sgml('</ADDRESS>', sub {
  719. if($_[0]->attribute('FORMAT')->type eq 'NOTATION'
  720. and $_[0]->attribute('FORMAT')->value->name eq 'LINESPECIFIC') {
  721. &verbatim_end;
  722. }
  723. &indent_block_end;
  724. });
  725. # This element is almost like an admonition (below),
  726. # only the default title is blank :)
  727. sgml('<BLOCKQUOTE>', sub {
  728. $_[0]->ext->{'title'} = '';
  729. &indent_block_start;
  730. push_output('string');
  731. });
  732. sgml('</BLOCKQUOTE>', sub {
  733. my $content = pop_output();
  734. if($_[0]->ext->{'title'}) {
  735. output ".B \"", $_[0]->ext->{'title'}, ":\"\n";
  736. }
  737. output $content;
  738. if($_[0]->ext->{'attribution'}) {
  739. man_output "\n\n -- ",
  740. $_[0]->ext->{'attribution'}, "\n";
  741. }
  742. &indent_block_end;
  743. });
  744. # Set off admonitions from the rest of the text by indenting.
  745. # FIXME: Need to check if this works inside paragraphs, not enclosing them.
  746. sub admonition_end {
  747. my $content = pop_output();
  748. # When the admonition is only one paragraph,
  749. # it looks nicer if the title was inline.
  750. my $num_para;
  751. while ($content =~ /^\.PP/gm) { $num_para++ }
  752. if($num_para==1) {
  753. $content =~ s/^\.PP\n//;
  754. }
  755. output ".B \"" . $_[0]->ext->{'title'} . ":\"\n";
  756. output $content;
  757. &indent_block_end;
  758. }
  759. sgml('<NOTE>', sub {
  760. # We can't see right now whether or not there is a TITLE
  761. # element, so we have to save the output now and add it back
  762. # at the end of this admonition.
  763. $_[0]->ext->{'title'} = 'Note'; # L10N
  764. &indent_block_start;
  765. push_output('string');
  766. });
  767. sgml('</NOTE>', \&admonition_end);
  768. # Same as above.
  769. sgml('<WARNING>', sub {
  770. $_[0]->ext->{'title'} = 'Warnung'; # L10N
  771. &indent_block_start;
  772. push_output('string');
  773. });
  774. sgml('</WARNING>', \&admonition_end);
  775. sgml('<TIP>', sub {
  776. $_[0]->ext->{'title'} = 'Tipp'; # L10N
  777. &indent_block_start;
  778. push_output('string');
  779. });
  780. sgml('</TIP>', \&admonition_end);
  781. sgml('<CAUTION>', sub {
  782. $_[0]->ext->{'title'} = 'Caution'; # L10N
  783. &indent_block_start;
  784. push_output('string');
  785. });
  786. sgml('</CAUTION>', \&admonition_end);
  787. sgml('<IMPORTANT>', sub {
  788. $_[0]->ext->{'title'} = 'Wichtig'; # L10N
  789. &indent_block_start;
  790. push_output('string');
  791. });
  792. sgml('</IMPORTANT>', \&admonition_end);
  793. ########################################################################
  794. #
  795. # Verbatim displays.
  796. #
  797. ########################################################################
  798. sub verbatim_start {
  799. $separator = '';
  800. man_output "\n.sp\n";
  801. man_output "\n.nf\n" unless $nocollapse_whitespace++;
  802. }
  803. sub verbatim_end {
  804. man_output "\n.sp\n";
  805. man_output "\n.fi\n" unless --$nocollapse_whitespace;
  806. $separator = $_[0]->parent->ext->{'separator'};
  807. }
  808. sgml('<PROGRAMLISTING>', \&verbatim_start);
  809. sgml('</PROGRAMLISTING>', \&verbatim_end);
  810. sgml('<SCREEN>', \&verbatim_start);
  811. sgml('</SCREEN>', \&verbatim_end);
  812. sgml('<LITERALLAYOUT>', \&verbatim_start);
  813. sgml('</LITERALLAYOUT>', \&verbatim_end);
  814. sgml('<SYNOPSIS>', sub {
  815. my $format = $_[0]->attribute('FORMAT');
  816. if($format->type eq 'NOTATION'
  817. and $format->value->name eq 'LINESPECIFIC')
  818. {
  819. &verbatim_start;
  820. } else {
  821. $separator = '';
  822. man_output "\n.sp\n";
  823. }
  824. });
  825. sgml('</SYNOPSIS>', sub {
  826. my $format = $_[0]->attribute('FORMAT');
  827. if($format->type eq 'NOTATION'
  828. and $format->value->name eq 'LINESPECIFIC')
  829. {
  830. &verbatim_end;
  831. } else {
  832. man_output "\n";
  833. $_[0]->parent->ext->{'separator'} = 'full';
  834. $separator = 'full';
  835. }
  836. });
  837. ########################################################################
  838. #
  839. # Lists
  840. #
  841. ########################################################################
  842. # Indent nested lists.
  843. sub list_start {
  844. man_output "\n.RS\n" if $list_nestlevel++;
  845. }
  846. sub list_end {
  847. man_output "\n.RE\n" if --$list_nestlevel;
  848. $_[0]->parent->ext->{'separator'} = 'full';
  849. $separator = 'full';
  850. }
  851. sgml('<VARIABLELIST>', \&list_start);
  852. sgml('</VARIABLELIST>', \&list_end);
  853. sgml('<ITEMIZEDLIST>', \&list_start);
  854. sgml('</ITEMIZEDLIST>', \&list_end);
  855. sgml('<ORDEREDLIST>', sub {
  856. &list_start;
  857. $_[0]->ext->{'count'} = 1;
  858. });
  859. sgml('</ORDEREDLIST>', \&list_end);
  860. # Output content on one line, bolded.
  861. sgml('<TERM>', sub {
  862. man_output "\n.TP\n";
  863. bold_on();
  864. push_output('string');
  865. });
  866. sgml('</TERM>', sub {
  867. my $term = StripString(pop_output());
  868. $term =~ tr/\n/ /;
  869. output $term;
  870. font_off();
  871. output "\n";
  872. $newline_last = 1;
  873. });
  874. sgml('<LISTITEM>', sub {
  875. # A bulleted list.
  876. if($_[0]->in('ITEMIZEDLIST')) {
  877. man_output "\n.TP 0.2i\n\\(bu\n";
  878. }
  879. # Need numbers.
  880. # Assume Arabic numeration for now.
  881. elsif($_[0]->in('ORDEREDLIST')) {
  882. man_output "\n.IP ", $_[0]->parent->ext->{'count'}++, ". \n";
  883. }
  884. $_[0]->ext->{'separator'} = 'none';
  885. $separator = 'none';
  886. });
  887. sgml('<SIMPLELIST>', sub {
  888. $_[0]->ext->{'first_member'} = 1;
  889. });
  890. sgml('<MEMBER>', sub {
  891. my $parent = $_[0]->parent;
  892. if($parent->attribute('TYPE')->value =~ /Inline/i) {
  893. if($parent->ext->{'first_member'}) {
  894. # If this is the first member don't put any commas
  895. $parent->ext->{'first_member'} = 0;
  896. } else {
  897. man_output ", ";
  898. }
  899. # We don't really have Horiz rendering, so it's the same
  900. # as Vert.
  901. } else {
  902. man_output "\n\n";
  903. }
  904. });
  905. # We implement Procedures as indent and lists
  906. sgml('<PROCEDURE>', sub {
  907. $_[0]->ext->{'count'} = 1;
  908. &indent_block_start;
  909. });
  910. sgml('</PROCEDURE>', sub {
  911. &indent_block_end;
  912. $_[0]->parent->ext->{'separator'} = 'full';
  913. $separator = 'full';
  914. });
  915. sgml('<STEP>', sub {
  916. man_output "\n.IP ", $_[0]->parent->ext->{'count'}++, ". \n";
  917. $_[0]->ext->{'separator'} = 'none';
  918. $separator = 'none';
  919. });
  920. ########################################################################
  921. #
  922. # Linkage, cross references
  923. #
  924. ########################################################################
  925. # Print the URL
  926. sgml('</ULINK>', sub {
  927. man_output ' <URL:', $_[0]->attribute('URL')->value, '>';
  928. });
  929. # If cross reference target is a RefEntry,
  930. # output CiteRefEntry-style references.
  931. sgml('<XREF>', sub {
  932. my $id;
  933. $id = $_[0]->attribute('LINKEND')->value;
  934. my $manref = $Refs->get("refentry:$id");
  935. if(!defined $manref) {
  936. $blank_xrefs++ if $write_manpages;
  937. man_output "[XRef auf $id]"; # L10N
  938. return;
  939. }
  940. # Limited ENDTERM support.
  941. if(defined $_[0]->attribute('ENDTERM')->value) {
  942. my $content = $Refs->get("title:$id") ||
  943. $Refs->get("refentrytitle:$id");
  944. man_output $content, ' [';
  945. }
  946. # This also displays the XREFLABEL (as bold)...
  947. # It's not worth the bother to fix it though, there
  948. # are better tools for this.
  949. my ($title, $sect) = ($manref =~ /(.*)(\(.*\))/);
  950. bold_on();
  951. man_output $title;
  952. font_off();
  953. man_output $sect;
  954. if(defined $_[0]->attribute('ENDTERM')->value) {
  955. man_output ']';
  956. }
  957. });
  958. # Anchor
  959. ########################################################################
  960. #
  961. # SDATA
  962. #
  963. ########################################################################
  964. man_sgml('|[lt ]|', '<');
  965. man_sgml('|[gt ]|', '>');
  966. man_sgml('|[amp ]|', '&');
  967. man_sgml('|[ndash ]|', '\(en');
  968. man_sgml('|[mdash ]|', '\(em');
  969. sgml('sdata',sub {
  970. man_output "|[", $_[0], "]|";
  971. warn "Warning: unrecognized SDATA: please add definition to docbook2man-spec.pl\n"; # L10N
  972. });
  973. #
  974. # Default handlers (uncomment these if needed). Right now, these are set
  975. # up to gag on any unrecognised elements, sdata, processing-instructions,
  976. # or entities.
  977. #
  978. # sgml('start_element',sub { die "Unknown element: " . $_[0]->name; }); # L10N
  979. # sgml('end_element','');
  980. # This is for weeding out and escaping certain characters.
  981. # This looks like it's inefficient since it's done on every line, but
  982. # in reality, SGMLSpm and sgmlspl parsing ESIS takes _much_ longer.
  983. sgml('cdata', sub
  984. {
  985. if(!$write_manpages) { return; }
  986. elsif($raw_cdata) { output $_[0]; return; }
  987. if($separator eq 'full') {
  988. output "\n" unless $newline_last++;
  989. output ".PP\n";
  990. $separator = '';
  991. }
  992. # Escape backslashes
  993. $_[0] =~ s/\\/\\\\/g;
  994. # In non-'pre'-type elements:
  995. if(!$nocollapse_whitespace) {
  996. # Change tabs to spaces
  997. $_[0] =~ tr/\t / /s;
  998. # Do not allow indents at beginning of line
  999. # groff chokes on that.
  1000. if($newline_last) {
  1001. $_[0] =~ s/^ //;
  1002. # If the line is all blank, don't do anything.
  1003. if($_[0] eq '') { return; }
  1004. $_[0] =~ s/^\./\\\&\./;
  1005. # Argh... roff doesn't like ' for some unknown reason
  1006. $_[0] =~ s/^\'/\\\&\'/;
  1007. }
  1008. }
  1009. $newline_last = 0;
  1010. output $_[0];
  1011. });
  1012. # When in whitespace-collapsing mode, we disallow consecutive newlines.
  1013. sgml('re', sub
  1014. {
  1015. if($nocollapse_whitespace || !$newline_last) {
  1016. output "\n";
  1017. }
  1018. $newline_last = 1;
  1019. });
  1020. sgml('pi', sub {});
  1021. sgml('entity',sub { die "Unknown external entity: " . $_[0]->name; }); # L10N
  1022. sgml('start_subdoc',sub { die "Unknown subdoc entity: " . $_[0]->name; });# L10N
  1023. sgml('end_subdoc',sub{});
  1024. sgml('conforming',sub{});
  1025. 1;