| 989 | } |
| 990 | |
| 991 | static void walk_dir(const char *dir_path, const char *rel_prefix, const cbm_discover_opts_t *opts, |
| 992 | const cbm_gitignore_t *gitignore, const cbm_gitignore_t *global_gi, |
| 993 | const cbm_gitignore_t *cbmignore, file_list_t *out) { |
| 994 | walk_stack_t ws = { |
| 995 | .frames = calloc(WALK_STACK_CAP, sizeof(walk_frame_t)), .top = 0, .cap = WALK_STACK_CAP}; |
| 996 | if (!ws.frames) { |
| 997 | out->failed = true; |
| 998 | return; |
| 999 | } |
| 1000 | /* Collect all owned gitignores and chain links — freed at the end because |
| 1001 | * child frames on the stack hold borrowed pointers to them. */ |
| 1002 | cbm_gitignore_t **owned_gis = NULL; |
| 1003 | size_t owned_count = 0; |
| 1004 | size_t owned_capacity = 0; |
| 1005 | gitignore_link_t *owned_links = NULL; |
| 1006 | |
| 1007 | int initial_directory_length = snprintf(ws.frames[0].dir, CBM_SZ_4K, "%s", dir_path); |
| 1008 | int initial_prefix_length = snprintf(ws.frames[0].prefix, CBM_SZ_4K, "%s", rel_prefix); |
| 1009 | if (initial_directory_length <= 0 || initial_directory_length >= CBM_SZ_4K || |
| 1010 | initial_prefix_length < 0 || initial_prefix_length >= CBM_SZ_4K) { |
| 1011 | out->failed = true; |
| 1012 | free(ws.frames); |
| 1013 | return; |
| 1014 | } |
| 1015 | if (gitignore) { |
| 1016 | ws.frames[0].ignore_chain = gitignore_link_new(gitignore, rel_prefix, NULL, &owned_links); |
| 1017 | if (!ws.frames[0].ignore_chain) { |
| 1018 | out->failed = true; |
| 1019 | free(ws.frames); |
| 1020 | return; |
| 1021 | } |
| 1022 | } |
| 1023 | ws.top++; |
| 1024 | |
| 1025 | while (ws.top > 0 && !file_list_should_stop(out)) { |
| 1026 | walk_frame_t frame = ws.frames[--ws.top]; |
| 1027 | |
| 1028 | cbm_gitignore_t *loaded = try_load_nested_gitignore(&frame); |
| 1029 | if (loaded) { |
| 1030 | if (!walk_owned_gitignore_append(&owned_gis, &owned_count, &owned_capacity, loaded)) { |
| 1031 | cbm_gitignore_free(loaded); |
| 1032 | out->failed = true; |
| 1033 | break; |
| 1034 | } |
| 1035 | /* owned_gis owns `loaded` from here on, even if the link fails. */ |
| 1036 | const gitignore_link_t *link = |
| 1037 | gitignore_link_new(loaded, frame.prefix, frame.ignore_chain, &owned_links); |
| 1038 | if (!link) { |
| 1039 | out->failed = true; |
| 1040 | break; |
| 1041 | } |
| 1042 | frame.ignore_chain = link; |
| 1043 | } |
| 1044 | |
| 1045 | cbm_dir_t *d = cbm_opendir(frame.dir); |
| 1046 | if (!d) { |
| 1047 | if (out->count_only) { |
| 1048 | out->failed = true; |
no test coverage detected