Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_files.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
27#include "nilorea/n_files.h"
28
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <stdint.h>
51#include <dirent.h>
52#include <sys/stat.h>
53#include <errno.h>
54
60void n_free_file_info(void* ptr) {
61 __n_assert(ptr, return);
62 N_FILE_INFO* file = (N_FILE_INFO*)ptr;
63 if (file->name)
64 free(file->name);
65 free(file);
66 return;
67}
68
78char* n_path_join(const char* dir, const char* name) {
79 if (!dir || !name) return NULL;
80
81 size_t dl = strlen(dir);
82 size_t nl = strlen(name);
83
84 int need_sep = 1;
85 if (dl == 0) {
86 need_sep = 0;
87 } else {
88 char last = dir[dl - 1];
89 if (last == '/' || last == '\\') need_sep = 0;
90 }
91
92 /* We use '/' for output paths. */
93 size_t out_len = dl + (need_sep ? 1 : 0) + nl + 1;
94 char* out = (char*)malloc(out_len);
95 if (!out) return NULL;
96
97 if (need_sep)
98 snprintf(out, out_len, "%s/%s", dir, name);
99 else
100 snprintf(out, out_len, "%s%s", dir, name);
101
102 return out;
103}
104
121int n_comp_file_info(const void* a, const void* b) {
122 const N_FILE_INFO* f1 = (const N_FILE_INFO*)a;
123 const N_FILE_INFO* f2 = (const N_FILE_INFO*)b;
124
125 if (!f1) return -1;
126 if (!f2) return 1;
127
128 if (f1->filetime < f2->filetime) return -1;
129 if (f1->filetime > f2->filetime) return 1;
130
131 if (f1->filetime_nsec < f2->filetime_nsec) return -1;
132 if (f1->filetime_nsec > f2->filetime_nsec) return 1;
133
134 return 0;
135}
136
146void n_set_time_from_stat(N_FILE_INFO* file, const struct stat* st) {
147 if (!file || !st) return;
148
149#if defined(__linux__) || defined(__sun)
150 file->filetime = (int64_t)st->st_mtim.tv_sec;
151 file->filetime_nsec = (int32_t)st->st_mtim.tv_nsec;
152#else
153 file->filetime = (int64_t)st->st_mtime;
154 file->filetime_nsec = 0;
155#endif
156}
157
180int n_scan_dir(const char* dir, LIST* result, const int recurse) {
181 DIR* dp = NULL;
182 const struct dirent* entry = NULL;
183
184 __n_assert(dir, return FALSE);
185 __n_assert(result, return FALSE);
186
187 int error = 0;
188 dp = opendir(dir);
189 error = errno;
190 if (!dp) {
191 n_log(LOG_ERR, "cannot open directory: %s, %s", dir, strerror(error));
192 return FALSE;
193 }
194
195 error = 0;
196 while ((entry = readdir(dp)) != NULL) {
197 const char* name = entry->d_name;
198
199 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
200 errno = 0;
201 continue;
202 }
203
204 char* fullpath = n_path_join(dir, name);
205 if (!fullpath) {
206 n_log(LOG_ERR, "malloc failed building path for %s/%s", dir, name);
207 closedir(dp);
208 return FALSE;
209 }
210
211 struct stat st;
212 if (stat(fullpath, &st) == -1) {
213 error = errno;
214 n_log(LOG_ERR, "unable to stat %s, %s", fullpath, strerror(error));
215 free(fullpath);
216 error = 0;
217 continue;
218 }
219
220 if (S_ISDIR(st.st_mode)) {
221 if (recurse != FALSE) {
222 if (n_scan_dir(fullpath, result, recurse) == FALSE) {
223 n_log(LOG_ERR, "error while recursively scanning %s", fullpath);
224 }
225 }
226 } else if (S_ISREG(st.st_mode)) {
227 N_FILE_INFO* file = NULL;
228 Malloc(file, N_FILE_INFO, 1);
229
230 if (file) {
231 /* Store full path to avoid ambiguity in recursion results */
232 file->name = strdup(fullpath);
233 n_set_time_from_stat(file, &st);
234
236 }
237 } else {
238 /* Ignore non-regular / non-directory entries (symlinks, sockets, etc.) */
239 /* If you want to include symlinks, use lstat() and handle S_ISLNK. */
240 }
241
242 free(fullpath);
243 errno = error = 0;
244 }
245
246 if (errno != 0) {
247 n_log(LOG_ERR, "readdir failed for %s: %s", dir, strerror(errno));
248 closedir(dp);
249 return FALSE;
250 }
251
252 closedir(dp);
253 return TRUE;
254}
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:203
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:278
int list_push_sorted(LIST *list, void *ptr, int(*comparator)(const void *a, const void *b), void(*destructor)(void *ptr))
Add a pointer sorted in the list , starting by the end of the list.
Definition n_list.c:260
Structure of a generic LIST container.
Definition n_list.h:58
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:88
#define LOG_ERR
error conditions
Definition n_log.h:75
time_t filetime
file creation time
Definition n_files.h:49
time_t filetime_nsec
Definition n_files.h:49
char * name
file name
Definition n_files.h:47
int n_scan_dir(const char *dir, LIST *result, const int recurse)
Scan a directory and append only regular files into a sorted list (oldest first).
Definition n_files.c:180
void n_free_file_info(void *ptr)
Cross-platform directory scanning (Linux / Solaris / Windows via MinGW) WITHOUT chdir().
Definition n_files.c:60
common file information
Definition n_files.h:45
void n_set_time_from_stat(N_FILE_INFO *file, const struct stat *st)
Fill filetime and filetime_nsec from a struct stat (mtime).
Definition n_files.c:146
char * n_path_join(const char *dir, const char *name)
Build "dir + '/' + name" (or without extra slash if dir already ends with '/' or '\').
Definition n_files.c:78
int n_comp_file_info(const void *a, const void *b)
Comparison function for sorting N_FILE_INFO by time (oldest first).
Definition n_files.c:121
Files configuration header.