1#include"cache.h" 2#include"dir.h" 3#include"iterator.h" 4#include"dir-iterator.h" 5 6struct dir_iterator_level { 7DIR*dir; 8 9/* 10 * The length of the directory part of path at this level 11 * (including a trailing '/'): 12 */ 13size_t prefix_len; 14}; 15 16/* 17 * The full data structure used to manage the internal directory 18 * iteration state. It includes members that are not part of the 19 * public interface. 20 */ 21struct dir_iterator_int { 22struct dir_iterator base; 23 24/* 25 * The number of levels currently on the stack. After the first 26 * call to dir_iterator_begin(), if it succeeds to open the 27 * first level's dir, this will always be at least 1. Then, 28 * when it comes to zero the iteration is ended and this 29 * struct is freed. 30 */ 31size_t levels_nr; 32 33/* The number of levels that have been allocated on the stack */ 34size_t levels_alloc; 35 36/* 37 * A stack of levels. levels[0] is the uppermost directory 38 * that will be included in this iteration. 39 */ 40struct dir_iterator_level *levels; 41 42/* Combination of flags for this dir-iterator */ 43unsigned int flags; 44}; 45 46/* 47 * Push a level in the iter stack and initialize it with information from 48 * the directory pointed by iter->base->path. It is assumed that this 49 * strbuf points to a valid directory path. Return 0 on success and -1 50 * otherwise, setting errno accordingly and leaving the stack unchanged. 51 */ 52static intpush_level(struct dir_iterator_int *iter) 53{ 54struct dir_iterator_level *level; 55 56ALLOC_GROW(iter->levels, iter->levels_nr +1, iter->levels_alloc); 57 level = &iter->levels[iter->levels_nr++]; 58 59if(!is_dir_sep(iter->base.path.buf[iter->base.path.len -1])) 60strbuf_addch(&iter->base.path,'/'); 61 level->prefix_len = iter->base.path.len; 62 63 level->dir =opendir(iter->base.path.buf); 64if(!level->dir) { 65int saved_errno = errno; 66if(errno != ENOENT) { 67warning_errno("error opening directory '%s'", 68 iter->base.path.buf); 69} 70 iter->levels_nr--; 71 errno = saved_errno; 72return-1; 73} 74 75return0; 76} 77 78/* 79 * Pop the top level on the iter stack, releasing any resources associated 80 * with it. Return the new value of iter->levels_nr. 81 */ 82static intpop_level(struct dir_iterator_int *iter) 83{ 84struct dir_iterator_level *level = 85&iter->levels[iter->levels_nr -1]; 86 87if(level->dir &&closedir(level->dir)) 88warning_errno("error closing directory '%s'", 89 iter->base.path.buf); 90 level->dir = NULL; 91 92return--iter->levels_nr; 93} 94 95/* 96 * Populate iter->base with the necessary information on the next iteration 97 * entry, represented by the given dirent de. Return 0 on success and -1 98 * otherwise, setting errno accordingly. 99 */ 100static intprepare_next_entry_data(struct dir_iterator_int *iter, 101struct dirent *de) 102{ 103int err, saved_errno; 104 105strbuf_addstr(&iter->base.path, de->d_name); 106/* 107 * We have to reset these because the path strbuf might have 108 * been realloc()ed at the previous strbuf_addstr(). 109 */ 110 iter->base.relative_path = iter->base.path.buf + 111 iter->levels[0].prefix_len; 112 iter->base.basename = iter->base.path.buf + 113 iter->levels[iter->levels_nr -1].prefix_len; 114 115if(iter->flags & DIR_ITERATOR_FOLLOW_SYMLINKS) 116 err =stat(iter->base.path.buf, &iter->base.st); 117else 118 err =lstat(iter->base.path.buf, &iter->base.st); 119 120 saved_errno = errno; 121if(err && errno != ENOENT) 122warning_errno("failed to stat '%s'", iter->base.path.buf); 123 124 errno = saved_errno; 125return err; 126} 127 128intdir_iterator_advance(struct dir_iterator *dir_iterator) 129{ 130struct dir_iterator_int *iter = 131(struct dir_iterator_int *)dir_iterator; 132 133if(S_ISDIR(iter->base.st.st_mode) &&push_level(iter)) { 134if(errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC) 135goto error_out; 136if(iter->levels_nr ==0) 137goto error_out; 138} 139 140/* Loop until we find an entry that we can give back to the caller. */ 141while(1) { 142struct dirent *de; 143struct dir_iterator_level *level = 144&iter->levels[iter->levels_nr -1]; 145 146strbuf_setlen(&iter->base.path, level->prefix_len); 147 errno =0; 148 de =readdir(level->dir); 149 150if(!de) { 151if(errno) { 152warning_errno("error reading directory '%s'", 153 iter->base.path.buf); 154if(iter->flags & DIR_ITERATOR_PEDANTIC) 155goto error_out; 156}else if(pop_level(iter) ==0) { 157returndir_iterator_abort(dir_iterator); 158} 159continue; 160} 161 162if(is_dot_or_dotdot(de->d_name)) 163continue; 164 165if(prepare_next_entry_data(iter, de)) { 166if(errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC) 167goto error_out; 168continue; 169} 170 171return ITER_OK; 172} 173 174error_out: 175dir_iterator_abort(dir_iterator); 176return ITER_ERROR; 177} 178 179intdir_iterator_abort(struct dir_iterator *dir_iterator) 180{ 181struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator; 182 183for(; iter->levels_nr; iter->levels_nr--) { 184struct dir_iterator_level *level = 185&iter->levels[iter->levels_nr -1]; 186 187if(level->dir &&closedir(level->dir)) { 188int saved_errno = errno; 189strbuf_setlen(&iter->base.path, level->prefix_len); 190 errno = saved_errno; 191warning_errno("error closing directory '%s'", 192 iter->base.path.buf); 193} 194} 195 196free(iter->levels); 197strbuf_release(&iter->base.path); 198free(iter); 199return ITER_DONE; 200} 201 202struct dir_iterator *dir_iterator_begin(const char*path,unsigned int flags) 203{ 204struct dir_iterator_int *iter =xcalloc(1,sizeof(*iter)); 205struct dir_iterator *dir_iterator = &iter->base; 206int saved_errno; 207 208strbuf_init(&iter->base.path, PATH_MAX); 209strbuf_addstr(&iter->base.path, path); 210 211ALLOC_GROW(iter->levels,10, iter->levels_alloc); 212 iter->levels_nr =0; 213 iter->flags = flags; 214 215/* 216 * Note: stat already checks for NULL or empty strings and 217 * inexistent paths. 218 */ 219if(stat(iter->base.path.buf, &iter->base.st) <0) { 220 saved_errno = errno; 221goto error_out; 222} 223 224if(!S_ISDIR(iter->base.st.st_mode)) { 225 saved_errno = ENOTDIR; 226goto error_out; 227} 228 229return dir_iterator; 230 231error_out: 232dir_iterator_abort(dir_iterator); 233 errno = saved_errno; 234return NULL; 235}