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