source: trunk/zoo-project/zoo-kernel/service.c @ 799

Last change on this file since 799 was 794, checked in by djay, 8 years ago

Add the initial C# language support.

  • Property svn:keywords set to Id
File size: 35.9 KB
Line 
1/*
2 * Author : Gérald FENOY
3 *
4 * Copyright (c) 2015 GeoLabs SARL
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include "service.h"
26
27
28#if defined(_MSC_VER) && _MSC_VER < 1800
29#include <stdarg.h>
30/**
31 * snprintf for Visual Studio compiler.
32 *
33 * See https://dxr.mozilla.org/mozilla-central/source/media/mtransport/third_party/nrappkit/src/util/util.c
34 */
35int snprintf(char *buffer, size_t n, const char *format, ...)
36{
37  va_list argp;
38  int ret;
39  va_start(argp, format);
40  ret = _vscprintf(format, argp);
41  vsnprintf_s(buffer, n, _TRUNCATE, format, argp);
42  va_end(argp);
43  return ret;
44}
45#endif
46
47/**
48 * Dump a map on stderr
49 *
50 * @param t the map to dump
51 */
52void _dumpMap(map* t){
53  if(t!=NULL){
54    fprintf(stderr,"%s: %s\n",t->name,t->value);
55    fflush(stderr);
56  }else{
57    fprintf(stderr,"NULL\n");
58    fflush(stderr);
59  }
60}
61
62/**
63 * Dump a map on stderr, see _dumpMap()
64 *
65 * @param t the map to dump
66 */
67void dumpMap(map* t){
68  map* tmp=t;
69  while(tmp!=NULL){
70    _dumpMap(tmp);
71    tmp=tmp->next;
72  }
73}
74
75/**
76 * Dump a map to a file
77 *
78 * @param t the map to dump to file
79 * @param file the file pointer to store the map
80 */
81void dumpMapToFile(map* t,FILE* file){
82  map* tmp=t;
83  while(tmp!=NULL){
84    fprintf(file,"%s = %s\n",tmp->name,tmp->value);
85    tmp=tmp->next;
86  }
87}
88
89/**
90 * Dump a maps on stderr, see dumpMap().
91 *
92 * @param m the map to dump
93 */
94void dumpMaps(maps* m){
95  maps* tmp=m;
96  while(tmp!=NULL){
97    fprintf(stderr,"MAP => [%s] \n",tmp->name);
98    fprintf(stderr," * CONTENT [%s] \n",tmp->name);
99    dumpMap(tmp->content);
100    fprintf(stderr," * CHILD [%s] \n",tmp->name);
101    dumpMaps(tmp->child);
102    tmp=tmp->next;
103  }
104}
105
106/**
107 * Dump a maps to a file, see dumpMapToFile().
108 *
109 * @param m the map to dump
110 * @param file the the file pointer to store the map
111 */
112void _dumpMapsToFile(maps* m,FILE* file,int limit){
113  maps* tmp=m;
114  int cnt=0;
115  while(tmp!=NULL){
116    fprintf(file,"[%s]\n",tmp->name);
117    if(tmp->child!=NULL){
118      _dumpMapsToFile(tmp->child,file,limit);
119    }else
120      dumpMapToFile(tmp->content,file);
121    fflush(file);
122    tmp=tmp->next;
123    cnt++;
124    if(limit>=0 && cnt==limit)
125      tmp=NULL;
126  }
127  fflush(file);
128}
129
130/**
131 * Dump a maps to a file, see _dumpMapsToFile().
132 *
133 * @param m the map to dump
134 * @param file_path the full path to the file name to store the map
135 * @param limit the number limiting the maps to be dumped
136 */
137void dumpMapsToFile(maps* m,char* file_path,int limit){
138  FILE* file=fopen(file_path,"w+");
139  _dumpMapsToFile(m,file,limit);
140  fflush(file);
141  fclose(file);
142}
143
144/**
145 * Create a new map
146 *
147 * @param name the key to add to the map
148 * @param value the corresponding value to add to the map
149 * @return a pointer to the allocated map
150 */
151map* createMap(const char* name,const char* value){
152  map* tmp=(map *)malloc(MAP_SIZE);
153  tmp->name=zStrdup(name);
154  tmp->value=zStrdup(value);
155  tmp->next=NULL;
156  return tmp;
157}
158
159/**
160 * Create a new maps with the given name
161 *
162 * @param name of the maps
163 * @return the allocated map
164 */
165maps* createMaps(const char* name){
166  maps* tmp = (maps *) malloc (MAPS_SIZE);
167  tmp->name = zStrdup (name);
168  tmp->content = NULL;
169  tmp->child = NULL;
170  tmp->next = NULL;
171  return tmp;
172}
173
174/**
175 * Count number of map in a map
176 *
177 * @param m the maps to count
178 * @return number of map in a map
179 */
180int count(map* m){
181  map* tmp=m;
182  int c=0;
183  while(tmp!=NULL){
184    c++;
185    tmp=tmp->next;
186  }
187  return c;
188}
189
190/**
191 * Verify if a key exist in a map
192 *
193 * @param m the map to search for the key
194 * @param key the key to search in the map
195 * @return true if the key wwas found, false in other case
196 */
197bool hasKey(map* m,const char *key){
198  map* tmp=m;
199  while(tmp!=NULL){
200    if(strcasecmp(tmp->name,key)==0)
201      return true;
202    tmp=tmp->next;
203  }
204#ifdef DEBUG_MAP
205  fprintf(stderr,"NOT FOUND \n");
206#endif
207  return false;
208}
209
210/**
211 * Access a specific maps
212 *
213 * @param m the maps to search for the key
214 * @param key the key to search in the maps
215 * @return a pointer on the maps found or NULL if not found
216 */
217maps* getMaps(maps* m,const char *key){
218  maps* tmp=m;
219  while(tmp!=NULL){
220    if(strcasecmp(tmp->name,key)==0){
221      return tmp;
222    }
223    tmp=tmp->next;
224  }
225  return NULL;
226}
227
228/**
229 * Access a specific map
230 *
231 * @param m the map to search for the key
232 * @param key the key to search in the map
233 * @return a pointer on the map found or NULL if not found
234 */
235map* getMap(map* m,const char *key){
236  map* tmp=m;
237  while(tmp!=NULL){
238    if(strcasecmp(tmp->name,key)==0){
239      return tmp;
240    }
241    tmp=tmp->next;
242  }
243  return NULL;
244}
245
246
247/**
248 * Access the last map
249 *
250 * @param m the map to search for the lastest map
251 * @return a pointer on the lastest map found or NULL if not found
252 */
253map* getLastMap(map* m){
254  map* tmp=m;
255  while(tmp!=NULL){
256    if(tmp->next==NULL){
257      return tmp;
258    }
259    tmp=tmp->next;
260  }
261  return NULL;
262}
263
264/**
265 * Access a specific map from a maps
266 *
267 * @param m the maps to search for the key
268 * @param key the key to search in the maps
269 * @param subkey the key to search in the map (found for the key, if any)
270 * @return a pointer on the map found or NULL if not found
271 */
272map* getMapFromMaps(maps* m,const char* key,const char* subkey){
273  maps* _tmpm=getMaps(m,key);
274  if(_tmpm!=NULL){
275    map* _ztmpm=getMap(_tmpm->content,subkey);
276    return _ztmpm;
277  }
278  else return NULL;
279}
280
281/**
282 * Free allocated memory of a map.
283 * Require to call free on mo after calling this function.
284 *
285 * @param mo the map to free
286 */
287void freeMap(map** mo){
288  map* _cursor=*mo;
289  if(_cursor!=NULL){
290#ifdef DEBUG
291    fprintf(stderr,"freeMap\n");
292#endif
293    free(_cursor->name);
294    free(_cursor->value);
295    if(_cursor->next!=NULL){
296      freeMap(&_cursor->next);
297      free(_cursor->next);
298    }
299  }
300}
301
302/**
303 * Free allocated memory of a maps.
304 * Require to call free on mo after calling this function.
305 *
306 * @param mo the maps to free
307 */
308void freeMaps(maps** mo){
309  maps* _cursor=*mo;
310  if(_cursor && _cursor!=NULL){
311#ifdef DEBUG
312    fprintf(stderr,"freeMaps\n");
313#endif
314    free(_cursor->name);
315    if(_cursor->content!=NULL){
316      freeMap(&_cursor->content);
317      free(_cursor->content);
318    }
319    if(_cursor->child!=NULL){
320      freeMaps(&_cursor->child);
321      free(_cursor->child);
322    }
323    if(_cursor->next!=NULL){
324      freeMaps(&_cursor->next);
325      free(_cursor->next);
326    }
327  }
328}
329
330/**
331 * Verify if an elements contains a name equal to the given key.
332 *
333 * @param e the elements to search for the key
334 * @param key the elements name to search
335 * @return true if the elements contains the name, false in other cases.
336 */ 
337bool hasElement(elements* e,const char* key){
338  elements* tmp=e;
339  while(tmp!=NULL){
340    if(strcasecmp(key,tmp->name)==0)
341      return true;
342    tmp=tmp->next;
343  }
344  return false;
345}
346
347/**
348 * Access a specific elements named key.
349 *
350 * @param m the elements to search
351 * @param key the elements name to search
352 * @return a pointer to the specific element if found, NULL in other case.
353 */ 
354elements* getElements(elements* m,char *key){
355  elements* tmp=m;
356  while(tmp!=NULL){
357    if(strcasecmp(tmp->name,key)==0)
358      return tmp;
359    tmp=tmp->next;
360  }
361  return NULL;
362}
363
364/**
365 * Free allocated memory of an iotype.
366 * Require to call free on i after calling this function.
367 *
368 * @param i the iotype to free
369 */
370void freeIOType(iotype** i){
371  iotype* _cursor=*i;
372  if(_cursor!=NULL){
373    if(_cursor->next!=NULL){
374      freeIOType(&_cursor->next);
375      free(_cursor->next);
376    }
377    freeMap(&_cursor->content);
378    free(_cursor->content);
379  }
380}
381
382/**
383 * Free allocated memory of an elements.
384 * Require to call free on e after calling this function.
385 *
386 * @param e the iotype to free
387 */
388void freeElements(elements** e){
389  elements* tmp=*e;
390  if(tmp!=NULL){
391    if(tmp->name!=NULL)
392      free(tmp->name);
393    freeMap(&tmp->content);
394    if(tmp->content!=NULL)
395      free(tmp->content);
396    freeMap(&tmp->metadata);
397    if(tmp->metadata!=NULL)
398      free(tmp->metadata);
399    if(tmp->format!=NULL)
400      free(tmp->format);
401    if(tmp->child!=NULL){
402      freeElements(&tmp->child);
403      free(tmp->child);
404    }
405    freeIOType(&tmp->defaults);
406    if(tmp->defaults!=NULL)
407      free(tmp->defaults);
408    freeIOType(&tmp->supported);
409    if(tmp->supported!=NULL){
410      free(tmp->supported);
411    }
412    freeElements(&tmp->next);
413    if(tmp->next!=NULL)
414      free(tmp->next);
415  }
416}
417
418/**
419 * Free allocated memory of a service.
420 * Require to call free on e after calling this function.
421 *
422 * @param s the service to free
423 */
424void freeService(service** s){
425  service* tmp=*s;
426  if(tmp!=NULL){
427    if(tmp->name!=NULL)
428      free(tmp->name);
429    freeMap(&tmp->content);
430    if(tmp->content!=NULL)
431      free(tmp->content);
432    freeMap(&tmp->metadata);
433    if(tmp->metadata!=NULL)
434      free(tmp->metadata);
435    freeElements(&tmp->inputs);
436    if(tmp->inputs!=NULL)
437      free(tmp->inputs);
438    freeElements(&tmp->outputs);
439    if(tmp->outputs!=NULL)
440      free(tmp->outputs);
441  }
442}
443
444/**
445 * Add key value pair to an existing map.
446 *
447 * @param m the map to add the KVP
448 * @param n the key to add
449 * @param v the corresponding value to add
450 */
451void addToMap(map* m,const char* n,const char* v){
452  if(hasKey(m,n)==false){
453    map* _cursor=m;
454    while(_cursor->next!=NULL){
455      _cursor=_cursor->next;
456    }
457    _cursor->next=createMap(n,v);
458  }
459  else{
460    map *tmp=getMap(m,n);
461    if(tmp->value!=NULL)
462      free(tmp->value);
463    tmp->value=zStrdup(v);
464  }
465}
466
467/**
468 * Add a key and an integer value to an existing map.
469 *
470 * @param m the map to add the KVP
471 * @param n the key to add
472 * @param v the corresponding value to add
473 */
474void addIntToMap(map* m,const char* n,const int v){
475  char svalue[10];
476  sprintf(svalue,"%d",v);
477  if(hasKey(m,n)==false){
478    map* _cursor=m;
479    while(_cursor->next!=NULL){
480      _cursor=_cursor->next;
481    }
482    _cursor->next=createMap(n,svalue);
483  }
484  else{
485    map *tmp=getMap(m,n);
486    if(tmp->value!=NULL)
487      free(tmp->value);
488    tmp->value=zStrdup(svalue);
489  }
490}
491
492/**
493 * Add a key and a binary value to an existing map.
494 *
495 * @param m the map to add the KVP
496 * @param n the key to add
497 * @param v the corresponding value to add
498 * @param size the size of the given value
499 * @return a pointer to the updated map m
500 */
501map* addToMapWithSize(map* m,const char* n,const char* v,int size){
502  if(hasKey(m,n)==false){
503    map* _cursor=m;
504    if(_cursor!=NULL){
505      addToMap(m,n,"");
506    }else{
507      m=createMap(n,"");
508    }
509  }
510  char sname[10]="size";
511  if(strlen(n)>5)
512    sprintf(sname,"size_%s",n+6);
513  map *tmp=getMap(m,n);
514  if(tmp->value!=NULL)
515    free(tmp->value);
516  tmp->value=(char*)malloc((size+1)*sizeof(char));
517  if(v!=NULL)
518    memmove(tmp->value,v,size*sizeof(char));
519  tmp->value[size]=0;
520  char sin[128];
521  sprintf(sin,"%d",size);
522  addToMap(m,sname,sin);
523  return m;
524}
525
526/**
527 * Add a map at the end of another map.
528 *
529 * @param mo the map to add mi
530 * @param mi the map to add to mo
531 */
532void addMapToMap(map** mo,map* mi){
533  map* tmp=mi;
534  map* _cursor=*mo;
535  while(tmp!=NULL){
536    if(_cursor==NULL){
537      *mo=createMap(tmp->name,tmp->value);
538      (*mo)->next=NULL;
539    }
540    else{
541#ifdef DEBUG
542      fprintf(stderr,"_CURSOR\n");
543      dumpMap(_cursor);
544#endif
545      while(_cursor->next!=NULL)
546        _cursor=_cursor->next;
547      map* tmp1=getMap(*mo,tmp->name);
548      if(tmp1==NULL){
549        _cursor->next=createMap(tmp->name,tmp->value);
550      }
551      else{
552        addToMap(*mo,tmp->name,tmp->value);
553      }
554    }
555    _cursor=*mo;
556    tmp=tmp->next;
557#ifdef DEBUG
558    fprintf(stderr,"MO\n");
559    dumpMap(*mo);
560#endif
561  }
562}
563
564/**
565 * Add a map to iotype.
566 *
567 * @param io the iotype to add the map
568 * @param mi the map to add to io
569 */
570void addMapToIoType(iotype** io,map* mi){
571  iotype* tmp=*io;
572  while(tmp->next!=NULL){
573    tmp=tmp->next;
574  }
575  tmp->next=(iotype*)malloc(IOTYPE_SIZE);
576  tmp->next->content=NULL;
577  addMapToMap(&tmp->next->content,mi);
578  tmp->next->next=NULL;
579}
580
581/**
582 * Access a specific map or set its value.
583 *
584 * @param m the map to search for the key
585 * @param key the key to search/add in the map
586 * @param value the value to add if the key does not exist
587 * @return a pointer on the map found or NULL if not found
588 */
589map* getMapOrFill(map** m,const char *key,const char* value){
590  map* tmp=*m;
591  map* tmpMap=getMap(tmp,key);
592  if(tmpMap==NULL){
593    if(tmp!=NULL){
594      addToMap((*m),key,value);
595    }
596    else
597      (*m)=createMap(key,value);
598    tmpMap=getMap(*m,key);
599  }
600  return tmpMap;
601}
602
603/**
604 * Verify if a map is contained in another map.
605 *
606 * @param m the map to search for i
607 * @param i the map to search in m
608 * @return true if i was found in m, false in other case
609 */
610bool contains(map* m,map* i){
611  while(i!=NULL){     
612    if(strcasecmp(i->name,"value")!=0 &&
613       strcasecmp(i->name,"xlink:href")!=0 &&
614       strcasecmp(i->name,"useMapServer")!=0 &&
615       strcasecmp(i->name,"asReference")!=0){
616      map *tmp;
617      if(hasKey(m,i->name) && (tmp=getMap(m,i->name))!=NULL && 
618         strcasecmp(i->value,tmp->value)!=0)
619        return false;
620    }
621    i=i->next;
622  }
623  return true;
624}
625
626/**
627 * Access a specific iotype from an elements.
628 *
629 * @param e the elements to search for the name
630 * @param name the name to search in the elements e
631 * @param values the map to verify it was contained in the defaults or
632 *  supported content of the elements e
633 * @return a pointer on the iotype found or NULL if not found
634 */
635iotype* getIoTypeFromElement(elements* e,char *name, map* values){
636  elements* cursor=e;
637  if(values!=NULL)
638    while(cursor!=NULL){
639      if(strcasecmp(cursor->name,name)==0 && (cursor->defaults!=NULL || cursor->supported!=NULL)){
640        if(contains(cursor->defaults->content,values)==true)
641          return cursor->defaults;
642        else{
643          iotype* tmp=cursor->supported;
644          while(tmp!=NULL){
645            if(contains(tmp->content,values)==true)
646              return tmp;           
647            tmp=tmp->next;
648          }
649        }
650      }
651      cursor=cursor->next;
652    }
653  return NULL;
654}
655
656/**
657 * Load binary values from a map (in) and add them to another map (out)
658 *
659 * @param out the map to add binaries values
660 * @param in the map containing the binary values to add ti out
661 * @param pos index of the binary in an array (in case of "MapArray")
662 */
663void loadMapBinary(map** out,map* in,int pos){
664  map* size=getMap(in,"size");
665  map *lout=*out;
666  if(size!=NULL && pos>0){
667    char tmp[11];
668    sprintf(tmp,"size_%d",pos);
669    size=getMap(in,tmp);
670    sprintf(tmp,"value_%d",pos);
671    map* tmpVin=getMap(in,tmp);
672    map* tmpVout=getMap(lout,tmp);
673    free(tmpVout->value);
674    tmpVout->value=(char*)malloc((atoi(size->value)+1)*sizeof(char));
675    memmove(tmpVout->value,tmpVin->value,atoi(size->value)*sizeof(char));
676    tmpVout->value[atoi(size->value)]=0;
677  }else{
678    if(size!=NULL){
679      map* tmpVin=getMap(in,"value");
680      map* tmpVout=getMap(lout,"value");
681      free(tmpVout->value);
682      tmpVout->value=(char*)malloc((atoi(size->value)+1)*sizeof(char));
683      memmove(tmpVout->value,tmpVin->value,atoi(size->value)*sizeof(char));
684      tmpVout->value[atoi(size->value)]=0;
685    }
686  }
687}
688 
689/**
690 * Load binary values from a map (in) and add them to another map (out).
691 * This function will take care of MapArray.
692 * @see loadMapBinary
693 *
694 * @param out the map to add binaries values
695 * @param in the map containing the binary values to add ti out
696 */
697void loadMapBinaries(map** out,map* in){
698  map* size=getMap(in,"size");
699  map* length=getMap(in,"length");
700  if(length!=NULL){
701    int len=atoi(length->value);
702    int i=0;
703    for(i=0;i<len;i++){
704      loadMapBinary(out,in,i);
705    }
706  }
707  else
708    if(size!=NULL)
709      loadMapBinary(out,in,-1);
710}
711
712/**
713 * Duplicate a Maps
714 *
715 * @param mo the maps to clone
716 * @return the allocated maps containing a copy of the mo maps
717 */
718maps* dupMaps(maps** mo){
719  maps* _cursor=*mo;
720  maps* res=NULL;
721  if(_cursor!=NULL){
722    res=createMaps(_cursor->name);
723    map* mc=_cursor->content;
724    if(mc!=NULL){
725      addMapToMap(&res->content,mc);
726      loadMapBinaries(&res->content,mc);
727    }
728    maps* mcs=_cursor->child;
729    if(mcs!=NULL){
730      res->child=dupMaps(&mcs);
731    }
732    res->next=dupMaps(&_cursor->next);
733  }
734  return res;
735}
736
737/**
738 * Add a maps at the end of another maps.
739 *
740 * @see addMapToMap, dupMaps, getMaps
741 * @param mo the maps to add mi
742 * @param mi the maps to add to mo
743 */
744void addMapsToMaps(maps** mo,maps* mi){
745  maps* tmp=mi;
746  maps* _cursor=*mo;
747  while(tmp!=NULL){
748    if(_cursor==NULL){
749      *mo=dupMaps(&mi);
750    }
751    else{
752      while(_cursor->next!=NULL)
753        _cursor=_cursor->next;
754      maps* tmp1=getMaps(*mo,tmp->name);
755      if(tmp1==NULL){
756        _cursor->next=dupMaps(&tmp);
757        if(tmp->child!=NULL)
758          _cursor->next->child=dupMaps(&tmp->child);
759        else
760          _cursor->next->child=NULL;
761      }
762      else{
763        addMapToMap(&tmp1->content,tmp->content);
764        if(tmp->child!=NULL)
765          tmp1->child=dupMaps(&tmp->child);
766        else
767          tmp1->child=NULL;
768      }
769      _cursor=*mo;
770    }
771    tmp=tmp->next;
772  }
773}
774
775/**
776 * Access a specific map array element
777 *
778 * @param m the map to search for the key
779 * @param key the key to search in the map
780 * @param index of the MapArray
781 * @return a pointer on the map found or NULL if not found
782 */
783map* getMapArray(map* m,const char* key,int index){
784  char tmp[1024];
785  if(index>0)
786    sprintf(tmp,"%s_%d",key,index);
787  else
788    sprintf(tmp,"%s",key);
789#ifdef DEBUG
790  fprintf(stderr,"** KEY %s\n",tmp);
791#endif
792  map* tmpMap=getMap(m,tmp);
793#ifdef DEBUG
794  if(tmpMap!=NULL)
795    dumpMap(tmpMap);
796#endif
797  return tmpMap;
798}
799
800/**
801 * Add a key value in a MapArray for a specific index
802 *
803 * @param m the map to search for the key
804 * @param key the key to search in the map
805 * @param index the index of the MapArray
806 * @param value the value to set in the MapArray
807 * @return a pointer on the map found or NULL if not found
808 */
809void setMapArray(map* m,const char* key,int index,const char* value){
810  char tmp[1024];
811  if(index>0){
812    sprintf(tmp,"%s_%d",key,index);
813    map* len=getMap(m,"length");
814    if((len!=NULL && atoi(len->value)<index+1) || len==NULL){
815      char tmp0[5];
816      sprintf(tmp0,"%d",index+1);
817      addToMap(m,"length",tmp0);
818    }
819  }
820  else
821    sprintf(tmp,"%s",key);
822  map* tmpSize=getMapArray(m,"size",index);
823  if(tmpSize!=NULL && strncasecmp(key,"value",5)==0){
824#ifdef DEBUG
825    fprintf(stderr,"%s\n",tmpSize->value);
826#endif
827    map* ptr=getMapOrFill(&m,tmp,(char *)"");
828    free(ptr->value);
829    ptr->value=(char*)malloc((atoi(tmpSize->value)+1)*sizeof(char));
830    memcpy(ptr->value,value,atoi(tmpSize->value)); 
831  }
832  else
833    addToMap(m,tmp,value);
834}
835
836/**
837 * Access the map "type"
838 *
839 * @param mt the map
840 * @return a pointer on the map for mimeType/dataType/CRS if found, NULL in
841 *  other case
842 */
843map* getMapType(map* mt){
844  map* tmap=getMap(mt,(char *)"mimeType");
845  if(tmap==NULL){
846    tmap=getMap(mt,"dataType");
847    if(tmap==NULL){
848      tmap=getMap(mt,"CRS");
849    }
850  }
851#ifdef DEBUG
852  dumpMap(tmap);
853#endif
854  return tmap;
855}
856
857/**
858 * Add a Maps containing a MapArray to a Maps
859 *
860 * @see getMapType
861 * @param mo the maps
862 * @param mi the maps
863 * @param typ the map "type"
864 * @return
865 */
866int addMapsArrayToMaps(maps** mo,maps* mi,char* typ){
867  maps* tmp=mi;   
868  maps* _cursor=getMaps(*mo,tmp->name);
869
870  if(_cursor==NULL)
871    return -1;
872
873  map* tmpLength=getMap(_cursor->content,"length");
874  char tmpLen[10];
875  int len=1;
876  if(tmpLength!=NULL){
877    len=atoi(tmpLength->value);
878  }
879
880  char *tmpV[12]={
881    (char*)"size",
882    (char*)"value",
883    (char*)"uom",
884    (char*)"Reference",
885    (char*)"Order",
886    (char*)"cache_file",
887    (char*)"fmimeType",
888    (char*)"xlink:href",
889    typ,
890    (char*)"schema",
891    (char*)"encoding",
892    (char*)"isCached"
893  };
894  sprintf(tmpLen,"%d",len+1);
895  addToMap(_cursor->content,"length",tmpLen);
896  int i=0;
897  for(i=0;i<12;i++){
898    map* tmpVI=getMap(tmp->content,tmpV[i]);
899    if(tmpVI!=NULL){
900#ifdef DEBUG
901      fprintf(stderr,"%s = %s\n",tmpV[i],tmpVI->value);
902#endif
903      setMapArray(_cursor->content,tmpV[i],len,tmpVI->value);
904    }
905  }
906   
907  addToMap(_cursor->content,"isArray","true");
908  return 0;
909}
910
911/**
912 * Set a key value pair to a map contained in a Maps
913 *
914 * @param m the maps
915 * @param key the maps name
916 * @param subkey the map name included in the maps corresponding to key
917 * @param value the corresponding value to add in the map
918 */
919void setMapInMaps(maps* m,const char* key,const char* subkey,const char *value){
920  maps* _tmpm=getMaps(m,key);
921  if(_tmpm!=NULL){
922    map* _ztmpm=getMap(_tmpm->content,subkey);
923    if(_ztmpm!=NULL){
924      if(_ztmpm->value!=NULL)
925        free(_ztmpm->value);
926      _ztmpm->value=zStrdup(value);
927    }else{
928      maps *tmp=createMaps(key);
929      tmp->content=createMap(subkey,value);
930      addMapsToMaps(&_tmpm,tmp);
931      freeMaps(&tmp);
932      free(tmp);
933    }
934  }else{
935    maps *tmp=createMaps(key);
936    tmp->content=createMap(subkey,value);
937    addMapsToMaps(&m,tmp);
938    freeMaps(&tmp);
939    free(tmp);
940  }
941}
942
943/**
944 * Create an empty elements
945 *
946 * @return a pointer to the allocated elements
947 */
948elements* createEmptyElements(){
949  elements* res=(elements*)malloc(ELEMENTS_SIZE);
950  res->name=NULL;
951  res->content=NULL;
952  res->metadata=NULL;
953  res->format=NULL;
954  res->defaults=NULL;
955  res->supported=NULL;
956  res->child=NULL;
957  res->next=NULL;
958  return res;
959}
960
961/**
962 * Create a named elements
963 *
964 * @param name the elements name
965 * @return a pointer to the allocated elements
966 */
967elements* createElements(char* name){
968  elements* res=(elements*)malloc(ELEMENTS_SIZE);
969  res->name=zStrdup(name);
970  res->content=NULL;
971  res->metadata=NULL;
972  res->format=NULL;
973  res->defaults=NULL;
974  res->supported=NULL;
975  res->child=NULL;
976  res->next=NULL;
977  return res;
978}
979
980/**
981 * Set the name of an elements
982 *
983 * @param name the elements name
984 * @return a pointer to the allocated elements
985 */
986void setElementsName(elements** elem,char* name){
987  elements* res=*elem;
988  res->name=zStrdup(name);
989  res->content=NULL;
990  res->metadata=NULL;
991  res->format=NULL;
992  res->defaults=NULL;
993  res->supported=NULL;
994  res->child=NULL;
995  res->next=NULL;
996}
997
998/**
999 * Dump an elements on stderr
1000 *
1001 * @param e the elements to dump
1002 */
1003void dumpElements(elements* e){
1004  elements* tmp=e;
1005  while(tmp!=NULL){
1006    fprintf(stderr,"ELEMENT [%s]\n",tmp->name);
1007    fprintf(stderr," > CONTENT [%s]\n",tmp->name);
1008    dumpMap(tmp->content);
1009    fprintf(stderr," > METADATA [%s]\n",tmp->name);
1010    dumpMap(tmp->metadata);
1011    fprintf(stderr," > FORMAT [%s]\n",tmp->format);
1012    iotype* tmpio=tmp->defaults;
1013    int ioc=0;
1014    while(tmpio!=NULL){
1015      fprintf(stderr," > DEFAULTS [%s] (%i)\n",tmp->name,ioc);
1016      dumpMap(tmpio->content);
1017      tmpio=tmpio->next;
1018      ioc++;
1019    }
1020    tmpio=tmp->supported;
1021    ioc=0;
1022    while(tmpio!=NULL){
1023      fprintf(stderr," > SUPPORTED [%s] (%i)\n",tmp->name,ioc);
1024      dumpMap(tmpio->content);
1025      tmpio=tmpio->next;
1026      ioc++;
1027    }
1028    if(tmp->child!=NULL){
1029      fprintf(stderr," > CHILD \n");
1030      dumpElements(tmp->child);
1031    }
1032    fprintf(stderr,"------------------\n");
1033    tmp=tmp->next;
1034  }
1035}
1036
1037/**
1038 * Dump an elements on stderr using the YAML syntaxe
1039 *
1040 * @param e the elements to dump
1041 */
1042void dumpElementsAsYAML(elements* e,int level){
1043  elements* tmp=e;
1044  int i;
1045  while(tmp!=NULL){
1046    for(i=0;i<2+(4*level);i++)
1047      fprintf(stderr," ");
1048    fprintf(stderr,"%s:\n",tmp->name);
1049    map* mcurs=tmp->content;
1050    while(mcurs!=NULL){
1051      for(i=0;i<4+(4*level);i++)
1052        fprintf(stderr," ");
1053      _dumpMap(mcurs);
1054      mcurs=mcurs->next;
1055    }
1056    mcurs=tmp->metadata;
1057    if(mcurs!=NULL){
1058      for(i=0;i<4+(4*level);i++)
1059        fprintf(stderr," ");
1060      fprintf(stderr,"MetaData:\n");
1061      while(mcurs!=NULL){
1062        for(i=0;i<6+(4*level);i++)
1063          fprintf(stderr," ");
1064        _dumpMap(mcurs);
1065        mcurs=mcurs->next;
1066      }
1067    }
1068    for(i=0;i<4+(4*level);i++)
1069      fprintf(stderr," ");
1070    if(tmp->format!=NULL)
1071      fprintf(stderr,"%s:\n",tmp->format);
1072    else{
1073      fprintf(stderr,"Child:\n");
1074      if(tmp->child!=NULL)
1075        dumpElementsAsYAML(tmp->child,level+1);
1076    }
1077    iotype* tmpio=tmp->defaults;
1078    int ioc=0;
1079    while(tmpio!=NULL){
1080      for(i=0;i<6+(4*level);i++)
1081        fprintf(stderr," ");
1082      fprintf(stderr,"default:\n");
1083      mcurs=tmpio->content;
1084      while(mcurs!=NULL){
1085        for(i=0;i<8+(4*level);i++)
1086          fprintf(stderr," ");
1087        if(strcasecmp(mcurs->name,"range")==0){
1088          fprintf(stderr,"range: \"%s\"\n",mcurs->value);
1089        }else
1090          _dumpMap(mcurs);
1091        mcurs=mcurs->next;
1092      }
1093      tmpio=tmpio->next;
1094      ioc++;
1095    }
1096    tmpio=tmp->supported;
1097    ioc=0;
1098    while(tmpio!=NULL){
1099      for(i=0;i<6+(4*level);i++)
1100        fprintf(stderr," ");
1101      fprintf(stderr,"supported:\n");
1102      mcurs=tmpio->content;
1103      while(mcurs!=NULL){
1104        for(i=0;i<8+(4*level);i++)
1105          fprintf(stderr," ");
1106        if(strcasecmp(mcurs->name,"range")==0){
1107          fprintf(stderr,"range: \"%s\"\n",mcurs->value);
1108        }else
1109          _dumpMap(mcurs);
1110        mcurs=mcurs->next;
1111      }
1112      tmpio=tmpio->next;
1113      ioc++;
1114    }
1115    tmp=tmp->next;
1116  }
1117}
1118
1119/**
1120 * Duplicate an elements
1121 *
1122 * @param e the elements to clone
1123 * @return the allocated elements containing a copy of the elements e
1124 */
1125elements* dupElements(elements* e){
1126  elements* cursor=e;
1127  elements* tmp=NULL;
1128  if(cursor!=NULL){
1129#ifdef DEBUG
1130    fprintf(stderr,">> %s %i\n",__FILE__,__LINE__);
1131    dumpElements(e);
1132    fprintf(stderr,">> %s %i\n",__FILE__,__LINE__);
1133#endif
1134    tmp=(elements*)malloc(ELEMENTS_SIZE);
1135    tmp->name=zStrdup(e->name);
1136    tmp->content=NULL;
1137    addMapToMap(&tmp->content,e->content);
1138    tmp->metadata=NULL;
1139    addMapToMap(&tmp->metadata,e->metadata);
1140    if(e->format!=NULL)
1141      tmp->format=zStrdup(e->format);
1142    else
1143      tmp->format=NULL;
1144    if(e->defaults!=NULL){
1145      tmp->defaults=(iotype*)malloc(IOTYPE_SIZE);
1146      tmp->defaults->content=NULL;
1147      addMapToMap(&tmp->defaults->content,e->defaults->content);
1148      tmp->defaults->next=NULL;
1149#ifdef DEBUG
1150      fprintf(stderr,">> %s %i\n",__FILE__,__LINE__);
1151      dumpMap(tmp->defaults->content);
1152#endif
1153    }else
1154      tmp->defaults=NULL;
1155    if(e->supported!=NULL){
1156      tmp->supported=(iotype*)malloc(IOTYPE_SIZE);
1157      tmp->supported->content=NULL;
1158      addMapToMap(&tmp->supported->content,e->supported->content);
1159      tmp->supported->next=NULL;
1160      iotype *tmp2=e->supported->next;
1161      while(tmp2!=NULL){
1162        addMapToIoType(&tmp->supported,tmp2->content);
1163#ifdef DEBUG
1164        fprintf(stderr,">> %s %i\n",__FILE__,__LINE__);
1165        dumpMap(tmp->defaults->content);
1166#endif
1167        tmp2=tmp2->next;
1168      }
1169    }
1170    else
1171      tmp->supported=NULL;
1172    if(cursor->child!=NULL)
1173      tmp->child=dupElements(cursor->child);
1174    else
1175      tmp->child=NULL;
1176    tmp->next=dupElements(cursor->next);
1177  }
1178  return tmp;
1179}
1180
1181/**
1182 * Add an elements to another elements.
1183 *
1184 * @see dupElements
1185 * @param m the elements to add the e
1186 * @param e the elements to be added to m
1187 */
1188void addToElements(elements** m,elements* e){
1189  elements* tmp=e;
1190  if(*m==NULL){
1191    *m=dupElements(tmp);
1192  }else{
1193    addToElements(&(*m)->next,tmp);
1194  }
1195}
1196
1197/**
1198 * Set the name of a service
1199 *
1200 * @param name the service name
1201 */
1202void setServiceName(service** serv,char* name){
1203  service* res=*serv;
1204  res->name=zStrdup(name);
1205  res->content=NULL;
1206  res->metadata=NULL;
1207  res->inputs=NULL;
1208  res->outputs=NULL;
1209}
1210
1211/**
1212 * Dump a service on stderr
1213 *
1214 * @param s the service to dump
1215 */
1216void dumpService(service* s){
1217  if(s==NULL)
1218    return;
1219  fprintf(stderr,"++++++++++++++++++\nSERVICE [%s]\n++++++++++++++++++\n",s->name);
1220  if(s->content!=NULL){
1221    fprintf(stderr,"CONTENT MAP\n");
1222    dumpMap(s->content);
1223    fprintf(stderr,"CONTENT METADATA\n");
1224    dumpMap(s->metadata);
1225  }
1226  if(s->inputs!=NULL){
1227    fprintf(stderr,"INPUT ELEMENTS [%s]\n------------------\n",s->name);
1228    dumpElements(s->inputs);
1229  }
1230  if(s->outputs!=NULL){
1231    fprintf(stderr,"OUTPUT ELEMENTS [%s]\n------------------\n",s->name);
1232    dumpElements(s->outputs);
1233  }
1234  fprintf(stderr,"++++++++++++++++++\n");
1235}
1236
1237/**
1238 * Dump a service on stderr using the YAML syntaxe
1239 *
1240 * @param s the service to dump
1241 */
1242void dumpServiceAsYAML(service* s){
1243  int i;
1244  fprintf(stderr,"# %s\n\n",s->name);
1245  if(s->content!=NULL){
1246    map* mcurs=s->content;
1247    dumpMap(mcurs);
1248    mcurs=s->metadata;
1249    if(mcurs!=NULL){
1250      fprintf(stderr,"MetaData:\n");
1251      while(mcurs!=NULL){
1252        for(i=0;i<2;i++)
1253          fprintf(stderr," ");
1254        _dumpMap(mcurs);
1255        mcurs=mcurs->next;
1256      }
1257    }
1258  }
1259  if(s->inputs!=NULL){
1260    fprintf(stderr,"\ninputs:\n");
1261    dumpElementsAsYAML(s->inputs,0);
1262  }
1263  if(s->outputs!=NULL){
1264    fprintf(stderr,"\noutputs:\n");
1265    dumpElementsAsYAML(s->outputs,0);
1266  }
1267}
1268
1269/**
1270 * Duplicate a service
1271 *
1272 * @param s the service to clone
1273 * @return the allocated service containing a copy of the serfvice s
1274 */
1275service* dupService(service* s){
1276  service *res=(service*)malloc(SERVICE_SIZE);
1277  res->name=zStrdup(s->name);
1278  res->content=NULL;
1279  addMapToMap(&res->content,s->content);
1280  res->metadata=NULL;
1281  addMapToMap(&res->metadata,s->metadata);
1282  res->inputs=dupElements(s->inputs);
1283  res->outputs=dupElements(s->outputs);
1284  return res;
1285}
1286
1287/**
1288 * Print the registry on stderr.
1289 *
1290 * @param r the registry
1291 */
1292void dumpRegistry(registry* r){
1293  registry* p=r;
1294  while(p!=NULL){
1295    fprintf(stderr,"%s \n",p->name);
1296    services* s=p->content;
1297    s=p->content;
1298    while(s!=NULL){
1299      dumpService(s->content);
1300      s=s->next;
1301    }
1302    p=p->next;
1303  }
1304}
1305
1306/**
1307 * Add a service to the registry
1308 *
1309 * @param reg the resgitry to add the service
1310 * @param name the registry name to update
1311 * @param content the service to add
1312 */
1313bool addServiceToRegistry(registry** reg,char* name,service* content){
1314  registry *l=*reg;
1315  int isInitial=-1;
1316  if(l==NULL){
1317    l=(registry*)malloc(REGISTRY_SIZE);
1318    isInitial=1;
1319  }
1320  if(l!=NULL){
1321    int hasLevel=-1;
1322    while(isInitial<0 && l!=NULL){
1323      if(l->name!=NULL && strcasecmp(name,l->name)==0){
1324        hasLevel=1;
1325        break;
1326      }
1327      l=l->next;
1328    }
1329    if(hasLevel<0){
1330      if(isInitial<0)
1331        l=(registry*)malloc(REGISTRY_SIZE);
1332      l->name=zStrdup(name);
1333      l->content=NULL;
1334      l->next=NULL;
1335    }
1336    if(l->content==NULL){
1337      l->content=(services*)malloc(SERVICES_SIZE);
1338      l->content->content=dupService(content);
1339      l->content->next=NULL;
1340    }
1341    else{
1342      services* s=l->content;
1343      while(s->next!=NULL)
1344        s=s->next;
1345      s->next=(services*)malloc(SERVICES_SIZE);
1346      s->next->content=dupService(content);
1347      s->next->next=NULL;
1348    }
1349    l->next=NULL;
1350    if(isInitial>0)
1351      *reg=l;
1352    else{
1353      registry *r=*reg;
1354      while(r->next!=NULL)
1355        r=r->next;
1356      r->next=l;
1357      r->next->next=NULL;
1358    }
1359    return true;
1360  }
1361  else
1362    return false;
1363}
1364
1365/**
1366 * Free memory allocated for the registry
1367 *
1368 * @param r the registry
1369 */
1370void freeRegistry(registry** r){
1371  registry* lr=*r;
1372  while(lr!=NULL){
1373    services* s=lr->content;
1374    free(lr->name);
1375    while(s!=NULL){
1376      service* s1=s->content;
1377      s=s->next;
1378      if(s1!=NULL){
1379        freeService(&s1);
1380        free(s1);
1381        s1=NULL;
1382      }
1383    }
1384    lr=lr->next;
1385  }   
1386}
1387
1388/**
1389 * Access a service in the registry
1390 *
1391 * @param r the registry
1392 * @param level the regitry to search ("concept", "generic" or "implementation")
1393 * @param sname the service name
1394 * @return the service pointer if a corresponding service was found or NULL
1395 */
1396service* getServiceFromRegistry(registry* r,char  *level,char* sname){
1397  registry *lr=r;
1398  while(lr!=NULL){
1399    if(strcasecmp(lr->name,level)==0){
1400      services* s=lr->content;
1401      while(s!=NULL){
1402        if(s->content!=NULL && strcasecmp(s->content->name,sname)==0)
1403          return s->content;
1404        s=s->next;
1405      }
1406      break;
1407    }
1408    lr=lr->next;
1409  }
1410  return NULL;
1411}
1412
1413/**
1414 * Apply inheritance to an out map from a reference in map
1415 *
1416 * @param out the map to update
1417 * @param in the reference map (containing inherited properties)
1418 */
1419void inheritMap(map** out,map* in){
1420  map* content=in;
1421  if((*out)==NULL){
1422    addMapToMap(out,in);
1423    return;
1424  }
1425  while(content!=NULL){
1426    map* cmap=getMap(*out,content->name);
1427    if(cmap==NULL)
1428      addToMap(*out,content->name,content->value);
1429    content=content->next;
1430  }
1431}
1432
1433/**
1434 * Apply inheritance to an out iotype from a reference in iotype
1435 *
1436 * @param out the iotype to update
1437 * @param in the reference iotype (containing inherited properties)
1438 */
1439void inheritIOType(iotype** out,iotype* in){
1440  iotype* io=in;
1441  iotype* oio=*out;
1442  if(io!=NULL){
1443    if(*out==NULL){
1444      *out=(iotype*)malloc(IOTYPE_SIZE);
1445      (*out)->content=NULL;
1446      addMapToMap(&(*out)->content,io->content);
1447      (*out)->next=NULL;
1448      oio=*out;
1449      inheritIOType(&oio->next,io->next);
1450    }else{
1451      inheritIOType(&oio->next,io->next);
1452    }
1453  }
1454}
1455
1456/**
1457 * Apply inheritance to an out elements from a reference in elements
1458 *
1459 * @param out the elements to update
1460 * @param in the reference elements (containing inherited properties)
1461 */
1462void inheritElements(elements** out,elements* in){
1463  elements* content=in;
1464  while(content!=NULL && *out!=NULL){
1465    elements* cmap=getElements(*out,content->name);
1466    if(cmap==NULL)
1467      addToElements(out,content);
1468    else{
1469      inheritMap(&cmap->content,content->content);
1470      inheritMap(&cmap->metadata,content->metadata);
1471      if(cmap->format==NULL && content->format!=NULL)
1472        cmap->format=zStrdup(content->format);
1473      inheritIOType(&cmap->defaults,content->defaults);
1474      if(cmap->supported==NULL)
1475        inheritIOType(&cmap->supported,content->supported);
1476      else{
1477        iotype* p=content->supported;
1478        while(p!=NULL){
1479          addMapToIoType(&cmap->supported,p->content);
1480          p=p->next;
1481        }
1482      }
1483    }
1484    content=content->next;
1485  }
1486}
1487
1488/**
1489 * Apply inheritance to a service based on a registry
1490 *
1491 * @param r the registry storing profiles hierarchy
1492 * @param s the service to update depending on its inheritance
1493 */
1494void inheritance(registry *r,service** s){
1495  if(r==NULL)
1496    return;
1497  service* ls=*s;
1498  if(ls->content==NULL)
1499    return;
1500  map* profile=getMap(ls->content,"extend");
1501  map* level=getMap(ls->content,"level");
1502  if(profile!=NULL&&level!=NULL){
1503    service* s1;
1504    if(strncasecmp(level->value,"profile",7)==0)
1505      s1=getServiceFromRegistry(r,(char*)"generic",profile->value);
1506    else
1507      s1=getServiceFromRegistry(r,level->value,profile->value);
1508     
1509    inheritMap(&ls->content,s1->content);
1510    inheritMap(&ls->metadata,s1->metadata);
1511    if(ls->inputs==NULL && s1->inputs!=NULL){
1512      ls->inputs=dupElements(s1->inputs);
1513    }else{
1514      inheritElements(&ls->inputs,s1->inputs);
1515    }
1516    if(ls->outputs==NULL && s1->outputs!=NULL){
1517      ls->outputs=dupElements(s1->outputs);
1518    }else
1519      inheritElements(&ls->outputs,s1->outputs);
1520  }
1521}
1522
1523/**
1524 * Convert a maps to a char*** (only used for Fortran support)
1525 *
1526 * @param m the maps to convert
1527 * @param c the resulting array
1528 */
1529void mapsToCharXXX(maps* m,char*** c){
1530  maps* tm=m;
1531  int i=0;
1532  int j=0;
1533  char tmp[10][30][1024];
1534  memset(tmp,0,1024*10*10);
1535  while(tm!=NULL){
1536    if(i>=10)
1537      break;
1538    strcpy(tmp[i][j],"name");
1539    j++;
1540    strcpy(tmp[i][j],tm->name);
1541    j++;
1542    map* tc=tm->content;
1543    while(tc!=NULL){
1544      if(j>=30)
1545        break;
1546      strcpy(tmp[i][j],tc->name);
1547      j++;
1548      strcpy(tmp[i][j],tc->value);
1549      j++;
1550      tc=tc->next;
1551    }
1552    tm=tm->next;
1553    j=0;
1554    i++;
1555  }
1556  memcpy(c,tmp,10*10*1024);
1557}
1558
1559/**
1560 * Convert a char*** to a maps (only used for Fortran support)
1561 *
1562 * @param c the array to convert
1563 * @param m the resulting maps
1564 */
1565void charxxxToMaps(char*** c,maps**m){
1566  maps* trorf=*m;
1567  int i,j;
1568  char tmp[10][30][1024];
1569  memcpy(tmp,c,10*30*1024);
1570  for(i=0;i<10;i++){
1571    if(strlen(tmp[i][1])==0)
1572      break;
1573    trorf->name=tmp[i][1];
1574    trorf->content=NULL;
1575    trorf->next=NULL;
1576    for(j=2;j<29;j+=2){
1577      if(strlen(tmp[i][j+1])==0)
1578        break;
1579      if(trorf->content==NULL)
1580        trorf->content=createMap(tmp[i][j],tmp[i][j+1]);
1581      else
1582        addToMap(trorf->content,tmp[i][j],tmp[i][j+1]);
1583    }
1584    trorf=trorf->next;
1585  }
1586  m=&trorf;
1587}
Note: See TracBrowser for help on using the repository browser.

Search

ZOO Sponsors

http://www.zoo-project.org/trac/chrome/site/img/geolabs-logo.pnghttp://www.zoo-project.org/trac/chrome/site/img/neogeo-logo.png http://www.zoo-project.org/trac/chrome/site/img/apptech-logo.png http://www.zoo-project.org/trac/chrome/site/img/3liz-logo.png http://www.zoo-project.org/trac/chrome/site/img/gateway-logo.png

Become a sponsor !

Knowledge partners

http://www.zoo-project.org/trac/chrome/site/img/ocu-logo.png http://www.zoo-project.org/trac/chrome/site/img/gucas-logo.png http://www.zoo-project.org/trac/chrome/site/img/polimi-logo.png http://www.zoo-project.org/trac/chrome/site/img/fem-logo.png http://www.zoo-project.org/trac/chrome/site/img/supsi-logo.png http://www.zoo-project.org/trac/chrome/site/img/cumtb-logo.png

Become a knowledge partner

Related links

http://zoo-project.org/img/ogclogo.png http://zoo-project.org/img/osgeologo.png