xref: /freebsd/contrib/kyua/doc/manbuild.sh (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
1*b0d29bc4SBrooks Davis#! /bin/sh
2*b0d29bc4SBrooks Davis# Copyright 2014 The Kyua Authors.
3*b0d29bc4SBrooks Davis# All rights reserved.
4*b0d29bc4SBrooks Davis#
5*b0d29bc4SBrooks Davis# Redistribution and use in source and binary forms, with or without
6*b0d29bc4SBrooks Davis# modification, are permitted provided that the following conditions are
7*b0d29bc4SBrooks Davis# met:
8*b0d29bc4SBrooks Davis#
9*b0d29bc4SBrooks Davis# * Redistributions of source code must retain the above copyright
10*b0d29bc4SBrooks Davis#   notice, this list of conditions and the following disclaimer.
11*b0d29bc4SBrooks Davis# * Redistributions in binary form must reproduce the above copyright
12*b0d29bc4SBrooks Davis#   notice, this list of conditions and the following disclaimer in the
13*b0d29bc4SBrooks Davis#   documentation and/or other materials provided with the distribution.
14*b0d29bc4SBrooks Davis# * Neither the name of Google Inc. nor the names of its contributors
15*b0d29bc4SBrooks Davis#   may be used to endorse or promote products derived from this software
16*b0d29bc4SBrooks Davis#   without specific prior written permission.
17*b0d29bc4SBrooks Davis#
18*b0d29bc4SBrooks Davis# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19*b0d29bc4SBrooks Davis# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20*b0d29bc4SBrooks Davis# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21*b0d29bc4SBrooks Davis# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22*b0d29bc4SBrooks Davis# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23*b0d29bc4SBrooks Davis# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24*b0d29bc4SBrooks Davis# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25*b0d29bc4SBrooks Davis# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26*b0d29bc4SBrooks Davis# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27*b0d29bc4SBrooks Davis# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28*b0d29bc4SBrooks Davis# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29*b0d29bc4SBrooks Davis
30*b0d29bc4SBrooks Davis# \file doc/manbuild.sh
31*b0d29bc4SBrooks Davis# Generates a manual page from a source file.
32*b0d29bc4SBrooks Davis#
33*b0d29bc4SBrooks Davis# Input files can have __VAR__-style patterns in them that are replaced
34*b0d29bc4SBrooks Davis# with the values provided by the caller via the -v VAR=VALUE flag.
35*b0d29bc4SBrooks Davis#
36*b0d29bc4SBrooks Davis# Input files can also include other files using the __include__ directive,
37*b0d29bc4SBrooks Davis# which takes a relative path to the file to include plus an optional
38*b0d29bc4SBrooks Davis# collection of additional variables to replace in the included file.
39*b0d29bc4SBrooks Davis
40*b0d29bc4SBrooks Davis
41*b0d29bc4SBrooks Davis# Name of the running program for error reporting purposes.
42*b0d29bc4SBrooks DavisProg_Name="${0##*/}"
43*b0d29bc4SBrooks Davis
44*b0d29bc4SBrooks Davis
45*b0d29bc4SBrooks Davis# Prints an error message and exits.
46*b0d29bc4SBrooks Davis#
47*b0d29bc4SBrooks Davis# Args:
48*b0d29bc4SBrooks Davis#   ...: The error message to print.  Multiple arguments are joined with a
49*b0d29bc4SBrooks Davis#       single space separator.
50*b0d29bc4SBrooks Daviserr() {
51*b0d29bc4SBrooks Davis    echo "${Prog_Name}: ${*}" 1>&2
52*b0d29bc4SBrooks Davis    exit 1
53*b0d29bc4SBrooks Davis}
54*b0d29bc4SBrooks Davis
55*b0d29bc4SBrooks Davis
56*b0d29bc4SBrooks Davis# Invokes sed(1) translating input variables to expressions.
57*b0d29bc4SBrooks Davis#
58*b0d29bc4SBrooks Davis# Args:
59*b0d29bc4SBrooks Davis#   ...: List of var=value pairs to replace.
60*b0d29bc4SBrooks Davis#
61*b0d29bc4SBrooks Davis# Returns:
62*b0d29bc4SBrooks Davis#   True if the operation succeeds; false otherwise.
63*b0d29bc4SBrooks Davissed_with_vars() {
64*b0d29bc4SBrooks Davis    local vars="${*}"
65*b0d29bc4SBrooks Davis
66*b0d29bc4SBrooks Davis    set --
67*b0d29bc4SBrooks Davis    for pair in ${vars}; do
68*b0d29bc4SBrooks Davis        local var="$(echo "${pair}" | cut -d = -f 1)"
69*b0d29bc4SBrooks Davis        local value="$(echo "${pair}" | cut -d = -f 2-)"
70*b0d29bc4SBrooks Davis        set -- "${@}" -e"s&__${var}__&${value}&g"
71*b0d29bc4SBrooks Davis    done
72*b0d29bc4SBrooks Davis
73*b0d29bc4SBrooks Davis    if [ "${#}" -gt 0 ]; then
74*b0d29bc4SBrooks Davis        sed "${@}"
75*b0d29bc4SBrooks Davis    else
76*b0d29bc4SBrooks Davis        cat
77*b0d29bc4SBrooks Davis    fi
78*b0d29bc4SBrooks Davis}
79*b0d29bc4SBrooks Davis
80*b0d29bc4SBrooks Davis
81*b0d29bc4SBrooks Davis# Generates the manual page reading from stdin and dumping to stdout.
82*b0d29bc4SBrooks Davis#
83*b0d29bc4SBrooks Davis# Args:
84*b0d29bc4SBrooks Davis#   include_dir: Path to the directory containing the include files.
85*b0d29bc4SBrooks Davis#   ...: List of var=value pairs to replace in the manpage.
86*b0d29bc4SBrooks Davis#
87*b0d29bc4SBrooks Davis# Returns:
88*b0d29bc4SBrooks Davis#   True if the generation succeeds; false otherwise.
89*b0d29bc4SBrooks Davisgenerate() {
90*b0d29bc4SBrooks Davis    local include_dir="${1}"; shift
91*b0d29bc4SBrooks Davis
92*b0d29bc4SBrooks Davis    while :; do
93*b0d29bc4SBrooks Davis        local read_ok=yes
94*b0d29bc4SBrooks Davis        local oldifs="${IFS}"
95*b0d29bc4SBrooks Davis        IFS=
96*b0d29bc4SBrooks Davis        read -r line || read_ok=no
97*b0d29bc4SBrooks Davis        IFS="${oldifs}"
98*b0d29bc4SBrooks Davis        [ "${read_ok}" = yes ] || break
99*b0d29bc4SBrooks Davis
100*b0d29bc4SBrooks Davis        case "${line}" in
101*b0d29bc4SBrooks Davis            __include__*)
102*b0d29bc4SBrooks Davis                local file="$(echo "${line}" | cut -d ' ' -f 2)"
103*b0d29bc4SBrooks Davis                local extra_vars="$(echo "${line}" | cut -d ' ' -f 3-)"
104*b0d29bc4SBrooks Davis                # If we fail to output the included file, just leave the line as
105*b0d29bc4SBrooks Davis                # is.  validate_file() will later error out.
106*b0d29bc4SBrooks Davis                [ -f "${include_dir}/${file}" ] || echo "${line}"
107*b0d29bc4SBrooks Davis                generate <"${include_dir}/${file}" "${include_dir}" \
108*b0d29bc4SBrooks Davis                    "${@}" ${extra_vars} || echo "${line}"
109*b0d29bc4SBrooks Davis                ;;
110*b0d29bc4SBrooks Davis
111*b0d29bc4SBrooks Davis            *)
112*b0d29bc4SBrooks Davis                echo "${line}"
113*b0d29bc4SBrooks Davis                ;;
114*b0d29bc4SBrooks Davis        esac
115*b0d29bc4SBrooks Davis    done | sed_with_vars "${@}"
116*b0d29bc4SBrooks Davis}
117*b0d29bc4SBrooks Davis
118*b0d29bc4SBrooks Davis
119*b0d29bc4SBrooks Davis# Validates that the manual page has been properly generated.
120*b0d29bc4SBrooks Davis#
121*b0d29bc4SBrooks Davis# In particular, this checks if any directives or common replacement patterns
122*b0d29bc4SBrooks Davis# have been left in place.
123*b0d29bc4SBrooks Davis#
124*b0d29bc4SBrooks Davis# Returns:
125*b0d29bc4SBrooks Davis#   True if the manual page is valid; false otherwise.
126*b0d29bc4SBrooks Davisvalidate_file() {
127*b0d29bc4SBrooks Davis    local filename="${1}"
128*b0d29bc4SBrooks Davis
129*b0d29bc4SBrooks Davis    if grep '__[A-Za-z0-9]*__' "${filename}" >/dev/null; then
130*b0d29bc4SBrooks Davis        return 1
131*b0d29bc4SBrooks Davis    else
132*b0d29bc4SBrooks Davis        return 0
133*b0d29bc4SBrooks Davis    fi
134*b0d29bc4SBrooks Davis}
135*b0d29bc4SBrooks Davis
136*b0d29bc4SBrooks Davis
137*b0d29bc4SBrooks Davis# Program entry point.
138*b0d29bc4SBrooks Davismain() {
139*b0d29bc4SBrooks Davis    local vars=
140*b0d29bc4SBrooks Davis
141*b0d29bc4SBrooks Davis    while getopts :v: arg; do
142*b0d29bc4SBrooks Davis        case "${arg}" in
143*b0d29bc4SBrooks Davis            v)
144*b0d29bc4SBrooks Davis                vars="${vars} ${OPTARG}"
145*b0d29bc4SBrooks Davis                ;;
146*b0d29bc4SBrooks Davis
147*b0d29bc4SBrooks Davis            \?)
148*b0d29bc4SBrooks Davis                err "Unknown option -${OPTARG}"
149*b0d29bc4SBrooks Davis                ;;
150*b0d29bc4SBrooks Davis        esac
151*b0d29bc4SBrooks Davis    done
152*b0d29bc4SBrooks Davis    shift $((${OPTIND} - 1))
153*b0d29bc4SBrooks Davis
154*b0d29bc4SBrooks Davis    [ ${#} -eq 2 ] || err "Must provide input and output names as arguments"
155*b0d29bc4SBrooks Davis    local input="${1}"; shift
156*b0d29bc4SBrooks Davis    local output="${1}"; shift
157*b0d29bc4SBrooks Davis
158*b0d29bc4SBrooks Davis    trap "rm -f '${output}.tmp'" EXIT HUP INT TERM
159*b0d29bc4SBrooks Davis    generate "$(dirname "${input}")" ${vars} \
160*b0d29bc4SBrooks Davis        <"${input}" >"${output}.tmp" \
161*b0d29bc4SBrooks Davis        || err "Failed to generate ${output}"
162*b0d29bc4SBrooks Davis    if validate_file "${output}.tmp"; then
163*b0d29bc4SBrooks Davis        :
164*b0d29bc4SBrooks Davis    else
165*b0d29bc4SBrooks Davis        err "Failed to generate ${output}; some patterns were left unreplaced"
166*b0d29bc4SBrooks Davis    fi
167*b0d29bc4SBrooks Davis    mv "${output}.tmp" "${output}"
168*b0d29bc4SBrooks Davis}
169*b0d29bc4SBrooks Davis
170*b0d29bc4SBrooks Davis
171*b0d29bc4SBrooks Davismain "${@}"
172