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