kjv

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

kjv_match.c (4634B)


      1 #include <assert.h>
      2 #include <regex.h>
      3 #include <stdbool.h>
      4 #include <stdlib.h>
      5 
      6 #include "kjv_data.h"
      7 #include "kjv_match.h"
      8 #include "intset.h"
      9 
     10 static bool
     11 kjv_verse_matches(const kjv_ref *ref, const kjv_verse *verse)
     12 {
     13     switch (ref->type) {
     14         case KJV_REF_SEARCH:
     15             return (ref->book == 0 || ref->book == verse->book) &&
     16                 (ref->chapter == 0 || verse->chapter == ref->chapter) &&
     17                 regexec(&ref->search, verse->text, 0, NULL, 0) == 0;
     18 
     19         case KJV_REF_EXACT:
     20             return ref->book == verse->book &&
     21                 (ref->chapter == 0 || ref->chapter == verse->chapter) &&
     22                 (ref->verse == 0 || ref->verse == verse->verse);
     23 
     24         case KJV_REF_EXACT_SET:
     25             return ref->book == verse->book &&
     26                 (ref->chapter == 0 || verse->chapter == ref->chapter) &&
     27                 intset_contains(ref->verse_set, verse->verse);
     28 
     29         case KJV_REF_RANGE:
     30             return ref->book == verse->book &&
     31                 ((ref->chapter_end == 0 && ref->chapter == verse->chapter) ||
     32                     (verse->chapter >= ref->chapter && verse->chapter <= ref->chapter_end)) &&
     33                 (ref->verse == 0 || verse->verse >= ref->verse) &&
     34                 (ref->verse_end == 0 || verse->verse <= ref->verse_end);
     35 
     36         case KJV_REF_RANGE_EXT:
     37             return ref->book == verse->book &&
     38                 (
     39                     (verse->chapter == ref->chapter && verse->verse >= ref->verse && ref->chapter != ref->chapter_end) ||
     40                     (verse->chapter > ref->chapter && verse->chapter < ref->chapter_end) ||
     41                     (verse->chapter == ref->chapter_end && verse->verse <= ref->verse_end && ref->chapter != ref->chapter_end) ||
     42                     (ref->chapter == ref->chapter_end && verse->chapter == ref->chapter && verse->verse >= ref->verse && verse->verse <= ref->verse_end)
     43                 );
     44 
     45         default:
     46             return false;
     47     }
     48 }
     49 
     50 #define KJV_DIRECTION_BEFORE -1
     51 #define KJV_DIRECTION_AFTER 1
     52 
     53 static int
     54 kjv_chapter_bounds(int i, int direction, int maximum_steps)
     55 {
     56     assert(direction == KJV_DIRECTION_BEFORE || direction == KJV_DIRECTION_AFTER);
     57 
     58     int steps = 0;
     59     for ( ; 0 <= i && i < kjv_verses_length; i += direction) {
     60         bool step_limit = (maximum_steps != -1 && steps >= maximum_steps) ||
     61             (direction == KJV_DIRECTION_BEFORE && i == 0) ||
     62             (direction == KJV_DIRECTION_AFTER && i + 1 == kjv_verses_length);
     63         if (step_limit) {
     64             break;
     65         }
     66 
     67         const kjv_verse *current = &kjv_verses[i], *next = &kjv_verses[i + direction];
     68         if (current->book != next->book || current->chapter != next->chapter) {
     69             break;
     70         }
     71         steps++;
     72     }
     73     return i;
     74 }
     75 
     76 static int
     77 kjv_next_match(const kjv_ref *ref, int i)
     78 {
     79     for ( ; i < kjv_verses_length; i++) {
     80         const kjv_verse *verse = &kjv_verses[i];
     81         if (kjv_verse_matches(ref, verse)) {
     82             return i;
     83         }
     84     }
     85     return -1;
     86 }
     87 
     88 static void
     89 kjv_next_addrange(kjv_next_data *next, kjv_range range) {
     90     if (next->matches[0].start == -1 && next->matches[0].end == -1) {
     91         next->matches[0] = range;
     92     } else if (range.start < next->matches[0].end) {
     93         next->matches[0] = range;
     94     } else {
     95         next->matches[1] = range;
     96     }
     97 }
     98 
     99 int
    100 kjv_next_verse(const kjv_ref *ref, const kjv_config *config, kjv_next_data *next)
    101 {
    102     if (next->current >= kjv_verses_length) {
    103         return -1;
    104     }
    105 
    106     if (next->matches[0].start != -1 && next->matches[0].end != -1 && next->current >= next->matches[0].end) {
    107         next->matches[0] = next->matches[1];
    108         next->matches[1] = (kjv_range){-1, -1};
    109     }
    110 
    111     if ((next->next_match == -1 || next->next_match < next->current) && next->next_match < kjv_verses_length) {
    112         int next_match = kjv_next_match(ref, next->current);
    113         if (next_match >= 0) {
    114             next->next_match = next_match;
    115             kjv_range bounds = {
    116                 .start = kjv_chapter_bounds(next_match, KJV_DIRECTION_BEFORE, config->context_chapter ? -1 : config->context_before),
    117                 .end = kjv_chapter_bounds(next_match, KJV_DIRECTION_AFTER, config->context_chapter ? -1 : config->context_after) + 1,
    118             };
    119             kjv_next_addrange(next, bounds);
    120         } else {
    121             next_match = kjv_verses_length;
    122         }
    123     }
    124 
    125     if (next->matches[0].start == -1 && next->matches[0].end == -1) {
    126         return -1;
    127     }
    128 
    129     if (next->current < next->matches[0].start) {
    130         next->current = next->matches[0].start;
    131     }
    132 
    133     return next->current++;
    134 }