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 proto=0 38 bl_level=0 39} 40 41function wtail() { 42 retval="" 43 while(w<nwords) { 44 if(length(retval)) 45 retval=retval OFS 46 retval=retval words[++w] 47 } 48 return retval 49} 50 51function add(str) { 52 for(;prenl;prenl--) 53 line=line "\n" 54 line=line str 55} 56 57! /^\./ { 58 for(;prenl;prenl--) 59 print "" 60 print 61 if(literal) 62 print ".br" 63 next 64} 65 66/^\.\\"/ { next } 67 68{ 69 option=0 70 parens=0 71 angles=0 72 sub("^\\.","") 73 nwords=split($0,words) 74 for(w=1;w<=nwords;w++) { 75 skip=0 76 if(match(words[w],"^Li|Pf$")) { 77 skip=1 78 } else if(match(words[w],"^Xo$")) { 79 skip=1 80 ext=1 81 if(length(line)&&!(match(line," $")||prenl)) 82 add(OFS) 83 } else if(match(words[w],"^Xc$")) { 84 skip=1 85 ext=0 86 if(!extopt) 87 prenl++ 88 w=nwords 89 } else if(match(words[w],"^Bd$")) { 90 skip=1 91 if(match(words[w+1],"-literal")) { 92 literal=1 93 prenl++ 94 w=nwords 95 } 96 } else if(match(words[w],"^Po$")) { 97 skip=1 98 add("(") 99 if(!nospace) 100 nospace=1 101 } else if(match(words[w],"^Pc$")) { 102 skip=1 103 add(")") 104 if(!nospace) 105 nospace=1 106 } else if(match(words[w],"^Ed$")) { 107 skip=1 108 literal=0 109 } else if(match(words[w],"^Ns$")) { 110 skip=1 111 if(!nospace) 112 nospace=1 113 sub(" $","",line) 114 } else if(match(words[w],"^No$")) { 115 skip=1 116 sub(" $","",line) 117 add(words[++w]) 118 } else if(match(words[w],"^Dq$")) { 119 skip=1 120 add("``") 121 add(words[++w]) 122 while(w<nwords&&!match(words[w+1],"^[\\.,]")) 123 add(OFS words[++w]) 124 add("''") 125 if(!nospace&&match(words[w+1],"^[\\.,]")) 126 nospace=1 127 } else if(match(words[w],"^Va$")) { 128 skip=1 129 add("\\fI" words[++w]) 130 while(w<nwords&&!match(words[w+1],"^[\\.,]")) 131 add(OFS words[++w]) 132 add("\\fP") 133 if(!nospace&&match(words[w+1],"^[\\.,]")) 134 nospace=1 135 } else if(match(words[w],"^Sq|Ql$")) { 136 skip=1 137 add("`" words[++w] "'") 138 if(!nospace&&match(words[w+1],"^[\\.,]")) 139 nospace=1 140 } else if(match(words[w],"^Oo$")) { 141 skip=1 142 extopt=1 143 if(!nospace) 144 nospace=1 145 add("[") 146 } else if(match(words[w],"^Oc$")) { 147 skip=1 148 extopt=0 149 add("]") 150 } 151 if(!skip) { 152 if(!nospace&&length(line)&&!(match(line," $")||prenl)) 153 add(OFS) 154 if(nospace==1) 155 nospace=0 156 } 157 if(match(words[w],"^Dd$")) { 158 date=wtail() 159 next 160 } else if(match(words[w],"^Dt$")) { 161 id=wtail() 162 next 163 } else if(match(words[w],"^Os$")) { 164 add(".TH " id " \"" date "\" \"" wtail() "\"") 165 } else if(match(words[w],"^Sh$")) { 166 add(".SH") 167 synopsis=match(words[w+1],"SYNOPSIS") 168 } else if(match(words[w],"^Xr$")) { 169 #add("\\fB" words[++w] "\\fP(" words[++w] ")" words[++w]) 170 add("\\fB" words[++w] "\\fP(" words[++w] ")") 171 sub("^Ns$", "", words[w+1]) 172 add(words[++w]) 173 } else if(match(words[w],"^Rs$")) { 174 split("",refauthors) 175 nrefauthors=0 176 reftitle="" 177 refissue="" 178 refdate="" 179 refopt="" 180 reference=1 181 next 182 } else if(match(words[w],"^Re$")) { 183 prenl++ 184 for(i=nrefauthors-1;i>0;i--) { 185 add(refauthors[i]) 186 if(i>1) 187 add(", ") 188 } 189 if(nrefauthors>1) 190 add(" and ") 191 add(refauthors[0] ", \\fI" reftitle "\\fP") 192 if(length(refissue)) 193 add(", " refissue) 194 if(length(refdate)) 195 add(", " refdate) 196 if(length(refopt)) 197 add(", " refopt) 198 add(".") 199 reference=0 200 } else if(reference) { 201 if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() } 202 if(match(words[w],"^%T$")) { 203 reftitle=wtail() 204 sub("^\"","",reftitle) 205 sub("\"$","",reftitle) 206 } 207 if(match(words[w],"^%N$")) { refissue=wtail() } 208 if(match(words[w],"^%D$")) { refdate=wtail() } 209 if(match(words[w],"^%O$")) { refopt=wtail() } 210 } else if(match(words[w],"^Nm$")) { 211 if(synopsis) { 212 add(".br") 213 prenl++ 214 } 215 n=words[++w] 216 if(!length(name)) 217 name=n 218 if(!length(n)) 219 n=name 220 add("\\fB" n "\\fP") 221 if(!nospace&&match(words[w+1],"^[\\.,]")) 222 nospace=1 223 } else if(match(words[w],"^Nd$")) { 224 add("\\- " wtail()) 225 } else if(match(words[w],"^Fl$")) { 226 add("\\fB\\-" words[++w] "\\fP") 227 if(!nospace&&match(words[w+1],"^[\\.,]")) 228 nospace=1 229 } else if(match(words[w],"^Ar$")) { 230 add("\\fI") 231 if(w==nwords) 232 add("file ...\\fP") 233 else { 234 add(words[++w] "\\fP") 235 while(match(words[w+1],"^\\|$")) 236 add(OFS words[++w] " \\fI" words[++w] "\\fP") 237 } 238 if(!nospace&&match(words[w+1],"^[\\.,]")) 239 nospace=1 240 } else if(match(words[w],"^Cm$")) { 241 add("\\fB" words[++w] "\\fP") 242 while(w<nwords&&match(words[w+1],"^[\\.,:;)]")) 243 add(words[++w]) 244 } else if(match(words[w],"^Op$")) { 245 option=1 246 if(!nospace) 247 nospace=1 248 add("[") 249 } else if(match(words[w],"^Pp$")) { 250 prenl++ 251 } else if(match(words[w],"^An$")) { 252 prenl++ 253 } else if(match(words[w],"^Ss$")) { 254 add(".SS") 255 } else if(match(words[w],"^Pa$")&&!option) { 256 add("\\fI") 257 w++ 258 if(match(words[w],"^\\.")) 259 add("\\&") 260 add(words[w] "\\fP") 261 while(w<nwords&&match(words[w+1],"^[\\.,:;)]")) 262 add(words[++w]) 263 } else if(match(words[w],"^Dv$")) { 264 #add(".BR") 265 } else if(match(words[w],"^Em|Ev$")) { 266 add(".IR") 267 } else if(match(words[w],"^Pq$")) { 268 add("(") 269 nospace=1 270 parens=1 271 } else if(match(words[w],"^Aq$")) { 272 add("<") 273 nospace=1 274 angles=1 275 } else if(match(words[w],"^S[xy]$")) { 276 add(".B " wtail()) 277 } else if(match(words[w],"^Ic$")) { 278 plain=1 279 add("\\fB") 280 while(w<nwords) { 281 w++ 282 if(match(words[w],"^Op$")) { 283 w++ 284 add("[") 285 words[nwords]=words[nwords] "]" 286 } 287 if(match(words[w],"^Ar$")) { 288 add("\\fI" words[++w] "\\fP") 289 } else if(match(words[w],"^[\\.,]")) { 290 sub(" $","",line) 291 if(plain) { 292 add("\\fP") 293 plain=0 294 } 295 add(words[w]) 296 } else if(match(words[w],"^Xo$")) { 297 } else { 298 if(!plain) { 299 add("\\fB") 300 plain=1 301 } 302 add(words[w]) 303 } 304 if(!nospace) 305 add(OFS) 306 } 307 sub(" $","",line) 308 if(plain) 309 add("\\fP") 310 } else if(match(words[w],"^Dl$")) { 311 ## remove is ok for editrc.5 312 } else if(match(words[w],"^Bl$")) { 313 ++bl_level 314 if (bl_level > 1) 315 add(".RS") 316 oldoptlist=optlist 317 if(match(words[w+1],"-bullet")) 318 optlist=1 319 else if(match(words[w+1],"-enum")) { 320 optlist=2 321 enum=0 322 } else if(match(words[w+1],"-tag")) 323 optlist=3 324 else if(match(words[w+1],"-item")) 325 optlist=4 326 else if(match(words[w+1],"-bullet")) 327 optlist=1 328 w=nwords 329 } else if(match(words[w],"^El$")) { 330 if (bl_level > 1) 331 add(".RE") 332 --bl_level 333 optlist=oldoptlist 334 } else if(match(words[w],"^Bk$")) { 335 if(match(words[w+1],"-words")) { 336 w++ 337 breakw=1 338 } 339 } else if(match(words[w],"^Ek$")) { 340 breakw=0 341 } else if(match(words[w],"^It$")&&optlist) { 342 if(optlist==1) 343 add(".IP \\(bu") 344 else if(optlist==2) 345 add(".IP " ++enum ".") 346 else if(optlist==3) { 347 add(".TP") 348 prenl++ 349 if(match(words[w+1],"^Pa$|^Ev$")) { 350 add(".B") 351 w++ 352 } 353 } else if(optlist==4) 354 add(".IP") 355 } else if(match(words[w],"^Sm$")) { 356 if(match(words[w+1],"off")) 357 nospace=2 358 else if(match(words[w+1],"on")) 359 nospace=0 360 w++ 361 } else if(match(words[w],"^Lb$")) { 362 wtail() 363 add("Command Line Editor Library (libedit, -ledit)") 364 } else if(match(words[w],"^In$")) { 365 add(".PP\n") 366 add("\\fB#include <" wtail() ">\\fP") 367 } else if(match(words[w],"^Ft$")) { 368 add(".PP\n") 369 add("\\fI" wtail() "\\fP\n") 370 add(".br") 371 proto=1 372 } else if(match(words[w],"^Fn$")) { 373 add("\\fB" words[++w] "\\fP(") 374 punct=0 375 while(++w<=nwords) { 376 if(match(words[w], "^\".*\"$")) { 377 sub("^\"", "", words[w]) 378 sub("\"$", "", words[w]) 379 add("\\fI" words[w] "\\fP") 380 if (w!=nwords) { 381 add(", ") 382 } 383 } else if(match(words[w], "^\"")) { 384 sub("^\"", "", words[w]) 385 add("\\fI" words[w] " ") 386 } else if (match(words[w], "\"$")) { 387 sub("\"$", "", words[w]) 388 add(words[w] "\\fP") 389 if (w!=nwords) { 390 add(", ") 391 } 392 } else { 393 if (w==nwords&&(match(words[w], "^[.,]$"))) { 394 punct=1 395 } else { 396 add(words[w] " ") 397 } 398 399 } 400 } 401 add(")") 402 if (punct==1) { 403 add(words[w-1]) 404 } else { 405 if (proto==1) { 406 add(";") 407 proto=0 408 } 409 } 410 } else if(match(words[w],"^Fa$")) { 411 punct=0 412 add("\\fI") 413 while(++w<=nwords) { 414 if(match(words[w], "^\".*\"$")) { 415 sub("^\"", "", words[w]) 416 sub("\"$", "", words[w]) 417 add(words[w]) 418 } else if(match(words[w], "^\"")) { 419 sub("^\"", "", words[w]) 420 add(words[w] " ") 421 } else if (match(words[w], "\"$")) { 422 sub("\"$", "", words[w]) 423 add(words[w]) 424 } else { 425 if (w==nwords&&(match(words[w], "^[.,]$"))) { 426 punct=1 427 } else { 428 if (w+1==nwords&&(match(words[w+1], "^[.,]$"))) { 429 add(words[w]) 430 } else { 431 add(words[w] " ") 432 } 433 } 434 } 435 } 436 add("\\fP") 437 if (punct==1) { 438 add(words[w-1]) 439 } 440 } else if(!skip) { 441 add(words[w]) 442 } 443 } 444 if(match(line,"^\\.[^a-zA-Z]")) 445 sub("^\\.","",line) 446 if(parens) 447 add(")") 448 if(angles) 449 add(">") 450 if(option) 451 add("]") 452 if(ext&&!extopt&&!match(line," $")) 453 add(OFS) 454 if(!ext&&!extopt&&length(line)) { 455 print line 456 prenl=0 457 line="" 458 } 459} 460