Blender V4.3
BLI_filelist.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cerrno>
10#include <cstdio>
11#include <cstdlib>
12#include <sys/types.h>
13
14#ifndef WIN32
15# include <dirent.h>
16#endif
17
18#include <cstring>
19#include <ctime>
20#include <sys/stat.h>
21
22#ifdef WIN32
23# include "BLI_winstuff.h"
24# include "utfconv.hh"
25# include <direct.h>
26# include <io.h>
27#else
28# include <pwd.h>
29# include <sys/ioctl.h>
30# include <unistd.h>
31#endif
32
33/* lib includes */
34#include "MEM_guardedalloc.h"
35
36#include "DNA_listBase.h"
37
38#include "BLI_fileops.h"
39#include "BLI_fileops_types.h"
40#include "BLI_listbase.h"
41#include "BLI_path_utils.hh"
42#include "BLI_string.h"
43#include "BLI_string_utils.hh"
44
45/*
46 * Ordering function for sorting lists of files/directories. Returns -1 if
47 * entry1 belongs before entry2, 0 if they are equal, 1 if they should be swapped.
48 */
49static int direntry_cmp(direntry *entry1, direntry *entry2)
50{
51 /* type is equal to stat.st_mode */
52
53 /* directories come before non-directories */
54 if (S_ISDIR(entry1->type)) {
55 if (S_ISDIR(entry2->type) == 0) {
56 return -1;
57 }
58 }
59 else {
60 if (S_ISDIR(entry2->type)) {
61 return 1;
62 }
63 }
64 /* non-regular files come after regular files */
65 if (S_ISREG(entry1->type)) {
66 if (S_ISREG(entry2->type) == 0) {
67 return -1;
68 }
69 }
70 else {
71 if (S_ISREG(entry2->type)) {
72 return 1;
73 }
74 }
75 /* arbitrary, but consistent, ordering of different types of non-regular files */
76 if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) {
77 return -1;
78 }
79 if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) {
80 return 1;
81 }
82
83 /* OK, now we know their S_IFMT fields are the same, go on to a name comparison */
84 /* make sure "." and ".." are always first */
85 if (FILENAME_IS_CURRENT(entry1->relname)) {
86 return -1;
87 }
88 if (FILENAME_IS_CURRENT(entry2->relname)) {
89 return 1;
90 }
91 if (FILENAME_IS_PARENT(entry1->relname)) {
92 return -1;
93 }
94 if (FILENAME_IS_PARENT(entry2->relname)) {
95 return 1;
96 }
97
98 return BLI_strcasecmp_natural(entry1->relname, entry2->relname);
99}
100
102 direntry *files; /* array[files_num] */
104};
105
109static void bli_builddir(BuildDirCtx *dir_ctx, const char *dirname)
110{
111 DIR *dir = opendir(dirname);
112 if (UNLIKELY(dir == nullptr)) {
113 fprintf(stderr,
114 "Failed to open dir (%s): %s\n",
115 errno ? strerror(errno) : "unknown error",
116 dirname);
117 return;
118 }
119
120 ListBase dirbase = {nullptr, nullptr};
121 int newnum = 0;
122 const dirent *fname;
123 bool has_current = false, has_parent = false;
124
125 char dirname_with_slash[FILE_MAXDIR + 1];
126 size_t dirname_with_slash_len = BLI_strncpy_rlen(
127 dirname_with_slash, dirname, sizeof(dirname_with_slash) - 1);
128
129 if ((dirname_with_slash_len > 0) &&
130 (BLI_path_slash_is_native_compat(dirname[dirname_with_slash_len - 1]) == false))
131 {
132 dirname_with_slash[dirname_with_slash_len++] = SEP;
133 dirname_with_slash[dirname_with_slash_len] = '\0';
134 }
135
136 while ((fname = readdir(dir)) != nullptr) {
137 dirlink *const dlink = (dirlink *)malloc(sizeof(dirlink));
138 if (dlink != nullptr) {
139 dlink->name = BLI_strdup(fname->d_name);
140 if (FILENAME_IS_PARENT(dlink->name)) {
141 has_parent = true;
142 }
143 else if (FILENAME_IS_CURRENT(dlink->name)) {
144 has_current = true;
145 }
146 BLI_addhead(&dirbase, dlink);
147 newnum++;
148 }
149 }
150
151 if (!has_parent) {
152 char pardir[FILE_MAXDIR];
153
154 STRNCPY(pardir, dirname);
155 if (BLI_path_parent_dir(pardir) && (BLI_access(pardir, R_OK) == 0)) {
156 dirlink *const dlink = (dirlink *)malloc(sizeof(dirlink));
157 if (dlink != nullptr) {
159 BLI_addhead(&dirbase, dlink);
160 newnum++;
161 }
162 }
163 }
164 if (!has_current) {
165 dirlink *const dlink = (dirlink *)malloc(sizeof(dirlink));
166 if (dlink != nullptr) {
168 BLI_addhead(&dirbase, dlink);
169 newnum++;
170 }
171 }
172
173 if (newnum) {
174 if (dir_ctx->files) {
175 void *const tmp = MEM_reallocN(dir_ctx->files,
176 (dir_ctx->files_num + newnum) * sizeof(direntry));
177 if (tmp) {
178 dir_ctx->files = (direntry *)tmp;
179 }
180 else { /* Reallocation may fail. */
181 MEM_freeN(dir_ctx->files);
182 dir_ctx->files = nullptr;
183 }
184 }
185
186 if (dir_ctx->files == nullptr) {
187 dir_ctx->files = (direntry *)MEM_mallocN(newnum * sizeof(direntry), __func__);
188 }
189
190 if (UNLIKELY(dir_ctx->files == nullptr)) {
191 fprintf(stderr, "Couldn't get memory for dir: %s\n", dirname);
192 dir_ctx->files_num = 0;
193 }
194 else {
195 dirlink *dlink = (dirlink *)dirbase.first;
196 direntry *file = &dir_ctx->files[dir_ctx->files_num];
197
198 while (dlink) {
199 memset(file, 0, sizeof(direntry));
200 file->relname = dlink->name;
201 file->path = BLI_string_joinN(dirname_with_slash, dlink->name);
202 if (BLI_stat(file->path, &file->s) != -1) {
203 file->type = file->s.st_mode;
204 }
205 else if (FILENAME_IS_CURRPAR(file->relname)) {
206 /* Unfortunately a hack around UNC paths on WIN32,
207 * which does not support `stat` on `\\SERVER\foo\..`. */
208 file->type |= S_IFDIR;
209 }
210 dir_ctx->files_num++;
211 file++;
212 dlink = dlink->next;
213 }
214
215 qsort(dir_ctx->files,
216 dir_ctx->files_num,
217 sizeof(direntry),
218 (int (*)(const void *, const void *))direntry_cmp);
219 }
220
221 BLI_freelist(&dirbase);
222 }
223
224 closedir(dir);
225}
226
228{
229 BuildDirCtx dir_ctx;
230
231 dir_ctx.files_num = 0;
232 dir_ctx.files = nullptr;
233
234 bli_builddir(&dir_ctx, dirname);
235
236 if (dir_ctx.files) {
237 *r_filelist = dir_ctx.files;
238 }
239 else {
240 /* Keep Blender happy. Blender stores this in a variable
241 * where 0 has special meaning..... */
242 *r_filelist = static_cast<direntry *>(MEM_mallocN(sizeof(**r_filelist), __func__));
243 }
244
245 return dir_ctx.files_num;
246}
247
248void BLI_filelist_entry_size_to_string(const struct stat *st,
249 const uint64_t st_size_fallback,
250 const bool compact,
251 char r_size[FILELIST_DIRENTRY_SIZE_LEN])
252{
253 /*
254 * Seems st_size is signed 32-bit value in *nix and Windows. This
255 * will buy us some time until files get bigger than 4GB or until
256 * everyone starts using __USE_FILE_OFFSET64 or equivalent.
257 */
258 double size = double(st ? st->st_size : st_size_fallback);
259#ifdef WIN32
260 if (compact) {
261 BLI_str_format_byte_unit_compact(r_size, size, false);
262 }
263 else {
264 BLI_str_format_byte_unit(r_size, size, false);
265 }
266#else
267 if (compact) {
268 BLI_str_format_byte_unit_compact(r_size, size, true);
269 }
270 else {
271 BLI_str_format_byte_unit(r_size, size, true);
272 }
273#endif
274}
275
276void BLI_filelist_entry_mode_to_string(const struct stat *st,
277 const bool /*compact*/,
278 char r_mode1[FILELIST_DIRENTRY_MODE_LEN],
279 char r_mode2[FILELIST_DIRENTRY_MODE_LEN],
280 char r_mode3[FILELIST_DIRENTRY_MODE_LEN])
281{
282 const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
283
284#ifdef WIN32
285 UNUSED_VARS(st);
286 BLI_strncpy(r_mode1, types[0], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
287 BLI_strncpy(r_mode2, types[0], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
288 BLI_strncpy(r_mode3, types[0], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
289#else
290 const int mode = st->st_mode;
291
292 BLI_strncpy(r_mode1, types[(mode & 0700) >> 6], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
293 BLI_strncpy(r_mode2, types[(mode & 0070) >> 3], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
294 BLI_strncpy(r_mode3, types[(mode & 0007)], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
295
296 if (((mode & S_ISGID) == S_ISGID) && (r_mode2[2] == '-')) {
297 r_mode2[2] = 'l';
298 }
299
300 if (mode & (S_ISUID | S_ISGID)) {
301 if (r_mode1[2] == 'x') {
302 r_mode1[2] = 's';
303 }
304 else {
305 r_mode1[2] = 'S';
306 }
307
308 if (r_mode2[2] == 'x') {
309 r_mode2[2] = 's';
310 }
311 }
312
313 if (mode & S_ISVTX) {
314 if (r_mode3[2] == 'x') {
315 r_mode3[2] = 't';
316 }
317 else {
318 r_mode3[2] = 'T';
319 }
320 }
321#endif
322}
323
324void BLI_filelist_entry_owner_to_string(const struct stat *st,
325 const bool /*compact*/,
326 char r_owner[FILELIST_DIRENTRY_OWNER_LEN])
327{
328#ifdef WIN32
329 UNUSED_VARS(st);
330 BLI_strncpy(r_owner, "unknown", FILELIST_DIRENTRY_OWNER_LEN);
331#else
332 const passwd *pwuser = getpwuid(st->st_uid);
333
334 if (pwuser) {
335 BLI_strncpy(r_owner, pwuser->pw_name, sizeof(*r_owner) * FILELIST_DIRENTRY_OWNER_LEN);
336 }
337 else {
338 BLI_snprintf(r_owner, sizeof(*r_owner) * FILELIST_DIRENTRY_OWNER_LEN, "%u", st->st_uid);
339 }
340#endif
341}
342
343void BLI_filelist_entry_datetime_to_string(const struct stat *st,
344 const int64_t ts,
345 const bool compact,
346 char r_time[FILELIST_DIRENTRY_TIME_LEN],
347 char r_date[FILELIST_DIRENTRY_DATE_LEN],
348 bool *r_is_today,
349 bool *r_is_yesterday)
350{
351 int today_year = 0;
352 int today_yday = 0;
353 int yesterday_year = 0;
354 int yesterday_yday = 0;
355
356 if (r_is_today || r_is_yesterday) {
357 /* `localtime()` has only one buffer so need to get data out before called again. */
358 const time_t ts_now = time(nullptr);
359 tm *today = localtime(&ts_now);
360
361 today_year = today->tm_year;
362 today_yday = today->tm_yday;
363 /* Handle a yesterday that spans a year */
364 today->tm_mday--;
365 mktime(today);
366 yesterday_year = today->tm_year;
367 yesterday_yday = today->tm_yday;
368
369 if (r_is_today) {
370 *r_is_today = false;
371 }
372 if (r_is_yesterday) {
373 *r_is_yesterday = false;
374 }
375 }
376
377 const time_t ts_mtime = ts;
378 const tm *tm = localtime(st ? &st->st_mtime : &ts_mtime);
379 const time_t zero = 0;
380
381 /* Prevent impossible dates in windows. */
382 if (tm == nullptr) {
383 tm = localtime(&zero);
384 }
385
386 if (r_time) {
387 strftime(r_time, sizeof(*r_time) * FILELIST_DIRENTRY_TIME_LEN, "%H:%M", tm);
388 }
389
390 if (r_date) {
391 strftime(r_date,
392 sizeof(*r_date) * FILELIST_DIRENTRY_DATE_LEN,
393 compact ? "%d/%m/%y" : "%d %b %Y",
394 tm);
395 }
396
397 if (r_is_today && (tm->tm_year == today_year) && (tm->tm_yday == today_yday)) {
398 *r_is_today = true;
399 }
400 else if (r_is_yesterday && (tm->tm_year == yesterday_year) && (tm->tm_yday == yesterday_yday)) {
401 *r_is_yesterday = true;
402 }
403}
404
406{
407 *dst = *src;
408 if (dst->relname) {
409 dst->relname = static_cast<char *>(MEM_dupallocN(src->relname));
410 }
411 if (dst->path) {
412 dst->path = static_cast<char *>(MEM_dupallocN(src->path));
413 }
414}
415
416void BLI_filelist_duplicate(direntry **dest_filelist,
417 direntry *const src_filelist,
418 const uint nrentries)
419{
420 uint i;
421
422 *dest_filelist = static_cast<direntry *>(
423 MEM_mallocN(sizeof(**dest_filelist) * size_t(nrentries), __func__));
424 for (i = 0; i < nrentries; i++) {
425 const direntry *src = &src_filelist[i];
426 direntry *dst = &(*dest_filelist)[i];
428 }
429}
430
432{
433 if (entry->relname) {
434 MEM_freeN((void *)entry->relname);
435 }
436 if (entry->path) {
437 MEM_freeN((void *)entry->path);
438 }
439}
440
441void BLI_filelist_free(direntry *filelist, const uint nrentries)
442{
443 uint i;
444 for (i = 0; i < nrentries; i++) {
445 BLI_filelist_entry_free(&filelist[i]);
446 }
447
448 if (filelist != nullptr) {
449 MEM_freeN(filelist);
450 }
451}
static int direntry_cmp(direntry *entry1, direntry *entry2)
void BLI_filelist_entry_owner_to_string(const struct stat *st, const bool, char r_owner[FILELIST_DIRENTRY_OWNER_LEN])
void BLI_filelist_duplicate(direntry **dest_filelist, direntry *const src_filelist, const uint nrentries)
void BLI_filelist_entry_datetime_to_string(const struct stat *st, const int64_t ts, const bool compact, char r_time[FILELIST_DIRENTRY_TIME_LEN], char r_date[FILELIST_DIRENTRY_DATE_LEN], bool *r_is_today, bool *r_is_yesterday)
static void bli_builddir(BuildDirCtx *dir_ctx, const char *dirname)
void BLI_filelist_entry_free(direntry *entry)
void BLI_filelist_entry_duplicate(direntry *dst, const direntry *src)
void BLI_filelist_entry_size_to_string(const struct stat *st, const uint64_t st_size_fallback, const bool compact, char r_size[FILELIST_DIRENTRY_SIZE_LEN])
uint BLI_filelist_dir_contents(const char *dirname, direntry **r_filelist)
void BLI_filelist_free(direntry *filelist, const uint nrentries)
void BLI_filelist_entry_mode_to_string(const struct stat *st, const bool, char r_mode1[FILELIST_DIRENTRY_MODE_LEN], char r_mode2[FILELIST_DIRENTRY_MODE_LEN], char r_mode3[FILELIST_DIRENTRY_MODE_LEN])
File and directory operations.
int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Some types for dealing with directories.
#define FILELIST_DIRENTRY_SIZE_LEN
#define FILELIST_DIRENTRY_MODE_LEN
#define FILELIST_DIRENTRY_OWNER_LEN
#define FILELIST_DIRENTRY_DATE_LEN
#define FILELIST_DIRENTRY_TIME_LEN
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
void BLI_freelist(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:482
bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1)
#define FILENAME_IS_CURRENT(_n)
#define FILENAME_IS_CURRPAR(_n)
#define SEP
BLI_INLINE bool BLI_path_slash_is_native_compat(const char ch)
#define FILENAME_CURRENT
#define FILENAME_PARENT
#define FILE_MAXDIR
#define FILENAME_IS_PARENT(_n)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
void BLI_str_format_byte_unit(char dst[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE], long long int bytes, bool base_10) ATTR_NONNULL(1)
Definition string.c:1192
void BLI_str_format_byte_unit_compact(char dst[BLI_STR_FORMAT_INT64_BYTE_UNIT_COMPACT_SIZE], long long int bytes, bool base_10) ATTR_NONNULL(1)
Definition string.c:1222
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
int char char int int int BLI_strcasecmp_natural(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char char size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define BLI_string_joinN(...)
unsigned int uint
#define UNUSED_VARS(...)
#define UNLIKELY(x)
Compatibility-like things for windows.
struct __dirstream DIR
struct dirent * readdir(DIR *dp)
int closedir(DIR *dp)
#define S_ISDIR(x)
const char * dirname(char *path)
DIR * opendir(const char *path)
#define S_ISREG(x)
typedef double(DMatrix)[4][4]
These structs are the foundation for all linked lists in the library system.
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
double time
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
direntry * files
void * first
char * d_name
const char * relname
const char * path