Blender V5.0
storage.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <cstdio>
12#include <cstdlib>
13#include <sys/types.h>
14
15#include <sys/stat.h>
16
17#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__HAIKU__)
18/* Other modern unix OS's should probably use this also. */
19# include <sys/statvfs.h>
20# define USE_STATFS_STATVFS
21#endif
22
23#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
24 defined(__DragonFly__)
25/* For statfs */
26# include <sys/mount.h>
27# include <sys/param.h>
28#endif
29
30#if defined(__linux__) || defined(__hpux) || defined(__GNU__) || defined(__GLIBC__)
31# include <sys/vfs.h>
32#endif
33
34#include <cstring>
35#include <fcntl.h>
36
37#ifdef WIN32
38# include "BLI_string_utf8.h"
39# include "BLI_winstuff.h"
40# include "utfconv.hh"
41# include <ShObjIdl.h>
42# include <direct.h>
43# include <io.h>
44# include <stdbool.h>
45#else
46# include <pwd.h>
47# include <sys/ioctl.h>
48# include <unistd.h>
49#endif
50
51/* lib includes */
52#include "MEM_guardedalloc.h"
53
54#include "BLI_fileops.h"
55#include "BLI_linklist.h"
56#include "BLI_path_utils.hh"
57#include "BLI_string.h"
58#include "BLI_threads.h"
59#include "BLI_utildefines.h"
60
61/* NOTE: The implementation for Apple lives in storage_apple.mm. */
62#if !defined(__APPLE__)
63bool BLI_change_working_dir(const char *dir)
64{
66
67 if (!BLI_is_dir(dir)) {
68 return false;
69 }
70# if defined(WIN32)
71 wchar_t wdir[FILE_MAX];
72 if (conv_utf_8_to_16(dir, wdir, ARRAY_SIZE(wdir)) != 0) {
73 return false;
74 }
75 return _wchdir(wdir) == 0;
76# else
77 return chdir(dir) == 0;
78# endif
79}
80
81char *BLI_current_working_dir(char *dir, const size_t maxncpy)
82{
83# if defined(WIN32)
84 wchar_t path[MAX_PATH];
85 if (_wgetcwd(path, MAX_PATH)) {
86 if (BLI_strncpy_wchar_as_utf8(dir, path, maxncpy) != maxncpy) {
87 return dir;
88 }
89 }
90 return nullptr;
91# else
92 return getcwd(dir, maxncpy);
93# endif
94}
95#endif /* !defined (__APPLE__) */
96
97const char *BLI_dir_home()
98{
99 const char *home_dir;
100
101#ifdef WIN32
102 home_dir = BLI_getenv("userprofile");
103#else
104 /* Return the users home directory with a fallback when the environment variable isn't set.
105 * Failure to access `$HOME` is rare but possible, see: #2931.
106 *
107 * Any errors accessing home is likely caused by a broken/unsupported configuration,
108 * nevertheless, failing to null check would crash which makes the error difficult
109 * for users troubleshoot. */
110 home_dir = BLI_getenv("HOME");
111 if (home_dir == nullptr) {
112 if (const passwd *pwuser = getpwuid(getuid())) {
113 home_dir = pwuser->pw_dir;
114 }
115 }
116#endif
117
118 return home_dir;
119}
120
121double BLI_dir_free_space(const char *dir)
122{
123#ifdef WIN32
124 DWORD sectorspc, bytesps, freec, clusters;
125 char tmp[4];
126
127 tmp[0] = '\\';
128 tmp[1] = 0; /* Just a fail-safe. */
129 if (ELEM(dir[0], '/', '\\')) {
130 tmp[0] = '\\';
131 tmp[1] = 0;
132 }
133 else if (dir[1] == ':') {
134 tmp[0] = dir[0];
135 tmp[1] = ':';
136 tmp[2] = '\\';
137 tmp[3] = 0;
138 }
139
140 GetDiskFreeSpace(tmp, &sectorspc, &bytesps, &freec, &clusters);
141
142 return double(freec * bytesps * sectorspc);
143#else
144
145# ifdef USE_STATFS_STATVFS
146 struct statvfs disk;
147# else
148 struct statfs disk;
149# endif
150
151 char dirname[FILE_MAXDIR], *slash;
152 int len = strlen(dir);
153
154 if (len >= FILE_MAXDIR) {
155 /* path too long */
156 return -1;
157 }
158
159 memcpy(dirname, dir, len + 1);
160
161 if (len) {
162 slash = strrchr(dirname, '/');
163 if (slash) {
164 slash[1] = '\0';
165 }
166 }
167 else {
168 dirname[0] = '/';
169 dirname[1] = '\0';
170 }
171
172# if defined(USE_STATFS_STATVFS)
173 if (statvfs(dirname, &disk)) {
174 return -1;
175 }
176# elif defined(USE_STATFS_4ARGS)
177 if (statfs(dirname, &disk, sizeof(struct statfs), 0)) {
178 return -1;
179 }
180# else
181 if (statfs(dirname, &disk)) {
182 return -1;
183 }
184# endif
185
186 return double(disk.f_bsize) * double(disk.f_bfree);
187#endif
188}
189
190int64_t BLI_ftell(FILE *stream)
191{
192#ifdef WIN32
193 return _ftelli64(stream);
194#else
195 return ftell(stream);
196#endif
197}
198
199int BLI_fseek(FILE *stream, int64_t offset, int whence)
200{
201#ifdef WIN32
202 return _fseeki64(stream, offset, whence);
203#else
204 return fseek(stream, offset, whence);
205#endif
206}
207
208int64_t BLI_lseek(int fd, int64_t offset, int whence)
209{
210#ifdef WIN32
211 return _lseeki64(fd, offset, whence);
212#else
213 return lseek(fd, offset, whence);
214#endif
215}
216
218{
219 BLI_stat_t st;
220 if ((file < 0) || (BLI_fstat(file, &st) == -1)) {
221 return -1;
222 }
223 return st.st_size;
224}
225
226size_t BLI_file_size(const char *path)
227{
228 BLI_stat_t stats;
229 if (BLI_stat(path, &stats) == -1) {
230 return -1;
231 }
232 return stats.st_size;
233}
234
235/* Return file attributes. Apple version of this function is defined in storage_apple.mm */
236#ifndef __APPLE__
238{
239 int ret = 0;
240
241# ifdef WIN32
242
243 if (BLI_path_extension_check(path, ".lnk")) {
244 return FILE_ATTR_ALIAS;
245 }
246
247 WCHAR wline[FILE_MAXDIR];
248 if (conv_utf_8_to_16(path, wline, ARRAY_SIZE(wline)) != 0) {
249 return eFileAttributes(ret);
250 }
251
252 DWORD attr = GetFileAttributesW(wline);
253 if (attr == INVALID_FILE_ATTRIBUTES) {
254 BLI_assert_msg(GetLastError() != ERROR_FILE_NOT_FOUND,
255 "BLI_file_attributes should only be called on existing files.");
256 return eFileAttributes(ret);
257 }
258
259 if (attr & FILE_ATTRIBUTE_READONLY) {
261 }
262 if (attr & FILE_ATTRIBUTE_HIDDEN) {
264 }
265 if (attr & FILE_ATTRIBUTE_SYSTEM) {
267 }
268 if (attr & FILE_ATTRIBUTE_ARCHIVE) {
270 }
271 if (attr & FILE_ATTRIBUTE_COMPRESSED) {
273 }
274 if (attr & FILE_ATTRIBUTE_ENCRYPTED) {
276 }
277 if (attr & FILE_ATTRIBUTE_TEMPORARY) {
279 }
280 if (attr & FILE_ATTRIBUTE_SPARSE_FILE) {
282 }
283 if (attr & FILE_ATTRIBUTE_OFFLINE || attr & FILE_ATTRIBUTE_RECALL_ON_OPEN ||
284 attr & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS)
285 {
287 }
288 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
290 }
291
292# else
293
294 UNUSED_VARS(path);
295
296 /* TODO:
297 * If Immutable set FILE_ATTR_READONLY
298 * If Archived set FILE_ATTR_ARCHIVE
299 */
300# endif
301 return eFileAttributes(ret);
302}
303#endif
304
305#ifndef __APPLE__ /* Apple version is defined in `storage_apple.mm`. */
306bool BLI_file_alias_target(const char *filepath,
307 /* This parameter can only be `const` on Linux since
308 * redirection is not supported there.
309 * NOLINTNEXTLINE: readability-non-const-parameter. */
310 char r_targetpath[FILE_MAXDIR])
311{
312# ifdef WIN32
313 if (!BLI_path_extension_check(filepath, ".lnk")) {
314 return false;
315 }
316
317 HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
318 if (FAILED(hr)) {
319 return false;
320 }
321
322 IShellLinkW *Shortcut = nullptr;
323 hr = CoCreateInstance(
324 CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&Shortcut);
325
326 bool success = false;
327 if (SUCCEEDED(hr)) {
328 IPersistFile *PersistFile;
329 hr = Shortcut->QueryInterface(IID_IPersistFile, (LPVOID *)&PersistFile);
330 if (SUCCEEDED(hr)) {
331 WCHAR path_utf16[FILE_MAXDIR] = {0};
332 if (conv_utf_8_to_16(filepath, path_utf16, ARRAY_SIZE(path_utf16)) == 0) {
333 hr = PersistFile->Load(path_utf16, STGM_READ);
334 if (SUCCEEDED(hr)) {
335 hr = Shortcut->Resolve(0, SLR_NO_UI | SLR_UPDATE | SLR_NOSEARCH);
336 if (SUCCEEDED(hr)) {
337 wchar_t target_utf16[FILE_MAXDIR] = {0};
338 hr = Shortcut->GetPath(target_utf16, FILE_MAXDIR, nullptr, 0);
339 if (SUCCEEDED(hr)) {
340 success = (conv_utf_16_to_8(target_utf16, r_targetpath, FILE_MAXDIR) == 0);
341 }
342 }
343 PersistFile->Release();
344 }
345 }
346 }
347 Shortcut->Release();
348 }
349
350 CoUninitialize();
351 return (success && r_targetpath[0]);
352# else
353 UNUSED_VARS(r_targetpath, filepath);
354 /* File-based redirection not supported. */
355 return false;
356# endif
357}
358#endif
359
360int BLI_exists(const char *path)
361{
362#if defined(WIN32)
363 BLI_stat_t st;
364 wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
365 int len, res;
366
367 len = wcslen(tmp_16);
368 /* in Windows #stat doesn't recognize dir ending on a slash
369 * so we remove it here */
370 if ((len > 3) && ELEM(tmp_16[len - 1], L'\\', L'/')) {
371 tmp_16[len - 1] = '\0';
372 }
373 /* two special cases where the trailing slash is needed:
374 * 1. after the share part of a UNC path
375 * 2. after the C:\ when the path is the volume only
376 */
377 if ((len >= 3) && (tmp_16[0] == L'\\') && (tmp_16[1] == L'\\')) {
378 BLI_path_normalize_unc_16(tmp_16);
379 }
380
381 if ((tmp_16[1] == L':') && (tmp_16[2] == L'\0')) {
382 tmp_16[2] = L'\\';
383 tmp_16[3] = L'\0';
384 }
385
386 res = BLI_wstat(tmp_16, &st);
387
388 free(tmp_16);
389 if (res == -1) {
390 return 0;
391 }
392#else
393 struct stat st;
395 if (stat(path, &st)) {
396 return 0;
397 }
398#endif
399 return (st.st_mode);
400}
401
402#ifdef WIN32
403int BLI_fstat(int fd, BLI_stat_t *buffer)
404{
405# if defined(_MSC_VER)
406 return _fstat64(fd, buffer);
407# else
408 return _fstat(fd, buffer);
409# endif
410}
411
412int BLI_stat(const char *path, BLI_stat_t *buffer)
413{
414 int r;
415 UTF16_ENCODE(path);
416
417 r = BLI_wstat(path_16, buffer);
418
419 UTF16_UN_ENCODE(path);
420 return r;
421}
422
423int BLI_wstat(const wchar_t *path, BLI_stat_t *buffer)
424{
425# if defined(_MSC_VER)
426 return _wstat64(path, buffer);
427# else
428 return _wstat(path, buffer);
429# endif
430}
431#else
432int BLI_fstat(int fd, struct stat *buffer)
433{
434 return fstat(fd, buffer);
435}
436
437int BLI_stat(const char *path, struct stat *buffer)
438{
439 return stat(path, buffer);
440}
441#endif
442
443bool BLI_is_dir(const char *path)
444{
445 return S_ISDIR(BLI_exists(path));
446}
447
448bool BLI_is_file(const char *path)
449{
450 const int mode = BLI_exists(path);
451 return (mode && !S_ISDIR(mode));
452}
453
455 bool read_size_exact,
456 size_t pad_bytes,
457 size_t *r_size)
458{
459 /* NOTE: Used for both text and binary file reading. */
460
461 BLI_stat_t st;
462 if (BLI_fstat(fileno(fp), &st) == -1) {
463 return nullptr;
464 }
465 if (S_ISDIR(st.st_mode)) {
466 return nullptr;
467 }
468 if (BLI_fseek(fp, 0L, SEEK_END) == -1) {
469 return nullptr;
470 }
471 /* Don't use the 'st_size' because it may be the symlink. */
472 const long int filelen = BLI_ftell(fp);
473 if (filelen == -1) {
474 return nullptr;
475 }
476 if (BLI_fseek(fp, 0L, SEEK_SET) == -1) {
477 return nullptr;
478 }
479
480 void *mem = MEM_mallocN(filelen + pad_bytes, __func__);
481 if (mem == nullptr) {
482 return nullptr;
483 }
484
485 const long int filelen_read = fread(mem, 1, filelen, fp);
486 if ((filelen_read < 0) || ferror(fp)) {
487 MEM_freeN(mem);
488 return nullptr;
489 }
490
491 if (read_size_exact) {
492 if (filelen_read != filelen) {
493 MEM_freeN(mem);
494 return nullptr;
495 }
496 }
497 else {
498 if (filelen_read < filelen) {
499 mem = MEM_reallocN(mem, filelen_read + pad_bytes);
500 if (mem == nullptr) {
501 return nullptr;
502 }
503 }
504 }
505
506 *r_size = filelen_read;
507
508 return mem;
509}
510
511void *BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
512{
513 FILE *fp = BLI_fopen(filepath, "r");
514 void *mem = nullptr;
515 if (fp) {
516 mem = BLI_file_read_data_as_mem_from_handle(fp, false, pad_bytes, r_size);
517 fclose(fp);
518 }
519 return mem;
520}
521
522void *BLI_file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
523{
524 FILE *fp = BLI_fopen(filepath, "rb");
525 void *mem = nullptr;
526 if (fp) {
527 mem = BLI_file_read_data_as_mem_from_handle(fp, true, pad_bytes, r_size);
528 fclose(fp);
529 }
530 return mem;
531}
532
534 bool trim_trailing_space,
535 size_t pad_bytes,
536 size_t *r_size)
537{
538 char *mem = static_cast<char *>(BLI_file_read_text_as_mem(filepath, pad_bytes, r_size));
539 if (mem != nullptr) {
540 char *mem_end = mem + *r_size;
541 if (pad_bytes != 0) {
542 *mem_end = '\0';
543 }
544 for (char *p = mem, *p_next; p != mem_end; p = p_next) {
545 p_next = static_cast<char *>(memchr(p, '\n', mem_end - p));
546 if (p_next != nullptr) {
547 if (trim_trailing_space) {
548 for (char *p_trim = p_next - 1; p_trim > p && ELEM(*p_trim, ' ', '\t'); p_trim--) {
549 *p_trim = '\0';
550 }
551 }
552 *p_next = '\0';
553 p_next++;
554 }
555 else {
556 p_next = mem_end;
557 }
558 }
559 }
560 return mem;
561}
562
563LinkNode *BLI_file_read_as_lines(const char *filepath)
564{
565 FILE *fp = BLI_fopen(filepath, "r");
566 LinkNodePair lines = {nullptr, nullptr};
567 char *buf;
568 size_t size;
569
570 if (!fp) {
571 return nullptr;
572 }
573
574 BLI_fseek(fp, 0, SEEK_END);
575 size = size_t(BLI_ftell(fp));
576 BLI_fseek(fp, 0, SEEK_SET);
577
578 if (UNLIKELY(size == size_t(-1))) {
579 fclose(fp);
580 return nullptr;
581 }
582
583 buf = MEM_calloc_arrayN<char>(size, "file_as_lines");
584 if (buf) {
585 size_t i, last = 0;
586
587 /*
588 * size = because on win32 reading
589 * all the bytes in the file will return
590 * less bytes because of `CRNL` changes.
591 */
592 size = fread(buf, 1, size, fp);
593 for (i = 0; i <= size; i++) {
594 if (i == size || buf[i] == '\n') {
595 char *line = BLI_strdupn(&buf[last], i - last);
596 BLI_linklist_append(&lines, line);
597 last = i + 1;
598 }
599 }
600
601 MEM_freeN(buf);
602 }
603
604 fclose(fp);
605
606 return lines.list;
607}
608
610{
611 BLI_linklist_freeN(lines);
612}
613
614bool BLI_file_older(const char *file1, const char *file2)
615{
616 BLI_stat_t st1, st2;
617 if (BLI_stat(file1, &st1)) {
618 return false;
619 }
620 if (BLI_stat(file2, &st2)) {
621 return false;
622 }
623 return (st1.st_mtime < st2.st_mtime);
624}
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
struct stat BLI_stat_t
eFileAttributes
@ FILE_ATTR_SPARSE_FILE
@ FILE_ATTR_COMPRESSED
@ FILE_ATTR_ENCRYPTED
@ FILE_ATTR_ALIAS
@ FILE_ATTR_TEMPORARY
@ FILE_ATTR_ARCHIVE
@ FILE_ATTR_REPARSE_POINT
@ FILE_ATTR_HIDDEN
@ FILE_ATTR_READONLY
@ FILE_ATTR_SYSTEM
@ FILE_ATTR_OFFLINE
void BLI_kdtree_nd_ free(KDTree *tree)
#define FILE_MAX
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_extension_check(const char *path, const char *ext) ATTR_NONNULL(1
#define FILE_MAXDIR
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst, const wchar_t *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
int BLI_thread_is_main(void)
Definition threads.cc:179
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define UNLIKELY(x)
#define ELEM(...)
Compatibility-like things for windows.
#define S_ISDIR(x)
const char * dirname(char *path)
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define L
return ret
bool BLI_change_working_dir(const char *dir)
Definition storage.cc:63
const char * BLI_dir_home()
Definition storage.cc:97
eFileAttributes BLI_file_attributes(const char *path)
Definition storage.cc:237
int BLI_exists(const char *path)
Definition storage.cc:360
bool BLI_file_older(const char *file1, const char *file2)
Definition storage.cc:614
void BLI_file_free_lines(LinkNode *lines)
Definition storage.cc:609
int BLI_fstat(int fd, struct stat *buffer)
Definition storage.cc:432
LinkNode * BLI_file_read_as_lines(const char *filepath)
Definition storage.cc:563
bool BLI_file_alias_target(const char *filepath, char r_targetpath[FILE_MAXDIR])
Definition storage.cc:306
int BLI_stat(const char *path, struct stat *buffer)
Definition storage.cc:437
size_t BLI_file_descriptor_size(int file)
Definition storage.cc:217
void * BLI_file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
Definition storage.cc:522
int64_t BLI_lseek(int fd, int64_t offset, int whence)
Definition storage.cc:208
double BLI_dir_free_space(const char *dir)
Definition storage.cc:121
size_t BLI_file_size(const char *path)
Definition storage.cc:226
bool BLI_is_dir(const char *path)
Definition storage.cc:443
int BLI_fseek(FILE *stream, int64_t offset, int whence)
Definition storage.cc:199
void * BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
Definition storage.cc:511
void * BLI_file_read_text_as_mem_with_newline_as_nil(const char *filepath, bool trim_trailing_space, size_t pad_bytes, size_t *r_size)
Definition storage.cc:533
char * BLI_current_working_dir(char *dir, const size_t maxncpy)
Definition storage.cc:81
void * BLI_file_read_data_as_mem_from_handle(FILE *fp, bool read_size_exact, size_t pad_bytes, size_t *r_size)
Definition storage.cc:454
bool BLI_is_file(const char *path)
Definition storage.cc:448
int64_t BLI_ftell(FILE *stream)
Definition storage.cc:190
LinkNode * list
i
Definition text_draw.cc:230
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition utfconv.cc:294
int conv_utf_8_to_16(const char *in8, wchar_t *out16, size_t size16)
Definition utfconv.cc:182
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition utfconv.cc:116
#define UTF16_ENCODE(in8str)
Definition utfconv.hh:80
#define UTF16_UN_ENCODE(in8str)
Definition utfconv.hh:84
uint len