示例#1
0
/**
 * Walk directory tree and call given callback function to process each file/directory.
 *
 * @param start_dir path to the directory to walk recursively
 * @param data the options specifying how to walk the directory tree
 * @return 0 on success, -1 on error
 */
static int dir_scan(file_t* start_dir, file_search_data* data)
{
	dir_entry *dirs_stack = NULL; /* root of the dir_list */
	dir_iterator* it;
	int level = 0;
	int max_depth = data->max_depth;
	int options = data->options;
	file_t file;

	if (max_depth < 0 || max_depth >= MAX_DIRS_DEPTH) {
		max_depth = MAX_DIRS_DEPTH - 1;
	}

	/* skip the directory if max_depth == 0 */
	if (!max_depth) return 0;

	if (!FILE_ISDIR(start_dir)) {
		errno = ENOTDIR;
		return -1;
	}

	/* check if we should descend into the root directory */
	if ((options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == 0) {
		if (!data->call_back(start_dir, data->call_back_data))
			return 0;
	}

	/* allocate array of counters of directory elements */
	it = (dir_iterator*)malloc((MAX_DIRS_DEPTH + 1) * sizeof(dir_iterator));
	if (!it) {
		return -1;
	}

	/* push dummy counter for the root element */
	it[0].count = 1;
	it[0].dir_path = 0;

	memset(&file, 0, sizeof(file));

	while (!(data->options & FIND_CANCEL))
	{
		dir_entry **insert_at;
		char* dir_path;
		DIR *dp;
		struct dirent *de;

		/* climb down from the tree */
		while (--it[level].count < 0) {
			/* do not need this dir_path anymore */
			free(it[level].dir_path);

			if (--level < 0) {
				/* walked the whole tree */
				assert(!dirs_stack);
				free(it);
				return 0;
			}
		}
		assert(level >= 0 && it[level].count >= 0);

		/* take a filename from dirs_stack and construct the next path */
		if (level) {
			assert(dirs_stack != NULL);
			dir_path = make_path(it[level].dir_path, dirs_stack->filename);
			dir_entry_drop_head(&dirs_stack);
		} else {
			/* the first cycle: start from a root directory */
			dir_path = rsh_strdup(start_dir->path);
		}

		if (!dir_path) continue;

		/* fill the next level of directories */
		level++;
		assert(level < MAX_DIRS_DEPTH);
		it[level].count = 0;
		it[level].dir_path = dir_path;

		if ((options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == FIND_WALK_DEPTH_FIRST)
		{
			int res;
			file_init(&file, dir_path, 1);
			res = file_stat2(&file, USE_LSTAT);

			/* check if we should skip the directory */
			if (res < 0 || !data->call_back(&file, data->call_back_data)) {
				if (res < 0 && (options & FIND_LOG_ERRORS)) {
					data->errors_count++;
				}
				file_cleanup(&file);
				continue;
			}
		}
		file_cleanup(&file);

		/* step into directory */
		dp = opendir(dir_path);
		if (!dp) continue;

		insert_at = &dirs_stack;

		while ((de = readdir(dp)) != NULL)
		{
			int res;

			/* skip the "." and ".." directories */
			if (IS_CURRENT_OR_PARENT_DIR(de->d_name)) continue;

			file.mode = 0;
			file.path = make_path(dir_path, de->d_name);
			if (!file.path) continue;

			res  = file_stat2(&file, USE_LSTAT);
			if (res >= 0) {
				/* process the file or directory */
				if (FILE_ISDIR(&file) && (options & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS))) {
					res = ((options & FIND_FOLLOW_SYMLINKS) || !FILE_ISLNK(&file));
				} else if (FILE_ISREG(&file)) {
					/* handle file by callback function */
					res = data->call_back(&file, data->call_back_data);
				}

				/* check if file is a directory and we need to walk it, */
				/* but don't go deeper than max_depth */
				if (FILE_ISDIR(&file) && res && level < max_depth &&
					((options & FIND_FOLLOW_SYMLINKS) || !FILE_ISLNK(&file)))
				{
					/* add the directory name to the dirs_stack */
					if (dir_entry_insert(insert_at, de->d_name, file.mode)) {
						/* the directory name was successfully inserted */
						insert_at = &((*insert_at)->next);
						it[level].count++;
					}
				}
			} else if (options & FIND_LOG_ERRORS) {
				/* report error only if FIND_LOG_ERRORS option is set */
				log_file_error(file.path);
				data->errors_count++;
			}
			file_cleanup(&file);
		}
		closedir(dp);
	}

	while (dirs_stack) {
		dir_entry_drop_head(&dirs_stack);
	}
	while (level) {
		free(it[level--].dir_path);
	}
	free(it);
	assert(file.path == 0);
	return 0;
}
示例#2
0
/**
 * Walk directory tree and call given callback function to process each file/directory.
 *
 * @param start_dir path to the directory to walk recursively
 * @param options the options specifying how to walk the directory tree
 */
int find_file(file_t* start_dir, find_file_options* options)
{
	dir_entry *dirs_stack = NULL; /* root of the dir_list */
	dir_iterator* it;
	int level = 1;
	int max_depth = options->max_depth;
	int flags = options->options;
	dir_entry* entry;
	file_t file;

	if(max_depth < 0 || max_depth >= MAX_DIRS_DEPTH) {
		max_depth = MAX_DIRS_DEPTH;
	}

	/* skip the directory if max_depth == 0 */
	if(max_depth == 0) {
		return 0;
	}

	memset(&file, 0, sizeof(file));

	if((start_dir->mode & FILE_IFDIR) == 0) {
		errno = ENOTDIR;
		return -1;
	}

	/* check if we should descend into the root directory */
	if((flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS)) == 0) {
		if(!options->call_back(start_dir, options->call_back_data))
			return 0;
	}

	it = (dir_iterator*)malloc(MAX_DIRS_DEPTH * sizeof(dir_iterator));
	if(!it) return 0;

	/* push root directory into dirs_stack */
	it[0].left = 1;
	it[0].prev_dir = rsh_strdup(start_dir->path);
	it[1].prev_dir = NULL;
	if(!it[0].prev_dir) {
		errno = ENOMEM;
		return -1;
	}
	entry = dir_entry_insert(&dirs_stack, NULL, 0);
	if(!entry) {
		free(it[0].prev_dir);
		free(it);
		errno = ENOMEM;
		return -1;
	}

	while(!(options->options & FIND_CANCEL)) {
		dir_entry *dir, **insert_at;
		char* dir_path;
		DIR *dp;
		struct dirent *de;

		/* walk back */
		while((--level) >= 0 && it[level].left <= 0) free(it[level+1].prev_dir);
		if(level < 0) break;
		assert(dirs_stack != NULL);
		/* on the first cycle: level == 0, stack[0] == 0; */

		dir = dirs_stack; /* take last dir from the list */
		dirs_stack = dirs_stack->next; /* remove last dir from the list */
		it[level].left--;

		dir_path = (!dir->filename ? rsh_strdup(it[level].prev_dir) :
			make_path(it[level].prev_dir, dir->filename) );
		dir_entry_free(dir);
		if(!dir_path) continue;

		level++;
		it[level].left = 0;
		it[level].prev_dir = dir_path;

		if((flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS))
			== FIND_WALK_DEPTH_FIRST) {
			rsh_file_cleanup(&file);
			file.path = dir_path;
			/* check if we should skip the directory */
			if(!options->call_back(&file, options->call_back_data))
				continue;
		}

		/* read dir */
		dp = opendir(dir_path);
		if(dp == NULL) continue;

		insert_at = &dirs_stack;

		while((de = readdir(dp)) != NULL) {
			int res;
			/* skip "." and ".." dirs */
			if(de->d_name[0] == '.' && (de->d_name[1] == 0 ||
				(de->d_name[1] == '.' && de->d_name[2] == 0 )))
				continue;

			if( !(file.path = make_path(dir_path, de->d_name)) ) continue;

			res  = rsh_file_stat2(&file, USE_LSTAT);
			/* process */
			if(res >= 0) {
				if((file.mode & FILE_IFDIR) &&
					(flags & (FIND_WALK_DEPTH_FIRST | FIND_SKIP_DIRS))) res = 1;
				else {
					/* handle file by callback function */
					res = options->call_back(&file, options->call_back_data);
				}

				/* if file is a directory and we need to walk it */
				if((file.mode & FILE_IFDIR) && res && level < max_depth) {
					/* don't go deeper if max_depth reached */

					/* add directory to dirs_stack */
					if(dir_entry_insert(insert_at, de->d_name, file.mode)) {
						/* if really added */
						insert_at = &((*insert_at)->next);
						it[level].left++;
					}
				}
			} else if (options->options & FIND_LOG_ERRORS) {
				log_file_error(file.path);
			}
			rsh_file_cleanup(&file);
			free(file.path);
		}
		closedir(dp);

		if(it[level].left > 0) level++;
	}
	assert(dirs_stack == NULL);

	free(it);
	return 0;
}