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("error opening directory %s: %s", 75 iter->base.path.buf, strerror(errno)); 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("error reading directory %s: %s", 126 iter->base.path.buf, strerror(errno)); 127 } else if (closedir(level->dir)) 128 warning("error closing directory %s: %s", 129 iter->base.path.buf, strerror(errno)); 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("error reading path '%s': %s", 144 iter->base.path.buf, 145 strerror(errno)); 146 continue; 147 } 148 149 /* 150 * We have to set these each time because 151 * the path strbuf might have been realloc()ed. 152 */ 153 iter->base.relative_path = 154 iter->base.path.buf + iter->levels[0].prefix_len; 155 iter->base.basename = 156 iter->base.path.buf + level->prefix_len; 157 level->dir_state = DIR_STATE_ITER; 158 159 return ITER_OK; 160 } 161 } 162} 163 164int dir_iterator_abort(struct dir_iterator *dir_iterator) 165{ 166 struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator; 167 168 for (; iter->levels_nr; iter->levels_nr--) { 169 struct dir_iterator_level *level = 170 &iter->levels[iter->levels_nr - 1]; 171 172 if (level->dir && closedir(level->dir)) { 173 strbuf_setlen(&iter->base.path, level->prefix_len); 174 warning("error closing directory %s: %s", 175 iter->base.path.buf, strerror(errno)); 176 } 177 } 178 179 free(iter->levels); 180 strbuf_release(&iter->base.path); 181 free(iter); 182 return ITER_DONE; 183} 184 185struct dir_iterator *dir_iterator_begin(const char *path) 186{ 187 struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter)); 188 struct dir_iterator *dir_iterator = &iter->base; 189 190 if (!path || !*path) 191 BUG("empty path passed to dir_iterator_begin()"); 192 193 strbuf_init(&iter->base.path, PATH_MAX); 194 strbuf_addstr(&iter->base.path, path); 195 196 ALLOC_GROW(iter->levels, 10, iter->levels_alloc); 197 198 iter->levels_nr = 1; 199 iter->levels[0].initialized = 0; 200 201 return dir_iterator; 202}