1#!/usr/bin/perl
2###
3### Quick usage:  mdoc2man.pl < mdoc_manpage.8 > man_manpage.8
4###
5###
6###  Copyright (c) 2001 University of Illinois Board of Trustees
7###  Copyright (c) 2001 Mark D. Roth
8###  All rights reserved.
9###
10###  Redistribution and use in source and binary forms, with or without
11###  modification, are permitted provided that the following conditions
12###  are met:
13###  1. Redistributions of source code must retain the above copyright
14###     notice, this list of conditions and the following disclaimer.
15###  2. Redistributions in binary form must reproduce the above copyright
16###     notice, this list of conditions and the following disclaimer in the
17###     documentation and/or other materials provided with the distribution.
18###  3. All advertising materials mentioning features or use of this software
19###     must display the following acknowledgement:
20###     This product includes software developed by the University of
21###     Illinois at Urbana, and their contributors.
22###  4. The University nor the names of their
23###     contributors may be used to endorse or promote products derived from
24###     this software without specific prior written permission.
25###
26###  THIS SOFTWARE IS PROVIDED BY THE TRUSTEES AND CONTRIBUTORS ``AS IS'' AND
27###  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28###  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29###  ARE DISCLAIMED.  IN NO EVENT SHALL THE TRUSTEES OR CONTRIBUTORS BE LIABLE
30###  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31###  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32###  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33###  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34###  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35###  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36###  SUCH DAMAGE.
37###
38
39use strict;
40
41my ($name, $date, $id);
42my ($line);
43my ($optlist, $nospace, $enum, $synopsis);
44
45
46$optlist = 0;		### 1 = bullet, 2 = enum, 3 = tag
47$nospace = 0;
48$synopsis = 0;
49
50while ($line = <STDIN>)
51{
52	if ($line !~ /^\./)
53	{
54		print $line;
55		next;
56	}
57
58	$line =~ s/^\.//;
59
60	next
61		if ($line =~ m/\\"/);
62
63	$line = ParseMacro($line);
64	print($line)
65		if (defined $line);
66}
67
68
69
70sub ParseMacro # ($line)
71{
72	my ($line) = @_;
73	my (@words, $retval, $option, $parens, $arg);
74
75	@words = split(/\s+/, $line);
76	$retval = '';
77	$option = 0;
78	$parens = 0;
79	$arg = 0;
80
81#	print('@words = ', scalar(@words), ': ', join(' ', @words), "\n");
82
83	while ($_ = shift @words)
84	{
85#		print "WORD: $_\n";
86
87		next
88			if (/^(Li|Pf|X[oc])$/);
89
90		if (/^Ns/)
91		{
92			$nospace = 1
93				if (! $nospace);
94			$retval =~ s/ $//;
95			next;
96		}
97
98		if (/^No/)
99		{
100			$retval =~ s/ $//;
101			$retval .= shift @words;
102			next;
103		}
104
105		if (/^Dq$/) {
106			$retval .= '``' . (shift @words) . '\'\'';
107			$nospace = 1
108				if (! $nospace && $words[0] =~ m/^[\.,]/);
109			next;
110		}
111
112		if (/^(Sq|Ql)$/) {
113			$retval .= '`' . (shift @words) . '\'';
114			$nospace = 1
115				if (! $nospace && $words[0] =~ m/^[\.,]/);
116			next;
117		}
118
119		$retval .= ' '
120			if (! $nospace && $retval ne '' && $retval !~ m/[\n ]$/);
121		$nospace = 0
122			if ($nospace == 1);
123
124		if (/^Dd$/) {
125			$date = join(' ', @words);
126			return undef;
127		}
128
129		if (/^Dt$/) {
130			$id = join(' ', @words);
131			return undef;
132		}
133
134		if (/^Os$/) {
135			$retval .= '.TH '
136				. $id
137				. " \"$date\" \""
138				. join(' ', @words)
139				. "\"";
140			last;
141		}
142
143		if (/^Sh$/) {
144			$retval .= '.SH';
145			if ($words[0] eq 'SYNOPSIS')
146			{
147				$synopsis = 1;
148			}
149			else
150			{
151				$synopsis = 0;
152			}
153			next;
154		}
155
156		if (/^Xr$/) {
157			$retval .= '\\fB' . (shift @words) .
158				'\\fR(' . (shift @words) . ')'
159				. (shift @words);
160			last;
161		}
162
163		if (/^Nm$/) {
164			$name = shift @words
165				if (@words > 0);
166			$retval .= ".br\n"
167				if ($synopsis);
168			$retval .= "\\fB$name\\fR";
169			$nospace = 1
170				if (! $nospace && $words[0] =~ m/^[\.,]/);
171			next;
172		}
173
174		if (/^Nd$/) {
175			$retval .= '\\-';
176			next;
177		}
178
179		if (/^Fl$/) {
180			$retval .= '\\fB\\-' . (shift @words) . '\\fR';
181			$nospace = 1
182				if (! $nospace && $words[0] =~ m/^[\.,]/);
183			next;
184		}
185
186		if (/^Ar$/) {
187			$retval .= '\\fI';
188			if (! defined $words[0])
189			{
190				$retval .= 'file ...\\fR';
191			}
192			$arg = 1;
193			$nospace = 1
194				if (! $nospace);
195			next;
196		}
197
198		if (/^Cm$/) {
199			$retval .= '\\fB' . (shift @words) . '\\fR';
200			next;
201		}
202
203		if (/^Op$/) {
204			$option = 1;
205			$nospace = 1
206				if (! $nospace);
207			$retval .= '[';
208			next;
209		}
210
211		if (/^Oo$/) {
212			$retval .= "[\\c\n";
213			next;
214		}
215
216		if (/^Oc$/) {
217			$retval .= ']';
218			next;
219		}
220
221		if (/^Pp$/) {
222			if ($optlist) {
223				$retval .= "\n";
224			} else {
225				$retval .= '.LP';
226			}
227			next;
228		}
229
230		if (/^Ss$/) {
231			$retval .= '.SS';
232			next;
233		}
234
235		if (/^Pa$/ && ! $option) {
236			$retval .= '\\fI';
237			$retval .= '\\&'
238				if ($words[0] =~ m/^\./);
239			$retval .= (shift @words) . '\\fR';
240			$nospace = 1
241				if (! $nospace && $words[0] =~ m/^[\.,]/);
242			next;
243		}
244
245		if (/^Dv$/) {
246			$retval .= '.BR';
247			next;
248		}
249
250		if (/^(Em|Ev)$/) {
251			$retval .= '.IR';
252			next;
253		}
254
255		if (/^Pq$/) {
256			$retval .= '(';
257			$nospace = 1;
258			$parens = 1;
259			next;
260		}
261
262		if (/^(S[xy])$/) {
263			$retval .= '.B ' . join(' ', @words);
264			last;
265		}
266
267		if (/^Ic$/)
268		{
269			$retval .= '\\fB';
270			while (defined $words[0]
271				&& $words[0] !~ m/^[\.,]/)
272			{
273				$retval .= shift @words;
274				$retval .= ' '
275					if (! $nospace);
276			}
277			$retval =~ s/ $//;
278			$retval .= '\\fR';
279			$retval .= shift @words
280				if (defined $words[0]);
281			last;
282		}
283
284		if (/^Bl$/) {
285			if ($words[0] eq '-bullet') {
286				$optlist = 1;
287			} elsif ($words[0] eq '-enum') {
288				$optlist = 2;
289				$enum = 0;
290			} elsif ($words[0] eq '-tag') {
291				$optlist = 3;
292			}
293			last;
294		}
295
296		if (/^El$/) {
297			$optlist = 0;
298			next;
299		}
300
301		if ($optlist && /^It$/) {
302			if ($optlist == 1) {
303				# bullets
304				$retval .= '.IP \\(bu';
305				next;
306			}
307
308			if ($optlist == 2) {
309				# enum
310				$retval .= '.IP ' . (++$enum) . '.';
311				next;
312			}
313
314			if ($optlist == 3) {
315				# tags
316				$retval .= ".TP\n";
317				if ($words[0] =~ m/^(Pa|Ev)$/)
318				{
319					shift @words;
320					$retval .= '.B';
321				}
322				next;
323			}
324
325			next;
326		}
327
328		if (/^Sm$/) {
329			if ($words[0] eq 'off') {
330				$nospace = 2;
331			} elsif ($words[0] eq 'on') {
332				$retval .= "\n";
333				$nospace = 0;
334			}
335			shift @words;
336			next;
337		}
338
339		$retval .= "$_";
340	}
341
342	return undef
343		if ($retval eq '.');
344
345	$retval =~ s/^\.([^a-zA-Z])/$1/;
346	$retval =~ s/ $//;
347
348	$retval .= ')'
349		if ($parens == 1);
350
351	$retval .= ']'
352		if ($option == 1);
353
354	$retval .= '\\fR'
355		if ($arg);
356
357	$retval .= '\\c'
358		if ($nospace && $retval ne '' && $retval !~ m/\n$/);
359
360	$retval .= "\n"
361		if ($retval ne '' && $retval !~ m/\n$/);
362
363	return $retval;
364}
365