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