FreeWRL/FreeX3D  3.0.0
resources.c
1 /*
2 
3  FreeWRL support library.
4  Resources handling: URL, files, ...
5 
6 */
7 
8 /****************************************************************************
9  This file is part of the FreeWRL/FreeX3D Distribution.
10 
11  Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
12 
13  FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
14  it under the terms of the GNU Lesser Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  FreeWRL/FreeX3D is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  GNU General Public License for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
25 ****************************************************************************/
26 
27 
28 
29 #include <config.h>
30 #include <system.h>
31 #include <system_threads.h>
32 #include <display.h>
33 #include <internal.h>
34 #include <libFreeWRL.h>
35 
36 #include "vrml_parser/Structs.h"
37 #include "input/InputFunctions.h"
38 #include "opengl/OpenGL_Utils.h"
39 #include "opengl/Textures.h" /* for finding a texture url in a multi url */
40 #include "opengl/LoadTextures.h" /* for finding a texture url in a multi url */
41 
42 #include <list.h>
43 #include <io_files.h>
44 #include <io_http.h>
45 #include <threads.h>
46 
47 #ifdef _ANDROID
48 #include <strings.h>
49 #endif
50 
51 #include "zlib.h"
52 //#define DEBUG_RES printf
53 static void possiblyUnzip (openned_file_t *of);
54 
55 void close_openned_file(openned_file_t *file);
56 
57 typedef struct presources{
58  struct Vector *resStack; //=NULL;
59  resource_item_t *lastBaseResource; //=NULL;
60 }* presources;
61 void *resources_constructor()
62 {
63  void *v = MALLOCV(sizeof(struct presources));
64  memset(v,0,sizeof(struct presources));
65  return v;
66 }
67 void resources_init(struct tresources* t)
68 {
69  //public
70  //private
71  //presources p;
72  t->prv = resources_constructor();
73  //p = (presources)t->prv);
74 }
75 void resources_clear(struct tresources* t)
76 {
77  //public
78  //private
79  presources p;
80  p = (presources)t->prv;
81  deleteVector(void *,p->resStack);
82 }
83 
84 
85 /* move Michel Briand's initialization code to one place to ensure consistency
86  when fields are added/removed */
87 
88 resource_item_t *newResourceItem() {
89  resource_item_t *item = XALLOC(resource_item_t);
90 
91  /* item is NULL for every byte; some of the enums might not work out to 0 */
92  item->media_type = resm_unknown;
93  item->type = rest_invalid;
94  item->status = ress_invalid;
95  item->parent = NULL;
96  item->actual_file = NULL;
97  item->cached_files = NULL;
98  item->tg = gglobal();
99  return item;
100 }
101 
102 
116 static void resource_tree_append(resource_item_t *item){
117  /* Lock access to the resource tree */
118  pthread_mutex_lock( &gglobal()->threads.mutex_resource_tree );
119 
120  if (!gglobal()->resources.root_res) {
121  /* This is the first resource we try to load */
122  gglobal()->resources.root_res = (void*)item;
123  DEBUG_RES("setting root_res in resource_create_single for file %s\n",request);
124  } else {
125  /* Not the first, so keep it in the main list */
126  ((resource_item_t*)gglobal()->resources.root_res)->children = ml_append(((resource_item_t*)gglobal()->resources.root_res)->children, ml_new(item));
127  item->parent = (resource_item_t*)gglobal()->resources.root_res;
128  }
129 
130  /* Unlock the resource tree mutex */
131  pthread_mutex_unlock( &gglobal()->threads.mutex_resource_tree );
132 }
133 
134 resource_item_t* resource_create_single0(const char *request)
135 {
136  resource_item_t *item;
137  DEBUG_RES("creating resource: SINGLE: %s\n", request);
138 
139  item = newResourceItem();
140  item->URLrequest = STRDUP(request);
141  item->_loadThread = NULL;
142  return item;
143 }
144 
145 resource_item_t* resource_create_single(const char *request)
146 {
147  resource_item_t *item = resource_create_single0(request);
148  resource_tree_append(item);
149  return item;
150 }
151 
157 resource_item_t* resource_create_multi0(s_Multi_String_t *request)
158 {
159  /* anchor to new scene might use the multi0 directly, so plugin_res isn't deleted in killOldWorld */
160  int i;
161  resource_item_t *item;
162  DEBUG_RES("creating resource: MULTI: %d, %s ...\n", request->n, request->p[0]->strptr);
163  item = newResourceItem();
164 
165 
166  item->type = rest_multi;
167 
168 
169  /* Convert Mutli_String to a list string */
170  for (i = 0; i < request->n; i++) {
171  char *url = STRDUP(request->p[i]->strptr);
172  //ConsoleMessage ("putting %s on the list\n",url);
173  item->m_request = ml_append(item->m_request, ml_new(url));
174  }
175  return item;
176 }
177 resource_item_t* resource_create_multi(s_Multi_String_t *request)
178 {
179  resource_item_t *item = resource_create_multi0(request);
180  resource_tree_append(item);
181  return item;
182 }
183 
189 resource_item_t* resource_create_from_string(const char *string)
190 {
191  resource_item_t *item;
192  DEBUG_RES("creating resource: STRING: %s\n", string);
193  item = newResourceItem();
194 
195 
196  item->URLrequest = STRDUP(string);
197  item->type = rest_string;
198  item->status = ress_loaded;
199 
200  resource_tree_append(item);
201  return item;
202 }
203 
204 
205 /*
206  * Check to see if the file name is a local file, or a network file.
207  * return TRUE if it looks like a file from the network, false if it
208  * is local to this machine
209  * October 2007 - Michel Briand suggested the https:// lines.
210  */
214 bool checkNetworkFile(const char *fn)
215 {
216  //int i = 0;
217  //char *pt = fn;
218 
219  if (fn == NULL) {
220  ConsoleMessage ("checkNetworkFile, got a NULL here");
221  return FALSE;
222  }
223 
224  // while (*pt != '\0') {
225  // ConsoleMessage ("cfn %d is %x %c",i,*pt,*pt);
226  // i++;
227  // pt++;
228  // }
229 
230  //ConsoleMessage ("checkNetworkFile, have %s, len %d\n",fn,strlen(fn));
231 
232  if ((strncmp(fn,"ftp://", strlen("ftp://"))) &&
233  (strncmp(fn,"FTP://", strlen("FTP://"))) &&
234  (strncmp(fn,"http://", strlen("http://"))) &&
235  (strncmp(fn,"HTTP://", strlen("HTTP://"))) &&
236  (strncmp(fn,"https://", strlen("https://"))) &&
237  (strncmp(fn,"HTTPS://", strlen("HTTPS://"))) &&
238 /* JAS - these really are local files | MB - indeed :^) !
239  (strncmp(fn,"file://", strlen("file://"))) &&
240  (strncmp(fn,"FILE://", strlen("FILE://"))) &&
241 */
242  (strncmp(fn,"urn://", strlen("urn://"))) &&
243  (strncmp(fn,"URN://", strlen("URN://")))
244 
245  ) {
246  //ConsoleMessage ("CNF returning FALSE");
247  return FALSE;
248  }
249  //ConsoleMessage ("CNF returning TRUE");
250  return TRUE;
251 }
252 
253 
274  static int res_id_error_once = 0;
275 void resource_identify(resource_item_t *baseResource, resource_item_t *res)
276 {
277  bool network;
278  char *url = NULL;
279  size_t len;
280  resource_item_t *defaults = NULL;
281 
282  ASSERT(res);
283  DEBUG_RES("resource_identify, we have resource %s ptrs %p and %p\n",res->URLrequest,baseResource,baseResource);
284 
285  if (baseResource) {
286  DEBUG_RES(" base specified, taking the base values.\n");
287  defaults = baseResource;
288  res->parent = baseResource;
289  } else {
290  if (res->parent) {
291  DEBUG_RES(" no base specified, taking parent's values.\n");
292  defaults = res->parent;
293  } else {
294  DEBUG_RES(" no base neither parent, no default values.\n");
295  }
296  }
297 
298  if (defaults) {
299  DEBUG_RES(" default values: network=%s type=%s status=%s"
300  " URLrequest=<%s> URLbase=<%s>parsed_request=<%s> [parent %p, %s]\n",
301  BOOL_STR(defaults->network), resourceTypeToString(defaults->type),
302  resourceStatusToString(defaults->status), defaults->URLrequest,
303  defaults->URLbase, defaults->parsed_request,
304  defaults->parent, (defaults->parent ? defaults->parent->URLbase : "N/A")
305  );
306  }
307 
308  if (res->type == rest_multi) {
309  /* We want to consume the list of requests */
310  if (res->m_request) {
311  s_list_t *l;
312  l = res->m_request;
313  /* Pick up next request in our list */
314  FREE_IF_NZ(res->URLrequest);
315  res->URLrequest = (char *) l->elem;
316  /* Point to the next... */
317  res->m_request = res->m_request->next;
318  ml_free(l);
319  } else {
320  /* list empty - this error can be caused by a wrong USE='name' on URL node */
321  if(!res_id_error_once) //don't flood, there's probably a better error message before this
322  ERROR_MSG("resource_identify: ERROR: empty multi string as input\n");
323  res_id_error_once++;
324  return;
325  }
326  }
327 
328  network = FALSE;
329  if (defaults) {
330  network = defaults->network;
331  }
332 
333  {
334  char* pound;
335  pound = NULL;
336  pound = strchr(res->URLrequest, '#'); //moved here Aug2014 Q. should it be later on strdup of URLrequest?
337  if (pound != NULL) {
338  *pound = '\0';
339  /* copy the name out, so that Anchors can go to correct Viewpoint */
340  pound++;
341  res->afterPoundCharacters = STRDUP(pound);
342  }
343  }
344  /* URI specifier at the beginning ? */
345  res->network = checkNetworkFile(res->URLrequest);
346 
347  DEBUG_RES("resource_identify: base network / resource network: %s/%s\n",
348  BOOL_STR(network),
349  BOOL_STR(res->network));
350 
351  /* Parse request as url or local file ? */
352  if (res->network || network) {
353  /* We will always have a network url */
354 
355  if (res->network) {
356  /* We have an absolute url for this resource */
357  res->type = rest_url;
358  res->status = ress_starts_good;
359  url = STRDUP(res->URLrequest);
360 
361  } else {
362  /* We have an absolute url for main world,
363  and a relative url for this resource:
364  Create an url with base+request */
365  if (defaults) {
366 
367  /* note that, if FRONTEND_GETS_FILES is defined, we have to clean
368  this, here. */
369 
370  char *cleanedURL;
371  cleanedURL = stripLocalFileName(res->URLrequest);
372 
373  /* Relative to base */
374  IF_cleanedURL_IS_ABSOLUTE {
375  /* this is an absolute url, which we can do, even if we have a base to
376  base this from. eg, url='/Users/john/tests/2.wrl' */
377  url = STRDUP(cleanedURL);
378  } else {
379  char *cwd;
380  cwd = STRDUP(defaults->URLbase);
381  url = concat_path(cwd, cleanedURL);
382  FREE_IF_NZ(cwd);
383  }
384  res->network = TRUE; //dug9 sep1,2013 added this line, so geoLod 2nd level texture sees its parent 2nd level .x3d as a network file
385  res->type = rest_url;
386  res->status = ress_starts_good;
387  } else {
388  res->type = rest_invalid;
389  ERROR_MSG("resource_identify: can't handle relative url without base: %s\n", res->URLrequest);
390  }
391  }
392 
393  } else {
394  /* We may have a local file */
395  DEBUG_RES("resource_identify, we may have a local file for resource %s\n", res->URLrequest);
396 
397  /* We do not want to have system error */
398  len = strlen(res->URLrequest);
399  if (len > PATH_MAX) {
400 
401  res->type = rest_invalid;
402  url="invalid URL";
403  ERROR_MSG("resource_identify: path too long: %s\n", res->URLrequest);
404 
405  } else {
406  char *cleanedURL = NULL;
407  /* remove any possible file:// off of the front of the name */
408  /* NOTE: this is NOT a new string, possibly just incremented res->request */
409 
410  cleanedURL = stripLocalFileName(res->URLrequest);
411 
412  /* We are relative to current dir or base */
413  if (defaults) {
414  /* Relative to base */
415  IF_cleanedURL_IS_ABSOLUTE {
416  /* this is an absolute url, which we can do, even if we have a base to
417  base this from. eg, url='/Users/john/tests/2.wrl' */
418  res->type = rest_file;
419  res->status = ress_starts_good;
420  url = STRDUP(cleanedURL);
421  } else {
422  char *cwd;
423  cwd = STRDUP(defaults->URLbase);
424  res->type = rest_file;
425  res->status = ress_starts_good;
426  url = concat_path(cwd, cleanedURL);
427  FREE_IF_NZ(cwd);
428  }
429 
430  } else {
431  /* No default values: we are hanging alone */
432  /* Is this a full path ? */
433  IF_cleanedURL_IS_ABSOLUTE {
434  /* This is an absolute filename */
435 
436  res->type = rest_file;
437  res->status = ress_starts_good;
438  url = STRDUP(cleanedURL);
439 
440  } else {
441  /* Relative to current dir (we are loading main file/world) */
442  char *cwd;
443 
444  cwd = get_current_dir();
445  removeFilenameFromPath(cwd);
446 
447  /* Make full path from current dir and relative filename */
448 
449  /* printf("about to join :%s: and :%s: resource.c L299\n",cwd,res->request);*/
450  url = concat_path(cwd, res->URLrequest);
451  res->type = rest_file;
452  res->status = ress_starts_good;
453  }
454  }
455  }
456  }
457 
458  /* record the url, and the path to the url */
459  FREE_IF_NZ(res->parsed_request);
460  res->parsed_request = url;
461  FREE_IF_NZ(res->URLbase);
462  res->URLbase = STRDUP(url);
463  removeFilenameFromPath(res->URLbase);
464 
465  // ok we should be good to go now res->network = TRUE;
466 
467  DEBUG_RES("resource_identify (end): network=%s type=%s status=%s"
468  " request=<%s> base=<%s> url=<%s> [parent %p, %s]\n",
469  BOOL_STR(res->network), resourceTypeToString(res->type),
470  resourceStatusToString(res->status), res->URLrequest,
471  res->URLbase, res->parsed_request,
472  res->parent, (res->parent ? res->parent->URLbase : "N/A"));
473  return;
474 }
475 textureTableIndexStruct_s *getTableIndex(int i);
476 bool imagery_load(resource_item_t *res){
477  bool retval;
478  int textureNumber;
479  struct textureTableIndexStruct *entry; // = res->whereToPlaceData;
480  textureNumber = res->textureNumber;
481  if(res->status == ress_downloaded){
482  entry = getTableIndex(textureNumber);
483  if(entry)
484  if (texture_load_from_file(entry, res->actual_file)) {
485  entry->status = TEX_READ; /* tell the texture thread to convert data to OpenGL-format */
486  res->status = ress_loaded;
487  retval = TRUE;
488  return retval;
489  }
490  }
491  res->status = ress_not_loaded;
492  retval = FALSE;
493  return retval;
494 }
495 
496 
500 bool resource_load(resource_item_t *res)
501 {
502  openned_file_t *of = NULL;
503 
504  DEBUG_RES("loading resource: %s, %s\n", resourceTypeToString(res->type), resourceStatusToString(res->status));
505 
506  ASSERT(res);
507 
508  switch (res->status) {
509  case ress_none:
510  case ress_starts_good:
511  case ress_invalid:
512  case ress_failed:
513  ERROR_MSG("resource_load: can't load not available resource: %s\n", res->URLrequest);
514  break;
515 
516 
517 
518  case ress_downloaded:
519  //if(1) printf("[%s]\n",res->parsed_request); //to print successfully downloaded urls
520  of = load_file(res->actual_file);
521 
522  if (of) {
523  res->status = ress_loaded;
524  //res->openned_files = ml_append( (s_list_t *) res->openned_files, ml_new(of) );
525  res->openned_files = of;
526 
527  /* If type is not specified by the caller try to identify it automatically */
528  if (res->media_type == resm_unknown) {
529  resource_identify_type(res);
530  }
531 
532  } else {
533 
534  res->status = ress_not_loaded;
535  ERROR_MSG("resource_load: can't load file: %s\n", res->actual_file);
536  }
537 
538  break;
539 
540  case ress_loaded:
541  ERROR_MSG("resource_load: MISTAKE: can't load already loaded resource: %s\n", res->URLrequest);
542  break;
543 
544  case ress_not_loaded:
545  ERROR_MSG("resource_load: loader already failed for this resource: %s\n", res->URLrequest);
546  break;
547 
548  case ress_parsed:
549  ERROR_MSG("resource_load: MISTAKE: can't load resource already parsed: %s\n", res->URLrequest);
550  break;
551 
552  case ress_not_parsed:
553  ERROR_MSG("resource_load: MISTAKE: can't load resource already parsed (and failed): %s\n", res->URLrequest);
554  break;
555  }
556 
557  return (of != NULL);
558 }
559 
563 void resource_identify_type(resource_item_t *res)
564 {
565  char *test_it = NULL;
566  int test_it_len = 0;
567 
568  //s_list_t *l;
569  openned_file_t *of;
570  int t;
571 
572  if (res->media_type != resm_unknown)
573  /* caller specified type, or we already identified it */
574  return;
575 
576  switch (res->status) {
577  case ress_loaded:
578  switch (res->type) {
579  case rest_invalid:
580  ERROR_MSG("can't identify type for invalid resource: %s\n", res->URLrequest);
581  return;
582  break;
583  case rest_string:
584  test_it = (char*)res->URLrequest;
585  ConsoleMessage ("test_it is :%s:",test_it);
586  test_it_len = (int)strlen(res->URLrequest);
587  break;
588  case rest_url:
589  case rest_file:
590  case rest_multi:
591  //l = (s_list_t *) res->openned_files;
592  //if (!l) {
593  // /* error */
594  // return;
595  //}
596  //
597  //of = ml_elem(l);
598  of = res->openned_files;
599  if (!of) {
600  /* error */
601  return;
602  }
603  /* maybe .x3z (.zip) archive? */
604  {
605  char *sourcename = (char *)of->fileFileName;
606  if(res->type == rest_url) sourcename = res->URLrequest;
607  if(!strcmp(&sourcename[strlen(sourcename)-4],".x3z")){
608  res->media_type = resm_x3z;
609  return;
610  }
611  }
612  /* might this be a gzipped input file? */
613  possiblyUnzip(of);
614  test_it = of->fileData;
615  test_it_len = of->fileDataSize;
616  break;
617  }
618 
619 
620  /* Test it */
621  t = determineFileType(test_it,test_it_len);
622  switch (t) {
623  case IS_TYPE_VRML:
624  case IS_TYPE_VRML1:
625 
626 #if defined (INCLUDE_STL_FILES)
627  case IS_TYPE_BINARY_STL: case IS_TYPE_ASCII_STL:
628 #endif //INCLUDE_STL_FILES
629 
630 
631  res->media_type = resm_vrml;
632  break;
633 
634 #if defined (INCLUDE_NON_WEB3D_FORMATS)
635  case IS_TYPE_COLLADA:
636  case IS_TYPE_KML:
637  case IS_TYPE_SKETCHUP:
638 #endif //INCLUDE_NON_WEB3D_FORMATS
639 
640  case IS_TYPE_XML_X3D:
641  res->media_type = resm_x3d;
642  break;
643  }
644  break;
645  default:
646  break;
647  }
648  return;
649 }
650 
651 
655 void remove_file_or_folder(const char *path);
656 
657 void resource_remove_cached_file(s_list_t *cfe)
658 {
659  const char *cached_file;
660  cached_file = (const char *) cfe->elem;
661  ASSERT(cached_file);
662  /* TODO: reference counter on cached files... */
663  remove_file_or_folder(cached_file);
664  //UNLINK(cached_file);
665 }
666 
672 void _resourceFreeCallback(void *resource);
673 
674 void resource_destroy(resource_item_t *res)
675 {
676  s_list_t *cf; // *of,
677 
678  if(!res) return;
679  DEBUG_RES("destroying resource: %d, %d\n", res->type, res->status);
680 
681  ASSERT(res);
682 
683  switch (res->type) {
684  case rest_invalid:
685  /* nothing to do */
686  break;
687  case rest_url:
688  switch (res->status) {
689  case ress_none:
690  case ress_starts_good:
691  case ress_invalid:
692  /* nothing to do */
693  break;
694 
695  case ress_downloaded:
696  case ress_failed:
697  case ress_loaded:
698  case ress_not_loaded:
699  case ress_parsed:
700  case ress_not_parsed:
701  if(0){
702  /* Remove openned file ? */
703  //of = (s_list_t *) res->openned_files;
704  //of = res->openned_files;
705  //if (of) {
706  // /* close any openned file */
707  // close( ((openned_file_t*)of->elem)->fileDescriptor );
708  //}
709 
710  /* Remove cached file ? */
711  cf = (s_list_t *) res->cached_files;
712  if (cf) {
713  /* remove any cached file:
714  TODO: reference counter on cached files...
715  */
716  ml_foreach(cf, resource_remove_cached_file(__l));
717  }
718  }
719  /* free the actual file */
720  FREE_IF_NZ(res->actual_file);
721  break;
722  }
723 
724  /* free the parsed_request url */
725  FREE_IF_NZ(res->parsed_request);
726  break;
727 
728  case rest_file:
729  switch (res->status) {
730  case ress_none:
731  case ress_starts_good:
732  case ress_invalid:
733  /* nothing to do */
734  break;
735 
736  case ress_downloaded:
737  case ress_failed:
738  case ress_loaded:
739  case ress_not_loaded:
740  case ress_parsed:
741  case ress_not_parsed:
742  /* Remove openned file ? */
743  //of = (s_list_t *) res->openned_files;
744  //if (of) {
745  // /* close any openned file */
746  //}
747 
748  /* free the actual file */
749  FREE(res->actual_file);
750  break;
751  }
752 
753  /* free the parsed_request url */
754  FREE_IF_NZ(res->parsed_request);
755  break;
756 
757  case rest_string:
758  /* Nothing to do */
759  break;
760 
761  case rest_multi:
762  /* JAS Apr 2017 - entry was not handled in case; do nothing? */
763  break;
764  }
765 
766  /* Free the list */
767  ml_delete_all2(res->m_request, (void (*)(void *))ml_free);
768  res->m_request = NULL;
769 
770  FREE_IF_NZ(res->URLbase);
771  FREE_IF_NZ(res->afterPoundCharacters);
772  FREE_IF_NZ(res->openned_files);
773  //if (!res->parent) {
774  // /* Remove base */
775  // FREE_IF_NZ(res->URLbase);
776  //} else {
777  // /* We used parent's base, so remove us from parent's childs */
778  // //resource_remove_child(res->parent, res);
779  //}
780 
781  FREE_IF_NZ(res->URLrequest);
782  FREE_IF_NZ(res);
783 }
784 
785 void resource_unlink_cachedfiles(resource_item_t *res)
786 {
787  s_list_t *cf;
788 
789  if(!res) return;
790  DEBUG_RES("destroying resource: %d, %d\n", res->type, res->status);
791 
792  ASSERT(res);
793 
794  /* Remove cached file ? */
795  cf = (s_list_t *) res->cached_files;
796  if (cf) {
797  /* remove any cached file:
798  TODO: reference counter on cached files...
799  */
800  ml_foreach(cf, resource_remove_cached_file(__l));
801  }
802 
803 }
804 
805 void resource_close_files(resource_item_t *res)
806 {
807 
808  if(!res) return;
809  DEBUG_RES("closing resource file: %d, %d\n", res->type, res->status);
810 
811  ASSERT(res);
812 
813  /* Remove openned file ? */
814 
815 }
816 
817 
821 void resource_remove_child(resource_item_t *parent, resource_item_t *child)
822 {
823  s_list_t *cf;
824 
825  ASSERT(parent);
826  ASSERT(child);
827 
828  //cf = ml_find_elem(parent->cached_files, child);
829  cf = ml_find_elem(parent->children, child);
830  if (cf) {
831  //ml_delete(parent->cached_files, cf);
832  ml_delete(parent->children, cf);
833  }
834 }
835 
839 void destroy_root_res()
840 {
841  resource_destroy((resource_item_t*)gglobal()->resources.root_res);
842  gglobal()->resources.root_res = NULL;
843 }
844 
845 void resource_tree_destroy()
846 {
847  resource_item_t* root;
848  root = (resource_item_t*)gglobal()->resources.root_res;
849  if(root){
850  ml_foreach(root->children,resource_close_files((resource_item_t*)ml_elem(__l)));
851  ml_foreach(root->children,resource_unlink_cachedfiles((resource_item_t*)ml_elem(__l)));
852  ml_foreach(root->children,resource_destroy((resource_item_t*)ml_elem(__l)));
853  ml_foreach(root->children,resource_remove_child(root,(resource_item_t*)ml_elem(__l)));
854  ml_foreach(root->children,ml_free(__l));
855  resource_close_files(root);
856  resource_unlink_cachedfiles(root);
857  destroy_root_res();
858  }
859 
860 }
864 void resource_dump(resource_item_t *res)
865 {
866  s_list_t *cf;
867  //openned_file_t *of;
868  //s_list_t *of;
869  void *ofv;
870 
871  PRINTF ("resource_dump: %p\n"
872  "request: %s\n"
873  "parsed request: %s\n"
874  "actual file: %s\n"
875  "cached files: ",
876  res, res->URLrequest, res->parsed_request, res->actual_file);
877 
878  cf = (s_list_t *) res->cached_files;
879  if (cf) {
880  ml_foreach(cf, PRINTF("%s ", (char *) ml_elem(__l)));
881  } else {
882  PRINTF("none");
883  }
884  PRINTF("\nopenned files: ");
885 
886  //of = (s_list_t *) res->openned_files;
887  ofv = res->openned_files;
888  if (ofv) {
889  openned_file_t *of = (openned_file_t*)ofv;
890  PRINTF("%s ", of->fileFileName);
891  } else {
892  PRINTF("none");
893  }
894  PRINTF("\n");
895 }
896 void splitpath_local_suffix(const char *url, char **local_name, char **suff);
900 void fwl_resource_push_single_request(const char *request)
901 {
902  resource_item_t *res;
903 
904  if (!request)
905  return;
906 
907  res = resource_create_single(request);
908  //send_resource_to_parser(res);
909  resitem_enqueue(ml_new(res));
910  if(request){
911  //update information about the scene for scripting (Q. what about window title?)
912  //not sure this is a good place, in part because the calling thread may not be in a gglobal thread
913  ttglobal tg = gglobal();
914  char* suff = NULL;
915  char* local_name = NULL;
916  splitpath_local_suffix(request, &local_name, &suff);
917  tg->Mainloop.scene_name = local_name;
918  tg->Mainloop.scene_suff = suff;
919  }
920 
921 }
922 
926 void resource_push_multi_request(struct Multi_String *request)
927 {
928  resource_item_t *res;
929 
930  if (!request)
931  return;
932 
933  res = resource_create_multi(request);
934  resitem_enqueue(ml_new(res));
935  //send_resource_to_parser(res);
936 }
937 
938 
943 void resource_tree_dump(int level, resource_item_t *root)
944 {
945 #define spacer for (lc=0; lc<level; lc++) printf ("\t");
946 
947  s_list_t *children;
948  int lc;
949 
950  if (root == NULL) return;
951  if (level == 0) printf("\nResource tree:\n\n");
952  else printf("\n");
953 
954  spacer printf("==> request:\t %s\n\n", root->URLrequest);
955  spacer printf("this:\t %p\n", root);
956  spacer printf("parent:\t %p\n", root->parent);
957  spacer printf("network:\t %s\n", BOOL_STR(root->network));
958  spacer printf("new_root:\t %s\n", BOOL_STR(root->new_root));
959  spacer printf("type:\t %u\n", root->type);
960  spacer printf("status:\t %u\n", root->status);
961  spacer printf("complete:\t %s\n", BOOL_STR(root->complete));
962  spacer printf("where:\t %p\n", root->whereToPlaceData);
963  spacer printf("offsetFromWhere:\t %d\n", root->offsetFromWhereToPlaceData);
964  spacer printf("m_request:\t %p\n", root->m_request);
965  spacer printf("base:\t %s\n", root->URLbase);
966  spacer printf("temp_dir:\t %s\n", root->temp_dir);
967  spacer printf("parsed_request:\t %s\n", root->parsed_request);
968  spacer printf("actual_file:\t %s\n", root->actual_file);
969  spacer printf("cached_files:\t %p\n", root->cached_files);
970  //if (root->openned_files) {
971  // spacer printf("openned_files:\t "); ml_foreach(root->openned_files, of_dump((openned_file_t *)ml_elem(__l)));
972  //} else {
973  // spacer printf("openned_files:\t <empty>\n");
974  //}
975  spacer printf("four_first_bytes:\t %c %c %c %c\n", root->four_first_bytes[0], root->four_first_bytes[1], root->four_first_bytes[2], root->four_first_bytes[3]);
976  spacer printf("media_type:\t %u\n", root->media_type);
977 
978  children = root->children;
979 
980  ml_foreach(children, resource_tree_dump(level + 1, ml_elem(__l)));
981 
982  printf("\n");
983 }
984 
985 void resource_tree_count_files(int *count, resource_item_t *root)
986 {
987  if (root == NULL) return;
988  (*count)++;
989  ml_foreach(root->children, resource_tree_count_files(count, ml_elem(__l)));
990 }
991 void printStatsResources()
992 {
993  int count = 0;
994  resource_tree_count_files(&count, gglobal()->resources.root_res);
995  ConsoleMessage("%25s %d\n","resource file count", count);
996 }
997 
1002 void resource_tree_list_files(int level, resource_item_t *root)
1003 {
1004 #define spacer for (lc=0; lc<level; lc++) printf ("\t");
1005  int lc;
1006 
1007  if (root == NULL) return;
1008  if (level == 0) printf("\nResource file list:\n");
1009 
1010  spacer printf("%s\n", root->actual_file);
1011  ml_foreach(root->children, resource_tree_list_files(-1, ml_elem(__l)));
1012 }
1013 
1014 char *resourceTypeToString(int type) {
1015  switch (type) {
1016  case rest_invalid: return "rest_invalid";
1017  case rest_url: return "rest_url";
1018  case rest_file: return "rest_file";
1019  case rest_multi: return "rest_multi";
1020  case rest_string : return "rest_string ";
1021  default: return "resource OUT OF RANGE";
1022  }
1023 }
1024 
1025 
1026 char *resourceStatusToString(int status) {
1027  switch (status) {
1028  case ress_none: return "ress_none";
1029  case ress_starts_good: return "ress_starts_good";
1030  case ress_invalid: return "ress_invalid";
1031  case ress_downloaded: return "ress_downloaded";
1032  case ress_failed: return "ress_failed";
1033  case ress_loaded: return "ress_loaded";
1034  case ress_not_loaded: return "ress_not_loaded";
1035  case ress_parsed: return "ress_parsed";
1036  case ress_not_parsed: return "ress_not_parsed";
1037  default: return "resource OUT OF RANGE";
1038  }
1039 }
1040 
1041 char *resourceMediaTypeToString (int mt) {
1042  switch (mt) {
1043  case resm_unknown: return " resm_unknown";
1044  case resm_vrml: return " resm_vrml";
1045  case resm_x3d: return " resm_x3d";
1046  case resm_image: return " resm_image";
1047  case resm_movie: return " resm_movie";
1048  case resm_pshader: return " resm_pshader";
1049  case resm_fshader: return " resm_fshader";
1050  case resm_x3z: return " resm_x3z";
1051  default: return "resource OUT OF RANGE";
1052  }
1053 }
1054 
1055 
1056 
1057 #define SLASHDOTDOTSLASH "/../"
1058 #if defined(_MSC_VER) || defined(_ANDROID) || defined(ANDROIDNDK)
1059 #define rindex strrchr
1060 #endif
1061 void removeFilenameFromPath (char *path) {
1062  char *slashindex;
1063  char *slashDotDotSlash;
1064 
1065  /* and strip off the file name from the current path, leaving any path */
1066  slashindex = (char *) rindex(path, ((int) '/'));
1067  if (slashindex != NULL) {
1068  /* slashindex ++; */ /* <msvc DO NOT> leave the slash there */
1069  *slashindex = 0;
1070  } else {path[0] = 0;}
1071  /* printf ("removeFielnameFromPath, parenturl is %s\n",path); */
1072 
1073  /* are there any "/../" bits in the path? if so, lets clean them up */
1074  slashDotDotSlash = strstr(path, SLASHDOTDOTSLASH);
1075  while (slashDotDotSlash != NULL) {
1076  char tmpline[2000];
1077  /* might have something like: _levels_plus/tiles/0/../1/../1/../2/../ */
1078  /* find the preceeding slash: */
1079  *slashDotDotSlash = '\0';
1080  /* printf ("have slashdotdot, path now :%s:\n",path); */
1081 
1082  slashindex = (char *)rindex(path, ((int) '/'));
1083  if (slashindex != NULL) {
1084 
1085  slashindex ++;
1086  *slashindex = '\0';
1087  slashDotDotSlash += strlen(SLASHDOTDOTSLASH);
1088  strcpy(tmpline,path);
1089  /* printf ("tmpline step 1 is :%s:\n",tmpline); */
1090  strcat (tmpline, slashDotDotSlash);
1091  /* printf ("tmpline step 2 is :%s:\n",tmpline); */
1092  strcpy (path, tmpline);
1093  slashDotDotSlash = strstr(path, SLASHDOTDOTSLASH);
1094  /* printf ("end of loop, path :%s: slashdot %u\n",path,slashDotDotSlash); */
1095 
1096 
1097  }
1098  }
1099 }
1100 
1101 
1102 /* is this a gzipped file? if so, unzip the text and replace the original with this. */
1103 static void possiblyUnzip (openned_file_t *of) {
1104 #if !(defined(IPHONE) || defined(_ANDROID))
1105  if (of->fileData == NULL) return;
1106  if (of->fileData[0] == '\0') return;
1107  if (of->fileData[1] == '\0') return;
1108  if (((unsigned char) of->fileData[0] == 0x1f) && ((unsigned char) of->fileData[1] == 0x8b)) {
1109  #define GZIP_BUFF_SIZE 2048
1110 
1111  gzFile source;
1112  FILE *dest;
1113  char buffer[GZIP_BUFF_SIZE];
1114  int num_read = 0;
1115  openned_file_t *newFile;
1116 
1117  char *tempname; // [1000];
1118 
1119  /* make a temporary name for the gunzipped file */
1120  // sprintf (tempname, "%s",tempnam(gglobal()->Mainloop.tmpFileLocation,"freewrl_tmp"));
1121  tempname = tempnam(gglobal()->Mainloop.tmpFileLocation, "freewrl_tmp");
1122 
1123  /* read in the text, unzip it, write it out again */
1124  source = gzopen(of->fileFileName,"rb");
1125  dest = fopen(tempname,"wb");
1126 
1127  if (!source || !source) {
1128  ConsoleMessage ("unable to unzip this file: %s\n",of->fileFileName);
1129  printf ("wow - problem\n");
1130  }
1131 
1132  while ((num_read = gzread(source, buffer, GZIP_BUFF_SIZE)) > 0) {
1133  fwrite(buffer, 1, num_read, dest);
1134  }
1135 
1136  gzclose(source);
1137  fclose(dest);
1138 
1139  /* read in the unzipped text... */
1140  newFile = load_file((const char *) tempname);
1141  UNLINK(tempname);
1142 
1143  if (newFile->fileData == NULL) {
1144  ConsoleMessage ("problem re-reading gunzipped text file");
1145  return;
1146  }
1147 
1148  /* replace the old text with the unzipped; and clean up */
1149  FREE_IF_NZ(of->fileData);
1150  of->fileData = newFile->fileData;
1151 /* seems odd that we wouldn't need to also update the fileDataSize, like so:
1152  of->fileDataSize = newFile->fileDataSize; */
1153  FREE_IF_NZ(newFile);
1154  unlink (tempname);
1155  }
1156 #endif
1157 }
1158 
1159 bool resource_is_root_loaded()
1160 {
1161  return ((gglobal()->resources.root_res != NULL) && (((resource_item_t*)gglobal()->resources.root_res)->status == ress_parsed));
1162 }
1163 
1170 /* keep the last base resource around, for times when we are making nodes during runtime, eg
1171  textures in Background nodes */
1172 
1173 void pushInputResource(resource_item_t *url)
1174 {
1175  presources p = gglobal()->resources.prv;
1176  DEBUG_MSG("pushInputResource current Resource is %s", url->parsed_request);
1177 
1178 
1179 
1180  /* push this one */
1181  if (p->resStack==NULL) {
1182  p->resStack = newStack (resource_item_t *);
1183  }
1184 
1185  /* is this an EAI/SAI request? If not, we don't push this one on the stack */
1186  /*
1187  if (url->parsed_request != NULL)
1188  if (strncmp(url->parsed_request,EAI_Flag,strlen(EAI_Flag)) == 0) {
1189  DEBUG_MSG("pushInputResource, from EAI, ignoring");
1190  return;
1191  }
1192 */
1193  stack_push (resource_item_t*, p->resStack, url);
1194  DEBUG_MSG("pushInputResource, after push, stack size %d",vectorSize(p->resStack));
1195 }
1196 
1197 void popInputResource() {
1198  resource_item_t *cwu;
1199  presources p = gglobal()->resources.prv;
1200 
1201  /* lets just keep this one around, to see if it is really the bottom of the stack */
1202  DEBUG_MSG("popInputResource, stack size %d",vectorSize(p->resStack));
1203 
1204  cwu = stack_top(resource_item_t *, p->resStack);
1205 
1206  /* pop the stack, and if we are at "nothing" keep the pointer to the last resource */
1207  stack_pop((resource_item_t *), p->resStack);
1208 
1209  if (stack_empty(p->resStack)) {
1210  DEBUG_MSG ("popInputResource, stack now empty and we have saved the last resource\n");
1211  p->lastBaseResource = cwu;
1212  } else {
1213  cwu = stack_top(resource_item_t *, p->resStack);
1214  DEBUG_MSG("popInputResource, cwu = %p",cwu);
1215  DEBUG_MSG("popInputResource before pop, current Resource is %s\n", cwu->parsed_request);
1216  }
1217 }
1218 
1219 resource_item_t *getInputResource()
1220 {
1221  resource_item_t *cwu;
1222  presources p = gglobal()->resources.prv;
1223 
1224 
1225  DEBUG_MSG("getInputResource \n");
1226  if (p->resStack==NULL) {
1227  DEBUG_MSG("getInputResource, stack NULL\n");
1228  return NULL;
1229  }
1230 
1231  /* maybe we are running, and are, say, making up background textures at runtime? */
1232  if (stack_empty(p->resStack)) {
1233  if (p->lastBaseResource == NULL) {
1234  ConsoleMessage ("stacking error - looking for input resource, but it is null");
1235  } else {
1236  DEBUG_MSG("so, returning %s\n",p->lastBaseResource->parsed_request);
1237  }
1238  return p->lastBaseResource;
1239  }
1240 
1241 
1242  cwu = stack_top(resource_item_t *, p->resStack);
1243  DEBUG_MSG("getInputResource current Resource is %lu %lx %s\n", (unsigned long int) cwu, (unsigned long int) cwu, cwu->parsed_request);
1244  return cwu;
1245 }
1246 
1247 //used by FEGF configs in frontend
1248 char* fwl_resitem_getURL(void *resp){
1249  resource_item_t *res = (resource_item_t *)resp;
1250  return res->parsed_request;
1251 }
1252 void fwl_resitem_setActualFile(void *resp, char *fname){
1253  resource_item_t *res = (resource_item_t *)resp;
1254  res->actual_file = STRDUP(fname);
1255  if(strcmp(res->actual_file,res->parsed_request)){
1256  //it's a temp file
1257  s_list_t *item;
1258  item = ml_new(res->actual_file);
1259  if (!res->cached_files)
1260  res->cached_files = (void *)item;
1261  else
1262  res->cached_files = ml_append(res->cached_files,item);
1263  }
1264 }
1265 char* fwl_resitem_getTempDir(void *resp){
1266  resource_item_t *res = (resource_item_t *)resp;
1267  return res->temp_dir;
1268 }
1269 void fwl_resitem_enqueuNextMulti(void *resp){
1270  resource_item_t *res = (resource_item_t *)resp;
1271  int more_multi = (res->status == ress_failed) && (res->m_request != NULL);
1272  if(more_multi){
1273  //still some hope via multi_string url, perhaps next one
1274  res->status = ress_invalid; //downgrade ress_fail to ress_invalid
1275  res->type = rest_multi; //should already be flagged
1276  //must consult BE to convert relativeURL to absoluteURL via baseURL
1277  //(or could we absolutize in a batch in resource_create_multi0()?)
1278  resource_identify(res->parent, res); //should increment multi pointer/iterator
1279  frontenditem_enqueue(ml_new(res));
1280  }
1281 }
1282 char *strBackslash2fore(char *);
1283 //int file2blob(resource_item_t *res);
1284 void fwl_resitem_setLocalPath(void *resp, char* path){
1285  int delete_after_load;
1286  resource_item_t *res = (resource_item_t *)resp;
1287  res->status = ress_downloaded;
1288  res->actual_file = strBackslash2fore(STRDUP(path));
1289  delete_after_load = 1;
1290  if (delete_after_load){
1291  //warning this will delete the actual_file setLocalPath is for downloaded/copied/cached files only,
1292  //not direct intranet files as with desktop.c
1293  s_list_t *item;
1294  item = ml_new(res->actual_file);
1295  if (!res->cached_files)
1296  res->cached_files = (void *)item;
1297  else
1298  res->cached_files = ml_append(res->cached_files, item);
1299  }
1300  res->_loadFunc = (void *)file2blob; //msvc can also do &file2blob
1301 }
1302 int fwl_resitem_getStatus(void *resp){
1303  resource_item_t *res = (resource_item_t *)resp;
1304  return res->status;
1305 }
1306 void fwl_resitem_setStatus(void *resp, int status) {
1307  resource_item_t *res = (resource_item_t *)resp;
1308  res->status = status;
1309 }
1310 
1311 int fwl_resitem_getType(void *resp){
1312  resource_item_t *res = (resource_item_t *)resp;
1313  return res->type;
1314 }
1315 int fwl_resitem_getMediaType(void *resp){
1316  resource_item_t *res = (resource_item_t *)resp;
1317  return res->media_type;
1318 }
1319 void fwl_resitem_setDownloadThread(void *resp, void *thread){
1320  resource_item_t *res = (resource_item_t *)resp;
1321  res->_loadThread = (pthread_t*)thread;
1322 }
1323 void * fwl_resitem_getDownloadThread(void *resp){
1324  resource_item_t *res = (resource_item_t *)resp;
1325  return res->_loadThread;
1326 }
1327 void * fwl_resitem_getGlobal(void *resp){
1328  resource_item_t *res = (resource_item_t *)resp;
1329  return res->tg;
1330 }
Definition: list.h:37
Definition: Vector.h:36