dir.c 4.02 KB
/*
 * dir.c: This is the home of the posix-style get_dir_first, get_dir_next.
 *        Right now, those function only return non-directory filenames.
 *        (This is because thats what i wanted for checkout_files.)
 *
 * Copyright (C) 2006 Georg Steffers
 *
 * Author:    Georg Steffers [GST] <georg.steffers@aschendorff.de>
 * Developer:
 *
 * Changes (for this file only):
 *   (2006-06-12) [GST] Started this changelog...well the program is
 *                      ready since some weeks right now.
 */
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <scot/dir.h>
#include <scot/exception.h>

/* 
 * within the get_dir_... functions one can decide if one wants to follow
 * symlinks or not. To achive this i use different stat functions to
 * identify files. A variable of this type is capable of holding those
 * stat functions.
 */
typedef int (*stat_func_t) (const char *, struct stat *);

/*
 * This functions tries an opendir on the given path and uses the
 * get_dir_next function to retrieve the first file within that directory.
 *
 * parameters:
 *   path: The path to the directory that should be read.
 *   file: A pointer to a structure that holds further information to the
 *         found file. (e.g. the path to the file)
 *   fsym: fsym==0 => don't follow symlinks, fsym!=0 => follow symlinks
 *
 * returns:
 *   NULL if an error occures or an handle to the opened directory on
 *   success. This handle should be closed later in the program.
 *   There should be a function that does this, but as this program
 *   actually exits anyway after directory reading i have not done it
 *   right now.
 */
DIR_HANDLE
get_dir_first (const char * path, FILE_DATA * file, int fsym)
{
	DIR * dir;

	dir = opendir (path); /* !!!FIXME!!! Do error handling here */

	/* opendir could fail for various reasons, most likely are two.
	 * The given string does not reflect any file, or is no directory */
	if (NULL == dir)
		/* errno is set by opendir */
		return NULL;

	if (get_dir_next (dir, path, file, fsym) != GET_DIRENT_OK)
		return NULL;

	return dir;
}

/*
 * This functions retrieves the next file within the given dir.
 *
 * parameters:
 *   dir:  Handle to a directory opened with get_dir_first previously.
 *   path: The path to the directory that is read.
 *         (hmmm, this seems not that ideal to me...why should this
 *          function construct the full path to the found file.
 *          The filename is more that enough i think, as the caller knows
 *          the path already.)
 *   file: A pointer to a structure that holds further information to the
 *         found file. (e.g. the path to the file)
 *   fsym: fsym==0 => don't follow symlinks, fsym!=0 => follow symlinks
 *
 * returns:
 *   This function returns always one of the following:
 *   GET_DIRENT_ERR: if an error occured...errno might or might not be set.
 *   NO_FILES_LEFT:  if the last entry of the directory was read.
 *   GET_DIRENT_OK:  if the next directory entry was read successfully.
 */
int
get_dir_next (DIR_HANDLE dir, const char * path, FILE_DATA * file, int fsym)
{
	stat_func_t my_stat_f;

	/* first look if we should use a symlink following stat */
	switch (fsym)
	{
		case DONT_FOLLOW_SYM:   my_stat_f = lstat; break;
		case FOLLOW_SYM:        
		default:                my_stat_f = stat;;
	}

	strncpy (file->path, path, MAX_PATH);

	/* add a / to path but don't write more than MAX_PATH */
	if (strlen (file->path) < MAX_PATH && 
	    file->path [strlen (file->path)-1] != '/')
	{
		file->path [strlen (file->path) + 1] = '\0';
		file->path [strlen (file->path)] = '/';
	}

	/* now fill the given struct dirent_stat */
	errno = 0;
	file->file = readdir (dir);

	if (NULL == file->file)
	{
		if (NULL != dir)
			closedir (dir);

		if (errno != EBADF)
			return NO_FILES_LEFT;

		return GET_DIRENT_ERR;
	}

	strncpy (
	      file->path+strlen (file->path),
	      file->file->d_name,
	      MAX_PATH-strlen (file->path));

	if (my_stat_f (file->path, &(file->stat)) == -1)
	{
		/* errno is filled by the stat function */
		return GET_DIRENT_ERR;
	}

	return GET_DIRENT_OK;
}