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