FreeWRL / FreeX3D 4.3.0
io_files.c
1//[s release];
2/*
3
4 FreeWRL support library.
5 IO with files.
6
7*/
8
9/****************************************************************************
10 This file is part of the FreeWRL/FreeX3D Distribution.
11
12 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
13
14 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
15 it under the terms of the GNU Lesser Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
26****************************************************************************/
27
28#include <config.h>
29
30#include <system.h>
31#include <display.h>
32#include <internal.h>
33#include <libFreeWRL.h>
34#include <resources.h>
35
36#include <list.h> /* internal use only */
37#include <io_files.h>
38#include <io_http.h>
39
40#include <sys/stat.h>
41
42#include <threads.h> /* for freewrlSystem */
43
44#if HAVE_DIRENT_H
45# include <dirent.h>
46#endif
47
48#ifndef _MSC_VER
49#include <sys/mman.h> /* mmap */
50#else
51#include <direct.h> //for getcwd
52#define getcwd _getcwd
53#define mkdir _mkdir
54#endif
55#include <limits.h> /* SSIZE_MAX */
56
57#include "main/ProdCon.h"
58#if !defined(IPHONE) && !defined(_ANDROID)
59#include "input/InputFunctions.h"
60#include "plugin/pluginUtils.h"
61#include "plugin/PluginSocket.h"
62#endif
63#include <stdio.h>
64#include <fcntl.h>
65#if defined (INCLUDE_STL_FILES)
66#include "input/convertSTL.h"
67#endif //INCLUDE_STL_FILES
68
69#define UNUSED(v) ((void) v)
70
71/* Internal function prototypes */
72void append_openned_file(s_list_t *list, const char *filename, int fd, char *text);
73
74int inputFileType = IS_TYPE_UNKNOWN;
75int inputFileVersion[3] = {0,0,0};
76
77
78int fw_mkdir(const char* path){
79#ifdef _MSC_VER
80 return mkdir(path);
81#else
82 return mkdir(path,0755);
83#endif
84}
85
89char* concat_path(const char *a, const char *b)
90{
91 size_t la, lb;
92 char *tmp;
93
94 if (!a) {
95 if (!b) return NULL;
96 /* returns "/b" */
97 lb = strlen(b);
98 tmp = MALLOC(char *, 2+lb); /* why 2? room for the slash and the trailing NULL */
99 sprintf(tmp, "/%s", b);
100 return tmp;
101 } else {
102 if (!b) {
103 /* returns "a/" */
104 la = strlen(a);
105 tmp = MALLOC(char *, la+2); /* why 2? room for the slash and the trailing NULL */
106 sprintf(tmp, "%s/", a);
107 return tmp;
108 }
109 }
110
111 la = strlen(a);
112 lb = strlen(b);
113
114 if (a[la-1] == '/') {
115 tmp = MALLOC(char *, la + lb + 1); /* why 1? room for the trailing NULL */
116 sprintf(tmp, "%s%s", a, b);
117 } else {
118 tmp = MALLOC(char *, la + lb + 2); /* why 2? room for the slash and the trailing NULL */
119 sprintf(tmp, "%s/%s", a, b);
120 }
121
122 return tmp;
123}
124
128char* remove_filename_from_path(const char *path)
129{
130 char *rv = NULL;
131 char *slash;
132
133 slash = strrchr(path, '/');
134 if (slash) {
135#ifdef DEBUG_MALLOC
136printf ("remove_filename_from_path going to copy %d\n", ((int)slash-(int)path)+1);
137 rv = strndup(path, ((int)slash-(int)path)+1);
138 rv = STRDUP(path);
139 slash = strrchr(rv,'/');
140 *slash = '\0';
141printf ("remove_filename_from_path, returning :%s:\n",rv);
142#else
143 rv = STRNDUP(path, (size_t)slash - (size_t)path + 1);
144#endif
145
146 }
147 return rv;
148}
149char *strBackslash2fore(char *str)
150{
151#ifdef _MSC_VER
152 int jj;
153 for( jj=0;jj<(int)strlen(str);jj++)
154 if(str[jj] == '\\' ) str[jj] = '/';
155#endif
156 return str;
157}
158
159char *get_current_dir()
160{
161 char *cwd , *retvar;
162 cwd = MALLOC(char *, PATH_MAX);
163 retvar = getcwd(cwd, PATH_MAX);
164 if (NULL != retvar) {
165 size_t ll;
166 ll = strlen(cwd);
167 cwd = strBackslash2fore(cwd);
168 cwd[ll] = '/'; /* put / ending to match posix version which puts local file name on end*/
169 cwd[ll+1] = '\0';
170 } else {
171 printf("Unable to establish current working directory in %s,%d errno=%d",__FILE__,__LINE__,errno) ;
172 FREE_IF_NZ(cwd);
173 cwd = STRDUP("./"); // "/tmp/";
174 }
175 return cwd;
176}
177
178/*
179 NOTES: temp dir
180
181 tmp_dir=/tmp/freewrl-YYYY-MM-DD-$PID/<main_world>/ must then
182 add <relative path> at the end.
183
184 input request: url "tex.jpg" => $tmp_dir/tex.jpg
185 url "images/tex.jpg" => create images subdir, => $tmp_dir/images/tex.jpg
186*/
187
188
192bool do_file_exists(const char *filename)
193{
194 struct stat ss;
195 if (stat(filename, &ss) == 0) {
196 return TRUE;
197 }
198 return FALSE;
199}
200
204bool do_file_readable(const char *filename)
205{
206 if (access(filename, R_OK) == 0) {
207 return TRUE;
208 }
209 return FALSE;
210}
211
212
216bool do_dir_exists(const char *dir)
217{
218 struct stat ss;
219
220#if defined(_MSC_VER)
221 /* TODO: Remove any trailing backslash from *dir */
222#endif
223
224 if (stat(dir, &ss) == 0) {
225 if (access(dir,X_OK) == 0) {
226 return TRUE;
227 } else {
228 WARN_MSG("directory '%s' exists but is not accessible\n", dir);
229 }
230 }
231 return FALSE;
232}
233
234
238void of_dump(openned_file_t *of)
239{
240 static char first_ten[11];
241 if (of->fileData) {
242 int len = of->fileDataSize;
243 if (len>10)len=10;
244 memcpy(first_ten, of->fileData, len);
245 }
246 printf("{%s, %d, %d, %s%s}\n", of->fileFileName, of->fileDescriptor, of->fileDataSize, (of->fileData ? first_ten : "(null)"), (of->fileData ? "..." : ""));
247}
248
254static openned_file_t* create_openned_file(const char *filename, int fd, int dataSize, char *data, int imageHeight, int imageWidth, bool imageAlpha)
255{
256 openned_file_t *of;
257#ifdef DISABLER
258 char *fileData = NULL;
259 if (dataSize > 0 && data)
260 {
261 fileData = MALLOC (char *, dataSize+1);
262 if (NULL != fileData)
263 {
264 memcpy (fileData, data, dataSize);
265 fileData[dataSize] = '\0';
266 data = fileData;
267 }
268 }
269#endif
270 of = XALLOC(openned_file_t);
271 of->fileFileName = filename;
272 of->fileDescriptor = fd;
273 of->fileData = data; // XXXX FREE_IF_NZ this after use.
274 of->fileDataSize = dataSize;
275 of->imageHeight = imageHeight;
276 of->imageWidth = imageWidth;
277 of->imageAlpha = imageAlpha;
278 //printf ("create_openned_file, datasize %d file %s\n",dataSize,filename);
279 //if (dataSize <4000) printf ("create_openned_file, stringlen of data %ld\n",strlen(data));
280 return of;
281}
282
283
284
288#if defined(FW_USE_MMAP)
289static void* load_file_mmap(const char *filename)
290{
291 struct stat ss;
292 char *text;
293 int fd;
294
295 if (stat(filename, &ss) < 0) {
296 PERROR_MSG("load_file_mmap: could not stat: %s\n", filename);
297 return NULL;
298 }
299 fd = open(filename, O_RDONLY | O_NONBLOCK);
300 if (fd < 0) {
301 PERROR_MSG("load_file_mmap: could not open: %s\n", filename);
302 return NULL;
303 }
304 if (!ss.st_size) {
305 ERROR_MSG("load_file_mmap: file is empty %s\n", filename);
306 close(fd);
307 return NULL;
308 }
309 text = mmap(NULL, ss.st_size, PROT_READ, MAP_SHARED, fd, 0);
310 if ((text == MAP_FAILED) || (!text)) {
311 PERROR_MSG("load_file_mmap: could not mmap: %s\n", filename);
312 close(fd);
313 return NULL;
314 }
315 return create_openned_file(filename, fd, text,0,0,FALSE);
316}
317#endif
318
319
323 int load_file_blob(const char *filename, char **blob, int *len){
324 struct stat ss;
325 int fd;
326 char *text, *current;
327 int left2read; //need signed int for math below
328#ifdef _MSC_VER
329 size_t blocksz, readsz; //, left2read;
330#else
331 ssize_t blocksz, readsz; //, left2read;
332#endif
333
334 if (stat(filename, &ss) < 0) {
335 PERROR_MSG("load_file_read: could not stat: %s\n", filename);
336 return 0;
337 }
338#ifdef _MSC_VER
339 fd = open(filename, O_RDONLY | O_BINARY);
340#else
341 fd = open(filename, O_RDONLY | O_NONBLOCK);
342#endif
343 if (fd < 0) {
344 PERROR_MSG("load_file_read: could not open: %s\n", filename);
345 return 0;
346 }
347 if (!ss.st_size) {
348 ERROR_MSG("load_file_read: file is empty %s\n", filename);
349 close(fd);
350 return 0;
351 }
352
353 text = current = MALLOC(char *, ss.st_size +1); /* include space for a null terminating character */
354 if (!text) {
355 ERROR_MSG("load_file_read: cannot allocate memory to read file %s\n", filename);
356 close(fd);
357 return 0;
358 }
359
360 if (ss.st_size > SSIZE_MAX) {
361 /* file is greater that read's max block size: we must make a loop */
362 blocksz = SSIZE_MAX;
363 } else {
364 blocksz = ss.st_size+1;
365 }
366
367 left2read = ss.st_size; //+1;
368 readsz = 0;
369
370 while (left2read > 0) {
371 readsz = read(fd, current, blocksz);
372 if (readsz > 0) {
373 /* ok, we have read a block, continue */
374 current += blocksz;
375 left2read -= blocksz;
376 } else {
377 /* is this the end of the file ? */
378 if (readsz == 0) {
379 /* yes */
380 break;
381 } else {
382 /* error */
383 PERROR_MSG("load_file_read: error reading file %s\n", filename);
384 /* cleanup */
385 FREE(text);
386 close(fd);
387 return 0;
388 }
389 }
390 }
391 /* null terminate this string */
392 text[ss.st_size] = '\0';
393 close(fd);
394 fd = 0; //NULL;
395 *blob = text;
396 *len = ss.st_size+1;
397 return 1;
398}
399static openned_file_t* load_file_read(const char *filename)
400{
401 char *blob;
402 int len;
403 openned_file_t *retval = NULL;
404 if( load_file_blob(filename, &blob, &len))
405 {
406 retval = create_openned_file(filename, 0, len, blob,0,0,FALSE);
407 }
408 return retval;
409}
410#ifdef OLDCODE
411OLDCODEstatic openned_file_t* load_file_read_old(const char *filename)
412OLDCODE{
413OLDCODE struct stat ss;
414OLDCODE int fd;
415OLDCODE unsigned char *text, *current;
416OLDCODE int left2read; //need signed int for math below
417OLDCODE#ifdef _MSC_VER
418OLDCODE size_t blocksz, readsz; //, left2read;
419OLDCODE#else
420OLDCODE ssize_t blocksz, readsz; //, left2read;
421OLDCODE#endif
422OLDCODE
423OLDCODE if (stat(filename, &ss) < 0) {
424OLDCODE PERROR_MSG("load_file_read: could not stat: %s\n", filename);
425OLDCODE return NULL;
426OLDCODE }
427OLDCODE#ifdef _MSC_VER
428OLDCODE fd = open(filename, O_RDONLY | O_BINARY);
429OLDCODE#else
430OLDCODE fd = open(filename, O_RDONLY | O_NONBLOCK);
431OLDCODE#endif
432OLDCODE if (fd < 0) {
433OLDCODE PERROR_MSG("load_file_read: could not open: %s\n", filename);
434OLDCODE return NULL;
435OLDCODE }
436OLDCODE if (!ss.st_size) {
437OLDCODE ERROR_MSG("load_file_read: file is empty %s\n", filename);
438OLDCODE close(fd);
439OLDCODE return NULL;
440OLDCODE }
441OLDCODE
442OLDCODE text = current = MALLOC(unsigned char *, ss.st_size +1); /* include space for a null terminating character */
443OLDCODE if (!text) {
444OLDCODE ERROR_MSG("load_file_read: cannot allocate memory to read file %s\n", filename);
445OLDCODE close(fd);
446OLDCODE return NULL;
447OLDCODE }
448OLDCODE
449OLDCODE if (ss.st_size > SSIZE_MAX) {
450OLDCODE /* file is greater that read's max block size: we must make a loop */
451OLDCODE blocksz = SSIZE_MAX;
452OLDCODE } else {
453OLDCODE blocksz = ss.st_size+1;
454OLDCODE }
455OLDCODE
456OLDCODE left2read = ss.st_size; //+1;
457OLDCODE readsz = 0;
458OLDCODE
459OLDCODE while (left2read > 0) {
460OLDCODE readsz = read(fd, current, blocksz);
461OLDCODE if (readsz > 0) {
462OLDCODE /* ok, we have read a block, continue */
463OLDCODE current += blocksz;
464OLDCODE left2read -= blocksz;
465OLDCODE } else {
466OLDCODE /* is this the end of the file ? */
467OLDCODE if (readsz == 0) {
468OLDCODE /* yes */
469OLDCODE break;
470OLDCODE } else {
471OLDCODE /* error */
472OLDCODE PERROR_MSG("load_file_read: error reading file %s\n", filename);
473OLDCODE /* cleanup */
474OLDCODE FREE(text);
475OLDCODE close(fd);
476OLDCODE return NULL;
477OLDCODE }
478OLDCODE }
479OLDCODE }
480OLDCODE /* null terminate this string */
481OLDCODE text[ss.st_size] = '\0';
482OLDCODE close(fd);
483OLDCODE fd = 0; //NULL;
484OLDCODE return create_openned_file(filename, fd, ss.st_size+1, text,0,0,FALSE);
485OLDCODE}
486#endif //OLDCODE
487
488
489
490
491char *fwg_frontEndWantsFileName() {return NULL;}
492void fwg_frontEndReturningData(char* fileData,int length,int width,int height,bool hasAlpha) {}
493
494
498openned_file_t* load_file(const char *filename)
499{
500 openned_file_t *of;
501 if (NULL == filename) {
502 return NULL;
503 }
504
505 of = NULL;
506
507
508
509
510
511 DEBUG_RES("loading file: %s pthread %p\n", filename,pthread_self());
512 //printf ("load_file, fileToGet %s, load_file %s thread %ld\n",fileToGet,filename,pthread_self());
513
514
515#if defined(FW_USE_MMAP)
516#if !defined(_MSC_VER)
517 /* UNIX mmap */
518 of = load_file_mmap(filename);
519#else
520 /* Windows CreateFileMapping / MapViewOfFile */
521 of = load_file_win32_mmap(filename);
522#endif
523#else
524 /* Standard read */
525 of = load_file_read(filename);
526#endif
527 DEBUG_RES("%s loading status: %s\n", filename, BOOL_STR((of!=NULL)));
528 return of;
529
530}
531
532
536int determineFileType(const char *buffer, const int len)
537{
538 const char *rv;
539 int count;
540 int foundStart = FALSE;
541
542 for (count = 0; count < 3; count ++) inputFileVersion[count] = 0;
543
544 /* is this an XML file? see also further down for < detection*/
545 if (strncmp((const char*)buffer,"<?xml version",12) == 0){
546 rv = buffer;
547
548 /* skip past the header; we will look for lines like:
549 <?xml version="1.0" encoding="UTF-8"?>
550 <!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "http://www.web3d.org/specifications/x3d-3.0.dtd">
551 <X3D ... version='3.3' ...>
552 */
553 rv++;
554 while (!foundStart) {
555 while ((*rv != '<') && (*rv != '\0')) rv++;
556 if (*rv == '<') {
557 rv++;
558 if (*rv != '!') foundStart = TRUE;
559 } else if (*rv == '\0') foundStart = TRUE;
560 }
561 if (strncmp((const char*)rv,"X3D",3) == 0) {
562 /* the full version number will be found by the parser */
563 inputFileVersion[0] = 3;
564 return IS_TYPE_XML_X3D;
565 }
566
567#if defined (INCLUDE_NON_WEB3D_FORMATS)
568 if (strncmp((const char*)rv,"COLLADA",7) == 0) {
569 return IS_TYPE_COLLADA;
570 }
571 if (strncmp((const char*)rv,"kml",3) == 0) {
572 return IS_TYPE_KML;
573 }
574#endif //INCLUDE_NON_WEB3D_FORMATS
575
576 } else {
577 //.wrl
578 if (strncmp((const char*)buffer,"#VRML V2.0 utf8",15) == 0) {
579 inputFileVersion[0] = 2;
580 return IS_TYPE_VRML;
581 }
582 //.x3dv
583 if (strncmp ((const char*)buffer, "#X3D",4) == 0) {
584 inputFileVersion[0] = 3;
585 /* ok, have X3D here, what version? */
586
587 if (strncmp ((const char*)buffer,"#X3D V3.0 utf8",14) == 0) {
588 inputFileVersion[1] = 0;
589 return IS_TYPE_VRML;
590 }
591 if (strncmp ((const char*)buffer,"#X3D V3.1 utf8",14) == 0) {
592 inputFileVersion[1] = 1;
593 return IS_TYPE_VRML;
594 }
595 if (strncmp ((const char*)buffer,"#X3D V3.2 utf8",14) == 0) {
596 inputFileVersion[1] = 2;
597 return IS_TYPE_VRML;
598 }
599 if (strncmp ((const char*)buffer,"#X3D V3.3 utf8",14) == 0) {
600 inputFileVersion[1] = 3;
601 return IS_TYPE_VRML;
602 }
603 if (strncmp ((const char*)buffer,"#X3D V4.0 utf8",14) == 0) {
604 inputFileVersion[0] = 4;
605 inputFileVersion[1] = 0;
606 return IS_TYPE_VRML;
607 }
608 /* if we fall off the end, we just assume X3D 3.0 */
609 }
610
611 /* VRML V1? */
612 if (strncmp((const char*)buffer,"#VRML V1.0 asc",10) == 0) {
613 return IS_TYPE_VRML1;
614 }
615
616
617 }
618 /* try simple x3d ie when its a partial string from createX3DfromString */
619 rv = buffer;
620 while(rv && *rv != '\0'){
621 if(*rv == '<') return IS_TYPE_XML_X3D;
622 if (*rv == '{') return IS_TYPE_VRML;
623 rv++;
624 }
625
626 #if defined (INCLUDE_STL_FILES)
627 return stlDTFT((const unsigned char*)buffer,len);
628 #endif //INCLUDE_STL_FILES
629
630 return IS_TYPE_UNKNOWN;
631}
632
633/*
634 * FIXME: what are the possible return codes for this function ???
635 *
636 * FIXME: refactor this function, too :)
637 *
638 */
639#if !defined( _MSC_VER) && !defined(_ANDROID) && !defined(ANDROIDNDK) && !defined(IOS)
640int freewrlSystem (const char *sysline)
641{
642
643//#ifdef _MSC_VER
644// return system(sysline);
645//#else
646#define MAXEXECPARAMS 10
647#define EXECBUFSIZE 2000
648 char *paramline[MAXEXECPARAMS];
649 char buf[EXECBUFSIZE];
650 char *internbuf;
651 int count;
652 /* pid_t childProcess[lastchildProcess]; */
653 pid_t child;
654 int pidStatus;
655 int waitForChild;
656 int haveXmessage;
657
658
659 /* initialize the paramline... */
660 memset(paramline, 0, sizeof(paramline));
661
662 waitForChild = TRUE;
663 haveXmessage = !strncmp(sysline, FREEWRL_MESSAGE_WRAPPER, strlen(FREEWRL_MESSAGE_WRAPPER));
664
665 internbuf = buf;
666
667 /* bounds check */
668 if (strlen(sysline)>=EXECBUFSIZE) return FALSE;
669 strcpy (buf,sysline);
670
671 /* printf ("freewrlSystem, have %s here\n",internbuf); */
672 count = 0;
673
674 /* do we have a console message - (which is text with spaces) */
675 if (haveXmessage) {
676 paramline[0] = FREEWRL_MESSAGE_WRAPPER;
677 paramline[1] = strchr(internbuf,' ');
678 count = 2;
679 } else {
680 /* split the command off of internbuf, for execing. */
681 while (internbuf != NULL) {
682 /* printf ("freewrlSystem: looping, count is %d\n",count); */
683 paramline[count] = internbuf;
684 internbuf = strchr(internbuf,' ');
685 if (internbuf != NULL) {
686 /* printf ("freewrlSystem: more strings here! :%s:\n",internbuf); */
687 *internbuf = '\0';
688 /* printf ("param %d is :%s:\n",count,paramline[count]); */
689 internbuf++;
690 count ++;
691 if (count >= MAXEXECPARAMS) return -1; /* never...*/
692 }
693 }
694 }
695
696 /* printf ("freewrlSystem: finished while loop, count %d\n",count);
697
698 { int xx;
699 for (xx=0; xx<MAXEXECPARAMS;xx++) {
700 printf ("item %d is :%s:\n",xx,paramline[xx]);
701 }} */
702
703 if (haveXmessage) {
704 waitForChild = FALSE;
705 } else {
706 /* is the last string "&"? if so, we don't need to wait around */
707 if (strncmp(paramline[count],"&",strlen(paramline[count])) == 0) {
708 waitForChild=FALSE;
709 paramline[count] = '\0'; /* remove the ampersand.*/
710 }
711 }
712
713 if (count > 0) {
714/* switch (childProcess[lastchildProcess]=fork()) { */
715 child = fork();
716 switch (child) {
717 case -1:
718 perror ("fork");
719 exit(1);
720 break;
721
722 case 0:
723 {
724 int Xrv;
725
726 /* child process */
727 /* printf ("freewrlSystem: child execing, pid %d %d\n",childProcess[lastchildProcess], getpid()); */
728 Xrv = execl((const char *)paramline[0],
729 (const char *)paramline[0],paramline[1], paramline[2],
730 paramline[3],paramline[4],paramline[5],
731 paramline[6],paramline[7], NULL);
732 printf ("FreeWRL: Fatal problem execing %s\n",paramline[0]);
733 perror("FreeWRL: ");
734 exit (Xrv);
735 }
736 break;
737
738 default:
739 {
740 /* parent process */
741 /* printf ("freewrlSystem: parent waiting for child %d\n",childProcess[lastchildProcess]); */
742
743 /* do we have to wait around? */
744 if (!waitForChild) {
745 /* printf ("freewrlSystem - do not have to wait around\n"); */
746 return TRUE;
747 }
748/* waitpid (childProcess[lastchildProcess],&pidStatus,0); */
749 waitpid(child, &pidStatus, 0);
750
751 /* printf ("freewrlSystem: parent - child finished - pidStatus %d \n",
752 pidStatus); */
753
754 /* printf ("freewrlSystem: WIFEXITED is %d\n",WIFEXITED(pidStatus)); */
755
756 /* if (WIFEXITED(pidStatus) == TRUE) printf ("returned ok\n"); else printf ("problem with return\n"); */
757 }
758 }
759 return (WIFEXITED(pidStatus) == TRUE);
760 } else {
761 printf ("System call failed :%s:\n",sysline);
762 }
763 return -1; /* should we return FALSE or -1 ??? */
764//#endif
765}
766#elif defined(_MSC_VER)
767int freewrlSystem (const char *sysline)
768{
769 return system(sysline);
770}
771#endif
772//goal: remove a directory and its contents - used for removing the temp unzip folder for .z3z / .zip file processing
773#ifdef _MSC_VER
774//http://msdn.microsoft.com/en-us/windows/desktop/aa365488
775#include <TCHAR.H>
776#ifdef UNICODE
777static TCHAR *singleDot = L".";
778static TCHAR *doubleDot = L"..";
779static TCHAR *backslash = L"\\";
780static TCHAR *star = L"*";
781
782#else
783static TCHAR *singleDot = ".";
784static TCHAR *doubleDot = "..";
785static TCHAR *backslash = "\\";
786static TCHAR *star = "*";
787#endif
788
789// http://www.codeproject.com/Articles/9089/Deleting-a-directory-along-with-sub-folders
790BOOL IsDots(const TCHAR* str) {
791 if(_tcscmp(str,singleDot) && _tcscmp(str,doubleDot))
792 return FALSE;
793 return TRUE;
794}
795BOOL DeleteDirectory0(const TCHAR* sPath) {
796 HANDLE hFind; // file handle
797 WIN32_FIND_DATA FindFileData;
798 TCHAR DirPath[MAX_PATH];
799 TCHAR FileName[MAX_PATH];
800 BOOL bSearch;
801
802 _tcscpy(DirPath,sPath);
803 _tcscat(DirPath,backslash); // searching all files
804 _tcscat(DirPath,star);
805 _tcscpy(FileName,sPath);
806 _tcscat(FileName,backslash);
807
808#if _MSC_VER > 1500
809 hFind = FindFirstFileEx(DirPath, FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0); // find the first file - requires windows XP or later
810#else
811 //
812 hFind = FindFirstFile(DirPath,&FindFileData); // find the first file
813#endif
814 if(hFind == INVALID_HANDLE_VALUE)
815 return FALSE;
816 _tcscpy(DirPath,FileName);
817
818 bSearch = TRUE;
819 while(bSearch) { // until we finds an entry
820 if(FindNextFile(hFind,&FindFileData)) {
821 if(IsDots(FindFileData.cFileName)) continue;
822 _tcscat(FileName,FindFileData.cFileName);
823 if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
824 // we have found a directory, recurse
825 if(!DeleteDirectory0(FileName)) {
826 FindClose(hFind);
827 return FALSE; // directory couldn't be deleted
828 }
829 RemoveDirectory(FileName); // remove the empty directory
830 _tcscpy(FileName,DirPath);
831 }
832 else {
833 if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
834 _tchmod(FileName, 777); //_S_IWRITE); // change read-only file mode
835 if(!DeleteFile(FileName)) { // delete the file
836 /*
837 DWORD err = GetLastError();
838 if (err == ERROR_FILE_NOT_FOUND)
839 printf("file not found\n");
840 else if (err == ERROR_ACCESS_DENIED)
841 printf("access denied\n");
842 else if (err == ERROR_SHARING_VIOLATION)
843 printf("sharing violation\n");
844 else
845 printf("other erro\n");
846 */
847 FindClose(hFind);
848 return FALSE;
849 }
850 _tcscpy(FileName,DirPath);
851 }
852 }
853 else {
854 if(GetLastError() == ERROR_NO_MORE_FILES) // no more files there
855 bSearch = FALSE;
856 else {
857 // some error occured, close the handle and return FALSE
858 FindClose(hFind);
859 return FALSE;
860 }
861 }
862 }
863 FindClose(hFind); // closing file handle
864 return RemoveDirectory(sPath); // remove the empty directory
865}
866
867BOOL tdirectory_remove_all(TCHAR *sPath){
868 BOOL retval;
869 retval = DeleteDirectory0(sPath);
870 return retval;
871}
872void tremove_file_or_folder(TCHAR *path){
873 int isDir; //iret,
874 DWORD finfo; //, err;
875#if _MSC_VER > 1500
876 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364946(v=vs.85).aspx
877 WIN32_FILE_ATTRIBUTE_DATA fad;
878 finfo = GetFileAttributesEx(path, GetFileExInfoStandard, &fad);
879 if (!finfo){
880 DWORD err;
881 err = GetLastError();
882 //FormatMessage()
883 ConsoleMessage("GetFileAttribuesEx err=%d maxpath%d pathlen%d", (int)err,MAX_PATH,_tcslen(path)); //http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
884 isDir = ! _tcsstr(path, singleDot);
885 return;
886 }else
887 isDir = finfo && (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
888#else
889 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364944%28v=vs.85%29.aspx
890 // http://msdn.microsoft.com/en-us/library/windows/desktop/gg258117%28v=vs.85%29.aspx
891 finfo = GetFileAttributes(path);
892 isDir = FILE_ATTRIBUTE_DIRECTORY & finfo;
893#endif
894 if(isDir)
895 tdirectory_remove_all(path);
896 else
897 DeleteFile(path);
898}
899void remove_file_or_folder(const char *path){
900 //libfreewrl uses ascii or multibyte string functions, like strcpy, that look for a '\0' as end of string
901 //when sending something into freewrl thats 2-byte wide string, first convert it to multibyte
902 //when coming out, if you want to go back to wide-string then you need to convert to wide string
903 //tchar functions are supposed to be agnostic -they compile either way
904 int jj;
905 size_t convertedChars = 0;
906 TCHAR wcstring[MAX_PATH];
907 char fname2[MAX_PATH];
908 size_t origsize; //= strlen(fname) + 1;
909 //BOOL retval;
910 origsize = strlen(path) + 1;
911 strcpy(fname2,path);
912 for(jj=0;jj<(int)strlen(fname2);jj++)
913 if(fname2[jj] == '/' ) fname2[jj] = '\\';
914
915#ifdef _UNICODE
916#if _MSC_VER >= 1500
917 mbstowcs_s(&convertedChars, wcstring, origsize, fname2, _TRUNCATE);
918#else
919 mbstowcs(wcstring, fname2, MB_CUR_MAX);
920#endif
921#else
922 _tcscpy(wcstring,fname2);
923#endif
924 tremove_file_or_folder(wcstring);
925}
926#else // POSIX and OSX - WARNING UNTESTED as of Sept 7, 2013
927//according to boost, unlike posix OSX must do separate rmdir for directory and unlink for file
928//goal: remove a directory and its contents - used for removing the temp unzip folder for .z3z / .zip file processing
929int directory_remove_all(const char *path)
930{
931 DIR *d = opendir(path);
932 size_t path_len = strlen(path);
933 int r = -1;
934
935 if (d)
936 {
937 struct dirent *p;
938 r = 0;
939
940 while (!r && (p=readdir(d)))
941 {
942 int r2 = -1;
943 char *buf;
944 size_t len;
945
946 /* Skip the names "." and ".." as we don't want to recurse on them. */
947 if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
948 {
949 continue;
950 }
951 len = path_len + strlen(p->d_name) + 2;
952 buf = MALLOC(void *, len);
953 if (buf)
954 {
955 struct stat statbuf;
956 snprintf(buf, len, "%s/%s", path, p->d_name);
957 if (!stat(buf, &statbuf))
958 {
959 if (S_ISDIR(statbuf.st_mode))
960 {
961 r2 = directory_remove_all(buf);
962 }
963 else
964 {
965 r2 = unlink(buf);
966 }
967 }
968 FREE(buf);
969 }
970 r = r2;
971 }
972 closedir(d);
973 }
974 if (!r)
975 {
976 r = rmdir(path);
977 }
978 return r;
979}
980void remove_file_or_folder(const char * path){
981 struct stat statbuf;
982 if (!stat(path, &statbuf))
983 {
984 int r2;
985 UNUSED (r2);
986
987 if (S_ISDIR(statbuf.st_mode))
988 {
989 r2 = directory_remove_all(path);
990 }
991 else
992 {
993 r2 = unlink(path);
994 }
995 }
996}
997#endif
998
999
1000
1001//could maybe be in a separate C file?
1002#ifdef HAVE_UNZIP_H
1003#include <unzip.h>
1004#define WRITEBUFFERSIZE (8192)
1005
1006
1007int unzip_archive_to_temp_folder(const char *zipfilename, const char* tempfolderpath)
1008{
1009
1010 const char *filename_to_extract=NULL;
1011 int ret_value=0;
1012 const char *dirname=NULL;
1013 char temppath[256];
1014 char *fullpath = NULL;
1015 unzFile uf=NULL;
1016
1017 uf = unzOpen(zipfilename);
1018 if (uf==NULL)
1019 {
1020 printf("Cannot open %s \n",zipfilename);
1021 return 1;
1022 }
1023 printf("%s opened\n",zipfilename);
1024 temppath[0] = '\0';
1025 if(tempfolderpath){
1026 fw_mkdir(tempfolderpath);
1027 }
1028
1029 {
1030 uLong i;
1031 unz_global_info gi;
1032 int err;
1033 FILE* fout=NULL;
1034
1035 err = unzGetGlobalInfo(uf,&gi);
1036 if (err!=UNZ_OK){
1037 printf("error %d with zipfile in unzGetGlobalInfo \n",err);
1038 return err;
1039 }
1040
1041 for (i=0;i<gi.number_entry;i++)
1042 {
1043 {
1044 char filename_inzip[256];
1045 char* filename_withoutpath;
1046 char* p;
1047 int err=UNZ_OK;
1048 FILE *fout=NULL;
1049 void* buf;
1050 uInt size_buf;
1051
1052 unz_file_info file_info;
1053 uLong ratio=0;
1054 err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
1055
1056 if (err!=UNZ_OK)
1057 {
1058 printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
1059 return err;
1060 }
1061
1062 size_buf = WRITEBUFFERSIZE;
1063 buf = (void*)MALLOC(void *, size_buf);
1064 if (buf==NULL)
1065 {
1066 printf("Error allocating memory\n");
1067 return UNZ_INTERNALERROR;
1068 }
1069
1070 p = filename_withoutpath = filename_inzip;
1071 while ((*p) != '\0')
1072 {
1073 if (((*p)=='/') || ((*p)=='\\'))
1074 filename_withoutpath = p+1;
1075 p++;
1076 }
1077
1078 if ((*filename_withoutpath)=='\0')
1079 {
1080 printf("creating directory: %s\n",filename_inzip);
1081 strcpy(temppath,tempfolderpath);
1082 strcat(temppath,"/");
1083 strcat(temppath,filename_inzip);
1084 //fw_mkdir(filename_inzip);
1085 fw_mkdir(temppath);
1086 }
1087 else
1088 {
1089 const char* write_filename;
1090 int skip=0;
1091
1092 write_filename = filename_inzip;
1093
1094 err = unzOpenCurrentFile(uf);
1095 if (err!=UNZ_OK)
1096 {
1097 printf("error %d with zipfile in unzOpenCurrentFile\n",err);
1098 }
1099
1100 if (err==UNZ_OK)
1101 {
1102 strcpy(temppath,tempfolderpath);
1103 strcat(temppath,"/");
1104 strcat(temppath,write_filename);
1105 //fout=fopen(write_filename,"wb");
1106 fout=fopen(temppath,"wb");
1107 }
1108
1109 if (fout!=NULL)
1110 {
1111 printf(" extracting: %s\n",write_filename);
1112
1113 do
1114 {
1115 err = unzReadCurrentFile(uf,buf,size_buf);
1116 if (err<0)
1117 {
1118 printf("error %d with zipfile in unzReadCurrentFile\n",err);
1119 break;
1120 }
1121 if (err>0)
1122 if (fwrite(buf,err,1,fout)!=1)
1123 {
1124 printf("error in writing extracted file\n");
1125 err=UNZ_ERRNO;
1126 break;
1127 }
1128 }
1129 while (err>0);
1130 if (fout)
1131 fclose(fout);
1132
1133 }
1134
1135 if (err==UNZ_OK)
1136 {
1137 err = unzCloseCurrentFile (uf);
1138 if (err!=UNZ_OK)
1139 {
1140 printf("error %d with zipfile in unzCloseCurrentFile\n",err);
1141 }
1142 }
1143 else
1144 unzCloseCurrentFile(uf); /* don't lose the error */
1145 }
1146
1147 FREE(buf);
1148 }
1149 if(err) break;
1150
1151 if ((i+1)<gi.number_entry)
1152 {
1153 err = unzGoToNextFile(uf);
1154 if (err!=UNZ_OK)
1155 {
1156 printf("error %d with zipfile in unzGoToNextFile\n",err);
1157 break;
1158 }
1159 }
1160 }
1161
1162 }
1163
1164 unzClose(uf);
1165 return ret_value;
1166}
1167
1168char* remove_filename_from_path(const char *path);
1169char *strBackslash2fore(char *str);
1170void resitem_enqueue(s_list_t *item);
1171void process_x3z(resource_item_t *res){
1172 int err;
1173 char request[256];
1174 char* tempfolderpath;
1175 if (1){
1176 tempfolderpath = TEMPNAM(gglobal()->Mainloop.tmpFileLocation, "freewrl_download_XXXXXXXX");
1177 }else{
1178 //for debugging if you need to have the temp unzip files in your working folder where your data files are
1179 tempfolderpath = STRDUP(res->URLrequest);
1180 tempfolderpath = strBackslash2fore(tempfolderpath);
1181 tempfolderpath = remove_filename_from_path(tempfolderpath);
1182 tempfolderpath = TEMPNAM(tempfolderpath, "freewrl_download_XXXXXXXX");
1183 }
1184 err = unzip_archive_to_temp_folder(res->actual_file, tempfolderpath);
1185 if(!err){
1186 resource_item_t *docx3d;
1187 //I need a resource just for cleaning up the temp folder in one shot
1188 strcpy(request,tempfolderpath);
1189 strcat(request,"/doc.x3d");
1190 docx3d = resource_create_single(request);
1191 docx3d->parent = NULL; //divorce so it doesn't inherit rest_url
1192 docx3d->type = rest_file;
1193 docx3d->media_type = resm_x3d;
1194 docx3d->treat_as_root = 1;
1195 //docx3d->temp_dir = tempfolderpath;
1196 resitem_enqueue(ml_new(docx3d));
1197 // clean up temp folder via resource with opennedfile entry
1198 res->cached_files = ml_append(res->cached_files,ml_new(tempfolderpath));
1199 ConsoleMessage("unzip folder:%s\n", tempfolderpath);
1200 }
1201 else{
1202 ConsoleMessage("unzip failed to folder:%s\n", tempfolderpath);
1203 }
1204}
1205
1206#else
1207void process_x3z(resource_item_t *res){
1208}
1209#endif
1210
1211
1212
1213enum {
1214 file2blob_task_chain,
1215 file2blob_task_spawn,
1216 file2blob_task_enqueue,
1217} file2blob_task_tactic2;
1218
1219void resource_remove_cached_file(s_list_t *cfe);
1220void delete_temp_file(resource_item_t *res){
1221 /*we delete a temp file immediately after it's loaded (ie after FILE2BLOB)
1222 (versus cleaning up on program exit. Bombing, killing and some configurations of mobile don't exit cleanly).
1223 stub this function if you want to see the temp files being created during a run.
1224 .x3z files need to hang around longer, for unzipping, and get cleaned up hopefully on exit.
1225 */
1226 s_list_t *cf;
1227 if(res->media_type != resm_x3z){
1228 cf = (s_list_t *)res->cached_files;
1229 if (cf) {
1230 ml_foreach(cf, resource_remove_cached_file(__l));
1231 //should clean up list items (but are contained strings constants/used elsewhere or strduped)
1232 ml_foreach(cf, ml_free(__l));
1233 res->cached_files = NULL;
1234 }
1235 }
1236}
1237
1238int file2blob(void *resp){
1239 resource_item_t *res;
1240 int retval;
1241
1242 res = (resource_item_t*)resp;
1243 if(res->media_type == resm_image){
1244#ifdef DISABLER
1245 printf("FREEWRL LOADING IMAGERY: %s", res->actual_file);
1246#endif
1247 retval = imagery_load(res); //FILE2TEXBLOB
1248 }else if(res->media_type == resm_movie){
1249 retval = movie_load(res);
1250 }else{
1251 retval = resource_load(res); //FILE2BLOB
1252 }
1253 delete_temp_file(res);
1254 return retval;
1255}
1256int async_thread_count = 0;
1257static void *thread_load_async (void *args){
1258 int loaded;
1259 resource_item_t *res = (resource_item_t *)args;
1260 async_thread_count++;
1261 printf("[%d]",async_thread_count);
1262 loaded = file2blob(res);
1263 //enqueue BLOB to BE
1264 if(loaded)
1265 resitem_enqueue_tg(ml_new(res),res->tg);
1266 async_thread_count--;
1267 return NULL;
1268}
1269void loadAsync (resource_item_t *res) {
1270 if(!res->_loadThread) res->_loadThread = malloc(sizeof(pthread_t));
1271 pthread_create ((pthread_t*)res->_loadThread, NULL,&thread_load_async, (void *)res);
1272}
1273void file2blob_task(s_list_t *item){
1274 //chain, spawn async/thread, or re-enqueue FILE2BLOB to some work thread
1275 resource_item_t *res = item->elem;
1276 int tactic = file2blob_task_enqueue; // linux>imlib2 likes to stay on same thread //file2blob_task_chain; //file2blob_task_spawn;
1277 if(tactic == file2blob_task_chain){
1278 //chain FILE2BLOB
1279 if(res->media_type == resm_image){
1280#ifdef DISABLER
1281 printf("FREEWRL LOADING IMAGERY: %s", res->actual_file);
1282#endif
1283 imagery_load(res); //FILE2TEXBLOB
1284 }else{
1285 resource_load(res); //FILE2BLOB
1286 }
1287 //enqueue BLOB to BE
1288 delete_temp_file(res);
1289 resitem_enqueue(item);
1290 }else if(tactic == file2blob_task_enqueue){
1291 //set BE load function to non-null
1292 //a) res->load_func = imagery_load or resource_load or file2blob
1293 res->_loadFunc = (int(*)(void*))file2blob; //msvc can also do &file2blob
1294 //b) backend_setloadfunction(file2blob) or backend_setimageryloadfunction(imagery_load) and backend_setresourceloadfunction(resource_load)
1295 //enqueue downloaded FILE
1296 resitem_enqueue(item);
1297 }else if(tactic == file2blob_task_spawn){
1298 //spawn thread
1299 loadAsync(res); //res already has res->tg with global context
1300 ml_free(item);
1301 }
1302}