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.awk (13541B)


      1 BEGIN {
      2 	#  $1 Book name
      3 	#  $2 Book abbreviation
      4 	#  $3 Book number
      5 	#  $4 Chapter number
      6 	#  $5 Verse number
      7 	#  $6 Verse
      8 	FS = "\t"
      9 
     10     header_ended = 0
     11     outputted_records = 0
     12 	MAX_WIDTH = envint("KJV_MAX_WIDTH", 80, 8, 80)
     13     NO_LINE_WRAP = envbool("KJV_NOLINEWRAP")
     14     NO_VERSE_NUMBERS = envbool("KJV_NOVERSENUMBERS")
     15     NO_CHAPTER_HEADINGS = envbool("KJV_NOCHAPTERHEADINGS")
     16     NO_TITLE = envbool("KJV_NOTITLE")
     17     NO_VERSE_BREAK = envbool("KJV_NOVERSEBREAK")
     18     if (NO_VERSE_BREAK) {
     19         NO_VERSE_NUMBERS = 1
     20     }
     21     if(envbool("KJV_NOFORMAT")) {
     22         NO_LINE_WRAP = 1
     23         NO_CHAPTER_HEADINGS = 1
     24         NO_TITLE = 1
     25         NO_VERSE_BREAK = 1
     26     }
     27 
     28     if (!is_set(cmd)) {
     29         cmd = "list"
     30     }
     31 
     32 	if (cmd == "ref") {
     33         if (lang == "he") {
     34                 re["num"] = "[ק-ת]?([יכלמנסעפצ]?[א-ט]?|טו|טז)"
     35                 re["book"] = "([א-ת]+ )?[א-ת]+"
     36                 re["chsep"] = ":"
     37         }
     38         else {
     39                 re["num"] = "[1-9]+[0-9]*"
     40                 re["book"] = "[1-9]?[a-zA-Z ]+"
     41                 re["chsep"] = ":?"
     42         }
     43 		mode = parseref(p, ref)
     44 	}
     45 }
     46 
     47 cmd == "clean" {
     48     processline()
     49 }
     50 
     51 cmd == "list" {
     52     if (/^#/) {
     53         next
     54     }
     55 	if (!($2 in seen_books)) {
     56 		printf("%s (%s)\n", $1, $2)
     57 		seen_books[$2] = 1
     58 	}
     59 }
     60 
     61 function parseref(arr, q,       i) {
     62     # NOTE: For Hebrew, the colon between book and chapter is required
     63 	# 0. *
     64 	# 1. <book>
     65 	# 1a. <book>[, ?<book>]...
     66 	# 2. <book>:?<chapter>
     67 	# 2a. <book>:?<chapter>[, ?<chapter>]...
     68 	# 3. <book>:?<chapter>:<verse>
     69 	# 3a. <book>:?<chapter>:<verse>[, ?<verse>]...
     70 	# 3b. <book>:?<chapter>:<verse>[, ?<chapter>:<verse>]...
     71 	# 4. <book>:?<chapter>-<chapter>
     72 	# 5. <book>:?<chapter>:<verse>-<verse>
     73 	# 6. <book>:?<chapter>[:<verse>]-<chapter>:<verse>
     74 	# 7. /~?<search>
     75 	# 8. <book>/~?search
     76 	# 8a. <book>[, ?<book>].../~?search
     77 	# 9. <book>:?<chapter>/~?search
     78     #10. @ <number of verses>?
     79     #11. <book> @ <number of verses>?
     80     #12. <book>:?<chapter> @ <number of verses>?
     81 
     82     if (q == "*") {
     83         return "all"
     84     }
     85 	if (match(q, re["book"]) == 1) {
     86             # 1, 1a, 2, 2a, 3, 3a, 3b, 4, 5, 6, 8, 9, 11, 12
     87             arr["book", cleanbook(substr(q, 1, RLENGTH))] = 1
     88             q = substr(q, RLENGTH + 1)
     89 	} else if (sub("^ */ *", "", q)) {
     90             # 7
     91             if (sub("^~ *", "", q)) {
     92                 arr["search"] = roughpattern(q)
     93                 return "rough_search"
     94             } else {
     95                 arr["search"] = q
     96                 return "search"
     97             }
     98 	}
     99 
    100 	if (match(q, sprintf("^%s%s", re["chsep"], re["num"]))) {
    101             # 2, 2a, 3, 3a, 3b, 4, 5, 6, 9, 12
    102             if (sub("^:", "", q)) {
    103                     arr["chapter"] = num(substr(q, 1, RLENGTH - 1))
    104                     q = substr(q, RLENGTH)
    105             } else {
    106                     arr["chapter"] = num(substr(q, 1, RLENGTH))
    107                     q = substr(q, RLENGTH + 1)
    108             }
    109 	} else if (sub("^ */ *", "", q)) {
    110             # 8
    111             if (sub("^~ *", "", q)) {
    112                 arr["search"] = roughpattern(q)
    113                 return "rough_search"
    114             } else {
    115                 arr["search"] = q
    116                 return "search"
    117             }
    118 	} else if (match(q, sprintf("^(,%s)+", re["book"]))) {
    119             # 1a
    120             # TODO make compatible with chapter/verse/searches etc.
    121             split(q, temp_arr, ",")
    122             for (i in temp_arr) {
    123                 if (temp_arr[i] != "") {
    124                     arr["book", cleanbook(temp_arr[i])] = 1
    125                 }
    126             }
    127             return "exact"
    128 	} else if (q == "") {
    129             # 1
    130             return "exact"
    131 	}
    132 
    133 	if (match(q, sprintf("^:%s", re["num"]))) {
    134             # 3, 3a, 3b, 5, 6
    135             arr["verse"] = num(substr(q, 2, RLENGTH - 1))
    136             q = substr(q, RLENGTH + 1)
    137 	} else if (match(q, sprintf("^-%s$", re["num"]))) {
    138             # 4
    139             arr["chapter_end"] = num(substr(q, 2))
    140             return "range"
    141 	} else if (sub("^ */ *", "", q)) {
    142             # 9
    143             if (sub("^~ *", "", q)) {
    144                 arr["search"] = roughpattern(q)
    145                 return "rough_search"
    146             } else {
    147                 arr["search"] = q
    148                 return "search"
    149             }
    150 	} else if (q == "") {
    151             # 2
    152             return "exact"
    153 	} else if (match(q, sprintf("^(, ?%s)+$", re["num"]))) {
    154             # 2a
    155             arr["chapter", arr["chapter"]] = 1
    156             delete arr["chapter"]
    157             while (match(q, sprintf("^, ?%s", re["num"]))) {
    158                     if(sub("^, ", "", q)) {
    159                         arr["chapter", substr(q, 1, RLENGTH - 2)] = 1
    160                         q = substr(q, RLENGTH - 1)
    161                     } else {
    162                         arr["chapter", substr(q, 2, RLENGTH - 1)] = 1
    163                         q = substr(q, RLENGTH + 1)
    164                     }
    165             }
    166 
    167             if (q != "") {
    168                     return "unknown"
    169             }
    170 
    171             return "exact_ch_set"
    172 	} else if (match(q, "^ *@ *")) {
    173             # 10, 11, 12
    174             q = substr(q, RLENGTH + 1)
    175             if (match(q, sprintf("^%s", re["num"]))) {
    176                 arr["numberOfVerses"] = num(q)
    177             } else {
    178                 arr["numberOfVerses"] = 1
    179             }
    180             NO_LINE_WRAP = 1
    181             return "random"
    182 	}
    183 
    184 	if (match(q, sprintf("^-%s$", re["num"]))) {
    185             # 5
    186             arr["verse_end"] = num(substr(q, 2))
    187             return "range"
    188 	} else if (match(q, sprintf("-%s", re["num"]))) {
    189             # 6
    190             arr["chapter_end"] = num(substr(q, 2, RLENGTH - 1))
    191             q = substr(q, RLENGTH + 1)
    192 	} else if (q == "") {
    193             # 3
    194             return "exact"
    195 	} else if (match(q, sprintf("^(, ?%s)+$", re["num"]))) {
    196             # 3a
    197             arr["verse", arr["verse"]] = 1
    198             delete arr["verse"]
    199             while (match(q, sprintf("^, ?%s", re["num"]))) {
    200                     if(sub("^, ", "", q)) {
    201                         arr["verse", substr(q, 1, RLENGTH - 2)] = 1
    202                         q = substr(q, RLENGTH - 1)
    203                     } else {
    204                         arr["verse", substr(q, 2, RLENGTH - 1)] = 1
    205                         q = substr(q, RLENGTH + 1)
    206                     }
    207             }
    208 
    209             if (q != "") {
    210                     return "unknown"
    211             }
    212 
    213             return "exact_set"
    214         } else if (match(q, sprintf("^, ?%s:%s", re["num"], re["num"]))) {
    215             # 3b
    216             arr["chapter:verse", arr["chapter"] ":" arr["verse"]] = 1
    217             delete arr["chapter"]
    218             delete arr["verse"]
    219             do {
    220                     if(sub("^, ", "", q)) {
    221                         arr["chapter:verse", substr(q, 1, RLENGTH - 2)] = 1
    222                         q = substr(q, RLENGTH - 1)
    223                     } else {
    224                         arr["chapter:verse", substr(q, 2, RLENGTH - 1)] = 1
    225                         q = substr(q, RLENGTH + 1)
    226                     }
    227             } while (match(q, sprintf("^, ?%s:%s", re["num"], re["num"])))
    228 
    229             if (q != "") {
    230                     return "unknown"
    231             }
    232 
    233             return "exact_set"
    234 	} else {
    235             return "unknown"
    236 	}
    237 
    238 	if (match(q, sprintf("^:%s$", re["num"]))) {
    239             # 6
    240             arr["verse_end"] = num(substr(q, 2))
    241             return "range_ext"
    242 	} else {
    243             return "unknown"
    244 	}
    245 }
    246 
    247 function cleanbook(book) {
    248 	book = tolower(book)
    249 	gsub(" +", "", book)
    250 	return book
    251 }
    252 
    253 function bookmatches(book, bookabbr, query) {
    254 	book = cleanbook(book)
    255 	if (book == query) {
    256 		return book
    257 	}
    258 	bookabbr = cleanbook(bookabbr)
    259 	if (bookabbr == query) {
    260 		return book
    261 	}
    262 	if (substr(book, 1, length(query)) == query) {
    263 		return book
    264 	}
    265 }
    266 
    267 function hasbook(book, bookabbr,    query) {
    268     for(query in p){
    269         if (sub("^book" SUBSEP, "", query) && bookmatches(book, bookabbr, query)) {
    270             return book
    271         }
    272     }
    273 }
    274 
    275 function roughpattern(regex) {
    276     # TODO Can mess with search pattern if regex is used on command line
    277     regex = tolower(regex)
    278     if (lang == "el") {
    279             polytonic["α"] = "[αάἀ-ἆὰᾀ-ᾆᾳ-ᾷ]"
    280             polytonic["ε"] = "[εέἐ-ἕὲ]"
    281             polytonic["η"] = "[ηήἠ-ἧὴᾐ-ᾗῃ-ῇ]"
    282             polytonic["ι"] = "[ιίΐϊἰ-ἷὶῒ-ῖ]"
    283             polytonic["ο"] = "[οόὀ-ὅὸ]"
    284             polytonic["υ"] = "[υΰϋύὐ-ὗὺῢῦ]"
    285             polytonic["ω"] = "[ωώὠ-ὧὼᾠ-ᾧῳ-ῷ]"
    286             for (letter in polytonic) {
    287                 gsub(letter, polytonic[letter], regex)
    288             }
    289     }
    290     else if (lang =="la" ) {
    291             gsub("a", "[aá]", regex)
    292             gsub("e", "[eéë]", regex)
    293             gsub("i", "[ií]", regex)
    294             gsub("o", "[oó]", regex)
    295             gsub("u", "[uú]", regex)
    296             gsub("y", "[yý]", regex)
    297             gsub("æ", "[æǽ]", regex)
    298             gsub("œ", "[œœ́]", regex)
    299     }
    300     return regex
    301 }
    302 
    303 function printverse(verse, word_count, characters_printed,     i) {
    304 	if (NO_LINE_WRAP) {
    305         if (NO_VERSE_BREAK) {
    306             printf("%s ", verse)
    307         } else {
    308             printf("%s\n", verse)
    309         }
    310 		return
    311 	}
    312 
    313 	word_count = split(verse, words, " ")
    314 	for (i = 1; i <= word_count; i++) {
    315 		if (characters_printed + length(words[i]) + (characters_printed > 0 ? 1 : 0) > MAX_WIDTH - 8) {
    316             if(cross_ref || NO_VERSE_BREAK) {
    317                 printf("\n")
    318             } else {
    319                 printf("\n\t")
    320             }
    321 			characters_printed = 0
    322 		}
    323 		if (characters_printed > 0) {
    324 			printf(" ")
    325 			characters_printed++
    326 		}
    327 		printf("%s", words[i])
    328 		characters_printed += length(words[i])
    329 	}
    330     if (NO_VERSE_BREAK) {
    331         printf(" ")
    332     } else {
    333         printf("\n")
    334     }
    335 }
    336 
    337 function process_alias(alias, aliasabbr, book_names,      arr, i) {
    338     if (hasbook(alias, aliasabbr)) {
    339         delete p["book", cleanbook(alias)]
    340         delete p["book", cleanbook(aliasabbr)]
    341         split(book_names, arr, ",")
    342         for(i in arr) {
    343             p["book", cleanbook(arr[i])] = 1
    344         }
    345     }
    346 }
    347 
    348 function processline() {
    349     newbook = (last_book_printed != $2)
    350 	if (newbook) {
    351         if(cross_ref) {
    352             print("")
    353         } else if (NO_TITLE) {
    354             if (last_book_printed) {
    355                 print("")
    356             }
    357         } else {
    358             if ($2 in long_title) {
    359                 printf("%s (%s)\n", long_title[$2], $2)
    360             } else {
    361                 print($1)
    362             }
    363         }
    364         last_book_printed = $2
    365 	}
    366 
    367     if (cross_ref || NO_CHAPTER_HEADINGS || NO_VERSE_NUMBERS) {
    368         if (NO_VERSE_NUMBERS && last_chapter_printed != $4) {
    369             if (cross_ref) {
    370                 if(NO_CHAPTER_HEADINGS) {
    371                     print("")
    372                 } else {
    373                     print("\n")
    374                 }
    375             } else if (NO_CHAPTER_HEADINGS) {
    376                 if (last_chapter_printed) {
    377                     print("")
    378                 }
    379             } else {
    380                 if (NO_VERSE_BREAK && !newbook) {
    381                     print("")
    382                 }
    383                 print($4)
    384             }
    385             last_chapter_printed = $4
    386         }
    387     } else {
    388         printf("%s:%s\t", $4, $5)
    389     }
    390 	printverse($6)
    391 	outputted_records++
    392 }
    393 
    394 
    395 cmd == "ref" && !header_ended {
    396     if (/^#/) {
    397         header_ended = 1
    398         next
    399     } else {
    400         process_alias($1, $2, $3)
    401         next
    402     }
    403 }
    404 
    405 /^#/ && cmd == "ref" {
    406     long_title[$3] = $4
    407     next
    408 }
    409 
    410 cmd == "ref" && mode == "all" {
    411     processline()
    412 }
    413 
    414 cmd == "ref" && mode == "exact" && hasbook($1, $2) && (p["chapter"] == "" || $4 == p["chapter"]) && (p["verse"] == "" || $5 == p["verse"]) {
    415 	processline()
    416 }
    417 
    418 cmd == "ref" && mode == "random" && (p["book"] == "" || hasbook($1, $2)) && (p["chapter"] == "" || $4 == p["chapter"]) {
    419     print
    420     outputted_records++
    421 }
    422 
    423 cmd == "ref" && mode == "exact_ch_set" && hasbook($1, $2) && p["chapter", $4] {
    424 	processline()
    425 }
    426 
    427 cmd == "ref" && mode == "exact_set" && hasbook($1, $2) && (((p["chapter"] == "" || $4 == p["chapter"]) && p["verse", $5]) || p["chapter:verse", $4 ":" $5]) {
    428 	processline()
    429 }
    430 
    431 cmd == "ref" && mode == "range" && hasbook($1, $2) && ((p["chapter_end"] == "" && $4 == p["chapter"]) || ($4 >= p["chapter"] && $4 <= p["chapter_end"])) && (p["verse"] == "" || $5 >= p["verse"]) && (p["verse_end"] == "" || $5 <= p["verse_end"]) {
    432 	processline()
    433 }
    434 
    435 cmd == "ref" && mode == "range_ext" && hasbook($1, $2) && (($4 == p["chapter"] && $5 >= p["verse"] && p["chapter"] != p["chapter_end"]) || ($4 > p["chapter"] && $4 < p["chapter_end"]) || ($4 == p["chapter_end"] && $5 <= p["verse_end"] && p["chapter"] != p["chapter_end"]) || (p["chapter"] == p["chapter_end"] && $4 == p["chapter"] && $5 >= p["verse"] && $5 <= p["verse_end"])) {
    436 	processline()
    437 }
    438 
    439 cmd == "ref" && (mode == "search" || mode == "rough_search") && (p["book"] == "" || hasbook($1, $2)) && (p["chapter"] == "" || $4 == p["chapter"]) && match(mode == "rough_search" ? tolower($6) : $6, p["search"]) {
    440 	processline()
    441 }
    442 
    443 END {
    444 	if (cmd == "ref") {
    445         if (outputted_records == 0) {
    446             if (!is_set(ref)) {
    447                 print "Opted to search by ref but no ref was specified"
    448             } else {
    449                 print "Unknown reference: " ref
    450             }
    451 		    exit 1
    452         } else if (is_set(mode) && mode == "random") {
    453             printf("~~~RANDOMS: %d\n", p["numberOfVerses"])
    454         }
    455     } else if (cmd != "list" && cmd != "clean") {
    456         print "Unknown cmd specified: " cmd
    457     }
    458 }