Blender V5.0
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
8
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{
112 DIR *dir = opendir(dirname);
113 if (UNLIKELY(dir == nullptr)) {
114 fprintf(stderr,
115 "Failed to open dir (%s): %s\n",
116 errno ? strerror(errno) : "unknown error",
117 dirname);
118 return;
119 }
120
121 ListBase dirbase = {nullptr, nullptr};
122 int newnum = 0;
123 const dirent *fname;
124 bool has_current = false, has_parent = false;
125
126 char dirname_with_slash[FILE_MAXDIR + 1];
127 size_t dirname_with_slash_len = BLI_strncpy_rlen(
128 dirname_with_slash, dirname, sizeof(dirname_with_slash) - 1);
129
130 if ((dirname_with_slash_len > 0) &&
131 (BLI_path_slash_is_native_compat(dirname[dirname_with_slash_len - 1]) == false))
132 {
133 dirname_with_slash[dirname_with_slash_len++] = SEP;
134 dirname_with_slash[dirname_with_slash_len] = '\0';
135 }
136
137 while ((fname = readdir(dir)) != nullptr) {
138 dirlink *const dlink = (dirlink *)malloc(sizeof(dirlink));
139 if (dlink != nullptr) {
140 dlink->name = BLI_strdup(fname->d_name);
141 if (FILENAME_IS_PARENT(dlink->name)) {
142 has_parent = true;
143 }
144 else if (FILENAME_IS_CURRENT(dlink->name)) {
145 has_current = true;
146 }
147 BLI_addhead(&dirbase, dlink);
148 newnum++;
149 }
150 }
151
152 if (!has_parent) {
153 char pardir[FILE_MAXDIR];
154
155 STRNCPY(pardir, dirname);
156 if (BLI_path_parent_dir(pardir) && (BLI_access(pardir, R_OK) == 0)) {
157 dirlink *const dlink = (dirlink *)malloc(sizeof(dirlink));
158 if (dlink != nullptr) {
160 BLI_addhead(&dirbase, dlink);
161 newnum++;
162 }
163 }
164 }
165 if (!has_current) {
166 dirlink *const dlink = (dirlink *)malloc(sizeof(dirlink));
167 if (dlink != nullptr) {
169 BLI_addhead(&dirbase, dlink);
170 newnum++;
171 }
172 }
173
174 if (newnum) {
175 if (dir_ctx->files) {
176 void *const tmp = MEM_reallocN(dir_ctx->files,
177 (dir_ctx->files_num + newnum) * sizeof(direntry));
178 if (tmp) {
179 dir_ctx->files = (direntry *)tmp;
180 }
181 else { /* Reallocation may fail. */
182 MEM_freeN(dir_ctx->files);
183 dir_ctx->files = nullptr;
184 }
185 }
186
187 if (dir_ctx->files == nullptr) {
188 dir_ctx->files = MEM_malloc_arrayN<direntry>(size_t(newnum), __func__);
189 }
190
191 if (UNLIKELY(dir_ctx->files == nullptr)) {
192 fprintf(stderr, "Couldn't get memory for dir: %s\n", dirname);
193 dir_ctx->files_num = 0;
194 }
195 else {
196 dirlink *dlink = (dirlink *)dirbase.first;
197 direntry *file = &dir_ctx->files[dir_ctx->files_num];
198
199 while (dlink) {
200 memset(file, 0, sizeof(direntry));
201 file->relname = dlink->name;
202 file->path = BLI_string_joinN(dirname_with_slash, dlink->name);
203 if (BLI_stat(file->path, &file->s) != -1) {
204 file->type = file->s.st_mode;
205 }
206 else if (FILENAME_IS_CURRPAR(file->relname)) {
207 /* Unfortunately a hack around UNC paths on WIN32,
208 * which does not support `stat` on `\\SERVER\foo\..`. */
209 file->type |= S_IFDIR;
210 }
211 dir_ctx->files_num++;
212 file++;
213 dlink = dlink->next;
214 }
215
216 qsort(dir_ctx->files,
217 dir_ctx->files_num,
218 sizeof(direntry),
219 (int (*)(const void *, const void *))direntry_cmp);
220 }
221
222 BLI_freelist(&dirbase);
223 }
224
225 closedir(dir);
226}
227
229{
230 BuildDirCtx dir_ctx;
231
232 dir_ctx.files_num = 0;
233 dir_ctx.files = nullptr;
234
235 bli_builddir(&dir_ctx, dirname);
236
237 if (dir_ctx.files) {
238 *r_filelist = dir_ctx.files;
239 }
240 else {
241 /* Keep Blender happy. Blender stores this in a variable
242 * where 0 has special meaning..... */
243 *r_filelist = MEM_mallocN<direntry>(__func__);
244 }
245
246 return dir_ctx.files_num;
247}
248
249void BLI_filelist_entry_size_to_string(const struct stat *st,
250 const uint64_t st_size_fallback,
251 const bool compact,
252 char r_size[FILELIST_DIRENTRY_SIZE_LEN])
253{
254 /*
255 * Seems st_size is signed 32-bit value in *nix and Windows. This
256 * will buy us some time until files get bigger than 4GB or until
257 * everyone starts using __USE_FILE_OFFSET64 or equivalent.
258 */
259 double size = double(st ? st->st_size : st_size_fallback);
260#ifdef WIN32
261 if (compact) {
263 }
264 else {
265 BLI_str_format_byte_unit(r_size, size, false);
266 }
267#else
268 if (compact) {
270 }
271 else {
272 BLI_str_format_byte_unit(r_size, size, true);
273 }
274#endif
275}
276
277void BLI_filelist_entry_mode_to_string(const struct stat *st,
278 const bool /*compact*/,
279 char r_mode1[FILELIST_DIRENTRY_MODE_LEN],
280 char r_mode2[FILELIST_DIRENTRY_MODE_LEN],
281 char r_mode3[FILELIST_DIRENTRY_MODE_LEN])
282{
283 const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
284
285#ifdef WIN32
286 UNUSED_VARS(st);
287 BLI_strncpy(r_mode1, types[0], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
288 BLI_strncpy(r_mode2, types[0], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
289 BLI_strncpy(r_mode3, types[0], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
290#else
291 const int mode = st->st_mode;
292
293 BLI_strncpy(r_mode1, types[(mode & 0700) >> 6], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
294 BLI_strncpy(r_mode2, types[(mode & 0070) >> 3], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
295 BLI_strncpy(r_mode3, types[(mode & 0007)], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
296
297 if (((mode & S_ISGID) == S_ISGID) && (r_mode2[2] == '-')) {
298 r_mode2[2] = 'l';
299 }
300
301 if (mode & (S_ISUID | S_ISGID)) {
302 if (r_mode1[2] == 'x') {
303 r_mode1[2] = 's';
304 }
305 else {
306 r_mode1[2] = 'S';
307 }
308
309 if (r_mode2[2] == 'x') {
310 r_mode2[2] = 's';
311 }
312 }
313
314 if (mode & S_ISVTX) {
315 if (r_mode3[2] == 'x') {
316 r_mode3[2] = 't';
317 }
318 else {
319 r_mode3[2] = 'T';
320 }
321 }
322#endif
323}
324
325void BLI_filelist_entry_owner_to_string(const struct stat *st,
326 const bool /*compact*/,
327 char r_owner[FILELIST_DIRENTRY_OWNER_LEN])
328{
329#ifdef WIN32
330 UNUSED_VARS(st);
331 BLI_strncpy(r_owner, "unknown", FILELIST_DIRENTRY_OWNER_LEN);
332#else
333 const passwd *pwuser = getpwuid(st->st_uid);
334
335 if (pwuser) {
336 BLI_strncpy(r_owner, pwuser->pw_name, sizeof(*r_owner) * FILELIST_DIRENTRY_OWNER_LEN);
337 }
338 else {
339 BLI_snprintf(r_owner, sizeof(*r_owner) * FILELIST_DIRENTRY_OWNER_LEN, "%u", st->st_uid);
340 }
341#endif
342}
343
344void BLI_filelist_entry_datetime_to_string(const struct stat *st,
345 const int64_t ts,
346 const bool compact,
347 char r_time[FILELIST_DIRENTRY_TIME_LEN],
348 char r_date[FILELIST_DIRENTRY_DATE_LEN],
349 bool *r_is_today,
350 bool *r_is_yesterday)
351{
352 int today_year = 0;
353 int today_yday = 0;
354 int yesterday_year = 0;
355 int yesterday_yday = 0;
356
357 if (r_is_today || r_is_yesterday) {
358 /* `localtime()` has only one buffer so need to get data out before called again. */
359 const time_t ts_now = time(nullptr);
360 tm *today = localtime(&ts_now);
361
362 today_year = today->tm_year;
363 today_yday = today->tm_yday;
364 /* Handle a yesterday that spans a year */
365 today->tm_mday--;
366 mktime(today);
367 yesterday_year = today->tm_year;
368 yesterday_yday = today->tm_yday;
369
370 if (r_is_today) {
371 *r_is_today = false;
372 }
373 if (r_is_yesterday) {
374 *r_is_yesterday = false;
375 }
376 }
377
378 const time_t ts_mtime = ts;
379 const tm *tm = localtime(st ? &st->st_mtime : &ts_mtime);
380 const time_t zero = 0;
381
382 /* Prevent impossible dates in windows. */
383 if (tm == nullptr) {
384 tm = localtime(&zero);
385 }
386
387 if (r_time) {
388 strftime(r_time, sizeof(*r_time) * FILELIST_DIRENTRY_TIME_LEN, "%H:%M", tm);
389 }
390
391 if (r_date) {
392 strftime(r_date,
393 sizeof(*r_date) * FILELIST_DIRENTRY_DATE_LEN,
394 compact ? "%d/%m/%y" : "%d %b %Y",
395 tm);
396 }
397
398 if (r_is_today && (tm->tm_year == today_year) && (tm->tm_yday == today_yday)) {
399 *r_is_today = true;
400 }
401 else if (r_is_yesterday && (tm->tm_year == yesterday_year) && (tm->tm_yday == yesterday_yday)) {
402 *r_is_yesterday = true;
403 }
404}
405
407{
408 *dst = *src;
409 if (dst->relname) {
410 dst->relname = static_cast<char *>(MEM_dupallocN(src->relname));
411 }
412 if (dst->path) {
413 dst->path = static_cast<char *>(MEM_dupallocN(src->path));
414 }
415}
416
417void BLI_filelist_duplicate(direntry **dest_filelist,
418 direntry *const src_filelist,
419 const uint nrentries)
420{
421 uint i;
422
423 *dest_filelist = MEM_malloc_arrayN<direntry>(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(entry->relname);
435 }
436 if (entry->path) {
437 MEM_freeN(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}
#define BLI_assert(a)
Definition BLI_assert.h:46
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_freelist(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:483
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1)
#define FILENAME_IS_CURRENT(_n)
#define FILENAME_IS_CURRPAR(_n)
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#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.cc:41
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.cc:1207
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.cc:1237
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
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)
These structs are the foundation for all linked lists in the library system.
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
long long int int64_t
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static char ** types
Definition makesdna.cc:71
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
direntry * files
void * first
char * d_name
struct stat s
const char * relname
const char * path
i
Definition text_draw.cc:230