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 }