#include "pms_match.h" #include "pms_data.h" #include "pms_util.h" #include #include #include static bool verse_matches(const pms_ref *ref, const pms_verse *v) { switch (ref->type) { case PMS_REF_SEARCH: { if (ref->book != 0 && (unsigned int)v->book != ref->book) { return false; } if (ref->chapter != 0 && (unsigned int)v->chapter != ref->chapter) { return false; } char folded[4096]; pms_fold_utf8(v->text, folded, sizeof(folded), NULL); return regexec(&ref->search, folded, 0, NULL, 0) == 0; } case PMS_REF_EXACT: return (unsigned int)v->book == ref->book && (ref->chapter == 0 || (unsigned int)v->chapter == ref->chapter) && (ref->verse == 0 || (unsigned int)v->verse == ref->verse); case PMS_REF_EXACT_SET: if ((unsigned int)v->book != ref->book || (unsigned int)v->chapter != ref->chapter) { return false; } for (int i = 0; i < ref->verse_set_count; i++) { if ((unsigned int)v->verse == ref->verse_set[i]) { return true; } } return false; case PMS_REF_RANGE: return (unsigned int)v->book == ref->book && ((ref->chapter_end == 0 && (unsigned int)v->chapter == ref->chapter) || ((unsigned int)v->chapter >= ref->chapter && (unsigned int)v->chapter <= ref->chapter_end)) && (ref->verse == 0 || (unsigned int)v->verse >= ref->verse) && (ref->verse_end == 0 || (unsigned int)v->verse <= ref->verse_end); case PMS_REF_RANGE_EXT: return (unsigned int)v->book == ref->book && (((unsigned int)v->chapter == ref->chapter && (unsigned int)v->verse >= ref->verse && ref->chapter != ref->chapter_end) || ((unsigned int)v->chapter > ref->chapter && (unsigned int)v->chapter < ref->chapter_end) || ((unsigned int)v->chapter == ref->chapter_end && (unsigned int)v->verse <= ref->verse_end && ref->chapter != ref->chapter_end) || (ref->chapter == ref->chapter_end && (unsigned int)v->chapter == ref->chapter && (unsigned int)v->verse >= ref->verse && (unsigned int)v->verse <= ref->verse_end)); default: return false; } } static pms_range chapter_bounds(int i, const pms_config *config) { pms_range bounds = {.start = i, .end = i + 1}; int book = pms_verses[i].book; int chapter = pms_verses[i].chapter; int max_before = config->chapter_context ? INT_MAX : config->before_context; for (int n = 0; n < max_before && bounds.start > 0; n++) { if (pms_verses[bounds.start - 1].book == book && (!config->chapter_context || pms_verses[bounds.start - 1].chapter == chapter)) { bounds.start--; } else { break; } } int max_after = config->chapter_context ? INT_MAX : config->after_context; for (int n = 0; n < max_after && bounds.end < pms_verses_length; n++) { if (pms_verses[bounds.end].book == book && (!config->chapter_context || pms_verses[bounds.end].chapter == chapter)) { bounds.end++; } else { break; } } return bounds; } static int find_next_match(const pms_ref *ref, const pms_config *config, int i, pms_range *range) { for (; i < pms_verses_length; i++) { if (verse_matches(ref, &pms_verses[i])) { *range = chapter_bounds(i, config); return i; } } return -1; } int pms_next_verse(const pms_ref *ref, const pms_config *config, pms_match_data *data) { if (data->current >= pms_verses_length) { return -1; } if (data->current == 0 && data->next_match == 0) { data->matches[0] = (pms_range){-1, -1}; data->matches[1] = (pms_range){-1, -1}; data->next_match = find_next_match(ref, config, 0, &data->matches[0]); if (data->next_match < 0) { data->current = pms_verses_length; return -1; } data->next_match = find_next_match(ref, config, data->next_match + 1, &data->matches[1]); } while (data->matches[1].start != -1 && data->matches[1].start <= data->matches[0].end) { if (data->matches[1].end > data->matches[0].end) { data->matches[0].end = data->matches[1].end; } data->matches[1] = (pms_range){-1, -1}; if (data->next_match >= 0) { data->next_match = find_next_match(ref, config, data->next_match + 1, &data->matches[1]); } } if (data->current < data->matches[0].start) { data->current = data->matches[0].start; } if (data->current < data->matches[0].end) { return data->current++; } if (data->matches[1].start != -1) { data->matches[0] = data->matches[1]; data->matches[1] = (pms_range){-1, -1}; if (data->next_match >= 0) { data->next_match = find_next_match(ref, config, data->next_match + 1, &data->matches[1]); } return pms_next_verse(ref, config, data); } data->current = pms_verses_length; return -1; }