1#include "cache.h" 2#include "dir.h" 3#include "iterator.h" 4#include "dir-iterator.h" 5 6struct dir_iterator_level { 7 int initialized; 8 9 DIR *dir; 10 11 /* 12 * The length of the directory part of path at this level 13 * (including a trailing '/'): 14 */ 15 size_t prefix_len; 16 17 /* 18 * The last action that has been taken with the current entry 19 * (needed for directories, which have to be included in the 20 * iteration and also iterated into): 21 */ 22 enum { 23 DIR_STATE_ITER, 24 DIR_STATE_RECURSE 25 } dir_state; 26}; 27 28/* 29 * The full data structure used to manage the internal directory 30 * iteration state. It includes members that are not part of the 31 * public interface. 32 */ 33struct dir_iterator_int { 34 struct dir_iterator base; 35 36 /* 37 * The number of levels currently on the stack. This is always 38 * at least 1, because when it becomes zero the iteration is 39 * ended and this struct is freed. 40 */ 41 size_t levels_nr; 42 43 /* The number of levels that have been allocated on the stack */ 44 size_t levels_alloc; 45 46 /* 47 * A stack of levels. levels[0] is the uppermost directory 48 * that will be included in this iteration. 49 */ 50 struct dir_iterator_level *levels; 51}; 52 53int dir_iterator_advance(struct dir_iterator *dir_iterator) 54{ 55 struct dir_iterator_int *iter = 56 (struct dir_iterator_int *)dir_iterator; 57 58 while (1) { 59 struct dir_iterator_level *level = 60 &iter->levels[iter->levels_nr - 1]; 61 struct dirent *de; 62 63 if (!level->initialized) { 64 /* 65 * Note: dir_iterator_begin() ensures that 66 * path is not the empty string. 67 */ 68 if (!is_dir_sep(iter->base.path.buf[iter->base.path.len - 1])) 69 strbuf_addch(&iter->base.path, '/'); 70 level->prefix_len = iter->base.path.len; 71 72 level->dir = opendir(iter->base.path.buf); 73 if (!level->dir && errno != ENOENT) { 74 warning_errno("error opening directory '%s'", 75 iter->base.path.buf); 76 /* Popping the level is handled below */ 77 } 78 79 level->initialized = 1; 80 } else if (S_ISDIR(iter->base.st.st_mode)) { 81 if (level->dir_state == DIR_STATE_ITER) { 82 /* 83 * The directory was just iterated 84 * over; now prepare to iterate into 85 * it. 86 */ 87 level->dir_state = DIR_STATE_RECURSE; 88 ALLOC_GROW(iter->levels, iter->levels_nr + 1, 89 iter->levels_alloc); 90 level = &iter->levels[iter->levels_nr++]; 91 level->initialized = 0; 92 continue; 93 } else { 94 /* 95 * The directory has already been 96 * iterated over and iterated into; 97 * we're done with it. 98 */ 99 } 100 } 101 102 if (!level->dir) { 103 /* 104 * This level is exhausted (or wasn't opened 105 * successfully); pop up a level. 106 */ 107 if (--iter->levels_nr == 0) 108 return dir_iterator_abort(dir_iterator); 109 110 continue; 111 } 112 113 /* 114 * Loop until we find an entry that we can give back 115 * to the caller: 116 */ 117 while (1) { 118 strbuf_setlen(&iter->base.path, level->prefix_len); 119 errno = 0; 120 de = readdir(level->dir); 121 122 if (!de) { 123 /* This level is exhausted; pop up a level. */ 124 if (errno) { 125 warning_errno("error reading directory '%s'", 126 iter->base.path.buf); 127 } else if (closedir(level->dir)) 128 warning_errno("error closing directory '%s'", 129 iter->base.path.buf); 130 131 level->dir = NULL; 132 if (--iter->levels_nr == 0) 133 return dir_iterator_abort(dir_iterator); 134 break; 135 } 136 137 if (is_dot_or_dotdot(de->d_name)) 138 continue; 139 140 strbuf_addstr(&iter->base.path, de->d_name); 141 if (lstat(iter->base.path.buf, &iter->base.st) < 0) { 142 if (errno != ENOENT) 143 warning_errno("failed to stat '%s'", 144 iter->base.path.buf); 145 continue; 146 } 147 148 /* 149 * We have to set these each time because 150 * the path strbuf might have been realloc()ed. 151 */ 152 iter->base.relative_path = 153 iter->base.path.buf + iter->levels[0].prefix_len; 154 iter->base.basename = 155 iter->base.path.buf + level->prefix_len; 156 level->dir_state = DIR_STATE_ITER; 157 158 return ITER_OK; 159 } 160 } 161} 162 163int dir_iterator_abort(struct dir_iterator *dir_iterator) 164{ 165 struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator; 166 167 for (; iter->levels_nr; iter->levels_nr--) { 168 struct dir_iterator_level *level = 169 &iter->levels[iter->levels_nr - 1]; 170 171 if (level->dir && closedir(level->dir)) { 172 int saved_errno = errno; 173 strbuf_setlen(&iter->base.path, level->prefix_len); 174 errno = saved_errno; 175 warning_errno("error closing directory '%s'", 176 iter->base.path.buf); 177 } 178 } 179 180 free(iter->levels); 181 strbuf_release(&iter->base.path); 182 free(iter); 183 return ITER_DONE; 184} 185 186struct dir_iterator *dir_iterator_begin(const char *path) 187{ 188 struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter)); 189 struct dir_iterator *dir_iterator = &iter->base; 190 191 if (!path || !*path) 192 BUG("empty path passed to dir_iterator_begin()"); 193 194 strbuf_init(&iter->base.path, PATH_MAX); 195 strbuf_addstr(&iter->base.path, path); 196 197 ALLOC_GROW(iter->levels, 10, iter->levels_alloc); 198 199 iter->levels_nr = 1; 200 iter->levels[0].initialized = 0; 201 202 return dir_iterator; 203}