Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions inc/dmod_sal.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ DMOD_BUILTIN_API(Dmod, 1.0, size_t , _FileSize, ( void* File ) );
DMOD_BUILTIN_API(Dmod, 1.0, void , _FileClose, ( void* File ) );
DMOD_BUILTIN_API(Dmod, 1.0, const char* , _GetRepoDir, ( void ) );
DMOD_BUILTIN_API(Dmod, 1.0, bool , _FileAvailable, ( const char* Path ) );
DMOD_BUILTIN_API(Dmod, 1.0, void* , _OpenDir, ( const char* Path ) );
DMOD_BUILTIN_API(Dmod, 1.0, const char* , _ReadDir, ( void* Dir ) );
DMOD_BUILTIN_API(Dmod, 1.0, void , _CloseDir, ( void* Dir ) );
DMOD_BUILTIN_API(Dmod, 1.0, void* , _OpenDir, ( const char* Path ) );
DMOD_BUILTIN_API(Dmod, 1.0, const char* , _ReadDir, ( void* Dir ) );
DMOD_BUILTIN_API(Dmod, 1.0, const Dmod_DirEntry_t* , _ReadDirEx, ( void* Dir ) );
DMOD_BUILTIN_API(Dmod, 1.0, void , _CloseDir, ( void* Dir ) );
DMOD_BUILTIN_API(Dmod, 1.0, int , _MakeDir, ( const char* Path, int Mode ) );
DMOD_BUILTIN_API(Dmod, 1.0, int , _Access, ( const char* Path, int Mode ) );
DMOD_BUILTIN_API(Dmod, 1.0, char* , _FileReadLine, ( char* Buffer, int Size, void* File ) );
Expand Down
23 changes: 23 additions & 0 deletions inc/dmod_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,29 @@ typedef struct
void* _Data; //!< Internal data used for iteration
} Dmod_ModuleNode_t;

/**
* @brief Directory entry type
*/
typedef enum
{
Dmod_DirEntryType_Unknown = 0, //!< Unknown type
Dmod_DirEntryType_File, //!< Regular file
Dmod_DirEntryType_Dir, //!< Directory
Dmod_DirEntryType_Link, //!< Symbolic link
Dmod_DirEntryType_Other //!< Other type (socket, FIFO, etc.)
} Dmod_DirEntryType_t;

/**
* @brief Directory entry structure
*
* Structure returned by Dmod_ReadDirEx containing information about a directory entry.
*/
typedef struct
{
const char* name; //!< Name of the entry
Dmod_DirEntryType_t type; //!< Type of the entry
} Dmod_DirEntry_t;

#ifdef __cplusplus
}
#endif
Expand Down
57 changes: 57 additions & 0 deletions src/system/if/dmod_if_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,63 @@ DMOD_INPUT_WEAK_API_DECLARATION(Dmod, 1.0, const char*, _ReadDir, ( void* Dir ))
#endif
}

/**
* @brief Read directory entry with extended information
*
* @param Dir Pointer to directory handle
*
* @return Pointer to Dmod_DirEntry_t structure with entry information, NULL when no more entries
*
* @note The returned pointer points to static storage that may be overwritten by subsequent calls.
* This behavior is consistent with the POSIX readdir() function and the original Dmod_ReadDir().
* Not thread-safe: use separate directory handles per thread.
*/
DMOD_INPUT_WEAK_API_DECLARATION(Dmod, 1.0, const Dmod_DirEntry_t*, _ReadDirEx, ( void* Dir ))
{
#if DMOD_USE_DIRENT
static Dmod_DirEntry_t dirEntry;
struct dirent* entry = readdir((DIR*)Dir);

if (entry == NULL)
{
return NULL;
}

dirEntry.name = entry->d_name;
dirEntry.type = Dmod_DirEntryType_Unknown;

// Determine entry type based on d_type if available
#if defined(_DIRENT_HAVE_D_TYPE)
// Use constants from dirent.h: DT_REG=8, DT_DIR=4, DT_LNK=10
// Direct comparison is used for compatibility with systems where these
// constants might not be properly available in preprocessor context
switch (entry->d_type)
{
case 8: // DT_REG - Regular file
dirEntry.type = Dmod_DirEntryType_File;
break;
case 4: // DT_DIR - Directory
dirEntry.type = Dmod_DirEntryType_Dir;
break;
case 10: // DT_LNK - Symbolic link
dirEntry.type = Dmod_DirEntryType_Link;
break;
case 0: // DT_UNKNOWN
dirEntry.type = Dmod_DirEntryType_Unknown;
break;
default: // Other types (socket, FIFO, etc.)
dirEntry.type = Dmod_DirEntryType_Other;
break;
}
#endif

return &dirEntry;
#else
DMOD_LOG_ERROR("Dmod_ReadDirEx interface not implemented\n");
return NULL;
#endif
}

/**
* @brief Close directory
*
Expand Down
135 changes: 135 additions & 0 deletions tests/system/if/tests_dmod_dir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,138 @@ TEST_F(DmodDirTest, RemoveDirNonExistent)
int result = Dmod_RemoveDir("/non/existent/path/xyz123456");
ASSERT_EQ(result, -1);
}

/**
* @brief Test for Dmod_ReadDirEx
*
* The test checks if the function can read directory entries with extended information.
*/
TEST_F(DmodDirTest, ReadDirEx)
{
// Use current directory which should have some entries
void* dir = Dmod_OpenDir(".");

ASSERT_NE(dir, nullptr);

// Read at least one entry
const Dmod_DirEntry_t* entry = Dmod_ReadDirEx(dir);
ASSERT_NE(entry, nullptr);

// Entry name should not be empty
ASSERT_NE(entry->name, nullptr);
ASSERT_GT(strlen(entry->name), 0);

// Entry type should be valid (even if unknown)
ASSERT_GE(entry->type, Dmod_DirEntryType_Unknown);
ASSERT_LE(entry->type, Dmod_DirEntryType_Other);

Dmod_CloseDir(dir);
}

/**
* @brief Test for Dmod_ReadDirEx reaching end
*
* The test checks if the function returns NULL when all entries are read.
*/
TEST_F(DmodDirTest, ReadDirExEnd)
{
// Create empty test directory
Dmod_MakeDir(testDirPath, 0755);

void* dir = Dmod_OpenDir(testDirPath);
ASSERT_NE(dir, nullptr);

// Read all entries (should be . and .. at minimum)
int count = 0;
const Dmod_DirEntry_t* entry;
while ((entry = Dmod_ReadDirEx(dir)) != NULL && count < 100)
{
count++;
}

// Should have read at least . and ..
ASSERT_GE(count, 2);

// Reading past the end should return NULL
ASSERT_EQ(Dmod_ReadDirEx(dir), nullptr);

Dmod_CloseDir(dir);
}

/**
* @brief Test for Dmod_ReadDirEx with directory type detection
*
* The test checks if the function correctly identifies directory types.
*/
TEST_F(DmodDirTest, ReadDirExTypeDetection)
{
// Use current directory which should have some entries
void* dir = Dmod_OpenDir(".");

ASSERT_NE(dir, nullptr);

bool foundDir = false;
bool foundAnyType = false;
const Dmod_DirEntry_t* entry;

// Read entries and look for directories (. and .. should be present)
while ((entry = Dmod_ReadDirEx(dir)) != NULL)
{
if (entry->type == Dmod_DirEntryType_Dir)
{
foundDir = true;
foundAnyType = true;
break;
}
if (entry->type != Dmod_DirEntryType_Unknown)
{
foundAnyType = true;
}
}

// Should find at least one directory (. or ..) OR have type detection available
// Note: On some filesystems, d_type might return DT_UNKNOWN for all entries
// In this case, we just verify that the function works without crashing
if (foundAnyType)
{
ASSERT_TRUE(foundDir);
}

Dmod_CloseDir(dir);
}

/**
* @brief Test backward compatibility between Dmod_ReadDir and Dmod_ReadDirEx
*
* The test ensures both functions work on the same directory and return the same entries.
*/
TEST_F(DmodDirTest, BackwardCompatibility)
{
// Create test directory with a known file
Dmod_MakeDir(testDirPath, 0755);

// Count entries using old API
void* dir1 = Dmod_OpenDir(testDirPath);
ASSERT_NE(dir1, nullptr);

int countOld = 0;
while (Dmod_ReadDir(dir1) != NULL && countOld < 100)
{
countOld++;
}
Dmod_CloseDir(dir1);

// Count entries using new API
void* dir2 = Dmod_OpenDir(testDirPath);
ASSERT_NE(dir2, nullptr);

int countNew = 0;
while (Dmod_ReadDirEx(dir2) != NULL && countNew < 100)
{
countNew++;
}
Dmod_CloseDir(dir2);

// Both should return the same count
ASSERT_EQ(countOld, countNew);
}
Loading