1124208Sdes#!/usr/bin/awk
2124208Sdes#
3204861Sdes# $Id: mdoc2man.awk,v 1.9 2009/10/24 00:52:42 dtucker Exp $
4180744Sdes#
5124208Sdes# Version history:
6180744Sdes#  v4+ Adapted for OpenSSH Portable (see cvs Id and history)
7124208Sdes#  v3, I put the program under a proper license
8124208Sdes#      Dan Nelson <dnelson@allantgroup.com> added .An, .Aq and fixed a typo
9124208Sdes#  v2, fixed to work on GNU awk --posix and MacOS X
10124208Sdes#  v1, first attempt, didn't work on MacOS X
11124208Sdes#
12124208Sdes# Copyright (c) 2003 Peter Stuge <stuge-mdoc2man@cdy.org>
13124208Sdes#
14124208Sdes# Permission to use, copy, modify, and distribute this software for any
15124208Sdes# purpose with or without fee is hereby granted, provided that the above
16124208Sdes# copyright notice and this permission notice appear in all copies.
17124208Sdes#
18124208Sdes# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
19124208Sdes# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
20124208Sdes# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
21124208Sdes# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22124208Sdes# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23124208Sdes# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24124208Sdes# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25124208Sdes
26124208Sdes
27124208SdesBEGIN {
28124208Sdes  optlist=0
29124208Sdes  oldoptlist=0
30124208Sdes  nospace=0
31124208Sdes  synopsis=0
32124208Sdes  reference=0
33124208Sdes  block=0
34124208Sdes  ext=0
35124208Sdes  extopt=0
36124208Sdes  literal=0
37124208Sdes  prenl=0
38137015Sdes  breakw=0
39124208Sdes  line=""
40124208Sdes}
41124208Sdes
42124208Sdesfunction wtail() {
43124208Sdes  retval=""
44124208Sdes  while(w<nwords) {
45124208Sdes    if(length(retval))
46124208Sdes      retval=retval OFS
47124208Sdes    retval=retval words[++w]
48124208Sdes  }
49124208Sdes  return retval
50124208Sdes}
51124208Sdes
52124208Sdesfunction add(str) {
53124208Sdes  for(;prenl;prenl--)
54124208Sdes    line=line "\n"
55124208Sdes  line=line str
56124208Sdes}
57124208Sdes
58124208Sdes! /^\./ {
59124208Sdes  for(;prenl;prenl--)
60124208Sdes    print ""
61124208Sdes  print
62124208Sdes  if(literal)
63124208Sdes    print ".br"
64124208Sdes  next
65124208Sdes}
66124208Sdes
67124208Sdes/^\.\\"/ { next }
68124208Sdes
69124208Sdes{
70124208Sdes  option=0
71124208Sdes  parens=0
72124208Sdes  angles=0
73124208Sdes  sub("^\\.","")
74124208Sdes  nwords=split($0,words)
75124208Sdes  for(w=1;w<=nwords;w++) {
76124208Sdes    skip=0
77124208Sdes    if(match(words[w],"^Li|Pf$")) {
78124208Sdes      skip=1
79124208Sdes    } else if(match(words[w],"^Xo$")) {
80124208Sdes      skip=1
81124208Sdes      ext=1
82124208Sdes      if(length(line)&&!(match(line," $")||prenl))
83126274Sdes	add(OFS)
84124208Sdes    } else if(match(words[w],"^Xc$")) {
85124208Sdes      skip=1
86124208Sdes      ext=0
87124208Sdes      if(!extopt)
88126274Sdes	prenl++
89124208Sdes      w=nwords
90124208Sdes    } else if(match(words[w],"^Bd$")) {
91124208Sdes      skip=1
92124208Sdes      if(match(words[w+1],"-literal")) {
93126274Sdes	literal=1
94126274Sdes	prenl++
95126274Sdes	w=nwords
96124208Sdes      }
97124208Sdes    } else if(match(words[w],"^Ed$")) {
98124208Sdes      skip=1
99124208Sdes      literal=0
100124208Sdes    } else if(match(words[w],"^Ns$")) {
101124208Sdes      skip=1
102124208Sdes      if(!nospace)
103126274Sdes	nospace=1
104124208Sdes      sub(" $","",line)
105124208Sdes    } else if(match(words[w],"^No$")) {
106124208Sdes      skip=1
107124208Sdes      sub(" $","",line)
108124208Sdes      add(words[++w])
109124208Sdes    } else if(match(words[w],"^Dq$")) {
110124208Sdes      skip=1
111124208Sdes      add("``")
112124208Sdes      add(words[++w])
113124208Sdes      while(w<nwords&&!match(words[w+1],"^[\\.,]"))
114126274Sdes	add(OFS words[++w])
115124208Sdes      add("''")
116124208Sdes      if(!nospace&&match(words[w+1],"^[\\.,]"))
117126274Sdes	nospace=1
118124208Sdes    } else if(match(words[w],"^Sq|Ql$")) {
119124208Sdes      skip=1
120124208Sdes      add("`" words[++w] "'")
121124208Sdes      if(!nospace&&match(words[w+1],"^[\\.,]"))
122126274Sdes	nospace=1
123124208Sdes    } else if(match(words[w],"^Oo$")) {
124124208Sdes      skip=1
125124208Sdes      extopt=1
126124208Sdes      if(!nospace)
127126274Sdes	nospace=1
128124208Sdes      add("[")
129124208Sdes    } else if(match(words[w],"^Oc$")) {
130124208Sdes      skip=1
131124208Sdes      extopt=0
132124208Sdes      add("]")
133124208Sdes    }
134124208Sdes    if(!skip) {
135124208Sdes      if(!nospace&&length(line)&&!(match(line," $")||prenl))
136126274Sdes	add(OFS)
137124208Sdes      if(nospace==1)
138126274Sdes	nospace=0
139124208Sdes    }
140124208Sdes    if(match(words[w],"^Dd$")) {
141180744Sdes      if(match(words[w+1],"^\\$Mdocdate:")) {
142180744Sdes        w++;
143180744Sdes        if(match(words[w+4],"^\\$$")) {
144180744Sdes          words[w+4] = ""
145180744Sdes        }
146180744Sdes      }
147124208Sdes      date=wtail()
148124208Sdes      next
149124208Sdes    } else if(match(words[w],"^Dt$")) {
150124208Sdes      id=wtail()
151124208Sdes      next
152204861Sdes    } else if(match(words[w],"^Ux$")) {
153204861Sdes      add("UNIX")
154204861Sdes      skip=1
155149749Sdes    } else if(match(words[w],"^Ox$")) {
156149749Sdes      add("OpenBSD")
157149749Sdes      skip=1
158124208Sdes    } else if(match(words[w],"^Os$")) {
159124208Sdes      add(".TH " id " \"" date "\" \"" wtail() "\"")
160124208Sdes    } else if(match(words[w],"^Sh$")) {
161124208Sdes      add(".SH")
162124208Sdes      synopsis=match(words[w+1],"SYNOPSIS")
163124208Sdes    } else if(match(words[w],"^Xr$")) {
164124208Sdes      add("\\fB" words[++w] "\\fP(" words[++w] ")" words[++w])
165124208Sdes    } else if(match(words[w],"^Rs$")) {
166124208Sdes      split("",refauthors)
167124208Sdes      nrefauthors=0
168124208Sdes      reftitle=""
169124208Sdes      refissue=""
170124208Sdes      refdate=""
171124208Sdes      refopt=""
172180744Sdes      refreport=""
173124208Sdes      reference=1
174124208Sdes      next
175124208Sdes    } else if(match(words[w],"^Re$")) {
176124208Sdes      prenl++
177124208Sdes      for(i=nrefauthors-1;i>0;i--) {
178126274Sdes	add(refauthors[i])
179126274Sdes	if(i>1)
180126274Sdes	  add(", ")
181124208Sdes      }
182124208Sdes      if(nrefauthors>1)
183126274Sdes	add(" and ")
184180744Sdes      if(nrefauthors>0)
185180744Sdes        add(refauthors[0] ", ")
186180744Sdes      add("\\fI" reftitle "\\fP")
187124208Sdes      if(length(refissue))
188126274Sdes	add(", " refissue)
189180744Sdes      if(length(refreport)) {
190180744Sdes	add(", " refreport)
191180744Sdes      }
192124208Sdes      if(length(refdate))
193126274Sdes	add(", " refdate)
194124208Sdes      if(length(refopt))
195126274Sdes	add(", " refopt)
196124208Sdes      add(".")
197124208Sdes      reference=0
198124208Sdes    } else if(reference) {
199124208Sdes      if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() }
200124208Sdes      if(match(words[w],"^%T$")) {
201126274Sdes	reftitle=wtail()
202126274Sdes	sub("^\"","",reftitle)
203126274Sdes	sub("\"$","",reftitle)
204124208Sdes      }
205124208Sdes      if(match(words[w],"^%N$")) { refissue=wtail() }
206124208Sdes      if(match(words[w],"^%D$")) { refdate=wtail() }
207124208Sdes      if(match(words[w],"^%O$")) { refopt=wtail() }
208180744Sdes      if(match(words[w],"^%R$")) { refreport=wtail() }
209124208Sdes    } else if(match(words[w],"^Nm$")) {
210124208Sdes      if(synopsis) {
211126274Sdes	add(".br")
212126274Sdes	prenl++
213124208Sdes      }
214124208Sdes      n=words[++w]
215124208Sdes      if(!length(name))
216126274Sdes	name=n
217124208Sdes      if(!length(n))
218126274Sdes	n=name
219124208Sdes      add("\\fB" n "\\fP")
220124208Sdes      if(!nospace&&match(words[w+1],"^[\\.,]"))
221126274Sdes	nospace=1
222124208Sdes    } else if(match(words[w],"^Nd$")) {
223124208Sdes      add("\\- " wtail())
224124208Sdes    } else if(match(words[w],"^Fl$")) {
225124208Sdes      add("\\fB\\-" words[++w] "\\fP")
226124208Sdes      if(!nospace&&match(words[w+1],"^[\\.,]"))
227126274Sdes	nospace=1
228124208Sdes    } else if(match(words[w],"^Ar$")) {
229124208Sdes      add("\\fI")
230124208Sdes      if(w==nwords)
231126274Sdes	add("file ...\\fP")
232124208Sdes      else {
233126274Sdes	add(words[++w] "\\fP")
234126274Sdes	while(match(words[w+1],"^\\|$"))
235126274Sdes	  add(OFS words[++w] " \\fI" words[++w] "\\fP")
236124208Sdes      }
237124208Sdes      if(!nospace&&match(words[w+1],"^[\\.,]"))
238126274Sdes	nospace=1
239124208Sdes    } else if(match(words[w],"^Cm$")) {
240124208Sdes      add("\\fB" words[++w] "\\fP")
241124208Sdes      while(w<nwords&&match(words[w+1],"^[\\.,:;)]"))
242126274Sdes	add(words[++w])
243124208Sdes    } else if(match(words[w],"^Op$")) {
244124208Sdes      option=1
245124208Sdes      if(!nospace)
246126274Sdes	nospace=1
247124208Sdes      add("[")
248124208Sdes    } else if(match(words[w],"^Pp$")) {
249124208Sdes      prenl++
250124208Sdes    } else if(match(words[w],"^An$")) {
251124208Sdes      prenl++
252124208Sdes    } else if(match(words[w],"^Ss$")) {
253124208Sdes      add(".SS")
254124208Sdes    } else if(match(words[w],"^Pa$")&&!option) {
255124208Sdes      add("\\fI")
256124208Sdes      w++
257124208Sdes      if(match(words[w],"^\\."))
258126274Sdes	add("\\&")
259124208Sdes      add(words[w] "\\fP")
260124208Sdes      while(w<nwords&&match(words[w+1],"^[\\.,:;)]"))
261126274Sdes	add(words[++w])
262124208Sdes    } else if(match(words[w],"^Dv$")) {
263124208Sdes      add(".BR")
264124208Sdes    } else if(match(words[w],"^Em|Ev$")) {
265124208Sdes      add(".IR")
266124208Sdes    } else if(match(words[w],"^Pq$")) {
267124208Sdes      add("(")
268124208Sdes      nospace=1
269124208Sdes      parens=1
270124208Sdes    } else if(match(words[w],"^Aq$")) {
271124208Sdes      add("<")
272124208Sdes      nospace=1
273124208Sdes      angles=1
274124208Sdes    } else if(match(words[w],"^S[xy]$")) {
275124208Sdes      add(".B " wtail())
276124208Sdes    } else if(match(words[w],"^Ic$")) {
277124208Sdes      plain=1
278124208Sdes      add("\\fB")
279124208Sdes      while(w<nwords) {
280126274Sdes	w++
281126274Sdes	if(match(words[w],"^Op$")) {
282126274Sdes	  w++
283126274Sdes	  add("[")
284126274Sdes	  words[nwords]=words[nwords] "]"
285126274Sdes	}
286126274Sdes	if(match(words[w],"^Ar$")) {
287126274Sdes	  add("\\fI" words[++w] "\\fP")
288126274Sdes	} else if(match(words[w],"^[\\.,]")) {
289126274Sdes	  sub(" $","",line)
290126274Sdes	  if(plain) {
291126274Sdes	    add("\\fP")
292126274Sdes	    plain=0
293126274Sdes	  }
294126274Sdes	  add(words[w])
295126274Sdes	} else {
296126274Sdes	  if(!plain) {
297126274Sdes	    add("\\fB")
298126274Sdes	    plain=1
299126274Sdes	  }
300126274Sdes	  add(words[w])
301126274Sdes	}
302126274Sdes	if(!nospace)
303126274Sdes	  add(OFS)
304124208Sdes      }
305124208Sdes      sub(" $","",line)
306124208Sdes      if(plain)
307126274Sdes	add("\\fP")
308124208Sdes    } else if(match(words[w],"^Bl$")) {
309124208Sdes      oldoptlist=optlist
310124208Sdes      if(match(words[w+1],"-bullet"))
311126274Sdes	optlist=1
312124208Sdes      else if(match(words[w+1],"-enum")) {
313126274Sdes	optlist=2
314126274Sdes	enum=0
315124208Sdes      } else if(match(words[w+1],"-tag"))
316126274Sdes	optlist=3
317124208Sdes      else if(match(words[w+1],"-item"))
318126274Sdes	optlist=4
319124208Sdes      else if(match(words[w+1],"-bullet"))
320126274Sdes	optlist=1
321124208Sdes      w=nwords
322124208Sdes    } else if(match(words[w],"^El$")) {
323124208Sdes      optlist=oldoptlist
324323134Sdes      if(!optlist)
325323134Sdes        add(".PP")
326137015Sdes    } else if(match(words[w],"^Bk$")) {
327137015Sdes      if(match(words[w+1],"-words")) {
328137015Sdes	w++
329137015Sdes	breakw=1
330137015Sdes      }
331137015Sdes    } else if(match(words[w],"^Ek$")) {
332137015Sdes      breakw=0
333124208Sdes    } else if(match(words[w],"^It$")&&optlist) {
334124208Sdes      if(optlist==1)
335126274Sdes	add(".IP \\(bu")
336124208Sdes      else if(optlist==2)
337126274Sdes	add(".IP " ++enum ".")
338124208Sdes      else if(optlist==3) {
339126274Sdes	add(".TP")
340126274Sdes	prenl++
341137015Sdes	if(match(words[w+1],"^Pa$|^Ev$")) {
342126274Sdes	  add(".B")
343126274Sdes	  w++
344126274Sdes	}
345124208Sdes      } else if(optlist==4)
346126274Sdes	add(".IP")
347124208Sdes    } else if(match(words[w],"^Sm$")) {
348124208Sdes      if(match(words[w+1],"off"))
349126274Sdes	nospace=2
350124208Sdes      else if(match(words[w+1],"on"))
351126274Sdes	nospace=0
352124208Sdes      w++
353124208Sdes    } else if(!skip) {
354124208Sdes      add(words[w])
355124208Sdes    }
356124208Sdes  }
357124208Sdes  if(match(line,"^\\.[^a-zA-Z]"))
358124208Sdes    sub("^\\.","",line)
359124208Sdes  if(parens)
360124208Sdes    add(")")
361124208Sdes  if(angles)
362124208Sdes    add(">")
363124208Sdes  if(option)
364124208Sdes    add("]")
365124208Sdes  if(ext&&!extopt&&!match(line," $"))
366124208Sdes    add(OFS)
367124208Sdes  if(!ext&&!extopt&&length(line)) {
368124208Sdes    print line
369124208Sdes    prenl=0
370124208Sdes    line=""
371124208Sdes  }
372124208Sdes}
373