CMake: add WITH_LINKER_LLD option for unix platforms
[blender-staging.git] / source / blender / blenlib / intern / storage.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bli
22  *
23  * Some really low-level file operations.
24  */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29
30 #include <sys/stat.h>
31
32 #if defined(__NetBSD__) || defined(__DragonFly__) || defined(__HAIKU__)
33 /* Other modern unix os's should probably use this also */
34 #  include <sys/statvfs.h>
35 #  define USE_STATFS_STATVFS
36 #endif
37
38 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \
39     defined(__DragonFly__)
40 /* For statfs */
41 #  include <sys/mount.h>
42 #  include <sys/param.h>
43 #endif
44
45 #if defined(__linux__) || defined(__hpux) || defined(__GNU__) || defined(__GLIBC__)
46 #  include <sys/vfs.h>
47 #endif
48
49 #include <fcntl.h>
50 #include <string.h> /* strcpy etc.. */
51
52 #ifdef WIN32
53 #  include "BLI_string_utf8.h"
54 #  include "BLI_winstuff.h"
55 #  include "utfconv.h"
56 #  include <direct.h>
57 #  include <io.h>
58 #  include <stdbool.h>
59 #else
60 #  include <pwd.h>
61 #  include <sys/ioctl.h>
62 #  include <unistd.h>
63 #endif
64
65 /* lib includes */
66 #include "MEM_guardedalloc.h"
67
68 #include "BLI_fileops.h"
69 #include "BLI_linklist.h"
70 #include "BLI_path_util.h"
71 #include "BLI_string.h"
72 #include "BLI_utildefines.h"
73
74 /**
75  * Copies the current working directory into *dir (max size maxncpy), and
76  * returns a pointer to same.
77  *
78  * \note can return NULL when the size is not big enough
79  */
80 char *BLI_current_working_dir(char *dir, const size_t maxncpy)
81 {
82 #if defined(WIN32)
83   wchar_t path[MAX_PATH];
84   if (_wgetcwd(path, MAX_PATH)) {
85     if (BLI_strncpy_wchar_as_utf8(dir, path, maxncpy) != maxncpy) {
86       return dir;
87     }
88   }
89   return NULL;
90 #else
91   const char *pwd = BLI_getenv("PWD");
92   if (pwd) {
93     size_t srclen = BLI_strnlen(pwd, maxncpy);
94     if (srclen != maxncpy) {
95       memcpy(dir, pwd, srclen + 1);
96       return dir;
97     }
98     else {
99       return NULL;
100     }
101   }
102   return getcwd(dir, maxncpy);
103 #endif
104 }
105
106 /**
107  * Returns the number of free bytes on the volume containing the specified pathname. */
108 /* Not actually used anywhere.
109  */
110 double 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 failsafe */
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 name[FILE_MAXDIR], *slash;
141   int len = strlen(dir);
142
143   if (len >= FILE_MAXDIR) {
144     /* path too long */
145     return -1;
146   }
147
148   strcpy(name, dir);
149
150   if (len) {
151     slash = strrchr(name, '/');
152     if (slash) {
153       slash[1] = 0;
154     }
155   }
156   else {
157     strcpy(name, "/");
158   }
159
160 #  if defined(USE_STATFS_STATVFS)
161   if (statvfs(name, &disk)) {
162     return -1;
163   }
164 #  elif defined(USE_STATFS_4ARGS)
165   if (statfs(name, &disk, sizeof(struct statfs), 0)) {
166     return -1;
167   }
168 #  else
169   if (statfs(name, &disk)) {
170     return -1;
171   }
172 #  endif
173
174   return (((double)disk.f_bsize) * ((double)disk.f_bfree));
175 #endif
176 }
177
178 int64_t BLI_ftell(FILE *stream)
179 {
180 #ifdef WIN32
181   return _ftelli64(stream);
182 #else
183   return ftell(stream);
184 #endif
185 }
186
187 int BLI_fseek(FILE *stream, int64_t offset, int whence)
188 {
189 #ifdef WIN32
190   return _fseeki64(stream, offset, whence);
191 #else
192   return fseek(stream, offset, whence);
193 #endif
194 }
195
196 int64_t BLI_lseek(int fd, int64_t offset, int whence)
197 {
198 #ifdef WIN32
199   return _lseeki64(fd, offset, whence);
200 #else
201   return lseek(fd, offset, whence);
202 #endif
203 }
204
205 /**
206  * Returns the file size of an opened file descriptor.
207  */
208 size_t BLI_file_descriptor_size(int file)
209 {
210   BLI_stat_t st;
211   if ((file < 0) || (BLI_fstat(file, &st) == -1)) {
212     return -1;
213   }
214   return st.st_size;
215 }
216
217 /**
218  * Returns the size of a file.
219  */
220 size_t BLI_file_size(const char *path)
221 {
222   BLI_stat_t stats;
223   if (BLI_stat(path, &stats) == -1) {
224     return -1;
225   }
226   return stats.st_size;
227 }
228
229 #ifndef __APPLE__
230 eFileAttributes BLI_file_attributes(const char *path)
231 {
232   int ret = 0;
233
234 #  ifdef WIN32
235   WCHAR wline[FILE_MAXDIR];
236   size_t bsize = count_utf_16_from_8(path);
237   conv_utf_8_to_16(path, wline, bsize);
238   DWORD attr = GetFileAttributesW(wline);
239   if (attr & FILE_ATTRIBUTE_READONLY) {
240     ret |= FILE_ATTR_READONLY;
241   }
242   if (attr & FILE_ATTRIBUTE_HIDDEN) {
243     ret |= FILE_ATTR_HIDDEN;
244   }
245   if (attr & FILE_ATTRIBUTE_SYSTEM) {
246     ret |= FILE_ATTR_SYSTEM;
247   }
248   if (attr & FILE_ATTRIBUTE_ARCHIVE) {
249     ret |= FILE_ATTR_ARCHIVE;
250   }
251   if (attr & FILE_ATTRIBUTE_COMPRESSED) {
252     ret |= FILE_ATTR_COMPRESSED;
253   }
254   if (attr & FILE_ATTRIBUTE_ENCRYPTED) {
255     ret |= FILE_ATTR_ENCRYPTED;
256   }
257   if (attr & FILE_ATTRIBUTE_TEMPORARY) {
258     ret |= FILE_ATTR_TEMPORARY;
259   }
260   if (attr & FILE_ATTRIBUTE_SPARSE_FILE) {
261     ret |= FILE_ATTR_SPARSE_FILE;
262   }
263   if (attr & FILE_ATTRIBUTE_OFFLINE) {
264     ret |= FILE_ATTR_OFFLINE;
265   }
266   if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
267     ret |= FILE_ATTR_REPARSE_POINT;
268   }
269
270 #  endif
271
272 #  ifdef __linux__
273   UNUSED_VARS(path);
274
275   /* TODO:
276    * If Immutable set FILE_ATTR_READONLY
277    * If Archived set FILE_ATTR_ARCHIVE
278    */
279
280 #  endif
281
282   return ret;
283 }
284 #endif
285
286 /**
287  * Returns the target path of a file-based redirection, like Mac Alias or Win32 Shortcut file.
288  */
289 #ifndef __APPLE__
290 bool BLI_file_alias_target(char UNUSED(target[FILE_MAXDIR]), const char *UNUSED(filepath))
291 {
292   /* TODO: Find target in Win32 Shortcut - Shell Link (.lnk) file.
293    * Format: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/ */
294   return false;
295 }
296 #endif
297
298 /**
299  * Returns the st_mode from stat-ing the specified path name, or 0 if stat fails
300  * (most likely doesn't exist or no access).
301  */
302 int BLI_exists(const char *name)
303 {
304 #if defined(WIN32)
305   BLI_stat_t st;
306   wchar_t *tmp_16 = alloc_utf16_from_8(name, 1);
307   int len, res;
308
309   len = wcslen(tmp_16);
310   /* in Windows #stat doesn't recognize dir ending on a slash
311    * so we remove it here */
312   if ((len > 3) && ELEM(tmp_16[len - 1], L'\\', L'/')) {
313     tmp_16[len - 1] = '\0';
314   }
315   /* two special cases where the trailing slash is needed:
316    * 1. after the share part of a UNC path
317    * 2. after the C:\ when the path is the volume only
318    */
319   if ((len >= 3) && (tmp_16[0] == L'\\') && (tmp_16[1] == L'\\')) {
320     BLI_path_normalize_unc_16(tmp_16);
321   }
322
323   if ((tmp_16[1] == L':') && (tmp_16[2] == L'\0')) {
324     tmp_16[2] = L'\\';
325     tmp_16[3] = L'\0';
326   }
327
328   res = BLI_wstat(tmp_16, &st);
329
330   free(tmp_16);
331   if (res == -1) {
332     return (0);
333   }
334 #else
335   struct stat st;
336   BLI_assert(!BLI_path_is_rel(name));
337   if (stat(name, &st)) {
338     return (0);
339   }
340 #endif
341   return (st.st_mode);
342 }
343
344 #ifdef WIN32
345 int BLI_fstat(int fd, BLI_stat_t *buffer)
346 {
347 #  if defined(_MSC_VER)
348   return _fstat64(fd, buffer);
349 #  else
350   return _fstat(fd, buffer);
351 #  endif
352 }
353
354 int BLI_stat(const char *path, BLI_stat_t *buffer)
355 {
356   int r;
357   UTF16_ENCODE(path);
358
359   r = BLI_wstat(path_16, buffer);
360
361   UTF16_UN_ENCODE(path);
362   return r;
363 }
364
365 int BLI_wstat(const wchar_t *path, BLI_stat_t *buffer)
366 {
367 #  if defined(_MSC_VER)
368   return _wstat64(path, buffer);
369 #  else
370   return _wstat(path, buffer);
371 #  endif
372 }
373 #else
374 int BLI_fstat(int fd, struct stat *buffer)
375 {
376   return fstat(fd, buffer);
377 }
378
379 int BLI_stat(const char *path, struct stat *buffer)
380 {
381   return stat(path, buffer);
382 }
383 #endif
384
385 /**
386  * Does the specified path point to a directory?
387  * \note Would be better in fileops.c except that it needs stat.h so add here
388  */
389 bool BLI_is_dir(const char *file)
390 {
391   return S_ISDIR(BLI_exists(file));
392 }
393
394 /**
395  * Does the specified path point to a non-directory?
396  */
397 bool BLI_is_file(const char *path)
398 {
399   const int mode = BLI_exists(path);
400   return (mode && !S_ISDIR(mode));
401 }
402
403 /**
404  * Use for both text and binary file reading.
405  */
406 static void *file_read_data_as_mem_impl(FILE *fp,
407                                         bool read_size_exact,
408                                         size_t pad_bytes,
409                                         size_t *r_size)
410 {
411   BLI_stat_t st;
412   if (BLI_fstat(fileno(fp), &st) == -1) {
413     return NULL;
414   }
415   if (S_ISDIR(st.st_mode)) {
416     return NULL;
417   }
418   if (BLI_fseek(fp, 0L, SEEK_END) == -1) {
419     return NULL;
420   }
421   /* Don't use the 'st_size' because it may be the symlink. */
422   const long int filelen = BLI_ftell(fp);
423   if (filelen == -1) {
424     return NULL;
425   }
426   if (BLI_fseek(fp, 0L, SEEK_SET) == -1) {
427     return NULL;
428   }
429
430   void *mem = MEM_mallocN(filelen + pad_bytes, __func__);
431   if (mem == NULL) {
432     return NULL;
433   }
434
435   const long int filelen_read = fread(mem, 1, filelen, fp);
436   if ((filelen_read < 0) || ferror(fp)) {
437     MEM_freeN(mem);
438     return NULL;
439   }
440
441   if (read_size_exact) {
442     if (filelen_read != filelen) {
443       MEM_freeN(mem);
444       return NULL;
445     }
446   }
447   else {
448     if (filelen_read < filelen) {
449       mem = MEM_reallocN(mem, filelen_read + pad_bytes);
450       if (mem == NULL) {
451         return NULL;
452       }
453     }
454   }
455
456   *r_size = filelen_read;
457
458   return mem;
459 }
460
461 void *BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
462 {
463   FILE *fp = BLI_fopen(filepath, "r");
464   void *mem = NULL;
465   if (fp) {
466     mem = file_read_data_as_mem_impl(fp, false, pad_bytes, r_size);
467     fclose(fp);
468   }
469   return mem;
470 }
471
472 void *BLI_file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
473 {
474   FILE *fp = BLI_fopen(filepath, "rb");
475   void *mem = NULL;
476   if (fp) {
477     mem = file_read_data_as_mem_impl(fp, true, pad_bytes, r_size);
478     fclose(fp);
479   }
480   return mem;
481 }
482
483 /**
484  * Return the text file data with:
485
486  * - Newlines replaced with '\0'.
487  * - Optionally trim whitespace, replacing trailing ' ' & '\t' with '\0'.
488  *
489  * This is an alternative to using #BLI_file_read_as_lines,
490  * allowing us to loop over lines without converting it into a linked list
491  * with individual allocations.
492  *
493  * \param trim_trailing_space: Replace trailing spaces & tabs with nil.
494  * This arguments prevents the caller from counting blank lines (if that's important).
495  * \param pad_bytes: When this is non-zero, the first byte is set to nil,
496  * to simplify parsing the file.
497  * It's recommended to pass in 1, so all text is nil terminated.
498  *
499  * Example looping over lines:
500  *
501  * \code{.c}
502  * size_t data_len;
503  * char *data = BLI_file_read_text_as_mem_with_newline_as_nil(filepath, true, 1, &data_len);
504  * char *data_end = data + data_len;
505  * for (char *line = data; line != data_end; line = strlen(line) + 1) {
506  *  printf("line='%s'\n", line);
507  * }
508  * \endcode
509  */
510 void *BLI_file_read_text_as_mem_with_newline_as_nil(const char *filepath,
511                                                     bool trim_trailing_space,
512                                                     size_t pad_bytes,
513                                                     size_t *r_size)
514 {
515   char *mem = BLI_file_read_text_as_mem(filepath, pad_bytes, r_size);
516   if (mem != NULL) {
517     char *mem_end = mem + *r_size;
518     if (pad_bytes != 0) {
519       *mem_end = '\0';
520     }
521     for (char *p = mem, *p_next; p != mem_end; p = p_next) {
522       p_next = memchr(p, '\n', mem_end - p);
523       if (p_next != NULL) {
524         if (trim_trailing_space) {
525           for (char *p_trim = p_next - 1; p_trim > p && ELEM(*p_trim, ' ', '\t'); p_trim--) {
526             *p_trim = '\0';
527           }
528         }
529         *p_next = '\0';
530         p_next++;
531       }
532       else {
533         p_next = mem_end;
534       }
535     }
536   }
537   return mem;
538 }
539
540 /**
541  * Reads the contents of a text file and returns the lines in a linked list.
542  */
543 LinkNode *BLI_file_read_as_lines(const char *name)
544 {
545   FILE *fp = BLI_fopen(name, "r");
546   LinkNodePair lines = {NULL, NULL};
547   char *buf;
548   size_t size;
549
550   if (!fp) {
551     return NULL;
552   }
553
554   BLI_fseek(fp, 0, SEEK_END);
555   size = (size_t)BLI_ftell(fp);
556   BLI_fseek(fp, 0, SEEK_SET);
557
558   if (UNLIKELY(size == (size_t)-1)) {
559     fclose(fp);
560     return NULL;
561   }
562
563   buf = MEM_mallocN(size, "file_as_lines");
564   if (buf) {
565     size_t i, last = 0;
566
567     /*
568      * size = because on win32 reading
569      * all the bytes in the file will return
570      * less bytes because of `CRNL` changes.
571      */
572     size = fread(buf, 1, size, fp);
573     for (i = 0; i <= size; i++) {
574       if (i == size || buf[i] == '\n') {
575         char *line = BLI_strdupn(&buf[last], i - last);
576         BLI_linklist_append(&lines, line);
577         last = i + 1;
578       }
579     }
580
581     MEM_freeN(buf);
582   }
583
584   fclose(fp);
585
586   return lines.list;
587 }
588
589 /*
590  * Frees memory from a previous call to BLI_file_read_as_lines.
591  */
592 void BLI_file_free_lines(LinkNode *lines)
593 {
594   BLI_linklist_freeN(lines);
595 }
596
597 /** is file1 older then file2 */
598 bool BLI_file_older(const char *file1, const char *file2)
599 {
600 #ifdef WIN32
601   struct _stat st1, st2;
602
603   UTF16_ENCODE(file1);
604   UTF16_ENCODE(file2);
605
606   if (_wstat(file1_16, &st1)) {
607     return false;
608   }
609   if (_wstat(file2_16, &st2)) {
610     return false;
611   }
612
613   UTF16_UN_ENCODE(file2);
614   UTF16_UN_ENCODE(file1);
615 #else
616   struct stat st1, st2;
617
618   if (stat(file1, &st1)) {
619     return false;
620   }
621   if (stat(file2, &st2)) {
622     return false;
623   }
624 #endif
625   return (st1.st_mtime < st2.st_mtime);
626 }