%% options
copyright owner = Dirk Krause
copyright year = 2015-xxxx
license = bsd
%% header
/** @file
Directory traversal using wchar_t 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 {
wchar_t *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 name length. */
} dk4_dir_wc_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_wc_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_wc_t *
dk4dir_wc_open(const wchar_t *path, int om, dk4_er_t *erp);
/** Destroy directory structure.
@param dptr Structure to destroy.
*/
void
dk4dir_wc_close(dk4_dir_wc_t *dptr);
/** Reset directory traversal.
@param dptr Directory structure.
*/
void
dk4dir_wc_reset(dk4_dir_wc_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 wchar_t *
dk4dir_wc_get_path(dk4_dir_wc_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 wchar_t *
dk4dir_wc_next_dir(dk4_dir_wc_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 wchar_t *
dk4dir_wc_next_file(dk4_dir_wc_t *dptr);
/** Skip files (remove information about files, release memory).
@param dptr Directory structure.
*/
void
dk4dir_wc_skip_files(dk4_dir_wc_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 "dk4dirwc.h"
#ifndef DK4DIR_H_INCLUDED
#include "dk4dir.h"
#endif
#ifndef DK4STR8_H_INCLUDED
#include "dk4strw.h"
#endif
#ifndef DK4STAT_H_INCLUDED
#include "dk4stat.h"
#endif
#ifndef DK4STAT8_H_INCLUDED
#include "dk4statw.h"
#endif
#ifndef DK4MEM_H_INCLUDED
#include "dk4mem.h"
#endif
#ifndef DK4MPL_H_INCLUDED
#include "dk4mpl.h"
#endif
#ifndef DK4MAASZ_H_INCLUDED
#include "dk4maasz.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_WCHAR_H
#ifndef WCHAR_H_INCLUDED
#include
#define WCHAR_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
#if DK4_ON_WINDOWS
int
dk4dir_wc_compare(const void *l, const void *r, int cr)
{
int back = 0;
if (NULL != l) {
if (NULL != r) {
back = dk4strw_pathcmp((const wchar_t *)l, (const wchar_t *)r);
} else {
back = 1;
}
} else {
if (NULL != r) { back = -1; }
}
return back;
}
#endif
static
void
dk4dir_wc_clean_storage(dk4_sto_t *s, dk4_sto_it_t *i)
{
wchar_t *n;
if (NULL != s) {
if (NULL != i) {
dk4sto_it_reset(i);
while (NULL != (n = (wchar_t *)dk4sto_it_next(i))) {
dk4mem_free(n);
}
dk4sto_it_close(i);
}
dk4sto_close(s);
}
}
void
dk4dir_wc_close(dk4_dir_wc_t *dptr)
{
if (NULL != dptr) {
dk4mem_release(dptr->path);
dk4dir_wc_clean_storage(dptr->s_dir, dptr->i_dir);
dptr->s_dir = NULL; dptr->i_dir = NULL;
dk4dir_wc_clean_storage(dptr->s_file, dptr->i_file);
dptr->s_file = NULL; dptr->i_file = NULL;
dk4mem_free(dptr);
}
}
#if DK4_ON_WINDOWS
/** Fixed texts used by the module.
*/
static const wchar_t * const dk4dir_wc_kw[] = {
$!string-table prefix=L
#
# 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_wc_fill_with_pab(
dk4_dir_wc_t *dptr,
int om,
wchar_t *pab,
size_t szpab,
dk4_er_t *erp
)
{
WIN32_FIND_DATAW ffdata; /* Find result */
HANDLE ffres; /* Result from findfirst */
dkChar *nn; /* Copy of name, dyn */
DWORD attr; /* File attributes */
size_t slen; /* Entry name length. */
int isdir; /* Flag: Entry is directory */
int back = 0;
$? "+ dk4dir_wc_fill_directory"
if (dk4strw_cpy_s(pab, szpab, dptr->path, erp)) {
if (dk4strw_cat_s(pab, szpab, dk4dir_wc_kw[3], erp)) {
if (dk4strw_cat_s(pab, szpab, dk4dir_wc_kw[4], erp)) {
ffres = FindFirstFileW(pab, &ffdata);
if (INVALID_HANDLE_VALUE != ffres) {
back = 1;
do {
if (0 != dk4strw_cmp(dk4dir_wc_kw[0], ffdata.cFileName)) {
if (0 != dk4strw_cmp(dk4dir_wc_kw[1], ffdata.cFileName)) {
slen = dk4strw_len(ffdata.cFileName);
if (slen > dptr->maxlen) { dptr->maxlen = slen; }
nn = dk4strw_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(FindNextFileW(ffres, &ffdata));
FindClose(ffres);
} else {
/* ERROR: FindFirstFile failed */
dk4error_set_ldetails(
erp, DK4_E_FINDFIRSTFILE_FAILED,
(long)((unsigned long)GetLastError())
);
}
}
}
}
$? "- dk4dir_wc_fill_directory %d", back
return back;
}
static
int
dk4dir_wc_fill_with_local(dk4_dir_wc_t *dptr, int om, dk4_er_t *erp)
{
wchar_t pab[DK4_MAX_PATH];
return (dk4dir_wc_fill_with_pab(dptr, om, pab, DK4_SIZEOF(pab,wchar_t), erp));
}
/** Fill directory structure with data about subdirectories and files.
@param dptr Directory structure to fill.
@param om Opening mode.
@param erp Error report, may be NULL.
@return 1 on success, 0 on errors.
*/
static
int
dk4dir_wc_fill_directory(dk4_dir_wc_t *dptr, int om, dk4_er_t *erp)
{
dk4_er_t er;
wchar_t *pab;
size_t dptrlgt;
int back = 0;
dptrlgt = dk4strw_len(dptr->path);
if ((DK4_MAX_PATH - 2) > dptrlgt) {
back = dk4dir_wc_fill_with_local(dptr, om, erp);
} else {
dk4error_init(&er);
dptrlgt = dk4ma_size_t_add(dptrlgt, 3, &er);
if (DK4_E_NONE == er.ec) {
pab = dk4mem_new(wchar_t, dptrlgt, erp);
if (NULL != pab) {
back = dk4dir_wc_fill_with_pab(dptr, om, pab, dptrlgt, erp);
dk4mem_free(pab);
}
} else {
dk4error_copy(erp, &er);
}
}
return back;
}
#endif
dk4_dir_wc_t *
dk4dir_wc_open(const wchar_t *path, int om, dk4_er_t *erp)
{
dk4_dir_wc_t *back = NULL;
#if DK4_ON_WINDOWS
int ok = 0;
#endif
if (NULL != path) {
#if DK4_ON_WINDOWS
back = dk4mem_new(dk4_dir_wc_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 = dk4strw_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_wc_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_wc_compare, 0);
}
back->i_file = dk4sto_it_open(back->s_file, erp);
if (NULL != back->i_file) {
ok = dk4dir_wc_fill_directory(back, om, erp);
}
}
}
}
}
if (0 == ok) {
dk4dir_wc_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);
}
return back;
}
void
dk4dir_wc_reset(dk4_dir_wc_t *dptr)
{
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);
}
}
}
const wchar_t *
dk4dir_wc_get_path(dk4_dir_wc_t *dptr)
{
const wchar_t *back = NULL;
if (NULL != dptr) {
back = (const wchar_t *)(dptr->path);
}
return back;
}
const wchar_t *
dk4dir_wc_next_dir(dk4_dir_wc_t *dptr)
{
const wchar_t *back = NULL;
if (NULL != dptr) {
if ((NULL != dptr->s_dir) && (NULL != dptr->i_dir)) {
back = (const wchar_t *)dk4sto_it_next(dptr->i_dir);
}
}
return back;
}
const wchar_t *
dk4dir_wc_next_file(dk4_dir_wc_t *dptr)
{
const wchar_t *back = NULL;
if (NULL != dptr) {
if ((NULL != dptr->s_file) && (NULL != dptr->i_file)) {
back = (const wchar_t *)dk4sto_it_next(dptr->i_file);
}
}
return back;
}
void
dk4dir_wc_skip_files(dk4_dir_wc_t *dptr)
{
if (NULL != dptr) {
dk4dir_wc_clean_storage(dptr->s_file, dptr->i_file);
dptr->s_file = NULL; dptr->i_file = NULL;
}
}
size_t
dk4dir_wc_get_max_entry_length(dk4_dir_wc_t *dptr)
{
size_t back = 0;
if (NULL != dptr) {
back = dptr->maxlen;
}
return back;
}