1#!/usr/bin/awk
2#
3# Version history:
4#  v4+ Adapted for OpenSSH Portable (see cvs Id and history)
5#  v3, I put the program under a proper license
6#      Dan Nelson <dnelson@allantgroup.com> added .An, .Aq and fixed a typo
7#  v2, fixed to work on GNU awk --posix and MacOS X
8#  v1, first attempt, didn't work on MacOS X
9#
10# Copyright (c) 2003 Peter Stuge <stuge-mdoc2man@cdy.org>
11#
12# Permission to use, copy, modify, and distribute this software for any
13# purpose with or without fee is hereby granted, provided that the above
14# copyright notice and this permission notice appear in all copies.
15#
16# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23
24
25BEGIN {
26  optlist=0
27  oldoptlist=0
28  nospace=0
29  synopsis=0
30  reference=0
31  block=0
32  ext=0
33  extopt=0
34  literal=0
35  prenl=0
36  breakw=0
37  line=""
38}
39
40function wtail() {
41  retval=""
42  while(w<nwords) {
43    if(length(retval))
44      retval=retval OFS
45    retval=retval words[++w]
46  }
47  return retval
48}
49
50function add(str) {
51  for(;prenl;prenl--)
52    line=line "\n"
53  line=line str
54}
55
56! /^\./ {
57  for(;prenl;prenl--)
58    print ""
59  print
60  if(literal)
61    print ".br"
62  next
63}
64
65/^\.\\"/ { next }
66
67{
68  option=0
69  parens=0
70  angles=0
71  sub("^\\.","")
72  nwords=split($0,words)
73  for(w=1;w<=nwords;w++) {
74    skip=0
75    if(match(words[w],"^Li|Pf$")) {
76      skip=1
77    } else if(match(words[w],"^Xo$")) {
78      skip=1
79      ext=1
80      if(length(line)&&!(match(line," $")||prenl))
81	add(OFS)
82    } else if(match(words[w],"^Xc$")) {
83      skip=1
84      ext=0
85      if(!extopt)
86	prenl++
87      w=nwords
88    } else if(match(words[w],"^Bd$")) {
89      skip=1
90      if(match(words[w+1],"-literal")) {
91	literal=1
92	prenl++
93	w=nwords
94      }
95    } else if(match(words[w],"^Ed$")) {
96      skip=1
97      literal=0
98    } else if(match(words[w],"^Ns$")) {
99      skip=1
100      if(!nospace)
101	nospace=1
102      sub(" $","",line)
103    } else if(match(words[w],"^No$")) {
104      skip=1
105      sub(" $","",line)
106      add(words[++w])
107    } else if(match(words[w],"^Dq$")) {
108      skip=1
109      add("``")
110      add(words[++w])
111      while(w<nwords&&!match(words[w+1],"^[\\.,]"))
112	add(OFS words[++w])
113      add("''")
114      if(!nospace&&match(words[w+1],"^[\\.,]"))
115	nospace=1
116    } else if(match(words[w],"^Sq|Ql$")) {
117      skip=1
118      add("`" words[++w] "'")
119      if(!nospace&&match(words[w+1],"^[\\.,]"))
120	nospace=1
121    } else if(match(words[w],"^Oo$")) {
122      skip=1
123      extopt=1
124      if(!nospace)
125	nospace=1
126      add("[")
127    } else if(match(words[w],"^Oc$")) {
128      skip=1
129      extopt=0
130      add("]")
131    }
132    if(!skip) {
133      if(!nospace&&length(line)&&!(match(line," $")||prenl))
134	add(OFS)
135      if(nospace==1)
136	nospace=0
137    }
138    if(match(words[w],"^Dd$")) {
139      if(match(words[w+1],"^\\$Mdocdate:")) {
140        w++;
141        if(match(words[w+4],"^\\$$")) {
142          words[w+4] = ""
143        }
144      }
145      date=wtail()
146      next
147    } else if(match(words[w],"^Dt$")) {
148      id=wtail()
149      next
150    } else if(match(words[w],"^Ux$")) {
151      add("UNIX")
152      skip=1
153    } else if(match(words[w],"^Ox$")) {
154      add("OpenBSD")
155      skip=1
156    } else if(match(words[w],"^Os$")) {
157      add(".TH " id " \"" date "\" \"" wtail() "\"")
158    } else if(match(words[w],"^Sh$")) {
159      add(".SH")
160      synopsis=match(words[w+1],"SYNOPSIS")
161    } else if(match(words[w],"^Xr$")) {
162      add("\\fB" words[++w] "\\fP(" words[++w] ")" words[++w])
163    } else if(match(words[w],"^Rs$")) {
164      split("",refauthors)
165      nrefauthors=0
166      reftitle=""
167      refissue=""
168      refdate=""
169      refopt=""
170      refreport=""
171      reference=1
172      next
173    } else if(match(words[w],"^Re$")) {
174      prenl++
175      for(i=nrefauthors-1;i>0;i--) {
176	add(refauthors[i])
177	if(i>1)
178	  add(", ")
179      }
180      if(nrefauthors>1)
181	add(" and ")
182      if(nrefauthors>0)
183        add(refauthors[0] ", ")
184      add("\\fI" reftitle "\\fP")
185      if(length(refissue))
186	add(", " refissue)
187      if(length(refreport)) {
188	add(", " refreport)
189      }
190      if(length(refdate))
191	add(", " refdate)
192      if(length(refopt))
193	add(", " refopt)
194      add(".")
195      reference=0
196    } else if(reference) {
197      if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() }
198      if(match(words[w],"^%T$")) {
199	reftitle=wtail()
200	sub("^\"","",reftitle)
201	sub("\"$","",reftitle)
202      }
203      if(match(words[w],"^%N$")) { refissue=wtail() }
204      if(match(words[w],"^%D$")) { refdate=wtail() }
205      if(match(words[w],"^%O$")) { refopt=wtail() }
206      if(match(words[w],"^%R$")) { refreport=wtail() }
207    } else if(match(words[w],"^Nm$")) {
208      if(synopsis) {
209	add(".br")
210	prenl++
211      }
212      n=words[++w]
213      if(!length(name))
214	name=n
215      if(!length(n))
216	n=name
217      add("\\fB" n "\\fP")
218      if(!nospace&&match(words[w+1],"^[\\.,]"))
219	nospace=1
220    } else if(match(words[w],"^Nd$")) {
221      add("\\- " wtail())
222    } else if(match(words[w],"^Fl$")) {
223      add("\\fB\\-" words[++w] "\\fP")
224      if(!nospace&&match(words[w+1],"^[\\.,]"))
225	nospace=1
226    } else if(match(words[w],"^Ar$")) {
227      add("\\fI")
228      if(w==nwords)
229	add("file ...\\fP")
230      else {
231	add(words[++w] "\\fP")
232	while(match(words[w+1],"^\\|$"))
233	  add(OFS words[++w] " \\fI" words[++w] "\\fP")
234      }
235      if(!nospace&&match(words[w+1],"^[\\.,]"))
236	nospace=1
237    } else if(match(words[w],"^Cm$")) {
238      add("\\fB" words[++w] "\\fP")
239      while(w<nwords&&match(words[w+1],"^[\\.,:;)]"))
240	add(words[++w])
241    } else if(match(words[w],"^Op$")) {
242      option=1
243      if(!nospace)
244	nospace=1
245      add("[")
246    } else if(match(words[w],"^Pp$")) {
247      prenl++
248    } else if(match(words[w],"^An$")) {
249      prenl++
250    } else if(match(words[w],"^Ss$")) {
251      add(".SS")
252    } else if(match(words[w],"^Pa$")&&!option) {
253      add("\\fI")
254      w++
255      if(match(words[w],"^\\."))
256	add("\\&")
257      add(words[w] "\\fP")
258      while(w<nwords&&match(words[w+1],"^[\\.,:;)]"))
259	add(words[++w])
260    } else if(match(words[w],"^Dv$")) {
261      add(".BR")
262    } else if(match(words[w],"^Em|Ev$")) {
263      add(".IR")
264    } else if(match(words[w],"^Pq$")) {
265      add("(")
266      nospace=1
267      parens=1
268    } else if(match(words[w],"^Aq$")) {
269      add("<")
270      nospace=1
271      angles=1
272    } else if(match(words[w],"^S[xy]$")) {
273      add(".B " wtail())
274    } else if(match(words[w],"^Ic$")) {
275      plain=1
276      add("\\fB")
277      while(w<nwords) {
278	w++
279	if(match(words[w],"^Op$")) {
280	  w++
281	  add("[")
282	  words[nwords]=words[nwords] "]"
283	}
284	if(match(words[w],"^Ar$")) {
285	  add("\\fI" words[++w] "\\fP")
286	} else if(match(words[w],"^[\\.,]")) {
287	  sub(" $","",line)
288	  if(plain) {
289	    add("\\fP")
290	    plain=0
291	  }
292	  add(words[w])
293	} else {
294	  if(!plain) {
295	    add("\\fB")
296	    plain=1
297	  }
298	  add(words[w])
299	}
300	if(!nospace)
301	  add(OFS)
302      }
303      sub(" $","",line)
304      if(plain)
305	add("\\fP")
306    } else if(match(words[w],"^Bl$")) {
307      oldoptlist=optlist
308      if(match(words[w+1],"-bullet"))
309	optlist=1
310      else if(match(words[w+1],"-enum")) {
311	optlist=2
312	enum=0
313      } else if(match(words[w+1],"-tag"))
314	optlist=3
315      else if(match(words[w+1],"-item"))
316	optlist=4
317      else if(match(words[w+1],"-bullet"))
318	optlist=1
319      w=nwords
320    } else if(match(words[w],"^El$")) {
321      optlist=oldoptlist
322      if(!optlist)
323        add(".PP")
324    } else if(match(words[w],"^Bk$")) {
325      if(match(words[w+1],"-words")) {
326	w++
327	breakw=1
328      }
329    } else if(match(words[w],"^Ek$")) {
330      breakw=0
331    } else if(match(words[w],"^It$")&&optlist) {
332      if(optlist==1)
333	add(".IP \\(bu")
334      else if(optlist==2)
335	add(".IP " ++enum ".")
336      else if(optlist==3) {
337	add(".TP")
338	prenl++
339	if(match(words[w+1],"^Pa$|^Ev$")) {
340	  add(".B")
341	  w++
342	}
343      } else if(optlist==4)
344	add(".IP")
345    } else if(match(words[w],"^Sm$")) {
346      if(match(words[w+1],"off"))
347	nospace=2
348      else if(match(words[w+1],"on"))
349	nospace=0
350      w++
351    } else if(!skip) {
352      add(words[w])
353    }
354  }
355  if(match(line,"^\\.[^a-zA-Z]"))
356    sub("^\\.","",line)
357  if(parens)
358    add(")")
359  if(angles)
360    add(">")
361  if(option)
362    add("]")
363  if(ext&&!extopt&&!match(line," $"))
364    add(OFS)
365  if(!ext&&!extopt&&length(line)) {
366    print line
367    prenl=0
368    line=""
369  }
370}
371