1#include"cache.h" 2#include"dir.h" 3#include"iterator.h" 4#include"dir-iterator.h" 5 6struct dir_iterator_level { 7int initialized; 8 9DIR*dir; 10 11/* 12 * The length of the directory part of path at this level 13 * (including a trailing '/'): 14 */ 15size_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 */ 22enum{ 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 { 34struct 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 */ 41size_t levels_nr; 42 43/* The number of levels that have been allocated on the stack */ 44size_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 */ 50struct dir_iterator_level *levels; 51}; 52 53intdir_iterator_advance(struct dir_iterator *dir_iterator) 54{ 55struct dir_iterator_int *iter = 56(struct dir_iterator_int *)dir_iterator; 57 58while(1) { 59struct dir_iterator_level *level = 60&iter->levels[iter->levels_nr -1]; 61struct dirent *de; 62 63if(!level->initialized) { 64/* 65 * Note: dir_iterator_begin() ensures that 66 * path is not the empty string. 67 */ 68if(!is_dir_sep(iter->base.path.buf[iter->base.path.len -1])) 69strbuf_addch(&iter->base.path,'/'); 70 level->prefix_len = iter->base.path.len; 71 72 level->dir =opendir(iter->base.path.buf); 73if(!level->dir && errno != ENOENT) { 74warning("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)) { 81if(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; 88ALLOC_GROW(iter->levels, iter->levels_nr +1, 89 iter->levels_alloc); 90 level = &iter->levels[iter->levels_nr++]; 91 level->initialized =0; 92continue; 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 102if(!level->dir) { 103/* 104 * This level is exhausted (or wasn't opened 105 * successfully); pop up a level. 106 */ 107if(--iter->levels_nr ==0) 108returndir_iterator_abort(dir_iterator); 109 110continue; 111} 112 113/* 114 * Loop until we find an entry that we can give back 115 * to the caller: 116 */ 117while(1) { 118strbuf_setlen(&iter->base.path, level->prefix_len); 119 errno =0; 120 de =readdir(level->dir); 121 122if(!de) { 123/* This level is exhausted; pop up a level. */ 124if(errno) { 125warning("error reading directory%s:%s", 126 iter->base.path.buf,strerror(errno)); 127}else if(closedir(level->dir)) 128warning("error closing directory%s:%s", 129 iter->base.path.buf,strerror(errno)); 130 131 level->dir = NULL; 132if(--iter->levels_nr ==0) 133returndir_iterator_abort(dir_iterator); 134break; 135} 136 137if(is_dot_or_dotdot(de->d_name)) 138continue; 139 140strbuf_addstr(&iter->base.path, de->d_name); 141if(lstat(iter->base.path.buf, &iter->base.st) <0) { 142if(errno != ENOENT) 143warning("error reading path '%s':%s", 144 iter->base.path.buf, 145strerror(errno)); 146continue; 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 159return ITER_OK; 160} 161} 162} 163 164intdir_iterator_abort(struct dir_iterator *dir_iterator) 165{ 166struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator; 167 168for(; iter->levels_nr; iter->levels_nr--) { 169struct dir_iterator_level *level = 170&iter->levels[iter->levels_nr -1]; 171 172if(level->dir &&closedir(level->dir)) { 173strbuf_setlen(&iter->base.path, level->prefix_len); 174warning("error closing directory%s:%s", 175 iter->base.path.buf,strerror(errno)); 176} 177} 178 179free(iter->levels); 180strbuf_release(&iter->base.path); 181free(iter); 182return ITER_DONE; 183} 184 185struct dir_iterator *dir_iterator_begin(const char*path) 186{ 187struct dir_iterator_int *iter =xcalloc(1,sizeof(*iter)); 188struct dir_iterator *dir_iterator = &iter->base; 189 190if(!path || !*path) 191BUG("empty path passed to dir_iterator_begin()"); 192 193strbuf_init(&iter->base.path, PATH_MAX); 194strbuf_addstr(&iter->base.path, path); 195 196ALLOC_GROW(iter->levels,10, iter->levels_alloc); 197 198 iter->levels_nr =1; 199 iter->levels[0].initialized =0; 200 201return dir_iterator; 202}