#!/usr/bin/awk # # Copyright (c) 2003 Peter Stuge # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # Dramatically overhauled by Tim Kientzle. This version almost # handles library-style pages with Fn, Ft, etc commands. Still # a lot of problems... BEGIN { displaylines = 0 listdepth = 0 trailer = "" out = "" sep = "" nextsep = " " spaces = " " NORMAL_STATE = 0 PRETAG_STATE = 1 STATE = NORMAL_STATE } # Add a word with appropriate preceding whitespace # Maintain a short queue of the expected upcoming word separators. function add(str) { out=out sep str sep = nextsep nextsep = " " } # Add a word with no following whitespace # Use for opening punctuation such as '(' function addopen(str) { add(str) sep = "" } # Add a word with no preceding whitespace # Use for closing punctuation such as ')' or '.' function addclose(str) { sep = "" add(str) } # Add a word with no space before or after # Use for separating punctuation such as '=' function addpunct(str) { sep = "" add(str) sep = "" } # Emit the current line so far function endline() { addclose(trailer) trailer = "" if(length(out) > 0) { if (STATE == PRETAG_STATE) { print out } else { print out " " } out="" } if(displaylines > 0) { displaylines = displaylines - 1 if (displaylines == 0) dispend() } # First word on next line has no preceding whitespace sep = "" } function linecmd(cmd) { endline() add(cmd) endline() } function breakline() { linecmd("
") } function crossref(name, sect, other) { if (name == "cpio" && sect == 1) { n = "ManPageBsdcpio1" } else if (name == "cpio" && sect == 5) { n = "ManPageCpio5" } else if (name == "mtree" && sect == 5) { n = "ManPageMtree5" } else if (name == "tar" && sect == 1) { n = "ManPageBsdtar1" } else if (name == "tar" && sect == 5) { n = "ManPageTar5" } else if (!match(name, "^archive") && !match(name, "^libarchive")) { n = name "(" sect ")|http://www.freebsd.org/cgi/man.cgi?query=" name "&sektion=" sect } else { n = "ManPage" numbits = split(name, namebits, "[_-]") for (i = 1; i <= numbits; ++i) { p = namebits[i] n = n toupper(substr(p, 0, 1)) substr(p, 2) } n = n sect } n = "[[" n "]]" if (length other > 0) n = n other return n } # Start an indented display function dispstart() { endline() print "```text" } # End an indented display function dispend() { endline() print "```" } # Collect rest of input line function wtail() { retval="" while(w 0) { sub("^[ \t]*", "", l) if (match(l, "^\"")) { l = substr(l, 2) o = index(l, "\"") if (o > 0) { w = substr(l, 1, o-1) l = substr(l, o+1) dest[n++] = w } else { dest[n++] = l l = "" } } else { o = match(l, "[ \t]") if (o > 0) { w = substr(l, 1, o-1) l = substr(l, o+1) dest[n++] = w } else { dest[n++] = l l = "" } } } return n-1 } ! /^\./ { out = $0 endline() next } /^\.\\"/ { next } { gsub("\\\\e", "\\") sub("^\\.","") nwords=splitwords($0, words) # TODO: Instead of iterating 'w' over the array, have a separate # function that returns 'next word' and use that. This will allow # proper handling of double-quoted arguments as well. for(w=1;w<=nwords;w++) { if(match(words[w],"^Li$")) { # Literal; rest of line is unformatted dispstart() displaylines = 1 } else if(match(words[w],"^Dl$")) { # Display literal dispstart() displaylines = 1 } else if(match(words[w],"^Bd$")) { # Begin display STATE = PRETAG_STATE if(match(words[w+1],"-literal")) { dispstart() displaylines=10000 w=nwords } } else if(match(words[w],"^Ed$")) { # End display displaylines = 0 dispend() STATE = NORMAL_STATE } else if(match(words[w],"^Ns$")) { # Suppress space before next word sep="" } else if(match(words[w],"^No$")) { # Normal text add(words[++w]) } else if(match(words[w],"^Dq$")) { # Quote addopen("\"") add(words[++w]) while(w[") } else if(match(words[w],"^Oc$")) { addclose("]") } else if(match(words[w],"^Ao$")) { addopen("<") } else if(match(words[w],"^Ac$")) { addclose(">") } else if(match(words[w],"^Dd$")) { date=wtail() next } else if(match(words[w],"^Dt$")) { id=words[++w] "(" words[++w] ")" next } else if(match(words[w],"^Ox$")) { add("OpenBSD") } else if(match(words[w],"^Fx$")) { add("FreeBSD") } else if(match(words[w],"^Bx$")) { add("BSD") } else if(match(words[w],"^Nx$")) { add("NetBSD") } else if(match(words[w],"^St$")) { if (match(words[w+1], "^-p1003.1$")) { w++ add("IEEE Std 1003.1 (``POSIX.1'')") } else if(match(words[w+1], "^-p1003.1-96$")) { w++ add("ISO/IEC 9945-1:1996 (``POSIX.1'')") } else if(match(words[w+1], "^-p1003.1-88$")) { w++ add("IEEE Std 1003.1-1988 (``POSIX.1'')") } else if(match(words[w+1], "^-p1003.1-2001$")) { w++ add("IEEE Std 1003.1-2001 (``POSIX.1'')") } else if(match(words[w+1], "^-susv2$")) { w++ add("Version 2 of the Single UNIX Specification (``SUSv2'')") } } else if(match(words[w],"^Ex$")) { if (match(words[w+1], "^-std$")) { w++ add("The '''" name "''' utility exits 0 on success, and >0 if an error occurs.") } } else if(match(words[w],"^Os$")) { add(id " manual page") } else if(match(words[w],"^Sh$")) { section=wtail() linecmd("== " section " ==") } else if(match(words[w],"^Xr$")) { add(crossref(words[w+1], words[w+2], words[w+3])) w = w + 3 } else if(match(words[w],"^Nm$")) { if(match(section,"SYNOPSIS")) breakline() if(w >= nwords) n=name else if (match(words[w+1], "^[A-Z][a-z]$")) n=name else if (match(words[w+1], "^[.,;:]$")) n=name else { n=words[++w] if(!length(name)) name=n } if(!length(n)) n=name if (displaylines == 0) add("'''" n "'''") else add(n) } else if(match(words[w],"^Nd$")) { add("- " wtail()) } else if(match(words[w],"^Fl$")) { addopen("-") } else if(match(words[w],"^Ar$")) { if(w==nwords) add("''file ...''") else { ++w gsub("<", "\\<", words[w]) if (displaylines > 0) add(words[w]) else add("''" words[w] "''") } } else if(match(words[w],"^Cm$")) { ++w if (displaylines == 0) { add("'''" words[w] "'''") } else add(words[w]) } else if(match(words[w],"^Op$")) { addopen("[") option=1 trailer="]" trailer } else if(match(words[w],"^Pp$")) { ++w endline() print "" } else if(match(words[w],"^An$")) { if (match(words[w+1],"-nosplit")) ++w endline() } else if(match(words[w],"^Ss$")) { add("===") trailer="===" } else if(match(words[w],"^Ft$")) { if (match(section, "SYNOPSIS")) { breakline() } l = wtail() add("''" l "''") if (match(section, "SYNOPSIS")) { breakline() } } else if(match(words[w],"^Fn$")) { ++w F = "'''" words[w] "'''(" Fsep = "" while(w#include <" words[w] ">'''") } else if(match(words[w],"^Pa$")) { w++ # if(match(words[w],"^\\.")) # add("\\&") if (displaylines == 0) add("''" words[w] "''") else add(words[w]) } else if(match(words[w],"^Dv$")) { linecmd() } else if(match(words[w],"^Em|Ev$")) { add(".IR") } else if(match(words[w],"^Pq$")) { addopen("(") trailer=")" trailer } else if(match(words[w],"^Aq$")) { addopen(" <") trailer=">" trailer } else if(match(words[w],"^Brq$")) { addopen("{") trailer="}" trailer } else if(match(words[w],"^S[xy]$")) { add(".B " wtail()) } else if(match(words[w],"^Tn$")) { n=wtail() add("'''" n "'''") } else if(match(words[w],"^Ic$")) { add("''") trailer="''" trailer } else if(match(words[w],"^Bl$")) { ++listdepth listnext[listdepth]="" if(match(words[w+1],"-bullet")) { optlist[listdepth]=1 addopen("
    ") listclose[listdepth]="
" } else if(match(words[w+1],"-enum")) { optlist[listdepth]=2 enum=0 addopen("
    ") listclose[listdepth]="
" } else if(match(words[w+1],"-tag")) { optlist[listdepth]=3 addopen("
") listclose[listdepth]="
" } else if(match(words[w+1],"-item")) { optlist[listdepth]=4 addopen("
    ") listclose[listdepth]="
" } w=nwords } else if(match(words[w],"^El$")) { addclose(listnext[listdepth]) addclose(listclose[listdepth]) listclose[listdepth]="" listdepth-- } else if(match(words[w],"^It$")) { addclose(listnext[listdepth]) if(optlist[listdepth]==1) { addpunct("
  • ") listnext[listdepth] = "
  • " } else if(optlist[listdepth]==2) { addpunct("
  • ") listnext[listdepth] = "
  • " } else if(optlist[listdepth]==3) { addpunct("
    ") listnext[listdepth] = "
    " if(match(words[w+1],"^Xo$")) { # Suppress trailer w++ } else if(match(words[w+1],"^Pa$|^Ev$")) { addopen("'''") w++ add(words[++w] "'''") trailer = listnext[listdepth] "
    " trailer listnext[listdepth] = "
    " } else { trailer = listnext[listdepth] "
    " trailer listnext[listdepth] = "
    " } } else if(optlist[listdepth]==4) { addpunct("
  • ") listnext[listdepth] = "
  • " } } else if(match(words[w], "^Vt$")) { w++ add("''" words[w] "''") } else if(match(words[w],"^Xo$")) { # TODO: Figure out how to handle this } else if(match(words[w],"^Xc$")) { # TODO: Figure out how to handle this if (optlist[listdepth] == 3) { addclose(listnext[listdepth]) addopen("
    ") listnext[listdepth] = "
    " } } else if(match(words[w],"^[=]$")) { addpunct(words[w]) } else if(match(words[w],"^[[{(]$")) { addopen(words[w]) } else if(match(words[w],"^[\\])}.,;:]$")) { addclose(words[w]) } else { sub("\\\\&", "", words[w]) add(words[w]) } } if(match(out,"^\\.[^a-zA-Z]")) sub("^\\.","",out) endline() }