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 }