cce1d2136e289f3b837fd207b1c7049093134ae0
[blender-staging.git] / source / blender / blenlib / intern / storage.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  * Reorganised mar-01 nzc
27  * Some really low-level file thingies.
28  */
29
30 /** \file blender/blenlib/intern/storage.c
31  *  \ingroup bli
32  */
33
34
35 #include <sys/types.h>
36 #include <stdio.h>
37 #include <stdlib.h>     
38
39 #ifndef WIN32
40 #include <dirent.h>
41 #endif
42
43 #include <time.h>
44 #include <sys/stat.h>
45
46 #if defined (__sun__) || defined (__sun) || defined (__NetBSD__)
47 #include <sys/statvfs.h> /* Other modern unix os's should probably use this also */
48 #elif !defined(__FreeBSD__) && !defined(linux) && (defined(__sparc) || defined(__sparc__))
49 #include <sys/statfs.h>
50 #endif
51
52 #if defined (__FreeBSD__) || defined (__OpenBSD__)
53 #include <sys/param.h>
54 #include <sys/mount.h>
55 #endif
56
57 #if defined(linux) || defined(__CYGWIN32__) || defined(__hpux) || defined(__GNU__) || defined(__GLIBC__)
58 #include <sys/vfs.h>
59 #endif
60
61 #ifdef __APPLE__
62 /* For statfs */
63 #include <sys/param.h>
64 #include <sys/mount.h>
65 #endif /* __APPLE__ */
66
67
68 #include <fcntl.h>
69 #include <string.h>                     /* strcpy etc.. */
70
71 #ifndef WIN32
72 #include <sys/ioctl.h>
73 #include <unistd.h>                     /*  */
74 #include <pwd.h>
75 #endif
76
77 #ifdef WIN32
78 #include <io.h>
79 #include <direct.h>
80 #include "BLI_winstuff.h"
81 #include "utfconv.h"
82 #endif
83
84
85 /* lib includes */
86 #include "MEM_guardedalloc.h"
87
88 #include "DNA_listBase.h"
89
90 #include "BLI_listbase.h"
91 #include "BLI_linklist.h"
92 #include "BLI_fileops.h"
93
94 #include "BLI_fileops_types.h"
95 #include "BLI_string.h"
96 #include "BLI_fileops.h"
97
98 #include "BKE_utildefines.h"
99
100 /* vars: */
101 static int totnum,actnum;
102 static struct direntry *files;
103
104 static struct ListBase dirbase_={NULL, NULL};
105 static struct ListBase *dirbase = &dirbase_;
106
107 /* can return NULL when the size is not big enough */
108 char *BLI_current_working_dir(char *dir, const int maxncpy)
109 {
110         const char *pwd= getenv("PWD");
111         if (pwd) {
112                 BLI_strncpy(dir, pwd, maxncpy);
113                 return dir;
114         }
115
116         return getcwd(dir, maxncpy);
117 }
118
119
120 static int bli_compare(struct direntry *entry1, struct direntry *entry2)
121 {
122         /* type is equal to stat.st_mode */
123
124         if (S_ISDIR(entry1->type)) {
125                 if (S_ISDIR(entry2->type)==0) return (-1);
126         }
127         else {
128                 if (S_ISDIR(entry2->type)) return (1);
129         }
130         if (S_ISREG(entry1->type)) {
131                 if (S_ISREG(entry2->type)==0) return (-1);
132         }
133         else {
134                 if (S_ISREG(entry2->type)) return (1);
135         }
136         if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return (-1);
137         if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return (1);
138         
139         /* make sure "." and ".." are always first */
140         if ( strcmp(entry1->relname, ".")==0 ) return (-1);
141         if ( strcmp(entry2->relname, ".")==0 ) return (1);
142         if ( strcmp(entry1->relname, "..")==0 ) return (-1);
143         if ( strcmp(entry2->relname, "..")==0 ) return (1);
144
145         return (BLI_natstrcmp(entry1->relname,entry2->relname));
146 }
147
148
149 double BLI_dir_free_space(const char *dir)
150 {
151 #ifdef WIN32
152         DWORD sectorspc, bytesps, freec, clusters;
153         char tmp[4];
154         
155         tmp[0]='\\'; tmp[1]=0; /* Just a failsafe */
156         if (dir[0]=='/' || dir[0]=='\\') {
157                 tmp[0]='\\';
158                 tmp[1]=0;
159         }
160         else if (dir[1]==':') {
161                 tmp[0]=dir[0];
162                 tmp[1]=':';
163                 tmp[2]='\\';
164                 tmp[3]=0;
165         }
166
167         GetDiskFreeSpace(tmp,&sectorspc, &bytesps, &freec, &clusters);
168
169         return (double) (freec*bytesps*sectorspc);
170 #else
171
172 #if defined (__sun__) || defined (__sun) || defined (__NetBSD__)
173         struct statvfs disk;
174 #else
175         struct statfs disk;
176 #endif
177         char name[FILE_MAXDIR],*slash;
178         int len = strlen(dir);
179         
180         if (len >= FILE_MAXDIR) /* path too long */
181                 return -1;
182         
183         strcpy(name,dir);
184
185         if (len) {
186                 slash = strrchr(name,'/');
187                 if (slash) slash[1] = 0;
188         }
189         else strcpy(name,"/");
190
191 #if defined (__FreeBSD__) || defined (linux) || defined (__OpenBSD__) || defined (__APPLE__) || defined(__GNU__) || defined(__GLIBC__)
192         if (statfs(name, &disk)) return(-1);
193 #endif
194
195 #if defined (__sun__) || defined (__sun) || defined (__NetBSD__)
196         if (statvfs(name, &disk)) return(-1);   
197 #elif !defined(__FreeBSD__) && !defined(linux) && (defined(__sparc) || defined(__sparc__))
198         /* WARNING - This may not be supported by geeneric unix os's - Campbell */
199         if (statfs(name, &disk, sizeof(struct statfs), 0)) return(-1);
200 #endif
201
202         return ( ((double) disk.f_bsize) * ((double) disk.f_bfree));
203 #endif
204 }
205
206 static void bli_builddir(const char *dirname, const char *relname)
207 {
208         struct dirent *fname;
209         struct dirlink *dlink;
210         int rellen, newnum = 0;
211         char buf[256];
212         DIR *dir;
213
214         BLI_strncpy(buf, relname, sizeof(buf));
215         rellen=strlen(relname);
216
217         if (rellen) {
218                 buf[rellen]='/';
219                 rellen++;
220         }
221 #ifndef WIN32
222         if (chdir(dirname) == -1) {
223                 perror(dirname);
224                 return;
225         }
226 #else
227         UTF16_ENCODE(dirname)
228         if (!SetCurrentDirectoryW(dirname_16)) {
229                 perror(dirname);
230                 free(dirname_16);
231                 return;
232         }
233         UTF16_UN_ENCODE(dirname)
234
235 #endif
236         if ( (dir = (DIR *)opendir(".")) ) {
237                 while ((fname = (struct dirent*) readdir(dir)) != NULL) {
238                         dlink = (struct dirlink *)malloc(sizeof(struct dirlink));
239                         if (dlink) {
240                                 BLI_strncpy(buf + rellen ,fname->d_name, sizeof(buf) - rellen);
241                                 dlink->name = BLI_strdup(buf);
242                                 BLI_addhead(dirbase,dlink);
243                                 newnum++;
244                         }
245                 }
246                 
247                 if (newnum) {
248
249                         if (files) {
250                                 void *tmp= realloc(files, (totnum+newnum) * sizeof(struct direntry));
251                                 if (tmp) {
252                                         files= (struct direntry *)tmp;
253                                 }
254                                 else { /* realloc fail */
255                                         free(files);
256                                         files= NULL;
257                                 }
258                         }
259                         
260                         if (files==NULL)
261                                 files=(struct direntry *)malloc(newnum * sizeof(struct direntry));
262
263                         if (files) {
264                                 dlink = (struct dirlink *) dirbase->first;
265                                 while(dlink) {
266                                         memset(&files[actnum], 0 , sizeof(struct direntry));
267                                         files[actnum].relname = dlink->name;
268                                         files[actnum].path = BLI_strdupcat(dirname, dlink->name);
269 // use 64 bit file size, only needed for WIN32 and WIN64. 
270 // Excluding other than current MSVC compiler until able to test
271 #ifdef WIN32
272                                         {wchar_t * name_16 = alloc_utf16_from_8(dlink->name,0);
273 #if (defined(WIN32) || defined(WIN64)) && (_MSC_VER>=1500)
274                                         _wstat64(name_16,&files[actnum].s);
275 #elif defined(__MINGW32__)
276                                         _stati64(dlink->name,&files[actnum].s);
277 #endif
278                                         free(name_16);};
279
280 #else
281                                         stat(dlink->name,&files[actnum].s);
282 #endif
283                                         files[actnum].type=files[actnum].s.st_mode;
284                                         files[actnum].flags = 0;
285                                         totnum++;
286                                         actnum++;
287                                         dlink = dlink->next;
288                                 }
289                         }
290                         else {
291                                 printf("Couldn't get memory for dir\n");
292                                 exit(1);
293                         }
294
295                         BLI_freelist(dirbase);
296                         if (files) qsort(files, actnum, sizeof(struct direntry), (int (*)(const void *,const void*))bli_compare);
297                 }
298                 else {
299                         printf("%s empty directory\n",dirname);
300                 }
301
302                 closedir(dir);
303         }
304         else {
305                 printf("%s non-existant directory\n",dirname);
306         }
307 }
308
309 static void bli_adddirstrings(void)
310 {
311         char datum[100];
312         char buf[512];
313         char size[250];
314         static const char * types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
315         int num, mode;
316 #ifdef WIN32
317         __int64 st_size;
318 #else
319         off_t st_size;
320 #endif
321         
322         struct direntry * file;
323         struct tm *tm;
324         time_t zero= 0;
325         
326         for (num=0, file= files; num<actnum; num++, file++) {
327 #ifdef WIN32
328                 mode = 0;
329                 BLI_strncpy(file->mode1, types[0], sizeof(file->mode1));
330                 BLI_strncpy(file->mode2, types[0], sizeof(file->mode2));
331                 BLI_strncpy(file->mode3, types[0], sizeof(file->mode3));
332 #else
333                 mode = file->s.st_mode;
334
335                 BLI_strncpy(file->mode1, types[(mode & 0700) >> 6], sizeof(file->mode1));
336                 BLI_strncpy(file->mode2, types[(mode & 0070) >> 3], sizeof(file->mode2));
337                 BLI_strncpy(file->mode3, types[(mode & 0007)], sizeof(file->mode3));
338                 
339                 if (((mode & S_ISGID) == S_ISGID) && (file->mode2[2]=='-'))file->mode2[2]='l';
340
341                 if (mode & (S_ISUID | S_ISGID)) {
342                         if (file->mode1[2]=='x') file->mode1[2]='s';
343                         else file->mode1[2]='S';
344
345                         if (file->mode2[2]=='x')file->mode2[2]='s';
346                 }
347
348                 if (mode & S_ISVTX) {
349                         if (file->mode3[2] == 'x') file->mode3[2] = 't';
350                         else file->mode3[2] = 'T';
351                 }
352 #endif
353
354 #ifdef WIN32
355                 strcpy(file->owner,"user");
356 #else
357                 {
358                         struct passwd *pwuser;
359                         pwuser = getpwuid(file->s.st_uid);
360                         if ( pwuser ) {
361                                 BLI_strncpy(file->owner, pwuser->pw_name, sizeof(file->owner));
362                         }
363                         else {
364                                 BLI_snprintf(file->owner, sizeof(file->owner), "%d", file->s.st_uid);
365                         }
366                 }
367 #endif
368
369                 tm= localtime(&file->s.st_mtime);
370                 // prevent impossible dates in windows
371                 if (tm==NULL) tm= localtime(&zero);
372                 strftime(file->time, sizeof(file->time), "%H:%M", tm);
373                 strftime(file->date, sizeof(file->date), "%d-%b-%y", tm);
374
375                 /*
376                  * Seems st_size is signed 32-bit value in *nix and Windows.  This
377                  * will buy us some time until files get bigger than 4GB or until
378                  * everyone starts using __USE_FILE_OFFSET64 or equivalent.
379                  */
380                 st_size= file->s.st_size;
381
382                 if (st_size > 1024*1024*1024) {
383                         BLI_snprintf(file->size, sizeof(file->size), "%.2f GB", ((double)st_size)/(1024*1024*1024));
384                 }
385                 else if (st_size > 1024*1024) {
386                         BLI_snprintf(file->size, sizeof(file->size), "%.1f MB", ((double)st_size)/(1024*1024));
387                 }
388                 else if (st_size > 1024) {
389                         BLI_snprintf(file->size, sizeof(file->size), "%d KB", (int)(st_size/1024));
390                 }
391                 else {
392                         BLI_snprintf(file->size, sizeof(file->size), "%d B", (int)st_size);
393                 }
394
395                 strftime(datum, 32, "%d-%b-%y %H:%M", tm); /* XXX, is this used? - campbell */
396
397                 if (st_size < 1000) {
398                         BLI_snprintf(size, sizeof(size), "%10d",
399                                      (int) st_size);
400                 }
401                 else if (st_size < 1000 * 1000) {
402                         BLI_snprintf(size, sizeof(size), "%6d %03d",
403                                      (int) (st_size / 1000), (int) (st_size % 1000));
404                 }
405                 else if (st_size < 100 * 1000 * 1000) {
406                         BLI_snprintf(size, sizeof(size), "%2d %03d %03d",
407                                      (int) (st_size / (1000 * 1000)), (int) ((st_size / 1000) % 1000), (int) ( st_size % 1000));
408                 }
409                 else {
410                         /* XXX, whats going on here?. 2x calls - campbell */
411                         BLI_snprintf(size, sizeof(size), "> %4.1f M", (double) (st_size / (1024.0 * 1024.0)));
412                         BLI_snprintf(size, sizeof(size), "%10d", (int) st_size);
413                 }
414
415                 BLI_snprintf(buf, sizeof(buf), "%s %s %s %7s %s %s %10s %s",
416                              file->mode1, file->mode2, file->mode3, file->owner,
417                              file->date, file->time, size, file->relname);
418
419                 file->string = BLI_strdup(buf);
420         }
421 }
422
423 unsigned int BLI_dir_contents(const char *dirname,  struct direntry **filelist)
424 {
425         // reset global variables
426         // memory stored in files is free()'d in
427         // filesel.c:freefilelist()
428
429         actnum = totnum = 0;
430         files = NULL;
431
432         bli_builddir(dirname,"");
433         bli_adddirstrings();
434
435         if (files) {
436                 *(filelist) = files;
437         }
438         else {
439                 // keep blender happy. Blender stores this in a variable
440                 // where 0 has special meaning.....
441                 *(filelist) = files = malloc(sizeof(struct direntry));
442         }
443
444         return(actnum);
445 }
446
447
448 size_t BLI_file_descriptor_size(int file)
449 {
450         struct stat buf;
451
452         if (file <= 0) return (-1);
453         fstat(file, &buf);//CHANGE
454         return (buf.st_size);
455 }
456
457 size_t BLI_file_size(const char *path)
458 {
459         int size, file = BLI_open(path, O_BINARY|O_RDONLY, 0);
460         
461         if (file == -1)
462                 return -1;
463         
464         size = BLI_file_descriptor_size(file);
465         close(file);
466         return size;
467 }
468
469
470 int BLI_exists(const char *name)
471 {
472 #if defined(WIN32) 
473 #ifndef __MINGW32__
474         struct _stat64i32 st;
475 #else
476         struct _stati64 st;
477 #endif
478         /*  in Windows stat doesn't recognize dir ending on a slash 
479                 To not break code where the ending slash is expected we
480                 don't mess with the argument name directly here - elubie */
481         wchar_t * tmp_16 = alloc_utf16_from_8(name, 0);
482         int len, res;
483         len = wcslen(tmp_16);
484         if (len > 3 && ( tmp_16[len-1]==L'\\' || tmp_16[len-1]==L'/') ) tmp_16[len-1] = '\0';
485 #ifndef __MINGW32__
486         res = _wstat(tmp_16, &st);
487 #else
488         res = _wstati64(tmp_16, &st);
489 #endif
490         free(tmp_16);
491         if (res == -1) return(0);
492 #else
493         struct stat st;
494         if (stat(name,&st)) return(0);  
495 #endif
496         return(st.st_mode);
497 }
498
499 /* would be better in fileops.c except that it needs stat.h so add here */
500 int BLI_is_dir(const char *file)
501 {
502         return S_ISDIR(BLI_exists(file));
503 }
504
505 int BLI_is_file(const char *path)
506 {
507         int mode= BLI_exists(path);
508         return (mode && !S_ISDIR(mode));
509 }
510
511 LinkNode *BLI_file_read_as_lines(const char *name)
512 {
513         FILE *fp= BLI_fopen(name, "r");
514         LinkNode *lines= NULL;
515         char *buf;
516         size_t size;
517
518         if (!fp) return NULL;
519                 
520         fseek(fp, 0, SEEK_END);
521         size= (size_t)ftell(fp);
522         fseek(fp, 0, SEEK_SET);
523
524         buf= MEM_mallocN(size, "file_as_lines");
525         if (buf) {
526                 size_t i, last= 0;
527                 
528                         /* 
529                          * size = because on win32 reading
530                          * all the bytes in the file will return
531                          * less bytes because of crnl changes.
532                          */
533                 size= fread(buf, 1, size, fp);
534                 for (i=0; i<=size; i++) {
535                         if (i==size || buf[i]=='\n') {
536                                 char *line= BLI_strdupn(&buf[last], i-last);
537
538                                 BLI_linklist_prepend(&lines, line);
539                                 last= i+1;
540                         }
541                 }
542                 
543                 MEM_freeN(buf);
544         }
545         
546         fclose(fp);
547         
548         BLI_linklist_reverse(&lines);
549         return lines;
550 }
551
552 void BLI_file_free_lines(LinkNode *lines)
553 {
554         BLI_linklist_free(lines, (void(*)(void*)) MEM_freeN);
555 }
556
557 int BLI_file_older(const char *file1, const char *file2)
558 {
559 #if WIN32
560         struct _stat st1, st2;
561
562         UTF16_ENCODE(file1)
563         UTF16_ENCODE(file2)
564         
565         if (_wstat(file1_16, &st1)) return 0;
566         if (_wstat(file2_16, &st2)) return 0;
567
568         UTF16_UN_ENCODE(file2)
569         UTF16_UN_ENCODE(file1)
570 #else
571         struct stat st1, st2;
572
573         if (stat(file1, &st1)) return 0;
574         if (stat(file2, &st2)) return 0;
575 #endif
576         return (st1.st_mtime < st2.st_mtime);
577 }
578