2016-01-26 04:55:35 +00:00
|
|
|
#! /bin/bash
|
|
|
|
#
|
2016-01-27 19:23:10 +00:00
|
|
|
# -- Program to locate all DocBook and LinuxDoc files in The Linux
|
|
|
|
# Documentation Project (TLDP) Version Control System (VCS) and
|
|
|
|
# produce HTML, PDF and text outputs (with companion Makefile)
|
2016-01-26 04:55:35 +00:00
|
|
|
#
|
2016-01-27 19:23:10 +00:00
|
|
|
# -- License: GPLv2
|
|
|
|
#
|
|
|
|
# -- written for TLDP in January 2016; Martin A. Brown <martin@linux-ip.net>
|
|
|
|
#
|
|
|
|
|
|
|
|
set -e
|
|
|
|
# set -x
|
|
|
|
|
|
|
|
SELFNAME="$( readlink --canonicalize ${0})"
|
|
|
|
ME="${SELFNAME##*/}" # -- basename
|
|
|
|
DIR="${SELFNAME%/*}" # -- dirname
|
|
|
|
MAKEFILE="${DIR}/Makefile"
|
2016-01-26 04:55:35 +00:00
|
|
|
|
2016-01-29 05:51:33 +00:00
|
|
|
SOURCETREE="${1:-$HOME/vcs/LDP/LDP}" && shift
|
|
|
|
SOURCETREE="$( readlink --canonicalize $SOURCETREE )"
|
2016-01-26 04:55:35 +00:00
|
|
|
|
2016-01-29 05:51:33 +00:00
|
|
|
OUTPUTTREE="${1:-$PWD/output}" && shift
|
|
|
|
OUTPUTTREE="$( readlink --canonicalize $OUTPUTTREE )"
|
2016-01-26 04:55:35 +00:00
|
|
|
|
2016-01-29 05:51:33 +00:00
|
|
|
SOURCEDIRS=""
|
|
|
|
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/faq/linuxdoc"
|
|
|
|
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/guide/linuxdoc"
|
|
|
|
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/howto/linuxdoc"
|
|
|
|
|
|
|
|
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/faq/docbook"
|
|
|
|
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/guide/docbook"
|
|
|
|
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/howto/docbook"
|
|
|
|
SOURCEDIRS="${SOURCEDIRS} ${SOURCETREE}/ref/docbook"
|
2016-01-26 04:55:35 +00:00
|
|
|
|
|
|
|
# -- my favorite shell functions (and their offspring)
|
|
|
|
#
|
|
|
|
gripe () { printf >&2 "%s\n" "$@"; }
|
|
|
|
debug () { : ; } # -- NOOP for now
|
|
|
|
notice () { gripe "${ME}[$$]: $@"; }
|
|
|
|
abort () {
|
|
|
|
EXIT_CODE="${1}" && shift;
|
|
|
|
gripe "E: $@";
|
|
|
|
exit $EXIT_CODE;
|
|
|
|
}
|
|
|
|
|
|
|
|
# -- defaults for logging output; override these in environment
|
|
|
|
# if you care
|
|
|
|
#
|
|
|
|
: "${LOG_FACILITY:=daemon}"
|
|
|
|
: "${LOG_PRIO:=err}"
|
|
|
|
: "${LOGGER_OPTS:=-t ${ME}[$$] -p ${LOG_FACILITY}.${LOG_PRIO}}"
|
|
|
|
|
|
|
|
# -- if we are connected to a terminal, then send logging output
|
|
|
|
# to STDERR, as well
|
|
|
|
|
|
|
|
# -- create logger as a shell function
|
|
|
|
#
|
|
|
|
logger () {
|
|
|
|
tty -s && LOGGER_OPTS="-s $LOGGER_OPTS"
|
|
|
|
command logger $LOGGER_OPTS -- "$@" ;
|
|
|
|
}
|
|
|
|
|
|
|
|
usage () {
|
|
|
|
gripe "usage: ${ME} [<ldp_checkout> [<destdir>]]" \
|
2016-01-29 05:51:33 +00:00
|
|
|
" default ldp_checkout=${SOURCETREE}" \
|
|
|
|
" default destdir=${OUTPUTTREE}" \
|
2016-01-26 04:55:35 +00:00
|
|
|
""
|
|
|
|
abort 1 "$@";
|
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
function select_source_files () {
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
|
|
|
# -- return a list of filenames in any directory which are (likely)
|
|
|
|
# to be documents for TLDP
|
|
|
|
# args 1: file extension sought
|
|
|
|
# args +2: base directories in which to seek
|
|
|
|
|
|
|
|
local ext=${1} && shift
|
|
|
|
|
|
|
|
for d in $@; do
|
|
|
|
|
|
|
|
test -e "$d" || abort 2 "ENOENT (2): No such file or directory: ${d}"
|
|
|
|
test -d "$d" || abort 20 "ENOTDIR (20): Not a directory: ${d}";
|
|
|
|
|
|
|
|
# -- locate each solo text document, e.g. XWindow-User-HOWTO.xml
|
|
|
|
#
|
|
|
|
find "$d" -mindepth 1 -maxdepth 1 -type f -name \*."${ext}" -printf "%p\n"
|
|
|
|
|
|
|
|
# -- locate each document owning a directory, e.g. 8021X-HOWTO/8021X-HOWTO.sgml
|
|
|
|
#
|
|
|
|
{
|
|
|
|
find "$d" -mindepth 1 -maxdepth 1 -type d -printf "%p\n" | while read DIR; do
|
|
|
|
|
|
|
|
# -- get just the stem of the document name from the directory, e.g. 8021X-HOWTO
|
|
|
|
stem=${DIR##*/}
|
|
|
|
|
|
|
|
# -- construct an abspath, e.g. /full/path/to/8021X-HOWTO/8021X-HOWTO.sgml
|
|
|
|
doc="${DIR}/${stem}.${ext}"
|
|
|
|
|
|
|
|
# -- see if file exists, and if so, print its name
|
2016-01-27 19:23:10 +00:00
|
|
|
# File existence test logic is reversed, because this script runs
|
|
|
|
# with "set -e". If the test for existence fails, then the script
|
|
|
|
# stops (this should be familiar to those who have written
|
|
|
|
# Makefiles).
|
|
|
|
#
|
|
|
|
test ! -e "${doc}" || printf "%s\n" "${doc}"
|
2016-01-26 04:55:35 +00:00
|
|
|
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
2016-01-27 19:23:10 +00:00
|
|
|
# - - - - - - - - - - - -
|
|
|
|
list_output_documents () {
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
|
|
|
# -- by definition, each directory in the output tree
|
|
|
|
# is named by the STEM, so this produces a list of the
|
|
|
|
# stem names of the documents that have been produced
|
|
|
|
#
|
|
|
|
find "$@" -mindepth 1 -maxdepth 1 -printf "%P\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
list_source_documents () {
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
|
|
|
select_source_files xml "$@";
|
|
|
|
select_source_files sgml "$@";
|
|
|
|
}
|
|
|
|
|
2016-01-26 04:55:35 +00:00
|
|
|
# - - - - - - - - - - - -
|
|
|
|
get_stem () {
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
2016-01-27 19:23:10 +00:00
|
|
|
# -- filter filenames and produce stem names
|
|
|
|
# - no args
|
|
|
|
# - STDIN: A filename (can be full filename)
|
|
|
|
# - STDOUT: Just the stem name.
|
|
|
|
#
|
|
|
|
while read FULLNAME ; do
|
|
|
|
fn="${FULLNAME##*/}"
|
|
|
|
printf "%s\n" "${fn%.*}";
|
|
|
|
done
|
2016-01-26 04:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
sort_by_stem () {
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
|
|
|
# -- typical decorate, sort, undecorate pattern, though,
|
|
|
|
# admittedly, not common in shell
|
|
|
|
# - no args
|
|
|
|
# - STDIN: stream of strings with full path
|
|
|
|
# - STDOUT: stem-sorted stream of strings with full path
|
|
|
|
#
|
|
|
|
{
|
|
|
|
{
|
|
|
|
while read OBJ; do
|
2016-01-27 19:23:10 +00:00
|
|
|
stem=$( printf "%s\n" "$OBJ" | get_stem )
|
2016-01-26 04:55:35 +00:00
|
|
|
printf "%s %s\n" "${stem}" "$OBJ"
|
|
|
|
done;
|
|
|
|
} | sort;
|
|
|
|
} | awk '{print $2}';
|
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
find_sort_by_mtime () {
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
|
|
|
local SOURCE="${1}" && shift
|
|
|
|
local OUTPUT="${1}" && shift
|
|
|
|
{
|
|
|
|
# -- directory age does not count, only file age!
|
|
|
|
#
|
|
|
|
test -e "${SOURCE}" \
|
|
|
|
|| abort 2 "ENOENT (2): we thought this directory just existed: ${SOURCE}"
|
|
|
|
find "$SOURCE" -type f -printf "%T@ SOURCE %p\n";
|
|
|
|
|
|
|
|
test -d "${OUTPUT}" \
|
|
|
|
&& find "$OUTPUT" -type f -printf "%T@ OUTPUT %p\n";
|
|
|
|
|
|
|
|
} | sort -gr;
|
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - -
|
2016-01-27 19:23:10 +00:00
|
|
|
function is_output_current () {
|
2016-01-26 04:55:35 +00:00
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
|
|
|
# -- return a list of filenames in any directory which are (likely)
|
|
|
|
# to be documents for TLDP
|
|
|
|
# args 1: input file name
|
2016-01-29 05:51:33 +00:00
|
|
|
# args 2: OUTPUTTREE
|
2016-01-26 04:55:35 +00:00
|
|
|
#
|
|
|
|
# -- there are two (probable) cases for the input, A) a single standalone
|
|
|
|
# file or B) the main document source file in a dedicated directory;
|
|
|
|
# below is a description of the logic....
|
|
|
|
#
|
|
|
|
# Case A) single standalone file; we don't want to find all files from
|
|
|
|
# the parent directory
|
|
|
|
#
|
|
|
|
# OBJ=/home/fred/vcs/LDP/LDP/howto/docbook/Software-Release-Practice-HOWTO.xml
|
2016-01-29 05:51:33 +00:00
|
|
|
# OUTPUTTREE=/home/fred/wip/tldp/output
|
2016-01-26 04:55:35 +00:00
|
|
|
# fn=Software-Release-Practice-HOWTO.xml
|
|
|
|
# stem=Software-Release-Practice-HOWTO
|
|
|
|
# parentdir=/home/fred/vcs/LDP/LDP/howto/docbook
|
|
|
|
# parentstem=docbook
|
|
|
|
#
|
|
|
|
# Case B) main document source file in a dedicated directory; we do want
|
|
|
|
# to look for all files starting from the parent directory
|
|
|
|
#
|
|
|
|
# OBJ=/home/fred/vcs/LDP/LDP/howto/docbook/Jabber-Server-Farming-HOWTO/Jabber-Server-Farming-HOWTO.xml
|
2016-01-29 05:51:33 +00:00
|
|
|
# OUTPUTTREE=/home/fred/wip/tldp/output
|
2016-01-26 04:55:35 +00:00
|
|
|
# fn=Jabber-Server-Farming-HOWTO.xml
|
|
|
|
# stem=Jabber-Server-Farming-HOWTO
|
|
|
|
# parentdir=/home/fred/vcs/LDP/LDP/howto/docbook/Jabber-Server-Farming-HOWTO
|
|
|
|
# parentstem=Jabber-Server-Farming-HOWTO
|
|
|
|
#
|
|
|
|
local stem="${1}" && shift
|
|
|
|
local OBJ="${1}" && shift
|
2016-01-29 05:51:33 +00:00
|
|
|
local OUTPUTTREE="${1}" && shift
|
2016-01-26 04:55:35 +00:00
|
|
|
|
|
|
|
local parentdir="${OBJ%/*}"
|
|
|
|
local parentstem="${parentdir##*/}"
|
|
|
|
|
|
|
|
startloc="$OBJ"
|
|
|
|
test "${stem}" == "${parentstem}" \
|
|
|
|
&& startloc="${parentdir}"
|
|
|
|
|
2016-01-29 05:51:33 +00:00
|
|
|
local outdir="${OUTPUTTREE}/${stem}"
|
2016-01-26 04:55:35 +00:00
|
|
|
|
|
|
|
result=$( find_sort_by_mtime "$startloc" "$outdir" ) \
|
|
|
|
|| abort 1 "Could not retrieve files for $stem sorted by mtime."
|
|
|
|
newest=$( head -n 1 <<<"$result" ) \
|
|
|
|
|| abort 1 "Could not get first result for $stem files."
|
|
|
|
|
|
|
|
DECISION=$( sed -e '/ OUTPUT /,$d' <<<"$newest" ) \
|
|
|
|
|| abort 1 "Could not check if the SOURCE files are newest...."
|
|
|
|
|
|
|
|
if test -z "$DECISION" ; then
|
|
|
|
debug "${stem} No new source files"
|
2016-01-27 19:23:10 +00:00
|
|
|
return 0
|
2016-01-26 04:55:35 +00:00
|
|
|
else
|
|
|
|
|
|
|
|
# -- squawk about it, to the user
|
|
|
|
#
|
|
|
|
while read TS SOURCE FNAME ; do
|
|
|
|
ts=$( date +%F-%T --date @"$TS" )
|
|
|
|
notice "${stem} updated file found ($ts): $FNAME"
|
|
|
|
done <<< "$DECISION"
|
|
|
|
|
2016-01-27 19:23:10 +00:00
|
|
|
return 1
|
2016-01-26 04:55:35 +00:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
generate_new_content () {
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
|
|
|
local stem="${1}" && shift
|
|
|
|
local OBJ="${1}" && shift
|
2016-01-29 05:51:33 +00:00
|
|
|
local OUTPUTTREE="${1}" && shift
|
2016-01-26 04:55:35 +00:00
|
|
|
#local makefile="${1}" && shift
|
|
|
|
|
|
|
|
# -- make a stab at processing the document, but don't let the driver
|
|
|
|
# stop just because a single document has an error
|
|
|
|
#
|
|
|
|
{
|
|
|
|
{
|
|
|
|
exec 2>&1;
|
2016-01-29 05:51:33 +00:00
|
|
|
make -f "$MAKEFILE" OBJ="${OBJ}" DESTDIR="${OUTPUTTREE}";
|
2016-01-26 04:55:35 +00:00
|
|
|
test $? -eq 0 \
|
|
|
|
|| gripe "FAILURE" "FAILURE producing all outputs from $OBJ" "FAILURE";
|
|
|
|
} | sed -ue "s/^/${stem} /";
|
|
|
|
} | command logger -s ${LOGGER_OPTS} --
|
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - -
|
2016-01-27 19:23:10 +00:00
|
|
|
list_orphaned_outputs () {
|
2016-01-26 04:55:35 +00:00
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
2016-01-29 05:51:33 +00:00
|
|
|
local OUTPUTTREE="${1}" && shift
|
|
|
|
local SOURCEDIRS="$@"
|
2016-01-27 19:23:10 +00:00
|
|
|
{
|
2016-01-29 05:51:33 +00:00
|
|
|
SOURCEDOCS=$( list_source_documents "${SOURCEDIRS}" | get_stem | sort )
|
|
|
|
OUTPUTDOCS=$( list_output_documents "${OUTPUTTREE}" | sort )
|
2016-01-27 19:23:10 +00:00
|
|
|
|
|
|
|
# -- list documents in source tree that do not have corresponding
|
|
|
|
# output directory
|
|
|
|
#
|
|
|
|
diff -Nur -- <( printf "%s\n" "$SOURCEDOCS" ) <( printf "%s\n" "$OUTPUTDOCS" ) \
|
|
|
|
| tail -n +2 \
|
|
|
|
| grep -- '^-' \
|
|
|
|
| sed -e 's|^-|Unbuilt source document |';
|
|
|
|
|
|
|
|
# -- list documents in output tree that do not have corresponding
|
|
|
|
# input directory
|
|
|
|
#
|
|
|
|
diff -Nur -- <( printf "%s\n" "$OUTPUTDOCS" ) <( printf "%s\n" "$SOURCEDOCS" ) \
|
|
|
|
| tail -n +2 \
|
|
|
|
| grep -- '^-' \
|
|
|
|
| sed -e 's|^-|Extra output document |';
|
|
|
|
|
|
|
|
} | command logger -s ${LOGGER_OPTS} --;
|
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
produce_ldp_outputs () {
|
|
|
|
# - - - - - - - - - - - -
|
|
|
|
#
|
2016-01-29 05:51:33 +00:00
|
|
|
local OUTPUTTREE="${1}" && shift
|
|
|
|
local SOURCEDIRS="$@"
|
|
|
|
list_source_documents "$SOURCEDIRS" | sort_by_stem | while read OBJ; do
|
2016-01-26 04:55:35 +00:00
|
|
|
|
2016-01-27 19:23:10 +00:00
|
|
|
stem=$( printf "%s\n" "$OBJ" | get_stem )
|
2016-01-26 04:55:35 +00:00
|
|
|
|
|
|
|
notice "${stem} checking if rebuild required for $OBJ"
|
|
|
|
|
|
|
|
# -- N.B. there's a dangerous looking ||: at the end of the line
|
|
|
|
# calling the Makefile. This is intentional. The Makefile
|
|
|
|
# may not be able to process the input successfully, but we
|
|
|
|
# would like to continue, if possible, and process the next
|
|
|
|
# available document.
|
|
|
|
#
|
2016-01-29 05:51:33 +00:00
|
|
|
is_output_current "${stem}" "$OBJ" "$OUTPUTTREE" \
|
|
|
|
|| (generate_new_content "${stem}" "${OBJ}" "${OUTPUTTREE}" || :)
|
2016-01-26 04:55:35 +00:00
|
|
|
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
# main
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
2016-01-29 05:51:33 +00:00
|
|
|
test -d "${OUTPUTTREE}" \
|
2016-01-26 04:55:35 +00:00
|
|
|
|| usage "Output directory must already exist, quitting."
|
|
|
|
|
2016-01-29 05:51:33 +00:00
|
|
|
produce_ldp_outputs "$OUTPUTTREE" "$SOURCEDIRS"
|
2016-01-27 19:23:10 +00:00
|
|
|
|
2016-01-29 05:51:33 +00:00
|
|
|
list_orphaned_outputs "$OUTPUTTREE" "$SOURCEDIRS"
|
2016-01-26 04:55:35 +00:00
|
|
|
|
|
|
|
# -- end of file
|