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