1133094Ssimon#!/usr/bin/perl -w
2133094Ssimon# Emacs should use -*- cperl -*- mode
3133094Ssimon#
4160802Ssimon# Copyright (c) 2003-2006 Simon L. Nielsen <simon@FreeBSD.org>
5133094Ssimon# All rights reserved.
6133094Ssimon#
7133094Ssimon# Redistribution and use in source and binary forms, with or without
8133094Ssimon# modification, are permitted provided that the following conditions
9133094Ssimon# are met:
10133094Ssimon# 1. Redistributions of source code must retain the above copyright
11133094Ssimon#    notice, this list of conditions and the following disclaimer.
12133094Ssimon# 2. Redistributions in binary form must reproduce the above copyright
13133094Ssimon#    notice, this list of conditions and the following disclaimer in the
14133094Ssimon#    documentation and/or other materials provided with the distribution.
15133094Ssimon#
16133094Ssimon# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17133094Ssimon# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18133094Ssimon# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19133094Ssimon# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20133094Ssimon# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21133094Ssimon# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22133094Ssimon# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23133094Ssimon# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24133094Ssimon# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25133094Ssimon# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26133094Ssimon# SUCH DAMAGE.
27133094Ssimon#
28133094Ssimon# $FreeBSD$
29133094Ssimon#
30133094Ssimon
31133094Ssimon# Parse the list of supported hardware out of section 4 manual pages
32133094Ssimon# and output it on stdout as SGML/DocBook entities.
33133094Ssimon
34133094Ssimon# The script will look for the following line in the manual page:
35133094Ssimon# .Sh HARDWARE
36133094Ssimon# and make an entity of the content until the line containing:
37133094Ssimon# .Sh
38133094Ssimon#
39133094Ssimon# For Lists only the first line will be printed.  If there are
40133094Ssimon# arguments to the .It command, only the argument will be printed.
41133094Ssimon
42133094Ssimon# Usage:
43166778Sbmah# man2hwnotes.pl [-cl] [-d 0-6] [-a <archlist file>] [-o <outputfile>]
44138845Ssimon#                <manualpage> [<manualpage> ...]
45133094Ssimon
46133094Ssimonuse strict;
47133094Ssimonuse Getopt::Std;
48133094Ssimonuse Digest::MD5 qw(md5_hex);
49133094Ssimon
50133094Ssimon# Section from manual page to extract
51133094Ssimonmy $hwlist_sect = "HARDWARE";
52133094Ssimon
53133094Ssimon# Override default archtecture list for some devices:
54133094Ssimonmy $archlist_file = "dev.archlist.txt";
55133094Ssimonmy %archlist;
56133094Ssimon
57133094Ssimon# Globals
58166778Sbmahmy $compat_mode = 0; # Enable compat for old Hardware Notes style
59133094Ssimonmy $debuglevel = 0;
60133094Ssimonmy $only_list_out = 0; # Should only lists be generated in the output?
61133094Ssimonmy @out_lines; # Single lines
62133094Ssimonmy @out_dev;   # Device entities
63133094Ssimon
64133094Ssimon# Getopt
65133094Ssimonmy %options = ();
66166778Sbmahif (!getopts("a:cd:lo:",\%options)) {
67133322Shrs    die("$!: Invalid command line arguments in ", __LINE__, "\n");
68133094Ssimon}
69133094Ssimon
70166778Sbmahif (defined($options{c})) {
71166778Sbmah    $compat_mode = 1;
72166778Sbmah}
73133094Ssimonif (defined($options{d})) {
74133094Ssimon    $debuglevel = $options{d};
75133094Ssimon}
76133094Ssimonif (defined($options{a})) {
77133094Ssimon    $archlist_file = $options{a};
78133094Ssimon}
79133094Ssimonif (defined($options{l})) {
80133094Ssimon    $only_list_out = 1;
81133094Ssimon}
82133094Ssimon
83133322Shrsmy $outputfile = $options{o};
84133322Shrs
85133094Ssimonif ($debuglevel > 0) {
86133094Ssimon    # Don't do output buffering in debug mode.
87133094Ssimon    $| = 1;
88133094Ssimon}
89133094Ssimon
90133094Ssimonload_archlist($archlist_file);
91133094Ssimon
92133322Shrsif (defined($outputfile)) {
93133322Shrs    open(OLDOUT, ">&STDOUT") || die("$!: Could not open STDOUT in ", __LINE__, ".\n");
94133322Shrs    open(STDOUT, ">$outputfile") || die("$!: Could not open $outputfile in ", __LINE__, ".\n");
95133322Shrs}
96133322Shrs
97133322Shrsprint <<EOT;
98133322Shrs<!--
99133322Shrs These are automatically generated device lists for FreeBSD hardware notes.
100133322Shrs-->
101133322ShrsEOT
102133322Shrs
103133094Ssimonif ($only_list_out) {
104133094Ssimon    # Print the default device preamble entities
105240515Sgjb    print "<!ENTITY hwlist.preamble.pre 'The'>\n";
106240515Sgjb    print "<!ENTITY hwlist.preamble.post 'driver supports:'>\n";
107133094Ssimon}
108133094Ssimon
109133094Ssimonforeach my $page (@ARGV) {
110133322Shrs    if ($page !~ m/\.4$/) {
111133322Shrs        dlog(2, "Skipped $page (not *.4)");
112133322Shrs        next;
113133322Shrs    }
114133094Ssimon    dlog(2, "Parsing $page");
115133094Ssimon    parse($page);
116133094Ssimon
117133322Shrs    if (@out_lines) {
118133322Shrs        print join("\n", @out_lines), "\n";
119133322Shrs    }
120133322Shrs    if (@out_dev) {
121133322Shrs        print join("\n", @out_dev), "\n";
122133322Shrs    }
123133094Ssimon
124133094Ssimon    @out_lines = ();
125133094Ssimon    @out_dev = ();
126133094Ssimon}
127133094Ssimon
128133322Shrsif (defined($outputfile)) {
129133322Shrs    open(STDOUT, ">&OLDOUT") || die("$!: Could not open STDOUT in ", __LINE__, ".\n");
130133322Shrs    close(OLDOUT) || die("$!: Could not close OLDOUT in ", __LINE__, ".\n");
131133322Shrs}
132133322Shrs
133133322Shrssub normalize (@) {
134133322Shrs    my @lines = @_;
135133322Shrs
136133322Shrs    foreach my $l (@lines) {
137133780Ssimon        $l =~ s/\\&//g;
138133322Shrs        $l =~ s:([\x21-\x2f\x5b-\x60\x7b-\x7f]):sprintf("&\#\%d;", ord($1)):eg;
139134770Ssimon        # Make sure ampersand is encoded as &amp; since jade seems to
140134770Ssimon        # be confused when it is encoded as &#38; inside an entity.
141134770Ssimon        $l =~ s/&#38;/&amp;/g;
142133322Shrs    }
143133322Shrs    return (wantarray) ? @lines : join "", @lines;
144133322Shrs}
145133322Shrs
146133094Ssimonsub parse {
147133094Ssimon    my ($manpage) = @_;
148133094Ssimon
149133094Ssimon    my $cur_mansection;
150133094Ssimon    my $found_hwlist = 0;
151133094Ssimon    my %mdocvars;
152133094Ssimon    $mdocvars{isin_hwlist} = 0;
153133094Ssimon    $mdocvars{isin_list} = 0;
154183621Ssimon    $mdocvars{first_para} = 1;
155133094Ssimon    $mdocvars{parabuf} = "";
156133094Ssimon    $mdocvars{listtype} = "";
157134397Ssimon    $mdocvars{it_nr} = 0;
158133094Ssimon
159133322Shrs    open(MANPAGE, "$manpage") || die("$!: Could not open $manpage in ", __LINE__, ".\n");
160133094Ssimon    while(<MANPAGE>) {
161133094Ssimon	chomp;
162133094Ssimon	my $line = $_;
163133094Ssimon
164133094Ssimon	dlog(5, "Read '$line'");
165133094Ssimon
166133094Ssimon	# Find commands
167133094Ssimon	if (s/^\.(.*)$/$1/) {
168134397Ssimon	    my $cmd = $1;
169134397Ssimon
170133094Ssimon	    # Detect, and ignore, comment lines
171133094Ssimon	    if (s/^\\"(.*)$/$1/) {
172133094Ssimon		next;
173133094Ssimon	    }
174133094Ssimon
175134397Ssimon	    $cmd =~ s/^([^ ]+).*$/$1/;
176134397Ssimon
177133094Ssimon	    if (/^Nm "?(\w+)"?/ && !defined($mdocvars{Nm})) {
178133094Ssimon		dlog(3, "Setting Nm to $1");
179133094Ssimon		$mdocvars{Nm} = $1;
180134342Shrs		# "_" cannot be used for an entity name.
181138845Ssimon		$mdocvars{EntNm} = $1;
182138845Ssimon		$mdocvars{EntNm} =~ s,_,.,g;
183133094Ssimon
184133094Ssimon	    } elsif (/^Nm$/) {
185133094Ssimon		if (defined($mdocvars{Nm}) && $mdocvars{Nm} ne "") {
186138845Ssimon		    parabuf_addline(\%mdocvars, "&man.".$mdocvars{EntNm}.".$cur_mansection;");
187133094Ssimon		} else {
188133094Ssimon		    dlog(2, "Warning: Bad Nm call in $manpage");
189133094Ssimon		}
190133094Ssimon
191133094Ssimon	    } elsif (/^Sh (.+)$/) {
192133094Ssimon		dlog(4, "Setting section to $1");
193133094Ssimon		my $cur_section = $1;
194133094Ssimon
195133094Ssimon		flush_out(\%mdocvars);
196133094Ssimon
197133094Ssimon		if ($cur_section =~ /^${hwlist_sect}$/) {
198133094Ssimon		    dlog(2, "Found the device section ${hwlist_sect}");
199133094Ssimon		    $mdocvars{isin_hwlist} = 1;
200133094Ssimon		    $found_hwlist = 1;
201133094Ssimon		    add_sgmltag(\%mdocvars, "<!ENTITY hwlist.".$mdocvars{cur_manname}." '");
202133094Ssimon		    if ($only_list_out) {
203257814Sgjb			add_sgmltag("<para xmlns=\"http://docbook.org/ns/docbook\">&hwlist.preamble.pre; " .
204138845Ssimon				    "&man.".$mdocvars{EntNm}.".$cur_mansection; " .
205133094Ssimon				    "&hwlist.preamble.post;</para>");
206133094Ssimon		    }
207133094Ssimon		} elsif ($mdocvars{isin_hwlist}) {
208133094Ssimon		    dlog(2, "Found a HWLIST STOP key!");
209133094Ssimon		    add_sgmltag(\%mdocvars, "'>");
210133094Ssimon		    $mdocvars{isin_hwlist} = 0;
211133094Ssimon		}
212160802Ssimon		if ($mdocvars{isin_list}) {
213160802Ssimon		    dlog(1, "Warning: Still in list, but just entered new " .
214160802Ssimon			 "section.  This is probably due to missing .El; " .
215160802Ssimon			 "check manual page for errors.");
216160802Ssimon		    # If we try to recover from this we will probably
217160802Ssimon		    # just end with bad SGML output and it really
218160802Ssimon		    # should be fixed in the manual page so we don't
219160802Ssimon		    # even try to "fix" this.
220160802Ssimon		}
221133094Ssimon
222160802Ssimon
223133094Ssimon	    } elsif (/^Dt ([^ ]+) ([^ ]+)/) {
224133094Ssimon		dlog(4, "Setting mansection to $2");
225133094Ssimon		$mdocvars{cur_manname} = lc($1);
226133094Ssimon		$cur_mansection = $2;
227133094Ssimon
228134342Shrs		# "_" cannot be used for an entity name.
229134342Shrs		$mdocvars{cur_manname} =~ s,_,.,g;
230134342Shrs
231133094Ssimon	    } elsif (/^It ?(.*)$/) {
232134318Ssimon		my $txt = $1;
233134318Ssimon
234134397Ssimon		$mdocvars{it_nr}++;
235134397Ssimon
236133094Ssimon		# Flush last item
237133094Ssimon		if ($mdocvars{parabuf} ne "") {
238133094Ssimon		    add_listitem(\%mdocvars);
239133094Ssimon		}
240134397Ssimon
241147767Ssimon		# Remove quotes, if any.
242147767Ssimon		$txt =~ s/"(.*)"/$1/;
243147767Ssimon
244134318Ssimon		if ($mdocvars{listtype} eq "column") {
245134397Ssimon		    # Ignore first item when it is likely to be a
246134397Ssimon		    # header.
247134397Ssimon		    if ($mdocvars{it_nr} == 1 && $txt =~ m/^(Em|Sy) /) {
248134397Ssimon			dlog(2, "Skipping header line in column list");
249134397Ssimon			next;
250134397Ssimon		    }
251134397Ssimon		    # Only extract the first column.
252134318Ssimon		    $txt =~ s/ Ta /\t/g;
253134318Ssimon		    $txt =~ s/([^\t]+)\t.*/$1/;
254134318Ssimon		}
255197747Ssimon
256197747Ssimon		# Remove Li commands
257197747Ssimon		$txt =~ s/^Li //g;
258197747Ssimon
259134318Ssimon		parabuf_addline(\%mdocvars, normalize($txt));
260133094Ssimon	    } elsif (/^Bl/) {
261133094Ssimon		$mdocvars{isin_list} = 1;
262133094Ssimon		flush_out(\%mdocvars);
263257814Sgjb		add_sgmltag(\%mdocvars, "<itemizedlist xmlns=\"http://docbook.org/ns/docbook\">");
264133094Ssimon
265133094Ssimon		if (/-tag/) {
266133094Ssimon		    $mdocvars{listtype} = "tag";
267133094Ssimon		    # YACK! Hack for ata(4)
268133094Ssimon		    if ($mdocvars{Nm} eq "ata") {
269133094Ssimon			$mdocvars{listtype} = "tagHACK";
270133094Ssimon		    }
271133094Ssimon		} elsif (/-bullet/) {
272133094Ssimon		    $mdocvars{listtype} = "bullet";
273134318Ssimon		} elsif (/-column/) {
274134318Ssimon		    $mdocvars{listtype} = "column";
275133094Ssimon		} else {
276133094Ssimon		    $mdocvars{listtype} = "unknown";
277133094Ssimon		}
278133094Ssimon		dlog(2, "Listtype set to $mdocvars{listtype}");
279133094Ssimon	    } elsif (/^El/) {
280133094Ssimon		if ($mdocvars{parabuf} ne "") {
281133094Ssimon		    add_listitem(\%mdocvars);
282133094Ssimon		}
283133094Ssimon
284133094Ssimon		add_sgmltag(\%mdocvars, "</itemizedlist>");
285133094Ssimon		$mdocvars{isin_list} = 0;
286133094Ssimon	    } elsif (/^Tn (.+)$/) {
287133094Ssimon		# For now we print TradeName text as regular text.
288134397Ssimon		my ($txt, $punct_str) = split_punct_chars($1);
289133094Ssimon
290134397Ssimon		parabuf_addline(\%mdocvars, normalize($txt . $punct_str));
291134408Ssimon	    } elsif (/^Xr ([^ ]+) (.+)$/) {
292134408Ssimon		my ($xr_sect, $punct_str) = split_punct_chars($2);
293134408Ssimon		my $txt;
294134408Ssimon
295133094Ssimon		# We need to check if the manual page exist to avoid
296133094Ssimon		# breaking the doc build just because of a broken
297133094Ssimon		# reference.
298134408Ssimon		#$txt = "&man.$1.$xr_sect;$punct_str";
299134408Ssimon		$txt = "$1($xr_sect)$punct_str";
300134408Ssimon		parabuf_addline(\%mdocvars, normalize($txt));
301133334Ssimon	    } elsif (/^Dq (.+)$/) {
302134397Ssimon		my ($txt, $punct_str) = split_punct_chars($1);
303133334Ssimon
304134397Ssimon		parabuf_addline(\%mdocvars,
305257814Sgjb				normalize("<quote xmlns=\"http://docbook.org/ns/docbook\">$txt</quote>$punct_str"));
306133780Ssimon	    } elsif (/^Sx (.+)$/) {
307133780Ssimon		if ($mdocvars{isin_hwlist}) {
308133780Ssimon		    dlog(1, "Warning: Reference to another section in the " .
309133780Ssimon			 "$hwlist_sect section in " . $mdocvars{Nm} .
310133780Ssimon			 "(${cur_mansection})");
311133780Ssimon		}
312133780Ssimon		parabuf_addline(\%mdocvars, normalize($1));
313134397Ssimon	    } elsif (/^Pa (.+)$/) {
314134397Ssimon		my ($txt, $punct_str) = split_punct_chars($1);
315134397Ssimon
316134397Ssimon		$txt = make_ulink($txt) . $punct_str;
317134397Ssimon		parabuf_addline(\%mdocvars, normalize($txt));
318183621Ssimon	    } elsif (/^Pp/) {
319183621Ssimon		dlog(3, "Got Pp command - forcing new para");
320183621Ssimon		flush_out(\%mdocvars);
321183621Ssimon	    } elsif (/^Fx (.+)/) {
322183621Ssimon		dlog(3, "Got Fx command");
323183621Ssimon		parabuf_addline(\%mdocvars, "FreeBSD $1");
324183621Ssimon	    } elsif (/^Fx/) {
325183621Ssimon		dlog(3, "Got Fx command");
326183621Ssimon		parabuf_addline(\%mdocvars, "FreeBSD");
327230332Sjhb	    } elsif (/^Em (.+)$/) {
328230332Sjhb		my ($txt, $punct_str) = split_punct_chars($1);
329230332Sjhb
330230332Sjhb		parabuf_addline(\%mdocvars,
331257814Sgjb				normalize("<emphasis xmlns=\"http://docbook.org/ns/docbook\">$txt</emphasis>$punct_str"));
332134397Ssimon	    } else {
333134397Ssimon		# Ignore all other commands.
334134397Ssimon		dlog(3, "Ignoring unknown command $cmd");
335133094Ssimon	    }
336133094Ssimon	} else {
337133094Ssimon	    # This is then regular text
338133322Shrs	    parabuf_addline(\%mdocvars, normalize($_));
339133094Ssimon	}
340133094Ssimon    }
341133322Shrs    close(MANPAGE) || die("$!: Could not close $manpage in ", __LINE__, ".\n");
342133094Ssimon    if (! $found_hwlist) {
343133780Ssimon	dlog(2, "Hardware list not found in $manpage");
344133094Ssimon    }
345133094Ssimon}
346133094Ssimon
347133094Ssimonsub dlog {
348133094Ssimon    my ($level, $txt) = @_;
349133094Ssimon
350133094Ssimon    if ($level <= $debuglevel) {
351133094Ssimon	print STDERR "$level: $txt\n";
352133094Ssimon    }
353133094Ssimon}
354133094Ssimon
355133094Ssimon# Output a SGML tag.
356133094Ssimonsub add_sgmltag {
357133094Ssimon    my ($mdocvars, $txt) = (@_);
358133094Ssimon
359133094Ssimon    # We only care about the HW list for now.
360133094Ssimon    if (${$mdocvars}{isin_hwlist}) {
361133094Ssimon	push(@out_dev, $txt);
362133094Ssimon    }
363133094Ssimon}
364133094Ssimon
365133094Ssimon# Add a text entity, and return the used entity name.
366133094Ssimonsub add_txt_ent {
367133094Ssimon    my ($itemtxt) = (@_);
368133094Ssimon    my ($entity_name);
369133094Ssimon
370133094Ssimon    # Convert mdoc(7) minus
371133094Ssimon    $itemtxt =~ s/\\-/-/g;
372133094Ssimon
373133094Ssimon    $itemtxt =~ s/'/&lsquo;/g;
374133094Ssimon
375133094Ssimon    $entity_name = "hwlist." . md5_hex($itemtxt);
376133094Ssimon    dlog(4, "Adding '$itemtxt' as entity $entity_name");
377133094Ssimon    push(@out_lines, "<!ENTITY $entity_name '$itemtxt'>");
378133094Ssimon
379133094Ssimon    return ($entity_name);
380133094Ssimon}
381133094Ssimonsub flush_out {
382133094Ssimon    my ($mdocvars) = (@_);
383133094Ssimon    my ($entity_name, $out);
384133094Ssimon    my $para_arch = "";
385133094Ssimon
386133094Ssimon    if (!${$mdocvars}{isin_hwlist} || ${$mdocvars}{parabuf} eq "") {
387133094Ssimon	return;
388133094Ssimon    }
389133094Ssimon
390133094Ssimon    $entity_name = add_txt_ent(${$mdocvars}{parabuf});
391133094Ssimon    ${$mdocvars}{parabuf} = "";
392133094Ssimon    if(defined($archlist{${$mdocvars}{Nm}})) {
393166778Sbmah	if ($compat_mode) {
394166778Sbmah	    $para_arch = ' arch="' . $archlist{${$mdocvars}{Nm}} . '"';
395166778Sbmah	} else {
396166778Sbmah	    $para_arch = '[' . $archlist{${$mdocvars}{Nm}} . '] ';
397166778Sbmah	}
398133094Ssimon    }
399166778Sbmah    if ($compat_mode) {
400257814Sgjb	$out = "<para xmlns=\"http://docbook.org/ns/docbook\"".$para_arch.">&".$entity_name.";</para>";
401166778Sbmah    } else {
402183621Ssimon	if (${$mdocvars}{first_para}) {
403257814Sgjb	    $out = "<para xmlns=\"http://docbook.org/ns/docbook\">".$para_arch."&".$entity_name.";</para>";
404183621Ssimon	} else {
405257814Sgjb	    $out = "<para xmlns=\"http://docbook.org/ns/docbook\">&".$entity_name.";</para>";
406183621Ssimon	}
407183621Ssimon	${$mdocvars}{first_para} = 0;
408166778Sbmah    }
409133094Ssimon
410133094Ssimon    dlog(4, "Flushing parabuf");
411133094Ssimon    add_sgmltag($mdocvars, $out);
412133094Ssimon}
413133094Ssimon
414133094Ssimon# Add a new list item from the "parabuf".
415133094Ssimonsub add_listitem {
416133094Ssimon    my ($mdocvars) = (@_);
417133094Ssimon    my ($listitem, $entity_name);
418133094Ssimon    my $para_arch = "";
419133094Ssimon
420133094Ssimon    $entity_name = add_txt_ent(${$mdocvars}{parabuf});
421133094Ssimon    ${$mdocvars}{parabuf} = "";
422133094Ssimon
423166778Sbmah    if ($compat_mode) {
424166778Sbmah	if(defined($archlist{${$mdocvars}{Nm}})) {
425166778Sbmah	    $para_arch = ' arch="' . $archlist{${$mdocvars}{Nm}} . '"';
426166778Sbmah	}
427133094Ssimon    }
428133094Ssimon    $listitem = "<listitem><para".$para_arch.">&".$entity_name.";</para></listitem>";
429133094Ssimon    dlog(4, "Adding '$listitem' to out_dev");
430133094Ssimon    push(@out_dev, $listitem);
431133094Ssimon
432133094Ssimon}
433133094Ssimon
434133094Ssimon# Add a line to the "paragraph buffer"
435133094Ssimonsub parabuf_addline {
436133094Ssimon    my $mdocvars = shift;
437133094Ssimon    my ($txt) = (@_);
438133094Ssimon
439160802Ssimon    dlog(5, "Now in parabuf_addline for '$txt'");
440133094Ssimon
441133094Ssimon    # We only care about the HW list for now.
442133094Ssimon    if (!${$mdocvars}{isin_hwlist}) {
443160802Ssimon	dlog(6, "Exiting parabuf_addline due to: !\${\$mdocvars}{isin_hwlist}");
444133094Ssimon	return;
445133094Ssimon    }
446133094Ssimon    if ($txt eq "") {
447160802Ssimon	dlog(6, "Exiting parabuf_addline due to: \$txt eq \"\"");
448133094Ssimon	return;
449133094Ssimon    }
450133094Ssimon
451133094Ssimon    if ($only_list_out && !${$mdocvars}{isin_list}) {
452160802Ssimon	dlog(6, "Exiting parabuf_addline due to: ".
453160802Ssimon	     "\$only_list_out && !\${\$mdocvars}{isin_list}");
454133094Ssimon	return;
455133094Ssimon    }
456133094Ssimon
457133094Ssimon    # We only add the first line for "tag" lists
458133094Ssimon    if (${$mdocvars}{parabuf} ne "" && ${$mdocvars}{isin_list} &&
459133094Ssimon	${$mdocvars}{listtype} eq "tag") {
460160802Ssimon	dlog(6, "Exiting parabuf_addline due to: ".
461160802Ssimon	     "\${\$mdocvars}{parabuf} ne \"\" && \${\$mdocvars}{isin_list} && ".
462160802Ssimon	     "\${\$mdocvars}{listtype} eq \"tag\"");
463133094Ssimon	return;
464133094Ssimon    }
465133094Ssimon
466133094Ssimon    if (${$mdocvars}{parabuf} ne "") {
467133094Ssimon	${$mdocvars}{parabuf} .= " ";
468133094Ssimon    }
469133094Ssimon
470133094Ssimon    dlog(4, "Adding '$txt' to parabuf");
471133094Ssimon
472133094Ssimon    ${$mdocvars}{parabuf} .= $txt;
473133094Ssimon}
474133094Ssimon
475133094Ssimonsub load_archlist {
476133094Ssimon    my ($file) = (@_);
477133094Ssimon
478133094Ssimon    my $lineno = 0;
479133094Ssimon
480133094Ssimon    dlog(2, "Parsing archlist $file");
481133094Ssimon
482133322Shrs    open(FILE, "$file") || die("$!: Could not open archlist $file in ", __LINE__, ".\n");
483133094Ssimon    while(<FILE>) {
484133094Ssimon	chomp;
485133094Ssimon	$lineno++;
486133094Ssimon
487133094Ssimon	if (/^#/ || $_ eq "") {
488133094Ssimon	    next;
489133094Ssimon	}
490133094Ssimon
491133094Ssimon	if (/(\w+)\t([\w,]+)/) {
492133094Ssimon	    dlog(4, "For driver $1 setting arch to $2");
493133094Ssimon	    $archlist{$1} = $2;
494133094Ssimon	} else {
495135080Ssimon	    dlog(1, "Warning: Could not parse archlist line $lineno");
496133094Ssimon	}
497133094Ssimon    }
498133094Ssimon
499133094Ssimon    close(FILE);
500133094Ssimon}
501133334Ssimon
502133334Ssimon# Check if a character is a mdoc(7) punctuation character.
503133334Ssimonsub is_punct_char {
504133334Ssimon    my ($str) = (@_);
505133334Ssimon
506134397Ssimon    return (length($str) == 1 && $str =~ /[\.,:;()\[\]\?!]/);
507133334Ssimon}
508134397Ssimon
509134397Ssimon# Split out the punctuation characters of a mdoc(7) line.
510134397Ssimonsub split_punct_chars {
511134397Ssimon    my ($str) = (@_);
512134397Ssimon    my (@stritems, $stritem, $punct_str);
513134397Ssimon
514134397Ssimon    $punct_str = "";
515134397Ssimon    @stritems = split(/ /, $str);
516134397Ssimon
517134397Ssimon    while (defined($stritem = $stritems[$#stritems]) &&
518134397Ssimon	   is_punct_char($stritem)) {
519134397Ssimon	$punct_str = $stritem . $punct_str;
520134397Ssimon	pop(@stritems);
521134397Ssimon    }
522134397Ssimon
523134397Ssimon    return (join(' ', @stritems), $punct_str);
524134397Ssimon}
525134397Ssimon
526134397Ssimon# Create a ulink, if the string contains an URL.
527134397Ssimonsub make_ulink {
528134397Ssimon    my ($str) = (@_);
529134397Ssimon
530257814Sgjb    $str =~ s,(http://[^ ]+),<link xmlns=\"http://docbook.org/ns/docbook\" xlink:href="$1"></link>,;
531134397Ssimon
532134397Ssimon    return $str;
533134397Ssimon}
534