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