mdoc2man.awk revision 137015
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}
38
39function wtail() {
40  retval=""
41  while(w<nwords) {
42    if(length(retval))
43      retval=retval OFS
44    retval=retval words[++w]
45  }
46  return retval
47}
48
49function add(str) {
50  for(;prenl;prenl--)
51    line=line "\n"
52  line=line str
53}
54
55! /^\./ {
56  for(;prenl;prenl--)
57    print ""
58  print
59  if(literal)
60    print ".br"
61  next
62}
63
64/^\.\\"/ { next }
65
66{
67  option=0
68  parens=0
69  angles=0
70  sub("^\\.","")
71  nwords=split($0,words)
72  for(w=1;w<=nwords;w++) {
73    skip=0
74    if(match(words[w],"^Li|Pf$")) {
75      skip=1
76    } else if(match(words[w],"^Xo$")) {
77      skip=1
78      ext=1
79      if(length(line)&&!(match(line," $")||prenl))
80	add(OFS)
81    } else if(match(words[w],"^Xc$")) {
82      skip=1
83      ext=0
84      if(!extopt)
85	prenl++
86      w=nwords
87    } else if(match(words[w],"^Bd$")) {
88      skip=1
89      if(match(words[w+1],"-literal")) {
90	literal=1
91	prenl++
92	w=nwords
93      }
94    } else if(match(words[w],"^Ed$")) {
95      skip=1
96      literal=0
97    } else if(match(words[w],"^Ns$")) {
98      skip=1
99      if(!nospace)
100	nospace=1
101      sub(" $","",line)
102    } else if(match(words[w],"^No$")) {
103      skip=1
104      sub(" $","",line)
105      add(words[++w])
106    } else if(match(words[w],"^Dq$")) {
107      skip=1
108      add("``")
109      add(words[++w])
110      while(w<nwords&&!match(words[w+1],"^[\\.,]"))
111	add(OFS words[++w])
112      add("''")
113      if(!nospace&&match(words[w+1],"^[\\.,]"))
114	nospace=1
115    } else if(match(words[w],"^Sq|Ql$")) {
116      skip=1
117      add("`" words[++w] "'")
118      if(!nospace&&match(words[w+1],"^[\\.,]"))
119	nospace=1
120    } else if(match(words[w],"^Oo$")) {
121      skip=1
122      extopt=1
123      if(!nospace)
124	nospace=1
125      add("[")
126    } else if(match(words[w],"^Oc$")) {
127      skip=1
128      extopt=0
129      add("]")
130    }
131    if(!skip) {
132      if(!nospace&&length(line)&&!(match(line," $")||prenl))
133	add(OFS)
134      if(nospace==1)
135	nospace=0
136    }
137    if(match(words[w],"^Dd$")) {
138      date=wtail()
139      next
140    } else if(match(words[w],"^Dt$")) {
141      id=wtail()
142      next
143    } else if(match(words[w],"^Os$")) {
144      add(".TH " id " \"" date "\" \"" wtail() "\"")
145    } else if(match(words[w],"^Sh$")) {
146      add(".SH")
147      synopsis=match(words[w+1],"SYNOPSIS")
148    } else if(match(words[w],"^Xr$")) {
149      add("\\fB" words[++w] "\\fP(" words[++w] ")" words[++w])
150    } else if(match(words[w],"^Rs$")) {
151      split("",refauthors)
152      nrefauthors=0
153      reftitle=""
154      refissue=""
155      refdate=""
156      refopt=""
157      reference=1
158      next
159    } else if(match(words[w],"^Re$")) {
160      prenl++
161      for(i=nrefauthors-1;i>0;i--) {
162	add(refauthors[i])
163	if(i>1)
164	  add(", ")
165      }
166      if(nrefauthors>1)
167	add(" and ")
168      add(refauthors[0] ", \\fI" reftitle "\\fP")
169      if(length(refissue))
170	add(", " refissue)
171      if(length(refdate))
172	add(", " refdate)
173      if(length(refopt))
174	add(", " refopt)
175      add(".")
176      reference=0
177    } else if(reference) {
178      if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() }
179      if(match(words[w],"^%T$")) {
180	reftitle=wtail()
181	sub("^\"","",reftitle)
182	sub("\"$","",reftitle)
183      }
184      if(match(words[w],"^%N$")) { refissue=wtail() }
185      if(match(words[w],"^%D$")) { refdate=wtail() }
186      if(match(words[w],"^%O$")) { refopt=wtail() }
187    } else if(match(words[w],"^Nm$")) {
188      if(synopsis) {
189	add(".br")
190	prenl++
191      }
192      n=words[++w]
193      if(!length(name))
194	name=n
195      if(!length(n))
196	n=name
197      add("\\fB" n "\\fP")
198      if(!nospace&&match(words[w+1],"^[\\.,]"))
199	nospace=1
200    } else if(match(words[w],"^Nd$")) {
201      add("\\- " wtail())
202    } else if(match(words[w],"^Fl$")) {
203      add("\\fB\\-" words[++w] "\\fP")
204      if(!nospace&&match(words[w+1],"^[\\.,]"))
205	nospace=1
206    } else if(match(words[w],"^Ar$")) {
207      add("\\fI")
208      if(w==nwords)
209	add("file ...\\fP")
210      else {
211	add(words[++w] "\\fP")
212	while(match(words[w+1],"^\\|$"))
213	  add(OFS words[++w] " \\fI" words[++w] "\\fP")
214      }
215      if(!nospace&&match(words[w+1],"^[\\.,]"))
216	nospace=1
217    } else if(match(words[w],"^Cm$")) {
218      add("\\fB" words[++w] "\\fP")
219      while(w<nwords&&match(words[w+1],"^[\\.,:;)]"))
220	add(words[++w])
221    } else if(match(words[w],"^Op$")) {
222      option=1
223      if(!nospace)
224	nospace=1
225      add("[")
226    } else if(match(words[w],"^Pp$")) {
227      prenl++
228    } else if(match(words[w],"^An$")) {
229      prenl++
230    } else if(match(words[w],"^Ss$")) {
231      add(".SS")
232    } else if(match(words[w],"^Pa$")&&!option) {
233      add("\\fI")
234      w++
235      if(match(words[w],"^\\."))
236	add("\\&")
237      add(words[w] "\\fP")
238      while(w<nwords&&match(words[w+1],"^[\\.,:;)]"))
239	add(words[++w])
240    } else if(match(words[w],"^Dv$")) {
241      add(".BR")
242    } else if(match(words[w],"^Em|Ev$")) {
243      add(".IR")
244    } else if(match(words[w],"^Pq$")) {
245      add("(")
246      nospace=1
247      parens=1
248    } else if(match(words[w],"^Aq$")) {
249      add("<")
250      nospace=1
251      angles=1
252    } else if(match(words[w],"^S[xy]$")) {
253      add(".B " wtail())
254    } else if(match(words[w],"^Ic$")) {
255      plain=1
256      add("\\fB")
257      while(w<nwords) {
258	w++
259	if(match(words[w],"^Op$")) {
260	  w++
261	  add("[")
262	  words[nwords]=words[nwords] "]"
263	}
264	if(match(words[w],"^Ar$")) {
265	  add("\\fI" words[++w] "\\fP")
266	} else if(match(words[w],"^[\\.,]")) {
267	  sub(" $","",line)
268	  if(plain) {
269	    add("\\fP")
270	    plain=0
271	  }
272	  add(words[w])
273	} else {
274	  if(!plain) {
275	    add("\\fB")
276	    plain=1
277	  }
278	  add(words[w])
279	}
280	if(!nospace)
281	  add(OFS)
282      }
283      sub(" $","",line)
284      if(plain)
285	add("\\fP")
286    } else if(match(words[w],"^Bl$")) {
287      oldoptlist=optlist
288      if(match(words[w+1],"-bullet"))
289	optlist=1
290      else if(match(words[w+1],"-enum")) {
291	optlist=2
292	enum=0
293      } else if(match(words[w+1],"-tag"))
294	optlist=3
295      else if(match(words[w+1],"-item"))
296	optlist=4
297      else if(match(words[w+1],"-bullet"))
298	optlist=1
299      w=nwords
300    } else if(match(words[w],"^El$")) {
301      optlist=oldoptlist
302    } else if(match(words[w],"^Bk$")) {
303      if(match(words[w+1],"-words")) {
304	w++
305	breakw=1
306      }
307    } else if(match(words[w],"^Ek$")) {
308      breakw=0
309    } else if(match(words[w],"^It$")&&optlist) {
310      if(optlist==1)
311	add(".IP \\(bu")
312      else if(optlist==2)
313	add(".IP " ++enum ".")
314      else if(optlist==3) {
315	add(".TP")
316	prenl++
317	if(match(words[w+1],"^Pa$|^Ev$")) {
318	  add(".B")
319	  w++
320	}
321      } else if(optlist==4)
322	add(".IP")
323    } else if(match(words[w],"^Sm$")) {
324      if(match(words[w+1],"off"))
325	nospace=2
326      else if(match(words[w+1],"on"))
327	nospace=0
328      w++
329    } else if(!skip) {
330      add(words[w])
331    }
332  }
333  if(match(line,"^\\.[^a-zA-Z]"))
334    sub("^\\.","",line)
335  if(parens)
336    add(")")
337  if(angles)
338    add(">")
339  if(option)
340    add("]")
341  if(ext&&!extopt&&!match(line," $"))
342    add(OFS)
343  if(!ext&&!extopt&&length(line)) {
344    print line
345    prenl=0
346    line=""
347  }
348}
349