bbl.sh (11834B)
1 #!/bin/sh 2 # bbl: Read the Holy Bible from your terminal 3 # License: Public domain 4 5 SELF="$0" 6 BIBLE="" 7 8 pr() { 9 printf '%s' "$@" 10 } 11 data_exists() { 12 sed '1,/^#EOF$/d' < "$SELF" | tar tz "$@" 13 } 14 ls_archive() { 15 # tar tz with no arguments just lists everything 16 data_exists 17 } 18 reading_exists() { 19 data_exists "$1.tsv" >/dev/null 2>&1 20 } 21 get_data() { 22 sed '1,/^#EOF$/d' < "$SELF" | tar xz -O "$@" 23 } 24 get_awk() { 25 get_data input.awk bbl.awk 26 } 27 get_data_if_exists() { 28 list=$(ls_archive) 29 existing_items=$(for arg in "$@"; do 30 echo "$list" | grep -x "$arg" 31 done) 32 [ -n "$existing_items" ] && get_data $existing_items 33 } 34 get_aliases() { 35 aliases="$1.aliases" 36 case "$1" in drb|grb|heb|kjv|knx|njb|rsv|vul) aliases="bibles.aliases $aliases";; esac 37 echo "$aliases" 38 } 39 get_reading() { 40 get_data_if_exists $(get_aliases "$1") "$1.tsv" 41 } 42 get_ref() { 43 # Thank you, StackExchange. This will cause $PAGER to give the same exit code 44 # that bbl would have given, so that a nonzero exit code can be used in scripts 45 # to know that the reference returned no results. 46 r="$1" shift 47 cr="$1" shift 48 { { { { get_reading "$r" | awk -v cmd=ref -v ref="$*" -v cross_ref="$cr" -v lang="$lang" "$(get_awk)"; echo $? >&3; } | ${PAGER} >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1 49 } 50 list_books() { 51 reading="$(echo "${BIBLE}" | cut -d " " -f 1)" 52 for f in "$reading.tsv" $(get_aliases "$reading"); do 53 get_data_if_exists "$f" 2>/dev/null | awk -v cmd=list "$(get_awk)" 54 done | ${PAGER} 55 exit 56 } 57 list_readings() { 58 sed '1,/^#EOF$/d' < "$SELF" | tar tz --wildcards "*.tsv" | sed 's/\.tsv$//' 59 exit 60 } 61 62 if [ ! -t 1 ]; then 63 # If output is not a terminal, prevent the default behavior of opening the data in the pager only to send it down the pipeline 64 PAGER="cat" 65 elif [ -z "$PAGER" ]; then 66 if command -v less >/dev/null; then 67 PAGER="less" 68 else 69 PAGER="cat" 70 fi 71 fi 72 73 show_help() { 74 exec >&2 75 echo "usage: $(basename "$0") [flags] [reference...]" 76 echo 77 echo " Flags:" 78 echo " -l, --list-books list book names (for the reading chosen)" 79 echo " -L, --list list options for readings (Vulgate, KJV, Latin poems, etc.)" 80 echo " -o <reading> choose a reading by name (i.e. by the name of the corresponding TSV file, sans file extension)" 81 echo " -W, --no-line-wrap no line wrap" 82 echo " -V, --no-verse-numbers no verse numbers are printed--just the book title at the top and a number for each chapter" 83 echo " -C, --no-ch-numbers no chapter headings either (implies -V)" 84 echo " -T, --no-title book title is not printed" 85 echo " -B, --no-verse-break No linebreaks at the end of each verse--each chapter runs like a continuous paragraph. Currently implies -V (I am working on changing that)" 86 echo " -N, --no-format Equivalent to -WCTB" 87 echo " -c, --cat echo text to STDOUT" 88 echo " -h, --help show help" 89 echo " Bibles:" 90 echo " -d, --douay Douay-Rheims Bible" 91 echo " -g, --greek Greek Bible (Septuagint + SBL NT)" 92 echo " -H, --hebrew The Bible in Hebrew (with cantillation marks and niqqudim)" 93 echo " -i, --ivrit The Bible in Hebrew without cantillation marks and niqqudim" 94 echo " -j, --jerusalem New Jerusalem Bible" 95 echo " -k, --kjv King James Bible" 96 echo " -n, --knox Knox Bible" 97 echo " -r, --rsv Revised Standard Version: Catholic Edition" 98 echo " -v, --vulgate Clementine Vulgate" 99 echo 100 echo "Specify multiple versions to cross-reference (view them in multi-column fashion)." 101 echo "This feature is not yet available for languages that are read right-to-left." 102 echo "Specifying -i or -H will currently override all other translations and output only the Hebrew Bible." 103 echo 104 echo " Reference types:" 105 echo " NOTE: The colon between book and chapter is required for Hebrew, optional for everything else." 106 echo " <Book> can refer either to the name of a book, or an alias referring to a list of books." 107 echo " Specify the -l flag to get list of both books and aliases" 108 echo " References for Hebrew must be in Hebrew; for all else, must be in English." 109 echo " *" 110 echo " Every book" 111 echo " <Book>" 112 echo " Individual book" 113 echo " <Book>:<Chapter>" 114 echo " Individual chapter of a book" 115 echo " <Book>:<Chapter>:<Verse>[,<Verse>]..." 116 echo " Individual verse(s) of a specific chapter of a book" 117 echo " <Book>:<Chapter>:<Verse>[,<Chapter>:<Verse>]..." 118 echo " Individual verses of different chapters of a book" 119 echo " <Book>:<Chapter>-<Chapter>" 120 echo " Range of chapters in a book" 121 echo " <Book>:<Chapter>:<Verse>-<Verse>" 122 echo " Range of verses in a book chapter" 123 echo " <Book>:<Chapter>:<Verse>-<Chapter>:<Verse>" 124 echo " Range of chapters and verses in a book" 125 echo 126 echo " /~?<Search>" 127 echo " All verses that match a pattern" 128 echo " <Book>/~?<Search>" 129 echo " All verses in a book that match a pattern" 130 echo " <Book>:<Chapter>/~?<Search>" 131 echo " All verses in a chapter of a book that match a pattern" 132 echo " In searches, the optional ~ indicates that the search should be approximate:" 133 echo " Case and accent marks will be disregarded. Note that this will often take" 134 echo " much longer than an exact search" 135 echo 136 echo " @ <Number-of-Verses>?" 137 echo " Random verse or assortment of verses from any book/chapter" 138 echo " <Book> @ <Number-of-Verses>?" 139 echo " Random verse or assortment of verses from any chapter in a given book" 140 echo " <Book>:<Chapter> @ <Number-of-Verses>?" 141 echo " Random verse or assortment of verses from the given book:chapter" 142 echo 143 echo " Exit code is 0 if no problems; 1 if cross-referencing and one or more references" 144 echo " returned nothing; 2 if no references returned anything." 145 } 146 147 set_bible() { 148 if [ -z "$BIBLE" ] || [ "$nocrossref" ]; then 149 BIBLE=$1 150 else 151 #For cross-referencing 152 BIBLE="$BIBLE $1" 153 fi 154 } 155 default_bible() { 156 [ "$DEFAULT_BIBLE" ] && BIBLE="$DEFAULT_BIBLE" || BIBLE="knx" 157 } 158 159 lang="en" # Language of text being used--most are English 160 list="" 161 nocrossref="" 162 opts="$(getopt -o lLo:WVCTBNchdgHijknrv -l list-books,list,no-line-wrap,no-verse-numbers,no-chapter-headings,no-title,no-verse-break,-no-format,cat,help,douay,greek,hebrew,ivrit,jerusalem,kjv,knox,rsv,vulgate -- "$@")" 163 eval set -- "$opts" 164 while [ $# -gt 0 ]; do 165 case $1 in 166 --) 167 shift 168 break;; 169 -l|--list-books) 170 # List all book names of the named reading with their abbreviations 171 list=1 172 shift ;; 173 -L|--list) 174 list_readings ;; 175 -o) 176 shift 177 nocrossref='y' 178 reading_exists "$1" && set_bible "$1" || 179 { echo "Error: $1.tsv not found."; exit 1 180 } 181 shift ;; 182 -W|--no-line-wrap) 183 export KJV_NOLINEWRAP=1 184 shift ;; 185 -V|--no-verse-numbers) 186 export KJV_NOVERSENUMBERS=1 187 shift ;; 188 -C|--no-chapter-headings) 189 export KJV_NOCHAPTERHEADINGS=1 190 shift ;; 191 -T|--no-title) 192 export KJV_NOTITLE=1 193 shift ;; 194 -B|--no-verse-break) 195 export KJV_NOVERSEBREAK=1 196 shift ;; 197 -N|--no-format) 198 export KJV_NOFORMAT=1 199 shift ;; 200 -c|--cat) 201 # Cat text to standard output instead of using $PAGER 202 PAGER="cat" 203 shift ;; 204 -h|--help) 205 show_help 206 exit 0 ;; 207 -d|--douay) 208 set_bible drb 209 shift ;; 210 -g|--greek) 211 set_bible grb 212 lang="el" 213 shift ;; 214 -H|--hebrew|-i|--ivrit) 215 nocrossref='y' 216 set_bible heb 217 lang="he" 218 # If reading in terminal, use `rev` to put it right-to-left 219 case $1 in 220 -i|--ivrit) 221 r2l="y";; 222 *) 223 [ "$UNICODE_TERM" ] || noTerm='y' ;; 224 esac 225 shift ;; 226 -j|--jerusalem) 227 set_bible njb 228 shift ;; 229 -k|--kjv) 230 set_bible kjv 231 shift ;; 232 -n|--knox) 233 set_bible knx 234 shift ;; 235 -r|--rsv) 236 set_bible rsv 237 shift ;; 238 -v|--vulgate) 239 set_bible vul 240 lang="la" 241 shift ;; 242 esac 243 done 244 245 [ -z "$BIBLE" ] && default_bible 246 247 [ "$list" ] && list_books 248 249 250 if cols=$(tput cols 2>/dev/null); then 251 versions=0 252 for b in $BIBLE; do 253 versions=$(( versions + 1 )) 254 done 255 spaceBetween=$(( 8 * (versions - 1))) 256 export KJV_MAX_WIDTH="$(( (cols - spaceBetween) / versions ))" 257 fi 258 259 if [ $# -eq 0 ]; then 260 if [ ! -t 0 ]; then 261 echo "Interactive mode cannot be used unless in a terminal" 262 exit 2 263 fi 264 265 # Interactive mode 266 b="$(echo "$BIBLE" | cut -d ' ' -f 1)" 267 while true; do 268 printf '%s> ' "$b" 269 if ! read -r ref; then 270 break 271 fi 272 get_ref "$b" "" "$ref" 273 done 274 exit 0 275 fi 276 277 i=0 278 tempDirPattern="${TMPDIR:-/tmp/}$(basename "$0")." 279 myTempDir=$(mktemp -d "${tempDirPattern}XXXXXXXXXXXX") 280 exitCode=0 281 atLeastOneSuccess='' 282 for version in $BIBLE; do 283 filename="${myTempDir}/${i}-${version}.txt" 284 get_ref "$version" "$i" "$@" > "$filename" 285 [ $? -ne 0 ] && exitCode=1 || atLeastOneSuccess='y' 286 i=$((i + 1)) 287 done 288 [ -z "$atLeastOneSuccess" ] && exitCode=2 289 290 if [ ${i} -gt 1 ]; then 291 filename="${myTempDir}/crossref.txt" 292 paste -d '@' $(ls -d "${myTempDir}/"*) | column -t -s "@" -o " " | sed '/^[a-zA-Z]/s/^/\t/;1s/^ *//;' > "$filename" 293 # Remove all files in the tmpdir with a hyphen in the name, i.e. everything except the file we just created 294 rm "$myTempDir"/*-* 295 else 296 lastLine="$(tail -n1 "${filename}")" 297 298 if echo "$lastLine" | grep -q '~~~RANDOMS:'; then 299 numberOfVerses=$(echo "${lastLine}" | grep -o '[0-9]*') 300 linesInFile=$(($(wc -l "$filename" | awk '{print $1}') - 1)) 301 sedCmd=$(shuf -i 1-$linesInFile -n "$numberOfVerses" | sort -n | tr '\n' ' ' | sed 's/ /p;/g' | sed 's/..$/{p;q}/') 302 sed -n "$sedCmd" "$filename" > "${myTempDir}/randomVerses" 303 awk -v cmd=clean "$(get_awk)" "${myTempDir}/randomVerses" 2>/dev/null > "${filename}" 304 fi 305 fi 306 307 if [ "$noTerm" ]; then 308 if command -v "$BROWSER" > /dev/null 2>&1; then 309 setsid -f "$BROWSER" "$filename" >/dev/null 2>&1 310 mv "$myTempDir" "${tempDirPattern}K" 311 else 312 echo "$0: Text may not be terminal-compatible and BROWSER environment variable is not set or cannot be invoked." 313 echo "To suppress this error and try to display on the terminal no matter what, set the environment" 314 echo "variable 'UNICODE_TERM' to a non-empty string" 315 exitCode=2 316 fi 317 elif [ "$r2l" ]; then 318 rev "${filename}" | $PAGER 319 else 320 $PAGER "$filename" 321 fi 322 323 rm -r "$tempDirPattern"??* 324 mv "${tempDirPattern}K" "$myTempDir" 2>/dev/null # Preserves browser-viewable files for only one invocation 325 exit "$exitCode"