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],"^Ox$")) { 144 add("OpenBSD") 145 skip=1 146 } else if(match(words[w],"^Os$")) { 147 add(".TH " id " \"" date "\" \"" wtail() "\"") 148 } else if(match(words[w],"^Sh$")) { 149 add(".SH") 150 synopsis=match(words[w+1],"SYNOPSIS") 151 } else if(match(words[w],"^Xr$")) { 152 add("\\fB" words[++w] "\\fP(" words[++w] ")" words[++w]) 153 } else if(match(words[w],"^Rs$")) { 154 split("",refauthors) 155 nrefauthors=0 156 reftitle="" 157 refissue="" 158 refdate="" 159 refopt="" 160 reference=1 161 next 162 } else if(match(words[w],"^Re$")) { 163 prenl++ 164 for(i=nrefauthors-1;i>0;i--) { 165 add(refauthors[i]) 166 if(i>1) 167 add(", ") 168 } 169 if(nrefauthors>1) 170 add(" and ") 171 add(refauthors[0] ", \\fI" reftitle "\\fP") 172 if(length(refissue)) 173 add(", " refissue) 174 if(length(refdate)) 175 add(", " refdate) 176 if(length(refopt)) 177 add(", " refopt) 178 add(".") 179 reference=0 180 } else if(reference) { 181 if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() } 182 if(match(words[w],"^%T$")) { 183 reftitle=wtail() 184 sub("^\"","",reftitle) 185 sub("\"$","",reftitle) 186 } 187 if(match(words[w],"^%N$")) { refissue=wtail() } 188 if(match(words[w],"^%D$")) { refdate=wtail() } 189 if(match(words[w],"^%O$")) { refopt=wtail() } 190 } else if(match(words[w],"^Nm$")) { 191 if(synopsis) { 192 add(".br") 193 prenl++ 194 } 195 n=words[++w] 196 if(!length(name)) 197 name=n 198 if(!length(n)) 199 n=name 200 add("\\fB" n "\\fP") 201 if(!nospace&&match(words[w+1],"^[\\.,]")) 202 nospace=1 203 } else if(match(words[w],"^Nd$")) { 204 add("\\- " wtail()) 205 } else if(match(words[w],"^Fl$")) { 206 add("\\fB\\-" words[++w] "\\fP") 207 if(!nospace&&match(words[w+1],"^[\\.,]")) 208 nospace=1 209 } else if(match(words[w],"^Ar$")) { 210 add("\\fI") 211 if(w==nwords) 212 add("file ...\\fP") 213 else { 214 add(words[++w] "\\fP") 215 while(match(words[w+1],"^\\|$")) 216 add(OFS words[++w] " \\fI" words[++w] "\\fP") 217 } 218 if(!nospace&&match(words[w+1],"^[\\.,]")) 219 nospace=1 220 } else if(match(words[w],"^Cm$")) { 221 add("\\fB" words[++w] "\\fP") 222 while(w<nwords&&match(words[w+1],"^[\\.,:;)]")) 223 add(words[++w]) 224 } else if(match(words[w],"^Op$")) { 225 option=1 226 if(!nospace) 227 nospace=1 228 add("[") 229 } else if(match(words[w],"^Pp$")) { 230 prenl++ 231 } else if(match(words[w],"^An$")) { 232 prenl++ 233 } else if(match(words[w],"^Ss$")) { 234 add(".SS") 235 } else if(match(words[w],"^Pa$")&&!option) { 236 add("\\fI") 237 w++ 238 if(match(words[w],"^\\.")) 239 add("\\&") 240 add(words[w] "\\fP") 241 while(w<nwords&&match(words[w+1],"^[\\.,:;)]")) 242 add(words[++w]) 243 } else if(match(words[w],"^Dv$")) { 244 add(".BR") 245 } else if(match(words[w],"^Em|Ev$")) { 246 add(".IR") 247 } else if(match(words[w],"^Pq$")) { 248 add("(") 249 nospace=1 250 parens=1 251 } else if(match(words[w],"^Aq$")) { 252 add("<") 253 nospace=1 254 angles=1 255 } else if(match(words[w],"^S[xy]$")) { 256 add(".B " wtail()) 257 } else if(match(words[w],"^Ic$")) { 258 plain=1 259 add("\\fB") 260 while(w<nwords) { 261 w++ 262 if(match(words[w],"^Op$")) { 263 w++ 264 add("[") 265 words[nwords]=words[nwords] "]" 266 } 267 if(match(words[w],"^Ar$")) { 268 add("\\fI" words[++w] "\\fP") 269 } else if(match(words[w],"^[\\.,]")) { 270 sub(" $","",line) 271 if(plain) { 272 add("\\fP") 273 plain=0 274 } 275 add(words[w]) 276 } else { 277 if(!plain) { 278 add("\\fB") 279 plain=1 280 } 281 add(words[w]) 282 } 283 if(!nospace) 284 add(OFS) 285 } 286 sub(" $","",line) 287 if(plain) 288 add("\\fP") 289 } else if(match(words[w],"^Bl$")) { 290 oldoptlist=optlist 291 if(match(words[w+1],"-bullet")) 292 optlist=1 293 else if(match(words[w+1],"-enum")) { 294 optlist=2 295 enum=0 296 } else if(match(words[w+1],"-tag")) 297 optlist=3 298 else if(match(words[w+1],"-item")) 299 optlist=4 300 else if(match(words[w+1],"-bullet")) 301 optlist=1 302 w=nwords 303 } else if(match(words[w],"^El$")) { 304 optlist=oldoptlist 305 } else if(match(words[w],"^Bk$")) { 306 if(match(words[w+1],"-words")) { 307 w++ 308 breakw=1 309 } 310 } else if(match(words[w],"^Ek$")) { 311 breakw=0 312 } else if(match(words[w],"^It$")&&optlist) { 313 if(optlist==1) 314 add(".IP \\(bu") 315 else if(optlist==2) 316 add(".IP " ++enum ".") 317 else if(optlist==3) { 318 add(".TP") 319 prenl++ 320 if(match(words[w+1],"^Pa$|^Ev$")) { 321 add(".B") 322 w++ 323 } 324 } else if(optlist==4) 325 add(".IP") 326 } else if(match(words[w],"^Sm$")) { 327 if(match(words[w+1],"off")) 328 nospace=2 329 else if(match(words[w+1],"on")) 330 nospace=0 331 w++ 332 } else if(!skip) { 333 add(words[w]) 334 } 335 } 336 if(match(line,"^\\.[^a-zA-Z]")) 337 sub("^\\.","",line) 338 if(parens) 339 add(")") 340 if(angles) 341 add(">") 342 if(option) 343 add("]") 344 if(ext&&!extopt&&!match(line," $")) 345 add(OFS) 346 if(!ext&&!extopt&&length(line)) { 347 print line 348 prenl=0 349 line="" 350 } 351} 352