|
#!/bin/bash
##---------------------------------------------------------------------------##
#
# Script: jman
# Author: Brian <genius@groupbcl.ca> :)
# Date: June 2001 (as 'cman', renamed to 'jman' in July 2004)
#
# jman produces a simple text-only version of a given man page, stripping
# out backspaces and BS-underscore combinations, generates a table of
# contents, and displays the page based on the value of JMAN_VIEWER.
#
# Parameters:
# -l spaces
# Number of leading spaces to use for indexing. Default 7.
# -w output_width
# Output width. Default 72.
# -m
# Generate output based only on the man page and ignore any info page
# -f file
# Generate output from file instead of searching the man pages
#
# Hint: If you use "jman mail", it works best with "-l 5". Likewise,
# "jman -l 8 perlfunc" will generate a useful index of all the perl
# functions.
#
##---------------------------------------------------------------------------##
#
# JMAN_VIEWER environment variable:
# * If unset, defaults to the value of $EDITOR, $PAGER, or 'less', in that
# order
# * If set to 'vi' or 'vim' (directly or via $EDITOR), uses 'view' so that
# the page file is read-only
# * If set to 'save_to_file', saves the formatted page as a file in the
# current directory (added February 2012)
#
##---------------------------------------------------------------------------##
# BUUS: This script is part of Brian's Useful Utilities Set
##---------------------------------------------------------------------------##
# help: display help and exit
##---------------------------------------------------------------------------##
function help {
echo "usage: jman [-l spaces] [-w output_width] [-m] [-f man_file]"
echo
echo "Parameters:"
echo " -l spaces"
echo " Number of leading spaces to use for indexing. Default 7."
echo " -w output_width"
echo " Output width. Default 72."
echo " -m "
echo " Generate output based only on the man page and ignore any info page"
echo " -f file"
echo " Generate output from file instead of searching the man pages"
echo
echo "JMAN_VIEWER environment variable:"
echo "* If unset, defaults to the value of \$EDITOR, \$PAGER, or 'less', in that"
echo " order"
echo "* If set to 'vi' or 'vim' (directly or via \$EDITOR), uses 'view' so that"
echo " the page file is read-only"
echo "* If set to 'save_to_file', saves the formatted page as a file in the"
echo " current directory"
echo
exit 0
}
##---------------------------------------------------------------------------##
# GenInfoPage - generate useable output from 'info'
# Returns with code 109 (ASCII "m", meaning "Try 'man' instead") if topic
# is not found or info indicates it processed a manpage
##---------------------------------------------------------------------------##
function GenInfoPage {
echo " * Generating info page"
INFO_FN="$(locate "$MAN_COMMAND.info" | grep -v -- '-[0-9]')"
if [ ! "$INFO_FN" ]; then RC=109; return; fi
info --subnodes $INFO_FN 2>/dev/null | awk -v man_command=$MAN_COMMAND '
BEGIN {
text_idx=0 # For indexing text array
toc_idx=0 # For indexing toc_text and toc_line arrays
}
#--- Bail immediately if this is a manpage ---
/^File: \*manpages\*/ { exit 109 } # 109 = ASCII "m" ("try man command instead")
#--- Lines beginning with "File: " and "info: Writing node" are skipped
/^(File: |info: Writing node)/{ next }
#--- Push the current line on to the text array ---
{ text[text_idx++] = $0 }
#--- Lines consisting of ***, ===, --- mark level 1, 2, and 3 headers ---
/^(\*|=|-)+$/{
toc_line[toc_idx] = text_idx-2
if ($0 ~ /^\*/) { foofix = ""; level_1_count++ }
if ($0 ~ /^=/) { foofix = " " }
if ($0 ~ /^-/) { foofix = " " }
toc_text[toc_idx] = foofix text[text_idx-2]
toc_idx++
}
END {
if (toc_idx > 0) {
print toupper(man_command) " Documentation"
print substr("**********************************************", 1, length(man_command)+14)
print ""
print "Table of Contents"
print "================="
for (i=0; i<toc_idx; i++) {
if (toc_text[i] !~ /^ / ) { print "" }
printf(" %5i. %s\n", toc_line[i]+toc_idx+level_1_count+6, toc_text[i])
}
for (i=0; i<text_idx; i++) { print text[i] }
} else {
exit 109 # No info page by this name, try "man"
}
}
' > $MAN_FN
RC=$?
}
##---------------------------------------------------------------------------##
# ProcManFile - processes a generated man page file
##---------------------------------------------------------------------------##
function ProcManFile {
echo " * Processing file"
#--- Get the manpage section from the output ---
SECTION="$(head -n 20 $TEMP1_FN | awk '{
if (match($0, /\(([[:alnum:]]+)\)[[:space:]]*$/, a)) { print a[1]; exit }
}' $TEMP1_FN)"
echo $SECTION
[ -z "$SECTION" ] && SECTION="x"
MAN_FN="${MAN_FN/.t/.${SECTION}.t}"
#----- Do some formatting on the file -----
# sed:
# Remove character-backspace pairs
# Change octal 255 characters to dashes
# grep:
# Remove error messages generated by nroff
# awk:
# Remove initial blank line
# Remove " |" at the end of lines (tcl man pages)
# Strip out multiple zero-length lines
#
sed "s/.${BS}//g" $TEMP1_FN |
grep -v -e "<standard input>:[0-9]*:" -e "^Reformatting " |
awk -v SW=0 '
length > 0 { SW=1 }
SW {
if (substr($0,length,1)=="|") {
i=length; x=0
while (i>1) { c=substr($0,i,1); if (c!=" " && c!="|") {x=i;i=0}; i-- }
if (x > 0) $0=substr($0,1,x)
}
print
}
length == 0 { SW = 0 }' >$TEMP2_FN
mv $TEMP2_FN $TEMP1_FN
#----- Generate a table of contents -----
# tcl man pages (section n) have special processing for SubTOC lines; that is,
# lines that describe actions for the main keyword (eg, keyword is "string",
# actions are "length", "first", "range", "index", etc)
echo " * Generating table of contents"
echo -e "\nCONTENTS" >$TEMP2_FN
echo " Line Section" >>$TEMP2_FN
SUB_TOC=""
[ "$SECTION" = n ] && SUB_TOC="-v SubTOC=$MAN_COMMAND -v SubTOC_Sw=0 "
awk -v X=0 -v NameSw=0 -v KeywordLead=$KEYWORD_LEAD $SUB_TOC '
BEGIN { KeywordSw = 0 }
{ LineNum++ }
length > 1 {
if (X++>2 && KeywordSw) { #(X is used to filter out the first two matching lines)
LeadChars=substr($0,1,KeywordLead)
Spaces=substr(" ",1,KeywordLead)
Ulines=substr("____________________",1,KeywordLead)
if (LeadChars != Spaces && LeadChars != Ulines) {
if (length <= 60) {
printf("__TOC__ %4i %s\n",NR,$0)
} else {
KeywordSw=0
}
}
}
if (SubTOC != "") {
# Throw away all references until DESCRIPTION is encountered
if ($1=="DESCRIPTION") SubTOC_Sw=1
# SubTOC lines can span two lines (argh!), so look to see if I have
# to add to the line or can output it now
if (SubTOC_Line != "") {
if (substr($0,8,1)==" ") {
if (length(SubTOC_Line) < 100) print SubTOC_Line
SubTOC_Line=""
} else {
SubTOC_Line=SubTOC_Line " " substr($0,8)
}
} else {
# Start a SubTOC line if the topic word is found in column 8
if (substr($0,8,length(SubTOC)+1) == SubTOC " ") {
SubTOC_Line=sprintf("__TOC__ %4i %s",NR,substr($0,8))
if (! SubTOC_Sw) SubTOC_Line=""
}
}
}
}
/^ *$/{ KeywordSw=1 }
' $TEMP1_FN >>$TEMP2_FN
#----- Update the line numbers to account for the index itself -----
awk -v Offset=$(echo $(cat $TEMP2_FN|wc -l)) '{
if ($1 == "__TOC__") $0=sprintf(" %4i %s",$2+Offset,substr($0,14))
print
}' $TEMP2_FN >$CONTENTS_FN
#----- Find the end of the NAME section in the manpage file -----
HEAD_COUNT="$(head -n 20 $TEMP1_FN | awk '
/^NAME|^Name/{NameSw=1}
length == 0 { if (NameSw) print NR-1; NameSw=0 }
')"
#----- Insert the index file into the manpage file -----
head -n $HEAD_COUNT $TEMP1_FN >$MAN_FN
cat $CONTENTS_FN >>$MAN_FN
tail -n +$[HEAD_COUNT+1] $TEMP1_FN >>$MAN_FN
rm $TEMP1_FN $TEMP2_FN $CONTENTS_FN
}
##---------------------------------------------------------------------------##
# M A I N P R O C E S S I N G
##---------------------------------------------------------------------------##
[ "$1" == '-h' -o "$1" == '--help' ] && help
[ -d /tmp ] && TEMP_DN='/tmp' || TEMP_DN='/tmp'
JMAN_DN="$TEMP_DN/jman"
[ -d $JMAN_DN ] || mkdir $JMAN_DN
TEMP1_FN=$TEMP_DN/jman.$$.1
TEMP2_FN=$TEMP_DN/jman.$$.2
CONTENTS_FN=$TEMP_DN/jman.$$.contents
BS=$'\010'
EMDASH=$'\255'
# export MANWIDTH=$[$(stty -a|head -n 1|cut -f7 -d" "|cut -f1 -d";") + 8]
export MANWIDTH=72
KEYWORD_LEAD=7 # Number of blanks before a keyword in the manpage
if [ -z "$1" ]
then
echo "usage: jman [-l keyword_lead] [-w output_width] [section] manpage"
exit 1
fi
#--- Parse the command line parameters -----
ERROR_SW=false
INFO_SW=true
for PARM in $*
do
if [ "$(expr substr "$PARM" 1 1)" = "-" ]
then
case "$(expr substr "$PARM" 2 1)" in
f) NEXT_PARM='FILENAME';;
l) NEXT_PARM="KEYWORD_LEAD";;
m) INFO_SW=false;;
w) NEXT_PARM="MANWIDTH";;
*) echo "jman: $PARM: unknown switch" >&2; ERROR_SW=true;;
esac
elif [ "$NEXT_PARM" ]
then
[ $NEXT_PARM = "FILENAME" ] && FILENAME="$PARM"
[ $NEXT_PARM = "KEYWORD_LEAD" ] && KEYWORD_LEAD=$PARM
[ $NEXT_PARM = "MANWIDTH" ] && MANWIDTH=$PARM
NEXT_PARM=""
elif [ -z "$WORD_1" ]
then
WORD_1=$PARM
elif [ -z "$WORD_2" ]
then
WORD_2=$PARM
else
echo "jman: $PARM: bad parameter" >&2
ERROR_SW=true
fi
done
# If we have only WORD_1, it's the command the user wants the man page for
[ -z "$WORD_2" ] && MAN_COMMAND=$WORD_1
# If we have WORD_2 and WORD_2, it's section and man page
if [ -n "$WORD_1" -a -n "$WORD_2" ]
then
MAN_SECTION=$WORD_1
MAN_COMMAND=$WORD_2
INFO_SW=false
fi
if [ "$FILENAME" ]
then
MAN_SECTION='-l'
MAN_COMMAND="$FILENAME"
fi
#echo "WORD_1=$WORD_1"
#echo "WORD_2=$WORD_2"
#echo "MAN_SECTION=$MAN_SECTION"
#echo "MAN_COMMAND=$MAN_COMMAND"
#echo "MANWIDTH=$MANWIDTH"
#echo "KEYWORD_LEAD=$KEYWORD_LEAD"
#pause
#--- Figure out if the user has his own doc, man, or tmp/temp dirs ---
for DIR in temp tmp doc man
do
[ -d "$HOME/$DIR" ] && HPATH="$HOME/$DIR"
done
# (The man section is figured out in ProcManFile and is added there)
MAN_FN="${JMAN_DN}/${MAN_COMMAND}.text"
#--- Check to see if we have an 'info' page on this topic ---
if $INFO_SW
then GenInfoPage # Returns '109' if we have a man page
else RC=109
fi
#--- Generate the man page ---
if [ $RC = 109 ]
then
echo " * Generating man page file"
rm -f $MAN_FN # Left over from GenInfoPage
if man $MAN_SECTION $MAN_COMMAND &>$TEMP1_FN
then
ProcManFile
else
cat $TEMP1_FN
rm -f $TEMP1_FN
fi
fi
#--- View the resulting file, if we have one ---
if [ -f $MAN_FN ]
then
if [ -z "$JMAN_VIEWER" ]
then
JMAN_VIEWER=$EDITOR
[ -z "$JMAN_VIEWER" ] && JMAN_VIEWER=$PAGER
[ -z "$JMAN_VIEWER" ] && JMAN_VIEWER='less'
fi
if [ "$JMAN_VIEWER" = vim -o "$JMAN_VIEWER" = vi ]
then
JMAN_VIEWER="view -c 'syntax off'"
fi
# Hack for cygwin: change \ to / in JMAN_VIEWER
if [ "$OSTYPE" = cygwin ]
then
JMAN_VIEWER=${JMAN_VIEWER//\\/\/}
MAN_FN="C:/cygwin$MAN_FN"
"$JMAN_VIEWER" $MAN_FN
elif [ "$JMAN_VIEWER" = 'save_to_file' ]
then
SAVE_FN="$(basename $MAN_FN)"; SAVE_FN="${SAVE_FN/__/}"
echo " * Saving formatted man page to $SAVE_FN"
mv "$MAN_FN" "$SAVE_FN"
else
$SHELL -c "$JMAN_VIEWER $MAN_FN"
fi
fi
#----- Ask about keeping the output file (default is to delete it) -----
#if [ -f $MAN_FN ]
#then
# echo -en "Do you want to keep \"$MAN_FN\" (y/n)? n\010"; read YES_NO
# if [ "$YES_NO" != "y" ]
# then
# rm $MAN_FN
# echo "Deleted"
# else
# echo "Kept"
# fi
#fi
echo "Formatted man page is in $MAN_FN"
# vim: tabstop=4
|