#!/usr/bin/mawk -f # md2man based on txt2man-1.5 # Copyright (C) 2001, 2002, 2003 Marc Vertes # Licensed under GPL v2 # # md2man version: very raw alpha # # fixes: # v mawk compatible # v setex-styled headers # v asciidoc-alike subheaders (starting with ".") function reset_section() { ls = 0 # line start index pls = 0 # previous line start index pnzls = 0 # previous non zero line start index ni = 0 # indent level ind[0] = 0 # indent offset table prevblankline = 0 text="" } function process_section( header ) { if ((in_bd + 0) == 1) { in_bd = 0 print ".fam T\n.fi" } if (section == "SYNOPSIS") { print ".fam T\n.fi" type["SYNOPSIS"] = "" } if (header ~/^[^ ]/) print ".SH " header else print ".SS" header sub(/^ +/, "") section = header if (section == "SYNOPSIS") print ".nf\n.fam C" reset_section() } BEGIN { print ".\\\" Text automatically generated by md2man " "uname -s" | getline source manual = source " Reference Manual" title = title ? title : ARGV[1]; sub(".*/","",title); sub("\\.md$","",title) section = title; sub(".*\\.","",section); sub("\\..*","",title) "LC_ALL=C.UTF-8 date -u +'%B %d, %Y'" | getline date print ".TH " title " " section " \"" date "\" \"" source "\" \"" manual "\"" word="[0-9a-zA-Z_]+" reset_section() } # comment /^#/ { next } { # to avoid some side effects in regexp sub(/\.\.\./, "\\.\\.\\.") # remove spaces in empty lines sub(/^ +$/,"") } # setex-style header text && /^=+$/ { process_section( toupper(text) ); next } text && /^-+$/ { process_section( toupper(text) ); next } # Section header /^[A-Z][A-Z ]+$/ { process_section( $0 ); next } # asciidoc-alike subheader /^\.[a-zA-Z]/ { process_section( " " substr($0,2) ); next } { # Compute line start index, handle start of example display block pls = ls if (ls != 0) pnzls = ls match($0, /[^ ]/) ls = RSTART # if (pls == 0 && pnzls > 0 && ls >= ( pnzls + 4 ) && $1 !~ /^[0-9\-\*\o]/) { if (pls == 0 && ls >= ( pls + 4 ) && $1 !~ /^[0-9\-\*\o]/) { # example display block if (prevblankline == 1) { print ".PP" prevblankline = 0 } print ".nf\n.fam C" in_bd = 1 # eoff = ls } if (ls > 0 && ind[0] == 0) ind[0] = ls } in_bd { # In example display block if (ls < 4) { # End of litteral display block in_bd = 0 print ".fam T\n.fi" } else { print; next } } section == "NAME" && NF > 1 { $1 = "\\fB" $1 sub(/ - /, " \\fP- ") print; next } section == "SYNOPSIS" && ! /^ *$/ { # Identify arguments of fcts and cmds if (type["SYNOPSIS"] == "") { if (index($0, "(") == 0 && index($0, ")") == 0 && index($0, "#include") == 0) type["SYNOPSIS"] = "cmd" else type["SYNOPSIS"] = "fct" } if (type["SYNOPSIS"] == "cmd") { # Line is a command line if ($1 !~ /^\[/) { b = $1 sub(/^\*/, "", b) subwords[b] = "\\fB" b "\\fP" } for (i = 2; i <= NF; i++) { a = $i gsub(/[\[\]\|]/, "", a) if (a ~ /^[^\-]/) subwords[a] = "\\fI" a "\\fP" } } else { # Line is a C function definition if ($1 == "typedef") subwords["\\<" $2 "\\>"] = "\\fI" $2 "\\fP" else if ($1 == "#define") subwords["\\<" $2 "\\>"] = "\\fI" $2 "\\fP" for (i = 1; i <= NF; i++) { if ($i ~ /[\,\)]/) { a = $i sub(/.*\(/, "", a) gsub(/\W/, "", a) if (a !~ /^void$/) subwords["\\<" a "\\>"] = "\\fI" a "\\fP" } } } } { # protect dots inside words while ($0 ~ word "\\." word ) sub(/\./, "~dOt~") # identify func calls and cross refs for (i = 1; i <= NF; i++) { b = $i sub(/^\*/, "", b) if ((a = index(b, ")(")) > 3) { w = substr(b, 3, a - 3) subwords[w] = "\\fI" w "\\fP" } if ((a = index(b, "(")) > 1) { w = substr(b, 1, a - 1) subwords[w "\\("] = "\\fB" w "\\fP(" } } # terrible workaround for lack of mawk \B regexp # spaces are not preserved literally gsub(/ +/, " ~sp4ce~ ") for (i=1; i<=NF; i++) { # shell options if ($i ~ /^[^a-zA-Z0-9][-a-zA-Z0-9]/ ) sub("-+[a-zA-Z][-a-zA-Z0-9_]*", "\\fB&\\fP", $i) # word attributes for (k in subwords) sub("[^-a-zA-Z0-9_]" k "|^" k, subwords[k], $i) # italic if ($i ~ "^_.*_$") $i = "\\fI" substr($i,2,length($i)-2) "\\fP" # bold if ($i ~ "^\\*.*\\*$") $i = "\\fB" substr($i,2,length($i)-2) "\\fP" } gsub(/~sp4ce~/, " ") # unprotect dots inside words gsub(/~dOt~/, ".") # tag list item if (match($0, /[^ ] +/) > 0) { if (text) { print text; text = "" } tag = substr($0, 1, RSTART) sub(/^ */, "", tag) if (RSTART+RLENGTH < length($0)) $0 = substr($0, RSTART + RLENGTH) else $0 = "" print ".TP\n.B" print tag prevblankline = 0 if (NF == 0) next # bullet list item } else if ($1 == "-"||$1 == "o"||$1 == "*") { if (text) { print text; text = "" } print ".IP \\(bu 3" prevblankline = 0 $1 = "" # enum list item } else if ($1 ~ /^[0-9]+[\).]$/) { if (text) { print text; text = "" } print ".IP " $1 " 4" prevblankline = 0 $1 = "" # new paragraph } else if (pls == 0) { if (section != "SYNOPSIS" || $0 ~ /^ ? ? ?/) sub(/ */,"") text = (text ? text " " : "") $0 if (prevblankline == 1) { print ".PP" prevblankline = 0 } next # blank line } else if (NF == 0) { if (text) { print text; text = "" } #if (prevblankline == 0) { # print ".PP" #} prevblankline = 1 next } else prevblankline = 0 # flush vertical space if (prevblankline == 1) { print ".PP" prevblankline = 0 } if (text) { print text; text = "" } if (section != "SYNOPSIS" || $0 ~ /^ ? ? ?/) sub(/ */,"") print }