seek.c (12515B)
1 2 3 #if 1 4 #define _GNU_SOURCE 5 #endif 6 7 #include "seek.h" 8 9 #include <string.h> 10 #include <ctype.h> 11 12 #include "config.h" 13 #include <sys/stat.h> 14 15 #if 0 16 #define BOUNDS_CHECK(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 17 #else 18 #define BOUNDS_CHECK(x, a, b) 19 #endif 20 21 int 22 str_contains_char(const char* string, char check) 23 { 24 return strchr(string, check) != NULL; 25 } 26 27 inline int 28 is_file_type(const char* file_path, const char* file_type) 29 { 30 int ftlen = strlen(file_type); 31 int offset = strlen(file_path) - ftlen; 32 if(offset >= 0 && memcmp(file_path + offset, file_type, ftlen) == 0) 33 return 1; 34 return 0; 35 } 36 37 char* 38 file_path_get_path(const char* path) 39 { 40 soft_assert(path, path = "/";); 41 42 const char* folder_start = strrchr(path, '/'); 43 if (!folder_start) 44 folder_start = path; 45 else 46 folder_start++; 47 int folder_len = folder_start - path; 48 char* folder = xmalloc(folder_len + 1); 49 50 memcpy(folder, path, folder_len); 51 folder[folder_len] = '\0'; 52 53 return folder; 54 } 55 56 inline int 57 path_is_folder(const char* path) 58 { 59 struct stat statbuf; 60 if (stat(path, &statbuf) != 0) 61 return 0; 62 return S_ISDIR(statbuf.st_mode); 63 } 64 65 inline int 66 fb_seek_char(const struct file_buffer* fb, int offset, char byte) 67 { 68 if (offset > fb->len) return -1; 69 char* new_buf = memchr(fb->contents + offset, byte, fb->len - offset); 70 if (!new_buf) return -1; 71 return new_buf - fb->contents; 72 } 73 74 inline int 75 fb_seek_char_backwards(const struct file_buffer* fb, int offset, char byte) 76 { 77 BOUNDS_CHECK(offset, 0, fb->len-1); 78 for (int n = offset-1; n >= 0; n--) { 79 if (fb->contents[n] == byte) { 80 return n+1; 81 } 82 } 83 return -1; 84 } 85 86 inline int 87 fb_seek_string(const struct file_buffer* fb, int offset, const char* string) 88 { 89 BOUNDS_CHECK(offset, 0, fb->len-1); 90 int str_len = strlen(string); 91 92 #if 0 93 for (int n = offset; n < fb->len - str_len; n++) 94 if (!memcmp(fb->contents + n, string, str_len)) 95 return n; 96 #else 97 char* res = memmem(fb->contents + offset, fb->len - offset, 98 string, str_len); 99 if (res) 100 return res - fb->contents; 101 #endif 102 return -1; 103 } 104 105 inline int 106 fb_seek_string_backwards(const struct file_buffer* fb, int offset, const char* string) 107 { 108 int str_len = strlen(string); 109 offset += str_len; 110 BOUNDS_CHECK(offset, 0, fb->len-1); 111 112 for (int n = offset - str_len; n >= 0; n--) 113 if (!memcmp(fb->contents + n, string, str_len)) 114 return n; 115 return -1; 116 } 117 118 inline int 119 wb_seek_string_wrap(const struct window_buffer* wb, int offset, const char* search) 120 { 121 struct file_buffer* fb = get_fb(focused_window); 122 if (*search == 0 || !fb_count_string_instances(fb, search, 0, NULL)) 123 return -1; 124 125 int new_offset = fb_seek_string(fb, offset, search); 126 if (new_offset < 0) 127 new_offset = fb_seek_string(fb, 0, search); 128 129 if (!(fb->mode & FB_SEARCH_BLOCKING)) 130 fb->mode |= FB_SEARCH_BLOCKING_IDLE; 131 return new_offset; 132 } 133 134 inline int 135 wb_seek_string_wrap_backwards(const struct window_buffer* wb, int offset, const char* search) 136 { 137 struct file_buffer* fb = get_fb(focused_window); 138 if (*search == 0 || !fb_count_string_instances(fb, search, 0, NULL)) 139 return -1; 140 141 int new_offset = fb_seek_string_backwards(fb, offset, search); 142 if (new_offset < 0) 143 new_offset = fb_seek_string_backwards(fb, fb->len, search); 144 145 if (!(fb->mode & FB_SEARCH_BLOCKING)) 146 fb->mode |= FB_SEARCH_BLOCKING_IDLE; 147 return new_offset; 148 } 149 150 int 151 fb_is_on_a_word(const struct file_buffer* fb, int offset, const char* word_seperators) 152 { 153 BOUNDS_CHECK(offset, 0, fb->len); 154 return !str_contains_char(word_seperators, fb->contents[offset]); 155 } 156 157 inline int 158 fb_is_start_of_a_word(const struct file_buffer* fb, int offset, const char* word_seperators) 159 { 160 BOUNDS_CHECK(offset, 0, fb->len); 161 return fb_is_on_a_word(fb, offset, word_seperators) && 162 (offset-1 <= 0 || str_contains_char(word_seperators, fb->contents[offset-1])); 163 } 164 165 inline int 166 fb_is_on_word(const struct file_buffer* fb, int offset, const char* word_seperators, const char* word) 167 { 168 BOUNDS_CHECK(offset, 0, fb->len); 169 int word_start = fb_seek_start_of_word_backwards(fb, offset, word_seperators); 170 int word_len = strlen(word); 171 if (word_start < offset - (word_len-1)) 172 return 0; 173 return fb_offset_starts_with(fb, word_start, word) && 174 !fb_is_on_a_word(fb, word_start + word_len, word_seperators); 175 } 176 177 inline int 178 fb_offset_starts_with(const struct file_buffer* fb, int offset, const char* start) 179 { 180 BOUNDS_CHECK(offset, 0, fb->len); 181 if (offset > 0 && fb->contents[offset-1] == '\\') return 0; 182 183 int len = strlen(start); 184 int mlen = MIN(len, fb->len - offset); 185 return memcmp(fb->contents + offset, start, mlen) == 0; 186 } 187 188 inline int 189 fb_seek_word(const struct file_buffer* fb, int offset, const char* word_seperators) 190 { 191 if (fb_is_on_a_word(fb, offset, word_seperators)) 192 offset = fb_seek_word_end(fb, offset, word_seperators); 193 while (offset < fb->len && str_contains_char(word_seperators, fb->contents[offset])) offset++; 194 return offset; 195 } 196 197 inline int 198 fb_seek_word_end(const struct file_buffer* fb, int offset, const char* word_seperators) 199 { 200 BOUNDS_CHECK(offset, 0, fb->len); 201 if (!fb_is_on_a_word(fb, offset, word_seperators)) 202 offset = fb_seek_word(fb, offset, word_seperators); 203 while (offset < fb->len && !str_contains_char(word_seperators, fb->contents[offset])) offset++; 204 return offset; 205 } 206 207 inline int 208 fb_seek_word_backwards(const struct file_buffer* fb, int offset, const char* word_seperators) 209 { 210 BOUNDS_CHECK(offset, 0, fb->len); 211 if (!fb_is_on_a_word(fb, offset, word_seperators)) 212 while (offset > 0 && str_contains_char(word_seperators, fb->contents[offset])) offset--; 213 return offset; 214 } 215 216 inline int 217 fb_seek_start_of_word_backwards(const struct file_buffer* fb, int offset, const char* word_seperators) 218 { 219 BOUNDS_CHECK(offset, 0, fb->len); 220 if (!fb_is_on_a_word(fb, offset, word_seperators)) 221 while (offset > 0 && str_contains_char(word_seperators, fb->contents[offset])) offset--; 222 while (offset > 0 && !str_contains_char(word_seperators, fb->contents[offset])) offset--; 223 return offset+1; 224 } 225 226 inline int 227 fb_seek_whitespace(const struct file_buffer* fb, int offset) 228 { 229 BOUNDS_CHECK(offset, 0, fb->len); 230 while (offset < fb->len && !isspace(fb->contents[offset])) offset++; 231 return offset; 232 } 233 234 inline int 235 fb_seek_whitespace_backwards(const struct file_buffer* fb, int offset) 236 { 237 BOUNDS_CHECK(offset, 0, fb->len); 238 while (offset > 0 && !isspace(fb->contents[offset])) offset--; 239 return offset; 240 } 241 242 inline int 243 fb_seek_not_whitespace(const struct file_buffer* fb, int offset) 244 { 245 BOUNDS_CHECK(offset, 0, fb->len); 246 while (offset < fb->len && isspace(fb->contents[offset])) offset++; 247 return offset; 248 } 249 250 inline int 251 fb_seek_not_whitespace_backwards(const struct file_buffer* fb, int offset) 252 { 253 BOUNDS_CHECK(offset, 0, fb->len); 254 while (offset > 0 && isspace(fb->contents[offset])) offset--; 255 return offset; 256 } 257 258 inline int 259 fb_count_string_instances(const struct file_buffer* fb, const char* string, int offset, int* before_offset) 260 { 261 int tmp; 262 if (!before_offset) 263 before_offset = &tmp; 264 if (!string || *string == 0) { 265 *before_offset = 0; 266 return 0; 267 } 268 269 int pos = -1; 270 int count = 0; 271 int once = 1; 272 while((pos = fb_seek_string(fb, pos+1, string)) >= 0) { 273 if (pos > 0 && fb->contents[pos-2] == '\\') 274 continue; 275 if (once && pos > offset) { 276 *before_offset = count; 277 once = 0; 278 } 279 count++; 280 } 281 if (once) 282 *before_offset = count; 283 return count; 284 } 285 286 /* 287 ** Search from the start of the file 288 ** Once any patterns are found, other patterns are disabled. 289 ** If the pattern we are searching for extends beyond *offset* then we will use that as our delimiter 290 */ 291 292 /////////////// 293 // " \" // we're here " 294 // ↑ ↑ 295 // start seeked end 296 int 297 fb_seek_string_not_escaped(const struct file_buffer* fb, int offset, const char* string) 298 { 299 for (;;) { 300 offset = fb_seek_string(fb, offset, string); 301 if (offset >= 0 && fb->contents[offset-1] == '\\') 302 offset++; 303 else 304 break; 305 } 306 return offset; 307 } 308 309 inline int 310 fb_seek_string_backwards_not_escaped(const struct file_buffer* fb, int offset, const char* string) 311 { 312 for (;;) { 313 offset = fb_seek_string_backwards(fb, offset, string); 314 if (offset >= 0 && fb->contents[offset-1] == '\\') 315 offset--; 316 else 317 break; 318 } 319 return offset; 320 } 321 322 inline static int 323 fb_get_delimiter_equal_start_end(const struct file_buffer* fb, int offset, const char* string, struct delimiter* ignore, int* start, int* end) 324 { 325 int ignore_index = 0; 326 int last_distance = 0; 327 328 while (last_distance >= 0) { 329 int d_res = INT32_MAX; 330 int i = 0; 331 if (ignore) { 332 for (struct delimiter* ign = ignore; ign->start; ign++) { 333 int d = fb_seek_string_not_escaped(fb, last_distance, ign->start); 334 if (d < 0 || d > offset) { 335 i++; 336 continue; 337 } 338 339 if (d < d_res) { 340 d_res = d; 341 ignore_index = i; 342 } 343 i++; 344 } 345 } 346 int str_d = fb_seek_string_not_escaped(fb, last_distance, string); 347 348 if ((str_d <= 0 || str_d > offset) && d_res == INT32_MAX) 349 return 0; 350 351 const char* end_str; 352 if (str_d >= 0 && str_d <= d_res) { 353 last_distance = str_d; 354 end_str = string; 355 *start = last_distance; 356 } else { 357 last_distance = d_res; 358 end_str = ignore[ignore_index].end; 359 } 360 361 last_distance = fb_seek_string_not_escaped(fb, last_distance+1, end_str); 362 363 if (end_str == string && last_distance > offset) { 364 *end = last_distance; 365 return 1; 366 } 367 if (last_distance >= 0) 368 last_distance++; 369 } 370 return 0; 371 } 372 373 inline static int 374 fb_get_matched_delimiter_end(const struct file_buffer* fb, int offset, struct delimiter d) 375 { 376 int closers_left = 1; 377 while (closers_left) { 378 int next_open = fb_seek_string_not_escaped(fb, offset, d.start); 379 int next_close = fb_seek_string_not_escaped(fb, offset, d.end); 380 381 if (next_close < 0 || next_open < 0) 382 return next_close; 383 384 if (next_open < next_close) { 385 closers_left++; 386 offset = next_open+1; 387 } else { 388 closers_left--; 389 offset = next_close; 390 if (closers_left) 391 offset++; 392 } 393 } 394 return offset; 395 } 396 397 inline static int 398 fb_get_matched_delimiter_start(const struct file_buffer* fb, int offset, struct delimiter d) 399 { 400 int openers_left = 1; 401 while (openers_left) { 402 int next_open = fb_seek_string_backwards_not_escaped(fb, offset, d.start); 403 int next_close = fb_seek_string_backwards_not_escaped(fb, offset, d.end); 404 405 if (next_close < 0 || next_open < 0) 406 return next_open; 407 408 if (next_open < next_close) { 409 openers_left++; 410 offset = next_open-1; 411 } else { 412 openers_left--; 413 offset = next_open; 414 if (openers_left) 415 offset--; 416 } 417 } 418 return offset; 419 } 420 421 inline int 422 fb_get_delimiter(const struct file_buffer* fb, int offset, struct delimiter d, struct delimiter* ignore, int* start, int* end) 423 { 424 const char* start_word = d.start; 425 const char* end_word = d.end; 426 427 soft_assert(start, return 0;); 428 soft_assert(end, return 0;); 429 soft_assert(start_word, return 0;); 430 soft_assert(end_word, return 0;); 431 432 if (strcmp(start_word, end_word) == 0) 433 return fb_get_delimiter_equal_start_end(fb, offset, start_word, ignore, start, end); 434 int wanted_opener = fb_get_matched_delimiter_start(fb, offset, d); 435 436 int ignore_index = 0; 437 int last_distance = 0; 438 439 while (last_distance >= 0) { 440 int d_res = INT32_MAX; 441 int i = 0; 442 if (ignore) { 443 for (struct delimiter* ign = ignore; ign->start; ign++) { 444 int d = fb_seek_string_not_escaped(fb, last_distance, ign->start); 445 if (d < 0 || d > offset) { 446 i++; 447 continue; 448 } 449 450 if (d < d_res) { 451 d_res = d; 452 ignore_index = i; 453 } 454 i++; 455 } 456 } 457 int str_d = fb_seek_string_not_escaped(fb, last_distance, start_word); 458 459 if ((str_d <= 0 || str_d > offset) && d_res == INT32_MAX) 460 return 0; 461 462 if (str_d >= 0 && str_d <= d_res) { 463 if (str_d == wanted_opener) { 464 last_distance = str_d; 465 *start = last_distance; 466 467 last_distance = fb_get_matched_delimiter_end(fb, last_distance+1, d); 468 469 if (last_distance > offset) { 470 *end = last_distance; 471 return 1; 472 } 473 } 474 } else { 475 last_distance = d_res; 476 477 last_distance = fb_seek_string_not_escaped(fb, last_distance+1, ignore[ignore_index].end); 478 } 479 480 if (last_distance >= 0) 481 last_distance++; 482 } 483 return 0; 484 }