bbl

Read, search and index the Bible on the command line -- Greek, Latin, KJV, Knox, RSV, and more
git clone git://git.wilsonrgheen.com/bbl
Log | Files | Refs | README | LICENSE

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"