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