mdoc2man.awk 8.4 KB

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