%% options
copyright owner = Dirk Krause
copyright year = 2015-xxxx
license = bsd
%% header
/** @file
Directory traversal using char file names.
CRT on Windows: Optional.
*/
#ifndef DK4CONF_H_INCLUDED
#include "dk4conf.h"
#endif
#ifndef DK4TYPES_H_INCLUDED
#include "dk4types.h"
#endif
#ifndef DK4ERROR_H_INCLUDED
#include "dk4error.h"
#endif
#ifndef DK4STO_H_INCLUDED
#include "dk4sto.h"
#endif
/** Directory structure.
*/
typedef struct {
char *path; /**< Directory path. */
dk4_sto_t *s_dir; /**< Storage for directories. */
dk4_sto_it_t *i_dir; /**< Iterator for directories. */
dk4_sto_t *s_file; /**< Storage for non-directories. */
dk4_sto_it_t *i_file; /**< Iterator for non-directories. */
size_t maxlen; /**< Maximum entry length. */
} dk4_dir_c8_t;
#ifdef __cplusplus
extern "C" {
#endif
/** Open directory, create directory structure.
@param path Directory path name.
@param om Opening mode.
@param erp Error report, may be NULL.
@return Pointer to new directory structure on success, NULL on error.
On success use dk4dir_c8_close() to destroy the directory
structure.
Error codes:
- DK4_E_INVALID_ARGUMENTS
if path is NULL,
- DK4_E_NOT_SUPPORTED
if no function to traverse directories was found during build process,
- DK4_E_MEMORY_ALLOCATION_FAILED
if a memory allocation failed,
- DK4_E_MATH_OVERFLOW
if a mathematical overflow occured in a size calculation,
- DK4_E_BUFFER_TOO_SMALL
if one of the involved directory or file names is too long for an
internal buffer,
- DK4_E_OPENDIR_FAILED
with errno value in iDetails1 if the attempt to open the directory
failed,
- DK4_E_FINDFIRSTFILE_FAILED
with GetLastError() value in lDetails1 if the FindFirstFile()
function failed on Windows,
- DK4_E_SYSTEM
if the stat() or lstat() function failed for one of the directory
items.
*/
dk4_dir_c8_t *
dk4dir_c8_open(const char *path, int om, dk4_er_t *erp);
/** Destroy directory structure.
@param dptr Structure to destroy.
*/
void
dk4dir_c8_close(dk4_dir_c8_t *dptr);
/** Reset directory traversal.
@param dptr Directory structure.
*/
void
dk4dir_c8_reset(dk4_dir_c8_t *dptr);
/** Retrieve directory path.
@param dptr Directory structure.
@return Directory path on success,
NULL on error or if no directory was specified for a
file name expansion.
*/
const char *
dk4dir_c8_get_path(dk4_dir_c8_t *dptr);
/** Retrieve next directory name.
@param dptr Directory structure.
@return Pointer to short name on success, NULL if there are no
more directories.
*/
const char *
dk4dir_c8_next_dir(dk4_dir_c8_t *dptr);
/** Retrieve next file (non-directory) name.
@param dptr Directory structure.
@return Pointer to short name on success, NULL if there are no more
files.
*/
const char *
dk4dir_c8_next_file(dk4_dir_c8_t *dptr);
/** Skip files (remove information about files, release memory).
@param dptr Directory structure.
*/
void
dk4dir_c8_skip_files(dk4_dir_c8_t *dptr);
/** Compare two file or directory names.
@param l Left name.
@param r Right name.
@param cr Comparison criteria (ignored).
@return 1 if l>r, 0 if l=r, -1 if l
#define WINDOWS_H_INCLUDED 1
#endif
#endif
#include "dk4dir08.h"
#ifndef DK4DIR_H_INCLUDED
#include "dk4dir.h"
#endif
#ifndef DK4STR8_H_INCLUDED
#include "dk4str8.h"
#endif
#ifndef DK4STAT_H_INCLUDED
#include "dk4stat.h"
#endif
#ifndef DK4STAT8_H_INCLUDED
#include "dk4stat8.h"
#endif
#ifndef DK4MEM_H_INCLUDED
#include "dk4mem.h"
#endif
#ifndef DK4MPL_H_INCLUDED
#include "dk4mpl.h"
#endif
#if DK4_HAVE_SYS_TYPES_H
#ifndef SYS_TYPES_H_INCLUDED
#include
#define SYS_TYPES_H_INCLUDED 1
#endif
#endif
#if DK4_HAVE_DIRENT_H
#ifndef DIRENT_H_INCLUDED
#include
#define DIRENT_H_INCLUDED 1
#endif
#endif
#if DK4_HAVE_ERRNO_H
#ifndef ERRNO_H_INCLUDED
#include
#define ERRNO_H_INCLUDED 1
#endif
#endif
$!trace-include
int
dk4dir_c8_compare(const void *l, const void *r, int cr)
{
int back = 0;
if (NULL != l) {
if (NULL != r) {
back = dk4str8_pathcmp((const char *)l, (const char *)r);
} else {
back = 1;
}
} else {
if (NULL != r) { back = -1; }
}
return back;
}
static
void
dk4dir_c8_clean_storage(dk4_sto_t *s, dk4_sto_it_t *i)
{
char *n;
if (NULL != s) {
if (NULL != i) {
dk4sto_it_reset(i);
while (NULL != (n = (char *)dk4sto_it_next(i))) {
dk4mem_free(n);
}
dk4sto_it_close(i);
}
dk4sto_close(s);
}
}
void
dk4dir_c8_close(dk4_dir_c8_t *dptr)
{
$? "+ dk4dir_c8_close"
if (NULL != dptr) {
dk4mem_release(dptr->path);
dk4dir_c8_clean_storage(dptr->s_dir, dptr->i_dir);
dptr->s_dir = NULL; dptr->i_dir = NULL;
dk4dir_c8_clean_storage(dptr->s_file, dptr->i_file);
dptr->s_file = NULL; dptr->i_file = NULL;
dk4mem_free(dptr);
}
$? "- dk4dir_c8_close"
}
#if DK4_ON_WINDOWS || DK4_HAVE_DIRENT_H
/** Fixed texts used by the module.
*/
static const char * const dk4dir_c8_kw[] = {
$!string-table
#
# 0 Current directory
#
.
#
# 1 Parent directory
#
..
#
# 2 Separator for non-Windows systems
#
/
#
# 3 Separator for Windows systems
#
\\
#
# 4 Pattern for all files
#
*
$!end
};
static
int
dk4dir_c8_fill_directory(dk4_dir_c8_t *dptr, int om, dk4_er_t *erp)
{
#if DK4_ON_WINDOWS
char pab[DK4_MAX_PATH]; /* Pattern buffer */
WIN32_FIND_DATAA ffdata; /* Find result */
HANDLE ffres; /* Result from FindFirstFile */
DWORD attr; /* File attributes */
char *nn; /* Copy of name, dyn */
size_t slen; /* Entry name length */
int isdir; /* Flag: Entry is directory */
int back = 0;
$? "+ dk4dir_c8_fill_directory"
if (dk4str8_cpy_s(pab, sizeof(pab), dptr->path, erp)) {
if (dk4str8_cat_s(pab, sizeof(pab), dk4dir_c8_kw[3], erp)) {
if (dk4str8_cat_s(pab, sizeof(pab), dk4dir_c8_kw[4], erp)) {
ffres = FindFirstFileA(pab, &ffdata);
if (INVALID_HANDLE_VALUE != ffres) {
back = 1;
do {
if (0 != dk4str8_cmp(dk4dir_c8_kw[0], ffdata.cFileName)) {
if (0 != dk4str8_cmp(dk4dir_c8_kw[1], ffdata.cFileName)) {
slen = dk4str8_len(ffdata.cFileName);
if (slen > dptr->maxlen) { dptr->maxlen = slen; }
nn = dk4str8_dup(ffdata.cFileName, erp);
if (NULL != nn) {
isdir = 0;
attr = ffdata.dwFileAttributes;
if (0 != (FILE_ATTRIBUTE_DIRECTORY & attr)) {
isdir = 1;
if (0 != (FILE_ATTRIBUTE_REPARSE_POINT & attr)) {
if (0 != (DK4_DIR_SYMLINK_DIR_AS_FILE & om)) {
isdir = 0;
}
}
}
if (0 != isdir) {
if (0 == dk4sto_add(dptr->s_dir, (void *)nn, erp)) {
back = 0;
dk4mem_free(nn);
}
} else {
if (0 == dk4sto_add(dptr->s_file, (void *)nn, erp)) {
back = 0;
dk4mem_free(nn);
}
}
} else {
back = 0;
}
}
}
} while(FindNextFileA(ffres, &ffdata));
FindClose(ffres);
} else {
/* ERROR: FindFirstFile failed */
dk4error_set_ldetails(
erp, DK4_E_FINDFIRSTFILE_FAILED,
(long)((unsigned long)GetLastError())
);
}
}
}
}
$? "- dk4dir_c8_fill_directory %d", back
return back;
#else
char fnb[DK4_MAX_PATH]; /* File name buffer */
dk4_stat_t stb; /* Stat buffer */
DIR *dir; /* Directory */
struct dirent *de; /* Directory entry */
char *nn; /* Dynamic copy of file name */
size_t slen; /* Entry name length */
int isdir = 0; /* Flag: Current item is directory */
int back = 0;
$? "+ dk4dir_c8_fill_directory"
errno = 0;
dir = opendir(dptr->path);
if (NULL != dir) { $? ". opendir"
back = 1;
do {
de = readdir(dir);
if (NULL != de) {
if (0 < (slen = dk4str8_len(de->d_name))) {
if (slen > dptr->maxlen) { dptr->maxlen = slen; }
if (0 != dk4str8_cmp(dk4dir_c8_kw[0], de->d_name)) {
if (0 != dk4str8_cmp(dk4dir_c8_kw[1], de->d_name)) {
if (dk4str8_cpy_s(fnb, sizeof(fnb), dptr->path, erp)) {
if (dk4str8_cat_s(fnb, sizeof(fnb), dk4dir_c8_kw[2], erp)) {
if (dk4str8_cat_s(fnb, sizeof(fnb), de->d_name, erp)) {
nn = dk4str8_dup(de->d_name, erp);
if (NULL != nn) {
isdir = 0;
if (0 != dk4stat_c8(&stb, fnb, erp)) {
isdir = dk4stat_is_directory(&stb, NULL);
if (0 != isdir) {
if (0 != (DK4_DIR_SYMLINK_DIR_AS_FILE & om)) {
if (0 != dk4stat_l_c8(&stb, fnb, NULL)) {
if (dk4stat_is_symlink(&stb, NULL)) {
isdir = 0;
}
}
}
}
} else { $? "! stat"
#if 0
/* 2015-04-11 Dangling links are not
directories.
*/
back = 0;
#endif
}
if (0 != isdir) {
if (0 == dk4sto_add(dptr->s_dir, (void *)nn, erp)) {
back = 0; $? "! memory"
dk4mem_free(nn);
}
} else {
if (0 == dk4sto_add(dptr->s_file, (void *)nn, erp)) {
back = 0; $? "! memory"
dk4mem_free(nn);
}
}
} else { $? "! memory"
back = 0;
}
} else { $? "! buffer too small"
back = 0;
}
} else { $? "! buffer too small"
back = 0;
}
} else { $? "! buffer too small"
back = 0;
}
}
}
}
}
} while(NULL != de);
closedir(dir);
} else { $? "! opendir"
dk4error_set_idetails(erp, DK4_E_OPENDIR_FAILED, errno);
} $? "- dk4dir_c8_fill_directory %d", back
return back;
#endif
}
#endif
dk4_dir_c8_t *
dk4dir_c8_open(const char *path, int om, dk4_er_t *erp)
{
dk4_dir_c8_t *back = NULL;
int ok = 0;
$? "+ dk4dir_c8_open \"%!8s\"", TR_8STR(path)
if (NULL != path) {
#if DK4_ON_WINDOWS || DK4_HAVE_DIRENT_H
back = dk4mem_new(dk4_dir_c8_t, 1, erp);
if (NULL != back) {
back->path = NULL;
back->s_dir = NULL;
back->i_dir = NULL;
back->s_file = NULL;
back->i_file = NULL;
back->path = dk4str8_dup(path, erp);
back->maxlen = 0;
if (NULL != back->path) {
back->s_dir = dk4sto_open(erp);
if (NULL != back->s_dir) {
if (0 != (DK4_DIR_OPEN_SORTED & om)) {
dk4sto_set_comp(back->s_dir, dk4dir_c8_compare, 0);
}
back->i_dir = dk4sto_it_open(back->s_dir, erp);
if (NULL != back->i_dir) {
back->s_file = dk4sto_open(erp);
if (NULL != back->s_file) {
if (0 != (DK4_DIR_OPEN_SORTED & om)) {
dk4sto_set_comp(back->s_file, dk4dir_c8_compare, 0);
}
back->i_file = dk4sto_it_open(back->s_file, erp);
if (NULL != back->i_file) {
ok = dk4dir_c8_fill_directory(back, om, erp);
}
}
}
}
}
if (0 == ok) {
dk4dir_c8_close(back);
back = NULL;
}
}
#else
dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED);
#endif
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
}
$? "- dk4dir_c8_open PTR=%d", TR_IPTR(back)
return back;
}
void
dk4dir_c8_reset(dk4_dir_c8_t *dptr)
{
$? "+ dk4dir_c8_reset"
if (NULL != dptr) {
if ((NULL != dptr->s_dir) && (NULL != dptr->i_dir)) {
dk4sto_it_reset(dptr->i_dir);
}
if ((NULL != dptr->s_file) && (NULL != dptr->i_file)) {
dk4sto_it_reset(dptr->i_file);
}
}
$? "- dk4dir_c8_reset"
}
const char *
dk4dir_c8_get_path(dk4_dir_c8_t *dptr)
{
const char *back = NULL;
$? "+ dk4dir_c8_get_path"
if (NULL != dptr) {
back = (const char *)(dptr->path);
}
$? "- dk4dir_c8_get_path \"%!8s\"", TR_8STR(back)
return back;
}
const char *
dk4dir_c8_next_dir(dk4_dir_c8_t *dptr)
{
const char *back = NULL;
$? "+ dk4dir_c8_next_dir"
if (NULL != dptr) {
if ((NULL != dptr->s_dir) && (NULL != dptr->i_dir)) {
back = (const char *)dk4sto_it_next(dptr->i_dir);
}
}
$? "- dk4dir_c8_next_dir \"%!8s\"", TR_8STR(back)
return back;
}
const char *
dk4dir_c8_next_file(dk4_dir_c8_t *dptr)
{
const char *back = NULL;
$? "+ dk4dir_c8_next_file"
if (NULL != dptr) {
if ((NULL != dptr->s_file) && (NULL != dptr->i_file)) {
back = (const char *)dk4sto_it_next(dptr->i_file);
}
}
$? "- dk4dir_c8_next_file \"%!8s\"", TR_8STR(back)
return back;
}
void
dk4dir_c8_skip_files(dk4_dir_c8_t *dptr)
{
$? "+ dk4dir_c8_skip_files"
if (NULL != dptr) {
dk4dir_c8_clean_storage(dptr->s_file, dptr->i_file);
dptr->s_file = NULL; dptr->i_file = NULL;
}
$? "- dk4dir_c8_skip_files"
}
size_t
dk4dir_c8_get_max_entry_length(dk4_dir_c8_t *dptr)
{
size_t back = 0;
$? "+ dk4dir_c8_get_max_entry_length"
if (NULL != dptr) {
back = dptr->maxlen;
}
$? "- dk4dir_c8_get_max_entry_length %lu", (unsigned long)back
return back;
}