Blender V4.3
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
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 int result = chdir(dir);
78 if (result == 0) {
79 BLI_setenv("PWD", dir);
80 }
81 return result == 0;
82# endif
83}
84
85char *BLI_current_working_dir(char *dir, const size_t maxncpy)
86{
87# if defined(WIN32)
88 wchar_t path[MAX_PATH];
89 if (_wgetcwd(path, MAX_PATH)) {
90 if (BLI_strncpy_wchar_as_utf8(dir, path, maxncpy) != maxncpy) {
91 return dir;
92 }
93 }
94 return NULL;
95# else
96 const char *pwd = BLI_getenv("PWD");
97 if (pwd) {
98 size_t srclen = BLI_strnlen(pwd, maxncpy);
99 if (srclen != maxncpy) {
100 memcpy(dir, pwd, srclen + 1);
101 return dir;
102 }
103 return nullptr;
104 }
105 return getcwd(dir, maxncpy);
106# endif
107}
108#endif /* !defined (__APPLE__) */
109
110double BLI_dir_free_space(const char *dir)
111{
112#ifdef WIN32
113 DWORD sectorspc, bytesps, freec, clusters;
114 char tmp[4];
115
116 tmp[0] = '\\';
117 tmp[1] = 0; /* Just a fail-safe. */
118 if (ELEM(dir[0], '/', '\\')) {
119 tmp[0] = '\\';
120 tmp[1] = 0;
121 }
122 else if (dir[1] == ':') {
123 tmp[0] = dir[0];
124 tmp[1] = ':';
125 tmp[2] = '\\';
126 tmp[3] = 0;
127 }
128
129 GetDiskFreeSpace(tmp, &sectorspc, &bytesps, &freec, &clusters);
130
131 return double(freec * bytesps * sectorspc);
132#else
133
134# ifdef USE_STATFS_STATVFS
135 struct statvfs disk;
136# else
137 struct statfs disk;
138# endif
139
140 char dirname[FILE_MAXDIR], *slash;
141 int len = strlen(dir);
142
143 if (len >= FILE_MAXDIR) {
144 /* path too long */
145 return -1;
146 }
147
148 memcpy(dirname, dir, len + 1);
149
150 if (len) {
151 slash = strrchr(dirname, '/');
152 if (slash) {
153 slash[1] = '\0';
154 }
155 }
156 else {
157 dirname[0] = '/';
158 dirname[1] = '\0';
159 }
160
161# if defined(USE_STATFS_STATVFS)
162 if (statvfs(dirname, &disk)) {
163 return -1;
164 }
165# elif defined(USE_STATFS_4ARGS)
166 if (statfs(dirname, &disk, sizeof(struct statfs), 0)) {
167 return -1;
168 }
169# else
170 if (statfs(dirname, &disk)) {
171 return -1;
172 }
173# endif
174
175 return double(disk.f_bsize) * double(disk.f_bfree);
176#endif
177}
178
179int64_t BLI_ftell(FILE *stream)
180{
181#ifdef WIN32
182 return _ftelli64(stream);
183#else
184 return ftell(stream);
185#endif
186}
187
188int BLI_fseek(FILE *stream, int64_t offset, int whence)
189{
190#ifdef WIN32
191 return _fseeki64(stream, offset, whence);
192#else
193 return fseek(stream, offset, whence);
194#endif
195}
196
197int64_t BLI_lseek(int fd, int64_t offset, int whence)
198{
199#ifdef WIN32
200 return _lseeki64(fd, offset, whence);
201#else
202 return lseek(fd, offset, whence);
203#endif
204}
205
207{
208 BLI_stat_t st;
209 if ((file < 0) || (BLI_fstat(file, &st) == -1)) {
210 return -1;
211 }
212 return st.st_size;
213}
214
215size_t BLI_file_size(const char *path)
216{
217 BLI_stat_t stats;
218 if (BLI_stat(path, &stats) == -1) {
219 return -1;
220 }
221 return stats.st_size;
222}
223
224/* Return file attributes. Apple version of this function is defined in storage_apple.mm */
225#ifndef __APPLE__
227{
228 int ret = 0;
229
230# ifdef WIN32
231
232 if (BLI_path_extension_check(path, ".lnk")) {
233 return FILE_ATTR_ALIAS;
234 }
235
236 WCHAR wline[FILE_MAXDIR];
237 if (conv_utf_8_to_16(path, wline, ARRAY_SIZE(wline)) != 0) {
238 return eFileAttributes(ret);
239 }
240
241 DWORD attr = GetFileAttributesW(wline);
242 if (attr == INVALID_FILE_ATTRIBUTES) {
243 BLI_assert_msg(GetLastError() != ERROR_FILE_NOT_FOUND,
244 "BLI_file_attributes should only be called on existing files.");
245 return eFileAttributes(ret);
246 }
247
248 if (attr & FILE_ATTRIBUTE_READONLY) {
250 }
251 if (attr & FILE_ATTRIBUTE_HIDDEN) {
253 }
254 if (attr & FILE_ATTRIBUTE_SYSTEM) {
256 }
257 if (attr & FILE_ATTRIBUTE_ARCHIVE) {
259 }
260 if (attr & FILE_ATTRIBUTE_COMPRESSED) {
262 }
263 if (attr & FILE_ATTRIBUTE_ENCRYPTED) {
265 }
266 if (attr & FILE_ATTRIBUTE_TEMPORARY) {
268 }
269 if (attr & FILE_ATTRIBUTE_SPARSE_FILE) {
271 }
272 if (attr & FILE_ATTRIBUTE_OFFLINE || attr & FILE_ATTRIBUTE_RECALL_ON_OPEN ||
273 attr & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS)
274 {
276 }
277 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
279 }
280
281# else
282
283 UNUSED_VARS(path);
284
285 /* TODO:
286 * If Immutable set FILE_ATTR_READONLY
287 * If Archived set FILE_ATTR_ARCHIVE
288 */
289# endif
290 return eFileAttributes(ret);
291}
292#endif
293
294/* Return alias/shortcut file target. Apple version is defined in storage_apple.mm */
295#ifndef __APPLE__
296bool BLI_file_alias_target(const char *filepath,
297 /* This parameter can only be `const` on Linux since
298 * redirection is not supported there.
299 * NOLINTNEXTLINE: readability-non-const-parameter. */
300 char r_targetpath[/*FILE_MAXDIR*/])
301{
302# ifdef WIN32
303 if (!BLI_path_extension_check(filepath, ".lnk")) {
304 return false;
305 }
306
307 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
308 if (FAILED(hr)) {
309 return false;
310 }
311
312 IShellLinkW *Shortcut = NULL;
313 hr = CoCreateInstance(
314 CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&Shortcut);
315
316 bool success = false;
317 if (SUCCEEDED(hr)) {
318 IPersistFile *PersistFile;
319 hr = Shortcut->QueryInterface(IID_IPersistFile, (LPVOID *)&PersistFile);
320 if (SUCCEEDED(hr)) {
321 WCHAR path_utf16[FILE_MAXDIR] = {0};
322 if (conv_utf_8_to_16(filepath, path_utf16, ARRAY_SIZE(path_utf16)) == 0) {
323 hr = PersistFile->Load(path_utf16, STGM_READ);
324 if (SUCCEEDED(hr)) {
325 hr = Shortcut->Resolve(0, SLR_NO_UI | SLR_UPDATE | SLR_NOSEARCH);
326 if (SUCCEEDED(hr)) {
327 wchar_t target_utf16[FILE_MAXDIR] = {0};
328 hr = Shortcut->GetPath(target_utf16, FILE_MAXDIR, NULL, 0);
329 if (SUCCEEDED(hr)) {
330 success = (conv_utf_16_to_8(target_utf16, r_targetpath, FILE_MAXDIR) == 0);
331 }
332 }
333 PersistFile->Release();
334 }
335 }
336 }
337 Shortcut->Release();
338 }
339
340 CoUninitialize();
341 return (success && r_targetpath[0]);
342# else
343 UNUSED_VARS(r_targetpath, filepath);
344 /* File-based redirection not supported. */
345 return false;
346# endif
347}
348#endif
349
350int BLI_exists(const char *path)
351{
352#if defined(WIN32)
353 BLI_stat_t st;
354 wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
355 int len, res;
356
357 len = wcslen(tmp_16);
358 /* in Windows #stat doesn't recognize dir ending on a slash
359 * so we remove it here */
360 if ((len > 3) && ELEM(tmp_16[len - 1], L'\\', L'/')) {
361 tmp_16[len - 1] = '\0';
362 }
363 /* two special cases where the trailing slash is needed:
364 * 1. after the share part of a UNC path
365 * 2. after the C:\ when the path is the volume only
366 */
367 if ((len >= 3) && (tmp_16[0] == L'\\') && (tmp_16[1] == L'\\')) {
368 BLI_path_normalize_unc_16(tmp_16);
369 }
370
371 if ((tmp_16[1] == L':') && (tmp_16[2] == L'\0')) {
372 tmp_16[2] = L'\\';
373 tmp_16[3] = L'\0';
374 }
375
376 res = BLI_wstat(tmp_16, &st);
377
378 free(tmp_16);
379 if (res == -1) {
380 return 0;
381 }
382#else
383 struct stat st;
385 if (stat(path, &st)) {
386 return 0;
387 }
388#endif
389 return (st.st_mode);
390}
391
392#ifdef WIN32
393int BLI_fstat(int fd, BLI_stat_t *buffer)
394{
395# if defined(_MSC_VER)
396 return _fstat64(fd, buffer);
397# else
398 return _fstat(fd, buffer);
399# endif
400}
401
402int BLI_stat(const char *path, BLI_stat_t *buffer)
403{
404 int r;
405 UTF16_ENCODE(path);
406
407 r = BLI_wstat(path_16, buffer);
408
409 UTF16_UN_ENCODE(path);
410 return r;
411}
412
413int BLI_wstat(const wchar_t *path, BLI_stat_t *buffer)
414{
415# if defined(_MSC_VER)
416 return _wstat64(path, buffer);
417# else
418 return _wstat(path, buffer);
419# endif
420}
421#else
422int BLI_fstat(int fd, struct stat *buffer)
423{
424 return fstat(fd, buffer);
425}
426
427int BLI_stat(const char *path, struct stat *buffer)
428{
429 return stat(path, buffer);
430}
431#endif
432
433bool BLI_is_dir(const char *path)
434{
435 return S_ISDIR(BLI_exists(path));
436}
437
438bool BLI_is_file(const char *path)
439{
440 const int mode = BLI_exists(path);
441 return (mode && !S_ISDIR(mode));
442}
443
448 bool read_size_exact,
449 size_t pad_bytes,
450 size_t *r_size)
451{
452 BLI_stat_t st;
453 if (BLI_fstat(fileno(fp), &st) == -1) {
454 return nullptr;
455 }
456 if (S_ISDIR(st.st_mode)) {
457 return nullptr;
458 }
459 if (BLI_fseek(fp, 0L, SEEK_END) == -1) {
460 return nullptr;
461 }
462 /* Don't use the 'st_size' because it may be the symlink. */
463 const long int filelen = BLI_ftell(fp);
464 if (filelen == -1) {
465 return nullptr;
466 }
467 if (BLI_fseek(fp, 0L, SEEK_SET) == -1) {
468 return nullptr;
469 }
470
471 void *mem = MEM_mallocN(filelen + pad_bytes, __func__);
472 if (mem == nullptr) {
473 return nullptr;
474 }
475
476 const long int filelen_read = fread(mem, 1, filelen, fp);
477 if ((filelen_read < 0) || ferror(fp)) {
478 MEM_freeN(mem);
479 return nullptr;
480 }
481
482 if (read_size_exact) {
483 if (filelen_read != filelen) {
484 MEM_freeN(mem);
485 return nullptr;
486 }
487 }
488 else {
489 if (filelen_read < filelen) {
490 mem = MEM_reallocN(mem, filelen_read + pad_bytes);
491 if (mem == nullptr) {
492 return nullptr;
493 }
494 }
495 }
496
497 *r_size = filelen_read;
498
499 return mem;
500}
501
502void *BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
503{
504 FILE *fp = BLI_fopen(filepath, "r");
505 void *mem = nullptr;
506 if (fp) {
507 mem = BLI_file_read_data_as_mem_from_handle(fp, false, pad_bytes, r_size);
508 fclose(fp);
509 }
510 return mem;
511}
512
513void *BLI_file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
514{
515 FILE *fp = BLI_fopen(filepath, "rb");
516 void *mem = nullptr;
517 if (fp) {
518 mem = BLI_file_read_data_as_mem_from_handle(fp, true, pad_bytes, r_size);
519 fclose(fp);
520 }
521 return mem;
522}
523
525 bool trim_trailing_space,
526 size_t pad_bytes,
527 size_t *r_size)
528{
529 char *mem = static_cast<char *>(BLI_file_read_text_as_mem(filepath, pad_bytes, r_size));
530 if (mem != nullptr) {
531 char *mem_end = mem + *r_size;
532 if (pad_bytes != 0) {
533 *mem_end = '\0';
534 }
535 for (char *p = mem, *p_next; p != mem_end; p = p_next) {
536 p_next = static_cast<char *>(memchr(p, '\n', mem_end - p));
537 if (p_next != nullptr) {
538 if (trim_trailing_space) {
539 for (char *p_trim = p_next - 1; p_trim > p && ELEM(*p_trim, ' ', '\t'); p_trim--) {
540 *p_trim = '\0';
541 }
542 }
543 *p_next = '\0';
544 p_next++;
545 }
546 else {
547 p_next = mem_end;
548 }
549 }
550 }
551 return mem;
552}
553
554LinkNode *BLI_file_read_as_lines(const char *filepath)
555{
556 FILE *fp = BLI_fopen(filepath, "r");
557 LinkNodePair lines = {nullptr, nullptr};
558 char *buf;
559 size_t size;
560
561 if (!fp) {
562 return nullptr;
563 }
564
565 BLI_fseek(fp, 0, SEEK_END);
566 size = size_t(BLI_ftell(fp));
567 BLI_fseek(fp, 0, SEEK_SET);
568
569 if (UNLIKELY(size == size_t(-1))) {
570 fclose(fp);
571 return nullptr;
572 }
573
574 buf = MEM_cnew_array<char>(size, "file_as_lines");
575 if (buf) {
576 size_t i, last = 0;
577
578 /*
579 * size = because on win32 reading
580 * all the bytes in the file will return
581 * less bytes because of `CRNL` changes.
582 */
583 size = fread(buf, 1, size, fp);
584 for (i = 0; i <= size; i++) {
585 if (i == size || buf[i] == '\n') {
586 char *line = BLI_strdupn(&buf[last], i - last);
587 BLI_linklist_append(&lines, line);
588 last = i + 1;
589 }
590 }
591
592 MEM_freeN(buf);
593 }
594
595 fclose(fp);
596
597 return lines.list;
598}
599
601{
602 BLI_linklist_freeN(lines);
603}
604
605bool BLI_file_older(const char *file1, const char *file2)
606{
607 BLI_stat_t st1, st2;
608 if (BLI_stat(file1, &st1)) {
609 return false;
610 }
611 if (BLI_stat(file2, &st2)) {
612 return false;
613 }
614 return (st1.st_mtime < st2.st_mtime);
615}
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
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
void BLI_setenv(const char *env, const char *val) ATTR_NONNULL(1)
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.c:29
int char char int int int int size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:909
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)
typedef double(DMatrix)[4][4]
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define NULL
int len
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
#define L
return ret
__int64 int64_t
Definition stdint.h:89
bool BLI_change_working_dir(const char *dir)
Definition storage.cc:63
eFileAttributes BLI_file_attributes(const char *path)
Definition storage.cc:226
int BLI_exists(const char *path)
Definition storage.cc:350
bool BLI_file_older(const char *file1, const char *file2)
Definition storage.cc:605
void BLI_file_free_lines(LinkNode *lines)
Definition storage.cc:600
int BLI_fstat(int fd, struct stat *buffer)
Definition storage.cc:422
LinkNode * BLI_file_read_as_lines(const char *filepath)
Definition storage.cc:554
int BLI_stat(const char *path, struct stat *buffer)
Definition storage.cc:427
size_t BLI_file_descriptor_size(int file)
Definition storage.cc:206
void * BLI_file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
Definition storage.cc:513
int64_t BLI_lseek(int fd, int64_t offset, int whence)
Definition storage.cc:197
double BLI_dir_free_space(const char *dir)
Definition storage.cc:110
size_t BLI_file_size(const char *path)
Definition storage.cc:215
bool BLI_file_alias_target(const char *filepath, char r_targetpath[])
Definition storage.cc:296
bool BLI_is_dir(const char *path)
Definition storage.cc:433
int BLI_fseek(FILE *stream, int64_t offset, int whence)
Definition storage.cc:188
void * BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
Definition storage.cc:502
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:524
char * BLI_current_working_dir(char *dir, const size_t maxncpy)
Definition storage.cc:85
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:447
bool BLI_is_file(const char *path)
Definition storage.cc:438
int64_t BLI_ftell(FILE *stream)
Definition storage.cc:179
LinkNode * list
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition utfconv.cc:292
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