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