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