1#!/usr/bin/awk 2# 3# $Id: mdoc2man.awk,v 1.9 2009/10/24 00:52:42 dtucker Exp $ 4# 5# Version history: 6# v4+ Adapted for OpenSSH Portable (see cvs Id and history) 7# v3, I put the program under a proper license 8# Dan Nelson <dnelson@allantgroup.com> added .An, .Aq and fixed a typo 9# v2, fixed to work on GNU awk --posix and MacOS X 10# v1, first attempt, didn't work on MacOS X 11# 12# Copyright (c) 2003 Peter Stuge <stuge-mdoc2man@cdy.org> 13# 14# Permission to use, copy, modify, and distribute this software for any 15# purpose with or without fee is hereby granted, provided that the above 16# copyright notice and this permission notice appear in all copies. 17# 18# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 19# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 20# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 21# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 22# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 23# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 24# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 25 26 27BEGIN { 28 optlist=0 29 oldoptlist=0 30 nospace=0 31 synopsis=0 32 reference=0 33 block=0 34 ext=0 35 extopt=0 36 literal=0 37 prenl=0 38 breakw=0 39 line="" 40} 41 42function wtail() { 43 retval="" 44 while(w<nwords) { 45 if(length(retval)) 46 retval=retval OFS 47 retval=retval words[++w] 48 } 49 return retval 50} 51 52function add(str) { 53 for(;prenl;prenl--) 54 line=line "\n" 55 line=line str 56} 57 58! /^\./ { 59 for(;prenl;prenl--) 60 print "" 61 print 62 if(literal) 63 print ".br" 64 next 65} 66 67/^\.\\"/ { next } 68 69{ 70 option=0 71 parens=0 72 angles=0 73 sub("^\\.","") 74 nwords=split($0,words) 75 for(w=1;w<=nwords;w++) { 76 skip=0 77 if(match(words[w],"^Li|Pf$")) { 78 skip=1 79 } else if(match(words[w],"^Xo$")) { 80 skip=1 81 ext=1 82 if(length(line)&&!(match(line," $")||prenl)) 83 add(OFS) 84 } else if(match(words[w],"^Xc$")) { 85 skip=1 86 ext=0 87 if(!extopt) 88 prenl++ 89 w=nwords 90 } else if(match(words[w],"^Bd$")) { 91 skip=1 92 if(match(words[w+1],"-literal")) { 93 literal=1 94 prenl++ 95 w=nwords 96 } 97 } else if(match(words[w],"^Ed$")) { 98 skip=1 99 literal=0 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=1 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],"^Ar$")) { 287 add("\\fI" words[++w] "\\fP") 288 } else if(match(words[w],"^[\\.,]")) { 289 sub(" $","",line) 290 if(plain) { 291 add("\\fP") 292 plain=0 293 } 294 add(words[w]) 295 } else { 296 if(!plain) { 297 add("\\fB") 298 plain=1 299 } 300 add(words[w]) 301 } 302 if(!nospace) 303 add(OFS) 304 } 305 sub(" $","",line) 306 if(plain) 307 add("\\fP") 308 } else if(match(words[w],"^Bl$")) { 309 oldoptlist=optlist 310 if(match(words[w+1],"-bullet")) 311 optlist=1 312 else if(match(words[w+1],"-enum")) { 313 optlist=2 314 enum=0 315 } else if(match(words[w+1],"-tag")) 316 optlist=3 317 else if(match(words[w+1],"-item")) 318 optlist=4 319 else if(match(words[w+1],"-bullet")) 320 optlist=1 321 w=nwords 322 } else if(match(words[w],"^El$")) { 323 optlist=oldoptlist 324 if(!optlist) 325 add(".PP") 326 } else if(match(words[w],"^Bk$")) { 327 if(match(words[w+1],"-words")) { 328 w++ 329 breakw=1 330 } 331 } else if(match(words[w],"^Ek$")) { 332 breakw=0 333 } else if(match(words[w],"^It$")&&optlist) { 334 if(optlist==1) 335 add(".IP \\(bu") 336 else if(optlist==2) 337 add(".IP " ++enum ".") 338 else if(optlist==3) { 339 add(".TP") 340 prenl++ 341 if(match(words[w+1],"^Pa$|^Ev$")) { 342 add(".B") 343 w++ 344 } 345 } else if(optlist==4) 346 add(".IP") 347 } else if(match(words[w],"^Sm$")) { 348 if(match(words[w+1],"off")) 349 nospace=2 350 else if(match(words[w+1],"on")) 351 nospace=0 352 w++ 353 } else if(!skip) { 354 add(words[w]) 355 } 356 } 357 if(match(line,"^\\.[^a-zA-Z]")) 358 sub("^\\.","",line) 359 if(parens) 360 add(")") 361 if(angles) 362 add(">") 363 if(option) 364 add("]") 365 if(ext&&!extopt&&!match(line," $")) 366 add(OFS) 367 if(!ext&&!extopt&&length(line)) { 368 print line 369 prenl=0 370 line="" 371 } 372} 373