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 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
28#include "nilorea/n_files.h"
29
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <stdint.h>
52#include <dirent.h>
53#include <sys/stat.h>
54#include <errno.h>
55
61void n_free_file_info(void* ptr) {
62 __n_assert(ptr, return);
63 N_FILE_INFO* file = (N_FILE_INFO*)ptr;
64 if (file->name)
65 free(file->name);
66 free(file);
67 return;
68}
69
79char* n_path_join(const char* dir, const char* name) {
80 if (!dir || !name) return NULL;
81
82 size_t dl = strlen(dir);
83 size_t nl = strlen(name);
84
85 int need_sep = 1;
86 if (dl == 0) {
87 need_sep = 0;
88 } else {
89 char last = dir[dl - 1];
90 if (last == '/' || last == '\\') need_sep = 0;
91 }
92
93 /* We use '/' for output paths. */
94 size_t out_len = dl + (need_sep ? 1 : 0) + nl + 1;
95 char* out = (char*)malloc(out_len);
96 if (!out) return NULL;
97
98 if (need_sep)
99 snprintf(out, out_len, "%s/%s", dir, name);
100 else
101 snprintf(out, out_len, "%s%s", dir, name);
102
103 return out;
104}
105
122int n_comp_file_info(const void* a, const void* b) {
123 const N_FILE_INFO* f1 = (const N_FILE_INFO*)a;
124 const N_FILE_INFO* f2 = (const N_FILE_INFO*)b;
125
126 if (!f1) return -1;
127 if (!f2) return 1;
128
129 if (f1->filetime < f2->filetime) return -1;
130 if (f1->filetime > f2->filetime) return 1;
131
132 if (f1->filetime_nsec < f2->filetime_nsec) return -1;
133 if (f1->filetime_nsec > f2->filetime_nsec) return 1;
134
135 return 0;
136}
137
147void n_set_time_from_stat(N_FILE_INFO* file, const struct stat* st) {
148 if (!file || !st) return;
149
150#if defined(__linux__) || defined(__sun)
151 file->filetime = (int64_t)st->st_mtim.tv_sec;
152 file->filetime_nsec = (int32_t)st->st_mtim.tv_nsec;
153#else
154 file->filetime = (int64_t)st->st_mtime;
155 file->filetime_nsec = 0;
156#endif
157}
158
181int n_scan_dir(const char* dir, LIST* result, const int recurse) {
182 DIR* dp = NULL;
183 const struct dirent* entry = NULL;
184
185 __n_assert(dir, return FALSE);
186 __n_assert(result, return FALSE);
187
188 int error = 0;
189 dp = opendir(dir);
190 error = errno;
191 if (!dp) {
192 n_log(LOG_ERR, "cannot open directory: %s, %s", dir, strerror(error));
193 return FALSE;
194 }
195
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 continue;
217 }
218
219 if (S_ISDIR(st.st_mode)) {
220 if (recurse != FALSE) {
221 if (n_scan_dir(fullpath, result, recurse) == FALSE) {
222 n_log(LOG_ERR, "error while recursively scanning %s", fullpath);
223 }
224 }
225 } else if (S_ISREG(st.st_mode)) {
226 N_FILE_INFO* file = NULL;
227 Malloc(file, N_FILE_INFO, 1);
228
229 if (file) {
230 /* Store full path to avoid ambiguity in recursion results */
231 file->name = strdup(fullpath);
232 n_set_time_from_stat(file, &st);
233
235 }
236 } else {
237 /* Ignore non-regular / non-directory entries (symlinks, sockets, etc.) */
238 /* If you want to include symlinks, use lstat() and handle S_ISLNK. */
239 }
240
241 free(fullpath);
242 errno = 0;
243 }
244
245 if (errno != 0) {
246 n_log(LOG_ERR, "readdir failed for %s: %s", dir, strerror(errno));
247 closedir(dp);
248 return FALSE;
249 }
250
251 closedir(dp);
252 return TRUE;
253}
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
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:261
Structure of a generic LIST container.
Definition n_list.h:59
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_ERR
error conditions
Definition n_log.h:76
time_t filetime
file creation time
Definition n_files.h:50
time_t filetime_nsec
Definition n_files.h:50
char * name
file name
Definition n_files.h:48
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:181
void n_free_file_info(void *ptr)
Cross-platform directory scanning (Linux / Solaris / Windows via MinGW) WITHOUT chdir().
Definition n_files.c:61
common file information
Definition n_files.h:46
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:147
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:79
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:122
Files configuration header.