search.c (5173B)
1 #include <string.h> 2 3 #include "view.h" 4 #include "buffer.h" 5 #include "search.h" 6 #include "memory.h" 7 8 /* FIXME: make sure only whole utf8 chars are matched 9 -> actually, isn't this always the case as long as the 10 pattern is valid utf8 because of the structure of utf8? */ 11 static char *last_search = NULL; 12 static size_t last_search_len = 0; 13 enum { 14 FORWARD, 15 BACKWARD 16 } last_dir = FORWARD; 17 18 void 19 search_cleanup(void) { 20 free(last_search); 21 last_search = NULL; 22 last_search_len = 0; 23 } 24 25 void 26 set_search_forward(char *pattern) { 27 last_dir = FORWARD; 28 free(last_search); 29 last_search = ledit_strdup(pattern); 30 last_search_len = strlen(last_search); 31 } 32 33 void 34 set_search_backward(char *pattern) { 35 last_dir = BACKWARD; 36 free(last_search); 37 last_search = ledit_strdup(pattern); 38 last_search_len = strlen(last_search); 39 } 40 41 static search_state 42 search_forward(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) { 43 *line_ret = view->cur_line; 44 *byte_ret = view->cur_index; 45 *len_ret = last_search_len; 46 /* if last_search is empty, strstr will find the ending '\0' */ 47 if (last_search == NULL || last_search[0] == '\0') 48 return SEARCH_NO_PATTERN; 49 size_t line = view->cur_line; 50 /* start one byte later so it doesn't get stuck on a match 51 note: in certain cases, this may not be a valid index */ 52 size_t byte = view->cur_index + 1; 53 char *res; 54 ledit_line *lline = buffer_get_line(view->buffer, line); 55 buffer_normalize_line(lline); 56 if (byte < lline->len && (res = strstr(lline->text + byte, last_search)) != NULL) { 57 *line_ret = line; 58 *byte_ret = (size_t)(res - lline->text); 59 return SEARCH_NORMAL; 60 } 61 for (size_t i = line + 1; i < view->lines_num; i++) { 62 lline = buffer_get_line(view->buffer, i); 63 buffer_normalize_line(lline); 64 if ((res = strstr(lline->text, last_search)) != NULL) { 65 *line_ret = i; 66 *byte_ret = (size_t)(res - lline->text); 67 return SEARCH_NORMAL; 68 } 69 } 70 for (size_t i = 0; i < line; i++) { 71 lline = buffer_get_line(view->buffer, i); 72 buffer_normalize_line(lline); 73 if ((res = strstr(lline->text, last_search)) != NULL) { 74 *line_ret = i; 75 *byte_ret = (size_t)(res - lline->text); 76 return SEARCH_WRAPPED; 77 } 78 } 79 lline = buffer_get_line(view->buffer, line); 80 buffer_normalize_line(lline); 81 if ((res = strstr(lline->text, last_search)) != NULL) { 82 *line_ret = line; 83 *byte_ret = (size_t)(res - lline->text); 84 return SEARCH_WRAPPED; 85 } 86 return SEARCH_NOT_FOUND; 87 } 88 89 /* FIXME: this is insanely inefficient */ 90 /* FIXME: just go backwards char-by-char and compare or knuth-morris-pratt */ 91 static search_state 92 search_backward(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) { 93 *line_ret = view->cur_line; 94 *byte_ret = view->cur_index; 95 *len_ret = last_search_len; 96 if (last_search == NULL || last_search[0] == '\0') 97 return SEARCH_NO_PATTERN; 98 size_t line = view->cur_line; 99 size_t byte = view->cur_index; 100 ledit_line *lline = buffer_get_line(view->buffer, line); 101 buffer_normalize_line(lline); 102 char *last = NULL, *res = lline->text; 103 while ((res = strstr(res, last_search)) != NULL && res < lline->text + byte) { 104 last = res; 105 res++; 106 } 107 if (last != NULL) { 108 *line_ret = line; 109 /* FIXME: check if this is safe */ 110 *byte_ret = (size_t)(last - lline->text); 111 return SEARCH_NORMAL; 112 } 113 for (size_t i = line; i > 0; i--) { 114 lline = buffer_get_line(view->buffer, i-1); 115 buffer_normalize_line(lline); 116 res = lline->text; 117 while ((res = strstr(res, last_search)) != NULL) { 118 last = res; 119 res++; 120 } 121 if (last != NULL) { 122 *line_ret = i-1; 123 *byte_ret = (size_t)(last - lline->text); 124 return SEARCH_NORMAL; 125 } 126 } 127 for (size_t i = view->lines_num - 1; i > line; i--) { 128 lline = buffer_get_line(view->buffer, i); 129 buffer_normalize_line(lline); 130 res = lline->text; 131 while ((res = strstr(res, last_search)) != NULL) { 132 last = res; 133 res++; 134 } 135 if (last != NULL) { 136 *line_ret = i; 137 *byte_ret = (size_t)(last - lline->text); 138 return SEARCH_WRAPPED; 139 } 140 } 141 lline = buffer_get_line(view->buffer, line); 142 buffer_normalize_line(lline); 143 res = lline->text + byte; 144 while ((res = strstr(res, last_search)) != NULL) { 145 last = res; 146 res++; 147 } 148 if (last != NULL) { 149 *line_ret = line; 150 *byte_ret = (size_t)(last - lline->text); 151 return SEARCH_WRAPPED; 152 } 153 return SEARCH_NOT_FOUND; 154 } 155 156 search_state 157 ledit_search_next(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) { 158 if (last_dir == FORWARD) 159 return search_forward(view, line_ret, byte_ret, len_ret); 160 else 161 return search_backward(view, line_ret, byte_ret, len_ret); 162 } 163 164 search_state 165 ledit_search_prev(ledit_view *view, size_t *line_ret, size_t *byte_ret, size_t *len_ret) { 166 if (last_dir == FORWARD) 167 return search_backward(view, line_ret, byte_ret, len_ret); 168 else 169 return search_forward(view, line_ret, byte_ret, len_ret); 170 } 171 172 char * 173 search_state_to_str(search_state s) { 174 switch (s) { 175 case SEARCH_NORMAL: 176 return "Found match"; 177 case SEARCH_WRAPPED: 178 return "Search wrapped"; 179 case SEARCH_NOT_FOUND: 180 return "Pattern not found"; 181 case SEARCH_NO_PATTERN: 182 return "No previous search pattern"; 183 default: 184 return "This message should not be shown. " 185 "Please bug lumidify about it."; 186 } 187 }