95 if (!dir || !name)
return NULL;
97 size_t dl = strlen(dir);
98 size_t nl = strlen(name);
104 char last = dir[dl - 1];
105 if (
last ==
'/' ||
last ==
'\\') need_sep = 0;
109 size_t out_len = dl + (need_sep ? 1 : 0) + nl + 1;
110 char* out = (
char*)malloc(out_len);
111 if (!out)
return NULL;
114 snprintf(out, out_len,
"%s/%s", dir, name);
116 snprintf(out, out_len,
"%s%s", dir, name);
163 if (!file || !st)
return;
165#if defined(__linux__) || defined(__sun)
166 file->
filetime = (int64_t)st->st_mtim.tv_sec;
169 file->
filetime = (int64_t)st->st_mtime;
198 const struct dirent* entry = NULL;
207 n_log(
LOG_ERR,
"cannot open directory: %s, %s", dir, strerror(error));
211 while ((entry = readdir(dp)) != NULL) {
212 const char* name = entry->d_name;
214 if (strcmp(name,
".") == 0 || strcmp(name,
"..") == 0) {
221 n_log(
LOG_ERR,
"malloc failed building path for %s/%s", dir, name);
227 if (stat(fullpath, &st) == -1) {
229 n_log(
LOG_ERR,
"unable to stat %s, %s", fullpath, strerror(error));
234 if (S_ISDIR(st.st_mode)) {
235 if (recurse != FALSE) {
236 if (
n_scan_dir(fullpath, result, recurse) == FALSE) {
237 n_log(
LOG_ERR,
"error while recursively scanning %s", fullpath);
240 }
else if (S_ISREG(st.st_mode)) {
246 file->
name = strdup(fullpath);
261 n_log(
LOG_ERR,
"readdir failed for %s: %s", dir, strerror(errno));
275 if (out_len) *out_len = 0;
277 FILE* f = fopen(path,
"rb");
279 n_log(
LOG_ERR,
"n_file_read_bounded: cannot open %s: %s", path, strerror(errno));
282 if (fseek(f, 0, SEEK_END) != 0) {
291 if ((
size_t)sz > max_size) {
292 n_log(
LOG_ERR,
"n_file_read_bounded: %s is %ld bytes, over the %zu cap", path, sz, max_size);
299 if (fseek(f, 0, SEEK_SET) != 0) {
300 n_log(
LOG_ERR,
"n_file_read_bounded: cannot seek back to the start of %s: %s", path, strerror(errno));
305 size_t len = (size_t)sz;
306 unsigned char* buf = NULL;
307 Malloc(buf,
unsigned char, len + 1);
312 if (len && fread(buf, 1, len, f) != len) {
313 n_log(
LOG_ERR,
"n_file_read_bounded: short read on %s", path);
322 if (out_len) *out_len = len;
327#define N_FILE_COPY_BLOCK 262144
336 unsigned char* x = NULL;
337 unsigned char* y = NULL;
351 if (got_a != got_b || (got_a && memcmp(x, y, got_a) != 0)) {
357 if (ferror(a) || ferror(b)) same = FALSE;
374 fb = fa ? fopen(b,
"rb") : NULL;
384 unsigned char* block = NULL;
386 size_t plen = 0, total = 0;
391 if (copied) *copied = 0;
393 in = fopen(to,
"rb");
396 n_log(
LOG_ERR,
"n_file_copy_verified: %s exists, not replaced", to);
400 Malloc(part,
char, plen + 6);
402 if (!part || !block) {
407 memcpy(part, to, plen);
408 memcpy(part + plen,
".part", 6);
410 in = fopen(from,
"rb");
411 out = in ? fopen(part,
"wb") : NULL;
413 n_log(
LOG_ERR,
"n_file_copy_verified: cannot open %s: %s", in ? part : from, strerror(errno));
419 if (got && fwrite(block, 1, got, out) != got) {
420 n_log(
LOG_ERR,
"n_file_copy_verified: writing %s: %s", part, strerror(errno));
427 n_log(
LOG_ERR,
"n_file_copy_verified: reading %s: %s", from, strerror(errno));
434 if (ok && fflush(out) != 0) ok = FALSE;
436 int fd = fileno(out);
438 if (fd >= 0) (void)_commit(fd);
440 if (fd >= 0) (void)fsync(fd);
443 if (fclose(out) != 0) ok = FALSE;
452 n_log(
LOG_ERR,
"n_file_copy_verified: the copy of %s does not match it", from);
455 if (ok && rename(part, to) != 0) {
456 n_log(
LOG_ERR,
"n_file_copy_verified: cannot name %s: %s", to, strerror(errno));
459 if (!ok) remove(part);
460 if (ok && copied) *copied = total;
468 if (!data && len > 0) {
469 n_log(
LOG_ERR,
"n_file_write_atomic: NULL data with len %zu", len);
473 size_t plen = strlen(path);
475 Malloc(tmp,
char, plen + 5);
477 memcpy(tmp, path, plen);
478 memcpy(tmp + plen,
".tmp", 5);
484 FILE* f = fopen(tmp,
"wb");
486 int tmpfd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, (
mode != 0) ? (mode_t)
mode : (mode_t)0666);
487 FILE* f = (tmpfd >= 0) ? fdopen(tmpfd,
"wb") : NULL;
488 if (!f && tmpfd >= 0) close(tmpfd);
491 n_log(
LOG_ERR,
"n_file_write_atomic: cannot create %s: %s", tmp, strerror(errno));
497 if (len && fwrite(data, 1, len, f) != len) ok = FALSE;
498 if (ok && fflush(f) != 0) ok = FALSE;
509 if (
mode != 0 && fchmod(fd, (mode_t)
mode) != 0) ok = FALSE;
513 if (fclose(f) != 0) ok = FALSE;
519 ok = MoveFileExA(tmp, path, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) ? TRUE : FALSE;
521 ok = (rename(tmp, path) == 0) ? TRUE : FALSE;
static N_GUI_MOUSE_EVENT last
the last event the widget was told of, and how many
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define Free(__ptr)
Free Handler to get errors.
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.
Structure of a generic LIST container.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
time_t filetime
file creation time
int n_file_same_content(const char *a, const char *b)
Do two files hold the same bytes.
int n_file_write_atomic(const char *path, const unsigned char *data, size_t len, int mode)
durably and atomically write len bytes to path: write a "<path>.tmp" sibling created with mode from t...
int n_file_copy_verified(const char *from, const char *to, size_t *copied)
Copy a file, check the copy against the original, and only then give it its name.
int n_file_read_bounded(const char *path, size_t max_size, unsigned char **out, size_t *out_len)
read a whole file into a freshly allocated, NUL-terminated buffer, refusing a file larger than max_si...
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).
void n_free_file_info(void *ptr)
Cross-platform directory scanning (Linux / Solaris / Windows via MinGW) WITHOUT chdir().
Common headers and low-level functions & define.
void n_set_time_from_stat(N_FILE_INFO *file, const struct stat *st)
Fill filetime and filetime_nsec from a struct stat (mtime).
char * n_path_join(const char *dir, const char *name)
Build "dir + '/' + name" (or without extra slash if dir already ends with '/' or '\').
#define N_FILE_COPY_BLOCK
Block size for the streaming copy and compare.
static int n_file_streams_equal(FILE *a, FILE *b)
Compare two open streams to their ends.
int n_comp_file_info(const void *a, const void *b)
Comparison function for sorting N_FILE_INFO by time (oldest first).
Files configuration header.