kjv

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

kjv_ref.c (5108B)


      1 #include <ctype.h>
      2 #include <regex.h>
      3 #include <stdbool.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 
      8 #include "kjv_data.h"
      9 #include "kjv_ref.h"
     10 
     11 kjv_ref *
     12 kjv_newref()
     13 {
     14     return calloc(1, sizeof(kjv_ref));
     15 }
     16 
     17 void
     18 kjv_freeref(kjv_ref *ref)
     19 {
     20     if (ref) {
     21         free(ref->search_str);
     22         regfree(&ref->search);
     23         free(ref);
     24     }
     25 }
     26 
     27 
     28 static bool
     29 kjv_bookequal(const char *a, const char *b, bool short_match)
     30 {
     31     for (size_t i = 0, j = 0; ; ) {
     32         if ((!a[i] && !b[j]) || (short_match && !b[j])) {
     33             return true;
     34         } else if (a[i] == ' ') {
     35             i++;
     36         } else if (b[j] == ' ') {
     37             j++;
     38         } else if (tolower(a[i]) != tolower(b[j])) {
     39             return false;
     40         } else {
     41             i++;
     42             j++;
     43         }
     44     }
     45 }
     46 
     47 static bool
     48 kjv_book_matches(const kjv_book *book, const char *s)
     49 {
     50     return kjv_bookequal(book->name, s, false) ||
     51         kjv_bookequal(book->abbr, s, false) ||
     52         kjv_bookequal(book->name, s, true);
     53 }
     54 
     55 static int
     56 kjv_book_fromname(const char *s)
     57 {
     58     for (int i = 0; i < kjv_books_length; i++) {
     59         const kjv_book *book = &kjv_books[i];
     60         if (kjv_book_matches(book, s)) {
     61             return book->number;
     62         }
     63     }
     64     return 0;
     65 }
     66 
     67 static int
     68 kjv_scanbook(const char *s, int *n)
     69 {
     70     int i;
     71     int mode = 0;
     72     for (i = 0; s[i]; i++) {
     73         if (s[i] == ' ') {
     74             continue;
     75         } else if (('a' <= s[i] && s[i] <= 'z') || ('A' <= s[i] && s[i] <= 'Z')) {
     76             mode = 2;
     77         } else if ('0' <= s[i] && s[i] <= '9' && 0 <= mode && mode <= 1) {
     78             mode = 1;
     79         } else {
     80             break;
     81         }
     82     }
     83     *n = i;
     84     return mode >= 1;
     85 }
     86 
     87 int
     88 kjv_parseref(kjv_ref *ref, const char *ref_str)
     89 {
     90     // 1. <book>
     91     // 2. <book>:?<chapter>
     92     // 3. <book>:?<chapter>:<verse>
     93     // 3a. <book>:?<chapter>:<verse>[,<verse>]...
     94     // 4. <book>:?<chapter>-<chapter>
     95     // 5. <book>:?<chapter>:<verse>-<verse>
     96     // 6. <book>:?<chapter>:<verse>-<chapter>:<verse>
     97     // 7. /<search>
     98     // 8. <book>/search
     99     // 9. <book>:?<chapter>/search
    100 
    101     ref->type = 0;
    102     ref->book = 0;
    103     ref->chapter = 0;
    104     ref->chapter_end = 0;
    105     ref->verse = 0;
    106     ref->verse_end = 0;
    107     intset_free(ref->verse_set);
    108     ref->verse_set = NULL;
    109     free(ref->search_str);
    110     ref->search_str = NULL;
    111     regfree(&ref->search);
    112 
    113     int n = 0;
    114     if (kjv_scanbook(ref_str, &n) == 1) {
    115         // 1, 2, 3, 3a, 4, 5, 6, 8, 9
    116         char *bookname = strndup(ref_str, n);
    117         ref->book = kjv_book_fromname(bookname);
    118         free(bookname);
    119         ref_str = &ref_str[n];
    120     } else if (ref_str[0] == '/') {
    121         // 7
    122         goto search;
    123     } else {
    124         return 1;
    125     }
    126 
    127     if (sscanf(ref_str, ": %u %n", &ref->chapter, &n) == 1 || sscanf(ref_str, "%u %n", &ref->chapter, &n) == 1) {
    128         // 2, 3, 3a, 4, 5, 6, 9
    129         ref_str = &ref_str[n];
    130     } else if (ref_str[0] == '/') {
    131         // 8
    132         goto search;
    133     } else if (ref_str[0] == '\0') {
    134         // 1
    135         ref->type = KJV_REF_EXACT;
    136         return 0;
    137     } else {
    138         return 1;
    139     }
    140 
    141     if (sscanf(ref_str, ": %u %n", &ref->verse, &n) == 1) {
    142         // 3, 3a, 5, 6
    143         ref_str = &ref_str[n];
    144     } else if (sscanf(ref_str, "- %u %n", &ref->chapter_end, &n) == 1) {
    145         // 4
    146         if (ref_str[n] != '\0') {
    147             return 1;
    148         }
    149         ref->type = KJV_REF_RANGE;
    150         return 0;
    151     } else if (ref_str[0] == '/') {
    152         // 9
    153         goto search;
    154     } else if (ref_str[0] == '\0') {
    155         // 2
    156         ref->type = KJV_REF_EXACT;
    157         return 0;
    158     } else {
    159         return 1;
    160     }
    161 
    162     unsigned int value;
    163     int ret = sscanf(ref_str, "- %u %n", &value, &n);
    164     if (ret == 1 && ref_str[n] == '\0') {
    165         // 5
    166         ref->verse_end = value;
    167         ref->type = KJV_REF_RANGE;
    168         return 0;
    169     } else if (ret == 1) {
    170         // 6
    171         ref->chapter_end = value;
    172         ref_str = &ref_str[n];
    173     } else if (ref_str[0] == '\0') {
    174         // 3
    175         ref->type = KJV_REF_EXACT;
    176         return 0;
    177     } else if (sscanf(ref_str, ", %u %n", &value, &n) == 1) {
    178         // 3a
    179         ref->verse_set = intset_new();
    180         intset_add(ref->verse_set, ref->verse);
    181         intset_add(ref->verse_set, value);
    182         ref_str = &ref_str[n];
    183         while (true) {
    184             if (sscanf(ref_str, ", %u %n", &value, &n) != 1) {
    185                 break;
    186             }
    187             intset_add(ref->verse_set, value);
    188             ref_str = &ref_str[n];
    189         }
    190         if (ref_str[0] != '\0') {
    191             return 1;
    192         }
    193         ref->type = KJV_REF_EXACT_SET;
    194         return 0;
    195     } else {
    196         return 1;
    197     }
    198 
    199     if (sscanf(ref_str, ": %u %n", &ref->verse_end, &n) == 1 && ref_str[n] == '\0') {
    200         // 6
    201         ref->type = KJV_REF_RANGE_EXT;
    202         return 0;
    203     } else {
    204         return 1;
    205     }
    206 
    207 search:
    208     ref->type = KJV_REF_SEARCH;
    209     if (regcomp(&ref->search, &ref_str[1], REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0) {
    210         return 2;
    211     }
    212     ref->search_str = strdup(&ref_str[1]);
    213     return 0;
    214 }