Index: /tags/rel-1.2.0-rc1/thirds/cgic206/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/Makefile	(revision 221)
@@ -0,0 +1,35 @@
+OS:=$(shell uname -s)
+ifeq ($(OS),Darwin)
+	MACOS_CFLAGS=-arch i386 -arch ppc -arch x86_64
+	LIBS= -L./ -lcgic /usr/lib/libfcgi.dylib
+else
+	LIBS= -L./ -lcgic /usr/lib/libfcgi.a
+endif
+CFLAGS=-g -Wall ${MACOS_CFLAGS}
+CC=gcc
+AR=ar
+RANLIB=ranlib
+
+all: libcgic.a cgictest.cgi capture
+
+install: libcgic.a
+	cp libcgic.a  ../../dist/lib
+	cp cgic.h  ../../dist//include
+	@echo libcgic.a is in  ../../dist/lib and cgic.h is in  ../../dist//include.
+
+libcgic.a: cgic.o cgic.h
+	rm -f libcgic.a
+	$(AR) rc libcgic.a cgic.o
+	$(RANLIB) libcgic.a
+
+#mingw32 and cygwin users: replace .cgi with .exe
+
+cgictest.cgi: cgictest.o libcgic.a
+	gcc cgictest.o -o cgictest.cgi ${LIBS}
+
+capture: capture.o libcgic.a
+	gcc capture.o -o capture ${LIBS}
+
+clean:
+	rm -f *.o *.a cgictest.cgi capture
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/capture.c
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/capture.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/capture.c	(revision 221)
@@ -0,0 +1,12 @@
+#include "cgic.h"
+
+int cgiMain() {
+	cgiWriteEnvironment("/tmp/capcgi.dat");
+	cgiHeaderContentType("text/html");
+	fprintf(cgiOut, "<title>Captured</title>\n");
+	fprintf(cgiOut, "<h1>Captured</h1>\n");
+	fprintf(cgiOut, "Your form submission was captured for use in\n");
+	fprintf(cgiOut, "debugging CGI code.\n");
+	return 0;
+}
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.c
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.c	(revision 221)
@@ -0,0 +1,2552 @@
+/* cgicTempDir is the only setting you are likely to need
+	to change in this file. */
+
+/* Used only in Unix environments, in conjunction with mkstemp(). 
+	Elsewhere (Windows), temporary files go where the tmpnam() 
+	function suggests. If this behavior does not work for you, 
+	modify the getTempFileName() function to suit your needs. */
+
+#define cgicTempDir "/tmp"
+
+#include <fcgi_stdio.h>
+#if CGICDEBUG
+#define CGICDEBUGSTART \
+	{ \
+		FILE *dout; \
+		dout = fopen("/home/boutell/public_html/debug", "a"); \
+	
+#define CGICDEBUGEND \
+		fclose(dout); \
+	}
+#else /* CGICDEBUG */
+#define CGICDEBUGSTART
+#define CGICDEBUGEND
+#endif /* CGICDEBUG */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <io.h>
+
+/* cgic 2.01 */
+#include <fcntl.h>
+
+#else
+#include <unistd.h>
+#endif /* WIN32 */
+#include "cgic.h"
+
+#define cgiStrEq(a, b) (!strcmp((a), (b)))
+
+char *cgiServerSoftware;
+char *cgiServerName;
+char *cgiGatewayInterface;
+char *cgiServerProtocol;
+char *cgiServerPort;
+char *cgiRequestMethod;
+char *cgiPathInfo;
+char *cgiPathTranslated;
+char *cgiScriptName;
+char *cgiQueryString;
+char *cgiRemoteHost;
+char *cgiRemoteAddr;
+char *cgiAuthType;
+char *cgiRemoteUser;
+char *cgiRemoteIdent;
+char cgiContentTypeData[1024];
+char *cgiContentType = cgiContentTypeData;
+char *cgiMultipartBoundary;
+char *cgiCookie;
+int cgiContentLength;
+char *cgiAccept;
+char *cgiUserAgent;
+char *cgiReferrer;
+char *cgiSid;
+
+FILE *cgiIn;
+FILE *cgiOut;
+
+/* True if CGI environment was restored from a file. */
+static int cgiRestored = 0;
+static int cgiTreatUrlEncoding;
+
+static void cgiGetenv(char **s, char *var);
+
+typedef enum {
+	cgiParseSuccess,
+	cgiParseMemory,
+	cgiParseIO
+} cgiParseResultType;
+
+/* One form entry, consisting of an attribute-value pair,
+	and an optional filename and content type. All of
+	these are guaranteed to be valid null-terminated strings,
+	which will be of length zero in the event that the
+	field is not present, with the exception of tfileName
+	which will be null when 'in' is null. DO NOT MODIFY THESE 
+	VALUES. Make local copies if modifications are desired. */
+
+typedef struct cgiFormEntryStruct {
+        char *attr;
+	/* value is populated for regular form fields only.
+		For file uploads, it points to an empty string, and file
+		upload data should be read from the file tfileName. */ 
+	char *value;
+	/* When fileName is not an empty string, tfileName is not null,
+		and 'value' points to an empty string. */
+	/* Valid for both files and regular fields; does not include
+		terminating null of regular fields. */
+	int valueLength;
+	char *fileName;	
+	char *contentType;
+	/* Temporary file name for working storage of file uploads. */
+	char *tfileName;
+        struct cgiFormEntryStruct *next;
+} cgiFormEntry;
+
+/* The first form entry. */
+static cgiFormEntry *cgiFormEntryFirst;
+
+static cgiParseResultType cgiParseGetFormInput();
+static cgiParseResultType cgiParsePostFormInput();
+static cgiParseResultType cgiParsePostMultipartInput();
+static cgiParseResultType cgiParseFormInput(char *data, int length);
+static void cgiSetupConstants();
+static void cgiFreeResources();
+static int cgiStrEqNc(char *s1, char *s2);
+static int cgiStrBeginsNc(char *s1, char *s2);
+
+//int cgiMain_init() {}
+
+
+int main(int argc, char *argv[]) {
+	int result;
+	char *cgiContentLengthString;
+	char *e;
+	while (FCGI_Accept() >= 0) {
+	cgiSetupConstants();
+	cgiGetenv(&cgiServerSoftware, "SERVER_SOFTWARE");
+	cgiGetenv(&cgiServerName, "SERVER_NAME");
+	cgiGetenv(&cgiGatewayInterface, "GATEWAY_INTERFACE");
+	cgiGetenv(&cgiServerProtocol, "SERVER_PROTOCOL");
+	cgiGetenv(&cgiServerPort, "SERVER_PORT");
+	cgiGetenv(&cgiRequestMethod, "REQUEST_METHOD");
+	if(strcmp(cgiRequestMethod,"")==0 && argc>=1)
+		cgiRequestMethod="GET";
+	cgiGetenv(&cgiPathInfo, "PATH_INFO");
+	cgiGetenv(&cgiPathTranslated, "PATH_TRANSLATED");
+	cgiGetenv(&cgiScriptName, "SCRIPT_NAME");
+	cgiGetenv(&cgiQueryString, "QUERY_STRING");
+	cgiSid=NULL;
+	if(strcmp(cgiQueryString,"")==0 && argc>=2){
+		cgiQueryString=argv[1];
+		if(argc>2){
+		  cgiSid=argv[2];
+		}
+	}
+	cgiGetenv(&cgiRemoteHost, "REMOTE_HOST");
+	cgiGetenv(&cgiRemoteAddr, "REMOTE_ADDR");
+	cgiGetenv(&cgiAuthType, "AUTH_TYPE");
+	cgiGetenv(&cgiRemoteUser, "REMOTE_USER");
+	cgiGetenv(&cgiRemoteIdent, "REMOTE_IDENT");
+	/* 2.0: the content type string needs to be parsed and modified, so
+		copy it to a buffer. */
+	e = getenv("CONTENT_TYPE");
+	if (e) {
+		if (strlen(e) < sizeof(cgiContentTypeData)) {
+			strcpy(cgiContentType, e);
+		} else {
+			/* Truncate safely in the event of what is almost certainly
+				a hack attempt */
+			strncpy(cgiContentType, e, sizeof(cgiContentTypeData));
+			cgiContentType[sizeof(cgiContentTypeData) - 1] = '\0';
+		}
+	} else {
+		cgiContentType[0] = '\0';
+	}
+	/* Never null */
+	cgiMultipartBoundary = "";
+	/* 2.0: parse semicolon-separated additional parameters of the
+		content type. The one we're interested in is 'boundary'.
+		We discard the rest to make cgiContentType more useful
+		to the typical programmer. */
+	if (strchr(cgiContentType, ';')) {
+		char *sat = strchr(cgiContentType, ';');
+		while (sat) {
+			*sat = '\0';
+			sat++;
+			while (isspace(*sat)) {
+				sat++;
+			}	
+			if (cgiStrBeginsNc(sat, "boundary=")) {
+				char *s;
+				cgiMultipartBoundary = sat + strlen("boundary=");
+				s = cgiMultipartBoundary;
+				while ((*s) && (!isspace(*s))) {
+					s++;
+				}
+				*s = '\0';
+				break;
+			} else {
+				sat = strchr(sat, ';');
+			} 	
+		}
+	}
+	cgiGetenv(&cgiContentLengthString, "CONTENT_LENGTH");
+	cgiContentLength = atoi(cgiContentLengthString);	
+	if(cgiContentLength==0 && argc>=2){
+		cgiContentLength=strlen(argv[1]);
+	}
+	cgiGetenv(&cgiAccept, "HTTP_ACCEPT");
+	cgiGetenv(&cgiUserAgent, "HTTP_USER_AGENT");
+	cgiGetenv(&cgiReferrer, "HTTP_REFERER");
+	cgiGetenv(&cgiCookie, "HTTP_COOKIE");
+#ifdef CGICDEBUG
+	CGICDEBUGSTART
+	fprintf(dout, "%d\n", cgiContentLength);
+	fprintf(dout, "%s\n", cgiRequestMethod);
+	fprintf(dout, "%s\n", cgiContentType);
+	CGICDEBUGEND	
+#endif /* CGICDEBUG */
+#ifdef WIN32
+	/* 1.07: Must set stdin and stdout to binary mode */
+	/* 2.0: this is particularly crucial now and must not be removed */
+	_setmode( FCGI_fileno( stdin ), _O_BINARY );
+	_setmode( FCGI_fileno( stdout ), _O_BINARY );
+#endif /* WIN32 */
+	cgiFormEntryFirst = 0;
+	cgiIn = FCGI_stdin;
+	cgiOut = FCGI_stdout;
+	cgiRestored = 0;
+
+
+	/* These five lines keep compilers from
+		producing warnings that argc and argv
+		are unused. They have no actual function. */
+	if (argc) {
+		if (argv[0]) {
+			cgiRestored = 0;
+		}
+	}	
+
+
+	cgiTreatUrlEncoding=1;
+	if (cgiStrEqNc(cgiRequestMethod, "post")) {
+		cgiTreatUrlEncoding=0;
+#ifdef CGICDEBUG
+		CGICDEBUGSTART
+		fprintf(dout, "POST recognized\n");
+		CGICDEBUGEND
+#endif /* CGICDEBUG */
+		if (cgiStrEqNc(cgiContentType, "application/x-www-form-urlencoded")) {	
+#ifdef CGICDEBUG
+			CGICDEBUGSTART
+			fprintf(dout, "Calling PostFormInput\n");
+			CGICDEBUGEND	
+#endif /* CGICDEBUG */
+			if (cgiParsePostFormInput() != cgiParseSuccess) {
+#ifdef CGICDEBUG
+				CGICDEBUGSTART
+				fprintf(dout, "PostFormInput failed\n");
+				CGICDEBUGEND	
+#endif /* CGICDEBUG */
+				cgiFreeResources();
+				return -1;
+			}	
+#ifdef CGICDEBUG
+			CGICDEBUGSTART
+			fprintf(dout, "PostFormInput succeeded\n");
+			CGICDEBUGEND	
+#endif /* CGICDEBUG */
+		} else if (cgiStrEqNc(cgiContentType, "multipart/form-data")) {
+#ifdef CGICDEBUG
+			CGICDEBUGSTART
+			fprintf(dout, "Calling PostMultipartInput\n");
+			CGICDEBUGEND	
+#endif /* CGICDEBUG */
+			if (cgiParsePostMultipartInput() != cgiParseSuccess) {
+#ifdef CGICDEBUG
+				CGICDEBUGSTART
+				fprintf(dout, "PostMultipartInput failed\n");
+				CGICDEBUGEND	
+#endif /* CGICDEBUG */
+				cgiFreeResources();
+				return -1;
+			}	
+#ifdef CGICDEBUG
+			CGICDEBUGSTART
+			fprintf(dout, "PostMultipartInput succeeded\n");
+			CGICDEBUGEND	
+#endif /* CGICDEBUG */
+		}
+	} else if (cgiStrEqNc(cgiRequestMethod, "get")) {	
+		/* The spec says this should be taken care of by
+			the server, but... it isn't */
+		cgiContentLength = strlen(cgiQueryString);
+		if (cgiParseGetFormInput() != cgiParseSuccess) {
+#ifdef CGICDEBUG
+			CGICDEBUGSTART
+			fprintf(dout, "GetFormInput failed\n");
+			CGICDEBUGEND	
+#endif /* CGICDEBUG */
+			cgiFreeResources();
+			return -1;
+		} else {	
+#ifdef CGICDEBUG
+			CGICDEBUGSTART
+			fprintf(dout, "GetFormInput succeeded\n");
+			CGICDEBUGEND	
+#endif /* CGICDEBUG */
+		}
+	}
+	result = cgiMain();
+	cgiFreeResources();
+	}
+	FCGI_Finish();
+	return result;
+}
+
+static void cgiGetenv(char **s, char *var){
+	*s = getenv(var);
+	if (!(*s)) {
+		*s = "";
+	}
+}
+
+static cgiParseResultType cgiParsePostFormInput() {
+	char *input;
+	cgiParseResultType result;
+	if (!cgiContentLength) {
+		return cgiParseSuccess;
+	}
+	input = (char *) malloc(cgiContentLength);
+	if (!input) {
+		return cgiParseMemory;	
+	}
+	if (((int) fread(input, 1, cgiContentLength, cgiIn)) 
+		!= cgiContentLength) 
+	{
+		return cgiParseIO;
+	}	
+	result = cgiParseFormInput(input, cgiContentLength);
+	free(input);
+	return result;
+}
+
+/* 2.0: A virtual datastream supporting putback of 
+	enough characters to handle multipart boundaries easily.
+	A simple memset(&mp, 0, sizeof(mp)) is suitable initialization. */
+
+typedef struct {
+	/* Buffer for putting characters back */
+	char putback[1024];	
+	/* Position in putback from which next character will be read.
+		If readPos == writePos, then next character should
+		come from cgiIn. */
+	int readPos;
+	/* Position in putback to which next character will be put back.
+		If writePos catches up to readPos, as opposed to the other
+		way around, the stream no longer functions properly.
+		Calling code must guarantee that no more than 
+		sizeof(putback) bytes are put back at any given time. */
+	int writePos;
+	/* Offset in the virtual datastream; can be compared
+		to cgiContentLength */
+	int offset;
+} mpStream, *mpStreamPtr;
+
+int mpRead(mpStreamPtr mpp, char *buffer, int len)
+{
+	int ilen = len;
+	int got = 0;
+	while (len) {
+		if (mpp->readPos != mpp->writePos) {
+			*buffer++ = mpp->putback[mpp->readPos++];
+			mpp->readPos %= sizeof(mpp->putback);
+			got++;
+			len--;
+		} else {
+			break;
+		}	
+	}
+	/* Refuse to read past the declared length in order to
+		avoid deadlock */
+	if (len > (cgiContentLength - mpp->offset)) {
+		len = cgiContentLength - mpp->offset;
+	}
+	if (len) {
+		int fgot = fread(buffer, 1, len, cgiIn);
+		if (fgot >= 0) {
+			mpp->offset += (got + fgot);
+			return got + fgot;
+		} else if (got > 0) {
+			mpp->offset += got;
+			return got;
+		} else {
+			/* EOF or error */
+			return fgot;
+		}
+	} else if (got) {
+		return got;
+	} else if (ilen) {	
+		return EOF;
+	} else {
+		/* 2.01 */
+		return 0;
+	}
+}
+
+void mpPutBack(mpStreamPtr mpp, char *data, int len)
+{
+	mpp->offset -= len;
+	while (len) {
+		mpp->putback[mpp->writePos++] = *data++;
+		mpp->writePos %= sizeof(mpp->putback);
+		len--;
+	}
+}
+
+/* This function copies the body to outf if it is not null, otherwise to
+	a newly allocated character buffer at *outP, which will be null
+	terminated; if both outf and outP are null the body is not stored.
+	If bodyLengthP is not null, the size of the body in bytes is stored
+	to *bodyLengthP, not including any terminating null added to *outP. 
+	If 'first' is nonzero, a preceding newline is not expected before
+	the boundary. If 'first' is zero, a preceding newline is expected.
+	Upon return mpp is positioned after the boundary and its trailing 
+	newline, if any; if the boundary is followed by -- the next two 
+	characters read after this function returns will be --. Upon error, 
+	if outP is not null, *outP is a null pointer; *bodyLengthP 
+	is set to zero. Returns cgiParseSuccess, cgiParseMemory 
+	or cgiParseIO. */
+
+static cgiParseResultType afterNextBoundary(mpStreamPtr mpp,
+	FILE *outf,
+	char **outP,
+	int *bodyLengthP,
+	int first
+	);
+
+static int readHeaderLine(
+	mpStreamPtr mpp,	
+	char *attr,
+	int attrSpace,
+	char *value,
+	int valueSpace);
+
+static void decomposeValue(char *value,
+	char *mvalue, int mvalueSpace,
+	char **argNames,
+	char **argValues,
+	int argValueSpace);
+
+/* tfileName must be 1024 bytes to ensure adequacy on
+	win32 (1024 exceeds the maximum path length and
+	certainly exceeds observed behavior of _tmpnam).
+	May as well also be 1024 bytes on Unix, although actual
+	length is strlen(cgiTempDir) + a short unique pattern. */
+	
+static cgiParseResultType getTempFileName(char *tfileName);
+
+static cgiParseResultType cgiParsePostMultipartInput() {
+	cgiParseResultType result;
+	cgiFormEntry *n = 0, *l = 0;
+	int got;
+	FILE *outf = 0;
+	char *out = 0;
+	char tfileName[1024];
+	mpStream mp;
+	mpStreamPtr mpp = &mp;
+	memset(&mp, 0, sizeof(mp));
+	if (!cgiContentLength) {
+		return cgiParseSuccess;
+	}
+	/* Read first boundary, including trailing newline */
+	result = afterNextBoundary(mpp, 0, 0, 0, 1);
+	if (result == cgiParseIO) {	
+		/* An empty submission is not necessarily an error */
+		return cgiParseSuccess;
+	} else if (result != cgiParseSuccess) {
+		return result;
+	}
+	while (1) {
+		char d[1024];
+		char fvalue[1024];
+		char fname[1024];
+		int bodyLength = 0;
+		char ffileName[1024];
+		char fcontentType[1024];
+		char attr[1024];
+		char value[1024];
+		fvalue[0] = 0;
+		fname[0] = 0;
+		ffileName[0] = 0;
+		fcontentType[0] = 0;
+		out = 0;
+		outf = 0;
+		/* Check for EOF */
+		got = mpRead(mpp, d, 2);
+		if (got < 2) {
+			/* Crude EOF */
+			break;
+		}
+		if ((d[0] == '-') && (d[1] == '-')) {
+			/* Graceful EOF */
+			break;
+		}
+		mpPutBack(mpp, d, 2);
+		/* Read header lines until end of header */
+		while (readHeaderLine(
+				mpp, attr, sizeof(attr), value, sizeof(value))) 
+		{
+			char *argNames[3];
+			char *argValues[2];
+			/* Content-Disposition: form-data; 
+				name="test"; filename="googley.gif" */
+			if (cgiStrEqNc(attr, "Content-Disposition")) {
+				argNames[0] = "name";
+				argNames[1] = "filename";
+				argNames[2] = 0;
+				argValues[0] = fname;
+				argValues[1] = ffileName;
+				decomposeValue(value, 
+					fvalue, sizeof(fvalue),
+					argNames,
+					argValues,
+					1024);	
+			} else if (cgiStrEqNc(attr, "Content-Type")) {
+				argNames[0] = 0;
+				decomposeValue(value, 
+					fcontentType, sizeof(fcontentType),
+					argNames,
+					0,
+					0);
+			}
+		}
+		if (!cgiStrEqNc(fvalue, "form-data")) {
+			/* Not form data */	
+			continue;
+		}
+		/* Body is everything from here until the next 
+			boundary. So, set it aside and move past boundary. 
+			If a filename was submitted as part of the
+			disposition header, store to a temporary file.
+			Otherwise, store to a memory buffer (it is
+			presumably a regular form field). */
+		if (strlen(ffileName)) {
+			if (getTempFileName(tfileName) != cgiParseSuccess) {
+				return cgiParseIO;
+			}	
+			outf = fopen(tfileName, "w+b");
+		} else {
+			outf = 0;
+			tfileName[0] = '\0';
+		}	
+		result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0);
+		if (result != cgiParseSuccess) {
+			/* Lack of a boundary here is an error. */
+			if (outf) {
+				fclose(outf);
+				unlink(tfileName);
+			}
+			if (out) {
+				free(out);
+			}
+			return result;
+		}
+		/* OK, we have a new pair, add it to the list. */
+		n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));	
+		if (!n) {
+			goto outOfMemory;
+		}
+		memset(n, 0, sizeof(cgiFormEntry));
+		/* 2.01: one of numerous new casts required
+			to please C++ compilers */
+		n->attr = (char *) malloc(strlen(fname) + 1);
+		if (!n->attr) {
+			goto outOfMemory;
+		}
+		strcpy(n->attr, fname);
+		if (out) {
+			n->value = out;
+			out = 0;
+		} else if (outf) {
+			n->value = (char *) malloc(1);
+			if (!n->value) {
+				goto outOfMemory;
+			}
+			n->value[0] = '\0';
+			fclose(outf);
+		}
+		n->valueLength = bodyLength;
+		n->next = 0;
+		if (!l) {
+			cgiFormEntryFirst = n;
+		} else {
+			l->next = n;
+		}
+		n->fileName = (char *) malloc(strlen(ffileName) + 1);
+		if (!n->fileName) {
+			goto outOfMemory;
+		}
+		strcpy(n->fileName, ffileName);
+		n->contentType = (char *) malloc(strlen(fcontentType) + 1);
+		if (!n->contentType) {
+			goto outOfMemory;
+		}
+		strcpy(n->contentType, fcontentType);
+		n->tfileName = (char *) malloc(strlen(tfileName) + 1);
+		if (!n->tfileName) {
+			goto outOfMemory;
+		}
+		strcpy(n->tfileName, tfileName);
+
+		l = n;			
+	}	
+	return cgiParseSuccess;
+outOfMemory:
+	if (n) {
+		if (n->attr) {
+			free(n->attr);
+		}
+		if (n->value) {
+			free(n->value);
+		}
+		if (n->fileName) {
+			free(n->fileName);
+		}
+		if (n->tfileName) {
+			free(n->tfileName);
+		}
+		if (n->contentType) {
+			free(n->contentType);
+		}
+		free(n);
+	}
+	if (out) {
+		free(out);
+	}
+	if (outf) {
+		fclose(outf);
+		unlink(tfileName);
+	}
+	return cgiParseMemory;
+}
+
+static cgiParseResultType getTempFileName(char *tfileName)
+{
+#ifndef WIN32
+	/* Unix. Use the robust 'mkstemp' function to create
+		a temporary file that is truly unique, with
+		permissions that are truly safe. The 
+		fopen-for-write destroys any bogus information
+		written by potential hackers during the brief
+		window between the file's creation and the
+		chmod call (glibc 2.0.6 and lower might
+		otherwise have allowed this). */
+	int outfd; 
+	strcpy(tfileName, cgicTempDir "/cgicXXXXXX");
+	outfd = mkstemp(tfileName);
+	if (outfd == -1) {
+		return cgiParseIO;
+	}
+	close(outfd);
+	/* Fix the permissions */
+	if (chmod(tfileName, 0600) != 0) {
+		unlink(tfileName);
+		return cgiParseIO;
+	}
+#else
+	/* Non-Unix. Do what we can. */
+	if (!tmpnam(tfileName)) {
+		return cgiParseIO;
+	}
+#endif
+	return cgiParseSuccess;
+}
+
+
+#define APPEND(string, char) \
+	{ \
+		if ((string##Len + 1) < string##Space) { \
+			string[string##Len++] = (char); \
+		} \
+	}
+
+#define RAPPEND(string, ch) \
+	{ \
+		if ((string##Len + 1) == string##Space)  { \
+			char *sold = string; \
+			string##Space *= 2; \
+			string = (char *) realloc(string, string##Space); \
+			if (!string) { \
+				string = sold; \
+				goto outOfMemory; \
+			} \
+		} \
+		string[string##Len++] = (ch); \
+	}
+		
+#define BAPPEND(ch) \
+	{ \
+		if (outf) { \
+			putc(ch, outf); \
+			outLen++; \
+		} else if (out) { \
+			RAPPEND(out, ch); \
+		} \
+	}
+
+cgiParseResultType afterNextBoundary(mpStreamPtr mpp, FILE *outf, char **outP,
+	int *bodyLengthP, int first)
+{
+	int outLen = 0;
+	int outSpace = 256;
+	char *out = 0;
+	cgiParseResultType result;
+	int boffset;
+	int got;
+	char d[2];	
+	/* This is large enough, because the buffer into which the
+		original boundary string is fetched is shorter by more
+		than four characters due to the space required for
+		the attribute name */
+	char workingBoundaryData[1024];
+	char *workingBoundary = workingBoundaryData;
+	int workingBoundaryLength;
+	if ((!outf) && (outP)) {
+		out = (char *) malloc(outSpace);
+		if (!out) {
+			goto outOfMemory;
+		}
+	}
+	boffset = 0;
+	sprintf(workingBoundaryData, "\r\n--%s", cgiMultipartBoundary);
+	if (first) {
+		workingBoundary = workingBoundaryData + 2;
+	}
+	workingBoundaryLength = strlen(workingBoundary);
+	while (1) {
+		got = mpRead(mpp, d, 1);
+		if (got != 1) {
+			/* 2.01: cgiParseIO, not cgiFormIO */
+			result = cgiParseIO;
+			goto error;
+		}
+		if (d[0] == workingBoundary[boffset]) {
+			/* We matched the next byte of the boundary.
+				Keep track of our progress into the
+				boundary and don't emit anything. */
+			boffset++;
+			if (boffset == workingBoundaryLength) {
+				break;
+			} 
+		} else if (boffset > 0) {
+			/* We matched part, but not all, of the
+				boundary. Now we have to be careful:
+				put back all except the first
+				character and try again. The 
+				real boundary could begin in the
+				middle of a false match. We can
+				emit the first character only so far. */
+			BAPPEND(workingBoundary[0]);
+			mpPutBack(mpp, 
+				workingBoundary + 1, boffset - 1);
+			mpPutBack(mpp, d, 1);
+			boffset = 0;
+		} else {		
+			/* Not presently in the middle of a boundary
+				match; just emit the character. */
+			BAPPEND(d[0]);
+		}	
+	}
+	/* Read trailing newline or -- EOF marker. A literal EOF here
+		would be an error in the input stream. */
+	got = mpRead(mpp, d, 2);
+	if (got != 2) {
+		result = cgiParseIO;
+		goto error;
+	}	
+	if ((d[0] == '\r') && (d[1] == '\n')) {
+		/* OK, EOL */
+	} else if (d[0] == '-') {
+		/* Probably EOF, but we check for
+			that later */
+		mpPutBack(mpp, d, 2);
+	}	
+	if (out && outSpace) {
+		char *oout = out;
+		out[outLen] = '\0';
+		out = (char *) realloc(out, outLen + 1);
+		if (!out) {
+			/* Surprising if it happens; and not fatal! We were
+				just trying to give some space back. We can
+				keep it if we have to. */
+			out = oout;
+		}
+		*outP = out;
+	}
+	if (bodyLengthP) {
+		*bodyLengthP = outLen;
+	}
+	return cgiParseSuccess;
+outOfMemory:
+	result = cgiParseMemory;
+	if (outP) {
+		if (out) {
+			free(out);
+		}
+		*outP = '\0';	
+	}
+error:
+	if (bodyLengthP) {
+		*bodyLengthP = 0;
+	}
+	if (out) {
+		free(out);
+	}
+	if (outP) {
+		*outP = 0;	
+	}
+	return result;
+}
+
+static void decomposeValue(char *value,
+	char *mvalue, int mvalueSpace,
+	char **argNames,
+	char **argValues,
+	int argValueSpace)
+{
+	char argName[1024];
+	int argNameSpace = sizeof(argName);
+	int argNameLen = 0;
+	int mvalueLen = 0;
+	char *argValue;
+	int argNum = 0;
+	while (argNames[argNum]) {
+		if (argValueSpace) {
+			argValues[argNum][0] = '\0';
+		}
+		argNum++;
+	}
+	while (isspace(*value)) {
+		value++;
+	}
+	/* Quoted mvalue */
+	if (*value == '\"') {
+		value++;
+		while ((*value) && (*value != '\"')) {
+			APPEND(mvalue, *value);
+			value++;
+		}
+		while ((*value) && (*value != ';')) {
+			value++;
+		}
+	} else {
+		/* Unquoted mvalue */
+		while ((*value) && (*value != ';')) {
+			APPEND(mvalue, *value);
+			value++;
+		}	
+	}	
+	if (mvalueSpace) {
+		mvalue[mvalueLen] = '\0';
+	}
+	while (*value == ';') {
+		int argNum;
+		int argValueLen = 0;
+		/* Skip the ; between parameters */
+		value++;
+		/* Now skip leading whitespace */
+		while ((*value) && (isspace(*value))) { 
+			value++;
+		}
+		/* Now read the parameter name */
+		argNameLen = 0;
+		while ((*value) && (isalnum(*value))) {
+			APPEND(argName, *value);
+			value++;
+		}
+		if (argNameSpace) {
+			argName[argNameLen] = '\0';
+		}
+		while ((*value) && isspace(*value)) {
+			value++;
+		}
+		if (*value != '=') {
+			/* Malformed line */
+			return;	
+		}
+		value++;
+		while ((*value) && isspace(*value)) {
+			value++;
+		}
+		/* Find the parameter in the argument list, if present */
+		argNum = 0;
+		argValue = 0;
+		while (argNames[argNum]) {
+			if (cgiStrEqNc(argName, argNames[argNum])) {
+				argValue = argValues[argNum];
+				break;
+			}
+			argNum++;
+		}		
+		/* Finally, read the parameter value */
+		if (*value == '\"') {
+			value++;
+			while ((*value) && (*value != '\"')) {
+				if (argValue) {
+					APPEND(argValue, *value);
+				}
+				value++;
+			}
+			while ((*value) && (*value != ';')) {
+				value++;
+			}
+		} else {
+			/* Unquoted value */
+			while ((*value) && (*value != ';')) {
+				if (argNames[argNum]) {
+					APPEND(argValue, *value);
+				}
+				value++;
+			}	
+		}	
+		if (argValueSpace) {
+			argValue[argValueLen] = '\0';
+		}
+	}	 	
+}
+
+static int readHeaderLine(
+	mpStreamPtr mpp,
+	char *attr,
+	int attrSpace,
+	char *value,
+	int valueSpace)
+{	
+	int attrLen = 0;
+	int valueLen = 0;
+	int valueFound = 0;
+	while (1) {
+		char d[1];
+		int got = mpRead(mpp, d, 1);
+		if (got != 1) {	
+			return 0;
+		}
+		if (d[0] == '\r') {
+			got = mpRead(mpp, d, 1);
+			if (got == 1) {	
+				if (d[0] == '\n') {
+					/* OK */
+				} else {
+					mpPutBack(mpp, d, 1);
+				}
+			}
+			break;
+		} else if (d[0] == '\n') {
+			break;
+		} else if ((d[0] == ':') && attrLen) {
+			valueFound = 1;
+			while (mpRead(mpp, d, 1) == 1) {
+				if (!isspace(d[0])) {
+					mpPutBack(mpp, d, 1);
+					break;
+				} 
+			}
+		} else if (!valueFound) {
+			if (!isspace(*d)) {
+				if (attrLen < (attrSpace - 1)) {
+					attr[attrLen++] = *d;
+				}
+			}		
+		} else if (valueFound) {	
+			if (valueLen < (valueSpace - 1)) {
+				value[valueLen++] = *d;
+			}
+		}
+	}
+	if (attrSpace) {
+		attr[attrLen] = '\0';
+	}
+	if (valueSpace) {
+		value[valueLen] = '\0';
+	}
+	if (attrLen && valueLen) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+static cgiParseResultType cgiParseGetFormInput() {
+	return cgiParseFormInput(cgiQueryString, cgiContentLength);
+}
+
+typedef enum {
+	cgiEscapeRest,
+	cgiEscapeFirst,
+	cgiEscapeSecond
+} cgiEscapeState;
+
+typedef enum {
+	cgiUnescapeSuccess,
+	cgiUnescapeMemory
+} cgiUnescapeResultType;
+
+static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
+
+static cgiParseResultType cgiParseFormInput(char *data, int length) {
+	/* Scan for pairs, unescaping and storing them as they are found. */
+	int pos = 0;
+	cgiFormEntry *n;
+	cgiFormEntry *l = 0;
+	while (pos != length) {
+		int foundEq = 0;
+		int foundAmp = 0;
+		int start = pos;
+		int len = 0;
+		char *attr;
+		char *value;
+		while (pos != length) {
+			if (data[pos] == '=') {
+				foundEq = 1;
+				pos++;
+				break;
+			}
+			pos++;
+			len++;
+		}
+		if (!foundEq) {
+			break;
+		}
+		if (cgiUnescapeChars(&attr, data+start, len)
+			!= cgiUnescapeSuccess) {
+			return cgiParseMemory;
+		}	
+		start = pos;
+		len = 0;
+		while (pos != length) {
+			if (data[pos] == '&') {
+				foundAmp = 1;
+				pos++;
+				break;
+			}
+			pos++;
+			len++;
+		}
+		/* The last pair probably won't be followed by a &, but
+			that's fine, so check for that after accepting it */
+		if (cgiUnescapeChars(&value, data+start, len)
+			!= cgiUnescapeSuccess) {
+			free(attr);
+			return cgiParseMemory;
+		}	
+		/* OK, we have a new pair, add it to the list. */
+		n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));	
+		if (!n) {
+			free(attr);
+			free(value);
+			return cgiParseMemory;
+		}
+		n->attr = attr;
+		n->value = value;
+		n->valueLength = strlen(n->value);
+		n->fileName = (char *) malloc(1);
+		if (!n->fileName) {
+			free(attr);
+			free(value);
+			free(n);
+			return cgiParseMemory;
+		}	
+		n->fileName[0] = '\0';
+		n->contentType = (char *) malloc(1);
+		if (!n->contentType) {
+			free(attr);
+			free(value);
+			free(n->fileName);
+			free(n);
+			return cgiParseMemory;
+		}	
+		n->contentType[0] = '\0';
+		n->tfileName = (char *) malloc(1);
+		if (!n->tfileName) {
+			free(attr);
+			free(value);
+			free(n->fileName);
+			free(n->contentType);
+			free(n);
+			return cgiParseMemory;
+		}	
+		n->tfileName[0] = '\0';
+		n->next = 0;
+		if (!l) {
+			cgiFormEntryFirst = n;
+		} else {
+			l->next = n;
+		}
+		l = n;
+		if (!foundAmp) {
+			break;
+		}			
+	}
+	return cgiParseSuccess;
+}
+
+static int cgiHexValue[256];
+
+cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len) {
+	char *s;
+	cgiEscapeState escapeState = cgiEscapeRest;
+	int escapedValue = 0;
+	int srcPos = 0;
+	int dstPos = 0;
+	s = (char *) malloc(len + 1);
+	if (!s) {
+		return cgiUnescapeMemory;
+	}
+	while (srcPos < len) {
+		int ch = cp[srcPos];
+		if(cgiTreatUrlEncoding==1)
+		switch (escapeState) {
+			case cgiEscapeRest:
+			if (ch == '%') {
+				escapeState = cgiEscapeFirst;
+			} else if (ch == '+') {
+				s[dstPos++] = ' ';
+			} else {
+				s[dstPos++] = ch;
+			}
+			break;
+			case cgiEscapeFirst:
+			escapedValue = cgiHexValue[ch] << 4;	
+			escapeState = cgiEscapeSecond;
+			break;
+			case cgiEscapeSecond:
+			escapedValue += cgiHexValue[ch];
+			s[dstPos++] = escapedValue;
+			escapeState = cgiEscapeRest;
+			break;
+		}
+		else
+		  s[dstPos++] = ch;
+		srcPos++;
+	}
+	s[dstPos] = '\0';
+	*sp = s;
+	return cgiUnescapeSuccess;
+}		
+	
+static void cgiSetupConstants() {
+	int i;
+	for (i=0; (i < 256); i++) {
+		cgiHexValue[i] = 0;
+	}
+	cgiHexValue['0'] = 0;	
+	cgiHexValue['1'] = 1;	
+	cgiHexValue['2'] = 2;	
+	cgiHexValue['3'] = 3;	
+	cgiHexValue['4'] = 4;	
+	cgiHexValue['5'] = 5;	
+	cgiHexValue['6'] = 6;	
+	cgiHexValue['7'] = 7;	
+	cgiHexValue['8'] = 8;	
+	cgiHexValue['9'] = 9;
+	cgiHexValue['A'] = 10;
+	cgiHexValue['B'] = 11;
+	cgiHexValue['C'] = 12;
+	cgiHexValue['D'] = 13;
+	cgiHexValue['E'] = 14;
+	cgiHexValue['F'] = 15;
+	cgiHexValue['a'] = 10;
+	cgiHexValue['b'] = 11;
+	cgiHexValue['c'] = 12;
+	cgiHexValue['d'] = 13;
+	cgiHexValue['e'] = 14;
+	cgiHexValue['f'] = 15;
+}
+
+static void cgiFreeResources() {
+	cgiFormEntry *c = cgiFormEntryFirst;
+	cgiFormEntry *n;
+	while (c) {
+		n = c->next;
+		free(c->attr);
+		free(c->value);
+		free(c->fileName);
+		free(c->contentType);
+		if (strlen(c->tfileName)) {
+			unlink(c->tfileName);
+		}
+		free(c->tfileName);
+		free(c);
+		c = n;
+	}
+	/* If the cgi environment was restored from a saved environment,
+		then these are in allocated space and must also be freed */
+	if (cgiRestored) {
+		free(cgiServerSoftware);
+		free(cgiServerName);
+		free(cgiGatewayInterface);
+		free(cgiServerProtocol);
+		free(cgiServerPort);
+		free(cgiRequestMethod);
+		free(cgiPathInfo);
+		free(cgiPathTranslated);
+		free(cgiScriptName);
+		free(cgiQueryString);
+		free(cgiRemoteHost);
+		free(cgiRemoteAddr);
+		free(cgiAuthType);
+		free(cgiRemoteUser);
+		free(cgiRemoteIdent);
+		free(cgiContentType);
+		free(cgiAccept);
+		free(cgiUserAgent);
+		free(cgiReferrer);
+	}
+	/* 2.0: to clean up the environment for cgiReadEnvironment,
+		we must set these correctly */
+	cgiFormEntryFirst = 0;
+	cgiRestored = 0;
+}
+
+static cgiFormResultType cgiFormEntryString(
+	cgiFormEntry *e, char *result, int max, int newlines);
+
+static cgiFormEntry *cgiFormEntryFindFirst(char *name);
+static cgiFormEntry *cgiFormEntryFindNext();
+
+cgiFormResultType cgiFormString(
+        char *name, char *result, int max) {
+	cgiFormEntry *e;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		strcpy(result, "");
+		return cgiFormNotFound;
+	}
+	return cgiFormEntryString(e, result, max, 1);
+}
+
+cgiFormResultType cgiFormFileName(
+	char *name, char *result, int resultSpace)
+{
+	cgiFormEntry *e;
+	int resultLen = 0;
+	char *s;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		strcpy(result, "");
+		return cgiFormNotFound;
+	}
+	s = e->fileName;
+	while (*s) {
+		APPEND(result, *s);
+		s++;
+	}	
+	if (resultSpace) {
+		result[resultLen] = '\0';
+	}
+	if (!strlen(e->fileName)) {
+		return cgiFormNoFileName;
+	} else if (((int) strlen(e->fileName)) > (resultSpace - 1)) {
+		return cgiFormTruncated;
+	} else {
+		return cgiFormSuccess;
+	}
+}
+
+cgiFormResultType cgiFormFileContentType(
+	char *name, char *result, int resultSpace)
+{
+	cgiFormEntry *e;
+	int resultLen = 0;
+	char *s;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		if (resultSpace) {
+			result[0] = '\0';
+		}	
+		return cgiFormNotFound;
+	}
+	s = e->contentType;
+	while (*s) {
+		APPEND(result, *s);
+		s++;
+	}	
+	if (resultSpace) {
+		result[resultLen] = '\0';
+	}
+	if (!strlen(e->contentType)) {
+		return cgiFormNoContentType;
+	} else if (((int) strlen(e->contentType)) > (resultSpace - 1)) {
+		return cgiFormTruncated;
+	} else {
+		return cgiFormSuccess;
+	}
+}
+
+cgiFormResultType cgiFormFileSize(
+	char *name, int *sizeP)
+{
+	cgiFormEntry *e;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		if (sizeP) {
+			*sizeP = 0;
+		}
+		return cgiFormNotFound;
+	} else if (!strlen(e->tfileName)) {
+		if (sizeP) {
+			*sizeP = 0;
+		}
+		return cgiFormNotAFile;
+	} else {
+		if (sizeP) {
+			*sizeP = e->valueLength;
+		}
+		return cgiFormSuccess;
+	}
+}
+
+typedef struct cgiFileStruct {
+	FILE *in;
+} cgiFile;
+
+cgiFormResultType cgiFormFileOpen(
+	char *name, cgiFilePtr *cfpp)
+{
+	cgiFormEntry *e;
+	cgiFilePtr cfp;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		*cfpp = 0;
+		return cgiFormNotFound;
+	}
+	if (!strlen(e->tfileName)) {
+		*cfpp = 0;
+		return cgiFormNotAFile;
+	}
+	cfp = (cgiFilePtr) malloc(sizeof(cgiFile));
+	if (!cfp) {
+		*cfpp = 0;
+		return cgiFormMemory;
+	}
+	cfp->in = fopen(e->tfileName, "rb");
+	if (!cfp->in) {
+		free(cfp);
+		return cgiFormIO;
+	}
+	*cfpp = cfp;
+	return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormFileRead(
+	cgiFilePtr cfp, char *buffer, 
+	int bufferSize, int *gotP)
+{
+	int got = 0;
+	if (!cfp) {
+		return cgiFormOpenFailed;
+	}
+	got = fread(buffer, 1, bufferSize, cfp->in);
+	if (got <= 0) {
+		return cgiFormEOF;
+	}
+	*gotP = got;
+	return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormFileClose(cgiFilePtr cfp)
+{
+	if (!cfp) {
+		return cgiFormOpenFailed;
+	}
+	fclose(cfp->in);
+	free(cfp);
+	return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormStringNoNewlines(
+        char *name, char *result, int max) {
+	cgiFormEntry *e;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		strcpy(result, "");
+		return cgiFormNotFound;
+	}
+	return cgiFormEntryString(e, result, max, 0);
+}
+
+cgiFormResultType cgiFormStringMultiple(
+        char *name, char ***result) {
+	char **stringArray;
+	cgiFormEntry *e;
+	int i;
+	int total = 0;
+	/* Make two passes. One would be more efficient, but this
+		function is not commonly used. The select menu and
+		radio box functions are faster. */
+	e = cgiFormEntryFindFirst(name);
+	if (e != 0) {
+		do {
+			total++;
+		} while ((e = cgiFormEntryFindNext()) != 0); 
+	}
+	stringArray = (char **) malloc(sizeof(char *) * (total + 1));
+	if (!stringArray) {
+		*result = 0;
+		return cgiFormMemory;
+	}
+	/* initialize all entries to null; the last will stay that way */
+	for (i=0; (i <= total); i++) {
+		stringArray[i] = 0;
+	}
+	/* Now go get the entries */
+	e = cgiFormEntryFindFirst(name);
+#ifdef CGICDEBUG
+	CGICDEBUGSTART
+	fprintf(dout, "StringMultiple Beginning\n");
+	CGICDEBUGEND
+#endif /* CGICDEBUG */
+	if (e) {
+		i = 0;
+		do {
+			int max = (int) (strlen(e->value) + 1);
+			stringArray[i] = (char *) malloc(max);
+			if (stringArray[i] == 0) {
+				/* Memory problems */
+				cgiStringArrayFree(stringArray);
+				*result = 0;
+				return cgiFormMemory;
+			}	
+			strcpy(stringArray[i], e->value);
+			cgiFormEntryString(e, stringArray[i], max, 1);
+			i++;
+		} while ((e = cgiFormEntryFindNext()) != 0); 
+		*result = stringArray;
+#ifdef CGICDEBUG
+		CGICDEBUGSTART
+		fprintf(dout, "StringMultiple Succeeding\n");
+		CGICDEBUGEND
+#endif /* CGICDEBUG */
+		return cgiFormSuccess;
+	} else {
+		*result = stringArray;
+#ifdef CGICDEBUG
+		CGICDEBUGSTART
+		fprintf(dout, "StringMultiple found nothing\n");
+		CGICDEBUGEND
+#endif /* CGICDEBUG */
+		return cgiFormNotFound;
+	}	
+}
+
+cgiFormResultType cgiFormStringSpaceNeeded(
+        char *name, int *result) {
+	cgiFormEntry *e;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		*result = 1;
+		return cgiFormNotFound; 
+	}
+	*result = ((int) strlen(e->value)) + 1;
+	return cgiFormSuccess;
+}
+
+static cgiFormResultType cgiFormEntryString(
+	cgiFormEntry *e, char *result, int max, int newlines) {
+	char *dp, *sp;
+	int truncated = 0;
+	int len = 0;
+	int avail = max-1;
+	int crCount = 0;
+	int lfCount = 0;	
+	dp = result;
+	sp = e->value;	
+	while (1) {
+		int ch;
+		/* 1.07: don't check for available space now.
+			We check for it immediately before adding
+			an actual character. 1.06 handled the
+			trailing null of the source string improperly,
+			resulting in a cgiFormTruncated error. */
+		ch = *sp;
+		/* Fix the CR/LF, LF, CR nightmare: watch for
+			consecutive bursts of CRs and LFs in whatever
+			pattern, then actually output the larger number 
+			of LFs. Consistently sane, yet it still allows
+			consecutive blank lines when the user
+			actually intends them. */
+		if ((ch == 13) || (ch == 10)) {
+			if (ch == 13) {
+				crCount++;
+			} else {
+				lfCount++;
+			}	
+		} else {
+			if (crCount || lfCount) {
+				int lfsAdd = crCount;
+				if (lfCount > crCount) {
+					lfsAdd = lfCount;
+				}
+				/* Stomp all newlines if desired */
+				if (!newlines) {
+					lfsAdd = 0;
+				}
+				while (lfsAdd) {
+					if (len >= avail) {
+						truncated = 1;
+						break;
+					}
+					*dp = 10;
+					dp++;
+					lfsAdd--;
+					len++;		
+				}
+				crCount = 0;
+				lfCount = 0;
+			}
+			if (ch == '\0') {
+				/* The end of the source string */
+				break;				
+			}	
+			/* 1.06: check available space before adding
+				the character, because a previously added
+				LF may have brought us to the limit */
+			if (len >= avail) {
+				truncated = 1;
+				break;
+			}
+			*dp = ch;
+			dp++;
+			len++;
+		}
+		sp++;	
+	}	
+	*dp = '\0';
+	if (truncated) {
+		return cgiFormTruncated;
+	} else if (!len) {
+		return cgiFormEmpty;
+	} else {
+		return cgiFormSuccess;
+	}
+}
+
+static int cgiFirstNonspaceChar(char *s);
+
+cgiFormResultType cgiFormInteger(
+        char *name, int *result, int defaultV) {
+	cgiFormEntry *e;
+	int ch;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		*result = defaultV;
+		return cgiFormNotFound; 
+	}	
+	if (!strlen(e->value)) {
+		*result = defaultV;
+		return cgiFormEmpty;
+	}
+	ch = cgiFirstNonspaceChar(e->value);
+	if (!(isdigit(ch)) && (ch != '-') && (ch != '+')) {
+		*result = defaultV;
+		return cgiFormBadType;
+	} else {
+		*result = atoi(e->value);
+		return cgiFormSuccess;
+	}
+}
+
+cgiFormResultType cgiFormIntegerBounded(
+        char *name, int *result, int min, int max, int defaultV) {
+	cgiFormResultType error = cgiFormInteger(name, result, defaultV);
+	if (error != cgiFormSuccess) {
+		return error;
+	}
+	if (*result < min) {
+		*result = min;
+		return cgiFormConstrained;
+	} 
+	if (*result > max) {
+		*result = max;
+		return cgiFormConstrained;
+	} 
+	return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormDouble(
+        char *name, double *result, double defaultV) {
+	cgiFormEntry *e;
+	int ch;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		*result = defaultV;
+		return cgiFormNotFound; 
+	}	
+	if (!strlen(e->value)) {
+		*result = defaultV;
+		return cgiFormEmpty;
+	} 
+	ch = cgiFirstNonspaceChar(e->value);
+	if (!(isdigit(ch)) && (ch != '.') && (ch != '-') && (ch != '+')) {
+		*result = defaultV;
+		return cgiFormBadType;
+	} else {
+		*result = atof(e->value);
+		return cgiFormSuccess;
+	}
+}
+
+cgiFormResultType cgiFormDoubleBounded(
+        char *name, double *result, double min, double max, double defaultV) {
+	cgiFormResultType error = cgiFormDouble(name, result, defaultV);
+	if (error != cgiFormSuccess) {
+		return error;
+	}
+	if (*result < min) {
+		*result = min;
+		return cgiFormConstrained;
+	} 
+	if (*result > max) {
+		*result = max;
+		return cgiFormConstrained;
+	} 
+	return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormSelectSingle(
+	char *name, char **choicesText, int choicesTotal, 
+	int *result, int defaultV) 
+{
+	cgiFormEntry *e;
+	int i;
+	e = cgiFormEntryFindFirst(name);
+#ifdef CGICDEBUG
+	CGICDEBUGSTART
+	fprintf(dout, "%d\n", (int) e);
+	CGICDEBUGEND
+#endif /* CGICDEBUG */
+	if (!e) {
+		*result = defaultV;
+		return cgiFormNotFound;
+	}
+	for (i=0; (i < choicesTotal); i++) {
+#ifdef CGICDEBUG
+		CGICDEBUGSTART
+		fprintf(dout, "%s %s\n", choicesText[i], e->value);
+		CGICDEBUGEND
+#endif /* CGICDEBUG */
+		if (cgiStrEq(choicesText[i], e->value)) {
+#ifdef CGICDEBUG
+			CGICDEBUGSTART
+			fprintf(dout, "MATCH\n");
+			CGICDEBUGEND
+#endif /* CGICDEBUG */
+			*result = i;
+			return cgiFormSuccess;
+		}
+	}
+	*result = defaultV;
+	return cgiFormNoSuchChoice;
+}
+
+cgiFormResultType cgiFormSelectMultiple(
+	char *name, char **choicesText, int choicesTotal, 
+	int *result, int *invalid) 
+{
+	cgiFormEntry *e;
+	int i;
+	int hits = 0;
+	int invalidE = 0;
+	for (i=0; (i < choicesTotal); i++) {
+		result[i] = 0;
+	}
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		*invalid = invalidE;
+		return cgiFormNotFound;
+	}
+	do {
+		int hit = 0;
+		for (i=0; (i < choicesTotal); i++) {
+			if (cgiStrEq(choicesText[i], e->value)) {
+				result[i] = 1;
+				hits++;
+				hit = 1;
+				break;
+			}
+		}
+		if (!(hit)) {
+			invalidE++;
+		}
+	} while ((e = cgiFormEntryFindNext()) != 0);
+
+	*invalid = invalidE;
+
+	if (hits) {
+		return cgiFormSuccess;
+	} else {
+		return cgiFormNotFound;
+	}
+}
+
+cgiFormResultType cgiFormCheckboxSingle(
+	char *name)
+{
+	cgiFormEntry *e;
+	e = cgiFormEntryFindFirst(name);
+	if (!e) {
+		return cgiFormNotFound;
+	}
+	return cgiFormSuccess;
+}
+
+extern cgiFormResultType cgiFormCheckboxMultiple(
+	char *name, char **valuesText, int valuesTotal, 
+	int *result, int *invalid)
+{
+	/* Implementation is identical to cgiFormSelectMultiple. */
+	return cgiFormSelectMultiple(name, valuesText, 
+		valuesTotal, result, invalid);
+}
+
+cgiFormResultType cgiFormRadio(
+	char *name, 
+	char **valuesText, int valuesTotal, int *result, int defaultV)
+{
+	/* Implementation is identical to cgiFormSelectSingle. */
+	return cgiFormSelectSingle(name, valuesText, valuesTotal, 
+		result, defaultV);
+}
+
+cgiFormResultType cgiCookieString(
+	char *name,
+	char *value,
+	int space)
+{
+	char *p = cgiCookie;
+	while (*p) {
+		char *n = name;
+		/* 2.02: if cgiCookie is exactly equal to name, this
+			can cause an overrun. The server probably wouldn't
+			allow it, since a name without values makes no sense 
+			-- but then again it might not check, so this is a
+			genuine security concern. Thanks to Nicolas 
+			Tomadakis. */
+		while (*p == *n) {
+			if ((p == '\0') && (n == '\0')) {
+				/* Malformed cookie header from client */
+				return cgiFormNotFound;
+			}
+			p++;
+			n++;
+		}
+		if ((!*n) && (*p == '=')) {
+			p++;
+			while ((*p != ';') && (*p != '\0') &&
+				(space > 1)) 
+			{
+				*value = *p;
+				value++;
+				p++;
+				space--;
+			}
+			if (space > 0) {
+				*value = '\0';
+			}
+			/* Correct parens: 2.02. Thanks to
+				Mathieu Villeneuve-Belair. */
+			if (!(((*p) == ';') || ((*p) == '\0')))
+			{
+				return cgiFormTruncated;
+			} else {	
+				return cgiFormSuccess;
+			}
+		} else {
+			/* Skip to next cookie */	
+			while (*p) {
+				if (*p == ';') {
+					break;
+				}
+				p++;
+			}
+			if (!*p) {
+				/* 2.01: default to empty */
+				if (space) {
+					*value = '\0';
+				}
+				return cgiFormNotFound;
+			}
+			p++;	
+			/* Allow whitespace after semicolon */
+			while ((*p) && isspace(*p)) {
+				p++;
+			} 
+		}
+	}
+	/* 2.01: actually the above loop never terminates except
+		with a return, but do this to placate gcc */
+	if (space) {
+		*value = '\0';
+	}
+	return cgiFormNotFound;
+}
+
+cgiFormResultType cgiCookieInteger(
+	char *name,
+	int *result,
+	int defaultV)
+{
+	char buffer[256];
+	cgiFormResultType r = 
+		cgiCookieString(name, buffer, sizeof(buffer));
+	if (r != cgiFormSuccess) {
+		*result = defaultV;
+	} else {
+		*result = atoi(buffer);
+	}
+	return r;
+}
+
+void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive,
+	char *path, char *domain)
+{
+	char svalue[256];
+	sprintf(svalue, "%d", value);
+	cgiHeaderCookieSetString(name, svalue, secondsToLive, path, domain);
+}
+
+char *days[] = {
+	"Sun",
+	"Mon",
+	"Tue",
+	"Wed",
+	"Thu",
+	"Fri",
+	"Sat"
+};
+
+char *months[] = {
+	"Jan",
+	"Feb",
+	"Mar",
+	"Apr",
+	"May",
+	"Jun",
+	"Jul",
+	"Aug",
+	"Sep",
+	"Oct",
+	"Nov",
+	"Dec"
+};
+
+void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive,
+	char *path, char *domain)
+{
+	/* cgic 2.02: simpler and more widely compatible implementation.
+		Thanks to Chunfu Lai. 
+	   cgic 2.03: yes, but it didn't work. Reimplemented by
+		Thomas Boutell. ; after last element was a bug. 
+	   Examples of real world cookies that really work:
+   	   Set-Cookie: MSNADS=UM=; domain=.slate.com; 
+             expires=Tue, 26-Apr-2022 19:00:00 GMT; path=/
+	   Set-Cookie: MC1=V=3&ID=b5bc08af2b8a43ff85fcb5efd8b238f0; 
+             domain=.slate.com; expires=Mon, 04-Oct-2021 19:00:00 GMT; path=/
+	*/
+	time_t now;
+	time_t then;
+	struct tm *gt;
+	time(&now);
+	then = now + secondsToLive;
+	gt = gmtime(&then);
+	fprintf(cgiOut, 
+		"Set-Cookie: %s=%s; domain=%s; expires=%s, %02d-%s-%04d %02d:%02d:%02d GMT; path=%s\r\n",
+		name, value, domain, 
+		days[gt->tm_wday],
+		gt->tm_mday,
+		months[gt->tm_mon],
+		gt->tm_year + 1900, 	
+		gt->tm_hour,
+		gt->tm_min,
+		gt->tm_sec,
+		path);
+}
+
+void cgiHeaderLocation(char *redirectUrl) {
+	fprintf(cgiOut, "Location: %s\r\n\r\n", redirectUrl);
+}
+
+void cgiHeaderStatus(int status, char *statusMessage) {
+	fprintf(cgiOut, "Status: %d %s\r\n\r\n", status, statusMessage);
+}
+
+void cgiHeaderContentType(char *mimeType) {
+	fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType);
+}
+
+static int cgiWriteString(FILE *out, char *s);
+
+static int cgiWriteInt(FILE *out, int i);
+
+#define CGIC_VERSION "2.0"
+
+cgiEnvironmentResultType cgiWriteEnvironment(char *filename) {
+	FILE *out;
+	cgiFormEntry *e;
+	/* Be sure to open in binary mode */
+	out = fopen(filename, "wb");
+	if (!out) {
+		/* Can't create file */
+		return cgiEnvironmentIO;
+	}
+	if (!cgiWriteString(out, "CGIC2.0")) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiServerSoftware)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiServerName)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiGatewayInterface)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiServerProtocol)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiServerPort)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiRequestMethod)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiPathInfo)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiPathTranslated)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiScriptName)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiQueryString)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiRemoteHost)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiRemoteAddr)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiAuthType)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiRemoteUser)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiRemoteIdent)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiContentType)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiAccept)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiUserAgent)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiReferrer)) {
+		goto error;
+	}
+	if (!cgiWriteString(out, cgiCookie)) {
+		goto error;
+	}
+	if (!cgiWriteInt(out, cgiContentLength)) {
+		goto error;
+	}
+	e = cgiFormEntryFirst;
+	while (e) {
+		cgiFilePtr fp;
+		if (!cgiWriteString(out, e->attr)) {
+			goto error;
+		}
+		if (!cgiWriteString(out, e->value)) {
+			goto error;
+		}
+		/* New 2.0 fields and file uploads */
+		if (!cgiWriteString(out, e->fileName)) {
+			goto error;
+		}
+		if (!cgiWriteString(out, e->contentType)) {
+			goto error;
+		}
+		if (!cgiWriteInt(out, e->valueLength)) {
+			goto error;
+		}
+		if (cgiFormFileOpen(e->attr, &fp) == cgiFormSuccess) {
+			char buffer[1024];
+			int got;
+			if (!cgiWriteInt(out, 1)) {
+				cgiFormFileClose(fp);
+				goto error;
+			}
+			while (cgiFormFileRead(fp, buffer, 
+				sizeof(buffer), &got) == cgiFormSuccess)
+			{
+				if (((int) fwrite(buffer, 1, got, out)) != got) {
+					cgiFormFileClose(fp);
+					goto error;
+				}
+			}
+			if (cgiFormFileClose(fp) != cgiFormSuccess) {
+				goto error;
+			}
+		} else {
+			if (!cgiWriteInt(out, 0)) {
+				goto error;
+			}
+		}
+		e = e->next;
+	}
+	fclose(out);
+	return cgiEnvironmentSuccess;
+error:
+	fclose(out);
+	/* If this function is not defined in your system,
+		you must substitute the appropriate 
+		file-deletion function. */
+	unlink(filename);
+	return cgiEnvironmentIO;
+}
+
+static int cgiWriteString(FILE *out, char *s) {
+	int len = (int) strlen(s);
+	cgiWriteInt(out, len);
+	if (((int) fwrite(s, 1, len, out)) != len) {
+		return 0;
+	}
+	return 1;
+}
+
+static int cgiWriteInt(FILE *out, int i) {
+	if (!fwrite(&i, sizeof(int), 1, out)) {
+		return 0;
+	}
+	return 1;
+}
+
+static int cgiReadString(FILE *out, char **s);
+
+static int cgiReadInt(FILE *out, int *i);
+
+cgiEnvironmentResultType cgiReadEnvironment(char *filename) {
+	FILE *in;
+	cgiFormEntry *e = 0, *p;
+	char *version;
+	/* Prevent compiler warnings */
+	cgiEnvironmentResultType result = cgiEnvironmentIO;
+	/* Free any existing data first */
+	cgiFreeResources();
+	/* Be sure to open in binary mode */
+	in = fopen(filename, "rb");
+	if (!in) {
+		/* Can't access file */
+		return cgiEnvironmentIO;
+	}
+	if (!cgiReadString(in, &version)) {
+		goto error;
+	}
+	if (strcmp(version, "CGIC" CGIC_VERSION)) {
+		/* 2.02: Merezko Oleg */
+		free(version);
+		return cgiEnvironmentWrongVersion;
+	}	
+	/* 2.02: Merezko Oleg */
+	free(version);
+	if (!cgiReadString(in, &cgiServerSoftware)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiServerName)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiGatewayInterface)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiServerProtocol)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiServerPort)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiRequestMethod)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiPathInfo)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiPathTranslated)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiScriptName)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiQueryString)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiRemoteHost)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiRemoteAddr)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiAuthType)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiRemoteUser)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiRemoteIdent)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiContentType)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiAccept)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiUserAgent)) {
+		goto error;
+	}
+	if (!cgiReadString(in, &cgiReferrer)) {
+		goto error;
+	}
+	/* 2.0 */
+	if (!cgiReadString(in, &cgiCookie)) {
+		goto error;
+	}
+	if (!cgiReadInt(in, &cgiContentLength)) {
+		goto error;
+	}
+	p = 0;
+	while (1) {
+		int fileFlag;
+		e = (cgiFormEntry *) calloc(1, sizeof(cgiFormEntry));
+		if (!e) {
+			cgiFreeResources();
+			fclose(in);
+			return cgiEnvironmentMemory;
+		}
+		memset(e, 0, sizeof(cgiFormEntry));
+		if (!cgiReadString(in, &e->attr)) {
+			/* This means we've reached the end of the list. */
+			/* 2.02: thanks to Merezko Oleg */
+			free(e);
+			break;
+		}
+		if (!cgiReadString(in, &e->value)) {
+			goto outOfMemory;
+		}
+		if (!cgiReadString(in, &e->fileName)) {
+			goto outOfMemory;
+		}
+		if (!cgiReadString(in, &e->contentType)) {
+			goto outOfMemory;
+		}
+		if (!cgiReadInt(in, &e->valueLength)) {
+			goto outOfMemory;
+		}
+		if (!cgiReadInt(in, &fileFlag)) {
+			goto outOfMemory;
+		}
+		if (fileFlag) {
+			char buffer[1024];
+			FILE *out;
+			char tfileName[1024];
+			int got;
+			int len = e->valueLength;
+			if (getTempFileName(tfileName)
+				!= cgiParseSuccess)
+			{
+				result = cgiEnvironmentIO;
+				goto error;
+			}
+			out = fopen(tfileName, "w+b");
+			if (!out) {
+				result = cgiEnvironmentIO;
+				goto error;
+			}
+			while (len > 0) {		
+				/* 2.01: try is a bad variable name in
+					C++, and it wasn't being used
+					properly either */
+				int tryr = len;
+				if (tryr > ((int) sizeof(buffer))) {
+					tryr = sizeof(buffer);
+				}
+				got = fread(buffer, 1, tryr, in);
+				if (got <= 0) {
+					result = cgiEnvironmentIO;
+					fclose(out);
+					unlink(tfileName);
+					goto error;
+				}
+				if (((int) fwrite(buffer, 1, got, out)) != got) {
+					result = cgiEnvironmentIO;
+					fclose(out);
+					unlink(tfileName);
+					goto error;
+				}
+				len -= got;
+			}
+			/* cgic 2.05: should be fclose not rewind */
+			fclose(out);
+			e->tfileName = (char *) malloc((int) strlen(tfileName) + 1);
+			if (!e->tfileName) {
+				result = cgiEnvironmentMemory;
+				unlink(tfileName);
+				goto error;
+			}
+			strcpy(e->tfileName, tfileName);
+		} else {
+			e->tfileName = (char *) malloc(1);
+			if (!e->tfileName) {
+				result = cgiEnvironmentMemory;
+				goto error;
+			}
+		}	
+		e->next = 0;
+		if (p) {
+			p->next = e;
+		} else {
+			cgiFormEntryFirst = e;
+		}	
+		p = e;
+	}
+	fclose(in);
+	cgiRestored = 1;
+	return cgiEnvironmentSuccess;
+outOfMemory:
+	result = cgiEnvironmentMemory;
+error:
+	cgiFreeResources();
+	fclose(in);
+	if (e) {
+		if (e->attr) {
+			free(e->attr);
+		}
+		if (e->value) {
+			free(e->value);
+		}
+		if (e->fileName) {
+			free(e->fileName);
+		}
+		if (e->contentType) {
+			free(e->contentType);
+		}
+		if (e->tfileName) {
+			free(e->tfileName);
+		}
+		free(e);
+	}
+	return result;
+}
+
+static int cgiReadString(FILE *in, char **s) {
+	int len;
+	/* 2.0 fix: test cgiReadInt for failure! */ 
+	if (!cgiReadInt(in, &len)) {
+		return 0;
+	}
+	*s = (char *) malloc(len + 1);
+	if (!(*s)) {
+		return 0;
+	}	
+	if (((int) fread(*s, 1, len, in)) != len) {
+		return 0;
+	}
+	(*s)[len] = '\0';
+	return 1;
+}
+
+static int cgiReadInt(FILE *out, int *i) {
+	if (!fread(i, sizeof(int), 1, out)) {
+		return 0;
+	}
+	return 1;
+}
+
+static int cgiStrEqNc(char *s1, char *s2) {
+	while(1) {
+		if (!(*s1)) {
+			if (!(*s2)) {
+				return 1;
+			} else {
+				return 0;
+			}
+		} else if (!(*s2)) {
+			return 0;
+		}
+		if (isalpha(*s1)) {
+			if (tolower(*s1) != tolower(*s2)) {
+				return 0;
+			}
+		} else if ((*s1) != (*s2)) {
+			return 0;
+		}
+		s1++;
+		s2++;
+	}
+}
+
+static int cgiStrBeginsNc(char *s1, char *s2) {
+	while(1) {
+		if (!(*s2)) {
+			return 1;
+		} else if (!(*s1)) {
+			return 0;
+		}
+		if (isalpha(*s1)) {
+			if (tolower(*s1) != tolower(*s2)) {
+				return 0;
+			}
+		} else if ((*s1) != (*s2)) {
+			return 0;
+		}
+		s1++;
+		s2++;
+	}
+}
+
+static char *cgiFindTarget = 0;
+static cgiFormEntry *cgiFindPos = 0;
+
+static cgiFormEntry *cgiFormEntryFindFirst(char *name) {
+	cgiFindTarget = name;
+	cgiFindPos = cgiFormEntryFirst;
+	return cgiFormEntryFindNext();
+}
+
+static cgiFormEntry *cgiFormEntryFindNext() {
+	while (cgiFindPos) {
+		cgiFormEntry *c = cgiFindPos;
+		cgiFindPos = c->next;
+		if (!strcmp(c -> attr, cgiFindTarget)) {
+			return c;
+		}
+	}
+	return 0;
+}
+
+static int cgiFirstNonspaceChar(char *s) {
+	int len = strspn(s, " \n\r\t");
+	return s[len];
+}
+
+void cgiStringArrayFree(char **stringArray) {
+	char *p;
+	char **arrayItself = stringArray;
+	p = *stringArray;
+	while (p) {
+		free(p);
+		stringArray++;
+		p = *stringArray;
+	}
+	/* 2.0: free the array itself! */
+	free(arrayItself);
+}	
+
+cgiFormResultType cgiCookies(char ***result) {
+	char **stringArray;
+	int i;
+	int total = 0;
+	char *p;
+	char *n;
+	p = cgiCookie;
+	while (*p) {
+		if (*p == '=') {
+			total++;
+		}
+		p++;
+	}
+	stringArray = (char **) malloc(sizeof(char *) * (total + 1));
+	if (!stringArray) {
+		*result = 0;
+		return cgiFormMemory;
+	}
+	/* initialize all entries to null; the last will stay that way */
+	for (i=0; (i <= total); i++) {
+		stringArray[i] = 0;
+	}
+	i = 0;
+	p = cgiCookie;
+	while (*p) {
+		while (*p && isspace(*p)) {
+			p++;
+		}
+		n = p;
+		while (*p && (*p != '=')) {
+			p++;
+		}
+		if (p != n) {
+			stringArray[i] = (char *) malloc((p - n) + 1);
+			if (!stringArray[i]) {
+				cgiStringArrayFree(stringArray);
+				*result = 0;
+				return cgiFormMemory;
+			}	
+			memcpy(stringArray[i], n, p - n);
+			stringArray[i][p - n] = '\0';
+			i++;
+		}
+		while (*p && (*p != ';')) {
+			p++;	
+		}
+		if (!*p) {
+			break;
+		}
+		if (*p == ';') {
+			p++;
+		}
+	}
+	*result = stringArray;
+	return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormEntries(char ***result) {
+	char **stringArray;
+	cgiFormEntry *e, *pe;
+	int i;
+	int total = 0;
+	e = cgiFormEntryFirst;
+	while (e) {
+		/* Don't count a field name more than once if
+			multiple values happen to be present for it */
+		pe = cgiFormEntryFirst;
+		while (pe != e) {
+			if (!strcmp(e->attr, pe->attr)) {
+				goto skipSecondValue;
+			}
+			pe = pe->next;					
+		}
+		total++;
+skipSecondValue:
+		e = e->next;
+	}
+	stringArray = (char **) malloc(sizeof(char *) * (total + 1));
+	if (!stringArray) {
+		*result = 0;
+		return cgiFormMemory;
+	}
+	/* initialize all entries to null; the last will stay that way */
+	for (i=0; (i <= total); i++) {
+		stringArray[i] = 0;
+	}
+	/* Now go get the entries */
+	e = cgiFormEntryFirst;
+	i = 0;
+	while (e) {
+		int space;
+		/* Don't return a field name more than once if
+			multiple values happen to be present for it */
+		pe = cgiFormEntryFirst;
+		while (pe != e) {
+			if (!strcmp(e->attr, pe->attr)) {
+				goto skipSecondValue2;
+			}
+			pe = pe->next;					
+		}		
+		space = (int) strlen(e->attr) + 1;
+		stringArray[i] = (char *) malloc(space);
+		if (stringArray[i] == 0) {
+			/* Memory problems */
+			cgiStringArrayFree(stringArray);
+			*result = 0;
+			return cgiFormMemory;
+		}	
+		strcpy(stringArray[i], e->attr);
+		i++;
+skipSecondValue2:
+		e = e->next;
+	}
+	*result = stringArray;
+	return cgiFormSuccess;
+}
+
+#define TRYPUTC(ch) \
+	{ \
+		if (putc((ch), cgiOut) == EOF) { \
+			return cgiFormIO; \
+		} \
+	} 
+
+cgiFormResultType cgiHtmlEscapeData(char *data, int len)
+{
+	while (len--) {
+		if (*data == '<') {
+			TRYPUTC('&');
+			TRYPUTC('l');
+			TRYPUTC('t');
+			TRYPUTC(';');
+		} else if (*data == '&') {
+			TRYPUTC('&');
+			TRYPUTC('a');
+			TRYPUTC('m');
+			TRYPUTC('p');
+			TRYPUTC(';');
+		} else if (*data == '>') {
+			TRYPUTC('&');
+			TRYPUTC('g');
+			TRYPUTC('t');
+			TRYPUTC(';');
+		} else {
+			TRYPUTC(*data);
+		}
+		data++;
+	}
+	return cgiFormSuccess;
+}
+
+cgiFormResultType cgiHtmlEscape(char *s)
+{
+	return cgiHtmlEscapeData(s, (int) strlen(s));
+}
+
+/* Output data with the " character HTML-escaped, and no
+	other characters escaped. This is useful when outputting
+	the contents of a tag attribute such as 'href' or 'src'.
+	'data' is not null-terminated; 'len' is the number of
+	bytes in 'data'. Returns cgiFormIO in the event
+	of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiValueEscapeData(char *data, int len)
+{
+	while (len--) {
+		if (*data == '\"') {
+			TRYPUTC('&');
+			TRYPUTC('#');
+			TRYPUTC('3');
+			TRYPUTC('4');
+			TRYPUTC(';');
+		} else {
+			TRYPUTC(*data);
+		}
+		data++;
+	}
+	return cgiFormSuccess;
+}
+
+cgiFormResultType cgiValueEscape(char *s)
+{
+	return cgiValueEscapeData(s, (int) strlen(s));
+}
+
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.h
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.h	(revision 221)
@@ -0,0 +1,457 @@
+/* The CGI_C library, by Thomas Boutell, version 2.01. CGI_C is intended
+	to be a high-quality API to simplify CGI programming tasks. */
+
+/* Make sure this is only included once. */
+
+#ifndef CGI_C
+#define CGI_C 1
+
+/* Bring in standard I/O since some of the functions refer to
+	types defined by it, such as FILE *. */
+
+#include "fcgi_stdio.h"
+//#include <stdio.h>
+
+/* The various CGI environment variables. Instead of using getenv(),
+	the programmer should refer to these, which are always
+	valid null-terminated strings (they may be empty, but they 
+	will never be null). If these variables are used instead
+	of calling getenv(), then it will be possible to save
+	and restore CGI environments, which is highly convenient
+	for debugging. */
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiServerSoftware;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiServerName;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiGatewayInterface;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiServerProtocol;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiServerPort;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiRequestMethod;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiPathInfo;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiPathTranslated;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiScriptName;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiQueryString;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiRemoteHost;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiRemoteAddr;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiAuthType;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiRemoteUser;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiRemoteIdent;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiContentType;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiAccept;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiUserAgent;
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiReferrer;
+
+/* Cookies as sent to the server. You can also get them
+	individually, or as a string array; see the documentation. */
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiCookie;
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+char *cgiSid;
+
+/* A macro providing the same incorrect spelling that is
+	found in the HTTP/CGI specifications */
+#define cgiReferer cgiReferrer
+
+/* The number of bytes of data received.
+	Note that if the submission is a form submission
+	the library will read and parse all the information
+	directly from cgiIn; the programmer need not do so. */
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+int cgiContentLength;
+
+/* Pointer to CGI output. The cgiHeader functions should be used
+	first to output the mime headers; the output HTML
+	page, GIF image or other web document should then be written
+	to cgiOut by the programmer. In the standard CGIC library,
+	cgiOut is always equivalent to stdout. */
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+FILE *cgiOut;
+
+/* Pointer to CGI input. The programmer does not read from this.
+	We have continued to export it for backwards compatibility
+	so that cgic 1.x applications link properly. */
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+FILE *cgiIn;
+
+/* Possible return codes from the cgiForm family of functions (see below). */
+
+typedef enum {
+	cgiFormSuccess,
+	cgiFormTruncated,
+	cgiFormBadType,
+	cgiFormEmpty,
+	cgiFormNotFound,
+	cgiFormConstrained,
+	cgiFormNoSuchChoice,
+	cgiFormMemory,
+	cgiFormNoFileName,
+	cgiFormNoContentType,
+	cgiFormNotAFile,
+	cgiFormOpenFailed,
+	cgiFormIO,
+	cgiFormEOF
+} cgiFormResultType;
+
+/* These functions are used to retrieve form data. See
+	cgic.html for documentation. */
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormString(
+	char *name, char *result, int max);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormStringNoNewlines(
+	char *name, char *result, int max);
+
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormStringSpaceNeeded(
+	char *name, int *length);
+
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormStringMultiple(
+	char *name, char ***ptrToStringArray);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+void cgiStringArrayFree(char **stringArray);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormInteger(
+	char *name, int *result, int defaultV);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormIntegerBounded(
+	char *name, int *result, int min, int max, int defaultV);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormDouble(
+	char *name, double *result, double defaultV);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormDoubleBounded(
+	char *name, double *result, double min, double max, double defaultV);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormSelectSingle(
+	char *name, char **choicesText, int choicesTotal, 
+	int *result, int defaultV);	
+
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormSelectMultiple(
+	char *name, char **choicesText, int choicesTotal, 
+	int *result, int *invalid);
+
+/* Just an alias; users have asked for this */
+#define cgiFormSubmitClicked cgiFormCheckboxSingle
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormCheckboxSingle(
+	char *name);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormCheckboxMultiple(
+	char *name, char **valuesText, int valuesTotal, 
+	int *result, int *invalid);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormRadio(
+	char *name, char **valuesText, int valuesTotal, 
+	int *result, int defaultV);	
+
+/* The paths returned by this function are the original names of files
+	as reported by the uploading web browser and shoult NOT be
+	blindly assumed to be "safe" names for server-side use! */
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormFileName(
+	char *name, char *result, int max);
+
+/* The content type of the uploaded file, as reported by the browser.
+	It should NOT be assumed that browsers will never falsify
+	such information. */
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormFileContentType(
+	char *name, char *result, int max);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormFileSize(
+	char *name, int *sizeP);
+
+typedef struct cgiFileStruct *cgiFilePtr;
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormFileOpen(
+	char *name, cgiFilePtr *cfpp);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormFileRead(
+	cgiFilePtr cfp, char *buffer, int bufferSize, int *gotP);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormFileClose(
+	cgiFilePtr cfp);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiCookieString(
+	char *name, char *result, int max);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiCookieInteger(
+	char *name, int *result, int defaultV);
+
+cgiFormResultType cgiCookies(
+	char ***ptrToStringArray);
+
+/* path can be null or empty in which case a path of / (entire site) is set. 
+	domain can be a single web site; if it is an entire domain, such as
+	'boutell.com', it should begin with a dot: '.boutell.com' */
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+void cgiHeaderCookieSetString(char *name, char *value, 
+	int secondsToLive, char *path, char *domain);
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+void cgiHeaderCookieSetInteger(char *name, int value,
+	int secondsToLive, char *path, char *domain);
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+void cgiHeaderLocation(char *redirectUrl);
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+void cgiHeaderStatus(int status, char *statusMessage);
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+void cgiHeaderContentType(char *mimeType);
+
+typedef enum {
+	cgiEnvironmentIO,
+	cgiEnvironmentMemory,
+	cgiEnvironmentSuccess,
+	cgiEnvironmentWrongVersion
+} cgiEnvironmentResultType;
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiEnvironmentResultType cgiWriteEnvironment(char *filename);
+extern cgiEnvironmentResultType cgiReadEnvironment(char *filename);
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+int cgiMain();
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+int cgiMain_init();
+
+
+extern 
+#ifdef __cplusplus
+"C" 
+#endif
+cgiFormResultType cgiFormEntries(
+	char ***ptrToStringArray);
+
+/* Output string with the <, &, and > characters HTML-escaped. 
+	's' is null-terminated. Returns cgiFormIO in the event
+	of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiHtmlEscape(char *s);
+
+/* Output data with the <, &, and > characters HTML-escaped. 
+	'data' is not null-terminated; 'len' is the number of
+	bytes in 'data'. Returns cgiFormIO in the event
+	of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiHtmlEscapeData(char *data, int len);
+
+/* Output string with the " character HTML-escaped, and no
+	other characters escaped. This is useful when outputting
+	the contents of a tag attribute such as 'href' or 'src'.
+	's' is null-terminated. Returns cgiFormIO in the event
+	of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiValueEscape(char *s);
+
+/* Output data with the " character HTML-escaped, and no
+	other characters escaped. This is useful when outputting
+	the contents of a tag attribute such as 'href' or 'src'.
+	'data' is not null-terminated; 'len' is the number of
+	bytes in 'data'. Returns cgiFormIO in the event
+	of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiValueEscapeData(char *data, int len);
+
+#endif /* CGI_C */
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.html
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.html	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.html	(revision 221)
@@ -0,0 +1,2116 @@
+<html>
+<head>
+<title>cgic: an ANSI C library for CGI Programming</title>
+</head>
+<body>
+<h1>cgic 2.05: an ANSI C library for CGI Programming</h1>
+<h2>By <a href="http://www.boutell.com/boutell/">Thomas Boutell</a></h2>
+<em><a href="http://www.boutell.com/cgic/">
+The LATEST documentation is available here. Check often
+for new releases.</a></em>
+<blockquote>
+<strong>IMPORTANT NOTICES:</strong>
+<p>
+If you have CGIC 1.05 or earlier, you should upgrade to CGIC 1.07,
+or to CGIC 2.02 or better, in order to obtain important security fixes.
+<p>
+If you have CGIC 2.0 or CGIC 2.01 and you use the cgiCookie routines, 
+you should upgrade to CGIC 2.02 or better, in order to obtain
+important security fixes.
+</blockquote>
+<h3>Table of Contents</h3>
+<ul>
+<li><a href="#credits">Credits and license terms</a>
+<li><a href="#support">How to get support</a>
+<li><a href="#whatsnew205">What's new in version XYZ of CGIC?</a>
+<li><a href="#whatis">What is cgic?</a>
+<li><a href="#obtain">Obtaining cgic</a>
+<li><a href="#build">Building and testing cgic: a sample application</a>
+<li><a href="#nocompile">What to do if it won't compile</a>
+<li><a href="#howto">How to write a cgic application</a>
+<li><a href="#images">How can I generate images from my cgic application?</a>
+<li><a href="#debug">CGI debugging features: using capture</a>
+<li><a href="#functions">cgic function reference</a>
+<li><a href="#variables">cgic variable reference</a>
+<li><a href="#resultcodes">cgic result code reference</a>
+<li><a href="#index">cgic quick index</a>
+</ul>
+
+<h3><a name="credits">Credits and License Terms</a></h3>
+
+cgic can be used free of charge, <strong>provided that a
+credit notice is provided online.</strong> Alternatively,
+a nonexclusive Commercial License can be purchased, which
+grants the right to use cgic without a public credit notice.
+<p>
+Please see the file
+<code><a href="license.txt">license.txt</a></code>
+for the details of the Basic License and Commercial License,
+including ordering information for the Commercial License.
+<p>
+Thanks are due to Robert Gustavsson, Ken Holervich, Bob Nestor, 
+Jon Ribbens, Thomas Strangert, Wu Yongwei, and other CGIC users 
+who have corresponded over the years. Although the implementation
+of multipart/form-data file upload support in CGIC 2.x is my own, 
+I particularly wish to thank those who submitted their own 
+implementations of this feature.
+<h3><a name="support">How to Get Support</a></h3>
+<blockquote>
+<h4>STOP! READ THIS FIRST! REALLY!</h4>
+    Are you getting a "server error," indicating that your web server
+    "cannot allow POST to this URL," or a similar message? <strong>YOU MUST
+    CONFIGURE YOUR WEB SERVER TO ALLOW CGI PROGRAMS, AND YOU MUST
+    INSTALL CGI PROGRAMS IN THE LOCATION (OR WITH THE EXTENSION) THAT
+    YOUR WEB SERVER EXPECTS TO SEE.</strong> Please don't send me email about
+    this, unless you wish me to configure your web server for you; I can
+    certainly do that for $50/hr, but you can probably straighten this out
+    yourself or have your web server administrator do it.
+</blockquote>
+<h4>Free Support</h4>
+Please submit support inquiries about CGIC via our                              <a href="http://www.boutell.com/contact/">contact page</a>.
+Please note that we receive a large volume of inquiries and cannot always
+respond personally. Sometimes
+the response must take the form of an eventual
+new release or an addition to a FAQ or other document, as opposed to an
+detailed individual response.
+<h4>Hourly Support</h4>
+Those requiring support in detail may arrange for direct support
+from the author, Thomas Boutell, at the rate of $50/hr, billed
+directly by credit card. To make arrangements, contact us via our
+our <a href="https://www.boutell.com/freeform/">secure
+message page</a>. To avoid delay, be sure to specifically mention
+that you wish to purchase CGIC support at the hourly rate above.
+<h3><a name="whatsnew205">What's new in version 2.05?</a></h3>
+Uploaded files properly closed; corrects a resource leak and enables
+file uploads to work properly on platforms with particular file
+locking semantics.
+<h3><a name="whatsnew204">What's new in version 2.04?</a></h3>
+Documentation fixes: the cgiHtmlEscape, cgiHtmlEscapeData,
+cgiValueEscape, and cgiValueEscapeData routines were named
+incorrectly in the manual. No code changes in version 2.04.
+<h3><a name="whatsnew203">What's new in version 2.03?</a></h3>
+<ul>
+<li>Support for setting cookies has been reimplemented. The new
+code closely follows the actual practice of web sites that successfully
+use cookies, rather than attempting to implement the specification.
+The new code can successfully set more than one cookie at a time in
+typical web browsers.
+</ul>
+<h3><a name="whatsnew202">What's new in version 2.02?</a></h3>
+<ul>
+<li>In CGIC 2.0 and 2.01, if the HTTP_COOKIE environment variable
+was exactly equal to the name of a cookie requested with cgiCookieString,
+with no value or equal sign or other characters present, a buffer
+overrun could take place. This was not normal behavior and it is
+unknown whether any actual web server would allow it to occur, however
+we have of course released a patch to correct it. 
+Thanks to Nicolas Tomadakis.
+<li>cgiCookieString returned cgiFormTruncated when cgiFormSuccess would
+be appropriate. Fixed; thanks to Mathieu Villeneuve-Belair.
+<li>Cookies are now set using a simpler Set-Cookie: header, and with
+one header line per cookie, based on data collected by Chunfu Lai. 
+<li>Memory leaks in cgiReadEnvironment fixed by Merezko Oleg. These
+memory leaks were <em>not</em> experienced in a normal CGI situation, only
+when reading a saved CGI environment.
+</ul>
+<h3><a name="whatsnew201">What's new in version 2.01?</a></h3>
+<ul>
+<li>Makefile supports "make install"
+<li>Compiles without warnings under both C and C++ with strict
+warnings and strict ANSI compliance enabled
+<li>Builds out of the box on Windows (#include &lt;fcntl.h&gt; was needed)
+<li>Rare problem in cgiReadEnvironment corrected; no impact on
+normal CGI operations
+<li>cgiCookieString now sets the result to an empty string
+when returning cgiFormNotFound
+<li>Minor code cleanups
+</ul>
+<h3><a name="whatsnew200">What's new in version 2.0?</a></h3>
+1. CGIC 2.0 provides support for file upload fields. User-uploaded
+files are kept in temporary files, to avoid the use of
+excessive swap space (Solaris users may wish to change the
+<code>cgicTempDir</code> macro in cgic.c before compiling).
+The <code><a href="#cgiFormFileName">cgiFormFileName</a></code>, 
+<code><a href="#cgiFormFileContentType">cgiFormFileContentType</a></code>, 
+<code><a href="#cgiFormFileSize">cgiFormFileSize</a></code>, 
+<code><a href="#cgiFormFileOpen">cgiFormFileOpen</a></code>, 
+<code><a href="#cgiFormFileRead">cgiFormFileRead</a></code>, and
+<code><a href="#cgiFormFileClose">cgiFormFileClose</a></code> functions
+provide a complete interface to this new functionality. Remember,
+the <code>enctype</code> attribute of the <code>form</code> tag
+must be set to <code>multipart/form-data</code> when
+<code>&lt;input type="file"&gt;</code> tags are used.
+<p>
+2. CGIC 2.0 provides support for setting and examining cookies
+(persistent data storage on the browser side).
+The <code><a href="#cgiCookieString">cgiCookieString</a></code>,
+and <code><a href="#cgiCookieInteger">cgiCookieInteger</a></code>
+and <code><a href="#cgiCookies">cgiCookies</a></code>
+functions retrieve cookies. The 
+<code><a href="#cgiHeaderCookieSetString">cgiHeaderCookieSetString</a></code>
+and <code><a href="#cgiHeaderCookieSetInteger">cgiHeaderCookieSetInteger</a></code> functions set cookies.
+<p>
+3. CGIC 2.0 offers a convenient way to retrieve a list of all form fields.
+The new <code><a href="#cgiFormEntries">cgiFormEntries</a></code>
+function performs this operation.
+<p>
+4. CGIC 2.0 provides convenience functions to correctly escape
+text before outputting it as part of HTML, or as part of the 
+value of a tag attribute, such as the <code>HREF</code> or
+<code>VALUE</code> attribute. See 
+<code><a href="#cgiHtmlEscape">cgiHtmlEscape</a></code>,
+<code><a href="#cgiHtmlEscapeData">cgiHtmlEscapeData</a></code>,
+<code><a href="#cgiValueEscape">cgiValueEscape</a></code> and
+<code><a href="#cgiValueEscapeData">cgiValueEscapeData</a></code>.
+<p>
+5. Users have often asked the correct way to determine which submit
+button was clicked. This could always be accomplished in previous versions,
+but CGIC 2.0 also provides 
+<a href="#cgiFormSubmitClicked">cgiFormSubmitClicked</a>,
+a convenient alternate label for the 
+<a href="#cgiFormCheckboxSingle">cgiFormCheckboxSingle</a> function.
+<h3><a name="whatsnew107">What's new in version 1.07?</a></h3>
+A problem with the cgiFormString and related functions has been
+corrected. These functions were previously incorrectly returning cgiFormTruncated
+in cases where the returned string fit the buffer exactly.
+<h3><a name="whatsnew106">What's new in version 1.06?</a></h3>
+1. A potentially significant buffer overflow problem has been
+corrected. Jon Ribbens correctly pointed out to me (and to the
+Internet's bugtraq mailing list) that the cgiFormEntryString
+function, which is used directly or indirectly by almost all
+CGIC programs, can potentially write past the buffer passed
+to it by the programmer. This bug has been corrected.
+Upgrading to version 1.06 is <strong>strongly recommended.</strong>
+<P>
+2. The function <code>cgiSaferSystem()</code> has been
+removed entirely. This function escaped only a few metacharacters,
+while most shells have many, and there was no way to account for
+the many different operating system shells that might be in use
+on different operating systems. Since this led to a false sense
+of security, the function has been removed. It is our recommendation
+that user input should never be passed directly on the command line
+unless it has been carefully shown to contain only characters
+regarded as safe and appropriate by the programmer. Even then, it is
+better to design your utilities to accept their input from standard
+input rather than the command line.
+<h3><a name="whatsnew105">What's new in version 1.05?</a></h3>
+Non-exclusive commercial license fee reduced to $200.
+<h3><a name="whatsnew104">What's new in version 1.04?</a></h3>
+For consistency with other packages, the standard Makefile
+now produces a true library for cgic (libcgic.a). 
+<h3><a name="whatsnew103">What's new in version 1.03?</a></h3>
+Version 1.03 sends line feeds only (ascii 10) to end 
+Content-type:, Status:, and other HTTP protocol output lines,
+instead of CR/LF sequences. The standard specifies CR/LF.
+Unfortunately, too many servers reject CR/LF to make
+implementation of that standard practical. No server
+tested ever rejects LF alone in this context. 
+<h3><a name="whatsnew102">What's new in version 1.02?</a></h3>
+Version 1.02 corrects bugs in previous versions:
+<ul>
+<li>
+<a href="#cgiFormDoubleBounded">cgiFormDoubleBounded</a> specified
+its arguments in the wrong order, with surprising results.
+This bug has been corrected.
+<li>
+Many small changes have been made to increase compatibility.
+cgic now compiles with no warnings under the compilers
+available at boutell.com.
+</ul>
+<h3><a name="whatsnew101">What's new in version 1.01?</a></h3>
+Version 1.01 adds no major functionality but corrects 
+significant bugs and incompatibilities:
+<ul>
+<li>
+<a href="#cgiFormInteger">cgiFormInteger</a>,
+<a href="#cgiFormIntegerBounded">cgiFormIntegerBounded</a>,
+<a href="#cgiFormDouble">cgiFormDouble</a> and
+<a href="#cgiFormDoubleBounded">cgiFormDoubleBounded</a> now
+accept negative numbers properly. They also accept positive
+numbers with an explicit + sign.
+<li>Hex values containing the digit <code>9</code> are
+now properly decoded.
+<li><a href="#cgiFormString">cgiFormString</a> now
+represents each newline as a single line feed (ascii 10 decimal)
+as described in the documentation, not a carriage return
+(ascii 13 decimal) as in version 1.0. The latter approach
+pleased no one.
+<li><a href="#cgiFormString">cgiFormString</a> and
+<a href="#cgiFormStringNoNewlines">cgiFormStringNoNewlines</a>
+no longer erroneously return cgiFormEmpty in place of
+cgiFormSuccess.
+<li>The main() function of cgic now flushes standard output
+and sleeps for one second before exiting in order to inhibit
+problems with the completion of I/O on some platforms. This was
+not a cgic bug per se, but has been reported as a common problem
+with CGI when used with the CERN server. This change should
+improve compatibility.
+<li>The single selection example in the testform.html
+example now works properly. This was an error in the
+form itself, not cgic.
+<li><a href="#cgiRemoteUser">cgiRemoteUser</a> and
+<a href="#cgiRemoteIdent">cgiRemoteIdent</a> are now
+documented accurately. They were reversed earlier.
+</ul>
+<h3><a name="whatis">What is cgic?</a></h3>
+cgic is an ANSI C-language library for the creation of CGI-based
+World Wide Web applications. For basic information about
+the CGI standard, see the <a href="http://hoohoo.ncsa.uiuc.edu/cgi/">
+CGI documentation</a> at NCSA.
+<p>
+cgic performs the following tasks:
+<ul>
+<li>Parses form data, correcting for defective and/or inconsistent browsers
+<li>Transparently accepts both GET and POST form data
+<li>Accepts uploaded files as well as regular form fields
+<li>Provides functions to set and retrieve "cookies"
+(browser-side persistent information)
+<li>Handles line breaks in form fields in a consistent manner
+<li>Provides string, integer, floating-point, and single- and
+multiple-choice functions to retrieve form data
+<li>Provides bounds checking for numeric fields
+<li>Loads CGI environment variables into C strings which are always non-null
+<li>Provides a way to capture CGI situations for replay in a debugging
+environment, including file uploads and cookies
+</ul>
+<p>
+cgic is compatible with any CGI-compliant server environment, and
+compiles without modification in Posix/Unix/Linux and Windows
+environments.
+<h3><a name="obtain">Obtaining cgic</a></h3>
+cgic is distributed via the web in two forms: as a Windows-compatible
+.ZIP file, and as a gzipped tar file. Most users of Windows and
+related operating systems have access to 'unzip' or 'pkunzip'. All modern Unix 
+systems come with 'gunzip' and 'tar' as standard equipment, and gzip/gunzip
+is not difficult to find if yours does not. Versions
+of these programs for other operating systems are widely
+available if you do not already have them.
+<p>
+<strong>Important:</strong> to use cgic, you will need an ANSI-standard
+C compiler. Under Unix, just obtain and use gcc. Most Unix systems have
+standardiszed on gcc. Users of Windows operating systems should not have
+ANSI C-related problems as all of the popular compilers follow the ANSI 
+standard.
+<p>
+<strong>Note for Windows Programmers:</strong> you must use a modern
+32-bit compiler. Visual C++ 2.0 or higher, Borland C++ and the
+mingw32 gcc compiler are all appropriate, as is cygwin. Do 
+<strong>NOT</strong> use an ancient 16-bit DOS executable compiler, please.
+<blockquote>
+<h4>What Operating System Does Your WEB SERVER Run?</h4>
+Remember, the computer on your desk is usually NOT your web server.
+Compiling a Windows console executable will not give you a CGI program that
+can be installed on a Linux-based server. 
+</blockquote>
+Your web browser should inquire whether to save the file to disk
+when you select one of the links below. Under Unix and compatible
+operating systems, save it, then issue the following
+commands to unpack it:
+<pre>
+gunzip cgic205.tar.gz
+tar -xf cgic205.tar
+</pre>
+This should produce the subdirectory 'cgic205', which will contain
+the complete cgic distribution for version 2.05, including a copy of this 
+documentation in the file cgic.html.
+<p>
+Under Windows and compatible operating systems, save it,
+open a console ("DOS") window, and issue the following commands to unpack it:
+<pre>
+unzip /d cgic205.zip
+</pre>
+Or use the unzip utility of your choice.
+<p>
+This command also produces the subdirectory 'cgic205', which will contain
+the complete cgic distribution for version 2.0, including a copy of this 
+documentation in the file cgic.html.
+<p>
+cgic is available via the web from www.boutell.com:
+<ul>
+<li><a href="http://www.boutell.com/cgic/cgic205.tar.gz">Obtain cgic: gzipped tar file</a>
+<li><a href="http://www.boutell.com/cgic/cgic205.zip">Obtain cgic: .ZIP file</a>
+</ul>
+<h3><a name="build">Building cgic: a sample application</a></h3>
+The sample application 'cgictest.c' is provided as part of the
+cgic distribution. This CGI program displays an input form, 
+accepts a submission, and then displays what was submitted.
+In the process essentially all of cgic's features are tested.
+<p>
+On a Unix system, you can build cgictest simply by typing
+'make cgictest.cgi'. cgic.c and cgictest.c will be compiled and linked
+together to produce the cgictest application. Under non-Unix
+operating systems, you will need to create and compile an appropriate
+project containing the files cgic.c and cgictest.c. 
+<p>
+<strong>IMPORTANT:</strong> after compiling cgictest.cgi, you will
+need to place it in a location on your server system which is
+designated by your server administrator as an appropriate location
+for CGI scripts. Some servers are configured to recognize any
+file ending in .cgi as a CGI program when it is found in any
+subdirectory of the server's web space, but this is not always
+the case! The right locations for CGI
+programs vary greatly from one server to another. Resolving
+this issue is between you, your web server administrator,
+and your web server documentation. Before submitting a bug
+report for cgic, make certain that the CGI example programs
+which came with your server <em>do</em> work for you. Otherwise
+it is very likely that you have a server configuration problem.
+<p>
+Once you have moved cgictest.cgi (or cgictest.exe, under Windows)
+to an appropriate cgi directory,
+use the web browser of your choice to access the URL at which
+you have installed it 
+(for instance, <code>www.mysite.com/cgi-bin/cgictest.cgi</code>).
+Fill out the various fields in any manner you wish, then
+select the SUBMIT button.
+<p>
+If all goes well, cgictest.cgi will respond with a page which
+indicates the various settings you submitted. If not,
+please reread the section above regarding the correct location in
+which to install your CGI program on your web server.
+<h3><a name="nocompile">What to do if it won't compile</a></h3>
+<ul>
+<li><strong>Are you using Visual C++ or Borland C++? Did you forget to add
+cgic.c to your project?</strong>
+<li><strong>Make sure you are using an ANSI C or C++ compiler.</strong>
+(All of the Windows compilers are ANSI C compliant.)
+</ul>
+If none of the above proves effective, please see the
+section regarding <a href="#support">support</a>.
+<h3><a name="howto">How to write a cgic application</a></h3>
+<em>Note: </em> All cgic applications must be linked to the cgic.c module
+itself. How to do this depends on your operating system; under Unix,
+just use the provided Makefile as an example.
+<p>
+Since all CGI applications must perform certain initial
+tasks, such as parsing form data and examining
+environment variables, the cgic library provides its
+own main() function. When you write applications that
+use cgic, you will begin your own programs by writing
+a cgiMain() function, which cgic will invoke when
+the initial cgi work has been successfully completed. Your
+program must also be sure to #include the file cgic.h.
+<p>
+<strong>Important:</strong> if you write your own main()
+function, your program will not link properly. Your own
+code should begin with cgiMain(). The library
+provides main() for you. (Those who prefer different behavior
+can easily modify cgic.c.)
+<p>
+Consider the cgiMain function of cgictest.c:
+<p>
+<PRE>
+int cgiMain() {
+#ifdef DEBUG
+  LoadEnvironment();
+#endif /* DEBUG */
+  /* Load a previously saved CGI scenario if that button
+    has been pressed. */
+  if (cgiFormSubmitClicked("loadenvironment") == cgiFormSuccess) {
+    LoadEnvironment();
+  }
+  /* Set any new cookie requested. Must be done *before*
+    outputting the content type. */
+  CookieSet();
+  /* Send the content type, letting the browser know this is HTML */
+  cgiHeaderContentType("text/html");
+  /* Top of the page */
+  fprintf(cgiOut, "&lt;HTML&gt;&lt;HEAD&gt;\n");
+  fprintf(cgiOut, "&lt;TITLE&gt;cgic test&lt;/TITLE&gt;&lt;/HEAD&gt;\n");
+  fprintf(cgiOut, "&lt;BODY&gt;&lt;H1&gt;cgic test&lt;/H1&gt;\n");
+  /* If a submit button has already been clicked, act on the 
+    submission of the form. */
+  if ((cgiFormSubmitClicked("testcgic") == cgiFormSuccess) ||
+    cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess)
+  {
+    HandleSubmit();
+    fprintf(cgiOut, "&lt;hr&gt;\n");
+  }
+  /* Now show the form */
+  ShowForm();
+  /* Finish up the page */
+  fprintf(cgiOut, "&lt;/BODY&gt;&lt;/HTML&gt;\n");
+  return 0;
+}
+</PRE>
+Note the DEBUG #ifdef. If DEBUG is defined at compile time, either by
+inserting the line "#define DEBUG 1" into the program or by setting
+it in the Makefile or other development environment, then the
+LoadEnvironment function is invoked. This function calls 
+<a href="#cgiReadEnvironment">cgiReadEnvironment()</a> 
+to restore a captured CGI environment for debugging purposes. See
+also the discussion of the <a href="#debug">capture</a> program, which is
+provided for use in CGI debugging. Because this is a test program,
+the <a href="#cgiFormSubmitClicked">cgiFormSubmitClicked</a> function is
+also called to check for the use of a button that requests the reloading
+of a saved CGI environment. A completed CGI program typically would
+never allow the end user to make that decision.
+<h4>Setting Cookies</h4>
+Next, one of the cgiHeader functions should be called.
+This particular program demonstrates many features, including
+the setting of cookies. If the programmer wishes to set a cookie,
+the cookie-setting function must be called
+first, before other headers are output. This is done by the
+CookieSet() function of cgictest.c:
+<pre>
+void CookieSet()
+{
+  char cname[1024];
+  char cvalue[1024];
+  /* Must set cookies BEFORE calling 
+    cgiHeaderContentType */
+  cgiFormString("cname", cname, sizeof(cname));  
+  cgiFormString("cvalue", cvalue, sizeof(cvalue));  
+  if (strlen(cname)) {
+    /* Cookie lives for one day (or until 
+      browser chooses to get rid of it, which 
+      may be immediately), and applies only to 
+      this script on this site. */  
+    cgiHeaderCookieSetString(cname, cvalue,
+      86400, cgiScriptName, cgiServerName);
+  }
+}
+</pre>
+Since this is a test program, the <a href="#cgiFormString">cgiFormString</a> 
+function is used to fetch the name and value from the form previously filled
+in by the user. Normally, cookie names and values are chosen to meet the
+needs of the programmer and provide a means of identifying the same
+user again later.
+<p>
+The <a href="#cgiHeaderCookieSetString">cgiHeaderCookieSetString</a>
+function sets the cookie by requesting that the web browser store it.
+<strong>There is never any guarantee that this will happen!</strong>
+Many browsers reject cookies completely; others do not necessarily keep
+them as long as requested or return them with their values intact.
+Always code defensively when using cookies.
+<p>
+The cname and cvalue parameters are of course the namd and value for
+the cookie. The third argument is the time, in seconds, that the
+cookie should "live" on the browser side before it expires; in this
+case it has been set to 86,400 seconds, which is exactly one day. 
+<strong>The browser may or may not respect this setting, as with everything
+else about cookies.</strong>
+<p>
+The fourth argument identifies the "path" within the web site for which
+the cookie is considered valid. A cookie that should be sent back
+for every access to the site should be set with a path of <code>/</code>.
+In this case the cookie is relevant only to the CGI program itself, so
+<code><a href="#cgiScriptName">cgiScriptName</a></code> (the URL of the CGI program, not including the
+domain name) is sent. Similarly, a cookie can be considered relevant
+to a single web site or to an entire domain, such as 
+<code>www.boutell.com</code> or the entire <code>.boutell.com</code>
+domain. In this case, the current site on which the program is running
+is the only relevant site, so <code><a href="#cgiServerName">cgiServerName</a></code> is used
+as the domain.
+<h4>Outputting the Content Type Header</h4>
+Next, <a href="#cgiHeaderContentType">cgiHeaderContentType()</a> is 
+called to indicate the MIME type of the document being output, in this case 
+"text/html" (a normal HTML document). A few other common MIME types are
+"image/gif", "image/jpeg" and "audio/wav". 
+<p>
+Note that <a href="#cgiHeaderStatus">cgiHeaderStatus()</a> or 
+<a href="#cgiHeaderLocation">cgiHeaderLocation()</a> could have
+been invoked instead to output an error code or redirect the
+request to a different URL. Only one of the cgiHeader functions
+should be called in a single execution of the program.
+<p>
+<strong>Important:</strong> one of the cgiHeader functions,
+usually <a href="#cgiHeaderContentType">cgiHeaderContentType()</a>, 
+<em>must</em> be invoked before outputting any other
+response to the user. Otherwise, the result will not be a valid
+document and the browser's behavior will be unpredictable.
+You may, of course, output your own ContentType and other
+header information to <a href="#cgiOut">cgiOut</a> if you prefer. The cgiHeader functions
+are provided as a convenience.
+<h4>Handling Form Submissions</h4>
+Like many CGI programs, cgictest makes decisions about the way it
+should behave based on whether various submit buttons have been clicked.
+When either the testcgic or saveenvironment button is present, cgictest
+invokes the HandleSubmit function, which invokes additional functions to
+handle various parts of the form:
+<pre>
+void HandleSubmit()
+{
+  Name();
+  Address();
+  Hungry();
+  Temperature();
+  Frogs();
+  Color();
+  Flavors();
+  NonExButtons();
+  RadioButtons();
+  File();
+  Entries();
+  Cookies();
+  /* The saveenvironment button, in addition to submitting 
+    the form, also saves the resulting CGI scenario to 
+    disk for later replay with the 'load saved environment' 
+    button. */
+  if (cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess) {
+    SaveEnvironment();
+  }
+}
+</pre>
+<h4>Handling Text Input</h4>
+The Name() function of cgictest is shown below, in its simplest
+possible form:
+<PRE>
+void Name() {
+        char name[81];
+        <a href="#cgiFormStringNoNewlines">cgiFormStringNoNewlines</a>("name", name, 81);
+        fprintf(cgiOut, "Name: ");
+        cgicHtmlEscape(name);
+        fprintf(cgiOut, "<BR>\n");
+}
+</PRE>
+The purpose of this function is to retrieve and display the name that was
+input by the user. Since the programmer has decided that names should
+be permitted to have up to 80 characters, a buffer of 81 characters
+has been declared (allowing for the final null character). 
+The <a href="#cgiFormStringNoNewlines">cgiFormStringNoNewlines()</a>
+function is then invoked to retrieve the name and ensure that
+carriage returns are not present in the name (despite the
+incorrect behavior of some web browsers). The first argument
+is the name of the input field in the form, the second argument
+is the buffer to which the data should be copied, and the third
+argument is the size of the buffer. cgic will never write beyond
+the size of the buffer, and will always provide a null-terminated
+string in response; if the buffer is too small, the string will
+be shortened. If this is not acceptable, the
+<a href="#cgiFormStringSpaceNeeded">cgiFormStringSpaceNeeded()</a>
+function can be used to check the amount of space needed; the
+return value of cgiFormStringNoNewlines() can also be checked
+to determine whether truncation occurred. See
+the full description of <a href="#cgiFormStringNoNewlines">
+cgiFormStringNoNewlines()</a>.
+<h4>Handling Output</h4>
+Note that Name() writes its HTML output to <a href="#cgiOut">cgiOut</a>, not
+to stdout.
+<p>
+The actual name submitted by the user may or may not contain
+characters that have special meaning in HTML, specifically the
+the <code>&lt;</code>, <code>&gt;</code>, and <code>&amp;</code> characters.
+The <a href="#cgiHtmlEscape">cgiHtmlEscape</a> function is used to output
+the user-entered name with any occurrences of these characters
+correctly escaped as <code>&amp;lt;</code>, <code>&amp;gt;</code>, 
+and <code>&amp;amp;</code>.
+<p>
+<strong>Important:</strong> <a href="#cgiOut">cgiOut</a> is normally equivalent
+to stdout, and there is no performance penalty for using it.
+It is recommended that you write output to <a href="#cgiOut">cgiOut</a> to ensure compatibility
+with modified versions of the cgic library for special
+environments that do not provide stdin and stdout for
+each cgi connection.
+<p>
+Note that, for text input areas in which carriage returns <em>are</em>
+desired, the function <a href="#cgiFormString">cgiFormString</a>
+should be used instead. cgiFormString ensures that line breaks
+are always represented by a single carriage return (ascii decimal 13),
+making life easier for the programmer. See the source code to
+the Address() function of cgictest.c for an example.
+<h4>Handling Single Checkboxes</h4>
+Consider the Hungry() function, which determines whether
+the user has selected the "hungry" checkbox:
+<PRE>
+void Hungry() {
+        if (<a href="#cgiFormCheckboxSingle">cgiFormCheckboxSingle</a>("hungry") == <a href="#cgiFormSuccess">cgiFormSuccess</a>) {
+                fprintf(cgiOut, "I'm Hungry!&lt;BR&gt;\n");
+        } else {
+                fprintf(cgiOut, "I'm Not Hungry!&lt;BR&gt;\n");
+        }
+}
+</PRE>
+This function takes advantage of the
+<a href="#cgiFormCheckboxSingle">cgiFormCheckboxSingle()</a> function, which
+determines whether a single checkbox has been selected. 
+cgiFormCheckboxSingle() accepts the name attribute of the checkbox
+as its sole argument and returns <a href="#cgiFormSuccess">
+cgiFormSuccess</a> if the checkbox is selected, or 
+<a href="#cgiFormNotFound">cgiFormNotFound</a> if it is not.
+If multiple checkboxes with the same name are in use,
+consider the <a href="#cgiFormCheckboxMultiple">
+cgiFormCheckboxMultiple()</a> and 
+<a href="#cgiFormStringMultiple">cgiFormStringMultiple()</a>
+functions.
+<h4>Handling Numeric Input</h4>
+Now consider the Temperature() function, which retrieves
+a temperature in degrees (a floating-point value) and ensures
+that it lies within particular bounds:
+<PRE>
+void Temperature() {
+        double temperature;
+        <a href="#cgiFormDoubleBounded">cgiFormDoubleBounded</a>("temperature", &amp;temperature, 80.0, 120.0, 98.6);
+        fprintf(<a href="#cgiOut">cgiOut</a>, "My temperature is %f.&lt;BR&gt;\n", temperature);
+}
+</PRE>
+The temperature is retrieved by the function 
+<a href="#cgiFormDoubleBounded">cgiFormDoubleBounded()</a>. The first
+argument is the name of the temperature input field in the form;
+the second argument points to the address of the variable that will 
+contain the result. The next two arguments are the lower and upper
+bounds, respectively. The final argument is the default value to
+be returned if the user did not submit a value.
+<p>
+This function always retrieves a reasonable value within the
+specified bounds; values above or below bounds are constrained
+to fit the bounds. However, the return value of
+cgiFormDoubleBounded can be checked to make sure the
+actual user entry was in bounds, not blank, and so forth;
+see the description of <a href="#cgiFormDoubleBounded">
+cgiFormDoubleBounded()</a> for more details. If bounds checking
+is not desired, consider using <a href="#cgiFormDouble">
+cgiFormDouble()</a> instead.
+<p>
+Note that, for integer input, the functions
+<a href="#cgiFormInteger">cgiFormInteger</a> and
+<a href="#cgiFormIntegerBounded">cgiFormIntegerBounded</a>
+are available. The behavior of these functions is similar to
+that of their floating-point counterparts above.
+<h4>Handling Single-Choice Input</h4>
+The &lt;SELECT&gt; tag of HTML is used to provide the user with
+several choices. Radio buttons and checkboxes can also be used
+when the number of choices is relatively small. Consider
+the Color() function of cgictest.c:
+<PRE>
+char *colors[] = {
+        "Red",
+        "Green",
+        "Blue"
+};
+
+void Color() {
+        int colorChoice;
+        <a href="#cgiFormSelectSingle">cgiFormSelectSingle</a>("colors", colors, 3, &amp;colorChoice, 0);
+        fprintf(<a href="#cgiOut">cgiOut</a>, "I am: %s&lt;BR&gt;\n", colors[colorChoice]);
+}
+</PRE>
+This function determines which of several colors the user chose
+from a &lt;SELECT&gt; list in the form. An array of colors is
+declared; the <a href="#cgiFormSelectSingle">cgiFormSelectSingle()</a>
+function is then invoked to determine which, if any, of those choices
+was selected. The first argument indicates the name of the input
+field in the form. The second argument points to the list of
+acceptable colors. The third argument indicates the number of
+entries in the color array. The fourth argument points to the
+variable which will accept the chosen color, and the last argument
+indicates the index of the default value to be set if no
+selection was submitted by the browser. 
+<p>
+<a href="#cgiFormSelectSingle">cgiFormSelectSingle()</a> will
+always indicate a reasonable selection value. However, if
+the programmer wishes to know for certain that a value was
+actually submitted, that the value submitted was a legal
+response, and so on, the return value of cgiFormSelectSingle()
+can be consulted. See the full description of
+<a href="#cgiFormSelectSingle">cgiFormSelectSingle()</a> for
+more information.
+<p>
+Note that radio button groups and &lt;SELECT&gt; lists can both
+be handled by this function. If you are processing radio
+button groups, you may prefer to invoke 
+<a href="#cgiFormRadio">cgiFormRadio()</a>, which functions
+identically. 
+<p>
+<em>"What if I won't know the acceptable choices at runtime?"</em>
+<p>
+If the acceptable choices aren't known <em>until</em> runtime,
+one can simply load the choices from disk. But if the acceptable
+choices aren't fixed at all (consider a list of country names;
+new names may be added to the form at any time and it is
+inconvenient to also update program code or a separate list
+of countries), simply invoke 
+<a href="#cgiFormStringNoNewlines">cgiFormStringNoNewlines()</a>
+instead to retrieve the string directly. Keep in mind that, if
+you do so, validating the response to make sure it is
+safe and legitimate becomes a problem for your own
+program to solve. The advantage of cgiFormSelectSingle() is that invalid 
+responses are never returned.
+<p>
+To handle multiple-selection &lt;SELECT&gt; lists and
+groups of checkboxes with the same name, see the
+discussion of the NonExButtons() function of cgictest.c, immediately below.
+<h4>Handling Multiple-Choice Input</h4>
+Consider the first half of the NonExButtons() function of cgictest.c:
+<PRE>
+char *votes[] = {
+  "A",
+  "B",
+  "C",
+  "D"
+};
+
+void NonExButtons() {
+  int voteChoices[4];
+  int i;
+  int result;  
+  int invalid;
+
+  char **responses;
+
+  /* Method #1: check for valid votes. This is a good idea,
+    since votes for nonexistent candidates should probably
+    be discounted... */
+  fprintf(<a href="#cgiOut">cgiOut</a>, "Votes (method 1):&lt;BR&gt;\n");
+  result = <a href="#cgiFormCheckboxMultiple">cgiFormCheckboxMultiple</a>("vote", votes, 4, 
+    voteChoices, &amp;invalid);
+  if (result == <a href="#cgiFormNotFound">cgiFormNotFound</a>) {
+    fprintf(<a href="#cgiOut">cgiOut</a>, "I hate them all!&lt;p&gt;\n");
+  } else {  
+    fprintf(<a href="#cgiOut">cgiOut</a>, "My preferred candidates are:\n");
+    fprintf(<a href="#cgiOut">cgiOut</a>, "&lt;ul&gt;\n");
+    for (i=0; (i &lt; 4); i++) {
+      if (voteChoices[i]) {
+        fprintf(<a href="#cgiOut">cgiOut</a>, "&lt;li&gt;%s\n", votes[i]);
+      }
+    }
+    fprintf(<a href="#cgiOut">cgiOut</a>, "&lt;/ul&gt;\n");
+  }
+</PRE>
+This function takes advantage of
+<a href="#cgiFormCheckboxMultiple">cgiFormCheckboxMultiple()</a>,
+which is used to identify one or more selected checkboxes with 
+the same name. This function performs identically to
+<a href="#cgiFormSelectMultiple">cgiFormSelectMultiple()</a>.
+That is, &lt;SELECT&gt; tags with the MULTIPLE attribute are handled
+just like a group of several checkboxes with the same name.
+<p>
+The first argument to <a href="#cgiFormCheckboxMultiple">
+cgiFormCheckboxMultiple()</a> is the name given to all
+checkbox input fields in the group. The second argument
+points to an array of legitimate values; these should
+correspond to the VALUE attributes of the checkboxes
+(or OPTION tags in a &lt;SELECT&gt; list). The third argument
+indicates the number of entries in the array of
+legitimate values. The fourth argument points to
+an array of integers with the same number of entries
+as the array of legitimate values; each entry
+will be set true if that checkbox or option was selected,
+false otherwise.
+<p>
+The last argument points to an integer which will be set to the 
+number of invalid responses (responses not in the array of
+valid responses) that were submitted. If this value is not
+of interest, the last argument may be a null pointer (0).
+<p>
+Note that the return value of cgiFormCheckboxMultiple is
+inspected to determine whether any choices at all were
+set. See the full description of
+<a href="#cgiFormCheckboxMultiple">cgiFormCheckboxMultiple</a>
+for other possible return values. 
+<p>
+<em>"What if I won't know the acceptable choices at runtime?"</em>
+<p>
+If the acceptable choices aren't known <em>until</em> runtime,
+one can simply load the choices from disk. But if the acceptable
+choices aren't fixed at all (consider a list of ice cream flavors;
+new names may be added to the form at any time and it is
+inconvenient to also update program code or a separate list
+of countries), a more dynamic approach is needed. Consider
+the second half of the NonExButtons() function of cgictest.c:
+<PRE>
+  /* Method #2: get all the names voted for and trust them.
+    This is good if the form will change more often
+    than the code and invented responses are not a danger
+    or can be checked in some other way. */
+  fprintf(<a href="#cgiOut">cgiOut</a>, "Votes (method 2):&lt;BR&gt;\n");
+  result = <a href="#cgiFormStringMultiple">cgiFormStringMultiple</a>("vote", &amp;responses);
+  if (result == <a href="#cgiFormNotFound">cgiFormNotFound</a>) {  
+    fprintf(<a href="#cgiOut">cgiOut</a>, "I hate them all!&lt;p&gt;\n");
+  } else {
+    int i = 0;
+    fprintf(<a href="#cgiOut">cgiOut</a>, "My preferred candidates are:\n");
+    fprintf(<a href="#cgiOut">cgiOut</a>, "&lt;ul&gt;\n");
+    while (responses[i]) {
+      fprintf(<a href="#cgiOut">cgiOut</a>, "&lt;li&gt;%s\n", responses[i]);
+      i++;
+    }
+    fprintf(<a href="#cgiOut">cgiOut</a>, "&lt;/ul&gt;\n");
+  }
+  /* We must be sure to free the string array or a memory
+    leak will occur. Simply calling free() would free
+    the array but not the individual strings. The
+    function cgiStringArrayFree() does the job completely. */  
+  <A HREF="#cgiStringArrayFree">cgiStringArrayFree</a>(responses);
+}
+</PRE>
+This code excerpt demonstrates an alternate means of retrieving
+a list of choices. The function
+<a href="#cgiFormStringMultiple">cgiFormStringMultiple()</a> is used
+to retrieve an array consisting of all the strings submitted
+for with a particular input field name. This works both for
+&lt;SELECT&gt; tags with the MULTIPLE attribute and for 
+groups of checkboxes with the same name. 
+<P>
+The first argument to <a href="#cgiFormStringMultiple">
+cgiFormStringMultiple()</a> is the name of the input field or
+group of input fields in question. The second argument should
+be the address of a pointer to a pointer to a string, which
+isn't as bad as it sounds. Consider the following simple call
+of the function:
+<PRE>
+/* An array of strings; each C string is an array of characters */
+char **responses; 
+
+<a href="#cgiFormStringMultiple">cgiFormStringMultiple</a>("vote", &amp;responses);
+</PRE>
+<em>"How do I know how many responses there are?"</em>
+<p>
+After the call, the last entry in the string array will be
+a null pointer. Thus the simple loop:
+<PRE>
+int i = 0;
+while (responses[i]) {
+  /* Do something with the string responses[i] */
+  i++;
+}
+</PRE>
+can be used to walk through the array until the last
+entry is encountered.
+<p>
+<strong>Important:</strong> the 
+<a href="#cgiFormStringMultiple">cgiFormStringMultiple</a> function
+returns a pointer to <strong>allocated memory</strong>. Your code
+should not modify the strings in the responses array or the responses
+array itself; if modification is needed, the strings should be
+copied. When your code is done examining the responses array,
+you <strong>MUST</strong> call <a href="#cgiStringArrayFree">
+cgiStringArrayFree()</a> with the array as an argument to free the memory 
+associated with the array. Otherwise, the memory will not be available 
+again until the program exists. <strong>Don't</strong> just call the 
+free() function; if you do, the individual strings will not be freed.
+<h4>Accessing Uploaded Files</h4>
+CGIC provides functions to access files that have been uploaded
+as part of a form submission. <strong>IMPORTANT: you MUST</strong> set
+the <code>enctype</code> attribute of your <code>form</code> tag
+to <code>multipart/form-data</code> for this feature to work! For an
+example, see the <a href="#ShowForm">ShowForm</a> function of 
+cgictest.c, examined below.
+<p>
+The <code>File</code> function of cgictest.c takes care of 
+receiving uploaded files:
+<pre>
+void File()
+{
+  cgiFilePtr file;
+  char name[1024];
+  char contentType[1024];
+  char buffer[1024];
+  int size;
+  int got;
+  if (cgiFormFileName("file", name, sizeof(name)) != 
+    cgiFormSuccess) 
+  {
+    printf("&lt;p&gt;No file was uploaded.&lt;p&gt;\n");
+    return;
+  } 
+        fprintf(cgiOut, "The filename submitted was: ");
+        cgiHtmlEscape(name);
+        fprintf(cgiOut, "&lt;p&gt;\n");
+        cgiFormFileSize("file", &size);
+        fprintf(cgiOut, "The file size was: %d bytes&lt;p&gt;\n", size);
+        cgiFormFileContentType("file", contentType, sizeof(contentType));
+        fprintf(cgiOut, "The alleged content type of the file was: ");
+        cgiHtmlEscape(contentType);
+        fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "Of course, this is only the claim the browser "
+    "made when uploading the file. Much like the filename, "
+    "it cannot be trusted.&lt;p&gt;\n");
+  fprintf(cgiOut, "The file's contents are shown here:&lt;p&gt;\n");
+  if (cgiFormFileOpen("file", &file) != cgiFormSuccess) {
+    fprintf(cgiOut, "Could not open the file.&lt;p&gt;\n");
+    return;
+  }
+  fprintf(cgiOut, "&lt;pre&gt;\n");
+  while (cgiFormFileRead(file, buffer, sizeof(buffer), &got) ==
+    cgiFormSuccess)
+  {
+    cgiHtmlEscapeData(buffer, got);
+  }
+  fprintf(cgiOut, "&lt;/pre&gt;\n");
+  cgiFormFileClose(file);
+}
+</pre>
+First, the File function checks to determine the filename that was
+submitted by the user. <strong>VERY IMPORTANT: this filename may or
+may not bear any relation to the real name of the file on the user's
+computer, may be deliberately manipulated with malicious intent,</strong>
+and should not be used for <strong>any</strong> purpose unless you have
+determined that its content is safe for your intended use and will not,
+at the very least, overwrite another file of importance to you, especially if
+you intend to use it as a file name on the server side. The cgic library
+itself does not use this file name for temporary storage.
+<p>
+If the <a href="#cgiFormFileName">cgiFormFileName</a> function does
+not succeed, no file was uploaded.
+<p>
+Next, the <a href="#cgiFormFileSize">cgiFormFileSize</a> function is called
+to determine the size of the uploaded file, in bytes.
+<p>
+The File function then proceeds to query the content type of the uploaded
+file.  Files uploaded by the user have their own content type information, 
+which may be useful in determining whether the file is an image, HTML document,
+word processing document, or other type of file. However,
+<strong>as with the filename and any other claim made by the browser,
+this information should not be blindly trusted.</strong> The browser
+may upload a file with the name <code>picture.jpg</code> and the
+content type <code>image/jpeg</code>, but this does not guarantee that the
+actual file will contain a valid JPEG image suitable for display.
+<p>
+The content type submitted by the browser can be queried using the
+<a href="#cgiFormFileContentType">cgiFormFileContentType</a> function.
+<p>
+Of course, CGIC also provides access to the actual uploded file. 
+First, the programmer calls <a href="#cgiFormFileOpen">cgiFormFileOpen</a>,
+passing the address of a <code>cgiFilePtr</code> object. If this function
+succeeds, the <code>cgiFilePtr</code> object becomes valid, and can be
+used in subsequent calls to <a href="#cgiFormFileRead">cgiFormFileRead</a>.
+Notice that the number of bytes read may be less than the number requested,
+in particular on the last successful call before cgiFormFileRead begins
+to return <code>cgiFormEOF</code>. When cgiFormFileRead no longer returns 
+cgiFormSuccess, 
+the programmer calls <a href="#cgiFormClose">cgiFormFileClose</a> to
+release the <code>cgiFilePtr</code> object.
+<p>
+The uploaded file data may contain anything, including binary data,
+null characters, and so on. The example program uses the 
+<a href="#cgiHtmlEscapeData">cgiHtmlEscapeData</a> function to output the
+data with any special characters that have meaning in HTML escaped.
+Most programs will save the uploaded information to a server-side file or
+database.
+<h4>Fetching All Form Entries</h4>
+From time to time, the programmer may not know the names of all
+form fields in advance. In such situations it is convenient to
+use the <a href="#cgiFormEntries">cgiFormEntries</a> function.
+The Entries function of cgictest.c demonstrates the use of
+cgiFormEntries:
+<pre>
+void Entries()
+{
+        char **array, **arrayStep;
+        fprintf(cgiOut, "List of All Submitted Form Field Names:&lt;p&gt;\n");
+        if (cgiFormEntries(&array) != cgiFormSuccess) {
+                return;
+        }
+        arrayStep = array;
+        fprintf(cgiOut, "&lt;ul&gt;\n");
+        while (*arrayStep) {
+                fprintf(cgiOut, "&lt;li&gt;");
+                cgiHtmlEscape(*arrayStep);
+                fprintf(cgiOut, "\n");
+                arrayStep++;
+        }
+        fprintf(cgiOut, "&lt;/ul&gt;\n");
+        cgiStringArrayFree(array);
+}
+</pre>
+The cgiFormEntries function retrieves an array of form field names.
+This array consists of pointers to strings, with a final null pointer
+to mark the end of the list. The above code illustrates one way of
+looping through the returned strings. Note the final call to
+<a href="#cgiStringArrayFree">cgiStringArrayFree</a>, which is
+essential in order to return the memory used to store the strings
+and the string array.
+<h4>Retrieving Cookies</h4>
+The Cookies function of cgictest.c displays a list of all cookies
+submitted by the browser with the current form submission, along
+with their values:
+<pre>
+void Cookies()
+{
+  char **array, **arrayStep;
+  char cname[1024], cvalue[1024];
+  fprintf(cgiOut, "Cookies Submitted On This Call, With Values "
+    "(Many Browsers NEVER Submit Cookies):&lt;p&gt;\n");
+  if (cgiCookies(&array) != cgiFormSuccess) {
+    return;
+  }
+  arrayStep = array;
+  fprintf(cgiOut, "&lt;table border=1&gt;\n");
+  fprintf(cgiOut, "&lt;tr&gt;&lt;th&gt;Cookie&lt;th&gt;Value&lt;/tr&gt;\n");
+  while (*arrayStep) {
+    char value[1024];
+    fprintf(cgiOut, "&lt;tr&gt;");
+    fprintf(cgiOut, "&lt;td&gt;");
+    cgiHtmlEscape(*arrayStep);
+    fprintf(cgiOut, "&lt;td&gt;");
+    cgiCookieString(*arrayStep, value, sizeof(value));
+    cgiHtmlEscape(value);
+    fprintf(cgiOut, "\n");
+    arrayStep++;
+  }
+  fprintf(cgiOut, "&lt;/table&gt;\n");
+  cgiFormString("cname", cname, sizeof(cname));  
+  cgiFormString("cvalue", cvalue, sizeof(cvalue));  
+  if (strlen(cname)) {
+    fprintf(cgiOut, "New Cookie Set On This Call:&lt;p&gt;\n");
+    fprintf(cgiOut, "Name: ");  
+    cgiHtmlEscape(cname);
+    fprintf(cgiOut, "Value: ");  
+    cgiHtmlEscape(cvalue);
+    fprintf(cgiOut, "&lt;p&gt;\n");
+    fprintf(cgiOut, "If your browser accepts cookies "
+      "(many do not), this new cookie should appear "
+      "in the above list the next time the form is "
+      "submitted.&lt;p&gt;\n"); 
+  }
+  cgiStringArrayFree(array);
+}
+</pre>
+<strong>VERY IMPORTANT: YOUR BROWSER MIGHT NOT SUBMIT COOKIES,
+EVER, REGARDLESS OF WHAT VALUES YOU ENTER INTO THE TEST FORM.</strong>
+Many, many browsers are configured not to accept or send cookies;
+others are configured to send them as little as possible to meet the
+bare minimum requirements for entry into popular sites. Users will often
+refuse your cookies; make sure your code still works in that situation!
+<p>
+The above code uses the <a href="#cgiCookies">cgiCookies</a> function
+to retrieve a list of all currently set cookies as a null-terminated
+array of strings. The <a href="#cgiCookieString">cgiCookieString</a>
+function is then used to fetch the value associated with each cookie;
+this function works much like <a href="#cgiFormString">cgiFormString</a>,
+discussed earlier. Note that a cookie set as a part of the current
+form submission process does not appear on this list immediately, as
+it has not yet been sent back by the browser. It should appear on
+future submissions, provided that the browser chooses to accept
+and resend the cookie at all.
+<h4>Displaying a Form That Submits to the Current Program</h4>
+CGI programmers often need to display HTML pages as part of the output
+of CGI programs; these HTML pages often contain forms which should submit
+fields back to the same program they came from. Provided that your
+web server is well-configured, this can be done conveniently using
+the cgiScriptName environment variable, as shown below. Here is the
+source code of the ShowForm function of cgictest.c:
+<pre>
+void ShowForm()
+{
+  fprintf(cgiOut, "&lt;!-- 2.0: multipart/form-data is required 
+    "for file uploads. --&gt;");
+  fprintf(cgiOut, "&lt;form method=\"POST\" "
+    "enctype=\"multipart/form-data\" ");
+  fprintf(cgiOut, "  action=\"");
+  cgiValueEscape(cgiScriptName);
+  fprintf(cgiOut, "\"&gt;\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "Text Field containing Plaintext\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;input type=\"text\" name=\"name\"&gt;Your Name\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "Multiple-Line Text Field\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;textarea NAME=\"address\" ROWS=4 COLS=40&gt;\n");
+  fprintf(cgiOut, "Default contents go here. \n");
+  fprintf(cgiOut, "&lt;/textarea&gt;\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "Checkbox\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;input type=\"checkbox\" name=\"hungry\" checked&gt;Hungry\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "Text Field containing a Numeric Value\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;input type=\"text\" name=\"temperature\" value=\"98.6\"&gt;\n");
+  fprintf(cgiOut, "Blood Temperature (80.0-120.0)\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "Text Field containing an Integer Value\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;input type=\"text\" name=\"frogs\" value=\"1\"&gt;\n");
+  fprintf(cgiOut, "Frogs Eaten\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "Single-SELECT\n");
+  fprintf(cgiOut, "&lt;br&gt;\n");
+  fprintf(cgiOut, "&lt;select name=\"colors\"&gt;\n");
+  fprintf(cgiOut, "&lt;option value=\"Red\"&gt;Red\n");
+  fprintf(cgiOut, "&lt;option value=\"Green\"&gt;Green\n");
+  fprintf(cgiOut, "&lt;option value=\"Blue\"&gt;Blue\n");
+  fprintf(cgiOut, "&lt;/select&gt;\n");
+  fprintf(cgiOut, "&lt;br&gt;\n");
+  fprintf(cgiOut, "Multiple-SELECT\n");
+  fprintf(cgiOut, "&lt;br&gt;\n");
+  fprintf(cgiOut, "&lt;select name=\"flavors\" multiple&gt;\n");
+  fprintf(cgiOut, "&lt;option value=\"pistachio\"&gt;Pistachio\n");
+  fprintf(cgiOut, "&lt;option value=\"walnut\"&gt;Walnut\n");
+  fprintf(cgiOut, "&lt;option value=\"creme\"&gt;Creme\n");
+  fprintf(cgiOut, "&lt;/select&gt;\n");
+  fprintf(cgiOut, "&lt;p&gt;Exclusive Radio Button Group: Age of "
+    "Truck in Years\n");
+  fprintf(cgiOut, "&lt;input type=\"radio\" name=\"age\" "
+    "value=\"1\"&gt;1\n");
+  fprintf(cgiOut, "&lt;input type=\"radio\" name=\"age\" "
+    "value=\"2\"&gt;2\n");
+  fprintf(cgiOut, "&lt;input type=\"radio\" name=\"age\" "
+    "value=\"3\" checked&gt;3\n");
+  fprintf(cgiOut, "&lt;input type=\"radio\" name=\"age\" "
+    "value=\"4\"&gt;4\n");
+  fprintf(cgiOut, "&lt;p&gt;Nonexclusive Checkbox Group: "
+    "Voting for Zero through Four Candidates\n");
+  fprintf(cgiOut, "&lt;input type=\"checkbox\" name=\"vote\" "
+    "value=\"A\"&gt;A\n");
+  fprintf(cgiOut, "&lt;input type=\"checkbox\" name=\"vote\" "
+    "value=\"B\"&gt;B\n");
+  fprintf(cgiOut, "&lt;input type=\"checkbox\" name=\"vote\" "
+    "value=\"C\"&gt;C\n");
+  fprintf(cgiOut, "&lt;input type=\"checkbox\" name=\"vote\" "
+    "value=\"D\"&gt;D\n");
+  fprintf(cgiOut, "&lt;p&gt;File Upload:\n");
+  fprintf(cgiOut, "&lt;input type=\"file\" name=\"file\" "
+    "value=\"\"&gt; (Select A Local File)\n");
+  fprintf(cgiOut, "&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;p&gt;Set a Cookie&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;input name=\"cname\" "
+    "value=\"\"&gt; Cookie Name\n");
+  fprintf(cgiOut, "&lt;input name=\"cvalue\" "
+    "value=\"\"&gt; Cookie Value&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;input type=\"submit\" "
+    "name=\"testcgic\" value=\"Submit Request\"&gt;\n");
+  fprintf(cgiOut, "&lt;input type=\"reset\" "
+    "value=\"Reset Request\"&gt;\n");
+  fprintf(cgiOut, "&lt;p&gt;Save the CGI Environment&lt;p&gt;\n");
+  fprintf(cgiOut, "Pressing this button will submit the form, then "
+    "save the CGI environment so that it can be replayed later "
+    "by calling cgiReadEnvironment (in a debugger, for "
+    "instance).&lt;p&gt;\n");
+  fprintf(cgiOut, "&lt;input type=\"submit\" name=\"saveenvironment\" "
+    "value=\"Save Environment\"&gt;\n");
+  fprintf(cgiOut, "&lt;/form&gt;\n");
+}
+</pre>
+Note the use of <code>enctype="multipart/form-data"</code> in the
+<code>FORM</code> tag. This is absolutely required if the form
+will contain file upload fields, as in the above example. Most
+browsers will not even attempt file uploads without the
+presence of this attribute.
+<h4>Examining CGI environment variables</h4>
+The CGI standard specifies a number of environment variables
+which are set by the server. However, servers are somewhat
+unpredictable as to whether these variables will be null or
+point to empty strings when an environment variable is not set.
+Also, in order to allow the programmer to restore saved
+CGI environments, the cgic library needs have a way of insulating
+the programmer from the actual environment variables.
+<p>
+Instead of calling getenv() to determine the value of a
+variable such as HTTP_USER_AGENT (the browser software being used),
+always use the
+<a href="#variables">cgic copies of the environment variables</a>,
+which are always valid C strings (they are never null, although
+they may point to an empty string). For instance, the cgic
+variable containing the name of the browser software is
+<a href="#cgiUserAgent">cgiUserAgent</a>. The referring URL appears
+in the variable <a href="#cgiReferrer">cgiReferrer</a>.
+<h3><a name="images">How can I generate images from my cgic application?</a></h3>
+cgic can be used in conjunction with the
+<a href="http://www.boutell.com/gd/">gd graphics library</a>, which
+can produce GIF images on the fly.
+<p>
+The following short sample program hints at the possibilities:
+<pre>
+#include "cgic.h"
+#include "gd.h"
+
+char *colors[] = {
+  "red", "green", "blue"
+};
+
+#define colorsTotal 3
+
+int cgiMain() {
+  int colorChosen;
+  gdImagePtr im;
+  int r, g, b;
+  /* Use gd to create an image */
+  im = gdImageCreate(64, 64);
+  r = gdImageColorAllocate(im, 255, 0, 0);  
+  g = gdImageColorAllocate(im, 0, 255, 0);  
+  b = gdImageColorAllocate(im, 0, 0, 255);  
+  /* Now use cgic to find out what color the user requested */
+  <a href="#cgiFormSelectSingle">cgiFormSelectSingle</a>("color", 3, &amp;colorChosen, 0);  
+  /* Now fill with the desired color */
+  switch(colorChosen) {
+    case 0:
+    gdImageFill(im, 32, 32, r);
+    break;
+    case 1:
+    gdImageFill(im, 32, 32, g);
+    break;
+    case 2:
+    gdImageFill(im, 32, 32, b);
+    break;
+  }  
+  /* Now output the image. Note the content type! */
+  cgiHeaderContentType("image/gif");
+  /* Send the image to cgiOut */
+  gdImageGif(im, cgiOut);
+  /* Free the gd image */
+  gdImageDestroy(im);
+  return 0;
+}
+</pre>
+Note that this program would need to be linked with both cgic.o
+and libgd.a. Often programs of this type respond to one
+cgiPathInfo value or set of form fields by returning an HTML page 
+with an inline image reference that, in turn, generates a GIF image.
+<h3><a name="debug">Debugging CGI applications: using capture</a></h3>
+Debugging CGI applications can be a painful task. Since CGI applications
+run in a special environment created by the web server, it is difficult
+to execute them in a debugger. However, the cgic library provides a way 
+of capturing "live" CGI environments to a file, and also provides a way
+to reload saved environments. 
+<p>
+The provided program 'capture.c' can be used to capture CGI
+environments. Just change the first line of the cgiMain() function
+of capture.c to save the CGI environment to a filename appropriate
+on your system and type 'make capture'. Then place capture in your
+cgi directory and set the form action or other link you want to test
+to point to it. When the form submission or other link takes place,
+capture will write the CGI environment active at that time to
+the filename you specified in the source. The
+<a href="#cgiReadEnvironment">cgiReadEnvironment()</a> function can then 
+be invoked on the same filename at the beginning of the cgiMain() function 
+of the application you want to test in order to restore the captured 
+environment.  You can then execute your program in the debugger of your choice,
+and it should perform exactly as it would have performed had
+it been launched by the actual web server, including file uploads,
+cookies and all other phenomena within the purview of cgic.
+<p>
+<strong>Important:</strong> Make sure you specify the full path, as the
+current working directory of a CGI script may not be what you
+think it is!
+<p>
+<strong>Even More Important:</strong> If you call getenv() yourself
+in your code, instead of using the provided <a href="#variables">
+cgic copies of the CGI environment variables</a>, you will
+<em>not</em> get the values you expect when running with
+a saved CGI environment. Always use the cgic variables instead
+of calling getenv().
+<h3><a name="functions">cgic function reference</a></h3>
+<dl>
+<br><dt><strong><a name="cgiFormString">cgiFormResultType cgiFormString(
+  char *name, char *result, int max)</a>
+</strong><br><dd>cgiFormString attempts to retrieve the string sent for the
+  specified input field. The text will be copied into
+  the buffer specified by result, up to but not
+  exceeding max-1 bytes; a terminating null is then
+  added to complete the string. Regardless of the newline
+  format submitted by the browser, cgiFormString always
+  encodes each newline as a single line feed (ascii decimal 10); as
+  a result the final string may be slightly shorter than indicated
+  by a call to <a href="#cgiFormStringSpaceNeeded">
+  cgiFormStringSpaceNeeded</a> but will never be longer.
+  cgiFormString returns <a href="#cgiFormSuccess">cgiFormSuccess</a> if the string was 
+  successfully retrieved, 
+  <a href="#cgiFormTruncated">cgiFormTruncated</a> if the string was
+  retrieved but was truncated to fit the buffer,
+  cgiFormEmpty if the string was 
+  retrieved but was empty, and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no 
+  such input field was submitted. In the last case, 
+  an empty string is copied to result. 
+<br><br><dt><strong><a name="cgiFormStringNoNewlines">
+cgiFormResultType cgiFormStringNoNewlines(
+  char *name, char *result, int max)</a>
+</strong><br><dd>
+cgiFormStringNoNewlines() is exactly equivalent to <a href="#cgiFormString">
+  cgiFormString()</a>, except
+  that any carriage returns or line feeds that occur in the input
+  will be stripped out. The use of this function is recommended
+  for single-line text input fields, as some browsers will submit
+  carriage returns and line feeds when they should not. 
+<br><br><dt><strong><a name="cgiFormStringSpaceNeeded">
+cgiFormResultType cgiFormStringSpaceNeeded(
+  char *name, int *length)</a>
+</strong><br><dd>
+cgiFormStringSpaceNeeded() is used to determine the length of the input text 
+  buffer needed to receive the contents of the specified input field. 
+  This is useful if the programmer wishes to allocate sufficient memory 
+  for input of arbitrary length. The actual length of the string 
+  retrieved by a subsequent call to cgiFormString() may be slightly shorter
+  but will never be longer than *result. On success, cgiFormStringSpaceNeeded() 
+  sets the value pointed to by length to the number of bytes of data, 
+  including the terminating null, and returns <a href="#cgiFormSuccess">cgiFormSuccess</a>. If no 
+  value was submitted for the specified field, cgiFormStringSpaceNeeded sets 
+  the value pointed to by length to 1 and returns <a href="#cgiFormNotFound">cgiFormNotFound</a>. 1 is
+  set to ensure space for an empty string (a single null
+  character) if cgiFormString is called despite the return value.
+
+<br><br><dt><strong><a name="cgiFormStringMultiple">cgiFormResultType cgiFormStringMultiple(
+  char *name, char ***ptrToStringArray)</a>
+</strong><br><dd>cgiFormStringMultiple is useful in the unusual case in which several
+  input elements in the form have the same name and, for whatever
+  reason, the programmer does not wish to use the checkbox, radio 
+  button and selection menu functions provided below. This is
+  occasionally needed if the programmer cannot know 
+  in advance what values might appear in a multiple-selection list
+  or group of checkboxes on a form. The value pointed to
+  by result will be set to a pointer to an array of strings; the last
+  entry in the array will be a null pointer.  This array is allocated 
+  by the CGI library. Important: when done working with the array,
+  you must call cgiStringArrayFree() with the array pointer as the 
+  argument.  cgiFormStringMultiple() returns <a href="#cgiFormSuccess">cgiFormSuccess</a> if at least
+  one occurrence of the name is found, <a href="#cgiFormNotFound">cgiFormNotFound</a>
+  if no occurrences are found, or cgiFormMemory if not enough
+  memory is available to allocate the array to be returned.
+  In all cases except the last, ptrToStringArray is set to point to a 
+  valid array of strings, with the last element in the array being a 
+  null pointer; in the out-of-memory case ptrToStringArray is set to 
+  a null pointer.
+
+<br><br><dt><strong><a name="cgiFormEntries">cgiFormResultType cgiFormEntries(
+  char ***ptrToStringArray)</a>
+</strong><br><dd>cgiFormEntries is useful when the programmer cannot know the names
+  of all relevant form fields in advance. The value pointed to
+  by result will be set to a pointer to an array of strings; the last
+  entry in the array will be a null pointer.  This array is allocated 
+  by the CGI library. Important: when done working with the array,
+  you must call cgiStringArrayFree() with the array pointer as the 
+  argument. cgiFormEntries() returns <a href="#cgiFormSuccess">cgiFormSuccess</a> except in the event of an out of memory error.
+  On success, ptrToStringArray is set to point to a 
+  valid array of strings, with the last element in the array being a 
+  null pointer; in the out-of-memory case ptrToStringArray is set to 
+  a null pointer, and 
+  <a href="#cgiFormOutOfMemory">cgiFormOutOfMemory</a> is returned.
+
+<br><br><dt><strong><a name="cgiStringArrayFree">void cgiStringArrayFree(char **stringArray)
+</a>
+</strong><br><dd>
+cgiStringArrayFree() is used to free the memory associated with
+  a string array created by 
+  <a href="#cgiFormStringMultiple">cgiFormStringMultiple()</a>,
+  <a href="#cgiFormEntries">cgiFormEntries()</a>, or
+  <a href="#cgiFormCookies">cgiFormCookies()</a>.
+<br><br><dt><strong><a name="cgiFormInteger">cgiFormResultType cgiFormInteger(
+  char *name, int *result, int defaultV)</a>
+</strong><br><dd>cgiFormInteger() attempts to retrieve the integer sent for the
+  specified input field. The value pointed to by result
+  will be set to the value submitted. cgiFormInteger() returns 
+  cgiFormSuccess if the value was successfully retrieved,
+  cgiFormEmpty if the value submitted is an empty string,
+  cgiFormBadType if the value submitted is not an integer,
+  and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no such input field was submitted. 
+  In the last three cases, the value pointed to by result
+  is set to the specified default.
+<br><br><dt><strong><a name="cgiFormIntegerBounded">
+cgiFormResultType cgiFormIntegerBounded(
+  char *name, int *result, int min, int max, int defaultV)</a>
+</strong><br><dd>cgiFormIntegerBounded() attempts to retrieve the integer sent for the
+  specified input field, and constrains the result to be within
+  the specified bounds. The value pointed to by result
+  will be set to the value submitted. cgiFormIntegerBounded() returns 
+  cgiFormSuccess if the value was successfully retrieved,
+  <a href="#cgiFormConstrained">cgiFormConstrained</a> if the value was out of bounds and result
+  was adjusted accordingly, <a href="#cgiFormEmpty">cgiFormEmpty</a> if the value submitted is 
+  an empty string, <a href="#cgiFormBadType">cgiFormBadType</a> if the value submitted is not an 
+  integer, and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no such input field was submitted. 
+  In the last three cases, the value pointed to by result
+  is set to the specified default.
+
+<br><br><dt><strong><a name="cgiFormDouble">cgiFormResultType cgiFormDouble(
+  char *name, double *result, double defaultV)</a>
+</strong><br><dd>cgiFormDouble attempts to retrieve the floating-point value sent for 
+  the specified input field. The value pointed to by result
+  will be set to the value submitted. cgiFormDouble returns 
+  cgiFormSuccess if the value was successfully retrieved,
+  cgiFormEmpty if the value submitted is an empty string,
+  cgiFormBadType if the value submitted is not a number,
+  and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no such input field was submitted. 
+  In the last three cases, the value pointed to by result
+  is set to the specified default. 
+<br><br><dt><strong><a name="cgiFormDoubleBounded">
+cgiFormResultType cgiFormDoubleBounded(
+  char *name, double *result, double min, double max, 
+  double defaultV)</a>
+</strong><br><dd>
+cgiFormDoubleBounded() attempts to retrieve the floating-point
+  value  sent for the specified input field, and constrains the 
+  result to be within the specified bounds. The value pointed to by 
+  result will be set to the value submitted. cgiFormDoubleBounded() returns 
+  cgiFormSuccess if the value was successfully retrieved,
+  <a href="#cgiFormConstrained">cgiFormConstrained</a> if the value was out of bounds and result
+  was adjusted accordingly, <a href="#cgiFormEmpty">cgiFormEmpty</a> if the value submitted is 
+  an empty string, <a href="#cgiFormBadType">cgiFormBadType</a> if the value submitted is not a 
+  number, and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no such input field was submitted. 
+  In the last three cases, the value pointed to by result
+  is set to the specified default. 
+
+<br><br><dt><strong><a name="cgiFormSelectSingle">
+cgiFormResultType cgiFormSelectSingle(
+  char *name, char **choicesText, int choicesTotal, 
+  int *result, int defaultV)</a>
+</strong><br><dd>
+cgiFormSelectSingle() retrieves the selection number associated with a
+  &lt;SELECT&gt; element that does not allow multiple selections. name
+  should identify the NAME attribute of the &lt;SELECT&gt; element. choicesText 
+  should point to an array of strings identifying each choice; 
+  choicesTotal should indicate the total number of choices. The value 
+  pointed to by result will be set to the position of the actual choice
+  selected within the choicesText array, if any, or to the value of 
+  default, if no selection was submitted or an invalid selection was 
+  made.  cgiFormSelectSingle() returns <a href="#cgiFormSuccess">cgiFormSuccess</a> if the value was
+  successfully retrieved, <a href="#cgiFormNotFound">cgiFormNotFound</a> if no selection
+  was submitted, and <a href="#cgiFormNoSuchChoice">cgiFormNoSuchChoice</a> if the selection
+  does not match any of the possibilities in the choicesText array. 
+<br><dt><strong>
+<a name="cgiFormSelectMultiple">
+cgiFormResultType cgiFormSelectMultiple(
+  char *name, char **choicesText, int choicesTotal, 
+  int *result, int *invalid)</a>
+</strong><br><dd>cgiFormSelectMultiple() retrieves the selection numbers associated with a
+  &lt;SELECT&gt; element that does allow multiple selections. name should
+  identify the NAME attribute of the &lt;SELECT&gt; element. choicesText 
+  should point to an array of strings identifying each choice; 
+  choicesTotal should indicate the total number of choices. result
+  should point to an array of integers with as many elements as there
+  are strings in the choicesText array. For each choice in the
+  choicesText array that is selected, the corresponding integer in
+  the result array will be set to one; other entries in the result
+  array will be set to zero. cgiFormSelectMultiple() returns <a href="#cgiFormSuccess">cgiFormSuccess</a> 
+  if at least one valid selection was successfully retrieved or
+  cgiFormNotFound if no valid selections were submitted.
+  The integer pointed to by invalid is set to the number of
+  invalid selections that were submitted, which should be zero
+  unless the form and the choicesText array do not agree.
+
+<br><br><dt><strong>
+<a name="cgiFormSubmitClicked">
+cgiFormResultType cgiFormSubmitClicked(
+  char *name)</a>
+</strong><br><dd>
+It is often desirable to know whether a particular submit button was clicked,
+  when multiple submit buttons with different name attributes exist.
+  cgiFormSubmitClicked is an alternative name for the 
+  <a href="#cgiFormCheckboxSingle">cgiFormCheckboxSingle</a> function,
+  which is suitable for testing whether a particular submit button
+  was used.
+<br><br><dt><strong>
+<a name="cgiFormCheckboxSingle">
+cgiFormResultType cgiFormCheckboxSingle(
+  char *name)</a>
+</strong><br><dd>
+cgiFormCheckboxSingle determines whether the checkbox with the specified name
+  is checked. cgiFormCheckboxSingle returns <a href="#cgiFormSuccess">cgiFormSuccess</a> if the
+  button is checked, <a href="#cgiFormNotFound">cgiFormNotFound</a> if the checkbox is
+  not checked. cgiFormCheckboxSingle is intended for single
+  checkboxes with a unique name; see below for functions to
+  deal with multiple checkboxes with the same name, and
+  with radio buttons.
+
+<br><br><dt><strong><a name="cgiFormCheckboxMultiple">
+cgiFormResultType cgiFormCheckboxMultiple(
+  char *name, char **valuesText, int valuesTotal, 
+  int *result, int *invalid)</a>
+</strong><br><dd>cgiFormCheckboxMultiple() determines which checkboxes among a group
+  of checkboxes with the same name are checked. This is distinct
+  from radio buttons (see <a href="#cgiFormRadio">cgiFormRadio</a>). 
+  valuesText 
+  should point to an array of strings identifying the VALUE
+  attribute of each checkbox; valuesTotal should indicate the total 
+  number of checkboxes. result should point to an array of integers with 
+  as many elements as there are strings in the valuesText array. For 
+  each choice in the valuesText array that is selected, the corresponding
+  integer in the result array will be set to one; other entries in the 
+  result array will be set to zero. cgiFormCheckboxMultiple returns 
+  cgiFormSuccess if at least one valid checkbox was checked or
+  cgiFormNotFound if no valid checkboxes were checked.
+  The integer pointed to by invalid is set to the number of
+  invalid selections that were submitted, which should be zero
+  unless the form and the valuesText array do not agree.
+<br><br><dt><strong><a name="cgiFormRadio">
+cgiFormResultType cgiFormRadio(
+  char *name, char **valuesText, int valuesTotal, 
+  int *result, int defaultV)</a>
+</strong><br><dd>cgiFormRadio() determines which, if any, of a group of radio boxes with
+  the same name was selected. valuesText should point to an array of 
+  strings identifying the VALUE attribute of each radio box; 
+  valuesTotal should indicate the total number of radio boxes. The value 
+  pointed to by result will be set to the position of the actual choice 
+  selected within the valuesText array, if any, or to the value of 
+  default, if no radio box was checked or an invalid selection was 
+  made. cgiFormRadio() returns <a href="#cgiFormSuccess">cgiFormSuccess</a> if a checked radio box was 
+  found in the group, <a href="#cgiFormNotFound">cgiFormNotFound</a> if no box was checked, and 
+  <a href="#cgiFormNoSuchChoice">cgiFormNoSuchChoice</a> if the radio box submitted does not match any of 
+  the possibilities in the valuesText array.
+
+<br><dt><strong><a name="cgiFormFileName">cgiFormResultType cgiFormFileName(
+  char *name, char *fileName, int max)</a>
+</strong><br><dd>cgiFormFileName attempts to retrieve the file name uploaded by the
+  user for the specified form input field of type <code>file</code>. 
+  <strong>NEVER, EVER TRUST THIS FILENAME TO BE REASONABLE AND
+  SAFE FOR DIRECT USE ON THE SERVER SIDE.</strong>
+  The text will be copied into
+  the buffer specified by fileName, up to but not
+  exceeding max-1 bytes; a terminating null is then
+  added to complete the string. cgiFormFileName returns 
+  <a href="#cgiFormSuccess">cgiFormSuccess</a> if the string was 
+  successfully retrieved and was not empty,
+  <a href="#cgiFormNoFileName">cgiFormNoFileName</a> if the string was 
+  successfully retrieved but empty indicating that no file was uploaded,
+  <a href="#cgiFormTruncated">cgiFormTruncated</a> if the string was
+  retrieved but was truncated to fit the buffer,
+  and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no 
+  such input field was submitted. In the last case, 
+  an empty string is copied to result. 
+<br><dt><strong><a name="cgiFormFileSize">cgiFormResultType cgiFormFileSize(
+  char *name, int *sizeP)</a>
+</strong><br><dd>cgiFormFileSize attempts to retrieve the size, in bytes, of a
+  file uploaded by the browser in response to the
+  input field of type <code>file</code> specified by the
+  <code>name</code> parameter. On success, the size is stored
+  to *sizeP, and this function returns 
+  <a href="#cgiFormSuccess">cgiFormSuccess</a>. If the form
+  field does not exist, this function returns 
+  <a href="#cgiFormNotFound">cgiFormNotFound</a>.
+  If the form field exists but no file was uploaded, this function
+  returns <a href="#cgiFormNotAFile">cgiFormNotAFile</a>.
+<br><dt><strong><a name="cgiFormFileContentType">cgiFormResultType cgiFormFileContentType(
+  char *name, char *contentType, int max)</a>
+</strong><br><dd>cgiFormString attempts to retrieve the content name claimed by the
+  user for the specified form input field of type <code>file</code>. 
+  <strong>THERE IS NO GUARANTEE THAT THE CONTENT TYPE WILL BE
+  ACCURATE.</strong>
+  The content type string will be copied into
+  the buffer specified by contentType, up to but not
+  exceeding max-1 bytes; a terminating null is then
+  added to complete the string. cgiFormFileContentType returns 
+  <a href="#cgiFormSuccess">cgiFormSuccess</a> if the string was 
+  successfully retrieved and was not empty,
+  <a href="#cgiFormNoContentType">cgiFormNoContentType</a> if the string was 
+  successfully retrieved but empty indicating that no file was uploaded
+  or the browser did not know the content type,
+  <a href="#cgiFormTruncated">cgiFormTruncated</a> if the string was
+  retrieved but was truncated to fit the buffer,
+  and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no 
+  such input field was submitted. In the last case, 
+  an empty string is copied to result. 
+
+<br><dt><strong><a name="cgiFormFileOpen">cgiFormResultType cgiFormFileOpen(
+  char *name, cgiFilePtr *cfpp)</a>
+</strong><br><dd>cgiFormFileOpen attempts to open the actual uploaded file data for
+  the specified form field of type <code>file</code>. Upon success,
+  this function returns retrieve the content name claimed by the
+  user for the specified form input field of type <code>file</code>. 
+  On success, this function sets *cfpp to a valid cgiFilePtr
+  object for use with <a href="#cgiFormFileRead</a>cgiFormFileRead</a>
+  and returns <a href="#cgiFormSuccess">cgiFormSuccess</a>.
+  On failure, this function sets *cfpp to a null pointer, and
+  returns <a href="#cgiFormNotFound">cgiFormNotFound</a>,
+  <a href="#cgiFormNotAFile">cgiFormNotAFile</a>,
+  <a href="#cgiFormMemory">cgiFormMemory</a> or
+  <a href="#cgiFormIO">cgiFormIO</a> as appropriate.
+<p>
+  See also <a href="#cgiFormFileRead">cgiFormFileRead</a> 
+and <a href="#cgiFormFileClose">cgiFormFileClose</a>.
+<br><dt><strong><a name="cgiFormFileRead">cgiFormResultType cgiFormFileRead(
+  cgiFilePtr cfp, char *buffer, int bufferSize, int *gotP)</a>
+</strong><br><dd>cgiFormFileRead attempts to read up to <code>bufferSize</code>
+  bytes from a cgiFilePtr object previously opened with
+  <a href="#cgiFormFileOpen">cgiFormFileOpen</a>. If any data
+  is successfully read, it is copied to <code>buffer</code>, 
+  and the number of bytes successfully read is stored 
+  to <code>*gotP</code>. This function returns 
+  <a href="#cgiFormSuccess">cgiFormSuccess</a> if any data
+  is successfully read. At end of file, this function
+  returns <a href="#cgiFormEOF">cgiFormEOF</a>. In the event
+  of an I/O error, this function returns 
+  <a href="#cgiFormIO">cgiFormIO</a>. If cfp is a null pointer,
+  this function returns <a href="#cgiFormOpenFailed">cgiFormOpenFailed</a>.
+  <p>
+  See also <a href="#cgiFormFileOpen">cgiFormFileOpen</a> 
+and <a href="#cgiFormFileClose">cgiFormFileClose</a>.
+<br><dt><strong><a name="cgiFormFileClose">cgiFormResultType cgiFormFileClose(
+  cgiFilePtr cfp)</a>
+</strong><br><dd>cgiFormFileClose closes a cgiFilePtr object previously opened
+  with <a href="#cgiFormFileOpen">cgiFormFileOpen</a>, freeing
+  memory and other system resources. This
+  function returns <a href="#cgiFormSuccess">cgiFormSuccess</a>
+  unless cfp is null, in which case 
+  <a href="#cgiFormOpenFailed">cgiFormOpenFailed</a> is returned.
+<p>
+  See also <a href="#cgiFormFileOpen">cgiFormFileOpen</a> 
+and <a href="#cgiFormFileRead">cgiFormFileRead</a>.
+<br><br><dt><strong><a name="cgiHeaderLocation">
+void cgiHeaderLocation(char *redirectUrl)</a>
+</strong><br><dd>
+cgiHeaderLocation() should be called if the programmer wishes to
+redirect the user to a different URL. No futher output
+is needed in this case.
+<p>
+If you wish to set cookies,
+<strong>you must make your calls to 
+<a href="#cgiHeaderCookieSetString">cgiHeaderCookieSetString</a>
+and 
+<a href="#cgiHeaderCookieSetInteger">cgiHeaderCookieSetInteger</a>
+</strong> BEFORE invoking cgiHeaderLocation. 
+<br><br><dt><strong><a name="cgiHeaderStatus">
+void cgiHeaderStatus(int status, char *statusMessage)</a>
+</strong><br><dd>
+cgiHeaderStatus() should be called if the programmer wishes to
+output an HTTP error status code instead of a document. The status
+code is the first argument; the second argument is the status
+message to be displayed to the user.
+<p>
+If you wish to set cookies,
+<strong>you must make your calls to 
+<a href="#cgiHeaderCookieSetString">cgiHeaderCookieSetString</a>
+and 
+<a href="#cgiHeaderCookieSetInteger">cgiHeaderCookieSetInteger</a>
+</strong> BEFORE invoking cgiHeaderStatus.
+<br><br><dt><strong><a name="cgiHeaderContentType">
+void cgiHeaderContentType(char *mimeType)</a>
+</strong><br><dd>
+cgiHeaderContentType() should be called if the programmer wishes to
+output a new document in response to the user's request. This is
+the normal case. The single argument is the MIME document type
+of the response; typical values are "text/html" for HTML documents, 
+"text/plain" for plain ASCII without HTML tags, "image/gif" for
+a GIF image and "audio/basic" for .au-format audio.
+<p>
+If you wish to set cookies,
+<strong>you must make your calls to 
+<a href="#cgiHeaderCookieSetString">cgiHeaderCookieSetString</a>
+and 
+<a href="#cgiHeaderCookieSetInteger">cgiHeaderCookieSetInteger</a>
+</strong> BEFORE invoking cgiHeaderContentType.
+<br><br><dt><strong><a name="cgiHeaderCookieSetString">
+void cgiHeaderCookieSetString(char *name, char *value,
+  int secondsToLive, char *path, char *domain)</a>
+</strong><br><dd>
+cgiHeaderCookieSetString() should be called when the programmer wishes
+to store a piece of information in the user's browser, so that the
+stored information is again presented to the server on subsequent
+accesses to the relevant site. The first argument is the name of the
+cookie to be stored; for best results in all browsers, use a short
+name without spaces or unusual punctuation. The second argument is
+the value of the cookie to be stored. Again, for best results, use
+a short string; it is recommended that cookies be used to store a
+unique identifier which is then used to look up more detailed
+information in a database on the server side. Attempts to store
+elaborate information on the browser side are much more likely to fail.
+The third argument is the number of seconds that the cookie should
+be kept by the browser; 86400 is a single full day, 365*86400 is
+roughly one year. The fourth argument is the partial URL of the
+web site within which the cookie is relevant. If the cookie should
+be sent to the server for every access to the entire site, 
+set this argument to <code>/</code>. The final argument is the
+web site name or entire domain for which this cookie should be
+submitted; if you choose to have the cookie sent back for an
+entire domain, this argument must begin with a dot, such as
+<code>.boutell.com</code>. The cgic variables <a name="#cgiScriptName</a>
+and <a name="#cgiServerName">cgiServerName</a> are convenient
+values for the fourth and fifth arguments.
+See also <a href="#cgiHeaderCookieSetInteger">cgiHeaderCookieSetInteger</a>,
+<a href="#cgiCookieString">cgiCookieString</a>, 
+<a href="#cgiCookieString">cgiCookieInteger</a> and
+<a href="#cgiCookies">cgiCookies</a>.
+<br><br><dt><strong><a name="cgiHeaderCookieSetInteger">
+void cgiHeaderCookieSetInteger(char *name, int value,
+  int secondsToLive, char *path, char *domain)</a>
+</strong><br><dd>
+cgiHeaderCookieSetInteger() is identical to
+<a href="#cgiHeaderCookieSetString">cgiHeaderCookieSetString</a>,
+except that the value to be set is an integer rather than a string.
+See <a href="#cgiHeaderCookieSetString">cgiHeaderCookieSetString</a>
+for complete information.
+<br>
+<br><dt><strong><a name="cgiCookieString">cgiFormResultType cgiCookieString(
+  char *name, char *result, int max)</a>
+</strong><br><dd>cgiFormString attempts to retrieve the string sent for the
+  specified cookie (browser-side persistent storage). The 
+  text will be copied into
+  the buffer specified by result, up to but not
+  exceeding max-1 bytes; a terminating null is then
+  added to complete the string.
+  cgiCookieString returns <a href="#cgiFormSuccess">cgiFormSuccess</a> if the string was 
+  successfully retrieved, 
+  <a href="#cgiFormTruncated">cgiFormTruncated</a> if the string was
+  retrieved but was truncated to fit the buffer,
+  cgiFormEmpty if the string was 
+  retrieved but was empty, and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no 
+  such cookie was submitted. In the last case, 
+  an empty string is copied to result. 
+<br><br><dt><strong><a name="cgiCookieInteger">cgiFormResultType cgiCookieInteger(
+  char *name, int *result, int defaultV)</a>
+  See also <a href="#cgiCookieString">cgiCookieInteger</a>,
+  <a href="#cgiCookies">cgiCookies</a>,
+  <a href="#cgiHeaderCookieSetString>cgiHeaderCookieSetString</a>, and
+  <a href="#cgiHeaderCookieSetInteger>cgiHeaderCookieSetInteger</a>.
+</strong><br><dd>cgiCookieInteger() attempts to retrieve the integer sent for the
+  specified cookie (browser-side persistent storage). The value 
+  pointed to by result will be set to the value submitted. 
+  cgiCookieInteger() returns 
+  cgiFormSuccess if the value was successfully retrieved,
+  cgiFormEmpty if the value submitted is an empty string,
+  cgiFormBadType if the value submitted is not an integer,
+  and <a href="#cgiFormNotFound">cgiFormNotFound</a> if no such 
+  input field was submitted. In the last three cases, the value 
+  pointed to by result is set to the specified default.
+  See also <a href="#cgiCookieString">cgiCookieString</a>,
+  <a href="#cgiCookies">cgiCookies</a>,
+  <a href="#cgiHeaderCookieSetString>cgiHeaderCookieSetString</a>, and
+  <a href="#cgiHeaderCookieSetInteger>cgiHeaderCookieSetInteger</a>.
+<br><br><dt><strong><a name="cgiCookies">cgiFormResultType cgiCookies(
+  char *name, char ***ptrToStringArray)</a>
+</strong><br><dd>cgiCookies is useful when the programmer cannot know the names
+  of all relevant cookies (browser-side persistent strings) in advance.
+  The value pointed to by result will be set to a pointer to an array 
+  of strings; the last
+  entry in the array will be a null pointer.  This array is allocated 
+  by the CGI library. Important: when done working with the array,
+  you must call cgiStringArrayFree() with the array pointer as the 
+  argument. cgiCookies() returns <a href="#cgiFormSuccess">cgiFormSuccess</a> except in the event of an out of memory error.
+  On success, ptrToStringArray is set to point to a 
+  valid array of strings, with the last element in the array being a 
+  null pointer; in the out-of-memory case ptrToStringArray is set to 
+  a null pointer, and 
+  <a href="#cgiFormOutOfMemory">cgiFormOutOfMemory</a> is returned.
+<br><br><dt><strong><a name="cgiHtmlEscape">
+cgiFormResultType cgiHtmlEscape(char *s)</a>
+</strong><br><dd>
+cgiHtmlEscape() outputs the specified null-terminated string to 
+<a href="#cgiOut">cgiOut</a>,
+escaping any &lt;, &amp;, and &gt; characters encountered correctly so that
+they do not interfere with HTML markup. Returns 
+<a href="#cgiFormSuccess">cgiFormSuccess</a>, or
+<a href="#cgiFormIO">cgiFormIO</a> in the event of an I/O error.
+<p> 
+<br><br><dt><strong><a name="cgiHtmlEscapeData">
+cgiFormResultType cgiHtmlEscapeData(char *data, int len)</a>
+</strong><br><dd>
+cgiHtmlEscapeData() is identical to <a href="#cgiHtmlEscape">cgiHtmlEscape</a>,
+except that the data is not null-terminated. This version of the function
+outputs <code>len</code> bytes. See <a href="#cgiHtmlEscape">cgiHtmlEscape</a>
+for more information.
+<br><br><dt><strong><a name="cgiValueEscape">
+cgiFormResultType cgiValueEscape(char *s)</a>
+</strong><br><dd>
+cgiValueEscape() outputs the specified null-terminated string to 
+<a href="#cgiOut">cgiOut</a>,
+escaping any " characters encountered correctly so that
+they do not interfere with the quotation marks of HTML attribute
+values. This is useful when outputting a string as part of the
+value attribute of an input tag, or the href attribute of a link
+or form tag. This function returns
+<a href="#cgiFormSuccess">cgiFormSuccess</a>, or
+<a href="#cgiFormIO">cgiFormIO</a> in the event of an I/O error.
+<p> 
+<br><br><dt><strong><a name="cgiValueEscapeData">
+cgiFormResultType cgiValueEscapeData(char *data, int len)</a>
+</strong><br><dd>
+cgiValueEscapeData() is identical to <a href="#cgiValueEscape">cgiValueEscape</a>,
+except that the data is not null-terminated. This version of the function
+outputs <code>len</code> bytes. See <a href="#cgiValueEscape">cgiValueEscape</a>
+for more information.
+<br><br><dt><strong><a name="cgiWriteEnvironment">
+cgiEnvironmentResultType cgiWriteEnvironment(char *filename)</a>
+</strong><br><dd>
+cgiWriteEnvironment() can
+  be used to write the entire CGI environment, including
+  form data, to the specified output file; <a href="#cgiReadEnvironment">
+  cgiReadEnvironment()</a> 
+  can then be used to restore that environment from the specified
+  input file for debugging. Of course, these will only work as expected
+  if you use the <a href="#variables">cgic copies of the CGI environment 
+  variables</a> and <a href="#cgiIn">cgiIn</a> and 
+  <a href="#cgiOut">cgiOut</a> rather than stdin and
+  stdout (also see above). These functions are useful in order 
+  to capture real CGI situations while the web server is running, then
+  recreate them in a debugging environment. Both functions
+  return <a href="#cgiEnvironmentSuccess">cgiEnvironmentSuccess</a> on 
+  success, <a href="#cgiEnvironmentIO">cgiEnvironmentIO</a> on an I/O 
+  error, and <a href="#cgiEnvironmentMemory">cgiEnvironmentMemory</a>
+  on an out-of-memory error.
+<br><br><dt><strong><a name="cgiReadEnvironment">
+cgiEnvironmentResultType cgiReadEnvironment(char *filename)</a>
+</strong><br><dd>
+cgiReadEnvironment() restores a CGI environment saved to the specified file by
+  <a href="#cgiWriteEnvironment">cgiWriteEnvironment().</a> 
+  Of course, these will only work as expected
+  if you use the <a href="#variables">cgic copies of the CGI environment 
+  variables</a> and <a href="#cgiIn">cgiIn</a> and 
+  <a href="#cgiOut">cgiOut</a> rather than stdin and
+  stdout (also see above). These functions are useful in order 
+  to capture real CGI situations while the web server is running, then
+  recreate them in a debugging environment. Both functions
+  return <a href="#cgiEnvironmentSuccess">cgiEnvironmentSuccess</a> on success, 
+  <a href="#cgiEnvironmentIO">cgiEnvironmentIO</a> on an I/O error, and 
+  <a href="#cgiEnvironmentMemory">cgiEnvironmentMemory</a>
+  on an out-of-memory error.
+<br><br><dt><strong><a name="cgiMain">int cgiMain()</a>
+</strong><br><dd><strong>The programmer must write this function</strong>, which performs 
+  the unique task of the program and is invoked by the true main() 
+  function, found in the cgic library itself. The return value from 
+  cgiMain will be the return value of the program. It is expected that 
+  the user will make numerous calls to the cgiForm functions
+  from within this function. See <a href="#howto">how to write
+  a cgic application</a> for details.
+</dl>
+<h3><a name="variables">cgic variable reference</a></h3>
+This section provides a reference guide to the various global
+variables provided by cgic for the programmer to utilize.
+These variables should always be used in preference to
+stdin, stdout, and calls to getenv() in order to ensure
+compatibility with the <a href="#debug">cgic CGI debugging features</a>.
+<p>
+Most of these variables are equivalent to various CGI environment
+variables. The most important difference is that the cgic
+environment string variables are never null pointers. They will always 
+point to valid C strings of zero or more characters.
+<dl>
+<br><dt><strong><a name="cgiServerSoftware">char *cgiServerSoftware</a>
+</strong><br><dd>Points to the name of the server software,
+or to an empty string if unknown.
+<br><dt><strong><a name="cgiServerName">char *cgiServerName</a>
+</strong><br><dd>Points to the name of the server,
+or to an empty string if unknown.
+<br><dt><strong><a name="cgiGatewayInterface">char *cgiGatewayInterface</a>
+</strong><br><dd>Points to the name of the gateway interface (usually CGI/1.1),
+or to an empty string if unknown.
+<br><dt><strong><a name="cgiServerProtocol">char *cgiServerProtocol</a>
+</strong><br><dd>Points to the protocol in use (usually HTTP/1.0),
+or to an empty string if unknown.
+<br><dt><strong><a name="cgiServerPort">char *cgiServerPort</a>
+</strong><br><dd>Points to the port number on which the server is listening
+for HTTP connections (usually 80), or an empty string if unknown.
+<br><dt><strong><a name="cgiRequestMethod">char *cgiRequestMethod</a>
+</strong><br><dd>Points to the method used in the request (usually GET or POST),
+or an empty string if unknown (this should not happen).
+<br><dt><strong><a name="cgiPathInfo">char *cgiPathInfo</a>
+</strong><br><dd>Most web servers recognize any additional path information in 
+the URL of the request beyond the name of the CGI program itself and
+pass that information on to the program. cgiPathInfo points to this
+additional path information.
+<br><dt><strong><a name="cgiPathTranslated">char *cgiPathTranslated</a>
+</strong><br><dd>Most web servers recognize any additional path information in 
+the URL of the request beyond the name of the CGI program itself and
+pass that information on to the program. cgiPathTranslated points
+to this additional path information, translated by the server into a 
+filesystem path on the local server.
+<br><dt><strong><a name="cgiScriptName">char *cgiScriptName</a>
+</strong><br><dd>Points to the name under which the program was invoked.
+<br><dt><strong><a name="cgiQueryString">char *cgiQueryString</a>
+</strong><br><dd>Contains any query information submitted by the user as a result
+of a GET-method form or an &lt;ISINDEX&gt; tag. Note that this
+information need not be parsed directly unless an &lt;ISINDEX&gt; tag
+was used; normally it is parsed automatically by the cgic library. Use 
+the cgiForm family of functions to retrieve the values associated
+with form input fields. See <a href="#howto">how to write
+a cgic application</a> for more information.
+<br><dt><strong><a name="cgiRemoteHost">char *cgiRemoteHost</a>
+</strong><br><dd>Points to the fully resolved hostname of the browser, if known,
+or an empty string if unknown.
+<br><dt><strong><a name="cgiRemoteAddr">char *cgiRemoteAddr</a>
+</strong><br><dd>Points to the dotted-decimal IP address of the browser, if known,
+or an empty string if unknown.
+<br><dt><strong><a name="cgiAuthType">char *cgiAuthType</a>
+</strong><br><dd>Points to the type of authorization used for the request,
+if any, or an empty string if none or unknown.
+<br><dt><strong><a name="cgiRemoteUser">char *cgiRemoteUser</a>
+</strong><br><dd>Points to the user name under which the user has 
+authenticated; an empty string if no authentication has
+taken place. The certainty of this information depends on
+the type of authorization in use; see
+<a href="#cgiAuthType">cgiAuthType</a>.
+<br><dt><strong><a name="cgiRemoteIdent">char *cgiRemoteIdent</a>
+</strong><br><dd>Points to the user name volunteered by the user via
+the user identification protocol; an empty
+string if unknown. This information is not secure.
+Identification demons can be installed by users on
+insecure systems such as Windows machines.
+<br><dt><strong><a name="cgiContentType">char *cgiContentType</a>
+</strong><br><dd>Points to the MIME content type of the information
+submitted by the user, if any; an empty string if no
+information was submitted. If this string is equal to
+<code>application/x-www-form-urlencoded</code> or
+<code>multipart/form-data</code>, the cgic
+library will automatically examine the form data submitted.
+If this string has any other non-empty value, a different
+type of data has been submitted. This is currently very rare,
+as most browsers can only submit forms and file uploads which
+cgic parses directly.
+<br><dt><strong><a name="cgiContentType">char *cgiCookie</a>
+</strong><br><dd>Points to the raw cookie (browser-side persistent storage)
+data submitted by the web browser. 
+Programmers should use the functions <a href="#cgiCookies">cgiCookies</a>,
+<a href="#cgiCookieString">cgiCookieString</a> and
+<a href="#cgiCookieInteger">cgiCookieInteger</a> instead of
+examining this string directly.
+<br><dt><strong><a name="cgiAccept">char *cgiAccept</a>
+</strong><br><dd>Points to a space-separated list of MIME content types
+acceptable to the browser (see <a href="#cgiHeaderContentType">
+cgiHeaderContentType()</a> ), or an empty string. Unfortunately, this variable
+is not supplied in a useful form by most current browsers. Programmers wishing
+to make decisions based on the capabilities of the browser
+are advised to check the <a href="#cgiUserAgent">cgiUserAgent</a>
+variable against a list of browsers and capabilities instead.
+<br><dt><strong><a name="cgiUserAgent">char *cgiUserAgent</a>
+</strong><br><dd>
+Points to the name of the browser in use, or an empty
+string if this information is not available. 
+<br><dt><strong><a name="cgiReferrer">char *cgiReferrer</a>
+</strong><br><dd>
+Points to the URL of the previous page visited by the user. This is
+often the URL of the form that brought the user to your program.
+Note that reporting this information is entirely up to the browser,
+which may choose not do so, and may choose not to do so truthfully.
+However, this variable is typically accurate. <strong>The frequently
+used misspelling cgiReferer is also supplied as a macro.</strong>
+<br><dt><strong><a name="cgiContentLength">int cgiContentLength</a>
+</strong><br><dd>The number of bytes of form or query data received.
+  Note that if the submission is a form or query submission
+  the library will read and parse all the information
+  directly from cgiIn and/or cgiQueryString. The programmer should
+  not do so, and indeed the cgiIn pointer will be at end-of-file
+  in such cases.
+<br><dt><strong><a name="cgiOut">FILE *cgiOut</a>
+</strong><br><dd>Pointer to CGI output. The cgiHeader functions, such as
+  <a href="#cgiHeaderContentType">cgiHeaderContentType</a>, should 
+  be used first to output the mime headers; the output HTML
+  page, GIF image or other web document should then be written
+  to cgiOut by the programmer using standard C I/O functions
+  such as fprintf() and fwrite(). cgiOut is normally equivalent
+  to stdout; however, it is recommended that cgiOut be used to
+  ensure compatibility with future versions of cgic for
+  specialized environments.
+<br><dt><strong><a name="cgiIn">FILE *cgiIn</a>
+</strong><br><dd>Pointer to CGI input. In 99.99% of cases, you will not 
+  need this. CGIC 2.0 supports both regular POST form submissions
+  and multipart/form-data file upload form submissions directly.
+</dl>
+<H3><a name="resultcodes">cgic result code reference</a></h3>
+<p>
+In most cases, cgic functions are designed to produce reasonable results
+even when browsers and users do unreasonable things. However, it is sometimes
+important to know precisely which unreasonable things took place, especially
+when assigning a default value or bounding a value is an inadequate
+solution. The following result codes are useful in making this determination.
+<dl>
+<br><dt><strong><a name="cgiFormSuccess">cgiFormSuccess</a>
+</strong><br><dd>Indicates that the function successfully performed at least one
+action (or retrieved at least one value, where applicable).
+<br><dt><strong><a name="cgiFormTruncated">cgiFormTruncated</a>
+</strong><br><dd>Indicates that a string value retrieved from the user was
+cut short to avoid overwriting the end of a buffer.
+<br><dt><strong><a name="cgiFormBadType">cgiFormBadType</a>
+</strong><br><dd>Indicates that a "numeric" value submitted by the user was
+in fact not a legal number.
+<br><dt><strong><a name="cgiFormEmpty">cgiFormEmpty</a>
+</strong><br><dd>Indicates that a field was retrieved but contained no data.
+<br><dt><strong><a name="cgiFormNotFound">cgiFormNotFound</a>
+</strong><br><dd>Indicates that no value was submitted for a particular field.
+<br><dt><strong><a name="cgiFormConstrained">cgiFormConstrained</a>
+</strong><br><dd>Indicates that a numeric value was beyond the specified bounds
+and was forced to the lower or upper bound as appropriate.
+<br><dt><strong><a name="cgiFormNoSuchChoice">cgiFormNoSuchChoice</a>
+</strong><br><dd>Indicates that the value submitted for a single-choice field
+(such as a radio-button group) was not one of the acceptable values.
+This usually indicates a discrepancy between the form and the program.
+<br><dt><strong><a name="cgiFormEOF">cgiFormEOF</a>
+</strong><br><dd>Returned by <a href="#cgiFormFileRead">cgiFormFileRead</a>
+when, at the start of the call, the cgiFilePtr object is already
+positioned at the end of the uploaded file data. 
+<br><dt><strong><a name="cgiFormEOF">cgiFormIO</a>
+</strong><br><dd>Returned by <a href="#cgiFormFileRead">cgiFormFileRead</a>
+when an I/O error occurs while reading uploaded file data.
+<br><dt><strong><a name="cgiFormNotAFile">cgiFormNotAFile</a>
+</strong><br><dd>Returned in response to an attempt to manipulate a form field
+that is not a file upload field using a file-related function.
+<br><dt><strong><a name="cgiFormNoContentType">cgiFormNoContentType</a>
+</strong><br><dd>Returned in response to an attempt to fetch the content type of
+a file-upload field when the content type is not specified by the browser.
+<br><dt><strong><a name="cgiFormNoFileName">cgiFormNoFileName</a>
+</strong><br><dd>Returned in response to an attempt to fetch the file name of
+a file-upload field when a file name is not specified by the browser.
+<br><dt><strong><a name="cgiFormOpenFailed">cgiFormOpenFailed</a>
+</strong><br><dd>Returned in response to an attempt to read from a null
+cgiFilePtr object, typically when the programmer has failed to
+check the result of a call to <a href="#cgiFormFileOpen">cgiFormFileOpen</a>.
+<br><dt><strong><a name="cgiEnvironmentMemory">cgiEnvironmentMemory</a>
+</strong><br><dd>Indicates that an attempt to read or write the CGI environment
+to or from a capture file failed due to an out-of-memory error.
+<br><dt><strong><a name="cgiEnvironmentSuccess">cgiEnvironmentSuccess</a>
+</strong><br><dd>Indicates that an attempt to read or write the CGI environment
+to or from a capture file was successful.
+<br><dt><strong><a name="cgiEnvironmentIO">cgiEnvironmentIO</a>
+</strong><br><dd>Indicates that an attempt to read or write the CGI environment
+to or from a capture file failed due to an I/O error. 
+<br><dt><strong><a name="cgiEnvironmentWrongVersion">cgiEnvironmentWrongVersion</a>
+</strong><br><dd>Indicates that an attempt to read from a saved debugging CGI environment
+produced by a pre-2.0 version of CGIC was made. 
+</dl>
+<h3><a name="index">cgic quick index</a></h3>
+<a href="#cgiAccept">cgiAccept</a> |
+<a href="#cgiAuthType">cgiAuthType</a> |
+<a href="#cgiContentLength">cgiContentLength</a> |
+<a href="#cgiContentType">cgiContentType</a> |
+<a href="#cgiEnvironmentIO">cgiEnvironmentIO</a> |
+<a href="#cgiEnvironmentMemory">cgiEnvironmentMemory</a> |
+<a href="#cgiEnvironmentSuccess">cgiEnvironmentSuccess</a> |
+<a href="#cgiCookieInteger">cgiCookieInteger</a> |
+<a href="#cgiCookies">cgiCookies</a> |
+<a href="#cgiCookieSetInteger">cgiCookieSetInteger</a> |
+<a href="#cgiCookieSetString">cgiCookieSetString</a> |
+<a href="#cgiCookieString">cgiCookieString</a> |
+<a href="#cgiHtmlEscape">cgiHtmlEscape</a> |
+<a href="#cgiHtmlEscapeData">cgiHtmlEscapeData</a> |
+<a href="#cgiValueEscape">cgiValueEscape</a> |
+<a href="#cgiValueEscapeData">cgiValueEscapeData</a> |
+<a href="#cgiFormBadType">cgiFormBadType</a> |
+<a href="#cgiFormCheckboxMultiple">cgiFormCheckboxMultiple()</a> |
+<a href="#cgiFormCheckboxSingle">cgiFormCheckboxSingle()</a> |
+<a href="#cgiFormConstrained">cgiFormConstrained</a> |
+<a href="#cgiFormDouble">cgiFormDouble()</a> |
+<a href="#cgiFormDoubleBounded">cgiFormDoubleBounded()</a> |
+<a href="#cgiFormEOF">cgiFormEOF</a> |
+<a href="#cgiFormEmpty">cgiFormEmpty</a> |
+<a href="#cgiFormEntries">cgiFormEntries</a> |
+<a href="#cgiFormFileClose">cgiFormFileClose</a> |
+<a href="#cgiFormFileContentType">cgiFormFileContentType</a> |
+<a href="#cgiFormFileName">cgiFormFileName</a> |
+<a href="#cgiFormFileOpen">cgiFormFileOpen</a> |
+<a href="#cgiFormFileRead">cgiFormFileRead</a> |
+<a href="#cgiFormFileSize">cgiFormFileSize</a> |
+<a href="#cgiFormInteger">cgiFormInteger()</a> |
+<a href="#cgiFormIntegerBounded">cgiFormIntegerBounded()</a> |
+<a href="#cgiFormNoContentType>cgiFormNoContentType</a> |
+<a href="#cgiFormNoFileName>cgiFormNoFileName</a> |
+<a href="#cgiFormNoSuchChoice">cgiFormNoSuchChoice</a> |
+<a href="#cgiFormNotAFile>cgiFormNotAFile</a> |
+<a href="#cgiFormNotFound">cgiFormNotFound</a> |
+<a href="#cgiFormRadio">cgiFormRadio()</a> |
+<a href="#cgiFormSelectMultiple">cgiFormSelectMultiple()</a> |
+<a href="#cgiFormSelectSingle">cgiFormSelectSingle()</a> |
+<a href="#cgiFormString">cgiFormString()</a> |
+<a href="#cgiFormStringMultiple">cgiFormStringMultiple()</a> |
+<a href="#cgiFormStringNoNewlines">cgiFormStringNoNewlines()</a> |
+<a href="#cgiFormStringSpaceNeeded">cgiFormStringSpaceNeeded()</a> |
+<a href="#cgiFormSuccess">cgiFormSuccess</a> |
+<a href="#cgiFormTruncated">cgiFormTruncated</a> |
+<a href="#cgiGatewayInterface">cgiGatewayInterface</a> |
+<a href="#cgiHeaderContentType">cgiHeaderContentType()</a> |
+<a href="#cgiHeaderLocation">cgiHeaderLocation()</a> |
+<a href="#cgiHeaderStatus">cgiHeaderStatus()</a> |
+<a href="#cgiIn">cgiIn</a> |
+<a href="#cgiMain">cgiMain()</a>
+<a href="#cgiOut">cgiOut</a> |
+<a href="#cgiPathInfo">cgiPathInfo</a> |
+<a href="#cgiPathTranslated">cgiPathTranslated</a> |
+<a href="#cgiQueryString">cgiQueryString</a> |
+<a href="#cgiReadEnvironment">cgiReadEnvironment()</a> |
+<a href="#cgiReferrer">cgiReferrer()</a> |
+<a href="#cgiRemoteAddr">cgiRemoteAddr</a> |
+<a href="#cgiRemoteHost">cgiRemoteHost</a> |
+<a href="#cgiRemoteIdent">cgiRemoteIdent</a> |
+<a href="#cgiRemoteUser">cgiRemoteUser</a> |
+<a href="#cgiRequestMethod">cgiRequestMethod</a> |
+<a href="#cgiScriptName">cgiScriptName</a> |
+<a href="#cgiServerName">cgiServerName</a> |
+<a href="#cgiServerPort">cgiServerPort</a> |
+<a href="#cgiServerProtocol">cgiServerProtocol</a> |
+<a href="#cgiServerSoftware">cgiServerSoftware</a> |
+<a href="#cgiStringArrayFree">cgiStringArrayFree()</a> |
+<a href="#cgiUserAgent">cgiUserAgent</a> |
+<a href="#cgiWriteEnvironment">cgiWriteEnvironment()</a>
+<p>
+<hr>
+<em><a href="http://www.boutell.com/">Boutell.Com, Inc.</a></em>
+</body>
+</html>
+
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/cgictest.c
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/cgictest.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/cgictest.c	(revision 221)
@@ -0,0 +1,222 @@
+
+/*
+  $Id: cgictest.c,v 1.2 2004/04/07 17:09:27 fox Exp $
+ */
+
+#include <stdio.h>
+#include "cgic.h"
+
+void Name();
+void Address();
+void Hungry();
+void Temperature();
+void Frogs();
+void Color();
+void Flavors();
+void NonExButtons();
+void RadioButtons();
+
+
+int cgiMain() {
+#if DEBUG
+	/* Load a saved CGI scenario if we're debugging */
+	cgiReadEnvironment("/home/boutell/public_html/capcgi.dat");
+#endif
+	dup2(cgiOut,stdout);
+	printf("Content-Type: text/html; charset=utf-8\r\nStatus: 200 OK\r\n\r\n");
+	//cgiHeaderContentType("text/html");
+	printf( "<HTML><HEAD>\n");
+	printf( "<TITLE>cgic test</TITLE></HEAD>\n");
+	printf( "<BODY><H1>cgic test</H1>\n");
+	Name();
+	Address();
+	Hungry();
+	Temperature();
+	Frogs();
+	Color();
+	Flavors();
+	NonExButtons();
+	RadioButtons();
+	printf( "</BODY></HTML>\n");
+	return 0;
+}
+
+void Name() {
+	char name[81];
+	int result = cgiFormStringNoNewlines("name", name, 81);
+	switch (result) {
+		case cgiFormSuccess:
+		printf( "Name fetched, result code: cgiFormSuccess<br>\n");
+		break;
+		case cgiFormTruncated:
+		printf( "Name fetched, result code: cgiFormTruncated<br>\n");
+		break;
+		case cgiFormEmpty:
+		printf( "Name fetched, result code: cgiFormEmpty<br>\n");
+		break;
+		case cgiFormNotFound:
+		printf( "Name fetched, result code: cgiFormNotFound<br>\n");
+		break;
+		case cgiFormMemory:
+		printf( "Name fetched, result code: cgiFormMemory<br>\n");
+		break;
+		default:
+		printf( "Name fetched, unexpected result code: %d\n", result);
+		break;
+	}	
+	printf( "Name: %s<BR>\n", name);
+}
+	
+void Address() {
+	char address[241];
+	cgiFormString("address", address, 241);
+	printf( "Address: <PRE>\n%s</PRE>\n", address);
+}
+
+void Hungry() {
+	if (cgiFormCheckboxSingle("hungry") == cgiFormSuccess) {
+		printf( "I'm Hungry!<BR>\n");
+	} else {
+		printf( "I'm Not Hungry!<BR>\n");
+	}
+}
+	
+void Temperature() {
+	double temperature;
+	cgiFormDoubleBounded("temperature", &temperature, 80.0, 120.0, 98.6);
+	printf( "My temperature is %f.<BR>\n", temperature);
+}
+	
+void Frogs() {
+	int frogsEaten;
+	cgiFormInteger("frogs", &frogsEaten, 0);
+	printf( "I have eaten %d frogs.<BR>\n", frogsEaten);
+}
+
+char *colors[] = {
+	"Red",
+	"Green",
+	"Blue"
+};
+
+void Color() {
+	int colorChoice;
+	cgiFormSelectSingle("colors", colors, 3, &colorChoice, 0);
+	printf( "I am: %s<BR>\n", colors[colorChoice]);
+}	 
+
+char *flavors[] = {
+	"pistachio",
+	"walnut",
+	"creme"
+};
+
+void Flavors() {
+	int flavorChoices[3];
+	int i;
+	int result;	
+	int invalid;
+	result = cgiFormSelectMultiple("flavors", flavors, 3, 
+		flavorChoices, &invalid);
+	if (result == cgiFormNotFound) {
+		printf( "I hate ice cream.<p>\n");
+	} else {	
+		printf( "My favorite ice cream flavors are:\n");
+		printf( "<ul>\n");
+		for (i=0; (i < 3); i++) {
+			if (flavorChoices[i]) {
+				printf( "<li>%s\n", flavors[i]);
+			}
+		}
+		printf( "</ul>\n");
+	}
+}
+
+char *ages[] = {
+	"1",
+	"2",
+	"3",
+	"4"
+};
+
+void RadioButtons() {
+	int ageChoice;
+	char ageText[10];
+	/* Approach #1: check for one of several valid responses. 
+		Good if there are a short list of possible button values and
+		you wish to enumerate them. */
+	cgiFormRadio("age", ages, 4, &ageChoice, 0);
+
+	printf( "Age of Truck: %s (method #1)<BR>\n", 
+		ages[ageChoice]);
+
+	/* Approach #2: just get the string. Good
+		if the information is not critical or if you wish
+		to verify it in some other way. Note that if
+		the information is numeric, cgiFormInteger,
+		cgiFormDouble, and related functions may be
+		used instead of cgiFormString. */	
+	cgiFormString("age", ageText, 10);
+
+	printf( "Age of Truck: %s (method #2)<BR>\n", ageText);
+}
+
+char *votes[] = {
+	"A",
+	"B",
+	"C",
+	"D"
+};
+
+void NonExButtons() {
+	int voteChoices[4];
+	int i;
+	int result;	
+	int invalid;
+
+	char **responses;
+
+	/* Method #1: check for valid votes. This is a good idea,
+		since votes for nonexistent candidates should probably
+		be discounted... */
+	printf( "Votes (method 1):<BR>\n");
+	result = cgiFormCheckboxMultiple("vote", votes, 4, 
+		voteChoices, &invalid);
+	if (result == cgiFormNotFound) {
+		printf( "I hate them all!<p>\n");
+	} else {	
+		printf( "My preferred candidates are:\n");
+		printf( "<ul>\n");
+		for (i=0; (i < 4); i++) {
+			if (voteChoices[i]) {
+				printf( "<li>%s\n", votes[i]);
+			}
+		}
+		printf( "</ul>\n");
+	}
+
+	/* Method #2: get all the names voted for and trust them.
+		This is good if the form will change more often
+		than the code and invented responses are not a danger
+		or can be checked in some other way. */
+	printf( "Votes (method 2):<BR>\n");
+	result = cgiFormStringMultiple("vote", &responses);
+	if (result == cgiFormNotFound) {	
+		printf( "I hate them all!<p>\n");
+	} else {
+		int i = 0;
+		printf( "My preferred candidates are:\n");
+		printf( "<ul>\n");
+		while (responses[i]) {
+			printf( "<li>%s\n", responses[i]);
+			i++;
+		}
+		printf( "</ul>\n");
+	}
+	/* We must be sure to free the string array or a memory
+		leak will occur. Simply calling free() would free
+		the array but not the individual strings. The
+		function cgiStringArrayFree() does the job completely. */	
+	cgiStringArrayFree(responses);
+}
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/license.txt
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/license.txt	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/license.txt	(revision 221)
@@ -0,0 +1,109 @@
+CGIC License Terms
+------------------
+
+Basic License
+-------------
+
+CGIC, copyright 2009 GeoLabs SARL.
+CGIC, copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+2004 by Thomas Boutell and Boutell.Com, Inc.. Permission is granted to 
+use CGIC in any application, commercial or noncommercial, at no cost. 
+HOWEVER, this copyright paragraph must appear on a "credits" page 
+accessible in the public online and offline documentation of the program. 
+Modified versions of the CGIC library should not be distributed without 
+the attachment of a clear statement regarding the author of the 
+modifications, and this notice may in no case be removed. 
+Modifications may also be submitted to the author for inclusion 
+in the main CGIC distribution.                                 
+
+IF YOU WOULD PREFER NOT TO ATTACH THE ABOVE NOTICE to
+the public documentation of your application, consult the
+information which follows regarding the availability
+of a nonexclusive commercial license for CGIC.
+
+Commercial License
+------------------
+
+The price of a nonexclusive commercial license is $200 U.S. 
+To purchase the license:
+
+1. Print and sign two copies of the document below.
+
+2. Send both copies, along with Visa, Mastercard, Discover, or
+Amex card number, cardholder's name, and expiration date 
+OR a check for $200 in U.S. funds drawn on a U.S. bank, to:
+
+Boutell.Com, Inc.
+PO Box 63767
+Philadelphia, PA 19147 USA
+
+Credit card orders may alternatively be faxed to:
+
+Boutell.Com, Inc.
+(208) 445-0327
+
+BE SURE TO INCLUDE AN EMAIL ADDRESS, as well as
+a postal address and phone number.
+
+3. We will return one signed copy to you promptly.
+
+You will also receive swift notification of new
+versions of CGIC, in addition to ongoing
+email support.
+
+* * *
+
+CGIC Nonexclusive Commercial License
+
+The licensee named below is granted the right
+to utilize CGIC, major version 1 or 2, any minor
+version thereof, in CGI applications without the
+need for a credit notice of any kind. CGI applications
+developed by the holder of this license may be
+distributed freely in source code or binary form
+without additional fees or royalties. This license does 
+not grant the right to use CGIC to create a development tool 
+which passes on substantially all of the capabilities of the 
+CGIC library to the user of the tool, unless that tool is 
+to be used internally by the license holder only in order
+to develop CGI applications. This license may not
+be resold, but applications developed in accordance
+with the terms of the license may be distributed
+freely subject to the limitations described above.
+
+Future minor (2.x) versions of CGIC will be covered by this
+license free of charge. If significant defects of workmanship
+are discovered in version 2.x, minor releases to correct them
+will be made available before or at the same time that 
+those defects are addressed in any future major version. 
+Future "major" (3.x) versions will be available to
+licensees at an upgrade price of $50.
+
+If, for any reason, any portion of this license is found 
+to be invalid, that portion of the license only
+is invalidated and the remainder of the agreement
+remains in effect.
+
+If this license has not been signed by Thomas Boutell
+or M. L. Grant on behalf of Boutell. Com, Inc., 
+it is invalid.
+
+Licensee's Name:              _____________________
+
+Signed for Licensee:          _____________________
+
+Complete Mailing Address:     _____________________
+
+                              _____________________
+
+                              _____________________
+
+Phone Number:                 _____________________
+
+Email Address:                _____________________
+
+Signed for Boutell.Com, Inc.: _____________________
+
+Date:                         _____________________
+
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/makefile.vc
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/makefile.vc	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/makefile.vc	(revision 221)
@@ -0,0 +1,35 @@
+GEODIR=C:/OSGeo4W
+DESTDIR=C:\OSGeo4W
+CFLAGS=-g -Wall
+CC=cl /TP
+LIBS=$(GEODIR)/lib/libfcgi.lib /nologo
+LIBS1=./libcgic.lib
+
+CFLAGS=/EHa /nologo /DCRT_SECURE_NO_WARNING /MT /W2 /O2 /D "WIN32" \
+	-I $(GEODIR)/include 
+
+all: libcgic.lib #cgictest.exe capture.exe 
+
+install: libcgic.lib
+	copy libcgic.lib  $(DESTDIR)\lib
+	copy cgic.h  $(DESTDIR)\include
+	@echo libcgic.lib is now installed in  $(DESTDIR)\lib and cgic.h is in  $(DESTDIR)\include.
+
+libcgic.lib: cgic.obj cgic.h
+	if exist cgic.lib del cgic.lib
+	lib /out:libcgic.lib cgic.obj $(LIBS)
+
+cgictest.obj: cgictest.c libcgic.lib
+	$(CC) $(CFLAGS) -c cgictest.c
+
+cgictest.exe: cgictest.obj 
+	link cgictest.obj $(LIBS) /out:cgictest.exe
+
+cgic.obj: 
+	$(CC) $(CFLAGS) -c cgic.c 
+
+capture.exe: capture.obj libcgic.lib
+	$(CC) $(CFLAGS) capture.c $(LIBS)
+
+clean:
+	erase *.obj *.lib *.exe *.manifest
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/readme.txt
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/readme.txt	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/readme.txt	(revision 221)
@@ -0,0 +1,14 @@
+This is the CGIC CGI development library for C programmers.
+Copyright 2009 GeoLabs SARL
+Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+2004, Thomas Boutell and Boutell.Com, Inc.
+
+See the file cgic.html for complete documentation in a single
+HTML hypertext file to be accessed with your web browser. If you
+preferer, there is a plaintext version in the file cgic.txt. 
+Or see http://www.boutell.com/cgic/ for the latest copy of 
+the documentation.
+
+See the files license.txt and support.txt for terms of use and
+technical support information.
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/support.txt
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/support.txt	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/support.txt	(revision 221)
@@ -0,0 +1,46 @@
+Support for CGIC
+----------------
+
+   ____
+  /    \
+ | STOP |
+  \____/
+  
+    Are you getting a "server error," indicating that your web server
+    "cannot allow POST to this URL," or a similar message? YOU MUST
+    CONFIGURE YOUR WEB SERVER TO ALLOW CGI PROGRAMS, AND YOU MUST
+    INSTALL CGI PROGRAMS IN THE LOCATION (OR WITH THE EXTENSION) THAT
+    YOUR WEB SERVER EXPECTS TO SEE. Please don't send me email about
+    this, unless you are purchasing commercial support. It is strictly 
+    between you and your web server's documentation, or between you and 
+    your ISP. If you wish to purchase commercial support, we will gladly 
+    attempt to help you resolve such problems, but the cooperation of 
+    your web server administrator will be necessary in most cases. Thanks!
+
+Free Support
+------------
+ 
+Anyone can mail questions about the gd and cgic libraries to 
+boutell@boutell.com. However, I receive a very large volume of email on 
+many subjects, and while I do my best to respond to all queries this can 
+take some time. Sometimes the response must take the form of an eventual 
+new release or an addition to a FAQ or other document, as opposed to an 
+detailed individual response.
+
+Hourly Support
+--------------
+
+Those requiring support in detail may arrange for direct support
+from the author, Thomas Boutell, at the rate of $50/hr, billed
+directly by credit card. Purchase orders are also accepted from
+Fortune 500 corporations and institutions in good standing.
+To make arrangements, contact boutell@boutell.com. Alternatively,
+use our free-form secure message page at:
+
+https://www.boutell.com/freeform/
+
+To avoid delay and/or confusion, be sure to specifically mention that 
+you wish to purchase CGIC support at the hourly rate above.
+
+-Thomas Boutell, boutell@boutell.com
+
Index: /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.c
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.c	(revision 221)
@@ -0,0 +1,146 @@
+/*
+
+    Implementation of POSIX directory browsing functions and types for Win32.
+
+    Author:  Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
+    History: Created March 1997. Updated June 2003.
+    Rights:  See end of file.
+
+*/
+
+#include <dirent.h>
+#include <errno.h>
+#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct DIR
+{
+    long                handle; /* -1 for failed rewind */
+    struct _finddata_t  info;
+    struct dirent       result; /* d_name null iff first time */
+    char                *name;  /* null-terminated char string */
+};
+
+DIR *opendir(const char *name)
+{
+    DIR *dir = 0;
+
+    if(name && name[0])
+    {
+        size_t base_length = strlen(name);
+        const char *all = /* search pattern must end with suitable wildcard */
+            strchr("/\\", name[base_length - 1]) ? "*" : "/*";
+
+        if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
+           (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
+        {
+            strcat(strcpy(dir->name, name), all);
+
+            if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
+            {
+                dir->result.d_name = 0;
+            }
+            else /* rollback */
+            {
+                free(dir->name);
+                free(dir);
+                dir = 0;
+            }
+        }
+        else /* rollback */
+        {
+            free(dir);
+            dir   = 0;
+            errno = ENOMEM;
+        }
+    }
+    else
+    {
+        errno = EINVAL;
+    }
+
+    return dir;
+}
+
+int closedir(DIR *dir)
+{
+    int result = -1;
+
+    if(dir)
+    {
+        if(dir->handle != -1)
+        {
+            result = _findclose(dir->handle);
+        }
+
+        free(dir->name);
+        free(dir);
+    }
+
+    if(result == -1) /* map all errors to EBADF */
+    {
+        errno = EBADF;
+    }
+
+    return result;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+    struct dirent *result = 0;
+
+    if(dir && dir->handle != -1)
+    {
+        if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
+        {
+            result         = &dir->result;
+            result->d_name = dir->info.name;
+        }
+    }
+    else
+    {
+        errno = EBADF;
+    }
+
+    return result;
+}
+
+void rewinddir(DIR *dir)
+{
+    if(dir && dir->handle != -1)
+    {
+        _findclose(dir->handle);
+        dir->handle = (long) _findfirst(dir->name, &dir->info);
+        dir->result.d_name = 0;
+    }
+    else
+    {
+        errno = EBADF;
+    }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+
+    Copyright Kevlin Henney, 1997, 2003. All rights reserved.
+
+    Permission to use, copy, modify, and distribute this software and its
+    documentation for any purpose is hereby granted without fee, provided
+    that this copyright and permissions notice appear in all copies and
+    derivatives.
+    
+    This software is supplied "as is" without express or implied warranty.
+
+    But that said, if there are any problems please get in touch.
+
+*/
+
Index: /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.h
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.h	(revision 221)
@@ -0,0 +1,50 @@
+#ifndef DIRENT_INCLUDED
+#define DIRENT_INCLUDED
+
+/*
+
+    Declaration of POSIX directory browsing functions and types for Win32.
+
+    Author:  Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
+    History: Created March 1997. Updated June 2003.
+    Rights:  See end of file.
+    
+*/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct DIR DIR;
+
+struct dirent
+{
+    char *d_name;
+};
+
+DIR           *opendir(const char *);
+int           closedir(DIR *);
+struct dirent *readdir(DIR *);
+void          rewinddir(DIR *);
+
+/*
+
+    Copyright Kevlin Henney, 1997, 2003. All rights reserved.
+
+    Permission to use, copy, modify, and distribute this software and its
+    documentation for any purpose is hereby granted without fee, provided
+    that this copyright and permissions notice appear in all copies and
+    derivatives.
+    
+    This software is supplied "as is" without express or implied warranty.
+
+    But that said, if there are any problems please get in touch.
+
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-api.js
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-api.js	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-api.js	(revision 221)
@@ -0,0 +1,6163 @@
+/**
+ * Author : René-Luc D'Hont
+ *
+ * Copyright 2010 3liz SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Copyright 2005-2010 OpenLayers Contributors, released under the Clear BSD
+ * license. Please see http://svn.openlayers.org/trunk/openlayers/license.txt
+ * for the full text of the license.
+ */
+
+/**
+ * Class: ZOO
+ */
+ZOO = {
+  /**
+   * Constant: SERVICE_ACCEPTED
+   * {Integer} used for
+   */
+  SERVICE_ACCEPTED: 0,
+  /**
+   * Constant: SERVICE_STARTED
+   * {Integer} used for
+   */
+  SERVICE_STARTED: 1,
+  /**
+   * Constant: SERVICE_PAUSED
+   * {Integer} used for
+   */
+  SERVICE_PAUSED: 2,
+  /**
+   * Constant: SERVICE_SUCCEEDED
+   * {Integer} used for
+   */
+  SERVICE_SUCCEEDED: 3,
+  /**
+   * Constant: SERVICE_FAILED
+   * {Integer} used for
+   */
+  SERVICE_FAILED: 4,
+  /** 
+   * Function: removeItem
+   * Remove an object from an array. Iterates through the array
+   *     to find the item, then removes it.
+   *
+   * Parameters:
+   * array - {Array}
+   * item - {Object}
+   * 
+   * Return
+   * {Array} A reference to the array
+   */
+  removeItem: function(array, item) {
+    for(var i = array.length - 1; i >= 0; i--) {
+        if(array[i] == item) {
+            array.splice(i,1);
+        }
+    }
+    return array;
+  },
+  /** 
+   * Function: indexOf
+   * 
+   * Parameters:
+   * array - {Array}
+   * obj - {Object}
+   * 
+   * Returns:
+   * {Integer} The index at, which the first object was found in the array.
+   *           If not found, returns -1.
+   */
+  indexOf: function(array, obj) {
+    for(var i=0, len=array.length; i<len; i++) {
+      if (array[i] == obj)
+        return i;
+    }
+    return -1;   
+  },
+  /**
+   * Function: extend
+   * Copy all properties of a source object to a destination object. Modifies
+   *     the passed in destination object.  Any properties on the source object
+   *     that are set to undefined will not be (re)set on the destination object.
+   *
+   * Parameters:
+   * destination - {Object} The object that will be modified
+   * source - {Object} The object with properties to be set on the destination
+   *
+   * Returns:
+   * {Object} The destination object.
+   */
+  extend: function(destination, source) {
+    destination = destination || {};
+    if(source) {
+      for(var property in source) {
+        var value = source[property];
+        if(value !== undefined)
+          destination[property] = value;
+      }
+    }
+    return destination;
+  },
+  /**
+   * Function: rad
+   * 
+   * Parameters:
+   * x - {Float}
+   * 
+   * Returns:
+   * {Float}
+   */
+  rad: function(x) {return x*Math.PI/180;},
+  /**
+   * Function: distVincenty
+   * Given two objects representing points with geographic coordinates, this
+   *     calculates the distance between those points on the surface of an
+   *     ellipsoid.
+   * 
+   * Parameters:
+   * p1 - {<ZOO.Geometry.Point>} (or any object with both .x, .y properties)
+   * p2 - {<ZOO.Geometry.Point>} (or any object with both .x, .y properties)
+   * 
+   * Returns:
+   * {Float} The distance (in km) between the two input points as measured on an
+   *     ellipsoid.  Note that the input point objects must be in geographic
+   *     coordinates (decimal degrees) and the return distance is in kilometers.
+   */
+  distVincenty: function(p1, p2) {
+    var a = 6378137, b = 6356752.3142,  f = 1/298.257223563;
+    var L = ZOO.rad(p2.x - p1.y);
+    var U1 = Math.atan((1-f) * Math.tan(ZOO.rad(p1.y)));
+    var U2 = Math.atan((1-f) * Math.tan(ZOO.rad(p2.y)));
+    var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
+    var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
+    var lambda = L, lambdaP = 2*Math.PI;
+    var iterLimit = 20;
+    while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
+        var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
+        var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+        (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
+        if (sinSigma==0) {
+            return 0;  // co-incident points
+        }
+        var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
+        var sigma = Math.atan2(sinSigma, cosSigma);
+        var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
+        var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
+        var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
+        var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+        lambdaP = lambda;
+        lambda = L + (1-C) * f * Math.sin(alpha) *
+        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
+    }
+    if (iterLimit==0) {
+        return NaN;  // formula failed to converge
+    }
+    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+    var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+    var s = b*A*(sigma-deltaSigma);
+    var d = s.toFixed(3)/1000; // round to 1mm precision
+    return d;
+  },
+  /**
+   * Function: Class
+   * Method used to create ZOO classes. Includes support for
+   *     multiple inheritance.
+   */
+  Class: function() {
+    var Class = function() {
+      this.initialize.apply(this, arguments);
+    };
+    var extended = {};
+    var parent;
+    for(var i=0; i<arguments.length; ++i) {
+      if(typeof arguments[i] == "function") {
+        // get the prototype of the superclass
+        parent = arguments[i].prototype;
+      } else {
+        // in this case we're extending with the prototype
+        parent = arguments[i];
+      }
+      ZOO.extend(extended, parent);
+    }
+    Class.prototype = extended;
+
+    return Class;
+  },
+  /**
+   * Function: UpdateStatus
+   * Method used to update the status of the process
+   *
+   * Parameters:
+   * env - {Object} The environment object
+   * value - {Float} the status value between 0 to 100
+   */
+  UpdateStatus: function(env,value) {
+    return ZOOUpdateStatus(env,value);
+  }
+};
+
+/**
+ * Class: ZOO.String
+ * Contains convenience methods for string manipulation
+ */
+ZOO.String = {
+  /**
+   * Function: startsWith
+   * Test whether a string starts with another string. 
+   * 
+   * Parameters:
+   * str - {String} The string to test.
+   * sub - {Sring} The substring to look for.
+   *  
+   * Returns:
+   * {Boolean} The first string starts with the second.
+   */
+  startsWith: function(str, sub) {
+    return (str.indexOf(sub) == 0);
+  },
+  /**
+   * Function: contains
+   * Test whether a string contains another string.
+   * 
+   * Parameters:
+   * str - {String} The string to test.
+   * sub - {String} The substring to look for.
+   * 
+   * Returns:
+   * {Boolean} The first string contains the second.
+   */
+  contains: function(str, sub) {
+    return (str.indexOf(sub) != -1);
+  },
+  /**
+   * Function: trim
+   * Removes leading and trailing whitespace characters from a string.
+   * 
+   * Parameters:
+   * str - {String} The (potentially) space padded string.  This string is not
+   *     modified.
+   * 
+   * Returns:
+   * {String} A trimmed version of the string with all leading and 
+   *     trailing spaces removed.
+   */
+  trim: function(str) {
+    return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+  },
+  /**
+   * Function: camelize
+   * Camel-case a hyphenated string. 
+   *     Ex. "chicken-head" becomes "chickenHead", and
+   *     "-chicken-head" becomes "ChickenHead".
+   *
+   * Parameters:
+   * str - {String} The string to be camelized.  The original is not modified.
+   * 
+   * Returns:
+   * {String} The string, camelized
+   *
+   */
+  camelize: function(str) {
+    var oStringList = str.split('-');
+    var camelizedString = oStringList[0];
+    for (var i=1, len=oStringList.length; i<len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+    return camelizedString;
+  },
+  /**
+   * Property: tokenRegEx
+   * Used to find tokens in a string.
+   * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
+   */
+  tokenRegEx:  /\$\{([\w.]+?)\}/g,
+  /**
+   * Property: numberRegEx
+   * Used to test strings as numbers.
+   */
+  numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
+  /**
+   * Function: isNumeric
+   * Determine whether a string contains only a numeric value.
+   *
+   * Examples:
+   * (code)
+   * ZOO.String.isNumeric("6.02e23") // true
+   * ZOO.String.isNumeric("12 dozen") // false
+   * ZOO.String.isNumeric("4") // true
+   * ZOO.String.isNumeric(" 4 ") // false
+   * (end)
+   *
+   * Returns:
+   * {Boolean} String contains only a number.
+   */
+  isNumeric: function(value) {
+    return ZOO.String.numberRegEx.test(value);
+  },
+  /**
+   * Function: numericIf
+   * Converts a string that appears to be a numeric value into a number.
+   * 
+   * Returns
+   * {Number|String} a Number if the passed value is a number, a String
+   *     otherwise. 
+   */
+  numericIf: function(value) {
+    return ZOO.String.isNumeric(value) ? parseFloat(value) : value;
+  }
+};
+
+/**
+ * Class: ZOO.Request
+ * Contains convenience methods for working with ZOORequest which
+ *     replace XMLHttpRequest. Because of we are not in a browser
+ *     JavaScript environment, ZOO Project provides a method to 
+ *     query servers which is based on curl : ZOORequest.
+ */
+ZOO.Request = {
+  /**
+   * Function: GET
+   * Send an HTTP GET request.
+   *
+   * Parameters:
+   * url - {String} The URL to request.
+   * params - {Object} Params to add to the url
+   * 
+   * Returns:
+   * {String} Request result.
+   */
+  Get: function(url,params) {
+    var paramsArray = [];
+    for (var key in params) {
+      var value = params[key];
+      if ((value != null) && (typeof value != 'function')) {
+        var encodedValue;
+        if (typeof value == 'object' && value.constructor == Array) {
+          /* value is an array; encode items and separate with "," */
+          var encodedItemArray = [];
+          for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
+            encodedItemArray.push(encodeURIComponent(value[itemIndex]));
+          }
+          encodedValue = encodedItemArray.join(",");
+        }
+        else {
+          /* value is a string; simply encode */
+          encodedValue = encodeURIComponent(value);
+        }
+        paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
+      }
+    }
+    var paramString = paramsArray.join("&");
+    if(paramString.length > 0) {
+      var separator = (url.indexOf('?') > -1) ? '&' : '?';
+      url += separator + paramString;
+    }
+    return ZOORequest('GET',url);
+  },
+  /**
+   * Function: POST
+   * Send an HTTP POST request.
+   *
+   * Parameters:
+   * url - {String} The URL to request.
+   * body - {String} The request's body to send.
+   * headers - {Object} A key-value object of headers to push to
+   *     the request's head
+   * 
+   * Returns:
+   * {String} Request result.
+   */
+  Post: function(url,body,headers) {
+    if(!(headers instanceof Array)) {
+      var headersArray = [];
+      for (var name in headers) {
+        headersArray.push(name+': '+headers[name]); 
+      }
+      headers = headersArray;
+    }
+    return ZOORequest('POST',url,body,headers);
+  }
+};
+
+/**
+ * Class: ZOO.Bounds
+ * Instances of this class represent bounding boxes.  Data stored as left,
+ *     bottom, right, top floats. All values are initialized to null,
+ *     however, you should make sure you set them before using the bounds
+ *     for anything.
+ */
+ZOO.Bounds = ZOO.Class({
+  /**
+   * Property: left
+   * {Number} Minimum horizontal coordinate.
+   */
+  left: null,
+  /**
+   * Property: bottom
+   * {Number} Minimum vertical coordinate.
+   */
+  bottom: null,
+  /**
+   * Property: right
+   * {Number} Maximum horizontal coordinate.
+   */
+  right: null,
+  /**
+   * Property: top
+   * {Number} Maximum vertical coordinate.
+   */
+  top: null,
+  /**
+   * Constructor: ZOO.Bounds
+   * Construct a new bounds object.
+   *
+   * Parameters:
+   * left - {Number} The left bounds of the box.  Note that for width
+   *        calculations, this is assumed to be less than the right value.
+   * bottom - {Number} The bottom bounds of the box.  Note that for height
+   *          calculations, this is assumed to be more than the top value.
+   * right - {Number} The right bounds.
+   * top - {Number} The top bounds.
+   */
+  initialize: function(left, bottom, right, top) {
+    if (left != null)
+      this.left = parseFloat(left);
+    if (bottom != null)
+      this.bottom = parseFloat(bottom);
+    if (right != null)
+      this.right = parseFloat(right);
+    if (top != null)
+      this.top = parseFloat(top);
+  },
+  /**
+   * Method: clone
+   * Create a cloned instance of this bounds.
+   *
+   * Returns:
+   * {<ZOO.Bounds>} A fresh copy of the bounds
+   */
+  clone:function() {
+    return new ZOO.Bounds(this.left, this.bottom, 
+                          this.right, this.top);
+  },
+  /**
+   * Method: equals
+   * Test a two bounds for equivalence.
+   *
+   * Parameters:
+   * bounds - {<ZOO.Bounds>}
+   *
+   * Returns:
+   * {Boolean} The passed-in bounds object has the same left,
+   *           right, top, bottom components as this.  Note that if bounds 
+   *           passed in is null, returns false.
+   */
+  equals:function(bounds) {
+    var equals = false;
+    if (bounds != null)
+        equals = ((this.left == bounds.left) && 
+                  (this.right == bounds.right) &&
+                  (this.top == bounds.top) && 
+                  (this.bottom == bounds.bottom));
+    return equals;
+  },
+  /** 
+   * Method: toString
+   * 
+   * Returns:
+   * {String} String representation of bounds object. 
+   *          (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
+   */
+  toString:function() {
+    return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
+              + " right-top=(" + this.right + "," + this.top + ")" );
+  },
+  /**
+   * APIMethod: toArray
+   *
+   * Returns:
+   * {Array} array of left, bottom, right, top
+   */
+  toArray: function() {
+    return [this.left, this.bottom, this.right, this.top];
+  },
+  /** 
+   * Method: toBBOX
+   * 
+   * Parameters:
+   * decimal - {Integer} How many significant digits in the bbox coords?
+   *                     Default is 6
+   * 
+   * Returns:
+   * {String} Simple String representation of bounds object.
+   *          (ex. <i>"5,42,10,45"</i>)
+   */
+  toBBOX:function(decimal) {
+    if (decimal== null)
+      decimal = 6; 
+    var mult = Math.pow(10, decimal);
+    var bbox = Math.round(this.left * mult) / mult + "," + 
+               Math.round(this.bottom * mult) / mult + "," + 
+               Math.round(this.right * mult) / mult + "," + 
+               Math.round(this.top * mult) / mult;
+    return bbox;
+  },
+  /**
+   * Method: toGeometry
+   * Create a new polygon geometry based on this bounds.
+   *
+   * Returns:
+   * {<ZOO.Geometry.Polygon>} A new polygon with the coordinates
+   *     of this bounds.
+   */
+  toGeometry: function() {
+    return new ZOO.Geometry.Polygon([
+      new ZOO.Geometry.LinearRing([
+        new ZOO.Geometry.Point(this.left, this.bottom),
+        new ZOO.Geometry.Point(this.right, this.bottom),
+        new ZOO.Geometry.Point(this.right, this.top),
+        new ZOO.Geometry.Point(this.left, this.top)
+      ])
+    ]);
+  },
+  /**
+   * Method: getWidth
+   * 
+   * Returns:
+   * {Float} The width of the bounds
+   */
+  getWidth:function() {
+    return (this.right - this.left);
+  },
+  /**
+   * Method: getHeight
+   * 
+   * Returns:
+   * {Float} The height of the bounds (top minus bottom).
+   */
+  getHeight:function() {
+    return (this.top - this.bottom);
+  },
+  /**
+   * Method: add
+   * 
+   * Parameters:
+   * x - {Float}
+   * y - {Float}
+   * 
+   * Returns:
+   * {<ZOO.Bounds>} A new bounds whose coordinates are the same as
+   *     this, but shifted by the passed-in x and y values.
+   */
+  add:function(x, y) {
+    if ( (x == null) || (y == null) )
+      return null;
+    return new ZOO.Bounds(this.left + x, this.bottom + y,
+                                 this.right + x, this.top + y);
+  },
+  /**
+   * Method: extend
+   * Extend the bounds to include the point, lonlat, or bounds specified.
+   *     Note, this function assumes that left < right and bottom < top.
+   * 
+   * Parameters: 
+   * object - {Object} Can be Point, or Bounds
+   */
+  extend:function(object) {
+    var bounds = null;
+    if (object) {
+      // clear cached center location
+      switch(object.CLASS_NAME) {
+        case "ZOO.Geometry.Point":
+          bounds = new ZOO.Bounds(object.x, object.y,
+                                         object.x, object.y);
+          break;
+        case "ZOO.Bounds":    
+          bounds = object;
+          break;
+      }
+      if (bounds) {
+        if ( (this.left == null) || (bounds.left < this.left))
+          this.left = bounds.left;
+        if ( (this.bottom == null) || (bounds.bottom < this.bottom) )
+          this.bottom = bounds.bottom;
+        if ( (this.right == null) || (bounds.right > this.right) )
+          this.right = bounds.right;
+        if ( (this.top == null) || (bounds.top > this.top) )
+          this.top = bounds.top;
+      }
+    }
+  },
+  /**
+   * APIMethod: contains
+   * 
+   * Parameters:
+   * x - {Float}
+   * y - {Float}
+   * inclusive - {Boolean} Whether or not to include the border.
+   *     Default is true.
+   *
+   * Returns:
+   * {Boolean} Whether or not the passed-in coordinates are within this
+   *     bounds.
+   */
+  contains:function(x, y, inclusive) {
+     //set default
+     if (inclusive == null)
+       inclusive = true;
+     if (x == null || y == null)
+       return false;
+     x = parseFloat(x);
+     y = parseFloat(y);
+
+     var contains = false;
+     if (inclusive)
+       contains = ((x >= this.left) && (x <= this.right) && 
+                   (y >= this.bottom) && (y <= this.top));
+     else
+       contains = ((x > this.left) && (x < this.right) && 
+                   (y > this.bottom) && (y < this.top));
+     return contains;
+  },
+  /**
+   * Method: intersectsBounds
+   * Determine whether the target bounds intersects this bounds.  Bounds are
+   *     considered intersecting if any of their edges intersect or if one
+   *     bounds contains the other.
+   * 
+   * Parameters:
+   * bounds - {<ZOO.Bounds>} The target bounds.
+   * inclusive - {Boolean} Treat coincident borders as intersecting.  Default
+   *     is true.  If false, bounds that do not overlap but only touch at the
+   *     border will not be considered as intersecting.
+   *
+   * Returns:
+   * {Boolean} The passed-in bounds object intersects this bounds.
+   */
+  intersectsBounds:function(bounds, inclusive) {
+    if (inclusive == null)
+      inclusive = true;
+    var intersects = false;
+    var mightTouch = (
+        this.left == bounds.right ||
+        this.right == bounds.left ||
+        this.top == bounds.bottom ||
+        this.bottom == bounds.top
+    );
+    if (inclusive || !mightTouch) {
+      var inBottom = (
+          ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) ||
+          ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top))
+          );
+      var inTop = (
+          ((bounds.top >= this.bottom) && (bounds.top <= this.top)) ||
+          ((this.top > bounds.bottom) && (this.top < bounds.top))
+          );
+      var inLeft = (
+          ((bounds.left >= this.left) && (bounds.left <= this.right)) ||
+          ((this.left >= bounds.left) && (this.left <= bounds.right))
+          );
+      var inRight = (
+          ((bounds.right >= this.left) && (bounds.right <= this.right)) ||
+          ((this.right >= bounds.left) && (this.right <= bounds.right))
+          );
+      intersects = ((inBottom || inTop) && (inLeft || inRight));
+    }
+    return intersects;
+  },
+  /**
+   * Method: containsBounds
+   * Determine whether the target bounds is contained within this bounds.
+   * 
+   * bounds - {<ZOO.Bounds>} The target bounds.
+   * partial - {Boolean} If any of the target corners is within this bounds
+   *     consider the bounds contained.  Default is false.  If true, the
+   *     entire target bounds must be contained within this bounds.
+   * inclusive - {Boolean} Treat shared edges as contained.  Default is
+   *     true.
+   *
+   * Returns:
+   * {Boolean} The passed-in bounds object is contained within this bounds. 
+   */
+  containsBounds:function(bounds, partial, inclusive) {
+    if (partial == null)
+      partial = false;
+    if (inclusive == null)
+      inclusive = true;
+    var bottomLeft  = this.contains(bounds.left, bounds.bottom, inclusive);
+    var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
+    var topLeft  = this.contains(bounds.left, bounds.top, inclusive);
+    var topRight = this.contains(bounds.right, bounds.top, inclusive);
+    return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
+                     : (bottomLeft && bottomRight && topLeft && topRight);
+  },
+  CLASS_NAME: 'ZOO.Bounds'
+});
+
+/**
+ * Class: ZOO.Projection
+ * Class for coordinate transforms between coordinate systems.
+ *     Depends on the zoo-proj4js library. zoo-proj4js library 
+ *     is loaded by the ZOO Kernel with zoo-api.
+ */
+ZOO.Projection = ZOO.Class({
+  /**
+   * Property: proj
+   * {Object} Proj4js.Proj instance.
+   */
+  proj: null,
+  /**
+   * Property: projCode
+   * {String}
+   */
+  projCode: null,
+  /**
+   * Constructor: ZOO.Projection
+   * This class offers several methods for interacting with a wrapped 
+   *     zoo-pro4js projection object. 
+   *
+   * Parameters:
+   * projCode - {String} A string identifying the Well Known Identifier for
+   *    the projection.
+   * options - {Object} An optional object to set additional properties.
+   *
+   * Returns:
+   * {<ZOO.Projection>} A projection object.
+   */
+  initialize: function(projCode, options) {
+    ZOO.extend(this, options);
+    this.projCode = projCode;
+    if (Proj4js) {
+      this.proj = new Proj4js.Proj(projCode);
+    }
+  },
+  /**
+   * Method: getCode
+   * Get the string SRS code.
+   *
+   * Returns:
+   * {String} The SRS code.
+   */
+  getCode: function() {
+    return this.proj ? this.proj.srsCode : this.projCode;
+  },
+  /**
+   * Method: getUnits
+   * Get the units string for the projection -- returns null if 
+   *     zoo-proj4js is not available.
+   *
+   * Returns:
+   * {String} The units abbreviation.
+   */
+  getUnits: function() {
+    return this.proj ? this.proj.units : null;
+  },
+  /**
+   * Method: toString
+   * Convert projection to string (getCode wrapper).
+   *
+   * Returns:
+   * {String} The projection code.
+   */
+  toString: function() {
+    return this.getCode();
+  },
+  /**
+   * Method: equals
+   * Test equality of two projection instances.  Determines equality based
+   *     soley on the projection code.
+   *
+   * Returns:
+   * {Boolean} The two projections are equivalent.
+   */
+  equals: function(projection) {
+    if (projection && projection.getCode)
+      return this.getCode() == projection.getCode();
+    else
+      return false;
+  },
+  /* Method: destroy
+   * Destroy projection object.
+   */
+  destroy: function() {
+    this.proj = null;
+    this.projCode = null;
+  },
+  CLASS_NAME: 'ZOO.Projection'
+});
+/**
+ * Method: transform
+ * Transform a point coordinate from one projection to another.  Note that
+ *     the input point is transformed in place.
+ * 
+ * Parameters:
+ * point - {{ZOO.Geometry.Point> | Object} An object with x and y
+ *     properties representing coordinates in those dimensions.
+ * sourceProj - {ZOO.Projection} Source map coordinate system
+ * destProj - {ZOO.Projection} Destination map coordinate system
+ *
+ * Returns:
+ * point - {object} A transformed coordinate.  The original point is modified.
+ */
+ZOO.Projection.transform = function(point, source, dest) {
+    if (source.proj && dest.proj)
+        point = Proj4js.transform(source.proj, dest.proj, point);
+    return point;
+};
+
+/**
+ * Class: ZOO.Format
+ * Base class for format reading/writing a variety of formats. Subclasses
+ *     of ZOO.Format are expected to have read and write methods.
+ */
+ZOO.Format = ZOO.Class({
+  /**
+   * Property: options
+   * {Object} A reference to options passed to the constructor.
+   */
+  options:null,
+  /**
+   * Property: externalProjection
+   * {<ZOO.Projection>} When passed a externalProjection and
+   *     internalProjection, the format will reproject the geometries it
+   *     reads or writes. The externalProjection is the projection used by
+   *     the content which is passed into read or which comes out of write.
+   *     In order to reproject, a projection transformation function for the
+   *     specified projections must be available. This support is provided 
+   *     via zoo-proj4js.
+   */
+  externalProjection: null,
+  /**
+   * Property: internalProjection
+   * {<ZOO.Projection>} When passed a externalProjection and
+   *     internalProjection, the format will reproject the geometries it
+   *     reads or writes. The internalProjection is the projection used by
+   *     the geometries which are returned by read or which are passed into
+   *     write.  In order to reproject, a projection transformation function
+   *     for the specified projections must be available. This support is 
+   *     provided via zoo-proj4js.
+   */
+  internalProjection: null,
+  /**
+   * Property: data
+   * {Object} When <keepData> is true, this is the parsed string sent to
+   *     <read>.
+   */
+  data: null,
+  /**
+   * Property: keepData
+   * {Object} Maintain a reference (<data>) to the most recently read data.
+   *     Default is false.
+   */
+  keepData: false,
+  /**
+   * Constructor: ZOO.Format
+   * Instances of this class are not useful.  See one of the subclasses.
+   *
+   * Parameters:
+   * options - {Object} An optional object with properties to set on the
+   *           format
+   *
+   * Valid options:
+   * keepData - {Boolean} If true, upon <read>, the data property will be
+   *     set to the parsed object (e.g. the json or xml object).
+   *
+   * Returns:
+   * An instance of ZOO.Format
+   */
+  initialize: function(options) {
+    ZOO.extend(this, options);
+    this.options = options;
+  },
+  /**
+   * Method: destroy
+   * Clean up.
+   */
+  destroy: function() {
+  },
+  /**
+   * Method: read
+   * Read data from a string, and return an object whose type depends on the
+   * subclass. 
+   * 
+   * Parameters:
+   * data - {string} Data to read/parse.
+   *
+   * Returns:
+   * Depends on the subclass
+   */
+  read: function(data) {
+  },
+  /**
+   * Method: write
+   * Accept an object, and return a string. 
+   *
+   * Parameters:
+   * object - {Object} Object to be serialized
+   *
+   * Returns:
+   * {String} A string representation of the object.
+   */
+  write: function(data) {
+  },
+  CLASS_NAME: 'ZOO.Format'
+});
+/**
+ * Class: ZOO.Format.WKT
+ * Class for reading and writing Well-Known Text. Create a new instance
+ * with the <ZOO.Format.WKT> constructor.
+ * 
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.WKT = ZOO.Class(ZOO.Format, {
+  /**
+   * Constructor: ZOO.Format.WKT
+   * Create a new parser for WKT
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *           this instance
+   *
+   * Returns:
+   * {<ZOO.Format.WKT>} A new WKT parser.
+   */
+  initialize: function(options) {
+    this.regExes = {
+      'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
+      'spaces': /\s+/,
+      'parenComma': /\)\s*,\s*\(/,
+      'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/,  // can't use {2} here
+      'trimParens': /^\s*\(?(.*?)\)?\s*$/
+    };
+    ZOO.Format.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * Method: read
+   * Deserialize a WKT string and return a vector feature or an
+   *     array of vector features.  Supports WKT for POINT, 
+   *     MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON, 
+   *     MULTIPOLYGON, and GEOMETRYCOLLECTION.
+   *
+   * Parameters:
+   * wkt - {String} A WKT string
+   *
+   * Returns:
+   * {<ZOO.Feature.Vector>|Array} A feature or array of features for
+   *     GEOMETRYCOLLECTION WKT.
+   */
+  read: function(wkt) {
+    var features, type, str;
+    var matches = this.regExes.typeStr.exec(wkt);
+    if(matches) {
+      type = matches[1].toLowerCase();
+      str = matches[2];
+      if(this.parse[type]) {
+        features = this.parse[type].apply(this, [str]);
+      }
+      if (this.internalProjection && this.externalProjection) {
+        if (features && 
+            features.CLASS_NAME == "ZOO.Feature") {
+          features.geometry.transform(this.externalProjection,
+                                      this.internalProjection);
+        } else if (features &&
+            type != "geometrycollection" &&
+            typeof features == "object") {
+          for (var i=0, len=features.length; i<len; i++) {
+            var component = features[i];
+            component.geometry.transform(this.externalProjection,
+                                         this.internalProjection);
+          }
+        }
+      }
+    }    
+    return features;
+  },
+  /**
+   * Method: write
+   * Serialize a feature or array of features into a WKT string.
+   *
+   * Parameters:
+   * features - {<ZOO.Feature.Vector>|Array} A feature or array of
+   *            features
+   *
+   * Returns:
+   * {String} The WKT string representation of the input geometries
+   */
+  write: function(features) {
+    var collection, geometry, type, data, isCollection;
+    if(features.constructor == Array) {
+      collection = features;
+      isCollection = true;
+    } else {
+      collection = [features];
+      isCollection = false;
+    }
+    var pieces = [];
+    if(isCollection)
+      pieces.push('GEOMETRYCOLLECTION(');
+    for(var i=0, len=collection.length; i<len; ++i) {
+      if(isCollection && i>0)
+        pieces.push(',');
+      geometry = collection[i].geometry;
+      type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
+      if(!this.extract[type])
+        return null;
+      if (this.internalProjection && this.externalProjection) {
+        geometry = geometry.clone();
+        geometry.transform(this.internalProjection, 
+                          this.externalProjection);
+      }                       
+      data = this.extract[type].apply(this, [geometry]);
+      pieces.push(type.toUpperCase() + '(' + data + ')');
+    }
+    if(isCollection)
+      pieces.push(')');
+    return pieces.join('');
+  },
+  /**
+   * Property: extract
+   * Object with properties corresponding to the geometry types.
+   * Property values are functions that do the actual data extraction.
+   */
+  extract: {
+    /**
+     * Return a space delimited string of point coordinates.
+     * @param {<ZOO.Geometry.Point>} point
+     * @returns {String} A string of coordinates representing the point
+     */
+    'point': function(point) {
+      return point.x + ' ' + point.y;
+    },
+    /**
+     * Return a comma delimited string of point coordinates from a multipoint.
+     * @param {<ZOO.Geometry.MultiPoint>} multipoint
+     * @returns {String} A string of point coordinate strings representing
+     *                  the multipoint
+     */
+    'multipoint': function(multipoint) {
+      var array = [];
+      for(var i=0, len=multipoint.components.length; i<len; ++i) {
+        array.push(this.extract.point.apply(this, [multipoint.components[i]]));
+      }
+      return array.join(',');
+    },
+    /**
+     * Return a comma delimited string of point coordinates from a line.
+     * @param {<ZOO.Geometry.LineString>} linestring
+     * @returns {String} A string of point coordinate strings representing
+     *                  the linestring
+     */
+    'linestring': function(linestring) {
+      var array = [];
+      for(var i=0, len=linestring.components.length; i<len; ++i) {
+        array.push(this.extract.point.apply(this, [linestring.components[i]]));
+      }
+      return array.join(',');
+    },
+    /**
+     * Return a comma delimited string of linestring strings from a multilinestring.
+     * @param {<ZOO.Geometry.MultiLineString>} multilinestring
+     * @returns {String} A string of of linestring strings representing
+     *                  the multilinestring
+     */
+    'multilinestring': function(multilinestring) {
+      var array = [];
+      for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+        array.push('(' +
+            this.extract.linestring.apply(this, [multilinestring.components[i]]) +
+            ')');
+      }
+      return array.join(',');
+    },
+    /**
+     * Return a comma delimited string of linear ring arrays from a polygon.
+     * @param {<ZOO.Geometry.Polygon>} polygon
+     * @returns {String} An array of linear ring arrays representing the polygon
+     */
+    'polygon': function(polygon) {
+      var array = [];
+      for(var i=0, len=polygon.components.length; i<len; ++i) {
+        array.push('(' +
+            this.extract.linestring.apply(this, [polygon.components[i]]) +
+            ')');
+      }
+      return array.join(',');
+    },
+    /**
+     * Return an array of polygon arrays from a multipolygon.
+     * @param {<ZOO.Geometry.MultiPolygon>} multipolygon
+     * @returns {Array} An array of polygon arrays representing
+     *                  the multipolygon
+     */
+    'multipolygon': function(multipolygon) {
+      var array = [];
+      for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+        array.push('(' +
+            this.extract.polygon.apply(this, [multipolygon.components[i]]) +
+            ')');
+      }
+      return array.join(',');
+    }
+  },
+  /**
+   * Property: parse
+   * Object with properties corresponding to the geometry types.
+   *     Property values are functions that do the actual parsing.
+   */
+  parse: {
+    /**
+     * Method: parse.point
+     * Return point feature given a point WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the point
+     * Returns:
+     * {<ZOO.Feature>} A point feature
+     */
+    'point': function(str) {
+       var coords = ZOO.String.trim(str).split(this.regExes.spaces);
+            return new ZOO.Feature(
+                new ZOO.Geometry.Point(coords[0], coords[1])
+            );
+    },
+    /**
+     * Method: parse.multipoint
+     * Return a multipoint feature given a multipoint WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the multipoint
+     *
+     * Returns:
+     * {<ZOO.Feature>} A multipoint feature
+     */
+    'multipoint': function(str) {
+       var points = ZOO.String.trim(str).split(',');
+       var components = [];
+       for(var i=0, len=points.length; i<len; ++i) {
+         components.push(this.parse.point.apply(this, [points[i]]).geometry);
+       }
+       return new ZOO.Feature(
+           new ZOO.Geometry.MultiPoint(components)
+           );
+    },
+    /**
+     * Method: parse.linestring
+     * Return a linestring feature given a linestring WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the linestring
+     *
+     * Returns:
+     * {<ZOO.Feature>} A linestring feature
+     */
+    'linestring': function(str) {
+      var points = ZOO.String.trim(str).split(',');
+      var components = [];
+      for(var i=0, len=points.length; i<len; ++i) {
+        components.push(this.parse.point.apply(this, [points[i]]).geometry);
+      }
+      return new ZOO.Feature(
+          new ZOO.Geometry.LineString(components)
+          );
+    },
+    /**
+     * Method: parse.multilinestring
+     * Return a multilinestring feature given a multilinestring WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the multilinestring
+     *
+     * Returns:
+     * {<ZOO.Feature>} A multilinestring feature
+     */
+    'multilinestring': function(str) {
+      var line;
+      var lines = ZOO.String.trim(str).split(this.regExes.parenComma);
+      var components = [];
+      for(var i=0, len=lines.length; i<len; ++i) {
+        line = lines[i].replace(this.regExes.trimParens, '$1');
+        components.push(this.parse.linestring.apply(this, [line]).geometry);
+      }
+      return new ZOO.Feature(
+          new ZOO.Geometry.MultiLineString(components)
+          );
+    },
+    /**
+     * Method: parse.polygon
+     * Return a polygon feature given a polygon WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the polygon
+     *
+     * Returns:
+     * {<ZOO.Feature>} A polygon feature
+     */
+    'polygon': function(str) {
+       var ring, linestring, linearring;
+       var rings = ZOO.String.trim(str).split(this.regExes.parenComma);
+       var components = [];
+       for(var i=0, len=rings.length; i<len; ++i) {
+         ring = rings[i].replace(this.regExes.trimParens, '$1');
+         linestring = this.parse.linestring.apply(this, [ring]).geometry;
+         linearring = new ZOO.Geometry.LinearRing(linestring.components);
+         components.push(linearring);
+       }
+       return new ZOO.Feature(
+           new ZOO.Geometry.Polygon(components)
+           );
+    },
+    /**
+     * Method: parse.multipolygon
+     * Return a multipolygon feature given a multipolygon WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the multipolygon
+     *
+     * Returns:
+     * {<ZOO.Feature>} A multipolygon feature
+     */
+    'multipolygon': function(str) {
+      var polygon;
+      var polygons = ZOO.String.trim(str).split(this.regExes.doubleParenComma);
+      var components = [];
+      for(var i=0, len=polygons.length; i<len; ++i) {
+        polygon = polygons[i].replace(this.regExes.trimParens, '$1');
+        components.push(this.parse.polygon.apply(this, [polygon]).geometry);
+      }
+      return new ZOO.Feature(
+          new ZOO.Geometry.MultiPolygon(components)
+          );
+    },
+    /**
+     * Method: parse.geometrycollection
+     * Return an array of features given a geometrycollection WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the geometrycollection
+     *
+     * Returns:
+     * {Array} An array of ZOO.Feature
+     */
+    'geometrycollection': function(str) {
+      // separate components of the collection with |
+      str = str.replace(/,\s*([A-Za-z])/g, '|$1');
+      var wktArray = ZOO.String.trim(str).split('|');
+      var components = [];
+      for(var i=0, len=wktArray.length; i<len; ++i) {
+        components.push(ZOO.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
+      }
+      return components;
+    }
+  },
+  CLASS_NAME: 'ZOO.Format.WKT'
+});
+/**
+ * Class: ZOO.Format.JSON
+ * A parser to read/write JSON safely. Create a new instance with the
+ *     <ZOO.Format.JSON> constructor.
+ *
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.JSON = ZOO.Class(ZOO.Format, {
+  /**
+   * Property: indent
+   * {String} For "pretty" printing, the indent string will be used once for
+   *     each indentation level.
+   */
+  indent: "    ",
+  /**
+   * Property: space
+   * {String} For "pretty" printing, the space string will be used after
+   *     the ":" separating a name/value pair.
+   */
+  space: " ",
+  /**
+   * Property: newline
+   * {String} For "pretty" printing, the newline string will be used at the
+   *     end of each name/value pair or array item.
+   */
+  newline: "\n",
+  /**
+   * Property: level
+   * {Integer} For "pretty" printing, this is incremented/decremented during
+   *     serialization.
+   */
+  level: 0,
+  /**
+   * Property: pretty
+   * {Boolean} Serialize with extra whitespace for structure.  This is set
+   *     by the <write> method.
+   */
+  pretty: false,
+  /**
+   * Constructor: ZOO.Format.JSON
+   * Create a new parser for JSON.
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *     this instance.
+   */
+  initialize: function(options) {
+    ZOO.Format.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * Method: read
+   * Deserialize a json string.
+   *
+   * Parameters:
+   * json - {String} A JSON string
+   * filter - {Function} A function which will be called for every key and
+   *     value at every level of the final result. Each value will be
+   *     replaced by the result of the filter function. This can be used to
+   *     reform generic objects into instances of classes, or to transform
+   *     date strings into Date objects.
+   *     
+   * Returns:
+   * {Object} An object, array, string, or number .
+   */
+  read: function(json, filter) {
+    /**
+     * Parsing happens in three stages. In the first stage, we run the text
+     *     against a regular expression which looks for non-JSON
+     *     characters. We are especially concerned with '()' and 'new'
+     *     because they can cause invocation, and '=' because it can cause
+     *     mutation. But just to be safe, we will reject all unexpected
+     *     characters.
+     */
+    try {
+      if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
+                          replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+                          replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+        /**
+         * In the second stage we use the eval function to compile the
+         *     text into a JavaScript structure. The '{' operator is
+         *     subject to a syntactic ambiguity in JavaScript - it can
+         *     begin a block or an object literal. We wrap the text in
+         *     parens to eliminate the ambiguity.
+         */
+        var object = eval('(' + json + ')');
+        /**
+         * In the optional third stage, we recursively walk the new
+         *     structure, passing each name/value pair to a filter
+         *     function for possible transformation.
+         */
+        if(typeof filter === 'function') {
+          function walk(k, v) {
+            if(v && typeof v === 'object') {
+              for(var i in v) {
+                if(v.hasOwnProperty(i)) {
+                  v[i] = walk(i, v[i]);
+                }
+              }
+            }
+            return filter(k, v);
+          }
+          object = walk('', object);
+        }
+        if(this.keepData) {
+          this.data = object;
+        }
+        return object;
+      }
+    } catch(e) {
+      // Fall through if the regexp test fails.
+    }
+    return null;
+  },
+  /**
+   * Method: write
+   * Serialize an object into a JSON string.
+   *
+   * Parameters:
+   * value - {String} The object, array, string, number, boolean or date
+   *     to be serialized.
+   * pretty - {Boolean} Structure the output with newlines and indentation.
+   *     Default is false.
+   *
+   * Returns:
+   * {String} The JSON string representation of the input value.
+   */
+  write: function(value, pretty) {
+    this.pretty = !!pretty;
+    var json = null;
+    var type = typeof value;
+    if(this.serialize[type]) {
+      try {
+        json = this.serialize[type].apply(this, [value]);
+      } catch(err) {
+        //OpenLayers.Console.error("Trouble serializing: " + err);
+      }
+    }
+    return json;
+  },
+  /**
+   * Method: writeIndent
+   * Output an indentation string depending on the indentation level.
+   *
+   * Returns:
+   * {String} An appropriate indentation string.
+   */
+  writeIndent: function() {
+    var pieces = [];
+    if(this.pretty) {
+      for(var i=0; i<this.level; ++i) {
+        pieces.push(this.indent);
+      }
+    }
+    return pieces.join('');
+  },
+  /**
+   * Method: writeNewline
+   * Output a string representing a newline if in pretty printing mode.
+   *
+   * Returns:
+   * {String} A string representing a new line.
+   */
+  writeNewline: function() {
+    return (this.pretty) ? this.newline : '';
+  },
+  /**
+   * Method: writeSpace
+   * Output a string representing a space if in pretty printing mode.
+   *
+   * Returns:
+   * {String} A space.
+   */
+  writeSpace: function() {
+    return (this.pretty) ? this.space : '';
+  },
+  /**
+   * Property: serialize
+   * Object with properties corresponding to the serializable data types.
+   *     Property values are functions that do the actual serializing.
+   */
+  serialize: {
+    /**
+     * Method: serialize.object
+     * Transform an object into a JSON string.
+     *
+     * Parameters:
+     * object - {Object} The object to be serialized.
+     * 
+     * Returns:
+     * {String} A JSON string representing the object.
+     */
+    'object': function(object) {
+       // three special objects that we want to treat differently
+       if(object == null)
+         return "null";
+       if(object.constructor == Date)
+         return this.serialize.date.apply(this, [object]);
+       if(object.constructor == Array)
+         return this.serialize.array.apply(this, [object]);
+       var pieces = ['{'];
+       this.level += 1;
+       var key, keyJSON, valueJSON;
+
+       var addComma = false;
+       for(key in object) {
+         if(object.hasOwnProperty(key)) {
+           // recursive calls need to allow for sub-classing
+           keyJSON = ZOO.Format.JSON.prototype.write.apply(this,
+                                                           [key, this.pretty]);
+           valueJSON = ZOO.Format.JSON.prototype.write.apply(this,
+                                                             [object[key], this.pretty]);
+           if(keyJSON != null && valueJSON != null) {
+             if(addComma)
+               pieces.push(',');
+             pieces.push(this.writeNewline(), this.writeIndent(),
+                         keyJSON, ':', this.writeSpace(), valueJSON);
+             addComma = true;
+           }
+         }
+       }
+       this.level -= 1;
+       pieces.push(this.writeNewline(), this.writeIndent(), '}');
+       return pieces.join('');
+    },
+    /**
+     * Method: serialize.array
+     * Transform an array into a JSON string.
+     *
+     * Parameters:
+     * array - {Array} The array to be serialized
+     * 
+     * Returns:
+     * {String} A JSON string representing the array.
+     */
+    'array': function(array) {
+      var json;
+      var pieces = ['['];
+      this.level += 1;
+      for(var i=0, len=array.length; i<len; ++i) {
+        // recursive calls need to allow for sub-classing
+        json = ZOO.Format.JSON.prototype.write.apply(this,
+                                                     [array[i], this.pretty]);
+        if(json != null) {
+          if(i > 0)
+            pieces.push(',');
+          pieces.push(this.writeNewline(), this.writeIndent(), json);
+        }
+      }
+      this.level -= 1;    
+      pieces.push(this.writeNewline(), this.writeIndent(), ']');
+      return pieces.join('');
+    },
+    /**
+     * Method: serialize.string
+     * Transform a string into a JSON string.
+     *
+     * Parameters:
+     * string - {String} The string to be serialized
+     * 
+     * Returns:
+     * {String} A JSON string representing the string.
+     */
+    'string': function(string) {
+      var m = {
+                '\b': '\\b',
+                '\t': '\\t',
+                '\n': '\\n',
+                '\f': '\\f',
+                '\r': '\\r',
+                '"' : '\\"',
+                '\\': '\\\\'
+      };
+      if(/["\\\x00-\x1f]/.test(string)) {
+        return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+            var c = m[b];
+            if(c)
+              return c;
+            c = b.charCodeAt();
+            return '\\u00' +
+            Math.floor(c / 16).toString(16) +
+            (c % 16).toString(16);
+        }) + '"';
+      }
+      return '"' + string + '"';
+    },
+    /**
+     * Method: serialize.number
+     * Transform a number into a JSON string.
+     *
+     * Parameters:
+     * number - {Number} The number to be serialized.
+     *
+     * Returns:
+     * {String} A JSON string representing the number.
+     */
+    'number': function(number) {
+      return isFinite(number) ? String(number) : "null";
+    },
+    /**
+     * Method: serialize.boolean
+     * Transform a boolean into a JSON string.
+     *
+     * Parameters:
+     * bool - {Boolean} The boolean to be serialized.
+     * 
+     * Returns:
+     * {String} A JSON string representing the boolean.
+     */
+    'boolean': function(bool) {
+      return String(bool);
+    },
+    /**
+     * Method: serialize.date
+     * Transform a date into a JSON string.
+     *
+     * Parameters:
+     * date - {Date} The date to be serialized.
+     * 
+     * Returns:
+     * {String} A JSON string representing the date.
+     */
+    'date': function(date) {    
+      function format(number) {
+        // Format integers to have at least two digits.
+        return (number < 10) ? '0' + number : number;
+      }
+      return '"' + date.getFullYear() + '-' +
+        format(date.getMonth() + 1) + '-' +
+        format(date.getDate()) + 'T' +
+        format(date.getHours()) + ':' +
+        format(date.getMinutes()) + ':' +
+        format(date.getSeconds()) + '"';
+    }
+  },
+  CLASS_NAME: 'ZOO.Format.JSON'
+});
+/**
+ * Class: ZOO.Format.GeoJSON
+ * Read and write GeoJSON. Create a new parser with the
+ *     <ZOO.Format.GeoJSON> constructor.
+ *
+ * Inherits from:
+ *  - <ZOO.Format.JSON>
+ */
+ZOO.Format.GeoJSON = ZOO.Class(ZOO.Format.JSON, {
+  /**
+   * Constructor: ZOO.Format.GeoJSON
+   * Create a new parser for GeoJSON.
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *     this instance.
+   */
+  initialize: function(options) {
+    ZOO.Format.JSON.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * Method: read
+   * Deserialize a GeoJSON string.
+   *
+   * Parameters:
+   * json - {String} A GeoJSON string
+   * type - {String} Optional string that determines the structure of
+   *     the output.  Supported values are "Geometry", "Feature", and
+   *     "FeatureCollection".  If absent or null, a default of
+   *     "FeatureCollection" is assumed.
+   * filter - {Function} A function which will be called for every key and
+   *     value at every level of the final result. Each value will be
+   *     replaced by the result of the filter function. This can be used to
+   *     reform generic objects into instances of classes, or to transform
+   *     date strings into Date objects.
+   *
+   * Returns: 
+   * {Object} The return depends on the value of the type argument. If type
+   *     is "FeatureCollection" (the default), the return will be an array
+   *     of <ZOO.Feature>. If type is "Geometry", the input json
+   *     must represent a single geometry, and the return will be an
+   *     <ZOO.Geometry>.  If type is "Feature", the input json must
+   *     represent a single feature, and the return will be an
+   *     <ZOO.Feature>.
+   */
+  read: function(json, type, filter) {
+    type = (type) ? type : "FeatureCollection";
+    var results = null;
+    var obj = null;
+    if (typeof json == "string")
+      obj = ZOO.Format.JSON.prototype.read.apply(this,[json, filter]);
+    else
+      obj = json;
+    if(!obj) {
+      //ZOO.Console.error("Bad JSON: " + json);
+    } else if(typeof(obj.type) != "string") {
+      //ZOO.Console.error("Bad GeoJSON - no type: " + json);
+    } else if(this.isValidType(obj, type)) {
+      switch(type) {
+        case "Geometry":
+          try {
+            results = this.parseGeometry(obj);
+          } catch(err) {
+            //ZOO.Console.error(err);
+          }
+          break;
+        case "Feature":
+          try {
+            results = this.parseFeature(obj);
+            results.type = "Feature";
+          } catch(err) {
+            //ZOO.Console.error(err);
+          }
+          break;
+        case "FeatureCollection":
+          // for type FeatureCollection, we allow input to be any type
+          results = [];
+          switch(obj.type) {
+            case "Feature":
+              try {
+                results.push(this.parseFeature(obj));
+              } catch(err) {
+                results = null;
+                //ZOO.Console.error(err);
+              }
+              break;
+            case "FeatureCollection":
+              for(var i=0, len=obj.features.length; i<len; ++i) {
+                try {
+                  results.push(this.parseFeature(obj.features[i]));
+                } catch(err) {
+                  results = null;
+                  //ZOO.Console.error(err);
+                }
+              }
+              break;
+            default:
+              try {
+                var geom = this.parseGeometry(obj);
+                results.push(new ZOO.Feature(geom));
+              } catch(err) {
+                results = null;
+                //ZOO.Console.error(err);
+              }
+          }
+          break;
+      }
+    }
+    return results;
+  },
+  /**
+   * Method: isValidType
+   * Check if a GeoJSON object is a valid representative of the given type.
+   *
+   * Returns:
+   * {Boolean} The object is valid GeoJSON object of the given type.
+   */
+  isValidType: function(obj, type) {
+    var valid = false;
+    switch(type) {
+      case "Geometry":
+        if(ZOO.indexOf(
+              ["Point", "MultiPoint", "LineString", "MultiLineString",
+              "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
+              obj.type) == -1) {
+          // unsupported geometry type
+          //ZOO.Console.error("Unsupported geometry type: " +obj.type);
+        } else {
+          valid = true;
+        }
+        break;
+      case "FeatureCollection":
+        // allow for any type to be converted to a feature collection
+        valid = true;
+        break;
+      default:
+        // for Feature types must match
+        if(obj.type == type) {
+          valid = true;
+        } else {
+          //ZOO.Console.error("Cannot convert types from " +obj.type + " to " + type);
+        }
+    }
+    return valid;
+  },
+  /**
+   * Method: parseFeature
+   * Convert a feature object from GeoJSON into an
+   *     <ZOO.Feature>.
+   *
+   * Parameters:
+   * obj - {Object} An object created from a GeoJSON object
+   *
+   * Returns:
+   * {<ZOO.Feature>} A feature.
+   */
+  parseFeature: function(obj) {
+    var feature, geometry, attributes, bbox;
+    attributes = (obj.properties) ? obj.properties : {};
+    bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
+    try {
+      geometry = this.parseGeometry(obj.geometry);
+    } catch(err) {
+      // deal with bad geometries
+      throw err;
+    }
+    feature = new ZOO.Feature(geometry, attributes);
+    if(bbox)
+      feature.bounds = ZOO.Bounds.fromArray(bbox);
+    if(obj.id)
+      feature.fid = obj.id;
+    return feature;
+  },
+  /**
+   * Method: parseGeometry
+   * Convert a geometry object from GeoJSON into an <ZOO.Geometry>.
+   *
+   * Parameters:
+   * obj - {Object} An object created from a GeoJSON object
+   *
+   * Returns: 
+   * {<ZOO.Geometry>} A geometry.
+   */
+  parseGeometry: function(obj) {
+    if (obj == null)
+      return null;
+    var geometry, collection = false;
+    if(obj.type == "GeometryCollection") {
+      if(!(obj.geometries instanceof Array)) {
+        throw "GeometryCollection must have geometries array: " + obj;
+      }
+      var numGeom = obj.geometries.length;
+      var components = new Array(numGeom);
+      for(var i=0; i<numGeom; ++i) {
+        components[i] = this.parseGeometry.apply(
+            this, [obj.geometries[i]]
+            );
+      }
+      geometry = new ZOO.Geometry.Collection(components);
+      collection = true;
+    } else {
+      if(!(obj.coordinates instanceof Array)) {
+        throw "Geometry must have coordinates array: " + obj;
+      }
+      if(!this.parseCoords[obj.type.toLowerCase()]) {
+        throw "Unsupported geometry type: " + obj.type;
+      }
+      try {
+        geometry = this.parseCoords[obj.type.toLowerCase()].apply(
+            this, [obj.coordinates]
+            );
+      } catch(err) {
+        // deal with bad coordinates
+        throw err;
+      }
+    }
+        // We don't reproject collections because the children are reprojected
+        // for us when they are created.
+    if (this.internalProjection && this.externalProjection && !collection) {
+      geometry.transform(this.externalProjection, 
+          this.internalProjection); 
+    }                       
+    return geometry;
+  },
+  /**
+   * Property: parseCoords
+   * Object with properties corresponding to the GeoJSON geometry types.
+   *     Property values are functions that do the actual parsing.
+   */
+  parseCoords: {
+    /**
+     * Method: parseCoords.point
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.Point>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Point>} A geometry.
+     */
+    "point": function(array) {
+      if(array.length != 2) {
+        throw "Only 2D points are supported: " + array;
+      }
+      return new ZOO.Geometry.Point(array[0], array[1]);
+    },
+    /**
+     * Method: parseCoords.multipoint
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.MultiPoint>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiPoint>} A geometry.
+     */
+    "multipoint": function(array) {
+      var points = [];
+      var p = null;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          p = this.parseCoords["point"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        points.push(p);
+      }
+      return new ZOO.Geometry.MultiPoint(points);
+    },
+    /**
+     * Method: parseCoords.linestring
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.LineString>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.LineString>} A geometry.
+     */
+    "linestring": function(array) {
+      var points = [];
+      var p = null;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          p = this.parseCoords["point"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        points.push(p);
+      }
+      return new ZOO.Geometry.LineString(points);
+    },
+    /**
+     * Method: parseCoords.multilinestring
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.MultiLineString>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiLineString>} A geometry.
+     */
+    "multilinestring": function(array) {
+      var lines = [];
+      var l = null;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          l = this.parseCoords["linestring"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        lines.push(l);
+      }
+      return new ZOO.Geometry.MultiLineString(lines);
+    },
+    /**
+     * Method: parseCoords.polygon
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.Polygon>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A geometry.
+     */
+    "polygon": function(array) {
+      var rings = [];
+      var r, l;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          l = this.parseCoords["linestring"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        r = new ZOO.Geometry.LinearRing(l.components);
+        rings.push(r);
+      }
+      return new ZOO.Geometry.Polygon(rings);
+    },
+    /**
+     * Method: parseCoords.multipolygon
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.MultiPolygon>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiPolygon>} A geometry.
+     */
+    "multipolygon": function(array) {
+      var polys = [];
+      var p = null;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          p = this.parseCoords["polygon"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        polys.push(p);
+      }
+      return new ZOO.Geometry.MultiPolygon(polys);
+    },
+    /**
+     * Method: parseCoords.box
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.Polygon>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A geometry.
+     */
+    "box": function(array) {
+      if(array.length != 2) {
+        throw "GeoJSON box coordinates must have 2 elements";
+      }
+      return new ZOO.Geometry.Polygon([
+          new ZOO.Geometry.LinearRing([
+            new ZOO.Geometry.Point(array[0][0], array[0][1]),
+            new ZOO.Geometry.Point(array[1][0], array[0][1]),
+            new ZOO.Geometry.Point(array[1][0], array[1][1]),
+            new ZOO.Geometry.Point(array[0][0], array[1][1]),
+            new Z0O.Geometry.Point(array[0][0], array[0][1])
+          ])
+      ]);
+    }
+  },
+  /**
+   * Method: write
+   * Serialize a feature, geometry, array of features into a GeoJSON string.
+   *
+   * Parameters:
+   * obj - {Object} An <ZOO.Feature>, <ZOO.Geometry>,
+   *     or an array of features.
+   * pretty - {Boolean} Structure the output with newlines and indentation.
+   *     Default is false.
+   *
+   * Returns:
+   * {String} The GeoJSON string representation of the input geometry,
+   *     features, or array of features.
+   */
+  write: function(obj, pretty) {
+    var geojson = {
+      "type": null
+    };
+    if(obj instanceof Array) {
+      geojson.type = "FeatureCollection";
+      var numFeatures = obj.length;
+      geojson.features = new Array(numFeatures);
+      for(var i=0; i<numFeatures; ++i) {
+        var element = obj[i];
+        if(!element instanceof ZOO.Feature) {
+          var msg = "FeatureCollection only supports collections " +
+            "of features: " + element;
+          throw msg;
+        }
+        geojson.features[i] = this.extract.feature.apply(this, [element]);
+      }
+    } else if (obj.CLASS_NAME.indexOf("ZOO.Geometry") == 0) {
+      geojson = this.extract.geometry.apply(this, [obj]);
+    } else if (obj instanceof ZOO.Feature) {
+      geojson = this.extract.feature.apply(this, [obj]);
+      /*
+      if(obj.layer && obj.layer.projection) {
+        geojson.crs = this.createCRSObject(obj);
+      }
+      */
+    }
+    return ZOO.Format.JSON.prototype.write.apply(this,
+                                                 [geojson, pretty]);
+  },
+  /**
+   * Method: createCRSObject
+   * Create the CRS object for an object.
+   *
+   * Parameters:
+   * object - {<ZOO.Feature>} 
+   *
+   * Returns:
+   * {Object} An object which can be assigned to the crs property
+   * of a GeoJSON object.
+   */
+  createCRSObject: function(object) {
+    //var proj = object.layer.projection.toString();
+    var proj = object.projection.toString();
+    var crs = {};
+    if (proj.match(/epsg:/i)) {
+      var code = parseInt(proj.substring(proj.indexOf(":") + 1));
+      if (code == 4326) {
+        crs = {
+          "type": "OGC",
+          "properties": {
+            "urn": "urn:ogc:def:crs:OGC:1.3:CRS84"
+          }
+        };
+      } else {    
+        crs = {
+          "type": "EPSG",
+          "properties": {
+            "code": code 
+          }
+        };
+      }    
+    }
+    return crs;
+  },
+  /**
+   * Property: extract
+   * Object with properties corresponding to the GeoJSON types.
+   *     Property values are functions that do the actual value extraction.
+   */
+  extract: {
+    /**
+     * Method: extract.feature
+     * Return a partial GeoJSON object representing a single feature.
+     *
+     * Parameters:
+     * feature - {<ZOO.Feature>}
+     *
+     * Returns:
+     * {Object} An object representing the point.
+     */
+    'feature': function(feature) {
+      var geom = this.extract.geometry.apply(this, [feature.geometry]);
+      return {
+        "type": "Feature",
+        "id": feature.fid == null ? feature.id : feature.fid,
+        "properties": feature.attributes,
+        "geometry": geom
+      };
+    },
+    /**
+     * Method: extract.geometry
+     * Return a GeoJSON object representing a single geometry.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry>}
+     *
+     * Returns:
+     * {Object} An object representing the geometry.
+     */
+    'geometry': function(geometry) {
+      if (geometry == null)
+        return null;
+      if (this.internalProjection && this.externalProjection) {
+        geometry = geometry.clone();
+        geometry.transform(this.internalProjection, 
+            this.externalProjection);
+      }                       
+      var geometryType = geometry.CLASS_NAME.split('.')[2];
+      var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
+      var json;
+      if(geometryType == "Collection")
+        json = {
+          "type": "GeometryCollection",
+          "geometries": data
+        };
+      else
+        json = {
+          "type": geometryType,
+          "coordinates": data
+        };
+      return json;
+    },
+    /**
+     * Method: extract.point
+     * Return an array of coordinates from a point.
+     *
+     * Parameters:
+     * point - {<ZOO.Geometry.Point>}
+     *
+     * Returns: 
+     * {Array} An array of coordinates representing the point.
+     */
+    'point': function(point) {
+      return [point.x, point.y];
+    },
+    /**
+     * Method: extract.multipoint
+     * Return an array of coordinates from a multipoint.
+     *
+     * Parameters:
+     * multipoint - {<ZOO.Geometry.MultiPoint>}
+     *
+     * Returns: 
+     * {Array} An array of point coordinate arrays representing
+     *     the multipoint.
+     */
+    'multipoint': function(multipoint) {
+      var array = [];
+      for(var i=0, len=multipoint.components.length; i<len; ++i) {
+        array.push(this.extract.point.apply(this, [multipoint.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.linestring
+     * Return an array of coordinate arrays from a linestring.
+     *
+     * Parameters:
+     * linestring - {<ZOO.Geometry.LineString>}
+     *
+     * Returns:
+     * {Array} An array of coordinate arrays representing
+     *     the linestring.
+     */
+    'linestring': function(linestring) {
+      var array = [];
+      for(var i=0, len=linestring.components.length; i<len; ++i) {
+        array.push(this.extract.point.apply(this, [linestring.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.multilinestring
+     * Return an array of linestring arrays from a linestring.
+     * 
+     * Parameters:
+     * multilinestring - {<ZOO.Geometry.MultiLineString>}
+     * 
+     * Returns:
+     * {Array} An array of linestring arrays representing
+     *     the multilinestring.
+     */
+    'multilinestring': function(multilinestring) {
+      var array = [];
+      for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+        array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.polygon
+     * Return an array of linear ring arrays from a polygon.
+     *
+     * Parameters:
+     * polygon - {<ZOO.Geometry.Polygon>}
+     * 
+     * Returns:
+     * {Array} An array of linear ring arrays representing the polygon.
+     */
+    'polygon': function(polygon) {
+      var array = [];
+      for(var i=0, len=polygon.components.length; i<len; ++i) {
+        array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.multipolygon
+     * Return an array of polygon arrays from a multipolygon.
+     * 
+     * Parameters:
+     * multipolygon - {<ZOO.Geometry.MultiPolygon>}
+     * 
+     * Returns:
+     * {Array} An array of polygon arrays representing
+     *     the multipolygon
+     */
+    'multipolygon': function(multipolygon) {
+      var array = [];
+      for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+        array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.collection
+     * Return an array of geometries from a geometry collection.
+     * 
+     * Parameters:
+     * collection - {<ZOO.Geometry.Collection>}
+     * 
+     * Returns:
+     * {Array} An array of geometry objects representing the geometry
+     *     collection.
+     */
+    'collection': function(collection) {
+      var len = collection.components.length;
+      var array = new Array(len);
+      for(var i=0; i<len; ++i) {
+        array[i] = this.extract.geometry.apply(
+            this, [collection.components[i]]
+            );
+      }
+      return array;
+    }
+  },
+  CLASS_NAME: 'ZOO.Format.GeoJSON'
+});
+/**
+ * Class: ZOO.Format.KML
+ * Read/Write KML. Create a new instance with the <ZOO.Format.KML>
+ *     constructor. 
+ * 
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.KML = ZOO.Class(ZOO.Format, {
+  /**
+   * Property: kmlns
+   * {String} KML Namespace to use. Defaults to 2.2 namespace.
+   */
+  kmlns: "http://www.opengis.net/kml/2.2",
+  /** 
+   * Property: foldersName
+   * {String} Name of the folders.  Default is "ZOO export".
+   *          If set to null, no name element will be created.
+   */
+  foldersName: "ZOO export",
+  /** 
+   * Property: foldersDesc
+   * {String} Description of the folders. Default is "Exported on [date]."
+   *          If set to null, no description element will be created.
+   */
+  foldersDesc: "Created on " + new Date(),
+  /** 
+   * Property: placemarksDesc
+   * {String} Name of the placemarks.  Default is "No description available".
+   */
+  placemarksDesc: "No description available",
+  /**
+   * Property: extractAttributes
+   * {Boolean} Extract attributes from KML.  Default is true.
+   *           Extracting styleUrls requires this to be set to true
+   */
+  extractAttributes: true,
+  /**
+   * Constructor: ZOO.Format.KML
+   * Create a new parser for KML.
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *     this instance.
+   */
+  initialize: function(options) {
+    // compile regular expressions once instead of every time they are used
+    this.regExes = {
+           trimSpace: (/^\s*|\s*$/g),
+           removeSpace: (/\s*/g),
+           splitSpace: (/\s+/),
+           trimComma: (/\s*,\s*/g),
+           kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/),
+           kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/),
+           straightBracket: (/\$\[(.*?)\]/g)
+    };
+    // KML coordinates are always in longlat WGS84
+    this.externalProjection = new ZOO.Projection("EPSG:4326");
+    ZOO.Format.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * APIMethod: read
+   * Read data from a string, and return a list of features. 
+   * 
+   * Parameters: 
+   * data    - {String} data to read/parse.
+   *
+   * Returns:
+   * {Array(<ZOO.Feature>)} List of features.
+   */
+  read: function(data) {
+    this.features = [];
+    data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+    data = new XML(data);
+    var placemarks = data..*::Placemark;
+    this.parseFeatures(placemarks);
+    return this.features;
+  },
+  /**
+   * Method: parseFeatures
+   * Loop through all Placemark nodes and parse them.
+   * Will create a list of features
+   * 
+   * Parameters: 
+   * nodes    - {Array} of {E4XElement} data to read/parse.
+   * options  - {Object} Hash of options
+   * 
+   */
+  parseFeatures: function(nodes) {
+    var features = new Array(nodes.length());
+    for(var i=0, len=nodes.length(); i<len; i++) {
+      var featureNode = nodes[i];
+      var feature = this.parseFeature.apply(this,[featureNode]) ;
+      features[i] = feature;
+    }
+    this.features = this.features.concat(features);
+  },
+  /**
+   * Method: parseFeature
+   * This function is the core of the KML parsing code in ZOO.
+   *     It creates the geometries that are then attached to the returned
+   *     feature, and calls parseAttributes() to get attribute data out.
+   *
+   * Parameters:
+   * node - {E4XElement}
+   *
+   * Returns:
+   * {<ZOO.Feature>} A vector feature.
+   */
+  parseFeature: function(node) {
+    // only accept one geometry per feature - look for highest "order"
+    var order = ["MultiGeometry", "Polygon", "LineString", "Point"];
+    var type, nodeList, geometry, parser;
+    for(var i=0, len=order.length; i<len; ++i) {
+      type = order[i];
+      nodeList = node.descendants(QName(null,type));
+      if (nodeList.length()> 0) {
+        var parser = this.parseGeometry[type.toLowerCase()];
+        if(parser) {
+          geometry = parser.apply(this, [nodeList[0]]);
+          if (this.internalProjection && this.externalProjection) {
+            geometry.transform(this.externalProjection, 
+                               this.internalProjection); 
+          }                       
+        }
+        // stop looking for different geometry types
+        break;
+      }
+    }
+    // construct feature (optionally with attributes)
+    var attributes;
+    if(this.extractAttributes) {
+      attributes = this.parseAttributes(node);
+    }
+    var feature = new ZOO.Feature(geometry, attributes);
+    var fid = node.@id || node.@name;
+    if(fid != null)
+      feature.fid = fid;
+    return feature;
+  },
+  /**
+   * Property: parseGeometry
+   * Properties of this object are the functions that parse geometries based
+   *     on their type.
+   */
+  parseGeometry: {
+    /**
+     * Method: parseGeometry.point
+     * Given a KML node representing a point geometry, create a ZOO
+     *     point geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A KML Point node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Point>} A point geometry.
+     */
+    'point': function(node) {
+      var coordString = node.*::coordinates.toString();
+      coordString = coordString.replace(this.regExes.removeSpace, "");
+      coords = coordString.split(",");
+      var point = null;
+      if(coords.length > 1) {
+        // preserve third dimension
+        if(coords.length == 2) {
+          coords[2] = null;
+        }
+        point = new ZOO.Geometry.Point(coords[0], coords[1], coords[2]);
+      }
+      return point;
+    },
+    /**
+     * Method: parseGeometry.linestring
+     * Given a KML node representing a linestring geometry, create a
+     *     ZOO linestring geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A KML LineString node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.LineString>} A linestring geometry.
+     */
+    'linestring': function(node, ring) {
+      var line = null;
+      var coordString = node.*::coordinates.toString();
+      coordString = coordString.replace(this.regExes.trimSpace,
+          "");
+      coordString = coordString.replace(this.regExes.trimComma,
+          ",");
+      var pointList = coordString.split(this.regExes.splitSpace);
+      var numPoints = pointList.length;
+      var points = new Array(numPoints);
+      var coords, numCoords;
+      for(var i=0; i<numPoints; ++i) {
+        coords = pointList[i].split(",");
+        numCoords = coords.length;
+        if(numCoords > 1) {
+          if(coords.length == 2) {
+            coords[2] = null;
+          }
+          points[i] = new ZOO.Geometry.Point(coords[0],
+                                             coords[1],
+                                             coords[2]);
+        }
+      }
+      if(numPoints) {
+        if(ring) {
+          line = new ZOO.Geometry.LinearRing(points);
+        } else {
+          line = new ZOO.Geometry.LineString(points);
+        }
+      } else {
+        throw "Bad LineString coordinates: " + coordString;
+      }
+      return line;
+    },
+    /**
+     * Method: parseGeometry.polygon
+     * Given a KML node representing a polygon geometry, create a
+     *     ZOO polygon geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A KML Polygon node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A polygon geometry.
+     */
+    'polygon': function(node) {
+      var nodeList = node..*::LinearRing;
+      var numRings = nodeList.length();
+      var components = new Array(numRings);
+      if(numRings > 0) {
+        // this assumes exterior ring first, inner rings after
+        var ring;
+        for(var i=0, len=nodeList.length(); i<len; ++i) {
+          ring = this.parseGeometry.linestring.apply(this,
+                                                     [nodeList[i], true]);
+          if(ring) {
+            components[i] = ring;
+          } else {
+            throw "Bad LinearRing geometry: " + i;
+          }
+        }
+      }
+      return new ZOO.Geometry.Polygon(components);
+    },
+    /**
+     * Method: parseGeometry.multigeometry
+     * Given a KML node representing a multigeometry, create a
+     *     ZOO geometry collection.
+     *
+     * Parameters:
+     * node - {E4XElement} A KML MultiGeometry node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Collection>} A geometry collection.
+     */
+    'multigeometry': function(node) {
+      var child, parser;
+      var parts = [];
+      var children = node.*::*;
+      for(var i=0, len=children.length(); i<len; ++i ) {
+        child = children[i];
+        var type = child.localName();
+        var parser = this.parseGeometry[type.toLowerCase()];
+        if(parser) {
+          parts.push(parser.apply(this, [child]));
+        }
+      }
+      return new ZOO.Geometry.Collection(parts);
+    }
+  },
+  /**
+   * Method: parseAttributes
+   *
+   * Parameters:
+   * node - {E4XElement}
+   *
+   * Returns:
+   * {Object} An attributes object.
+   */
+  parseAttributes: function(node) {
+    var attributes = {};
+    var edNodes = node.*::ExtendedData;
+    if (edNodes.length() > 0) {
+      attributes = this.parseExtendedData(edNodes[0])
+    }
+    var child, grandchildren;
+    var children = node.*::*;
+    for(var i=0, len=children.length(); i<len; ++i) {
+      child = children[i];
+      grandchildren = child..*::*;
+      if(grandchildren.length() == 1) {
+        var name = child.localName();
+        var value = child.toString();
+        if (value) {
+          value = value.replace(this.regExes.trimSpace, "");
+          attributes[name] = value;
+        }
+      }
+    }
+    return attributes;
+  },
+  /**
+   * Method: parseExtendedData
+   * Parse ExtendedData from KML. Limited support for schemas/datatypes.
+   *     See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata
+   *     for more information on extendeddata.
+   *
+   * Parameters:
+   * node - {E4XElement}
+   *
+   * Returns:
+   * {Object} An attributes object.
+   */
+  parseExtendedData: function(node) {
+    var attributes = {};
+    var dataNodes = node.*::Data;
+    for (var i = 0, len = dataNodes.length(); i < len; i++) {
+      var data = dataNodes[i];
+      var key = data.@name;
+      var ed = {};
+      var valueNode = data.*::value;
+      if (valueNode.length() > 0)
+        ed['value'] = valueNode[0].toString();
+      var nameNode = data.*::displayName;
+      if (nameNode.length() > 0)
+        ed['displayName'] = valueNode[0].toString();
+      attributes[key] = ed;
+    }
+    return attributes;
+  },
+  /**
+   * Method: write
+   * Accept Feature Collection, and return a string. 
+   * 
+   * Parameters:
+   * features - {Array(<ZOO.Feature>} An array of features.
+   *
+   * Returns:
+   * {String} A KML string.
+   */
+  write: function(features) {
+    if(!(features instanceof Array))
+      features = [features];
+    var kml = new XML('<kml xmlns="'+this.kmlns+'"></kml>');
+    var folder = kml.Document.Folder;
+    folder.name = this.foldersName;
+    folder.description = this.foldersDesc;
+    for(var i=0, len=features.length; i<len; ++i) {
+      folder.Placemark[i] = this.createPlacemark(features[i]);
+    }
+    return kml.toXMLString();
+  },
+  /**
+   * Method: createPlacemark
+   * Creates and returns a KML placemark node representing the given feature. 
+   * 
+   * Parameters:
+   * feature - {<ZOO.Feature>}
+   * 
+   * Returns:
+   * {E4XElement}
+   */
+  createPlacemark: function(feature) {
+    var placemark = new XML('<Placemark xmlns="'+this.kmlns+'"></Placemark>');
+    placemark.name = (feature.attributes.name) ?
+                    feature.attributes.name : feature.id;
+    placemark.description = (feature.attributes.description) ?
+                             feature.attributes.description : this.placemarksDesc;
+    if(feature.fid != null)
+      placemark.@id = feature.fid;
+    placemark.*[2] = this.buildGeometryNode(feature.geometry);
+    return placemark;
+  },
+  /**
+   * Method: buildGeometryNode
+   * Builds and returns a KML geometry node with the given geometry.
+   * 
+   * Parameters:
+   * geometry - {<ZOO.Geometry>}
+   * 
+   * Returns:
+   * {E4XElement}
+   */
+  buildGeometryNode: function(geometry) {
+    if (this.internalProjection && this.externalProjection) {
+      geometry = geometry.clone();
+      geometry.transform(this.internalProjection, 
+                         this.externalProjection);
+    }
+    var className = geometry.CLASS_NAME;
+    var type = className.substring(className.lastIndexOf(".") + 1);
+    var builder = this.buildGeometry[type.toLowerCase()];
+    var node = null;
+    if(builder) {
+      node = builder.apply(this, [geometry]);
+    }
+    return node;
+  },
+  /**
+   * Property: buildGeometry
+   * Object containing methods to do the actual geometry node building
+   *     based on geometry type.
+   */
+  buildGeometry: {
+    /**
+     * Method: buildGeometry.point
+     * Given a ZOO point geometry, create a KML point.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Point>} A point geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML point node.
+     */
+    'point': function(geometry) {
+      var kml = new XML('<Point xmlns="'+this.kmlns+'"></Point>');
+      kml.coordinates = this.buildCoordinatesNode(geometry);
+      return kml;
+    },
+    /**
+     * Method: buildGeometry.multipoint
+     * Given a ZOO multipoint geometry, create a KML
+     *     GeometryCollection.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiPoint>} A multipoint geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML GeometryCollection node.
+     */
+    'multipoint': function(geometry) {
+      return this.buildGeometry.collection.apply(this, [geometry]);
+    },
+    /**
+     * Method: buildGeometry.linestring
+     * Given a ZOO linestring geometry, create a KML linestring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.LineString>} A linestring geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML linestring node.
+     */
+    'linestring': function(geometry) {
+      var kml = new XML('<LineString xmlns="'+this.kmlns+'"></LineString>');
+      kml.coordinates = this.buildCoordinatesNode(geometry);
+      return kml;
+    },
+    /**
+     * Method: buildGeometry.multilinestring
+     * Given a ZOO multilinestring geometry, create a KML
+     *     GeometryCollection.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiLineString>} A multilinestring geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML GeometryCollection node.
+     */
+    'multilinestring': function(geometry) {
+      return this.buildGeometry.collection.apply(this, [geometry]);
+    },
+    /**
+     * Method: buildGeometry.linearring
+     * Given a ZOO linearring geometry, create a KML linearring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.LinearRing>} A linearring geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML linearring node.
+     */
+    'linearring': function(geometry) {
+      var kml = new XML('<LinearRing xmlns="'+this.kmlns+'"></LinearRing>');
+      kml.coordinates = this.buildCoordinatesNode(geometry);
+      return kml;
+    },
+    /**
+     * Method: buildGeometry.polygon
+     * Given a ZOO polygon geometry, create a KML polygon.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Polygon>} A polygon geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML polygon node.
+     */
+    'polygon': function(geometry) {
+      var kml = new XML('<Polygon xmlns="'+this.kmlns+'"></Polygon>');
+      var rings = geometry.components;
+      var ringMember, ringGeom, type;
+      for(var i=0, len=rings.length; i<len; ++i) {
+        type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+        ringMember = new XML('<'+type+' xmlns="'+this.kmlns+'"></'+type+'>');
+        ringMember.LinearRing = this.buildGeometry.linearring.apply(this,[rings[i]]);
+        kml.*[i] = ringMember;
+      }
+      return kml;
+    },
+    /**
+     * Method: buildGeometry.multipolygon
+     * Given a ZOO multipolygon geometry, create a KML
+     *     GeometryCollection.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Point>} A multipolygon geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML GeometryCollection node.
+     */
+    'multipolygon': function(geometry) {
+      return this.buildGeometry.collection.apply(this, [geometry]);
+    },
+    /**
+     * Method: buildGeometry.collection
+     * Given a ZOO geometry collection, create a KML MultiGeometry.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Collection>} A geometry collection.
+     *
+     * Returns:
+     * {E4XElement} A KML MultiGeometry node.
+     */
+    'collection': function(geometry) {
+      var kml = new XML('<MultiGeometry xmlns="'+this.kmlns+'"></MultiGeometry>');
+      var child;
+      for(var i=0, len=geometry.components.length; i<len; ++i) {
+        kml.*[i] = this.buildGeometryNode.apply(this,[geometry.components[i]]);
+      }
+      return kml;
+    }
+  },
+  /**
+   * Method: buildCoordinatesNode
+   * Builds and returns the KML coordinates node with the given geometry
+   *     <coordinates>...</coordinates>
+   * 
+   * Parameters:
+   * geometry - {<ZOO.Geometry>}
+   * 
+   * Return:
+   * {E4XElement}
+   */
+  buildCoordinatesNode: function(geometry) {
+    var cooridnates = new XML('<coordinates xmlns="'+this.kmlns+'"></coordinates>');
+    var points = geometry.components;
+    if(points) {
+      // LineString or LinearRing
+      var point;
+      var numPoints = points.length;
+      var parts = new Array(numPoints);
+      for(var i=0; i<numPoints; ++i) {
+        point = points[i];
+        parts[i] = point.x + "," + point.y;
+      }
+      coordinates = parts.join(" ");
+    } else {
+      // Point
+      coordinates = geometry.x + "," + geometry.y;
+    }
+    return coordinates;
+  },
+  CLASS_NAME: 'ZOO.Format.KML'
+});
+/**
+ * Class: ZOO.Format.GML
+ * Read/Write GML. Create a new instance with the <ZOO.Format.GML>
+ *     constructor.  Supports the GML simple features profile.
+ * 
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.GML = ZOO.Class(ZOO.Format, {
+  /**
+   * Property: schemaLocation
+   * {String} Schema location for a particular minor version.
+   */
+  schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
+  /**
+   * Property: namespaces
+   * {Object} Mapping of namespace aliases to namespace URIs.
+   */
+  namespaces: {
+    ogr: "http://ogr.maptools.org/",
+    gml: "http://www.opengis.net/gml",
+    xlink: "http://www.w3.org/1999/xlink",
+    xsi: "http://www.w3.org/2001/XMLSchema-instance",
+    wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
+  },
+  /**
+   * Property: defaultPrefix
+   */
+  defaultPrefix: 'ogr',
+  /** 
+   * Property: collectionName
+   * {String} Name of featureCollection element.
+   */
+  collectionName: "FeatureCollection",
+  /*
+   * Property: featureName
+   * {String} Element name for features. Default is "sql_statement".
+   */
+  featureName: "sql_statement",
+  /**
+   * Property: geometryName
+   * {String} Name of geometry element.  Defaults to "geometryProperty".
+   */
+  geometryName: "geometryProperty",
+  /**
+   * Property: xy
+   * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
+   * Changing is not recommended, a new Format should be instantiated.
+   */
+  xy: true,
+  /**
+   * Property: extractAttributes
+   * {Boolean} Could we extract attributes
+   */
+  extractAttributes: true,
+  /**
+   * Constructor: ZOO.Format.GML
+   * Create a new parser for GML.
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *     this instance.
+   */
+  initialize: function(options) {
+    // compile regular expressions once instead of every time they are used
+    this.regExes = {
+      trimSpace: (/^\s*|\s*$/g),
+      removeSpace: (/\s*/g),
+      splitSpace: (/\s+/),
+      trimComma: (/\s*,\s*/g)
+    };
+    ZOO.Format.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * Method: read
+   * Read data from a string, and return a list of features. 
+   * 
+   * Parameters:
+   * data - {String} data to read/parse.
+   *
+   * Returns:
+   * {Array(<ZOO.Feature>)} An array of features.
+   */
+  read: function(data) {
+    this.features = [];
+    data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+    data = new XML(data);
+
+    var gmlns = Namespace(this.namespaces['gml']);
+    var featureNodes = data..gmlns::featureMember;
+    if (data.localName() == 'featureMember')
+      featureNodes = data;
+    var features = [];
+    for(var i=0,len=featureNodes.length(); i<len; i++) {
+      var feature = this.parseFeature(featureNodes[i]);
+      if(feature) {
+        features.push(feature);
+      }
+    }
+    return features;
+  },
+  /**
+   * Method: parseFeature
+   * This function is the core of the GML parsing code in ZOO.
+   *    It creates the geometries that are then attached to the returned
+   *    feature, and calls parseAttributes() to get attribute data out.
+   *    
+   * Parameters:
+   * node - {E4XElement} A GML feature node. 
+   */
+  parseFeature: function(node) {
+    // only accept one geometry per feature - look for highest "order"
+    var gmlns = Namespace(this.namespaces['gml']);
+    var order = ["MultiPolygon", "Polygon",
+                 "MultiLineString", "LineString",
+                 "MultiPoint", "Point", "Envelope", "Box"];
+    var type, nodeList, geometry, parser;
+    for(var i=0; i<order.length; ++i) {
+      type = order[i];
+      nodeList = node.descendants(QName(gmlns,type));
+      if (nodeList.length() > 0) {
+        var parser = this.parseGeometry[type.toLowerCase()];
+        if(parser) {
+          geometry = parser.apply(this, [nodeList[0]]);
+          if (this.internalProjection && this.externalProjection) {
+            geometry.transform(this.externalProjection, 
+                               this.internalProjection); 
+          }                       
+        }
+        // stop looking for different geometry types
+        break;
+      }
+    }
+    var attributes;
+    if(this.extractAttributes) {
+      attributes = this.parseAttributes(node);
+    }
+    var feature = new ZOO.Feature(geometry, attributes);
+    return feature;
+  },
+  /**
+   * Property: parseGeometry
+   * Properties of this object are the functions that parse geometries based
+   *     on their type.
+   */
+  parseGeometry: {
+    /**
+     * Method: parseGeometry.point
+     * Given a GML node representing a point geometry, create a ZOO
+     *     point geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Point>} A point geometry.
+     */
+    'point': function(node) {
+      /**
+       * Three coordinate variations to consider:
+       * 1) <gml:pos>x y z</gml:pos>
+       * 2) <gml:coordinates>x, y, z</gml:coordinates>
+       * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
+       */
+      var nodeList, coordString;
+      var coords = [];
+      // look for <gml:pos>
+      var nodeList = node..*::pos;
+      if(nodeList.length() > 0) {
+        coordString = nodeList[0].toString();
+        coordString = coordString.replace(this.regExes.trimSpace, "");
+        coords = coordString.split(this.regExes.splitSpace);
+      }
+      // look for <gml:coordinates>
+      if(coords.length == 0) {
+        nodeList = node..*::coordinates;
+        if(nodeList.length() > 0) {
+          coordString = nodeList[0].toString();
+          coordString = coordString.replace(this.regExes.removeSpace,"");
+          coords = coordString.split(",");
+        }
+      }
+      // look for <gml:coord>
+      if(coords.length == 0) {
+        nodeList = node..*::coord;
+        if(nodeList.length() > 0) {
+          var xList = nodeList[0].*::X;
+          var yList = nodeList[0].*::Y;
+          if(xList.length() > 0 && yList.length() > 0)
+            coords = [xList[0].toString(),
+                      yList[0].toString()];
+        }
+      }
+      // preserve third dimension
+      if(coords.length == 2)
+        coords[2] = null;
+      if (this.xy)
+        return new ZOO.Geometry.Point(coords[0],coords[1],coords[2]);
+      else
+        return new ZOO.Geometry.Point(coords[1],coords[0],coords[2]);
+    },
+    /**
+     * Method: parseGeometry.multipoint
+     * Given a GML node representing a multipoint geometry, create a
+     *     ZOO multipoint geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiPoint>} A multipoint geometry.
+     */
+    'multipoint': function(node) {
+      var nodeList = node..*::Point;
+      var components = [];
+      if(nodeList.length() > 0) {
+        var point;
+        for(var i=0, len=nodeList.length(); i<len; ++i) {
+          point = this.parseGeometry.point.apply(this, [nodeList[i]]);
+          if(point)
+            components.push(point);
+        }
+      }
+      return new ZOO.Geometry.MultiPoint(components);
+    },
+    /**
+     * Method: parseGeometry.linestring
+     * Given a GML node representing a linestring geometry, create a
+     *     ZOO linestring geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.LineString>} A linestring geometry.
+     */
+    'linestring': function(node, ring) {
+      /**
+       * Two coordinate variations to consider:
+       * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
+       * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
+       */
+      var nodeList, coordString;
+      var coords = [];
+      var points = [];
+      // look for <gml:posList>
+      nodeList = node..*::posList;
+      if(nodeList.length() > 0) {
+        coordString = nodeList[0].toString();
+        coordString = coordString.replace(this.regExes.trimSpace, "");
+        coords = coordString.split(this.regExes.splitSpace);
+        var dim = parseInt(nodeList[0].@dimension);
+        var j, x, y, z;
+        for(var i=0; i<coords.length/dim; ++i) {
+          j = i * dim;
+          x = coords[j];
+          y = coords[j+1];
+          z = (dim == 2) ? null : coords[j+2];
+          if (this.xy)
+            points.push(new ZOO.Geometry.Point(x, y, z));
+          else
+            points.push(new Z0O.Geometry.Point(y, x, z));
+        }
+      }
+      // look for <gml:coordinates>
+      if(coords.length == 0) {
+        nodeList = node..*::coordinates;
+        if(nodeList.length() > 0) {
+          coordString = nodeList[0].toString();
+          coordString = coordString.replace(this.regExes.trimSpace,"");
+          coordString = coordString.replace(this.regExes.trimComma,",");
+          var pointList = coordString.split(this.regExes.splitSpace);
+          for(var i=0; i<pointList.length; ++i) {
+            coords = pointList[i].split(",");
+            if(coords.length == 2)
+              coords[2] = null;
+            if (this.xy)
+              points.push(new ZOO.Geometry.Point(coords[0],coords[1],coords[2]));
+            else
+              points.push(new ZOO.Geometry.Point(coords[1],coords[0],coords[2]));
+          }
+        }
+      }
+      var line = null;
+      if(points.length != 0) {
+        if(ring)
+          line = new ZOO.Geometry.LinearRing(points);
+        else
+          line = new ZOO.Geometry.LineString(points);
+      }
+      return line;
+    },
+    /**
+     * Method: parseGeometry.multilinestring
+     * Given a GML node representing a multilinestring geometry, create a
+     *     ZOO multilinestring geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiLineString>} A multilinestring geometry.
+     */
+    'multilinestring': function(node) {
+      var nodeList = node..*::LineString;
+      var components = [];
+      if(nodeList.length() > 0) {
+        var line;
+        for(var i=0, len=nodeList.length(); i<len; ++i) {
+          line = this.parseGeometry.linestring.apply(this, [nodeList[i]]);
+          if(point)
+            components.push(point);
+        }
+      }
+      return new ZOO.Geometry.MultiLineString(components);
+    },
+    /**
+     * Method: parseGeometry.polygon
+     * Given a GML node representing a polygon geometry, create a
+     *     ZOO polygon geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A polygon geometry.
+     */
+    'polygon': function(node) {
+      nodeList = node..*::LinearRing;
+      var components = [];
+      if(nodeList.length() > 0) {
+        // this assumes exterior ring first, inner rings after
+        var ring;
+        for(var i=0, len = nodeList.length(); i<len; ++i) {
+          ring = this.parseGeometry.linestring.apply(this,[nodeList[i], true]);
+          if(ring)
+            components.push(ring);
+        }
+      }
+      return new ZOO.Geometry.Polygon(components);
+    },
+    /**
+     * Method: parseGeometry.multipolygon
+     * Given a GML node representing a multipolygon geometry, create a
+     *     ZOO multipolygon geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiPolygon>} A multipolygon geometry.
+     */
+    'multipolygon': function(node) {
+      var nodeList = node..*::Polygon;
+      var components = [];
+      if(nodeList.length() > 0) {
+        var polygon;
+        for(var i=0, len=nodeList.length(); i<len; ++i) {
+          polygon = this.parseGeometry.polygon.apply(this, [nodeList[i]]);
+          if(polygon)
+            components.push(polygon);
+        }
+      }
+      return new ZOO.Geometry.MultiPolygon(components);
+    },
+    /**
+     * Method: parseGeometry.envelope
+     * Given a GML node representing an envelope, create a
+     *     ZOO polygon geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A polygon geometry.
+     */
+    'envelope': function(node) {
+      var components = [];
+      var coordString;
+      var envelope;
+      var lpoint = node..*::lowerCorner;
+      if (lpoint.length() > 0) {
+        var coords = [];
+        if(lpoint.length() > 0) {
+          coordString = lpoint[0].toString();
+          coordString = coordString.replace(this.regExes.trimSpace, "");
+          coords = coordString.split(this.regExes.splitSpace);
+        }
+        if(coords.length == 2)
+          coords[2] = null;
+        if (this.xy)
+          var lowerPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]);
+        else
+          var lowerPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]);
+      }
+      var upoint = node..*::upperCorner;
+      if (upoint.length() > 0) {
+        var coords = [];
+        if(upoint.length > 0) {
+          coordString = upoint[0].toString();
+          coordString = coordString.replace(this.regExes.trimSpace, "");
+          coords = coordString.split(this.regExes.splitSpace);
+        }
+        if(coords.length == 2)
+          coords[2] = null;
+        if (this.xy)
+          var upperPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]);
+        else
+          var upperPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]);
+      }
+      if (lowerPoint && upperPoint) {
+        components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y));
+        components.push(new ZOO.Geometry.Point(upperPoint.x, lowerPoint.y));
+        components.push(new ZOO.Geometry.Point(upperPoint.x, upperPoint.y));
+        components.push(new ZOO.Geometry.Point(lowerPoint.x, upperPoint.y));
+        components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y));
+        var ring = new ZOO.Geometry.LinearRing(components);
+        envelope = new ZOO.Geometry.Polygon([ring]);
+      }
+      return envelope;
+    }
+  },
+  /**
+   * Method: parseAttributes
+   *
+   * Parameters:
+   * node - {<E4XElement>}
+   *
+   * Returns:
+   * {Object} An attributes object.
+   */
+  parseAttributes: function(node) {
+    var attributes = {};
+    // assume attributes are children of the first type 1 child
+    var childNode = node.*::*[0];
+    var child, grandchildren;
+    var children = childNode.*::*;
+    for(var i=0, len=children.length(); i<len; ++i) {
+      child = children[i];
+      grandchildren = child..*::*;
+      if(grandchildren.length() == 1) {
+        var name = child.localName();
+        var value = child.toString();
+        if (value) {
+          value = value.replace(this.regExes.trimSpace, "");
+          attributes[name] = value;
+        } else
+          attributes[name] = null;
+      }
+    }
+    return attributes;
+  },
+  /**
+   * Method: write
+   * Generate a GML document string given a list of features. 
+   * 
+   * Parameters:
+   * features - {Array(<ZOO.Feature>)} List of features to
+   *     serialize into a string.
+   *
+   * Returns:
+   * {String} A string representing the GML document.
+   */
+  write: function(features) {
+    if(!(features instanceof Array)) {
+      features = [features];
+    }
+    var pfx = this.defaultPrefix;
+    var name = pfx+':'+this.collectionName;
+    var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" xmlns:gml="'+this.namespaces['gml']+'" xmlns:xsi="'+this.namespaces['xsi']+'" xsi:schemaLocation="'+this.schemaLocation+'"></'+name+'>');
+    for(var i=0; i<features.length; i++) {
+      gml.*::*[i] = this.createFeature(features[i]);
+    }
+    return gml.toXMLString();
+  },
+  /** 
+   * Method: createFeature
+   * Accept an ZOO.Feature, and build a GML node for it.
+   *
+   * Parameters:
+   * feature - {<ZOO.Feature>} The feature to be built as GML.
+   *
+   * Returns:
+   * {E4XElement} A node reprensting the feature in GML.
+   */
+  createFeature: function(feature) {
+    var pfx = this.defaultPrefix;
+    var name = pfx+':'+this.featureName;
+    var fid = feature.fid || feature.id;
+    var gml = new XML('<gml:featureMember xmlns:gml="'+this.namespaces['gml']+'"><'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" fid="'+fid+'"></'+name+'></gml:featureMember>');
+    var geometry = feature.geometry;
+    gml.*::*[0].*::* = this.buildGeometryNode(geometry);
+    for(var attr in feature.attributes) {
+      var attrNode = new XML('<'+pfx+':'+attr+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'">'+feature.attributes[attr]+'</'+pfx+':'+attr+'>');
+      gml.*::*[0].appendChild(attrNode);
+    }
+    return gml;
+  },
+  /**
+   * Method: buildGeometryNode
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} The geometry to be built as GML.
+   *
+   * Returns:
+   * {E4XElement} A node reprensting the geometry in GML.
+   */
+  buildGeometryNode: function(geometry) {
+    if (this.externalProjection && this.internalProjection) {
+      geometry = geometry.clone();
+      geometry.transform(this.internalProjection, 
+          this.externalProjection);
+    }    
+    var className = geometry.CLASS_NAME;
+    var type = className.substring(className.lastIndexOf(".") + 1);
+    var builder = this.buildGeometry[type.toLowerCase()];
+    var pfx = this.defaultPrefix;
+    var name = pfx+':'+this.geometryName;
+    var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'"></'+name+'>');
+    if (builder)
+      gml.*::* = builder.apply(this, [geometry]);
+    return gml;
+  },
+  /**
+   * Property: buildGeometry
+   * Object containing methods to do the actual geometry node building
+   *     based on geometry type.
+   */
+  buildGeometry: {
+    /**
+     * Method: buildGeometry.point
+     * Given a ZOO point geometry, create a GML point.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Point>} A point geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML point node.
+     */
+    'point': function(geometry) {
+      var gml = new XML('<gml:Point xmlns:gml="'+this.namespaces['gml']+'"></gml:Point>');
+      gml.*::*[0] = this.buildCoordinatesNode(geometry);
+      return gml;
+    },
+    /**
+     * Method: buildGeometry.multipoint
+     * Given a ZOO multipoint geometry, create a GML multipoint.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiPoint>} A multipoint geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML multipoint node.
+     */
+    'multipoint': function(geometry) {
+      var gml = new XML('<gml:MultiPoint xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiPoint>');
+      var points = geometry.components;
+      var pointMember;
+      for(var i=0; i<points.length; i++) { 
+        pointMember = new XML('<gml:pointMember xmlns:gml="'+this.namespaces['gml']+'"></gml:pointMember>');
+        pointMember.*::* = this.buildGeometry.point.apply(this,[points[i]]);
+        gml.*::*[i] = pointMember;
+      }
+      return gml;            
+    },
+    /**
+     * Method: buildGeometry.linestring
+     * Given a ZOO linestring geometry, create a GML linestring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.LineString>} A linestring geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML linestring node.
+     */
+    'linestring': function(geometry) {
+      var gml = new XML('<gml:LineString xmlns:gml="'+this.namespaces['gml']+'"></gml:LineString>');
+      gml.*::*[0] = this.buildCoordinatesNode(geometry);
+      return gml;
+    },
+    /**
+     * Method: buildGeometry.multilinestring
+     * Given a ZOO multilinestring geometry, create a GML
+     *     multilinestring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiLineString>} A multilinestring
+     *     geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML multilinestring node.
+     */
+    'multilinestring': function(geometry) {
+      var gml = new XML('<gml:MultiLineString xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiLineString>');
+      var lines = geometry.components;
+      var lineMember;
+      for(var i=0; i<lines.length; i++) { 
+        lineMember = new XML('<gml:lineStringMember xmlns:gml="'+this.namespaces['gml']+'"></gml:lineStringMember>');
+        lineMember.*::* = this.buildGeometry.linestring.apply(this,[lines[i]]);
+        gml.*::*[i] = lineMember;
+      }
+      return gml;            
+    },
+    /**
+     * Method: buildGeometry.linearring
+     * Given a ZOO linearring geometry, create a GML linearring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.LinearRing>} A linearring geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML linearring node.
+     */
+    'linearring': function(geometry) {
+      var gml = new XML('<gml:LinearRing xmlns:gml="'+this.namespaces['gml']+'"></gml:LinearRing>');
+      gml.*::*[0] = this.buildCoordinatesNode(geometry);
+      return gml;
+    },
+    /**
+     * Method: buildGeometry.polygon
+     * Given an ZOO polygon geometry, create a GML polygon.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Polygon>} A polygon geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML polygon node.
+     */
+    'polygon': function(geometry) {
+      var gml = new XML('<gml:Polygon xmlns:gml="'+this.namespaces['gml']+'"></gml:Polygon>');
+      var rings = geometry.components;
+      var ringMember, type;
+      for(var i=0; i<rings.length; ++i) {
+        type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+        var ringMember = new XML('<gml:'+type+' xmlns:gml="'+this.namespaces['gml']+'"></gml:'+type+'>');
+        ringMember.*::* = this.buildGeometry.linearring.apply(this,[rings[i]]);
+        gml.*::*[i] = ringMember;
+      }
+      return gml;
+    },
+    /**
+     * Method: buildGeometry.multipolygon
+     * Given a ZOO multipolygon geometry, create a GML multipolygon.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiPolygon>} A multipolygon
+     *     geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML multipolygon node.
+     */
+    'multipolygon': function(geometry) {
+      var gml = new XML('<gml:MultiPolygon xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiPolygon>');
+      var polys = geometry.components;
+      var polyMember;
+      for(var i=0; i<polys.length; i++) { 
+        polyMember = new XML('<gml:polygonMember xmlns:gml="'+this.namespaces['gml']+'"></gml:polygonMember>');
+        polyMember.*::* = this.buildGeometry.polygon.apply(this,[polys[i]]);
+        gml.*::*[i] = polyMember;
+      }
+      return gml;            
+    }
+  },
+  /**
+   * Method: buildCoordinatesNode
+   * builds the coordinates XmlNode
+   * (code)
+   * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
+   * (end)
+   * Parameters: 
+   * geometry - {<ZOO.Geometry>} 
+   *
+   * Returns:
+   * {E4XElement} created E4XElement
+   */
+  buildCoordinatesNode: function(geometry) {
+    var parts = [];
+    if(geometry instanceof ZOO.Bounds){
+      parts.push(geometry.left + "," + geometry.bottom);
+      parts.push(geometry.right + "," + geometry.top);
+    } else {
+      var points = (geometry.components) ? geometry.components : [geometry];
+      for(var i=0; i<points.length; i++) {
+        parts.push(points[i].x + "," + points[i].y);                
+      }            
+    }
+    return new XML('<gml:coordinates xmlns:gml="'+this.namespaces['gml']+'" decimal="." cs=", " ts=" ">'+parts.join(" ")+'</gml:coordinates>');
+  },
+  CLASS_NAME: 'ZOO.Format.GML'
+});
+/**
+ * Class: ZOO.Format.WPS
+ * Read/Write WPS. Create a new instance with the <ZOO.Format.WPS>
+ *     constructor. Supports only parseExecuteResponse.
+ * 
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.WPS = ZOO.Class(ZOO.Format, {
+  /**
+   * Property: schemaLocation
+   * {String} Schema location for a particular minor version.
+   */
+  schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd",
+  /**
+   * Property: namespaces
+   * {Object} Mapping of namespace aliases to namespace URIs.
+   */
+  namespaces: {
+    ows: "http://www.opengis.net/ows/1.1",
+    wps: "http://www.opengis.net/wps/1.0.0",
+    xlink: "http://www.w3.org/1999/xlink",
+    xsi: "http://www.w3.org/2001/XMLSchema-instance",
+  },
+  /**
+   * Method: read
+   *
+   * Parameters:
+   * data - {String} A WPS xml document
+   *
+   * Returns:
+   * {Object} Execute response.
+   */
+  read:function(data) {
+    data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+    data = new XML(data);
+    switch (data.localName()) {
+      case 'ExecuteResponse':
+        return this.parseExecuteResponse(data);
+      default:
+        return null;
+    }
+  },
+  /**
+   * Method: parseExecuteResponse
+   *
+   * Parameters:
+   * node - {E4XElement} A WPS ExecuteResponse document
+   *
+   * Returns:
+   * {Object} Execute response.
+   */
+  parseExecuteResponse: function(node) {
+    var outputs = node.*::ProcessOutputs.*::Output;
+    if (outputs.length() > 0) {
+      var data = outputs[0].*::Data.*::*[0];
+      var builder = this.parseData[data.localName().toLowerCase()];
+      if (builder)
+        return builder.apply(this,[data]);
+      else
+        return null;
+    } else
+      return null;
+  },
+  /**
+   * Property: parseData
+   * Object containing methods to analyse data response.
+   */
+  parseData: {
+    /**
+     * Method: parseData.complexdata
+     * Given an Object representing the WPS complex data response.
+     *
+     * Parameters:
+     * node - {E4XElement} A WPS node.
+     *
+     * Returns:
+     * {Object} A WPS complex data response.
+     */
+    'complexdata': function(node) {
+      var result = {value:node.toString()};
+      if (node.@mimeType.length()>0)
+        result.mimeType = node.@mimeType;
+      if (node.@encoding.length()>0)
+        result.encoding = node.@encoding;
+      if (node.@schema.length()>0)
+        result.schema = node.@schema;
+      return result;
+    },
+    /**
+     * Method: parseData.literaldata
+     * Given an Object representing the WPS literal data response.
+     *
+     * Parameters:
+     * node - {E4XElement} A WPS node.
+     *
+     * Returns:
+     * {Object} A WPS literal data response.
+     */
+    'literaldata': function(node) {
+      var result = {value:node.toString()};
+      if (node.@dataType.length()>0)
+        result.dataType = node.@dataType;
+      if (node.@uom.length()>0)
+        result.uom = node.@uom;
+      return result;
+    },
+    /**
+     * Method: parseData.reference
+     * Given an Object representing the WPS reference response.
+     *
+     * Parameters:
+     * node - {E4XElement} A WPS node.
+     *
+     * Returns:
+     * {Object} A WPS reference response.
+     */
+    'reference': function(node) {
+      var result = {type:'reference',value:node.*::href};
+      return result;
+    }
+  },
+  CLASS_NAME: 'ZOO.Format.WPS'
+});
+
+/**
+ * Class: ZOO.Feature
+ * Vector features use the ZOO.Geometry classes as geometry description.
+ * They have an 'attributes' property, which is the data object
+ */
+ZOO.Feature = ZOO.Class({
+  /** 
+   * Property: fid 
+   * {String} 
+   */
+  fid: null,
+  /** 
+   * Property: geometry 
+   * {<ZOO.Geometry>} 
+   */
+  geometry: null,
+  /** 
+   * Property: attributes 
+   * {Object} This object holds arbitrary properties that describe the
+   *     feature.
+   */
+  attributes: null,
+  /**
+   * Property: bounds
+   * {<ZOO.Bounds>} The box bounding that feature's geometry, that
+   *     property can be set by an <ZOO.Format> object when
+   *     deserializing the feature, so in most cases it represents an
+   *     information set by the server. 
+   */
+  bounds: null,
+  /** 
+   * Constructor: ZOO.Feature
+   * Create a vector feature. 
+   * 
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} The geometry that this feature
+   *     represents.
+   * attributes - {Object} An optional object that will be mapped to the
+   *     <attributes> property. 
+   */
+  initialize: function(geometry, attributes) {
+    this.geometry = geometry ? geometry : null;
+    this.attributes = {};
+    if (attributes)
+      this.attributes = ZOO.extend(this.attributes,attributes);
+  },
+  /** 
+   * Method: destroy
+   * nullify references to prevent circular references and memory leaks
+   */
+  destroy: function() {
+    this.geometry = null;
+  },
+  /**
+   * Method: clone
+   * Create a clone of this vector feature.  Does not set any non-standard
+   *     properties.
+   *
+   * Returns:
+   * {<ZOO.Feature>} An exact clone of this vector feature.
+   */
+  clone: function () {
+    return new ZOO.Feature(this.geometry ? this.geometry.clone() : null,
+            this.attributes);
+  },
+  /**
+   * Method: move
+   * Moves the feature and redraws it at its new location
+   *
+   * Parameters:
+   * x - {Float}
+   * y - {Float}
+   */
+  move: function(x, y) {
+    if(!this.geometry.move)
+      return;
+
+    this.geometry.move(x,y);
+    return this.geometry;
+  },
+  CLASS_NAME: 'ZOO.Feature'
+});
+
+/**
+ * Class: ZOO.Geometry
+ * A Geometry is a description of a geographic object. Create an instance
+ * of this class with the <ZOO.Geometry> constructor. This is a base class,
+ * typical geometry types are described by subclasses of this class.
+ */
+ZOO.Geometry = ZOO.Class({
+  /**
+   * Property: id
+   * {String} A unique identifier for this geometry.
+   */
+  id: null,
+  /**
+   * Property: parent
+   * {<ZOO.Geometry>}This is set when a Geometry is added as component
+   * of another geometry
+   */
+  parent: null,
+  /**
+   * Property: bounds 
+   * {<ZOO.Bounds>} The bounds of this geometry
+   */
+  bounds: null,
+  /**
+   * Constructor: ZOO.Geometry
+   * Creates a geometry object.  
+   */
+  initialize: function() {
+    //generate unique id
+  },
+  /**
+   * Method: destroy
+   * Destroy this geometry.
+   */
+  destroy: function() {
+    this.id = null;
+    this.bounds = null;
+  },
+  /**
+   * Method: clone
+   * Create a clone of this geometry.  Does not set any non-standard
+   *     properties of the cloned geometry.
+   * 
+   * Returns:
+   * {<ZOO.Geometry>} An exact clone of this geometry.
+   */
+  clone: function() {
+    return new ZOO.Geometry();
+  },
+  /**
+   * Method: extendBounds
+   * Extend the existing bounds to include the new bounds. 
+   * If geometry's bounds is not yet set, then set a new Bounds.
+   * 
+   * Parameters:
+   * newBounds - {<ZOO.Bounds>} 
+   */
+  extendBounds: function(newBounds){
+    var bounds = this.getBounds();
+    if (!bounds)
+      this.setBounds(newBounds);
+    else
+      this.bounds.extend(newBounds);
+  },
+  /**
+   * Set the bounds for this Geometry.
+   * 
+   * Parameters:
+   * bounds - {<ZOO.Bounds>} 
+   */
+  setBounds: function(bounds) {
+    if (bounds)
+      this.bounds = bounds.clone();
+  },
+  /**
+   * Method: clearBounds
+   * Nullify this components bounds and that of its parent as well.
+   */
+  clearBounds: function() {
+    this.bounds = null;
+    if (this.parent)
+      this.parent.clearBounds();
+  },
+  /**
+   * Method: getBounds
+   * Get the bounds for this Geometry. If bounds is not set, it 
+   * is calculated again, this makes queries faster.
+   * 
+   * Returns:
+   * {<ZOO.Bounds>}
+   */
+  getBounds: function() {
+    if (this.bounds == null) {
+      this.calculateBounds();
+    }
+    return this.bounds;
+  },
+  /** 
+   * Method: calculateBounds
+   * Recalculate the bounds for the geometry. 
+   */
+  calculateBounds: function() {
+    // This should be overridden by subclasses.
+    return this.bounds;
+  },
+  distanceTo: function(geometry, options) {
+  },
+  getVertices: function(nodes) {
+  },
+  getLength: function() {
+    return 0.0;
+  },
+  getArea: function() {
+    return 0.0;
+  },
+  getCentroid: function() {
+    return null;
+  },
+  /**
+   * Method: toString
+   * Returns the Well-Known Text representation of a geometry
+   *
+   * Returns:
+   * {String} Well-Known Text
+   */
+  toString: function() {
+    return ZOO.Format.WKT.prototype.write(
+        new ZOO.Feature(this)
+    );
+  },
+  CLASS_NAME: 'ZOO.Geometry'
+});
+/**
+ * Function: ZOO.Geometry.fromWKT
+ * Generate a geometry given a Well-Known Text string.
+ *
+ * Parameters:
+ * wkt - {String} A string representing the geometry in Well-Known Text.
+ *
+ * Returns:
+ * {<ZOO.Geometry>} A geometry of the appropriate class.
+ */
+ZOO.Geometry.fromWKT = function(wkt) {
+  var format = arguments.callee.format;
+  if(!format) {
+    format = new ZOO.Format.WKT();
+    arguments.callee.format = format;
+  }
+  var geom;
+  var result = format.read(wkt);
+  if(result instanceof ZOO.Feature) {
+    geom = result.geometry;
+  } else if(result instanceof Array) {
+    var len = result.length;
+    var components = new Array(len);
+    for(var i=0; i<len; ++i) {
+      components[i] = result[i].geometry;
+    }
+    geom = new ZOO.Geometry.Collection(components);
+  }
+  return geom;
+};
+ZOO.Geometry.segmentsIntersect = function(seg1, seg2, options) {
+  var point = options && options.point;
+  var tolerance = options && options.tolerance;
+  var intersection = false;
+  var x11_21 = seg1.x1 - seg2.x1;
+  var y11_21 = seg1.y1 - seg2.y1;
+  var x12_11 = seg1.x2 - seg1.x1;
+  var y12_11 = seg1.y2 - seg1.y1;
+  var y22_21 = seg2.y2 - seg2.y1;
+  var x22_21 = seg2.x2 - seg2.x1;
+  var d = (y22_21 * x12_11) - (x22_21 * y12_11);
+  var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
+  var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
+  if(d == 0) {
+    // parallel
+    if(n1 == 0 && n2 == 0) {
+      // coincident
+      intersection = true;
+    }
+  } else {
+    var along1 = n1 / d;
+    var along2 = n2 / d;
+    if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
+      // intersect
+      if(!point) {
+        intersection = true;
+      } else {
+        // calculate the intersection point
+        var x = seg1.x1 + (along1 * x12_11);
+        var y = seg1.y1 + (along1 * y12_11);
+        intersection = new ZOO.Geometry.Point(x, y);
+      }
+    }
+  }
+  if(tolerance) {
+    var dist;
+    if(intersection) {
+      if(point) {
+        var segs = [seg1, seg2];
+        var seg, x, y;
+        // check segment endpoints for proximity to intersection
+        // set intersection to first endpoint within the tolerance
+        outer: for(var i=0; i<2; ++i) {
+          seg = segs[i];
+          for(var j=1; j<3; ++j) {
+            x = seg["x" + j];
+            y = seg["y" + j];
+            dist = Math.sqrt(
+                Math.pow(x - intersection.x, 2) +
+                Math.pow(y - intersection.y, 2)
+            );
+            if(dist < tolerance) {
+              intersection.x = x;
+              intersection.y = y;
+              break outer;
+            }
+          }
+        }
+      }
+    } else {
+      // no calculated intersection, but segments could be within
+      // the tolerance of one another
+      var segs = [seg1, seg2];
+      var source, target, x, y, p, result;
+      // check segment endpoints for proximity to intersection
+      // set intersection to first endpoint within the tolerance
+      outer: for(var i=0; i<2; ++i) {
+        source = segs[i];
+        target = segs[(i+1)%2];
+        for(var j=1; j<3; ++j) {
+          p = {x: source["x"+j], y: source["y"+j]};
+          result = ZOO.Geometry.distanceToSegment(p, target);
+          if(result.distance < tolerance) {
+            if(point) {
+              intersection = new ZOO.Geometry.Point(p.x, p.y);
+            } else {
+              intersection = true;
+            }
+            break outer;
+          }
+        }
+      }
+    }
+  }
+  return intersection;
+};
+ZOO.Geometry.distanceToSegment = function(point, segment) {
+  var x0 = point.x;
+  var y0 = point.y;
+  var x1 = segment.x1;
+  var y1 = segment.y1;
+  var x2 = segment.x2;
+  var y2 = segment.y2;
+  var dx = x2 - x1;
+  var dy = y2 - y1;
+  var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
+               (Math.pow(dx, 2) + Math.pow(dy, 2));
+  var x, y;
+  if(along <= 0.0) {
+    x = x1;
+    y = y1;
+  } else if(along >= 1.0) {
+    x = x2;
+    y = y2;
+  } else {
+    x = x1 + along * dx;
+    y = y1 + along * dy;
+  }
+  return {
+    distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)),
+    x: x, y: y
+  };
+};
+/**
+ * Class: ZOO.Geometry.Collection
+ * A Collection is exactly what it sounds like: A collection of different 
+ * Geometries. These are stored in the local parameter <components> (which
+ * can be passed as a parameter to the constructor). 
+ * 
+ * As new geometries are added to the collection, they are NOT cloned. 
+ * When removing geometries, they need to be specified by reference (ie you 
+ * have to pass in the *exact* geometry to be removed).
+ * 
+ * The <getArea> and <getLength> functions here merely iterate through
+ * the components, summing their respective areas and lengths.
+ *
+ * Create a new instance with the <ZOO.Geometry.Collection> constructor.
+ *
+ * Inerhits from:
+ *  - <ZOO.Geometry> 
+ */
+ZOO.Geometry.Collection = ZOO.Class(ZOO.Geometry, {
+  /**
+   * Property: components
+   * {Array(<ZOO.Geometry>)} The component parts of this geometry
+   */
+  components: null,
+  /**
+   * Property: componentTypes
+   * {Array(String)} An array of class names representing the types of
+   * components that the collection can include.  A null value means the
+   * component types are not restricted.
+   */
+  componentTypes: null,
+  /**
+   * Constructor: ZOO.Geometry.Collection
+   * Creates a Geometry Collection -- a list of geoms.
+   *
+   * Parameters: 
+   * components - {Array(<ZOO.Geometry>)} Optional array of geometries
+   *
+   */
+  initialize: function (components) {
+    ZOO.Geometry.prototype.initialize.apply(this, arguments);
+    this.components = [];
+    if (components != null) {
+      this.addComponents(components);
+    }
+  },
+  /**
+   * Method: destroy
+   * Destroy this geometry.
+   */
+  destroy: function () {
+    this.components.length = 0;
+    this.components = null;
+  },
+  /**
+   * Method: clone
+   * Clone this geometry.
+   *
+   * Returns:
+   * {<ZOO.Geometry.Collection>} An exact clone of this collection
+   */
+  clone: function() {
+    var geometry = eval("new " + this.CLASS_NAME + "()");
+    for(var i=0, len=this.components.length; i<len; i++) {
+      geometry.addComponent(this.components[i].clone());
+    }
+    return geometry;
+  },
+  /**
+   * Method: getComponentsString
+   * Get a string representing the components for this collection
+   * 
+   * Returns:
+   * {String} A string representation of the components of this geometry
+   */
+  getComponentsString: function(){
+    var strings = [];
+    for(var i=0, len=this.components.length; i<len; i++) {
+      strings.push(this.components[i].toShortString()); 
+    }
+    return strings.join(",");
+  },
+  /**
+   * Method: calculateBounds
+   * Recalculate the bounds by iterating through the components and 
+   * calling extendBounds() on each item.
+   */
+  calculateBounds: function() {
+    this.bounds = null;
+    if ( this.components && this.components.length > 0) {
+      this.setBounds(this.components[0].getBounds());
+      for (var i=1, len=this.components.length; i<len; i++) {
+        this.extendBounds(this.components[i].getBounds());
+      }
+    }
+    return this.bounds
+  },
+  /**
+   * APIMethod: addComponents
+   * Add components to this geometry.
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry>)} An array of geometries to add
+   */
+  addComponents: function(components){
+    if(!(components instanceof Array))
+      components = [components];
+    for(var i=0, len=components.length; i<len; i++) {
+      this.addComponent(components[i]);
+    }
+  },
+  /**
+   * Method: addComponent
+   * Add a new component (geometry) to the collection.  If this.componentTypes
+   * is set, then the component class name must be in the componentTypes array.
+   *
+   * The bounds cache is reset.
+   * 
+   * Parameters:
+   * component - {<ZOO.Geometry>} A geometry to add
+   * index - {int} Optional index into the array to insert the component
+   *
+   * Returns:
+   * {Boolean} The component geometry was successfully added
+   */
+  addComponent: function(component, index) {
+    var added = false;
+    if(component) {
+      if(this.componentTypes == null ||
+          (ZOO.indexOf(this.componentTypes,
+                       component.CLASS_NAME) > -1)) {
+        if(index != null && (index < this.components.length)) {
+          var components1 = this.components.slice(0, index);
+          var components2 = this.components.slice(index, 
+                                                  this.components.length);
+          components1.push(component);
+          this.components = components1.concat(components2);
+        } else {
+          this.components.push(component);
+        }
+        component.parent = this;
+        this.clearBounds();
+        added = true;
+      }
+    }
+    return added;
+  },
+  /**
+   * Method: removeComponents
+   * Remove components from this geometry.
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry>)} The components to be removed
+   */
+  removeComponents: function(components) {
+    if(!(components instanceof Array))
+      components = [components];
+    for(var i=components.length-1; i>=0; --i) {
+      this.removeComponent(components[i]);
+    }
+  },
+  /**
+   * Method: removeComponent
+   * Remove a component from this geometry.
+   *
+   * Parameters:
+   * component - {<ZOO.Geometry>} 
+   */
+  removeComponent: function(component) {      
+    ZOO.removeItem(this.components, component);
+    // clearBounds() so that it gets recalculated on the next call
+    // to this.getBounds();
+    this.clearBounds();
+  },
+  /**
+   * Method: getLength
+   * Calculate the length of this geometry
+   *
+   * Returns:
+   * {Float} The length of the geometry
+   */
+  getLength: function() {
+    var length = 0.0;
+    for (var i=0, len=this.components.length; i<len; i++) {
+      length += this.components[i].getLength();
+    }
+    return length;
+  },
+  /**
+   * APIMethod: getArea
+   * Calculate the area of this geometry. Note how this function is 
+   * overridden in <ZOO.Geometry.Polygon>.
+   *
+   * Returns:
+   * {Float} The area of the collection by summing its parts
+   */
+  getArea: function() {
+    var area = 0.0;
+    for (var i=0, len=this.components.length; i<len; i++) {
+      area += this.components[i].getArea();
+    }
+    return area;
+  },
+  /** 
+   * APIMethod: getGeodesicArea
+   * Calculate the approximate area of the polygon were it projected onto
+   *     the earth.
+   *
+   * Parameters:
+   * projection - {<ZOO.Projection>} The spatial reference system
+   *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+   *     assumed.
+   * 
+   * Reference:
+   * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+   *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+   *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+   *
+   * Returns:
+   * {float} The approximate geodesic area of the geometry in square meters.
+   */
+  getGeodesicArea: function(projection) {
+    var area = 0.0;
+    for(var i=0, len=this.components.length; i<len; i++) {
+      area += this.components[i].getGeodesicArea(projection);
+    }
+    return area;
+  },
+  /**
+   * Method: getCentroid
+   *
+   * Returns:
+   * {<ZOO.Geometry.Point>} The centroid of the collection
+   */
+  getCentroid: function() {
+    return this.components.length && this.components[0].getCentroid();
+  },
+  /**
+   * Method: getGeodesicLength
+   * Calculate the approximate length of the geometry were it projected onto
+   *     the earth.
+   *
+   * Parameters:
+   * projection - {<ZOO.Projection>} The spatial reference system
+   *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+   *     assumed.
+   * 
+   * Returns:
+   * {Float} The appoximate geodesic length of the geometry in meters.
+   */
+  getGeodesicLength: function(projection) {
+    var length = 0.0;
+    for(var i=0, len=this.components.length; i<len; i++) {
+      length += this.components[i].getGeodesicLength(projection);
+    }
+    return length;
+  },
+  /**
+   * Method: move
+   * Moves a geometry by the given displacement along positive x and y axes.
+   *     This modifies the position of the geometry and clears the cached
+   *     bounds.
+   *
+   * Parameters:
+   * x - {Float} Distance to move geometry in positive x direction. 
+   * y - {Float} Distance to move geometry in positive y direction.
+   */
+  move: function(x, y) {
+    for(var i=0, len=this.components.length; i<len; i++) {
+      this.components[i].move(x, y);
+    }
+  },
+  /**
+   * Method: rotate
+   * Rotate a geometry around some origin
+   *
+   * Parameters:
+   * angle - {Float} Rotation angle in degrees (measured counterclockwise
+   *                 from the positive x-axis)
+   * origin - {<ZOO.Geometry.Point>} Center point for the rotation
+   */
+  rotate: function(angle, origin) {
+    for(var i=0, len=this.components.length; i<len; ++i) {
+      this.components[i].rotate(angle, origin);
+    }
+  },
+  /**
+   * Method: resize
+   * Resize a geometry relative to some origin.  Use this method to apply
+   *     a uniform scaling to a geometry.
+   *
+   * Parameters:
+   * scale - {Float} Factor by which to scale the geometry.  A scale of 2
+   *                 doubles the size of the geometry in each dimension
+   *                 (lines, for example, will be twice as long, and polygons
+   *                 will have four times the area).
+   * origin - {<ZOO.Geometry.Point>} Point of origin for resizing
+   * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+   * 
+   * Returns:
+   * {ZOO.Geometry} - The current geometry. 
+   */
+  resize: function(scale, origin, ratio) {
+    for(var i=0; i<this.components.length; ++i) {
+      this.components[i].resize(scale, origin, ratio);
+    }
+    return this;
+  },
+  distanceTo: function(geometry, options) {
+    var edge = !(options && options.edge === false);
+    var details = edge && options && options.details;
+    var result, best;
+    var min = Number.POSITIVE_INFINITY;
+    for(var i=0, len=this.components.length; i<len; ++i) {
+      result = this.components[i].distanceTo(geometry, options);
+      distance = details ? result.distance : result;
+      if(distance < min) {
+        min = distance;
+        best = result;
+        if(min == 0)
+          break;
+      }
+    }
+    return best;
+  },
+  /** 
+   * Method: equals
+   * Determine whether another geometry is equivalent to this one.  Geometries
+   *     are considered equivalent if all components have the same coordinates.
+   * 
+   * Parameters:
+   * geom - {<ZOO.Geometry>} The geometry to test. 
+   *
+   * Returns:
+   * {Boolean} The supplied geometry is equivalent to this geometry.
+   */
+  equals: function(geometry) {
+    var equivalent = true;
+    if(!geometry || !geometry.CLASS_NAME ||
+       (this.CLASS_NAME != geometry.CLASS_NAME))
+      equivalent = false;
+    else if(!(geometry.components instanceof Array) ||
+             (geometry.components.length != this.components.length))
+      equivalent = false;
+    else
+      for(var i=0, len=this.components.length; i<len; ++i) {
+        if(!this.components[i].equals(geometry.components[i])) {
+          equivalent = false;
+          break;
+        }
+      }
+    return equivalent;
+  },
+  /**
+   * Method: transform
+   * Reproject the components geometry from source to dest.
+   * 
+   * Parameters:
+   * source - {<ZOO.Projection>} 
+   * dest - {<ZOO.Projection>}
+   * 
+   * Returns:
+   * {<ZOO.Geometry>} 
+   */
+  transform: function(source, dest) {
+    if (source && dest) {
+      for (var i=0, len=this.components.length; i<len; i++) {  
+        var component = this.components[i];
+        component.transform(source, dest);
+      }
+      this.bounds = null;
+    }
+    return this;
+  },
+  /**
+   * Method: intersects
+   * Determine if the input geometry intersects this one.
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} Any type of geometry.
+   *
+   * Returns:
+   * {Boolean} The input geometry intersects this one.
+   */
+  intersects: function(geometry) {
+    var intersect = false;
+    for(var i=0, len=this.components.length; i<len; ++ i) {
+      intersect = geometry.intersects(this.components[i]);
+      if(intersect)
+        break;
+    }
+    return intersect;
+  },
+  /**
+   * Method: getVertices
+   * Return a list of all points in this geometry.
+   *
+   * Parameters:
+   * nodes - {Boolean} For lines, only return vertices that are
+   *     endpoints.  If false, for lines, only vertices that are not
+   *     endpoints will be returned.  If not provided, all vertices will
+   *     be returned.
+   *
+   * Returns:
+   * {Array} A list of all vertices in the geometry.
+   */
+  getVertices: function(nodes) {
+    var vertices = [];
+    for(var i=0, len=this.components.length; i<len; ++i) {
+      Array.prototype.push.apply(
+          vertices, this.components[i].getVertices(nodes)
+          );
+    }
+    return vertices;
+  },
+  CLASS_NAME: 'ZOO.Geometry.Collection'
+});
+/**
+ * Class: ZOO.Geometry.Point
+ * Point geometry class. 
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry> 
+ */
+ZOO.Geometry.Point = ZOO.Class(ZOO.Geometry, {
+  /** 
+   * Property: x 
+   * {float} 
+   */
+  x: null,
+  /** 
+   * Property: y 
+   * {float} 
+   */
+  y: null,
+  /**
+   * Constructor: ZOO.Geometry.Point
+   * Construct a point geometry.
+   *
+   * Parameters:
+   * x - {float} 
+   * y - {float}
+   * 
+   */
+  initialize: function(x, y) {
+    ZOO.Geometry.prototype.initialize.apply(this, arguments);
+    this.x = parseFloat(x);
+    this.y = parseFloat(y);
+  },
+  /**
+   * Method: clone
+   * 
+   * Returns:
+   * {<ZOO.Geometry.Point>} An exact clone of this ZOO.Geometry.Point
+   */
+  clone: function(obj) {
+    if (obj == null)
+      obj = new ZOO.Geometry.Point(this.x, this.y);
+    // catch any randomly tagged-on properties
+    // ZOO.Util.applyDefaults(obj, this);
+    return obj;
+  },
+  /** 
+   * Method: calculateBounds
+   * Create a new Bounds based on the x/y
+   */
+  calculateBounds: function () {
+    this.bounds = new ZOO.Bounds(this.x, this.y,
+                                        this.x, this.y);
+  },
+  distanceTo: function(geometry, options) {
+    var edge = !(options && options.edge === false);
+    var details = edge && options && options.details;
+    var distance, x0, y0, x1, y1, result;
+    if(geometry instanceof ZOO.Geometry.Point) {
+      x0 = this.x;
+      y0 = this.y;
+      x1 = geometry.x;
+      y1 = geometry.y;
+      distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
+      result = !details ?
+        distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance};
+    } else {
+      result = geometry.distanceTo(this, options);
+      if(details) {
+        // switch coord order since this geom is target
+        result = {
+          x0: result.x1, y0: result.y1,
+          x1: result.x0, y1: result.y0,
+          distance: result.distance
+        };
+      }
+    }
+    return result;
+  },
+  /** 
+   * Method: equals
+   * Determine whether another geometry is equivalent to this one.  Geometries
+   *     are considered equivalent if all components have the same coordinates.
+   * 
+   * Parameters:
+   * geom - {<ZOO.Geometry.Point>} The geometry to test. 
+   *
+   * Returns:
+   * {Boolean} The supplied geometry is equivalent to this geometry.
+   */
+  equals: function(geom) {
+    var equals = false;
+    if (geom != null)
+      equals = ((this.x == geom.x && this.y == geom.y) ||
+                (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
+    return equals;
+  },
+  /**
+   * Method: toShortString
+   *
+   * Returns:
+   * {String} Shortened String representation of Point object. 
+   *         (ex. <i>"5, 42"</i>)
+   */
+  toShortString: function() {
+    return (this.x + ", " + this.y);
+  },
+  /**
+   * Method: move
+   * Moves a geometry by the given displacement along positive x and y axes.
+   *     This modifies the position of the geometry and clears the cached
+   *     bounds.
+   *
+   * Parameters:
+   * x - {Float} Distance to move geometry in positive x direction. 
+   * y - {Float} Distance to move geometry in positive y direction.
+   */
+  move: function(x, y) {
+    this.x = this.x + x;
+    this.y = this.y + y;
+    this.clearBounds();
+  },
+  /**
+   * Method: rotate
+   * Rotate a point around another.
+   *
+   * Parameters:
+   * angle - {Float} Rotation angle in degrees (measured counterclockwise
+   *                 from the positive x-axis)
+   * origin - {<ZOO.Geometry.Point>} Center point for the rotation
+   */
+  rotate: function(angle, origin) {
+        angle *= Math.PI / 180;
+        var radius = this.distanceTo(origin);
+        var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
+        this.x = origin.x + (radius * Math.cos(theta));
+        this.y = origin.y + (radius * Math.sin(theta));
+        this.clearBounds();
+  },
+  /**
+   * Method: getCentroid
+   *
+   * Returns:
+   * {<ZOO.Geometry.Point>} The centroid of the collection
+   */
+  getCentroid: function() {
+    return new ZOO.Geometry.Point(this.x, this.y);
+  },
+  /**
+   * Method: resize
+   * Resize a point relative to some origin.  For points, this has the effect
+   *     of scaling a vector (from the origin to the point).  This method is
+   *     more useful on geometry collection subclasses.
+   *
+   * Parameters:
+   * scale - {Float} Ratio of the new distance from the origin to the old
+   *                 distance from the origin.  A scale of 2 doubles the
+   *                 distance between the point and origin.
+   * origin - {<ZOO.Geometry.Point>} Point of origin for resizing
+   * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+   * 
+   * Returns:
+   * {ZOO.Geometry} - The current geometry. 
+   */
+  resize: function(scale, origin, ratio) {
+    ratio = (ratio == undefined) ? 1 : ratio;
+    this.x = origin.x + (scale * ratio * (this.x - origin.x));
+    this.y = origin.y + (scale * (this.y - origin.y));
+    this.clearBounds();
+    return this;
+  },
+  /**
+   * Method: intersects
+   * Determine if the input geometry intersects this one.
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} Any type of geometry.
+   *
+   * Returns:
+   * {Boolean} The input geometry intersects this one.
+   */
+  intersects: function(geometry) {
+    var intersect = false;
+    if(geometry.CLASS_NAME == "ZOO.Geometry.Point") {
+      intersect = this.equals(geometry);
+    } else {
+      intersect = geometry.intersects(this);
+    }
+    return intersect;
+  },
+  /**
+   * Method: transform
+   * Translate the x,y properties of the point from source to dest.
+   * 
+   * Parameters:
+   * source - {<ZOO.Projection>} 
+   * dest - {<ZOO.Projection>}
+   * 
+   * Returns:
+   * {<ZOO.Geometry>} 
+   */
+  transform: function(source, dest) {
+    if ((source && dest)) {
+      ZOO.Projection.transform(
+          this, source, dest); 
+      this.bounds = null;
+    }       
+    return this;
+  },
+  /**
+   * Method: getVertices
+   * Return a list of all points in this geometry.
+   *
+   * Parameters:
+   * nodes - {Boolean} For lines, only return vertices that are
+   *     endpoints.  If false, for lines, only vertices that are not
+   *     endpoints will be returned.  If not provided, all vertices will
+   *     be returned.
+   *
+   * Returns:
+   * {Array} A list of all vertices in the geometry.
+   */
+  getVertices: function(nodes) {
+    return [this];
+  },
+  CLASS_NAME: 'ZOO.Geometry.Point'
+});
+/**
+ * Class: ZOO.Geometry.Surface
+ * Surface geometry class. 
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry> 
+ */
+ZOO.Geometry.Surface = ZOO.Class(ZOO.Geometry, {
+  initialize: function() {
+    ZOO.Geometry.prototype.initialize.apply(this, arguments);
+  },
+  CLASS_NAME: "ZOO.Geometry.Surface"
+});
+/**
+ * Class: ZOO.Geometry.MultiPoint
+ * MultiPoint is a collection of Points. Create a new instance with the
+ * <ZOO.Geometry.MultiPoint> constructor.
+ *
+ * Inherits from:
+ *  - <ZOO.Geometry.Collection>
+ */
+ZOO.Geometry.MultiPoint = ZOO.Class(
+  ZOO.Geometry.Collection, {
+  /**
+   * Property: componentTypes
+   * {Array(String)} An array of class names representing the types of
+   * components that the collection can include.  A null value means the
+   * component types are not restricted.
+   */
+  componentTypes: ["ZOO.Geometry.Point"],
+  /**
+   * Constructor: ZOO.Geometry.MultiPoint
+   * Create a new MultiPoint Geometry
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry.Point>)} 
+   *
+   * Returns:
+   * {<ZOO.Geometry.MultiPoint>}
+   */
+  initialize: function(components) {
+    ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+  },
+  /**
+   * Method: addPoint
+   * Wrapper for <ZOO.Geometry.Collection.addComponent>
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>} Point to be added
+   * index - {Integer} Optional index
+   */
+  addPoint: function(point, index) {
+    this.addComponent(point, index);
+  },
+  /**
+   * Method: removePoint
+   * Wrapper for <ZOO.Geometry.Collection.removeComponent>
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>} Point to be removed
+   */
+  removePoint: function(point){
+    this.removeComponent(point);
+  },
+  CLASS_NAME: "ZOO.Geometry.MultiPoint"
+});
+/**
+ * Class: ZOO.Geometry.Curve
+ * A Curve is a MultiPoint, whose points are assumed to be connected. To 
+ * this end, we provide a "getLength()" function, which iterates through 
+ * the points, summing the distances between them. 
+ * 
+ * Inherits: 
+ *  - <ZOO.Geometry.MultiPoint>
+ */
+ZOO.Geometry.Curve = ZOO.Class(ZOO.Geometry.MultiPoint, {
+  /**
+   * Property: componentTypes
+   * {Array(String)} An array of class names representing the types of 
+   *                 components that the collection can include.  A null 
+   *                 value means the component types are not restricted.
+   */
+  componentTypes: ["ZOO.Geometry.Point"],
+  /**
+   * Constructor: ZOO.Geometry.Curve
+   * 
+   * Parameters:
+   * point - {Array(<ZOO.Geometry.Point>)}
+   */
+  initialize: function(points) {
+    ZOO.Geometry.MultiPoint.prototype.initialize.apply(this,arguments);
+  },
+  /**
+   * Method: getLength
+   * 
+   * Returns:
+   * {Float} The length of the curve
+   */
+  getLength: function() {
+    var length = 0.0;
+    if ( this.components && (this.components.length > 1)) {
+      for(var i=1, len=this.components.length; i<len; i++) {
+        length += this.components[i-1].distanceTo(this.components[i]);
+      }
+    }
+    return length;
+  },
+  /**
+     * APIMethod: getGeodesicLength
+     * Calculate the approximate length of the geometry were it projected onto
+     *     the earth.
+     *
+     * projection - {<ZOO.Projection>} The spatial reference system
+     *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+     *     assumed.
+     * 
+     * Returns:
+     * {Float} The appoximate geodesic length of the geometry in meters.
+     */
+    getGeodesicLength: function(projection) {
+      var geom = this;  // so we can work with a clone if needed
+      if(projection) {
+        var gg = new ZOO.Projection("EPSG:4326");
+        if(!gg.equals(projection)) {
+          geom = this.clone().transform(projection, gg);
+       }
+     }
+     var length = 0.0;
+     if(geom.components && (geom.components.length > 1)) {
+       var p1, p2;
+       for(var i=1, len=geom.components.length; i<len; i++) {
+         p1 = geom.components[i-1];
+         p2 = geom.components[i];
+        // this returns km and requires x/y properties
+        length += ZOO.distVincenty(p1,p2);
+      }
+    }
+    // convert to m
+    return length * 1000;
+  },
+  CLASS_NAME: "ZOO.Geometry.Curve"
+});
+/**
+ * Class: ZOO.Geometry.LineString
+ * A LineString is a Curve which, once two points have been added to it, can 
+ * never be less than two points long.
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry.Curve>
+ */
+ZOO.Geometry.LineString = ZOO.Class(ZOO.Geometry.Curve, {
+  /**
+   * Constructor: ZOO.Geometry.LineString
+   * Create a new LineString geometry
+   *
+   * Parameters:
+   * points - {Array(<ZOO.Geometry.Point>)} An array of points used to
+   *          generate the linestring
+   *
+   */
+  initialize: function(points) {
+    ZOO.Geometry.Curve.prototype.initialize.apply(this, arguments);        
+  },
+  /**
+   * Method: removeComponent
+   * Only allows removal of a point if there are three or more points in 
+   * the linestring. (otherwise the result would be just a single point)
+   *
+   * Parameters: 
+   * point - {<ZOO.Geometry.Point>} The point to be removed
+   */
+  removeComponent: function(point) {
+    if ( this.components && (this.components.length > 2))
+      ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments);
+  },
+  /**
+   * Method: intersects
+   * Test for instersection between two geometries.  This is a cheapo
+   *     implementation of the Bently-Ottmann algorigithm.  It doesn't
+   *     really keep track of a sweep line data structure.  It is closer
+   *     to the brute force method, except that segments are sorted and
+   *     potential intersections are only calculated when bounding boxes
+   *     intersect.
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>}
+   *
+   * Returns:
+   * {Boolean} The input geometry intersects this geometry.
+   */
+  intersects: function(geometry) {
+    var intersect = false;
+    var type = geometry.CLASS_NAME;
+    if(type == "ZOO.Geometry.LineString" ||
+       type == "ZOO.Geometry.LinearRing" ||
+       type == "ZOO.Geometry.Point") {
+      var segs1 = this.getSortedSegments();
+      var segs2;
+      if(type == "ZOO.Geometry.Point")
+        segs2 = [{
+          x1: geometry.x, y1: geometry.y,
+          x2: geometry.x, y2: geometry.y
+        }];
+      else
+        segs2 = geometry.getSortedSegments();
+      var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
+          seg2, seg2y1, seg2y2;
+      // sweep right
+      outer: for(var i=0, len=segs1.length; i<len; ++i) {
+         seg1 = segs1[i];
+         seg1x1 = seg1.x1;
+         seg1x2 = seg1.x2;
+         seg1y1 = seg1.y1;
+         seg1y2 = seg1.y2;
+         inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
+           seg2 = segs2[j];
+           if(seg2.x1 > seg1x2)
+             break;
+           if(seg2.x2 < seg1x1)
+             continue;
+           seg2y1 = seg2.y1;
+           seg2y2 = seg2.y2;
+           if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2))
+             continue;
+           if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2))
+             continue;
+           if(ZOO.Geometry.segmentsIntersect(seg1, seg2)) {
+             intersect = true;
+             break outer;
+           }
+         }
+      }
+    } else {
+      intersect = geometry.intersects(this);
+    }
+    return intersect;
+  },
+  /**
+   * Method: getSortedSegments
+   *
+   * Returns:
+   * {Array} An array of segment objects.  Segment objects have properties
+   *     x1, y1, x2, and y2.  The start point is represented by x1 and y1.
+   *     The end point is represented by x2 and y2.  Start and end are
+   *     ordered so that x1 < x2.
+   */
+  getSortedSegments: function() {
+    var numSeg = this.components.length - 1;
+    var segments = new Array(numSeg);
+    for(var i=0; i<numSeg; ++i) {
+      point1 = this.components[i];
+      point2 = this.components[i + 1];
+      if(point1.x < point2.x)
+        segments[i] = {
+          x1: point1.x,
+          y1: point1.y,
+          x2: point2.x,
+          y2: point2.y
+        };
+      else
+        segments[i] = {
+          x1: point2.x,
+          y1: point2.y,
+          x2: point1.x,
+          y2: point1.y
+        };
+    }
+    // more efficient to define this somewhere static
+    function byX1(seg1, seg2) {
+      return seg1.x1 - seg2.x1;
+    }
+    return segments.sort(byX1);
+  },
+  /**
+   * Method: splitWithSegment
+   * Split this geometry with the given segment.
+   *
+   * Parameters:
+   * seg - {Object} An object with x1, y1, x2, and y2 properties referencing
+   *     segment endpoint coordinates.
+   * options - {Object} Properties of this object will be used to determine
+   *     how the split is conducted.
+   *
+   * Valid options:
+   * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+   *     true.  If false, a vertex on the source segment must be within the
+   *     tolerance distance of the intersection to be considered a split.
+   * tolerance - {Number} If a non-null value is provided, intersections
+   *     within the tolerance distance of one of the source segment's
+   *     endpoints will be assumed to occur at the endpoint.
+   *
+   * Returns:
+   * {Object} An object with *lines* and *points* properties.  If the given
+   *     segment intersects this linestring, the lines array will reference
+   *     geometries that result from the split.  The points array will contain
+   *     all intersection points.  Intersection points are sorted along the
+   *     segment (in order from x1,y1 to x2,y2).
+   */
+  splitWithSegment: function(seg, options) {
+    var edge = !(options && options.edge === false);
+    var tolerance = options && options.tolerance;
+    var lines = [];
+    var verts = this.getVertices();
+    var points = [];
+    var intersections = [];
+    var split = false;
+    var vert1, vert2, point;
+    var node, vertex, target;
+    var interOptions = {point: true, tolerance: tolerance};
+    var result = null;
+    for(var i=0, stop=verts.length-2; i<=stop; ++i) {
+      vert1 = verts[i];
+      points.push(vert1.clone());
+      vert2 = verts[i+1];
+      target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y};
+      point = ZOO.Geometry.segmentsIntersect(seg, target, interOptions);
+      if(point instanceof ZOO.Geometry.Point) {
+        if((point.x === seg.x1 && point.y === seg.y1) ||
+           (point.x === seg.x2 && point.y === seg.y2) ||
+            point.equals(vert1) || point.equals(vert2))
+          vertex = true;
+        else
+          vertex = false;
+        if(vertex || edge) {
+          // push intersections different than the previous
+          if(!point.equals(intersections[intersections.length-1]))
+            intersections.push(point.clone());
+          if(i === 0) {
+            if(point.equals(vert1))
+              continue;
+          }
+          if(point.equals(vert2))
+            continue;
+          split = true;
+          if(!point.equals(vert1))
+            points.push(point);
+          lines.push(new ZOO.Geometry.LineString(points));
+          points = [point.clone()];
+        }
+      }
+    }
+    if(split) {
+      points.push(vert2.clone());
+      lines.push(new ZOO.Geometry.LineString(points));
+    }
+    if(intersections.length > 0) {
+      // sort intersections along segment
+      var xDir = seg.x1 < seg.x2 ? 1 : -1;
+      var yDir = seg.y1 < seg.y2 ? 1 : -1;
+      result = {
+        lines: lines,
+        points: intersections.sort(function(p1, p2) {
+           return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
+        })
+      };
+    }
+    return result;
+  },
+  /**
+   * Method: split
+   * Use this geometry (the source) to attempt to split a target geometry.
+   * 
+   * Parameters:
+   * target - {<ZOO.Geometry>} The target geometry.
+   * options - {Object} Properties of this object will be used to determine
+   *     how the split is conducted.
+   *
+   * Valid options:
+   * mutual - {Boolean} Split the source geometry in addition to the target
+   *     geometry.  Default is false.
+   * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+   *     true.  If false, a vertex on the source must be within the tolerance
+   *     distance of the intersection to be considered a split.
+   * tolerance - {Number} If a non-null value is provided, intersections
+   *     within the tolerance distance of an existing vertex on the source
+   *     will be assumed to occur at the vertex.
+   * 
+   * Returns:
+   * {Array} A list of geometries (of this same type as the target) that
+   *     result from splitting the target with the source geometry.  The
+   *     source and target geometry will remain unmodified.  If no split
+   *     results, null will be returned.  If mutual is true and a split
+   *     results, return will be an array of two arrays - the first will be
+   *     all geometries that result from splitting the source geometry and
+   *     the second will be all geometries that result from splitting the
+   *     target geometry.
+   */
+  split: function(target, options) {
+    var results = null;
+    var mutual = options && options.mutual;
+    var sourceSplit, targetSplit, sourceParts, targetParts;
+    if(target instanceof ZOO.Geometry.LineString) {
+      var verts = this.getVertices();
+      var vert1, vert2, seg, splits, lines, point;
+      var points = [];
+      sourceParts = [];
+      for(var i=0, stop=verts.length-2; i<=stop; ++i) {
+        vert1 = verts[i];
+        vert2 = verts[i+1];
+        seg = {
+          x1: vert1.x, y1: vert1.y,
+          x2: vert2.x, y2: vert2.y
+        };
+        targetParts = targetParts || [target];
+        if(mutual)
+          points.push(vert1.clone());
+        for(var j=0; j<targetParts.length; ++j) {
+          splits = targetParts[j].splitWithSegment(seg, options);
+          if(splits) {
+            // splice in new features
+            lines = splits.lines;
+            if(lines.length > 0) {
+              lines.unshift(j, 1);
+              Array.prototype.splice.apply(targetParts, lines);
+              j += lines.length - 2;
+            }
+            if(mutual) {
+              for(var k=0, len=splits.points.length; k<len; ++k) {
+                point = splits.points[k];
+                if(!point.equals(vert1)) {
+                  points.push(point);
+                  sourceParts.push(new ZOO.Geometry.LineString(points));
+                  if(point.equals(vert2))
+                    points = [];
+                  else
+                    points = [point.clone()];
+                }
+              }
+            }
+          }
+        }
+      }
+      if(mutual && sourceParts.length > 0 && points.length > 0) {
+        points.push(vert2.clone());
+        sourceParts.push(new ZOO.Geometry.LineString(points));
+      }
+    } else {
+      results = target.splitWith(this, options);
+    }
+    if(targetParts && targetParts.length > 1)
+      targetSplit = true;
+    else
+      targetParts = [];
+    if(sourceParts && sourceParts.length > 1)
+      sourceSplit = true;
+    else
+      sourceParts = [];
+    if(targetSplit || sourceSplit) {
+      if(mutual)
+        results = [sourceParts, targetParts];
+      else
+        results = targetParts;
+    }
+    return results;
+  },
+  /**
+   * Method: splitWith
+   * Split this geometry (the target) with the given geometry (the source).
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} A geometry used to split this
+   *     geometry (the source).
+   * options - {Object} Properties of this object will be used to determine
+   *     how the split is conducted.
+   *
+   * Valid options:
+   * mutual - {Boolean} Split the source geometry in addition to the target
+   *     geometry.  Default is false.
+   * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+   *     true.  If false, a vertex on the source must be within the tolerance
+   *     distance of the intersection to be considered a split.
+   * tolerance - {Number} If a non-null value is provided, intersections
+   *     within the tolerance distance of an existing vertex on the source
+   *     will be assumed to occur at the vertex.
+   * 
+   * Returns:
+   * {Array} A list of geometries (of this same type as the target) that
+   *     result from splitting the target with the source geometry.  The
+   *     source and target geometry will remain unmodified.  If no split
+   *     results, null will be returned.  If mutual is true and a split
+   *     results, return will be an array of two arrays - the first will be
+   *     all geometries that result from splitting the source geometry and
+   *     the second will be all geometries that result from splitting the
+   *     target geometry.
+   */
+  splitWith: function(geometry, options) {
+    return geometry.split(this, options);
+  },
+  /**
+   * Method: getVertices
+   * Return a list of all points in this geometry.
+   *
+   * Parameters:
+   * nodes - {Boolean} For lines, only return vertices that are
+   *     endpoints.  If false, for lines, only vertices that are not
+   *     endpoints will be returned.  If not provided, all vertices will
+   *     be returned.
+   *
+   * Returns:
+   * {Array} A list of all vertices in the geometry.
+   */
+  getVertices: function(nodes) {
+    var vertices;
+    if(nodes === true)
+      vertices = [
+        this.components[0],
+        this.components[this.components.length-1]
+      ];
+    else if (nodes === false)
+      vertices = this.components.slice(1, this.components.length-1);
+    else
+      vertices = this.components.slice();
+    return vertices;
+  },
+  distanceTo: function(geometry, options) {
+    var edge = !(options && options.edge === false);
+    var details = edge && options && options.details;
+    var result, best = {};
+    var min = Number.POSITIVE_INFINITY;
+    if(geometry instanceof ZOO.Geometry.Point) {
+      var segs = this.getSortedSegments();
+      var x = geometry.x;
+      var y = geometry.y;
+      var seg;
+      for(var i=0, len=segs.length; i<len; ++i) {
+        seg = segs[i];
+        result = ZOO.Geometry.distanceToSegment(geometry, seg);
+        if(result.distance < min) {
+          min = result.distance;
+          best = result;
+          if(min === 0)
+            break;
+        } else {
+          // if distance increases and we cross y0 to the right of x0, no need to keep looking.
+          if(seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2)))
+            break;
+        }
+      }
+      if(details)
+        best = {
+          distance: best.distance,
+          x0: best.x, y0: best.y,
+          x1: x, y1: y
+        };
+      else
+        best = best.distance;
+    } else if(geometry instanceof ZOO.Geometry.LineString) { 
+      var segs0 = this.getSortedSegments();
+      var segs1 = geometry.getSortedSegments();
+      var seg0, seg1, intersection, x0, y0;
+      var len1 = segs1.length;
+      var interOptions = {point: true};
+      outer: for(var i=0, len=segs0.length; i<len; ++i) {
+        seg0 = segs0[i];
+        x0 = seg0.x1;
+        y0 = seg0.y1;
+        for(var j=0; j<len1; ++j) {
+          seg1 = segs1[j];
+          intersection = ZOO.Geometry.segmentsIntersect(seg0, seg1, interOptions);
+          if(intersection) {
+            min = 0;
+            best = {
+              distance: 0,
+              x0: intersection.x, y0: intersection.y,
+              x1: intersection.x, y1: intersection.y
+            };
+            break outer;
+          } else {
+            result = ZOO.Geometry.distanceToSegment({x: x0, y: y0}, seg1);
+            if(result.distance < min) {
+              min = result.distance;
+              best = {
+                distance: min,
+                x0: x0, y0: y0,
+                x1: result.x, y1: result.y
+              };
+            }
+          }
+        }
+      }
+      if(!details)
+        best = best.distance;
+      if(min !== 0) {
+        // check the final vertex in this line's sorted segments
+        if(seg0) {
+          result = geometry.distanceTo(
+              new ZOO.Geometry.Point(seg0.x2, seg0.y2),
+              options
+              );
+          var dist = details ? result.distance : result;
+          if(dist < min) {
+            if(details)
+              best = {
+                distance: min,
+                x0: result.x1, y0: result.y1,
+                x1: result.x0, y1: result.y0
+              };
+            else
+              best = dist;
+          }
+        }
+      }
+    } else {
+      best = geometry.distanceTo(this, options);
+      // swap since target comes from this line
+      if(details)
+        best = {
+          distance: best.distance,
+          x0: best.x1, y0: best.y1,
+          x1: best.x0, y1: best.y0
+        };
+    }
+    return best;
+  },
+  CLASS_NAME: "ZOO.Geometry.LineString"
+});
+/**
+ * Class: ZOO.Geometry.LinearRing
+ * 
+ * A Linear Ring is a special LineString which is closed. It closes itself 
+ * automatically on every addPoint/removePoint by adding a copy of the first
+ * point as the last point. 
+ * 
+ * Also, as it is the first in the line family to close itself, a getArea()
+ * function is defined to calculate the enclosed area of the linearRing
+ * 
+ * Inherits:
+ *  - <ZOO.Geometry.LineString>
+ */
+ZOO.Geometry.LinearRing = ZOO.Class(
+  ZOO.Geometry.LineString, {
+  /**
+   * Property: componentTypes
+   * {Array(String)} An array of class names representing the types of 
+   *                 components that the collection can include.  A null 
+   *                 value means the component types are not restricted.
+   */
+  componentTypes: ["ZOO.Geometry.Point"],
+  /**
+   * Constructor: ZOO.Geometry.LinearRing
+   * Linear rings are constructed with an array of points.  This array
+   *     can represent a closed or open ring.  If the ring is open (the last
+   *     point does not equal the first point), the constructor will close
+   *     the ring.  If the ring is already closed (the last point does equal
+   *     the first point), it will be left closed.
+   * 
+   * Parameters:
+   * points - {Array(<ZOO.Geometry.Point>)} points
+   */
+  initialize: function(points) {
+    ZOO.Geometry.LineString.prototype.initialize.apply(this,arguments);
+  },
+  /**
+   * Method: addComponent
+   * Adds a point to geometry components.  If the point is to be added to
+   *     the end of the components array and it is the same as the last point
+   *     already in that array, the duplicate point is not added.  This has 
+   *     the effect of closing the ring if it is not already closed, and 
+   *     doing the right thing if it is already closed.  This behavior can 
+   *     be overridden by calling the method with a non-null index as the 
+   *     second argument.
+   *
+   * Parameter:
+   * point - {<ZOO.Geometry.Point>}
+   * index - {Integer} Index into the array to insert the component
+   * 
+   * Returns:
+   * {Boolean} Was the Point successfully added?
+   */
+  addComponent: function(point, index) {
+    var added = false;
+    //remove last point
+    var lastPoint = this.components.pop();
+    // given an index, add the point
+    // without an index only add non-duplicate points
+    if(index != null || !point.equals(lastPoint))
+      added = ZOO.Geometry.Collection.prototype.addComponent.apply(this,arguments);
+    //append copy of first point
+    var firstPoint = this.components[0];
+    ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]);
+    return added;
+  },
+  /**
+   * APIMethod: removeComponent
+   * Removes a point from geometry components.
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>}
+   */
+  removeComponent: function(point) {
+    if (this.components.length > 4) {
+      //remove last point
+      this.components.pop();
+      //remove our point
+      ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments);
+      //append copy of first point
+      var firstPoint = this.components[0];
+      ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]);
+    }
+  },
+  /**
+   * Method: move
+   * Moves a geometry by the given displacement along positive x and y axes.
+   *     This modifies the position of the geometry and clears the cached
+   *     bounds.
+   *
+   * Parameters:
+   * x - {Float} Distance to move geometry in positive x direction. 
+   * y - {Float} Distance to move geometry in positive y direction.
+   */
+  move: function(x, y) {
+    for(var i = 0, len=this.components.length; i<len - 1; i++) {
+      this.components[i].move(x, y);
+    }
+  },
+  /**
+   * Method: rotate
+   * Rotate a geometry around some origin
+   *
+   * Parameters:
+   * angle - {Float} Rotation angle in degrees (measured counterclockwise
+   *                 from the positive x-axis)
+   * origin - {<ZOO.Geometry.Point>} Center point for the rotation
+   */
+  rotate: function(angle, origin) {
+    for(var i=0, len=this.components.length; i<len - 1; ++i) {
+      this.components[i].rotate(angle, origin);
+    }
+  },
+  /**
+   * Method: resize
+   * Resize a geometry relative to some origin.  Use this method to apply
+   *     a uniform scaling to a geometry.
+   *
+   * Parameters:
+   * scale - {Float} Factor by which to scale the geometry.  A scale of 2
+   *                 doubles the size of the geometry in each dimension
+   *                 (lines, for example, will be twice as long, and polygons
+   *                 will have four times the area).
+   * origin - {<ZOO.Geometry.Point>} Point of origin for resizing
+   * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+   * 
+   * Returns:
+   * {ZOO.Geometry} - The current geometry. 
+   */
+  resize: function(scale, origin, ratio) {
+    for(var i=0, len=this.components.length; i<len - 1; ++i) {
+      this.components[i].resize(scale, origin, ratio);
+    }
+    return this;
+  },
+  /**
+   * Method: transform
+   * Reproject the components geometry from source to dest.
+   *
+   * Parameters:
+   * source - {<ZOO.Projection>}
+   * dest - {<ZOO.Projection>}
+   * 
+   * Returns:
+   * {<ZOO.Geometry>} 
+   */
+  transform: function(source, dest) {
+    if (source && dest) {
+      for (var i=0, len=this.components.length; i<len - 1; i++) {
+        var component = this.components[i];
+        component.transform(source, dest);
+      }
+      this.bounds = null;
+    }
+    return this;
+  },
+  /**
+   * Method: getCentroid
+   *
+   * Returns:
+   * {<ZOO.Geometry.Point>} The centroid of the ring
+   */
+  getCentroid: function() {
+    if ( this.components && (this.components.length > 2)) {
+      var sumX = 0.0;
+      var sumY = 0.0;
+      for (var i = 0; i < this.components.length - 1; i++) {
+        var b = this.components[i];
+        var c = this.components[i+1];
+        sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y);
+        sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y);
+      }
+      var area = -1 * this.getArea();
+      var x = sumX / (6 * area);
+      var y = sumY / (6 * area);
+    }
+    return new ZOO.Geometry.Point(x, y);
+  },
+  /**
+   * Method: getArea
+   * Note - The area is positive if the ring is oriented CW, otherwise
+   *         it will be negative.
+   * 
+   * Returns:
+   * {Float} The signed area for a ring.
+   */
+  getArea: function() {
+    var area = 0.0;
+    if ( this.components && (this.components.length > 2)) {
+      var sum = 0.0;
+      for (var i=0, len=this.components.length; i<len - 1; i++) {
+        var b = this.components[i];
+        var c = this.components[i+1];
+        sum += (b.x + c.x) * (c.y - b.y);
+      }
+      area = - sum / 2.0;
+    }
+    return area;
+  },
+  /**
+   * Method: getGeodesicArea
+   * Calculate the approximate area of the polygon were it projected onto
+   *     the earth.  Note that this area will be positive if ring is oriented
+   *     clockwise, otherwise it will be negative.
+   *
+   * Parameters:
+   * projection - {<ZOO.Projection>} The spatial reference system
+   *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+   *     assumed.
+   * 
+   * Reference:
+   * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+   *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+   *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+   *
+   * Returns:
+   * {float} The approximate signed geodesic area of the polygon in square
+   *     meters.
+   */
+  getGeodesicArea: function(projection) {
+    var ring = this;  // so we can work with a clone if needed
+    if(projection) {
+      var gg = new ZOO.Projection("EPSG:4326");
+      if(!gg.equals(projection)) {
+        ring = this.clone().transform(projection, gg);
+      }
+    }
+    var area = 0.0;
+    var len = ring.components && ring.components.length;
+    if(len > 2) {
+      var p1, p2;
+      for(var i=0; i<len-1; i++) {
+        p1 = ring.components[i];
+        p2 = ring.components[i+1];
+        area += ZOO.rad(p2.x - p1.x) *
+                (2 + Math.sin(ZOO.rad(p1.y)) +
+                Math.sin(ZOO.rad(p2.y)));
+      }
+      area = area * 6378137.0 * 6378137.0 / 2.0;
+    }
+    return area;
+  },
+  /**
+   * Method: containsPoint
+   * Test if a point is inside a linear ring.  For the case where a point
+   *     is coincident with a linear ring edge, returns 1.  Otherwise,
+   *     returns boolean.
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>}
+   *
+   * Returns:
+   * {Boolean | Number} The point is inside the linear ring.  Returns 1 if
+   *     the point is coincident with an edge.  Returns boolean otherwise.
+   */
+  containsPoint: function(point) {
+    var approx = OpenLayers.Number.limitSigDigs;
+    var digs = 14;
+    var px = approx(point.x, digs);
+    var py = approx(point.y, digs);
+    function getX(y, x1, y1, x2, y2) {
+      return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2);
+    }
+    var numSeg = this.components.length - 1;
+    var start, end, x1, y1, x2, y2, cx, cy;
+    var crosses = 0;
+    for(var i=0; i<numSeg; ++i) {
+      start = this.components[i];
+      x1 = approx(start.x, digs);
+      y1 = approx(start.y, digs);
+      end = this.components[i + 1];
+      x2 = approx(end.x, digs);
+      y2 = approx(end.y, digs);
+
+      /**
+       * The following conditions enforce five edge-crossing rules:
+       *    1. points coincident with edges are considered contained;
+       *    2. an upward edge includes its starting endpoint, and
+       *    excludes its final endpoint;
+       *    3. a downward edge excludes its starting endpoint, and
+       *    includes its final endpoint;
+       *    4. horizontal edges are excluded; and
+       *    5. the edge-ray intersection point must be strictly right
+       *    of the point P.
+       */
+      if(y1 == y2) {
+        // horizontal edge
+        if(py == y1) {
+          // point on horizontal line
+          if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert
+              x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
+            // point on edge
+            crosses = -1;
+            break;
+          }
+        }
+        // ignore other horizontal edges
+        continue;
+      }
+      cx = approx(getX(py, x1, y1, x2, y2), digs);
+      if(cx == px) {
+        // point on line
+        if(y1 < y2 && (py >= y1 && py <= y2) || // upward
+            y1 > y2 && (py <= y1 && py >= y2)) { // downward
+          // point on edge
+          crosses = -1;
+          break;
+        }
+      }
+      if(cx <= px) {
+        // no crossing to the right
+        continue;
+      }
+      if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
+        // no crossing
+        continue;
+      }
+      if(y1 < y2 && (py >= y1 && py < y2) || // upward
+          y1 > y2 && (py < y1 && py >= y2)) { // downward
+        ++crosses;
+      }
+    }
+    var contained = (crosses == -1) ?
+      // on edge
+      1 :
+      // even (out) or odd (in)
+      !!(crosses & 1);
+
+    return contained;
+  },
+  intersects: function(geometry) {
+    var intersect = false;
+    if(geometry.CLASS_NAME == "ZOO.Geometry.Point")
+      intersect = this.containsPoint(geometry);
+    else if(geometry.CLASS_NAME == "ZOO.Geometry.LineString")
+      intersect = geometry.intersects(this);
+    else if(geometry.CLASS_NAME == "ZOO.Geometry.LinearRing")
+      intersect = ZOO.Geometry.LineString.prototype.intersects.apply(
+          this, [geometry]
+          );
+    else
+      for(var i=0, len=geometry.components.length; i<len; ++ i) {
+        intersect = geometry.components[i].intersects(this);
+        if(intersect)
+          break;
+      }
+    return intersect;
+  },
+  getVertices: function(nodes) {
+    return (nodes === true) ? [] : this.components.slice(0, this.components.length-1);
+  },
+  CLASS_NAME: "ZOO.Geometry.LinearRing"
+});
+/**
+ * Class: ZOO.Geometry.MultiLineString
+ * A MultiLineString is a geometry with multiple <ZOO.Geometry.LineString>
+ * components.
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry.Collection>
+ */
+ZOO.Geometry.MultiLineString = ZOO.Class(
+  ZOO.Geometry.Collection, {
+  componentTypes: ["ZOO.Geometry.LineString"],
+  /**
+   * Constructor: ZOO.Geometry.MultiLineString
+   * Constructor for a MultiLineString Geometry.
+   *
+   * Parameters: 
+   * components - {Array(<ZOO.Geometry.LineString>)} 
+   *
+   */
+  initialize: function(components) {
+    ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);        
+  },
+  split: function(geometry, options) {
+    var results = null;
+    var mutual = options && options.mutual;
+    var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
+    var sourceParts = [];
+    var targetParts = [geometry];
+    for(var i=0, len=this.components.length; i<len; ++i) {
+      sourceLine = this.components[i];
+      sourceSplit = false;
+      for(var j=0; j < targetParts.length; ++j) { 
+        splits = sourceLine.split(targetParts[j], options);
+        if(splits) {
+          if(mutual) {
+            sourceLines = splits[0];
+            for(var k=0, klen=sourceLines.length; k<klen; ++k) {
+              if(k===0 && sourceParts.length)
+                sourceParts[sourceParts.length-1].addComponent(
+                  sourceLines[k]
+                );
+              else
+                sourceParts.push(
+                  new ZOO.Geometry.MultiLineString([
+                    sourceLines[k]
+                    ])
+                );
+            }
+            sourceSplit = true;
+            splits = splits[1];
+          }
+          if(splits.length) {
+            // splice in new target parts
+            splits.unshift(j, 1);
+            Array.prototype.splice.apply(targetParts, splits);
+            break;
+          }
+        }
+      }
+      if(!sourceSplit) {
+        // source line was not hit
+        if(sourceParts.length) {
+          // add line to existing multi
+          sourceParts[sourceParts.length-1].addComponent(
+              sourceLine.clone()
+              );
+        } else {
+          // create a fresh multi
+          sourceParts = [
+            new ZOO.Geometry.MultiLineString(
+                sourceLine.clone()
+                )
+            ];
+        }
+      }
+    }
+    if(sourceParts && sourceParts.length > 1)
+      sourceSplit = true;
+    else
+      sourceParts = [];
+    if(targetParts && targetParts.length > 1)
+      targetSplit = true;
+    else
+      targetParts = [];
+    if(sourceSplit || targetSplit) {
+      if(mutual)
+        results = [sourceParts, targetParts];
+      else
+        results = targetParts;
+    }
+    return results;
+  },
+  splitWith: function(geometry, options) {
+    var results = null;
+    var mutual = options && options.mutual;
+    var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
+    if(geometry instanceof ZOO.Geometry.LineString) {
+      targetParts = [];
+      sourceParts = [geometry];
+      for(var i=0, len=this.components.length; i<len; ++i) {
+        targetSplit = false;
+        targetLine = this.components[i];
+        for(var j=0; j<sourceParts.length; ++j) {
+          splits = sourceParts[j].split(targetLine, options);
+          if(splits) {
+            if(mutual) {
+              sourceLines = splits[0];
+              if(sourceLines.length) {
+                // splice in new source parts
+                sourceLines.unshift(j, 1);
+                Array.prototype.splice.apply(sourceParts, sourceLines);
+                j += sourceLines.length - 2;
+              }
+              splits = splits[1];
+              if(splits.length === 0) {
+                splits = [targetLine.clone()];
+              }
+            }
+            for(var k=0, klen=splits.length; k<klen; ++k) {
+              if(k===0 && targetParts.length) {
+                targetParts[targetParts.length-1].addComponent(
+                    splits[k]
+                    );
+              } else {
+                targetParts.push(
+                    new ZOO.Geometry.MultiLineString([
+                      splits[k]
+                      ])
+                    );
+              }
+            }
+            targetSplit = true;                    
+          }
+        }
+        if(!targetSplit) {
+          // target component was not hit
+          if(targetParts.length) {
+            // add it to any existing multi-line
+            targetParts[targetParts.length-1].addComponent(
+                targetLine.clone()
+                );
+          } else {
+            // or start with a fresh multi-line
+            targetParts = [
+              new ZOO.Geometry.MultiLineString([
+                  targetLine.clone()
+                  ])
+              ];
+          }
+
+        }
+      }
+    } else {
+      results = geometry.split(this);
+    }
+    if(sourceParts && sourceParts.length > 1)
+      sourceSplit = true;
+    else
+      sourceParts = [];
+    if(targetParts && targetParts.length > 1)
+      targetSplit = true;
+    else
+      targetParts = [];
+    if(sourceSplit || targetSplit) {
+      if(mutual)
+        results = [sourceParts, targetParts];
+      else
+        results = targetParts;
+    }
+    return results;
+  },
+  CLASS_NAME: "ZOO.Geometry.MultiLineString"
+});
+/**
+ * Class: ZOO.Geometry.Polygon 
+ * Polygon is a collection of <ZOO.Geometry.LinearRing>. 
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry.Collection> 
+ */
+ZOO.Geometry.Polygon = ZOO.Class(
+  ZOO.Geometry.Collection, {
+  componentTypes: ["ZOO.Geometry.LinearRing"],
+  /**
+   * Constructor: ZOO.Geometry.Polygon
+   * Constructor for a Polygon geometry. 
+   * The first ring (this.component[0])is the outer bounds of the polygon and 
+   * all subsequent rings (this.component[1-n]) are internal holes.
+   *
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry.LinearRing>)} 
+   */
+  initialize: function(components) {
+    ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+  },
+  /** 
+   * Method: getArea
+   * Calculated by subtracting the areas of the internal holes from the 
+   *   area of the outer hole.
+   * 
+   * Returns:
+   * {float} The area of the geometry
+   */
+  getArea: function() {
+    var area = 0.0;
+    if ( this.components && (this.components.length > 0)) {
+      area += Math.abs(this.components[0].getArea());
+      for (var i=1, len=this.components.length; i<len; i++) {
+        area -= Math.abs(this.components[i].getArea());
+      }
+    }
+    return area;
+  },
+  /** 
+   * APIMethod: getGeodesicArea
+   * Calculate the approximate area of the polygon were it projected onto
+   *     the earth.
+   *
+   * Parameters:
+   * projection - {<ZOO.Projection>} The spatial reference system
+   *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+   *     assumed.
+   * 
+   * Reference:
+   * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+   *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+   *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+   *
+   * Returns:
+   * {float} The approximate geodesic area of the polygon in square meters.
+   */
+  getGeodesicArea: function(projection) {
+    var area = 0.0;
+    if(this.components && (this.components.length > 0)) {
+      area += Math.abs(this.components[0].getGeodesicArea(projection));
+      for(var i=1, len=this.components.length; i<len; i++) {
+          area -= Math.abs(this.components[i].getGeodesicArea(projection));
+      }
+    }
+    return area;
+  },
+  /**
+   * Method: containsPoint
+   * Test if a point is inside a polygon.  Points on a polygon edge are
+   *     considered inside.
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>}
+   *
+   * Returns:
+   * {Boolean | Number} The point is inside the polygon.  Returns 1 if the
+   *     point is on an edge.  Returns boolean otherwise.
+   */
+  containsPoint: function(point) {
+    var numRings = this.components.length;
+    var contained = false;
+    if(numRings > 0) {
+    // check exterior ring - 1 means on edge, boolean otherwise
+      contained = this.components[0].containsPoint(point);
+      if(contained !== 1) {
+        if(contained && numRings > 1) {
+          // check interior rings
+          var hole;
+          for(var i=1; i<numRings; ++i) {
+            hole = this.components[i].containsPoint(point);
+            if(hole) {
+              if(hole === 1)
+                contained = 1;
+              else
+                contained = false;
+              break;
+            }
+          }
+        }
+      }
+    }
+    return contained;
+  },
+  intersects: function(geometry) {
+    var intersect = false;
+    var i, len;
+    if(geometry.CLASS_NAME == "ZOO.Geometry.Point") {
+      intersect = this.containsPoint(geometry);
+    } else if(geometry.CLASS_NAME == "ZOO.Geometry.LineString" ||
+              geometry.CLASS_NAME == "ZOO.Geometry.LinearRing") {
+      // check if rings/linestrings intersect
+      for(i=0, len=this.components.length; i<len; ++i) {
+        intersect = geometry.intersects(this.components[i]);
+        if(intersect) {
+          break;
+        }
+      }
+      if(!intersect) {
+        // check if this poly contains points of the ring/linestring
+        for(i=0, len=geometry.components.length; i<len; ++i) {
+          intersect = this.containsPoint(geometry.components[i]);
+          if(intersect) {
+            break;
+          }
+        }
+      }
+    } else {
+      for(i=0, len=geometry.components.length; i<len; ++ i) {
+        intersect = this.intersects(geometry.components[i]);
+        if(intersect)
+          break;
+      }
+    }
+    // check case where this poly is wholly contained by another
+    if(!intersect && geometry.CLASS_NAME == "ZOO.Geometry.Polygon") {
+      // exterior ring points will be contained in the other geometry
+      var ring = this.components[0];
+      for(i=0, len=ring.components.length; i<len; ++i) {
+        intersect = geometry.containsPoint(ring.components[i]);
+        if(intersect)
+          break;
+      }
+    }
+    return intersect;
+  },
+  distanceTo: function(geometry, options) {
+    var edge = !(options && options.edge === false);
+    var result;
+    // this is the case where we might not be looking for distance to edge
+    if(!edge && this.intersects(geometry))
+      result = 0;
+    else
+      result = ZOO.Geometry.Collection.prototype.distanceTo.apply(
+          this, [geometry, options]
+          );
+    return result;
+  },
+  CLASS_NAME: "ZOO.Geometry.Polygon"
+});
+/**
+ * Method: createRegularPolygon
+ * Create a regular polygon around a radius. Useful for creating circles 
+ * and the like.
+ *
+ * Parameters:
+ * origin - {<ZOO.Geometry.Point>} center of polygon.
+ * radius - {Float} distance to vertex, in map units.
+ * sides - {Integer} Number of sides. 20 approximates a circle.
+ * rotation - {Float} original angle of rotation, in degrees.
+ */
+ZOO.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {  
+    var angle = Math.PI * ((1/sides) - (1/2));
+    if(rotation) {
+        angle += (rotation / 180) * Math.PI;
+    }
+    var rotatedAngle, x, y;
+    var points = [];
+    for(var i=0; i<sides; ++i) {
+        rotatedAngle = angle + (i * 2 * Math.PI / sides);
+        x = origin.x + (radius * Math.cos(rotatedAngle));
+        y = origin.y + (radius * Math.sin(rotatedAngle));
+        points.push(new ZOO.Geometry.Point(x, y));
+    }
+    var ring = new ZOO.Geometry.LinearRing(points);
+    return new ZOO.Geometry.Polygon([ring]);
+};
+/**
+ * Class: ZOO.Geometry.MultiPolygon
+ * MultiPolygon is a geometry with multiple <ZOO.Geometry.Polygon>
+ * components.  Create a new instance with the <ZOO.Geometry.MultiPolygon>
+ * constructor.
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry.Collection>
+ */
+ZOO.Geometry.MultiPolygon = ZOO.Class(
+  ZOO.Geometry.Collection, {
+  componentTypes: ["ZOO.Geometry.Polygon"],
+  /**
+   * Constructor: ZOO.Geometry.MultiPolygon
+   * Create a new MultiPolygon geometry
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry.Polygon>)} An array of polygons
+   *              used to generate the MultiPolygon
+   *
+   */
+  initialize: function(components) {
+    ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+  },
+  CLASS_NAME: "ZOO.Geometry.MultiPolygon"
+});
+/**
+ * Class: ZOO.Process
+ * Used to query OGC WPS process defined by its URL and its identifier. 
+ * Usefull for chaining localhost process.
+ */
+ZOO.Process = ZOO.Class({
+  /**
+   * Property: schemaLocation
+   * {String} Schema location for a particular minor version.
+   */
+  schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd",
+  /**
+   * Property: namespaces
+   * {Object} Mapping of namespace aliases to namespace URIs.
+   */
+  namespaces: {
+    ows: "http://www.opengis.net/ows/1.1",
+    wps: "http://www.opengis.net/wps/1.0.0",
+    xlink: "http://www.w3.org/1999/xlink",
+    xsi: "http://www.w3.org/2001/XMLSchema-instance",
+  },
+  /**
+   * Property: url
+   * {String} The OGC's Web PRocessing Service URL, 
+   *          default is http://localhost/zoo.
+   */
+  url: 'http://localhost/zoo',
+  /**
+   * Property: identifier
+   * {String} Process identifier in the OGC's Web Processing Service.
+   */
+  identifier: null,
+  /**
+   * Constructor: ZOO.Process
+   * Create a new Process
+   *
+   * Parameters:
+   * url - {String} The OGC's Web Processing Service URL.
+   * identifier - {String} The process identifier in the OGC's Web Processing Service.
+   *
+   */
+  initialize: function(url,identifier) {
+    this.url = url;
+    this.identifier = identifier;
+  },
+  /**
+   * Method: Execute
+   * Query the OGC's Web PRocessing Servcie to Execute the process.
+   *
+   * Parameters:
+   * inputs - {Object}
+   *
+   * Returns:
+   * {String} The OGC's Web processing Service XML response. The result 
+   *          needs to be interpreted.
+   */
+  Execute: function(inputs) {
+    if (this.identifier == null)
+      return null;
+    var body = new XML('<wps:Execute service="WPS" version="1.0.0" xmlns:wps="'+this.namespaces['wps']+'" xmlns:ows="'+this.namespaces['ows']+'" xmlns:xlink="'+this.namespaces['xlink']+'" xmlns:xsi="'+this.namespaces['xsi']+'" xsi:schemaLocation="'+this.schemaLocation+'"><ows:Identifier>'+this.identifier+'</ows:Identifier>'+this.buildDataInputsNode(inputs)+'</wps:Execute>');
+    body = body.toXMLString();
+    var response = ZOO.Request.Post(this.url,body,['Content-Type: text/xml; charset=UTF-8']);
+    return response;
+  },
+  /**
+   * Property: buildInput
+   * Object containing methods to build WPS inputs.
+   */
+  buildInput: {
+    /**
+     * Method: buildInput.complex
+     * Given an E4XElement representing the WPS complex data input.
+     *
+     * Parameters:
+     * identifier - {String} the input indetifier
+     * data - {Object} A WPS complex data input.
+     *
+     * Returns:
+     * {E4XElement} A WPS Input node.
+     */
+    'complex': function(identifier,data) {
+      var input = new XML('<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Data><wps:ComplexData>'+data.value+'</wps:ComplexData></wps:Data></wps:Input>');
+      input.*::Data.*::ComplexData.@mimeType = data.mimetype ? data.mimetype : 'text/plain';
+      if (data.encoding)
+        input.*::Data.*::ComplexData.@encoding = data.encoding;
+      if (data.schema)
+        input.*::Data.*::ComplexData.@schema = data.schema;
+      input = input.toXMLString();
+      return input;
+    },
+    /**
+     * Method: buildInput.reference
+     * Given an E4XElement representing the WPS reference input.
+     *
+     * Parameters:
+     * identifier - {String} the input indetifier
+     * data - {Object} A WPS reference input.
+     *
+     * Returns:
+     * {E4XElement} A WPS Input node.
+     */
+    'reference': function(identifier,data) {
+      return '<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Reference xmlns:xlink="'+this.namespaces['xlink']+'" xlink:href="'+data.value.replace('&','&amp;','gi')+'"/></wps:Input>';
+    },
+    /**
+     * Method: buildInput.literal
+     * Given an E4XElement representing the WPS literal data input.
+     *
+     * Parameters:
+     * identifier - {String} the input indetifier
+     * data - {Object} A WPS literal data input.
+     *
+     * Returns:
+     * {E4XElement} The WPS Input node.
+     */
+    'literal': function(identifier,data) {
+      var input = new XML('<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Data><wps:LiteralData>'+data.value+'</wps:LiteralData></wps:Data></wps:Input>');
+      if (data.type)
+        input.*::Data.*::LiteralData.@dataType = data.type;
+      if (data.uom)
+        input.*::Data.*::LiteralData.@uom = data.uom;
+      input = input.toXMLString();
+      return input;
+    }
+  },
+  /**
+   * Method: buildDataInputsNode
+   * Method to build the WPS DataInputs element.
+   *
+   * Parameters:
+   * inputs - {Object}
+   *
+   * Returns:
+   * {E4XElement} The WPS DataInputs node for Execute query.
+   */
+  buildDataInputsNode:function(inputs){
+    var data, builder, inputsArray=[];
+    for (var attr in inputs) {
+      data = inputs[attr];
+      if (data.mimetype || data.type == 'complex')
+        builder = this.buildInput['complex'];
+      else if (data.type == 'reference' || data.type == 'url')
+        builder = this.buildInput['reference'];
+      else
+        builder = this.buildInput['literal'];
+      inputsArray.push(builder.apply(this,[attr,data]));
+    }
+    return '<wps:DataInputs xmlns:wps="'+this.namespaces['wps']+'">'+inputsArray.join('\n')+'</wps:DataInputs>';
+  },
+  CLASS_NAME: "ZOO.Process"
+});
Index: /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-proj4js.js
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-proj4js.js	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-proj4js.js	(revision 221)
@@ -0,0 +1,6054 @@
+/**
+ * Author : René-Luc D'Hont
+ *
+ * Copyright 2010 3liz SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Author:       Mike Adair madairATdmsolutions.ca
+ *               Richard Greenwood rich@greenwoodmap.com
+ * License:      LGPL as per: http://www.gnu.org/copyleft/lesser.html
+ * $Id: Proj.js 2956 2007-07-09 12:17:52Z steven $
+ */
+
+/**
+ * Class: ZOO
+ */
+ZOO = {
+  /**
+   * Constant: SERVICE_ACCEPTED
+   * {Integer} used for
+   */
+  SERVICE_ACCEPTED: 0,
+  /**
+   * Constant: SERVICE_STARTED
+   * {Integer} used for
+   */
+  SERVICE_STARTED: 1,
+  /**
+   * Constant: SERVICE_PAUSED
+   * {Integer} used for
+   */
+  SERVICE_PAUSED: 2,
+  /**
+   * Constant: SERVICE_SUCCEEDED
+   * {Integer} used for
+   */
+  SERVICE_SUCCEEDED: 3,
+  /**
+   * Constant: SERVICE_FAILED
+   * {Integer} used for
+   */
+  SERVICE_FAILED: 4,
+  /** 
+   * Function: removeItem
+   * Remove an object from an array. Iterates through the array
+   *     to find the item, then removes it.
+   *
+   * Parameters:
+   * array - {Array}
+   * item - {Object}
+   * 
+   * Return
+   * {Array} A reference to the array
+   */
+  removeItem: function(array, item) {
+    for(var i = array.length - 1; i >= 0; i--) {
+        if(array[i] == item) {
+            array.splice(i,1);
+        }
+    }
+    return array;
+  },
+  /** 
+   * Function: indexOf
+   * 
+   * Parameters:
+   * array - {Array}
+   * obj - {Object}
+   * 
+   * Returns:
+   * {Integer} The index at, which the first object was found in the array.
+   *           If not found, returns -1.
+   */
+  indexOf: function(array, obj) {
+    for(var i=0, len=array.length; i<len; i++) {
+      if (array[i] == obj)
+        return i;
+    }
+    return -1;   
+  },
+  /**
+   * Function: extend
+   * Copy all properties of a source object to a destination object. Modifies
+   *     the passed in destination object.  Any properties on the source object
+   *     that are set to undefined will not be (re)set on the destination object.
+   *
+   * Parameters:
+   * destination - {Object} The object that will be modified
+   * source - {Object} The object with properties to be set on the destination
+   *
+   * Returns:
+   * {Object} The destination object.
+   */
+  extend: function(destination, source) {
+    destination = destination || {};
+    if(source) {
+      for(var property in source) {
+        var value = source[property];
+        if(value !== undefined)
+          destination[property] = value;
+      }
+    }
+    return destination;
+  },
+  /**
+   * Function: rad
+   * 
+   * Parameters:
+   * x - {Float}
+   * 
+   * Returns:
+   * {Float}
+   */
+  rad: function(x) {return x*Math.PI/180;},
+  /**
+   * Function: distVincenty
+   * Given two objects representing points with geographic coordinates, this
+   *     calculates the distance between those points on the surface of an
+   *     ellipsoid.
+   * 
+   * Parameters:
+   * p1 - {<ZOO.Geometry.Point>} (or any object with both .x, .y properties)
+   * p2 - {<ZOO.Geometry.Point>} (or any object with both .x, .y properties)
+   * 
+   * Returns:
+   * {Float} The distance (in km) between the two input points as measured on an
+   *     ellipsoid.  Note that the input point objects must be in geographic
+   *     coordinates (decimal degrees) and the return distance is in kilometers.
+   */
+  distVincenty: function(p1, p2) {
+    var a = 6378137, b = 6356752.3142,  f = 1/298.257223563;
+    var L = ZOO.rad(p2.x - p1.y);
+    var U1 = Math.atan((1-f) * Math.tan(ZOO.rad(p1.y)));
+    var U2 = Math.atan((1-f) * Math.tan(ZOO.rad(p2.y)));
+    var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
+    var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
+    var lambda = L, lambdaP = 2*Math.PI;
+    var iterLimit = 20;
+    while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
+        var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
+        var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+        (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
+        if (sinSigma==0) {
+            return 0;  // co-incident points
+        }
+        var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
+        var sigma = Math.atan2(sinSigma, cosSigma);
+        var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
+        var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
+        var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
+        var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+        lambdaP = lambda;
+        lambda = L + (1-C) * f * Math.sin(alpha) *
+        (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
+    }
+    if (iterLimit==0) {
+        return NaN;  // formula failed to converge
+    }
+    var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+    var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+    var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+    var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+    var s = b*A*(sigma-deltaSigma);
+    var d = s.toFixed(3)/1000; // round to 1mm precision
+    return d;
+  },
+  /**
+   * Function: Class
+   * Method used to create ZOO classes. Includes support for
+   *     multiple inheritance.
+   */
+  Class: function() {
+    var Class = function() {
+      this.initialize.apply(this, arguments);
+    };
+    var extended = {};
+    var parent;
+    for(var i=0; i<arguments.length; ++i) {
+      if(typeof arguments[i] == "function") {
+        // get the prototype of the superclass
+        parent = arguments[i].prototype;
+      } else {
+        // in this case we're extending with the prototype
+        parent = arguments[i];
+      }
+      ZOO.extend(extended, parent);
+    }
+    Class.prototype = extended;
+
+    return Class;
+  },
+  /**
+   * Function: UpdateStatus
+   * Method used to update the status of the process
+   *
+   * Parameters:
+   * env - {Object} The environment object
+   * value - {Float} the status value between 0 to 100
+   */
+  UpdateStatus: function(env,value) {
+    return ZOOUpdateStatus(env,value);
+  }
+};
+
+/**
+ * Class: ZOO.String
+ * Contains convenience methods for string manipulation
+ */
+ZOO.String = {
+  /**
+   * Function: startsWith
+   * Test whether a string starts with another string. 
+   * 
+   * Parameters:
+   * str - {String} The string to test.
+   * sub - {Sring} The substring to look for.
+   *  
+   * Returns:
+   * {Boolean} The first string starts with the second.
+   */
+  startsWith: function(str, sub) {
+    return (str.indexOf(sub) == 0);
+  },
+  /**
+   * Function: contains
+   * Test whether a string contains another string.
+   * 
+   * Parameters:
+   * str - {String} The string to test.
+   * sub - {String} The substring to look for.
+   * 
+   * Returns:
+   * {Boolean} The first string contains the second.
+   */
+  contains: function(str, sub) {
+    return (str.indexOf(sub) != -1);
+  },
+  /**
+   * Function: trim
+   * Removes leading and trailing whitespace characters from a string.
+   * 
+   * Parameters:
+   * str - {String} The (potentially) space padded string.  This string is not
+   *     modified.
+   * 
+   * Returns:
+   * {String} A trimmed version of the string with all leading and 
+   *     trailing spaces removed.
+   */
+  trim: function(str) {
+    return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+  },
+  /**
+   * Function: camelize
+   * Camel-case a hyphenated string. 
+   *     Ex. "chicken-head" becomes "chickenHead", and
+   *     "-chicken-head" becomes "ChickenHead".
+   *
+   * Parameters:
+   * str - {String} The string to be camelized.  The original is not modified.
+   * 
+   * Returns:
+   * {String} The string, camelized
+   *
+   */
+  camelize: function(str) {
+    var oStringList = str.split('-');
+    var camelizedString = oStringList[0];
+    for (var i=1, len=oStringList.length; i<len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+    return camelizedString;
+  },
+  /**
+   * Property: tokenRegEx
+   * Used to find tokens in a string.
+   * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
+   */
+  tokenRegEx:  /\$\{([\w.]+?)\}/g,
+  /**
+   * Property: numberRegEx
+   * Used to test strings as numbers.
+   */
+  numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
+  /**
+   * Function: isNumeric
+   * Determine whether a string contains only a numeric value.
+   *
+   * Examples:
+   * (code)
+   * ZOO.String.isNumeric("6.02e23") // true
+   * ZOO.String.isNumeric("12 dozen") // false
+   * ZOO.String.isNumeric("4") // true
+   * ZOO.String.isNumeric(" 4 ") // false
+   * (end)
+   *
+   * Returns:
+   * {Boolean} String contains only a number.
+   */
+  isNumeric: function(value) {
+    return ZOO.String.numberRegEx.test(value);
+  },
+  /**
+   * Function: numericIf
+   * Converts a string that appears to be a numeric value into a number.
+   * 
+   * Returns
+   * {Number|String} a Number if the passed value is a number, a String
+   *     otherwise. 
+   */
+  numericIf: function(value) {
+    return ZOO.String.isNumeric(value) ? parseFloat(value) : value;
+  }
+};
+
+/**
+ * Class: ZOO.Request
+ * Contains convenience methods for working with ZOORequest which
+ *     replace XMLHttpRequest. Because of we are not in a browser
+ *     JavaScript environment, ZOO Project provides a method to 
+ *     query servers which is based on curl : ZOORequest.
+ */
+ZOO.Request = {
+  /**
+   * Function: GET
+   * Send an HTTP GET request.
+   *
+   * Parameters:
+   * url - {String} The URL to request.
+   * params - {Object} Params to add to the url
+   * 
+   * Returns:
+   * {String} Request result.
+   */
+  Get: function(url,params) {
+    var paramsArray = [];
+    for (var key in params) {
+      var value = params[key];
+      if ((value != null) && (typeof value != 'function')) {
+        var encodedValue;
+        if (typeof value == 'object' && value.constructor == Array) {
+          /* value is an array; encode items and separate with "," */
+          var encodedItemArray = [];
+          for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
+            encodedItemArray.push(encodeURIComponent(value[itemIndex]));
+          }
+          encodedValue = encodedItemArray.join(",");
+        }
+        else {
+          /* value is a string; simply encode */
+          encodedValue = encodeURIComponent(value);
+        }
+        paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
+      }
+    }
+    var paramString = paramsArray.join("&");
+    if(paramString.length > 0) {
+      var separator = (url.indexOf('?') > -1) ? '&' : '?';
+      url += separator + paramString;
+    }
+    return ZOORequest('GET',url);
+  },
+  /**
+   * Function: POST
+   * Send an HTTP POST request.
+   *
+   * Parameters:
+   * url - {String} The URL to request.
+   * body - {String} The request's body to send.
+   * headers - {Object} A key-value object of headers to push to
+   *     the request's head
+   * 
+   * Returns:
+   * {String} Request result.
+   */
+  Post: function(url,body,headers) {
+    if(!(headers instanceof Array)) {
+      var headersArray = [];
+      for (var name in headers) {
+        headersArray.push(name+': '+headers[name]); 
+      }
+      headers = headersArray;
+    }
+    return ZOORequest('POST',url,body,headers);
+  }
+};
+
+/**
+ * Class: ZOO.Bounds
+ * Instances of this class represent bounding boxes.  Data stored as left,
+ *     bottom, right, top floats. All values are initialized to null,
+ *     however, you should make sure you set them before using the bounds
+ *     for anything.
+ */
+ZOO.Bounds = ZOO.Class({
+  /**
+   * Property: left
+   * {Number} Minimum horizontal coordinate.
+   */
+  left: null,
+  /**
+   * Property: bottom
+   * {Number} Minimum vertical coordinate.
+   */
+  bottom: null,
+  /**
+   * Property: right
+   * {Number} Maximum horizontal coordinate.
+   */
+  right: null,
+  /**
+   * Property: top
+   * {Number} Maximum vertical coordinate.
+   */
+  top: null,
+  /**
+   * Constructor: ZOO.Bounds
+   * Construct a new bounds object.
+   *
+   * Parameters:
+   * left - {Number} The left bounds of the box.  Note that for width
+   *        calculations, this is assumed to be less than the right value.
+   * bottom - {Number} The bottom bounds of the box.  Note that for height
+   *          calculations, this is assumed to be more than the top value.
+   * right - {Number} The right bounds.
+   * top - {Number} The top bounds.
+   */
+  initialize: function(left, bottom, right, top) {
+    if (left != null)
+      this.left = parseFloat(left);
+    if (bottom != null)
+      this.bottom = parseFloat(bottom);
+    if (right != null)
+      this.right = parseFloat(right);
+    if (top != null)
+      this.top = parseFloat(top);
+  },
+  /**
+   * Method: clone
+   * Create a cloned instance of this bounds.
+   *
+   * Returns:
+   * {<ZOO.Bounds>} A fresh copy of the bounds
+   */
+  clone:function() {
+    return new ZOO.Bounds(this.left, this.bottom, 
+                          this.right, this.top);
+  },
+  /**
+   * Method: equals
+   * Test a two bounds for equivalence.
+   *
+   * Parameters:
+   * bounds - {<ZOO.Bounds>}
+   *
+   * Returns:
+   * {Boolean} The passed-in bounds object has the same left,
+   *           right, top, bottom components as this.  Note that if bounds 
+   *           passed in is null, returns false.
+   */
+  equals:function(bounds) {
+    var equals = false;
+    if (bounds != null)
+        equals = ((this.left == bounds.left) && 
+                  (this.right == bounds.right) &&
+                  (this.top == bounds.top) && 
+                  (this.bottom == bounds.bottom));
+    return equals;
+  },
+  /** 
+   * Method: toString
+   * 
+   * Returns:
+   * {String} String representation of bounds object. 
+   *          (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
+   */
+  toString:function() {
+    return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
+              + " right-top=(" + this.right + "," + this.top + ")" );
+  },
+  /**
+   * APIMethod: toArray
+   *
+   * Returns:
+   * {Array} array of left, bottom, right, top
+   */
+  toArray: function() {
+    return [this.left, this.bottom, this.right, this.top];
+  },
+  /** 
+   * Method: toBBOX
+   * 
+   * Parameters:
+   * decimal - {Integer} How many significant digits in the bbox coords?
+   *                     Default is 6
+   * 
+   * Returns:
+   * {String} Simple String representation of bounds object.
+   *          (ex. <i>"5,42,10,45"</i>)
+   */
+  toBBOX:function(decimal) {
+    if (decimal== null)
+      decimal = 6; 
+    var mult = Math.pow(10, decimal);
+    var bbox = Math.round(this.left * mult) / mult + "," + 
+               Math.round(this.bottom * mult) / mult + "," + 
+               Math.round(this.right * mult) / mult + "," + 
+               Math.round(this.top * mult) / mult;
+    return bbox;
+  },
+  /**
+   * Method: toGeometry
+   * Create a new polygon geometry based on this bounds.
+   *
+   * Returns:
+   * {<ZOO.Geometry.Polygon>} A new polygon with the coordinates
+   *     of this bounds.
+   */
+  toGeometry: function() {
+    return new ZOO.Geometry.Polygon([
+      new ZOO.Geometry.LinearRing([
+        new ZOO.Geometry.Point(this.left, this.bottom),
+        new ZOO.Geometry.Point(this.right, this.bottom),
+        new ZOO.Geometry.Point(this.right, this.top),
+        new ZOO.Geometry.Point(this.left, this.top)
+      ])
+    ]);
+  },
+  /**
+   * Method: getWidth
+   * 
+   * Returns:
+   * {Float} The width of the bounds
+   */
+  getWidth:function() {
+    return (this.right - this.left);
+  },
+  /**
+   * Method: getHeight
+   * 
+   * Returns:
+   * {Float} The height of the bounds (top minus bottom).
+   */
+  getHeight:function() {
+    return (this.top - this.bottom);
+  },
+  /**
+   * Method: add
+   * 
+   * Parameters:
+   * x - {Float}
+   * y - {Float}
+   * 
+   * Returns:
+   * {<ZOO.Bounds>} A new bounds whose coordinates are the same as
+   *     this, but shifted by the passed-in x and y values.
+   */
+  add:function(x, y) {
+    if ( (x == null) || (y == null) )
+      return null;
+    return new ZOO.Bounds(this.left + x, this.bottom + y,
+                                 this.right + x, this.top + y);
+  },
+  /**
+   * Method: extend
+   * Extend the bounds to include the point, lonlat, or bounds specified.
+   *     Note, this function assumes that left < right and bottom < top.
+   * 
+   * Parameters: 
+   * object - {Object} Can be Point, or Bounds
+   */
+  extend:function(object) {
+    var bounds = null;
+    if (object) {
+      // clear cached center location
+      switch(object.CLASS_NAME) {
+        case "ZOO.Geometry.Point":
+          bounds = new ZOO.Bounds(object.x, object.y,
+                                         object.x, object.y);
+          break;
+        case "ZOO.Bounds":    
+          bounds = object;
+          break;
+      }
+      if (bounds) {
+        if ( (this.left == null) || (bounds.left < this.left))
+          this.left = bounds.left;
+        if ( (this.bottom == null) || (bounds.bottom < this.bottom) )
+          this.bottom = bounds.bottom;
+        if ( (this.right == null) || (bounds.right > this.right) )
+          this.right = bounds.right;
+        if ( (this.top == null) || (bounds.top > this.top) )
+          this.top = bounds.top;
+      }
+    }
+  },
+  /**
+   * APIMethod: contains
+   * 
+   * Parameters:
+   * x - {Float}
+   * y - {Float}
+   * inclusive - {Boolean} Whether or not to include the border.
+   *     Default is true.
+   *
+   * Returns:
+   * {Boolean} Whether or not the passed-in coordinates are within this
+   *     bounds.
+   */
+  contains:function(x, y, inclusive) {
+     //set default
+     if (inclusive == null)
+       inclusive = true;
+     if (x == null || y == null)
+       return false;
+     x = parseFloat(x);
+     y = parseFloat(y);
+
+     var contains = false;
+     if (inclusive)
+       contains = ((x >= this.left) && (x <= this.right) && 
+                   (y >= this.bottom) && (y <= this.top));
+     else
+       contains = ((x > this.left) && (x < this.right) && 
+                   (y > this.bottom) && (y < this.top));
+     return contains;
+  },
+  /**
+   * Method: intersectsBounds
+   * Determine whether the target bounds intersects this bounds.  Bounds are
+   *     considered intersecting if any of their edges intersect or if one
+   *     bounds contains the other.
+   * 
+   * Parameters:
+   * bounds - {<ZOO.Bounds>} The target bounds.
+   * inclusive - {Boolean} Treat coincident borders as intersecting.  Default
+   *     is true.  If false, bounds that do not overlap but only touch at the
+   *     border will not be considered as intersecting.
+   *
+   * Returns:
+   * {Boolean} The passed-in bounds object intersects this bounds.
+   */
+  intersectsBounds:function(bounds, inclusive) {
+    if (inclusive == null)
+      inclusive = true;
+    var intersects = false;
+    var mightTouch = (
+        this.left == bounds.right ||
+        this.right == bounds.left ||
+        this.top == bounds.bottom ||
+        this.bottom == bounds.top
+    );
+    if (inclusive || !mightTouch) {
+      var inBottom = (
+          ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) ||
+          ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top))
+          );
+      var inTop = (
+          ((bounds.top >= this.bottom) && (bounds.top <= this.top)) ||
+          ((this.top > bounds.bottom) && (this.top < bounds.top))
+          );
+      var inLeft = (
+          ((bounds.left >= this.left) && (bounds.left <= this.right)) ||
+          ((this.left >= bounds.left) && (this.left <= bounds.right))
+          );
+      var inRight = (
+          ((bounds.right >= this.left) && (bounds.right <= this.right)) ||
+          ((this.right >= bounds.left) && (this.right <= bounds.right))
+          );
+      intersects = ((inBottom || inTop) && (inLeft || inRight));
+    }
+    return intersects;
+  },
+  /**
+   * Method: containsBounds
+   * Determine whether the target bounds is contained within this bounds.
+   * 
+   * bounds - {<ZOO.Bounds>} The target bounds.
+   * partial - {Boolean} If any of the target corners is within this bounds
+   *     consider the bounds contained.  Default is false.  If true, the
+   *     entire target bounds must be contained within this bounds.
+   * inclusive - {Boolean} Treat shared edges as contained.  Default is
+   *     true.
+   *
+   * Returns:
+   * {Boolean} The passed-in bounds object is contained within this bounds. 
+   */
+  containsBounds:function(bounds, partial, inclusive) {
+    if (partial == null)
+      partial = false;
+    if (inclusive == null)
+      inclusive = true;
+    var bottomLeft  = this.contains(bounds.left, bounds.bottom, inclusive);
+    var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
+    var topLeft  = this.contains(bounds.left, bounds.top, inclusive);
+    var topRight = this.contains(bounds.right, bounds.top, inclusive);
+    return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
+                     : (bottomLeft && bottomRight && topLeft && topRight);
+  },
+  CLASS_NAME: 'ZOO.Bounds'
+});
+
+/**
+ * Class: ZOO.Projection
+ * Class for coordinate transforms between coordinate systems.
+ *     Depends on the zoo-proj4js library. zoo-proj4js library 
+ *     is loaded by the ZOO Kernel with zoo-api.
+ */
+ZOO.Projection = ZOO.Class({
+  /**
+   * Property: proj
+   * {Object} Proj4js.Proj instance.
+   */
+  proj: null,
+  /**
+   * Property: projCode
+   * {String}
+   */
+  projCode: null,
+  /**
+   * Constructor: ZOO.Projection
+   * This class offers several methods for interacting with a wrapped 
+   *     zoo-pro4js projection object. 
+   *
+   * Parameters:
+   * projCode - {String} A string identifying the Well Known Identifier for
+   *    the projection.
+   * options - {Object} An optional object to set additional properties.
+   *
+   * Returns:
+   * {<ZOO.Projection>} A projection object.
+   */
+  initialize: function(projCode, options) {
+    ZOO.extend(this, options);
+    this.projCode = projCode;
+    if (Proj4js) {
+      this.proj = new Proj4js.Proj(projCode);
+    }
+  },
+  /**
+   * Method: getCode
+   * Get the string SRS code.
+   *
+   * Returns:
+   * {String} The SRS code.
+   */
+  getCode: function() {
+    return this.proj ? this.proj.srsCode : this.projCode;
+  },
+  /**
+   * Method: getUnits
+   * Get the units string for the projection -- returns null if 
+   *     zoo-proj4js is not available.
+   *
+   * Returns:
+   * {String} The units abbreviation.
+   */
+  getUnits: function() {
+    return this.proj ? this.proj.units : null;
+  },
+  /**
+   * Method: toString
+   * Convert projection to string (getCode wrapper).
+   *
+   * Returns:
+   * {String} The projection code.
+   */
+  toString: function() {
+    return this.getCode();
+  },
+  /**
+   * Method: equals
+   * Test equality of two projection instances.  Determines equality based
+   *     soley on the projection code.
+   *
+   * Returns:
+   * {Boolean} The two projections are equivalent.
+   */
+  equals: function(projection) {
+    if (projection && projection.getCode)
+      return this.getCode() == projection.getCode();
+    else
+      return false;
+  },
+  /* Method: destroy
+   * Destroy projection object.
+   */
+  destroy: function() {
+    this.proj = null;
+    this.projCode = null;
+  },
+  CLASS_NAME: 'ZOO.Projection'
+});
+/**
+ * Method: transform
+ * Transform a point coordinate from one projection to another.  Note that
+ *     the input point is transformed in place.
+ * 
+ * Parameters:
+ * point - {{ZOO.Geometry.Point> | Object} An object with x and y
+ *     properties representing coordinates in those dimensions.
+ * sourceProj - {ZOO.Projection} Source map coordinate system
+ * destProj - {ZOO.Projection} Destination map coordinate system
+ *
+ * Returns:
+ * point - {object} A transformed coordinate.  The original point is modified.
+ */
+ZOO.Projection.transform = function(point, source, dest) {
+    if (source.proj && dest.proj)
+        point = Proj4js.transform(source.proj, dest.proj, point);
+    return point;
+};
+
+/**
+ * Class: ZOO.Format
+ * Base class for format reading/writing a variety of formats. Subclasses
+ *     of ZOO.Format are expected to have read and write methods.
+ */
+ZOO.Format = ZOO.Class({
+  /**
+   * Property: options
+   * {Object} A reference to options passed to the constructor.
+   */
+  options:null,
+  /**
+   * Property: externalProjection
+   * {<ZOO.Projection>} When passed a externalProjection and
+   *     internalProjection, the format will reproject the geometries it
+   *     reads or writes. The externalProjection is the projection used by
+   *     the content which is passed into read or which comes out of write.
+   *     In order to reproject, a projection transformation function for the
+   *     specified projections must be available. This support is provided 
+   *     via zoo-proj4js.
+   */
+  externalProjection: null,
+  /**
+   * Property: internalProjection
+   * {<ZOO.Projection>} When passed a externalProjection and
+   *     internalProjection, the format will reproject the geometries it
+   *     reads or writes. The internalProjection is the projection used by
+   *     the geometries which are returned by read or which are passed into
+   *     write.  In order to reproject, a projection transformation function
+   *     for the specified projections must be available. This support is 
+   *     provided via zoo-proj4js.
+   */
+  internalProjection: null,
+  /**
+   * Property: data
+   * {Object} When <keepData> is true, this is the parsed string sent to
+   *     <read>.
+   */
+  data: null,
+  /**
+   * Property: keepData
+   * {Object} Maintain a reference (<data>) to the most recently read data.
+   *     Default is false.
+   */
+  keepData: false,
+  /**
+   * Constructor: ZOO.Format
+   * Instances of this class are not useful.  See one of the subclasses.
+   *
+   * Parameters:
+   * options - {Object} An optional object with properties to set on the
+   *           format
+   *
+   * Valid options:
+   * keepData - {Boolean} If true, upon <read>, the data property will be
+   *     set to the parsed object (e.g. the json or xml object).
+   *
+   * Returns:
+   * An instance of ZOO.Format
+   */
+  initialize: function(options) {
+    ZOO.extend(this, options);
+    this.options = options;
+  },
+  /**
+   * Method: destroy
+   * Clean up.
+   */
+  destroy: function() {
+  },
+  /**
+   * Method: read
+   * Read data from a string, and return an object whose type depends on the
+   * subclass. 
+   * 
+   * Parameters:
+   * data - {string} Data to read/parse.
+   *
+   * Returns:
+   * Depends on the subclass
+   */
+  read: function(data) {
+  },
+  /**
+   * Method: write
+   * Accept an object, and return a string. 
+   *
+   * Parameters:
+   * object - {Object} Object to be serialized
+   *
+   * Returns:
+   * {String} A string representation of the object.
+   */
+  write: function(data) {
+  },
+  CLASS_NAME: 'ZOO.Format'
+});
+/**
+ * Class: ZOO.Format.WKT
+ * Class for reading and writing Well-Known Text. Create a new instance
+ * with the <ZOO.Format.WKT> constructor.
+ * 
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.WKT = ZOO.Class(ZOO.Format, {
+  /**
+   * Constructor: ZOO.Format.WKT
+   * Create a new parser for WKT
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *           this instance
+   *
+   * Returns:
+   * {<ZOO.Format.WKT>} A new WKT parser.
+   */
+  initialize: function(options) {
+    this.regExes = {
+      'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
+      'spaces': /\s+/,
+      'parenComma': /\)\s*,\s*\(/,
+      'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/,  // can't use {2} here
+      'trimParens': /^\s*\(?(.*?)\)?\s*$/
+    };
+    ZOO.Format.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * Method: read
+   * Deserialize a WKT string and return a vector feature or an
+   *     array of vector features.  Supports WKT for POINT, 
+   *     MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON, 
+   *     MULTIPOLYGON, and GEOMETRYCOLLECTION.
+   *
+   * Parameters:
+   * wkt - {String} A WKT string
+   *
+   * Returns:
+   * {<ZOO.Feature.Vector>|Array} A feature or array of features for
+   *     GEOMETRYCOLLECTION WKT.
+   */
+  read: function(wkt) {
+    var features, type, str;
+    var matches = this.regExes.typeStr.exec(wkt);
+    if(matches) {
+      type = matches[1].toLowerCase();
+      str = matches[2];
+      if(this.parse[type]) {
+        features = this.parse[type].apply(this, [str]);
+      }
+      if (this.internalProjection && this.externalProjection) {
+        if (features && 
+            features.CLASS_NAME == "ZOO.Feature") {
+          features.geometry.transform(this.externalProjection,
+                                      this.internalProjection);
+        } else if (features &&
+            type != "geometrycollection" &&
+            typeof features == "object") {
+          for (var i=0, len=features.length; i<len; i++) {
+            var component = features[i];
+            component.geometry.transform(this.externalProjection,
+                                         this.internalProjection);
+          }
+        }
+      }
+    }    
+    return features;
+  },
+  /**
+   * Method: write
+   * Serialize a feature or array of features into a WKT string.
+   *
+   * Parameters:
+   * features - {<ZOO.Feature.Vector>|Array} A feature or array of
+   *            features
+   *
+   * Returns:
+   * {String} The WKT string representation of the input geometries
+   */
+  write: function(features) {
+    var collection, geometry, type, data, isCollection;
+    if(features.constructor == Array) {
+      collection = features;
+      isCollection = true;
+    } else {
+      collection = [features];
+      isCollection = false;
+    }
+    var pieces = [];
+    if(isCollection)
+      pieces.push('GEOMETRYCOLLECTION(');
+    for(var i=0, len=collection.length; i<len; ++i) {
+      if(isCollection && i>0)
+        pieces.push(',');
+      geometry = collection[i].geometry;
+      type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
+      if(!this.extract[type])
+        return null;
+      if (this.internalProjection && this.externalProjection) {
+        geometry = geometry.clone();
+        geometry.transform(this.internalProjection, 
+                          this.externalProjection);
+      }                       
+      data = this.extract[type].apply(this, [geometry]);
+      pieces.push(type.toUpperCase() + '(' + data + ')');
+    }
+    if(isCollection)
+      pieces.push(')');
+    return pieces.join('');
+  },
+  /**
+   * Property: extract
+   * Object with properties corresponding to the geometry types.
+   * Property values are functions that do the actual data extraction.
+   */
+  extract: {
+    /**
+     * Return a space delimited string of point coordinates.
+     * @param {<ZOO.Geometry.Point>} point
+     * @returns {String} A string of coordinates representing the point
+     */
+    'point': function(point) {
+      return point.x + ' ' + point.y;
+    },
+    /**
+     * Return a comma delimited string of point coordinates from a multipoint.
+     * @param {<ZOO.Geometry.MultiPoint>} multipoint
+     * @returns {String} A string of point coordinate strings representing
+     *                  the multipoint
+     */
+    'multipoint': function(multipoint) {
+      var array = [];
+      for(var i=0, len=multipoint.components.length; i<len; ++i) {
+        array.push(this.extract.point.apply(this, [multipoint.components[i]]));
+      }
+      return array.join(',');
+    },
+    /**
+     * Return a comma delimited string of point coordinates from a line.
+     * @param {<ZOO.Geometry.LineString>} linestring
+     * @returns {String} A string of point coordinate strings representing
+     *                  the linestring
+     */
+    'linestring': function(linestring) {
+      var array = [];
+      for(var i=0, len=linestring.components.length; i<len; ++i) {
+        array.push(this.extract.point.apply(this, [linestring.components[i]]));
+      }
+      return array.join(',');
+    },
+    /**
+     * Return a comma delimited string of linestring strings from a multilinestring.
+     * @param {<ZOO.Geometry.MultiLineString>} multilinestring
+     * @returns {String} A string of of linestring strings representing
+     *                  the multilinestring
+     */
+    'multilinestring': function(multilinestring) {
+      var array = [];
+      for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+        array.push('(' +
+            this.extract.linestring.apply(this, [multilinestring.components[i]]) +
+            ')');
+      }
+      return array.join(',');
+    },
+    /**
+     * Return a comma delimited string of linear ring arrays from a polygon.
+     * @param {<ZOO.Geometry.Polygon>} polygon
+     * @returns {String} An array of linear ring arrays representing the polygon
+     */
+    'polygon': function(polygon) {
+      var array = [];
+      for(var i=0, len=polygon.components.length; i<len; ++i) {
+        array.push('(' +
+            this.extract.linestring.apply(this, [polygon.components[i]]) +
+            ')');
+      }
+      return array.join(',');
+    },
+    /**
+     * Return an array of polygon arrays from a multipolygon.
+     * @param {<ZOO.Geometry.MultiPolygon>} multipolygon
+     * @returns {Array} An array of polygon arrays representing
+     *                  the multipolygon
+     */
+    'multipolygon': function(multipolygon) {
+      var array = [];
+      for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+        array.push('(' +
+            this.extract.polygon.apply(this, [multipolygon.components[i]]) +
+            ')');
+      }
+      return array.join(',');
+    }
+  },
+  /**
+   * Property: parse
+   * Object with properties corresponding to the geometry types.
+   *     Property values are functions that do the actual parsing.
+   */
+  parse: {
+    /**
+     * Method: parse.point
+     * Return point feature given a point WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the point
+     * Returns:
+     * {<ZOO.Feature>} A point feature
+     */
+    'point': function(str) {
+       var coords = ZOO.String.trim(str).split(this.regExes.spaces);
+            return new ZOO.Feature(
+                new ZOO.Geometry.Point(coords[0], coords[1])
+            );
+    },
+    /**
+     * Method: parse.multipoint
+     * Return a multipoint feature given a multipoint WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the multipoint
+     *
+     * Returns:
+     * {<ZOO.Feature>} A multipoint feature
+     */
+    'multipoint': function(str) {
+       var points = ZOO.String.trim(str).split(',');
+       var components = [];
+       for(var i=0, len=points.length; i<len; ++i) {
+         components.push(this.parse.point.apply(this, [points[i]]).geometry);
+       }
+       return new ZOO.Feature(
+           new ZOO.Geometry.MultiPoint(components)
+           );
+    },
+    /**
+     * Method: parse.linestring
+     * Return a linestring feature given a linestring WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the linestring
+     *
+     * Returns:
+     * {<ZOO.Feature>} A linestring feature
+     */
+    'linestring': function(str) {
+      var points = ZOO.String.trim(str).split(',');
+      var components = [];
+      for(var i=0, len=points.length; i<len; ++i) {
+        components.push(this.parse.point.apply(this, [points[i]]).geometry);
+      }
+      return new ZOO.Feature(
+          new ZOO.Geometry.LineString(components)
+          );
+    },
+    /**
+     * Method: parse.multilinestring
+     * Return a multilinestring feature given a multilinestring WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the multilinestring
+     *
+     * Returns:
+     * {<ZOO.Feature>} A multilinestring feature
+     */
+    'multilinestring': function(str) {
+      var line;
+      var lines = ZOO.String.trim(str).split(this.regExes.parenComma);
+      var components = [];
+      for(var i=0, len=lines.length; i<len; ++i) {
+        line = lines[i].replace(this.regExes.trimParens, '$1');
+        components.push(this.parse.linestring.apply(this, [line]).geometry);
+      }
+      return new ZOO.Feature(
+          new ZOO.Geometry.MultiLineString(components)
+          );
+    },
+    /**
+     * Method: parse.polygon
+     * Return a polygon feature given a polygon WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the polygon
+     *
+     * Returns:
+     * {<ZOO.Feature>} A polygon feature
+     */
+    'polygon': function(str) {
+       var ring, linestring, linearring;
+       var rings = ZOO.String.trim(str).split(this.regExes.parenComma);
+       var components = [];
+       for(var i=0, len=rings.length; i<len; ++i) {
+         ring = rings[i].replace(this.regExes.trimParens, '$1');
+         linestring = this.parse.linestring.apply(this, [ring]).geometry;
+         linearring = new ZOO.Geometry.LinearRing(linestring.components);
+         components.push(linearring);
+       }
+       return new ZOO.Feature(
+           new ZOO.Geometry.Polygon(components)
+           );
+    },
+    /**
+     * Method: parse.multipolygon
+     * Return a multipolygon feature given a multipolygon WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the multipolygon
+     *
+     * Returns:
+     * {<ZOO.Feature>} A multipolygon feature
+     */
+    'multipolygon': function(str) {
+      var polygon;
+      var polygons = ZOO.String.trim(str).split(this.regExes.doubleParenComma);
+      var components = [];
+      for(var i=0, len=polygons.length; i<len; ++i) {
+        polygon = polygons[i].replace(this.regExes.trimParens, '$1');
+        components.push(this.parse.polygon.apply(this, [polygon]).geometry);
+      }
+      return new ZOO.Feature(
+          new ZOO.Geometry.MultiPolygon(components)
+          );
+    },
+    /**
+     * Method: parse.geometrycollection
+     * Return an array of features given a geometrycollection WKT fragment.
+     *
+     * Parameters:
+     * str - {String} A WKT fragment representing the geometrycollection
+     *
+     * Returns:
+     * {Array} An array of ZOO.Feature
+     */
+    'geometrycollection': function(str) {
+      // separate components of the collection with |
+      str = str.replace(/,\s*([A-Za-z])/g, '|$1');
+      var wktArray = ZOO.String.trim(str).split('|');
+      var components = [];
+      for(var i=0, len=wktArray.length; i<len; ++i) {
+        components.push(ZOO.Format.WKT.prototype.read.apply(this,[wktArray[i]]));
+      }
+      return components;
+    }
+  },
+  CLASS_NAME: 'ZOO.Format.WKT'
+});
+/**
+ * Class: ZOO.Format.JSON
+ * A parser to read/write JSON safely. Create a new instance with the
+ *     <ZOO.Format.JSON> constructor.
+ *
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.JSON = ZOO.Class(ZOO.Format, {
+  /**
+   * Property: indent
+   * {String} For "pretty" printing, the indent string will be used once for
+   *     each indentation level.
+   */
+  indent: "    ",
+  /**
+   * Property: space
+   * {String} For "pretty" printing, the space string will be used after
+   *     the ":" separating a name/value pair.
+   */
+  space: " ",
+  /**
+   * Property: newline
+   * {String} For "pretty" printing, the newline string will be used at the
+   *     end of each name/value pair or array item.
+   */
+  newline: "\n",
+  /**
+   * Property: level
+   * {Integer} For "pretty" printing, this is incremented/decremented during
+   *     serialization.
+   */
+  level: 0,
+  /**
+   * Property: pretty
+   * {Boolean} Serialize with extra whitespace for structure.  This is set
+   *     by the <write> method.
+   */
+  pretty: false,
+  /**
+   * Constructor: ZOO.Format.JSON
+   * Create a new parser for JSON.
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *     this instance.
+   */
+  initialize: function(options) {
+    ZOO.Format.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * Method: read
+   * Deserialize a json string.
+   *
+   * Parameters:
+   * json - {String} A JSON string
+   * filter - {Function} A function which will be called for every key and
+   *     value at every level of the final result. Each value will be
+   *     replaced by the result of the filter function. This can be used to
+   *     reform generic objects into instances of classes, or to transform
+   *     date strings into Date objects.
+   *     
+   * Returns:
+   * {Object} An object, array, string, or number .
+   */
+  read: function(json, filter) {
+    /**
+     * Parsing happens in three stages. In the first stage, we run the text
+     *     against a regular expression which looks for non-JSON
+     *     characters. We are especially concerned with '()' and 'new'
+     *     because they can cause invocation, and '=' because it can cause
+     *     mutation. But just to be safe, we will reject all unexpected
+     *     characters.
+     */
+    try {
+      if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
+                          replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+                          replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+        /**
+         * In the second stage we use the eval function to compile the
+         *     text into a JavaScript structure. The '{' operator is
+         *     subject to a syntactic ambiguity in JavaScript - it can
+         *     begin a block or an object literal. We wrap the text in
+         *     parens to eliminate the ambiguity.
+         */
+        var object = eval('(' + json + ')');
+        /**
+         * In the optional third stage, we recursively walk the new
+         *     structure, passing each name/value pair to a filter
+         *     function for possible transformation.
+         */
+        if(typeof filter === 'function') {
+          function walk(k, v) {
+            if(v && typeof v === 'object') {
+              for(var i in v) {
+                if(v.hasOwnProperty(i)) {
+                  v[i] = walk(i, v[i]);
+                }
+              }
+            }
+            return filter(k, v);
+          }
+          object = walk('', object);
+        }
+        if(this.keepData) {
+          this.data = object;
+        }
+        return object;
+      }
+    } catch(e) {
+      // Fall through if the regexp test fails.
+    }
+    return null;
+  },
+  /**
+   * Method: write
+   * Serialize an object into a JSON string.
+   *
+   * Parameters:
+   * value - {String} The object, array, string, number, boolean or date
+   *     to be serialized.
+   * pretty - {Boolean} Structure the output with newlines and indentation.
+   *     Default is false.
+   *
+   * Returns:
+   * {String} The JSON string representation of the input value.
+   */
+  write: function(value, pretty) {
+    this.pretty = !!pretty;
+    var json = null;
+    var type = typeof value;
+    if(this.serialize[type]) {
+      try {
+        json = this.serialize[type].apply(this, [value]);
+      } catch(err) {
+        //OpenLayers.Console.error("Trouble serializing: " + err);
+      }
+    }
+    return json;
+  },
+  /**
+   * Method: writeIndent
+   * Output an indentation string depending on the indentation level.
+   *
+   * Returns:
+   * {String} An appropriate indentation string.
+   */
+  writeIndent: function() {
+    var pieces = [];
+    if(this.pretty) {
+      for(var i=0; i<this.level; ++i) {
+        pieces.push(this.indent);
+      }
+    }
+    return pieces.join('');
+  },
+  /**
+   * Method: writeNewline
+   * Output a string representing a newline if in pretty printing mode.
+   *
+   * Returns:
+   * {String} A string representing a new line.
+   */
+  writeNewline: function() {
+    return (this.pretty) ? this.newline : '';
+  },
+  /**
+   * Method: writeSpace
+   * Output a string representing a space if in pretty printing mode.
+   *
+   * Returns:
+   * {String} A space.
+   */
+  writeSpace: function() {
+    return (this.pretty) ? this.space : '';
+  },
+  /**
+   * Property: serialize
+   * Object with properties corresponding to the serializable data types.
+   *     Property values are functions that do the actual serializing.
+   */
+  serialize: {
+    /**
+     * Method: serialize.object
+     * Transform an object into a JSON string.
+     *
+     * Parameters:
+     * object - {Object} The object to be serialized.
+     * 
+     * Returns:
+     * {String} A JSON string representing the object.
+     */
+    'object': function(object) {
+       // three special objects that we want to treat differently
+       if(object == null)
+         return "null";
+       if(object.constructor == Date)
+         return this.serialize.date.apply(this, [object]);
+       if(object.constructor == Array)
+         return this.serialize.array.apply(this, [object]);
+       var pieces = ['{'];
+       this.level += 1;
+       var key, keyJSON, valueJSON;
+
+       var addComma = false;
+       for(key in object) {
+         if(object.hasOwnProperty(key)) {
+           // recursive calls need to allow for sub-classing
+           keyJSON = ZOO.Format.JSON.prototype.write.apply(this,
+                                                           [key, this.pretty]);
+           valueJSON = ZOO.Format.JSON.prototype.write.apply(this,
+                                                             [object[key], this.pretty]);
+           if(keyJSON != null && valueJSON != null) {
+             if(addComma)
+               pieces.push(',');
+             pieces.push(this.writeNewline(), this.writeIndent(),
+                         keyJSON, ':', this.writeSpace(), valueJSON);
+             addComma = true;
+           }
+         }
+       }
+       this.level -= 1;
+       pieces.push(this.writeNewline(), this.writeIndent(), '}');
+       return pieces.join('');
+    },
+    /**
+     * Method: serialize.array
+     * Transform an array into a JSON string.
+     *
+     * Parameters:
+     * array - {Array} The array to be serialized
+     * 
+     * Returns:
+     * {String} A JSON string representing the array.
+     */
+    'array': function(array) {
+      var json;
+      var pieces = ['['];
+      this.level += 1;
+      for(var i=0, len=array.length; i<len; ++i) {
+        // recursive calls need to allow for sub-classing
+        json = ZOO.Format.JSON.prototype.write.apply(this,
+                                                     [array[i], this.pretty]);
+        if(json != null) {
+          if(i > 0)
+            pieces.push(',');
+          pieces.push(this.writeNewline(), this.writeIndent(), json);
+        }
+      }
+      this.level -= 1;    
+      pieces.push(this.writeNewline(), this.writeIndent(), ']');
+      return pieces.join('');
+    },
+    /**
+     * Method: serialize.string
+     * Transform a string into a JSON string.
+     *
+     * Parameters:
+     * string - {String} The string to be serialized
+     * 
+     * Returns:
+     * {String} A JSON string representing the string.
+     */
+    'string': function(string) {
+      var m = {
+                '\b': '\\b',
+                '\t': '\\t',
+                '\n': '\\n',
+                '\f': '\\f',
+                '\r': '\\r',
+                '"' : '\\"',
+                '\\': '\\\\'
+      };
+      if(/["\\\x00-\x1f]/.test(string)) {
+        return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+            var c = m[b];
+            if(c)
+              return c;
+            c = b.charCodeAt();
+            return '\\u00' +
+            Math.floor(c / 16).toString(16) +
+            (c % 16).toString(16);
+        }) + '"';
+      }
+      return '"' + string + '"';
+    },
+    /**
+     * Method: serialize.number
+     * Transform a number into a JSON string.
+     *
+     * Parameters:
+     * number - {Number} The number to be serialized.
+     *
+     * Returns:
+     * {String} A JSON string representing the number.
+     */
+    'number': function(number) {
+      return isFinite(number) ? String(number) : "null";
+    },
+    /**
+     * Method: serialize.boolean
+     * Transform a boolean into a JSON string.
+     *
+     * Parameters:
+     * bool - {Boolean} The boolean to be serialized.
+     * 
+     * Returns:
+     * {String} A JSON string representing the boolean.
+     */
+    'boolean': function(bool) {
+      return String(bool);
+    },
+    /**
+     * Method: serialize.date
+     * Transform a date into a JSON string.
+     *
+     * Parameters:
+     * date - {Date} The date to be serialized.
+     * 
+     * Returns:
+     * {String} A JSON string representing the date.
+     */
+    'date': function(date) {    
+      function format(number) {
+        // Format integers to have at least two digits.
+        return (number < 10) ? '0' + number : number;
+      }
+      return '"' + date.getFullYear() + '-' +
+        format(date.getMonth() + 1) + '-' +
+        format(date.getDate()) + 'T' +
+        format(date.getHours()) + ':' +
+        format(date.getMinutes()) + ':' +
+        format(date.getSeconds()) + '"';
+    }
+  },
+  CLASS_NAME: 'ZOO.Format.JSON'
+});
+/**
+ * Class: ZOO.Format.GeoJSON
+ * Read and write GeoJSON. Create a new parser with the
+ *     <ZOO.Format.GeoJSON> constructor.
+ *
+ * Inherits from:
+ *  - <ZOO.Format.JSON>
+ */
+ZOO.Format.GeoJSON = ZOO.Class(ZOO.Format.JSON, {
+  /**
+   * Constructor: ZOO.Format.GeoJSON
+   * Create a new parser for GeoJSON.
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *     this instance.
+   */
+  initialize: function(options) {
+    ZOO.Format.JSON.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * Method: read
+   * Deserialize a GeoJSON string.
+   *
+   * Parameters:
+   * json - {String} A GeoJSON string
+   * type - {String} Optional string that determines the structure of
+   *     the output.  Supported values are "Geometry", "Feature", and
+   *     "FeatureCollection".  If absent or null, a default of
+   *     "FeatureCollection" is assumed.
+   * filter - {Function} A function which will be called for every key and
+   *     value at every level of the final result. Each value will be
+   *     replaced by the result of the filter function. This can be used to
+   *     reform generic objects into instances of classes, or to transform
+   *     date strings into Date objects.
+   *
+   * Returns: 
+   * {Object} The return depends on the value of the type argument. If type
+   *     is "FeatureCollection" (the default), the return will be an array
+   *     of <ZOO.Feature>. If type is "Geometry", the input json
+   *     must represent a single geometry, and the return will be an
+   *     <ZOO.Geometry>.  If type is "Feature", the input json must
+   *     represent a single feature, and the return will be an
+   *     <ZOO.Feature>.
+   */
+  read: function(json, type, filter) {
+    type = (type) ? type : "FeatureCollection";
+    var results = null;
+    var obj = null;
+    if (typeof json == "string")
+      obj = ZOO.Format.JSON.prototype.read.apply(this,[json, filter]);
+    else
+      obj = json;
+    if(!obj) {
+      //ZOO.Console.error("Bad JSON: " + json);
+    } else if(typeof(obj.type) != "string") {
+      //ZOO.Console.error("Bad GeoJSON - no type: " + json);
+    } else if(this.isValidType(obj, type)) {
+      switch(type) {
+        case "Geometry":
+          try {
+            results = this.parseGeometry(obj);
+          } catch(err) {
+            //ZOO.Console.error(err);
+          }
+          break;
+        case "Feature":
+          try {
+            results = this.parseFeature(obj);
+            results.type = "Feature";
+          } catch(err) {
+            //ZOO.Console.error(err);
+          }
+          break;
+        case "FeatureCollection":
+          // for type FeatureCollection, we allow input to be any type
+          results = [];
+          switch(obj.type) {
+            case "Feature":
+              try {
+                results.push(this.parseFeature(obj));
+              } catch(err) {
+                results = null;
+                //ZOO.Console.error(err);
+              }
+              break;
+            case "FeatureCollection":
+              for(var i=0, len=obj.features.length; i<len; ++i) {
+                try {
+                  results.push(this.parseFeature(obj.features[i]));
+                } catch(err) {
+                  results = null;
+                  //ZOO.Console.error(err);
+                }
+              }
+              break;
+            default:
+              try {
+                var geom = this.parseGeometry(obj);
+                results.push(new ZOO.Feature(geom));
+              } catch(err) {
+                results = null;
+                //ZOO.Console.error(err);
+              }
+          }
+          break;
+      }
+    }
+    return results;
+  },
+  /**
+   * Method: isValidType
+   * Check if a GeoJSON object is a valid representative of the given type.
+   *
+   * Returns:
+   * {Boolean} The object is valid GeoJSON object of the given type.
+   */
+  isValidType: function(obj, type) {
+    var valid = false;
+    switch(type) {
+      case "Geometry":
+        if(ZOO.indexOf(
+              ["Point", "MultiPoint", "LineString", "MultiLineString",
+              "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
+              obj.type) == -1) {
+          // unsupported geometry type
+          //ZOO.Console.error("Unsupported geometry type: " +obj.type);
+        } else {
+          valid = true;
+        }
+        break;
+      case "FeatureCollection":
+        // allow for any type to be converted to a feature collection
+        valid = true;
+        break;
+      default:
+        // for Feature types must match
+        if(obj.type == type) {
+          valid = true;
+        } else {
+          //ZOO.Console.error("Cannot convert types from " +obj.type + " to " + type);
+        }
+    }
+    return valid;
+  },
+  /**
+   * Method: parseFeature
+   * Convert a feature object from GeoJSON into an
+   *     <ZOO.Feature>.
+   *
+   * Parameters:
+   * obj - {Object} An object created from a GeoJSON object
+   *
+   * Returns:
+   * {<ZOO.Feature>} A feature.
+   */
+  parseFeature: function(obj) {
+    var feature, geometry, attributes, bbox;
+    attributes = (obj.properties) ? obj.properties : {};
+    bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
+    try {
+      geometry = this.parseGeometry(obj.geometry);
+    } catch(err) {
+      // deal with bad geometries
+      throw err;
+    }
+    feature = new ZOO.Feature(geometry, attributes);
+    if(bbox)
+      feature.bounds = ZOO.Bounds.fromArray(bbox);
+    if(obj.id)
+      feature.fid = obj.id;
+    return feature;
+  },
+  /**
+   * Method: parseGeometry
+   * Convert a geometry object from GeoJSON into an <ZOO.Geometry>.
+   *
+   * Parameters:
+   * obj - {Object} An object created from a GeoJSON object
+   *
+   * Returns: 
+   * {<ZOO.Geometry>} A geometry.
+   */
+  parseGeometry: function(obj) {
+    if (obj == null)
+      return null;
+    var geometry, collection = false;
+    if(obj.type == "GeometryCollection") {
+      if(!(obj.geometries instanceof Array)) {
+        throw "GeometryCollection must have geometries array: " + obj;
+      }
+      var numGeom = obj.geometries.length;
+      var components = new Array(numGeom);
+      for(var i=0; i<numGeom; ++i) {
+        components[i] = this.parseGeometry.apply(
+            this, [obj.geometries[i]]
+            );
+      }
+      geometry = new ZOO.Geometry.Collection(components);
+      collection = true;
+    } else {
+      if(!(obj.coordinates instanceof Array)) {
+        throw "Geometry must have coordinates array: " + obj;
+      }
+      if(!this.parseCoords[obj.type.toLowerCase()]) {
+        throw "Unsupported geometry type: " + obj.type;
+      }
+      try {
+        geometry = this.parseCoords[obj.type.toLowerCase()].apply(
+            this, [obj.coordinates]
+            );
+      } catch(err) {
+        // deal with bad coordinates
+        throw err;
+      }
+    }
+        // We don't reproject collections because the children are reprojected
+        // for us when they are created.
+    if (this.internalProjection && this.externalProjection && !collection) {
+      geometry.transform(this.externalProjection, 
+          this.internalProjection); 
+    }                       
+    return geometry;
+  },
+  /**
+   * Property: parseCoords
+   * Object with properties corresponding to the GeoJSON geometry types.
+   *     Property values are functions that do the actual parsing.
+   */
+  parseCoords: {
+    /**
+     * Method: parseCoords.point
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.Point>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Point>} A geometry.
+     */
+    "point": function(array) {
+      if(array.length != 2) {
+        throw "Only 2D points are supported: " + array;
+      }
+      return new ZOO.Geometry.Point(array[0], array[1]);
+    },
+    /**
+     * Method: parseCoords.multipoint
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.MultiPoint>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiPoint>} A geometry.
+     */
+    "multipoint": function(array) {
+      var points = [];
+      var p = null;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          p = this.parseCoords["point"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        points.push(p);
+      }
+      return new ZOO.Geometry.MultiPoint(points);
+    },
+    /**
+     * Method: parseCoords.linestring
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.LineString>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.LineString>} A geometry.
+     */
+    "linestring": function(array) {
+      var points = [];
+      var p = null;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          p = this.parseCoords["point"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        points.push(p);
+      }
+      return new ZOO.Geometry.LineString(points);
+    },
+    /**
+     * Method: parseCoords.multilinestring
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.MultiLineString>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiLineString>} A geometry.
+     */
+    "multilinestring": function(array) {
+      var lines = [];
+      var l = null;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          l = this.parseCoords["linestring"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        lines.push(l);
+      }
+      return new ZOO.Geometry.MultiLineString(lines);
+    },
+    /**
+     * Method: parseCoords.polygon
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.Polygon>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A geometry.
+     */
+    "polygon": function(array) {
+      var rings = [];
+      var r, l;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          l = this.parseCoords["linestring"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        r = new ZOO.Geometry.LinearRing(l.components);
+        rings.push(r);
+      }
+      return new ZOO.Geometry.Polygon(rings);
+    },
+    /**
+     * Method: parseCoords.multipolygon
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.MultiPolygon>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiPolygon>} A geometry.
+     */
+    "multipolygon": function(array) {
+      var polys = [];
+      var p = null;
+      for(var i=0, len=array.length; i<len; ++i) {
+        try {
+          p = this.parseCoords["polygon"].apply(this, [array[i]]);
+        } catch(err) {
+          throw err;
+        }
+        polys.push(p);
+      }
+      return new ZOO.Geometry.MultiPolygon(polys);
+    },
+    /**
+     * Method: parseCoords.box
+     * Convert a coordinate array from GeoJSON into an
+     *     <ZOO.Geometry.Polygon>.
+     *
+     * Parameters:
+     * array - {Object} The coordinates array from the GeoJSON fragment.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A geometry.
+     */
+    "box": function(array) {
+      if(array.length != 2) {
+        throw "GeoJSON box coordinates must have 2 elements";
+      }
+      return new ZOO.Geometry.Polygon([
+          new ZOO.Geometry.LinearRing([
+            new ZOO.Geometry.Point(array[0][0], array[0][1]),
+            new ZOO.Geometry.Point(array[1][0], array[0][1]),
+            new ZOO.Geometry.Point(array[1][0], array[1][1]),
+            new ZOO.Geometry.Point(array[0][0], array[1][1]),
+            new Z0O.Geometry.Point(array[0][0], array[0][1])
+          ])
+      ]);
+    }
+  },
+  /**
+   * Method: write
+   * Serialize a feature, geometry, array of features into a GeoJSON string.
+   *
+   * Parameters:
+   * obj - {Object} An <ZOO.Feature>, <ZOO.Geometry>,
+   *     or an array of features.
+   * pretty - {Boolean} Structure the output with newlines and indentation.
+   *     Default is false.
+   *
+   * Returns:
+   * {String} The GeoJSON string representation of the input geometry,
+   *     features, or array of features.
+   */
+  write: function(obj, pretty) {
+    var geojson = {
+      "type": null
+    };
+    if(obj instanceof Array) {
+      geojson.type = "FeatureCollection";
+      var numFeatures = obj.length;
+      geojson.features = new Array(numFeatures);
+      for(var i=0; i<numFeatures; ++i) {
+        var element = obj[i];
+        if(!element instanceof ZOO.Feature) {
+          var msg = "FeatureCollection only supports collections " +
+            "of features: " + element;
+          throw msg;
+        }
+        geojson.features[i] = this.extract.feature.apply(this, [element]);
+      }
+    } else if (obj.CLASS_NAME.indexOf("ZOO.Geometry") == 0) {
+      geojson = this.extract.geometry.apply(this, [obj]);
+    } else if (obj instanceof ZOO.Feature) {
+      geojson = this.extract.feature.apply(this, [obj]);
+      /*
+      if(obj.layer && obj.layer.projection) {
+        geojson.crs = this.createCRSObject(obj);
+      }
+      */
+    }
+    return ZOO.Format.JSON.prototype.write.apply(this,
+                                                 [geojson, pretty]);
+  },
+  /**
+   * Method: createCRSObject
+   * Create the CRS object for an object.
+   *
+   * Parameters:
+   * object - {<ZOO.Feature>} 
+   *
+   * Returns:
+   * {Object} An object which can be assigned to the crs property
+   * of a GeoJSON object.
+   */
+  createCRSObject: function(object) {
+    //var proj = object.layer.projection.toString();
+    var proj = object.projection.toString();
+    var crs = {};
+    if (proj.match(/epsg:/i)) {
+      var code = parseInt(proj.substring(proj.indexOf(":") + 1));
+      if (code == 4326) {
+        crs = {
+          "type": "OGC",
+          "properties": {
+            "urn": "urn:ogc:def:crs:OGC:1.3:CRS84"
+          }
+        };
+      } else {    
+        crs = {
+          "type": "EPSG",
+          "properties": {
+            "code": code 
+          }
+        };
+      }    
+    }
+    return crs;
+  },
+  /**
+   * Property: extract
+   * Object with properties corresponding to the GeoJSON types.
+   *     Property values are functions that do the actual value extraction.
+   */
+  extract: {
+    /**
+     * Method: extract.feature
+     * Return a partial GeoJSON object representing a single feature.
+     *
+     * Parameters:
+     * feature - {<ZOO.Feature>}
+     *
+     * Returns:
+     * {Object} An object representing the point.
+     */
+    'feature': function(feature) {
+      var geom = this.extract.geometry.apply(this, [feature.geometry]);
+      return {
+        "type": "Feature",
+        "id": feature.fid == null ? feature.id : feature.fid,
+        "properties": feature.attributes,
+        "geometry": geom
+      };
+    },
+    /**
+     * Method: extract.geometry
+     * Return a GeoJSON object representing a single geometry.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry>}
+     *
+     * Returns:
+     * {Object} An object representing the geometry.
+     */
+    'geometry': function(geometry) {
+      if (geometry == null)
+        return null;
+      if (this.internalProjection && this.externalProjection) {
+        geometry = geometry.clone();
+        geometry.transform(this.internalProjection, 
+            this.externalProjection);
+      }                       
+      var geometryType = geometry.CLASS_NAME.split('.')[2];
+      var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
+      var json;
+      if(geometryType == "Collection")
+        json = {
+          "type": "GeometryCollection",
+          "geometries": data
+        };
+      else
+        json = {
+          "type": geometryType,
+          "coordinates": data
+        };
+      return json;
+    },
+    /**
+     * Method: extract.point
+     * Return an array of coordinates from a point.
+     *
+     * Parameters:
+     * point - {<ZOO.Geometry.Point>}
+     *
+     * Returns: 
+     * {Array} An array of coordinates representing the point.
+     */
+    'point': function(point) {
+      return [point.x, point.y];
+    },
+    /**
+     * Method: extract.multipoint
+     * Return an array of coordinates from a multipoint.
+     *
+     * Parameters:
+     * multipoint - {<ZOO.Geometry.MultiPoint>}
+     *
+     * Returns: 
+     * {Array} An array of point coordinate arrays representing
+     *     the multipoint.
+     */
+    'multipoint': function(multipoint) {
+      var array = [];
+      for(var i=0, len=multipoint.components.length; i<len; ++i) {
+        array.push(this.extract.point.apply(this, [multipoint.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.linestring
+     * Return an array of coordinate arrays from a linestring.
+     *
+     * Parameters:
+     * linestring - {<ZOO.Geometry.LineString>}
+     *
+     * Returns:
+     * {Array} An array of coordinate arrays representing
+     *     the linestring.
+     */
+    'linestring': function(linestring) {
+      var array = [];
+      for(var i=0, len=linestring.components.length; i<len; ++i) {
+        array.push(this.extract.point.apply(this, [linestring.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.multilinestring
+     * Return an array of linestring arrays from a linestring.
+     * 
+     * Parameters:
+     * multilinestring - {<ZOO.Geometry.MultiLineString>}
+     * 
+     * Returns:
+     * {Array} An array of linestring arrays representing
+     *     the multilinestring.
+     */
+    'multilinestring': function(multilinestring) {
+      var array = [];
+      for(var i=0, len=multilinestring.components.length; i<len; ++i) {
+        array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.polygon
+     * Return an array of linear ring arrays from a polygon.
+     *
+     * Parameters:
+     * polygon - {<ZOO.Geometry.Polygon>}
+     * 
+     * Returns:
+     * {Array} An array of linear ring arrays representing the polygon.
+     */
+    'polygon': function(polygon) {
+      var array = [];
+      for(var i=0, len=polygon.components.length; i<len; ++i) {
+        array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.multipolygon
+     * Return an array of polygon arrays from a multipolygon.
+     * 
+     * Parameters:
+     * multipolygon - {<ZOO.Geometry.MultiPolygon>}
+     * 
+     * Returns:
+     * {Array} An array of polygon arrays representing
+     *     the multipolygon
+     */
+    'multipolygon': function(multipolygon) {
+      var array = [];
+      for(var i=0, len=multipolygon.components.length; i<len; ++i) {
+        array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
+      }
+      return array;
+    },
+    /**
+     * Method: extract.collection
+     * Return an array of geometries from a geometry collection.
+     * 
+     * Parameters:
+     * collection - {<ZOO.Geometry.Collection>}
+     * 
+     * Returns:
+     * {Array} An array of geometry objects representing the geometry
+     *     collection.
+     */
+    'collection': function(collection) {
+      var len = collection.components.length;
+      var array = new Array(len);
+      for(var i=0; i<len; ++i) {
+        array[i] = this.extract.geometry.apply(
+            this, [collection.components[i]]
+            );
+      }
+      return array;
+    }
+  },
+  CLASS_NAME: 'ZOO.Format.GeoJSON'
+});
+/**
+ * Class: ZOO.Format.KML
+ * Read/Write KML. Create a new instance with the <ZOO.Format.KML>
+ *     constructor. 
+ * 
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.KML = ZOO.Class(ZOO.Format, {
+  /**
+   * Property: kmlns
+   * {String} KML Namespace to use. Defaults to 2.2 namespace.
+   */
+  kmlns: "http://www.opengis.net/kml/2.2",
+  /** 
+   * Property: foldersName
+   * {String} Name of the folders.  Default is "ZOO export".
+   *          If set to null, no name element will be created.
+   */
+  foldersName: "ZOO export",
+  /** 
+   * Property: foldersDesc
+   * {String} Description of the folders. Default is "Exported on [date]."
+   *          If set to null, no description element will be created.
+   */
+  foldersDesc: "Created on " + new Date(),
+  /** 
+   * Property: placemarksDesc
+   * {String} Name of the placemarks.  Default is "No description available".
+   */
+  placemarksDesc: "No description available",
+  /**
+   * Property: extractAttributes
+   * {Boolean} Extract attributes from KML.  Default is true.
+   *           Extracting styleUrls requires this to be set to true
+   */
+  extractAttributes: true,
+  /**
+   * Constructor: ZOO.Format.KML
+   * Create a new parser for KML.
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *     this instance.
+   */
+  initialize: function(options) {
+    // compile regular expressions once instead of every time they are used
+    this.regExes = {
+           trimSpace: (/^\s*|\s*$/g),
+           removeSpace: (/\s*/g),
+           splitSpace: (/\s+/),
+           trimComma: (/\s*,\s*/g),
+           kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/),
+           kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/),
+           straightBracket: (/\$\[(.*?)\]/g)
+    };
+    // KML coordinates are always in longlat WGS84
+    this.externalProjection = new ZOO.Projection("EPSG:4326");
+    ZOO.Format.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * APIMethod: read
+   * Read data from a string, and return a list of features. 
+   * 
+   * Parameters: 
+   * data    - {String} data to read/parse.
+   *
+   * Returns:
+   * {Array(<ZOO.Feature>)} List of features.
+   */
+  read: function(data) {
+    this.features = [];
+    data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+    data = new XML(data);
+    var placemarks = data..*::Placemark;
+    this.parseFeatures(placemarks);
+    return this.features;
+  },
+  /**
+   * Method: parseFeatures
+   * Loop through all Placemark nodes and parse them.
+   * Will create a list of features
+   * 
+   * Parameters: 
+   * nodes    - {Array} of {E4XElement} data to read/parse.
+   * options  - {Object} Hash of options
+   * 
+   */
+  parseFeatures: function(nodes) {
+    var features = new Array(nodes.length());
+    for(var i=0, len=nodes.length(); i<len; i++) {
+      var featureNode = nodes[i];
+      var feature = this.parseFeature.apply(this,[featureNode]) ;
+      features[i] = feature;
+    }
+    this.features = this.features.concat(features);
+  },
+  /**
+   * Method: parseFeature
+   * This function is the core of the KML parsing code in ZOO.
+   *     It creates the geometries that are then attached to the returned
+   *     feature, and calls parseAttributes() to get attribute data out.
+   *
+   * Parameters:
+   * node - {E4XElement}
+   *
+   * Returns:
+   * {<ZOO.Feature>} A vector feature.
+   */
+  parseFeature: function(node) {
+    // only accept one geometry per feature - look for highest "order"
+    var order = ["MultiGeometry", "Polygon", "LineString", "Point"];
+    var type, nodeList, geometry, parser;
+    for(var i=0, len=order.length; i<len; ++i) {
+      type = order[i];
+      nodeList = node.descendants(QName(null,type));
+      if (nodeList.length()> 0) {
+        var parser = this.parseGeometry[type.toLowerCase()];
+        if(parser) {
+          geometry = parser.apply(this, [nodeList[0]]);
+          if (this.internalProjection && this.externalProjection) {
+            geometry.transform(this.externalProjection, 
+                               this.internalProjection); 
+          }                       
+        }
+        // stop looking for different geometry types
+        break;
+      }
+    }
+    // construct feature (optionally with attributes)
+    var attributes;
+    if(this.extractAttributes) {
+      attributes = this.parseAttributes(node);
+    }
+    var feature = new ZOO.Feature(geometry, attributes);
+    var fid = node.@id || node.@name;
+    if(fid != null)
+      feature.fid = fid;
+    return feature;
+  },
+  /**
+   * Property: parseGeometry
+   * Properties of this object are the functions that parse geometries based
+   *     on their type.
+   */
+  parseGeometry: {
+    /**
+     * Method: parseGeometry.point
+     * Given a KML node representing a point geometry, create a ZOO
+     *     point geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A KML Point node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Point>} A point geometry.
+     */
+    'point': function(node) {
+      var coordString = node.*::coordinates.toString();
+      coordString = coordString.replace(this.regExes.removeSpace, "");
+      coords = coordString.split(",");
+      var point = null;
+      if(coords.length > 1) {
+        // preserve third dimension
+        if(coords.length == 2) {
+          coords[2] = null;
+        }
+        point = new ZOO.Geometry.Point(coords[0], coords[1], coords[2]);
+      }
+      return point;
+    },
+    /**
+     * Method: parseGeometry.linestring
+     * Given a KML node representing a linestring geometry, create a
+     *     ZOO linestring geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A KML LineString node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.LineString>} A linestring geometry.
+     */
+    'linestring': function(node, ring) {
+      var line = null;
+      var coordString = node.*::coordinates.toString();
+      coordString = coordString.replace(this.regExes.trimSpace,
+          "");
+      coordString = coordString.replace(this.regExes.trimComma,
+          ",");
+      var pointList = coordString.split(this.regExes.splitSpace);
+      var numPoints = pointList.length;
+      var points = new Array(numPoints);
+      var coords, numCoords;
+      for(var i=0; i<numPoints; ++i) {
+        coords = pointList[i].split(",");
+        numCoords = coords.length;
+        if(numCoords > 1) {
+          if(coords.length == 2) {
+            coords[2] = null;
+          }
+          points[i] = new ZOO.Geometry.Point(coords[0],
+                                             coords[1],
+                                             coords[2]);
+        }
+      }
+      if(numPoints) {
+        if(ring) {
+          line = new ZOO.Geometry.LinearRing(points);
+        } else {
+          line = new ZOO.Geometry.LineString(points);
+        }
+      } else {
+        throw "Bad LineString coordinates: " + coordString;
+      }
+      return line;
+    },
+    /**
+     * Method: parseGeometry.polygon
+     * Given a KML node representing a polygon geometry, create a
+     *     ZOO polygon geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A KML Polygon node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A polygon geometry.
+     */
+    'polygon': function(node) {
+      var nodeList = node..*::LinearRing;
+      var numRings = nodeList.length();
+      var components = new Array(numRings);
+      if(numRings > 0) {
+        // this assumes exterior ring first, inner rings after
+        var ring;
+        for(var i=0, len=nodeList.length(); i<len; ++i) {
+          ring = this.parseGeometry.linestring.apply(this,
+                                                     [nodeList[i], true]);
+          if(ring) {
+            components[i] = ring;
+          } else {
+            throw "Bad LinearRing geometry: " + i;
+          }
+        }
+      }
+      return new ZOO.Geometry.Polygon(components);
+    },
+    /**
+     * Method: parseGeometry.multigeometry
+     * Given a KML node representing a multigeometry, create a
+     *     ZOO geometry collection.
+     *
+     * Parameters:
+     * node - {E4XElement} A KML MultiGeometry node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Collection>} A geometry collection.
+     */
+    'multigeometry': function(node) {
+      var child, parser;
+      var parts = [];
+      var children = node.*::*;
+      for(var i=0, len=children.length(); i<len; ++i ) {
+        child = children[i];
+        var type = child.localName();
+        var parser = this.parseGeometry[type.toLowerCase()];
+        if(parser) {
+          parts.push(parser.apply(this, [child]));
+        }
+      }
+      return new ZOO.Geometry.Collection(parts);
+    }
+  },
+  /**
+   * Method: parseAttributes
+   *
+   * Parameters:
+   * node - {E4XElement}
+   *
+   * Returns:
+   * {Object} An attributes object.
+   */
+  parseAttributes: function(node) {
+    var attributes = {};
+    var edNodes = node.*::ExtendedData;
+    if (edNodes.length() > 0) {
+      attributes = this.parseExtendedData(edNodes[0])
+    }
+    var child, grandchildren;
+    var children = node.*::*;
+    for(var i=0, len=children.length(); i<len; ++i) {
+      child = children[i];
+      grandchildren = child..*::*;
+      if(grandchildren.length() == 1) {
+        var name = child.localName();
+        var value = child.toString();
+        if (value) {
+          value = value.replace(this.regExes.trimSpace, "");
+          attributes[name] = value;
+        }
+      }
+    }
+    return attributes;
+  },
+  /**
+   * Method: parseExtendedData
+   * Parse ExtendedData from KML. Limited support for schemas/datatypes.
+   *     See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata
+   *     for more information on extendeddata.
+   *
+   * Parameters:
+   * node - {E4XElement}
+   *
+   * Returns:
+   * {Object} An attributes object.
+   */
+  parseExtendedData: function(node) {
+    var attributes = {};
+    var dataNodes = node.*::Data;
+    for (var i = 0, len = dataNodes.length(); i < len; i++) {
+      var data = dataNodes[i];
+      var key = data.@name;
+      var ed = {};
+      var valueNode = data.*::value;
+      if (valueNode.length() > 0)
+        ed['value'] = valueNode[0].toString();
+      var nameNode = data.*::displayName;
+      if (nameNode.length() > 0)
+        ed['displayName'] = valueNode[0].toString();
+      attributes[key] = ed;
+    }
+    return attributes;
+  },
+  /**
+   * Method: write
+   * Accept Feature Collection, and return a string. 
+   * 
+   * Parameters:
+   * features - {Array(<ZOO.Feature>} An array of features.
+   *
+   * Returns:
+   * {String} A KML string.
+   */
+  write: function(features) {
+    if(!(features instanceof Array))
+      features = [features];
+    var kml = new XML('<kml xmlns="'+this.kmlns+'"></kml>');
+    var folder = kml.Document.Folder;
+    folder.name = this.foldersName;
+    folder.description = this.foldersDesc;
+    for(var i=0, len=features.length; i<len; ++i) {
+      folder.Placemark[i] = this.createPlacemark(features[i]);
+    }
+    return kml.toXMLString();
+  },
+  /**
+   * Method: createPlacemark
+   * Creates and returns a KML placemark node representing the given feature. 
+   * 
+   * Parameters:
+   * feature - {<ZOO.Feature>}
+   * 
+   * Returns:
+   * {E4XElement}
+   */
+  createPlacemark: function(feature) {
+    var placemark = new XML('<Placemark xmlns="'+this.kmlns+'"></Placemark>');
+    placemark.name = (feature.attributes.name) ?
+                    feature.attributes.name : feature.id;
+    placemark.description = (feature.attributes.description) ?
+                             feature.attributes.description : this.placemarksDesc;
+    if(feature.fid != null)
+      placemark.@id = feature.fid;
+    placemark.*[2] = this.buildGeometryNode(feature.geometry);
+    return placemark;
+  },
+  /**
+   * Method: buildGeometryNode
+   * Builds and returns a KML geometry node with the given geometry.
+   * 
+   * Parameters:
+   * geometry - {<ZOO.Geometry>}
+   * 
+   * Returns:
+   * {E4XElement}
+   */
+  buildGeometryNode: function(geometry) {
+    if (this.internalProjection && this.externalProjection) {
+      geometry = geometry.clone();
+      geometry.transform(this.internalProjection, 
+                         this.externalProjection);
+    }
+    var className = geometry.CLASS_NAME;
+    var type = className.substring(className.lastIndexOf(".") + 1);
+    var builder = this.buildGeometry[type.toLowerCase()];
+    var node = null;
+    if(builder) {
+      node = builder.apply(this, [geometry]);
+    }
+    return node;
+  },
+  /**
+   * Property: buildGeometry
+   * Object containing methods to do the actual geometry node building
+   *     based on geometry type.
+   */
+  buildGeometry: {
+    /**
+     * Method: buildGeometry.point
+     * Given a ZOO point geometry, create a KML point.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Point>} A point geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML point node.
+     */
+    'point': function(geometry) {
+      var kml = new XML('<Point xmlns="'+this.kmlns+'"></Point>');
+      kml.coordinates = this.buildCoordinatesNode(geometry);
+      return kml;
+    },
+    /**
+     * Method: buildGeometry.multipoint
+     * Given a ZOO multipoint geometry, create a KML
+     *     GeometryCollection.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiPoint>} A multipoint geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML GeometryCollection node.
+     */
+    'multipoint': function(geometry) {
+      return this.buildGeometry.collection.apply(this, [geometry]);
+    },
+    /**
+     * Method: buildGeometry.linestring
+     * Given a ZOO linestring geometry, create a KML linestring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.LineString>} A linestring geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML linestring node.
+     */
+    'linestring': function(geometry) {
+      var kml = new XML('<LineString xmlns="'+this.kmlns+'"></LineString>');
+      kml.coordinates = this.buildCoordinatesNode(geometry);
+      return kml;
+    },
+    /**
+     * Method: buildGeometry.multilinestring
+     * Given a ZOO multilinestring geometry, create a KML
+     *     GeometryCollection.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiLineString>} A multilinestring geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML GeometryCollection node.
+     */
+    'multilinestring': function(geometry) {
+      return this.buildGeometry.collection.apply(this, [geometry]);
+    },
+    /**
+     * Method: buildGeometry.linearring
+     * Given a ZOO linearring geometry, create a KML linearring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.LinearRing>} A linearring geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML linearring node.
+     */
+    'linearring': function(geometry) {
+      var kml = new XML('<LinearRing xmlns="'+this.kmlns+'"></LinearRing>');
+      kml.coordinates = this.buildCoordinatesNode(geometry);
+      return kml;
+    },
+    /**
+     * Method: buildGeometry.polygon
+     * Given a ZOO polygon geometry, create a KML polygon.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Polygon>} A polygon geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML polygon node.
+     */
+    'polygon': function(geometry) {
+      var kml = new XML('<Polygon xmlns="'+this.kmlns+'"></Polygon>');
+      var rings = geometry.components;
+      var ringMember, ringGeom, type;
+      for(var i=0, len=rings.length; i<len; ++i) {
+        type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+        ringMember = new XML('<'+type+' xmlns="'+this.kmlns+'"></'+type+'>');
+        ringMember.LinearRing = this.buildGeometry.linearring.apply(this,[rings[i]]);
+        kml.*[i] = ringMember;
+      }
+      return kml;
+    },
+    /**
+     * Method: buildGeometry.multipolygon
+     * Given a ZOO multipolygon geometry, create a KML
+     *     GeometryCollection.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Point>} A multipolygon geometry.
+     *
+     * Returns:
+     * {E4XElement} A KML GeometryCollection node.
+     */
+    'multipolygon': function(geometry) {
+      return this.buildGeometry.collection.apply(this, [geometry]);
+    },
+    /**
+     * Method: buildGeometry.collection
+     * Given a ZOO geometry collection, create a KML MultiGeometry.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Collection>} A geometry collection.
+     *
+     * Returns:
+     * {E4XElement} A KML MultiGeometry node.
+     */
+    'collection': function(geometry) {
+      var kml = new XML('<MultiGeometry xmlns="'+this.kmlns+'"></MultiGeometry>');
+      var child;
+      for(var i=0, len=geometry.components.length; i<len; ++i) {
+        kml.*[i] = this.buildGeometryNode.apply(this,[geometry.components[i]]);
+      }
+      return kml;
+    }
+  },
+  /**
+   * Method: buildCoordinatesNode
+   * Builds and returns the KML coordinates node with the given geometry
+   *     <coordinates>...</coordinates>
+   * 
+   * Parameters:
+   * geometry - {<ZOO.Geometry>}
+   * 
+   * Return:
+   * {E4XElement}
+   */
+  buildCoordinatesNode: function(geometry) {
+    var cooridnates = new XML('<coordinates xmlns="'+this.kmlns+'"></coordinates>');
+    var points = geometry.components;
+    if(points) {
+      // LineString or LinearRing
+      var point;
+      var numPoints = points.length;
+      var parts = new Array(numPoints);
+      for(var i=0; i<numPoints; ++i) {
+        point = points[i];
+        parts[i] = point.x + "," + point.y;
+      }
+      coordinates = parts.join(" ");
+    } else {
+      // Point
+      coordinates = geometry.x + "," + geometry.y;
+    }
+    return coordinates;
+  },
+  CLASS_NAME: 'ZOO.Format.KML'
+});
+/**
+ * Class: ZOO.Format.GML
+ * Read/Write GML. Create a new instance with the <ZOO.Format.GML>
+ *     constructor.  Supports the GML simple features profile.
+ * 
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.GML = ZOO.Class(ZOO.Format, {
+  /**
+   * Property: schemaLocation
+   * {String} Schema location for a particular minor version.
+   */
+  schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
+  /**
+   * Property: namespaces
+   * {Object} Mapping of namespace aliases to namespace URIs.
+   */
+  namespaces: {
+    ogr: "http://ogr.maptools.org/",
+    gml: "http://www.opengis.net/gml",
+    xlink: "http://www.w3.org/1999/xlink",
+    xsi: "http://www.w3.org/2001/XMLSchema-instance",
+    wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
+  },
+  /**
+   * Property: defaultPrefix
+   */
+  defaultPrefix: 'ogr',
+  /** 
+   * Property: collectionName
+   * {String} Name of featureCollection element.
+   */
+  collectionName: "FeatureCollection",
+  /*
+   * Property: featureName
+   * {String} Element name for features. Default is "sql_statement".
+   */
+  featureName: "sql_statement",
+  /**
+   * Property: geometryName
+   * {String} Name of geometry element.  Defaults to "geometryProperty".
+   */
+  geometryName: "geometryProperty",
+  /**
+   * Property: xy
+   * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
+   * Changing is not recommended, a new Format should be instantiated.
+   */
+  xy: true,
+  /**
+   * Constructor: ZOO.Format.GML
+   * Create a new parser for GML.
+   *
+   * Parameters:
+   * options - {Object} An optional object whose properties will be set on
+   *     this instance.
+   */
+  initialize: function(options) {
+    // compile regular expressions once instead of every time they are used
+    this.regExes = {
+      trimSpace: (/^\s*|\s*$/g),
+      removeSpace: (/\s*/g),
+      splitSpace: (/\s+/),
+      trimComma: (/\s*,\s*/g)
+    };
+    ZOO.Format.prototype.initialize.apply(this, [options]);
+  },
+  /**
+   * Method: read
+   * Read data from a string, and return a list of features. 
+   * 
+   * Parameters:
+   * data - {String} data to read/parse.
+   *
+   * Returns:
+   * {Array(<ZOO.Feature>)} An array of features.
+   */
+  read: function(data) {
+    this.features = [];
+    data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+    data = new XML(data);
+
+    var gmlns = Namespace(this.namespaces['gml']);
+    var featureNodes = data..gmlns::featureMember;
+    var features = [];
+    for(var i=0,len=featureNodes.length(); i<len; i++) {
+      var feature = this.parseFeature(featureNodes[i]);
+      if(feature) {
+        features.push(feature);
+      }
+    }
+    return features;
+  },
+  /**
+   * Method: parseFeature
+   * This function is the core of the GML parsing code in ZOO.
+   *    It creates the geometries that are then attached to the returned
+   *    feature, and calls parseAttributes() to get attribute data out.
+   *    
+   * Parameters:
+   * node - {E4XElement} A GML feature node. 
+   */
+  parseFeature: function(node) {
+    // only accept one geometry per feature - look for highest "order"
+    var gmlns = Namespace(this.namespaces['gml']);
+    var order = ["MultiPolygon", "Polygon",
+                 "MultiLineString", "LineString",
+                 "MultiPoint", "Point", "Envelope", "Box"];
+    var type, nodeList, geometry, parser;
+    for(var i=0; i<order.length; ++i) {
+      type = order[i];
+      nodeList = node.descendants(QName(gmlns,type));
+      if (nodeList.length() > 0) {
+        var parser = this.parseGeometry[type.toLowerCase()];
+        if(parser) {
+          geometry = parser.apply(this, [nodeList[0]]);
+          if (this.internalProjection && this.externalProjection) {
+            geometry.transform(this.externalProjection, 
+                               this.internalProjection); 
+          }                       
+        }
+        // stop looking for different geometry types
+        break;
+      }
+    }
+    var attributes;
+    if(this.extractAttributes) {
+      attributes = this.parseAttributes(node);
+    }
+    var feature = new ZOO.Feature(geometry, attributes);
+    return feature;
+  },
+  /**
+   * Property: parseGeometry
+   * Properties of this object are the functions that parse geometries based
+   *     on their type.
+   */
+  parseGeometry: {
+    /**
+     * Method: parseGeometry.point
+     * Given a GML node representing a point geometry, create a ZOO
+     *     point geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Point>} A point geometry.
+     */
+    'point': function(node) {
+      /**
+       * Three coordinate variations to consider:
+       * 1) <gml:pos>x y z</gml:pos>
+       * 2) <gml:coordinates>x, y, z</gml:coordinates>
+       * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
+       */
+      var nodeList, coordString;
+      var coords = [];
+      // look for <gml:pos>
+      var nodeList = node..*::pos;
+      if(nodeList.length() > 0) {
+        coordString = nodeList[0].toString();
+        coordString = coordString.replace(this.regExes.trimSpace, "");
+        coords = coordString.split(this.regExes.splitSpace);
+      }
+      // look for <gml:coordinates>
+      if(coords.length == 0) {
+        nodeList = node..*::coordinates;
+        if(nodeList.length() > 0) {
+          coordString = nodeList[0].toString();
+          coordString = coordString.replace(this.regExes.removeSpace,"");
+          coords = coordString.split(",");
+        }
+      }
+      // look for <gml:coord>
+      if(coords.length == 0) {
+        nodeList = node..*::coord;
+        if(nodeList.length() > 0) {
+          var xList = nodeList[0].*::X;
+          var yList = nodeList[0].*::Y;
+          if(xList.length() > 0 && yList.length() > 0)
+            coords = [xList[0].toString(),
+                      yList[0].toString()];
+        }
+      }
+      // preserve third dimension
+      if(coords.length == 2)
+        coords[2] = null;
+      if (this.xy)
+        return new ZOO.Geometry.Point(coords[0],coords[1],coords[2]);
+      else
+        return new ZOO.Geometry.Point(coords[1],coords[0],coords[2]);
+    },
+    /**
+     * Method: parseGeometry.multipoint
+     * Given a GML node representing a multipoint geometry, create a
+     *     ZOO multipoint geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiPoint>} A multipoint geometry.
+     */
+    'multipoint': function(node) {
+      var nodeList = node..*::Point;
+      var components = [];
+      if(nodeList.length() > 0) {
+        var point;
+        for(var i=0, len=nodeList.length(); i<len; ++i) {
+          point = this.parseGeometry.point.apply(this, [nodeList[i]]);
+          if(point)
+            components.push(point);
+        }
+      }
+      return new ZOO.Geometry.MultiPoint(components);
+    },
+    /**
+     * Method: parseGeometry.linestring
+     * Given a GML node representing a linestring geometry, create a
+     *     ZOO linestring geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.LineString>} A linestring geometry.
+     */
+    'linestring': function(node, ring) {
+      /**
+       * Two coordinate variations to consider:
+       * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
+       * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
+       */
+      var nodeList, coordString;
+      var coords = [];
+      var points = [];
+      // look for <gml:posList>
+      nodeList = node..*::posList;
+      if(nodeList.length() > 0) {
+        coordString = nodeList[0].toString();
+        coordString = coordString.replace(this.regExes.trimSpace, "");
+        coords = coordString.split(this.regExes.splitSpace);
+        var dim = parseInt(nodeList[0].@dimension);
+        var j, x, y, z;
+        for(var i=0; i<coords.length/dim; ++i) {
+          j = i * dim;
+          x = coords[j];
+          y = coords[j+1];
+          z = (dim == 2) ? null : coords[j+2];
+          if (this.xy)
+            points.push(new ZOO.Geometry.Point(x, y, z));
+          else
+            points.push(new Z0O.Geometry.Point(y, x, z));
+        }
+      }
+      // look for <gml:coordinates>
+      if(coords.length == 0) {
+        nodeList = node..*::coordinates;
+        if(nodeList.length() > 0) {
+          coordString = nodeList[0].toString();
+          coordString = coordString.replace(this.regExes.trimSpace,"");
+          coordString = coordString.replace(this.regExes.trimComma,",");
+          var pointList = coordString.split(this.regExes.splitSpace);
+          for(var i=0; i<pointList.length; ++i) {
+            coords = pointList[i].split(",");
+            if(coords.length == 2)
+              coords[2] = null;
+            if (this.xy)
+              points.push(new ZOO.Geometry.Point(coords[0],coords[1],coords[2]));
+            else
+              points.push(new ZOO.Geometry.Point(coords[1],coords[0],coords[2]));
+          }
+        }
+      }
+      var line = null;
+      if(points.length != 0) {
+        if(ring)
+          line = new ZOO.Geometry.LinearRing(points);
+        else
+          line = new ZOO.Geometry.LineString(points);
+      }
+      return line;
+    },
+    /**
+     * Method: parseGeometry.multilinestring
+     * Given a GML node representing a multilinestring geometry, create a
+     *     ZOO multilinestring geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiLineString>} A multilinestring geometry.
+     */
+    'multilinestring': function(node) {
+      var nodeList = node..*::LineString;
+      var components = [];
+      if(nodeList.length() > 0) {
+        var line;
+        for(var i=0, len=nodeList.length(); i<len; ++i) {
+          line = this.parseGeometry.linestring.apply(this, [nodeList[i]]);
+          if(point)
+            components.push(point);
+        }
+      }
+      return new ZOO.Geometry.MultiLineString(components);
+    },
+    /**
+     * Method: parseGeometry.polygon
+     * Given a GML node representing a polygon geometry, create a
+     *     ZOO polygon geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A polygon geometry.
+     */
+    'polygon': function(node) {
+      nodeList = node..*::LinearRing;
+      var components = [];
+      if(nodeList.length() > 0) {
+        // this assumes exterior ring first, inner rings after
+        var ring;
+        for(var i=0, len = nodeList.length(); i<len; ++i) {
+          ring = this.parseGeometry.linestring.apply(this,[nodeList[i], true]);
+          if(ring)
+            components.push(ring);
+        }
+      }
+      return new ZOO.Geometry.Polygon(components);
+    },
+    /**
+     * Method: parseGeometry.multipolygon
+     * Given a GML node representing a multipolygon geometry, create a
+     *     ZOO multipolygon geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.MultiPolygon>} A multipolygon geometry.
+     */
+    'multipolygon': function(node) {
+      var nodeList = node..*::Polygon;
+      var components = [];
+      if(nodeList.length() > 0) {
+        var polygon;
+        for(var i=0, len=nodeList.length(); i<len; ++i) {
+          polygon = this.parseGeometry.polygon.apply(this, [nodeList[i]]);
+          if(polygon)
+            components.push(polygon);
+        }
+      }
+      return new ZOO.Geometry.MultiPolygon(components);
+    },
+    /**
+     * Method: parseGeometry.polygon
+     * Given a GML node representing an envelope, create a
+     *     ZOO polygon geometry.
+     *
+     * Parameters:
+     * node - {E4XElement} A GML node.
+     *
+     * Returns:
+     * {<ZOO.Geometry.Polygon>} A polygon geometry.
+     */
+    'envelope': function(node) {
+      var components = [];
+      var coordString;
+      var envelope;
+      var lpoint = node..*::lowerCorner;
+      if (lpoint.length() > 0) {
+        var coords = [];
+        if(lpoint.length() > 0) {
+          coordString = lpoint[0].toString();
+          coordString = coordString.replace(this.regExes.trimSpace, "");
+          coords = coordString.split(this.regExes.splitSpace);
+        }
+        if(coords.length == 2)
+          coords[2] = null;
+        if (this.xy)
+          var lowerPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]);
+        else
+          var lowerPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]);
+      }
+      var upoint = node..*::upperCorner;
+      if (upoint.length() > 0) {
+        var coords = [];
+        if(upoint.length > 0) {
+          coordString = upoint[0].toString();
+          coordString = coordString.replace(this.regExes.trimSpace, "");
+          coords = coordString.split(this.regExes.splitSpace);
+        }
+        if(coords.length == 2)
+          coords[2] = null;
+        if (this.xy)
+          var upperPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]);
+        else
+          var upperPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]);
+      }
+      if (lowerPoint && upperPoint) {
+        components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y));
+        components.push(new ZOO.Geometry.Point(upperPoint.x, lowerPoint.y));
+        components.push(new ZOO.Geometry.Point(upperPoint.x, upperPoint.y));
+        components.push(new ZOO.Geometry.Point(lowerPoint.x, upperPoint.y));
+        components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y));
+        var ring = new ZOO.Geometry.LinearRing(components);
+        envelope = new ZOO.Geometry.Polygon([ring]);
+      }
+      return envelope;
+    }
+  },
+  /**
+   * Method: parseAttributes
+   *
+   * Parameters:
+   * node - {<E4XElement>}
+   *
+   * Returns:
+   * {Object} An attributes object.
+   */
+  parseAttributes: function(node) {
+    var attributes = {};
+    // assume attributes are children of the first type 1 child
+    var childNode = node.*::*[0];
+    var child, grandchildren;
+    var children = childNode.*::*;
+    for(var i=0, len=children.length(); i<len; ++i) {
+      child = children[i];
+      grandchildren = child..*::*;
+      if(grandchildren.length() == 1) {
+        var name = child.localName();
+        var value = child.toString();
+        if (value) {
+          value = value.replace(this.regExes.trimSpace, "");
+          attributes[name] = value;
+        } else
+          attributes[name] = null;
+      }
+    }
+    return attributes;
+  },
+  /**
+   * Method: write
+   * Generate a GML document string given a list of features. 
+   * 
+   * Parameters:
+   * features - {Array(<ZOO.Feature>)} List of features to
+   *     serialize into a string.
+   *
+   * Returns:
+   * {String} A string representing the GML document.
+   */
+  write: function(features) {
+    if(!(features instanceof Array)) {
+      features = [features];
+    }
+    var pfx = this.defaultPrefix;
+    var name = pfx+':'+this.collectionName;
+    var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" xmlns:gml="'+this.namespaces['gml']+'" xmlns:xsi="'+this.namespaces['xsi']+'" xsi:schemaLocation="'+this.schemaLocation+'"></'+name+'>');
+    for(var i=0; i<features.length; i++) {
+      gml.*::*[i] = this.createFeature(features[i]);
+    }
+    return gml.toXMLString();
+  },
+  /** 
+   * Method: createFeature
+   * Accept an ZOO.Feature, and build a GML node for it.
+   *
+   * Parameters:
+   * feature - {<ZOO.Feature>} The feature to be built as GML.
+   *
+   * Returns:
+   * {E4XElement} A node reprensting the feature in GML.
+   */
+  createFeature: function(feature) {
+    var pfx = this.defaultPrefix;
+    var name = pfx+':'+this.featureName;
+    var fid = feature.fid || feature.id;
+    var gml = new XML('<gml:featureMember xmlns:gml="'+this.namespaces['gml']+'"><'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" fid="'+fid+'"></'+name+'></gml:featureMember>');
+    var geometry = feature.geometry;
+    gml.*::*[0].*::* = this.buildGeometryNode(geometry);
+    for(var attr in feature.attributes) {
+      var attrNode = new XML('<'+pfx+':'+attr+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'">'+feature.attributes[attr]+'</'+pfx+':'+attr+'>');
+      gml.*::*[0].appendChild(attrNode);
+    }
+    return gml;
+  },
+  /**
+   * Method: buildGeometryNode
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} The geometry to be built as GML.
+   *
+   * Returns:
+   * {E4XElement} A node reprensting the geometry in GML.
+   */
+  buildGeometryNode: function(geometry) {
+    if (this.externalProjection && this.internalProjection) {
+      geometry = geometry.clone();
+      geometry.transform(this.internalProjection, 
+          this.externalProjection);
+    }    
+    var className = geometry.CLASS_NAME;
+    var type = className.substring(className.lastIndexOf(".") + 1);
+    var builder = this.buildGeometry[type.toLowerCase()];
+    var pfx = this.defaultPrefix;
+    var name = pfx+':'+this.geometryName;
+    var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'"></'+name+'>');
+    if (builder)
+      gml.*::* = builder.apply(this, [geometry]);
+    return gml;
+  },
+  /**
+   * Property: buildGeometry
+   * Object containing methods to do the actual geometry node building
+   *     based on geometry type.
+   */
+  buildGeometry: {
+    /**
+     * Method: buildGeometry.point
+     * Given a ZOO point geometry, create a GML point.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Point>} A point geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML point node.
+     */
+    'point': function(geometry) {
+      var gml = new XML('<gml:Point xmlns:gml="'+this.namespaces['gml']+'"></gml:Point>');
+      gml.*::*[0] = this.buildCoordinatesNode(geometry);
+      return gml;
+    },
+    /**
+     * Method: buildGeometry.multipoint
+     * Given a ZOO multipoint geometry, create a GML multipoint.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiPoint>} A multipoint geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML multipoint node.
+     */
+    'multipoint': function(geometry) {
+      var gml = new XML('<gml:MultiPoint xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiPoint>');
+      var points = geometry.components;
+      var pointMember;
+      for(var i=0; i<points.length; i++) { 
+        pointMember = new XML('<gml:pointMember xmlns:gml="'+this.namespaces['gml']+'"></gml:pointMember>');
+        pointMember.*::* = this.buildGeometry.point.apply(this,[points[i]]);
+        gml.*::*[i] = pointMember;
+      }
+      return gml;            
+    },
+    /**
+     * Method: buildGeometry.linestring
+     * Given a ZOO linestring geometry, create a GML linestring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.LineString>} A linestring geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML linestring node.
+     */
+    'linestring': function(geometry) {
+      var gml = new XML('<gml:LineString xmlns:gml="'+this.namespaces['gml']+'"></gml:LineString>');
+      gml.*::*[0] = this.buildCoordinatesNode(geometry);
+      return gml;
+    },
+    /**
+     * Method: buildGeometry.multilinestring
+     * Given a ZOO multilinestring geometry, create a GML
+     *     multilinestring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiLineString>} A multilinestring
+     *     geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML multilinestring node.
+     */
+    'multilinestring': function(geometry) {
+      var gml = new XML('<gml:MultiLineString xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiLineString>');
+      var lines = geometry.components;
+      var lineMember;
+      for(var i=0; i<lines.length; i++) { 
+        lineMember = new XML('<gml:lineStringMember xmlns:gml="'+this.namespaces['gml']+'"></gml:lineStringMember>');
+        lineMember.*::* = this.buildGeometry.linestring.apply(this,[lines[i]]);
+        gml.*::*[i] = lineMember;
+      }
+      return gml;            
+    },
+    /**
+     * Method: buildGeometry.linearring
+     * Given a ZOO linearring geometry, create a GML linearring.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.LinearRing>} A linearring geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML linearring node.
+     */
+    'linearring': function(geometry) {
+      var gml = new XML('<gml:LinearRing xmlns:gml="'+this.namespaces['gml']+'"></gml:LinearRing>');
+      gml.*::*[0] = this.buildCoordinatesNode(geometry);
+      return gml;
+    },
+    /**
+     * Method: buildGeometry.polygon
+     * Given an ZOO polygon geometry, create a GML polygon.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.Polygon>} A polygon geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML polygon node.
+     */
+    'polygon': function(geometry) {
+      var gml = new XML('<gml:Polygon xmlns:gml="'+this.namespaces['gml']+'"></gml:Polygon>');
+      var rings = geometry.components;
+      var ringMember, type;
+      for(var i=0; i<rings.length; ++i) {
+        type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+        var ringMember = new XML('<gml:'+type+' xmlns:gml="'+this.namespaces['gml']+'"></gml:'+type+'>');
+        ringMember.*::* = this.buildGeometry.linearring.apply(this,[rings[i]]);
+        gml.*::*[i] = ringMember;
+      }
+      return gml;
+    },
+    /**
+     * Method: buildGeometry.multipolygon
+     * Given a ZOO multipolygon geometry, create a GML multipolygon.
+     *
+     * Parameters:
+     * geometry - {<ZOO.Geometry.MultiPolygon>} A multipolygon
+     *     geometry.
+     *
+     * Returns:
+     * {E4XElement} A GML multipolygon node.
+     */
+    'multipolygon': function(geometry) {
+      var gml = new XML('<gml:MultiPolygon xmlns:gml="'+this.namespaces['gml']+'"></gml:MultiPolygon>');
+      var polys = geometry.components;
+      var polyMember;
+      for(var i=0; i<polys.length; i++) { 
+        polyMember = new XML('<gml:polygonMember xmlns:gml="'+this.namespaces['gml']+'"></gml:polygonMember>');
+        polyMember.*::* = this.buildGeometry.polygon.apply(this,[polys[i]]);
+        gml.*::*[i] = polyMember;
+      }
+      return gml;            
+    }
+  },
+  /**
+   * Method: buildCoordinatesNode
+   * builds the coordinates XmlNode
+   * (code)
+   * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
+   * (end)
+   * Parameters: 
+   * geometry - {<ZOO.Geometry>} 
+   *
+   * Returns:
+   * {E4XElement} created E4XElement
+   */
+  buildCoordinatesNode: function(geometry) {
+    var parts = [];
+    if(geometry instanceof ZOO.Bounds){
+      parts.push(geometry.left + "," + geometry.bottom);
+      parts.push(geometry.right + "," + geometry.top);
+    } else {
+      var points = (geometry.components) ? geometry.components : [geometry];
+      for(var i=0; i<points.length; i++) {
+        parts.push(points[i].x + "," + points[i].y);                
+      }            
+    }
+    return new XML('<gml:coordinates xmlns:gml="'+this.namespaces['gml']+'" decimal="." cs=", " ts=" ">'+parts.join(" ")+'</gml:coordinates>');
+  },
+  CLASS_NAME: 'ZOO.Format.GML'
+});
+/**
+ * Class: ZOO.Format.WPS
+ * Read/Write WPS. Create a new instance with the <ZOO.Format.WPS>
+ *     constructor. Supports only parseExecuteResponse.
+ * 
+ * Inherits from:
+ *  - <ZOO.Format>
+ */
+ZOO.Format.WPS = ZOO.Class(ZOO.Format, {
+  /**
+   * Property: schemaLocation
+   * {String} Schema location for a particular minor version.
+   */
+  schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd",
+  /**
+   * Property: namespaces
+   * {Object} Mapping of namespace aliases to namespace URIs.
+   */
+  namespaces: {
+    ows: "http://www.opengis.net/ows/1.1",
+    wps: "http://www.opengis.net/wps/1.0.0",
+    xlink: "http://www.w3.org/1999/xlink",
+    xsi: "http://www.w3.org/2001/XMLSchema-instance",
+  },
+  /**
+   * Method: read
+   *
+   * Parameters:
+   * data - {String} A WPS xml document
+   *
+   * Returns:
+   * {Object} Execute response.
+   */
+  read:function(data) {
+    data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+    data = new XML(data);
+    switch (data.localName()) {
+      case 'ExecuteResponse':
+        return this.parseExecuteResponse(data);
+      default:
+        return null;
+    }
+  },
+  /**
+   * Method: parseExecuteResponse
+   *
+   * Parameters:
+   * node - {E4XElement} A WPS ExecuteResponse document
+   *
+   * Returns:
+   * {Object} Execute response.
+   */
+  parseExecuteResponse: function(node) {
+    var outputs = node.*::ProcessOutputs.*::Output;
+    if (outputs.length() > 0) {
+      var data = outputs[0].*::Data.*::*[0];
+      var builder = this.parseData[data.localName().toLowerCase()];
+      if (builder)
+        return builder.apply(this,[data]);
+      else
+        return null;
+    } else
+      return null;
+  },
+  /**
+   * Property: parseData
+   * Object containing methods to analyse data response.
+   */
+  parseData: {
+    /**
+     * Method: parseData.complexdata
+     * Given an Object representing the WPS complex data response.
+     *
+     * Parameters:
+     * node - {E4XElement} A WPS node.
+     *
+     * Returns:
+     * {Object} A WPS complex data response.
+     */
+    'complexdata': function(node) {
+      var result = {value:node.toString()};
+      if (node.@mimeType.length()>0)
+        result.mimeType = node.@mimeType;
+      if (node.@encoding.length()>0)
+        result.encoding = node.@encoding;
+      if (node.@schema.length()>0)
+        result.schema = node.@schema;
+      return result;
+    },
+    /**
+     * Method: parseData.literaldata
+     * Given an Object representing the WPS literal data response.
+     *
+     * Parameters:
+     * node - {E4XElement} A WPS node.
+     *
+     * Returns:
+     * {Object} A WPS literal data response.
+     */
+    'literaldata': function(node) {
+      var result = {value:node.toString()};
+      if (node.@dataType.length()>0)
+        result.dataType = node.@dataType;
+      if (node.@uom.length()>0)
+        result.uom = node.@uom;
+      return result;
+    }
+  },
+  CLASS_NAME: 'ZOO.Format.WPS'
+});
+
+/**
+ * Class: ZOO.Feature
+ * Vector features use the ZOO.Geometry classes as geometry description.
+ * They have an 'attributes' property, which is the data object
+ */
+ZOO.Feature = ZOO.Class({
+  /** 
+   * Property: fid 
+   * {String} 
+   */
+  fid: null,
+  /** 
+   * Property: geometry 
+   * {<ZOO.Geometry>} 
+   */
+  geometry: null,
+  /** 
+   * Property: attributes 
+   * {Object} This object holds arbitrary properties that describe the
+   *     feature.
+   */
+  attributes: null,
+  /**
+   * Property: bounds
+   * {<ZOO.Bounds>} The box bounding that feature's geometry, that
+   *     property can be set by an <ZOO.Format> object when
+   *     deserializing the feature, so in most cases it represents an
+   *     information set by the server. 
+   */
+  bounds: null,
+  /** 
+   * Constructor: ZOO.Feature
+   * Create a vector feature. 
+   * 
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} The geometry that this feature
+   *     represents.
+   * attributes - {Object} An optional object that will be mapped to the
+   *     <attributes> property. 
+   */
+  initialize: function(geometry, attributes) {
+    this.geometry = geometry ? geometry : null;
+    this.attributes = {};
+    if (attributes)
+      this.attributes = ZOO.extend(this.attributes,attributes);
+  },
+  /** 
+   * Method: destroy
+   * nullify references to prevent circular references and memory leaks
+   */
+  destroy: function() {
+    this.geometry = null;
+  },
+  /**
+   * Method: clone
+   * Create a clone of this vector feature.  Does not set any non-standard
+   *     properties.
+   *
+   * Returns:
+   * {<ZOO.Feature>} An exact clone of this vector feature.
+   */
+  clone: function () {
+    return new ZOO.Feature(this.geometry ? this.geometry.clone() : null,
+            this.attributes);
+  },
+  /**
+   * Method: move
+   * Moves the feature and redraws it at its new location
+   *
+   * Parameters:
+   * x - {Float}
+   * y - {Float}
+   */
+  move: function(x, y) {
+    if(!this.geometry.move)
+      return;
+
+    this.geometry.move(x,y);
+    return this.geometry;
+  },
+  CLASS_NAME: 'ZOO.Feature'
+});
+
+/**
+ * Class: ZOO.Geometry
+ * A Geometry is a description of a geographic object. Create an instance
+ * of this class with the <ZOO.Geometry> constructor. This is a base class,
+ * typical geometry types are described by subclasses of this class.
+ */
+ZOO.Geometry = ZOO.Class({
+  /**
+   * Property: id
+   * {String} A unique identifier for this geometry.
+   */
+  id: null,
+  /**
+   * Property: parent
+   * {<ZOO.Geometry>}This is set when a Geometry is added as component
+   * of another geometry
+   */
+  parent: null,
+  /**
+   * Property: bounds 
+   * {<ZOO.Bounds>} The bounds of this geometry
+   */
+  bounds: null,
+  /**
+   * Constructor: ZOO.Geometry
+   * Creates a geometry object.  
+   */
+  initialize: function() {
+    //generate unique id
+  },
+  /**
+   * Method: destroy
+   * Destroy this geometry.
+   */
+  destroy: function() {
+    this.id = null;
+    this.bounds = null;
+  },
+  /**
+   * Method: clone
+   * Create a clone of this geometry.  Does not set any non-standard
+   *     properties of the cloned geometry.
+   * 
+   * Returns:
+   * {<ZOO.Geometry>} An exact clone of this geometry.
+   */
+  clone: function() {
+    return new ZOO.Geometry();
+  },
+  /**
+   * Method: extendBounds
+   * Extend the existing bounds to include the new bounds. 
+   * If geometry's bounds is not yet set, then set a new Bounds.
+   * 
+   * Parameters:
+   * newBounds - {<ZOO.Bounds>} 
+   */
+  extendBounds: function(newBounds){
+    var bounds = this.getBounds();
+    if (!bounds)
+      this.setBounds(newBounds);
+    else
+      this.bounds.extend(newBounds);
+  },
+  /**
+   * Set the bounds for this Geometry.
+   * 
+   * Parameters:
+   * bounds - {<ZOO.Bounds>} 
+   */
+  setBounds: function(bounds) {
+    if (bounds)
+      this.bounds = bounds.clone();
+  },
+  /**
+   * Method: clearBounds
+   * Nullify this components bounds and that of its parent as well.
+   */
+  clearBounds: function() {
+    this.bounds = null;
+    if (this.parent)
+      this.parent.clearBounds();
+  },
+  /**
+   * Method: getBounds
+   * Get the bounds for this Geometry. If bounds is not set, it 
+   * is calculated again, this makes queries faster.
+   * 
+   * Returns:
+   * {<ZOO.Bounds>}
+   */
+  getBounds: function() {
+    if (this.bounds == null) {
+      this.calculateBounds();
+    }
+    return this.bounds;
+  },
+  /** 
+   * Method: calculateBounds
+   * Recalculate the bounds for the geometry. 
+   */
+  calculateBounds: function() {
+    // This should be overridden by subclasses.
+    return this.bounds;
+  },
+  distanceTo: function(geometry, options) {
+  },
+  getVertices: function(nodes) {
+  },
+  getLength: function() {
+    return 0.0;
+  },
+  getArea: function() {
+    return 0.0;
+  },
+  getCentroid: function() {
+    return null;
+  },
+  /**
+   * Method: toString
+   * Returns the Well-Known Text representation of a geometry
+   *
+   * Returns:
+   * {String} Well-Known Text
+   */
+  toString: function() {
+    return ZOO.Format.WKT.prototype.write(
+        new ZOO.Feature(this)
+    );
+  },
+  CLASS_NAME: 'ZOO.Geometry'
+});
+/**
+ * Function: OpenLayers.Geometry.fromWKT
+ * Generate a geometry given a Well-Known Text string.
+ *
+ * Parameters:
+ * wkt - {String} A string representing the geometry in Well-Known Text.
+ *
+ * Returns:
+ * {<ZOO.Geometry>} A geometry of the appropriate class.
+ */
+ZOO.Geometry.fromWKT = function(wkt) {
+  var format = arguments.callee.format;
+  if(!format) {
+    format = new ZOO.Format.WKT();
+    arguments.callee.format = format;
+  }
+  var geom;
+  var result = format.read(wkt);
+  if(result instanceof ZOO.Feature) {
+    geom = result.geometry;
+  } else if(result instanceof Array) {
+    var len = result.length;
+    var components = new Array(len);
+    for(var i=0; i<len; ++i) {
+      components[i] = result[i].geometry;
+    }
+    geom = new ZOO.Geometry.Collection(components);
+  }
+  return geom;
+};
+ZOO.Geometry.segmentsIntersect = function(seg1, seg2, options) {
+  var point = options && options.point;
+  var tolerance = options && options.tolerance;
+  var intersection = false;
+  var x11_21 = seg1.x1 - seg2.x1;
+  var y11_21 = seg1.y1 - seg2.y1;
+  var x12_11 = seg1.x2 - seg1.x1;
+  var y12_11 = seg1.y2 - seg1.y1;
+  var y22_21 = seg2.y2 - seg2.y1;
+  var x22_21 = seg2.x2 - seg2.x1;
+  var d = (y22_21 * x12_11) - (x22_21 * y12_11);
+  var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
+  var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
+  if(d == 0) {
+    // parallel
+    if(n1 == 0 && n2 == 0) {
+      // coincident
+      intersection = true;
+    }
+  } else {
+    var along1 = n1 / d;
+    var along2 = n2 / d;
+    if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
+      // intersect
+      if(!point) {
+        intersection = true;
+      } else {
+        // calculate the intersection point
+        var x = seg1.x1 + (along1 * x12_11);
+        var y = seg1.y1 + (along1 * y12_11);
+        intersection = new ZOO.Geometry.Point(x, y);
+      }
+    }
+  }
+  if(tolerance) {
+    var dist;
+    if(intersection) {
+      if(point) {
+        var segs = [seg1, seg2];
+        var seg, x, y;
+        // check segment endpoints for proximity to intersection
+        // set intersection to first endpoint within the tolerance
+        outer: for(var i=0; i<2; ++i) {
+          seg = segs[i];
+          for(var j=1; j<3; ++j) {
+            x = seg["x" + j];
+            y = seg["y" + j];
+            dist = Math.sqrt(
+                Math.pow(x - intersection.x, 2) +
+                Math.pow(y - intersection.y, 2)
+            );
+            if(dist < tolerance) {
+              intersection.x = x;
+              intersection.y = y;
+              break outer;
+            }
+          }
+        }
+      }
+    } else {
+      // no calculated intersection, but segments could be within
+      // the tolerance of one another
+      var segs = [seg1, seg2];
+      var source, target, x, y, p, result;
+      // check segment endpoints for proximity to intersection
+      // set intersection to first endpoint within the tolerance
+      outer: for(var i=0; i<2; ++i) {
+        source = segs[i];
+        target = segs[(i+1)%2];
+        for(var j=1; j<3; ++j) {
+          p = {x: source["x"+j], y: source["y"+j]};
+          result = ZOO.Geometry.distanceToSegment(p, target);
+          if(result.distance < tolerance) {
+            if(point) {
+              intersection = new ZOO.Geometry.Point(p.x, p.y);
+            } else {
+              intersection = true;
+            }
+            break outer;
+          }
+        }
+      }
+    }
+  }
+  return intersection;
+};
+ZOO.Geometry.distanceToSegment = function(point, segment) {
+  var x0 = point.x;
+  var y0 = point.y;
+  var x1 = segment.x1;
+  var y1 = segment.y1;
+  var x2 = segment.x2;
+  var y2 = segment.y2;
+  var dx = x2 - x1;
+  var dy = y2 - y1;
+  var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
+               (Math.pow(dx, 2) + Math.pow(dy, 2));
+  var x, y;
+  if(along <= 0.0) {
+    x = x1;
+    y = y1;
+  } else if(along >= 1.0) {
+    x = x2;
+    y = y2;
+  } else {
+    x = x1 + along * dx;
+    y = y1 + along * dy;
+  }
+  return {
+    distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)),
+    x: x, y: y
+  };
+};
+/**
+ * Class: OpenLayers.Geometry.Collection
+ * A Collection is exactly what it sounds like: A collection of different 
+ * Geometries. These are stored in the local parameter <components> (which
+ * can be passed as a parameter to the constructor). 
+ * 
+ * As new geometries are added to the collection, they are NOT cloned. 
+ * When removing geometries, they need to be specified by reference (ie you 
+ * have to pass in the *exact* geometry to be removed).
+ * 
+ * The <getArea> and <getLength> functions here merely iterate through
+ * the components, summing their respective areas and lengths.
+ *
+ * Create a new instance with the <ZOO.Geometry.Collection> constructor.
+ *
+ * Inerhits from:
+ *  - <ZOO.Geometry> 
+ */
+ZOO.Geometry.Collection = ZOO.Class(ZOO.Geometry, {
+  /**
+   * Property: components
+   * {Array(<ZOO.Geometry>)} The component parts of this geometry
+   */
+  components: null,
+  /**
+   * Property: componentTypes
+   * {Array(String)} An array of class names representing the types of
+   * components that the collection can include.  A null value means the
+   * component types are not restricted.
+   */
+  componentTypes: null,
+  /**
+   * Constructor: ZOO.Geometry.Collection
+   * Creates a Geometry Collection -- a list of geoms.
+   *
+   * Parameters: 
+   * components - {Array(<ZOO.Geometry>)} Optional array of geometries
+   *
+   */
+  initialize: function (components) {
+    ZOO.Geometry.prototype.initialize.apply(this, arguments);
+    this.components = [];
+    if (components != null) {
+      this.addComponents(components);
+    }
+  },
+  /**
+   * Method: destroy
+   * Destroy this geometry.
+   */
+  destroy: function () {
+    this.components.length = 0;
+    this.components = null;
+  },
+  /**
+   * Method: clone
+   * Clone this geometry.
+   *
+   * Returns:
+   * {<ZOO.Geometry.Collection>} An exact clone of this collection
+   */
+  clone: function() {
+    var geometry = eval("new " + this.CLASS_NAME + "()");
+    for(var i=0, len=this.components.length; i<len; i++) {
+      geometry.addComponent(this.components[i].clone());
+    }
+    return geometry;
+  },
+  /**
+   * Method: getComponentsString
+   * Get a string representing the components for this collection
+   * 
+   * Returns:
+   * {String} A string representation of the components of this geometry
+   */
+  getComponentsString: function(){
+    var strings = [];
+    for(var i=0, len=this.components.length; i<len; i++) {
+      strings.push(this.components[i].toShortString()); 
+    }
+    return strings.join(",");
+  },
+  /**
+   * Method: calculateBounds
+   * Recalculate the bounds by iterating through the components and 
+   * calling calling extendBounds() on each item.
+   */
+  calculateBounds: function() {
+    this.bounds = null;
+    if ( this.components && this.components.length > 0) {
+      this.setBounds(this.components[0].getBounds());
+      for (var i=1, len=this.components.length; i<len; i++) {
+        this.extendBounds(this.components[i].getBounds());
+      }
+    }
+    return this.bounds
+  },
+  /**
+   * APIMethod: addComponents
+   * Add components to this geometry.
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry>)} An array of geometries to add
+   */
+  addComponents: function(components){
+    if(!(components instanceof Array))
+      components = [components];
+    for(var i=0, len=components.length; i<len; i++) {
+      this.addComponent(components[i]);
+    }
+  },
+  /**
+   * Method: addComponent
+   * Add a new component (geometry) to the collection.  If this.componentTypes
+   * is set, then the component class name must be in the componentTypes array.
+   *
+   * The bounds cache is reset.
+   * 
+   * Parameters:
+   * component - {<ZOO.Geometry>} A geometry to add
+   * index - {int} Optional index into the array to insert the component
+   *
+   * Returns:
+   * {Boolean} The component geometry was successfully added
+   */
+  addComponent: function(component, index) {
+    var added = false;
+    if(component) {
+      if(this.componentTypes == null ||
+          (ZOO.indexOf(this.componentTypes,
+                       component.CLASS_NAME) > -1)) {
+        if(index != null && (index < this.components.length)) {
+          var components1 = this.components.slice(0, index);
+          var components2 = this.components.slice(index, 
+                                                  this.components.length);
+          components1.push(component);
+          this.components = components1.concat(components2);
+        } else {
+          this.components.push(component);
+        }
+        component.parent = this;
+        this.clearBounds();
+        added = true;
+      }
+    }
+    return added;
+  },
+  /**
+   * Method: removeComponents
+   * Remove components from this geometry.
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry>)} The components to be removed
+   */
+  removeComponents: function(components) {
+    if(!(components instanceof Array))
+      components = [components];
+    for(var i=components.length-1; i>=0; --i) {
+      this.removeComponent(components[i]);
+    }
+  },
+  /**
+   * Method: removeComponent
+   * Remove a component from this geometry.
+   *
+   * Parameters:
+   * component - {<ZOO.Geometry>} 
+   */
+  removeComponent: function(component) {      
+    ZOO.removeItem(this.components, component);
+    // clearBounds() so that it gets recalculated on the next call
+    // to this.getBounds();
+    this.clearBounds();
+  },
+  /**
+   * Method: getLength
+   * Calculate the length of this geometry
+   *
+   * Returns:
+   * {Float} The length of the geometry
+   */
+  getLength: function() {
+    var length = 0.0;
+    for (var i=0, len=this.components.length; i<len; i++) {
+      length += this.components[i].getLength();
+    }
+    return length;
+  },
+  /**
+   * APIMethod: getArea
+   * Calculate the area of this geometry. Note how this function is 
+   * overridden in <ZOO.Geometry.Polygon>.
+   *
+   * Returns:
+   * {Float} The area of the collection by summing its parts
+   */
+  getArea: function() {
+    var area = 0.0;
+    for (var i=0, len=this.components.length; i<len; i++) {
+      area += this.components[i].getArea();
+    }
+    return area;
+  },
+  /** 
+   * APIMethod: getGeodesicArea
+   * Calculate the approximate area of the polygon were it projected onto
+   *     the earth.
+   *
+   * Parameters:
+   * projection - {<ZOO.Projection>} The spatial reference system
+   *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+   *     assumed.
+   * 
+   * Reference:
+   * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+   *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+   *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+   *
+   * Returns:
+   * {float} The approximate geodesic area of the geometry in square meters.
+   */
+  getGeodesicArea: function(projection) {
+    var area = 0.0;
+    for(var i=0, len=this.components.length; i<len; i++) {
+      area += this.components[i].getGeodesicArea(projection);
+    }
+    return area;
+  },
+  /**
+   * Method: getCentroid
+   *
+   * Returns:
+   * {<ZOO.Geometry.Point>} The centroid of the collection
+   */
+  getCentroid: function() {
+    return this.components.length && this.components[0].getCentroid();
+  },
+  /**
+   * Method: getGeodesicLength
+   * Calculate the approximate length of the geometry were it projected onto
+   *     the earth.
+   *
+   * projection - {<ZOO.Projection>} The spatial reference system
+   *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+   *     assumed.
+   * 
+   * Returns:
+   * {Float} The appoximate geodesic length of the geometry in meters.
+   */
+  getGeodesicLength: function(projection) {
+    var length = 0.0;
+    for(var i=0, len=this.components.length; i<len; i++) {
+      length += this.components[i].getGeodesicLength(projection);
+    }
+    return length;
+  },
+  /**
+   * Method: move
+   * Moves a geometry by the given displacement along positive x and y axes.
+   *     This modifies the position of the geometry and clears the cached
+   *     bounds.
+   *
+   * Parameters:
+   * x - {Float} Distance to move geometry in positive x direction. 
+   * y - {Float} Distance to move geometry in positive y direction.
+   */
+  move: function(x, y) {
+    for(var i=0, len=this.components.length; i<len; i++) {
+      this.components[i].move(x, y);
+    }
+  },
+  /**
+   * Method: rotate
+   * Rotate a geometry around some origin
+   *
+   * Parameters:
+   * angle - {Float} Rotation angle in degrees (measured counterclockwise
+   *                 from the positive x-axis)
+   * origin - {<ZOO.Geometry.Point>} Center point for the rotation
+   */
+  rotate: function(angle, origin) {
+    for(var i=0, len=this.components.length; i<len; ++i) {
+      this.components[i].rotate(angle, origin);
+    }
+  },
+  /**
+   * Method: resize
+   * Resize a geometry relative to some origin.  Use this method to apply
+   *     a uniform scaling to a geometry.
+   *
+   * Parameters:
+   * scale - {Float} Factor by which to scale the geometry.  A scale of 2
+   *                 doubles the size of the geometry in each dimension
+   *                 (lines, for example, will be twice as long, and polygons
+   *                 will have four times the area).
+   * origin - {<ZOO.Geometry.Point>} Point of origin for resizing
+   * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+   * 
+   * Returns:
+   * {ZOO.Geometry} - The current geometry. 
+   */
+  resize: function(scale, origin, ratio) {
+    for(var i=0; i<this.components.length; ++i) {
+      this.components[i].resize(scale, origin, ratio);
+    }
+    return this;
+  },
+  distanceTo: function(geometry, options) {
+    var edge = !(options && options.edge === false);
+    var details = edge && options && options.details;
+    var result, best;
+    var min = Number.POSITIVE_INFINITY;
+    for(var i=0, len=this.components.length; i<len; ++i) {
+      result = this.components[i].distanceTo(geometry, options);
+      distance = details ? result.distance : result;
+      if(distance < min) {
+        min = distance;
+        best = result;
+        if(min == 0)
+          break;
+      }
+    }
+    return best;
+  },
+  /** 
+   * Method: equals
+   * Determine whether another geometry is equivalent to this one.  Geometries
+   *     are considered equivalent if all components have the same coordinates.
+   * 
+   * Parameters:
+   * geom - {<ZOO.Geometry>} The geometry to test. 
+   *
+   * Returns:
+   * {Boolean} The supplied geometry is equivalent to this geometry.
+   */
+  equals: function(geometry) {
+    var equivalent = true;
+    if(!geometry || !geometry.CLASS_NAME ||
+       (this.CLASS_NAME != geometry.CLASS_NAME))
+      equivalent = false;
+    else if(!(geometry.components instanceof Array) ||
+             (geometry.components.length != this.components.length))
+      equivalent = false;
+    else
+      for(var i=0, len=this.components.length; i<len; ++i) {
+        if(!this.components[i].equals(geometry.components[i])) {
+          equivalent = false;
+          break;
+        }
+      }
+    return equivalent;
+  },
+  /**
+   * Method: transform
+   * Reproject the components geometry from source to dest.
+   * 
+   * Parameters:
+   * source - {<ZOO.Projection>} 
+   * dest - {<ZOO.Projection>}
+   * 
+   * Returns:
+   * {<ZOO.Geometry>} 
+   */
+  transform: function(source, dest) {
+    if (source && dest) {
+      for (var i=0, len=this.components.length; i<len; i++) {  
+        var component = this.components[i];
+        component.transform(source, dest);
+      }
+      this.bounds = null;
+    }
+    return this;
+  },
+  /**
+   * Method: intersects
+   * Determine if the input geometry intersects this one.
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} Any type of geometry.
+   *
+   * Returns:
+   * {Boolean} The input geometry intersects this one.
+   */
+  intersects: function(geometry) {
+    var intersect = false;
+    for(var i=0, len=this.components.length; i<len; ++ i) {
+      intersect = geometry.intersects(this.components[i]);
+      if(intersect)
+        break;
+    }
+    return intersect;
+  },
+  /**
+   * Method: getVertices
+   * Return a list of all points in this geometry.
+   *
+   * Parameters:
+   * nodes - {Boolean} For lines, only return vertices that are
+   *     endpoints.  If false, for lines, only vertices that are not
+   *     endpoints will be returned.  If not provided, all vertices will
+   *     be returned.
+   *
+   * Returns:
+   * {Array} A list of all vertices in the geometry.
+   */
+  getVertices: function(nodes) {
+    var vertices = [];
+    for(var i=0, len=this.components.length; i<len; ++i) {
+      Array.prototype.push.apply(
+          vertices, this.components[i].getVertices(nodes)
+          );
+    }
+    return vertices;
+  },
+  CLASS_NAME: 'ZOO.Geometry.Collection'
+});
+/**
+ * Class: ZOO.Geometry.Point
+ * Point geometry class. 
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry> 
+ */
+ZOO.Geometry.Point = ZOO.Class(ZOO.Geometry, {
+  /** 
+   * Property: x 
+   * {float} 
+   */
+  x: null,
+  /** 
+   * Property: y 
+   * {float} 
+   */
+  y: null,
+  /**
+   * Constructor: ZOO.Geometry.Point
+   * Construct a point geometry.
+   *
+   * Parameters:
+   * x - {float} 
+   * y - {float}
+   * 
+   */
+  initialize: function(x, y) {
+    ZOO.Geometry.prototype.initialize.apply(this, arguments);
+    this.x = parseFloat(x);
+    this.y = parseFloat(y);
+  },
+  /**
+   * Method: clone
+   * 
+   * Returns:
+   * {<ZOO.Geometry.Point>} An exact clone of this ZOO.Geometry.Point
+   */
+  clone: function(obj) {
+    if (obj == null)
+      obj = new ZOO.Geometry.Point(this.x, this.y);
+    // catch any randomly tagged-on properties
+    // ZOO.Util.applyDefaults(obj, this);
+    return obj;
+  },
+  /** 
+   * Method: calculateBounds
+   * Create a new Bounds based on the x/y
+   */
+  calculateBounds: function () {
+    this.bounds = new ZOO.Bounds(this.x, this.y,
+                                        this.x, this.y);
+  },
+  distanceTo: function(geometry, options) {
+    var edge = !(options && options.edge === false);
+    var details = edge && options && options.details;
+    var distance, x0, y0, x1, y1, result;
+    if(geometry instanceof ZOO.Geometry.Point) {
+      x0 = this.x;
+      y0 = this.y;
+      x1 = geometry.x;
+      y1 = geometry.y;
+      distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
+      result = !details ?
+        distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance};
+    } else {
+      result = geometry.distanceTo(this, options);
+      if(details) {
+        // switch coord order since this geom is target
+        result = {
+          x0: result.x1, y0: result.y1,
+          x1: result.x0, y1: result.y0,
+          distance: result.distance
+        };
+      }
+    }
+    return result;
+  },
+  /** 
+   * Method: equals
+   * Determine whether another geometry is equivalent to this one.  Geometries
+   *     are considered equivalent if all components have the same coordinates.
+   * 
+   * Parameters:
+   * geom - {<ZOO.Geometry.Point>} The geometry to test. 
+   *
+   * Returns:
+   * {Boolean} The supplied geometry is equivalent to this geometry.
+   */
+  equals: function(geom) {
+    var equals = false;
+    if (geom != null)
+      equals = ((this.x == geom.x && this.y == geom.y) ||
+                (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
+    return equals;
+  },
+  /**
+   * Method: toShortString
+   *
+   * Returns:
+   * {String} Shortened String representation of Point object. 
+   *         (ex. <i>"5, 42"</i>)
+   */
+  toShortString: function() {
+    return (this.x + ", " + this.y);
+  },
+  /**
+   * Method: move
+   * Moves a geometry by the given displacement along positive x and y axes.
+   *     This modifies the position of the geometry and clears the cached
+   *     bounds.
+   *
+   * Parameters:
+   * x - {Float} Distance to move geometry in positive x direction. 
+   * y - {Float} Distance to move geometry in positive y direction.
+   */
+  move: function(x, y) {
+    this.x = this.x + x;
+    this.y = this.y + y;
+    this.clearBounds();
+  },
+  /**
+   * Method: rotate
+   * Rotate a point around another.
+   *
+   * Parameters:
+   * angle - {Float} Rotation angle in degrees (measured counterclockwise
+   *                 from the positive x-axis)
+   * origin - {<ZOO.Geometry.Point>} Center point for the rotation
+   */
+  rotate: function(angle, origin) {
+        angle *= Math.PI / 180;
+        var radius = this.distanceTo(origin);
+        var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
+        this.x = origin.x + (radius * Math.cos(theta));
+        this.y = origin.y + (radius * Math.sin(theta));
+        this.clearBounds();
+  },
+  /**
+   * Method: getCentroid
+   *
+   * Returns:
+   * {<ZOO.Geometry.Point>} The centroid of the collection
+   */
+  getCentroid: function() {
+    return new ZOO.Geometry.Point(this.x, this.y);
+  },
+  /**
+   * Method: resize
+   * Resize a point relative to some origin.  For points, this has the effect
+   *     of scaling a vector (from the origin to the point).  This method is
+   *     more useful on geometry collection subclasses.
+   *
+   * Parameters:
+   * scale - {Float} Ratio of the new distance from the origin to the old
+   *                 distance from the origin.  A scale of 2 doubles the
+   *                 distance between the point and origin.
+   * origin - {<ZOO.Geometry.Point>} Point of origin for resizing
+   * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+   * 
+   * Returns:
+   * {ZOO.Geometry} - The current geometry. 
+   */
+  resize: function(scale, origin, ratio) {
+    ratio = (ratio == undefined) ? 1 : ratio;
+    this.x = origin.x + (scale * ratio * (this.x - origin.x));
+    this.y = origin.y + (scale * (this.y - origin.y));
+    this.clearBounds();
+    return this;
+  },
+  /**
+   * Method: intersects
+   * Determine if the input geometry intersects this one.
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} Any type of geometry.
+   *
+   * Returns:
+   * {Boolean} The input geometry intersects this one.
+   */
+  intersects: function(geometry) {
+    var intersect = false;
+    if(geometry.CLASS_NAME == "ZOO.Geometry.Point") {
+      intersect = this.equals(geometry);
+    } else {
+      intersect = geometry.intersects(this);
+    }
+    return intersect;
+  },
+  /**
+   * Method: transform
+   * Translate the x,y properties of the point from source to dest.
+   * 
+   * Parameters:
+   * source - {<ZOO.Projection>} 
+   * dest - {<ZOO.Projection>}
+   * 
+   * Returns:
+   * {<ZOO.Geometry>} 
+   */
+  transform: function(source, dest) {
+    if ((source && dest)) {
+      ZOO.Projection.transform(
+          this, source, dest); 
+      this.bounds = null;
+    }       
+    return this;
+  },
+  /**
+   * Method: getVertices
+   * Return a list of all points in this geometry.
+   *
+   * Parameters:
+   * nodes - {Boolean} For lines, only return vertices that are
+   *     endpoints.  If false, for lines, only vertices that are not
+   *     endpoints will be returned.  If not provided, all vertices will
+   *     be returned.
+   *
+   * Returns:
+   * {Array} A list of all vertices in the geometry.
+   */
+  getVertices: function(nodes) {
+    return [this];
+  },
+  CLASS_NAME: 'ZOO.Geometry.Point'
+});
+/**
+ * Class: ZOO.Geometry.Surface
+ * Surface geometry class. 
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry> 
+ */
+ZOO.Geometry.Surface = ZOO.Class(ZOO.Geometry, {
+  initialize: function() {
+    ZOO.Geometry.prototype.initialize.apply(this, arguments);
+  },
+  CLASS_NAME: "ZOO.Geometry.Surface"
+});
+/**
+ * Class: ZOO.Geometry.MultiPoint
+ * MultiPoint is a collection of Points. Create a new instance with the
+ * <ZOO.Geometry.MultiPoint> constructor.
+ *
+ * Inherits from:
+ *  - <ZOO.Geometry.Collection>
+ */
+ZOO.Geometry.MultiPoint = ZOO.Class(
+  ZOO.Geometry.Collection, {
+  /**
+   * Property: componentTypes
+   * {Array(String)} An array of class names representing the types of
+   * components that the collection can include.  A null value means the
+   * component types are not restricted.
+   */
+  componentTypes: ["ZOO.Geometry.Point"],
+  /**
+   * Constructor: ZOO.Geometry.MultiPoint
+   * Create a new MultiPoint Geometry
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry.Point>)} 
+   *
+   * Returns:
+   * {<ZOO.Geometry.MultiPoint>}
+   */
+  initialize: function(components) {
+    ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+  },
+  /**
+   * Method: addPoint
+   * Wrapper for <ZOO.Geometry.Collection.addComponent>
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>} Point to be added
+   * index - {Integer} Optional index
+   */
+  addPoint: function(point, index) {
+    this.addComponent(point, index);
+  },
+  /**
+   * Method: removePoint
+   * Wrapper for <ZOO.Geometry.Collection.removeComponent>
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>} Point to be removed
+   */
+  removePoint: function(point){
+    this.removeComponent(point);
+  },
+  CLASS_NAME: "ZOO.Geometry.MultiPoint"
+});
+/**
+ * Class: ZOO.Geometry.Curve
+ * A Curve is a MultiPoint, whose points are assumed to be connected. To 
+ * this end, we provide a "getLength()" function, which iterates through 
+ * the points, summing the distances between them. 
+ * 
+ * Inherits: 
+ *  - <ZOO.Geometry.MultiPoint>
+ */
+ZOO.Geometry.Curve = ZOO.Class(ZOO.Geometry.MultiPoint, {
+  /**
+   * Property: componentTypes
+   * {Array(String)} An array of class names representing the types of 
+   *                 components that the collection can include.  A null 
+   *                 value means the component types are not restricted.
+   */
+  componentTypes: ["ZOO.Geometry.Point"],
+  /**
+   * Constructor: ZOO.Geometry.Curve
+   * 
+   * Parameters:
+   * point - {Array(<ZOO.Geometry.Point>)}
+   */
+  initialize: function(points) {
+    ZOO.Geometry.MultiPoint.prototype.initialize.apply(this,arguments);
+  },
+  /**
+   * Method: getLength
+   * 
+   * Returns:
+   * {Float} The length of the curve
+   */
+  getLength: function() {
+    var length = 0.0;
+    if ( this.components && (this.components.length > 1)) {
+      for(var i=1, len=this.components.length; i<len; i++) {
+        length += this.components[i-1].distanceTo(this.components[i]);
+      }
+    }
+    return length;
+  },
+  /**
+     * APIMethod: getGeodesicLength
+     * Calculate the approximate length of the geometry were it projected onto
+     *     the earth.
+     *
+     * projection - {<ZOO.Projection>} The spatial reference system
+     *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+     *     assumed.
+     * 
+     * Returns:
+     * {Float} The appoximate geodesic length of the geometry in meters.
+     */
+    getGeodesicLength: function(projection) {
+      var geom = this;  // so we can work with a clone if needed
+      if(projection) {
+        var gg = new ZOO.Projection("EPSG:4326");
+        if(!gg.equals(projection)) {
+          geom = this.clone().transform(projection, gg);
+       }
+     }
+     var length = 0.0;
+     if(geom.components && (geom.components.length > 1)) {
+       var p1, p2;
+       for(var i=1, len=geom.components.length; i<len; i++) {
+         p1 = geom.components[i-1];
+         p2 = geom.components[i];
+        // this returns km and requires x/y properties
+        length += ZOO.distVincenty(p1,p2);
+      }
+    }
+    // convert to m
+    return length * 1000;
+  },
+  CLASS_NAME: "ZOO.Geometry.Curve"
+});
+/**
+ * Class: ZOO.Geometry.LineString
+ * A LineString is a Curve which, once two points have been added to it, can 
+ * never be less than two points long.
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry.Curve>
+ */
+ZOO.Geometry.LineString = ZOO.Class(ZOO.Geometry.Curve, {
+  /**
+   * Constructor: ZOO.Geometry.LineString
+   * Create a new LineString geometry
+   *
+   * Parameters:
+   * points - {Array(<ZOO.Geometry.Point>)} An array of points used to
+   *          generate the linestring
+   *
+   */
+  initialize: function(points) {
+    ZOO.Geometry.Curve.prototype.initialize.apply(this, arguments);        
+  },
+  /**
+   * Method: removeComponent
+   * Only allows removal of a point if there are three or more points in 
+   * the linestring. (otherwise the result would be just a single point)
+   *
+   * Parameters: 
+   * point - {<ZOO.Geometry.Point>} The point to be removed
+   */
+  removeComponent: function(point) {
+    if ( this.components && (this.components.length > 2))
+      ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments);
+  },
+  /**
+   * Method: intersects
+   * Test for instersection between two geometries.  This is a cheapo
+   *     implementation of the Bently-Ottmann algorigithm.  It doesn't
+   *     really keep track of a sweep line data structure.  It is closer
+   *     to the brute force method, except that segments are sorted and
+   *     potential intersections are only calculated when bounding boxes
+   *     intersect.
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>}
+   *
+   * Returns:
+   * {Boolean} The input geometry intersects this geometry.
+   */
+  intersects: function(geometry) {
+    var intersect = false;
+    var type = geometry.CLASS_NAME;
+    if(type == "ZOO.Geometry.LineString" ||
+       type == "ZOO.Geometry.LinearRing" ||
+       type == "ZOO.Geometry.Point") {
+      var segs1 = this.getSortedSegments();
+      var segs2;
+      if(type == "ZOO.Geometry.Point")
+        segs2 = [{
+          x1: geometry.x, y1: geometry.y,
+          x2: geometry.x, y2: geometry.y
+        }];
+      else
+        segs2 = geometry.getSortedSegments();
+      var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
+          seg2, seg2y1, seg2y2;
+      // sweep right
+      outer: for(var i=0, len=segs1.length; i<len; ++i) {
+         seg1 = segs1[i];
+         seg1x1 = seg1.x1;
+         seg1x2 = seg1.x2;
+         seg1y1 = seg1.y1;
+         seg1y2 = seg1.y2;
+         inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
+           seg2 = segs2[j];
+           if(seg2.x1 > seg1x2)
+             break;
+           if(seg2.x2 < seg1x1)
+             continue;
+           seg2y1 = seg2.y1;
+           seg2y2 = seg2.y2;
+           if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2))
+             continue;
+           if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2))
+             continue;
+           if(ZOO.Geometry.segmentsIntersect(seg1, seg2)) {
+             intersect = true;
+             break outer;
+           }
+         }
+      }
+    } else {
+      intersect = geometry.intersects(this);
+    }
+    return intersect;
+  },
+  /**
+   * Method: getSortedSegments
+   *
+   * Returns:
+   * {Array} An array of segment objects.  Segment objects have properties
+   *     x1, y1, x2, and y2.  The start point is represented by x1 and y1.
+   *     The end point is represented by x2 and y2.  Start and end are
+   *     ordered so that x1 < x2.
+   */
+  getSortedSegments: function() {
+    var numSeg = this.components.length - 1;
+    var segments = new Array(numSeg);
+    for(var i=0; i<numSeg; ++i) {
+      point1 = this.components[i];
+      point2 = this.components[i + 1];
+      if(point1.x < point2.x)
+        segments[i] = {
+          x1: point1.x,
+          y1: point1.y,
+          x2: point2.x,
+          y2: point2.y
+        };
+      else
+        segments[i] = {
+          x1: point2.x,
+          y1: point2.y,
+          x2: point1.x,
+          y2: point1.y
+        };
+    }
+    // more efficient to define this somewhere static
+    function byX1(seg1, seg2) {
+      return seg1.x1 - seg2.x1;
+    }
+    return segments.sort(byX1);
+  },
+  /**
+   * Method: splitWithSegment
+   * Split this geometry with the given segment.
+   *
+   * Parameters:
+   * seg - {Object} An object with x1, y1, x2, and y2 properties referencing
+   *     segment endpoint coordinates.
+   * options - {Object} Properties of this object will be used to determine
+   *     how the split is conducted.
+   *
+   * Valid options:
+   * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+   *     true.  If false, a vertex on the source segment must be within the
+   *     tolerance distance of the intersection to be considered a split.
+   * tolerance - {Number} If a non-null value is provided, intersections
+   *     within the tolerance distance of one of the source segment's
+   *     endpoints will be assumed to occur at the endpoint.
+   *
+   * Returns:
+   * {Object} An object with *lines* and *points* properties.  If the given
+   *     segment intersects this linestring, the lines array will reference
+   *     geometries that result from the split.  The points array will contain
+   *     all intersection points.  Intersection points are sorted along the
+   *     segment (in order from x1,y1 to x2,y2).
+   */
+  splitWithSegment: function(seg, options) {
+    var edge = !(options && options.edge === false);
+    var tolerance = options && options.tolerance;
+    var lines = [];
+    var verts = this.getVertices();
+    var points = [];
+    var intersections = [];
+    var split = false;
+    var vert1, vert2, point;
+    var node, vertex, target;
+    var interOptions = {point: true, tolerance: tolerance};
+    var result = null;
+    for(var i=0, stop=verts.length-2; i<=stop; ++i) {
+      vert1 = verts[i];
+      points.push(vert1.clone());
+      vert2 = verts[i+1];
+      target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y};
+      point = ZOO.Geometry.segmentsIntersect(seg, target, interOptions);
+      if(point instanceof ZOO.Geometry.Point) {
+        if((point.x === seg.x1 && point.y === seg.y1) ||
+           (point.x === seg.x2 && point.y === seg.y2) ||
+            point.equals(vert1) || point.equals(vert2))
+          vertex = true;
+        else
+          vertex = false;
+        if(vertex || edge) {
+          // push intersections different than the previous
+          if(!point.equals(intersections[intersections.length-1]))
+            intersections.push(point.clone());
+          if(i === 0) {
+            if(point.equals(vert1))
+              continue;
+          }
+          if(point.equals(vert2))
+            continue;
+          split = true;
+          if(!point.equals(vert1))
+            points.push(point);
+          lines.push(new ZOO.Geometry.LineString(points));
+          points = [point.clone()];
+        }
+      }
+    }
+    if(split) {
+      points.push(vert2.clone());
+      lines.push(new ZOO.Geometry.LineString(points));
+    }
+    if(intersections.length > 0) {
+      // sort intersections along segment
+      var xDir = seg.x1 < seg.x2 ? 1 : -1;
+      var yDir = seg.y1 < seg.y2 ? 1 : -1;
+      result = {
+        lines: lines,
+        points: intersections.sort(function(p1, p2) {
+           return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
+        })
+      };
+    }
+    return result;
+  },
+  /**
+   * Method: split
+   * Use this geometry (the source) to attempt to split a target geometry.
+   * 
+   * Parameters:
+   * target - {<ZOO.Geometry>} The target geometry.
+   * options - {Object} Properties of this object will be used to determine
+   *     how the split is conducted.
+   *
+   * Valid options:
+   * mutual - {Boolean} Split the source geometry in addition to the target
+   *     geometry.  Default is false.
+   * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+   *     true.  If false, a vertex on the source must be within the tolerance
+   *     distance of the intersection to be considered a split.
+   * tolerance - {Number} If a non-null value is provided, intersections
+   *     within the tolerance distance of an existing vertex on the source
+   *     will be assumed to occur at the vertex.
+   * 
+   * Returns:
+   * {Array} A list of geometries (of this same type as the target) that
+   *     result from splitting the target with the source geometry.  The
+   *     source and target geometry will remain unmodified.  If no split
+   *     results, null will be returned.  If mutual is true and a split
+   *     results, return will be an array of two arrays - the first will be
+   *     all geometries that result from splitting the source geometry and
+   *     the second will be all geometries that result from splitting the
+   *     target geometry.
+   */
+  split: function(target, options) {
+    var results = null;
+    var mutual = options && options.mutual;
+    var sourceSplit, targetSplit, sourceParts, targetParts;
+    if(target instanceof ZOO.Geometry.LineString) {
+      var verts = this.getVertices();
+      var vert1, vert2, seg, splits, lines, point;
+      var points = [];
+      sourceParts = [];
+      for(var i=0, stop=verts.length-2; i<=stop; ++i) {
+        vert1 = verts[i];
+        vert2 = verts[i+1];
+        seg = {
+          x1: vert1.x, y1: vert1.y,
+          x2: vert2.x, y2: vert2.y
+        };
+        targetParts = targetParts || [target];
+        if(mutual)
+          points.push(vert1.clone());
+        for(var j=0; j<targetParts.length; ++j) {
+          splits = targetParts[j].splitWithSegment(seg, options);
+          if(splits) {
+            // splice in new features
+            lines = splits.lines;
+            if(lines.length > 0) {
+              lines.unshift(j, 1);
+              Array.prototype.splice.apply(targetParts, lines);
+              j += lines.length - 2;
+            }
+            if(mutual) {
+              for(var k=0, len=splits.points.length; k<len; ++k) {
+                point = splits.points[k];
+                if(!point.equals(vert1)) {
+                  points.push(point);
+                  sourceParts.push(new ZOO.Geometry.LineString(points));
+                  if(point.equals(vert2))
+                    points = [];
+                  else
+                    points = [point.clone()];
+                }
+              }
+            }
+          }
+        }
+      }
+      if(mutual && sourceParts.length > 0 && points.length > 0) {
+        points.push(vert2.clone());
+        sourceParts.push(new ZOO.Geometry.LineString(points));
+      }
+    } else {
+      results = target.splitWith(this, options);
+    }
+    if(targetParts && targetParts.length > 1)
+      targetSplit = true;
+    else
+      targetParts = [];
+    if(sourceParts && sourceParts.length > 1)
+      sourceSplit = true;
+    else
+      sourceParts = [];
+    if(targetSplit || sourceSplit) {
+      if(mutual)
+        results = [sourceParts, targetParts];
+      else
+        results = targetParts;
+    }
+    return results;
+  },
+  /**
+   * Method: splitWith
+   * Split this geometry (the target) with the given geometry (the source).
+   *
+   * Parameters:
+   * geometry - {<ZOO.Geometry>} A geometry used to split this
+   *     geometry (the source).
+   * options - {Object} Properties of this object will be used to determine
+   *     how the split is conducted.
+   *
+   * Valid options:
+   * mutual - {Boolean} Split the source geometry in addition to the target
+   *     geometry.  Default is false.
+   * edge - {Boolean} Allow splitting when only edges intersect.  Default is
+   *     true.  If false, a vertex on the source must be within the tolerance
+   *     distance of the intersection to be considered a split.
+   * tolerance - {Number} If a non-null value is provided, intersections
+   *     within the tolerance distance of an existing vertex on the source
+   *     will be assumed to occur at the vertex.
+   * 
+   * Returns:
+   * {Array} A list of geometries (of this same type as the target) that
+   *     result from splitting the target with the source geometry.  The
+   *     source and target geometry will remain unmodified.  If no split
+   *     results, null will be returned.  If mutual is true and a split
+   *     results, return will be an array of two arrays - the first will be
+   *     all geometries that result from splitting the source geometry and
+   *     the second will be all geometries that result from splitting the
+   *     target geometry.
+   */
+  splitWith: function(geometry, options) {
+    return geometry.split(this, options);
+  },
+  /**
+   * Method: getVertices
+   * Return a list of all points in this geometry.
+   *
+   * Parameters:
+   * nodes - {Boolean} For lines, only return vertices that are
+   *     endpoints.  If false, for lines, only vertices that are not
+   *     endpoints will be returned.  If not provided, all vertices will
+   *     be returned.
+   *
+   * Returns:
+   * {Array} A list of all vertices in the geometry.
+   */
+  getVertices: function(nodes) {
+    var vertices;
+    if(nodes === true)
+      vertices = [
+        this.components[0],
+        this.components[this.components.length-1]
+      ];
+    else if (nodes === false)
+      vertices = this.components.slice(1, this.components.length-1);
+    else
+      vertices = this.components.slice();
+    return vertices;
+  },
+  distanceTo: function(geometry, options) {
+    var edge = !(options && options.edge === false);
+    var details = edge && options && options.details;
+    var result, best = {};
+    var min = Number.POSITIVE_INFINITY;
+    if(geometry instanceof ZOO.Geometry.Point) {
+      var segs = this.getSortedSegments();
+      var x = geometry.x;
+      var y = geometry.y;
+      var seg;
+      for(var i=0, len=segs.length; i<len; ++i) {
+        seg = segs[i];
+        result = ZOO.Geometry.distanceToSegment(geometry, seg);
+        if(result.distance < min) {
+          min = result.distance;
+          best = result;
+          if(min === 0)
+            break;
+        } else {
+          // if distance increases and we cross y0 to the right of x0, no need to keep looking.
+          if(seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2)))
+            break;
+        }
+      }
+      if(details)
+        best = {
+          distance: best.distance,
+          x0: best.x, y0: best.y,
+          x1: x, y1: y
+        };
+      else
+        best = best.distance;
+    } else if(geometry instanceof ZOO.Geometry.LineString) { 
+      var segs0 = this.getSortedSegments();
+      var segs1 = geometry.getSortedSegments();
+      var seg0, seg1, intersection, x0, y0;
+      var len1 = segs1.length;
+      var interOptions = {point: true};
+      outer: for(var i=0, len=segs0.length; i<len; ++i) {
+        seg0 = segs0[i];
+        x0 = seg0.x1;
+        y0 = seg0.y1;
+        for(var j=0; j<len1; ++j) {
+          seg1 = segs1[j];
+          intersection = ZOO.Geometry.segmentsIntersect(seg0, seg1, interOptions);
+          if(intersection) {
+            min = 0;
+            best = {
+              distance: 0,
+              x0: intersection.x, y0: intersection.y,
+              x1: intersection.x, y1: intersection.y
+            };
+            break outer;
+          } else {
+            result = ZOO.Geometry.distanceToSegment({x: x0, y: y0}, seg1);
+            if(result.distance < min) {
+              min = result.distance;
+              best = {
+                distance: min,
+                x0: x0, y0: y0,
+                x1: result.x, y1: result.y
+              };
+            }
+          }
+        }
+      }
+      if(!details)
+        best = best.distance;
+      if(min !== 0) {
+        // check the final vertex in this line's sorted segments
+        if(seg0) {
+          result = geometry.distanceTo(
+              new ZOO.Geometry.Point(seg0.x2, seg0.y2),
+              options
+              );
+          var dist = details ? result.distance : result;
+          if(dist < min) {
+            if(details)
+              best = {
+                distance: min,
+                x0: result.x1, y0: result.y1,
+                x1: result.x0, y1: result.y0
+              };
+            else
+              best = dist;
+          }
+        }
+      }
+    } else {
+      best = geometry.distanceTo(this, options);
+      // swap since target comes from this line
+      if(details)
+        best = {
+          distance: best.distance,
+          x0: best.x1, y0: best.y1,
+          x1: best.x0, y1: best.y0
+        };
+    }
+    return best;
+  },
+  CLASS_NAME: "ZOO.Geometry.LineString"
+});
+/**
+ * Class: ZOO.Geometry.LinearRing
+ * 
+ * A Linear Ring is a special LineString which is closed. It closes itself 
+ * automatically on every addPoint/removePoint by adding a copy of the first
+ * point as the last point. 
+ * 
+ * Also, as it is the first in the line family to close itself, a getArea()
+ * function is defined to calculate the enclosed area of the linearRing
+ * 
+ * Inherits:
+ *  - <OpenLayers.Geometry.LineString>
+ */
+ZOO.Geometry.LinearRing = ZOO.Class(
+  ZOO.Geometry.LineString, {
+  /**
+   * Property: componentTypes
+   * {Array(String)} An array of class names representing the types of 
+   *                 components that the collection can include.  A null 
+   *                 value means the component types are not restricted.
+   */
+  componentTypes: ["ZOO.Geometry.Point"],
+  /**
+   * Constructor: OpenLayers.Geometry.LinearRing
+   * Linear rings are constructed with an array of points.  This array
+   *     can represent a closed or open ring.  If the ring is open (the last
+   *     point does not equal the first point), the constructor will close
+   *     the ring.  If the ring is already closed (the last point does equal
+   *     the first point), it will be left closed.
+   * 
+   * Parameters:
+   * points - {Array(<ZOO.Geometry.Point>)} points
+   */
+  initialize: function(points) {
+    ZOO.Geometry.LineString.prototype.initialize.apply(this,arguments);
+  },
+  /**
+   * Method: addComponent
+   * Adds a point to geometry components.  If the point is to be added to
+   *     the end of the components array and it is the same as the last point
+   *     already in that array, the duplicate point is not added.  This has 
+   *     the effect of closing the ring if it is not already closed, and 
+   *     doing the right thing if it is already closed.  This behavior can 
+   *     be overridden by calling the method with a non-null index as the 
+   *     second argument.
+   *
+   * Parameter:
+   * point - {<ZOO.Geometry.Point>}
+   * index - {Integer} Index into the array to insert the component
+   * 
+   * Returns:
+   * {Boolean} Was the Point successfully added?
+   */
+  addComponent: function(point, index) {
+    var added = false;
+    //remove last point
+    var lastPoint = this.components.pop();
+    // given an index, add the point
+    // without an index only add non-duplicate points
+    if(index != null || !point.equals(lastPoint))
+      added = ZOO.Geometry.Collection.prototype.addComponent.apply(this,arguments);
+    //append copy of first point
+    var firstPoint = this.components[0];
+    ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]);
+    return added;
+  },
+  /**
+   * APIMethod: removeComponent
+   * Removes a point from geometry components.
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>}
+   */
+  removeComponent: function(point) {
+    if (this.components.length > 4) {
+      //remove last point
+      this.components.pop();
+      //remove our point
+      ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments);
+      //append copy of first point
+      var firstPoint = this.components[0];
+      ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]);
+    }
+  },
+  /**
+   * Method: move
+   * Moves a geometry by the given displacement along positive x and y axes.
+   *     This modifies the position of the geometry and clears the cached
+   *     bounds.
+   *
+   * Parameters:
+   * x - {Float} Distance to move geometry in positive x direction. 
+   * y - {Float} Distance to move geometry in positive y direction.
+   */
+  move: function(x, y) {
+    for(var i = 0, len=this.components.length; i<len - 1; i++) {
+      this.components[i].move(x, y);
+    }
+  },
+  /**
+   * Method: rotate
+   * Rotate a geometry around some origin
+   *
+   * Parameters:
+   * angle - {Float} Rotation angle in degrees (measured counterclockwise
+   *                 from the positive x-axis)
+   * origin - {<ZOO.Geometry.Point>} Center point for the rotation
+   */
+  rotate: function(angle, origin) {
+    for(var i=0, len=this.components.length; i<len - 1; ++i) {
+      this.components[i].rotate(angle, origin);
+    }
+  },
+  /**
+   * Method: resize
+   * Resize a geometry relative to some origin.  Use this method to apply
+   *     a uniform scaling to a geometry.
+   *
+   * Parameters:
+   * scale - {Float} Factor by which to scale the geometry.  A scale of 2
+   *                 doubles the size of the geometry in each dimension
+   *                 (lines, for example, will be twice as long, and polygons
+   *                 will have four times the area).
+   * origin - {<ZOO.Geometry.Point>} Point of origin for resizing
+   * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
+   * 
+   * Returns:
+   * {ZOO.Geometry} - The current geometry. 
+   */
+  resize: function(scale, origin, ratio) {
+    for(var i=0, len=this.components.length; i<len - 1; ++i) {
+      this.components[i].resize(scale, origin, ratio);
+    }
+    return this;
+  },
+  /**
+   * Method: transform
+   * Reproject the components geometry from source to dest.
+   *
+   * Parameters:
+   * source - {<ZOO.Projection>}
+   * dest - {<ZOO.Projection>}
+   * 
+   * Returns:
+   * {<ZOO.Geometry>} 
+   */
+  transform: function(source, dest) {
+    if (source && dest) {
+      for (var i=0, len=this.components.length; i<len - 1; i++) {
+        var component = this.components[i];
+        component.transform(source, dest);
+      }
+      this.bounds = null;
+    }
+    return this;
+  },
+  /**
+   * Method: getCentroid
+   *
+   * Returns:
+   * {<ZOO.Geometry.Point>} The centroid of the ring
+   */
+  getCentroid: function() {
+    if ( this.components && (this.components.length > 2)) {
+      var sumX = 0.0;
+      var sumY = 0.0;
+      for (var i = 0; i < this.components.length - 1; i++) {
+        var b = this.components[i];
+        var c = this.components[i+1];
+        sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y);
+        sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y);
+      }
+      var area = -1 * this.getArea();
+      var x = sumX / (6 * area);
+      var y = sumY / (6 * area);
+    }
+    return new ZOO.Geometry.Point(x, y);
+  },
+  /**
+   * Method: getArea
+   * Note - The area is positive if the ring is oriented CW, otherwise
+   *         it will be negative.
+   * 
+   * Returns:
+   * {Float} The signed area for a ring.
+   */
+  getArea: function() {
+    var area = 0.0;
+    if ( this.components && (this.components.length > 2)) {
+      var sum = 0.0;
+      for (var i=0, len=this.components.length; i<len - 1; i++) {
+        var b = this.components[i];
+        var c = this.components[i+1];
+        sum += (b.x + c.x) * (c.y - b.y);
+      }
+      area = - sum / 2.0;
+    }
+    return area;
+  },
+  /**
+   * Method: getGeodesicArea
+   * Calculate the approximate area of the polygon were it projected onto
+   *     the earth.  Note that this area will be positive if ring is oriented
+   *     clockwise, otherwise it will be negative.
+   *
+   * Parameters:
+   * projection - {<ZOO.Projection>} The spatial reference system
+   *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+   *     assumed.
+   * 
+   * Reference:
+   * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+   *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+   *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+   *
+   * Returns:
+   * {float} The approximate signed geodesic area of the polygon in square
+   *     meters.
+   */
+  getGeodesicArea: function(projection) {
+    var ring = this;  // so we can work with a clone if needed
+    if(projection) {
+      var gg = new ZOO.Projection("EPSG:4326");
+      if(!gg.equals(projection)) {
+        ring = this.clone().transform(projection, gg);
+      }
+    }
+    var area = 0.0;
+    var len = ring.components && ring.components.length;
+    if(len > 2) {
+      var p1, p2;
+      for(var i=0; i<len-1; i++) {
+        p1 = ring.components[i];
+        p2 = ring.components[i+1];
+        area += ZOO.rad(p2.x - p1.x) *
+                (2 + Math.sin(ZOO.rad(p1.y)) +
+                Math.sin(ZOO.rad(p2.y)));
+      }
+      area = area * 6378137.0 * 6378137.0 / 2.0;
+    }
+    return area;
+  },
+  /**
+   * Method: containsPoint
+   * Test if a point is inside a linear ring.  For the case where a point
+   *     is coincident with a linear ring edge, returns 1.  Otherwise,
+   *     returns boolean.
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>}
+   *
+   * Returns:
+   * {Boolean | Number} The point is inside the linear ring.  Returns 1 if
+   *     the point is coincident with an edge.  Returns boolean otherwise.
+   */
+  containsPoint: function(point) {
+    var approx = OpenLayers.Number.limitSigDigs;
+    var digs = 14;
+    var px = approx(point.x, digs);
+    var py = approx(point.y, digs);
+    function getX(y, x1, y1, x2, y2) {
+      return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2);
+    }
+    var numSeg = this.components.length - 1;
+    var start, end, x1, y1, x2, y2, cx, cy;
+    var crosses = 0;
+    for(var i=0; i<numSeg; ++i) {
+      start = this.components[i];
+      x1 = approx(start.x, digs);
+      y1 = approx(start.y, digs);
+      end = this.components[i + 1];
+      x2 = approx(end.x, digs);
+      y2 = approx(end.y, digs);
+
+      /**
+       * The following conditions enforce five edge-crossing rules:
+       *    1. points coincident with edges are considered contained;
+       *    2. an upward edge includes its starting endpoint, and
+       *    excludes its final endpoint;
+       *    3. a downward edge excludes its starting endpoint, and
+       *    includes its final endpoint;
+       *    4. horizontal edges are excluded; and
+       *    5. the edge-ray intersection point must be strictly right
+       *    of the point P.
+       */
+      if(y1 == y2) {
+        // horizontal edge
+        if(py == y1) {
+          // point on horizontal line
+          if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert
+              x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
+            // point on edge
+            crosses = -1;
+            break;
+          }
+        }
+        // ignore other horizontal edges
+        continue;
+      }
+      cx = approx(getX(py, x1, y1, x2, y2), digs);
+      if(cx == px) {
+        // point on line
+        if(y1 < y2 && (py >= y1 && py <= y2) || // upward
+            y1 > y2 && (py <= y1 && py >= y2)) { // downward
+          // point on edge
+          crosses = -1;
+          break;
+        }
+      }
+      if(cx <= px) {
+        // no crossing to the right
+        continue;
+      }
+      if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
+        // no crossing
+        continue;
+      }
+      if(y1 < y2 && (py >= y1 && py < y2) || // upward
+          y1 > y2 && (py < y1 && py >= y2)) { // downward
+        ++crosses;
+      }
+    }
+    var contained = (crosses == -1) ?
+      // on edge
+      1 :
+      // even (out) or odd (in)
+      !!(crosses & 1);
+
+    return contained;
+  },
+  intersects: function(geometry) {
+    var intersect = false;
+    if(geometry.CLASS_NAME == "ZOO.Geometry.Point")
+      intersect = this.containsPoint(geometry);
+    else if(geometry.CLASS_NAME == "ZOO.Geometry.LineString")
+      intersect = geometry.intersects(this);
+    else if(geometry.CLASS_NAME == "ZOO.Geometry.LinearRing")
+      intersect = ZOO.Geometry.LineString.prototype.intersects.apply(
+          this, [geometry]
+          );
+    else
+      for(var i=0, len=geometry.components.length; i<len; ++ i) {
+        intersect = geometry.components[i].intersects(this);
+        if(intersect)
+          break;
+      }
+    return intersect;
+  },
+  getVertices: function(nodes) {
+    return (nodes === true) ? [] : this.components.slice(0, this.components.length-1);
+  },
+  CLASS_NAME: "ZOO.Geometry.LinearRing"
+});
+/**
+ * Class: ZOO.Geometry.MultiLineString
+ * A MultiLineString is a geometry with multiple <ZOO.Geometry.LineString>
+ * components.
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry.Collection>
+ */
+ZOO.Geometry.MultiLineString = ZOO.Class(
+  ZOO.Geometry.Collection, {
+  componentTypes: ["ZOO.Geometry.LineString"],
+  /**
+   * Constructor: ZOO.Geometry.MultiLineString
+   * Constructor for a MultiLineString Geometry.
+   *
+   * Parameters: 
+   * components - {Array(<ZOO.Geometry.LineString>)} 
+   *
+   */
+  initialize: function(components) {
+    ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);        
+  },
+  split: function(geometry, options) {
+    var results = null;
+    var mutual = options && options.mutual;
+    var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
+    var sourceParts = [];
+    var targetParts = [geometry];
+    for(var i=0, len=this.components.length; i<len; ++i) {
+      sourceLine = this.components[i];
+      sourceSplit = false;
+      for(var j=0; j < targetParts.length; ++j) { 
+        splits = sourceLine.split(targetParts[j], options);
+        if(splits) {
+          if(mutual) {
+            sourceLines = splits[0];
+            for(var k=0, klen=sourceLines.length; k<klen; ++k) {
+              if(k===0 && sourceParts.length)
+                sourceParts[sourceParts.length-1].addComponent(
+                  sourceLines[k]
+                );
+              else
+                sourceParts.push(
+                  new ZOO.Geometry.MultiLineString([
+                    sourceLines[k]
+                    ])
+                );
+            }
+            sourceSplit = true;
+            splits = splits[1];
+          }
+          if(splits.length) {
+            // splice in new target parts
+            splits.unshift(j, 1);
+            Array.prototype.splice.apply(targetParts, splits);
+            break;
+          }
+        }
+      }
+      if(!sourceSplit) {
+        // source line was not hit
+        if(sourceParts.length) {
+          // add line to existing multi
+          sourceParts[sourceParts.length-1].addComponent(
+              sourceLine.clone()
+              );
+        } else {
+          // create a fresh multi
+          sourceParts = [
+            new ZOO.Geometry.MultiLineString(
+                sourceLine.clone()
+                )
+            ];
+        }
+      }
+    }
+    if(sourceParts && sourceParts.length > 1)
+      sourceSplit = true;
+    else
+      sourceParts = [];
+    if(targetParts && targetParts.length > 1)
+      targetSplit = true;
+    else
+      targetParts = [];
+    if(sourceSplit || targetSplit) {
+      if(mutual)
+        results = [sourceParts, targetParts];
+      else
+        results = targetParts;
+    }
+    return results;
+  },
+  splitWith: function(geometry, options) {
+    var results = null;
+    var mutual = options && options.mutual;
+    var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
+    if(geometry instanceof ZOO.Geometry.LineString) {
+      targetParts = [];
+      sourceParts = [geometry];
+      for(var i=0, len=this.components.length; i<len; ++i) {
+        targetSplit = false;
+        targetLine = this.components[i];
+        for(var j=0; j<sourceParts.length; ++j) {
+          splits = sourceParts[j].split(targetLine, options);
+          if(splits) {
+            if(mutual) {
+              sourceLines = splits[0];
+              if(sourceLines.length) {
+                // splice in new source parts
+                sourceLines.unshift(j, 1);
+                Array.prototype.splice.apply(sourceParts, sourceLines);
+                j += sourceLines.length - 2;
+              }
+              splits = splits[1];
+              if(splits.length === 0) {
+                splits = [targetLine.clone()];
+              }
+            }
+            for(var k=0, klen=splits.length; k<klen; ++k) {
+              if(k===0 && targetParts.length) {
+                targetParts[targetParts.length-1].addComponent(
+                    splits[k]
+                    );
+              } else {
+                targetParts.push(
+                    new ZOO.Geometry.MultiLineString([
+                      splits[k]
+                      ])
+                    );
+              }
+            }
+            targetSplit = true;                    
+          }
+        }
+        if(!targetSplit) {
+          // target component was not hit
+          if(targetParts.length) {
+            // add it to any existing multi-line
+            targetParts[targetParts.length-1].addComponent(
+                targetLine.clone()
+                );
+          } else {
+            // or start with a fresh multi-line
+            targetParts = [
+              new ZOO.Geometry.MultiLineString([
+                  targetLine.clone()
+                  ])
+              ];
+          }
+
+        }
+      }
+    } else {
+      results = geometry.split(this);
+    }
+    if(sourceParts && sourceParts.length > 1)
+      sourceSplit = true;
+    else
+      sourceParts = [];
+    if(targetParts && targetParts.length > 1)
+      targetSplit = true;
+    else
+      targetParts = [];
+    if(sourceSplit || targetSplit) {
+      if(mutual)
+        results = [sourceParts, targetParts];
+      else
+        results = targetParts;
+    }
+    return results;
+  },
+  CLASS_NAME: "ZOO.Geometry.MultiLineString"
+});
+/**
+ * Class: ZOO.Geometry.Polygon 
+ * Polygon is a collection of <ZOO.Geometry.LinearRing>. 
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry.Collection> 
+ */
+ZOO.Geometry.Polygon = ZOO.Class(
+  ZOO.Geometry.Collection, {
+  componentTypes: ["ZOO.Geometry.LinearRing"],
+  /**
+   * Constructor: OpenLayers.Geometry.Polygon
+   * Constructor for a Polygon geometry. 
+   * The first ring (this.component[0])is the outer bounds of the polygon and 
+   * all subsequent rings (this.component[1-n]) are internal holes.
+   *
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry.LinearRing>)} 
+   */
+  initialize: function(components) {
+    ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+  },
+  /** 
+   * Method: getArea
+   * Calculated by subtracting the areas of the internal holes from the 
+   *   area of the outer hole.
+   * 
+   * Returns:
+   * {float} The area of the geometry
+   */
+  getArea: function() {
+    var area = 0.0;
+    if ( this.components && (this.components.length > 0)) {
+      area += Math.abs(this.components[0].getArea());
+      for (var i=1, len=this.components.length; i<len; i++) {
+        area -= Math.abs(this.components[i].getArea());
+      }
+    }
+    return area;
+  },
+  /** 
+   * APIMethod: getGeodesicArea
+   * Calculate the approximate area of the polygon were it projected onto
+   *     the earth.
+   *
+   * Parameters:
+   * projection - {<ZOO.Projection>} The spatial reference system
+   *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
+   *     assumed.
+   * 
+   * Reference:
+   * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+   *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+   *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+   *
+   * Returns:
+   * {float} The approximate geodesic area of the polygon in square meters.
+   */
+  getGeodesicArea: function(projection) {
+    var area = 0.0;
+    if(this.components && (this.components.length > 0)) {
+      area += Math.abs(this.components[0].getGeodesicArea(projection));
+      for(var i=1, len=this.components.length; i<len; i++) {
+          area -= Math.abs(this.components[i].getGeodesicArea(projection));
+      }
+    }
+    return area;
+  },
+  /**
+   * Method: containsPoint
+   * Test if a point is inside a polygon.  Points on a polygon edge are
+   *     considered inside.
+   *
+   * Parameters:
+   * point - {<ZOO.Geometry.Point>}
+   *
+   * Returns:
+   * {Boolean | Number} The point is inside the polygon.  Returns 1 if the
+   *     point is on an edge.  Returns boolean otherwise.
+   */
+  containsPoint: function(point) {
+    var numRings = this.components.length;
+    var contained = false;
+    if(numRings > 0) {
+    // check exterior ring - 1 means on edge, boolean otherwise
+      contained = this.components[0].containsPoint(point);
+      if(contained !== 1) {
+        if(contained && numRings > 1) {
+          // check interior rings
+          var hole;
+          for(var i=1; i<numRings; ++i) {
+            hole = this.components[i].containsPoint(point);
+            if(hole) {
+              if(hole === 1)
+                contained = 1;
+              else
+                contained = false;
+              break;
+            }
+          }
+        }
+      }
+    }
+    return contained;
+  },
+  intersects: function(geometry) {
+    var intersect = false;
+    var i, len;
+    if(geometry.CLASS_NAME == "ZOO.Geometry.Point") {
+      intersect = this.containsPoint(geometry);
+    } else if(geometry.CLASS_NAME == "ZOO.Geometry.LineString" ||
+              geometry.CLASS_NAME == "ZOO.Geometry.LinearRing") {
+      // check if rings/linestrings intersect
+      for(i=0, len=this.components.length; i<len; ++i) {
+        intersect = geometry.intersects(this.components[i]);
+        if(intersect) {
+          break;
+        }
+      }
+      if(!intersect) {
+        // check if this poly contains points of the ring/linestring
+        for(i=0, len=geometry.components.length; i<len; ++i) {
+          intersect = this.containsPoint(geometry.components[i]);
+          if(intersect) {
+            break;
+          }
+        }
+      }
+    } else {
+      for(i=0, len=geometry.components.length; i<len; ++ i) {
+        intersect = this.intersects(geometry.components[i]);
+        if(intersect)
+          break;
+      }
+    }
+    // check case where this poly is wholly contained by another
+    if(!intersect && geometry.CLASS_NAME == "ZOO.Geometry.Polygon") {
+      // exterior ring points will be contained in the other geometry
+      var ring = this.components[0];
+      for(i=0, len=ring.components.length; i<len; ++i) {
+        intersect = geometry.containsPoint(ring.components[i]);
+        if(intersect)
+          break;
+      }
+    }
+    return intersect;
+  },
+  distanceTo: function(geometry, options) {
+    var edge = !(options && options.edge === false);
+    var result;
+    // this is the case where we might not be looking for distance to edge
+    if(!edge && this.intersects(geometry))
+      result = 0;
+    else
+      result = ZOO.Geometry.Collection.prototype.distanceTo.apply(
+          this, [geometry, options]
+          );
+    return result;
+  },
+  CLASS_NAME: "ZOO.Geometry.Polygon"
+});
+/**
+ * Method: createRegularPolygon
+ * Create a regular polygon around a radius. Useful for creating circles 
+ * and the like.
+ *
+ * Parameters:
+ * origin - {<ZOO.Geometry.Point>} center of polygon.
+ * radius - {Float} distance to vertex, in map units.
+ * sides - {Integer} Number of sides. 20 approximates a circle.
+ * rotation - {Float} original angle of rotation, in degrees.
+ */
+OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {  
+    var angle = Math.PI * ((1/sides) - (1/2));
+    if(rotation) {
+        angle += (rotation / 180) * Math.PI;
+    }
+    var rotatedAngle, x, y;
+    var points = [];
+    for(var i=0; i<sides; ++i) {
+        rotatedAngle = angle + (i * 2 * Math.PI / sides);
+        x = origin.x + (radius * Math.cos(rotatedAngle));
+        y = origin.y + (radius * Math.sin(rotatedAngle));
+        points.push(new ZOO.Geometry.Point(x, y));
+    }
+    var ring = new ZOO.Geometry.LinearRing(points);
+    return new ZOO.Geometry.Polygon([ring]);
+};
+/**
+ * Class: ZOO.Geometry.MultiPolygon
+ * MultiPolygon is a geometry with multiple <ZOO.Geometry.Polygon>
+ * components.  Create a new instance with the <ZOO.Geometry.MultiPolygon>
+ * constructor.
+ * 
+ * Inherits from:
+ *  - <ZOO.Geometry.Collection>
+ */
+ZOO.Geometry.MultiPolygon = ZOO.Class(
+  ZOO.Geometry.Collection, {
+  componentTypes: ["ZOO.Geometry.Polygon"],
+  /**
+   * Constructor: OpenLayers.Geometry.MultiPolygon
+   * Create a new MultiPolygon geometry
+   *
+   * Parameters:
+   * components - {Array(<ZOO.Geometry.Polygon>)} An array of polygons
+   *              used to generate the MultiPolygon
+   *
+   */
+  initialize: function(components) {
+    ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+  },
+  CLASS_NAME: "ZOO.Geometry.MultiPolygon"
+});
+
+ZOO.Process = ZOO.Class({
+  schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd",
+  namespaces: {
+    ows: "http://www.opengis.net/ows/1.1",
+    wps: "http://www.opengis.net/wps/1.0.0",
+    xlink: "http://www.w3.org/1999/xlink",
+    xsi: "http://www.w3.org/2001/XMLSchema-instance",
+  },
+  url: 'http://localhost/zoo',
+  identifier: null,
+  initialize: function(url,identifier) {
+    this.url = url;
+    this.identifier = identifier;
+  },
+  Execute: function(inputs) {
+    if (this.identifier == null)
+      return null;
+    var body = new XML('<wps:Execute service="WPS" version="1.0.0" xmlns:wps="'+this.namespaces['wps']+'" xmlns:ows="'+this.namespaces['ows']+'" xmlns:xlink="'+this.namespaces['xlink']+'" xmlns:xsi="'+this.namespaces['xsi']+'" xsi:schemaLocation="'+this.schemaLocation+'"><ows:Identifier>'+this.identifier+'</ows:Identifier>'+this.buildDataInputsNode(inputs)+'</wps:Execute>');
+    body = body.toXMLString();
+    var response = ZOO.Request.Post(this.url,body,['Content-Type: text/xml; charset=UTF-8']);
+    return response;
+  },
+  buildInput: {
+    'complex': function(identifier,data) {
+      var input = new XML('<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Data><wps:ComplexData>'+data.value+'</wps:ComplexData></wps:Data></wps:Input>');
+      input.*::Data.*::ComplexData.@mimeType = data.mimetype ? data.mimetype : 'text/plain';
+      if (data.encoding)
+        input.*::Data.*::ComplexData.@encoding = data.encoding;
+      if (data.schema)
+        input.*::Data.*::ComplexData.@schema = data.schema;
+      input = input.toXMLString();
+      return input;
+    },
+    'reference': function(identifier,data) {
+      return '<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Reference xmlns:xlink="'+this.namespaces['xlink']+'" xlink:href="'+data.value.replace('&','&amp;','gi')+'"/></wps:Input>';
+    },
+    'literal': function(identifier,data) {
+      var input = new XML('<wps:Input xmlns:wps="'+this.namespaces['wps']+'"><ows:Identifier xmlns:ows="'+this.namespaces['ows']+'">'+identifier+'</ows:Identifier><wps:Data><wps:LiteralData>'+data.value+'</wps:LiteralData></wps:Data></wps:Input>');
+      if (data.type)
+        input.*::Data.*::LiteralData.@dataType = data.type;
+      if (data.uom)
+        input.*::Data.*::LiteralData.@uom = data.uom;
+      input = input.toXMLString();
+      return input;
+    }
+  },
+  buildDataInputsNode:function(inputs){
+    var data, builder, inputsArray=[];
+    for (var attr in inputs) {
+      data = inputs[attr];
+      if (data.mimetype || data.type == 'complex')
+        builder = this.buildInput['complex'];
+      else if (data.type == 'reference' || data.type == 'url')
+        builder = this.buildInput['reference'];
+      else
+        builder = this.buildInput['literal'];
+      inputsArray.push(builder.apply(this,[attr,data]));
+    }
+    return '<wps:DataInputs xmlns:wps="'+this.namespaces['wps']+'">'+inputsArray.join('\n')+'</wps:DataInputs>';
+  },
+  CLASS_NAME: "ZOO.Process"
+});
Index: /tags/rel-1.2.0-rc1/zoo-kernel/Doxyfile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/Doxyfile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/Doxyfile	(revision 221)
@@ -0,0 +1,1417 @@
+# Doxyfile 1.5.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file 
+# that follow. The default is UTF-8 which is also the encoding used for all 
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
+# iconv built into libc) for the transcoding. See 
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = ZOO Kernel
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 0.1
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = YES
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, 
+# and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Java. For instance, namespaces will be presented as packages, qualified 
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
+# sources. Doxygen will then generate output that is tailored for 
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
+# to include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter 
+# and setter methods for a property. Setting this option to YES (the default) 
+# will make doxygen to replace the get and set methods by a property in the 
+# documentation. This will only work if the methods are indeed getting or 
+# setting a simple type. If this is not the case, or you want to show the 
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
+# is documented as struct, union, or enum with the name of the typedef. So 
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
+# with name TypeT. When disabled the typedef will appear as a member of a file, 
+# namespace, or class. And the struct will be named TypeS. This can typically 
+# be useful for C code in case the coding convention dictates that all compound 
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = YES
+
+# If this flag is set to YES, the members of anonymous namespaces will be 
+# extracted and appear in the documentation as a namespace called 
+# 'anonymous_namespace{file}', where file will be replaced with the base 
+# name of the file that contains the anonymous namespace. By default 
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
+# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the 
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from 
+# the version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = .
+
+# This tag can be used to specify the character encoding of the source files 
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
+# also the default input encoding. Doxygen uses libiconv (or the iconv built 
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = 
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = 
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the 
+# output. The symbol name can be a fully qualified name, a word, or if the 
+# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = YES
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files 
+# will be generated that can be used as input for Apple's Xcode 3 
+# integrated development environment, introduced with OSX 10.5 (Leopard). 
+# To create a documentation set, doxygen will generate a Makefile in the 
+# HTML output directory. Running make will produce the docset in that 
+# directory and running "make install" will install the docset in 
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
+# it at startup.
+
+GENERATE_DOCSET        = YES
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
+# feed. A documentation feed provides an umbrella under which multiple 
+# documentation sets from a single provider (such as a company or product suite) 
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
+# should uniquely identify the documentation set bundle. This should be a 
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = YES
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature. Other possible values 
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hiererachy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW      = NONE
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included 
+# as images in the HTML documentation. The default is 10. Note that 
+# when you change the font size after a successful doxygen run you need 
+# to manually remove any form_*.png images from the HTML output directory 
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see 
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
+# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# default search path.
+
+MSCGEN_PATH            = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output 
+# directory and reference it in all dot files that doxygen generates. This 
+# font does not include all possible unicode characters however, so when you need 
+# these (or just want a differently looking font) you can specify the font name 
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
+# which can be done by putting it in a standard location or by setting the 
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# By default doxygen will tell dot to use the output directory to look for the 
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
+# different font using DOT_FONTNAME you can set the path where dot 
+# can find it using this tag.
+
+DOT_FONTPATH           = 
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
+# doxygen will generate a call dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable call graphs 
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = YES
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
+# doxygen will generate a caller dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable caller 
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the 
+# number of direct children of the root node in a graph is already larger than 
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is enabled by default, which results in a transparent 
+# background. Warning: Depending on the platform used, enabling this option 
+# may lead to badly anti-aliased labels on the edges of a graph (i.e. they 
+# become hard to read).
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
Index: /tags/rel-1.2.0-rc1/zoo-kernel/Makefile.in
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/Makefile.in	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/Makefile.in	(revision 221)
@@ -0,0 +1,110 @@
+OS:=$(shell uname -s)
+ifeq ($(OS),Darwin)
+	MACOS_LD_FLAGS=-lintl -framework SystemConfiguration -framework CoreFoundation
+	MACOS_CFLAGS=-arch i386 -arch ppc -arch x86_64
+endif
+
+JAVACFLAGS=@JAVA_CPPFLAGS@
+JAVALDFLAGS=@JAVA_LDFLAGS@
+JAVA_ENABLED=@JAVA_ENABLED@
+JAVA_FILE=@JAVA_FILE@
+
+CFLAGS=${MACOS_CFLAGS} -I../thirds/cgic206 -I. -DLINUX_FREE_ISSUE #-DDEBUG #-DDEBUG_SERVICE_CONF
+
+PHPCFLAGS=@PHP_CPPFLAGS@
+PHPLDFLAGS=@PHP_LDFLAGS@
+PHP_ENABLED=@PHP_ENABLED@
+PHP_FILE=@PHP_FILE@
+
+
+PYTHONCFLAGS=@PYTHON_CPPFLAGS@
+PYTHONLDFLAGS=@PYTHON_LDFLAGS@
+PYTHON_ENABLED=@PYTHON_ENABLED@
+PYTHON_FILE=@PYTHON_FILE@
+
+JSCFLAGS=@JS_CPPFLAGS@
+JSLDFLAGS=@JS_LDFLAGS@
+JS_ENABLED=@JS_ENABLED@
+JS_FILE=@JS_FILE@
+
+XML2CFLAGS=@XML2_CPPFLAGS@
+XML2LDFLAGS=@XML2_LDFLAGS@
+
+GDAL_CFLAGS=@GDAL_CFLAGS@
+GDAL_LIBS=@GDAL_LIBS@
+
+PERLCFLAGS=@PERL_CPPFLAGS@
+PERLLDFLAGS=@PERL_LDFLAGS@
+PERL_ENABLED=@PERL_ENABLED@
+PERL_FILE=@PERL_FILE@
+
+
+all: zoo_loader.cgi
+
+main_conf_read.tab.c: main_conf_read.y service.h
+	bison -p cr -d main_conf_read.y
+
+main_conf_read.tab.o: main_conf_read.tab.c service.h
+	g++ ${CFLAGS} -c main_conf_read.tab.c
+
+lex.cr.c: main_conf_read.y main_conf_read.l main_conf_read.tab.c service.h
+	flex -P cr --header-file main_conf_read.l
+
+lex.cr.o: lex.cr.c service.h
+	g++ ${CFLAGS} -c lex.cr.c
+
+service_conf.tab.c: service_conf.y service.h
+	bison -p sr -d service_conf.y
+
+service_conf.tab.o: service_conf.tab.c service.h
+	g++ ${CFLAGS} -c service_conf.tab.c
+
+lex.sr.c: service_conf.y service_conf.l service_conf.tab.c service.h
+	flex -P sr --header-file service_conf.l
+
+lex.sr.o: lex.sr.c service.h
+	g++ ${CFLAGS} -c lex.sr.c
+
+ulinet.o: ulinet.c
+	gcc ${XML2CFLAGS} ${CFLAGS} ${JSCFLAGS} ${JS_ENABLED} -c ulinet.c
+
+service_internal.o: service_internal.c service.h
+	gcc ${JS_ENABLED} ${JSCFLAGS} ${XML2CFLAGS} ${CFLAGS} -fPIC -c service_internal.c
+
+service_internal_python.o: service_internal_python.c service.h
+	g++ ${XML2CFLAGS} ${PYTHONCFLAGS} ${CFLAGS} -c service_internal_python.c
+
+service_internal_php.o: service_internal_php.c service.h
+	g++ -c ${XML2CFLAGS} ${PHPCFLAGS} ${CFLAGS}  ${PHP_ENABLED} service_internal_php.c
+
+service_internal_perl.o: service_internal_perl.c service.h
+	gcc -c ${XML2CFLAGS} ${PERLCFLAGS} ${CFLAGS}  ${PERL_ENABLED} service_internal_perl.c
+
+
+service_internal_java.o: service_internal_java.c service.h
+	gcc -c ${XML2CFLAGS} ${JAVACFLAGS} ${CFLAGS} ${JAVA_ENABLED} service_internal_java.c
+
+service_internal_js.o: service_internal_js.c service_internal_js.h
+	gcc ${XML2CFLAGS} ${JSCFLAGS} ${CFLAGS} ${JS_ENABLED} -c service_internal_js.c
+
+
+service_loader.o: service_loader.c service.h
+	g++ -c ${XML2CFLAGS} ${PYTHONCFLAGS} ${CFLAGS} service_loader.c
+
+zoo_service_loader.o: zoo_service_loader.c service.h
+	g++ -g -O2 ${XML2CFLAGS} ${CFLAGS} ${PYTHONCFLAGS} ${JAVACFLAGS} ${JSCFLAGS} ${PERLCFLAGS} ${PHPCFLAGS} ${PYTHON_ENABLED} ${JS_ENABLED} ${PHP_ENABLED} ${PERL_ENABLED} ${JAVA_ENABLED} -c zoo_service_loader.c  -fno-common -DPIC -o zoo_service_loader.o
+
+zoo_loader.cgi: zoo_loader.c zoo_service_loader.o  ulinet.o service.h lex.sr.o service_conf.tab.o service_conf.y ulinet.o main_conf_read.tab.o lex.cr.o service_internal.o ${PYTHON_FILE}   ${PHP_FILE} ${JAVA_FILE} ${JS_FILE} ${PERL_FILE}
+	g++ -g -O2 ${JSCFLAGS} ${PHPCFLAGS}  ${PERLCFLAGS}   ${JAVACFLAGS} ${XML2CFLAGS} ${PYTHONCFLAGS} ${CFLAGS} -c zoo_loader.c  -fno-common -DPIC -o zoo_loader.o
+	g++  ${JSCFLAGS} ${GDAL_CFLAGS} ${XML2CFLAGS} ${PHPCFLAGS} ${PERLCFLAGS} ${JAVACFLAGS} ${PYTHONCFLAGS} ${CFLAGS} zoo_loader.o zoo_service_loader.o service_internal.o ${PYTHON_FILE}  ${PERL_FILE} ${PHP_FILE}  ${JS_FILE} ${JAVA_FILE} ulinet.o lex.cr.o lex.sr.o service_conf.tab.o main_conf_read.tab.o -o zoo_loader.cgi -lcurl -L../thirds/cgic206 -lcgic -lcurl ${GDAL_LIBS} ${XML2LDFLAGS} ${PYTHONLDFLAGS} ${PERLLDFLAGS}  ${PHPLDFLAGS} ${JAVALDFLAGS} ${JSLDFLAGS} -lfcgi -lcrypto ${MACOS_LD_FLAGS}
+
+install:
+	@echo "##############################################################################"
+	@echo "# This won't install anything !!!                                            #"
+	@echo "#                                                                            #"
+	@echo "# Please copy the zoo_loader.cgi and its companion main.cfg into your cgbin  #"
+	@echo "# directory.                                                                 #"
+	@echo "##############################################################################"
+
+clean:
+	rm -f *.o *.zo *.eo *.tab.c *.tab.h *.sr.c* service_loader lex.* *.lreg *.sibling service_loader.dSYM 
Index: /tags/rel-1.2.0-rc1/zoo-kernel/README
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/README	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/README	(revision 221)
@@ -0,0 +1,2 @@
+For information on how to compile and install the ZOO Kernel, please refer to:
+http://zoo-project.org/trac/wiki/ZooDocumentation/ZOOKernel/Installation
Index: /tags/rel-1.2.0-rc1/zoo-kernel/configure.ac
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/configure.ac	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/configure.ac	(revision 221)
@@ -0,0 +1,368 @@
+AC_INIT([ZOO Kernel], [1.2.0], [bugs@zoo-project.org])
+
+# Checks for programs.
+AC_PROG_YACC
+AC_PROG_CC
+AC_PROG_LEX
+AC_PROG_CXX
+AC_PROG_SED
+
+# Checks for libraries.
+AC_CHECK_LIB([cgic], [cgiMain])
+AC_CHECK_LIB([curl], [curl_easy_init curl_easy_setopt curl_easy_cleanup curl_easy_perform])
+AC_CHECK_LIB([dl], [dlopen dlsym dlerror dlclose])
+AC_CHECK_LIB([fl], [main])
+AC_CHECK_LIB([pthread], [main])
+AC_CHECK_LIB([fcgi], [main])
+AC_CHECK_LIB([ssl], [main])
+
+# Checks for header files.
+AC_FUNC_ALLOCA
+AC_CHECK_HEADERS([fcntl.h inttypes.h libintl.h malloc.h stddef.h stdlib.h string.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT8_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
+
+# Checks for library functions.
+AC_FUNC_FORK
+AC_FUNC_MALLOC
+AC_FUNC_REALLOC
+AC_CHECK_FUNCS([dup2 getcwd memset setenv strdup strstr])
+
+#============================================================================
+# Detect if gdal is installed
+#============================================================================
+
+AC_ARG_WITH([gdal-config], 
+	[AS_HELP_STRING([--with-gdal-config=FILE], [specify an alternative gdal-config file])], 
+	[GDAL_CONFIG="$withval"], [GDAL_CONFIG=""])
+if test -z $GDAL_CONFIG;
+then
+	AC_PATH_PROG([GDAL_CONFIG], [gdal-config])
+	if test -z $GDAL_CONFIG; 
+	then
+		AC_MSG_ERROR([could not find gdal-config from libgdal within the current path. You may need to try re-running configure with a --with-gdal-config parameter.])
+	fi
+	
+else
+	if test -f $GDAL_CONFIG; then
+		AC_MSG_RESULT([Using user-specified gdal-config file: $GDAL_CONFIG])
+	else
+		AC_MSG_ERROR([the user-specified gdal-config file $GDAL_CONFIG does not exist])
+	fi
+fi
+
+GDAL_CFLAGS="`$GDAL_CONFIG --cflags`"
+GDAL_LIBS="`$GDAL_CONFIG --libs`"
+
+AC_SUBST([GDAL_CFLAGS])
+AC_SUBST([GDAL_LIBS])
+
+# ===========================================================================
+# Detect if libxml2 is installed
+# ===========================================================================
+
+AC_ARG_WITH([xml2config], 
+	[AS_HELP_STRING([--with-xml2config=FILE], [specify an alternative xml2-config file])], 
+	[XML2CONFIG="$withval"], [XML2CONFIG=""])
+
+if test "x$XML2CONFIG" = "x"; then
+	# XML2CONFIG was not specified, so search within the current path
+	AC_PATH_PROG([XML2CONFIG], [xml2-config])
+
+	# If we couldn't find xml2-config, display a warning
+	if test "x$XML2CONFIG" = "x"; then
+		AC_MSG_ERROR([could not find xml2-config from libxml2 within the current path. You may need to try re-running configure with a --with-xml2config parameter.])
+	fi
+else
+	# XML2CONFIG was specified; display a message to the user
+	if test "x$XML2CONFIG" = "xyes"; then
+		AC_MSG_ERROR([you must specify a parameter to --with-xml2config, e.g. --with-xml2config=/path/to/xml2-config])
+	else
+		if test -f $XML2CONFIG; then
+			AC_MSG_RESULT([Using user-specified xml2-config file: $XML2CONFIG])
+		else
+			AC_MSG_ERROR([the user-specified xml2-config file $XML2CONFIG does not exist])
+		fi	
+	fi
+fi
+
+# Extract the linker and include flags 
+XML2_LDFLAGS=`$XML2CONFIG --libs`
+XML2_CPPFLAGS=`$XML2CONFIG --cflags`
+
+# Check headers file
+CPPFLAGS_SAVE="$CPPFLAGS"
+CPPFLAGS="$XML2_CPPFLAGS"
+AC_CHECK_HEADERS([libxml/tree.h libxml/parser.h libxml/xpath.h libxml/xpathInternals.h],
+		 [], [AC_MSG_ERROR([could not find headers include related to libxml2])])
+
+# Ensure we can link against libxml2
+LIBS_SAVE="$LIBS"
+LIBS="$XML2_LDFLAGS"
+AC_CHECK_LIB([xml2], [xmlInitParser], [], [AC_MSG_ERROR([could not find libxml2])], [])
+
+AC_SUBST([XML2_CPPFLAGS])
+AC_SUBST([XML2_LDFLAGS])
+
+# ===========================================================================
+# Detect if python is installed
+# ===========================================================================
+
+AC_ARG_WITH([python], 
+	[AS_HELP_STRING([--with-python=PATH], [To enable python support or specify an alternative directory for python installation,  disabled by default])], 
+	[PYTHON_PATH="$withval"; PYTHON_ENABLED="-DUSE_PYTHON"], [PYTHON_ENABLED=""])
+
+
+if test -z "$PYTHON_ENABLED"
+then
+	PYTHON_FILE=""
+else
+	PYTHONCONFIG="$PYTHON_PATH/bin/python-config"
+	PYTHON_FILE="service_internal_python.o"
+	if test  "$PYTHON_PATH" = "yes"
+	then
+		# PHP was not specified, so search within the current path
+		AC_PATH_PROG([PYTHONCONFIG], [python-config])
+	else
+		PYTHONCONFIG="$PYTHON_PATH/bin/python-config"
+	fi
+
+	# Extract the linker and include flags 
+	PYTHON_LDFLAGS=`$PYTHONCONFIG --ldflags`
+	PYTHON_CPPFLAGS=`$PYTHONCONFIG --cflags`
+
+	# Check headers file
+	CPPFLAGS_SAVE="$CPPFLAGS"
+	CPPFLAGS="$PYTHON_CPPFLAGS"
+	AC_CHECK_HEADERS([Python.h],
+		 [], [AC_MSG_ERROR([could not find headers include related to libpython])])
+
+	# Ensure we can link against libphp
+	LIBS_SAVE="$LIBS"
+	LIBS="$PYTHON_LDFLAGS"
+	PY_LIB=`$PYTHONCONFIG --libs | sed -e 's/^.*\(python2\..\)$/\1/'`
+	AC_CHECK_LIB([$PY_LIB], [PyObject_CallObject], [], [AC_MSG_ERROR([could not find libpython])], [])
+	AC_SUBST([PYTHON_CPPFLAGS])
+	AC_SUBST([PYTHON_LDFLAGS])
+fi
+
+AC_SUBST([PYTHON_ENABLED])
+AC_SUBST([PYTHON_FILE])
+
+# ===========================================================================
+# Detect if php is installed
+# ===========================================================================
+
+AC_ARG_WITH([php], 
+	[AS_HELP_STRING([--with-php=PATH], [To enable php support or specify an alternative directory for php installation,  disabled by default])], 
+	[PHP_PATH="$withval"; PHP_ENABLED="-DUSE_PHP"], [PHP_ENABLED=""])
+
+
+if test -z "$PHP_ENABLED"
+then
+	PHP_FILE=""
+else
+	PHPCONFIG="$PHP_PATH/bin/php-config"
+	PHP_FILE="service_internal_php.o"
+	if test  "$PHP_PATH" = "yes"
+	then
+		# PHP was not specified, so search within the current path
+		AC_PATH_PROG([PHPCONFIG], [php-config])
+	else
+		PHPCONFIG="$PHP_PATH/bin/php-config"
+	fi
+
+	# Extract the linker and include flags 
+	PHP_LDFLAGS="-L/`$PHPCONFIG --prefix`/lib -lphp5"
+	PHP_CPPFLAGS=`$PHPCONFIG --includes`
+
+	# Check headers file
+	CPPFLAGS_SAVE="$CPPFLAGS"
+	CPPFLAGS="$PHP_CPPFLAGS"
+	AC_CHECK_HEADERS([sapi/embed/php_embed.h],
+		 [], [AC_MSG_ERROR([could not find headers include related to libphp])])
+
+	# Ensure we can link against libphp
+	LIBS_SAVE="$LIBS"
+	LIBS="$PHP_LDFLAGS"
+	# Shouldn't we get php here rather than php5 :) ??
+	AC_CHECK_LIB([php5], [call_user_function], [], [AC_MSG_ERROR([could not find libphp])], [])
+	AC_SUBST([PHP_CPPFLAGS])
+	AC_SUBST([PHP_LDFLAGS])
+fi
+
+AC_SUBST([PHP_ENABLED])
+AC_SUBST([PHP_FILE])
+
+# ===========================================================================
+# Detect if perl is installed
+# ===========================================================================
+
+AC_ARG_WITH([perl], 
+	[AS_HELP_STRING([--with-perl=PATH], [To enable perl support or specify an alternative directory for perl installation,  disabled by default])], 
+	[PERL_PATH="$withval"; PERL_ENABLED="-DUSE_PERL"], [PERL_ENABLED=""])
+
+
+if test -z "$PERL_ENABLED"
+then
+	PERL_FILE=""
+else
+	PERL_FILE="service_internal_perl.o"
+	if test  "$PERL_PATH" = "yes"
+	then
+		# Perl was not specified, so search within the current path
+		AC_PATH_PROG([PERLCONFIG], [perl])
+	else
+		PERLCONFIG="$PERL_PATH/bin/perl"
+	fi
+
+	# Extract the linker and include flags 
+	PERL_LDFLAGS=`$PERLCONFIG -MExtUtils::Embed -e ldopts`
+	PERL_CPPFLAGS=`$PERLCONFIG -MExtUtils::Embed -e ccopts`
+
+	# Check headers file
+	CPPFLAGS_SAVE="$CPPFLAGS"
+	CPPFLAGS="$PERL_CPPFLAGS"
+	AC_CHECK_HEADERS([EXTERN.h],
+		 [], [AC_MSG_ERROR([could not find headers include related to libperl])])
+
+	AC_SUBST([PERL_CPPFLAGS])
+	AC_SUBST([PERL_LDFLAGS])
+fi
+
+AC_SUBST([PERL_ENABLED])
+AC_SUBST([PERL_FILE])
+
+# ===========================================================================
+# Detect if java is installed
+# ===========================================================================
+
+AC_ARG_WITH([java], 
+	[AS_HELP_STRING([--with-java=PATH], [To enable java support, specify a JDK_HOME,  disabled by default])], 
+	[JDKHOME="$withval"; JAVA_ENABLED="-DUSE_JAVA"], [JAVA_ENABLED=""])
+
+if test -z "$JAVA_ENABLED"
+then
+	JAVA_FILE=""
+else
+	JAVA_FILE="service_internal_java.o"
+	if test "x$JDKHOME" = "x"; 
+	then
+		AC_MSG_ERROR([could not find java installation path within the current path. You may need to try re-running configure with a --with-java parameter.])
+	fi	# JAVA was specified; display a message to the user
+	if test "x$JDKHOME" = "xyes"; 
+	then
+		AC_MSG_ERROR([you must specify a parameter to --with-java, e.g. --with-java=/path/to/java])
+	fi
+
+	# Extract the linker and include flags
+	if test "x$JDKHOME" = "xmacos";
+	then
+		JAVA_LDFLAGS="-framework JavaVM"
+		JAVA_CPPFLAGS="-I/Developer//SDKs/MacOSX10.6.sdk/System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/"
+	else
+		if test -d "$JDKHOME/jre/lib/i386";
+		then
+			JAVA_LDFLAGS="-L$JDKHOME/jre/lib/i386/client/ -ljvm -lpthread"
+			JAVA_CPPFLAGS="-I$JDKHOME/include -I$JDKHOME/include/linux"
+		else
+			JAVA_LDFLAGS="-L$JDKHOME/jre/lib/amd64/client/ -ljvm -lpthread"
+			JAVA_CPPFLAGS="-I$JDKHOME/include -I$JDKHOME/include/linux"
+		fi
+	fi
+
+	# Check headers file (second time we check that in fact)
+	CPPFLAGS_SAVE="$CPPFLAGS"
+	CPPFLAGS="$JAVA_CPPFLAGS"
+	AC_CHECK_HEADERS([jni.h],
+			 [], [AC_MSG_ERROR([could not find headers include related to libjava])])
+
+	# Ensure we can link against libjava
+	LIBS_SAVE="$LIBS"
+	LIBS="$JAVA_LDFLAGS"
+	if test "x$JDKHOME" != "xmacos";
+	then
+		AC_CHECK_LIB([jvm], [JNI_CreateJavaVM], [], [AC_MSG_ERROR([could not find libjava])], [])
+	fi
+
+	AC_SUBST([JAVA_CPPFLAGS])
+	AC_SUBST([JAVA_LDFLAGS])
+fi 
+
+AC_SUBST([JAVA_ENABLED])
+AC_SUBST([JAVA_FILE])
+
+# ===========================================================================
+# Detect if spidermonkey is installed
+# ===========================================================================
+
+AC_ARG_WITH([js], 
+	[AS_HELP_STRING([--with-js=PATH], [specify --with-js=path-to-js to enable js support, specify --with-js on linux debian like, js support is disabled by default ])], 
+	[JSHOME="$withval";JS_ENABLED="-DUSE_JS"], [JS_ENABLED=""])
+
+if test -z "$JS_ENABLED"
+then
+	JS_FILE=""
+else
+	if test "$JSHOME" = "yes"
+	then
+        	JS_FILE="service_internal_js.o"
+
+		#on teste si on est sous debian like 
+		if test -f "/usr/bin/dpkg"
+		then
+			if test -n "`dpkg -l | grep libmozjs-dev`"
+			then
+				JS_CPPFLAGS="-I/usr/include/mozjs/"
+                        	JS_LDFLAGS="-L/usr/lib -lmozjs -lm"
+                        	JS_LIB="mozjs"
+			else 
+				XUL_VERSION="`dpkg -l | grep xulrunner | grep dev | awk '{print $3;}' | sed -e 's/\([[0-9]]\{1,2\}\.[[0-9]]\{1,2\}\.[[0-9]]\{1,2\}\.[[0-9]]\{1,2\}\).*/\1/'`"
+				if test -n "$XUL_VERSION"
+				then
+					JS_CPPFLAGS="-I/usr/include/xulrunner-$XUL_VERSION"
+					JS_LDFLAGS="-L/usr/lib/xulrunner-$XUL_VERSION -lmozjs -lm"
+					JS_LIB="mozjs"
+				else
+					AC_MSG_ERROR([You must install libmozjs-dev or xulrunner-dev ])
+				fi
+			fi
+		else
+			AC_MSG_ERROR([You must  specify your custom install of libjs])
+		fi
+	else
+		JS_CPPFLAGS="-I$JSHOME/include/"
+                JS_LDFLAGS="-L$JSHOME/lib -lmozjs -lm"
+                JS_LIB="mozjs"
+
+	fi 
+	CPPFLAGS_SAVE="$CPPFLAGS"
+        CPPFLAGS="$JS_CPPFLAGS"
+
+	#AC_CHECK_HEADERS([jsapi.h],
+        #                [], [AC_MSG_ERROR([could not find headers include related to libjs])])
+
+	
+	LIBS_SAVE="$LIBS"
+        LIBS="$JS_LDFLAGS"
+
+        AC_CHECK_LIB([$JS_LIB], [JS_CallFunctionName], [], [AC_MSG_ERROR([could not find $JS_LIB])], [])
+			
+        AC_SUBST([JS_CPPFLAGS])
+        AC_SUBST([JS_LDFLAGS])
+fi
+
+AC_SUBST([JS_ENABLED])
+AC_SUBST([JS_FILE])
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
Index: /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/en_US.utf8.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/en_US.utf8.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/en_US.utf8.po	(revision 221)
@@ -0,0 +1,171 @@
+# English translations for ZOO Kernel package.
+# Copyright (C) 2010 THE ZOO Kernel'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the ZOO Kernel package.
+# root <gerald.fenoy@geolabs.fr>, 2010.
+#
+#: service_internal.c:1672 zoo_service_loader.c:158 zoo_service_loader.c:160
+#: zoo_service_loader.c:220 zoo_service_loader.c:267 zoo_service_loader.c:341
+#: zoo_service_loader.c:1135 zoo_service_loader.c:1277
+#: zoo_service_loader.c:1364
+msgid ""
+msgstr ""
+"Project-Id-Version: ZOO Kernel 0.0.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-28 00:38+0200\n"
+"PO-Revision-Date: 2010-09-28 00:39+0200\n"
+"Last-Translator: root <gerald.fenoy@geolabs.fr>\n"
+"Language-Team: English\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: service_internal.c:1056
+#, c-format
+msgid ""
+"ZOO Service \"%s\" is currently running. Please, reload this document to get "
+"the up-to-date status of the Service."
+msgstr ""
+"ZOO Service \"%s\" is currently running. Please, reload this document to get "
+"the up-to-date status of the Service."
+
+#: service_internal.c:1062
+#, c-format
+msgid ""
+"Service \"%s\" was accepted by the ZOO Kernel and it run as a background "
+"task. Please consult the statusLocation attribtue providen in this document "
+"to get the up-to-date document."
+msgstr ""
+"Service \"%s\" was accepted by the ZOO Kernel and it run as a background "
+"task. Please consult the statusLocation attribtue providen in this document "
+"to get the up-to-date document."
+
+#: service_internal.c:1079
+msgid "No more information available"
+msgstr "No more information available"
+
+#: service_internal.c:1432 service_internal.c:1485
+msgid "No debug message available"
+msgstr "No debug message available"
+
+#: service_internal.c:1546
+msgid "Unable to fetch any result"
+msgstr "Unable to fetch any result"
+
+#: service_internal.c:1587
+#, c-format
+msgid ""
+"Unable to run the Service. The message returned back by the Service was the "
+"following : %s"
+msgstr ""
+"Unable to run the Service. The message returned back by the Service was the "
+"following : %s"
+
+#: service_internal.c:1589
+#, c-format
+msgid ""
+"Unable to run the Service. No more information was returned back by the "
+"Service."
+msgstr ""
+"Unable to run the Service. No more information was returned back by the "
+"Service."
+
+#: zoo_service_loader.c:119
+#, c-format
+msgid "ZOO Kernel failed to process your request receiving signal %d = %s"
+msgstr "ZOO Kernel failed to process your request receiving signal %d = %s"
+
+#: zoo_service_loader.c:147 zoo_service_loader.c:280 zoo_service_loader.c:367
+#: zoo_service_loader.c:420 zoo_service_loader.c:547 zoo_service_loader.c:623
+#: zoo_service_loader.c:637 zoo_service_loader.c:664 zoo_service_loader.c:705
+#: zoo_service_loader.c:788 zoo_service_loader.c:806 zoo_service_loader.c:864
+#: zoo_service_loader.c:905 zoo_service_loader.c:952 zoo_service_loader.c:987
+#: zoo_service_loader.c:1005 zoo_service_loader.c:1146
+#: zoo_service_loader.c:1218 zoo_service_loader.c:1238
+#: zoo_service_loader.c:1298 zoo_service_loader.c:1324
+msgid "Unable to allocate memory."
+msgstr "Unable to allocate memory."
+
+#: zoo_service_loader.c:181
+msgid "Parameter <request> was not specified"
+msgstr "Parameter <request> was not specified"
+
+#: zoo_service_loader.c:191 zoo_service_loader.c:402
+msgid ""
+"Unenderstood <request> value. Please check that it was set to "
+"GetCapabilities, DescribeProcess or Execute."
+msgstr ""
+"Unenderstood <request> value. Please check that it was set to "
+"GetCapabilities, DescribeProcess or Execute."
+
+#: zoo_service_loader.c:201
+msgid "Parameter <service> was not specified"
+msgstr "Parameter <service> was not specified"
+
+#: zoo_service_loader.c:210
+msgid "Parameter <version> was not specified"
+msgstr "Parameter <version> was not specified"
+
+#: zoo_service_loader.c:257
+msgid "The specified path doesn't exist."
+msgstr "The specified path doesn't exist."
+
+#: zoo_service_loader.c:311
+msgid "Mandatory <identifier> was not specified"
+msgstr "Mandatory <identifier> was not specified"
+
+#: zoo_service_loader.c:322
+msgid "The specified path path doesn't exist."
+msgstr "The specified path path doesn't exist."
+
+#: zoo_service_loader.c:404
+#, c-format
+msgid "No request found %s"
+msgstr "No request found %s"
+
+#: zoo_service_loader.c:441
+#, c-format
+msgid ""
+"The value for <indetifier> seems to be wrong (%s). Please, ensure that the "
+"process exist using the GetCapabilities request."
+msgstr ""
+"The value for <indetifier> seems to be wrong (%s). Please, ensure that the "
+"process exist using the GetCapabilities request."
+
+#: zoo_service_loader.c:519 zoo_service_loader.c:530
+msgid "Unable to allocate memory"
+msgstr "Unable to allocate memory"
+
+#: zoo_service_loader.c:606
+msgid "Parameter <DataInputs> was not specified"
+msgstr "Parameter <DataInputs> was not specified"
+
+#: service_internal.c:1048
+#, c-format
+msgid "Service \"%s\" run successfully."
+msgstr "Service \"%s\" run successfully."
+
+#: zoo_service_loader.c:1366
+#, c-format
+msgid ""
+"The <%s> argument was not specified in DataInputs but defined as requested "
+"in ZOO ServicesProvider configuration file, please correct your query or the "
+"ZOO Configuration file."
+msgstr ""
+"The <%s> argument was not specified in DataInputs but defined as requested "
+"in ZOO ServicesProvider configuration file, please correct your query or the "
+"ZOO Configuration file."
+
+#: zoo_service_loader.c:1651 zoo_service_loader.c:1871
+#, c-format
+msgid ""
+"Programming Language (%s) set in ZCFG file is not currently supported by ZOO "
+"Kernel.\n"
+msgstr ""
+"Programming Language (%s) set in ZCFG file is not currently supported by ZOO "
+"Kernel.\n"
+
+
+#: zoo_service_loader.c:1700
+msgid "Unable to run the child process properly"
+msgstr "Unable to run the child process properly"
Index: /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/fr_FR.utf8.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/fr_FR.utf8.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/fr_FR.utf8.po	(revision 221)
@@ -0,0 +1,174 @@
+# French translations for ZOO Kernel package.
+# Copyright (C) 2010 THE ZOO Kernel'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the ZOO Kernel package.
+# root <gerald.fenoy@geolabs.fr>, 2010.
+#
+#: service_internal.c:1672 zoo_service_loader.c:158 zoo_service_loader.c:160
+#: zoo_service_loader.c:220 zoo_service_loader.c:267 zoo_service_loader.c:341
+#: zoo_service_loader.c:1135 zoo_service_loader.c:1277
+#: zoo_service_loader.c:1364
+msgid ""
+msgstr ""
+"Project-Id-Version: ZOO Kernel 0.0.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-28 00:38+0200\n"
+"PO-Revision-Date: 2010-09-28 00:39+0200\n"
+"Last-Translator: root <gerald.fenoy@geolabs.fr>\n"
+"Language-Team: French\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: service_internal.c:34
+msgid "0123456789abcdef"
+msgstr ""
+
+#: service_internal.c:1056
+#, c-format
+msgid ""
+"ZOO Service \"%s\" is currently running. Please, reload this document to get "
+"the up-to-date status of the Service."
+msgstr ""
+"Le Service ZOO \"%s\" est en cous d'exécution. Merci de recharger document pour"
+" obtenir une version à jour du status du Service."
+
+#: service_internal.c:1062
+#, c-format
+msgid ""
+"Service \"%s\" was accepted by the ZOO Kernel and it run as a background "
+"task. Please consult the statusLocation attribtue providen in this document "
+"to get the up-to-date document."
+msgstr ""
+"Le Service \"%s\" a été accepté par le Noyau ZOO et tourne en tâche de fond. "
+"Merci de consulter le lien fourni dans ce document afin d'obtenir le status actuel du "
+"Service."
+
+#: service_internal.c:1079
+msgid "No more information available"
+msgstr "Pas plus d'information disponible"
+
+#: service_internal.c:1432 service_internal.c:1485
+msgid "No debug message available"
+msgstr "Pas de message de débuguage disponible"
+
+#: service_internal.c:1546
+msgid "Unable to fetch any result"
+msgstr "Impossible de récupérer un résultat"
+
+#: service_internal.c:1587
+#, c-format
+msgid ""
+"Unable to run the Service. The message returned back by the Service was the "
+"following : %s"
+msgstr ""
+"Impossible d'exécuter le Servive. Le message retourné par le Service est le suivant : %s"
+
+#: service_internal.c:1589
+#, c-format
+msgid ""
+"Unable to run the Service. No more information was returned back by the "
+"Service."
+msgstr ""
+"Impossible d'exécuter le Service. Pas plus d'information disponible."
+
+#: zoo_service_loader.c:119
+#, c-format
+msgid "ZOO Kernel failed to process your request receiving signal %d = %s"
+msgstr ""
+"Le Noyau ZOO a rencontré un problème lors de l'exécution de votre resquête et a "
+"reçu le signal %d : %s "
+
+#: zoo_service_loader.c:147 zoo_service_loader.c:280 zoo_service_loader.c:367
+#: zoo_service_loader.c:420 zoo_service_loader.c:547 zoo_service_loader.c:623
+#: zoo_service_loader.c:637 zoo_service_loader.c:664 zoo_service_loader.c:705
+#: zoo_service_loader.c:788 zoo_service_loader.c:806 zoo_service_loader.c:864
+#: zoo_service_loader.c:905 zoo_service_loader.c:952 zoo_service_loader.c:987
+#: zoo_service_loader.c:1005 zoo_service_loader.c:1146
+#: zoo_service_loader.c:1218 zoo_service_loader.c:1238
+#: zoo_service_loader.c:1298 zoo_service_loader.c:1324
+msgid "Unable to allocate memory."
+msgstr "Impossible d'allouer de la mémoire."
+
+#: zoo_service_loader.c:181
+msgid "Parameter <request> was not specified"
+msgstr "Le paramètre <request> n'a pas été spécifié"
+
+#: zoo_service_loader.c:191 zoo_service_loader.c:402
+msgid ""
+"Unenderstood <request> value. Please check that it was set to "
+"GetCapabilities, DescribeProcess or Execute."
+msgstr ""
+"La valeur de <request> est incompréhensible. Merci d'utiliser l'une des valeurs "
+"suivantes : GetCapabilities, DescribeProcess ou Execute."
+
+#: zoo_service_loader.c:201
+msgid "Parameter <service> was not specified"
+msgstr "La paramètre <service> n'a pas été spécifié"
+
+#: zoo_service_loader.c:210
+msgid "Parameter <version> was not specified"
+msgstr "Le paramètre <version> n'a pas été spécifié"
+
+#: zoo_service_loader.c:257
+msgid "The specified path doesn't exist."
+msgstr "Le chemin spécifié n'existe pas"
+
+#: zoo_service_loader.c:311
+msgid "Mandatory <identifier> was not specified"
+msgstr "Le paramètre obligatoire <identifier> n'a pas été précisé"
+
+#: zoo_service_loader.c:322
+msgid "The specified path path doesn't exist."
+msgstr "Le chemin fourni n'existe pas."
+
+#: zoo_service_loader.c:404
+#, c-format
+msgid "No request found %s"
+msgstr "Aucune requête trouvé %s"
+
+#: zoo_service_loader.c:441
+#, c-format
+msgid ""
+"The value for <indetifier> seems to be wrong (%s). Please, ensure that the "
+"process exist using the GetCapabilities request."
+msgstr ""
+"La valeur poru le paramètre <identifier> semble éronné (%s). Merci de vous assurer que"
+" le service existe en utilisant un requête GetCapabilities."
+
+#: zoo_service_loader.c:519 zoo_service_loader.c:530
+msgid "Unable to allocate memory"
+msgstr "Impossible d'allouer de la mémoire"
+
+#: zoo_service_loader.c:606
+msgid "Parameter <DataInputs> was not specified"
+msgstr "Paramètre <DataInputs> n'a pas été précisé"
+
+#: service_internal.c:1048
+#, c-format
+msgid "Service \"%s\" run successfully."
+msgstr "Le Service \"%s\" a été exécuté avec succès."
+
+#: zoo_service_loader.c:1366
+#, c-format
+msgid ""
+"The <%s> argument was not specified in DataInputs but defined as requested "
+"in ZOO ServicesProvider configuration file, please correct your query or the "
+"ZOO Configuration file."
+msgstr ""
+"L'argument <%s> n'a pas été précisé dans les données en entrée (DataInputs) mais "
+"est défini comme obligatoire dans le fichier de méta-données du Service ZOO (zcfg). "
+"Merci de corriger votre requête ou le fichier de méta-données."
+
+#: zoo_service_loader.c:1651 zoo_service_loader.c:1871
+#, c-format
+msgid ""
+"Programming Language (%s) set in ZCFG file is not currently supported by ZOO "
+"Kernel.\n"
+msgstr ""
+"Le langage de programmation (%s) utilisé dans le fichier ZCFG n'est actuellement pas "
+"supporté par votre installation du noyau ZOO.\n"
+
+#: zoo_service_loader.c:1700
+msgid "Unable to run the child process properly"
+msgstr "Impossible d'exécuter le processus en tâche de fond"
Index: /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/ja_JP.utf8.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/ja_JP.utf8.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/ja_JP.utf8.po	(revision 221)
@@ -0,0 +1,174 @@
+# Japanese translations for ZOO Kernel package.
+# Copyright (C) 2010 THE ZOO Kernel'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the ZOO Kernel package.
+# Daisuke YOSHIDA <yoshida@la.tezuka-gu.ac.jp>, 2010.
+#
+#: service_internal.c:1672 zoo_service_loader.c:158 zoo_service_loader.c:160
+#: zoo_service_loader.c:220 zoo_service_loader.c:267 zoo_service_loader.c:341
+#: zoo_service_loader.c:1135 zoo_service_loader.c:1277
+#: zoo_service_loader.c:1364
+msgid ""
+msgstr ""
+"Project-Id-Version: ZOO Kernel 0.0.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-28 00:38+0200\n"
+"PO-Revision-Date: 2010-09-28 00:39+0200\n"
+"Last-Translator: Daisuke YOSHIDA <yoshida@la.tezuka-gu.ac.jp>\n"
+"Language-Team: Japanese\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: service_internal.c:34
+msgid "0123456789abcdef"
+msgstr ""
+
+#: service_internal.c:1056
+#, c-format
+msgid ""
+"ZOO Service \"%s\" is currently running. Please, reload this document to get "
+"the up-to-date status of the Service."
+msgstr ""
+"現在，ZOOサービス \"%s\" が動作しています. このサービスのステータスを最新のものにするために，"
+"このドキュメントを再読込してください."
+
+#: service_internal.c:1062
+#, c-format
+msgid ""
+"Service \"%s\" was accepted by the ZOO Kernel and it run as a background "
+"task. Please consult the statusLocation attribtue providen in this document "
+"to get the up-to-date document."
+msgstr ""
+"ZOOカーネルが \"%s\" サービスをアクセプトし，バックグラウンドタスクとして動作しています．"
+"ドキュメントを最新の状態に保つために，ドキュメントで提供されている"
+"statusLocation属性を確認してください．"
+
+#: service_internal.c:1079
+msgid "No more information available"
+msgstr "情報はありません．"
+
+#: service_internal.c:1432 service_internal.c:1485
+msgid "No debug message available"
+msgstr "デバッグメッセージはありません．"
+
+#: service_internal.c:1546
+msgid "Unable to fetch any result"
+msgstr "結果が取得できませんでした．"
+
+#: service_internal.c:1587
+#, c-format
+msgid ""
+"Unable to run the Service. The message returned back by the Service was the "
+"following : %s"
+msgstr ""
+"サービスを開始することができませんでした．このサービスによって返されたメッセージは  %s　です。"
+
+#: service_internal.c:1589
+#, c-format
+msgid ""
+"Unable to run the Service. No more information was returned back by the "
+"Service."
+msgstr ""
+"サービスを開始できませんでした．このサービスによって返された情報はありません ."
+
+#: zoo_service_loader.c:119
+#, c-format
+msgid "ZOO Kernel failed to process your request receiving signal %d = %s"
+msgstr ""
+"ZOOカーネルはリクエストを処理することができませんでした．受け取ったシグナルは"
+"%d : %s です．"
+
+#: zoo_service_loader.c:147 zoo_service_loader.c:280 zoo_service_loader.c:367
+#: zoo_service_loader.c:420 zoo_service_loader.c:547 zoo_service_loader.c:623
+#: zoo_service_loader.c:637 zoo_service_loader.c:664 zoo_service_loader.c:705
+#: zoo_service_loader.c:788 zoo_service_loader.c:806 zoo_service_loader.c:864
+#: zoo_service_loader.c:905 zoo_service_loader.c:952 zoo_service_loader.c:987
+#: zoo_service_loader.c:1005 zoo_service_loader.c:1146
+#: zoo_service_loader.c:1218 zoo_service_loader.c:1238
+#: zoo_service_loader.c:1298 zoo_service_loader.c:1324
+msgid "Unable to allocate memory."
+msgstr "Impossible d'allouer de la mémoire."
+
+#: zoo_service_loader.c:181
+msgid "Parameter <request> was not specified"
+msgstr "パラメーターが指定されていません．"
+
+#: zoo_service_loader.c:191 zoo_service_loader.c:402
+msgid ""
+"Unenderstood <request> value. Please check that it was set to "
+"GetCapabilities, DescribeProcess or Execute."
+msgstr ""
+"<request>値を認識しました. 値が下記のリクエストを指定されているか確認してください．"
+"GetCapabilities, DescribeProcess, Execute."
+
+#: zoo_service_loader.c:201
+msgid "Parameter <service> was not specified"
+msgstr "<service> パラメーターが指定されていません．"
+
+#: zoo_service_loader.c:210
+msgid "Parameter <version> was not specified"
+msgstr "<version>パラメーターが指定されていません．"
+
+#: zoo_service_loader.c:257
+msgid "The specified path doesn't exist."
+msgstr "指定されたパスが存在しません．"
+
+#: zoo_service_loader.c:311
+msgid "Mandatory <identifier> was not specified"
+msgstr "<identifier>値（要指定）が指定されていません．"
+
+#: zoo_service_loader.c:322
+msgid "The specified path path doesn't exist."
+msgstr "指定されたパスが存在しません．"
+
+#: zoo_service_loader.c:404
+#, c-format
+msgid "No request found %s"
+msgstr "%sのリクエストが見つかりません．"
+
+#: zoo_service_loader.c:441
+#, c-format
+msgid ""
+"The value for <indetifier> seems to be wrong (%s). Please, ensure that the "
+"process exist using the GetCapabilities request."
+msgstr ""
+"<identifier>についての値が不正です(%s). GetCapabilitiesにより"
+"そのプロセスが存在しているか確認してください."
+
+#: zoo_service_loader.c:519 zoo_service_loader.c:530
+msgid "Unable to allocate memory"
+msgstr "メモリを割り当てられません．"
+
+#: zoo_service_loader.c:606
+msgid "Parameter <DataInputs> was not specified"
+msgstr "<DataInputs> パラメーターが指定されていません．"
+
+#: service_internal.c:1048
+#, c-format
+msgid "Service \"%s\" run successfully."
+msgstr " \"%s\"サービスが動作しました."
+
+#: zoo_service_loader.c:1366
+#, c-format
+msgid ""
+"The <%s> argument was not specified in DataInputs but defined as requested "
+"in ZOO ServicesProvider configuration file, please correct your query or the "
+"ZOO Configuration file."
+msgstr ""
+"DataInputsの中の引数 <%s>が指定されていませんでしたが，"
+"ZOO ServicesProviderの設定ファイル(zcfg)の中で定義されています．"
+"クエリー，もしくはZOO設定ファイルを修正してください．"
+
+#: zoo_service_loader.c:1651 zoo_service_loader.c:1871
+#, c-format
+msgid ""
+"Programming Language (%s) set in ZCFG file is not currently supported by ZOO "
+"Kernel.\n"
+msgstr ""
+"ZCFGファイルの中で設定されているプログラム言語 (%s) は "
+"現在のZOOカーネルではサポートしていません.\n"
+
+#: zoo_service_loader.c:1700
+msgid "Unable to run the child process properly"
+msgstr "子プロセスを開始することができません．"
Index: /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/messages.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/messages.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/locale/po/messages.po	(revision 221)
@@ -0,0 +1,1725 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#: service_internal.c:1672 zoo_service_loader.c:158 zoo_service_loader.c:160
+#: zoo_service_loader.c:220 zoo_service_loader.c:267 zoo_service_loader.c:341
+#: zoo_service_loader.c:1135 zoo_service_loader.c:1277
+#: zoo_service_loader.c:1364
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-28 00:38+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: service_internal.c:34
+msgid "0123456789abcdef"
+msgstr ""
+
+#: service_internal.c:44 service_internal.c:88 service_internal.c:90
+#: service_internal.c:92 service_internal.c:108 service_internal.c:122
+#: service_internal.c:986 service_internal.c:1054 service_internal.c:1070
+#: service_internal.c:1075 service_internal.c:1398 service_internal.c:1585
+#: zoo_service_loader.c:1443
+msgid "lenv"
+msgstr ""
+
+#: service_internal.c:44 service_internal.c:108 service_internal.c:986
+#: service_internal.c:1398 zoo_service_loader.c:1446
+msgid "sid"
+msgstr ""
+
+#: service_internal.c:49 service_internal.c:113
+#, c-format
+msgid "shmget failed to update value\n"
+msgstr ""
+
+#: service_internal.c:54 service_internal.c:118
+#, c-format
+msgid "shmat failed to update value\n"
+msgstr ""
+
+#: service_internal.c:77
+#, c-format
+msgid "Number of arguments used to call the function : %i"
+msgstr ""
+
+#: service_internal.c:84 service_internal.c:988 zoo_service_loader.c:1445
+#, c-format
+msgid "%i"
+msgstr ""
+
+#: service_internal.c:88 service_internal.c:90 service_internal.c:92
+#: service_internal.c:122 service_internal.c:1054 service_internal.c:1285
+#: service_internal.c:1327 zoo_service_loader.c:1158 zoo_service_loader.c:1448
+msgid "status"
+msgstr ""
+
+#: service_internal.c:92
+msgid "15"
+msgstr ""
+
+#: service_internal.c:140
+#, c-format
+msgid "shmget failed in getStatus\n"
+msgstr ""
+
+#: service_internal.c:145
+#, c-format
+msgid "shmat failed in getStatus\n"
+msgstr ""
+
+#: service_internal.c:151
+msgid "-1"
+msgstr ""
+
+#: service_internal.c:223
+#, c-format
+msgid "zooXmlAddNs %d \n"
+msgstr ""
+
+#: service_internal.c:246 service_internal.c:250
+#, c-format
+msgid "zooXmlCleanup %d\n"
+msgstr ""
+
+#: service_internal.c:269 service_internal.c:276 service_internal.c:546
+#: service_internal.c:584 service_internal.c:940 service_internal.c:950
+msgid "http://www.opengis.net/wps/1.0.0"
+msgstr ""
+
+#: service_internal.c:269 service_internal.c:276 service_internal.c:546
+#: service_internal.c:579 service_internal.c:584 service_internal.c:606
+#: service_internal.c:940 service_internal.c:950
+msgid "wps"
+msgstr ""
+
+#: service_internal.c:271 service_internal.c:494 service_internal.c:961
+#: service_internal.c:1395 service_internal.c:1454 service_internal.c:1513
+#: service_internal.c:1517 service_internal.c:1518 zoo_service_loader.c:225
+#: zoo_service_loader.c:1683
+msgid "main"
+msgstr ""
+
+#: service_internal.c:273
+msgid "Capabilities"
+msgstr ""
+
+#: service_internal.c:274 service_internal.c:548 service_internal.c:582
+#: service_internal.c:608 service_internal.c:944 service_internal.c:1408
+#: service_internal.c:1410 service_internal.c:1456 service_internal.c:1460
+msgid "http://www.opengis.net/ows/1.1"
+msgstr ""
+
+#: service_internal.c:274 service_internal.c:548 service_internal.c:582
+#: service_internal.c:608 service_internal.c:944 service_internal.c:1408
+#: service_internal.c:1410 service_internal.c:1456 service_internal.c:1460
+msgid "ows"
+msgstr ""
+
+#: service_internal.c:277 service_internal.c:586 service_internal.c:948
+#: service_internal.c:1412 service_internal.c:1461
+msgid "http://www.w3.org/2001/XMLSchema-instance"
+msgstr ""
+
+#: service_internal.c:277 service_internal.c:586 service_internal.c:948
+#: service_internal.c:1412 service_internal.c:1461
+msgid "xsi"
+msgstr ""
+
+#: service_internal.c:279 service_internal.c:550 service_internal.c:585
+#: service_internal.c:610 service_internal.c:946 service_internal.c:1411
+#: service_internal.c:1463
+msgid "http://www.w3.org/1999/xlink"
+msgstr ""
+
+#: service_internal.c:279 service_internal.c:550 service_internal.c:585
+#: service_internal.c:610 service_internal.c:946 service_internal.c:1411
+#: service_internal.c:1463
+msgid "xlink"
+msgstr ""
+
+#: service_internal.c:281 service_internal.c:589 service_internal.c:952
+#: service_internal.c:1465
+msgid "schemaLocation"
+msgstr ""
+
+#: service_internal.c:281
+msgid ""
+"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/"
+"wpsGetCapabilities_response.xsd"
+msgstr ""
+
+#: service_internal.c:282 service_internal.c:590 service_internal.c:954
+msgid "service"
+msgstr ""
+
+#: service_internal.c:282 service_internal.c:340 service_internal.c:590
+#: service_internal.c:954
+msgid "WPS"
+msgstr ""
+
+#: service_internal.c:285 service_internal.c:287 service_internal.c:290
+#: service_internal.c:293 service_internal.c:306 service_internal.c:591
+#: service_internal.c:955 service_internal.c:1375 service_internal.c:1415
+#: service_internal.c:1468
+msgid "version"
+msgstr ""
+
+#: service_internal.c:290 service_internal.c:293 service_internal.c:343
+#: service_internal.c:591 service_internal.c:955 service_internal.c:1380
+#: service_internal.c:1383
+msgid "1.0.0"
+msgstr ""
+
+#: service_internal.c:297
+msgid "ServiceIdentification"
+msgstr ""
+
+#: service_internal.c:298
+msgid "identification"
+msgstr ""
+
+#: service_internal.c:302 service_internal.c:313 service_internal.c:364
+#: service_internal.c:407
+msgid "keywords"
+msgstr ""
+
+#: service_internal.c:303 service_internal.c:365 service_internal.c:455
+#: service_internal.c:963 service_internal.c:1518 zoo_service_loader.c:225
+msgid "serverAddress"
+msgstr ""
+
+#: service_internal.c:304 service_internal.c:366 service_internal.c:496
+msgid "lang"
+msgstr ""
+
+#: service_internal.c:305 service_internal.c:1293 service_internal.c:1362
+#: service_internal.c:1559 zoo_service_loader.c:840 zoo_service_loader.c:1073
+#: zoo_service_loader.c:1186 zoo_service_loader.c:1286
+msgid "encoding"
+msgstr ""
+
+#: service_internal.c:314 service_internal.c:408
+msgid "Keywords"
+msgstr ""
+
+#: service_internal.c:326 service_internal.c:334 service_internal.c:420
+#: service_internal.c:428
+msgid "Keyword"
+msgstr ""
+
+#: service_internal.c:339
+msgid "ServiceType"
+msgstr ""
+
+#: service_internal.c:342
+msgid "ServiceTypeVersion"
+msgstr ""
+
+#: service_internal.c:350 service_internal.c:438
+#, c-format
+msgid "TMP4 NOT FOUND !!"
+msgstr ""
+
+#: service_internal.c:355 zoo_service_loader.c:261 zoo_service_loader.c:335
+#: zoo_service_loader.c:1477 zoo_service_loader.c:1684
+#: zoo_service_loader.c:1728
+msgid "ServiceProvider"
+msgstr ""
+
+#: service_internal.c:356
+msgid "ServiceContact"
+msgstr ""
+
+#: service_internal.c:357
+msgid "ContactInfo"
+msgstr ""
+
+#: service_internal.c:358 service_internal.c:387 service_internal.c:390
+msgid "Phone"
+msgstr ""
+
+#: service_internal.c:359 service_internal.c:396 service_internal.c:399
+msgid "Address"
+msgstr ""
+
+#: service_internal.c:360
+msgid "provider"
+msgstr ""
+
+#: service_internal.c:368
+msgid "ProviderName"
+msgstr ""
+
+#: service_internal.c:374
+msgid "ProviderSite"
+msgstr ""
+
+#: service_internal.c:376 service_internal.c:472 service_internal.c:476
+#: service_internal.c:1320 zoo_service_loader.c:843 zoo_service_loader.c:942
+#: zoo_service_loader.c:968 zoo_service_loader.c:994
+msgid "href"
+msgstr ""
+
+#: service_internal.c:380
+msgid "IndividualName"
+msgstr ""
+
+#: service_internal.c:381
+msgid "PositionName"
+msgstr ""
+
+#: service_internal.c:447
+msgid "OperationsMetadata"
+msgstr ""
+
+#: service_internal.c:449 zoo_service_loader.c:188 zoo_service_loader.c:207
+#: zoo_service_loader.c:249
+msgid "GetCapabilities"
+msgstr ""
+
+#: service_internal.c:450 zoo_service_loader.c:189 zoo_service_loader.c:329
+msgid "DescribeProcess"
+msgstr ""
+
+#: service_internal.c:451 zoo_service_loader.c:190 zoo_service_loader.c:401
+msgid "Execute"
+msgstr ""
+
+#: service_internal.c:460 service_internal.c:463
+msgid "not_found"
+msgstr ""
+
+#: service_internal.c:466
+msgid "Operation"
+msgstr ""
+
+#: service_internal.c:467
+msgid "name"
+msgstr ""
+
+#: service_internal.c:468
+msgid "DCP"
+msgstr ""
+
+#: service_internal.c:469
+msgid "HTTP"
+msgstr ""
+
+#: service_internal.c:470
+msgid "Get"
+msgstr ""
+
+#: service_internal.c:471 zoo_service_loader.c:245 zoo_service_loader.c:277
+#: zoo_service_loader.c:363 zoo_service_loader.c:424 zoo_service_loader.c:1473
+#: zoo_service_loader.c:1478 zoo_service_loader.c:1727
+#: zoo_service_loader.c:1730
+#, c-format
+msgid "%s/%s"
+msgstr ""
+
+#: service_internal.c:475
+msgid "Post"
+msgstr ""
+
+#: service_internal.c:487
+msgid "ProcessOfferings"
+msgstr ""
+
+#: service_internal.c:490
+msgid "Languages"
+msgstr ""
+
+#: service_internal.c:491 service_internal.c:674 service_internal.c:679
+#: service_internal.c:830 service_internal.c:833
+msgid "Default"
+msgstr ""
+
+#: service_internal.c:492 service_internal.c:757 service_internal.c:761
+#: service_internal.c:876 service_internal.c:880
+msgid "Supported"
+msgstr ""
+
+#: service_internal.c:509 service_internal.c:517 service_internal.c:526
+msgid "Language"
+msgstr ""
+
+#: service_internal.c:514 service_internal.c:592 service_internal.c:956
+#: service_internal.c:1414 service_internal.c:1467
+msgid "xml:lang"
+msgstr ""
+
+#: service_internal.c:556 service_internal.c:1017
+msgid "Process"
+msgstr ""
+
+#: service_internal.c:557 service_internal.c:559 service_internal.c:615
+#: service_internal.c:624 service_internal.c:1018 service_internal.c:1021
+msgid "processVersion"
+msgstr ""
+
+#: service_internal.c:563 service_internal.c:638
+msgid "Metadata"
+msgstr ""
+
+#: service_internal.c:579 service_internal.c:606
+msgid "http://schemas.opengis.net/wps/1.0.0"
+msgstr ""
+
+#: service_internal.c:581
+msgid "ProcessDescriptions"
+msgstr ""
+
+#: service_internal.c:589
+msgid ""
+"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/"
+"wpsDescribeProcess_response.xsd"
+msgstr ""
+
+#: service_internal.c:592 service_internal.c:956 service_internal.c:1414
+#: service_internal.c:1467
+msgid "en"
+msgstr ""
+
+#: service_internal.c:613
+msgid "ProcessDescription"
+msgstr ""
+
+#: service_internal.c:616
+msgid "storeSupported"
+msgstr ""
+
+#: service_internal.c:617
+msgid "statusSupported"
+msgstr ""
+
+#: service_internal.c:630 service_internal.c:991 service_internal.c:1199
+#: service_internal.c:1227 zoo_service_loader.c:1458
+msgid "false"
+msgstr ""
+
+#: service_internal.c:644 service_internal.c:646
+msgid "Profile"
+msgstr ""
+
+#: service_internal.c:651 service_internal.c:1098 zoo_service_loader.c:597
+msgid "DataInputs"
+msgstr ""
+
+#: service_internal.c:655 service_internal.c:1103
+msgid "Input"
+msgstr ""
+
+#: service_internal.c:656 service_internal.c:805 service_internal.c:1626
+msgid "minOccurs"
+msgstr ""
+
+#: service_internal.c:660 service_internal.c:809
+msgid "maxOccurs"
+msgstr ""
+
+#: service_internal.c:673 service_internal.c:789 service_internal.c:827
+#: service_internal.c:1274 zoo_service_loader.c:1041
+msgid "LiteralData"
+msgstr ""
+
+#: service_internal.c:675 service_internal.c:758 service_internal.c:834
+#: service_internal.c:877
+msgid "Format"
+msgstr ""
+
+#: service_internal.c:678 service_internal.c:829
+msgid "UOMs"
+msgstr ""
+
+#: service_internal.c:687 service_internal.c:839
+#, c-format
+msgid "DATATYPE DEFAULT ? %s\n"
+msgstr ""
+
+#: service_internal.c:689 service_internal.c:690 service_internal.c:700
+#: service_internal.c:841 service_internal.c:842 service_internal.c:853
+#: service_internal.c:887
+msgid "DataType"
+msgstr ""
+
+#: service_internal.c:693 service_internal.c:845
+#, c-format
+msgid "http://www.w3.org/TR/xmlschema-2/#%s"
+msgstr ""
+
+#: service_internal.c:694 service_internal.c:846 service_internal.c:1324
+msgid "reference"
+msgstr ""
+
+#: service_internal.c:699 service_internal.c:852 service_internal.c:886
+#: service_internal.c:1197 service_internal.c:1199 service_internal.c:1225
+#: service_internal.c:1227 service_internal.c:1284 service_internal.c:1329
+#: service_internal.c:1510 zoo_service_loader.c:1189
+msgid "asReference"
+msgstr ""
+
+#: service_internal.c:701 service_internal.c:723 service_internal.c:724
+msgid "AllowedValues"
+msgstr ""
+
+#: service_internal.c:702 service_internal.c:718 service_internal.c:1282
+#: service_internal.c:1295 service_internal.c:1323 service_internal.c:1522
+#: service_internal.c:1544 service_internal.c:1640 service_internal.c:1642
+#: zoo_service_loader.c:667 zoo_service_loader.c:709 zoo_service_loader.c:870
+#: zoo_service_loader.c:890 zoo_service_loader.c:960 zoo_service_loader.c:1013
+#: zoo_service_loader.c:1093
+msgid "value"
+msgstr ""
+
+#: service_internal.c:703 service_internal.c:854 service_internal.c:888
+#: service_internal.c:1283 service_internal.c:1287 service_internal.c:1325
+#: service_internal.c:1512
+msgid "extension"
+msgstr ""
+
+#: service_internal.c:719
+msgid "DefaultValue"
+msgstr ""
+
+#: service_internal.c:726 service_internal.c:734 zoo_service_loader.c:344
+#: zoo_service_loader.c:382
+msgid ","
+msgstr ""
+
+#: service_internal.c:728
+msgid "Value"
+msgstr ""
+
+#: service_internal.c:732
+#, c-format
+msgid "strgin : %s\n"
+msgstr ""
+
+#: service_internal.c:745
+msgid "AnyValue"
+msgstr ""
+
+#: service_internal.c:800 service_internal.c:1131
+msgid "ProcessOutputs"
+msgstr ""
+
+#: service_internal.c:804 service_internal.c:1117 service_internal.c:1135
+#: zoo_service_loader.c:1179
+msgid "Output"
+msgstr ""
+
+#: service_internal.c:816
+msgid "LITERALDATA"
+msgstr ""
+
+#: service_internal.c:817 service_internal.c:826
+msgid "LiteralOutput"
+msgstr ""
+
+#: service_internal.c:819
+msgid "COMPLEXDATA"
+msgstr ""
+
+#: service_internal.c:820
+msgid "ComplexOutput"
+msgstr ""
+
+#: service_internal.c:884
+#, c-format
+msgid "DATATYPE SUPPORTED ? %s\n"
+msgstr ""
+
+#: service_internal.c:939 service_internal.c:1394 zoo_service_loader.c:259
+#: zoo_service_loader.c:333 zoo_service_loader.c:928
+msgid "1.0"
+msgstr ""
+
+#: service_internal.c:943
+msgid "ExecuteResponse"
+msgstr ""
+
+#: service_internal.c:952
+msgid ""
+"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/"
+"wpsExecute_response.xsd"
+msgstr ""
+
+#: service_internal.c:980
+#, c-format
+msgid "%s/GetStatus.zcfg"
+msgstr ""
+
+#: service_internal.c:984
+msgid "rewriteUrl"
+msgstr ""
+
+#: service_internal.c:990 service_internal.c:1439 service_internal.c:1571
+#: service_internal.c:1577 zoo_service_loader.c:247 zoo_service_loader.c:503
+#: zoo_service_loader.c:532 zoo_service_loader.c:604 zoo_service_loader.c:635
+#: zoo_service_loader.c:901 zoo_service_loader.c:938
+#, c-format
+msgid "%s"
+msgstr ""
+
+#: service_internal.c:992 service_internal.c:1000
+#, c-format
+msgid ""
+"%s/?request=Execute&amp;service=WPS&amp;version=1.0.0&amp;"
+"Identifier=GetStatus&amp;DataInputs=sid=%s&amp;RawDataOutput=Result"
+msgstr ""
+
+#: service_internal.c:995 service_internal.c:1511
+msgid "true"
+msgstr ""
+
+#: service_internal.c:996
+#, c-format
+msgid "%s/%s/GetStatus/%s"
+msgstr ""
+
+#: service_internal.c:998
+#, c-format
+msgid "%s/GetStatus/%s"
+msgstr ""
+
+#: service_internal.c:1003 service_internal.c:1517
+msgid "tmpUrl"
+msgstr ""
+
+#: service_internal.c:1005
+#, c-format
+msgid "%s/%s/%s_%i.xml"
+msgstr ""
+
+#: service_internal.c:1009 zoo_service_loader.c:426 zoo_service_loader.c:1475
+#: zoo_service_loader.c:1732
+#, c-format
+msgid "%s/"
+msgstr ""
+
+#: service_internal.c:1012
+msgid "serviceInstance"
+msgstr ""
+
+#: service_internal.c:1014
+msgid "statusLocation"
+msgstr ""
+
+#: service_internal.c:1028
+msgid "Status"
+msgstr ""
+
+#: service_internal.c:1040
+msgid "%Y-%m-%dT%I:%M:%SZ"
+msgstr ""
+
+#: service_internal.c:1042
+msgid "creationTime"
+msgstr ""
+
+#: service_internal.c:1047
+msgid "ProcessSucceeded"
+msgstr ""
+
+#: service_internal.c:1048
+#, c-format
+msgid "Service \"%s\" run successfully."
+msgstr ""
+
+#: service_internal.c:1053
+msgid "ProcessStarted"
+msgstr ""
+
+#: service_internal.c:1055
+msgid "percentCompleted"
+msgstr ""
+
+#: service_internal.c:1056
+#, c-format
+msgid ""
+"ZOO Service \"%s\" is currently running. Please, reload this document to get "
+"the up-to-date status of the Service."
+msgstr ""
+
+#: service_internal.c:1061
+msgid "ProcessAccepted"
+msgstr ""
+
+#: service_internal.c:1062
+#, c-format
+msgid ""
+"Service \"%s\" was accepted by the ZOO Kernel and it run as a background "
+"task. Please consult the statusLocation attribtue providen in this document "
+"to get the up-to-date document."
+msgstr ""
+
+#: service_internal.c:1067
+msgid "ProcessFailed"
+msgstr ""
+
+#: service_internal.c:1070 service_internal.c:1072 service_internal.c:1074
+#: service_internal.c:1419 service_internal.c:1472 service_internal.c:1547
+#: service_internal.c:1591 zoo_service_loader.c:1368
+msgid "code"
+msgstr ""
+
+#: service_internal.c:1074 service_internal.c:1423 service_internal.c:1476
+msgid "NoApplicableCode"
+msgstr ""
+
+#: service_internal.c:1075 service_internal.c:1585
+msgid "message"
+msgstr ""
+
+#: service_internal.c:1077 service_internal.c:1079 service_internal.c:1425
+#: service_internal.c:1478 service_internal.c:1546 service_internal.c:1590
+#: zoo_service_loader.c:1367 zoo_service_loader.c:1608
+#: zoo_service_loader.c:1652 zoo_service_loader.c:1830
+#: zoo_service_loader.c:1872
+msgid "text"
+msgstr ""
+
+#: service_internal.c:1079
+msgid "No more information available"
+msgstr ""
+
+#: service_internal.c:1084
+#, c-format
+msgid "error code not know : %i\n"
+msgstr ""
+
+#: service_internal.c:1093
+#, c-format
+msgid "printProcessResponse 1 161\n"
+msgstr ""
+
+#: service_internal.c:1096 zoo_service_loader.c:1157
+msgid "lineage"
+msgstr ""
+
+#: service_internal.c:1110
+#, c-format
+msgid "printProcessResponse 1 177\n"
+msgstr ""
+
+#: service_internal.c:1113
+msgid "OutputDefinitions"
+msgstr ""
+
+#: service_internal.c:1124
+#, c-format
+msgid "printProcessResponse 1 190\n"
+msgstr ""
+
+#: service_internal.c:1142
+#, c-format
+msgid "printProcessResponse 1 202\n"
+msgstr ""
+
+#: service_internal.c:1156 service_internal.c:1401 service_internal.c:1404
+#: service_internal.c:1406
+#, c-format
+msgid ""
+"Content-Type: text/xml; charset=%s\r\n"
+"Status: 200 OK\r\n"
+"\r\n"
+msgstr ""
+
+#: service_internal.c:1190
+msgid "MIMETYPE"
+msgstr ""
+
+#: service_internal.c:1191
+msgid "ENCODING"
+msgstr ""
+
+#: service_internal.c:1192
+msgid "SCHEMA"
+msgstr ""
+
+#: service_internal.c:1193
+msgid "UOM"
+msgstr ""
+
+#: service_internal.c:1245 service_internal.c:1342 zoo_service_loader.c:308
+#: zoo_service_loader.c:343 zoo_service_loader.c:427 zoo_service_loader.c:783
+#: zoo_service_loader.c:1212 zoo_service_loader.c:1318
+#: zoo_service_loader.c:1515 zoo_service_loader.c:1562
+#: zoo_service_loader.c:1757 zoo_service_loader.c:1801
+msgid "Identifier"
+msgstr ""
+
+#: service_internal.c:1268
+#, c-format
+msgid "FORMAT %s %s\n"
+msgstr ""
+
+#: service_internal.c:1270 service_internal.c:1319 service_internal.c:1521
+#: zoo_service_loader.c:828
+msgid "Reference"
+msgstr ""
+
+#: service_internal.c:1272 zoo_service_loader.c:1031
+msgid "Data"
+msgstr ""
+
+#: service_internal.c:1273
+msgid "LITERALOUTPUT"
+msgstr ""
+
+#: service_internal.c:1276
+msgid "COMPLEXOUTPUT"
+msgstr ""
+
+#: service_internal.c:1277 zoo_service_loader.c:1066
+msgid "ComplexData"
+msgstr ""
+
+#: service_internal.c:1286 service_internal.c:1328 zoo_service_loader.c:1156
+#: zoo_service_loader.c:1438
+msgid "storeExecuteResponse"
+msgstr ""
+
+#: service_internal.c:1292 service_internal.c:1294 service_internal.c:1553
+#: zoo_service_loader.c:839 zoo_service_loader.c:1072
+#: zoo_service_loader.c:1185 zoo_service_loader.c:1285
+msgid "mimeType"
+msgstr ""
+
+#: service_internal.c:1296
+msgid "base64"
+msgstr ""
+
+#: service_internal.c:1297
+msgid "image/"
+msgstr ""
+
+#: service_internal.c:1298
+msgid "application/"
+msgstr ""
+
+#: service_internal.c:1299 service_internal.c:1573
+msgid "size"
+msgstr ""
+
+#: service_internal.c:1302
+#, c-format
+msgid "%d"
+msgstr ""
+
+#: service_internal.c:1303
+msgid "z"
+msgstr ""
+
+#: service_internal.c:1308
+msgid "text/js"
+msgstr ""
+
+#: service_internal.c:1309
+msgid "application/js"
+msgstr ""
+
+#: service_internal.c:1326
+msgid "abstract"
+msgstr ""
+
+#: service_internal.c:1347 zoo_service_loader.c:799 zoo_service_loader.c:1231
+msgid "Title"
+msgstr ""
+
+#: service_internal.c:1348 zoo_service_loader.c:800 zoo_service_loader.c:1232
+msgid "Abstract"
+msgstr ""
+
+#: service_internal.c:1367 service_internal.c:1370 zoo_service_loader.c:929
+msgid "UTF-8"
+msgstr ""
+
+#: service_internal.c:1409 service_internal.c:1457
+msgid "ExceptionReport"
+msgstr ""
+
+#: service_internal.c:1413
+msgid "xsi:schemaLocation"
+msgstr ""
+
+#: service_internal.c:1413 service_internal.c:1465
+msgid ""
+"http://www.opengis.net/ows/1.1 http://schemas.opengis.net/ows/1.1.0/"
+"owsExceptionReport.xsd"
+msgstr ""
+
+#: service_internal.c:1415 service_internal.c:1468
+msgid "1.1.0"
+msgstr ""
+
+#: service_internal.c:1417 service_internal.c:1470
+msgid "Exception"
+msgstr ""
+
+#: service_internal.c:1421 service_internal.c:1423 service_internal.c:1474
+#: service_internal.c:1476
+msgid "exceptionCode"
+msgstr ""
+
+#: service_internal.c:1426 service_internal.c:1479
+msgid "ExceptionText"
+msgstr ""
+
+#: service_internal.c:1432 service_internal.c:1485
+msgid "No debug message available"
+msgstr ""
+
+#: service_internal.c:1498
+#, c-format
+msgid "printProcessResponse\n"
+msgstr ""
+
+#: service_internal.c:1500 zoo_service_loader.c:491 zoo_service_loader.c:1277
+msgid "RawDataOutput"
+msgstr ""
+
+#: service_internal.c:1507
+#, c-format
+msgid "REQUEST_OUTPUTS FINAL\n"
+msgstr ""
+
+#: service_internal.c:1513 zoo_service_loader.c:1683
+msgid "tmpPath"
+msgstr ""
+
+#: service_internal.c:1515
+#, c-format
+msgid "%s/%s_%i.%s"
+msgstr ""
+
+#: service_internal.c:1516
+msgid "w"
+msgstr ""
+
+#: service_internal.c:1520
+#, c-format
+msgid "%s/%s/%s_%i.%s"
+msgstr ""
+
+#: service_internal.c:1529
+msgid "serviceProvider"
+msgstr ""
+
+#: service_internal.c:1531
+#, c-format
+msgid "SERVICE : %s\n"
+msgstr ""
+
+#: service_internal.c:1546
+msgid "Unable to fetch any result"
+msgstr ""
+
+#: service_internal.c:1547 service_internal.c:1591 zoo_service_loader.c:120
+#: zoo_service_loader.c:147 zoo_service_loader.c:280 zoo_service_loader.c:367
+#: zoo_service_loader.c:420 zoo_service_loader.c:519 zoo_service_loader.c:530
+#: zoo_service_loader.c:547 zoo_service_loader.c:623 zoo_service_loader.c:637
+#: zoo_service_loader.c:664 zoo_service_loader.c:705 zoo_service_loader.c:788
+#: zoo_service_loader.c:806 zoo_service_loader.c:864 zoo_service_loader.c:905
+#: zoo_service_loader.c:952 zoo_service_loader.c:987 zoo_service_loader.c:1005
+#: zoo_service_loader.c:1146 zoo_service_loader.c:1218
+#: zoo_service_loader.c:1238 zoo_service_loader.c:1298
+#: zoo_service_loader.c:1324 zoo_service_loader.c:1700
+msgid "InternalError"
+msgstr ""
+
+#: service_internal.c:1555 service_internal.c:1557
+#, c-format
+msgid "SERVICE OUTPUTS\n"
+msgstr ""
+
+#: service_internal.c:1562
+#, c-format
+msgid ""
+"Content-Type: %s; charset=%s\r\n"
+"Status: 200 OK\r\n"
+"\r\n"
+msgstr ""
+
+#: service_internal.c:1567
+#, c-format
+msgid ""
+"Content-Type: %s; charset=UTF-8\r\n"
+"Status: 200 OK\r\n"
+"\r\n"
+msgstr ""
+
+#: service_internal.c:1570
+#, c-format
+msgid ""
+"Content-Type: text/plain; charset=utf-8\r\n"
+"Status: 200 OK\r\n"
+"\r\n"
+msgstr ""
+
+#: service_internal.c:1572
+msgid "image"
+msgstr ""
+
+#: service_internal.c:1587
+#, c-format
+msgid ""
+"Unable to run the Service. The message returned back by the Service was the "
+"following : %s"
+msgstr ""
+
+#: service_internal.c:1589
+#, c-format
+msgid ""
+"Unable to run the Service. No more information was returned back by the "
+"Service."
+msgstr ""
+
+#: service_internal.c:1616
+#, c-format
+msgid "BASE64 [%s] \n"
+msgstr ""
+
+#: service_internal.c:1627 zoo_service_loader.c:1363
+msgid "inputs"
+msgstr ""
+
+#: service_internal.c:1642
+msgid "NULL"
+msgstr ""
+
+#: service_internal.c:1660
+#, c-format
+msgid "addDefaultValues %s => %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:27 zoo_service_loader.c:28 zoo_service_loader.c:31
+#: zoo_service_loader.c:1470 zoo_service_loader.c:1723
+msgid "C"
+msgstr ""
+
+#: zoo_service_loader.c:98
+msgid "SIGSEGV"
+msgstr ""
+
+#: zoo_service_loader.c:101
+msgid "SIGTERM"
+msgstr ""
+
+#: zoo_service_loader.c:104
+msgid "SIGINT"
+msgstr ""
+
+#: zoo_service_loader.c:107
+msgid "SIGILL"
+msgstr ""
+
+#: zoo_service_loader.c:110
+msgid "SIGFPE"
+msgstr ""
+
+#: zoo_service_loader.c:113
+msgid "SIGABRT"
+msgstr ""
+
+#: zoo_service_loader.c:116
+msgid "UNKNOWN"
+msgstr ""
+
+#: zoo_service_loader.c:119
+#, c-format
+msgid "ZOO Kernel failed to process your request receiving signal %d = %s"
+msgstr ""
+
+#: zoo_service_loader.c:122
+#, c-format
+msgid "Not this time!\n"
+msgstr ""
+
+#: zoo_service_loader.c:147 zoo_service_loader.c:280 zoo_service_loader.c:367
+#: zoo_service_loader.c:420 zoo_service_loader.c:547 zoo_service_loader.c:623
+#: zoo_service_loader.c:637 zoo_service_loader.c:664 zoo_service_loader.c:705
+#: zoo_service_loader.c:788 zoo_service_loader.c:806 zoo_service_loader.c:864
+#: zoo_service_loader.c:905 zoo_service_loader.c:952 zoo_service_loader.c:987
+#: zoo_service_loader.c:1005 zoo_service_loader.c:1146
+#: zoo_service_loader.c:1218 zoo_service_loader.c:1238
+#: zoo_service_loader.c:1298 zoo_service_loader.c:1324
+msgid "Unable to allocate memory."
+msgstr ""
+
+#: zoo_service_loader.c:155 zoo_service_loader.c:158 zoo_service_loader.c:160
+#: zoo_service_loader.c:165 zoo_service_loader.c:243 zoo_service_loader.c:1471
+#: zoo_service_loader.c:1725
+msgid "metapath"
+msgstr ""
+
+#: zoo_service_loader.c:162
+#, c-format
+msgid "ADD METAPATH\n"
+msgstr ""
+
+#: zoo_service_loader.c:168
+#, c-format
+msgid "%s/%s/main.cfg"
+msgstr ""
+
+#: zoo_service_loader.c:171
+#, c-format
+msgid "***** BEGIN MAPS\n"
+msgstr ""
+
+#: zoo_service_loader.c:173
+#, c-format
+msgid "***** END MAPS\n"
+msgstr ""
+
+#: zoo_service_loader.c:179
+msgid "Request"
+msgstr ""
+
+#: zoo_service_loader.c:181
+msgid "Parameter <request> was not specified"
+msgstr ""
+
+#: zoo_service_loader.c:181 zoo_service_loader.c:201 zoo_service_loader.c:210
+#: zoo_service_loader.c:311 zoo_service_loader.c:606 zoo_service_loader.c:1368
+msgid "MissingParameterValue"
+msgstr ""
+
+#: zoo_service_loader.c:191 zoo_service_loader.c:402
+msgid ""
+"Unenderstood <request> value. Please check that it was set to "
+"GetCapabilities, DescribeProcess or Execute."
+msgstr ""
+
+#: zoo_service_loader.c:191 zoo_service_loader.c:257 zoo_service_loader.c:322
+#: zoo_service_loader.c:402 zoo_service_loader.c:442
+msgid "InvalidParameterValue"
+msgstr ""
+
+#: zoo_service_loader.c:199
+msgid "Service"
+msgstr ""
+
+#: zoo_service_loader.c:201
+msgid "Parameter <service> was not specified"
+msgstr ""
+
+#: zoo_service_loader.c:208
+msgid "Version"
+msgstr ""
+
+#: zoo_service_loader.c:210
+msgid "Parameter <version> was not specified"
+msgstr ""
+
+#: zoo_service_loader.c:218 zoo_service_loader.c:220
+msgid "serviceprovider"
+msgstr ""
+
+#: zoo_service_loader.c:257
+msgid "The specified path doesn't exist."
+msgstr ""
+
+#: zoo_service_loader.c:275
+msgid ".zcfg"
+msgstr ""
+
+#: zoo_service_loader.c:283 zoo_service_loader.c:370
+#, c-format
+msgid ""
+"#################\n"
+"%s\n"
+"#################\n"
+msgstr ""
+
+#: zoo_service_loader.c:311
+msgid "Mandatory <identifier> was not specified"
+msgstr ""
+
+#: zoo_service_loader.c:322
+msgid "The specified path path doesn't exist."
+msgstr ""
+
+#: zoo_service_loader.c:355
+#, c-format
+msgid "%s.zcfg"
+msgstr ""
+
+#: zoo_service_loader.c:358
+#, c-format
+msgid ""
+"\n"
+"#######%s\n"
+"########\n"
+msgstr ""
+
+#: zoo_service_loader.c:404
+#, c-format
+msgid "No request found %s"
+msgstr ""
+
+#: zoo_service_loader.c:422
+msgid "MetaPath"
+msgstr ""
+
+#: zoo_service_loader.c:429
+#, c-format
+msgid "%s/%s.zcfg"
+msgstr ""
+
+#: zoo_service_loader.c:432 zoo_service_loader.c:1481
+#: zoo_service_loader.c:1737
+#, c-format
+msgid "Trying to load %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:441
+#, c-format
+msgid ""
+"The value for <indetifier> seems to be wrong (%s). Please, ensure that the "
+"process exist using the GetCapabilities request."
+msgstr ""
+
+#: zoo_service_loader.c:470 zoo_service_loader.c:974
+msgid "ZooWPSClient"
+msgstr ""
+
+#: zoo_service_loader.c:476 zoo_service_loader.c:978
+#, c-format
+msgid "WARNING : hInternet handle failed to initialize"
+msgstr ""
+
+#: zoo_service_loader.c:481
+msgid "xrequest"
+msgstr ""
+
+#: zoo_service_loader.c:488 zoo_service_loader.c:494
+#, c-format
+msgid "OUTPUT Parsing ... \n"
+msgstr ""
+
+#: zoo_service_loader.c:490 zoo_service_loader.c:1135
+msgid "ResponseDocument"
+msgstr ""
+
+#: zoo_service_loader.c:498
+#, c-format
+msgid "OUTPUT Parsing start now ... \n"
+msgstr ""
+
+#: zoo_service_loader.c:514
+#, c-format
+msgid "OUTPUT [%s]\n"
+msgstr ""
+
+#: zoo_service_loader.c:516 zoo_service_loader.c:533 zoo_service_loader.c:620
+#: zoo_service_loader.c:639
+msgid ";"
+msgstr ""
+
+#: zoo_service_loader.c:519 zoo_service_loader.c:530
+msgid "Unable to allocate memory"
+msgstr ""
+
+#: zoo_service_loader.c:524 zoo_service_loader.c:526 zoo_service_loader.c:572
+#: zoo_service_loader.c:628 zoo_service_loader.c:632
+#, c-format
+msgid "***%s***\n"
+msgstr ""
+
+#: zoo_service_loader.c:540 zoo_service_loader.c:574 zoo_service_loader.c:647
+#: zoo_service_loader.c:670 zoo_service_loader.c:716
+msgid "@"
+msgstr ""
+
+#: zoo_service_loader.c:555 zoo_service_loader.c:652 zoo_service_loader.c:675
+msgid "="
+msgstr ""
+
+#: zoo_service_loader.c:561
+#, c-format
+msgid "OUTPUT DEF [%s]=[%s]\n"
+msgstr ""
+
+#: zoo_service_loader.c:599
+#, c-format
+msgid "DATA INPUTS [%s]\n"
+msgstr ""
+
+#: zoo_service_loader.c:606
+msgid "Parameter <DataInputs> was not specified"
+msgstr ""
+
+#: zoo_service_loader.c:650
+#, c-format
+msgid ""
+"***\n"
+"***%s***\n"
+msgstr ""
+
+#: zoo_service_loader.c:659
+#, c-format
+msgid ""
+"***\n"
+"*** %s = %s ***\n"
+msgstr ""
+
+#: zoo_service_loader.c:673
+#, c-format
+msgid ""
+"*** KVP NON URL-ENCODED \n"
+"***%s***\n"
+msgstr ""
+
+#: zoo_service_loader.c:677 zoo_service_loader.c:685
+#, c-format
+msgid ""
+"*** VALUE NON URL-ENCODED \n"
+"***%s***\n"
+msgstr ""
+
+#: zoo_service_loader.c:684
+#, c-format
+msgid ""
+"*** NAME NON URL-ENCODED \n"
+"***%s***\n"
+msgstr ""
+
+#: zoo_service_loader.c:687
+msgid "xlink:href"
+msgstr ""
+
+#: zoo_service_loader.c:691
+#, c-format
+msgid "REQUIRE TO DOWNLOAD A FILE FROM A SERVER : url(%s)\n"
+msgstr ""
+
+#: zoo_service_loader.c:700
+#, c-format
+msgid "(%s) content-length : %d,,res.nDataAlloc %d \n"
+msgstr ""
+
+#: zoo_service_loader.c:745
+#, c-format
+msgid "BEFORE %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:751
+#, c-format
+msgid "AFTER\n"
+msgstr ""
+
+#: zoo_service_loader.c:759
+msgid "/*/*/*[local-name()='Input']"
+msgstr ""
+
+#: zoo_service_loader.c:762 zoo_service_loader.c:1132
+#: zoo_service_loader.c:1274
+#, c-format
+msgid "*****%d*****\n"
+msgstr ""
+
+#: zoo_service_loader.c:772
+#, c-format
+msgid "= element 0 node \"%s\"\n"
+msgstr ""
+
+#: zoo_service_loader.c:808 zoo_service_loader.c:1240
+msgid "missingIndetifier"
+msgstr ""
+
+#: zoo_service_loader.c:835
+#, c-format
+msgid "REFERENCE\n"
+msgstr ""
+
+#: zoo_service_loader.c:841 zoo_service_loader.c:1074
+#: zoo_service_loader.c:1187 zoo_service_loader.c:1287
+msgid "schema"
+msgstr ""
+
+#: zoo_service_loader.c:842 zoo_service_loader.c:855
+msgid "method"
+msgstr ""
+
+#: zoo_service_loader.c:847
+#, c-format
+msgid "*** %s ***"
+msgstr ""
+
+#: zoo_service_loader.c:857
+msgid "POST"
+msgstr ""
+
+#: zoo_service_loader.c:875 zoo_service_loader.c:909 zoo_service_loader.c:1061
+#: zoo_service_loader.c:1087 zoo_service_loader.c:1173
+#: zoo_service_loader.c:1202 zoo_service_loader.c:1308
+#: zoo_service_loader.c:1507
+#, c-format
+msgid "%s\n"
+msgstr ""
+
+#: zoo_service_loader.c:880
+#, c-format
+msgid "Parse Header and Body from Reference \n"
+msgstr ""
+
+#: zoo_service_loader.c:885
+msgid "Header"
+msgstr ""
+
+#: zoo_service_loader.c:889
+msgid "key"
+msgstr ""
+
+#: zoo_service_loader.c:897
+#, c-format
+msgid "%s = %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:907
+#, c-format
+msgid "%s: %s"
+msgstr ""
+
+#: zoo_service_loader.c:918
+#, c-format
+msgid "Try to fetch the body part of the request ...\n"
+msgstr ""
+
+#: zoo_service_loader.c:920
+msgid "Body"
+msgstr ""
+
+#: zoo_service_loader.c:922
+#, c-format
+msgid "Body part found !!!\n"
+msgstr ""
+
+#: zoo_service_loader.c:935
+#, c-format
+msgid "Body part found !!! %s %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:945 zoo_service_loader.c:997
+#, c-format
+msgid "%s %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:962 zoo_service_loader.c:1015
+#, c-format
+msgid "DL CONTENT : (%s)\n"
+msgstr ""
+
+#: zoo_service_loader.c:967
+msgid "BodyReference"
+msgstr ""
+
+#: zoo_service_loader.c:1023
+#, c-format
+msgid "Header and Body was parsed from Reference \n"
+msgstr ""
+
+#: zoo_service_loader.c:1027
+#, c-format
+msgid "= element 2 node \"%s\" = (%s)\n"
+msgstr ""
+
+#: zoo_service_loader.c:1033
+#, c-format
+msgid "DATA\n"
+msgstr ""
+
+#: zoo_service_loader.c:1047
+msgid "dataType"
+msgstr ""
+
+#: zoo_service_loader.c:1048 zoo_service_loader.c:1188
+#: zoo_service_loader.c:1288
+msgid "uom"
+msgstr ""
+
+#: zoo_service_loader.c:1051
+#, c-format
+msgid "*** LiteralData %s ***"
+msgstr ""
+
+#: zoo_service_loader.c:1077
+#, c-format
+msgid "*** ComplexData %s ***"
+msgstr ""
+
+#: zoo_service_loader.c:1099
+#, c-format
+msgid "cur2 next \n"
+msgstr ""
+
+#: zoo_service_loader.c:1105
+#, c-format
+msgid "ADD MAPS TO REQUEST MAPS !\n"
+msgstr ""
+
+#: zoo_service_loader.c:1111
+#, c-format
+msgid "******TMPMAPS*****\n"
+msgstr ""
+
+#: zoo_service_loader.c:1113
+#, c-format
+msgid "******REQUESTMAPS*****\n"
+msgstr ""
+
+#: zoo_service_loader.c:1125
+#, c-format
+msgid "Search for response document node\n"
+msgstr ""
+
+#: zoo_service_loader.c:1129
+msgid "/*/*/*[local-name()='ResponseDocument']"
+msgstr ""
+
+#: zoo_service_loader.c:1148 zoo_service_loader.c:1300
+msgid "unknownIdentifier"
+msgstr ""
+
+#: zoo_service_loader.c:1162 zoo_service_loader.c:1192
+#: zoo_service_loader.c:1291
+#, c-format
+msgid "*** %s ***\t"
+msgstr ""
+
+#: zoo_service_loader.c:1271
+msgid "/*/*/*[local-name()='RawDataOutput']"
+msgstr ""
+
+#: zoo_service_loader.c:1353
+#, c-format
+msgid ""
+"\n"
+"%i\n"
+msgstr ""
+
+#: zoo_service_loader.c:1366
+#, c-format
+msgid ""
+"The <%s> argument was not specified in DataInputs but defined as requested "
+"in ZOO ServicesProvider configuration file, please correct your query or the "
+"ZOO Configuration file."
+msgstr ""
+
+#: zoo_service_loader.c:1383
+msgid "outputs"
+msgstr ""
+
+#: zoo_service_loader.c:1386
+#, c-format
+msgid "REQUEST_INPUTS\n"
+msgstr ""
+
+#: zoo_service_loader.c:1388
+#, c-format
+msgid "REQUEST_OUTPUTS\n"
+msgstr ""
+
+#: zoo_service_loader.c:1392
+msgid "env"
+msgstr ""
+
+#: zoo_service_loader.c:1400 zoo_service_loader.c:1423
+#, c-format
+msgid "[ZOO: setenv (%s=%s)]\n"
+msgstr ""
+
+#: zoo_service_loader.c:1404
+#, c-format
+msgid "[ZOO: Env var finish with \r]\n"
+msgstr ""
+
+#: zoo_service_loader.c:1410
+#, c-format
+msgid "setting variable... %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:1414
+msgid "OK"
+msgstr ""
+
+#: zoo_service_loader.c:1414
+msgid "FAILED"
+msgstr ""
+
+#: zoo_service_loader.c:1448
+msgid "0"
+msgstr ""
+
+#: zoo_service_loader.c:1465 zoo_service_loader.c:1504
+#: zoo_service_loader.c:1718 zoo_service_loader.c:1752
+msgid "serviceType"
+msgstr ""
+
+#: zoo_service_loader.c:1467
+#, c-format
+msgid "LOAD A %s SERVICE PROVIDER IN NORMAL MODE \n"
+msgstr ""
+
+#: zoo_service_loader.c:1492
+#, c-format
+msgid "%s loaded (%d) \n"
+msgstr ""
+
+#: zoo_service_loader.c:1501 zoo_service_loader.c:1799
+#, c-format
+msgid "Library loaded %s \n"
+msgstr ""
+
+#: zoo_service_loader.c:1502
+#, c-format
+msgid "Service Shared Object = %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:1510 zoo_service_loader.c:1756
+msgid "C-FORTRAN"
+msgstr ""
+
+#: zoo_service_loader.c:1517 zoo_service_loader.c:1763
+#, c-format
+msgid "%s_"
+msgstr ""
+
+#: zoo_service_loader.c:1519 zoo_service_loader.c:1564
+#: zoo_service_loader.c:1759 zoo_service_loader.c:1765
+#: zoo_service_loader.c:1803
+#, c-format
+msgid "Try to load function %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:1534 zoo_service_loader.c:1583
+#: zoo_service_loader.c:1816
+#, c-format
+msgid "Function loaded %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:1552
+#, c-format
+msgid "Function run successfully \n"
+msgstr ""
+
+#: zoo_service_loader.c:1559 zoo_service_loader.c:1571
+#, c-format
+msgid "Function %s failed to load because of %d\n"
+msgstr ""
+
+#: zoo_service_loader.c:1587
+#, c-format
+msgid "Now run the function \n"
+msgstr ""
+
+#: zoo_service_loader.c:1592 zoo_service_loader.c:1889
+#, c-format
+msgid "Function loaded and returned %d\n"
+msgstr ""
+
+#: zoo_service_loader.c:1607 zoo_service_loader.c:1829
+#, c-format
+msgid "C Library can't be loaded %s \n"
+msgstr ""
+
+#: zoo_service_loader.c:1615 zoo_service_loader.c:1839
+msgid "PYTHON"
+msgstr ""
+
+#: zoo_service_loader.c:1622 zoo_service_loader.c:1844
+msgid "JAVA"
+msgstr ""
+
+#: zoo_service_loader.c:1629 zoo_service_loader.c:1851
+msgid "PHP"
+msgstr ""
+
+#: zoo_service_loader.c:1637 zoo_service_loader.c:1858
+msgid "PERL"
+msgstr ""
+
+#: zoo_service_loader.c:1644 zoo_service_loader.c:1864
+msgid "JS"
+msgstr ""
+
+#: zoo_service_loader.c:1651 zoo_service_loader.c:1871
+#, c-format
+msgid ""
+"Programming Language (%s) set in ZCFG file is not currently supported by ZOO "
+"Kernel.\n"
+msgstr ""
+
+#: zoo_service_loader.c:1661
+#, c-format
+msgid ""
+"\n"
+"PID : %d\n"
+msgstr ""
+
+#: zoo_service_loader.c:1675
+#, c-format
+msgid "father pid continue (origin %d) %d ...\n"
+msgstr ""
+
+#: zoo_service_loader.c:1686
+#, c-format
+msgid "%s/%s_%d.xml"
+msgstr ""
+
+#: zoo_service_loader.c:1688
+#, c-format
+msgid "%s/%s_%d_error.log"
+msgstr ""
+
+#: zoo_service_loader.c:1690
+#, c-format
+msgid "RUN IN BACKGROUND MODE \n"
+msgstr ""
+
+#: zoo_service_loader.c:1691
+#, c-format
+msgid "son pid continue (origin %d) %d ...\n"
+msgstr ""
+
+#: zoo_service_loader.c:1692
+#, c-format
+msgid ""
+"\n"
+"FILE TO STORE DATA %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:1694 zoo_service_loader.c:1696
+msgid "w+"
+msgstr ""
+
+#: zoo_service_loader.c:1700
+msgid "Unable to run the child process properly"
+msgstr ""
+
+#: zoo_service_loader.c:1720
+#, c-format
+msgid "LOAD A %s SERVICE PROVIDER IN BACKGROUND MODE \n"
+msgstr ""
+
+#: zoo_service_loader.c:1754
+#, c-format
+msgid "r_inputs->value = %s\n"
+msgstr ""
+
+#: zoo_service_loader.c:1918
+#, c-format
+msgid "Processed response \n"
+msgstr ""
Index: /tags/rel-1.2.0-rc1/zoo-kernel/main.cfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/main.cfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/main.cfg	(revision 221)
@@ -0,0 +1,29 @@
+[main]
+encoding = utf-8
+version = 1.0.0
+serverAddress = http://www.zoo-project.org/zoo/
+lang = fr-FR,en-CA
+tmpPath=/YourFullTmpPathHere/
+tmpUrl = ../TmpPathRelativeToServerAdress/
+
+[identification]
+title = The Zoo WPS Development Server
+abstract = Development version of ZooWPS. See http://www.zoo-project.org
+fees = None
+accessConstraints = none
+keywords = WPS,GIS,buffer
+
+[provider]
+providerName=ZOO Project
+providerSite=http://www.zoo-project.org
+individualName=Gerald FENOY
+positionName=Developer
+role=Dev
+adressDeliveryPoint=1280, avenue des Platanes
+addressCity=Lattes
+addressAdministrativeArea=False
+addressPostalCode=34970
+addressCountry=fr
+addressElectronicMailAddress=gerald@geolabs.fr
+phoneVoice=False
+phoneFacsimile=False
Index: /tags/rel-1.2.0-rc1/zoo-kernel/main_conf_read.l
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/main_conf_read.l	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/main_conf_read.l	(revision 221)
@@ -0,0 +1,119 @@
+/* Line number from bison */
+%option yylineno
+
+%{
+//======================================================
+/**
+Zoo main configuration file parser
+**/
+//======================================================
+
+
+#include <string.h>
+#include "main_conf_read.tab.h"
+static int affichetrace = 0 ;
+static int attentionImpossibleDeTrouverXMLDeclapres = 0 ;
+static int attentionImpossibleDeTrouverPIapres = 0 ;
+
+%}
+
+
+S		[ \t\r\n]+
+
+CharRef		"&#"[0-9]+";"|"&#x"[0-9a-fA-F]+";"
+
+egalevolue		{S}?"="{S}?
+
+Name		([_:]|[\x41-\x5A]|[\x61-\x7A]|[\xC0-\xD6]|[\xD8-\xF6]|[\xF8-\xFF])(([\x41-\x5A]|[\x61-\x7A]|[\xC0-\xD6]|[\xD8-\xF6]|[\xF8-\xFF])|[0-9.\-_:])*
+
+chardata	[^<]*
+
+attname	[a-zA-Z0-9_\-:]+
+
+attvalue1	[,@a-zA-Z0-9_\-.:" "\"\'/\\\(\)\+\x41-\xff]+
+
+attvalue		\"[^"]*\"|\'[^']*\'
+
+virgule	[,]+
+
+whitespace                      [ ]{0,}
+whitesp                      [ ]
+newline                 [\r\n]|[\n]
+newlines                 [\r\n]{1,}|[\n]{1,}
+
+
+
+%x DANSBALISE HORSBALISE PAIRSTART
+
+
+
+
+%%
+
+"\n" {if (affichetrace==1) printf ("\n\nNEWLINE\n") ; return NEWLINE;}
+
+{newline}+{whitesp}*			{if (affichetrace==1) printf ("\n\nNEWLINE 1\n") ; return NEWLINE;}
+
+<INITIAL,HORSBALISE>"["{attname}"]"             {if (affichetrace==1) printf ("\n\nANID:%s\n",yytext); crlval.chaine=yytext;crlval.chaine[strlen(crlval.chaine)-1]=0;crlval.chaine+=1;return ANID; }
+
+<INITIAL,HORSBALISE>{attname}             {if (affichetrace==1) printf ("\n\nATT_NAME:%s\n",yytext); crlval.chaine=yytext; return SPAIR; }
+
+<PAIRSTART,HORSBALISE>{attvalue1}             {if (affichetrace==1) printf ("\n\nATT_VALUE:%s\n",yytext);crlval.chaine=yytext;BEGIN(INITIAL);return EPAIR;}
+
+<PAIRSTART,INITIAL,HORSBALISE>{whitesp}*"="{whitesp}*             {BEGIN(PAIRSTART);}
+
+<PAIRSTART,INITIAL,HORSBALISE,DANSBALISE>{newline}+{whitesp}*			{if (affichetrace==1) printf ("\n\nNEWLINE 2\n") ; BEGIN(INITIAL); return NEWLINE;}
+
+<INITIAL>"<?"[Xx][Mm][Ll]  { if (attentionImpossibleDeTrouverXMLDeclapres == 1 || attentionImpossibleDeTrouverPIapres == 1) { printf("\nerror : LINE %d : comment ot PI before xml declaration\n",yylineno); exit (10) ; } ; if (affichetrace==1) printf ("\n\nSTARTXMLDECL:%s\n",yytext) ;return STARTXMLDECL;}
+
+<INITIAL>"version"{egalevolue}\"1.0\"|"version"{egalevolue}\'1.0\'  {if (affichetrace==1) printf ("\n\nVERSIONDECL:%s\n",yytext) ;return VERSIONDECL;}
+<INITIAL>"version"{egalevolue}\"[^"]*\"|"version"{egalevolue}\'[^']*\'  {/* erreur de version encoding */ 	printf("\nerror : LINE %d : XML Version not supported : %s\n",yylineno,yytext); exit (9) ; }
+
+
+<INITIAL>"encoding"{egalevolue}\"[Ii][Ss][Oo]"-8859-1"\"|"encoding"{egalevolue}\'[Ii][Ss][Oo]"-8859-1"\'  {if (affichetrace==1) printf ("\n\nENCODINGDECL:%s\n",yytext) ; return ENCODINGDECL;}
+<INITIAL>"encoding"{egalevolue}\"[^"]*\"|"encoding"{egalevolue}\'[^']*\'  {/* erreur de version encoding */ 	printf("\nerror : LINE %d : encoding version not supported : %s\n",yylineno,yytext); exit (8) ; }
+
+
+<INITIAL>"standalone"{egalevolue}\"yes\"|"standalone"{egalevolue}\'yes\'|"standalone"{egalevolue}\"no\"|"standalone"{egalevolue}\'no\'  {if (affichetrace==1) printf ("\n\nSDDECL:%s\n",yytext) ; return SDDECL;}
+
+<INITIAL>"standalone"{egalevolue}\"[^"]*\"|"standalone"{egalevolue}\'[^']*\'|"standalone"{egalevolue}\"[^"]*\"|"standalone"{egalevolue}\'[^']*\'  {/* erreur de version encoding */ 	printf("\nerror : LINE %d : standalone version not supported : %s\n",yylineno,yytext); exit (7) ; }
+
+
+<INITIAL>"?>"  {if (affichetrace==1) printf ("\n\nENDXMLDECL:%s\n",yytext) ; BEGIN(HORSBALISE);return ENDXMLDECL;}
+
+
+<DANSBALISE,INITIAL,HORSBALISE>{S}   {if (affichetrace==1) printf ("\n\nS:'%s'\n",yytext) ; }
+
+
+<HORSBALISE>"<?"[Xx][Mm][Ll]{S}({S}|{chardata})*"?>"|"<?"[Xx][Mm][Ll]"?>"	{if (affichetrace==1) printf ("\n\nPIERROR:%s\n",yytext) ; return PIERROR;}
+<INITIAL,HORSBALISE>"<?"([^xX]|([xX][^mM])|([xX][mM][^lL]))({S}|([^?]|("?"[^>])))*"?>"		{attentionImpossibleDeTrouverPIapres=1 ;  if (affichetrace==1) printf ("\n\nPI:%s\n",yytext) ; return PI;}
+
+
+<INITIAL,HORSBALISE>{newline}*"<"		    {if (affichetrace==1) printf ("\n\nINFCAR:%s\n",yytext) ; BEGIN(DANSBALISE);return INFCAR;}
+
+
+<DANSBALISE>">"			{if (affichetrace==1) printf ("\n\nSUPCAR:%s\n",yytext) ; BEGIN(HORSBALISE);return SUPCAR;}
+
+
+<DANSBALISE>"/"		{if (affichetrace==1) printf ("\n\nSLASH:%s\n",yytext) ; return SLASH;}
+
+
+<DANSBALISE>{egalevolue}			{if (affichetrace==1) printf ("\n\nEq:'%s'\n",yytext) ; return Eq;}
+
+
+<DANSBALISE>{Name}			{if (affichetrace==1) printf ("\n\nID:%s\n",yytext) ; crlval.s=yytext;return ID;}
+
+
+<DANSBALISE>{attvalue}		{if (affichetrace==1) printf ("\n\nATTVALUE:%s\n",yytext) ; return ATTVALUE;}
+
+
+<INITIAL,HORSBALISE>"<!--"([^-]|"-"[^-])*"-->"		{attentionImpossibleDeTrouverXMLDeclapres=1; }
+
+
+<INITIAL,DANSBALISE,HORSBALISE>.|\n	{if (affichetrace==1)printf("error : LINE %d : character not supported '%s'\n",yylineno,yytext); }
+
+%%
+
+
+int crwrap()
+{return 1;}
Index: /tags/rel-1.2.0-rc1/zoo-kernel/main_conf_read.y
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/main_conf_read.y	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/main_conf_read.y	(revision 221)
@@ -0,0 +1,347 @@
+%{
+//======================================================
+/**
+   Zoo main configuration file parser
+**/
+//======================================================
+
+#include <string>
+#include <stdio.h>
+#include <ctype.h>
+#include <service.h>
+#include <vector>
+
+static int defaultsc=0;
+static maps* my_maps=NULL;
+static maps* current_maps=NULL;
+static map* previous_content=NULL;
+static map* current_content=NULL;
+static elements* current_element=NULL;
+static map* scontent=NULL;
+static char* curr_key;
+static int debug=0;
+static int previous_data=0;
+static int current_data=0;
+using namespace std;
+
+extern void crerror(const char *s);
+
+void usage(void) ;
+
+extern int crdebug;
+
+extern char crtext[];
+
+extern int crlineno;
+
+extern FILE* crin;
+
+extern int crlex(void);
+extern int crlex_destroy(void);
+
+%}
+
+
+
+//======================================================
+/* le type des lval des jetons et des elements non terminaux bison */
+//======================================================
+%union 
+{
+   char * s;
+}
+%union { char* chaine; char* key;char* val;}
+//======================================================
+
+// jetons //
+//======================================================
+/* les jetons que l on retrouve dans FLEX */
+//======================================================
+/* texte on a besoin de récupérer une valeur char* pour la comparer */
+%token <s> ID
+%token <s> CHAINE
+/* STARTXMLDECL et ENDXMLDECL qui sont <?xml et ?>*/
+%token STARTXMLDECL ENDXMLDECL
+//======================================================
+/* version="xxx" et encoding="xxx" */
+%token VERSIONDECL ENCODINGDECL SDDECL
+//======================================================
+/* < et > */
+%token INFCAR SUPCAR 
+//======================================================
+/* / = a1  texte "texte" */
+%token SLASH Eq CHARDATA ATTVALUE PAIR SPAIR EPAIR EPAIRS ANID
+%type <chaine> PAIR
+%type <chaine> EPAIRS
+%type <chaine> EPAIR
+%type <chaine> SPAIR
+
+//======================================================
+/* <!-- xxx -> <? xxx yyy ?> */
+%token PI PIERROR /** COMMENT **/
+//======================================================
+/* <!-- xxx -> <? xxx yyy ?> */
+%token ERREURGENERALE CDATA WHITESPACE NEWLINE
+//======================================================
+// non terminaux typés
+//======================================================
+/* elements non terminaux de type char *     */
+/* uniquement ceux qui devrons etre comparés */
+//======================================================
+%type <s> STag
+%type <s> ETag
+%type <s> ANID
+//======================================================
+// %start
+//======================================================
+
+%%
+// document <//===
+//======================================================
+// regle 1
+// on est a la racine du fichier xml
+//======================================================
+document
+ : miscetoile element miscetoile {}
+ | contentetoile processid contentetoile document {}
+ ;
+
+miscetoile
+ : miscetoile PIERROR {crerror("processing instruction begining with <?xml ?> impossible\n");}
+ | miscetoile PI {}
+ | {}
+ ;
+// element
+//======================================================
+// regle 39
+// OUVRANTE CONTENU FERMANTE obligatoirement
+// ou neutre
+// on ne peut pas avoir Epsilon
+// un fichier xml ne peut pas etre vide ou seulement avec un prolog
+//======================================================
+element
+ : STag contentetoile ETag	
+{
+  /* les non terminaux rendent les valeurs de leur identifiants de balise */
+  /* en char*, donc on peut comparer ces valeurs avec la fonction C++ strcmp(const char*;const char*) */
+  /* de string */
+  if (strcmp($1,$3) != 0)
+    {
+      crerror("Opening and ending tag mismatch");
+      printf("\n  ::details : tag '%s' et '%s' \n",$1,$3);
+      return 1;
+      // on retourne different de 0
+      // sinon yyparse rendra 0
+      // et dans le main on croira a le fichier xml est valide !
+    }
+}
+// pour neutre
+// on a rien a faire, meme pas renvoyer l identificateur de balise
+// vu qu'il n y a pas de comparaison d'identificateurs avec un balise jumelle .
+ | EmptyElemTag          {}
+ ;
+//======================================================
+// STag
+//======================================================
+// regle 40
+// BALISE OUVRANTE
+// on est obligé de faire appel a infcar et supcar
+// pour acceder aux start conditions DANSBALISE et INITIAL
+//======================================================
+STag
+ : INFCAR ID Attributeetoile SUPCAR
+{	
+
+#ifdef DEBUG
+	printf("* Identifiant : %s\n",$2);
+#endif
+	
+	$$ = $2 ;
+}
+ ;
+//======================================================
+// Attributeetoile
+//======================================================
+// regle 41
+// une liste qui peut etre vide d'attributs
+// utiliser la récursivité a gauche
+//======================================================
+Attributeetoile
+ : Attributeetoile attribute  {}
+ | 	                          {/* Epsilon */}
+ ;
+//======================================================
+// attribute
+//======================================================
+// regle 41
+// un attribut est compose d'un identifiant
+// d'un "="
+// et d'une définition de chaine de caractere
+// ( "xxx" ou 'xxx' )
+//======================================================
+attribute
+ : ID Eq ATTVALUE		
+{
+	// on verifie que les attributst ne sont pas en double
+	// sinon on ajoute au vector
+}
+ ;
+//======================================================
+// EmptyElemTag
+//======================================================
+// regle 44
+// ICI ON DEFINIT NEUTRE
+// on ne renvoie pas de char*
+// parce qu'il n'y a pas de comparaisons a faire
+// avec un identifiant d'une balise jumelle
+//======================================================
+EmptyElemTag
+ : INFCAR ID Attributeetoile SLASH SUPCAR	{}
+ ;
+//======================================================
+// ETag
+//======================================================
+// regle 42
+// BALISE FERMANTE
+// les separateurs après ID sont filtrés
+//======================================================
+ETag
+ : INFCAR SLASH ID SUPCAR
+{
+  /* on renvoie l'identifiant de la balise pour pouvoir comparer les 2 */
+  /* /!\ une balise fermante n'a pas d'attributs (c.f. : W3C) */
+  $$ = $3;
+}
+ ;
+//======================================================
+// contentetoile
+//======================================================
+// regle 43
+// ENTRE 2 BALISES
+// entre 2 balises, on peut avoir :
+// --- OUVRANTE CONTENU FERMANTE (recursivement !)
+// --- DU TEXTE quelconque
+// --- COMMENTS 
+// --- DES PROCESSES INSTRUCTIONS
+// --- /!\ il peut y avoir une processing instruction invalide ! <?xml
+// --- EPSILON
+// ### et/ou tout ca a la suite en nombre indeterminé
+// ### donc c'est un operateur etoile (*)
+//======================================================
+contentetoile
+: contentetoile element	          {}
+ | contentetoile PIERROR	          {crerror("processing instruction <?xml ?> impossible\n");}
+ | contentetoile PI	                  {}
+///// on filtre les commentaires | contentetoile comment              {} 
+ | contentetoile NEWLINE {/*printf("NEWLINE FOUND !!");*/}
+ | contentetoile pair {}
+ | contentetoile processid {}
+ | contentetoile texteinterbalise	  {}
+ | contentetoile CDATA {}  
+ | {/* Epsilon */}
+ ;
+//======================================================
+// texteinterbalise
+//======================================================
+// regle 14
+// DU TEXTE quelconque
+// c'est du CHARDATA
+// il y a eut un probleme avec ID,
+// on a mis des starts conditions,
+// maintenant on croise les ID dans les dbalises
+// et des CHARDATA hors des balises
+//======================================================
+texteinterbalise
+ : CHARDATA		{}
+ ;
+//======================================================
+
+pair: PAIR {curr_key=strdup($1);/*printf("START 0 PAIR FOUND !! \n [%s]\n",$1);*/}
+| EPAIR {
+  if(current_content==NULL) 
+    current_content=createMap(curr_key,$1);
+  else{ 
+    addToMap(current_content,curr_key,$1);
+  }
+  if(debug){ 
+    printf("EPAIR FOUND !! \n"); 
+    printf("[%s=>%s]\n",curr_key,$1);
+  }
+  free(curr_key);
+  }
+| SPAIR  {curr_key=strdup($1);if(debug) printf("SPAIR FOUND !!\n"); }
+ ;
+
+
+processid
+: ANID  {
+   if(current_maps->name!=NULL){
+     addMapToMap(&current_maps->content,current_content);
+     freeMap(&current_content);
+     free(current_content);
+     current_maps->next=NULL;
+     current_maps->next=(maps*)malloc(MAPS_SIZE);
+     current_maps->next->name=strdup($1);
+     current_maps->next->content=NULL;
+     current_maps->next->next=NULL;
+     current_maps=current_maps->next;
+     current_content=current_maps->content;
+   }
+   else{
+     current_maps->name=(char*)malloc((strlen($1)+1)*sizeof(char));
+     snprintf(current_maps->name,(strlen($1)+1),"%s",$1);
+     current_maps->content=NULL;
+     current_maps->next=NULL;
+     current_content=NULL;
+   }
+ }
+ ;
+
+%%
+
+// crerror
+//======================================================
+/* fonction qui affiche l erreur si il y en a une */
+//======================================================
+void crerror(const char *s)
+{
+  if(debug)
+    printf("\nligne %d : %s\n",crlineno,s);
+}
+
+// main
+//======================================================
+/* fonction principale : entrée dans le programme */
+//======================================================
+int conf_read(const char* file,maps* my_map){
+  
+  crin = fopen(file,"r");
+  if (crin==NULL){
+    printf("error : le fichier specifie n'existe pas ou n'est pas accessible en lecture\n") ;
+    return 2 ;
+  }
+
+  my_maps=my_map;
+  my_maps->name=NULL;
+  current_maps=my_maps;
+  
+  int resultatYYParse = crparse() ;
+  if(current_content!=NULL){
+    addMapToMap(&current_maps->content,current_content);
+    current_maps->next=NULL;
+    freeMap(&current_content);
+    free(current_content);
+  }
+
+  fclose(crin);
+#ifndef WIN32
+  crlex_destroy();
+#endif
+
+  return resultatYYParse;
+}
+
+
+//======================================================
+// FIN //
+//======================================================
Index: /tags/rel-1.2.0-rc1/zoo-kernel/makefile.vc
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/makefile.vc	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/makefile.vc	(revision 221)
@@ -0,0 +1,61 @@
+# WIN32 Makefile tested using VC-9.0
+# Don't forget to set your PATH using the following command :
+# c:\Progam Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat
+# set PATH=%PATH%;$(TOOLS)
+# using value for TOOLS relative to your local installation
+#
+
+!INCLUDE nmake.opt
+
+all:  zoo_loader.cgi
+
+main_conf_read.tab.c: main_conf_read.y service.h
+	$(TOOLS)\bison -p cr -d main_conf_read.y
+
+main_conf_read.tab.obj: main_conf_read.tab.c service.h
+	$(CPP) /EHsc $(CFLAGS) main_conf_read.tab.c /c
+
+lex.cr.c: main_conf_read.y main_conf_read.l main_conf_read.tab.c service.h
+	$(TOOLS)\flex -Pcr main_conf_read.l
+
+lex.cr.obj: lex.cr.c service.h
+	$(CPP) $(CFLAGS) /c lex.cr.c
+
+service_conf.tab.c: service_conf.y service.h
+	$(TOOLS)\bison -p sr -d service_conf.y
+
+service_conf.tab.obj: service_conf.tab.c service.h
+	$(CPP) $(CFLAGS) service_conf.tab.c /c
+
+lex.sr.c: service_conf.y service_conf.l service_conf.tab.c service.h
+	$(TOOLS)\flex -Psr service_conf.l
+
+lex.sr.obj: lex.sr.c service.h
+	$(CPP) $(CFLAGS) /c lex.sr.c
+
+service_internal.obj: service_internal.c
+	$(CPP) $(CFLAGS) /c service_internal.c
+
+service_internal_python.obj: service_internal_python.c service.h
+	$(CPP) /c $(CFLAGS) service_internal_python.c
+
+service_internal_java.obj: service_internal_java.c service.h
+	$(CPP) /c $(CFLAGS) $(CJFLAGS) service_internal_java.c
+
+service_loader.obj: service_loader.c service.h
+	$(CPP) /c $(CFLAGS)  service_loader.c
+
+zoo_service_loader.obj: zoo_service_loader.c service.h
+	$(CPP) /c $(CFLAGS)  zoo_service_loader.c
+
+zoo_loader.obj: zoo_loader.c service.h
+	$(CPP) /EHsc /c $(CFLAGS) zoo_loader.c
+
+dirent.obj:
+	$(CPP) /EHsc /c $(CFLAGS) ..\thirds\dirent-win32\dirent.c
+
+zoo_loader.cgi: zoo_loader.obj zoo_service_loader.obj service_internal.obj service_internal_python.obj ulinet.obj lex.cr.obj lex.sr.obj service_conf.tab.obj main_conf_read.tab.obj dirent.obj
+	link zoo_loader.obj dirent.obj service_internal.obj service_internal_python.obj ulinet.obj main_conf_read.tab.obj lex.cr.obj service_conf.tab.obj lex.sr.obj  zoo_service_loader.obj /out:zoo_loader.cgi $(LDFLAGS)
+
+clean:
+	erase -f *.cgi *.obj *.tab.c* *.tab.h *.sr.c* lex.* *.lreg *.sibling
Index: /tags/rel-1.2.0-rc1/zoo-kernel/nmake.opt
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/nmake.opt	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/nmake.opt	(revision 221)
@@ -0,0 +1,14 @@
+LIBINTL_CPATH=..\..\..\
+PYTHON_CPATH=..\..\..\
+TPATH=..\..\..\tools
+GEODIR=c:/OSGeo4W
+DESTDIR=c:/OSGeo4W
+TOOLS=$(TPATH)\bin
+
+CC=cl $(CFLAGS)
+CPP=cl /TP $(CFLAGS)
+
+CFLAGS=-DUSE_PYTHON /EHa /nologo /MT /W3 /EHsc /O2 /D_CRT_SECURE_NO_WARNINGS /DWIN32 $(CJFLAGS) -I./ -I..\thirds\dirent-win32 -I$(PYTHON_CPATH)\include -I$(GEODIR)/include -ILIBINTL_CPATH\include -I$(TPATH)\include -DLINUX_FREE_ISSUE #-DDEBUG #-DDEBUG_SERVICE_CONF
+
+LDFLAGS=$(GEODIR)/lib/libfcgi.lib $(GEODIR)/lib/libcurl_im
+p.lib  $(GEODIR)/apps/Python25/libs/python25.lib $(GEODIR)/lib/libxml2.lib ../thirds/cgic206/libcgic.lib $(GEODIR)/lib/gdal_i.lib $(TOOLS)\..\lib\libeay32.dll.a $(TOOLS)\..\lib\libcrypto.a $(TOOLS)\..\lib\libssl32.dll.a $(TOOLS)\..\lib\libintl.lib /machine:i386 
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service.h	(revision 221)
@@ -0,0 +1,758 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef ZOO_SERVICE_H
+#define ZOO_SERVICE_H 1
+
+#pragma once
+
+#ifdef WIN32
+#define strncasecmp strnicmp
+#define strcasecmp stricmp
+#define snprintf sprintf_s
+#endif 
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#define bool int
+#define true 1
+#define false -1
+
+#define SERVICE_ACCEPTED 0
+#define SERVICE_STARTED 1
+#define SERVICE_PAUSED 2
+#define SERVICE_SUCCEEDED 3
+#define SERVICE_FAILED 4
+
+#define ELEMENTS_SIZE (sizeof(char*)+(((2*sizeof(char*))+sizeof(maps*))*2)+sizeof(char*)+(((2*sizeof(char*))+sizeof(iotype*))*2)+sizeof(elements*))
+#define MAP_SIZE (2*sizeof(char*))+sizeof(NULL)
+#define IOTYPE_SIZE MAP_SIZE+sizeof(NULL)
+#define MAPS_SIZE (2*sizeof(char*))+sizeof(map*)+MAP_SIZE
+#define SERVICE_SIZE (ELEMENTS_SIZE*2)+(MAP_SIZE*2)+sizeof(char*)
+
+#define SHMSZ     27
+
+
+  /**
+   * \struct map
+   * \brief KVP linked list
+   *
+   * Deal with WPS KVP (name,value).
+   * A map is defined as:
+   *  - name : a key,
+   *  - value: a value,
+   *  - next : a pointer to the next map if any.
+   */
+  typedef struct map{
+    char* name;
+    char* value;
+    struct map* next;
+  } map;
+
+#ifdef WIN32
+#define NULLMAP ((map*) 0)
+#else
+#define NULLMAP NULL
+#endif
+
+  /**
+   * \struct maps
+   * \brief linked list of map pointer
+   *
+   * Small object to store WPS KVP set.
+   * Maps is defined as:
+   *  - a name, 
+   *  - a content map,
+   *  - a pointer to the next maps if any.
+   */
+  typedef struct maps{
+    char* name;          
+    struct map* content; 
+    struct maps* next;   
+  } maps;
+
+  /**
+   * \brief Dump a map on stderr
+   */
+  static void _dumpMap(map* t){
+    if(t!=NULL){
+      fprintf(stderr,"[%s] => [%s] \n",t->name,t->value);
+      fflush(stderr);
+    }else{
+      fprintf(stderr,"NULL\n");
+      fflush(stderr);
+    }
+  }
+
+  static void dumpMap(map* t){
+    map* tmp=t;
+    while(tmp!=NULL){
+      _dumpMap(tmp);
+      tmp=tmp->next;
+    }
+  }
+
+  static void dumpMapToFile(map* t,FILE* file){
+    map* tmp=t;
+    while(tmp!=NULL){
+      fprintf(file,"%s = %s\n",t->name,t->value);
+      tmp=tmp->next;
+    }
+  }
+
+  static void dumpMaps(maps* m){
+    maps* tmp=m;
+    while(tmp!=NULL){
+      fprintf(stderr,"MAP => [%s] \n",tmp->name);
+      dumpMap(tmp->content);
+      tmp=tmp->next;
+    }
+  }
+
+  static void dumpMapsToFile(maps* m,FILE* file){
+    maps* tmp=m;
+    if(tmp!=NULL){
+      fprintf(file,"[%s]\n",tmp->name);
+      dumpMapToFile(tmp->content,file);
+    }
+  }
+
+  static map* createMap(const char* name,const char* value){
+    map* tmp=(map *)malloc(MAP_SIZE);
+    tmp->name=strdup(name);
+    tmp->value=strdup(value);
+    tmp->next=NULL;
+    return tmp;
+  }
+
+  static int count(map* m){
+    map* tmp=m;
+    int c=0;
+    while(tmp!=NULL){
+      c++;
+      tmp=tmp->next;
+    }
+    return c;
+  }
+    
+  static bool hasKey(map* m,const char *key){
+    map* tmp=m;
+    while(tmp!=NULL){
+      if(strcasecmp(tmp->name,key)==0)
+	return true;
+      tmp=tmp->next;
+    }
+#ifdef DEBUG_MAP
+    fprintf(stderr,"NOT FOUND \n");
+#endif
+    return false;
+  }
+
+  static maps* getMaps(maps* m,const char *key){
+    maps* tmp=m;
+    while(tmp!=NULL){
+      if(strcasecmp(tmp->name,key)==0){
+	return tmp;
+      }
+      tmp=tmp->next;
+    }
+    return NULL;
+  }
+
+  static map* getMap(map* m,const char *key){
+    map* tmp=m;
+    while(tmp!=NULL){
+      if(strcasecmp(tmp->name,key)==0){
+	return tmp;
+      }
+      tmp=tmp->next;
+    }
+    return NULL;
+  }
+
+  static map* getLastMap(map* m){
+    map* tmp=m;
+    while(tmp!=NULL){
+      if(tmp->next==NULL){
+	return tmp;
+      }
+      tmp=tmp->next;
+    }
+    return NULL;
+  }
+
+  static map* getMapFromMaps(maps* m,const char* key,const char* subkey){
+    maps* _tmpm=getMaps(m,key);
+    if(_tmpm!=NULL){
+      map* _ztmpm=getMap(_tmpm->content,subkey);
+      return _ztmpm;
+    }
+    else return NULL;
+  }
+
+  static char* getMapsAsKVP(maps* m,int length,int type){
+    char *dataInputsKVP=(char*) malloc(length*sizeof(char));
+    maps* curs=m;
+    int i=0;
+    while(curs!=NULL){
+      if(i==0)
+	if(type==0)
+	  sprintf(dataInputsKVP,"%s=",curs->name);
+	else
+	  sprintf(dataInputsKVP,"%s",curs->name);
+      else{
+	char *temp=strdup(dataInputsKVP);
+	if(type==0)
+	  sprintf(dataInputsKVP,"%s;%s=",temp,curs->name);
+	else
+	  sprintf(dataInputsKVP,"%s;%s",temp,curs->name);
+	free(temp);
+      }
+      map* icurs=curs->content;
+      if(type==0){
+	map* tmp=getMap(curs->content,"value");
+	char *temp=strdup(dataInputsKVP);
+	if(getMap(m->content,"xlink:href")!=NULL)
+	  sprintf(dataInputsKVP,"%sReference",temp);
+	else
+	  sprintf(dataInputsKVP,"%s%s",temp,icurs->value);
+	free(temp);
+      }
+      int j=0;
+      while(icurs!=NULL){
+	if(strcasecmp(icurs->name,"value")!=0 &&
+	   strcasecmp(icurs->name,"Reference")!=0 &&
+	   strcasecmp(icurs->name,"minOccurs")!=0 &&
+	   strcasecmp(icurs->name,"maxOccurs")!=0 &&
+	   strcasecmp(icurs->name,"inRequest")!=0){
+	  char *itemp=strdup(dataInputsKVP);
+	  sprintf(dataInputsKVP,"%s@%s=%s",itemp,icurs->name,icurs->value);
+	  free(itemp);
+	}
+	icurs=icurs->next;
+      }
+      curs=curs->next;
+      i++;
+    }
+    return dataInputsKVP;
+  }
+
+
+  static void freeMap(map** mo){
+    map* _cursor=*mo;
+    if(_cursor!=NULL){
+#ifdef DEBUG
+      fprintf(stderr,"freeMap\n");
+#endif
+      free(_cursor->name);
+      free(_cursor->value);
+      if(_cursor->next!=NULL){
+	freeMap(&_cursor->next);
+	free(_cursor->next);
+      }
+    }
+  }
+
+  static void freeMaps(maps** mo){
+    maps* _cursor=*mo;
+    fflush(stderr);
+    if(_cursor && _cursor!=NULL){
+#ifdef DEBUG
+      fprintf(stderr,"freeMaps\n");
+#endif
+      free(_cursor->name);
+      if(_cursor->content!=NULL){
+      	freeMap(&_cursor->content);
+      	free(_cursor->content);
+      }
+      if(_cursor->next!=NULL){
+	freeMaps(&_cursor->next);
+	free(_cursor->next);
+      }
+    }
+  }
+
+  /**
+   * \brief Not named linked list
+   *
+   * Used to store informations about formats, such as mimeType, encoding ... 
+   *
+   * An iotype is defined as :
+   *  - a content map,
+   *  - a pointer to the next iotype if any.
+   */
+  typedef struct iotype{
+    struct map* content;
+    struct iotype* next;
+  } iotype;
+
+  /**
+   * \brief Metadata information about input or output.
+   *
+   * The elements are used to store metadata informations defined in the ZCFG.
+   *
+   * An elements is defined as :
+   *  - a name,
+   *  - a content map,
+   *  - a metadata map,
+   *  - a format (possible values are LiteralData, ComplexData or 
+   * BoundingBoxData),
+   *  - a default iotype,
+   *  - a pointer to the next elements id any.
+   */
+  typedef struct elements{
+    char* name;
+    struct map* content;
+    struct map* metadata;
+    char* format;
+    struct iotype* defaults;
+    struct iotype* supported;
+    struct elements* next;
+  } elements;
+
+  typedef struct service{
+    char* name;
+    struct map* content;
+    struct map* metadata;
+    struct elements* inputs;
+    struct elements* outputs; 
+  } service;
+
+  typedef struct services{
+    struct service* content; 
+    struct services* next; 
+  } services;
+
+  static bool hasElement(elements* e,const char* key){
+    elements* tmp=e;
+    while(tmp!=NULL){
+      if(strcasecmp(key,tmp->name)==0)
+	return true;
+      tmp=tmp->next;
+    }
+    return false;
+  }
+
+  static elements* getElements(elements* m,char *key){
+    elements* tmp=m;
+    while(tmp!=NULL){
+      if(strcasecmp(tmp->name,key)==0)
+	return tmp;
+      tmp=tmp->next;
+    }
+    return NULL;
+  }
+
+
+  static void freeIOType(iotype** i){
+    iotype* _cursor=*i;
+    if(_cursor!=NULL){
+      if(_cursor->next!=NULL){
+	freeIOType(&_cursor->next);
+	free(_cursor->next);
+      }
+      freeMap(&_cursor->content);
+      free(_cursor->content);
+    }
+  }
+
+  static void freeElements(elements** e){
+    elements* tmp=*e;
+    if(tmp!=NULL){
+      if(tmp->name!=NULL)
+	free(tmp->name);
+      freeMap(&tmp->content);
+      if(tmp->content!=NULL)
+	free(tmp->content);
+      freeMap(&tmp->metadata);
+      if(tmp->metadata!=NULL)
+	free(tmp->metadata);
+      if(tmp->format!=NULL)
+	free(tmp->format);
+      freeIOType(&tmp->defaults);
+      if(tmp->defaults!=NULL)
+	free(tmp->defaults);
+      freeIOType(&tmp->supported);
+      if(tmp->supported!=NULL){
+	free(tmp->supported);
+      }
+      freeElements(&tmp->next);
+      if(tmp->next!=NULL)
+	free(tmp->next);
+    }
+  }
+
+  static void freeService(service** s){
+    service* tmp=*s;
+    if(tmp!=NULL){
+      if(tmp->name!=NULL)
+	free(tmp->name);
+      freeMap(&tmp->content);
+      if(tmp->content!=NULL)
+	free(tmp->content);
+      freeMap(&tmp->metadata);
+      if(tmp->metadata!=NULL)
+	free(tmp->metadata);
+      freeElements(&tmp->inputs);
+      if(tmp->inputs!=NULL)
+	free(tmp->inputs);
+      freeElements(&tmp->outputs);
+      if(tmp->outputs!=NULL)
+	free(tmp->outputs);
+    }
+  }
+
+  static void addToMap(map* m,const char* n,const char* v){
+    if(hasKey(m,n)==false){
+      map* _cursor=m;
+      while(_cursor->next!=NULL)
+	_cursor=_cursor->next;
+      _cursor->next=createMap(n,v);
+    }
+    else{
+      map *tmp=getMap(m,n);
+      if(tmp->value!=NULL)
+      	free(tmp->value);
+      tmp->value=strdup(v);
+    }
+  }
+
+  static void addMapToMap(map** mo,map* mi){
+    map* tmp=mi;
+    map* _cursor=*mo;
+    if(tmp==NULL){
+      if(_cursor!=NULL){
+	while(_cursor!=NULL)
+	  _cursor=_cursor->next;
+	_cursor=NULL;
+      }else
+	*mo=NULL;
+    }
+    while(tmp!=NULL){
+      if(_cursor==NULL){
+	if(*mo==NULL)
+	  *mo=createMap(tmp->name,tmp->value);
+	else
+	  addToMap(*mo,tmp->name,tmp->value);
+      }
+      else{
+#ifdef DEBUG
+	fprintf(stderr,"_CURSOR\n");
+	dumpMap(_cursor);
+#endif
+	while(_cursor!=NULL)
+	  _cursor=_cursor->next;
+	_cursor=createMap(tmp->name,tmp->value);
+	_cursor->next=NULL;
+      }
+      tmp=tmp->next;
+#ifdef DEBUG
+      fprintf(stderr,"MO\n");
+      dumpMap(*mo);
+#endif
+    }
+  }
+
+  static void addMapToIoType(iotype** io,map* mi){
+    iotype* tmp=*io;
+    while(tmp->next!=NULL){
+      tmp=tmp->next;
+    }
+    tmp->next=(iotype*)malloc(IOTYPE_SIZE);
+    tmp->next->content=NULL;
+    addMapToMap(&tmp->next->content,mi);
+    tmp->next->next=NULL;
+  }
+
+  static bool contains(map* m,map* i){
+    while(i!=NULL){      
+      if(strcasecmp(i->name,"value")!=0 &&
+	 strcasecmp(i->name,"xlink:href")!=0){
+	map *tmp;
+	if(hasKey(m,i->name) && (tmp=getMap(m,i->name))!=NULL && 
+	   strcasecmp(i->value,tmp->value)!=0)
+	  return false;
+      }
+      i=i->next;
+    }
+    return true;
+  }
+
+  static iotype* getIoTypeFromElement(elements* e,char *name, map* values){
+    elements* cursor=e;
+    while(cursor!=NULL){
+      if(strcasecmp(cursor->name,name)==0){
+	if(contains(cursor->defaults->content,values)==true)
+	  return cursor->defaults;
+	else{
+	  iotype* tmp=cursor->supported;
+	  while(tmp!=NULL){
+	    if(contains(tmp->content,values)==true)
+	      return tmp;	    
+	    tmp=tmp->next;
+	  }
+	}
+      }
+      cursor=cursor->next;
+    }
+    return NULL;
+  }
+
+  static maps* dupMaps(maps** mo){
+    maps* _cursor=*mo;
+    maps* res=NULL;
+    if(_cursor!=NULL){
+      res=(maps*)malloc(MAPS_SIZE);
+      res->name=strdup(_cursor->name);
+      res->content=NULL;
+      res->next=NULL;
+      map* mc=_cursor->content;
+      map* tmp=getMap(mc,"size");
+      char* tmpSized=NULL;
+      if(tmp!=NULL){
+	map* tmpV=getMap(mc,"value");
+	tmpSized=(char*)malloc((atoi(tmp->value)+1)*sizeof(char));
+	memmove(tmpSized,tmpV->value,atoi(tmp->value)*sizeof(char));
+      }
+      if(mc!=NULL){
+	addMapToMap(&res->content,mc);
+      }
+      if(tmp!=NULL){
+	map* tmpV=getMap(res->content,"value");
+	free(tmpV->value);
+	tmpV->value=(char*)malloc(atoi(tmp->value)*sizeof(char));
+	memmove(tmpV->value,tmpSized,atoi(tmp->value)*sizeof(char));
+	free(tmpSized);
+      }
+      res->next=dupMaps(&_cursor->next);
+    }
+    return res;
+  }
+
+  static void addMapsToMaps(maps** mo,maps* mi){
+    maps* tmp=mi;
+    maps* _cursor=*mo;
+    while(tmp!=NULL){
+      if(_cursor==NULL){
+	*mo=dupMaps(&mi);
+	(*mo)->next=NULL;
+      }
+      else{
+	while(_cursor->next!=NULL)
+	  _cursor=_cursor->next;
+	_cursor->next=dupMaps(&tmp);
+      }
+      tmp=tmp->next;
+    }
+  }
+
+
+  static void setMapInMaps(maps* m,const char* key,const char* subkey,const char *value){
+    maps* _tmpm=getMaps(m,key);
+    if(_tmpm!=NULL){
+      map* _ztmpm=getMap(_tmpm->content,subkey);
+      if(_ztmpm!=NULL){
+	if(_ztmpm->value!=NULL)
+	  free(_ztmpm->value);
+	_ztmpm->value=strdup(value);
+      }else{
+	addToMap(_tmpm->content,subkey,value);
+      }
+    }
+  }
+
+
+  static void dumpElements(elements* e){
+    elements* tmp=e;
+    while(tmp!=NULL){
+      fprintf(stderr,"ELEMENT [%s]\n",tmp->name);
+      fprintf(stderr," > CONTENT [%s]\n",tmp->name);
+      dumpMap(tmp->content);
+      fprintf(stderr," > METADATA [%s]\n",tmp->name);
+      dumpMap(tmp->metadata);
+      fprintf(stderr," > FORMAT [%s]\n",tmp->format);
+      iotype* tmpio=tmp->defaults;
+      int ioc=0;
+      while(tmpio!=NULL){
+	fprintf(stderr," > DEFAULTS [%s] (%i)\n",tmp->name,ioc);
+	dumpMap(tmpio->content);
+	tmpio=tmpio->next;
+	ioc++;
+      }
+      tmpio=tmp->supported;
+      ioc=0;
+      while(tmpio!=NULL){
+	fprintf(stderr," > SUPPORTED [%s] (%i)\n",tmp->name,ioc);
+	dumpMap(tmpio->content);
+	tmpio=tmpio->next;
+	ioc++;
+      }
+      fprintf(stderr,"------------------\n");
+      tmp=tmp->next;
+    }
+  }
+
+  static elements* dupElements(elements* e){
+    elements* cursor=e;
+    elements* tmp=NULL;
+    if(cursor!=NULL){
+#ifdef DEBUG
+      fprintf(stderr,">> %s %i\n",__FILE__,__LINE__);
+      dumpElements(e);
+      fprintf(stderr,">> %s %i\n",__FILE__,__LINE__);
+#endif
+      tmp=(elements*)malloc(ELEMENTS_SIZE);
+      tmp->name=strdup(e->name);
+      tmp->content=NULL;
+      addMapToMap(&tmp->content,e->content);
+      tmp->metadata=NULL;
+      addMapToMap(&tmp->metadata,e->metadata);
+      tmp->format=strdup(e->format);
+      if(e->defaults!=NULL){
+	tmp->defaults=(iotype*)malloc(IOTYPE_SIZE);
+	tmp->defaults->content=NULL;
+	addMapToMap(&tmp->defaults->content,e->defaults->content);
+	tmp->defaults->next=NULL;
+#ifdef DEBUG
+	fprintf(stderr,">> %s %i\n",__FILE__,__LINE__);
+	dumpMap(tmp->defaults->content);
+#endif
+      }else
+	tmp->defaults=NULL;
+      if(e->supported!=NULL){
+	tmp->supported=(iotype*)malloc(IOTYPE_SIZE);
+	tmp->supported->content=NULL;
+	addMapToMap(&tmp->supported->content,e->supported->content);
+	tmp->supported->next=NULL;
+	iotype *tmp2=e->supported->next;
+	while(tmp2!=NULL){
+	  addMapToIoType(&tmp->supported,tmp2->content);
+#ifdef DEBUG
+	  fprintf(stderr,">> %s %i\n",__FILE__,__LINE__);
+	  dumpMap(tmp->defaults->content);
+#endif
+	  tmp2=tmp2->next;
+	}
+      }
+      else
+	tmp->supported=NULL;
+      tmp->next=dupElements(cursor->next);
+    }
+    return tmp;
+  }
+
+  static void addToElements(elements** m,elements* e){
+    elements* tmp=e;
+    if(*m==NULL){
+      *m=dupElements(tmp);
+    }else{
+      addToElements(&(*m)->next,tmp);
+    }
+  }
+
+  static void dumpService(service* s){
+    fprintf(stderr,"++++++++++++++++++\nSERVICE [%s]\n++++++++++++++++++\n",s->name);
+    if(s->content!=NULL){
+      fprintf(stderr,"CONTENT MAP\n");
+      dumpMap(s->content);
+      fprintf(stderr,"CONTENT METADATA\n");
+      dumpMap(s->metadata);
+    }
+    if(s->inputs!=NULL){
+      fprintf(stderr,"INPUT ELEMENTS [%s]\n------------------\n",s->name);
+      dumpElements(s->inputs);
+    }
+    if(s->outputs!=NULL){
+      fprintf(stderr,"OUTPUT ELEMENTS [%s]\n------------------\n",s->name);
+      dumpElements(s->outputs);
+    }
+    fprintf(stderr,"++++++++++++++++++\n");
+  }
+
+  static void mapsToCharXXX(maps* m,char*** c){
+    maps* tm=m;
+    int i=0;
+    int j=0;
+    char tmp[10][30][1024];
+    memset(tmp,0,1024*10*10);
+    while(tm!=NULL){
+      if(i>=10)
+	break;
+      strcpy(tmp[i][j],"name");
+      j++;
+      strcpy(tmp[i][j],tm->name);
+      j++;
+      map* tc=tm->content;
+      while(tc!=NULL){
+	if(j>=30)
+	  break;
+	strcpy(tmp[i][j],tc->name);
+	j++;
+	strcpy(tmp[i][j],tc->value);
+	j++;
+	tc=tc->next;
+      }
+      tm=tm->next;
+      j=0;
+      i++;
+    }
+    memcpy(c,tmp,10*10*1024);
+  }
+
+  static void charxxxToMaps(char*** c,maps**m){
+    maps* trorf=*m;
+    int i,j;
+    char tmp[10][30][1024];
+    memcpy(tmp,c,10*30*1024);
+    for(i=0;i<10;i++){
+      if(strlen(tmp[i][1])==0)
+	break;
+      trorf->name=tmp[i][1];
+      trorf->content=NULL;
+      trorf->next=NULL;
+      for(j=2;j<29;j+=2){
+	if(strlen(tmp[i][j+1])==0)
+	  break;
+	if(trorf->content==NULL)
+	  trorf->content=createMap(tmp[i][j],tmp[i][j+1]);
+	else
+	  addToMap(trorf->content,tmp[i][j],tmp[i][j+1]);
+      }
+      trorf=trorf->next;
+    }
+    m=&trorf;
+  }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_conf.l
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_conf.l	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_conf.l	(revision 221)
@@ -0,0 +1,123 @@
+%option noyywrap
+%option yylineno
+
+
+%{
+//======================================================
+/**
+
+ authors : Jean-Marie CODOL, Naitan GROLLEMUND
+
+**/
+//======================================================
+
+
+#include <string.h>
+#include "service_conf.tab.h"
+
+#ifdef DEBUG_SERVICE_CONF
+int affichetrace = 1;
+#else
+int affichetrace = 0;
+#endif
+
+
+int attentionImpossibleDeTrouverXMLDeclapres = 0 ;
+
+int attentionImpossibleDeTrouverPIapres = 0 ;
+
+%}
+
+
+S		[ \t\r\n]+
+
+CharRef		"&#"[0-9]+";"|"&#x"[0-9a-fA-F]+";"
+
+egalevolue		{S}?"="{S}?
+
+Name		([_:]|[\x41-\x5A]|[\x61-\x7A]|[\xC0-\xD6]|[\xD8-\xF6]|[\xF8-\xFF])(([\x41-\x5A]|[\x61-\x7A]|[\xC0-\xD6]|[\xD8-\xF6]|[\xF8-\xFF])|[0-9.\-_:])*
+
+chardata	[^<]*
+
+attname	[a-zA-Z0-9_\-]+
+attvalue1	[\^\*\+,;@a-zA-Z0-9_\-::.:" "\"\'/\\\(\)\t\|]+
+
+attvalue		\"[^"]*\"|\'[^']*\'\(\)
+
+whitespace                      [\t]{0,}|[ ]{0,}
+whitesp                      [\t]|[ ]
+newline                 [\r\n]|[\n]
+newlines                 [\r\n]{1,}|[\n]{1,}
+
+
+%x DANSBALISE HORSBALISE PAIRSTART
+
+
+
+
+%%
+
+"\n" {  if (affichetrace==1) fprintf (stderr,"\n\nNEWLINE\n") ;return NEWLINE;}
+
+{newline}+{whitesp}*			{  if (affichetrace==1) fprintf (stderr,"\n\nNEWLINE 1\n") ; return NEWLINE;}
+
+<INITIAL,HORSBALISE>"["{attname}"]"             {  srlval.chaine=yytext;return ANID; }
+
+<INITIAL,HORSBALISE>{attname}             {  srlval.chaine=yytext; return SPAIR; }
+
+<PAIRSTART,HORSBALISE>{attvalue1}             { srlval.chaine=yytext;/*BEGIN(INITIAL);*/ return EPAIR;}
+
+<PAIRSTART,INITIAL,HORSBALISE>{whitesp}*"="{whitesp}*             {  BEGIN(PAIRSTART);}
+
+<PAIRSTART,INITIAL,HORSBALISE,DANSBALISE>{newline}+{whitesp}*             { BEGIN(INITIAL);  return NEWLINE;}
+
+<INITIAL>"<?"[Xx][Mm][Ll]  {   if (attentionImpossibleDeTrouverXMLDeclapres == 1 || attentionImpossibleDeTrouverPIapres == 1) {/* il y a eut un commentaire ou une balise applicative avant la declaration xml */ fprintf(stderr,"\nerror : a la ligne %d : il y a eut un commentaire ou un PI avant la declaration xml\n",srlineno); exit (10) ; } ; return STARTXMLDECL;}
+
+<INITIAL>"version"{egalevolue}\"1.0\"|"version"{egalevolue}\'1.0\'  { return VERSIONDECL;  }
+<INITIAL>"version"{egalevolue}\"[^"]*\"|"version"{egalevolue}\'[^']*\'  {/* erreur de version encoding */ fprintf(stderr,"\nerror : a la ligne %d : la version xml n est pas reconnue : %s\n",srlineno,yytext); exit (9) ; }
+
+
+<INITIAL>"encoding"{egalevolue}\"[Ii][Ss][Oo]"-8859-1"\"|"encoding"{egalevolue}\'[Ii][Ss][Oo]"-8859-1"\'  { return ENCODINGDECL;}
+<INITIAL>"encoding"{egalevolue}\"[^"]*\"|"encoding"{egalevolue}\'[^']*\'  {/* erreur de version encoding */ 	fprintf(stderr,"\nerror : a la ligne %d : la version d encodage n est pas reconnue : %s\n",srlineno,yytext); exit (8) ; }
+
+
+<INITIAL>"standalone"{egalevolue}\"yes\"|"standalone"{egalevolue}\'yes\'|"standalone"{egalevolue}\"no\"|"standalone"{egalevolue}\'no\'  { return SDDECL;}
+
+<INITIAL>"standalone"{egalevolue}\"[^"]*\"|"standalone"{egalevolue}\'[^']*\'|"standalone"{egalevolue}\"[^"]*\"|"standalone"{egalevolue}\'[^']*\'  { /* erreur de version encoding */ 	fprintf(stderr,"\nerror : a la ligne %d : la version standalone n est pas reconnue : %s\n",srlineno,yytext); exit (7) ; }
+
+
+<INITIAL>"?>"  { BEGIN(HORSBALISE); return ENDXMLDECL;}
+
+
+<DANSBALISE,INITIAL,HORSBALISE>{S}   {  }
+
+
+<HORSBALISE>"<?"[Xx][Mm][Ll]{S}({S}|{chardata})*"?>"|"<?"[Xx][Mm][Ll]"?>"	{ return PIERROR;}
+<INITIAL,HORSBALISE>"<?"([^xX]|([xX][^mM])|([xX][mM][^lL]))({S}|([^?]|("?"[^>])))*"?>"		{ attentionImpossibleDeTrouverPIapres=1 ; return PI;}
+
+
+<INITIAL,HORSBALISE>{newline}*"<"		    { BEGIN(DANSBALISE); return INFCAR;}
+
+
+<DANSBALISE>">"			{ BEGIN(HORSBALISE);return SUPCAR;}
+
+
+<DANSBALISE>"/"		{return SLASH;}
+
+
+<DANSBALISE>{egalevolue}			{return Eq;}
+
+
+<DANSBALISE>{Name}{newline}*			{memmove(srlval.chaine,yytext,(strlen(yytext)+1)*sizeof(char));return ID;}
+
+
+<DANSBALISE>{attvalue}		{return ATTVALUE;}
+
+
+<INITIAL,HORSBALISE>"<!--"([^-]|"-"[^-])*"-->"		{attentionImpossibleDeTrouverXMLDeclapres=1; }
+
+
+<INITIAL,DANSBALISE,HORSBALISE>.|\n	{fprintf(stderr,"error : ligne %d : caractere non reconnu '%s'\n",srlineno,yytext);}
+
+%%
+
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_conf.y
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_conf.y	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_conf.y	(revision 221)
@@ -0,0 +1,807 @@
+%{
+//======================================================
+/**
+ * Thx to Jean-Marie CODOL and Naitan GROLLEMUND
+ * copyright 2009 GeoLabs SARL 
+ * Author : Gérald FENOY
+ *
+ */
+//======================================================
+
+#include <string>
+#include <stdio.h>
+#include <ctype.h>
+#include <service.h>
+  //#include <vector>
+
+static int tmp_count=1;
+static int defaultsc=0;
+static bool wait_maincontent=true;
+static bool wait_mainmetadata=false;
+static bool wait_metadata=false;
+static bool wait_inputs=false;
+static bool wait_defaults=false;
+static bool wait_supporteds=false;
+static bool wait_outputs=false;
+static bool wait_data=false;
+static int services_c=0;
+static service* my_service=NULL;
+static map* previous_content=NULL;
+static map* current_content=NULL;
+static elements* current_element=NULL;
+static map* scontent=NULL;
+static char* curr_key;
+static int debug=0;
+static int data=-1;
+static int previous_data=0;
+static int current_data=0;
+// namespace
+using namespace std;
+//======================================================
+
+// srerror
+void srerror(const char *s);
+//======================================================
+
+// usage ()
+void usage(void) ;
+//======================================================
+
+// srdebug
+extern int srdebug;
+//======================================================
+
+extern char srtext[];
+
+// srlineno
+extern int srlineno;
+//======================================================
+
+// srin
+extern FILE* srin;
+//======================================================
+
+// srlex
+extern int srlex(void);
+extern int srlex_destroy(void);
+
+//vector<char*> lattribute;
+
+%}
+
+
+
+%union 
+{char * s;char* chaine;char* key;char* val;}
+
+// jetons //
+%token <s> ID
+%token <s> CHAINE
+/* STARTXMLDECL et ENDXMLDECL qui sont <?xml et ?>*/
+%token STARTXMLDECL ENDXMLDECL
+//======================================================
+/* version="xxx" et encoding="xxx" */
+%token VERSIONDECL ENCODINGDECL SDDECL
+//======================================================
+/* < et > */
+%token INFCAR SUPCAR 
+//======================================================
+/* / = a1  texte "texte" */
+%token SLASH Eq CHARDATA ATTVALUE PAIR SPAIR EPAIR ANID
+%type <chaine> PAIR
+%type <chaine> EPAIR
+%type <chaine> SPAIR
+//======================================================
+/* <!-- xxx -> <? xxx yyy ?> */
+%token PI PIERROR /** COMMENT **/
+//======================================================
+/* <!-- xxx -> <? xxx yyy ?> */
+%token ERREURGENERALE CDATA WHITESPACE NEWLINE
+%type <s> STag
+%type <s> ETag
+%type <s> ANID
+//======================================================
+// %start
+//======================================================
+
+%%
+// document <//===
+//======================================================
+// regle 1
+// on est a la racine du fichier xml
+//======================================================
+document
+ : miscetoile element miscetoile {}
+ | contentetoile processid contentetoile document {}
+ ;
+
+miscetoile
+ : miscetoile PIERROR {  srerror("processing instruction begining with <?xml ?> impossible\n");}
+ | miscetoile PI {}
+ | {}
+ ;
+// element
+//======================================================
+// regle 39
+// OUVRANTE CONTENU FERMANTE obligatoirement
+// ou neutre
+// on ne peut pas avoir Epsilon
+// un fichier xml ne peut pas etre vide ou seulement avec un prolog
+//======================================================
+element
+ : STag contentetoile ETag	
+{
+}
+
+// pour neutre
+// on a rien a faire, meme pas renvoyer l identificateur de balise
+// vu qu'il n y a pas de comparaison d'identificateurs avec un balise jumelle .
+ | EmptyElemTag          {}
+ ;
+
+//======================================================
+// STag
+//======================================================
+// regle 40
+// BALISE OUVRANTE
+// on est obligé de faire appel a infcar et supcar
+// pour acceder aux start conditions DANSBALISE et INITIAL
+//======================================================
+STag
+: INFCAR ID Attributeetoile SUPCAR
+{
+  if(my_service->content==NULL){
+#ifdef DEBUG_SERVICE_CONF
+    fprintf(stderr,"NO CONTENT\n");
+#endif
+    addMapToMap(&my_service->content,current_content);
+    freeMap(&current_content);
+    free(current_content);
+    current_content=NULL;
+    my_service->metadata=NULL;
+    wait_maincontent=false;
+  }
+  if(strncasecmp($2,"DataInputs",10)==0){
+    if(wait_mainmetadata==true){
+      addMapToMap(&my_service->metadata,current_content);
+      freeMap(&current_content);
+      free(current_content);
+      current_content=NULL;
+      wait_mainmetadata=false;
+    }
+    if(current_element==NULL){
+#ifdef DEBUG_SERVICE_CONF
+      fprintf(stderr,"(DATAINPUTS - %d) FREE current_element\n",__LINE__);
+#endif
+      freeElements(&current_element);
+      free(current_element);
+#ifdef DEBUG_SERVICE_CONF
+      fprintf(stderr,"(DATAINPUTS - %d) ALLOCATE current_element\n",__LINE__);
+#endif
+      current_element=NULL;
+      current_element=(elements*)malloc(ELEMENTS_SIZE);
+      current_element->name=NULL;
+      current_element->content=NULL;
+      current_element->metadata=NULL;
+      current_element->format=NULL;
+      current_element->defaults=NULL;
+      current_element->supported=NULL;
+      current_element->next=NULL;
+    }
+    wait_inputs=true;
+    current_data=1;
+    previous_data=1;
+  }
+  else
+    if(strncasecmp($2,"DataOutputs",11)==0){
+      if(wait_inputs==true){
+#ifdef DEBUG_SERVICE_CONF
+	fprintf(stderr,"(DATAOUTPUTS %d) DUP INPUTS current_element\n",__LINE__);
+	fprintf(stderr,"CURRENT_ELEMENT\n");
+	dumpElements(current_element);
+	fprintf(stderr,"SERVICE INPUTS\n");
+	dumpElements(my_service->inputs);
+	dumpService(my_service);
+#endif	
+	if(my_service->inputs==NULL){
+	  my_service->inputs=dupElements(current_element);
+	  my_service->inputs->next=NULL;
+	}
+	else if(current_element!=NULL && current_element->name!=NULL){
+	  addToElements(&my_service->inputs,current_element);
+	}
+#ifdef DEBUG_SERVICE_CONF
+	fprintf(stderr,"CURRENT_ELEMENT\n");
+	dumpElements(current_element);
+	fprintf(stderr,"SERVICE INPUTS\n");
+	dumpElements(my_service->inputs);
+	fprintf(stderr,"(DATAOUTPUTS) FREE current_element\n");
+#endif
+	freeElements(&current_element);
+	free(current_element);
+	current_element=NULL;
+	wait_inputs=false;
+      }
+      if(current_element==NULL){
+#ifdef DEBUG_SERVICE_CONF
+	fprintf(stderr,"(DATAOUTPUTS - %d) ALLOCATE current_element (%s)\n",__LINE__,$2);
+#endif
+	current_element=(elements*)malloc(ELEMENTS_SIZE);
+	current_element->name=NULL;
+	current_element->content=NULL;
+	current_element->metadata=NULL;
+	current_element->format=NULL;
+	current_element->defaults=NULL;
+	current_element->supported=NULL;
+	current_element->next=NULL;
+      }
+      wait_outputs=true;
+      current_data=2;
+      previous_data=2;
+    }
+    else
+      if(strncasecmp($2,"MetaData",8)==0){
+	previous_data=current_data;
+	current_data=3;
+	if(current_element!=NULL){
+#ifdef DEBUG_SERVICE_CONF
+	  fprintf(stderr,"add current_content to current_element->content\n");
+	  fprintf(stderr,"LINE %d",__LINE__);
+#endif
+	  addMapToMap(&current_element->content,current_content);
+	  freeMap(&current_content);
+	  free(current_content);
+	  if(previous_data==1 || previous_data==2)
+	    wait_metadata=true;
+	  else
+	    wait_mainmetadata=true;
+	}
+	else{
+	  if(previous_data==1 || previous_data==2)
+	    wait_metadata=true;
+	  else
+	    wait_mainmetadata=true;
+	}
+	current_content=NULL;
+      }
+      else
+	if(strncasecmp($2,"ComplexData",11)==0 || strncasecmp($2,"LiteralData",10)==0
+	   || strncasecmp($2,"ComplexOutput",13)==0 || strncasecmp($2,"LiteralOutput",12)==0
+	   || strncasecmp($2,"BoundingBoxOutput",13)==0 || strncasecmp($2,"BoundingBoxData",12)==0){
+	  current_data=4;
+	  if(wait_metadata==true){
+	    if(current_content!=NULL){
+	      addMapToMap(&current_element->metadata,current_content);
+	      current_element->next=NULL;
+	      if($2!=NULL)
+		current_element->format=strdup($2);
+	      
+	      current_element->defaults=NULL;
+	      current_element->supported=NULL;
+	      freeMap(&current_content);
+	      free(current_content);
+	    }
+	  }else{ 
+	    // No MainMetaData
+	    addMapToMap(&current_element->content,current_content);
+	    freeMap(&current_content);
+	    free(current_content);
+	    current_element->metadata=NULL;
+	    current_element->next=NULL;
+	    if($2!=NULL)
+	    current_element->format=strdup($2);
+	    current_element->defaults=NULL;
+	    current_element->supported=NULL;
+	  }
+	  current_content=NULL;
+	  wait_metadata=false;
+	}
+	else
+	  if(strncasecmp($2,"Default",7)==0){
+	    wait_defaults=true;
+	    current_data=5;
+	  }
+	  else
+	    if(strncasecmp($2,"Supported",9)==0){
+	      wait_supporteds=true;
+	      if(wait_defaults==true){
+		defaultsc++;
+	      }
+	      current_data=5;
+	    }
+#ifdef DEBUG_SERVICE_CONF
+  printf("* Identifiant : %s\n",$2);
+#endif
+}
+ ;
+
+//======================================================
+// Attributeetoile
+//======================================================
+// regle 41
+// une liste qui peut etre vide d'attributs
+// utiliser la récursivité a gauche
+//======================================================
+Attributeetoile
+ : Attributeetoile attribute  {}
+ | 	                          {/* Epsilon */}
+ ;
+
+//======================================================
+// attribute
+//======================================================
+// regle 41
+// un attribut est compose d'un identifiant
+// d'un "="
+// et d'une définition de chaine de caractere
+// ( "xxx" ou 'xxx' )
+//======================================================
+attribute
+ : ID Eq ATTVALUE		
+{
+#ifdef DEBUG_SERVICE_CONF
+  printf ("attribute : %s\n",$1) ;
+#endif
+}
+ ;
+
+//======================================================
+// EmptyElemTag
+//======================================================
+// regle 44
+// ICI ON DEFINIT NEUTRE
+// on ne renvoie pas de char*
+// parce qu'il n'y a pas de comparaisons a faire
+// avec un identifiant d'une balise jumelle
+//======================================================
+EmptyElemTag
+ : INFCAR ID Attributeetoile SLASH SUPCAR	{}
+ ;
+
+//======================================================
+// ETag
+//======================================================
+// regle 42
+// BALISE FERMANTE
+// les separateurs après ID sont filtrés
+//======================================================
+ETag
+ : INFCAR SLASH ID SUPCAR
+{
+  if(strcmp($3,"DataInputs")==0){
+    current_data=1;
+  }
+  if(strcmp($3,"DataOutputs")==0){
+    current_data=2;
+  }
+  if(strcmp($3,"MetaData")==0){
+    current_data=previous_data;
+  }
+  if(strcmp($3,"ComplexData")==0 || strcmp($3,"LiteralData")==0 
+     || strcmp($3,"ComplexOutput")==0 || strcmp($3,"LiteralOutput")==0){
+    current_content=NULL;
+  }
+  if(strcmp($3,"Default")==0){
+    current_data=previous_data;
+    if(current_element->defaults==NULL){
+      current_element->defaults=(iotype*)malloc(IOTYPE_SIZE);
+      current_element->defaults->content=NULL;
+    }
+    addMapToMap(&current_element->defaults->content,current_content);
+    freeMap(&current_content);
+    free(current_content);
+    current_element->defaults->next=NULL;
+    wait_defaults=false;
+    current_content=NULL;
+    current_element->supported=NULL;
+    current_element->next=NULL;
+  }
+  if(strcmp($3,"Supported")==0){
+    current_data=previous_data;
+    if(current_element->supported==NULL){
+      if(current_content!=NULL){
+	current_element->supported=(iotype*)malloc(IOTYPE_SIZE);
+	current_element->supported->content=NULL;
+	addMapToMap(&current_element->supported->content,current_content);
+	freeMap(&current_content);
+	free(current_content);
+	current_element->supported->next=NULL;
+	current_content=NULL;
+      }else{
+	current_element->supported=NULL;
+	current_element->next=NULL;
+      }
+    }
+    else{
+#ifdef DEBUG_SERVICE_CONF
+      fprintf(stderr,"SECOND SUPPORTED FORMAT !!!!\n");
+#endif
+      addMapToIoType(&current_element->supported,current_content);
+      freeMap(&current_content);
+      free(current_content);
+      current_content=NULL;
+#ifdef DEBUG_SERVICE_CONF
+      dumpElements(current_element);
+      fprintf(stderr,"SECOND SUPPORTED FORMAT !!!!\n");
+#endif
+    }
+    current_content=NULL;
+  }
+}
+ ;
+
+//======================================================
+// contentetoile
+//======================================================
+// regle 43
+// ENTRE 2 BALISES
+// entre 2 balises, on peut avoir :
+// --- OUVRANTE CONTENU FERMANTE (recursivement !)
+// --- DU TEXTE quelconque
+// --- COMMENTS 
+// --- DES PROCESSES INSTRUCTIONS
+// --- /!\ il peut y avoir une processing instruction invalide ! <?xml
+// --- EPSILON
+// ### et/ou tout ca a la suite en nombre indeterminé
+// ### donc c'est un operateur etoile (*)
+//======================================================
+contentetoile
+: contentetoile element	          {}
+ | contentetoile PIERROR	          {srerror("processing instruction <?xml ?> impossible\n");}
+ | contentetoile PI	                  {}
+///// on filtre les commentaires | contentetoile comment              {} 
+ | contentetoile NEWLINE {/*printf("NEWLINE FOUND !!");*/}
+ | contentetoile pair {}
+ | contentetoile processid {}
+ | contentetoile texteinterbalise	  {}
+ | contentetoile CDATA {}  
+ | {/* Epsilon */}
+ ;
+
+//======================================================
+// texteinterbalise
+//======================================================
+// regle 14
+// DU TEXTE quelconque
+// c'est du CHARDATA
+// il y a eut un probleme avec ID,
+// on a mis des starts conditions,
+// maintenant on croise les ID dans les dbalises
+// et des CHARDATA hors des balises
+//======================================================
+texteinterbalise
+ : CHARDATA		{}
+ ;
+//======================================================
+
+pair: PAIR { if(debug) fprintf(stderr,"PAIR FOUND !!\n");if(curr_key!=NULL){free(curr_key);curr_key=NULL;} }
+| EPAIR {
+#ifdef DEBUG_SERVICE_CONF
+  fprintf(stderr,"EPAIR FOUND !! \n"); 
+  fprintf(stderr,"[%s=>%s]\n",curr_key,$1);
+  fprintf(stderr,"[ZOO: service_conf.y line %d free(%s)]\n",__LINE__,curr_key);
+  dumpMap(current_content);
+  fflush(stderr);
+#endif
+  if($1!=NULL){
+    if(current_content==NULL){
+#ifdef DEBUG_SERVICE_CONF
+      fprintf(stderr,"[ZOO: service_conf.y line %d free(%s)]\n",__LINE__,curr_key);
+#endif
+      current_content=createMap(curr_key,$1);
+#ifdef DEBUG_SERVICE_CONF
+      fprintf(stderr,"[ZOO: service_conf.y line %d free(%s)]\n",__LINE__,curr_key);
+#endif
+      //current_content->next=NULL;
+    }
+    else{ 
+#ifdef DEBUG_SERVICE_CONF
+      dumpMap(current_content);
+      fprintf(stderr,"addToMap(current_content,%s,%s) !! \n",curr_key,$1); 
+#endif
+      addToMap(current_content,curr_key,$1);
+#ifdef DEBUG_SERVICE_CONF
+      fprintf(stderr,"addToMap(current_content,%s,%s) end !! \n",curr_key,$1); 
+#endif    
+    }
+  }
+#ifdef DEBUG_SERVICE_CONF
+  fprintf(stderr,"EPAIR FOUND !! \n"); 
+  fprintf(stderr,"[%s=>%s]\n",curr_key,$1);
+  fprintf(stderr,"[ZOO: service_conf.y line %d free(%s)]\n",__LINE__,curr_key);
+  fflush(stderr);
+#endif
+  if(curr_key!=NULL){
+    free(curr_key);
+    curr_key=NULL;
+  }
+  }
+| SPAIR  { if(curr_key!=NULL) {free(curr_key);curr_key=NULL;} if($1!=NULL) curr_key=strdup($1);if(debug) fprintf(stderr,"SPAIR FOUND !!\n"); }
+ ;
+
+
+processid
+: ANID  {
+  if(data==-1){
+    data=1;
+    if($1!=NULL){
+      char *cen=strdup($1);
+      my_service->name=(char*)malloc((strlen(cen)-1)*sizeof(char*));
+      cen[strlen(cen)-1]=0;
+      cen+=1;
+      sprintf(my_service->name,"%s",cen);
+      cen-=1;
+      free(cen);
+      my_service->content=NULL;
+      my_service->metadata=NULL;
+      my_service->inputs=NULL;
+      my_service->outputs=NULL;
+    }
+  } else {
+    if(current_data==1){
+      if(my_service->content!=NULL && current_element->name!=NULL){
+	if(my_service->inputs==NULL){
+	  my_service->inputs=dupElements(current_element);
+	  my_service->inputs->next=NULL;
+	  tmp_count++;
+	}
+	else{
+	  addToElements(&my_service->inputs,current_element);
+	}
+#ifdef DEBUG_SERVICE_CONF
+	fprintf(stderr,"(%s %d)FREE current_element (after adding to allread existing inputs)",__FILE__,__LINE__);
+	dumpElements(current_element);
+	fprintf(stderr,"(%s %d)FREE current_element (after adding to allread existing inputs)",__FILE__,__LINE__);
+	dumpElements(my_service->inputs);
+#endif
+	freeElements(&current_element);
+	free(current_element);
+	current_element=NULL;
+#ifdef DEBUG_SERVICE_CONF
+	fprintf(stderr,"(DATAINPUTS - 489) ALLOCATE current_element\n");
+#endif
+	current_element=(elements*)malloc(ELEMENTS_SIZE);
+	current_element->name=NULL;
+	current_element->content=NULL;
+	current_element->metadata=NULL;
+	current_element->format=NULL;
+	current_element->defaults=NULL;
+	current_element->supported=NULL;
+	current_element->next=NULL;
+      }
+      if(current_element->name==NULL){
+#ifdef DEBUG_SERVICE_CONF
+	fprintf(stderr,"NAME IN %s (current - %s)\n",
+		$1,current_element->name);
+#endif
+	wait_inputs=true;
+#ifdef DEBUG_SERVICE_CONF
+	fprintf(stderr,"(DATAINPUTS - 501) SET NAME OF current_element\n");
+#endif
+	if($1!=NULL){ 
+	  char *cen=strdup($1);
+	  current_element->name=(char*)malloc((strlen(cen)-1)*sizeof(char*));
+	  cen[strlen(cen)-1]=0;
+	  cen+=1;
+	  sprintf(current_element->name,"%s",cen);
+	  cen-=1;
+	  free(cen);
+#ifdef DEBUG_SERVICE_CONF
+	  fprintf(stderr,"NAME IN %s (current - %s)\n",$1,current_element->name);
+#endif
+	  current_element->content=NULL;
+	  current_element->metadata=NULL;
+	  current_element->format=NULL;
+	  current_element->defaults=NULL;
+	  current_element->supported=NULL;
+	  current_element->next=NULL;
+#ifdef DEBUG_SERVICE_CONF
+	  fprintf(stderr,"NAME IN %s (current - %s)\n",$1,current_element->name);
+#endif
+	}
+      }
+    }
+    else
+      if(current_data==2){ 
+	wait_outputs=true;
+	if(wait_inputs){
+	  if(current_element!=NULL && current_element->name!=NULL){
+	    if(my_service->outputs==NULL){
+	      my_service->outputs=dupElements(current_element);
+	      my_service->outputs->next=NULL;
+	    }
+	    else{
+#ifdef DEBUG_SERVICE_CONF
+	      fprintf(stderr,"LAST NAME IN %s (current - %s)\n",$1,current_element->name);
+#endif
+	      addToElements(&my_service->outputs,current_element);
+	    }
+#ifdef DEBUG_SERVICE_CONF
+	    dumpElements(current_element);
+	    fprintf(stderr,"(DATAOUTPUTS) FREE current_element %s %i\n",__FILE__,__LINE__);
+#endif
+	    freeElements(&current_element);
+	    free(current_element);
+	    current_element=NULL;
+#ifdef DEBUG_SERVICE_CONF
+	    fprintf(stderr,"(DATAOUTPUTS -%d) ALLOCATE current_element %s \n",__LINE__,__FILE__);
+#endif
+	    current_element=(elements*)malloc(ELEMENTS_SIZE);
+	    current_element->name=NULL;
+	    current_element->content=NULL;
+	    current_element->metadata=NULL;
+	    current_element->format=NULL;
+	    current_element->defaults=NULL;
+	    current_element->supported=NULL;
+	    current_element->next=NULL;
+	  }
+	  if(current_element->name==NULL){
+#ifdef DEBUG_SERVICE_CONF
+	    fprintf(stderr,"NAME OUT %s\n",$1);
+	    fprintf(stderr,"(DATAOUTPUTS - %d) SET NAME OF current_element\n",__LINE__);
+#endif
+	    if($1!=NULL){ 
+	      char *cen=strdup($1);
+	      current_element->name=(char*)malloc((strlen(cen)-1)*sizeof(char));
+	      cen[strlen(cen)-1]=0;
+	      cen+=1;
+	      sprintf(current_element->name,"%s",cen);
+	      cen-=1;
+	      free(cen);
+	      current_element->content=NULL;
+	      current_element->metadata=NULL;
+	      current_element->format=NULL;
+	      current_element->defaults=NULL;
+	      current_element->supported=NULL;
+	      current_element->next=NULL;
+	    }
+	  }
+
+	  current_content=NULL;
+	}
+	else
+	  if(current_element!=NULL && current_element->name!=NULL){
+	    if(my_service->outputs==NULL)
+	      my_service->outputs=dupElements(current_element);
+	    else
+	      addToElements(&my_service->outputs,current_element);
+#ifdef DEBUG_SERVICE_CONF
+	    fprintf(stderr,"ADD TO OUTPUTS Elements\n");
+	    dupElements(current_element);
+#endif
+	    freeElements(&current_element);
+	    free(current_element);
+	    current_element=NULL;
+	  }
+	  else{
+#ifdef DEBUG_SERVICE_CONF
+	    fprintf(stderr,"NAME OUT %s\n",$1);
+	    fprintf(stderr,"(DATAOUTPUTS - 545) SET NAME OF current_element\n");
+#endif
+	    if($1!=NULL){ 
+	      char *cen=strdup($1);
+	      current_element->name=(char*)malloc((strlen(cen)-1)*sizeof(char*));
+	      cen[strlen(cen)-1]=0;
+#ifdef DEBUG
+	      fprintf(stderr,"tmp %s\n",cen);
+#endif
+	      cen+=1;
+	      sprintf(current_element->name,"%s",cen);
+	      cen-=1;
+	      free(cen);
+	      current_element->content=NULL;
+	      current_element->metadata=NULL;
+	      current_element->format=NULL;
+	      current_element->defaults=NULL;
+	      current_element->supported=NULL;
+	      current_element->next=NULL;
+	    }
+	  }
+	wait_inputs=false;
+	wait_outputs=true;
+	//wait_outputs=true;
+      }
+  }
+ }
+ ;
+
+%%
+
+// srerror
+//======================================================
+/* fonction qui affiche l erreur si il y en a une */
+//======================================================
+void srerror(const char *s)
+{
+  if(debug)
+    fprintf(stderr,"\nligne %d : %s\n",srlineno,s);
+}
+
+/**
+ * getServiceFromFile :
+ * set service given as second parameter with informations extracted from the
+ * definition file.
+ */
+int getServiceFromFile(const char* file,service** service){
+
+  freeMap(&previous_content);
+  previous_content=NULL;
+  freeMap(&current_content);
+  current_content=NULL;
+  freeMap(&scontent);
+#ifdef DEBUG_SERVICE_CONF
+  fprintf(stderr,"(STARTING)FREE current_element\n");
+#endif
+  freeElements(&current_element);
+  free(current_element);
+  current_element=NULL;
+  my_service=NULL;
+  scontent=NULL;
+
+  wait_maincontent=true;
+  wait_mainmetadata=false;
+  wait_metadata=false;
+  wait_inputs=false;
+  wait_defaults=false;
+  wait_supporteds=false;
+  wait_outputs=false;
+  wait_data=false;
+  data=-1;
+  previous_data=1;
+  current_data=0;
+  
+  my_service=*service;
+
+  srin = fopen(file,"r");
+  if (srin==NULL){
+    fprintf(stderr,"error : file not found\n") ;
+    return -1;
+  }
+
+  int resultatYYParse = srparse() ;
+  
+  if(wait_outputs && current_element!=NULL && current_element->name!=NULL){
+    if(my_service->outputs==NULL){      
+#ifdef DEBUG_SERVICE_CONF
+      fprintf(stderr,"(DATAOUTPUTS - %d) DUP current_element\n",__LINE__);
+#endif
+      my_service->outputs=dupElements(current_element);
+      my_service->outputs->next=NULL;
+    }
+    else{
+#ifdef DEBUG_SERVICE_CONF
+      fprintf(stderr,"(DATAOUTPUTS - %d) COPY current_element\n",__LINE__);
+#endif
+      addToElements(&my_service->outputs,current_element);
+    }
+#ifdef DEBUG_SERVICE_CONF
+    fprintf(stderr,"(DATAOUTPUTS - %d) FREE current_element\n",__LINE__);
+#endif
+    freeElements(&current_element);
+    free(current_element);
+    current_element=NULL;
+#ifdef DEBUG_SERVICE_CONF
+    fprintf(stderr,"(DATAOUTPUTS - %d) FREE current_element\n",__LINE__);
+#endif
+  }
+  if(current_element!=NULL){
+    freeElements(&current_element);
+    free(current_element);
+    current_element=NULL;
+  }
+  if(current_content!=NULL){
+    freeMap(&current_content);
+    free(current_content);
+    current_content=NULL;
+  }
+  fclose(srin);
+#ifdef DEBUG_SERVICE_CONF
+  dumpService(my_service);
+#endif
+  *service=my_service;
+
+#ifndef WIN32
+  srlex_destroy();
+#endif
+  return resultatYYParse;
+}
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal.c	(revision 221)
@@ -0,0 +1,2236 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2011 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "service_internal.h"
+
+#ifdef WIN32
+char *
+strtok_r (char *s1, const char *s2, char **lasts)
+{
+  char *ret;
+  if (s1 == NULL)
+    s1 = *lasts;
+  while (*s1 && strchr(s2, *s1))
+    ++s1;
+  if (*s1 == '\0')
+    return NULL;
+  ret = s1;
+  while (*s1 && !strchr(s2, *s1))
+    ++s1;
+  if (*s1)
+    *s1++ = '\0';
+  *lasts = s1;
+  return ret;
+}
+#endif
+
+void addLangAttr(xmlNodePtr n,maps *m){
+  map *tmpLmap=getMapFromMaps(m,"main","language");
+  if(tmpLmap!=NULL)
+    xmlNewProp(n,BAD_CAST "xml:lang",BAD_CAST tmpLmap->value);
+  else
+    xmlNewProp(n,BAD_CAST "xml:lang",BAD_CAST "en-US");
+}
+
+/* Converts a hex character to its integer value */
+char from_hex(char ch) {
+  return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
+}
+
+/* Converts an integer value to its hex character*/
+char to_hex(char code) {
+  static char hex[] = "0123456789abcdef";
+  return hex[code & 15];
+}
+
+#ifdef WIN32
+
+#include <windows.h>
+#include <stdio.h>
+#include <conio.h>
+#include <tchar.h>
+
+#define SHMEMSIZE 4096
+
+static LPVOID lpvMemG = NULL;      // pointer to shared memory
+static HANDLE hMapObjectG = NULL;  // handle to file mapping
+
+void updateStatus(maps *conf){
+	fprintf(stderr,"OK Final 1 \n");
+	fflush(stderr);
+	LPWSTR lpszTmp;
+	BOOL fInit;
+	char *s=NULL;
+	map *tmpMap=getMapFromMaps(conf,"lenv","sid");
+	fprintf(stderr,"OK Final 11 \n");
+	fflush(stderr);
+	if(hMapObjectG==NULL)
+	hMapObjectG = CreateFileMapping( 
+		INVALID_HANDLE_VALUE,   // use paging file
+		NULL,                   // default security attributes
+		PAGE_READWRITE,         // read/write access
+		0,                      // size: high 32-bits
+		SHMEMSIZE,              // size: low 32-bits
+		TEXT(tmpMap->value));   // name of map object
+	if (hMapObjectG == NULL){
+		fprintf(stderr,"Unable to create share memory segment %s !! \n",tmpMap->value);
+		return ;
+	}
+	fprintf(stderr,"OK Final 2 \n");
+	fflush(stderr);
+	fInit = (GetLastError() != ERROR_ALREADY_EXISTS); 
+	if(lpvMemG==NULL)
+	lpvMemG = MapViewOfFile( 
+		hMapObjectG,     // object to map view of
+		FILE_MAP_WRITE, // read/write access
+		0,              // high offset:  map from
+		0,              // low offset:   beginning
+		0);             // default: map entire file
+	if (lpvMemG == NULL){
+		fprintf(stderr,"Unable to create or access the shared memory segment %s !! \n",tmpMap->value);
+		return ;
+	} 
+	fprintf(stderr,"OK Final 3 \n");
+	fflush(stderr);
+	if (fInit)
+		memset(lpvMemG, '\0', SHMEMSIZE);
+	fprintf(stderr,"OK Final 4 \n");
+	fflush(stderr);
+	tmpMap=getMapFromMaps(conf,"lenv","status");
+	lpszTmp = (LPWSTR) lpvMemG;
+	for(s=tmpMap->value;*s!=NULL;s++)
+		*lpszTmp++ = *s;
+	*lpszTmp = '\0'; 
+}
+
+char* getStatus(int pid){
+  LPWSTR lpszBuf=NULL;
+  LPWSTR lpszTmp=NULL;
+  LPVOID lpvMem = NULL;
+  HANDLE hMapObject = NULL;
+  BOOL fIgnore,fInit;
+  char tmp[100];
+  sprintf(tmp,"%i",pid);
+  if(hMapObject==NULL)
+    hMapObject = CreateFileMapping( 
+				   INVALID_HANDLE_VALUE,   // use paging file
+				   NULL,                   // default security attributes
+				   PAGE_READWRITE,         // read/write access
+				   0,                      // size: high 32-bits
+				   4096,                   // size: low 32-bits
+				   TEXT(tmp));   // name of map object
+  if (hMapObject == NULL) 
+    return FALSE;
+  if((GetLastError() != ERROR_ALREADY_EXISTS)){
+    fIgnore = UnmapViewOfFile(lpvMem); 
+    fIgnore = CloseHandle(hMapObject);
+    return "-1";
+  }
+  fInit=TRUE;
+  if(lpvMem==NULL)
+    lpvMem = MapViewOfFile( 
+			   hMapObject,     // object to map view of
+			   FILE_MAP_READ,  // read/write access
+			   0,              // high offset:  map from
+			   0,              // low offset:   beginning
+			   0);             // default: map entire file
+  if (lpvMem == NULL) 
+    return "-1"; 
+  lpszTmp = (LPWSTR) lpvMem;
+  while (*lpszTmp!=NULL)
+    *lpszBuf++ = *lpszTmp++;
+  *lpszBuf = '\0';
+  fIgnore = UnmapViewOfFile(lpvMem); 
+  fIgnore = CloseHandle(hMapObject);
+  return (char*)lpszBuf;
+}
+
+void unhandleStatus(maps *conf){
+  BOOL fIgnore;
+  fIgnore = UnmapViewOfFile(lpvMemG); 
+  fIgnore = CloseHandle(hMapObjectG);
+}
+#else
+void unhandleStatus(maps *conf){
+  int shmid,i;
+  key_t key;
+  void *shm;
+  struct shmid_ds shmids;
+  char *s,*s1;
+  map *tmpMap=getMapFromMaps(conf,"lenv","sid");
+  if(tmpMap!=NULL){
+    key=atoi(tmpMap->value);
+    if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
+#ifdef DEBUG
+      fprintf(stderr,"shmget failed to update value\n");
+#endif
+    }else{
+      if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
+#ifdef DEBUG
+	fprintf(stderr,"shmat failed to update value\n");
+#endif
+      }else{
+	shmdt(shm);
+	shmctl(shmid,IPC_RMID,&shmids);
+      }
+    }
+  }
+}
+
+void updateStatus(maps *conf){
+  int shmid,i;
+  key_t key;
+  char *shm,*s,*s1;
+  map *tmpMap=NULL;
+  tmpMap=getMapFromMaps(conf,"lenv","sid");
+  if(tmpMap!=NULL){
+    key=atoi(tmpMap->value);
+    if ((shmid = shmget(key, SHMSZ, IPC_CREAT | 0666)) < 0) {
+#ifdef DEBUG
+      fprintf(stderr,"shmget failed to update value\n");
+#endif
+    }else{
+      if ((shm = (char*) shmat(shmid, NULL, 0)) == (char *) -1) {
+#ifdef DEBUG
+	fprintf(stderr,"shmat failed to update value\n");
+#endif
+      }
+      else{
+	tmpMap=getMapFromMaps(conf,"lenv","status");
+	s1=shm;
+	for(s=tmpMap->value;s!=NULL;s++)
+	  *s1++=*s;
+	shmdt((void *)shm);
+      }
+    }
+  }
+}
+
+char* getStatus(int pid){
+  int shmid,i;
+  key_t key;
+  void *shm;
+  char *s;
+  key=pid;
+  if ((shmid = shmget(key, SHMSZ, 0666)) < 0) {
+#ifdef DEBUG
+    fprintf(stderr,"shmget failed in getStatus\n");
+#endif
+  }else{
+    if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
+#ifdef DEBUG
+      fprintf(stderr,"shmat failed in getStatus\n");
+#endif
+    }else{
+      return (char*)shm;
+    }
+  }
+  return "-1";
+}
+
+#endif
+
+#ifdef USE_JS
+
+JSBool
+JSUpdateStatus(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+  JS_MaybeGC(cx);
+  char *sid;
+  int istatus=0;
+  char *status=NULL;
+  maps *conf;
+  int i=0;
+  if(argc>2){
+#ifdef JS_DEBUG
+    fprintf(stderr,"Number of arguments used to call the function : %i",argc);
+#endif
+    return JS_FALSE;
+  }
+  conf=mapsFromJSObject(cx,argv[0]);
+  if(JS_ValueToInt32(cx,argv[1],&istatus)==JS_TRUE){
+    char tmpStatus[4];
+    sprintf(tmpStatus,"%i",istatus);
+    tmpStatus[3]=0;
+    status=strdup(tmpStatus);
+  }
+  if(getMapFromMaps(conf,"lenv","status")!=NULL){
+    if(status!=NULL)
+      setMapInMaps(conf,"lenv","status",status);
+    else
+      setMapInMaps(conf,"lenv","status","15");
+    updateStatus(conf);
+  }
+  freeMaps(&conf);
+  free(conf);
+  JS_MaybeGC(cx);
+  return JS_TRUE;
+}
+
+#endif
+
+
+
+/* Returns a url-encoded version of str */
+/* IMPORTANT: be sure to free() the returned string after use */
+char *url_encode(char *str) {
+  char *pstr = str, *buf = (char*) malloc(strlen(str) * 3 + 1), *pbuf = buf;
+  while (*pstr) {
+    if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') 
+      *pbuf++ = *pstr;
+    else if (*pstr == ' ') 
+      *pbuf++ = '+';
+    else 
+      *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
+    pstr++;
+  }
+  *pbuf = '\0';
+  return buf;
+}
+
+/* Returns a url-decoded version of str */
+/* IMPORTANT: be sure to free() the returned string after use */
+char *url_decode(char *str) {
+  char *pstr = str, *buf = (char*) malloc(strlen(str) + 1), *pbuf = buf;
+  while (*pstr) {
+    if (*pstr == '%') {
+      if (pstr[1] && pstr[2]) {
+        *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
+        pstr += 2;
+      }
+    } else if (*pstr == '+') { 
+      *pbuf++ = ' ';
+    } else {
+      *pbuf++ = *pstr;
+    }
+    pstr++;
+  }
+  *pbuf = '\0';
+  return buf;
+}
+
+char *zCapitalize1(char *tmp){
+        char *res=strdup(tmp);
+        if(res[0]>=97 && res[0]<=122)
+                res[0]-=32;
+        return res;
+}
+
+char *zCapitalize(char *tmp){
+  int i=0;
+  char *res=strdup(tmp);
+  for(i=0;i<strlen(res);i++)
+    if(res[i]>=97 && res[i]<=122)
+      res[i]-=32;
+  return res;
+}
+
+
+int zooXmlSearchForNs(const char* name){
+  int i;
+  int res=-1;
+  for(i=0;i<nbNs;i++)
+    if(strncasecmp(name,nsName[i],strlen(nsName[i]))==0){
+      res=i;
+      break;
+    }
+  return res;
+}
+
+int zooXmlAddNs(xmlNodePtr nr,const char* url,const char* name){
+#ifdef DEBUG
+  fprintf(stderr,"zooXmlAddNs %d \n",nbNs);
+#endif
+  int currId=-1;
+  if(nbNs==0){
+    nbNs++;
+    currId=0;
+    nsName[currId]=strdup(name);
+    usedNs[currId]=xmlNewNs(nr,BAD_CAST url,BAD_CAST name);
+  }else{
+    currId=zooXmlSearchForNs(name);
+    if(currId<0){
+      nbNs++;
+      currId=nbNs-1;
+      nsName[currId]=strdup(name);
+      usedNs[currId]=xmlNewNs(nr,BAD_CAST url,BAD_CAST name);
+    }
+  }
+  return currId;
+}
+
+void zooXmlCleanupNs(){
+  int j;
+#ifdef DEBUG
+  fprintf(stderr,"zooXmlCleanup %d\n",nbNs);
+#endif
+  for(j=nbNs-1;j>=0;j--){
+#ifdef DEBUG
+    fprintf(stderr,"zooXmlCleanup %d\n",j);
+#endif
+    if(j==0)
+      xmlFreeNs(usedNs[j]);
+    free(nsName[j]);
+    nbNs--;
+  }
+  nbNs=0;
+}
+
+xmlNodePtr printGetCapabilitiesHeader(xmlDocPtr doc,const char* service,maps* m){
+
+  xmlNsPtr ns,ns_ows,ns_xlink,ns_xsi;
+  xmlNodePtr n,nc,nc1,nc2,nc3,nc4,nc5,nc6,pseudor;
+  xmlChar *xmlbuff;
+  int buffersize;
+  /**
+   * Create the document and its temporary root.
+   */
+  int wpsId=zooXmlAddNs(NULL,"http://www.opengis.net/wps/1.0.0","wps");
+  ns=usedNs[wpsId];
+  maps* toto1=getMaps(m,"main");
+
+  n = xmlNewNode(ns, BAD_CAST "Capabilities");
+  int owsId=zooXmlAddNs(n,"http://www.opengis.net/ows/1.1","ows");
+  ns_ows=usedNs[owsId];
+  xmlNewNs(n,BAD_CAST "http://www.opengis.net/wps/1.0.0",BAD_CAST "wps");
+  int xsiId=zooXmlAddNs(n,"http://www.w3.org/2001/XMLSchema-instance","xsi");
+  ns_xsi=usedNs[xsiId];
+  int xlinkId=zooXmlAddNs(n,"http://www.w3.org/1999/xlink","xlink");
+  ns_xlink=usedNs[xlinkId];
+  xmlNewNsProp(n,ns_xsi,BAD_CAST "schemaLocation",BAD_CAST "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsGetCapabilities_response.xsd"); 
+  xmlNewProp(n,BAD_CAST "service",BAD_CAST "WPS");
+  addLangAttr(n,m);
+  
+  if(toto1!=NULL){
+    map* tmp=getMap(toto1->content,"version");
+    if(tmp!=NULL){
+      xmlNewProp(n,BAD_CAST "version",BAD_CAST tmp->value);
+    }
+    else
+      xmlNewProp(n,BAD_CAST "version",BAD_CAST "1.0.0");
+  }
+  else
+    xmlNewProp(n,BAD_CAST "version",BAD_CAST "1.0.0");
+
+  char tmp[256];
+  
+  nc = xmlNewNode(ns_ows, BAD_CAST "ServiceIdentification");
+  maps* tmp4=getMaps(m,"identification");
+  if(tmp4!=NULL){
+    map* tmp2=tmp4->content;
+    char *orderedFields[5];
+    orderedFields[0]="Title";
+    orderedFields[1]="Abstract";
+    orderedFields[2]="Keywords";
+    orderedFields[3]="Fees";
+    orderedFields[4]="AccessConstraints";
+    int oI=0;
+    for(oI=0;oI<5;oI++)
+      if((tmp2=getMap(tmp4->content,orderedFields[oI]))!=NULL){
+	if(strcasecmp(tmp2->name,"abstract")==0 ||
+	   strcasecmp(tmp2->name,"title")==0 ||
+	   strcasecmp(tmp2->name,"accessConstraints")==0 ||
+	   strcasecmp(tmp2->name,"fees")==0){
+	  tmp2->name[0]=toupper(tmp2->name[0]);
+	  nc1 = xmlNewNode(ns_ows, BAD_CAST tmp2->name);
+	  xmlAddChild(nc1,xmlNewText(BAD_CAST tmp2->value));
+	  xmlAddChild(nc,nc1);
+	}
+	else
+	  if(strcmp(tmp2->name,"keywords")==0){
+	    nc1 = xmlNewNode(ns_ows, BAD_CAST "Keywords");
+	    char *toto=tmp2->value;
+	    char buff[256];
+	    int i=0;
+	    int j=0;
+	    while(toto[i]){
+	      if(toto[i]!=',' && toto[i]!=0){
+		buff[j]=toto[i];
+		buff[j+1]=0;
+		j++;
+	      }
+	      else{
+		nc2 = xmlNewNode(ns_ows, BAD_CAST "Keyword");
+		xmlAddChild(nc2,xmlNewText(BAD_CAST buff));	      
+		xmlAddChild(nc1,nc2);
+		j=0;
+	      }
+	      i++;
+	    }
+	    if(strlen(buff)>0){
+	      nc2 = xmlNewNode(ns_ows, BAD_CAST "Keyword");
+	      xmlAddChild(nc2,xmlNewText(BAD_CAST buff));	      
+	      xmlAddChild(nc1,nc2);
+	    }
+	    xmlAddChild(nc,nc1);
+	    nc2 = xmlNewNode(ns_ows, BAD_CAST "ServiceType");
+	    xmlAddChild(nc2,xmlNewText(BAD_CAST "WPS"));
+	    xmlAddChild(nc,nc2);
+	    nc2 = xmlNewNode(ns_ows, BAD_CAST "ServiceTypeVersion");
+	    xmlAddChild(nc2,xmlNewText(BAD_CAST "1.0.0"));
+	    xmlAddChild(nc,nc2);	  
+	  }
+	tmp2=tmp2->next;
+      }
+  }
+  else{
+    fprintf(stderr,"TMP4 NOT FOUND !!");
+    return NULL;
+  }
+  xmlAddChild(n,nc);
+
+  nc = xmlNewNode(ns_ows, BAD_CAST "ServiceProvider");
+  nc3 = xmlNewNode(ns_ows, BAD_CAST "ServiceContact");
+  nc4 = xmlNewNode(ns_ows, BAD_CAST "ContactInfo");
+  nc5 = xmlNewNode(ns_ows, BAD_CAST "Phone");
+  nc6 = xmlNewNode(ns_ows, BAD_CAST "Address");
+  tmp4=getMaps(m,"provider");
+  if(tmp4!=NULL){
+    map* tmp2=tmp4->content;
+    char *tmpAddress[6];
+    tmpAddress[0]="addressDeliveryPoint";
+    tmpAddress[1]="addressCity";
+    tmpAddress[2]="addressAdministrativeArea";
+    tmpAddress[3]="addressPostalCode";
+    tmpAddress[4]="addressCountry";
+    tmpAddress[5]="addressElectronicMailAddress";
+    char *tmpPhone[2];
+    tmpPhone[0]="phoneVoice";
+    tmpPhone[1]="phoneFacsimile";
+    char *orderedFields[12];
+    orderedFields[0]="providerName";
+    orderedFields[1]="providerSite";
+    orderedFields[2]="individualName";
+    orderedFields[3]="positionName";
+    orderedFields[4]=tmpPhone[0];
+    orderedFields[5]=tmpPhone[1];
+    orderedFields[6]=tmpAddress[0];
+    orderedFields[7]=tmpAddress[1];
+    orderedFields[8]=tmpAddress[2];
+    orderedFields[9]=tmpAddress[3];
+    orderedFields[10]=tmpAddress[4];
+    orderedFields[11]=tmpAddress[5];
+    int oI=0;
+    for(oI=0;oI<12;oI++)
+      if((tmp2=getMap(tmp4->content,orderedFields[oI]))!=NULL){
+	if(strcmp(tmp2->name,"keywords")!=0 &&
+	   strcmp(tmp2->name,"serverAddress")!=0 &&
+	   strcmp(tmp2->name,"lang")!=0){
+	  tmp2->name[0]=toupper(tmp2->name[0]);
+	  if(strcmp(tmp2->name,"ProviderName")==0){
+	    nc1 = xmlNewNode(ns_ows, BAD_CAST tmp2->name);
+	    xmlAddChild(nc1,xmlNewText(BAD_CAST tmp2->value));
+	    xmlAddChild(nc,nc1);
+	  }
+	  else{
+	    if(strcmp(tmp2->name,"ProviderSite")==0){
+	      nc1 = xmlNewNode(ns_ows, BAD_CAST tmp2->name);
+	      xmlNewNsProp(nc1,ns_xlink,BAD_CAST "href",BAD_CAST tmp2->value);
+	      xmlAddChild(nc,nc1);
+	    } 
+	    else  
+	      if(strcmp(tmp2->name,"IndividualName")==0 || 
+		 strcmp(tmp2->name,"PositionName")==0){
+		nc1 = xmlNewNode(ns_ows, BAD_CAST tmp2->name);
+		xmlAddChild(nc1,xmlNewText(BAD_CAST tmp2->value));
+		xmlAddChild(nc3,nc1);
+	      } 
+	      else 
+		if(strncmp(tmp2->name,"Phone",5)==0){
+		  int j;
+		  for(j=0;j<2;j++)
+		    if(strcasecmp(tmp2->name,tmpPhone[j])==0){
+		      char *toto=NULL;
+		      char *toto1=tmp2->name;
+		      toto=strstr(toto1,"Phone");
+		      nc1 = xmlNewNode(ns_ows, BAD_CAST toto1+5);
+		      xmlAddChild(nc1,xmlNewText(BAD_CAST tmp2->value));
+		      xmlAddChild(nc5,nc1);
+		    }
+		}
+		else 
+		  if(strncmp(tmp2->name,"Address",7)==0){
+		    int j;
+		    for(j=0;j<6;j++)
+		      if(strcasecmp(tmp2->name,tmpAddress[j])==0){
+			char *toto=NULL;
+			char *toto1=tmp2->name;
+			toto=strstr(toto1,"Address");
+			nc1 = xmlNewNode(ns_ows, BAD_CAST toto1+7);
+			xmlAddChild(nc1,xmlNewText(BAD_CAST tmp2->value));
+			xmlAddChild(nc6,nc1);
+		      }
+		  }
+	  }
+	}
+	else
+	  if(strcmp(tmp2->name,"keywords")==0){
+	    nc1 = xmlNewNode(ns_ows, BAD_CAST "Keywords");
+	    char *toto=tmp2->value;
+	    char buff[256];
+	    int i=0;
+	    int j=0;
+	    while(toto[i]){
+	      if(toto[i]!=',' && toto[i]!=0){
+		buff[j]=toto[i];
+		buff[j+1]=0;
+		j++;
+	      }
+	      else{
+		nc2 = xmlNewNode(ns_ows, BAD_CAST "Keyword");
+		xmlAddChild(nc2,xmlNewText(BAD_CAST buff));	      
+		xmlAddChild(nc1,nc2);
+		j=0;
+	      }
+	      i++;
+	    }
+	    if(strlen(buff)>0){
+	      nc2 = xmlNewNode(ns_ows, BAD_CAST "Keyword");
+	      xmlAddChild(nc2,xmlNewText(BAD_CAST buff));	      
+	      xmlAddChild(nc1,nc2);
+	    }
+	    xmlAddChild(nc,nc1);
+	  }
+	tmp2=tmp2->next;
+      }
+  }
+  else{
+    fprintf(stderr,"TMP4 NOT FOUND !!");
+  }
+  xmlAddChild(nc4,nc5);
+  xmlAddChild(nc4,nc6);
+  xmlAddChild(nc3,nc4);
+  xmlAddChild(nc,nc3);
+  xmlAddChild(n,nc);
+
+
+  nc = xmlNewNode(ns_ows, BAD_CAST "OperationsMetadata");
+  char *tmp2[3];
+  tmp2[0]=strdup("GetCapabilities");
+  tmp2[1]=strdup("DescribeProcess");
+  tmp2[2]=strdup("Execute");
+  int j=0;
+
+  if(toto1!=NULL){
+    map* tmp=getMap(toto1->content,"serverAddress");
+    if(tmp!=NULL){
+      SERVICE_URL = strdup(tmp->value);
+    }
+    else
+      SERVICE_URL = strdup("not_found");
+  }
+  else
+    SERVICE_URL = strdup("not_found");
+
+  for(j=0;j<3;j++){
+    nc1 = xmlNewNode(ns_ows, BAD_CAST "Operation");
+    xmlNewProp(nc1,BAD_CAST "name",BAD_CAST tmp2[j]);
+    nc2 = xmlNewNode(ns_ows, BAD_CAST "DCP");
+    nc3 = xmlNewNode(ns_ows, BAD_CAST "HTTP");
+    nc4 = xmlNewNode(ns_ows, BAD_CAST "Get");
+    sprintf(tmp,"%s/%s",SERVICE_URL,service);
+    xmlNewNsProp(nc4,ns_xlink,BAD_CAST "href",BAD_CAST tmp);
+    xmlAddChild(nc3,nc4);
+    if(j>0){
+      nc4 = xmlNewNode(ns_ows, BAD_CAST "Post");
+      xmlNewNsProp(nc4,ns_xlink,BAD_CAST "href",BAD_CAST tmp);
+      xmlAddChild(nc3,nc4);
+    }
+    xmlAddChild(nc2,nc3);
+    xmlAddChild(nc1,nc2);    
+    xmlAddChild(nc,nc1);    
+  }
+  for(j=2;j>=0;j--)
+    free(tmp2[j]);
+  xmlAddChild(n,nc);
+
+  nc = xmlNewNode(ns, BAD_CAST "ProcessOfferings");
+  xmlAddChild(n,nc);
+
+  nc1 = xmlNewNode(ns, BAD_CAST "Languages");
+  nc2 = xmlNewNode(ns, BAD_CAST "Default");
+  nc3 = xmlNewNode(ns, BAD_CAST "Supported");
+  
+  toto1=getMaps(m,"main");
+  if(toto1!=NULL){
+    map* tmp1=getMap(toto1->content,"lang");
+    char *toto=tmp1->value;
+    char buff[256];
+    int i=0;
+    int j=0;
+    int dcount=0;
+    while(toto[i]){
+      if(toto[i]!=',' && toto[i]!=0){
+	buff[j]=toto[i];
+	buff[j+1]=0;
+	j++;
+      }
+      else{
+	nc4 = xmlNewNode(ns_ows, BAD_CAST "Language");
+	xmlAddChild(nc4,xmlNewText(BAD_CAST buff));
+	if(dcount==0){
+	  xmlAddChild(nc2,nc4);
+	  xmlAddChild(nc1,nc2);
+	  dcount++;
+	}
+	nc4 = xmlNewNode(ns_ows, BAD_CAST "Language");
+	xmlAddChild(nc4,xmlNewText(BAD_CAST buff));
+	xmlAddChild(nc3,nc4);
+	j=0;
+	buff[j]=0;
+      }
+      i++;
+    }
+    if(strlen(buff)>0){
+      nc4 = xmlNewNode(ns_ows, BAD_CAST "Language");
+      xmlAddChild(nc4,xmlNewText(BAD_CAST buff));	      
+      xmlAddChild(nc3,nc4);
+    }
+  }
+  xmlAddChild(nc1,nc3);
+  xmlAddChild(n,nc1);
+  
+  xmlDocSetRootElement(doc, n);
+  //xmlFreeNs(ns);
+  free(SERVICE_URL);
+  return nc;
+}
+
+void printGetCapabilitiesForProcess(maps* m,xmlNodePtr nc,service* serv){
+  xmlNsPtr ns,ns_ows,ns_xlink;
+  xmlNodePtr nr,n,nc1,nc2,nc3,nc4,nc5,nc6,pseudor;
+  /**
+   * Initialize or get existing namspaces
+   */
+  int wpsId=zooXmlAddNs(NULL,"http://www.opengis.net/wps/1.0.0","wps");
+  ns=usedNs[wpsId];
+  int owsId=zooXmlAddNs(NULL,"http://www.opengis.net/ows/1.1","ows");
+  ns_ows=usedNs[owsId];
+  int xlinkId=zooXmlAddNs(n,"http://www.w3.org/1999/xlink","xlink");
+  ns_xlink=usedNs[xlinkId];
+
+  int cursor=0;
+  map* tmp1;
+  if(serv->content!=NULL){
+    nc1 = xmlNewNode(ns, BAD_CAST "Process");
+    tmp1=getMap(serv->content,"processVersion");
+    if(tmp1!=NULL)
+      xmlNewNsProp(nc1,ns,BAD_CAST "processVersion",BAD_CAST tmp1->value);
+    printDescription(nc1,ns_ows,serv->name,serv->content);
+    tmp1=serv->metadata;
+    while(tmp1!=NULL){
+      nc2 = xmlNewNode(ns_ows, BAD_CAST "Metadata");
+      xmlNewNsProp(nc2,ns_xlink,BAD_CAST tmp1->name,BAD_CAST tmp1->value);
+      xmlAddChild(nc1,nc2);
+      tmp1=tmp1->next;
+    }
+    xmlAddChild(nc,nc1);
+  }
+}
+
+xmlNodePtr printDescribeProcessHeader(xmlDocPtr doc,const char* service,maps* m){
+
+  xmlNsPtr ns,ns_ows,ns_xlink,ns_xsi;
+  xmlNodePtr n,nr;
+  xmlChar *xmlbuff;
+  int buffersize;
+
+  int wpsId=zooXmlAddNs(NULL,"http://schemas.opengis.net/wps/1.0.0","wps");
+  ns=usedNs[wpsId];
+  n = xmlNewNode(ns, BAD_CAST "ProcessDescriptions");
+  int owsId=zooXmlAddNs(n,"http://www.opengis.net/ows/1.1","ows");
+  ns_ows=usedNs[owsId];
+  xmlNewNs(n,BAD_CAST "http://www.opengis.net/wps/1.0.0",BAD_CAST "wps");
+  zooXmlAddNs(n,"http://www.w3.org/1999/xlink","xlink");
+  int xsiId=zooXmlAddNs(n,"http://www.w3.org/2001/XMLSchema-instance","xsi");
+  ns_xsi=usedNs[xsiId];
+  
+  xmlNewNsProp(n,ns_xsi,BAD_CAST "schemaLocation",BAD_CAST "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsDescribeProcess_response.xsd");
+  xmlNewProp(n,BAD_CAST "service",BAD_CAST "WPS");
+  xmlNewProp(n,BAD_CAST "version",BAD_CAST "1.0.0");
+  addLangAttr(n,m);
+
+  xmlDocSetRootElement(doc, n);
+
+  return n;
+}
+
+void printDescribeProcessForProcess(maps* m,xmlNodePtr nc,service* serv,int sc){
+  xmlNsPtr ns,ns_ows,ns_xlink,ns_xsi;
+  xmlNodePtr nr,n,nc1,nc2,nc3,nc4,nc5,nc6,pseudor;
+
+  char tmp[256];
+  n=nc;
+  
+  int wpsId=zooXmlAddNs(NULL,"http://schemas.opengis.net/wps/1.0.0","wps");
+  ns=usedNs[wpsId];
+  int owsId=zooXmlAddNs(NULL,"http://www.opengis.net/ows/1.1","ows");
+  ns_ows=usedNs[owsId];
+  int xlinkId=zooXmlAddNs(NULL,"http://www.w3.org/1999/xlink","xlink");
+  ns_xlink=usedNs[xlinkId];
+
+  nc = xmlNewNode(NULL, BAD_CAST "ProcessDescription");
+  char *tmp4[3];
+  tmp4[0]="processVersion";
+  tmp4[1]="storeSupported";
+  tmp4[2]="statusSupported";
+  int j=0;
+  map* tmp1=NULL;
+  for(j=0;j<3;j++){
+    tmp1=getMap(serv->content,tmp4[j]);
+    if(tmp1!=NULL){
+      if(j==0)
+	xmlNewNsProp(nc,ns,BAD_CAST "processVersion",BAD_CAST tmp1->value);      
+      else
+	xmlNewProp(nc,BAD_CAST tmp4[j],BAD_CAST tmp1->value);      
+    }
+    else{
+      if(j>0)
+	xmlNewProp(nc,BAD_CAST tmp4[j],BAD_CAST "false");      
+    }
+  }
+  
+  printDescription(nc,ns_ows,serv->name,serv->content);
+
+  tmp1=serv->metadata;
+  while(tmp1!=NULL){
+    nc1 = xmlNewNode(ns_ows, BAD_CAST "Metadata");
+    xmlNewNsProp(nc1,ns_xlink,BAD_CAST tmp1->name,BAD_CAST tmp1->value);
+    xmlAddChild(nc,nc1);
+    tmp1=tmp1->next;
+  }
+
+  tmp1=getMap(serv->content,"Profile");
+  if(tmp1!=NULL){
+    nc1 = xmlNewNode(ns, BAD_CAST "Profile");
+    xmlAddChild(nc1,xmlNewText(BAD_CAST tmp1->value));
+    xmlAddChild(nc,nc1);
+  }
+
+  nc1 = xmlNewNode(NULL, BAD_CAST "DataInputs");
+  elements* e=serv->inputs;
+  printFullDescription(e,"Input",ns_ows,nc1);
+  xmlAddChild(nc,nc1);
+
+  nc1 = xmlNewNode(NULL, BAD_CAST "ProcessOutputs");
+  e=serv->outputs;
+  printFullDescription(e,"Output",ns_ows,nc1);
+  xmlAddChild(nc,nc1);
+
+  xmlAddChild(n,nc);
+
+}
+
+void printFullDescription(elements *elem,const char* type,xmlNsPtr ns_ows,xmlNodePtr nc1){
+  char *orderedFields[7];
+  orderedFields[0]="mimeType";
+  orderedFields[1]="encoding";
+  orderedFields[2]="schema";
+  orderedFields[3]="dataType";
+  orderedFields[4]="uom";
+  orderedFields[5]="CRS";
+  orderedFields[6]="value";
+
+  xmlNodePtr nc2,nc3,nc4,nc5,nc6,nc7;
+  elements* e=elem;
+  map* tmp1=NULL;
+  while(e!=NULL){
+    int default1=0;
+    int isAnyValue=1;
+    nc2 = xmlNewNode(NULL, BAD_CAST type);
+    tmp1=getMap(e->content,"minOccurs");
+    if(tmp1){
+      xmlNewProp(nc2,BAD_CAST tmp1->name,BAD_CAST tmp1->value);
+    }
+    tmp1=getMap(e->content,"maxOccurs");
+    if(tmp1){
+      xmlNewProp(nc2,BAD_CAST tmp1->name,BAD_CAST tmp1->value);
+    }
+
+    printDescription(nc2,ns_ows,e->name,e->content);
+
+    if(strncmp(type,"Output",6)==0){
+      if(strncasecmp(e->format,"LITERALDATA",strlen(e->format))==0)
+	nc3 = xmlNewNode(NULL, BAD_CAST "LiteralOutput");
+      else if(strncasecmp(e->format,"COMPLEXDATA",strlen(e->format))==0)
+	nc3 = xmlNewNode(NULL, BAD_CAST "ComplexOutput");
+      else if(strncasecmp(e->format,"BOUNDINGBOXDATA",strlen(e->format))==0)
+	nc3 = xmlNewNode(NULL, BAD_CAST "BoundingBoxOutput");
+      else
+	nc3 = xmlNewNode(NULL, BAD_CAST e->format);
+    }else{
+      if(strncasecmp(e->format,"LITERALDATA",strlen(e->format))==0){
+	nc3 = xmlNewNode(NULL, BAD_CAST "LiteralData");
+      }
+      else if(strncasecmp(e->format,"COMPLEXDATA",strlen(e->format))==0)
+	nc3 = xmlNewNode(NULL, BAD_CAST "ComplexData");
+      else if(strncasecmp(e->format,"BOUNDINGBOXDATA",strlen(e->format))==0)
+	nc3 = xmlNewNode(NULL, BAD_CAST "BoundingBoxData");
+      else
+	nc3 = xmlNewNode(NULL, BAD_CAST e->format);
+    }
+    iotype* _tmp=e->defaults;
+    int datatype=0;
+    bool hasDefault=false;
+    bool hasUOM=false;
+    if(_tmp!=NULL){
+      if(strcmp(e->format,"LiteralOutput")==0 ||
+	 strcmp(e->format,"LiteralData")==0){
+     	datatype=1;
+	nc4 = xmlNewNode(NULL, BAD_CAST "UOMs");
+	nc5 = xmlNewNode(NULL, BAD_CAST "Default");
+      }
+      else if(strcmp(e->format,"BoundingBoxOutput")==0 ||
+	      strcmp(e->format,"BoundingBoxData")==0){
+	datatype=2;
+	//nc4 = xmlNewNode(NULL, BAD_CAST "BoundingBoxOutput");
+	nc5 = xmlNewNode(NULL, BAD_CAST "Default");
+      }
+      else{
+	nc4 = xmlNewNode(NULL, BAD_CAST "Default");
+	nc5 = xmlNewNode(NULL, BAD_CAST "Format");
+      }
+      
+      tmp1=_tmp->content;
+      int avcnt=0;
+      int dcnt=0;
+      int oI=0;
+      for(oI=0;oI<7;oI++)
+	if((tmp1=getMap(_tmp->content,orderedFields[oI]))!=NULL){
+	  //while(tmp1!=NULL){
+#ifdef DEBUG
+	  printf("DATATYPE DEFAULT ? %s\n",tmp1->name);
+#endif
+	  if(strncasecmp(tmp1->name,"DataType",8)==0){
+	    nc6 = xmlNewNode(ns_ows, BAD_CAST "DataType");
+	    xmlAddChild(nc6,xmlNewText(BAD_CAST tmp1->value));
+	    char tmp[1024];
+	    sprintf(tmp,"http://www.w3.org/TR/xmlschema-2/#%s",tmp1->value);
+	    xmlNewNsProp(nc6,ns_ows,BAD_CAST "reference",BAD_CAST tmp);
+	    xmlAddChild(nc3,nc6);
+	    tmp1=tmp1->next;
+	    datatype=1;
+	    continue;
+	  }
+	  if(strcmp(tmp1->name,"asReference")!=0 &&
+	     strncasecmp(tmp1->name,"DataType",8)!=0 &&
+	     strcasecmp(tmp1->name,"extension")!=0 &&
+	     strcasecmp(tmp1->name,"value")!=0 &&
+	     strncasecmp(tmp1->name,"AllowedValues",13)!=0){
+	    if(datatype!=1){
+	      char *tmp2=zCapitalize1(tmp1->name);
+	      nc6 = xmlNewNode(NULL, BAD_CAST tmp2);
+	      free(tmp2);
+	    }
+	    else{
+	      char *tmp2=zCapitalize(tmp1->name);
+	      nc6 = xmlNewNode(ns_ows, BAD_CAST tmp2);
+	      free(tmp2);
+	    }
+	    xmlAddChild(nc6,xmlNewText(BAD_CAST tmp1->value));
+	    xmlAddChild(nc5,nc6);
+	    hasUOM=true;
+	  }else 
+	    if(strncmp(type,"Input",5)==0){
+	      if(strcmp(tmp1->name,"value")==0){
+		nc7 = xmlNewNode(NULL, BAD_CAST "DefaultValue");
+		xmlAddChild(nc7,xmlNewText(BAD_CAST tmp1->value));
+		default1=1;
+	      }
+	      if(strncasecmp(tmp1->name,"AllowedValues",13)==0){
+		nc6 = xmlNewNode(ns_ows, BAD_CAST "AllowedValues");
+		fprintf(stderr,"ALLOWED VALUE %s\n",tmp1->value);
+		char *token,*saveptr1;
+		token=strtok_r(tmp1->value,",",&saveptr1);
+		while(token!=NULL){
+		  nc7 = xmlNewNode(ns_ows, BAD_CAST "Value");
+		  char *tmps=strdup(token);
+		  tmps[strlen(tmps)]=0;
+		  xmlAddChild(nc7,xmlNewText(BAD_CAST tmps));
+		  fprintf(stderr,"strgin : %s\n",tmps);
+		  xmlAddChild(nc6,nc7);
+		  token=strtok_r(NULL,",",&saveptr1);
+		}
+		xmlAddChild(nc3,nc6);
+		isAnyValue=-1;
+	      }
+	      hasDefault=true;
+	    }
+	  tmp1=tmp1->next;
+	  if(datatype!=2){
+	    if(hasUOM==true){
+	      xmlAddChild(nc4,nc5);
+	      xmlAddChild(nc3,nc4);
+	    }
+	  }else{
+	    xmlAddChild(nc3,nc5);
+	  }
+	 
+	  if(strncmp(type,"Input",5)==0){
+	    if(datatype==1 && isAnyValue==1 && avcnt==0){
+	      xmlAddChild(nc3,xmlNewNode(ns_ows, BAD_CAST "AnyValue"));
+	      hasDefault=true;
+	      avcnt++;
+	    }
+	    if(datatype==1 && default1>0){
+	      xmlAddChild(nc3,nc7);
+	    }
+	  }
+	}
+    }
+    _tmp=e->supported;
+    int hasSupported=-1;
+    while(_tmp!=NULL){
+      if(hasSupported<0){
+	if(datatype==0){
+	  nc4 = xmlNewNode(NULL, BAD_CAST "Supported");
+	  nc5 = xmlNewNode(NULL, BAD_CAST "Format");
+	}
+	else
+	  nc5 = xmlNewNode(NULL, BAD_CAST "Supported");
+	hasSupported=0;
+      }else
+	if(datatype==0)
+	  nc5 = xmlNewNode(NULL, BAD_CAST "Format");
+      tmp1=_tmp->content;
+      int oI=0;
+      for(oI=0;oI<6;oI++)
+	if((tmp1=getMap(_tmp->content,orderedFields[oI]))!=NULL){
+#ifdef DEBUG
+	  printf("DATATYPE SUPPORTED ? %s\n",tmp1->name);
+#endif
+	  if(strcmp(tmp1->name,"asReference")!=0 && 
+	     strcmp(tmp1->name,"DataType")!=0 &&
+	     strcasecmp(tmp1->name,"extension")!=0){
+	    if(datatype!=1){
+	      char *tmp2=zCapitalize1(tmp1->name);
+	      nc6 = xmlNewNode(NULL, BAD_CAST tmp2);
+	      free(tmp2);
+	    }
+	    else{
+	      char *tmp2=zCapitalize(tmp1->name);
+	      nc6 = xmlNewNode(ns_ows, BAD_CAST tmp2);
+	      free(tmp2);
+	    }
+	    if(datatype==2){
+	      char *tmpv,*tmps;
+	      tmps=strtok_r(tmp1->value,",",&tmpv);
+	      while(tmps){
+		xmlAddChild(nc6,xmlNewText(BAD_CAST tmps));
+		xmlAddChild(nc5,nc6);
+		tmps=strtok_r(NULL,",",&tmpv);
+		if(tmps){
+		  char *tmp2=zCapitalize1(tmp1->name);
+		  nc6 = xmlNewNode(NULL, BAD_CAST tmp2);
+		  free(tmp2);
+		}
+	      }
+	    }
+	    else{
+	      xmlAddChild(nc6,xmlNewText(BAD_CAST tmp1->value));
+	      xmlAddChild(nc5,nc6);
+	    }
+	  }
+	  tmp1=tmp1->next;
+	}
+      if(hasSupported<=0){
+	if(datatype!=2){
+	  xmlAddChild(nc4,nc5);
+	  xmlAddChild(nc3,nc4);
+	}else
+	  xmlAddChild(nc3,nc5);
+	hasSupported=1;
+      }
+      else
+	if(datatype!=2){
+	  xmlAddChild(nc4,nc5);
+	}
+	else
+	  xmlAddChild(nc3,nc5);
+      _tmp=_tmp->next;
+    }
+    xmlAddChild(nc2,nc3);
+    
+    if(datatype!=2 && hasUOM==true){
+      xmlAddChild(nc3,nc4);
+      xmlAddChild(nc2,nc3);
+    }else if(datatype!=2){
+      if(hasDefault!=true && strncmp(type,"Input",5)==0)
+	xmlAddChild(nc3,xmlNewNode(ns_ows, BAD_CAST "AnyValue"));
+      xmlFreeNodeList(nc5);
+      xmlFreeNodeList(nc4);
+    }
+    
+    xmlAddChild(nc1,nc2);
+    
+    e=e->next;
+  }
+}
+
+void printProcessResponse(maps* m,map* request, int pid,service* serv,const char* service,int status,maps* inputs,maps* outputs){
+  xmlNsPtr ns,ns_ows,ns_xlink,ns_xsi;
+  xmlNodePtr nr,n,nc,nc1,nc2,nc3,pseudor;
+  xmlDocPtr doc;
+  xmlChar *xmlbuff;
+  int buffersize;
+  time_t time1;  
+  time(&time1);
+  /**
+   * Create the document and its temporary root.
+   */
+  doc = xmlNewDoc(BAD_CAST "1.0");
+  int wpsId=zooXmlAddNs(NULL,"http://www.opengis.net/wps/1.0.0","wps");
+  ns=usedNs[wpsId];
+  
+  n = xmlNewNode(ns, BAD_CAST "ExecuteResponse");
+  int owsId=zooXmlAddNs(n,"http://www.opengis.net/ows/1.1","ows");
+  ns_ows=usedNs[owsId];
+  int xlinkId=zooXmlAddNs(n,"http://www.w3.org/1999/xlink","xlink");
+  ns_xlink=usedNs[xlinkId];
+  int xsiId=zooXmlAddNs(n,"http://www.w3.org/2001/XMLSchema-instance","xsi");
+  ns_xsi=usedNs[xsiId];
+  xmlNewNs(n,BAD_CAST "http://www.opengis.net/wps/1.0.0",BAD_CAST "wps");
+
+  xmlNewNsProp(n,ns_xsi,BAD_CAST "schemaLocation",BAD_CAST "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd");
+  
+  xmlNewProp(n,BAD_CAST "service",BAD_CAST "WPS");
+  xmlNewProp(n,BAD_CAST "version",BAD_CAST "1.0.0");
+  addLangAttr(n,m);
+
+  char tmp[256];
+  char url[1024];
+  char stored_path[1024];
+  memset(tmp,0,256);
+  memset(url,0,1024);
+  memset(stored_path,0,1024);
+  maps* tmp_maps=getMaps(m,"main");
+  if(tmp_maps!=NULL){
+    map* tmpm1=getMap(tmp_maps->content,"serverAddress");
+    /**
+     * Check if the ZOO Service GetStatus is available in the local directory.
+     * If yes, then it uses a reference to an URL which the client can access
+     * to get information on the status of a running Service (using the 
+     * percentCompleted attribute). 
+     * Else fallback to the initial method using the xml file to write in ...
+     */
+    char ntmp[1024];
+#ifndef WIN32
+    getcwd(ntmp,1024);
+#else
+    _getcwd(ntmp,1024);
+#endif
+    struct stat myFileInfo;
+    int statRes;
+    char file_path[1024];
+    sprintf(file_path,"%s/GetStatus.zcfg",ntmp);
+    statRes=stat(file_path,&myFileInfo);
+    if(statRes==0){
+      char currentSid[128];
+      map* tmpm=getMap(tmp_maps->content,"rewriteUrl");
+      map *tmp_lenv=NULL;
+      tmp_lenv=getMapFromMaps(m,"lenv","sid");
+      if(tmp_lenv==NULL)
+	sprintf(currentSid,"%i",pid);
+      else
+	sprintf(currentSid,"%s",tmp_lenv->value);
+      if(tmpm==NULL || strcasecmp(tmpm->value,"false")==0){
+	sprintf(url,"%s/?request=Execute&service=WPS&version=1.0.0&Identifier=GetStatus&DataInputs=sid=%s&RawDataOutput=Result",tmpm1->value,currentSid);
+      }else{
+	if(strlen(tmpm->value)>0)
+	  if(strcasecmp(tmpm->value,"true")!=0)
+	    sprintf(url,"%s/%s/GetStatus/%s",tmpm1->value,tmpm->value,currentSid);
+	  else
+	    sprintf(url,"%s/GetStatus/%s",tmpm1->value,currentSid);
+	else
+	  sprintf(url,"%s/?request=Execute&service=WPS&version=1.0.0&Identifier=GetStatus&DataInputs=sid=%s&RawDataOutput=Result",tmpm1->value,currentSid);
+      }
+    }else{
+      map* tmpm2=getMap(tmp_maps->content,"tmpUrl");
+      if(tmpm1!=NULL && tmpm2!=NULL){
+	sprintf(url,"%s/%s/%s_%i.xml",tmpm1->value,tmpm2->value,service,pid);
+      }
+    }
+    if(tmpm1!=NULL)
+      sprintf(tmp,"%s/",tmpm1->value);
+    tmpm1=getMapFromMaps(m,"main","TmpPath");
+    sprintf(stored_path,"%s/%s_%i.xml",tmpm1->value,service,pid);
+  }
+
+  
+
+  xmlNewProp(n,BAD_CAST "serviceInstance",BAD_CAST tmp);
+  map* test=getMap(request,"storeExecuteResponse");
+  bool hasStoredExecuteResponse=false;
+  if(test!=NULL && strcasecmp(test->value,"true")==0){
+    xmlNewProp(n,BAD_CAST "statusLocation",BAD_CAST url);
+    hasStoredExecuteResponse=true;
+  }
+
+  nc = xmlNewNode(ns, BAD_CAST "Process");
+  map* tmp2=getMap(serv->content,"processVersion");
+
+  if(tmp2!=NULL)
+    xmlNewNsProp(nc,ns,BAD_CAST "processVersion",BAD_CAST tmp2->value);
+  
+  printDescription(nc,ns_ows,serv->name,serv->content);
+  fflush(stderr);
+
+  xmlAddChild(n,nc);
+
+  nc = xmlNewNode(ns, BAD_CAST "Status");
+  const struct tm *tm;
+  size_t len;
+  time_t now;
+  char *tmp1;
+  map *tmpStatus;
+  
+  now = time ( NULL );
+  tm = localtime ( &now );
+
+  tmp1 = (char*)malloc((TIME_SIZE+1)*sizeof(char));
+
+  len = strftime ( tmp1, TIME_SIZE, "%Y-%m-%dT%I:%M:%SZ", tm );
+
+  xmlNewProp(nc,BAD_CAST "creationTime",BAD_CAST tmp1);
+
+  char sMsg[2048];
+  switch(status){
+  case SERVICE_SUCCEEDED:
+    nc1 = xmlNewNode(ns, BAD_CAST "ProcessSucceeded");
+    sprintf(sMsg,_("Service \"%s\" run successfully."),serv->name);
+    nc3=xmlNewText(BAD_CAST sMsg);
+    xmlAddChild(nc1,nc3);
+    break;
+  case SERVICE_STARTED:
+    nc1 = xmlNewNode(ns, BAD_CAST "ProcessStarted");
+    tmpStatus=getMapFromMaps(m,"lenv","status");
+    xmlNewProp(nc1,BAD_CAST "percentCompleted",BAD_CAST tmpStatus->value);
+    sprintf(sMsg,_("ZOO Service \"%s\" is currently running. Please, reload this document to get the up-to-date status of the Service."),serv->name);
+    nc3=xmlNewText(BAD_CAST sMsg);
+    xmlAddChild(nc1,nc3);
+    break;
+  case SERVICE_ACCEPTED:
+    nc1 = xmlNewNode(ns, BAD_CAST "ProcessAccepted");
+    sprintf(sMsg,_("Service \"%s\" was accepted by the ZOO Kernel and it run as a background task. Please consult the statusLocation attribtue providen in this document to get the up-to-date document."),serv->name);
+    nc3=xmlNewText(BAD_CAST sMsg);
+    xmlAddChild(nc1,nc3);
+    break;
+  case SERVICE_FAILED:
+    nc1 = xmlNewNode(ns, BAD_CAST "ProcessFailed");
+    map *errorMap;
+    map *te;
+    te=getMapFromMaps(m,"lenv","code");
+    if(te!=NULL)
+      errorMap=createMap("code",te->value);
+    else
+      errorMap=createMap("code","NoApplicableCode");
+    te=getMapFromMaps(m,"lenv","message");
+    if(te!=NULL)
+      addToMap(errorMap,"text",_ss(te->value));
+    else
+      addToMap(errorMap,"text",_("No more information available"));
+    nc3=createExceptionReportNode(m,errorMap,0);
+    freeMap(&errorMap);
+    free(errorMap);
+    xmlAddChild(nc1,nc3);
+    break;
+  default :
+    printf(_("error code not know : %i\n"),status);
+    //exit(1);
+    break;
+  }
+  xmlAddChild(nc,nc1);
+  xmlAddChild(n,nc);
+  free(tmp1);
+
+#ifdef DEBUG
+  fprintf(stderr,"printProcessResponse 1 161\n");
+#endif
+
+  map* lineage=getMap(request,"lineage");
+  if(lineage!=NULL && strcasecmp(lineage->value,"true")==0){
+    nc = xmlNewNode(ns, BAD_CAST "DataInputs");
+    int i;
+    maps* mcursor=inputs;
+    elements* scursor=NULL;
+    while(mcursor!=NULL /*&& scursor!=NULL*/){
+      scursor=getElements(serv->inputs,mcursor->name);
+      printIOType(doc,nc,ns,ns_ows,ns_xlink,scursor,mcursor,"Input");
+      mcursor=mcursor->next;
+    }
+    xmlAddChild(n,nc);
+    
+#ifdef DEBUG
+    fprintf(stderr,"printProcessResponse 1 177\n");
+#endif
+
+    nc = xmlNewNode(ns, BAD_CAST "OutputDefinitions");
+    mcursor=outputs;
+    scursor=NULL;
+    while(mcursor!=NULL){
+      scursor=getElements(serv->outputs,mcursor->name);
+      printOutputDefinitions1(doc,nc,ns,ns_ows,scursor,mcursor,"Output");
+      mcursor=mcursor->next;
+    }
+    xmlAddChild(n,nc);
+  }
+#ifdef DEBUG
+  fprintf(stderr,"printProcessResponse 1 190\n");
+#endif
+
+  /**
+   * Display the process output only when requested !
+   */
+  if(status==SERVICE_SUCCEEDED){
+    nc = xmlNewNode(ns, BAD_CAST "ProcessOutputs");
+    maps* mcursor=outputs;
+    elements* scursor=serv->outputs;
+    while(mcursor!=NULL){
+      scursor=getElements(serv->outputs,mcursor->name);
+      printIOType(doc,nc,ns,ns_ows,ns_xlink,scursor,mcursor,"Output");
+      mcursor=mcursor->next;
+    }
+    xmlAddChild(n,nc);
+  }
+#ifdef DEBUG
+  fprintf(stderr,"printProcessResponse 1 202\n");
+#endif
+  xmlDocSetRootElement(doc, n);
+  if(hasStoredExecuteResponse==true){
+    /* We need to write the ExecuteResponse Document somewhere */
+    FILE* output=fopen(stored_path,"w");
+    xmlChar *xmlbuff;
+    int buffersize;
+    xmlDocDumpFormatMemoryEnc(doc, &xmlbuff, &buffersize, "UTF-8", 1);
+    fwrite(xmlbuff,1,xmlStrlen(xmlbuff)*sizeof(char),output);
+    xmlFree(xmlbuff);
+    fclose(output);
+  }
+  printDocument(m,doc,pid);
+
+  xmlCleanupParser();
+  zooXmlCleanupNs();
+}
+
+
+void printDocument(maps* m, xmlDocPtr doc,int pid){
+  char *encoding=getEncoding(m);
+  if(pid==getpid()){
+    printf("Content-Type: text/xml; charset=%s\r\nStatus: 200 OK\r\n\r\n",encoding);
+  }
+  fflush(stdout);
+  xmlChar *xmlbuff;
+  int buffersize;
+  /*
+   * Dump the document to a buffer and print it on stdout
+   * for demonstration purposes.
+   */
+  xmlDocDumpFormatMemoryEnc(doc, &xmlbuff, &buffersize, encoding, 1);
+  printf("%s",xmlbuff);
+  fflush(stdout);
+  /*
+   * Free associated memory.
+   */
+  xmlFree(xmlbuff);
+  xmlFreeDoc(doc);
+  xmlCleanupParser();
+  zooXmlCleanupNs();
+}
+
+void printOutputDefinitions1(xmlDocPtr doc,xmlNodePtr nc,xmlNsPtr ns_wps,xmlNsPtr ns_ows,elements* e,maps* m,const char* type){
+  xmlNodePtr nc1;
+  nc1=xmlNewNode(ns_wps, BAD_CAST type);
+  map *tmp=NULL;  
+  if(e!=NULL && e->defaults!=NULL)
+    tmp=e->defaults->content;
+  else{
+    /*
+    dumpElements(e);
+    */
+    return;
+  }
+  while(tmp!=NULL){
+    if(strncasecmp(tmp->name,"MIMETYPE",strlen(tmp->name))==0
+       || strncasecmp(tmp->name,"ENCODING",strlen(tmp->name))==0
+       || strncasecmp(tmp->name,"SCHEMA",strlen(tmp->name))==0
+       || strncasecmp(tmp->name,"UOM",strlen(tmp->name))==0)
+    xmlNewProp(nc1,BAD_CAST tmp->name,BAD_CAST tmp->value);
+    tmp=tmp->next;
+  }
+  tmp=getMap(e->defaults->content,"asReference");
+  if(tmp==NULL)
+    xmlNewProp(nc1,BAD_CAST "asReference",BAD_CAST "false");
+
+  tmp=e->content;
+
+  printDescription(nc1,ns_ows,m->name,e->content);
+
+  xmlAddChild(nc,nc1);
+
+}
+
+void printOutputDefinitions(xmlDocPtr doc,xmlNodePtr nc,xmlNsPtr ns_wps,xmlNsPtr ns_ows,elements* e,map* m,const char* type){
+  xmlNodePtr nc1,nc2,nc3;
+  nc1=xmlNewNode(ns_wps, BAD_CAST type);
+  map *tmp=NULL;  
+  if(e!=NULL && e->defaults!=NULL)
+    tmp=e->defaults->content;
+  else{
+    /*
+    dumpElements(e);
+    */
+    return;
+  }
+  while(tmp!=NULL){
+    xmlNewProp(nc1,BAD_CAST tmp->name,BAD_CAST tmp->value);
+    tmp=tmp->next;
+  }
+  tmp=getMap(e->defaults->content,"asReference");
+  if(tmp==NULL)
+    xmlNewProp(nc1,BAD_CAST "asReference",BAD_CAST "false");
+
+  tmp=e->content;
+
+  printDescription(nc1,ns_ows,m->name,e->content);
+
+  xmlAddChild(nc,nc1);
+
+}
+
+void printIOType(xmlDocPtr doc,xmlNodePtr nc,xmlNsPtr ns_wps,xmlNsPtr ns_ows,xmlNsPtr ns_xlink,elements* e,maps* m,const char* type){
+  xmlNodePtr nc1,nc2,nc3;
+  nc1=xmlNewNode(ns_wps, BAD_CAST type);
+  map *tmp=NULL;
+  if(e!=NULL)
+    tmp=e->content;
+  else
+    tmp=m->content;
+#ifdef DEBUG
+  dumpMap(tmp);
+  dumpElements(e);
+#endif
+  nc2=xmlNewNode(ns_ows, BAD_CAST "Identifier");
+  if(e!=NULL)
+    nc3=xmlNewText(BAD_CAST e->name);
+  else
+    nc3=xmlNewText(BAD_CAST m->name);
+  xmlAddChild(nc2,nc3);
+  xmlAddChild(nc1,nc2);
+  xmlAddChild(nc,nc1);
+  // Extract Title required to be first element in the ZCFG file !
+  bool isTitle=true;
+  if(e!=NULL)
+    tmp=getMap(e->content,"Title");
+  else
+    tmp=getMap(m->content,"Title");
+  
+  if(tmp!=NULL){
+    nc2=xmlNewNode(ns_ows, BAD_CAST tmp->name);
+    nc3=xmlNewText(BAD_CAST _ss(tmp->value));
+    xmlAddChild(nc2,nc3);  
+    xmlAddChild(nc1,nc2);
+  }
+
+  if(e!=NULL)
+    tmp=getMap(e->content,"Abstract");
+  else
+    tmp=getMap(m->content,"Abstract");
+  if(tmp!=NULL){
+    nc2=xmlNewNode(ns_ows, BAD_CAST tmp->name);
+    nc3=xmlNewText(BAD_CAST _ss(tmp->value));
+    xmlAddChild(nc2,nc3);  
+    xmlAddChild(nc1,nc2);
+    xmlAddChild(nc,nc1);
+  }
+
+  /**
+   * IO type Reference or full Data ?
+   */
+#ifdef DEBUG
+  fprintf(stderr,"FORMAT %s %s\n",e->format,e->format);
+#endif
+  map *tmpMap=getMap(m->content,"Reference");
+  if(tmpMap==NULL){
+    nc2=xmlNewNode(ns_wps, BAD_CAST "Data");
+    if(e!=NULL){
+      if(strncasecmp(e->format,"LiteralOutput",strlen(e->format))==0)
+	nc3=xmlNewNode(ns_wps, BAD_CAST "LiteralData");
+      else
+	if(strncasecmp(e->format,"ComplexOutput",strlen(e->format))==0)
+	  nc3=xmlNewNode(ns_wps, BAD_CAST "ComplexData");
+	else if(strncasecmp(e->format,"BoundingBoxOutput",strlen(e->format))==0)
+	  nc3=xmlNewNode(ns_wps, BAD_CAST "BoundingBoxData");
+	else
+	  nc3=xmlNewNode(ns_wps, BAD_CAST e->format);
+    }
+    else{
+      map* tmpV=getMapFromMaps(m,"format","value");
+      if(tmpV!=NULL)
+	nc3=xmlNewNode(ns_wps, BAD_CAST tmpV->value);
+      else
+	nc3=xmlNewNode(ns_wps, BAD_CAST "LitteralData");
+    } 
+    tmp=m->content;
+    while(tmp!=NULL){
+      if(strcasecmp(tmp->name,"mimeType")==0 ||
+	 strcasecmp(tmp->name,"encoding")==0 ||
+	 strcasecmp(tmp->name,"schema")==0 ||
+	 strcasecmp(tmp->name,"datatype")==0 ||
+	 strcasecmp(tmp->name,"uom")==0)
+	xmlNewProp(nc3,BAD_CAST tmp->name,BAD_CAST tmp->value);
+      tmp=tmp->next;
+      xmlAddChild(nc2,nc3);
+    }
+    if(e!=NULL && e->format!=NULL && strcasecmp(e->format,"BoundingBoxData")==0){
+      map* bb=getMap(m->content,"value");
+      if(bb!=NULL){
+	map* tmpRes=parseBoundingBox(bb->value);
+	printBoundingBox(ns_ows,nc3,tmpRes);
+	freeMap(&tmpRes);
+	free(tmpRes);
+      }
+    }else{
+      if(e!=NULL)
+	tmp=getMap(e->defaults->content,"mimeType");
+      else
+	tmp=NULL;
+      map* tmp1=getMap(m->content,"encoding");
+      map* tmp2=getMap(m->content,"mimeType");
+      map* toto=getMap(m->content,"value");
+      if((tmp1!=NULL && strncmp(tmp1->value,"base64",6)==0)
+	 || (tmp2!=NULL && (strncmp(tmp2->value,"image/",6)==0 ||
+			    (strncmp(tmp2->value,"application/",12)==0) &&
+			    strncmp(tmp2->value,"application/json",16)!=0))) {
+	map* rs=getMap(m->content,"size");
+	bool isSized=true;
+	if(rs==NULL){
+	  char tmp1[1024];
+	  sprintf(tmp1,"%d",strlen(toto->value));
+	  rs=createMap("z",tmp1);
+	  isSized=false;
+	}
+
+	xmlAddChild(nc3,xmlNewText(BAD_CAST base64(toto->value, atoi(rs->value))));
+	if(!isSized){
+	  freeMap(&rs);
+	  free(rs);
+	}
+      }
+      else if(tmp!=NULL){
+	if(strncmp(tmp->value,"text/js",4)==0 ||
+	   strncmp(tmp->value,"application/js",14)==0)
+	  xmlAddChild(nc3,xmlNewCDataBlock(doc,BAD_CAST toto->value,strlen(toto->value)));
+	else
+	  xmlAddChild(nc3,xmlNewText(BAD_CAST toto->value));
+	xmlAddChild(nc2,nc3);
+      }
+      else
+	xmlAddChild(nc3,xmlNewText(BAD_CAST toto->value));
+    }
+  }
+  else{
+    nc3=nc2=xmlNewNode(ns_wps, BAD_CAST "Reference");
+    if(strcasecmp(type,"Output")==0)
+      xmlNewProp(nc3,BAD_CAST "href",BAD_CAST tmpMap->value);
+    else
+      xmlNewNsProp(nc3,ns_xlink,BAD_CAST "href",BAD_CAST tmpMap->value);
+    tmp=m->content;
+    while(tmp!=NULL){
+      if(strcasecmp(tmp->name,"mimeType")==0 ||
+	 strcasecmp(tmp->name,"encoding")==0 ||
+	 strcasecmp(tmp->name,"schema")==0 ||
+	 strcasecmp(tmp->name,"datatype")==0 ||
+	 strcasecmp(tmp->name,"uom")==0)
+	xmlNewProp(nc3,BAD_CAST tmp->name,BAD_CAST tmp->value);
+      tmp=tmp->next;
+      xmlAddChild(nc2,nc3);
+    }
+  }
+
+  xmlAddChild(nc1,nc2);
+  xmlAddChild(nc,nc1);
+
+}
+
+void printDescription(xmlNodePtr root,xmlNsPtr ns_ows,const char* identifier,map* amap){
+  xmlNodePtr nc2 = xmlNewNode(ns_ows, BAD_CAST "Identifier");
+  xmlAddChild(nc2,xmlNewText(BAD_CAST identifier));
+  xmlAddChild(root,nc2);
+  map* tmp=amap;
+  char *tmp2[2];
+  tmp2[0]="Title";
+  tmp2[1]="Abstract";
+  int j=0;
+  for(j=0;j<2;j++){
+    map* tmp1=getMap(tmp,tmp2[j]);
+    if(tmp1!=NULL){
+      nc2 = xmlNewNode(ns_ows, BAD_CAST tmp2[j]);
+      xmlAddChild(nc2,xmlNewText(BAD_CAST _ss(tmp1->value)));
+      xmlAddChild(root,nc2);
+    }
+  }
+}
+
+char* getEncoding(maps* m){
+  if(m!=NULL){
+    map* tmp=getMap(m->content,"encoding");
+    if(tmp!=NULL){
+      return tmp->value;
+    }
+    else
+      return "UTF-8";
+  }
+  else
+    return "UTF-8";  
+}
+
+char* getVersion(maps* m){
+  if(m!=NULL){
+    map* tmp=getMap(m->content,"version");
+    if(tmp!=NULL){
+      return tmp->value;
+    }
+    else
+      return "1.0.0";
+  }
+  else
+    return "1.0.0";
+}
+
+void printExceptionReportResponse(maps* m,map* s){
+  int buffersize;
+  xmlDocPtr doc;
+  xmlChar *xmlbuff;
+  xmlNodePtr n;
+
+  doc = xmlNewDoc(BAD_CAST "1.0");
+  maps* tmpMap=getMaps(m,"main");
+  char *encoding=getEncoding(tmpMap);
+  if(m!=NULL){
+    map *tmpSid=getMapFromMaps(m,"lenv","sid");
+    if(tmpSid!=NULL){
+      if( getpid()==atoi(tmpSid->value) )
+	printf("Content-Type: text/xml; charset=%s\r\nStatus: 200 OK\r\n\r\n",encoding);
+    }
+    else
+      printf("Content-Type: text/xml; charset=%s\r\nStatus: 200 OK\r\n\r\n",encoding);
+  }else
+    printf("Content-Type: text/xml; charset=%s\r\nStatus: 200 OK\r\n\r\n",encoding);
+  n=createExceptionReportNode(m,s,1);
+  xmlDocSetRootElement(doc, n);
+  xmlDocDumpFormatMemoryEnc(doc, &xmlbuff, &buffersize, encoding, 1);
+  printf("%s",xmlbuff);
+  fflush(stdout);
+  xmlFreeDoc(doc);
+  xmlFree(xmlbuff);
+  xmlCleanupParser();
+  zooXmlCleanupNs();
+}
+
+xmlNodePtr createExceptionReportNode(maps* m,map* s,int use_ns){
+  
+  int buffersize;
+  xmlChar *xmlbuff;
+  xmlNsPtr ns,ns_ows,ns_xlink,ns_xsi;
+  xmlNodePtr n,nc,nc1,nc2;
+
+  maps* tmpMap=getMaps(m,"main");
+
+  int nsid=zooXmlAddNs(NULL,"http://www.opengis.net/ows/1.1","ows");
+  ns=usedNs[nsid];
+  n = xmlNewNode(ns, BAD_CAST "ExceptionReport");
+
+  if(use_ns==1){
+    ns_ows=xmlNewNs(n,BAD_CAST "http://www.opengis.net/ows/1.1",BAD_CAST "ows");
+    int xsiId=zooXmlAddNs(n,"http://www.w3.org/2001/XMLSchema-instance","xsi");
+    ns_xsi=usedNs[xsiId];
+    int xlinkId=zooXmlAddNs(n,"http://www.w3.org/1999/xlink","xlink");
+    ns_xlink=usedNs[xlinkId];
+    xmlNewNsProp(n,ns_xsi,BAD_CAST "schemaLocation",BAD_CAST "http://www.opengis.net/ows/1.1 http://schemas.opengis.net/ows/1.1.0/owsExceptionReport.xsd");
+  }
+  addLangAttr(n,m);
+  xmlNewProp(n,BAD_CAST "version",BAD_CAST "1.1.0");
+  
+  nc = xmlNewNode(ns, BAD_CAST "Exception");
+
+  map* tmp=getMap(s,"code");
+  if(tmp!=NULL)
+    xmlNewProp(nc,BAD_CAST "exceptionCode",BAD_CAST tmp->value);
+  else
+    xmlNewProp(nc,BAD_CAST "exceptionCode",BAD_CAST "NoApplicableCode");
+
+  tmp=getMap(s,"text");
+  nc1 = xmlNewNode(ns, BAD_CAST "ExceptionText");
+  nc2=NULL;
+  if(tmp!=NULL){
+    xmlNodeSetContent(nc1, BAD_CAST tmp->value);
+  }
+  else{
+    xmlNodeSetContent(nc1, BAD_CAST _("No debug message available"));
+  }
+  xmlAddChild(nc,nc1);
+  xmlAddChild(n,nc);
+  return n;
+}
+
+
+void outputResponse(service* s,maps* request_inputs,maps* request_outputs,
+		    map* request_inputs1,int cpid,maps* m,int res){
+#ifdef DEBUG
+  dumpMaps(request_inputs);
+  dumpMaps(request_outputs);
+  fprintf(stderr,"printProcessResponse\n");
+#endif
+  map* toto=getMap(request_inputs1,"RawDataOutput");
+  int asRaw=0;
+  if(toto!=NULL)
+    asRaw=1;
+  
+  map *_tmp=getMapFromMaps(m,"lenv","cookie");
+  if(_tmp!=NULL){
+    printf("Set-Cookie: %s\r\n",_tmp->value);
+    maps *tmpSess=getMaps(m,"senv");
+    if(tmpSess!=NULL){
+      char session_file_path[1024];
+      map *tmpPath=getMapFromMaps(m,"main","sessPath");
+      if(tmpPath==NULL)
+	tmpPath=getMapFromMaps(m,"main","tmpPath");
+      char *tmp1=strtok(_tmp->value,";");
+      if(tmp1!=NULL)
+	sprintf(session_file_path,"%s/sess_%s.cfg",tmpPath->value,strstr(tmp1,"=")+1);
+      else
+	sprintf(session_file_path,"%s/sess_%s.cfg",tmpPath->value,strstr(_tmp->value,"=")+1);
+      FILE* file=fopen(session_file_path,"w");
+      dumpMapsToFile(tmpSess,file);
+      fclose(file);
+    }
+  }
+  if(asRaw==0){
+#ifdef DEBUG
+    fprintf(stderr,"REQUEST_OUTPUTS FINAL\n");
+    dumpMaps(request_outputs);
+#endif
+    maps* tmpI=request_outputs;
+    while(tmpI!=NULL){
+      toto=getMap(tmpI->content,"asReference");
+      if(toto!=NULL && strcasecmp(toto->value,"true")==0){
+	elements* in=getElements(s->outputs,tmpI->name);
+	char *format=NULL;
+	if(in!=NULL){
+	  format=strdup(in->format);
+	}else
+	  format=strdup("LiteralData");
+	if(strcasecmp(format,"BoundingBoxData")==0){
+	  addToMap(tmpI->content,"extension","xml");
+	  addToMap(tmpI->content,"mimeType","text/xml");
+	  addToMap(tmpI->content,"encoding","UTF-8");
+	  addToMap(tmpI->content,"schema","http://schemas.opengis.net/ows/1.1.0/owsCommon.xsd");
+	}
+	map *ext=getMap(tmpI->content,"extension");
+	map *tmp1=getMapFromMaps(m,"main","tmpPath");
+	char *file_name;
+	bool hasExt=true;
+	if(ext==NULL){
+	  // We can fallback to a default list of supported formats using
+	  // mimeType information if present here. Maybe we can add more formats
+	  // here.
+	  // If mimeType was not found, we then set txt as the default extension.
+	  map* mtype=getMap(tmpI->content,"mimeType");
+	  if(mtype!=NULL){
+	    if(strcasecmp(mtype->value,"text/xml")==0)
+	      ext=createMap("extension","xml");
+	    else if(strcasecmp(mtype->value,"application/json")==0)
+	      ext=createMap("extension","js");
+	    else
+	      ext=createMap("extension","txt");
+	  }
+	  else
+	    ext=createMap("extension","txt");
+	  hasExt=false;
+	}
+	file_name=(char*)malloc((strlen(tmp1->value)+strlen(s->name)+strlen(ext->value)+strlen(tmpI->name)+13)*sizeof(char));
+	sprintf(file_name,"%s/%s_%s_%i.%s",tmp1->value,s->name,tmpI->name,cpid+100000,ext->value);
+	FILE *ofile=fopen(file_name,"w");
+	if(ofile==NULL)
+	  fprintf(stderr,"Unable to create file on disk implying segfault ! \n");
+	map *tmp2=getMapFromMaps(m,"main","tmpUrl");
+	map *tmp3=getMapFromMaps(m,"main","serverAddress");
+	char *file_url;
+	file_url=(char*)malloc((strlen(tmp3->value)+strlen(tmp2->value)+strlen(s->name)+strlen(ext->value)+strlen(tmpI->name)+13)*sizeof(char));
+	sprintf(file_url,"%s/%s/%s_%s_%i.%s",tmp3->value,tmp2->value,s->name,tmpI->name,cpid+100000,ext->value);
+	addToMap(tmpI->content,"Reference",file_url);
+	if(hasExt!=true){
+	  freeMap(&ext);
+	  free(ext);
+	}
+	toto=getMap(tmpI->content,"value");
+	if(strcasecmp(format,"BoundingBoxData")!=0){
+	  map* size=getMap(tmpI->content,"size");
+	  if(size!=NULL && toto!=NULL)
+	    fwrite(toto->value,1,atoi(size->value)*sizeof(char),ofile);
+	  else
+	    if(toto!=NULL && toto->value!=NULL)
+	      fwrite(toto->value,1,strlen(toto->value)*sizeof(char),ofile);
+	}else{
+	  printBoundingBoxDocument(m,tmpI,ofile);
+	}
+	free(format);
+	fclose(ofile);
+	free(file_name);
+	free(file_url);	
+      }
+      tmpI=tmpI->next;
+    }
+    map *r_inputs=getMap(s->content,"serviceProvider");
+#ifdef DEBUG
+    fprintf(stderr,"SERVICE : %s\n",r_inputs->value);
+    dumpMaps(m);
+#endif
+    printProcessResponse(m,request_inputs1,cpid,
+			 s,r_inputs->value,res,
+			 request_inputs,
+			 request_outputs);
+  }
+  else
+    if(res!=SERVICE_FAILED){
+      /**
+       * We get the requested output or fallback to the first one if the 
+       * requested one is not present in the resulting outputs maps.
+       */
+      maps* tmpI=NULL;
+      map* tmpIV=getMap(request_inputs1,"RawDataOutput");
+      if(tmpIV!=NULL){
+	tmpI=getMaps(request_outputs,tmpIV->value);
+      }
+      if(tmpI==NULL)
+	tmpI=request_outputs;
+      elements* e=getElements(s->outputs,tmpI->name);
+      if(e!=NULL && strcasecmp(e->format,"BoundingBoxData")==0){
+	printBoundingBoxDocument(m,tmpI,NULL);
+      }else{
+	toto=getMap(tmpI->content,"value");
+	if(toto==NULL){
+	  char tmpMsg[1024];
+	  sprintf(tmpMsg,_("Wrong RawDataOutput parameter, unable to fetch any result for the name your provided : \"%s\"."),tmpI->name);
+	  map * errormap = createMap("text",tmpMsg);
+	  addToMap(errormap,"code", "InvalidParameterValue");
+	  printExceptionReportResponse(m,errormap);
+	  freeMap(&errormap);
+	  free(errormap);
+	  return;
+	}
+	char mime[1024];
+	map* mi=getMap(tmpI->content,"mimeType");
+#ifdef DEBUG
+	fprintf(stderr,"SERVICE OUTPUTS\n");
+	dumpMaps(request_outputs);
+	fprintf(stderr,"SERVICE OUTPUTS\n");
+#endif
+	map* en=getMap(tmpI->content,"encoding");
+	if(mi!=NULL && en!=NULL)
+	  sprintf(mime,
+		  "Content-Type: %s; charset=%s\r\nStatus: 200 OK\r\n\r\n",
+		  mi->value,en->value);
+	else
+	  if(mi!=NULL)
+	    sprintf(mime,
+		    "Content-Type: %s; charset=UTF-8\r\nStatus: 200 OK\r\n\r\n",
+		    mi->value);
+	  else
+	    sprintf(mime,"Content-Type: text/plain; charset=utf-8\r\nStatus: 200 OK\r\n\r\n");
+	printf("%s",mime);
+	if(mi!=NULL && strncmp(mi->value,"image",5)==0){
+	  map* rs=getMapFromMaps(tmpI,tmpI->name,"size");
+	  fwrite(toto->value,atoi(rs->value),1,stdout);
+	}
+	else
+	  printf("%s",toto->value);
+#ifdef DEBUG
+	dumpMap(toto);
+#endif
+      }
+    }else{
+      char tmp[1024];
+      map * errormap;
+      map *lenv;
+      lenv=getMapFromMaps(m,"lenv","message");
+      if(lenv!=NULL)
+	sprintf(tmp,_("Unable to run the Service. The message returned back by the Service was the following : %s"),lenv->value);
+      else
+	sprintf(tmp,_("Unable to run the Service. No more information was returned back by the Service."));
+      errormap = createMap("text",tmp);      
+      addToMap(errormap,"code", "InternalError");
+      printExceptionReportResponse(m,errormap);
+      freeMap(&errormap);
+      free(errormap);
+    }
+}
+
+char *base64(const char *input, int length)
+{
+  BIO *bmem, *b64;
+  BUF_MEM *bptr;
+
+  b64 = BIO_new(BIO_f_base64());
+  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+  bmem = BIO_new(BIO_s_mem());
+  b64 = BIO_push(b64, bmem);
+  BIO_write(b64, input, length);
+  BIO_flush(b64);
+  BIO_get_mem_ptr(b64, &bptr);
+
+  char *buff = (char *)malloc((bptr->length)*sizeof(char));
+  memcpy(buff, bptr->data, bptr->length-1);
+  buff[bptr->length-1] = 0;
+
+  BIO_free_all(b64);
+
+  return buff;
+}
+
+char *base64d(const char *input, int length,int* red)
+{
+  BIO *b64, *bmem;
+
+  char *buffer = (char *)malloc(length);
+  if(buffer){
+    memset(buffer, 0, length);
+    b64 = BIO_new(BIO_f_base64());
+    if(b64){
+      bmem = BIO_new_mem_buf((unsigned char*)input,length);
+      bmem = BIO_push(b64, bmem);
+      *red=BIO_read(bmem, buffer, length);
+      buffer[length-1]=0;
+      BIO_free_all(bmem);
+    }
+  }
+  return buffer;
+}
+
+void ensureDecodedBase64(maps **in){
+  maps* cursor=*in;
+  while(cursor!=NULL){
+    map *tmp=getMap(cursor->content,"encoding");
+    if(tmp!=NULL && strncasecmp(tmp->value,"base64",6)==0){
+      tmp=getMap(cursor->content,"value");
+      addToMap(cursor->content,"base64_value",tmp->value);
+      int size=0;
+      char *s=strdup(tmp->value);
+      free(tmp->value);
+      tmp->value=base64d(s,strlen(s),&size);
+      free(s);
+      char sizes[1024];
+      sprintf(sizes,"%d",size);
+      addToMap(cursor->content,"size",sizes);
+    }
+    cursor=cursor->next;
+  }
+}
+
+char* addDefaultValues(maps** out,elements* in,maps* m,int type){
+  elements* tmpInputs=in;
+  maps* out1=*out;
+  while(tmpInputs!=NULL){
+    maps *tmpMaps=getMaps(out1,tmpInputs->name);
+    if(tmpMaps==NULL){
+      maps* tmpMaps2=(maps*)malloc(MAPS_SIZE);
+      tmpMaps2->name=strdup(tmpInputs->name);
+      tmpMaps2->content=NULL;
+      tmpMaps2->next=NULL;
+      
+      if(type==0){
+	map* tmpMapMinO=getMap(tmpInputs->content,"minOccurs");
+	if(tmpMapMinO!=NULL)
+	  if(atoi(tmpMapMinO->value)>=1){
+	    freeMaps(&tmpMaps2);
+	    free(tmpMaps2);
+	    return tmpInputs->name;
+	  }
+	  else{
+	    if(tmpMaps2->content==NULL)
+	      tmpMaps2->content=createMap("minOccurs",tmpMapMinO->value);
+	    else
+	      addToMap(tmpMaps2->content,"minOccurs",tmpMapMinO->value);
+	  }
+	map* tmpMaxO=getMap(tmpInputs->content,"maxOccurs");
+	if(tmpMaxO!=NULL)
+	  if(tmpMaps2->content==NULL)
+	    tmpMaps2->content=createMap("maxOccurs",tmpMaxO->value);
+	  else
+	    addToMap(tmpMaps2->content,"maxOccurs",tmpMaxO->value);
+      }
+
+      iotype* tmpIoType=tmpInputs->defaults;
+      if(tmpIoType!=NULL){
+	map* tmpm=tmpIoType->content;
+	while(tmpm!=NULL){
+	  if(tmpMaps2->content==NULL)
+	    tmpMaps2->content=createMap(tmpm->name,tmpm->value);
+	  else
+	    addToMap(tmpMaps2->content,tmpm->name,tmpm->value);
+	  tmpm=tmpm->next;
+	}
+      }
+      addToMap(tmpMaps2->content,"inRequest","false");
+      if(type==0){
+	map *tmpMap=getMap(tmpMaps2->content,"value");
+	if(tmpMap==NULL)
+	  addToMap(tmpMaps2->content,"value","NULL");
+      }
+      if(out1==NULL){
+	*out=dupMaps(&tmpMaps2);
+	out1=*out;
+      }
+      else
+	addMapsToMaps(&out1,tmpMaps2);
+      freeMap(&tmpMaps2->content);
+      free(tmpMaps2->content);
+      tmpMaps2->content=NULL;
+      freeMaps(&tmpMaps2);
+      free(tmpMaps2);
+      tmpMaps2=NULL;
+    }
+    else{
+      iotype* tmpIoType=getIoTypeFromElement(tmpInputs,tmpInputs->name,
+					     tmpMaps->content);
+      if(type==0) {
+	/**
+	 * In case of an Input maps, then add the minOccurs and maxOccurs to the
+	 * content map.
+	 */
+	map* tmpMap1=getMap(tmpInputs->content,"minOccurs");
+	if(tmpMap1!=NULL){
+	  if(tmpMaps->content==NULL)
+	    tmpMaps->content=createMap("minOccurs",tmpMap1->value);
+	  else
+	    addToMap(tmpMaps->content,"minOccurs",tmpMap1->value);
+	}
+	map* tmpMaxO=getMap(tmpInputs->content,"maxOccurs");
+	if(tmpMaxO!=NULL){
+	  if(tmpMaps->content==NULL)
+	    tmpMaps->content=createMap("maxOccurs",tmpMap1->value);
+	  else
+	    addToMap(tmpMaps->content,"maxOccurs",tmpMap1->value);
+	}
+	/**
+	 * Parsing BoundingBoxData, fill the following map and then add it to
+	 * the content map of the Input maps: 
+	 * lowerCorner, upperCorner, srs and dimensions
+	 * cf. parseBoundingBox
+	 */
+	if(strcasecmp(tmpInputs->format,"BoundingBoxData")==0){
+	  maps* tmpI=getMaps(*out,tmpInputs->name);
+	  if(tmpI!=NULL){
+	    map* tmpV=getMap(tmpI->content,"value");
+	    if(tmpV!=NULL){
+	      char *tmpVS=strdup(tmpV->value);
+	      map* tmp=parseBoundingBox(tmpVS);
+	      free(tmpVS);
+	      map* tmpC=tmp;
+	      while(tmpC!=NULL){
+		addToMap(tmpMaps->content,tmpC->name,tmpC->value);
+		tmpC=tmpC->next;
+	      }
+	      freeMap(&tmp);
+	      free(tmp);
+	    }
+	  }
+	}
+      }
+
+      if(tmpIoType!=NULL){
+	map* tmpContent=tmpIoType->content;
+	map* cval=NULL;
+
+	while(tmpContent!=NULL){
+	  if((cval=getMap(tmpMaps->content,tmpContent->name))==NULL){
+#ifdef DEBUG
+	    fprintf(stderr,"addDefaultValues %s => %s\n",tmpContent->name,tmpContent->value);
+#endif
+	    if(tmpMaps->content==NULL)
+	      tmpMaps->content=createMap(tmpContent->name,tmpContent->value);
+	    else
+	      addToMap(tmpMaps->content,tmpContent->name,tmpContent->value);
+	  }
+	  tmpContent=tmpContent->next;
+	}
+      }
+      if(tmpMaps->content==NULL)
+	tmpMaps->content=createMap("inRequest","true");
+      else
+	addToMap(tmpMaps->content,"inRequest","true");
+    }
+    tmpInputs=tmpInputs->next;
+  }
+  return "";
+}
+
+/**
+ * parseBoundingBox : parse a BoundingBox string
+ *
+ * OGC 06-121r3 : 10.2 Bounding box
+ *
+ * value is provided as : lowerCorner,upperCorner,crs,dimension
+ * exemple : 189000,834000,285000,962000,urn:ogc:def:crs:OGC:1.3:CRS84
+ *
+ * Need to create a map to store boundingbox informations :
+ *  - lowerCorner : double,double (minimum within this bounding box)
+ *  - upperCorner : double,double (maximum within this bounding box)
+ *  - crs : URI (Reference to definition of the CRS)
+ *  - dimensions : int 
+ * 
+ * Note : support only 2D bounding box.
+ */
+map* parseBoundingBox(const char* value){
+  map *res=NULL;
+  if(value!=NULL){
+    char *cv,*cvp;
+    cv=strtok_r((char*) value,",",&cvp);
+    int cnt=0;
+    int icnt=0;
+    char *currentValue=NULL;
+    while(cv){
+      if(cnt<2)
+	if(currentValue!=NULL){
+	  char *finalValue=(char*)malloc((strlen(currentValue)+strlen(cv)+1)*sizeof(char));
+	  sprintf(finalValue,"%s%s",currentValue,cv);
+	  switch(cnt){
+	  case 0:
+	    res=createMap("lowerCorner",finalValue);
+	    break;
+	  case 1:
+	    addToMap(res,"upperCorner",finalValue);
+	    icnt=-1;
+	    break;
+	  }
+	  cnt++;
+	  free(currentValue);
+	  currentValue=NULL;
+	  free(finalValue);
+	}
+	else{
+	  currentValue=(char*)malloc((strlen(cv)+2)*sizeof(char));
+	  sprintf(currentValue,"%s ",cv);
+	}
+      else
+	if(cnt==2){
+	  addToMap(res,"crs",cv);
+	  cnt++;
+	}
+	else
+	  addToMap(res,"dimensions",cv);
+      icnt++;
+      cv=strtok_r(NULL,",",&cvp);
+    }
+  }
+  return res;
+}
+
+/**
+ * printBoundingBox : fill a BoundingBox node (ows:BoundingBox or 
+ * wps:BoundingBoxData). Set crs and dimensions attributes, add 
+ * Lower/UpperCorner nodes to a pre-existing XML node.
+ */
+void printBoundingBox(xmlNsPtr ns_ows,xmlNodePtr n,map* boundingbox){
+
+  xmlNodePtr bb,lw,uc;
+
+  map* tmp=getMap(boundingbox,"value");
+
+  tmp=getMap(boundingbox,"lowerCorner");
+  if(tmp!=NULL){
+    lw=xmlNewNode(ns_ows,BAD_CAST "LowerCorner");
+    xmlAddChild(lw,xmlNewText(BAD_CAST tmp->value));
+  }
+
+  tmp=getMap(boundingbox,"upperCorner");
+  if(tmp!=NULL){
+    uc=xmlNewNode(ns_ows,BAD_CAST "UpperCorner");
+    xmlAddChild(uc,xmlNewText(BAD_CAST tmp->value));
+  }
+
+  tmp=getMap(boundingbox,"crs");
+  if(tmp!=NULL)
+    xmlNewProp(n,BAD_CAST "crs",BAD_CAST tmp->value);
+
+  tmp=getMap(boundingbox,"dimensions");
+  if(tmp!=NULL)
+    xmlNewProp(n,BAD_CAST "dimensions",BAD_CAST tmp->value);
+
+  xmlAddChild(n,lw);
+  xmlAddChild(n,uc);
+
+}
+
+void printBoundingBoxDocument(maps* m,maps* boundingbox,FILE* file){
+  if(file==NULL)
+    rewind(stdout);
+  xmlNodePtr n;
+  xmlDocPtr doc;
+  xmlNsPtr ns_ows,ns_xsi;
+  xmlChar *xmlbuff;
+  int buffersize;
+  char *encoding=getEncoding(m);
+  map *tmp;
+  if(file==NULL){
+    int pid=0;
+    tmp=getMapFromMaps(m,"lenv","sid");
+    if(tmp!=NULL)
+      pid=atoi(tmp->value);
+    if(pid==getpid()){
+      printf("Content-Type: text/xml; charset=%s\r\nStatus: 200 OK\r\n\r\n",encoding);
+    }
+    fflush(stdout);
+  }
+
+  doc = xmlNewDoc(BAD_CAST "1.0");
+  int owsId=zooXmlAddNs(NULL,"http://www.opengis.net/ows/1.1","ows");
+  ns_ows=usedNs[owsId];
+  n = xmlNewNode(ns_ows, BAD_CAST "BoundingBox");
+  xmlNewNs(n,BAD_CAST "http://www.opengis.net/ows/1.1",BAD_CAST "ows");
+  int xsiId=zooXmlAddNs(n,"http://www.w3.org/2001/XMLSchema-instance","xsi");
+  ns_xsi=usedNs[xsiId];
+  xmlNewNsProp(n,ns_xsi,BAD_CAST "schemaLocation",BAD_CAST "http://www.opengis.net/ows/1.1 http://schemas.opengis.net/ows/1.1.0/owsCommon.xsd");
+  map *tmp1=getMap(boundingbox->content,"value");
+  tmp=parseBoundingBox(tmp1->value);
+  printBoundingBox(ns_ows,n,tmp);
+  xmlDocSetRootElement(doc, n);
+
+  xmlDocDumpFormatMemoryEnc(doc, &xmlbuff, &buffersize, encoding, 1);
+  if(file==NULL)
+    printf("%s",xmlbuff);
+  else{
+    fprintf(file,"%s",xmlbuff);
+  }
+
+  if(tmp!=NULL){
+    freeMap(&tmp);
+    free(tmp);
+  }
+  xmlFree(xmlbuff);
+  xmlFreeDoc(doc);
+  xmlCleanupParser();
+  zooXmlCleanupNs();
+  
+}
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal.h	(revision 221)
@@ -0,0 +1,125 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2011 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef ZOO_SERVICE_INTERNAL_H
+#define ZOO_SERVICE_INTERNAL_H 1
+
+#pragma once 
+
+#define DEFAULT_SERVICE_URL "http://www.zoo-project.org/"
+#define TIME_SIZE 40
+
+#include <libintl.h>
+#include <locale.h>
+#define _(String) dgettext ("zoo-kernel",String)
+#define _ss(String) dgettext ("zoo-services",String)
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifndef WIN32
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#else
+#include <direct.h>
+#endif
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <ctype.h>
+#ifndef WIN32
+#include <xlocale.h>
+#endif
+#include "service.h"
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+
+extern   int getServiceFromFile(const char*,service**);
+extern   int conf_read(const char*,maps*);
+
+#ifdef USE_JS
+#define XP_UNIX 0
+#include "service_internal_js.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <libxml/parser.h>
+  static char* SERVICE_URL;
+  static xmlNsPtr usedNs[5];
+  static char* nsName[5];
+  static int nbNs=0;
+
+  void unhandleStatus(maps*);
+  void updateStatus(maps*);
+  char* getStatus(int);
+
+#ifdef USE_JS
+  char* JSValToChar(JSContext*,jsval*);
+  JSBool JSUpdateStatus(JSContext*,JSObject*,uintN,jsval *,jsval *);
+#endif
+  
+  void URLDecode(char *);
+  char *url_encode(char *);
+  char* getEncoding(maps*);
+
+  int zooXmlSearchForNs(const char*);
+  int zooXmlAddNs(xmlNodePtr,const char*,const char*);
+  void zooXmlCleanupNs();
+  
+  void printExceptionReportResponse(maps*,map*);
+  xmlNodePtr createExceptionReportNode(maps*,map*,int);
+  void printProcessResponse(maps*,map*,int,service*,const char*,int,maps*,maps*);
+  xmlNodePtr printGetCapabilitiesHeader(xmlDocPtr,const char*,maps*);
+  void printGetCapabilitiesForProcess(maps*,xmlNodePtr,service*);
+  xmlNodePtr printDescribeProcessHeader(xmlDocPtr,const char*,maps*);
+  void printDescribeProcessForProcess(maps*,xmlNodePtr,service*,int);
+  void printFullDescription(elements*,const char*,xmlNsPtr,xmlNodePtr);
+  void printDocument(maps*,xmlDocPtr,int);
+  void printDescription(xmlNodePtr,xmlNsPtr,const char*,map*);
+  void printIOType(xmlDocPtr,xmlNodePtr,xmlNsPtr,xmlNsPtr,xmlNsPtr,elements*,maps*,const char*);
+  map* parseBoundingBox(const char*);
+  void printBoundingBox(xmlNsPtr,xmlNodePtr,map*);
+  void printBoundingBoxDocument(maps*,maps*,FILE*);
+  void printOutputDefinitions1(xmlDocPtr,xmlNodePtr,xmlNsPtr,xmlNsPtr,elements*,maps*,const char*);
+  
+  void outputResponse(service*,maps*,maps*,map*,int,maps*,int);
+
+  char *base64(const char*,int);
+  char *base64d(const char*,int,int*);
+  void ensureDecodedBase64(maps**);
+
+  char* addDefaultValues(maps**,elements*,maps*,int);
+
+  /*defined in zoo_loader.c*/ 
+  int errorException(maps *m, const char *message, const char *errorcode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_java.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_java.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_java.c	(revision 221)
@@ -0,0 +1,372 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2011 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "service_internal_java.h"
+
+int zoo_java_support(maps** main_conf,map* request,service* s,maps **real_inputs,maps **real_outputs){
+  maps* m=*main_conf;
+  maps* inputs=*real_inputs;
+  maps* outputs=*real_outputs;
+  char ntmp[1024];
+  getcwd(ntmp,1024);
+  map* tmp=getMap(request,"metapath");
+  char classpath[2048];
+  char oclasspath[2068];
+  int res=SERVICE_FAILED;
+  if(tmp!=NULL){
+    sprintf(classpath,"%s/%s/:$CLASSPATH",ntmp,tmp->value);
+    sprintf(oclasspath,"-Djava.class.path=%s/%s",ntmp,tmp->value);
+  }
+  else{
+    sprintf(classpath,"%s:$CLASSPATH",ntmp);
+    sprintf(oclasspath,"-Djava.class.path=%s",ntmp);
+  }
+#ifdef DEBUG
+  fprintf(stderr,"CLASSPATH=%s\n",classpath);
+#endif
+  setenv("CLASSPATH",classpath,1);
+
+  JavaVMOption options[1];
+  JavaVMInitArgs vm_args;
+  JavaVM *jvm;
+  JNIEnv *env;
+  long result;
+  jmethodID pmid;
+  jfieldID fid;
+  jobject jobj;
+  jclass cls,cls_gr;
+  int i;
+
+  options[0].optionString = oclasspath;
+
+  vm_args.version = JNI_VERSION_1_2;
+  JNI_GetDefaultJavaVMInitArgs(&vm_args);
+  vm_args.options = options;
+  vm_args.nOptions = 1;
+  vm_args.ignoreUnrecognized = JNI_FALSE;
+
+  result = JNI_CreateJavaVM(&jvm,(void **)&env, &vm_args);
+  if(result == JNI_ERR ) {
+    fprintf(stderr,"Error invoking the JVM");
+    return -1;
+  }
+#ifdef DEBUG
+  else  
+    fprintf(stderr,"JAVA VM Started\n");
+#endif
+
+  tmp=getMap(s->content,"serviceProvider");
+  cls = (*env)->FindClass(env,tmp->value);
+  cls_gr = (*env)->NewGlobalRef(env, cls);
+  if( cls == NULL ) {
+    char pbt[10240];
+    sprintf(pbt,"can't find class %s\n",tmp->value);
+    map* err=createMap("text",pbt);
+    addToMap(err,"code","NoApplicableCode");
+    printExceptionReportResponse(m,err);
+    freeMap(&err);
+    free(err);
+    (*jvm)->DestroyJavaVM(jvm);
+    return 1;
+  }
+#ifdef DEBUG
+  else{
+    fprintf(stderr,"%s loaded\n",tmp->value);
+  }
+#endif
+
+  if (cls != NULL) {
+    (*env)->ExceptionClear(env);
+    pmid=(*env)->GetStaticMethodID(env,cls_gr, s->name, "(Ljava/util/HashMap;Ljava/util/HashMap;Ljava/util/HashMap;)I");
+    if (pmid!=0){
+#ifdef DEBUG
+      fprintf(stderr,"Function successfully loaded\n");
+#endif
+      jclass scHashMapClass,scHashMap_class;
+      jmethodID scHashMap_constructor;
+      scHashMapClass = (*env)->FindClass(env, "java/util/HashMap");
+      scHashMap_class = (*env)->NewGlobalRef(env, scHashMapClass);
+      scHashMap_constructor = (*env)->GetMethodID(env, scHashMap_class, "<init>", "()V");
+      /**
+       * The 3 standard parameter for each services
+       */
+      jobject arg1=HashMap_FromMaps(env,m,scHashMapClass,scHashMap_class,scHashMap_constructor);
+      jobject arg2=HashMap_FromMaps(env,inputs,scHashMapClass,scHashMap_class,scHashMap_constructor);
+      jobject arg3=HashMap_FromMaps(env,outputs,scHashMapClass,scHashMap_class,scHashMap_constructor);
+      jint pValue=0;
+
+      pValue=(*env)->CallStaticIntMethod(env,cls,pmid,arg1,arg2,arg3);
+      if (pValue != (jint)NULL){
+	res=pValue;
+	m=mapsFromHashMap(env,arg1,scHashMapClass);
+	*main_conf=m;
+	outputs=mapsFromHashMap(env,arg3,scHashMapClass);
+	*real_outputs=outputs;
+
+#ifdef DEBUG
+	fprintf(stderr,"Result of call: %i\n", pValue);
+	dumpMaps(inputs);
+	dumpMaps(outputs);
+#endif
+      }else{	  
+	/**
+	 * Error handling displayig stack trace in ExceptionReport
+	 */
+	map *tmpm=getMapFromMaps(*main_conf,"main","tmpPath");
+	char tmps[1024];
+	sprintf(tmps,"%s/%d.ztmp",tmpm->value,getpid());
+	int old_stdout=dup(fileno(stdout));
+	FILE* new_stdout=fopen(tmps,"w+");
+	dup2(fileno(new_stdout),fileno(stdout));
+	(*env)->ExceptionDescribe(env);
+	fflush(stdout);
+	dup2(old_stdout,fileno(stdout));
+	fseek(new_stdout, 0, SEEK_END);
+	long flen=ftell(new_stdout);
+	rewind(new_stdout);
+	char tmps1[flen];
+	fread(tmps1,flen,1,new_stdout);
+	fclose(new_stdout);
+	char pbt[100+flen];
+	sprintf(pbt,"Unable to run your java process properly. Server returns : %s",tmps1);
+	map* err=createMap("text",pbt);
+	addToMap(err,"code","NoApplicableCode");
+	printExceptionReportResponse(m,err);
+	freeMap(&err);
+	free(err);
+	(*jvm)->DestroyJavaVM(jvm);
+	return -1;
+      }
+    }
+    else{
+      char tmpS[1024];
+      sprintf(tmpS, "Cannot find function %s \n", s->name);
+      map* err=createMap("text",tmpS);
+      printExceptionReportResponse(m,err);
+      freeMap(&err);
+      free(err);
+      (*jvm)->DestroyJavaVM(jvm);
+      return -1;
+    }
+  }else{
+    char tmpS[1024];
+    sprintf(tmpS, "Cannot find function %s \n", tmp->value);
+    map* err=createMap("text",tmpS);
+    printExceptionReportResponse(m,err);
+    freeMap(&err);
+    free(err);
+    (*jvm)->DestroyJavaVM(jvm);
+    return -1;
+  }
+  (*jvm)->DestroyJavaVM(jvm);
+  return res;
+}
+
+jobject HashMap_FromMaps(JNIEnv *env,maps* t,jclass scHashMapClass,jclass scHashMap_class,jmethodID scHashMap_constructor){
+  jobject scObject,scObject1;
+  if(scHashMap_constructor!=NULL){
+    scObject = (*env)->NewObject(env, scHashMap_class, scHashMap_constructor);
+    jmethodID put_mid = 0;
+
+    put_mid = (*env)->GetMethodID(env,scHashMapClass, "put",
+				  "(Ljava/lang/Object;Ljava/lang/Object;)"
+				  "Ljava/lang/Object;");
+    maps* tmp=t;
+    while(tmp!=NULL){
+      map* tmp1=tmp->content;
+      scObject1 = (*env)->NewObject(env, scHashMap_class, scHashMap_constructor);
+      map* sizeV=getMap(tmp1,"size");
+      while(tmp1!=NULL){
+	if(sizeV!=NULL && strcmp(tmp1->name,"value")==0){
+	  jbyteArray tmpData=(*env)->NewByteArray(env,atoi(sizeV->value));
+	  (*env)->SetByteArrayRegion(env,tmpData,0,atoi(sizeV->value),tmp1->value);
+	  (*env)->CallObjectMethod(env,scObject1, put_mid, (*env)->NewStringUTF(env,tmp1->name), tmpData);
+	}else
+	  (*env)->CallObjectMethod(env,scObject1, put_mid, (*env)->NewStringUTF(env,tmp1->name), (*env)->NewStringUTF(env,tmp1->value));
+	tmp1=tmp1->next;
+      }
+      (*env)->CallObjectMethod(env,scObject, put_mid, (*env)->NewStringUTF(env,tmp->name), scObject1);
+      tmp=tmp->next;
+    }
+    return scObject;
+  }
+  else
+    return NULL;
+}
+
+maps* mapsFromHashMap(JNIEnv *env,jobject t,jclass scHashMapClass){
+#ifdef DEBUG
+  fprintf(stderr,"mapsFromHashMap start\n");
+#endif
+  /**
+   * What need to be done (in java).
+   * Set set = hm.entrySet();
+   * Iterator i = set.iterator();
+   * while(i.hasNext()){
+   *   Map.Entry me = (Map.Entry)i.next();
+   *   System.out.println(me.getKey() + " : " + me.getValue() );
+   * }
+   */
+  jclass scHashMap_class,scSetClass,scIteratorClass,scMapEntryClass,scSet_class,scMapClass;
+  jmethodID entrySet_mid,containsKey_mid,get_mid,iterator_mid,hasNext_mid,next_mid,getKey_mid,getValue_mid;
+  jobject scObject,scObject1;
+  if(scHashMapClass==NULL){
+#ifdef DEBUG
+    fprintf(stderr,"Unable to load java.util.HashMap\n");
+#endif
+    return NULL;
+  }
+  entrySet_mid = (*env)->GetMethodID(env, scHashMapClass, "entrySet", "()Ljava/util/Set;"); 
+  containsKey_mid = (*env)->GetMethodID(env, scHashMapClass, "containsKey", "(Ljava/lang/Object;)Z");
+  get_mid = (*env)->GetMethodID(env, scHashMapClass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); 
+
+  if(containsKey_mid==0){
+#ifdef DEBUG
+    fprintf(stderr,"unable to load containsKey from HashMap object (%d) \n",entrySet_mid);
+#endif
+    return NULL;
+  }
+  if(get_mid==0){
+#ifdef DEBUG
+    fprintf(stderr,"unable to load get from HashMap object (%d) \n",entrySet_mid);
+#endif
+    return NULL;
+  }
+  if(entrySet_mid==0){
+#ifdef DEBUG
+    fprintf(stderr,"unable to load entrySet from HashMap object (%d) \n",entrySet_mid);
+#endif
+    return NULL;
+  }
+#ifdef DEBUG
+  else
+    fprintf(stderr,"entrySet loaded from HashMap object (%d) \n",entrySet_mid);
+#endif
+
+  scSetClass = (*env)->FindClass(env, "java/util/Set");
+  iterator_mid = (*env)->GetMethodID(env, scSetClass, "iterator", "()Ljava/util/Iterator;"); 
+#ifdef DEBUG
+  fprintf(stderr,"mapsFromHashMap 1 (%d) \n",iterator_mid);
+#endif
+
+  scIteratorClass = (*env)->FindClass(env, "java/util/Iterator");
+  hasNext_mid = (*env)->GetMethodID(env, scIteratorClass, "hasNext", "()Z");
+#ifdef DEBUG
+  fprintf(stderr,"mapsFromHashMap 2 (%d)\n",hasNext_mid);
+#endif
+  next_mid = (*env)->GetMethodID(env, scIteratorClass, "next", "()Ljava/lang/Object;");
+#ifdef DEBUG
+  fprintf(stderr,"mapsFromHashMap 3 (%d)\n",next_mid);
+#endif
+
+  scMapEntryClass = (*env)->FindClass(env, "java/util/Map$Entry");
+  getKey_mid = (*env)->GetMethodID(env, scMapEntryClass, "getKey", "()Ljava/lang/Object;");
+#ifdef DEBUG
+  fprintf(stderr,"mapsFromHashMap 4 (%d)\n",getKey_mid);
+#endif
+  getValue_mid = (*env)->GetMethodID(env, scMapEntryClass, "getValue", "()Ljava/lang/Object;");
+#ifdef DEBUG
+  fprintf(stderr,"mapsFromHashMap 5 (%d)\n",getValue_mid);
+#endif
+
+  jobject final_set=(*env)->CallObjectMethod(env,t,entrySet_mid);
+  jobject final_iterator=(*env)->CallObjectMethod(env,final_set,iterator_mid);
+
+
+  maps* final_res=NULL;
+  map* res=NULL;
+  while((*env)->CallBooleanMethod(env,final_iterator,hasNext_mid)){
+    jobject tmp=(*env)->CallObjectMethod(env,final_iterator,next_mid);
+
+    jobject imap=(*env)->CallObjectMethod(env,tmp,getValue_mid);
+    jobject set=(*env)->CallObjectMethod(env,imap,entrySet_mid);
+    jobject iterator=(*env)->CallObjectMethod(env,set,iterator_mid);
+
+    int size=-1;
+    if((*env)->CallBooleanMethod(env,imap,containsKey_mid,(*env)->NewStringUTF(env,"size"))){
+      jobject sizeV=(*env)->CallObjectMethod(env, imap, get_mid,(*env)->NewStringUTF(env,"size"));
+      const char* sizeVS=(*env)->GetStringUTFChars(env, sizeV, NULL);
+      size=atoi(sizeVS);
+      fprintf(stderr,"SIZE : %s\n",sizeVS);
+      (*env)->ReleaseStringUTFChars(env, sizeV, sizeVS);
+    }
+    
+    while((*env)->CallBooleanMethod(env,iterator,hasNext_mid)){
+      jobject tmp1=(*env)->CallObjectMethod(env,iterator,next_mid);
+      jobject jk=(*env)->CallObjectMethod(env,tmp1,getKey_mid);
+      jobject jv=(*env)->CallObjectMethod(env,tmp1,getValue_mid);
+
+      const char* jkd=(*env)->GetStringUTFChars(env, jk, NULL);
+      if(size>=0 && strcmp(jkd,"value")==0){
+	jobject value=(*env)->GetByteArrayElements(env, jv, NULL);
+	if(res==NULL){
+	  res=createMap(jkd,"");
+	}else{
+	  addToMap(res,jkd,"");
+	}
+	map* tmpR=getMap(res,"value");
+	free(tmpR->value);
+	tmpR->value=(char*)malloc((size+1)*sizeof(char));
+	memmove(tmpR->value,value,size*sizeof(char));
+	tmpR->value[size]=0;
+	char tmp[128];
+	sprintf(tmp,"%d",size);
+	addToMap(res,"size",tmp);
+      }
+      else{
+	const char* jvd=(*env)->GetStringUTFChars(env, jv, NULL);
+	if(res==NULL){
+	  res=createMap(jkd,jvd);
+	}else{
+	  addToMap(res,jkd,jvd);
+	}
+	(*env)->ReleaseStringUTFChars(env, jv, jvd);
+      }
+
+      (*env)->ReleaseStringUTFChars(env, jk, jkd);
+
+    }
+    jobject jk=(*env)->CallObjectMethod(env,tmp,getKey_mid);
+    maps* cmap=(maps*)malloc(sizeof(maps));
+    cmap->name=(*env)->GetStringUTFChars(env, jk, NULL);
+#ifdef DEBUG
+    fprintf(stderr," / %s \n",cmap->name);
+#endif
+    cmap->content=res;
+    cmap->next=NULL;
+    if(final_res==NULL)
+      final_res=dupMaps(&cmap);
+    else
+      addMapsToMaps(&final_res,cmap);
+    freeMaps(&cmap);
+    free(cmap);
+    cmap=NULL;
+    res=NULL;
+  }
+#ifdef DEBUG
+  fprintf(stderr,"mapsFromHashMap end\n");
+#endif
+
+  return final_res;
+}
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_java.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_java.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_java.h	(revision 221)
@@ -0,0 +1,46 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef ZOO_SERVICE_INTERNAL_JAVA_H
+#define ZOO_SERVICE_INTERNAL_JAVA_H 1
+
+#pragma once 
+
+#include "service.h"
+#include "service_internal.h"
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  jobject HashMap_FromMaps(JNIEnv*,maps*,jclass,jclass,jmethodID);
+  
+  maps* mapsFromHashMap(JNIEnv*,jobject,jclass);
+  
+  int zoo_java_support(maps**,map*,service*,maps**,maps**);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_js.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_js.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_js.c	(revision 221)
@@ -0,0 +1,464 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "service_internal.h"
+
+static char dbg[1024];
+
+int zoo_js_support(maps** main_conf,map* request,service* s,
+		   maps **inputs,maps **outputs)
+{
+  maps* main=*main_conf;
+  maps* _inputs=*inputs;
+  maps* _outputs=*outputs;
+
+  /* The class of the global object. */
+  JSClass global_class = {
+    "global", JSCLASS_GLOBAL_FLAGS,
+    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+  };
+
+  /* JS variables. */
+  JSRuntime *rt;
+  JSContext *cx;
+  JSObject  *global;
+
+  /* Create a JS runtime. */
+  rt = JS_NewRuntime(8L * 1024L * 1024L);
+  if (rt == NULL)
+    return 1;
+  
+  /* Create a context. */
+  cx = JS_NewContext(rt,8192);
+  if (cx == NULL){
+    return 1;
+  }
+  JS_SetOptions(cx, JSOPTION_VAROBJFIX);
+  JS_SetVersion(cx, JSVERSION_LATEST);
+  JS_SetErrorReporter(cx, reportError);
+
+  /* Create the global object. */
+  global = JS_NewObject(cx, &global_class, NULL, NULL);
+  if (global == NULL){
+    return 1;
+  }
+
+  /* Populate the global object with the standard globals,
+     like Object and Array. */
+  if (!JS_InitStandardClasses(cx, global)){
+    return 1;
+  }
+  if (!JS_DefineFunction(cx, global, "ZOORequest", JSRequest, 4, 0))
+    return 1;
+  if (!JS_DefineFunction(cx, global, "ZOOUpdateStatus", JSUpdateStatus, 2, 0))
+    return 1;
+
+  map* tmpm1=getMap(request,"metapath");
+  char ntmp[1024];
+  getcwd(ntmp,1024);
+
+  /**
+   * Load the first part of the ZOO-API
+   */
+  char api0[strlen(tmpm1->value)+strlen(ntmp)+15];
+  sprintf(api0,"%s/%sZOO-proj4js.js",ntmp,tmpm1->value);
+  fprintf(stderr,"Trying to load %s\n",api0);
+  JSScript *api_script1=loadZooApiFile(cx,global,api0);
+  fflush(stderr);
+
+  char api1[strlen(tmpm1->value)+strlen(ntmp)+11];
+  sprintf(api1,"%s/%sZOO-api.js",ntmp,tmpm1->value);
+  fprintf(stderr,"Trying to load %s\n",api1);
+  JSScript *api_script2=loadZooApiFile(cx,global,api1);
+  fflush(stderr);
+
+  /* Your application code here. This may include JSAPI calls
+     to create your own custom JS objects and run scripts. */
+  maps* out=*outputs;
+  int res=SERVICE_FAILED;
+  maps* mc=*main_conf;
+  map* tmpm2=getMap(s->content,"serviceProvider");
+
+  char filename[strlen(tmpm1->value)+strlen(tmpm2->value)+strlen(ntmp)+2];
+  sprintf(filename,"%s/%s%s",ntmp,tmpm1->value,tmpm2->value);
+  filename[strlen(tmpm1->value)+strlen(tmpm2->value)+strlen(ntmp)+1]=0;
+  fprintf(stderr,"FILENAME %s\n",filename);
+  struct stat file_status;
+  stat(filename, &file_status);
+  char source[file_status.st_size];
+  uint16 lineno;
+  jsval rval;
+  FILE *jsfile=fopen(filename,"r");
+  JSBool ok ;
+  JSScript *script = JS_CompileFileHandle(cx, global, filename,jsfile);
+  if(script!=NULL){
+    (void)JS_ExecuteScript(cx, global, script, &rval);
+  }
+  else{
+    char tmp1[1024];
+    sprintf(tmp1,"Unable to load JavaScript file %s",filename);
+    map* err=createMap("text",tmp1);
+    addMapToMap(&err,createMap("code","NoApplicableCode"));
+    printExceptionReportResponse(mc,err);
+    JS_DestroyContext(cx);
+    JS_DestroyRuntime(rt);
+    JS_ShutDown();
+    fclose(jsfile);
+    exit(-1);
+  }
+  /* Call a function in obj's scope. */
+  jsval argv[3];
+  JSObject *jsargv1=JSObject_FromMaps(cx,*main_conf);
+  argv[0] = OBJECT_TO_JSVAL(jsargv1);
+  JSObject *jsargv2=JSObject_FromMaps(cx,*inputs);
+  argv[1] = OBJECT_TO_JSVAL(jsargv2);
+  JSObject *jsargv3=JSObject_FromMaps(cx,*outputs);
+  argv[2] = OBJECT_TO_JSVAL(jsargv3);
+  jsval rval1=JSVAL_NULL;
+#ifdef JS_DEBUG
+  fprintf(stderr, "object %p\n", (void *) argv[2]);
+#endif
+
+  ok = JS_CallFunctionName(cx, global, s->name, 3, argv, &rval1);
+
+#ifdef JS_DEBUG
+  fprintf(stderr, "object %p\n", (void *) argv[2]);
+#endif
+
+  JSObject *d;
+  if (ok==JS_TRUE && JSVAL_IS_OBJECT(rval1)==JS_TRUE) {
+#ifdef JS_DEBUG
+    fprintf(stderr,"Function run sucessfully !\n");
+#endif
+    /* Should get a number back from the service function call. */
+    ok = JS_ValueToObject(cx, rval1, &d);
+  }else{
+    /* Unable to run JS function */
+    char tmp1[1024];
+    if(strlen(dbg)==0)
+      sprintf(dbg,"No result was found after the function call");
+    sprintf(tmp1,"Unable to run %s from the JavScript file %s : \n %s",s->name,filename,dbg);
+    fprintf(stderr,"%s",tmp1);
+    map* err=createMap("text",tmp1);
+    addToMap(err,"code","NoApplicableCode");
+    printExceptionReportResponse(*main_conf,err);
+    freeMap(&err);
+    free(err);
+    JS_DestroyContext(cx);
+    JS_DestroyRuntime(rt);
+    JS_ShutDown();
+    // Should return -1 here but the unallocation won't work from zoo_service_loader.c line 1847
+    exit(-1);
+  }
+
+  jsval t=OBJECT_TO_JSVAL(d);
+  if(JS_IsArrayObject(cx,d)){
+#ifdef JS_DEBUG
+    fprintf(stderr,"An array was returned !\n");
+#endif
+    jsint len;
+    if((JS_GetArrayLength(cx, d, &len)==JS_FALSE)){
+#ifdef JS_DEBUG
+      fprintf(stderr,"outputs array is empty\n");
+#endif
+    }
+    jsval tmp1;
+    JSBool hasResult=JS_GetElement(cx,d,0,&tmp1);
+    res=JSVAL_TO_INT(tmp1);
+#ifdef JS_DEBUG
+    fprintf(stderr," * %d * \n",res);
+#endif
+    jsval tmp2;
+    JSBool hasElement=JS_GetElement(cx,d,1,&tmp2);
+    if(hasElement==JS_TRUE){
+      *outputs=mapsFromJSObject(cx,tmp2);
+    }
+  }
+  else{
+#ifdef JS_DEBUG
+    fprintf(stderr,"The serice didn't return an array !\n");
+#endif
+    jsval tmp1;
+    JSBool hasResult=JS_GetProperty(cx,d,"result",&tmp1);
+    res=JSVAL_TO_INT(tmp1);
+
+#ifdef JS_DEBUG
+    fprintf(stderr," * %d * \n",res);
+#endif
+    jsval tmp2;
+    JSBool hasElement=JS_GetProperty(cx,d,"outputs",&tmp2);
+    if(!hasElement)
+      fprintf(stderr,"No outputs property returned\n");
+    if(JS_IsArrayObject(cx,JSVAL_TO_OBJECT(tmp2)))
+      fprintf(stderr,"outputs is array an as expected\n");
+    else
+      fprintf(stderr,"outputs is not an array as expected\n");
+    *outputs=mapsFromJSObject(cx,tmp2);
+#ifdef JS_DEBUG
+    dumpMaps(out);
+#endif
+  }
+
+  /* Cleanup. */
+  JS_DestroyScript(cx, script);
+  JS_DestroyScript(cx, api_script1);
+  JS_DestroyScript(cx, api_script2);
+  //JS_MaybeGC(cx);
+  // If we use the DestroyContext as requested to release memory then we get 
+  // issue getting back the main configuration maps after coming back to the 
+  // runRequest function ...
+  //JS_DestroyContext(cx);
+  JS_DestroyRuntime(rt);
+  JS_ShutDown();
+#ifdef JS_DEBUG
+  fprintf(stderr,"Returned value %d\n",res);
+#endif
+  return res;
+}
+
+JSScript * loadZooApiFile(JSContext *cx,JSObject  *global, char* filename){
+  struct stat api_status;
+  int s=stat(filename, &api_status);
+  if(s==0){
+    jsval rval;
+    FILE *jsfile=fopen(filename,"r");
+    JSBool ok ;
+    JSScript *script = JS_CompileFileHandle(cx, global, filename,jsfile);
+    if(script!=NULL){
+      (void)JS_ExecuteScript(cx, global, script, &rval);
+      fprintf(stderr,"**************\n%s correctly loaded\n**************\n",filename);
+      return script;
+    }
+    else
+      fprintf(stderr,"\n**************\nUnable to run %s\n**************\n",filename);
+  }
+  else
+    fprintf(stderr,"\n**************\nUnable to load %s\n**************\n",filename);
+  return NULL;
+}
+
+JSObject* JSObject_FromMaps(JSContext *cx,maps* t){
+  JSObject *res = JS_NewArrayObject(cx, 0, NULL);
+  if(res==NULL)
+    fprintf(stderr,"Array Object is NULL!\n");
+  maps* tmp=t;
+  while(tmp!=NULL){
+    jsuint len;
+    JSObject* res1=JS_NewObject(cx, NULL, NULL, NULL);
+    JSObject *pval=JSObject_FromMap(cx,tmp->content);
+    jsval pvalj=OBJECT_TO_JSVAL(pval);
+    JS_SetProperty(cx, res1, tmp->name, &pvalj);
+    JS_GetArrayLength(cx, res, &len);
+    jsval res1j = OBJECT_TO_JSVAL(res1);
+    JS_SetElement(cx,res,len,&res1j);
+#ifdef JS_DEBUG
+    fprintf(stderr,"Length of the Array %d, element : %s added \n",len,tmp->name);
+#endif
+    tmp=tmp->next;
+  }  
+  return res;
+}
+
+JSObject* JSObject_FromMap(JSContext *cx,map* t){
+  JSObject* res=JS_NewObject(cx, NULL, NULL, NULL);
+  jsval resf =  OBJECT_TO_JSVAL(res);
+  map* tmpm=t;
+  while(tmpm!=NULL){
+    jsval jsstr = STRING_TO_JSVAL(JS_NewString(cx,tmpm->value,strlen(tmpm->value)));
+    JS_SetProperty(cx, res, tmpm->name,&jsstr);
+#ifdef JS_DEBUG
+    fprintf(stderr,"%s => %s\n",tmpm->name,tmpm->value);
+#endif
+    tmpm=tmpm->next;
+  }
+  return res;
+}
+
+maps* mapsFromJSObject(JSContext *cx,jsval t){
+  maps *res=NULL;
+  maps *tres=NULL;
+  jsint oi=0;
+  JSObject* tt=JSVAL_TO_OBJECT(t);
+#ifdef JS_DEBUG
+  fprintf(stderr,"Is finally an array ?\n");
+  if(JS_IsArrayObject(cx,tt)){
+    fprintf(stderr,"Is finally an array !\n");
+  }
+  else
+    fprintf(stderr,"Is not an array !\n");
+#endif
+  jsint len;
+  JSBool hasLen=JS_GetArrayLength(cx, tt, &len);
+  if(hasLen==JS_FALSE){
+#ifdef JS_DEBUG
+    fprintf(stderr,"outputs array is empty\n");
+#endif
+  }
+#ifdef JS_DEBUG
+  fprintf(stderr,"outputs array length : %d\n",len);
+#endif
+  for(oi=0;oi < len;oi++){
+#ifdef JS_DEBUG
+    fprintf(stderr,"outputs array length : %d step %d \n",len,oi);
+#endif
+    jsval tmp1;
+    JSBool hasElement=JS_GetElement(cx,tt,oi,&tmp1);
+    JSObject *otmp1=JSVAL_TO_OBJECT(tmp1);
+    JSIdArray *idp=JS_Enumerate(cx,otmp1);
+    if(idp!=NULL) {
+      int index;
+      jsdouble argNum;
+#ifdef JS_DEBUG
+      fprintf(stderr,"Properties length :  %d \n",idp->length);
+#endif
+      for (index=0,argNum=idp->length;index<argNum;index++) { 
+	jsval id = idp->vector[index];
+	jsval vp;
+	JSString* str; 
+	JS_IdToValue(cx,id,&vp);
+	char *c, *tmp;
+	JSString *jsmsg;
+	size_t len1;
+	jsmsg = JS_ValueToString(cx,vp);
+	len1 = JS_GetStringLength(jsmsg);
+#ifdef JS_DEBUG
+	fprintf(stderr,"Enumerate id : %d => %s\n",oi,JS_GetStringBytes(jsmsg));
+#endif
+	jsval nvp=JSVAL_NULL;
+	if((JS_GetProperty(cx, JSVAL_TO_OBJECT(tmp1), JS_GetStringBytes(jsmsg), &nvp)==JS_FALSE))
+#ifdef JS_DEBUG
+	fprintf(stderr,"Enumerate id : %d => %s => No more value\n",oi,JS_GetStringBytes(jsmsg));
+#endif
+	if(JSVAL_IS_OBJECT(nvp)){
+#ifdef JS_DEBUG
+	  fprintf(stderr,"JSVAL NVP IS OBJECT\n");
+#endif
+	}
+#ifdef JS_DEBUG
+	else
+	  fprintf(stderr,"JSVAL NVP IS NOT OBJECT !!\n");
+#endif
+	JSObject *nvp1;
+	JS_ValueToObject(cx,nvp,&nvp1);
+	jsval nvp1j=OBJECT_TO_JSVAL(nvp1);
+	if(JSVAL_IS_OBJECT(nvp1j)){
+#ifdef JS_DEBUG
+	  fprintf(stderr,"JSVAL NVP1J IS OBJECT\n");
+#endif
+	  tres=(maps*)malloc(MAPS_SIZE);
+	  tres->name=strdup(JS_GetStringBytes(jsmsg));
+	  tres->content=mapFromJSObject(cx,nvp1j);
+	  tres->next=NULL;
+#ifdef JS_DEBUG
+	  dumpMaps(res);
+#endif
+	  if(res==NULL)
+	    res=dupMaps(&tres);
+	  else
+	    addMapsToMaps(&res,tres);
+	  freeMaps(&tres);
+	  free(tres);
+	  tres=NULL;
+#ifdef JS_DEBUG
+	  dumpMaps(res);
+#endif
+	}
+#ifdef JS_DEBUG
+	else
+	  fprintf(stderr,"JSVAL NVP1J IS NOT OBJECT !!\n");
+#endif
+      }
+    }
+  }
+#ifdef JS_DEBUG
+  dumpMaps(res);
+#endif
+  return res;
+}
+
+map* mapFromJSObject(JSContext *cx,jsval t){
+  map *res=NULL;
+  JSIdArray *idp=JS_Enumerate(cx,JSVAL_TO_OBJECT(t));
+#ifdef JS_DEBUG
+  fprintf(stderr,"Properties %p\n",(void*)t);
+#endif
+  if(idp!=NULL) {
+    int index;
+    jsdouble argNum;
+#ifdef JS_DEBUG
+    fprintf(stderr,"Properties length :  %d \n",idp->length);
+#endif
+    for (index=0,argNum=idp->length;index<argNum;index++) { 
+      jsval id = idp->vector[index];
+      jsval vp;
+      JSString* str; 
+      JS_IdToValue(cx,id,&vp);
+      char *c, *tmp;
+      JSString *jsmsg,*jsmsg1;
+      size_t len,len1;
+      jsmsg = JS_ValueToString(cx,vp);
+      len = JS_GetStringLength(jsmsg);
+      jsval nvp;
+      JS_GetProperty(cx, JSVAL_TO_OBJECT(t), JS_GetStringBytes(jsmsg), &nvp);
+      jsmsg1 = JS_ValueToString(cx,nvp);
+      len1 = JS_GetStringLength(jsmsg1);
+#ifdef JS_DEBUG
+      fprintf(stderr,"Enumerate id : %d [ %s => %s ]\n",index,JS_GetStringBytes(jsmsg),JS_GetStringBytes(jsmsg1));
+#endif
+      if(res!=NULL){
+#ifdef JS_DEBUG
+	fprintf(stderr,"%s - %s\n",JS_GetStringBytes(jsmsg),JS_GetStringBytes(jsmsg1));
+#endif
+	addToMap(res,JS_GetStringBytes(jsmsg),JS_GetStringBytes(jsmsg1));
+      }
+      else{
+	res=createMap(JS_GetStringBytes(jsmsg),JS_GetStringBytes(jsmsg1));
+	res->next=NULL;
+      }
+#ifdef JS_DEBUG
+      dumpMap(res);
+#endif
+    }
+  }
+#ifdef JS_DEBUG
+  dumpMap(res);
+#endif
+  return res;
+}
+
+/* The error reporter callback. */
+void reportError(JSContext *cx, const char *message, JSErrorReport *report)
+{
+  sprintf(dbg,"%s:%u:%s\n",
+	  report->filename ? report->filename : "<no filename>",
+	  (unsigned int) report->lineno,
+	  message);
+#ifdef JS_DEBUG
+  fprintf(stderr,"%s",dbg);
+#endif
+  fflush(stderr);
+}
+
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_js.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_js.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_js.h	(revision 221)
@@ -0,0 +1,60 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef ZOO_SERVICE_INTERNAL_JS_H
+#define ZOO_SERVICE_INTERNAL_JS_H 1
+
+#pragma once 
+
+#define XP_UNIX 0
+
+#include "service.h"
+#include "service_internal.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "jsapi.h"
+#include "ulinet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  char* JSValToChar(JSContext*, jsval*);
+  JSObject* JSObject_FromMaps(JSContext *,maps*);
+  JSObject* JSObject_FromMap(JSContext *,map*);
+  maps* mapsFromJSObject(JSContext *,jsval);
+  map* mapFromJSObject(JSContext *,jsval);
+
+  void reportError(JSContext *cx, const char *message, JSErrorReport *report);
+  
+  int zoo_js_support(maps**,map*,service*,maps **,maps **);
+
+  JSScript *loadZooApiFile(JSContext*,JSObject*,char*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_perl.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_perl.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_perl.c	(revision 221)
@@ -0,0 +1,201 @@
+/**
+ * Author : David SAGGIORATO
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "service_internal_perl.h"
+
+
+static PerlInterpreter *my_perl;
+
+
+void xs_init(pTHX)
+{
+	char *file = __FILE__;
+	dXSUB_SYS;
+	
+	/* DynaLoader is a special case */
+	newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+}
+
+
+
+int map_to_hash(map * m, HV ** hash_map) {
+	HV * tmp = *hash_map;
+	map * tmp_m = m;
+	do {
+		//printf("map name %s  value %s \n",m->name,m->value);
+
+		if ( NULL == hv_store( tmp, tmp_m->name, strlen(tmp_m->name), sv_2mortal(newSVpv(tmp_m->value, strlen(tmp_m->value))), 0) ) {
+			return 1;
+			}
+		tmp_m = tmp_m->next;
+		}
+	while (tmp_m != NULL);
+	return 0;
+}
+
+int maps_to_hash( maps* m, HV ** hash_maps){
+	HV * tmp = *hash_maps;
+	if (m != NULL) {
+		//printf("maps name %s \n",m->name);
+		HV* hash_m = (HV *)sv_2mortal((SV *)newHV());
+		if (map_to_hash(m->content,&hash_m) != 0){
+			return 1;
+		}
+		
+		if ( NULL == hv_store( tmp, m->name, strlen(m->name),sv_2mortal(newRV_inc((SV *)hash_m)), 0) ) {
+			return 1;
+		}	
+		return maps_to_hash(m->next,hash_maps);
+	}
+	return 0;
+}
+
+int hash_to_map(HV * hh,map ** m){
+	hv_iterinit(hh);
+	*m = (map *)malloc(MAP_SIZE);
+	if (*m == NULL){
+		// erreur d'allocation memoire
+		return 1;
+	}
+	map * tmp = *m;
+	HE * he = hv_iternext(hh);
+	while (he != NULL){
+		//fprintf(stderr,"key : %s  value : %s \n",HeKEY(he),(char *)SvRV(HeVAL(he)));
+		tmp->name = HeKEY(he);
+		tmp->value = (char *)SvRV(HeVAL(he));
+		he = hv_iternext(hh);
+		if(he != NULL){
+			tmp->next = (map *)malloc(MAP_SIZE);
+			if (tmp->next == NULL){
+				//erreur allocation memoire
+				return 1;
+			}
+			tmp=tmp->next;
+		}
+		else {
+			tmp->next = NULL;
+		}
+
+	}
+
+	return 1;
+}
+	
+int hash_to_maps(HV * hh,maps** m){
+	hv_iterinit(hh);
+	*m = (maps *)malloc(MAPS_SIZE);
+	maps * tmp = *m;
+	HE * he = hv_iternext(hh);
+	map *mm;
+	while (he != NULL) {
+		//fprintf(stderr,"key ===> %s \n",HeKEY(he));
+		tmp->name = HeKEY(he);
+		hash_to_map((HV *) SvRV(HeVAL(he)),&mm);
+		tmp->content = mm;
+		he = hv_iternext(hh);
+		if (he != NULL){
+			tmp->next = (maps *)malloc(MAPS_SIZE);
+			tmp= tmp->next;
+		}
+		else {
+			tmp->next = NULL;
+		}		
+	}
+	return 1;
+}
+	
+int zoo_perl_support(maps** main_conf,map* request,service* s,maps **real_inputs,maps **real_outputs){
+	maps* m=*main_conf;
+ 	maps* inputs=*real_inputs;
+	maps* outputs=*real_outputs;
+  	int res=SERVICE_FAILED;
+  	map * tmp=getMap(s->content,"serviceProvide");
+
+	char *my_argv[] = { "", tmp->value };
+	if ((my_perl = perl_alloc()) == NULL){
+		fprintf(stderr,"no memmory");
+		exit(1);
+	}
+	perl_construct( my_perl );
+	perl_parse(my_perl, xs_init, 2, my_argv, (char **)NULL);
+	perl_run(my_perl);
+	
+
+	HV* h_main_conf = (HV *)sv_2mortal((SV *)newHV());
+	HV* h_real_inputs = (HV *)sv_2mortal((SV *)newHV());
+	HV* h_real_outputs = (HV *)sv_2mortal((SV *)newHV());
+	maps_to_hash(m,&h_main_conf);
+	maps_to_hash(inputs,&h_real_inputs);
+	maps_to_hash(outputs,&h_real_outputs);
+	dSP;
+    	ENTER;
+    	SAVETMPS;
+    	PUSHMARK(SP);
+	XPUSHs(sv_2mortal(newRV_inc((SV *)h_main_conf)));
+	XPUSHs(sv_2mortal(newRV_inc((SV *)h_real_inputs)));
+	XPUSHs(sv_2mortal(newRV_inc((SV *)h_real_outputs)));
+	PUTBACK;
+	call_pv(s->name, G_SCALAR);
+	SPAGAIN;
+	res = POPi;
+	hash_to_maps(h_real_outputs,real_outputs);
+	//dumpMaps(*real_outputs);
+	PUTBACK;
+    	FREETMPS;
+    	LEAVE;
+	return SERVICE_SUCCEEDED;
+}
+
+	
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_perl.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_perl.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_perl.h	(revision 221)
@@ -0,0 +1,64 @@
+/**
+ * Author : David SAGGIORATO
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef ZOO_SERVICE_INTERNAL_PERL_H
+#define ZOO_SERVICE_INTERNAL_PERL_H 1
+
+#pragma once 
+
+#include "service.h"
+#include "service_internal.h"
+#include <stdio.h>
+#include <EXTERN.h>
+#include <perl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void xs_init (pTHX);
+
+void boot_DynaLoader (pTHX_ CV*);
+
+int map_to_hash(map*, HV** );
+
+int maps_to_hash(maps *, HV ** );
+
+int hash_to_map(HV* ,map**);
+
+int hash_to_maps(HV* ,maps** );
+
+int zoo_perl_support(maps**,map*,service*,maps**,maps**);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+
+
+
+
+
+
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_php.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_php.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_php.c	(revision 221)
@@ -0,0 +1,238 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "service_internal_php.h"
+
+#ifdef ZTS
+void ***tsrm_ls;
+#endif
+
+int zoo_php_support(maps** main_conf,map* request,service* s,maps **real_inputs,maps **real_outputs){
+  fprintf(stderr,"STARING PHP SCRIPT\n");
+  maps* m=*main_conf;
+  maps* inputs=*real_inputs;
+  maps* outputs=*real_outputs;
+  char ntmp[1024];
+  getcwd(ntmp,1024);
+  map* tmp=getMap(request,"metapath");
+
+  int res=SERVICE_FAILED;
+
+  tmp=getMap(s->content,"serviceProvider");
+  zend_file_handle iscript;
+  iscript.type=ZEND_HANDLE_FP;
+  iscript.filename=tmp->value;
+  iscript.opened_path=NULL;
+  //iscript.free_filname=0;
+  if(!(iscript.handle.fp=fopen(iscript.filename,"rb"))){
+    char tmp1[1024];
+    sprintf(tmp1,"Unable to load PHP file %s",tmp->value);
+    map* err=createMap("text",tmp1);
+    addToMap(err,"code","NoApplicableCode");
+    printExceptionReportResponse(m,err);
+    exit(-1);
+  }
+
+  php_embed_init(0,NULL,&tsrm_ls);
+
+  fprintf(stderr,"PHP EMBEDED\n");
+  
+  zend_try {
+    fprintf(stderr,"PHP EMBEDED include script %s\n",tmp->value);
+    php_execute_script(&iscript TSRMLS_CC);
+    fprintf(stderr,"PHP EMBEDED include script done\n");
+
+    zval *iargs[3];
+    zval iret, ifunc,ifile;
+      
+    ZVAL_STRING(&ifunc, s->name, 0);
+    fprintf(stderr,"PHP EMBEDED include script done\n");
+    iargs[0] = php_Array_from_maps(*main_conf);
+    fprintf(stderr,"PHP EMBEDED include script done\n");
+    iargs[1] = php_Array_from_maps(*real_inputs);
+    fprintf(stderr,"PHP EMBEDED include script done\n");
+    iargs[2] = php_Array_from_maps(*real_outputs);
+    fprintf(stderr,"PHP EMBEDED include script done\n");
+    
+    call_user_function(EG(function_table), NULL, &ifunc, &iret, 3, iargs TSRMLS_CC);
+
+    HashTable* t=HASH_OF(iargs[2]);
+    *real_outputs=php_maps_from_Array(t);
+    
+    dumpMaps(*real_outputs);
+
+    char tmp1[1024];
+
+    sprintf(tmp1,"PHP EMBEDED ran function successfully and return %d !?",Z_STRVAL(iret));
+
+    res=SERVICE_SUCCEEDED;
+
+  } zend_catch { 
+    map* err=createMap("text","Unable to process.");
+    addToMap(err,"code","NoApplicableCode");
+    printExceptionReportResponse(m,err);
+    exit(-1);
+  } zend_end_try();
+
+  php_embed_shutdown(TSRMLS_C);
+
+  return res;
+}
+
+zval *php_Array_from_maps(maps* t){
+  zval *mapArray;
+  zval *mapArrayTmp;
+  maps* tmp=t;
+  int tres=0;
+  fprintf(stderr,"arra_init\n");
+  MAKE_STD_ZVAL(mapArray);
+  tres=array_init(mapArray);
+  fprintf(stderr,"arra_init %d\n",tres);
+  while(tmp!=NULL){
+    add_assoc_zval(mapArray,tmp->name,php_Array_from_map(tmp->content));
+    tmp=tmp->next;
+  }
+  return mapArray;
+}
+
+zval *php_Array_from_map(map* t){
+  zval *mapArray;
+  zval *mapArrayTmp;
+  map* tmp=t;
+  int tres=0;
+  fprintf(stderr,"arra_init\n");
+  MAKE_STD_ZVAL(mapArray);
+  tres=array_init(mapArray);
+  fprintf(stderr,"arra_init\n");
+  while(tmp!=NULL){
+    fprintf(stderr,"=> %s %d %s %d \n",tmp->name,strlen(tmp->name),tmp->value,strlen(tmp->value));
+    tres=add_assoc_string(mapArray,tmp->name,tmp->value,1);
+    tmp=tmp->next;
+  }
+  return mapArray;
+}
+
+maps* php_maps_from_Array(HashTable *t){
+  //#ifdef DEBUG
+  fprintf(stderr,"mapsFromPHPArray start\n");
+  //#endif
+  maps* final_res=NULL;
+  maps* cursor=final_res;
+  char key[1024];
+  for(zend_hash_internal_pointer_reset(t); 
+      zend_hash_has_more_elements(t) == SUCCESS; 
+      zend_hash_move_forward(t)) { 
+    char *key; 
+    uint keylen; 
+    ulong idx; 
+    int type; 
+    zval **ppzval, tmpcopy; 
+    type = zend_hash_get_current_key_ex(t, &key, &keylen, &idx, 0, NULL); 
+    fprintf(stderr,"key : %s\n",key);
+    if (zend_hash_get_current_data(t, (void**)&ppzval) == FAILURE) { 
+      /**
+       * Should never actually fail since the key is known to exist.
+       */
+      continue; 
+    }
+    /**
+     * Duplicate the zval so that * the orignal’s contents are not destroyed
+     */
+    tmpcopy = **ppzval;
+    fprintf(stderr,"key : %s\n",key);
+    zval_copy_ctor(&tmpcopy); 
+    fprintf(stderr,"key : %s\n",key);
+    /**
+     * Reset refcount & Convert
+     */
+    INIT_PZVAL(&tmpcopy); 
+    //convert_to_string(&tmpcopy); 
+    if (type == HASH_KEY_IS_STRING) { 
+      /**
+       * String Key / Associative
+       */
+      cursor=(maps*)malloc(MAPS_SIZE);
+      cursor->name=strdup(key);
+    }
+    fprintf(stderr,"key : %s\n",key);
+    HashTable* t=HASH_OF(*ppzval);
+    fprintf(stderr,"key : %s\n",key);
+    cursor->content=php_map_from_HasTable(t);
+    cursor->next=NULL;
+    if(final_res==NULL)
+      final_res=cursor;
+    else
+      addMapsToMaps(&final_res,cursor);
+    fprintf(stderr,"key : %s\n",key);
+    /**
+     * Toss out old copy
+     */
+    zval_dtor(&tmpcopy);
+  }
+  return final_res;
+}
+
+map* php_map_from_HasTable(HashTable* t){
+#ifdef DEBUG
+  fprintf(stderr,"mapsFromPHPArray start\n");
+#endif
+  map* final_res=(map*)malloc(MAP_SIZE);
+  final_res=NULL;
+  char key[1024];
+  for(zend_hash_internal_pointer_reset(t); 
+      zend_hash_has_more_elements(t) == SUCCESS; 
+      zend_hash_move_forward(t)) { 
+    char *key; 
+    uint keylen; 
+    ulong idx; 
+    int type; 
+    zval **ppzval, tmpcopy; 
+    type = zend_hash_get_current_key_ex(t, &key, &keylen, &idx, 0, NULL); 
+    if (zend_hash_get_current_data(t, (void**)&ppzval) == FAILURE) { 
+      /* Should never actually fail * since the key is known to exist. */ 
+      continue; 
+    }
+    /**
+     * Duplicate the zval so that * the orignal’s contents are not destroyed 
+     */ 
+    tmpcopy = **ppzval; 
+    zval_copy_ctor(&tmpcopy); 
+    /**
+     * Reset refcount & Convert 
+     */ 
+    INIT_PZVAL(&tmpcopy); 
+    convert_to_string(&tmpcopy); 
+    if(final_res==NULL){
+      fprintf(stderr,"%s => %s\n",key,Z_STRVAL(tmpcopy));
+      final_res=createMap(key,Z_STRVAL(tmpcopy));
+    }
+    else{
+      fprintf(stderr,"%s => %s\n",key,Z_STRVAL(tmpcopy));
+      addMapToMap(&final_res,createMap(key,Z_STRVAL(tmpcopy)));
+    }
+    /* Toss out old copy */ 
+    zval_dtor(&tmpcopy); 
+  }
+  return final_res;
+}
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_php.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_php.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_php.h	(revision 221)
@@ -0,0 +1,49 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef ZOO_SERVICE_INTERNAL_PHP_H
+#define ZOO_SERVICE_INTERNAL_PHP_H 1
+
+#pragma once 
+
+#include "service.h"
+#include "service_internal.h"
+#include <stdio.h>
+#include <sapi/embed/php_embed.h>
+#include <zend_stream.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  zval *php_Array_from_maps(maps* t);
+  zval*  php_Array_from_map(map*);
+  maps* php_maps_from_Array(HashTable* t);
+  map* php_map_from_HasTable(HashTable* t);
+  int zoo_php_support(maps**,map*,service*,maps**,maps**);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_python.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_python.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_python.c	(revision 221)
@@ -0,0 +1,336 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2011 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "service_internal_python.h"
+
+int zoo_python_support(maps** main_conf,map* request,service* s,maps **real_inputs,maps **real_outputs){
+  maps* m=*main_conf;
+  maps* inputs=*real_inputs;
+  maps* outputs=*real_outputs;
+  char ntmp[1024];
+  getcwd(ntmp,1024);
+  map* tmp=NULL;
+  tmp=getMapFromMaps(*main_conf,"env","PYTHONPATH");
+  char *python_path;
+#ifdef DEBUG
+  fprintf(stderr,"PYTHON SUPPORT \n");
+#endif
+  fflush(stderr);
+  if(tmp!=NULL){
+#ifdef DEBUG
+    fprintf(stderr,"PYTHON SUPPORT (%i)\n",strlen(tmp->value));
+#endif
+    python_path=(char*)malloc((strlen(tmp->value))*sizeof(char));
+    sprintf(python_path,"%s",tmp->value);
+  }
+  else{
+    python_path=strdup(".");
+  }
+  tmp=NULL;
+  tmp=getMap(request,"metapath");
+  char *pythonpath=(char*)malloc((1+strlen(python_path)+2048)*sizeof(char));
+  if(tmp!=NULL && strcmp(tmp->value,"")!=0)
+#ifdef WIN32
+    sprintf(pythonpath,"%s/%s/;%s",ntmp,tmp->value,python_path);
+#else
+  sprintf(pythonpath,"%s/%s/:%s",ntmp,tmp->value,python_path);
+#endif
+  else
+#ifdef WIN32
+    sprintf(pythonpath,"%s;%s",ntmp,python_path);
+#else
+  sprintf(pythonpath,"%s:%s",ntmp,python_path);
+#endif
+#ifdef DEBUG
+    fprintf(stderr,"PYTHONPATH=%s\n",pythonpath);
+#endif
+#ifndef WIN32
+  setenv("PYTHONPATH",pythonpath,1);
+#else
+  SetEnvironmentVariable("PYTHONPATH",pythonpath);
+#endif
+  free(python_path);
+  free(pythonpath);
+
+  Py_Initialize();
+  PyObject *pName, *pModule, *pFunc;
+  tmp=getMap(s->content,"serviceProvider");
+  if(tmp!=NULL)
+    pName = PyString_FromString(tmp->value);
+  else{
+    map* err=createMap("text","Unable to parse serviceProvider please check your zcfg file.");
+    addToMap(err,"code","NoApplicableCode");
+    printExceptionReportResponse(m,err);
+    exit(-1);
+  }
+  pModule = PyImport_Import(pName);
+  int res=SERVICE_FAILED;
+  if (pModule != NULL) {
+    pFunc=PyObject_GetAttrString(pModule,s->name);
+    if (pFunc && PyCallable_Check(pFunc)){
+      PyObject *pValue;
+      PyDictObject* arg1=PyDict_FromMaps(m);
+      PyDictObject* arg2=PyDict_FromMaps(inputs);
+      PyDictObject* arg3=PyDict_FromMaps(outputs);
+      PyObject *pArgs=PyTuple_New(3);
+      if (!pArgs)
+	return -1;
+      PyTuple_SetItem(pArgs, 0, (PyObject *)arg1);
+      PyTuple_SetItem(pArgs, 1, (PyObject *)arg2);
+      PyTuple_SetItem(pArgs, 2, (PyObject *)arg3);
+      tmp=getMap(request,"storeExecuteResponse");
+#ifdef DEBUG
+      fprintf(stderr,"RUN IN NORMAL MODE \n");
+      fflush(stderr);
+#endif
+      pValue = PyObject_CallObject(pFunc, pArgs);
+      if (pValue != NULL) {
+	res=PyInt_AsLong(pValue);
+	freeMaps(real_outputs);
+	free(*real_outputs);
+	freeMaps(main_conf);
+	free(*main_conf);
+	*main_conf=mapsFromPyDict(arg1);
+	*real_outputs=mapsFromPyDict(arg3);
+#ifdef DEBUG
+	fprintf(stderr,"Result of call: %i\n", PyInt_AsLong(pValue));
+	dumpMaps(inputs);
+	dumpMaps(outputs);
+#endif
+	Py_DECREF(arg1);
+	Py_DECREF(arg2);
+	Py_DECREF(arg3);
+      	Py_DECREF(pArgs);
+	Py_DECREF(pValue);
+	Py_XDECREF(pFunc);
+	Py_DECREF(pModule);
+      }else{	  
+	PyObject *ptype,*pvalue, *ptraceback;
+	PyErr_Fetch(&ptype, &pvalue, &ptraceback);
+	PyObject *trace=PyObject_Str(pvalue);
+	char pbt[10240];
+	if(PyString_Check(trace))
+	  sprintf(pbt,"TRACE : %s",PyString_AsString(trace));
+	else
+	  fprintf(stderr,"EMPTY TRACE ?");
+	trace=NULL;
+	trace=PyObject_Str(ptype);
+	if(PyString_Check(trace)){
+	  char *tpbt=strdup(pbt);
+	  sprintf(pbt,"%s\nTRACE : %s",tpbt,PyString_AsString(trace));
+	  free(tpbt);
+	}
+	else
+	  fprintf(stderr,"EMPTY TRACE ?");
+	pName = PyString_FromString("traceback");
+	pModule = PyImport_Import(pName);
+	pArgs = PyTuple_New(1);
+	PyTuple_SetItem(pArgs, 0, ptraceback);
+	pFunc = PyObject_GetAttrString(pModule,"format_tb");
+	pValue = PyObject_CallObject(pFunc, pArgs);
+	trace=NULL;
+	trace=PyObject_Str(pValue);
+	if(PyString_Check(trace))
+	  sprintf(pbt,"%s\nUnable to run your python process properly. Please check the following messages : %s",pbt,PyString_AsString(trace));
+	else
+	  sprintf(pbt,"%s \n Unable to run your python process properly. Unable to provide any futher informations.",pbt);
+	map* err=createMap("text",pbt);
+	addToMap(err,"code","NoApplicableCode");
+	printExceptionReportResponse(m,err);
+	Py_DECREF(arg1);
+	Py_DECREF(arg2);
+	Py_DECREF(arg3);
+	Py_XDECREF(pFunc);
+      	Py_DECREF(pArgs);
+	Py_DECREF(pModule);
+	Py_DECREF(ptraceback);
+	Py_DECREF(ptype);
+	Py_DECREF(pValue);
+#if not(defined(macintosh)) && not(defined(__MACH__) && defined(__APPLE__))
+	Py_Finalize();
+#endif
+	exit(-1);
+      }
+    }
+    else{
+      char tmpS[1024];
+      sprintf(tmpS, "Cannot find the %s function int the %s file.\n", s->name, tmp->value);
+      map* tmps=createMap("text",tmpS);
+      printExceptionReportResponse(m,tmps);
+      Py_XDECREF(pFunc);
+      Py_DECREF(pModule);
+      exit(-1);
+    }
+  } else{
+    char tmpS[1024];
+    sprintf(tmpS, "Python module %s cannot be loaded.\n", tmp->value);
+    map* tmps=createMap("text",tmpS);
+    printExceptionReportResponse(m,tmps);
+    if (PyErr_Occurred())
+      PyErr_Print();
+    exit(-1);
+  } 
+#if not(defined(macintosh)) && not(defined(__MACH__) && defined(__APPLE__))
+  Py_Finalize();
+#endif
+  return res;
+}
+
+PyDictObject* PyDict_FromMaps(maps* t){
+  PyObject* res=PyDict_New( );
+  maps* tmp=t;
+  while(tmp!=NULL){
+    PyObject* subc=(PyObject*)PyDict_FromMap(tmp->content);
+    if(PyDict_SetItem(res,PyString_FromString(tmp->name),subc)<0){
+      fprintf(stderr,"Unable to parse params...");
+      exit(1);
+    }
+    Py_DECREF(subc);
+    tmp=tmp->next;
+  }  
+  return (PyDictObject*) res;
+}
+
+PyDictObject* PyDict_FromMap(map* t){
+  PyObject* res=PyDict_New( );
+  map* tmp=t;
+  map* size=getMap(tmp,"size");
+  while(tmp!=NULL){
+    PyObject* name=PyString_FromString(tmp->name);
+    if(strcasecmp(tmp->name,"value")==0){
+      if(size!=NULL){
+	PyObject* value=PyString_FromStringAndSize(tmp->value,atoi(size->value));
+	if(PyDict_SetItem(res,name,value)<0){
+	  fprintf(stderr,"Unable to parse params...");
+	  Py_DECREF(value);
+	  exit(1);
+	}
+	Py_DECREF(value);
+      }
+      else{
+	PyObject* value=PyString_FromString(tmp->value);
+	if(PyDict_SetItem(res,name,value)<0){
+	  fprintf(stderr,"Unable to parse params...");
+	  Py_DECREF(value);
+	  exit(1);
+	}
+	Py_DECREF(value);
+      }
+    }
+    else{
+      PyObject* value=PyString_FromString(tmp->value);
+      if(PyDict_SetItem(res,name,value)<0){
+	fprintf(stderr,"Unable to parse params...");
+	Py_DECREF(value);
+	exit(1);
+      }
+      Py_DECREF(value);
+    }
+    Py_DECREF(name);
+    tmp=tmp->next;
+  }
+  return (PyDictObject*) res;
+}
+
+maps* mapsFromPyDict(PyDictObject* t){
+  maps* res=NULL;
+  maps* cursor=res;
+  PyObject* list=PyDict_Keys((PyObject*)t);
+  int nb=PyList_Size(list);
+  int i;
+  for(i=0;i<nb;i++){
+#ifdef DEBUG
+    fprintf(stderr,">> parsing maps %d\n",i);
+#endif
+    PyObject* key=PyList_GetItem(list,i);
+    PyObject* value=PyDict_GetItem((PyObject*)t,key);
+#ifdef DEBUG
+    fprintf(stderr,">> DEBUG VALUES : %s => %s\n",
+	    PyString_AsString(key),PyString_AsString(value));
+#endif
+    cursor=(maps*)malloc(MAPS_SIZE);
+    cursor->name=PyString_AsString(key);
+#ifdef DEBUG
+    dumpMap(mapFromPyDict((PyDictObject*)value));
+#endif
+    cursor->content=mapFromPyDict((PyDictObject*)value);
+    cursor->next=NULL;
+    if(res==NULL)
+      res=dupMaps(&cursor);
+    else
+      addMapsToMaps(&res,cursor);
+    freeMap(&cursor->content);
+    free(cursor->content);
+    free(cursor);
+    Py_DECREF(value);
+    Py_DECREF(key);
+#ifdef DEBUG
+    dumpMaps(res);
+    fprintf(stderr,">> parsed maps %d\n",i);
+#endif
+  }
+  Py_DECREF(list);
+  return res;
+}
+
+map* mapFromPyDict(PyDictObject* t){
+  map* res=NULL;
+  PyObject* list=PyDict_Keys((PyObject*)t);
+  int nb=PyList_Size(list);
+  int i;
+  for(i=0;i<nb;i++){
+    PyObject* key=PyList_GetItem(list,i);
+    PyObject* value=PyDict_GetItem((PyObject*)t,key);
+#ifdef DEBUG
+    fprintf(stderr,">> DEBUG VALUES : %s => %s\n",
+	    PyString_AsString(key),PyString_AsString(value));
+#endif
+    if(strcmp(PyString_AsString(key),"value")==0){
+      char *buffer=NULL;
+      Py_ssize_t size;
+      PyString_AsStringAndSize(value,&buffer,&size);
+      if(res!=NULL){
+	addToMap(res,PyString_AsString(key),"");
+      }else{
+	res=createMap(PyString_AsString(key),"");
+      }
+      map* tmpR=getMap(res,"value");
+      free(tmpR->value);
+      tmpR->value=(char*)malloc((size+1)*sizeof(char));
+      memmove(tmpR->value,buffer,size*sizeof(char));
+      tmpR->value[size]=0;
+      char sin[1024];
+      sprintf(sin,"%d",size);
+      addToMap(res,"size",sin);
+    }else{
+      if(res!=NULL)
+	addToMap(res,PyString_AsString(key),PyString_AsString(value));
+      else
+	res=createMap(PyString_AsString(key),PyString_AsString(value));
+    }
+    Py_DECREF(key);
+  }
+  Py_DECREF(list);
+  return res;
+}
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_python.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_python.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_internal_python.h	(revision 221)
@@ -0,0 +1,47 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef ZOO_SERVICE_INTERNAL_PYTHON_H
+#define ZOO_SERVICE_INTERNAL_PYTHON_H 1
+
+#pragma once 
+
+#include "service.h"
+#include "service_internal.h"
+#include <Python.h>
+#ifdef WIN32
+#include <windows.h>
+#include <direct.h>
+#endif
+
+PyDictObject* PyDict_FromMaps(maps* t);
+PyDictObject* PyDict_FromMap(map* t);
+
+maps* mapsFromPyDict(PyDictObject* t);
+void createMapsFromPyDict(maps**,PyDictObject*);
+map* mapFromPyDict(PyDictObject* t);
+
+int zoo_python_support(maps**,map*,service*,maps**,maps**);
+
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-kernel/service_loader.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/service_loader.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/service_loader.c	(revision 221)
@@ -0,0 +1,272 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2009-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Environment variables definitions.
+ * All the following fixed values should be extracted from a configuration main
+ * configuration file for all the potential services.
+ */
+/**
+ * END "Environment variables definitions"
+ */
+#define length(x) (sizeof(x) / sizeof(x[0]))
+
+extern "C" int yylex();
+extern "C" int crlex();
+
+#include <string.h>
+
+#include "service.h"
+#include "service_internal.h"
+#include "service_internal_python.h"
+
+#include <dirent.h>
+#include <signal.h>
+#include <unistd.h>
+#ifndef WIN32
+#include <dlfcn.h>
+#include <libgen.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <stdarg.h>
+
+void sigint_handler(int sig){
+    printf("Not this time!\n");
+}
+
+int main(int argc, char *argv[])
+{
+  if (argc < 4){
+    printf( "Usage: %s <servicename> <directory>|<definition_file> <REQUEST> [<functionname> <param_1>[...<param_n>]]\n", basename(argv[0]) );
+    return 1;
+  }
+ 
+  map* outputs=NULL;
+  /**
+   * Parsing inputs (need a loop over all files in the service path !!)
+   */
+  maps* m;
+  m=(maps*)malloc(MAP_SIZE);
+  conf_read("main.cfg",m);
+  map* tmpm=getMapFromMaps(m,"main","serverAddress");
+  int toto=count(tmpm);
+  //printf(" - %i \n",toto);
+
+  if(tmpm!=NULL)
+    SERVICE_URL=strdup(tmpm->value);
+  else
+    SERVICE_URL=DEFAULT_SERVICE_URL;
+
+  service* s[100];
+  service* s1;
+  int scount=0;
+
+  if(strcmp(argv[3],"GetCapabilities")==0){
+    int i=0;
+    struct dirent *dp;
+    DIR *dirp = opendir(argv[1]);
+    int t;
+    xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
+    xmlNodePtr n = printGetCapabilitiesHeader(doc,argv[2],m);
+    
+    int saved_stdout = dup(fileno(stdout));
+    stdout = freopen("/dev/null" , "w" , stdout);
+    while ((dp = readdir(dirp)) != NULL)
+      if(strstr(dp->d_name,".zcfg")!=0){
+	char toto1[1024];
+	sprintf(toto1,"%s%s",argv[1],dp->d_name);
+	char *toto=toto1;
+	s1=(service*)malloc(sizeof(char*)+(MAP_SIZE*2)+(2*ELEMENTS_SIZE));
+	//s[scount]=(service*)malloc(sizeof(service*));
+	//#ifdef DEBUG
+	fprintf(stderr,"#################\n%s\n#################\n",toto1);
+	//printf("|(1)");
+	//#endif
+	t=getServiceFromFile(toto1,&s1);
+	
+	//printf("|(2)");
+	printGetCapabilitiesForProcess(m,n,s1);
+	/**
+	 * Valgrind told us that there is an issue regarding a 
+	 * "conditional jump or move depends on uninitialised value(s)" for
+	 * freeIOType
+	 */
+	//freeService(&s1);
+	scount++;
+      }
+    char buf[20];
+    sprintf(buf, "/dev/fd/%d", saved_stdout);
+    stdout = freopen(buf , "w" , stdout);
+
+    printDocument(doc);
+    fflush(stdout);
+    free(m);
+    return 0;
+  }
+  else{
+    s1=(service*)malloc(sizeof(char*)+(MAP_SIZE*2)+(2*ELEMENTS_SIZE));
+    //s[0]=(service*)malloc(sizeof(service*));
+    int t=getServiceFromFile(argv[1],&s1);
+    if(strcmp(argv[3],"DescribeProcess")==0){
+      printDescribeProcessResponse(s1,argv[2]);
+      //dumpMaps(m);
+      //free(s1);
+      return 0;
+    }
+    else
+      if(strcmp(argv[3],"Execute")!=0){
+	fprintf(stderr,"");
+	//free(s);
+	return 0;
+      }
+  }
+  //dumpService(s);
+  s[0]=s1;
+  map* inputs=NULL;
+  elements* c_inputs=s1->inputs;
+  int j;
+  for(j=0;j<argc-5;j++){
+    //dumpElements(c_inputs);
+    if(inputs!=NULL)
+      addToMap(inputs,c_inputs->name,argv[j+5]);
+    else
+      inputs=createMap(c_inputs->name,argv[j+5]);
+    if(c_inputs->next!=NULL || j+1>=argc-5)
+      c_inputs=c_inputs->next;
+    else{
+      map* tmps=createMap("text","ERROR you provided more inputs than requested.");
+      printExceptionReportResponse(m,tmps);
+      //printf("ERROR you provided more inputs than requested.");
+      return -1;
+    }
+#ifdef DEBUG
+    printf("ARGV1 %d %s\n",j,inputs->value);
+#endif
+  }
+
+#ifdef DEBUG
+  dumpMap(inputs);
+#endif
+
+  const struct tm *tm;
+  size_t len;
+  time_t now;
+  char *sDate;
+  
+  now = time ( NULL );
+  tm = localtime ( &now );
+
+  sDate = new char[TIME_SIZE];
+
+  len = strftime ( sDate, TIME_SIZE, "%d-%B-%YT%I:%M:%SZ", tm );
+
+#ifdef DEBUG
+  printf("Trying to load %s\n", argv[2]);
+#endif
+  void* so = dlopen(argv[2], RTLD_LAZY);
+  char *errstr;
+  errstr = dlerror();
+  if( so != NULL ) {
+    typedef int (*execute_t)(map**,map**);
+#ifdef DEBUG
+    printf("Library loaded %s \n",errstr);
+#endif
+    execute_t execute=(execute_t)dlsym(so,argv[4]);
+#ifdef DEBUG
+    errstr = dlerror();
+    printf("Function loaded %s\n",errstr);
+#endif	
+
+    /**
+     * Need to check if we need to fork to load a status enabled 
+     */
+    char _toto[10];
+    sprintf(_toto,"input_%i",argc-5);
+    map* toto=getMap(inputs,_toto);
+    if(strcmp(argv[argc-1],"bg")!=0){
+#ifdef DEBUG
+      printf("RUN IN NORMAL MODE \n");
+#endif
+      int res=execute(&inputs,&outputs);
+#ifdef DEBUG
+      printf("RUNNED IN NORMAL MODE \n");
+      dumpMap(inputs);
+      dumpMap(outputs);
+#endif
+      printProcessResponse(m,getpid(),s[0],argv[2],res,inputs,outputs);
+    }
+    else{
+      pid_t   pid;
+      int cpid=getpid();
+      pid = fork ();
+      if (pid > 0) {
+	/**
+	 * dady :
+	 * set status to SERVICE_ACCEPTED
+	 */
+	printProcessResponse(m,pid,s[0],argv[2],SERVICE_ACCEPTED,inputs,outputs);
+      }else if (pid == 0) {
+	/* son */
+	if (signal(SIGINT, sigint_handler) == SIG_ERR) {
+	  printf("signal");
+	  map* tmps=createMap("text","father received sigint.");
+	  printExceptionReportResponse(m,tmps);
+	  exit(1);
+	}
+#ifdef DEBUG
+	printf("RUN IN BACKGROUND MODE \n");
+#endif
+	char tmp1[256];
+	sprintf(tmp1,"service/temp/%s_%d.xml",argv[2],getpid());
+	stdout = freopen(tmp1 , "w+" , stdout);
+	/**
+	 * set status to SERVICE_STARTED
+	 */
+	printProcessResponse(m,getpid(),s[0],argv[2],SERVICE_STARTED,inputs,outputs);
+	fflush(stdout);
+	rewind(stdout);
+	int t=execute(&inputs,&outputs);
+	/**
+	 * set status to status code returned by the service function
+	 */
+	printProcessResponse(m,getpid(),s[0],argv[2],t,inputs,outputs);
+      } else {
+	/* error */
+      }
+    }
+#ifdef DEBUG
+    errstr = dlerror();
+    printf("Function successfully loaded %s, unloading now.\n",errstr);
+#endif
+    dlclose(so);
+  }
+  else {
+#ifdef DEBUG
+    printf("C Library can't be loaded %s \n",errstr);
+#endif
+    python_support(m,s[0],argc,argv,inputs,outputs);
+  }
+  return 0;
+}
Index: /tags/rel-1.2.0-rc1/zoo-kernel/ulinet.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/ulinet.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/ulinet.c	(revision 221)
@@ -0,0 +1,501 @@
+/**
+ *  ulinet.c
+ *
+ * Author : Gérald FENOY
+ *
+ * Copyright (c) 2008-2010 GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#define _ULINET
+#include "ulinet.h"
+#include <assert.h>
+
+size_t write_data_into(void *buffer, size_t size, size_t nmemb, void *data){
+  size_t realsize = size * nmemb;
+  HINTERNET *psInternet;
+  if(buffer==NULL){
+    buffer=NULL;
+    return -1;
+  }
+  psInternet=(HINTERNET *)data;
+  if(psInternet->pabyData){
+    psInternet->pabyData=(char*)realloc(psInternet->pabyData,psInternet->nDataLen+realsize+1);
+    psInternet->nDataAlloc+=psInternet->nDataLen+realsize+1;
+  }
+  else{
+    psInternet->pabyData=(char*)malloc(psInternet->nDataLen+realsize+1);
+    psInternet->nDataAlloc=realsize+1;
+  }
+
+  if (psInternet->pabyData) {
+    memcpy( psInternet->pabyData + psInternet->nDataLen, buffer, realsize);
+    psInternet->nDataLen += realsize;
+    psInternet->pabyData[psInternet->nDataLen] = 0;
+  }
+
+  buffer=NULL;
+  return realsize;
+}
+
+size_t header_write_data(void *buffer, size_t size, size_t nmemb, void *data){
+  if(strncmp("Set-Cookie: ",buffer,12)==0){
+    int i;
+    char env[1024];
+    char path[1024];
+    char domain[1024];
+	char* tmp;
+    for(i=0;i<12;i++)
+#ifndef WIN32
+      buffer++;
+#else
+	;
+#endif
+    sscanf(buffer,"%s; path=%s; domain=%s",env,path,domain);
+    tmp=strcat(env,CCookie);
+#ifdef MSG_LAF_OUT
+    printf("\n**Cookie env : [%s] , path : [%s], domain : [%s]**\n",env,path,domain);
+    printf("buffer : %d (%s) (%s) (%s)\n",(buffer==NULL),buffer,tmp,CCookie);
+#endif
+    strcpy(CCookie,tmp);
+  }
+  return size * nmemb;//write_data_into(buffer,size,nmemb,data,HEADER);
+};
+
+
+void setProxy(CURL* handle,char* host,long port){
+}
+
+/**
+ * MACOSX
+ */
+#if defined(macintosh) || (defined(__MACH__) && defined(__APPLE__))
+
+
+char* CFStringToCString(CFStringRef dest,char *buffer){
+  CFStringEncoding encoding = kCFStringEncodingUTF8;
+  Boolean bool2 = CFStringGetCString(dest,buffer,1024,encoding);
+  if(bool2){
+    printf("Loaded into local_buffer");
+    return buffer;
+  }
+  return NULL;
+}
+
+OSStatus setProxiesForProtcol(CURL* handle,const char *proto){
+  OSStatus		err;
+  CFDictionaryRef proxyDict;
+  CFArrayRef		proxies;
+  
+  CFStringRef key_enabled;
+  CFStringRef key_host;
+  CFStringRef key_port;
+  
+  bool proxy_enabled;
+  char *proxy_host;
+  long proxy_port;
+  
+  proxyDict = NULL;
+  proxies = NULL;
+
+  err = noErr;
+  proxyDict = SCDynamicStoreCopyProxies(NULL);
+
+  if(proto=="http"){
+      key_enabled=kSCPropNetProxiesHTTPEnable;
+      key_host=kSCPropNetProxiesHTTPProxy;
+      key_port=kSCPropNetProxiesHTTPPort;
+  }
+  else
+    if(proto=="https"){
+      key_enabled=kSCPropNetProxiesHTTPSEnable;
+      key_host=kSCPropNetProxiesHTTPSProxy;
+      key_port=kSCPropNetProxiesHTTPSPort;
+    }
+
+  CFNumberGetValue(CFDictionaryGetValue(proxyDict,key_enabled),kCFNumberIntType,&proxy_enabled);
+  if(proxy_enabled){
+    CFNumberGetValue(CFDictionaryGetValue(proxyDict,key_port),CFNumberGetType(CFDictionaryGetValue(proxyDict,key_port)),&proxy_port);
+    char buffer[1024];
+    CFStringToCString(CFDictionaryGetValue(proxyDict,key_host),buffer);
+    proxy_host=buffer;
+
+#ifdef MSG_LAF_VERBOSE
+    printf("\n**[PROXY SETTINGS DETECTION %s (%d) %s:%li (%s)]**\n",proto,proxy_enabled,(char*)proxy_host,proxy_port,buffer);
+#endif
+
+    if (proxyDict == NULL) {
+      err = coreFoundationUnknownErr;
+    }
+
+    setProxy(handle,proxy_host,proxy_port);
+  }
+  return err;
+}
+#else
+/**
+ * Linux (Gnome)
+ */
+bool setProxiesForProtcol(CURL* handle,const char *proto){
+#ifdef MSG_LAF_VERBOSE
+  fprintf( stderr, "setProxiesForProtocol (do nothing) ...\n" );
+#endif
+}
+#endif
+
+HINTERNET InternetOpen(char* lpszAgent,int dwAccessType,char* lpszProxyName,char* lpszProxyBypass,int dwFlags){
+  
+  HINTERNET ret;
+  struct MemoryStruct header;
+  ret.hasCacheFile=0;
+  ret.nDataAlloc = 0;
+
+  ret.handle=curl_easy_init();
+
+  curl_easy_setopt(ret.handle, CURLOPT_COOKIEFILE, "ALL");
+#ifndef TIGER
+  curl_easy_setopt(ret.handle, CURLOPT_COOKIELIST, "ALL");
+#endif
+  curl_easy_setopt(ret.handle, CURLOPT_USERAGENT, lpszAgent);
+  
+  curl_easy_setopt(ret.handle,CURLOPT_FOLLOWLOCATION,1);
+  curl_easy_setopt(ret.handle,CURLOPT_MAXREDIRS,3);
+  
+  header.memory=NULL;
+  header.size = 0;
+
+  curl_easy_setopt(ret.handle, CURLOPT_HEADERFUNCTION, header_write_data);
+  curl_easy_setopt(ret.handle, CURLOPT_WRITEHEADER, (void *)&header);
+
+#ifdef MSG_LAF_VERBOSE
+  curl_easy_setopt(ret.handle, CURLOPT_VERBOSE, 1);
+#endif
+
+  return ret;
+}
+
+static size_t 
+CurlWriteCB(void *buffer, size_t size, size_t nmemb, void *reqInfo){
+  HINTERNET *psInternet = (HINTERNET *) reqInfo;
+
+  memcpy( psInternet->pabyData + psInternet->nDataLen, buffer,  nmemb * size );
+  psInternet->nDataLen += nmemb * size;
+  psInternet->pabyData[psInternet->nDataLen] = 0;
+
+  return nmemb *size;
+}
+
+void InternetCloseHandle(HINTERNET handle){
+  if(handle.hasCacheFile>0){
+    fclose(handle.file);
+    unlink(handle.filename);
+  }
+  else{
+    handle.pabyData = NULL;
+    handle.nDataAlloc = handle.nDataLen = 0;
+  }
+  if(handle.handle)
+    curl_easy_cleanup(handle.handle);
+  curl_global_cleanup();
+}
+
+HINTERNET InternetOpenUrl(HINTERNET hInternet,LPCTSTR lpszUrl,LPCTSTR lpszHeaders,size_t dwHeadersLength,size_t dwFlags,size_t dwContext){
+
+  char filename[255];
+  hInternet.nDataLen = 0;
+
+  hInternet.nDataAlloc = 0;
+  hInternet.pabyData= NULL;
+      
+  switch(dwFlags)
+    {
+    case INTERNET_FLAG_NO_CACHE_WRITE:    
+      hInternet.hasCacheFile=-1;
+      curl_easy_setopt(hInternet.handle, CURLOPT_WRITEFUNCTION, write_data_into);
+      curl_easy_setopt(hInternet.handle, CURLOPT_WRITEDATA, &hInternet);
+      break;
+    default:
+      sprintf(filename,"/tmp/ZOO_Cache%d",(int)time(NULL));
+      filename[24]=0;
+      fprintf(stderr,"file=%s",filename);
+#ifdef MSG_LAF_VERBOSE
+      fprintf(stderr,"file=%s",filename);
+#endif
+      hInternet.filename=filename;
+      hInternet.file=fopen(hInternet.filename,"w+");
+    
+      hInternet.hasCacheFile=1;
+      curl_easy_setopt(hInternet.handle, CURLOPT_WRITEFUNCTION, NULL);
+      curl_easy_setopt(hInternet.handle, CURLOPT_WRITEDATA, hInternet.file);
+      hInternet.nDataLen=0;
+      break;
+    }
+#ifdef ULINET_DEBUG
+  fprintf(stderr,"URL (%s)\nBODY (%s)\n",lpszUrl,lpszHeaders);
+#endif
+  if(lpszHeaders!=NULL && strlen(lpszHeaders)>0){
+#ifdef MSG_LAF_VERBOSE
+    fprintf(stderr,"FROM ULINET !!");
+    fprintf(stderr,"HEADER : %s\n",lpszHeaders);
+#endif
+    //curl_easy_setopt(hInternet.handle,CURLOPT_COOKIE,lpszHeaders);
+    curl_easy_setopt(hInternet.handle,CURLOPT_POST,1);
+#ifdef ULINET_DEBUG
+    fprintf(stderr,"** (%s) %d **\n",lpszHeaders,dwHeadersLength);
+#endif
+    curl_easy_setopt(hInternet.handle,CURLOPT_POSTFIELDS,lpszHeaders);
+    //curl_easy_setopt(hInternet.handle,CURLOPT_POSTFIELDSIZE,dwHeadersLength+1);
+    if(hInternet.header!=NULL)
+      curl_easy_setopt(hInternet.handle,CURLOPT_HTTPHEADER,hInternet.header);
+  }
+
+  curl_easy_setopt(hInternet.handle,CURLOPT_URL,lpszUrl);
+  curl_easy_perform(hInternet.handle);
+
+  return hInternet;
+};
+
+int freeCookieList(HINTERNET hInternet){
+  memset(&CCookie[0],0,1024);
+#ifndef TIGER
+  curl_easy_setopt(hInternet.handle, CURLOPT_COOKIELIST, "ALL");
+#endif
+  return 1;
+}
+
+int InternetReadFile(HINTERNET hInternet,LPVOID lpBuffer,int dwNumberOfBytesToRead, size_t *lpdwNumberOfBytesRead){
+  int dwDataSize;
+
+  if(hInternet.hasCacheFile>0){
+    fseek (hInternet.file , 0 , SEEK_END);
+    dwDataSize=ftell(hInternet.file); //taille du ficher
+    rewind (hInternet.file);
+  }
+  else{
+    memset(lpBuffer,0,hInternet.nDataLen+1);
+    memcpy( lpBuffer, hInternet.pabyData, hInternet.nDataLen );
+    dwDataSize=hInternet.nDataLen;
+    free( hInternet.pabyData );
+    hInternet.pabyData=NULL;
+  }
+
+  if( dwNumberOfBytesToRead /* buffer size */ < dwDataSize )
+    return 0;
+
+#ifdef MSG_LAF_VERBOSE
+  printf("\nfile size : %dko\n",dwDataSize/1024);
+#endif
+
+  if(hInternet.hasCacheFile>0){
+    *lpdwNumberOfBytesRead = fread(lpBuffer,1,dwDataSize,hInternet.file); 
+  }
+  else{
+    *lpdwNumberOfBytesRead = hInternet.nDataLen;
+    free( hInternet.pabyData );
+    hInternet.pabyData = NULL;
+    hInternet.nDataAlloc = hInternet.nDataLen = 0;
+  }
+
+  CCookie[0]=0;
+
+  if( *lpdwNumberOfBytesRead < dwDataSize )
+      return 0;
+  else
+      return 1; // TRUE
+}
+
+bool InternetGetCookie(LPCTSTR lpszUrl,LPCTSTR lpszCookieName,LPTSTR lpszCookieData,LPDWORD lpdwSize){
+
+  bool ret=1;  
+  int count=0;
+  int hasCookie=-1;
+  char TMP[1024];
+  int j;
+  int tmpC=0;
+  lpszUrl=NULL;
+
+  for(j=0;j<strlen(CCookie);j++){
+    if(lpszCookieName[count]==CCookie[j]){
+      hasCookie=1;
+      count++;
+      if(count==strlen(lpszCookieName))
+	break;
+      continue;
+    }
+  }
+
+  if(hasCookie>0){
+    if(CCookie[count]=='='){
+      int i=0;
+      count++;
+      for(i=count;i<strlen(CCookie);i++){
+	if(CCookie[i]!=';'){
+	  TMP[tmpC]=CCookie[i];
+	  tmpC++;
+	}
+	else{
+	  break;
+	}
+      }
+    }
+  }
+  else
+    return -1;
+
+  TMP[tmpC]=0;
+  strncpy(lpszCookieData,TMP,strlen(TMP)+1);
+  lpdwSize=(size_t*) strlen(lpszCookieData);
+
+#ifdef MSG_LAF_VERBOSE
+  printf("Cookie returned : (%s)",(char*)lpszCookieData);
+#endif
+
+  return ret;
+
+}
+
+#ifdef USE_JS
+#include "jsapi.h"
+
+char* JSValToChar(JSContext* context, jsval* arg) {
+  if(!JSVAL_IS_STRING(*arg)) {
+    return NULL;
+  }
+  char *c, *tmp;
+  JSString *jsmsg;
+  size_t len;
+  jsmsg = JS_ValueToString(context,*arg);
+  len = JS_GetStringLength(jsmsg);
+  tmp = JS_GetStringBytes(jsmsg);
+  c = (char*)malloc((len+1)*sizeof(char));
+  c[len] = '\0';
+  int i;
+#ifdef ULINET_DEBUG
+  fprintf(stderr,"%d \n",len);
+#endif
+  for(i = 0;i < len;i++) {
+    c[i] = tmp[i];
+    c[i+1] = 0;
+  }
+#ifdef ULINET_DEBUG
+  fprintf(stderr,"%s \n",c);
+#endif
+  return c;
+}
+
+HINTERNET setHeader(HINTERNET handle,JSContext *cx,JSObject *header){
+  jsuint length=0;
+#ifdef ULINET_DEBUG
+  fprintf(stderr,"setHeader\n");
+#endif
+  if(JS_IsArrayObject(cx,header)){
+#ifdef ULINET_DEBUG
+    fprintf(stderr,"header is an array\n");
+#endif
+    JS_GetArrayLength(cx,header,&length);
+#ifdef ULINET_DEBUG
+    fprintf(stderr,"header is an array of %d elements\n",length);
+#endif
+    jsint i=0;
+    handle.header=NULL;
+    for(i=0;i<length;i++){
+      jsval tmp;
+      JS_GetElement(cx,header,i,&tmp);
+      char *tmp1=JSValToChar(cx,&tmp);
+#ifdef ULINET_DEBUG
+      fprintf(stderr,"Element of array n° %d, value : %s\n",i,tmp1);
+#endif
+      handle.header=curl_slist_append(handle.header, tmp1);
+      free(tmp1);
+    }
+  }
+  else{
+    fprintf(stderr,"not an array !!!!!!!\n");
+  }
+  return handle;
+}
+
+JSBool
+JSRequest(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+  HINTERNET hInternet;
+  char *url;
+  char *method;
+  JS_MaybeGC(cx);
+  hInternet=InternetOpen((LPCTSTR)"ZooWPSClient\0",
+			 INTERNET_OPEN_TYPE_PRECONFIG,
+			 NULL,NULL, 0);
+  if(!CHECK_INET_HANDLE(hInternet))
+    return JS_FALSE;
+  int i=0;
+  if(argc>=2){
+    method=JSValToChar(cx,&argv[0]);
+    url=JSValToChar(cx,&argv[1]);
+  }
+  else{
+    method=strdup("GET");
+    url=JSValToChar(cx,argv);
+  }
+  HINTERNET res;
+  if(argc==4){
+    char *body;
+    body=JSValToChar(cx,&argv[2]);
+    JSObject *header=JSVAL_TO_OBJECT(argv[3]);
+    HINTERNET res1;
+#ifdef ULINET_DEBUG
+    fprintf(stderr,"URL (%s) \nBODY (%s)\n",url,body);
+#endif
+    if(JS_IsArrayObject(cx,header))
+      res1=setHeader(hInternet,cx,header);
+#ifdef ULINET_DEBUG
+    fprintf(stderr,"BODY (%s)\n",body);
+#endif
+    res=InternetOpenUrl(res1,url,body,strlen(body),
+			INTERNET_FLAG_NO_CACHE_WRITE,0);    
+    free(body);
+  }else{
+    if(argc==3){
+      char *body=JSValToChar(cx,&argv[2]);
+      res=InternetOpenUrl(hInternet,url,body,strlen(body),
+			  INTERNET_FLAG_NO_CACHE_WRITE,0);
+      free(body);
+    }
+    res=InternetOpenUrl(hInternet,url,NULL,0,
+			INTERNET_FLAG_NO_CACHE_WRITE,0);
+  }
+  char* tmpValue=(char*)malloc((res.nDataLen+1)*sizeof(char));
+  size_t dwRead;
+  InternetReadFile(res,(LPVOID)tmpValue,res.nDataLen,&dwRead);
+#ifdef ULINET_DEBUG
+  fprintf(stderr,"content downloaded (%d) (%s) \n",dwRead,tmpValue);
+#endif
+  *rval=STRING_TO_JSVAL(JS_NewString(cx,tmpValue,strlen(tmpValue)));
+  free(url);
+  if(argc>=2)
+    free(method);
+  if(argc==4 && res.header!=NULL){
+    curl_slist_free_all(res.header);
+  }
+  InternetCloseHandle(hInternet);
+  JS_MaybeGC(cx);
+  return JS_TRUE;
+}
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-kernel/ulinet.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/ulinet.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/ulinet.h	(revision 221)
@@ -0,0 +1,148 @@
+/**
+ * Author : Gérald FENOY
+ *
+ *  Copyright 2008-2009 GeoLabs SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _ULINET_H
+#define _ULINET_H
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <curl/curl.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <string.h>
+#include "time.h"
+#ifdef USE_JS
+#define XP_UNIX 0
+#include "jsapi.h"
+#endif
+
+
+#ifdef _ULINET
+static char CCookie[1024];
+#else
+extern char HEADER[3072];
+extern char CCookie[1024];
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  //static pthread_mutex_t hMutexConnect = PTHREAD_MUTEX_INITIALIZER;
+
+struct MemoryStruct {
+  char *memory;
+  size_t size;
+};
+
+typedef struct {
+  CURL *handle;
+  struct curl_slist *header;
+  char* filename;
+  FILE* file;
+  size_t size;
+  int hasCacheFile;
+  int nDataLen;
+  int nDataAlloc;
+  unsigned char *pabyData;
+} HINTERNET;
+
+size_t write_data_into(void *buffer, size_t size, size_t nmemb, void *data);
+
+size_t content_write_data(void *buffer, size_t size, size_t nmemb, void *data);
+
+size_t header_write_data(void *buffer, size_t size, size_t nmemb, void *data);
+
+
+void setProxy(CURL* handle,char* host,long port);
+
+
+#if defined(macintosh) || (defined(__MACH__) && defined(__APPLE__))
+
+#include <CoreServices/CoreServices.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+char* CFStringToCString(CFStringRef dest,char * buffer);
+OSStatus setProxiesForProtcol(CURL* handle,const char *proto);
+
+#else
+
+//#include <gconf/gconf-client.h>
+int setProxiesForProtcol(CURL* handle,const char *proto);
+
+#endif
+
+
+#define INTERNET_OPEN_TYPE_DIRECT                      0
+#define INTERNET_OPEN_TYPE_PRECONFIG                   1
+#define INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY 2
+#define INTERNET_OPEN_TYPE_PROXY                       3
+#ifndef WIN32
+typedef char* LPCTSTR;
+#endif
+HINTERNET InternetOpen(char* lpszAgent,int dwAccessType,char* lpszProxyName,char* lpszProxyBypass,int dwFlags);
+
+void InternetCloseHandle(HINTERNET handle);
+
+#define INTERNET_FLAG_EXISTING_CONNECT         0
+#define INTERNET_FLAG_HYPERLINK                1
+#define INTERNET_FLAG_IGNORE_CERT_CN_INVALID   2
+#define INTERNET_FLAG_IGNORE_CERT_DATE_INVALID 3
+#define INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP  4
+#define INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS 5
+#define INTERNET_FLAG_KEEP_CONNECTION          6
+#define INTERNET_FLAG_NEED_FILE                7
+#define INTERNET_FLAG_NO_AUTH                  8
+#define INTERNET_FLAG_NO_AUTO_REDIRECT         9
+#define INTERNET_FLAG_NO_CACHE_WRITE          10
+//typedef char* LPVOID;
+#ifndef WIN32
+typedef void* LPVOID;
+typedef void* LPTSTR;
+typedef size_t* LPDWORD;
+#endif
+#ifndef bool
+#define bool int
+#endif
+
+#  define CHECK_INET_HANDLE(h) (h.handle != 0)
+
+HINTERNET InternetOpenUrl(HINTERNET hInternet,LPCTSTR lpszUrl,LPCTSTR lpszHeaders,size_t dwHeadersLength,size_t dwFlags,size_t dwContext);
+
+int freeCookieList(HINTERNET hInternet);
+
+int InternetReadFile(HINTERNET hInternet,LPVOID lpBuffer,int dwNumberOfBytesToRead,size_t *lpdwNumberOfBytesRead);
+
+bool InternetGetCookie(LPCTSTR lpszUrl,LPCTSTR lpszCookieName,LPTSTR lpszCookieData,LPDWORD lpdwSize);
+
+#ifdef USE_JS
+JSBool JSRequest(JSContext*, JSObject*, uintN, jsval*, jsval*);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
Index: /tags/rel-1.2.0-rc1/zoo-kernel/zoo_loader.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/zoo_loader.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/zoo_loader.c	(revision 221)
@@ -0,0 +1,256 @@
+/**
+ * Author : Gérald FENOY
+ *
+ *  Copyright 2008-2011 GeoLabs SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define MALLOC_CHECK_ 0
+#define MALLOC_CHECK 0
+
+#ifdef WIN32
+#include "windows.h"
+#endif
+/**
+ * Specific includes
+ */
+#include "fcgio.h"
+#include "fcgi_config.h" 
+#include "fcgi_stdio.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include "service_internal.h"
+
+extern "C" {
+#include "cgic.h"
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+}
+
+xmlXPathObjectPtr extractFromDoc(xmlDocPtr,const char*);
+int runRequest(map*);
+
+using namespace std;
+
+/* ************************************************************************* */
+
+int errorException(maps *m, const char *message, const char *errorcode) 
+{
+  map* errormap = createMap("text", message);
+  addToMap(errormap,"code", errorcode);
+  printExceptionReportResponse(m,errormap);
+  freeMap(&errormap);
+  free(errormap);
+  return -1;
+}
+
+/* ************************************************************************* */
+
+
+#define TRUE 1
+#define FALSE -1
+
+int cgiMain(){
+  /**
+   * We'll use cgiOut as the default output (stdout) to produce plain text 
+   * response.
+   */
+  dup2(fileno(cgiOut),fileno(stdout));
+#ifdef DEBUG
+  fprintf(cgiOut,"Content-Type: text/plain; charset=utf-8\r\nStatus: 200 OK\r\n\r\n");
+  fprintf(cgiOut,"Welcome on ZOO verbose debuging mode \r\n\r\n");
+  fflush(cgiOut);
+#endif
+  
+#ifdef DEBUG
+  fprintf (stderr, "Addr:%s\n", cgiRemoteAddr); 
+  fprintf (stderr, "RequestMethod:%s\n", cgiRequestMethod); 
+  fprintf (stderr, "Request: %s\n", cgiQueryString);
+#endif
+
+  map* tmpMap=NULL;
+
+  if(strncmp(cgiContentType,"text/xml",8)==0 || 
+     strncasecmp(cgiRequestMethod,"post",4)==0){
+    char *buffer=new char[cgiContentLength+1];
+    if(fread(buffer,1,cgiContentLength,cgiIn)){
+      buffer[cgiContentLength]=0;
+      tmpMap=createMap("request",buffer);
+    }else{
+      char **array, **arrayStep;
+      if (cgiFormEntries(&array) != cgiFormSuccess) {
+	return 1;
+      }
+      arrayStep = array;
+      while (*arrayStep) {
+	char *value=new char[cgiContentLength];
+	cgiFormStringNoNewlines(*arrayStep, value, cgiContentLength);
+	char* tmpValueFinal=(char*) malloc((strlen(*arrayStep)+strlen(value)+1)*sizeof(char));
+	sprintf(tmpValueFinal,"%s=%s",*arrayStep,value);
+	tmpMap=createMap("request",tmpValueFinal);
+	free(tmpValueFinal);
+#ifdef DEBUG
+	fprintf(stderr,"(( \n %s \n %s \n ))",*arrayStep,value);
+#endif
+	delete[]value;
+	arrayStep++;
+      }
+    }
+    delete[]buffer;
+  }
+  else{
+    char **array, **arrayStep;
+    if (cgiFormEntries(&array) != cgiFormSuccess) {
+      return 1;
+    }
+    arrayStep = array;
+    while (*arrayStep) {
+      char *value=new char[cgiContentLength];
+      cgiFormStringNoNewlines(*arrayStep, value, cgiContentLength);
+#ifdef DEBUG
+      fprintf(stderr,"(( \n %s \n %s \n ))",*arrayStep,value);
+#endif
+      if(tmpMap!=NULL)
+	addToMap(tmpMap,*arrayStep,value);
+      else
+	tmpMap=createMap(*arrayStep,value);
+      arrayStep++;
+      delete[]value;
+    }
+    cgiStringArrayFree(array);
+  }
+
+  /**
+   * In case that the POST method was used, then check if params came in XML
+   * format else try to use the attribute "request" which should be the only 
+   * one.
+   */
+  if(strncasecmp(cgiRequestMethod,"post",4)==0 || 
+     (count(tmpMap)==1 && strncmp(tmpMap->value,"<",1)==0)){
+    /**
+     * First include the MetaPath and the ServiceProvider default parameters
+     * (which should be always available in GET params so in cgiQueryString)
+     */
+    char *str1;
+    str1=cgiQueryString;
+    /**
+     * Store the original XML request in xrequest map
+     */
+    map* t1=getMap(tmpMap,"request");
+    if(t1!=NULL){
+      addToMap(tmpMap,"xrequest",t1->value);
+      xmlInitParser();
+      xmlDocPtr doc = xmlParseMemory(t1->value,cgiContentLength);
+      xmlNodePtr cur = xmlDocGetRootElement(doc);
+      char *tval;
+      tval=NULL;
+      tval = (char*) xmlGetProp(cur,BAD_CAST "service");
+      if(tval!=NULL)
+	addToMap(tmpMap,"service",tval);
+      tval=NULL;
+      tval = (char*) xmlGetProp(cur,BAD_CAST "language");
+      if(tval!=NULL)
+	addToMap(tmpMap,"language",tval);
+      
+      const char* requests[3];
+      requests[0]="GetCapabilities";
+      requests[1]="DescribeProcess";
+      requests[2]="Execute";
+      for(int j=0;j<3;j++){
+	char tt[35];
+	sprintf(tt,"/*[local-name()='%s']",requests[j]);
+	xmlXPathObjectPtr reqptr=extractFromDoc(doc,tt);
+	if(reqptr!=NULL){
+	  xmlNodeSet* req=reqptr->nodesetval;
+#ifdef DEBUG
+	  fprintf(stderr,"%i",req->nodeNr);
+#endif
+	  if(req!=NULL && req->nodeNr==1){
+	    t1->value=strdup(requests[j]);
+	    j=2;
+	  }
+	  xmlXPathFreeObject(reqptr);
+	}
+	//xmlFree(req);
+      }
+      if(strncasecmp(t1->value,"GetCapabilities",15)==0){
+	xmlXPathObjectPtr versptr=extractFromDoc(doc,"/*/*/*[local-name()='Version']");
+	xmlNodeSet* vers=versptr->nodesetval;
+	xmlChar* content=xmlNodeListGetString(doc, vers->nodeTab[0]->xmlChildrenNode,1);
+	addToMap(tmpMap,"version",(char*)content);
+	xmlXPathFreeObject(versptr);
+	//xmlFree(vers);
+	xmlFree(content);
+      }else{
+	tval=NULL;
+	tval = (char*) xmlGetProp(cur,BAD_CAST "version");
+	if(tval!=NULL)
+	  addToMap(tmpMap,"version",tval);
+	xmlFree(tval);
+	tval = (char*) xmlGetProp(cur,BAD_CAST "language");
+	if(tval!=NULL)
+	  addToMap(tmpMap,"language",tval);
+	xmlXPathObjectPtr idptr=extractFromDoc(doc,"/*/*[local-name()='Identifier']");
+	if(idptr!=NULL){
+	  xmlNodeSet* id=idptr->nodesetval;
+	  if(id!=NULL){
+	    char* identifiers=NULL;
+	    identifiers=(char*)calloc(cgiContentLength,sizeof(char));
+	    identifiers[0]=0;
+	    for(int k=0;k<id->nodeNr;k++){
+	      xmlChar* content=xmlNodeListGetString(doc, id->nodeTab[k]->xmlChildrenNode,1);
+	      if(strlen(identifiers)>0){
+		char *tmp=strdup(identifiers);
+		snprintf(identifiers,strlen(tmp)+xmlStrlen(content)+2,"%s,%s",tmp,content);
+		free(tmp);
+	      }
+	      else{
+		snprintf(identifiers,xmlStrlen(content)+1,"%s",content);
+	      }
+	      xmlFree(content);
+	    }
+	    xmlXPathFreeObject(idptr);
+	    addToMap(tmpMap,"Identifier",identifiers);
+	    free(identifiers);
+	  }
+	}
+	//xmlFree(id);
+      }
+      xmlFree(tval);
+      xmlFreeDoc(doc);
+      xmlCleanupParser();
+    }
+  }
+
+  runRequest(tmpMap);
+
+  /** 
+   * Required but can't be made after executing a process using POST requests.
+   */
+  if(strncasecmp(cgiRequestMethod,"post",4)!=0 && count(tmpMap)!=1 && tmpMap!=NULL){
+    freeMap(&tmpMap);
+    free(tmpMap);
+  }
+  return 0;
+
+}
Index: /tags/rel-1.2.0-rc1/zoo-kernel/zoo_service_loader.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-kernel/zoo_service_loader.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-kernel/zoo_service_loader.c	(revision 221)
@@ -0,0 +1,1982 @@
+/**
+ * Author : Gérald FENOY
+ *
+ *  Copyright 2008-2011 GeoLabs SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define length(x) (sizeof(x) / sizeof(x[0]))
+
+extern "C" int yylex();
+extern "C" int crlex();
+
+extern "C" {
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+}
+
+#include "cgic.h"
+#include "ulinet.h"
+
+#include <libintl.h>
+#include <locale.h>
+#include <string.h>
+
+#include "service.h"
+
+#include "service_internal.h"
+
+#ifdef USE_PYTHON
+#include "service_internal_python.h"
+#endif
+
+#ifdef USE_JAVA
+#include "service_internal_java.h"
+#endif
+
+#ifdef USE_PHP
+#include "service_internal_php.h"
+#endif
+
+#ifdef USE_JS
+#include "service_internal_js.h"
+#endif
+
+#ifdef USE_PERL
+#include "service_internal_perl.h"
+#endif
+
+
+
+#include <dirent.h>
+#include <signal.h>
+#include <unistd.h>
+#ifndef WIN32
+#include <dlfcn.h>
+#include <libgen.h>
+#else
+#include <windows.h>
+#include <direct.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <stdarg.h>
+
+#define _(String) dgettext ("zoo-kernel",String)
+
+
+void translateChar(char* str,char toReplace,char toReplaceBy){
+  int i=0,len=strlen(str);
+  for(i=0;i<len;i++){
+    if(str[i]==toReplace)
+      str[i]=toReplaceBy;
+  }
+}
+
+xmlXPathObjectPtr extractFromDoc(xmlDocPtr doc,const char* search){
+  xmlXPathContextPtr xpathCtx;
+  xmlXPathObjectPtr xpathObj;
+  xpathCtx = xmlXPathNewContext(doc);
+  xpathObj = xmlXPathEvalExpression(BAD_CAST search,xpathCtx);
+  xmlXPathFreeContext(xpathCtx);
+  return xpathObj;
+}
+
+void donothing(int sig){
+  fprintf(stderr,"Signal %d after the ZOO-Kernel returned result !\n",sig);
+  exit(0);
+}
+
+void sig_handler(int sig){
+  char tmp[100];
+  const char *ssig;
+  switch(sig){
+  case SIGSEGV:
+    ssig="SIGSEGV";
+    break;
+  case SIGTERM:
+    ssig="SIGTERM";
+    break;
+  case SIGINT:
+    ssig="SIGINT";
+    break;
+  case SIGILL:
+    ssig="SIGILL";
+    break;
+  case SIGFPE:
+    ssig="SIGFPE";
+    break;
+  case SIGABRT:
+    ssig="SIGABRT";
+    break;
+  default:
+    ssig="UNKNOWN";
+    break;
+  }
+  sprintf(tmp,_("ZOO Kernel failed to process your request receiving signal %d = %s"),sig,ssig);
+  errorException(NULL, tmp, "InternalError");
+#ifdef DEBUG
+  fprintf(stderr,"Not this time!\n");
+#endif
+  exit(0);
+}
+
+void loadServiceAndRun(maps **myMap,service* s1,map* request_inputs,maps **inputs,maps** ioutputs,int* eres){
+  char tmps1[1024];
+  char ntmp[1024];
+  maps *m=*myMap;
+  maps *request_output_real_format=*ioutputs;
+  maps *request_input_real_format=*inputs;
+  /**
+   * Extract serviceType to know what kind of service should be loaded
+   */
+  map* r_inputs=NULL;
+#ifndef WIN32
+  char* pntmp=getcwd(ntmp,1024);
+#else
+  _getcwd(ntmp,1024);
+#endif
+  r_inputs=getMap(s1->content,"serviceType");
+#ifdef DEBUG
+  fprintf(stderr,"LOAD A %s SERVICE PROVIDER \n",r_inputs->value);
+  fflush(stderr);
+#endif
+  if(strncasecmp(r_inputs->value,"C",1)==0){
+    r_inputs=getMap(request_inputs,"metapath");
+    if(r_inputs!=NULL)
+      sprintf(tmps1,"%s/%s",ntmp,r_inputs->value);
+    else
+      sprintf(tmps1,"%s/",ntmp);
+    char *altPath=strdup(tmps1);
+    r_inputs=getMap(s1->content,"ServiceProvider");
+    sprintf(tmps1,"%s/%s",altPath,r_inputs->value);
+    free(altPath);
+#ifdef DEBUG
+    fprintf(stderr,"Trying to load %s\n",tmps1);
+#endif
+#ifdef WIN32
+    HINSTANCE so = LoadLibraryEx(tmps1,NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
+#else
+    void* so = dlopen(tmps1, RTLD_LAZY);
+#endif
+#ifdef DEBUG
+#ifdef WIN32
+    DWORD errstr;
+    errstr = GetLastError();
+    fprintf(stderr,"%s loaded (%d) \n",tmps1,errstr);
+#else
+    char *errstr;
+    errstr = dlerror();
+#endif
+#endif
+
+    if( so != NULL ) {
+#ifdef DEBUG
+      fprintf(stderr,"Library loaded %s \n",errstr);
+      fprintf(stderr,"Service Shared Object = %s\n",r_inputs->value);
+#endif
+      r_inputs=getMap(s1->content,"serviceType");
+#ifdef DEBUG
+      dumpMap(r_inputs);
+      fprintf(stderr,"%s\n",r_inputs->value);
+      fflush(stderr);
+#endif
+      if(strncasecmp(r_inputs->value,"C-FORTRAN",9)==0){
+	r_inputs=getMap(request_inputs,"Identifier");
+	char fname[1024];
+	sprintf(fname,"%s_",r_inputs->value);
+#ifdef DEBUG
+	fprintf(stderr,"Try to load function %s\n",fname);
+#endif
+#ifdef WIN32
+	typedef int (CALLBACK* execute_t)(char***,char***,char***);
+	execute_t execute=(execute_t)GetProcAddress(so,fname);
+#else
+	typedef int (*execute_t)(char***,char***,char***);
+	execute_t execute=(execute_t)dlsym(so,fname);
+#endif
+#ifdef DEBUG
+#ifdef WIN32
+	errstr = GetLastError();
+#else
+	errstr = dlerror();
+#endif
+	fprintf(stderr,"Function loaded %s\n",errstr);
+#endif	
+
+	char main_conf[10][30][1024];
+	char inputs[10][30][1024];
+	char outputs[10][30][1024];
+	for(int i=0;i<10;i++){
+	  for(int j=0;j<30;j++){
+	    memset(main_conf[i][j],0,1024);
+	    memset(inputs[i][j],0,1024);
+	    memset(outputs[i][j],0,1024);
+	  }
+	}
+	mapsToCharXXX(m,(char***)main_conf);
+	mapsToCharXXX(request_input_real_format,(char***)inputs);
+	mapsToCharXXX(request_output_real_format,(char***)outputs);
+	*eres=execute((char***)&main_conf[0],(char***)&inputs[0],(char***)&outputs[0]);
+#ifdef DEBUG
+	fprintf(stderr,"Function run successfully \n");
+#endif
+	charxxxToMaps((char***)&outputs[0],&request_output_real_format);
+      }else{
+#ifdef DEBUG
+#ifdef WIN32
+	errstr = GetLastError();
+	fprintf(stderr,"Function %s failed to load because of %d\n",r_inputs->value,errstr);
+#endif
+#endif
+	r_inputs=getMap(request_inputs,"Identifier");
+#ifdef DEBUG
+	fprintf(stderr,"Try to load function %s\n",r_inputs->value);
+#endif
+	typedef int (*execute_t)(maps**,maps**,maps**);
+#ifdef WIN32
+	execute_t execute=(execute_t)GetProcAddress(so,r_inputs->value); 
+#else
+	execute_t execute=(execute_t)dlsym(so,r_inputs->value);
+#endif
+
+#ifdef DEBUG
+#ifdef WIN32
+	errstr = GetLastError();
+#else
+	errstr = dlerror();
+#endif
+	fprintf(stderr,"Function loaded %s\n",errstr);
+#endif	
+
+#ifdef DEBUG
+	fprintf(stderr,"Now run the function \n");
+	fflush(stderr);
+#endif
+	*eres=execute(&m,&request_input_real_format,&request_output_real_format);
+#ifdef DEBUG
+	fprintf(stderr,"Function loaded and returned %d\n",eres);
+	fflush(stderr);
+#endif
+      }
+#ifdef WIN32
+      *ioutputs=dupMaps(&request_output_real_format);
+      FreeLibrary(so);
+#else
+      dlclose(so);
+#endif
+    } else {
+      /**
+       * Unable to load the specified shared library
+       */
+      char tmps[1024];
+#ifdef WIN32
+      DWORD errstr = GetLastError();
+#else
+      char* errstr = dlerror();
+#endif
+      sprintf(tmps,_("C Library can't be loaded %s \n"),errstr);
+      map* tmps1=createMap("text",tmps);
+      printExceptionReportResponse(m,tmps1);
+      *eres=-1;
+    }
+  }
+  else
+#ifdef USE_PYTHON
+    if(strncasecmp(r_inputs->value,"PYTHON",6)==0){
+      *eres=zoo_python_support(&m,request_inputs,s1,&request_input_real_format,&request_output_real_format);
+    }
+    else
+#endif
+	
+#ifdef USE_JAVA
+      if(strncasecmp(r_inputs->value,"JAVA",4)==0){
+	*eres=zoo_java_support(&m,request_inputs,s1,&request_input_real_format,&request_output_real_format);
+      }
+      else
+#endif
+
+#ifdef USE_PHP
+	if(strncasecmp(r_inputs->value,"PHP",3)==0){
+	  *eres=zoo_php_support(&m,request_inputs,s1,&request_input_real_format,&request_output_real_format);
+	}
+	else
+#endif
+	    
+	    
+#ifdef USE_PERL
+          if(strncasecmp(r_inputs->value,"PERL",4)==0){
+            *eres=zoo_perl_support(&m,request_inputs,s1,&request_input_real_format,&request_output_real_format);
+          }
+          else
+#endif
+
+#ifdef USE_JS
+	    if(strncasecmp(r_inputs->value,"JS",2)==0){
+	      *eres=zoo_js_support(&m,request_inputs,s1,&request_input_real_format,&request_output_real_format);
+	    }
+	    else
+#endif
+	      {
+		char tmpv[1024];
+		sprintf(tmpv,_("Programming Language (%s) set in ZCFG file is not currently supported by ZOO Kernel.\n"),r_inputs->value);
+		map* tmps=createMap("text",tmpv);
+		printExceptionReportResponse(m,tmps);
+		*eres=-1;
+	      }
+  *myMap=m;
+#ifndef WIN32
+  *ioutputs=request_output_real_format;
+#endif
+}
+
+#ifdef WIN32
+/**
+ * createProcess function: create a new process after setting some env variables
+ */
+void createProcess(maps* m,map* request_inputs,service* s1,char* opts,int cpid, maps* inputs,maps* outputs){
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+  ZeroMemory( &si, sizeof(si) );
+  si.cb = sizeof(si);
+  ZeroMemory( &pi, sizeof(pi) );
+  char *tmp=(char *)malloc((1024+cgiContentLength)*sizeof(char));
+  char *tmpq=(char *)malloc((1024+cgiContentLength)*sizeof(char));
+  map *req=getMap(request_inputs,"request");
+  map *id=getMap(request_inputs,"identifier");
+  map *di=getMap(request_inputs,"DataInputs");
+
+  char *dataInputsKVP=getMapsAsKVP(inputs,cgiContentLength,0);
+  char *dataOutputsKVP=getMapsAsKVP(outputs,cgiContentLength,1);
+  fprintf(stderr,"DATAINPUTSKVP %s\n",dataInputsKVP);
+  fprintf(stderr,"DATAOUTPUTSKVP %s\n",dataOutputsKVP);
+  map *sid=getMapFromMaps(m,"lenv","sid");
+  map* r_inputs=getMapFromMaps(m,"main","tmpPath");
+  map* r_inputs1=getMap(s1->content,"ServiceProvider");
+  map* r_inputs2=getMap(s1->content,"ResponseDocument");
+  if(r_inputs2==NULL)
+    r_inputs2=getMap(s1->content,"RawDataOutput");
+  map *tmpPath=getMapFromMaps(m,"lenv","cwd");
+
+  if(r_inputs2!=NULL){
+    sprintf(tmp,"\"request=%s&service=WPS&version=1.0.0&Identifier=%s&DataInputs=%s&%s=%s&cgiSid=%s\"",req->value,id->value,dataInputsKVP,r_inputs2->name,r_inputs2->value,sid->value);
+	sprintf(tmpq,"request=%s&service=WPS&version=1.0.0&Identifier=%s&DataInputs=%s&%s=%s",req->value,id->value,dataInputsKVP,r_inputs2->name,dataOutputsKVP);
+  }
+  else{
+    sprintf(tmp,"\"request=%s&service=WPS&version=1.0.0&Identifier=%s&DataInputs=%s&cgiSid=%s\"",req->value,id->value,dataInputsKVP,sid->value);
+    sprintf(tmpq,"request=%s&service=WPS&version=1.0.0&Identifier=%s&DataInputs=%s",req->value,id->value,dataInputsKVP,sid->value);
+  }
+
+  char *tmp1=strdup(tmp);
+  sprintf(tmp,"zoo_loader.cgi %s \"%s\"",tmp1,sid->value);
+
+  free(dataInputsKVP);
+  free(dataOutputsKVP);
+  fprintf(stderr,"REQUEST IS : %s \n",tmp);
+  SetEnvironmentVariable("CGISID",TEXT(sid->value));
+  SetEnvironmentVariable("QUERY_STRING",TEXT(tmpq));
+  char clen[1000];
+  sprintf(clen,"%d",strlen(tmpq));
+  SetEnvironmentVariable("CONTENT_LENGTH",TEXT(clen));
+
+  if( !CreateProcess( NULL,             // No module name (use command line)
+		      TEXT(tmp),        // Command line
+		      NULL,             // Process handle not inheritable
+		      NULL,             // Thread handle not inheritable
+		      FALSE,            // Set handle inheritance to FALSE
+		      CREATE_NO_WINDOW, // Apache won't wait until the end
+		      NULL,             // Use parent's environment block
+		      NULL,             // Use parent's starting directory 
+		      &si,              // Pointer to STARTUPINFO struct
+		      &pi )             // Pointer to PROCESS_INFORMATION struct
+      ) 
+    { 
+      fprintf( stderr, "CreateProcess failed (%d).\n", GetLastError() );
+      return ;
+    }else{
+    fprintf( stderr, "CreateProcess successfull (%d).\n\n\n\n", GetLastError() );
+  }
+  CloseHandle( pi.hProcess );
+  CloseHandle( pi.hThread );
+  fprintf(stderr,"CreateProcess finished !\n");
+}
+#endif
+
+int runRequest(map* request_inputs)
+{
+
+#ifndef USE_GDB
+  (void) signal(SIGSEGV,sig_handler);
+  (void) signal(SIGTERM,sig_handler);
+  (void) signal(SIGINT,sig_handler);
+  (void) signal(SIGILL,sig_handler);
+  (void) signal(SIGFPE,sig_handler);
+  (void) signal(SIGABRT,sig_handler);
+#endif
+
+  map* r_inputs=NULL;
+  maps* m=NULL;
+
+  char* REQUEST=NULL;
+  /**
+   * Parsing service specfic configuration file
+   */
+  m=(maps*)calloc(1,MAPS_SIZE);
+  if(m == NULL){
+    return errorException(m, _("Unable to allocate memory."), "InternalError");
+  }
+  char ntmp[1024];
+#ifndef WIN32
+  char *pntmp=getcwd(ntmp,1024);
+#else
+  _getcwd(ntmp,1024);
+#endif
+  r_inputs=getMap(request_inputs,"metapath");
+  if(r_inputs==NULL){
+    if(request_inputs==NULL)
+      request_inputs=createMap("metapath","");
+    else
+      addToMap(request_inputs,"metapath","");
+#ifdef DEBUG
+    fprintf(stderr,"ADD METAPATH\n");
+    dumpMap(request_inputs);
+#endif
+    r_inputs=getMap(request_inputs,"metapath");
+  }
+  char conf_file[10240];
+  snprintf(conf_file,10240,"%s/%s/main.cfg",ntmp,r_inputs->value);
+  conf_read(conf_file,m);
+#ifdef DEBUG
+  fprintf(stderr, "***** BEGIN MAPS\n"); 
+  dumpMaps(m);
+  fprintf(stderr, "***** END MAPS\n");
+#endif
+
+  bindtextdomain ("zoo-kernel","/usr/share/locale/");
+  bindtextdomain ("zoo-services","/usr/share/locale/");
+  
+  if((r_inputs=getMap(request_inputs,"language"))!=NULL){
+    char *tmp=strdup(r_inputs->value);
+    translateChar(tmp,'-','_');
+    setlocale (LC_ALL, tmp);
+    free(tmp);
+    setMapInMaps(m,"main","language",r_inputs->value);
+  }
+  else{
+    setlocale (LC_ALL, "en_US");
+    setMapInMaps(m,"main","language","en-US");
+  }
+  setlocale (LC_NUMERIC, "en_US");
+  bind_textdomain_codeset("zoo-kernel","UTF-8");
+  textdomain("zoo-kernel");
+  bind_textdomain_codeset("zoo-services","UTF-8");
+  textdomain("zoo-services");
+
+
+  /**
+   * Check for minimum inputs
+   */
+  r_inputs=getMap(request_inputs,"Request");
+  if(request_inputs==NULL || r_inputs==NULL){ 
+    errorException(m, _("Parameter <request> was not specified"),"MissingParameterValue");
+    freeMaps(&m);
+    free(m);
+    freeMap(&request_inputs);
+    free(request_inputs);
+    free(REQUEST);
+    return 1;
+  }
+  else{
+    REQUEST=strdup(r_inputs->value);
+    if(strncasecmp(r_inputs->value,"GetCapabilities",15)!=0
+       && strncasecmp(r_inputs->value,"DescribeProcess",15)!=0
+       && strncasecmp(r_inputs->value,"Execute",7)!=0){ 
+      errorException(m, _("Unenderstood <request> value. Please check that it was set to GetCapabilities, DescribeProcess or Execute."), "InvalidParameterValue");
+      freeMaps(&m);
+      free(m);
+      free(REQUEST);
+      return 1;
+    }
+  }
+  r_inputs=NULL;
+  r_inputs=getMap(request_inputs,"Service");
+  if(r_inputs==NULLMAP){
+    errorException(m, _("Parameter <service> was not specified"),"MissingParameterValue");
+    freeMaps(&m);
+    free(m);
+    free(REQUEST);
+    return 1;
+  }
+  if(strncasecmp(REQUEST,"GetCapabilities",15)!=0){
+    r_inputs=getMap(request_inputs,"Version");
+    if(r_inputs==NULL){ 
+      errorException(m, _("Parameter <version> was not specified"),"MissingParameterValue");
+      freeMaps(&m);
+      free(m);
+      free(REQUEST);
+      return 1;
+    }
+  }
+
+  r_inputs=getMap(request_inputs,"serviceprovider");
+  if(r_inputs==NULL){
+    addToMap(request_inputs,"serviceprovider","");
+  }
+
+  maps* request_output_real_format=NULL;
+  map* tmpm=getMapFromMaps(m,"main","serverAddress");
+  if(tmpm!=NULL)
+    SERVICE_URL=strdup(tmpm->value);
+  else
+    SERVICE_URL=strdup(DEFAULT_SERVICE_URL);
+
+  service* s1;
+  int scount=0;
+
+#ifdef DEBUG
+  dumpMap(r_inputs);
+#endif
+  char conf_dir[1024];
+  int t;
+  char tmps1[1024];
+
+  r_inputs=NULL;
+  r_inputs=getMap(request_inputs,"metapath");
+  if(r_inputs!=NULL)
+    snprintf(conf_dir,1024,"%s/%s",ntmp,r_inputs->value);
+  else
+    snprintf(conf_dir,1024,"%s",ntmp);
+
+  if(strncasecmp(REQUEST,"GetCapabilities",15)==0){
+    struct dirent *dp;
+#ifdef DEBUG
+    dumpMap(r_inputs);
+#endif
+    DIR *dirp = opendir(conf_dir);
+    if(dirp==NULL){
+      return errorException(m, _("The specified path doesn't exist."),"InvalidParameterValue");
+    }
+    xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
+    r_inputs=NULL;
+    r_inputs=getMap(request_inputs,"ServiceProvider");
+    xmlNodePtr n;
+    if(r_inputs!=NULL)
+      n = printGetCapabilitiesHeader(doc,r_inputs->value,m);
+    else
+      n = printGetCapabilitiesHeader(doc,"",m);
+    /**
+     * Here we need to close stdout to ensure that not supported chars 
+     * has been found in the zcfg and then printed on stdout
+     */
+    int saved_stdout = dup(fileno(stdout));
+    dup2(fileno(stderr),fileno(stdout));
+    while ((dp = readdir(dirp)) != NULL)
+      if(strstr(dp->d_name,".zcfg")!=0){
+	memset(tmps1,0,1024);
+	snprintf(tmps1,1024,"%s/%s",conf_dir,dp->d_name);
+	s1=(service*)calloc(1,SERVICE_SIZE);
+	if(s1 == NULL){ 
+	  return errorException(m, _("Unable to allocate memory."),"InternalError");
+	}
+#ifdef DEBUG
+	fprintf(stderr,"#################\n%s\n#################\n",tmps1);
+#endif
+	t=getServiceFromFile(tmps1,&s1);
+#ifdef DEBUG
+	dumpService(s1);
+	fflush(stdout);
+	fflush(stderr);
+#endif
+	printGetCapabilitiesForProcess(m,n,s1);
+	freeService(&s1);
+	free(s1);
+	scount++;
+      }
+    (void)closedir(dirp);
+    fflush(stdout);
+    dup2(saved_stdout,fileno(stdout));
+    printDocument(m,doc,getpid());
+    freeMaps(&m);
+    free(m);
+    free(REQUEST);
+    free(SERVICE_URL);
+    fflush(stdout);
+    return 0;
+  }
+  else{
+    r_inputs=getMap(request_inputs,"Identifier");
+    if(r_inputs==NULL 
+       || strlen(r_inputs->name)==0 || strlen(r_inputs->value)==0){ 
+      errorException(m, _("Mandatory <identifier> was not specified"),"MissingParameterValue");
+      freeMaps(&m);
+      free(m);
+      free(REQUEST);
+      free(SERVICE_URL);
+      return 0;
+    }
+
+    struct dirent *dp;
+    DIR *dirp = opendir(conf_dir);
+    if(dirp==NULL){
+      errorException(m, _("The specified path path doesn't exist."),"InvalidParameterValue");
+      freeMaps(&m);
+      free(m);
+      free(REQUEST);
+      free(SERVICE_URL);
+      return 0;
+    }
+    if(strncasecmp(REQUEST,"DescribeProcess",15)==0){
+      /**
+       * Loop over Identifier list
+       */
+      xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
+      r_inputs=NULL;
+      r_inputs=getMap(request_inputs,"ServiceProvider");
+
+      xmlNodePtr n;
+      if(r_inputs!=NULL)
+	n = printDescribeProcessHeader(doc,r_inputs->value,m);
+      else
+	n = printDescribeProcessHeader(doc,"",m);
+
+      r_inputs=getMap(request_inputs,"Identifier");
+      char *tmps=strtok(r_inputs->value,",");
+      
+      char buff[256];
+      char buff1[1024];
+      int saved_stdout = dup(fileno(stdout));
+      dup2(fileno(stderr),fileno(stdout));
+      while(tmps){
+	memset(buff,0,256);
+	snprintf(buff,256,"%s.zcfg",tmps);
+	memset(buff1,0,1024);
+#ifdef DEBUG
+	fprintf(stderr,"\n#######%s\n########\n",buff1);
+#endif
+	while ((dp = readdir(dirp)) != NULL)
+	  if(strcmp(dp->d_name,buff)==0){
+	    memset(buff1,0,1024);
+	    snprintf(buff1,1024,"%s/%s",conf_dir,dp->d_name);
+	    s1=(service*)calloc(1,SERVICE_SIZE);
+	    if(s1 == NULL){
+	      return errorException(m, _("Unable to allocate memory."),"InternalError");
+	    }
+#ifdef DEBUG
+	    fprintf(stderr,"#################\n%s\n#################\n",buff1);
+#endif
+	    t=getServiceFromFile(buff1,&s1);
+#ifdef DEBUG
+	    dumpService(s1);
+#endif
+	    printDescribeProcessForProcess(m,n,s1,1);
+	    freeService(&s1);
+	    free(s1);
+	    scount++;
+	  }
+	rewinddir(dirp);
+	tmps=strtok(NULL,",");
+      }
+      closedir(dirp);
+      fflush(stdout);
+      dup2(saved_stdout,fileno(stdout));
+      printDocument(m,doc,getpid());
+      freeMaps(&m);
+      free(m);
+      free(REQUEST);
+      free(SERVICE_URL);
+      fflush(stdout);
+#ifndef LINUX_FREE_ISSUE
+      if(s1)
+	free(s1);
+#endif
+      return 0;
+    }
+    else
+      if(strncasecmp(REQUEST,"Execute",strlen(REQUEST))!=0){
+	errorException(m, _("Unenderstood <request> value. Please check that it was set to GetCapabilities, DescribeProcess or Execute."), "InvalidParameterValue");
+#ifdef DEBUG
+	fprintf(stderr,"No request found %s",REQUEST);
+#endif	
+	closedir(dirp);
+	return 0;
+      }
+    closedir(dirp);
+  }
+  
+  s1=NULL;
+  s1=(service*)calloc(1,SERVICE_SIZE);
+  if(s1 == NULL){
+    freeMaps(&m);
+    free(m);
+    free(REQUEST);
+    free(SERVICE_URL);
+    return errorException(m, _("Unable to allocate memory."),"InternalError");
+  }
+  r_inputs=getMap(request_inputs,"MetaPath");
+  if(r_inputs!=NULL)
+    snprintf(tmps1,1024,"%s/%s",ntmp,r_inputs->value);
+  else
+    snprintf(tmps1,1024,"%s/",ntmp);
+  r_inputs=getMap(request_inputs,"Identifier");
+  char *ttmp=strdup(tmps1);
+  snprintf(tmps1,1024,"%s/%s.zcfg",ttmp,r_inputs->value);
+  free(ttmp);
+#ifdef DEBUG
+  fprintf(stderr,"Trying to load %s\n", tmps1);
+#endif
+  int saved_stdout = dup(fileno(stdout));
+    dup2(fileno(stderr),fileno(stdout));
+  t=getServiceFromFile(tmps1,&s1);
+  fflush(stdout);
+  dup2(saved_stdout,fileno(stdout));
+  if(t<0){
+    char *tmpMsg=(char*)malloc(2048+strlen(r_inputs->value));
+    
+    sprintf(tmpMsg,_("The value for <indetifier> seems to be wrong (%s). Please, ensure that the process exist using the GetCapabilities request."),r_inputs->value);
+    errorException(m, tmpMsg, "InvalidParameterValue");
+    free(tmpMsg);
+    freeService(&s1);
+    free(s1);
+    freeMaps(&m);
+    free(m);
+    free(REQUEST);
+    free(SERVICE_URL);
+    return 0;
+  }
+  //close(saved_stdout);
+
+#ifdef DEBUG
+  dumpService(s1);
+#endif
+  int j;
+  
+  /**
+   * Create the input maps data structure
+   */
+  int i=0;
+  HINTERNET hInternet;
+  HINTERNET res;
+  hInternet=InternetOpen(
+#ifndef WIN32
+			 (LPCTSTR)
+#endif
+			 "ZooWPSClient\0",
+			 INTERNET_OPEN_TYPE_PRECONFIG,
+			 NULL,NULL, 0);
+
+#ifndef WIN32
+  if(!CHECK_INET_HANDLE(hInternet))
+    fprintf(stderr,"WARNING : hInternet handle failed to initialize");
+#endif
+  maps* request_input_real_format=NULL;
+  maps* tmpmaps = request_input_real_format;
+  map* postRequest=NULL;
+  postRequest=getMap(request_inputs,"xrequest");
+  if(postRequest==NULLMAP){
+    /**
+     * Parsing outputs provided as KVP
+     */
+    r_inputs=NULL;
+#ifdef DEBUG
+    fprintf(stderr,"OUTPUT Parsing ... \n");
+#endif
+    r_inputs=getMap(request_inputs,"ResponseDocument");	
+    if(r_inputs==NULL) r_inputs=getMap(request_inputs,"RawDataOutput");
+    
+#ifdef DEBUG
+    fprintf(stderr,"OUTPUT Parsing ... \n");
+#endif
+    if(r_inputs!=NULL){
+#ifdef DEBUG
+      fprintf(stderr,"OUTPUT Parsing start now ... \n");
+#endif
+      char cursor_output[10240];
+      char *cotmp=strdup(r_inputs->value);
+      snprintf(cursor_output,10240,"%s",cotmp);
+      free(cotmp);
+      j=0;
+	
+      /**
+       * Put each Output into the outputs_as_text array
+       */
+      char * pToken;
+      maps* tmp_output=NULL;
+#ifdef DEBUG
+      fprintf(stderr,"OUTPUT [%s]\n",cursor_output);
+#endif
+      pToken=strtok(cursor_output,";");
+      char** outputs_as_text=(char**)calloc(128,sizeof(char*));
+      if(outputs_as_text == NULL) {
+	return errorException(m, _("Unable to allocate memory"), "InternalError");
+      }
+      i=0;
+      while(pToken!=NULL){
+#ifdef DEBUG
+	fprintf(stderr,"***%s***\n",pToken);
+	fflush(stderr);
+	fprintf(stderr,"***%s***\n",pToken);
+#endif
+	outputs_as_text[i]=(char*)calloc(strlen(pToken)+1,sizeof(char));
+	if(outputs_as_text[i] == NULL) {
+	  return errorException(m, _("Unable to allocate memory"), "InternalError");
+	}
+	snprintf(outputs_as_text[i],strlen(pToken)+1,"%s",pToken);
+	pToken = strtok(NULL,";");
+	i++;
+      }
+      for(j=0;j<i;j++){
+	char *tmp=strdup(outputs_as_text[j]);
+	free(outputs_as_text[j]);
+	char *tmpc;
+	tmpc=strtok(tmp,"@");
+	int k=0;
+	while(tmpc!=NULL){
+	  if(k==0){
+	    if(tmp_output==NULL){
+	      tmp_output=(maps*)calloc(1,MAPS_SIZE);
+	      if(tmp_output == NULL){
+		return errorException(m, _("Unable to allocate memory."), "InternalError");
+	      }
+	      tmp_output->name=strdup(tmpc);
+	      tmp_output->content=NULL;
+	      tmp_output->next=NULL;
+	    }
+	  }
+	  else{
+	    char *tmpv=strstr(tmpc,"=");
+	    char tmpn[256];
+	    memset(tmpn,0,256);
+	    strncpy(tmpn,tmpc,(strlen(tmpc)-strlen(tmpv))*sizeof(char));
+	    tmpn[strlen(tmpc)-strlen(tmpv)]=0;
+#ifdef DEBUG
+	    fprintf(stderr,"OUTPUT DEF [%s]=[%s]\n",tmpn,tmpv+1);
+#endif
+	    if(tmp_output->content==NULL){
+	      tmp_output->content=createMap(tmpn,tmpv+1);
+	      tmp_output->content->next=NULL;
+	    }
+	    else
+	      addToMap(tmp_output->content,tmpn,tmpv+1);
+	  }
+	  k++;
+#ifdef DEBUG
+	  fprintf(stderr,"***%s***\n",tmpc);
+#endif
+	  tmpc=strtok(NULL,"@");
+	}
+	if(request_output_real_format==NULL)
+	  request_output_real_format=dupMaps(&tmp_output);
+	else
+	  addMapsToMaps(&request_output_real_format,tmp_output);
+	freeMaps(&tmp_output);
+	free(tmp_output);
+	tmp_output=NULL;
+#ifdef DEBUG
+	dumpMaps(tmp_output);
+	fflush(stderr);
+#endif
+	free(tmp);
+      }
+      free(outputs_as_text);
+    }
+
+
+    /**
+     * Parsing inputs provided as KVP
+     */
+    r_inputs=getMap(request_inputs,"DataInputs");
+#ifdef DEBUG
+    fprintf(stderr,"DATA INPUTS [%s]\n",r_inputs->value);
+#endif
+    char cursor_input[40960];
+    if(r_inputs!=NULL)
+      snprintf(cursor_input,40960,"%s",r_inputs->value);
+    else{
+      errorException(m, _("Parameter <DataInputs> was not specified"),"MissingParameterValue");
+      freeMaps(&m);
+      free(m);
+      free(REQUEST);
+      free(SERVICE_URL);
+      InternetCloseHandle(hInternet);
+      freeService(&s1);
+      free(s1);
+      return 0;
+    }
+    j=0;
+  
+    /**
+     * Put each DataInputs into the inputs_as_text array
+     */
+    char * pToken;
+    pToken=strtok(cursor_input,";");
+    char** inputs_as_text=(char**)calloc(100,sizeof(char*));
+    if(inputs_as_text == NULL){
+      return errorException(m, _("Unable to allocate memory."), "InternalError");
+    }
+    i=0;
+    while(pToken!=NULL){
+#ifdef DEBUG
+      fprintf(stderr,"***%s***\n",pToken);
+#endif
+      fflush(stderr);
+#ifdef DEBUG
+      fprintf(stderr,"***%s***\n",pToken);
+#endif
+      inputs_as_text[i]=(char*)calloc(strlen(pToken)+1,sizeof(char));
+      snprintf(inputs_as_text[i],strlen(pToken)+1,"%s",pToken);
+      if(inputs_as_text[i] == NULL){
+	return errorException(m, _("Unable to allocate memory."), "InternalError");
+      }
+      pToken = strtok(NULL,";");
+      i++;
+    }
+
+    for(j=0;j<i;j++){
+      char *tmp=strdup(inputs_as_text[j]);
+      free(inputs_as_text[j]);
+      char *tmpc;
+      tmpc=strtok(tmp,"@");
+      while(tmpc!=NULL){
+#ifdef DEBUG
+	fprintf(stderr,"***\n***%s***\n",tmpc);
+#endif
+	char *tmpv=strstr(tmpc,"=");
+	char tmpn[256];
+	memset(tmpn,0,256);
+	if(tmpv!=NULL){
+	  strncpy(tmpn,tmpc,(strlen(tmpc)-strlen(tmpv))*sizeof(char));
+	  tmpn[strlen(tmpc)-strlen(tmpv)]=0;
+	}
+	else{
+	  strncpy(tmpn,tmpc,strlen(tmpc)*sizeof(char));
+	  tmpn[strlen(tmpc)]=0;
+	}
+#ifdef DEBUG
+	fprintf(stderr,"***\n*** %s = %s ***\n",tmpn,tmpv+1);
+#endif
+	if(tmpmaps==NULL){
+	  tmpmaps=(maps*)calloc(1,MAPS_SIZE);
+	  if(tmpmaps == NULL){
+	    return errorException(m, _("Unable to allocate memory."), "InternalError");
+	  }
+	  tmpmaps->name=strdup(tmpn);
+	  if(tmpv!=NULL)
+	    tmpmaps->content=createMap("value",tmpv+1);
+	  else
+	    tmpmaps->content=createMap("value","Reference");
+	  tmpmaps->next=NULL;
+	}
+	tmpc=strtok(NULL,"@");
+	while(tmpc!=NULL){
+#ifdef DEBUG
+	  fprintf(stderr,"*** KVP NON URL-ENCODED \n***%s***\n",tmpc);
+#endif
+	  char *tmpv1=strstr(tmpc,"=");
+#ifdef DEBUG
+	  fprintf(stderr,"*** VALUE NON URL-ENCODED \n***%s***\n",tmpv1+1);
+#endif
+	  char tmpn1[1024];
+	  memset(tmpn1,0,1024);
+	  if(tmpv1!=NULL){
+	    strncpy(tmpn1,tmpc,strlen(tmpc)-strlen(tmpv1));
+	    tmpn1[strlen(tmpc)-strlen(tmpv1)]=0;
+	    addToMap(tmpmaps->content,tmpn1,tmpv1+1);
+	  }
+	  else{
+	    strncpy(tmpn1,tmpc,strlen(tmpc));
+	    tmpn1[strlen(tmpc)]=0;
+	    map* lmap=getLastMap(tmpmaps->content);
+	    char *tmpValue=(char*)calloc((strlen(lmap->value)+strlen(tmpc)+1),sizeof(char));
+	    sprintf(tmpValue,"%s@%s",lmap->value,tmpc);
+	    free(lmap->value);
+	    lmap->value=strdup(tmpValue);
+	    free(tmpValue);
+	    dumpMap(tmpmaps->content);
+	    tmpc=strtok(NULL,"@");
+	    continue;
+	  }
+#ifdef DEBUG
+	  fprintf(stderr,"*** NAME NON URL-ENCODED \n***%s***\n",tmpn1);
+	  fprintf(stderr,"*** VALUE NON URL-ENCODED \n***%s***\n",tmpv1+1);
+#endif
+	  if(strcmp(tmpn1,"xlink:href")!=0)
+	    addToMap(tmpmaps->content,tmpn1,tmpv1+1);
+	  else
+	    if(tmpv1!=NULL){
+	      if(strncasecmp(tmpv1+1,"http://",7)!=0 &&
+		 strncasecmp(tmpv1+1,"ftp://",6)!=0){
+		char emsg[1024];
+		sprintf(emsg,_("Unable to find a valid protocol to download the remote file %s"),tmpv1+1);
+		errorException(m,emsg,"InternalError");
+		freeMaps(&m);
+		free(m);
+		free(REQUEST);
+		free(SERVICE_URL);
+		InternetCloseHandle(hInternet);
+		freeService(&s1);
+		free(s1);
+		return 0;
+	      }
+#ifdef DEBUG
+	      fprintf(stderr,"REQUIRE TO DOWNLOAD A FILE FROM A SERVER : url(%s)\n",tmpv1+1);
+#endif
+#ifndef WIN32
+	      if(CHECK_INET_HANDLE(hInternet))
+#endif
+		{
+		  res=InternetOpenUrl(hInternet,tmpv1+1,NULL,0,
+				      INTERNET_FLAG_NO_CACHE_WRITE,0);
+#ifdef DEBUG
+		  fprintf(stderr,"(%s) content-length : %d,,res.nDataAlloc %d \n",
+			  tmpv1+1,res.nDataAlloc,res.nDataLen);
+#endif
+		  char* tmpContent=(char*)calloc((res.nDataLen+1),sizeof(char));
+		  if(tmpContent == NULL){
+		    return errorException(m, _("Unable to allocate memory."), "InternalError");
+		  }
+		  size_t dwRead;
+		  InternetReadFile(res, (LPVOID)tmpContent,res.nDataLen, &dwRead);
+		  map* tmpMap=getMap(tmpmaps->content,"value");
+		  if(tmpMap!=NULL){
+		    free(tmpMap->value);
+		    tmpMap->value=(char*)malloc((res.nDataLen+1)*sizeof(char));
+		    memmove(tmpMap->value,tmpContent,(res.nDataLen)*sizeof(char));
+		    tmpMap->value[res.nDataLen]=0;
+		    if(strlen(tmpContent)!=res.nDataLen){
+		      char tmp[256];
+		      sprintf(tmp,"%d",res.nDataLen*sizeof(char));
+		      addToMap(tmpmaps->content,"size",tmp);
+		    }
+		  }
+		  free(tmpContent);
+		}
+	      char *tmpx=url_encode(tmpv1+1);
+	      addToMap(tmpmaps->content,tmpn1,tmpx);
+	      free(tmpx);
+	      addToMap(tmpmaps->content,"Reference",tmpv1+1);
+	      dumpMap(tmpmaps->content);
+	    }
+	  tmpc=strtok(NULL,"@");
+	}
+#ifdef DEBUG
+	dumpMaps(tmpmaps);
+	fflush(stderr);
+#endif
+	if(request_input_real_format==NULL)
+	  request_input_real_format=dupMaps(&tmpmaps);
+	else
+	  addMapsToMaps(&request_input_real_format,tmpmaps);
+	freeMaps(&tmpmaps);
+	free(tmpmaps);
+	tmpmaps=NULL;
+	free(tmp);
+      }
+    }
+    free(inputs_as_text);
+  }
+  else {
+    /**
+     * Parse XML request
+     */ 
+    xmlInitParser();
+#ifdef DEBUG
+    fflush(stderr);
+    fprintf(stderr,"BEFORE %s\n",postRequest->value);
+    fflush(stderr);
+#endif
+    xmlDocPtr doc =
+      xmlParseMemory(postRequest->value,cgiContentLength);
+#ifdef DEBUG
+    fprintf(stderr,"AFTER\n");
+    fflush(stderr);
+#endif
+    /**
+     * Parse every Input in DataInputs node.
+     */
+    xmlXPathObjectPtr tmpsptr=extractFromDoc(doc,"/*/*/*[local-name()='Input']");
+    xmlNodeSet* tmps=tmpsptr->nodesetval;
+#ifdef DEBUG
+    fprintf(stderr,"*****%d*****\n",tmps->nodeNr);
+#endif
+    for(int k=0;k<tmps->nodeNr;k++){
+      maps *tmpmaps=NULL;
+      xmlNodePtr cur=tmps->nodeTab[k];
+      if(tmps->nodeTab[k]->type == XML_ELEMENT_NODE) {
+	/**
+	 * A specific Input node.
+	 */
+#ifdef DEBUG
+	fprintf(stderr, "= element 0 node \"%s\"\n", cur->name);
+#endif
+	xmlNodePtr cur2=cur->children;
+	while(cur2!=NULL){
+	  while(cur2!=NULL && cur2->type!=XML_ELEMENT_NODE)
+	    cur2=cur2->next;
+	  if(cur2==NULL)
+	    break;
+	  /**
+	   * Indentifier
+	   */
+	  if(xmlStrncasecmp(cur2->name,BAD_CAST "Identifier",xmlStrlen(cur2->name))==0){
+	    xmlChar *val= xmlNodeListGetString(doc,cur2->xmlChildrenNode,1);
+	    if(tmpmaps==NULL){
+	      tmpmaps=(maps*)calloc(1,MAPS_SIZE);
+	      if(tmpmaps == NULL){
+		return errorException(m, _("Unable to allocate memory."), "InternalError");
+	      }
+	      tmpmaps->name=strdup((char*)val);
+	      tmpmaps->content=NULL;
+	      tmpmaps->next=NULL;
+	    }
+	    xmlFree(val);
+	  }
+	  /**
+	   * Title, Asbtract
+	   */
+	  if(xmlStrncasecmp(cur2->name,BAD_CAST "Title",xmlStrlen(cur2->name))==0 ||
+	     xmlStrncasecmp(cur2->name,BAD_CAST "Abstract",xmlStrlen(cur2->name))==0){
+	    xmlChar *val=
+	      xmlNodeListGetString(doc,cur2->xmlChildrenNode,1);
+	    if(tmpmaps==NULL){
+	      tmpmaps=(maps*)calloc(1,MAPS_SIZE);
+	      if(tmpmaps == NULL){
+		return errorException(m, _("Unable to allocate memory."), "InternalError");
+	      }
+	      tmpmaps->name=strdup("missingIndetifier");
+	      tmpmaps->content=createMap((char*)cur2->name,(char*)val);
+	      tmpmaps->next=NULL;
+	    }
+	    else{
+	      if(tmpmaps->content!=NULL)
+		addToMap(tmpmaps->content,
+			 (char*)cur2->name,(char*)val);
+	      else
+		tmpmaps->content=
+		  createMap((char*)cur2->name,(char*)val);
+	    }
+#ifdef DEBUG
+	    dumpMaps(tmpmaps);
+#endif
+	    xmlFree(val);
+	  }
+	  /**
+	   * InputDataFormChoice (Reference or Data ?) 
+	   */
+	  if(xmlStrcasecmp(cur2->name,BAD_CAST "Reference")==0){
+	    /**
+	     * Get every attribute from a Reference node
+	     * mimeType, encoding, schema, href, method
+	     * Header and Body gesture should be added here
+	     */
+#ifdef DEBUG
+	    fprintf(stderr,"REFERENCE\n");
+#endif
+	    const char *refs[5];
+	    refs[0]="mimeType";
+	    refs[1]="encoding";
+	    refs[2]="schema";
+	    refs[3]="method";
+	    refs[4]="href";
+	    for(int l=0;l<5;l++){
+#ifdef DEBUG
+	      fprintf(stderr,"*** %s ***",refs[l]);
+#endif
+	      xmlChar *val=xmlGetProp(cur2,BAD_CAST refs[l]);
+	      if(val!=NULL && xmlStrlen(val)>0){
+		if(tmpmaps->content!=NULL)
+		  addToMap(tmpmaps->content,refs[l],(char*)val);
+		else
+		  tmpmaps->content=createMap(refs[l],(char*)val);
+		map* ltmp=getMap(tmpmaps->content,"method");
+		if(l==4){
+		  if(!(ltmp!=NULL && strcmp(ltmp->value,"POST")==0)
+		     && CHECK_INET_HANDLE(hInternet)){
+		    res=InternetOpenUrl(hInternet,(char*)val,NULL,0,
+					INTERNET_FLAG_NO_CACHE_WRITE,0);
+		    char* tmpContent=
+		      (char*)calloc((res.nDataLen+1),sizeof(char));
+		    if(tmpContent == NULL){
+		      return errorException(m, _("Unable to allocate memory."), "InternalError");
+		    }
+		    size_t dwRead;
+		    InternetReadFile(res, (LPVOID)tmpContent,
+				     res.nDataLen, &dwRead);
+		    tmpContent[res.nDataLen]=0;
+		    addToMap(tmpmaps->content,"value",tmpContent);
+		  }
+		}
+	      }
+#ifdef DEBUG
+	      fprintf(stderr,"%s\n",val);
+#endif
+	      xmlFree(val);
+	    }
+#ifdef POST_DEBUG
+	    fprintf(stderr,"Parse Header and Body from Reference \n");
+#endif
+	    xmlNodePtr cur3=cur2->children;
+	    hInternet.header=NULL;
+	    while(cur3){
+	      while(cur3!=NULL && cur3->type!=XML_ELEMENT_NODE)
+		cur2=cur3->next;
+	      if(xmlStrcasecmp(cur3->name,BAD_CAST "Header")==0 ){
+		const char *ha[2];
+		ha[0]="key";
+		ha[1]="value";
+		int hai;
+		char *has;
+		char *key;
+		for(hai=0;hai<2;hai++){
+		  xmlChar *val=xmlGetProp(cur3,BAD_CAST ha[hai]);
+#ifdef POST_DEBUG
+		  fprintf(stderr,"%s = %s\n",ha[hai],(char*)val);
+#endif
+		  if(hai==0){
+		    key=(char*)calloc((1+strlen((char*)val)),sizeof(char));
+		    snprintf(key,1+strlen((char*)val),"%s",(char*)val);
+		  }else{
+		    has=(char*)calloc((3+strlen((char*)val)+strlen(key)),sizeof(char));
+		    if(has == NULL){
+		      return errorException(m, _("Unable to allocate memory."), "InternalError");
+		    }
+		    snprintf(has,(3+strlen((char*)val)+strlen(key)),"%s: %s",key,(char*)val);
+#ifdef POST_DEBUG
+		    fprintf(stderr,"%s\n",has);
+#endif
+		  }
+		}
+		hInternet.header=curl_slist_append(hInternet.header, has);
+		free(has);
+	      }
+	      else{
+#ifdef POST_DEBUG
+		fprintf(stderr,"Try to fetch the body part of the request ...\n");
+#endif
+		if(xmlStrcasecmp(cur3->name,BAD_CAST "Body")==0 ){
+#ifdef POST_DEBUG
+		  fprintf(stderr,"Body part found !!!\n",(char*)cur3->content);
+#endif
+		  char *tmp=new char[cgiContentLength];
+		  memset(tmp,0,cgiContentLength);
+		  xmlNodePtr cur4=cur3->children;
+		  while(cur4!=NULL){
+		    while(cur4->type!=XML_ELEMENT_NODE)
+		      cur4=cur4->next;
+		    xmlDocPtr bdoc = xmlNewDoc(BAD_CAST "1.0");
+		    bdoc->encoding = xmlCharStrdup ("UTF-8");
+		    xmlDocSetRootElement(bdoc,cur4);
+		    xmlChar* btmps;
+		    int bsize;
+		    xmlDocDumpMemory(bdoc,&btmps,&bsize);
+#ifdef POST_DEBUG
+		    fprintf(stderr,"Body part found !!! %s %s\n",tmp,(char*)btmps);
+#endif
+		    if(btmps!=NULL)
+		      sprintf(tmp,"%s",(char*)btmps);
+		    xmlFreeDoc(bdoc);
+		    cur4=cur4->next;
+		  }
+		  map *btmp=getMap(tmpmaps->content,"href");
+		  if(btmp!=NULL){
+#ifdef POST_DEBUG
+		    fprintf(stderr,"%s %s\n",btmp->value,tmp);
+		    curl_easy_setopt(hInternet.handle, CURLOPT_VERBOSE, 1);
+#endif
+		    res=InternetOpenUrl(hInternet,btmp->value,tmp,strlen(tmp),
+					INTERNET_FLAG_NO_CACHE_WRITE,0);
+		    char* tmpContent = (char*)calloc((res.nDataLen+1),sizeof(char));
+		    if(tmpContent == NULL){
+		      return errorException(m, _("Unable to allocate memory."), "InternalError");
+		    }
+		    size_t dwRead;
+		    InternetReadFile(res, (LPVOID)tmpContent,
+				     res.nDataLen, &dwRead);
+		    tmpContent[res.nDataLen]=0;
+		    if(hInternet.header!=NULL)
+		      curl_slist_free_all(hInternet.header);
+		    addToMap(tmpmaps->content,"value",tmpContent);
+#ifdef POST_DEBUG
+		    fprintf(stderr,"DL CONTENT : (%s)\n",tmpContent);
+#endif
+		  }
+		}
+		else
+		  if(xmlStrcasecmp(cur3->name,BAD_CAST "BodyReference")==0 ){
+		    xmlChar *val=xmlGetProp(cur3,BAD_CAST "href");
+		    HINTERNET bInternet,res1;
+		    bInternet=InternetOpen(
+#ifndef WIN32
+					   (LPCTSTR)
+#endif
+					   "ZooWPSClient\0",
+					   INTERNET_OPEN_TYPE_PRECONFIG,
+					   NULL,NULL, 0);
+		    if(!CHECK_INET_HANDLE(bInternet))
+		      fprintf(stderr,"WARNING : hInternet handle failed to initialize");
+#ifdef POST_DEBUG
+		    curl_easy_setopt(bInternet.handle, CURLOPT_VERBOSE, 1);
+#endif
+		    res1=InternetOpenUrl(bInternet,(char*)val,NULL,0,
+					 INTERNET_FLAG_NO_CACHE_WRITE,0);
+		    char* tmp=
+		      (char*)calloc((res1.nDataLen+1),sizeof(char));
+		    if(tmp == NULL){
+		      return errorException(m, _("Unable to allocate memory."), "InternalError");
+		    }
+		    size_t bRead;
+		    InternetReadFile(res1, (LPVOID)tmp,
+				     res1.nDataLen, &bRead);
+		    tmp[res1.nDataLen]=0;
+		    InternetCloseHandle(bInternet);
+		    map *btmp=getMap(tmpmaps->content,"href");
+		    if(btmp!=NULL){
+#ifdef POST_DEBUG
+		      fprintf(stderr,"%s %s\n",btmp->value,tmp);
+		      curl_easy_setopt(hInternet.handle, CURLOPT_VERBOSE, 1);
+#endif
+		      res=InternetOpenUrl(hInternet,btmp->value,tmp,
+					  strlen(tmp),
+					  INTERNET_FLAG_NO_CACHE_WRITE,0);
+		      char* tmpContent = (char*)calloc((res.nDataLen+1),sizeof(char));
+		      if(tmpContent == NULL){
+			return errorException(m, _("Unable to allocate memory."), "InternalError");
+		      }
+		      size_t dwRead;
+		      InternetReadFile(res, (LPVOID)tmpContent,
+				       res.nDataLen, &dwRead);
+		      tmpContent[res.nDataLen]=0;
+		      if(hInternet.header!=NULL)
+			curl_slist_free_all(hInternet.header);
+		      addToMap(tmpmaps->content,"value",tmpContent);
+#ifdef POST_DEBUG
+		      fprintf(stderr,"DL CONTENT : (%s)\n",tmpContent);
+#endif
+		    }
+		  }
+	      }
+	      cur3=cur3->next;
+	    }
+#ifdef POST_DEBUG
+	    fprintf(stderr,"Header and Body was parsed from Reference \n");
+#endif
+#ifdef DEBUG
+	    dumpMap(tmpmaps->content);
+	    fprintf(stderr, "= element 2 node \"%s\" = (%s)\n", 
+		    cur2->name,cur2->content);
+#endif
+	  }
+	  else if(xmlStrcasecmp(cur2->name,BAD_CAST "Data")==0){
+#ifdef DEBUG
+	    fprintf(stderr,"DATA\n");
+#endif
+	    xmlNodePtr cur4=cur2->children;
+	    while(cur4!=NULL){
+	      while(cur4!=NULL &&cur4->type!=XML_ELEMENT_NODE)
+		cur4=cur4->next;
+	      if(cur4==NULL)
+		break;
+	      if(xmlStrcasecmp(cur4->name, BAD_CAST "LiteralData")==0){
+		/**
+		 * Get every attribute from a LiteralData node
+		 * dataType , uom
+		 */
+		char *list[2];
+		list[0]=strdup("dataType");
+		list[1]=strdup("uom");
+		for(int l=0;l<2;l++){
+#ifdef DEBUG
+		  fprintf(stderr,"*** LiteralData %s ***",list[l]);
+#endif
+		  xmlChar *val=xmlGetProp(cur4,BAD_CAST list[l]);
+		  if(val!=NULL && strlen((char*)val)>0){
+		    if(tmpmaps->content!=NULL)
+		      addToMap(tmpmaps->content,list[l],(char*)val);
+		    else
+		      tmpmaps->content=createMap(list[l],(char*)val);
+		  }
+#ifdef DEBUG
+		  fprintf(stderr,"%s\n",val);
+#endif
+		  xmlFree(val);
+		  free(list[l]);
+		}
+	      }
+	      else if(xmlStrcasecmp(cur4->name, BAD_CAST "ComplexData")==0){
+		/**
+		 * Get every attribute from a Reference node
+		 * mimeType, encoding, schema
+		 */
+		const char *coms[3];
+		coms[0]="mimeType";
+		coms[1]="encoding";
+		coms[2]="schema";
+		for(int l=0;l<3;l++){
+#ifdef DEBUG
+		  fprintf(stderr,"*** ComplexData %s ***",coms[l]);
+#endif
+		  xmlChar *val=xmlGetProp(cur4,BAD_CAST coms[l]);
+		  if(val!=NULL && strlen((char*)val)>0){
+		    if(tmpmaps->content!=NULL)
+		      addToMap(tmpmaps->content,coms[l],(char*)val);
+		    else
+		      tmpmaps->content=createMap(coms[l],(char*)val);
+		  }
+#ifdef DEBUG
+		  fprintf(stderr,"%s\n",val);
+#endif
+		  xmlFree(val);
+		}
+	      }
+	      map* test=getMap(tmpmaps->content,"encoding");
+	      if(test==NULL || strcasecmp(test->value,"base64")!=0){
+		xmlChar* mv=xmlNodeListGetString(doc,cur4->xmlChildrenNode,1);
+		if(mv==NULL){
+		  xmlDocPtr doc1=xmlNewDoc(BAD_CAST "1.0");
+		  int buffersize;
+		  xmlDocSetRootElement(doc1,cur4->xmlChildrenNode);
+		  xmlDocDumpFormatMemoryEnc(doc1, &mv, &buffersize, "utf-8", 1);
+		  char size[1024];
+		  sprintf(size,"%d",buffersize);
+		  addToMap(tmpmaps->content,"size",size);
+		}
+		addToMap(tmpmaps->content,"value",(char*)mv);
+		xmlFree(mv);
+	      }else{
+		xmlChar* tmp=xmlNodeListGetRawString(doc,cur4->xmlChildrenNode,0);
+		addToMap(tmpmaps->content,"value",(char*)tmp);
+		map* tmpv=getMap(tmpmaps->content,"value");
+		char *res=NULL;
+		char *curs=tmpv->value;
+		for(int i=0;i<=strlen(tmpv->value)/64;i++) {
+		  if(res==NULL)
+		    res=(char*)malloc(67*sizeof(char));
+		  else
+		    res=(char*)realloc(res,(((i+1)*65)+i)*sizeof(char));
+		  int csize=i*65;
+		  strncpy(res + csize,curs,64);
+		  if(i==xmlStrlen(tmp)/64)
+		    strcat(res,"\n\0");
+		  else{
+		    strncpy(res + (((i+1)*64)+i),"\n\0",2);
+		    curs+=64;
+		  }
+		}
+		free(tmpv->value);
+		tmpv->value=strdup(res);
+		free(res);
+		xmlFree(tmp);
+	      }
+	      cur4=cur4->next;
+	    }
+	  }
+#ifdef DEBUG
+	  fprintf(stderr,"cur2 next \n");
+	  fflush(stderr);
+#endif
+	  cur2=cur2->next;
+	}
+#ifdef DEBUG
+	fprintf(stderr,"ADD MAPS TO REQUEST MAPS !\n");
+	fflush(stderr);
+#endif
+	addMapsToMaps(&request_input_real_format,tmpmaps);
+	
+#ifdef DEBUG
+	fprintf(stderr,"******TMPMAPS*****\n");
+	dumpMaps(tmpmaps);
+	fprintf(stderr,"******REQUESTMAPS*****\n");
+	dumpMaps(request_input_real_format);
+#endif
+	freeMaps(&tmpmaps);
+	free(tmpmaps);
+	tmpmaps=NULL;	      
+      }
+#ifdef DEBUG
+      dumpMaps(tmpmaps); 
+#endif
+    }
+#ifdef DEBUG
+    fprintf(stderr,"Search for response document node\n");
+#endif
+    xmlXPathFreeObject(tmpsptr);
+    
+    tmpsptr=extractFromDoc(doc,"/*/*/*[local-name()='ResponseDocument']");
+    bool asRaw=false;
+    tmps=tmpsptr->nodesetval;
+    if(tmps->nodeNr==0){
+      tmpsptr=extractFromDoc(doc,"/*/*/*[local-name()='RawDataOutput']");
+      tmps=tmpsptr->nodesetval;
+      asRaw=true;
+    }
+#ifdef DEBUG
+    fprintf(stderr,"*****%d*****\n",tmps->nodeNr);
+#endif
+    for(int k=0;k<tmps->nodeNr;k++){
+      if(asRaw==true)
+	addToMap(request_inputs,"RawDataOutput","");
+      else
+	addToMap(request_inputs,"ResponseDocument","");
+      maps *tmpmaps=NULL;
+      xmlNodePtr cur=tmps->nodeTab[k];
+      if(cur->type == XML_ELEMENT_NODE) {
+	/**
+	 * A specific responseDocument node.
+	 */
+	if(tmpmaps==NULL){
+	  tmpmaps=(maps*)calloc(1,MAPS_SIZE);
+	  if(tmpmaps == NULL){
+	    return errorException(m, _("Unable to allocate memory."), "InternalError");
+	  }
+	  tmpmaps->name=strdup("unknownIdentifier");
+	  tmpmaps->next=NULL;
+	}
+	/**
+	 * Get every attribute from a LiteralData node
+	 * storeExecuteResponse, lineage, status
+	 */
+	const char *ress[3];
+	ress[0]="storeExecuteResponse";
+	ress[1]="lineage";
+	ress[2]="status";
+	xmlChar *val;
+	for(int l=0;l<3;l++){
+#ifdef DEBUG
+	  fprintf(stderr,"*** %s ***\t",ress[l]);
+#endif
+	  val=xmlGetProp(cur,BAD_CAST ress[l]);
+	  if(val!=NULL && strlen((char*)val)>0){
+	    if(tmpmaps->content!=NULL)
+	      addToMap(tmpmaps->content,ress[l],(char*)val);
+	    else
+	      tmpmaps->content=createMap(ress[l],(char*)val);
+	    addToMap(request_inputs,ress[l],(char*)val);
+	  }
+#ifdef DEBUG
+	  fprintf(stderr,"%s\n",val);
+#endif
+	  xmlFree(val);
+	}
+	xmlNodePtr cur1=cur->children;
+	while(cur1){
+	  if(xmlStrncasecmp(cur1->name,BAD_CAST "Output",xmlStrlen(cur1->name))==0){
+	    /**
+	     * Get every attribute from a Output node
+	     * mimeType, encoding, schema, uom, asReference
+	     */
+	    const char *outs[5];
+	    outs[0]="mimeType";
+	    outs[1]="encoding";
+	    outs[2]="schema";
+	    outs[3]="uom";
+	    outs[4]="asReference";
+	    for(int l=0;l<5;l++){
+#ifdef DEBUG
+	      fprintf(stderr,"*** %s ***\t",outs[l]);
+#endif
+	      val=xmlGetProp(cur1,BAD_CAST outs[l]);
+	      if(val!=NULL && strlen((char*)val)>0){
+		if(tmpmaps->content!=NULL)
+		  addToMap(tmpmaps->content,outs[l],(char*)val);
+		else
+		  tmpmaps->content=createMap(outs[l],(char*)val);
+	      }
+#ifdef DEBUG
+	      fprintf(stderr,"%s\n",val);
+#endif
+	      xmlFree(val);
+	    }
+	    
+	    xmlNodePtr cur2=cur1->children;
+	    while(cur2){
+	      /**
+	       * Indentifier
+	       */
+	      if(xmlStrncasecmp(cur2->name,BAD_CAST "Identifier",xmlStrlen(cur2->name))==0){
+		xmlChar *val=
+		  xmlNodeListGetString(doc,cur2->xmlChildrenNode,1);
+		if(tmpmaps==NULL){
+		  tmpmaps=(maps*)calloc(1,MAPS_SIZE);
+		  if(tmpmaps == NULL){
+		    return errorException(m, _("Unable to allocate memory."), "InternalError");
+		  }
+		  tmpmaps->name=strdup((char*)val);
+		  tmpmaps->content=NULL;
+		  tmpmaps->next=NULL;
+		}
+		else
+		  tmpmaps->name=strdup((char*)val);;
+		xmlFree(val);
+	      }
+	      /**
+	       * Title, Asbtract
+	       */
+	      if(xmlStrncasecmp(cur2->name,BAD_CAST "Title",xmlStrlen(cur2->name))==0 ||
+		 xmlStrncasecmp(cur2->name,BAD_CAST "Abstract",xmlStrlen(cur2->name))==0){
+		xmlChar *val=
+		  xmlNodeListGetString(doc,cur2->xmlChildrenNode,1);
+		if(tmpmaps==NULL){
+		  tmpmaps=(maps*)calloc(1,MAPS_SIZE);
+		  if(tmpmaps == NULL){
+		    return errorException(m, _("Unable to allocate memory."), "InternalError");
+		  }
+		  tmpmaps->name=strdup("missingIndetifier");
+		  tmpmaps->content=createMap((char*)cur2->name,(char*)val);
+		  tmpmaps->next=NULL;
+		}
+		else{
+		  if(tmpmaps->content!=NULL)
+		    addToMap(tmpmaps->content,
+			     (char*)cur2->name,(char*)val);
+		  else
+		    tmpmaps->content=
+		      createMap((char*)cur2->name,(char*)val);
+		}
+		xmlFree(val);
+	      }
+	      cur2=cur2->next;
+	    }
+	  }
+	  cur1=cur1->next;
+	}
+      }
+      if(request_output_real_format==NULL)
+	request_output_real_format=dupMaps(&tmpmaps);
+      else
+	addMapsToMaps(&request_output_real_format,tmpmaps);
+#ifdef DEBUG
+      dumpMaps(tmpmaps);
+#endif
+      freeMaps(&tmpmaps);
+      free(tmpmaps);
+    }
+
+    xmlXPathFreeObject(tmpsptr);
+    xmlCleanupParser();
+  }
+  
+  //if(CHECK_INET_HANDLE(hInternet))
+  InternetCloseHandle(hInternet);
+
+#ifdef DEBUG
+  fprintf(stderr,"\n%i\n",i);
+  dumpMaps(request_input_real_format);
+  dumpMaps(request_output_real_format);
+  dumpMap(request_inputs);
+#endif
+
+  /**
+   * Ensure that each requested arguments are present in the request
+   * DataInputs and ResponseDocument / RawDataOutput
+   */
+  char *dfv=addDefaultValues(&request_input_real_format,s1->inputs,m,0);
+  if(strcmp(dfv,"")!=0){
+    char tmps[1024];
+    snprintf(tmps,1024,_("The <%s> argument was not specified in DataInputs but defined as requested in ZOO ServicesProvider configuration file, please correct your query or the ZOO Configuration file."),dfv);
+    map* tmpe=createMap("text",tmps);
+    addToMap(tmpe,"code","MissingParameterValue");
+    printExceptionReportResponse(m,tmpe);
+    freeService(&s1);
+    free(s1);
+    freeMap(&tmpe);
+    free(tmpe);
+    freeMaps(&m);
+    free(m);
+    free(REQUEST);
+    free(SERVICE_URL);
+    freeMaps(&request_input_real_format);
+    free(request_input_real_format);
+    freeMaps(&request_output_real_format);
+    free(request_output_real_format);
+    freeMaps(&tmpmaps);
+    free(tmpmaps);
+    return 1;
+  }
+  addDefaultValues(&request_output_real_format,s1->outputs,m,1);
+
+  ensureDecodedBase64(&request_input_real_format);
+
+#ifdef DEBUG
+  fprintf(stderr,"REQUEST_INPUTS\n");
+  dumpMaps(request_input_real_format);
+  fprintf(stderr,"REQUEST_OUTPUTS\n");
+  dumpMaps(request_output_real_format);
+#endif
+
+  maps* curs=getMaps(m,"env");
+  if(curs!=NULL){
+    map* mapcs=curs->content;
+    while(mapcs!=NULLMAP){
+#ifndef WIN32
+      setenv(mapcs->name,mapcs->value,1);
+#else
+#ifdef DEBUG
+      fprintf(stderr,"[ZOO: setenv (%s=%s)]\n",mapcs->name,mapcs->value);
+#endif
+      if(mapcs->value[strlen(mapcs->value)-2]=='\r'){
+#ifdef DEBUG
+	fprintf(stderr,"[ZOO: Env var finish with \r]\n");
+#endif
+	mapcs->value[strlen(mapcs->value)-1]=0;
+      }
+#ifdef DEBUG
+      fflush(stderr);
+      fprintf(stderr,"setting variable... %s\n",
+#endif
+	      SetEnvironmentVariable(mapcs->name,mapcs->value)
+#ifdef DEBUG
+	      ? "OK" : "FAILED");
+#else
+      ;
+#endif
+#ifdef DEBUG
+      fflush(stderr);
+#endif
+#endif
+#ifdef DEBUG
+      fprintf(stderr,"[ZOO: setenv (%s=%s)]\n",mapcs->name,mapcs->value);
+      fflush(stderr);
+#endif
+      mapcs=mapcs->next;
+    }
+  }
+  
+#ifdef DEBUG
+  dumpMap(request_inputs);
+#endif
+
+  /**
+   * Need to check if we need to fork to load a status enabled 
+   */
+  r_inputs=NULL;
+  map* store=getMap(request_inputs,"storeExecuteResponse");
+  map* status=getMap(request_inputs,"status");
+  /**
+   * 05-007r7 WPS 1.0.0 page 57 :
+   * 'If status="true" and storeExecuteResponse is "false" then the service 
+   * shall raise an exception.'
+   */
+  if(status!=NULL && strcmp(status->value,"true")==0 && 
+     store!=NULL && strcmp(store->value,"false")==0){
+    errorException(m, _("Status cannot be set to true with storeExecuteResponse to false. Please, modify your request parameters."), "InvalidParameterValue");
+    freeService(&s1);
+    free(s1);
+    freeMaps(&m);
+    free(m);
+    
+    freeMaps(&request_input_real_format);
+    free(request_input_real_format);
+    
+    freeMaps(&request_output_real_format);
+    free(request_output_real_format);
+    
+    free(REQUEST);
+    free(SERVICE_URL);
+    return 1;
+  }
+  r_inputs=getMap(request_inputs,"storeExecuteResponse");
+  int eres=SERVICE_STARTED;
+  int cpid=getpid();
+
+  maps *_tmpMaps=(maps*)malloc(MAPS_SIZE);
+  _tmpMaps->name=strdup("lenv");
+  char tmpBuff[100];
+  sprintf(tmpBuff,"%i",cpid);
+  _tmpMaps->content=createMap("sid",tmpBuff);
+  _tmpMaps->next=NULL;
+  addToMap(_tmpMaps->content,"status","0");
+  addToMap(_tmpMaps->content,"cwd",ntmp);
+  if(cgiCookie!=NULL && strlen(cgiCookie)>0){
+    addToMap(_tmpMaps->content,"sessid",strstr(cgiCookie,"=")+1);
+    char session_file_path[1024];
+    map *tmpPath=getMapFromMaps(m,"main","sessPath");
+    if(tmpPath==NULL)
+      tmpPath=getMapFromMaps(m,"main","tmpPath");
+    sprintf(session_file_path,"%s/sess_%s.cfg",tmpPath->value,strstr(cgiCookie,"=")+1);
+    maps *tmpSess=(maps*)calloc(1,MAPS_SIZE);
+    struct stat file_status;
+    int istat = stat(session_file_path, &file_status);
+    if(istat==0){
+      conf_read(session_file_path,tmpSess);
+      dumpMaps(tmpSess);
+      addMapsToMaps(&m,tmpSess);
+      freeMaps(&tmpSess);
+    }
+    free(tmpSess);
+  }
+  addMapsToMaps(&m,_tmpMaps);
+  freeMaps(&_tmpMaps);
+  free(_tmpMaps);
+
+#ifdef DEBUG
+  dumpMap(request_inputs);
+#endif
+#ifdef WIN32
+  char *cgiSidL=NULL;
+  if(getenv("CGISID")!=NULL)
+	addToMap(request_inputs,"cgiSid",getenv("CGISID"));
+  map* test1=getMap(request_inputs,"cgiSid");
+  if(test1!=NULL){
+    cgiSid=test1->value;
+  }
+  if(cgiSid!=NULL){
+    addToMap(request_inputs,"storeExecuteResponse","true");
+    addToMap(request_inputs,"status","true");
+    status=getMap(request_inputs,"status");
+    dumpMap(request_inputs);
+    fprintf(stderr,"cgiSID : %s",cgiSid);
+  }
+#endif
+  if(status!=NULL)
+    if(strcasecmp(status->value,"false")==0)
+      status=NULL;
+  if(status==NULLMAP){
+    loadServiceAndRun(&m,s1,request_inputs,&request_input_real_format,&request_output_real_format,&eres);
+  }
+  else{
+    pid_t   pid;
+#ifdef DEBUG
+    fprintf(stderr,"\nPID : %d\n",cpid);
+#endif
+
+#ifndef WIN32
+    pid = fork ();
+#else
+    if(cgiSid==NULL){
+      addToMap(request_inputs,"cgSid",cgiSid);
+      createProcess(m,request_inputs,s1,NULL,cpid,request_input_real_format,request_output_real_format);
+      pid = cpid;
+    }else{
+      pid=0;
+      cpid=atoi(cgiSid);
+    }
+    fflush(stderr);
+#endif
+    if (pid > 0) {
+      /**
+       * dady :
+       * set status to SERVICE_ACCEPTED
+       */
+#ifdef DEBUG
+      fprintf(stderr,"father pid continue (origin %d) %d ...\n",cpid,getpid());
+#endif
+      eres=SERVICE_ACCEPTED;
+    }else if (pid == 0) {
+      /**
+       * son : have to close the stdout, stdin and stderr to let the parent
+       * process answer to http client.
+       */
+      r_inputs=getMapFromMaps(m,"main","tmpPath");
+      map* r_inputs1=getMap(s1->content,"ServiceProvider");
+      char* fbkp=(char*)malloc((strlen(r_inputs->value)+strlen(r_inputs1->value)+100)*sizeof(char));
+      sprintf(fbkp,"%s/%s_%d.xml",r_inputs->value,r_inputs1->value,cpid);
+      char* flog=(char*)malloc((strlen(r_inputs->value)+strlen(r_inputs1->value)+100)*sizeof(char));
+      sprintf(flog,"%s/%s_%d_error.log",r_inputs->value,r_inputs1->value,cpid);
+#ifdef DEBUG
+      fprintf(stderr,"RUN IN BACKGROUND MODE \n");
+      fprintf(stderr,"son pid continue (origin %d) %d ...\n",cpid,getpid());
+      fprintf(stderr,"\nFILE TO STORE DATA %s\n",r_inputs->value);
+#endif
+      freopen(flog,"w+",stderr);
+      freopen(fbkp , "w+", stdout);
+      fclose(stdin);
+      free(fbkp);
+      free(flog);
+      /**
+       * set status to SERVICE_STARTED and flush stdout to ensure full 
+       * content was outputed (the file used to store the ResponseDocument).
+       * The rewind stdout to restart writing from the bgining of the file,
+       * this way the data will be updated at the end of the process run.
+       */
+      updateStatus(m);
+      printProcessResponse(m,request_inputs,cpid,
+			   s1,r_inputs1->value,SERVICE_STARTED,
+			   request_input_real_format,
+			   request_output_real_format);
+#ifndef WIN32
+      fflush(stdout);
+      rewind(stdout);
+#endif
+
+      loadServiceAndRun(&m,s1,request_inputs,&request_input_real_format,&request_output_real_format,&eres);
+
+    } else {
+      /**
+       * error server don't accept the process need to output a valid 
+       * error response here !!!
+       */
+      eres=-1;
+      errorException(m, _("Unable to run the child process properly"), "InternalError");
+    }
+  }
+
+#ifdef DEBUG
+  dumpMaps(request_output_real_format);
+  fprintf(stderr,"Function loaded and returned %d\n",*eres);
+  fflush(stderr);
+#endif
+  if(eres!=-1)
+    outputResponse(s1,request_input_real_format,
+		   request_output_real_format,request_inputs,
+		   cpid,m,eres);
+  fflush(stdout);
+  /**
+   * Ensure that if error occurs when freeing memory, no signal will return
+   * an ExceptionReport document as the result was already returned to the 
+   * client.
+   */
+#ifndef USE_GDB
+  (void) signal(SIGSEGV,donothing);
+  (void) signal(SIGTERM,donothing);
+  (void) signal(SIGINT,donothing);
+  (void) signal(SIGILL,donothing);
+  (void) signal(SIGFPE,donothing);
+  (void) signal(SIGABRT,donothing);
+#endif
+
+  if(((int)getpid())!=cpid){
+    fclose(stdout);
+    fclose(stderr);
+    unhandleStatus(m);
+  }
+
+  freeService(&s1);
+  free(s1);
+  freeMaps(&m);
+  free(m);
+  
+  freeMaps(&request_input_real_format);
+  free(request_input_real_format);
+  
+  freeMaps(&request_output_real_format);
+  free(request_output_real_format);
+  
+  free(REQUEST);
+  free(SERVICE_URL);
+#ifdef DEBUG
+  fprintf(stderr,"Processed response \n");
+  fflush(stdout);
+  fflush(stderr);
+#endif
+
+  return 0;
+}
Index: /tags/rel-1.2.0-rc1/zoo-services/arithmetics/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/arithmetics/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/arithmetics/Makefile	(revision 221)
@@ -0,0 +1,8 @@
+CFLAGS=-I../../zoo-kernel/ -I./ `xml2-config --cflags` `python-config --cflags`  `gdal-config --cflags`   -DLINUX_FREE_ISSUE #-DDEBUG
+CC=gcc
+
+cgi-env/test_service.zo: test_service.c
+	g++ ${CFLAGS} -shared -fpic -o cgi-env/test_service.zo ./test_service.c
+
+clean:
+	rm -f cgi-env/test_service.zo
Index: /tags/rel-1.2.0-rc1/zoo-services/arithmetics/cgi-env/Multiply.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/arithmetics/cgi-env/Multiply.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/arithmetics/cgi-env/Multiply.zcfg	(revision 221)
@@ -0,0 +1,43 @@
+[Multiply]
+ Title = Multiply two values
+ Abstract = Multiply two values and stor the result in Result.
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = test_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [A]
+   Title = first value
+   Abstract = The value used to multiply by B.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = float
+    <Default>
+    </Default>
+   </LiteralData>
+  [B]
+   Title = second value
+   Abstract = The value used to multiply by A.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = float
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = A x B 
+   Abstract = The value of A x B.
+   <LiteralOutput>
+    DataType = float
+    <Default>
+    </Default>
+   </LiteralOutput>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/arithmetics/makefile.vc
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/arithmetics/makefile.vc	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/arithmetics/makefile.vc	(revision 221)
@@ -0,0 +1,11 @@
+GEODIR=c:/OSGeo4W/
+TOOLS=c:/Users/djay/GeoLabs/tools/
+CFLAGS=-I$(GEODIR)/include -I$(TOOLS)/include -I../../zoo-kernel/ -I./ -DLINUX_FREE_ISSUE -DDEBUG -DWIN32
+CPP=cl /TP 
+
+cgi-env/test_service.zo: test_service.c
+	$(CPP) $(CFLAGS) /c test_service.c
+	link /dll /out:cgi-env/test_service.zo ../../zoo-kernel/service_internal.obj ./test_service.obj -L$(TOOLS)/lib/libssl32.dll.a $(GEODIR)/lib/libxml2.lib $(GEODIR)/lib/gdal_i.lib $(TOOLS)/lib/libeay32.dll.a $(TOOLS)/lib/libcrypto.a $(TOOLS)/lib/libssl32.dll.a $(TOOLS)/lib/libintl.lib
+
+clean:
+	erase cgi-env\demo_service.*
Index: /tags/rel-1.2.0-rc1/zoo-services/arithmetics/test_service.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/arithmetics/test_service.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/arithmetics/test_service.c	(revision 221)
@@ -0,0 +1,103 @@
+#include "service.h"
+
+extern "C" {
+
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+  int Multiply(maps*& conf,maps*& inputs,maps*& outputs){
+  	fprintf(stderr,"\nService internal print\n");
+  	maps* cursor=inputs;
+	int A,B,res;
+	A=0;B=0;
+	if(cursor!=NULL){
+		fprintf(stderr,"\nService internal print\n");
+		dumpMaps(cursor);
+		maps* tmp=getMaps(inputs,"A");
+		if(tmp==NULL)
+			return SERVICE_FAILED;
+		fprintf(stderr,"\nService internal print\n");
+		dumpMap(tmp->content);
+		map* tmpv=getMap(tmp->content,"value");
+		fprintf(stderr,"\nService internal print\n");
+		A=atoi(tmpv->value);
+		fprintf(stderr,"\nService internal print (A value: %i)\n",A);
+		cursor=cursor->next;
+	}
+	if(cursor!=NULL){
+		maps* tmp=getMaps(cursor,"B");
+		map* tmpv=getMap(tmp->content,"value");
+		if(tmpv==NULL)
+			return SERVICE_FAILED;
+		B=atoi(tmpv->value);
+		fprintf(stderr,"\nService internal print (B value: %i)\n",B);
+	}
+	res=A*B;
+	outputs=(maps*)malloc(sizeof(maps*));
+	outputs->name="Result";
+	char tmp[256];
+	sprintf(tmp,"%i",res);
+	outputs->content=createMap("value",tmp);
+	addMapToMap(&outputs->content,createMap("datatype","float"));
+	addMapToMap(&outputs->content,createMap("uom","meter"));
+	outputs->next=NULL;
+	dumpMaps(outputs);
+  	fprintf(stderr,"\nService internal print\n===\n");
+	return SERVICE_SUCCEEDED;
+  }
+
+  int helloworld1(maps*& conf,maps*& inputs,maps*& outputs){
+    outputs=(maps*)malloc(sizeof(maps*));
+    outputs->name="Result";
+    outputs->content=createMap("value","Hello World");
+    addMapToMap(&outputs->content,createMap("datatype","string"));
+    return SERVICE_SUCCEEDED; 
+  }
+
+  int helloworld(map*& inputs,map*& outputs){
+    outputs=createMap("output_0","Hello World\n");
+    return SERVICE_SUCCEEDED; 
+  }
+
+  int printArguments(map*& inputs,map*& outputs){
+    char *res=(char *)malloc(sizeof(char));
+    map* tmp=inputs;
+    while(tmp!=NULL){
+      res=(char *)realloc(res,strlen(res)+strlen(tmp->value)+strlen(tmp->name)+6);
+      sprintf(res,"%s,\"%s\"=\"%s\"",res,tmp->name,tmp->value);
+      //sprintf(res,"%s,\"%s\"=\"%s\"",res,tmp->name,tmp->value);
+      tmp=tmp->next;
+    }
+    char *tmpVal=strdup(res+1);
+    outputs=createMap("output_0",tmpVal);
+    addToMap(outputs,"output_1",tmpVal);
+
+    /*dumpMap(outputs);
+      dumpMap(inputs);*/
+    return SERVICE_SUCCEEDED; 
+  }
+
+  int buildJsonArrayOfArgs(map*& inputs,map*& outputs){
+    char *res=(char *)malloc(sizeof(char));
+    map* tmp=inputs;
+    while(tmp!=NULL){
+      res=(char *)realloc(res,strlen(res)+strlen(tmp->value)+3);
+      sprintf(res,"%s,\"%s\"",res,tmp->value);
+      tmp=tmp->next;
+    }
+    char *tmpVal;
+    if(strncmp(res,",",1)!=0){
+      free(tmpVal);
+      tmpVal=strdup(res+1);
+      //dumpMap(inputs);      
+    }else
+      tmpVal=strdup(res);
+    tmpVal=(char*)realloc(tmpVal,strlen(tmpVal)+2);
+    sprintf(tmpVal,"[%s]",tmpVal);
+    outputs=createMap("output_0",tmpVal+1);
+    //dumpMap(outputs);
+    //dumpMap(inputs);
+    return SERVICE_SUCCEEDED; 
+  }
+
+}
Index: /tags/rel-1.2.0-rc1/zoo-services/arithmetics/test_service.h
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/arithmetics/test_service.h	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/arithmetics/test_service.h	(revision 221)
@@ -0,0 +1,1 @@
+int helloworld(int);
Index: /tags/rel-1.2.0-rc1/zoo-services/cgal/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/cgal/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/cgal/Makefile	(revision 221)
@@ -0,0 +1,9 @@
+CFLAGS=-I../../zoo-kernel/ -I./ `xml2-config --cflags` `python-config --cflags` -DLINUX_FREE_ISSUE #-DDEBUG
+CC=gcc
+
+cgi-env/cgal_service.zo: service.c
+	g++ ${CFLAGS} -shared -fpic -o cgi-env/cgal_service.zo ./service.c -lgdal -lCGAL
+
+clean:
+	rm -f cgi-env/*.zo *.o *.tab.c *.tab.h *.sr.c* service_loader lex.* *.lreg *.sibling
+	rm -rf service_loader.dSYM
Index: /tags/rel-1.2.0-rc1/zoo-services/cgal/cgi-env/Voronoi.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/cgal/cgi-env/Voronoi.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/cgal/cgi-env/Voronoi.zcfg	(revision 221)
@@ -0,0 +1,59 @@
+[Voronoi]
+ Title = Voronoi Diagram. 
+ Abstract = Computes the edges of Voronoi diagram of a set of data points.
+ Profile = urn:ogc:wps:1.0.0:voronoi
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = cgal_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPoints]
+   Title = Data points
+   Abstract = The set of data points.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = Voronoi Diagram.
+   Abstract = JSON String / GML Entity of the Voronoi Diagram.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexOutput>
+    <Default>
+     mimeType = application/json
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+    <Supported>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+     asReference = true	
+    </Supported>
+   </ComplexOutput>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/cgal/service.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/cgal/service.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/cgal/service.c	(revision 221)
@@ -0,0 +1,272 @@
+#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
+#include <CGAL/Triangulation_euclidean_traits_xy_3.h>
+#include <CGAL/Delaunay_triangulation_2.h>
+#include <CGAL/Constrained_Delaunay_triangulation_2.h>
+#include <CGAL/Triangulation_conformer_2.h>
+#include <CGAL/Triangulation_face_base_2.h>
+
+#include <fstream>
+
+#include "cpl_minixml.h"
+#include "ogr_api.h"
+#include "ogrsf_frmts.h"
+#include "service.h"
+
+typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
+
+typedef CGAL::Delaunay_triangulation_2<K>  Triangulation;
+typedef Triangulation::Edge_iterator  Edge_iterator;
+typedef Triangulation::Point          Point;
+
+typedef CGAL::Constrained_Delaunay_triangulation_2<K> CDT;
+typedef CDT::Point Point;
+typedef CDT::Vertex_handle Vertex_handle;
+
+typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
+typedef CGAL::Triangulation_euclidean_traits_xy_3<K>  Gt;
+typedef CGAL::Delaunay_triangulation_2<Gt> DelaunayTriangulation;
+
+typedef Triangulation::Vertex_circulator Vertex_circulator;
+
+typedef K::Point_3   Point1;
+
+extern "C" {
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+
+  xmlNodeSet* extractFromDoc(xmlDocPtr,char*);
+  void printExceptionReportResponse(maps*,map*);
+
+  int Voronoi(maps*& conf,maps*& inputs,maps*& outputs){
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print\nStarting\n");
+#endif
+    maps* cursor=inputs;
+    OGRGeometryH geometry,res;
+    int bufferDistance;
+    xmlInitParser();
+    map* tmpm=NULL;
+    tmpm=getMapFromMaps(inputs,"InputPoints","value");
+    
+    xmlInitParser();
+    xmlDocPtr doc =
+      xmlParseMemory(tmpm->value,strlen(tmpm->value));
+    xmlNodePtr cur = xmlDocGetRootElement(doc);
+    /**
+     * Parse every Input in DataInputs node.
+     */
+    maps* tempMaps=NULL;
+    xmlXPathContextPtr xpathCtx;
+    xmlXPathObjectPtr xpathObj;
+    xpathCtx = xmlXPathNewContext(doc);
+    xpathObj = xmlXPathEvalExpression(BAD_CAST "/*/*[local-name()='featureMember']/*/*/*[local-name()='Point']/*[local-name()='coordinates']",xpathCtx);
+    xmlXPathFreeContext(xpathCtx); 
+    xmlNodeSet* nSet=xpathObj->nodesetval;
+
+    if(nSet==NULL){
+      setMapInMaps(conf,"lenv","message","Unable to continue !!!");
+      return SERVICE_FAILED;
+    }
+    char filepath[2048];
+    map* tmpMap=getMapFromMaps(conf,"main","tmpPath");
+    if(tmpMap!=NULL){
+      sprintf(filepath,"%s/varonoi_%d.tmp",tmpMap->value,getpid());
+    }
+    FILE *fo=fopen(filepath,"w");
+#ifdef DEBUG
+    fprintf(stderr,"File Creation (%s) OK\nPrinting %d Points.\n",filepath,nSet->nodeNr);
+#endif
+    for(int k=0;k<nSet->nodeNr;k++){
+      xmlNodePtr cur=nSet->nodeTab[k];
+      char *val=
+	(char*)xmlNodeListGetString(doc,cur->xmlChildrenNode,1);
+      char *tmp=strstr(val,",");
+      char tmp1[1024];
+      strncpy(tmp1,val,strlen(val)-strlen(tmp));
+      tmp1[strlen(val)-strlen(tmp)]=0;
+      char buff[1024];
+      sprintf(buff,"%s %s\n",tmp1,tmp+1);
+      fwrite(buff,1,strlen(buff)*sizeof(char),fo);
+    }
+    fclose(fo);
+#ifdef DEBUG
+    fprintf(stderr,"File Close (%s) OK\n",filepath);
+#endif
+
+    std::ifstream in(filepath);
+    std::istream_iterator<Point> begin(in);
+    std::istream_iterator<Point> end;
+    Triangulation T;
+    T.insert(begin, end);
+
+    OGRRegisterAll();
+    /* -------------------------------------------------------------------- */
+    /*      Try opening the output datasource as an existing, writable      */
+    /* -------------------------------------------------------------------- */
+    OGRDataSource       *poODS;
+    
+    OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar();
+    OGRSFDriver          *poDriver = NULL;
+    int                  iDriver;
+
+    tmpMap=getMapFromMaps(outputs,"Result","mimeType");
+    const char *oDriver;
+    oDriver="GeoJSON";
+    if(tmpMap!=NULL){
+      if(strcmp(tmpMap->value,"text/xml")==0){
+	oDriver="GML";
+      }
+    }
+    
+    for( iDriver = 0;
+	 iDriver < poR->GetDriverCount() && poDriver == NULL;
+	 iDriver++ )
+      {
+#ifdef DEBUG
+	fprintf(stderr,"D:%s\n",poR->GetDriver(iDriver)->GetName());
+#endif
+	if( EQUAL(poR->GetDriver(iDriver)->GetName(),oDriver) )
+	  {
+	    poDriver = poR->GetDriver(iDriver);
+	  }
+      }
+
+    if( poDriver == NULL )
+      {
+	char emessage[8192];
+	sprintf( emessage, "Unable to find driver `%s'.\n", oDriver );
+	sprintf( emessage,  "%sThe following drivers are available:\n",emessage );
+        
+	for( iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ )
+	  {
+	    sprintf( emessage,  "%s  -> `%s'\n", emessage, poR->GetDriver(iDriver)->GetName() );
+	  }
+
+	setMapInMaps(conf,"lenv","message",emessage);
+	return SERVICE_FAILED;
+
+      }
+
+    if( !poDriver->TestCapability( ODrCCreateDataSource ) ){
+      char emessage[1024];
+      sprintf( emessage,  "%s driver does not support data source creation.\n",
+	       "json" );
+      setMapInMaps(conf,"lenv","message",emessage);
+      return SERVICE_FAILED;
+    }
+
+    /* -------------------------------------------------------------------- */
+    /*      Create the output data source.                                  */
+    /* -------------------------------------------------------------------- */
+    map* tpath=getMapFromMaps(conf,"main","tmpPath");
+    char *pszDestDataSource=(char*)malloc(strlen(tpath->value)+20);
+    char **papszDSCO=NULL;
+    sprintf(pszDestDataSource,"%s/result_%d.json",tpath->value,getpid());
+    poODS = poDriver->CreateDataSource( pszDestDataSource, papszDSCO );
+    if( poODS == NULL ){
+      char emessage[1024];      
+      sprintf( emessage,  "%s driver failed to create %s\n", 
+	       "json", pszDestDataSource );
+      setMapInMaps(conf,"lenv","message",emessage);
+      return SERVICE_FAILED;
+    }
+
+    /* -------------------------------------------------------------------- */
+    /*      Create the layer.                                               */
+    /* -------------------------------------------------------------------- */
+    if( !poODS->TestCapability( ODsCCreateLayer ) )
+      {
+	char emessage[1024];
+	sprintf( emessage, 
+		 "Layer %s not found, and CreateLayer not supported by driver.", 
+		 "Result" );
+	setMapInMaps(conf,"lenv","message",emessage);
+	return SERVICE_FAILED;
+      }
+    
+    CPLErrorReset();
+    
+    OGRLayer *poDstLayer = poODS->CreateLayer( "Result", NULL,wkbLineString,NULL);
+    if( poDstLayer == NULL ){
+      setMapInMaps(conf,"lenv","message","Layer creation failed.\n");
+      return SERVICE_FAILED;
+    }
+
+
+    int ns = 0;
+    int nr = 0;
+    Edge_iterator eit =T.edges_begin();
+    for ( ; eit !=T.edges_end(); ++eit) {
+      CGAL::Object o = T.dual(eit);
+      if (const K::Segment_2 *tmp=CGAL::object_cast<K::Segment_2>(&o)) {
+	const K::Point_2 p1=tmp->source();
+	const K::Point_2 p2=tmp->target();
+#ifdef DEBUG
+	fprintf(stderr,"P1 %d %d | P2 %d %d\n",p1.x(),p1.y(),p2.x(),p2.y());
+#endif
+	OGRFeatureH hFeature = OGR_F_Create( OGR_L_GetLayerDefn( poDstLayer ) );
+	OGRGeometryH currLine=OGR_G_CreateGeometry(wkbLineString);
+	OGR_G_AddPoint_2D(currLine,p1.x(),p1.y());
+	OGR_G_AddPoint_2D(currLine,p2.x(),p2.y());
+	OGR_F_SetGeometry( hFeature, currLine ); 
+	OGR_G_DestroyGeometry(currLine);
+	if( OGR_L_CreateFeature( poDstLayer, hFeature ) != OGRERR_NONE ){
+	  setMapInMaps(conf,"lenv","message","Failed to create feature in file.\n");
+	  return SERVICE_FAILED;
+	}
+	OGR_F_Destroy( hFeature );
+	++ns ;
+      }
+      else if (CGAL::object_cast<K::Ray_2>(&o)) {++nr;}
+    }
+    OGR_DS_Destroy( poODS );
+    OGRCleanupAll();
+
+#ifdef DEBUG
+    std::cerr << "The Voronoi diagram has " << ns << " finite edges "
+	      << " and " << nr << " rays" << std::endl;
+    sprintf(tmp1,"%d finite edges, %d rays",ns,nr);
+#endif
+    
+    char tmp1[1024];
+
+    FILE * fichier=fopen(pszDestDataSource,"r"); 
+    struct stat file_status;
+    stat(pszDestDataSource, &file_status);
+    char *res1=(char *)malloc(file_status.st_size*sizeof(char));
+    if(fichier==NULL){
+      char tmp[1024];
+      sprintf(tmp,"Failed to open file %s for reading purpose.\n",
+	      pszDestDataSource);
+      setMapInMaps(conf,"lenv","message",tmp);
+      return SERVICE_FAILED;
+    }
+    fread(res1,1,(file_status.st_size)*sizeof(char),fichier);
+    res1[strlen(res1)]=0;
+    fclose(fichier);
+    unlink(pszDestDataSource);
+    
+    setMapInMaps(outputs,"Result","value",res1);
+    
+    if(strcmp(oDriver,"GML")==0)
+      setMapInMaps(outputs,"Result","mimeType","text/xml");
+    else
+      setMapInMaps(outputs,"Result","mimeType","text/plain");
+
+    setMapInMaps(outputs,"Result","encoding","UTF-8");
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print\n===\n");
+#endif
+    xmlCleanupParser();
+    return SERVICE_SUCCEEDED;
+  }
+
+}
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/Makefile	(revision 221)
@@ -0,0 +1,8 @@
+CFLAGS=-I../../../zoo-kernel-svn/ -I./ `xml2-config --cflags` `python-config --cflags` -DLINUX_FREE_ISSUE #-DDEBUG
+CC=gcc
+
+cgi-env/service.zo: service.c
+	g++  -DZOO_SERVICE ${CFLAGS} -shared -fpic -o cgi-env/gdal_grid_service.zo ./service.c -lgdal
+
+clean:
+	rm -f cgi-env/*.zo
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/cgi-env/Gdal_Grid.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/cgi-env/Gdal_Grid.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/cgi-env/Gdal_Grid.zcfg	(revision 221)
@@ -0,0 +1,69 @@
+[Gdal_Grid]
+ Title = Convert raster data from one format to another. 
+ Abstract = Converts raster data between different formats.
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceType = C
+ serviceProvider = gdal_grid_service.zo
+ <MetaData>
+   title = My Demo
+ </MetaData>
+ <DataInputs>
+  [OF]
+   Title = Format of the output data
+   Abstract = Select the output format.
+   minOccurs = 0
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+     value = AAIGrid
+    </Default>
+    <Supported>
+     value = AAIGrid
+    </Supported>
+   </LiteralData>
+  [InputDSN]
+   Title = The input data source name
+   Abstract = The input data source name to use as source for convertion.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+     uom = feet
+    </Default>	
+    <Supported>
+     value = AAIGrid
+    </Supported>
+   </LiteralData>
+  [OutputDSN]
+   Title = The output data source name
+   Abstract = The output data source name to use as source for convertion.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+     uom = feet
+    </Default>	
+    <Supported>
+     value = AAIGrid
+    </Supported>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [OutputedDataSourceName]
+   Title = The resulting converted file
+   Abstract = The file name resulting of the convertion
+   <LiteralData>
+    DataType = string
+    <Default>
+     uom = feet
+    </Default>	
+    <Supported>
+     value = AAIGrid
+    </Supported>
+   </LiteralData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/service.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/service.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/grid/service.c	(revision 221)
@@ -0,0 +1,1258 @@
+/* ****************************************************************************
+ * $Id: gdal_grid.cpp 15053 2008-07-27 17:29:27Z rouault $
+ *
+ * Project:  GDAL Utilities
+ * Purpose:  GDAL scattered data gridding (interpolation) tool
+ * Author:   Andrey Kiselev, dron@ak4719.spb.edu
+ *
+ * ****************************************************************************
+ * Copyright (c) 2007, Andrey Kiselev <dron@ak4719.spb.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include <vector>
+#include <algorithm>
+
+#include "cpl_string.h"
+#include "gdal.h"
+#include "gdal_alg.h"
+#include "ogr_spatialref.h"
+#include "ogr_api.h"
+
+#ifdef ZOO_SERVICE
+#include "service.h"
+#endif
+
+CPL_CVSID("$Id: gdal_grid.cpp 15053 2008-07-27 17:29:27Z rouault $");
+
+#ifdef ZOO_SERVICE
+extern "C" {
+#endif
+
+static const char szAlgNameInvDist[] = "invdist";
+static const char szAlgNameAverage[] = "average";
+static const char szAlgNameNearest[] = "nearest";
+static const char szAlgNameMinimum[] = "minimum";
+static const char szAlgNameMaximum[] = "maximum";
+static const char szAlgNameRange[] = "range";
+
+/************************************************************************/
+/*                               Usage()                                */
+/************************************************************************/
+
+static void Usage()
+{
+#ifdef ZOO_SERVICE
+  fprintf(stderr,
+#else
+    printf( 
+#endif
+        "Usage: gdal_grid [--help-general] [--formats]\n"
+        "    [-ot {Byte/Int16/UInt16/UInt32/Int32/Float32/Float64/\n"
+        "          CInt16/CInt32/CFloat32/CFloat64}]\n"
+        "    [-of format] [-co \"NAME=VALUE\"]\n"
+        "    [-zfield field_name]\n"
+        "    [-a_srs srs_def] [-spat xmin ymin xmax ymax]\n"
+        "    [-l layername]* [-where expression] [-sql select_statement]\n"
+        "    [-txe xmin xmax] [-tye ymin ymax] [-outsize xsize ysize]\n"
+        "    [-a algorithm[:parameter1=value1]*]"
+        "    [-quiet]\n"
+        "    <src_datasource> <dst_filename>\n"
+        "\n"
+        "Available algorithms and parameters with their defaults:\n"
+        "    Inverse distance to a power (default)\n"
+        "        invdist:power=2.0:smoothing=0.0:radius1=0.0:radius2=0.0:angle=0.0:max_points=0:min_points=0:nodata=0.0\n"
+        "    Moving average\n"
+        "        average:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
+        "    Nearest neighbor\n"
+        "        nearest:radius1=0.0:radius2=0.0:angle=0.0:nodata=0.0\n"
+        "    Various data metrics\n"
+        "        <metric name>:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
+        "        possible metrics are:\n"
+        "            minimum\n"
+        "            maximum\n"
+        "            range\n"
+        "\n");
+#ifndef ZOO_SERVICE
+    exit( 1 );
+#endif
+}
+
+/************************************************************************/
+/*                          GetAlgorithmName()                          */
+/*                                                                      */
+/*      Translates algortihm code into mnemonic name.                   */
+/************************************************************************/
+
+void PrintAlgorithmAndOptions(GDALGridAlgorithm eAlgorithm, void *pOptions)
+{
+    switch ( eAlgorithm )
+    {
+        case GGA_InverseDistanceToAPower:
+
+#ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+		   "Algorithm name: \"%s\".\n", szAlgNameInvDist );
+#ifdef ZOO_SERVICE
+		fprintf(stderr,
+#else
+			  printf( 
+#endif
+				 "Options are "
+				 "\"power=%f:smoothing=%f:radius1=%f:radius2=%f:angle=%f"
+                    ":max_points=%lu:min_points=%lu:nodata=%f\"\n",
+                ((GDALGridInverseDistanceToAPowerOptions *)pOptions)->dfPower,
+                ((GDALGridInverseDistanceToAPowerOptions *)pOptions)->dfSmoothing,
+                ((GDALGridInverseDistanceToAPowerOptions *)pOptions)->dfRadius1,
+                ((GDALGridInverseDistanceToAPowerOptions *)pOptions)->dfRadius2,
+                ((GDALGridInverseDistanceToAPowerOptions *)pOptions)->dfAngle,
+                (unsigned long)((GDALGridInverseDistanceToAPowerOptions *)pOptions)->nMaxPoints,
+                (unsigned long)((GDALGridInverseDistanceToAPowerOptions *)pOptions)->nMinPoints,
+                ((GDALGridInverseDistanceToAPowerOptions *)pOptions)->dfNoDataValue);
+            break;
+        case GGA_MovingAverage:
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Algorithm name: \"%s\".\n", szAlgNameAverage );
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Options are "
+                    "\"radius1=%f:radius2=%f:angle=%f:min_points=%lu"
+                    ":nodata=%f\"\n",
+                ((GDALGridMovingAverageOptions *)pOptions)->dfRadius1,
+                ((GDALGridMovingAverageOptions *)pOptions)->dfRadius2,
+                ((GDALGridMovingAverageOptions *)pOptions)->dfAngle,
+                (unsigned long)((GDALGridMovingAverageOptions *)pOptions)->nMinPoints,
+                ((GDALGridMovingAverageOptions *)pOptions)->dfNoDataValue);
+            break;
+        case GGA_NearestNeighbor:
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Algorithm name: \"%s\".\n", szAlgNameNearest );
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Options are "
+                    "\"radius1=%f:radius2=%f:angle=%f:nodata=%f\"\n",
+                ((GDALGridNearestNeighborOptions *)pOptions)->dfRadius1,
+                ((GDALGridNearestNeighborOptions *)pOptions)->dfRadius2,
+                ((GDALGridNearestNeighborOptions *)pOptions)->dfAngle,
+                ((GDALGridNearestNeighborOptions *)pOptions)->dfNoDataValue);
+            break;
+        case GGA_MetricMinimum:
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Algorithm name: \"%s\".\n", szAlgNameMinimum );
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Options are "
+                    "\"radius1=%f:radius2=%f:angle=%f:min_points=%lu"
+                    ":nodata=%f\"\n",
+                ((GDALGridDataMetricsOptions *)pOptions)->dfRadius1,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfRadius2,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfAngle,
+                (unsigned long)((GDALGridDataMetricsOptions *)pOptions)->nMinPoints,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfNoDataValue);
+            break;
+        case GGA_MetricMaximum:
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Algorithm name: \"%s\".\n", szAlgNameMaximum );
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Options are "
+                    "\"radius1=%f:radius2=%f:angle=%f:min_points=%lu"
+                    ":nodata=%f\"\n",
+                ((GDALGridDataMetricsOptions *)pOptions)->dfRadius1,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfRadius2,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfAngle,
+                (unsigned long)((GDALGridDataMetricsOptions *)pOptions)->nMinPoints,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfNoDataValue);
+            break;
+        case GGA_MetricRange:
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Algorithm name: \"%s\".\n", szAlgNameRange );
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Options are "
+                    "\"radius1=%f:radius2=%f:angle=%f:min_points=%lu"
+                    ":nodata=%f\"\n",
+                ((GDALGridDataMetricsOptions *)pOptions)->dfRadius1,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfRadius2,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfAngle,
+                (unsigned long)((GDALGridDataMetricsOptions *)pOptions)->nMinPoints,
+                ((GDALGridDataMetricsOptions *)pOptions)->dfNoDataValue);
+            break;
+        default:
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Algorithm unknown.\n" );
+            break;
+    }
+}
+
+/************************************************************************/
+/*                      ParseAlgorithmAndOptions()                      */
+/*                                                                      */
+/*      Translates mnemonic gridding algorithm names into               */
+/*      GDALGridAlgorithm code, parse control parameters and assign     */
+/*      defaults.                                                       */
+/************************************************************************/
+
+static CPLErr ParseAlgorithmAndOptions( const char *pszAlgoritm,
+                                        GDALGridAlgorithm *peAlgorithm,
+                                        void **ppOptions )
+{
+    char **papszParms = CSLTokenizeString2( pszAlgoritm, ":", FALSE );
+
+    if ( CSLCount(papszParms) < 1 )
+        return CE_Failure;
+
+    if ( EQUAL(papszParms[0], szAlgNameInvDist) )
+        *peAlgorithm = GGA_InverseDistanceToAPower;
+    else if ( EQUAL(papszParms[0], szAlgNameAverage) )
+        *peAlgorithm = GGA_MovingAverage;
+    else if ( EQUAL(papszParms[0], szAlgNameNearest) )
+        *peAlgorithm = GGA_NearestNeighbor;
+    else if ( EQUAL(papszParms[0], szAlgNameMinimum) )
+        *peAlgorithm = GGA_MetricMinimum;
+    else if ( EQUAL(papszParms[0], szAlgNameMaximum) )
+        *peAlgorithm = GGA_MetricMaximum;
+    else if ( EQUAL(papszParms[0], szAlgNameRange) )
+        *peAlgorithm = GGA_MetricRange;
+    else
+    {
+        fprintf( stderr, "Unsupported gridding method \"%s\".\n",
+                 papszParms[0] );
+        CSLDestroy( papszParms );
+        return CE_Failure;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Parse algorithm parameters and assign defaults.                 */
+/* -------------------------------------------------------------------- */
+    const char  *pszValue;
+
+    switch ( *peAlgorithm )
+    {
+        case GGA_InverseDistanceToAPower:
+        default:
+            *ppOptions =
+                CPLMalloc( sizeof(GDALGridInverseDistanceToAPowerOptions) );
+
+            pszValue = CSLFetchNameValue( papszParms, "power" );
+            ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
+                dfPower = (pszValue) ? atof(pszValue) : 2.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "smoothing" );
+            ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
+                dfSmoothing = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "radius1" );
+            ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
+                dfRadius1 = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "radius2" );
+            ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
+                dfRadius2 = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "angle" );
+            ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
+                dfAngle = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "max_points" );
+            ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
+                nMaxPoints = (pszValue) ? atol(pszValue) : 0;
+
+            pszValue = CSLFetchNameValue( papszParms, "min_points" );
+            ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
+                nMinPoints = (pszValue) ? atol(pszValue) : 0;
+
+            pszValue = CSLFetchNameValue( papszParms, "nodata" );
+            ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
+                dfNoDataValue = (pszValue) ? atof(pszValue) : 0.0;
+            break;
+
+        case GGA_MovingAverage:
+            *ppOptions =
+                CPLMalloc( sizeof(GDALGridMovingAverageOptions) );
+
+            pszValue = CSLFetchNameValue( papszParms, "radius1" );
+            ((GDALGridMovingAverageOptions *)*ppOptions)->
+                dfRadius1 = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "radius2" );
+            ((GDALGridMovingAverageOptions *)*ppOptions)->
+                dfRadius2 = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "angle" );
+            ((GDALGridMovingAverageOptions *)*ppOptions)->
+                dfAngle = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "min_points" );
+            ((GDALGridMovingAverageOptions *)*ppOptions)->
+                nMinPoints = (pszValue) ? atol(pszValue) : 0;
+
+            pszValue = CSLFetchNameValue( papszParms, "nodata" );
+            ((GDALGridMovingAverageOptions *)*ppOptions)->
+                dfNoDataValue = (pszValue) ? atof(pszValue) : 0.0;
+            break;
+
+        case GGA_NearestNeighbor:
+            *ppOptions =
+                CPLMalloc( sizeof(GDALGridNearestNeighborOptions) );
+
+            pszValue = CSLFetchNameValue( papszParms, "radius1" );
+            ((GDALGridNearestNeighborOptions *)*ppOptions)->
+                dfRadius1 = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "radius2" );
+            ((GDALGridNearestNeighborOptions *)*ppOptions)->
+                dfRadius2 = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "angle" );
+            ((GDALGridNearestNeighborOptions *)*ppOptions)->
+                dfAngle = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "nodata" );
+            ((GDALGridNearestNeighborOptions *)*ppOptions)->
+                dfNoDataValue = (pszValue) ? atof(pszValue) : 0.0;
+            break;
+
+        case GGA_MetricMinimum:
+        case GGA_MetricMaximum:
+        case GGA_MetricRange:
+            *ppOptions =
+                CPLMalloc( sizeof(GDALGridDataMetricsOptions) );
+
+            pszValue = CSLFetchNameValue( papszParms, "radius1" );
+            ((GDALGridDataMetricsOptions *)*ppOptions)->
+                dfRadius1 = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "radius2" );
+            ((GDALGridDataMetricsOptions *)*ppOptions)->
+                dfRadius2 = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "angle" );
+            ((GDALGridDataMetricsOptions *)*ppOptions)->
+                dfAngle = (pszValue) ? atof(pszValue) : 0.0;
+
+            pszValue = CSLFetchNameValue( papszParms, "min_points" );
+            ((GDALGridDataMetricsOptions *)*ppOptions)->
+                nMinPoints = (pszValue) ? atol(pszValue) : 0;
+
+            pszValue = CSLFetchNameValue( papszParms, "nodata" );
+            ((GDALGridDataMetricsOptions *)*ppOptions)->
+                dfNoDataValue = (pszValue) ? atof(pszValue) : 0.0;
+            break;
+
+   }
+
+    CSLDestroy( papszParms );
+    return CE_None;
+}
+
+/************************************************************************/
+/*                            ProcessLayer()                            */
+/*                                                                      */
+/*      Process all the features in a layer selection, collecting       */
+/*      geometries and burn values.                                     */
+/************************************************************************/
+
+static void ProcessLayer( OGRLayerH hSrcLayer, GDALDatasetH hDstDS,
+                          GUInt32 nXSize, GUInt32 nYSize, int nBand,
+                          int& bIsXExtentSet, int& bIsYExtentSet,
+                          double& dfXMin, double& dfXMax,
+                          double& dfYMin, double& dfYMax,
+                          const char *pszBurnAttribute,
+                          GDALDataType eType,
+                          GDALGridAlgorithm eAlgorithm, void *pOptions,
+                          int bQuiet, GDALProgressFunc pfnProgress )
+
+{
+/* -------------------------------------------------------------------- */
+/*      Get field index, and check.                                     */
+/* -------------------------------------------------------------------- */
+    int iBurnField = -1;
+
+    if ( pszBurnAttribute )
+    {
+        iBurnField = OGR_FD_GetFieldIndex( OGR_L_GetLayerDefn( hSrcLayer ),
+                                           pszBurnAttribute );
+        if( iBurnField == -1 )
+        {
+            #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Failed to find field %s on layer %s, skipping.\n",
+                    pszBurnAttribute, 
+                    OGR_FD_GetName( OGR_L_GetLayerDefn( hSrcLayer ) ) );
+            return;
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Collect the geometries from this layer, and build list of       */
+/*      values to be interpolated.                                      */
+/* -------------------------------------------------------------------- */
+    OGRFeatureH hFeat;
+    std::vector<double> adfX, adfY, adfZ;
+
+    OGR_L_ResetReading( hSrcLayer );
+
+    while( (hFeat = OGR_L_GetNextFeature( hSrcLayer )) != NULL )
+    {
+        OGRGeometryH hGeom;
+
+        hGeom = OGR_F_GetGeometryRef( hFeat );
+
+        // FIXME: handle collections
+        if ( hGeom != NULL &&
+             (OGR_G_GetGeometryType( hGeom ) == wkbPoint
+              || OGR_G_GetGeometryType( hGeom ) == wkbPoint25D) )
+        {
+            adfX.push_back( OGR_G_GetX( hGeom, 0 ) );
+            adfY.push_back( OGR_G_GetY( hGeom, 0 ) );
+            if ( iBurnField < 0 )
+                adfZ.push_back( OGR_G_GetZ( hGeom, 0 ) );
+            else
+                adfZ.push_back( OGR_F_GetFieldAsDouble( hFeat, iBurnField ) );
+        }
+
+        
+        OGR_F_Destroy( hFeat );
+    }
+
+    if (adfX.size() == 0)
+    {
+        #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "No point geometry found on layer %s, skipping.\n",
+                OGR_FD_GetName( OGR_L_GetLayerDefn( hSrcLayer ) ) );
+        return;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Compute grid geometry.                                          */
+/* -------------------------------------------------------------------- */
+
+    if ( !bIsXExtentSet )
+    {
+        dfXMin = *std::min_element(adfX.begin(), adfX.end());
+        dfXMax = *std::max_element(adfX.begin(), adfX.end());
+        bIsXExtentSet = TRUE;
+    }
+
+    if ( !bIsYExtentSet )
+    {
+        dfYMin = *std::min_element(adfY.begin(), adfY.end());
+        dfYMax = *std::max_element(adfY.begin(), adfY.end());
+        bIsYExtentSet = TRUE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Perform gridding.                                               */
+/* -------------------------------------------------------------------- */
+
+    const double    dfDeltaX = ( dfXMax - dfXMin ) / nXSize;
+    const double    dfDeltaY = ( dfYMax - dfYMin ) / nYSize;
+
+    if ( !bQuiet )
+    {
+        #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Grid data type is \"%s\"\n", GDALGetDataTypeName(eType) );
+        #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Grid size = (%lu %lu).\n",
+                (unsigned long)nXSize, (unsigned long)nYSize );
+        #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Corner coordinates = (%f %f)-(%f %f).\n",
+                dfXMin - dfDeltaX / 2, dfYMax + dfDeltaY / 2,
+                dfXMax + dfDeltaX / 2, dfYMin - dfDeltaY / 2 );
+        #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Grid cell size = (%f %f).\n", dfDeltaX, dfDeltaY );
+        #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+ "Source point count = %lu.\n", (unsigned long)adfX.size() );
+        PrintAlgorithmAndOptions( eAlgorithm, pOptions );
+        #ifdef ZOO_SERVICE
+	  fprintf(stderr,
+#else
+            printf( 
+#endif
+"\n");
+    }
+
+    GDALRasterBandH hBand = GDALGetRasterBand( hDstDS, nBand );
+
+    if (adfX.size() == 0)
+    {
+        // FIXME: Shoulda' set to nodata value instead
+        GDALFillRaster( hBand, 0.0 , 0.0 );
+        return;
+    }
+
+    GUInt32 nXOffset, nYOffset;
+    int     nBlockXSize, nBlockYSize;
+
+    GDALGetBlockSize( hBand, &nBlockXSize, &nBlockYSize );
+    void    *pData =
+        CPLMalloc( nBlockXSize * nBlockYSize * GDALGetDataTypeSize(eType) );
+
+    GUInt32 nBlock = 0;
+    GUInt32 nBlockCount = ((nXSize + nBlockXSize - 1) / nBlockXSize)
+        * ((nYSize + nBlockYSize - 1) / nBlockYSize);
+
+    for ( nYOffset = 0; nYOffset < nYSize; nYOffset += nBlockYSize )
+    {
+        for ( nXOffset = 0; nXOffset < nXSize; nXOffset += nBlockXSize )
+        {
+            void *pScaledProgress;
+            pScaledProgress =
+                GDALCreateScaledProgress( 0.0,
+                                          (double)++nBlock / nBlockCount,
+                                          pfnProgress, NULL );
+
+            int nXRequest = nBlockXSize;
+            if (nXOffset + nXRequest > nXSize)
+                nXRequest = nXSize - nXOffset;
+
+            int nYRequest = nBlockYSize;
+            if (nYOffset + nYRequest > nYSize)
+                nYRequest = nYSize - nYOffset;
+
+            GDALGridCreate( eAlgorithm, pOptions,
+                            adfX.size(), &(adfX[0]), &(adfY[0]), &(adfZ[0]),
+                            dfXMin + dfDeltaX * nXOffset,
+                            dfXMin + dfDeltaX * (nXOffset + nXRequest),
+                            dfYMin + dfDeltaY * nYOffset,
+                            dfYMin + dfDeltaY * (nYOffset + nYRequest),
+                            nXRequest, nYRequest, eType, pData,
+                            GDALScaledProgress, pScaledProgress );
+
+            GDALRasterIO( hBand, GF_Write, nXOffset, nYOffset,
+                          nXRequest, nYRequest, pData,
+                          nXRequest, nYRequest, eType, 0, 0 );
+
+            GDALDestroyScaledProgress( pScaledProgress );
+        }
+    }
+
+    CPLFree( pData );
+}
+
+/************************************************************************/
+/*                                main()                                */
+/************************************************************************/
+#ifdef ZOO_SERVICE
+int Gdal_Grid(maps*& conf,maps*& inputs,maps*& outputs)
+#else
+int main( int argc, char ** argv )
+#endif
+{
+    GDALDriverH     hDriver;
+    const char      *pszSource=NULL, *pszDest=NULL, *pszFormat = "GTiff";
+    char            **papszLayers = NULL;
+    const char      *pszBurnAttribute = NULL;
+    const char      *pszWHERE = NULL, *pszSQL = NULL;
+    GDALDataType    eOutputType = GDT_Float64;
+    char            **papszCreateOptions = NULL;
+    GUInt32         nXSize = 0, nYSize = 0;
+    double          dfXMin = 0.0, dfXMax = 0.0, dfYMin = 0.0, dfYMax = 0.0;
+    int             bIsXExtentSet = FALSE, bIsYExtentSet = FALSE;
+    GDALGridAlgorithm eAlgorithm = GGA_InverseDistanceToAPower;
+    void            *pOptions = NULL;
+    char            *pszOutputSRS = NULL;
+    int             bQuiet = FALSE;
+    GDALProgressFunc pfnProgress = GDALTermProgress;
+    int             i;
+    OGRGeometryH    hSpatialFilter = NULL;
+
+    /* Check that we are running against at least GDAL 1.5 */
+    /* Note to developers : if we use newer API, please change the requirement */
+    if (atoi(GDALVersionInfo("VERSION_NUM")) < 1500)
+    {
+#ifdef ZOO_SERVICE
+        fprintf(stderr, "At least, GDAL >= 1.5.0 is required for this version of this ZOO ServiceProvider, "
+                "which was compiled against GDAL %s\n", GDAL_RELEASE_NAME);
+#else
+        fprintf(stderr, "At least, GDAL >= 1.5.0 is required for this version of %s, "
+                "which was compiled against GDAL %s\n", argv[0], GDAL_RELEASE_NAME);
+#endif
+	return SERVICE_FAILED;
+    }
+
+    GDALAllRegister();
+    OGRRegisterAll();
+
+#ifdef ZOO_SERVICE
+    bQuiet = TRUE;
+    pfnProgress = GDALDummyProgress;
+    map* tmpMap=NULL;
+
+    char dataPath[1024];
+    tmpMap=getMapFromMaps(conf,"main","dataPath");
+    if(tmpMap!=NULL)
+      sprintf(dataPath,"%s",tmpMap->value);
+    tmpMap=NULL;
+
+    char tempPath[1024];
+    tmpMap=getMapFromMaps(conf,"main","tmpPath");
+    if(tmpMap!=NULL){
+      sprintf(tempPath,"%s",tmpMap->value);
+    }
+    tmpMap=NULL;
+
+    tmpMap=getMapFromMaps(inputs,"OF","value");
+    if(tmpMap!=NULL){
+      pszFormat=tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"OT","value");
+    if(tmpMap!=NULL){
+      int iType;      
+      for( iType = 1; iType < GDT_TypeCount; iType++ )
+	{
+	  if( GDALGetDataTypeName((GDALDataType)iType) != NULL
+	      && EQUAL(GDALGetDataTypeName((GDALDataType)iType),
+                             tmpMap->value) )
+	    {
+	      eOutputType = (GDALDataType) iType;
+	    }
+	}
+      if( eOutputType == GDT_Unknown )
+	{
+	  fprintf( stderr, "Unknown output pixel type: %s\n", tmpMap->value );
+	  Usage();
+	  return SERVICE_FAILED;
+	}
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"TXE","value");
+    if(tmpMap!=NULL){
+      char *tmp=tmpMap->value;
+      char *t=strtok(tmp,",");
+      int cnt=0;
+      while(t!=NULL){
+        switch(cnt){
+        case 0:
+          dfXMin = atof(t);
+          break;
+        case 1:
+          dfXMax = atof(t);
+          break;
+        }
+	t=strtok(NULL,",");
+	cnt++;
+      }
+      bIsXExtentSet = TRUE;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"TYE","value");
+    if(tmpMap!=NULL){
+      char *tmp=tmpMap->value;
+      char *t=strtok(tmp,",");
+      int cnt=0;
+      while(t!=NULL){
+        switch(cnt){
+        case 0:
+          dfYMin = atof(t);
+          break;
+        case 1:
+          dfYMax = atof(t);
+          break;
+        }
+	t=strtok(NULL,",");
+	cnt++;
+	
+      }
+      bIsYExtentSet = TRUE;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"OUTSIZE","value");
+    if(tmpMap!=NULL){
+      char *tmp=tmpMap->value;
+      char *t=strtok(tmp,",");
+      int cnt=0;
+      while(t!=NULL){
+        switch(cnt){
+        case 0:
+          nXSize = atoi(t);
+          break;
+        case 1:
+          nYSize = atoi(t);
+          break;
+        }
+	cnt++;
+      }
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"CO","value");
+    if(tmpMap!=NULL){
+      papszCreateOptions = CSLAddString( papszCreateOptions, tmpMap->value );
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"ZFIELD","value");
+    if(tmpMap!=NULL){
+      pszBurnAttribute = tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"WHERE","value");
+    if(tmpMap!=NULL){
+      pszWHERE = tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"L","value");
+    if(tmpMap!=NULL){
+      papszLayers = CSLAddString( papszLayers, tmpMap->value );
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"SQL","value");
+    if(tmpMap!=NULL){
+      pszSQL = tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"A","value");
+    if(tmpMap!=NULL){
+      if ( ParseAlgorithmAndOptions(tmpMap->value, &eAlgorithm, &pOptions )
+	   != CE_None )
+	{
+	  fprintf( stderr,
+		   "Failed to process algoritm name and parameters.\n" );
+	  return SERVICE_FAILED;
+	}
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"SPAT","value");
+    if(tmpMap!=NULL){
+      char *tmp=tmpMap->value;
+      char *t=strtok(tmp,",");
+      int cnt=0;
+      double dfULX, dfULY, dfLRX, dfLRY;
+      while(t!=NULL){
+        switch(cnt){
+        case 0:
+          dfULX = atof(t);
+          break;
+        case 1:
+          dfULY = atof(t);
+          break;
+        case 2:
+          dfLRX = atof(t);
+          break;
+        case 3:
+          dfLRY = atof(t);
+          break;
+        }
+        fprintf(stderr,"%s\n\n",t);
+        fprintf(stderr,"%f - %f - %f - %f\n\n",dfULX,dfULY,dfLRX,dfLRY);
+        t=strtok(NULL,",");
+        cnt++;
+      }
+      OGRGeometryH hRing = OGR_G_CreateGeometry( wkbLinearRing );
+      
+      OGR_G_AddPoint_2D( hRing, dfULX, dfULY );
+      OGR_G_AddPoint_2D( hRing, dfULX, dfLRY );
+      OGR_G_AddPoint_2D( hRing, dfLRY, dfLRY );
+      OGR_G_AddPoint_2D( hRing, dfLRY, dfULY );
+      OGR_G_AddPoint_2D( hRing, dfULX, dfULY );
+      
+      hSpatialFilter = OGR_G_CreateGeometry( wkbPolygon );
+      OGR_G_AddGeometry( hSpatialFilter, hRing );
+	
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"A_SRS","value");
+    if(tmpMap!=NULL){
+      OGRSpatialReference oOutputSRS;
+      
+      if( oOutputSRS.SetFromUserInput( tmpMap->value ) != OGRERR_NONE )
+	{
+	  fprintf( stderr, "Failed to process SRS definition: %s\n", 
+		   tmpMap->value );
+	  GDALDestroyDriverManager();
+	  return SERVICE_FAILED;
+	}
+      
+      oOutputSRS.exportToWkt( &pszOutputSRS );
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"InputDSN","value");
+    if(tmpMap!=NULL){
+      pszSource=(char*)malloc(sizeof(char)*(strlen(dataPath)+strlen(tmpMap->value)+1));
+      sprintf((char*)pszSource,"%s/%s",dataPath,tmpMap->value);
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"OutputDSN","value");
+    if(tmpMap!=NULL){
+      pszDest=(char*)malloc(sizeof(char)*(strlen(tempPath)+strlen(tmpMap->value)+4));
+      char *ext=new char[4];
+      ext="tif";
+      if(strcmp(mtoupper((char*)pszFormat),"AAIGRID")==0)
+        ext="csv";
+      else 
+        if(strcmp(mtoupper((char*)pszFormat),"PNG")==0)
+          ext="png";
+        else
+          if(strcmp(mtoupper((char*)pszFormat),"GIF")==0)
+            ext="gif";
+          else
+            if(strcmp(mtoupper((char*)pszFormat),"JPEG")==0)
+              ext="jpg";
+      sprintf((char*)pszDest,"%s/%s.%s",tempPath,tmpMap->value,ext);
+    }
+
+#else
+    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
+    if( argc < 1 )
+        exit( -argc );
+
+/* -------------------------------------------------------------------- */
+/*      Parse arguments.                                                */
+/* -------------------------------------------------------------------- */
+    for( i = 1; i < argc; i++ )
+    {
+        if( EQUAL(argv[i], "--utility_version") )
+        {
+            printf("%s was compiled against GDAL %s and is running against GDAL %s\n",
+                   argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
+            return 0;
+        }
+        else if( EQUAL(argv[i],"-of") && i < argc-1 )
+        {
+            pszFormat = argv[++i];
+        }
+
+        else if( EQUAL(argv[i],"-quiet") )
+        {
+            bQuiet = TRUE;
+            pfnProgress = GDALDummyProgress;
+        }
+
+        else if( EQUAL(argv[i],"-ot") && i < argc-1 )
+        {
+            int	iType;
+            
+            for( iType = 1; iType < GDT_TypeCount; iType++ )
+            {
+                if( GDALGetDataTypeName((GDALDataType)iType) != NULL
+                    && EQUAL(GDALGetDataTypeName((GDALDataType)iType),
+                             argv[i+1]) )
+                {
+                    eOutputType = (GDALDataType) iType;
+                }
+            }
+
+            if( eOutputType == GDT_Unknown )
+            {
+                fprintf( stderr, "Unknown output pixel type: %s\n", argv[i+1] );
+                Usage();
+                exit( 2 );
+            }
+            i++;
+        }
+
+        else if( EQUAL(argv[i],"-txe") && i < argc-2 )
+        {
+            dfXMin = atof(argv[++i]);
+            dfXMax = atof(argv[++i]);
+            bIsXExtentSet = TRUE;
+        }   
+
+        else if( EQUAL(argv[i],"-tye") && i < argc-2 )
+        {
+            dfYMin = atof(argv[++i]);
+            dfYMax = atof(argv[++i]);
+            bIsYExtentSet = TRUE;
+        }   
+
+        else if( EQUAL(argv[i],"-outsize") && i < argc-2 )
+        {
+            nXSize = atoi(argv[++i]);
+            nYSize = atoi(argv[++i]);
+        }   
+
+        else if( EQUAL(argv[i],"-co") && i < argc-1 )
+        {
+            papszCreateOptions = CSLAddString( papszCreateOptions, argv[++i] );
+        }   
+
+        else if( EQUAL(argv[i],"-zfield") && i < argc-1 )
+        {
+            pszBurnAttribute = argv[++i];
+        }
+
+        else if( EQUAL(argv[i],"-where") && i < argc-1 )
+        {
+            pszWHERE = argv[++i];
+        }
+
+        else if( EQUAL(argv[i],"-l") && i < argc-1 )
+        {
+            papszLayers = CSLAddString( papszLayers, argv[++i] );
+        }
+
+        else if( EQUAL(argv[i],"-sql") && i < argc-1 )
+        {
+            pszSQL = argv[++i];
+        }
+
+        else if( EQUAL(argv[i],"-spat") 
+                 && argv[i+1] != NULL 
+                 && argv[i+2] != NULL 
+                 && argv[i+3] != NULL 
+                 && argv[i+4] != NULL )
+        {
+            OGRGeometryH hRing = OGR_G_CreateGeometry( wkbLinearRing );
+
+            OGR_G_AddPoint_2D( hRing, atof(argv[i+1]), atof(argv[i+2]) );
+            OGR_G_AddPoint_2D( hRing, atof(argv[i+1]), atof(argv[i+4]) );
+            OGR_G_AddPoint_2D( hRing, atof(argv[i+3]), atof(argv[i+4]) );
+            OGR_G_AddPoint_2D( hRing, atof(argv[i+3]), atof(argv[i+2]) );
+            OGR_G_AddPoint_2D( hRing, atof(argv[i+1]), atof(argv[i+2]) );
+
+            hSpatialFilter = OGR_G_CreateGeometry( wkbPolygon );
+            OGR_G_AddGeometry( hSpatialFilter, hRing );
+            i += 4;
+        }
+
+        else if( EQUAL(argv[i],"-a_srs") && i < argc-1 )
+        {
+            OGRSpatialReference oOutputSRS;
+
+            if( oOutputSRS.SetFromUserInput( argv[i+1] ) != OGRERR_NONE )
+            {
+                fprintf( stderr, "Failed to process SRS definition: %s\n", 
+                         argv[i+1] );
+                GDALDestroyDriverManager();
+                exit( 1 );
+            }
+
+            oOutputSRS.exportToWkt( &pszOutputSRS );
+            i++;
+        }   
+
+        else if( EQUAL(argv[i],"-a") && i < argc-1 )
+        {
+            if ( ParseAlgorithmAndOptions( argv[++i], &eAlgorithm, &pOptions )
+                 != CE_None )
+            {
+                fprintf( stderr,
+                         "Failed to process algoritm name and parameters.\n" );
+                exit( 1 );
+            }
+        }
+
+        else if( argv[i][0] == '-' )
+        {
+            fprintf( stderr, "Option %s incomplete, or not recognised.\n\n", 
+                    argv[i] );
+            Usage();
+            GDALDestroyDriverManager();
+            exit( 2 );
+        }
+
+        else if( pszSource == NULL )
+        {
+            pszSource = argv[i];
+        }
+
+        else if( pszDest == NULL )
+        {
+            pszDest = argv[i];
+        }
+
+        else
+        {
+            fprintf( stderr, "Too many command options.\n\n" );
+            Usage();
+            GDALDestroyDriverManager();
+            exit( 2 );
+        }
+    }
+#endif
+
+    if( pszSource == NULL || pszDest == NULL
+        || (pszSQL == NULL && papszLayers == NULL) )
+    {
+        Usage();
+        GDALDestroyDriverManager();
+        exit( 2 );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Find the output driver.                                         */
+/* -------------------------------------------------------------------- */
+    hDriver = GDALGetDriverByName( pszFormat );
+    if( hDriver == NULL )
+    {
+        int	iDr;
+        
+        fprintf( stderr, "Output driver `%s' not recognised.\n", pszFormat );
+        fprintf( stderr,
+        "The following format drivers are configured and support output:\n" );
+        for( iDr = 0; iDr < GDALGetDriverCount(); iDr++ )
+        {
+            GDALDriverH hDriver = GDALGetDriver(iDr);
+
+            if( GDALGetMetadataItem( hDriver, GDAL_DCAP_CREATE, NULL ) != NULL
+                || GDALGetMetadataItem( hDriver, GDAL_DCAP_CREATECOPY,
+                                        NULL ) != NULL )
+            {
+                fprintf( stderr, "  %s: %s\n",
+                         GDALGetDriverShortName( hDriver  ),
+                         GDALGetDriverLongName( hDriver ) );
+            }
+        }
+        printf( "\n" );
+        Usage();
+        
+        GDALDestroyDriverManager();
+#ifndef ZOO_SERVICE
+        CSLDestroy( argv );
+#endif
+        CSLDestroy( papszCreateOptions );
+	return SERVICE_FAILED;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Open input datasource.                                          */
+/* -------------------------------------------------------------------- */
+    OGRDataSourceH hSrcDS;
+
+    hSrcDS = OGROpen( pszSource, FALSE, NULL );
+    if( hSrcDS == NULL )
+    {
+        fprintf( stderr, "Unable to open input datasource \"%s\".\n",
+                 pszSource );
+        fprintf( stderr, "%s\n", CPLGetLastErrorMsg() );
+	return SERVICE_FAILED;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Create target raster file.                                      */
+/* -------------------------------------------------------------------- */
+    GDALDatasetH    hDstDS;
+    int             nLayerCount = CSLCount(papszLayers);
+    int             nBands = nLayerCount;
+
+    if ( pszSQL )
+        nBands++;
+
+    // FIXME
+    if ( nXSize == 0 )
+        nXSize = 256;
+    if ( nYSize == 0 )
+        nYSize = 256;
+
+    hDstDS = GDALCreate( hDriver, pszDest, nXSize, nYSize, nBands,
+                         eOutputType, papszCreateOptions );
+    if ( hDstDS == NULL )
+    {
+        fprintf( stderr, "Unable to create target dataset \"%s\".\n",
+                 pszDest );
+        fprintf( stderr, "%s\n", CPLGetLastErrorMsg() );
+	return SERVICE_FAILED;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If algorithm was not specified assigh default one.              */
+/* -------------------------------------------------------------------- */
+    if ( !pOptions )
+        ParseAlgorithmAndOptions( szAlgNameInvDist, &eAlgorithm, &pOptions );
+
+/* -------------------------------------------------------------------- */
+/*      Process SQL request.                                            */
+/* -------------------------------------------------------------------- */
+    if( pszSQL != NULL )
+    {
+        OGRLayerH hLayer;
+
+        hLayer = OGR_DS_ExecuteSQL( hSrcDS, pszSQL, hSpatialFilter, NULL ); 
+        if( hLayer != NULL )
+        {
+            // Custom layer will be rasterized in the first band.
+            ProcessLayer( hLayer, hDstDS, nXSize, nYSize, 1,
+                          bIsXExtentSet, bIsYExtentSet,
+                          dfXMin, dfXMax, dfYMin, dfYMax, pszBurnAttribute,
+                          eOutputType, eAlgorithm, pOptions,
+                          bQuiet, pfnProgress );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Process each layer.                                             */
+/* -------------------------------------------------------------------- */
+    for( i = 0; i < nLayerCount; i++ )
+    {
+        OGRLayerH hLayer = OGR_DS_GetLayerByName( hSrcDS, papszLayers[i] );
+        if( hLayer == NULL )
+        {
+            fprintf( stderr, "Unable to find layer \"%s\", skipping.\n", 
+                     papszLayers[i] );
+            continue;
+        }
+
+        if( pszWHERE )
+        {
+            if( OGR_L_SetAttributeFilter( hLayer, pszWHERE ) != OGRERR_NONE )
+                break;
+        }
+
+        if( hSpatialFilter != NULL )
+          OGR_L_SetSpatialFilter( hLayer, hSpatialFilter );
+
+        // Fetch the first meaningful SRS definition
+        if ( !pszOutputSRS )
+        {
+            OGRSpatialReferenceH hSRS = OGR_L_GetSpatialRef( hLayer );
+            if ( hSRS )
+                OSRExportToWkt( hSRS, &pszOutputSRS );
+        }
+
+        ProcessLayer( hLayer, hDstDS, nXSize, nYSize,
+                      i + 1 + nBands - nLayerCount,
+                      bIsXExtentSet, bIsYExtentSet,
+                      dfXMin, dfXMax, dfYMin, dfYMax, pszBurnAttribute,
+                      eOutputType, eAlgorithm, pOptions,
+                      bQuiet, pfnProgress );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Apply geotransformation matrix.                                 */
+/* -------------------------------------------------------------------- */
+    double  adfGeoTransform[6];
+    adfGeoTransform[0] = dfXMin;
+    adfGeoTransform[1] = (dfXMax - dfXMin) / nXSize;
+    adfGeoTransform[2] = 0.0;
+    adfGeoTransform[3] = dfYMin;
+    adfGeoTransform[4] = 0.0;
+    adfGeoTransform[5] = (dfYMax - dfYMin) / nYSize;
+    GDALSetGeoTransform( hDstDS, adfGeoTransform );
+
+/* -------------------------------------------------------------------- */
+/*      Apply SRS definition if set.                                    */
+/* -------------------------------------------------------------------- */
+    if ( pszOutputSRS )
+    {
+        GDALSetProjection( hDstDS, pszOutputSRS );
+        CPLFree( pszOutputSRS );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Cleanup                                                         */
+/* -------------------------------------------------------------------- */
+    CSLDestroy( papszCreateOptions );
+    CPLFree( pOptions );
+    OGR_DS_Destroy( hSrcDS );
+    GDALClose( hDstDS );
+#ifndef ZOO_SERVICE
+    CSLDestroy( argv );
+#endif
+    CSLDestroy( papszLayers );
+    OGRCleanupAll();
+
+    GDALDestroyDriverManager();
+ 
+    outputs=(maps*)malloc(sizeof(maps*));
+    outputs->name="OutputedPolygon";
+    outputs->content=createMap("value",(char*)pszDest);
+    addMapToMap(&outputs->content,createMap("dataType","string"));
+    outputs->next=NULL;
+    return SERVICE_SUCCEEDED;
+}
+
+
+#ifdef ZOO_SERVICE
+}
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/Makefile	(revision 221)
@@ -0,0 +1,8 @@
+CFLAGS=-I../../../zoo-kernel/ -I./ `xml2-config --cflags` `python-config --cflags` -DLINUX_FREE_ISSUE #-DDEBUG
+CC=gcc
+
+cgi-env/gdal_profile_service.zo: service.c
+	g++  -DZOO_SERVICE ${CFLAGS} -shared -fpic -o cgi-env/gdal_profile_service.zo ./service.c -lgdal
+
+clean:
+	rm -f cgi-env/*.zo
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/cgi-env/GdalExtractProfile.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/cgi-env/GdalExtractProfile.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/cgi-env/GdalExtractProfile.zcfg	(revision 221)
@@ -0,0 +1,57 @@
+[GdalExtractProfile]
+ Title = Convert raster data from one format to another. 
+ Abstract = Converts raster data between different formats.
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceType = C
+ serviceProvider = gdal_profile_service.zo
+ <MetaData>
+   title = My Demo
+ </MetaData>
+ <DataInputs>
+  [RasterFile]
+   Title = The name fo the MNT file
+   Abstract = The file containing elevation model relative to the dataPath defined in the ZOO-Project main configuration file.
+   minOccurs = 0
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+     value = topofr.tif
+    </Default>
+   </LiteralData>
+  [Geometry]
+   Title = The path to calaculate profile
+   Abstract = The input data source name to use as source for convertion.
+   minOccurs = 1
+   maxOccurs = 1
+   <ComplexData>
+    <Default>
+     mimeType = application/json
+     encoding = UTF-8
+     extension = js
+     asReference = true	
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Profile]
+   Title = The resulting profile
+   Abstract = GeoJSON string containing the X Y Z values where (X,Y) is corresponding to the original coordinates and Z the elevation value 
+   <ComplexData>
+    <Default>
+     mimeType = application/json
+     encoding = UTF-8
+     extension = js
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/service.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/service.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/profile/service.c	(revision 221)
@@ -0,0 +1,191 @@
+/* ****************************************************************************
+ * $Id$
+ *
+ * Project:  GdalExtractProfile
+ * Purpose:  Extract Profile from a Raster file for an Input Geometry (LINE)
+ * Author:   Gérald Fenoy, gerald.fenoy@geolabs.fr
+ *
+ * ****************************************************************************
+ * Copyright (c) 2010-2011, GeoLabs SARL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#ifdef ZOO_SERVICE
+#include "service.h"
+#include "service_internal.h"
+#endif
+#include "gdal.h"
+#include "cpl_conv.h"
+#include "ogr_api.h"
+
+#ifdef ZOO_SERVICE
+extern "C" {
+#endif
+
+#ifdef ZOO_SERVICE
+int GdalExtractProfile(maps*& conf,maps*& inputs,maps*& outputs)
+#else
+int main(int argc,char** argv)
+#endif
+{
+  char *pszFilename;
+#ifdef ZOO_SERVICE
+  map* tmp=NULL;
+  map* tmp1=NULL;
+  tmp=getMapFromMaps(conf,"main","dataPath");
+  tmp1=getMapFromMaps(inputs,"RasterFile","value");
+  pszFilename=(char *)malloc((2+strlen(tmp->value)+strlen(tmp1->value))*sizeof(char));
+  sprintf(pszFilename,"%s/%s",tmp->value,tmp1->value);
+#else
+  pszFilename=argv[1];
+#endif
+  GDALDatasetH  hDataset;  
+  GDALAllRegister();
+  OGRRegisterAll();
+ 
+  hDataset = GDALOpen( pszFilename, GA_ReadOnly );
+  free(pszFilename);
+  if( hDataset != NULL )
+    {
+      GDALDriverH   hDriver;
+      double        adfGeoTransform[6];
+
+      if( GDALGetGeoTransform( hDataset, adfGeoTransform ) == CE_None )
+	{
+
+
+        GDALRasterBandH hBand;
+        int             nBlockXSize, nBlockYSize;
+        int             bGotMin, bGotMax;
+        double          adfMinMax[2];
+        
+        hBand = GDALGetRasterBand( hDataset, 1 );
+
+        adfMinMax[0] = GDALGetRasterMinimum( hBand, &bGotMin );
+        adfMinMax[1] = GDALGetRasterMaximum( hBand, &bGotMax );
+        if( ! (bGotMin && bGotMax) )
+            GDALComputeRasterMinMax( hBand, TRUE, adfMinMax );
+
+#ifdef ZOO_SERVICE
+	  tmp1=getMapFromMaps(inputs,"Geometry","value");
+	  OGRGeometryH geometry=OGR_G_CreateGeometryFromJson(tmp1->value);
+#else
+	  OGRGeometryH geometry=OGR_G_CreateGeometryFromJson(argv[2]);
+#endif
+	  OGR_G_Segmentize(geometry, adfGeoTransform[1]);
+	  int nbGeom=OGR_G_GetPointCount(geometry);
+	  int k=0;
+	  double ppx=0,ppy=0;
+	  double value;
+	  char *buffer=NULL;
+	  int length=0;
+	  buffer=(char*)malloc(37*sizeof(char));
+	  sprintf(buffer,"{\"type\":\"LineString\",\"coordinates\":[");
+	  length+=strlen(buffer);
+	  for(k=0;k<nbGeom;k++){
+	    //OGRGeometryH point;
+	    double prx,pry,prz;
+	    OGR_G_GetPoint(geometry,k,&prx,&pry,&prz);
+	    float *pafScanline;
+	    pafScanline = (float *) CPLMalloc(sizeof(float));
+	    int px=(int)floor((prx-adfGeoTransform[0])/adfGeoTransform[1]);
+	    int py=(int)floor((pry-adfGeoTransform[3])/adfGeoTransform[5]);
+	    if(px!=ppx || py!=ppy){
+	      if(GDALRasterIO( hBand, GF_Read, px, py, 1, 1, 
+			    pafScanline, 1, 1, GDT_Float32, 
+			       0, 0 ) != CE_None){
+		char *tmp;
+		tmp=(char*) malloc(300*sizeof(char));
+		sprintf(tmp,"GDALRasterIO failed for point (%d,%d)",px,py);
+		setMapInMaps(conf,"lenv","message",_ss(tmp));
+		CPLFree(pafScanline);
+		free(tmp);
+		return SERVICE_FAILED;
+	      }
+	      if(buffer!=NULL){
+		int len=strlen(buffer);
+		buffer=(char*)realloc(buffer,(len+50+1)*sizeof(char));
+	      }
+	      else
+		buffer=(char*)malloc((51)*sizeof(char));
+	      char *tmpValue=(char *)malloc(50*sizeof(char));
+	      sprintf(tmpValue,"[%.6f,%.6f,%.6f]%c",prx,pry,pafScanline[0],(k+1==nbGeom?' ':','));
+	      strncpy(buffer+length,tmpValue,strlen(tmpValue));
+	      length+=strlen(tmpValue);
+	      buffer[length]=0;
+	      value=pafScanline[0];
+	      free(tmpValue);
+	      //Usefull if we can export 3D JSON string at the end
+	      //OGR_G_SetPoint(geometry,k,prx,pry,pafScanline[0]);	      
+	    }
+	    else{
+	      if(buffer!=NULL)
+		buffer=(char*)realloc(buffer,(strlen(buffer)+50+1)*sizeof(char));
+	      else
+		buffer=(char*)malloc((51)*sizeof(char));
+	      char *tmpValue=(char *)malloc(50*sizeof(char));
+	      sprintf(tmpValue,"[%.6f,%.6f,%.6f]%c",prx,pry,value,(k+1==nbGeom?' ':','));
+	      strncpy(buffer+length,tmpValue,strlen(tmpValue));
+	      length+=strlen(tmpValue);
+	      buffer[length]=0;
+	      free(tmpValue);
+	      value=value;
+	    }
+	    CPLFree(pafScanline);
+	    ppx=px;
+	    ppy=py;
+	  }
+	  buffer=(char*)realloc(buffer,(strlen(buffer)+3)*sizeof(char));
+	  char *tmpValue=(char *)malloc(3*sizeof(char));
+	  sprintf(tmpValue,"]}");
+	  tmpValue[2]=0;
+	  strncpy(buffer+length,tmpValue,strlen(tmpValue));
+	  length+=strlen(tmpValue);
+	  buffer[length]=0;
+#ifdef ZOO_SERVICE
+	  setMapInMaps(outputs,"Profile","value",buffer);
+	  setMapInMaps(outputs,"Profile","mimeType","text/plain");
+#else
+	  fprintf(stderr,"%s\n",buffer);
+#endif
+	  free(buffer);
+	  free(tmpValue);
+	  OGR_G_DestroyGeometry(geometry);
+	}
+    }
+  else{
+#ifdef ZOO_SERVICE
+    setMapInMaps(conf,"lenv","message",_ss("Unable to load your raster file !"));
+    return SERVICE_FAILED;
+#else
+    printf("Unable to load your raster file %s !\n",argv[1]);
+#endif
+  }
+  OGRCleanupAll();
+  GDALClose(hDataset);
+  GDALDestroyDriverManager();
+#ifdef ZOO_SERVICE
+  return SERVICE_SUCCEEDED;
+#endif
+}
+
+#ifdef ZOO_SERVICE
+}
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/Makefile	(revision 221)
@@ -0,0 +1,9 @@
+CFLAGS=-I../../../zoo-kernel-svn/ -I./ `xml2-config --cflags` `python-config --cflags` -DLINUX_FREE_ISSUE #-DDEBUG
+CC=gcc
+
+cgi-env/service.zo: service.c
+	g++ ${CFLAGS} -shared -fpic -o cgi-env/service.zo ./service.c -lgdal
+
+clean:
+	rm -f *.zo *.o *.tab.c *.tab.h *.sr.c* service_loader lex.* *.lreg *.sibling
+	rm -rf service_loader.dSYM
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/cgi-env/Gdal_Translate.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/cgi-env/Gdal_Translate.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/cgi-env/Gdal_Translate.zcfg	(revision 221)
@@ -0,0 +1,54 @@
+[Gdal_Translate]
+ Title = Convert raster data from one format to another. 
+ Abstract = Converts raster data between different formats.
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceType = C
+ serviceProvider = gdal_service.zo
+ <MetaData>
+   Test = My Demo
+ </MetaData>
+ <DataInputs>
+  [Format]
+   Title = Format of the output data
+   Abstract = Select the output format.
+   minOccurs = 0
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+     value = AAIGrid
+    </Default>
+   </LiteralData>
+  [InputDSN]
+   Title = The input data source name
+   Abstract = The input data source name to use as source for convertion.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+   </LiteralData>
+  [OutputDataSourceName]
+   Title = The output data source name
+   Abstract = The output data source name to use as source for convertion.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [OutputedDataSourceName]
+   Title = The resulting converted file
+   Abstract = The file name resulting of the convertion
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+   </LiteralData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/service.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/service.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/gdal/translate/service.c	(revision 221)
@@ -0,0 +1,825 @@
+/******************************************************************************
+ * $Id$
+ *
+ * Project:  GDAL Utilities
+ * Purpose:  GDAL Image Translator Program
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1998, 2002, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "cpl_vsi.h"
+#include "cpl_conv.h"
+#include "cpl_string.h"
+#include "gdal_priv.h"
+#include "ogr_spatialref.h"
+#include "vrtdataset.h"
+
+#include "service.h"
+
+
+CPL_CVSID("$Id$");
+
+extern "C" {
+
+
+  static void AttachMetadata( GDALDatasetH, char ** );
+  static int bSubCall = FALSE;
+
+  /************************************************************************/
+  /*                          Gdal_Translate()                            */
+  /************************************************************************/
+
+  int Gdal_Translate(maps*& conf,maps*& inputs,maps*& outputs)
+  {
+    
+    fprintf(stderr,"STARTING GDAL TRANSLATE\n");
+    fflush(stderr);
+
+    GDALDatasetH	hDataset, hOutDS;
+    int			i;
+    int			nRasterXSize, nRasterYSize;
+    const char		*pszSource=NULL, *pszDest=NULL, *pszFormat = "GTiff";
+    GDALDriverH		hDriver;
+    int			*panBandList = NULL, nBandCount = 0, bDefBands = TRUE;
+    double		adfGeoTransform[6];
+    GDALDataType	eOutputType = GDT_Unknown;
+    int			nOXSize = 0, nOYSize = 0;
+    char		*pszOXSize=NULL, *pszOYSize=NULL;
+    char                **papszCreateOptions = NULL;
+    int                 anSrcWin[4], bStrict = FALSE;
+    const char          *pszProjection;
+    int                 bScale = FALSE, bHaveScaleSrc = FALSE;
+    double	        dfScaleSrcMin=0.0, dfScaleSrcMax=255.0;
+    double              dfScaleDstMin=0.0, dfScaleDstMax=255.0;
+    double              dfULX, dfULY, dfLRX, dfLRY;
+    char                **papszMetadataOptions = NULL;
+    char                *pszOutputSRS = NULL;
+    int                 bQuiet = TRUE, bGotBounds = FALSE;
+    GDALProgressFunc    pfnProgress = GDALDummyProgress;
+    int                 nGCPCount = 0;
+    GDAL_GCP            *pasGCPs = NULL;
+    int                 iSrcFileArg = -1, iDstFileArg = -1;
+    int                 bCopySubDatasets = FALSE;
+    double              adfULLR[4] = { 0,0,0,0 };
+    int                 bSetNoData = FALSE;
+    double		dfNoDataReal = 0.0;
+    int                 nRGBExpand = 0;
+
+    anSrcWin[0] = 0;
+    anSrcWin[1] = 0;
+    anSrcWin[2] = 0;
+    anSrcWin[3] = 0;
+
+    dfULX = dfULY = dfLRX = dfLRY = 0.0;
+
+    /* ----------------------------------------------------------------- */
+    /*      Register standard GDAL drivers, and process generic GDAL     */
+    /* ----------------------------------------------------------------- */
+    GDALAllRegister();
+
+    /* ----------------------------------------------------------------- */
+    /* Extract Format, InputDSN, OutputDSN parameters                    */
+    /* ----------------------------------------------------------------- */
+
+    map* tmpMap=NULL;
+
+    char dataPath[1024];
+    tmpMap=getMapFromMaps(conf,"main","dataPath");
+    if(tmpMap!=NULL)
+      sprintf(dataPath,"%s",tmpMap->value);
+    tmpMap=NULL;
+
+    char tempPath[1024];
+    tmpMap=getMapFromMaps(conf,"main","tmpPath");
+    if(tmpMap!=NULL){
+      sprintf(tempPath,"%s",tmpMap->value);
+    }
+    tmpMap=NULL;
+
+    tmpMap=getMapFromMaps(inputs,"Format","value");
+    if(tmpMap!=NULL){
+      pszFormat=tmpMap->value;
+    }
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"InputDSN","value");
+    if(tmpMap!=NULL){
+      pszSource=(char*)malloc(sizeof(char)*(strlen(dataPath)+strlen(tmpMap->value)+4));
+      sprintf((char*)pszSource,"%s/%s.tif",dataPath,tmpMap->value);
+    }
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"OutputDSN","value");
+    if(tmpMap!=NULL){
+      pszDest=(char*)malloc(sizeof(char)*(strlen(tempPath)+strlen(tmpMap->value)+4));
+      char *ext=new char[4];
+      ext="tif";
+      if(strcmp(mtoupper((char*)pszFormat),"AAIGRID")==0)
+	ext="csv";
+      else 
+	if(strcmp(mtoupper((char*)pszFormat),"PNG")==0)
+	  ext="png";
+	else
+	  if(strcmp(mtoupper((char*)pszFormat),"GIF")==0)
+	    ext="gif";
+	  else
+	    if(strcmp(mtoupper((char*)pszFormat),"JPEG")==0)
+	      ext="jpg";
+      sprintf((char*)pszDest,"%s/%s.%s",tempPath,tmpMap->value,ext);
+      fprintf(stderr,"DEBUG pszDest : %s\n",pszDest);
+    }
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"ProjWin","value");
+    if(tmpMap!=NULL){
+      char *tmp=tmpMap->value;
+      char *t=strtok(tmp,",");
+      int cnt=0;
+      while(t!=NULL){
+	switch(cnt){
+	case 0:
+	  dfULX = atof(t);
+	  break;
+	case 1:
+	  dfULY = atof(t);
+	  break;
+	case 2:
+	  dfLRX = atof(t);
+	  break;
+	case 3:
+	  dfLRY = atof(t);
+	  break;
+	}
+	fprintf(stderr,"%s\n\n",t);
+	fprintf(stderr,"%f - %f - %f - %f\n\n",dfULX,dfULY,dfLRX,dfLRY);
+	t=strtok(NULL,",");
+	cnt++;
+      }
+    }
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"SRS","value");
+    if(tmpMap!=NULL){
+      OGRSpatialReference oOutputSRS;
+      if( oOutputSRS.SetFromUserInput( tmpMap->value ) != OGRERR_NONE )
+	{
+	  fprintf( stderr, "Failed to process SRS definition: %s\n", 
+		   tmpMap->value );
+	    /**
+	     * Avoiding GDALDestroyDriverManager() call
+	     */
+	  exit( 1 );
+	}
+      oOutputSRS.exportToWkt( &pszOutputSRS );
+    }
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"Type","value");
+    if(tmpMap!=NULL){
+      int	iType;
+      
+      for( iType = 1; iType < GDT_TypeCount; iType++ )
+	{
+	  if( GDALGetDataTypeName((GDALDataType)iType) != NULL
+	      && EQUAL(GDALGetDataTypeName((GDALDataType)iType),
+		       tmpMap->value) )
+	    {
+	      eOutputType = (GDALDataType) iType;
+	    }
+	}
+      
+      if( eOutputType == GDT_Unknown )
+	{
+	  printf( "Unknown output pixel type: %s\n", tmpMap->value );
+	  /**
+	   * Avoiding GDALDestroyDriverManager() call
+	   */
+	  exit( 2 );
+	}
+    }
+    fprintf(stderr,"==%s %s %s %==\n",pszFormat,pszSource,pszDest);
+    fflush(stderr);
+
+    if( pszDest == NULL ){
+	fprintf(stderr,"exit line 416");
+	fflush(stderr);
+	/**
+	 * Avoiding GDALDestroyDriverManager() call
+	 */
+        exit( 10 );
+      }
+
+    if ( strcmp(pszSource, pszDest) == 0)
+      {
+        fprintf(stderr, "Source and destination datasets must be different.\n");
+	fflush(stderr);
+	/**
+	 * Avoiding GDALDestroyDriverManager() call
+	 */
+        exit( 1 );
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*      Attempt to open source file.                                 */
+    /* ----------------------------------------------------------------- */
+
+    hDataset = GDALOpenShared( pszSource, GA_ReadOnly );
+    
+    if( hDataset == NULL ){
+        fprintf( stderr,
+                 "GDALOpen failed - %d\n%s\n",
+                 CPLGetLastErrorNo(), CPLGetLastErrorMsg() );
+	fflush(stderr);
+	/**
+	 * Avoiding GDALDestroyDriverManager() call
+	 */
+        exit( 1 );
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*      Handle subdatasets.                                          */
+    /* ----------------------------------------------------------------- */
+    if( !bCopySubDatasets 
+        && CSLCount(GDALGetMetadata( hDataset, "SUBDATASETS" )) > 0 
+        && GDALGetRasterCount(hDataset) == 0 )
+      {
+        fprintf( stderr,
+                 "Input file contains subdatasets. Please, select one of them for reading.\n" );
+	fflush(stderr);
+        GDALClose( hDataset );
+	/**
+	 * Avoiding GDALDestroyDriverManager() call
+	 */
+        exit( 1 );
+      }
+
+    if( CSLCount(GDALGetMetadata( hDataset, "SUBDATASETS" )) > 0 
+        && bCopySubDatasets )
+      {
+        char **papszSubdatasets = GDALGetMetadata(hDataset,"SUBDATASETS");
+        char *pszSubDest = (char *) CPLMalloc(strlen(pszDest)+32);
+        int i;
+        int bOldSubCall = bSubCall;
+        
+        //argv[iDstFileArg] = pszSubDest;
+        bSubCall = TRUE;
+        for( i = 0; papszSubdatasets[i] != NULL; i += 2 )
+	  {
+            //argv[iSrcFileArg] = strstr(papszSubdatasets[i],"=")+1;
+            sprintf( pszSubDest, "%s%d", pszDest, i/2 + 1 );
+            /*if( ProxyMain( argc, argv ) != 0 )
+	      break;*/
+	  }
+        
+        bSubCall = bOldSubCall;
+        CPLFree( pszSubDest );
+
+        GDALClose( hDataset );
+
+        if( !bSubCall )
+	  {
+            GDALDumpOpenDatasets( stderr );
+	    fflush(stderr);
+	    /**
+	     * Avoiding GDALDestroyDriverManager() call
+	     */
+	  }
+        return 1;
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*      Collect some information from the source file.               */
+    /* ----------------------------------------------------------------- */
+    nRasterXSize = GDALGetRasterXSize( hDataset );
+    nRasterYSize = GDALGetRasterYSize( hDataset );
+
+    if( !bQuiet )
+      printf( "Input file size is %d, %d\n", nRasterXSize, nRasterYSize );
+
+    if( anSrcWin[2] == 0 && anSrcWin[3] == 0 ){
+        anSrcWin[2] = nRasterXSize;
+        anSrcWin[3] = nRasterYSize;
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*	Build band list to translate	                                 */
+    /* ----------------------------------------------------------------- */
+    if( nBandCount == 0 ){
+        nBandCount = GDALGetRasterCount( hDataset );
+        if( nBandCount == 0 ){
+            fprintf( stderr, "Input file has no bands, and so cannot be translated.\n" );
+	    fflush(stderr);
+	    /**
+	     * Avoiding GDALDestroyDriverManager() call
+	     */
+            exit(1 );
+	  }
+
+        panBandList = (int *) CPLMalloc(sizeof(int)*nBandCount);
+        for( i = 0; i < nBandCount; i++ )
+	  panBandList[i] = i+1;
+      }
+    else
+      {
+        for( i = 0; i < nBandCount; i++ )
+	  {
+            if( panBandList[i] < 1 || panBandList[i] > GDALGetRasterCount(hDataset) )
+	      {
+                fprintf( stderr, 
+                         "Band %d requested, but only bands 1 to %d available.\n",
+                         panBandList[i], GDALGetRasterCount(hDataset) );
+		fflush(stderr);
+		/**
+		 * Avoiding GDALDestroyDriverManager() call
+		 */
+                exit( 2 );
+	      }
+	  }
+
+        if( nBandCount != GDALGetRasterCount( hDataset ) )
+	  bDefBands = FALSE;
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*   Compute the source window from the projected source window      */
+    /*   if the projected coordinates were provided.  Note that the      */
+    /*   projected coordinates are in ulx, uly, lrx, lry format,         */
+    /*   while the anSrcWin is xoff, yoff, xsize, ysize with the         */
+    /*   xoff,yoff being the ulx, uly in pixel/line.                     */
+    /* ----------------------------------------------------------------- */
+    if( dfULX != 0.0 || dfULY != 0.0 
+        || dfLRX != 0.0 || dfLRY != 0.0 )
+      {
+        double	adfGeoTransform[6];
+
+        GDALGetGeoTransform( hDataset, adfGeoTransform );
+
+        if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ){
+            fprintf( stderr, 
+                     "The -projwin option was used, but the geotransform is\n"
+                     "rotated.  This configuration is not supported.\n" );
+            GDALClose( hDataset );
+            CPLFree( panBandList );
+	    fflush(stderr);
+	    /**
+	     * Avoiding GDALDestroyDriverManager() call
+	     */
+            exit( 1 );
+	  }
+
+        anSrcWin[0] = (int) 
+	  ((dfULX - adfGeoTransform[0]) / adfGeoTransform[1] + 0.001);
+        anSrcWin[1] = (int) 
+	  ((dfULY - adfGeoTransform[3]) / adfGeoTransform[5] + 0.001);
+
+        anSrcWin[2] = (int) ((dfLRX - dfULX) / adfGeoTransform[1] + 0.5);
+        anSrcWin[3] = (int) ((dfLRY - dfULY) / adfGeoTransform[5] + 0.5);
+
+        if( !bQuiet )
+	  fprintf( stdout, 
+		   "Computed -srcwin %d %d %d %d from projected window.\n",
+		   anSrcWin[0], 
+		   anSrcWin[1], 
+		   anSrcWin[2], 
+		   anSrcWin[3] );
+        
+        if( anSrcWin[0] < 0 || anSrcWin[1] < 0 
+            || anSrcWin[0] + anSrcWin[2] > GDALGetRasterXSize(hDataset) 
+            || anSrcWin[1] + anSrcWin[3] > GDALGetRasterYSize(hDataset) )
+	  {
+            fprintf( stderr, 
+                     "Computed -srcwin falls outside raster size of %dx%d.\n",
+                     GDALGetRasterXSize(hDataset), 
+                     GDALGetRasterYSize(hDataset) );
+            exit( 1 );
+	  }
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*      Verify source window.                                        */
+    /* ----------------------------------------------------------------- */
+    if( anSrcWin[0] < 0 || anSrcWin[1] < 0 
+        || anSrcWin[2] <= 0 || anSrcWin[3] <= 0
+        || anSrcWin[0] + anSrcWin[2] > GDALGetRasterXSize(hDataset) 
+        || anSrcWin[1] + anSrcWin[3] > GDALGetRasterYSize(hDataset) )
+      {
+        fprintf( stderr, 
+                 "-srcwin %d %d %d %d falls outside raster size of %dx%d\n"
+                 "or is otherwise illegal.\n",
+                 anSrcWin[0],
+                 anSrcWin[1],
+                 anSrcWin[2],
+                 anSrcWin[3],
+                 GDALGetRasterXSize(hDataset), 
+                 GDALGetRasterYSize(hDataset) );
+        exit( 1 );
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*      Find the output driver.                                      */
+    /* ----------------------------------------------------------------- */
+    hDriver = GDALGetDriverByName( pszFormat );
+    if( hDriver == NULL )
+      {
+        int	iDr;
+        
+        printf( "Output driver `%s' not recognised.\n", pszFormat );
+        printf( "The following format drivers are configured and support output:\n" );
+        for( iDr = 0; iDr < GDALGetDriverCount(); iDr++ )
+	  {
+            GDALDriverH hDriver = GDALGetDriver(iDr);
+
+            if( GDALGetMetadataItem( hDriver, GDAL_DCAP_CREATE, NULL ) != NULL
+                || GDALGetMetadataItem( hDriver, GDAL_DCAP_CREATECOPY,
+                                        NULL ) != NULL )
+	      {
+                printf( "  %s: %s\n",
+                        GDALGetDriverShortName( hDriver  ),
+                        GDALGetDriverLongName( hDriver ) );
+	      }
+	  }
+        printf( "\n" );
+        
+        GDALClose( hDataset );
+        CPLFree( panBandList );
+	fflush(stderr);
+	/**
+	 * Avoiding GDALDestroyDriverManager() call
+	 */
+        CSLDestroy( papszCreateOptions );
+        exit( 1 );
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*   The short form is to CreateCopy().  We use this if the input    */
+    /*   matches the whole dataset.  Eventually we should rewrite        */
+    /*   this entire program to use virtual datasets to construct a      */
+    /*   virtual input source to copy from.                              */
+    /* ----------------------------------------------------------------- */
+    if( eOutputType == GDT_Unknown 
+        && !bScale && CSLCount(papszMetadataOptions) == 0 && bDefBands 
+        && anSrcWin[0] == 0 && anSrcWin[1] == 0 
+        && anSrcWin[2] == GDALGetRasterXSize(hDataset)
+        && anSrcWin[3] == GDALGetRasterYSize(hDataset) 
+        && pszOXSize == NULL && pszOYSize == NULL 
+        && nGCPCount == 0 && !bGotBounds
+        && pszOutputSRS == NULL && !bSetNoData
+        && nRGBExpand == 0)
+      {
+        
+        hOutDS = GDALCreateCopy( hDriver, pszDest, hDataset, 
+                                 bStrict, papszCreateOptions, 
+                                 pfnProgress, NULL );
+
+        if( hOutDS != NULL )
+	  GDALClose( hOutDS );
+        
+        GDALClose( hDataset );
+
+        CPLFree( panBandList );
+
+        if( !bSubCall )
+	  {
+            GDALDumpOpenDatasets( stderr );
+	    /**
+	     * Avoiding GDALDestroyDriverManager() call
+	     */
+	  }
+
+        CSLDestroy( papszCreateOptions );
+	outputs=(maps*)malloc(sizeof(maps*));
+	outputs->name="OutputedPolygon";
+	outputs->content=createMap("value",(char*)pszDest);
+	outputs->next=NULL;
+	
+	return SERVICE_SUCCEEDED;
+      }
+    fprintf(stderr,"==%s %s %s %==\n",pszFormat,pszSource,pszDest);
+    fflush(stderr);
+
+    /* ----------------------------------------------------------------- */
+    /*      Establish some parameters.                                   */
+    /* ----------------------------------------------------------------- */
+    if( pszOXSize == NULL )
+      {
+        nOXSize = anSrcWin[2];
+        nOYSize = anSrcWin[3];
+      }
+    else
+      {
+        nOXSize = (int) ((pszOXSize[strlen(pszOXSize)-1]=='%' 
+                          ? atof(pszOXSize)/100*anSrcWin[2] : atoi(pszOXSize)));
+        nOYSize = (int) ((pszOYSize[strlen(pszOYSize)-1]=='%' 
+                          ? atof(pszOYSize)/100*anSrcWin[3] : atoi(pszOYSize)));
+      }
+    fprintf(stderr,"==%s %s %s %==\n",pszFormat,pszSource,pszDest);
+    fflush(stderr);
+
+    /* ================================================================= */
+    /*      Create a virtual dataset.                                    */
+    /* ================================================================= */
+    VRTDataset *poVDS;
+        
+    /* ----------------------------------------------------------------- */
+    /*      Make a virtual clone.                                        */
+    /* ----------------------------------------------------------------- */
+    poVDS = (VRTDataset *) VRTCreate( nOXSize, nOYSize );
+
+    if( nGCPCount == 0 )
+      {
+        if( pszOutputSRS != NULL )
+	  {
+            poVDS->SetProjection( pszOutputSRS );
+	  }
+        else
+	  {
+            pszProjection = GDALGetProjectionRef( hDataset );
+            if( pszProjection != NULL && strlen(pszProjection) > 0 )
+	      poVDS->SetProjection( pszProjection );
+	  }
+      }
+
+    if( bGotBounds )
+      {
+        adfGeoTransform[0] = adfULLR[0];
+        adfGeoTransform[1] = (adfULLR[2] - adfULLR[0]) / nOXSize;
+        adfGeoTransform[2] = 0.0;
+        adfGeoTransform[3] = adfULLR[1];
+        adfGeoTransform[4] = 0.0;
+        adfGeoTransform[5] = (adfULLR[3] - adfULLR[1]) / nOYSize;
+
+        poVDS->SetGeoTransform( adfGeoTransform );
+      }
+
+    else if( GDALGetGeoTransform( hDataset, adfGeoTransform ) == CE_None 
+	     && nGCPCount == 0 )
+      {
+        adfGeoTransform[0] += anSrcWin[0] * adfGeoTransform[1]
+	  + anSrcWin[1] * adfGeoTransform[2];
+        adfGeoTransform[3] += anSrcWin[0] * adfGeoTransform[4]
+	  + anSrcWin[1] * adfGeoTransform[5];
+        
+        adfGeoTransform[1] *= anSrcWin[2] / (double) nOXSize;
+        adfGeoTransform[2] *= anSrcWin[3] / (double) nOYSize;
+        adfGeoTransform[4] *= anSrcWin[2] / (double) nOXSize;
+        adfGeoTransform[5] *= anSrcWin[3] / (double) nOYSize;
+        
+        poVDS->SetGeoTransform( adfGeoTransform );
+      }
+
+    if( nGCPCount != 0 )
+      {
+        const char *pszGCPProjection = pszOutputSRS;
+
+        if( pszGCPProjection == NULL )
+	  pszGCPProjection = GDALGetGCPProjection( hDataset );
+        if( pszGCPProjection == NULL )
+	  pszGCPProjection = "";
+
+        poVDS->SetGCPs( nGCPCount, pasGCPs, pszGCPProjection );
+
+        GDALDeinitGCPs( nGCPCount, pasGCPs );
+        CPLFree( pasGCPs );
+      }
+
+    else if( GDALGetGCPCount( hDataset ) > 0 )
+      {
+        GDAL_GCP *pasGCPs;
+        int       nGCPs = GDALGetGCPCount( hDataset );
+
+        pasGCPs = GDALDuplicateGCPs( nGCPs, GDALGetGCPs( hDataset ) );
+
+        for( i = 0; i < nGCPs; i++ )
+	  {
+            pasGCPs[i].dfGCPPixel -= anSrcWin[0];
+            pasGCPs[i].dfGCPLine  -= anSrcWin[1];
+            pasGCPs[i].dfGCPPixel *= (nOXSize / (double) anSrcWin[2] );
+            pasGCPs[i].dfGCPLine  *= (nOYSize / (double) anSrcWin[3] );
+	  }
+            
+        poVDS->SetGCPs( nGCPs, pasGCPs,
+                        GDALGetGCPProjection( hDataset ) );
+
+        GDALDeinitGCPs( nGCPs, pasGCPs );
+        CPLFree( pasGCPs );
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*      Transfer generally applicable metadata.                      */
+    /* ----------------------------------------------------------------- */
+    poVDS->SetMetadata( ((GDALDataset*)hDataset)->GetMetadata() );
+    AttachMetadata( (GDALDatasetH) poVDS, papszMetadataOptions );
+    fprintf(stderr,"Transfer generally applicable metadata.\n");
+    fflush(stderr);
+
+    /* ----------------------------------------------------------------- */
+    /*      Transfer metadata that remains valid if the spatial          */
+    /*      arrangement of the data is unaltered.                        */
+    /* ----------------------------------------------------------------- */
+    if( anSrcWin[0] == 0 && anSrcWin[1] == 0 
+        && anSrcWin[2] == GDALGetRasterXSize(hDataset)
+        && anSrcWin[3] == GDALGetRasterYSize(hDataset) 
+        && pszOXSize == NULL && pszOYSize == NULL )
+      {
+        char **papszMD;
+
+        papszMD = ((GDALDataset*)hDataset)->GetMetadata("RPC");
+        if( papszMD != NULL )
+	  poVDS->SetMetadata( papszMD, "RPC" );
+      }
+
+    if (nRGBExpand != 0)
+      nBandCount += nRGBExpand - 1;
+
+    /* ================================================================= */
+    /*      Process all bands.                                           */
+    /* ================================================================= */
+    for( i = 0; i < nBandCount; i++ )
+      {
+        VRTSourcedRasterBand   *poVRTBand;
+        GDALRasterBand  *poSrcBand;
+        GDALDataType    eBandType;
+
+        if (nRGBExpand != 0 && i < nRGBExpand)
+	  {
+            poSrcBand = ((GDALDataset *) 
+			 hDataset)->GetRasterBand(panBandList[0]);
+            if (poSrcBand->GetColorTable() == NULL)
+	      {
+                fprintf(stderr, "Error : band %d has no color table\n", panBandList[0]);
+                GDALClose( hDataset );
+                CPLFree( panBandList );
+		fflush(stderr);
+		/**
+		 * Avoiding GDALDestroyDriverManager() call
+		 */
+                CSLDestroy( papszCreateOptions );
+                exit( 1 );
+	      }
+	  }
+        else
+	  poSrcBand = ((GDALDataset *) 
+		       hDataset)->GetRasterBand(panBandList[i]);
+
+	/* ------------------------------------------------------------ */
+	/*      Select output data type to match source.                */
+	/* ------------------------------------------------------------ */
+        if( eOutputType == GDT_Unknown )
+	  eBandType = poSrcBand->GetRasterDataType();
+        else
+	  eBandType = eOutputType;
+
+	/* ------------------------------------------------------------ */
+	/*      Create this band.                                       */
+	/* ------------------------------------------------------------ */
+        poVDS->AddBand( eBandType, NULL );
+        poVRTBand = (VRTSourcedRasterBand *) poVDS->GetRasterBand( i+1 );
+            
+	/* ------------------------------------------------------------ */
+	/*      Do we need to collect scaling information?              */
+	/* ------------------------------------------------------------ */
+        double dfScale=1.0, dfOffset=0.0;
+
+        if( bScale && !bHaveScaleSrc )
+	  {
+            double	adfCMinMax[2];
+            GDALComputeRasterMinMax( poSrcBand, TRUE, adfCMinMax );
+            dfScaleSrcMin = adfCMinMax[0];
+            dfScaleSrcMax = adfCMinMax[1];
+	  }
+
+        if( bScale )
+	  {
+            if( dfScaleSrcMax == dfScaleSrcMin )
+	      dfScaleSrcMax += 0.1;
+            if( dfScaleDstMax == dfScaleDstMin )
+	      dfScaleDstMax += 0.1;
+
+            dfScale = (dfScaleDstMax - dfScaleDstMin) 
+	      / (dfScaleSrcMax - dfScaleSrcMin);
+            dfOffset = -1 * dfScaleSrcMin * dfScale + dfScaleDstMin;
+	  }
+
+	/* ------------------------------------------------------------ */
+	/*      Create a simple or complex data source depending on the */
+	/*      translation type required.                              */
+	/* ------------------------------------------------------------ */
+        if( bScale || (nRGBExpand != 0 && i < nRGBExpand) )
+	  {
+            poVRTBand->AddComplexSource( poSrcBand,
+                                         anSrcWin[0], anSrcWin[1], 
+                                         anSrcWin[2], anSrcWin[3], 
+                                         0, 0, nOXSize, nOYSize,
+                                         dfOffset, dfScale,
+                                         VRT_NODATA_UNSET,
+                                         (nRGBExpand != 0 && i < nRGBExpand) ? i + 1 : 0 );
+	  }
+        else
+	  poVRTBand->AddSimpleSource( poSrcBand,
+				      anSrcWin[0], anSrcWin[1], 
+				      anSrcWin[2], anSrcWin[3], 
+				      0, 0, nOXSize, nOYSize );
+
+        /* In case of color table translate, we only set the color interpretation */
+        /* other info copied by CopyCommonInfoFrom are not relevant in RGB expansion */
+        if (nRGBExpand != 0 && i < nRGBExpand)
+	  {
+            poVRTBand->SetColorInterpretation( (GDALColorInterp) (GCI_RedBand + i) );
+	  }
+        else
+	  {
+	    /* --------------------------------------------------------- */
+	    /*      copy over some other information of interest.        */
+	    /* --------------------------------------------------------- */
+            poVRTBand->CopyCommonInfoFrom( poSrcBand );
+	  }
+
+	/* ------------------------------------------------------------- */
+	/*      Set a forcable nodata value?                             */
+	/* ------------------------------------------------------------- */
+        if( bSetNoData )
+	  poVRTBand->SetNoDataValue( dfNoDataReal );
+      }
+
+    /* ----------------------------------------------------------------- */
+    /*      Write to the output file using CopyCreate().                 */
+    /* ----------------------------------------------------------------- */
+    fprintf(stderr,"DEBUG pszDest %s\n",pszDest);
+    hOutDS = GDALCreateCopy( hDriver, pszDest, (GDALDatasetH) poVDS,
+                             bStrict, papszCreateOptions, 
+                             pfnProgress, NULL );
+    fprintf(stderr,"DEBUG pszDest %s\n",pszDest);
+    fflush(stderr);
+
+    if( hOutDS != NULL )
+      {
+        GDALClose( hOutDS );
+      }
+    
+    GDALClose( (GDALDatasetH) poVDS );
+        
+    GDALClose( hDataset );
+
+    CPLFree( panBandList );
+    
+    CPLFree( pszOutputSRS );
+
+    if( !bSubCall )
+      {
+        GDALDumpOpenDatasets( stderr );
+	fflush(stderr);
+	/**
+	 * Avoiding GDALDestroyDriverManager() call
+	 */
+      }
+
+    CSLDestroy( papszCreateOptions );
+    
+    outputs=(maps*)malloc(sizeof(maps*));
+    outputs->name="OutputedPolygon";
+    outputs->content=createMap("value",(char*)pszDest);
+    outputs->next=NULL;
+
+    return SERVICE_SUCCEEDED;
+  }
+
+
+  /************************************************************************/
+  /*                           AttachMetadata()                           */
+  /************************************************************************/
+
+  static void AttachMetadata( GDALDatasetH hDS, char **papszMetadataOptions )
+
+  {
+    int nCount = CSLCount(papszMetadataOptions);
+    int i;
+
+    for( i = 0; i < nCount; i++ )
+      {
+        char    *pszKey = NULL;
+        const char *pszValue;
+        
+        pszValue = CPLParseNameValue( papszMetadataOptions[i], &pszKey );
+        GDALSetMetadataItem(hDS,pszKey,pszValue,NULL);
+        CPLFree( pszKey );
+      }
+
+    CSLDestroy( papszMetadataOptions );
+  }
+
+}
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/Makefile	(revision 221)
@@ -0,0 +1,5 @@
+cgi-env/service.zo: servive.for
+	gfortran -shared -fpic -o cgi-env/fortran_hello.zo ./service.f
+
+clean:
+	rm -f cgi-env/*.zo
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/cgi-env/hellof.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/cgi-env/hellof.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/cgi-env/hellof.zcfg	(revision 221)
@@ -0,0 +1,40 @@
+[hellof]
+ Title = Hello string
+ Abstract = Create a welcome message.
+ Profile = urn:ogc:wps:1.0.0:buffer
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = driftx_service.zo
+ serviceType = C-FORTRAN
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [S]
+   Title = The string
+   Abstract = The name to display in the welcome message.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [result]
+   Title = The string
+   Abstract = The string created by service.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/service.f
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/service.f	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-fotran/service.f	(revision 221)
@@ -0,0 +1,44 @@
+ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
+c     Simply create a welcome message
+ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
+       Integer FUNCTION HELLOF(zoo_main_cfg,zoo_inputs,zoo_outputs)
+     & RESULT (R)
+       Integer R, ls, iLenStr
+       CHARACTER*(1024) zoo_main_cfg(10,30),zoo_inputs(10,30),
+     & zoo_outputs(10,30)
+        CHARACTER*(1024) TMP
+
+       write(0,*) 'Hello '//zoo_inputs(4,1)//' from the Fortran world !'
+
+       ls = iLenStr(zoo_inputs(4,1))
+       TMP = zoo_inputs(4,1)
+       zoo_outputs(1,1) = 'name'//CHAR(0)
+       zoo_outputs(2,1) = 'result'//CHAR(0)
+       zoo_outputs(3,1) = 'value'//CHAR(0)
+       zoo_outputs(4,1) = 'Hello '//TMP(1:ls)//
+     & ' from the Fortran world !'//CHAR(0)
+       zoo_outputs(5,1) = 'datatype'//CHAR(0)
+       zoo_outputs(6,1) = 'string'//CHAR(0)
+
+       R = 3
+       Return
+       END
+
+       Integer Function iLenStr(cString)
+ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
+c     Compute String Length (thanks to Abdelatif Djerboua from RHEA™)
+ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
+       Character*(*) cString
+       Integer       iLen,i
+
+      iLen = Len(cString)
+      Do i=iLen,1,-1
+         If(ichar(cString(i:i)).NE.0) Goto 10
+      EndDo
+      i = 1
+  10  Continue
+      iLenStr = i
+
+      Return
+      End Function iLenStr
+
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-java/HelloJava.java
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-java/HelloJava.java	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-java/HelloJava.java	(revision 221)
@@ -0,0 +1,38 @@
+/**
+ * Author : Gérald FENOY
+ *
+ *  Copyright 2008-2009 GeoLabs SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+import java.lang.*;
+import java.util.*;
+
+public class HelloJava {
+    public static int HelloWorldJava(HashMap conf,HashMap inputs, HashMap outputs) {
+        HashMap hm1 = new HashMap();
+        hm1.put("dataType","string");
+        HashMap tmp=(HashMap)(inputs.get("S"));
+        String v=tmp.get("value").toString();
+        hm1.put("value","Hello "+v+" from JAVA World !!");
+        outputs.put("Result",hm1);
+        return 3;
+  }
+}
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-java/cgi-env/HelloWorldJava.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-java/cgi-env/HelloWorldJava.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-java/cgi-env/HelloWorldJava.zcfg	(revision 221)
@@ -0,0 +1,33 @@
+[HelloWorldJava]
+ Title = Display a string
+ Abstract = Display a string which contains an hello message
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = HelloJava
+ serviceType = Java
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [S]
+   Title = the string
+   Abstract = The string to add to the hellow one.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The hello string
+   Abstract = The Hello message string.
+   <LiteralOutput>
+    DataType = string
+    <Default>
+    </Default>
+   </LiteralOutput>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hello.js
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hello.js	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hello.js	(revision 221)
@@ -0,0 +1,13 @@
+
+function hellojs(conf,inputs,outputs){
+	outputs[0]["result"]["value"]="Hello "+inputs[0]["S"]["value"]+" from the JS World !";
+	//SERVICE_SUCEEDED
+	return Array(3,outputs);
+}
+
+function hellojs1(conf,inputs,outputs){
+	outputs[0]["result"]["value"]="Hello "+inputs[0]["S"]["value"]+" from the JS World !";
+	//SERVICE_SUCEEDED
+	return {"result":3,"outputs": outputs};
+}
+
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hellojs.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hellojs.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hellojs.zcfg	(revision 221)
@@ -0,0 +1,40 @@
+[hellojs]
+ Title = HelloWorld Service in JavaScript
+ Abstract = Output and Hello Wolrd string
+ Profile = urn:ogc:wps:1.0.0:buffer
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = hello.js
+ serviceType = JS
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [S]
+   Title = Name
+   Abstract = The name to display in the hello message
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <LiteralData>
+    dataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [result]
+   Title = The resulting string
+   Abstract = The string created by service.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <LiteralOutput>
+     dataType = string
+     <Default>
+     </Default>
+   </LiteralOutput>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hellojs1.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hellojs1.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-js/cgi-env/hellojs1.zcfg	(revision 221)
@@ -0,0 +1,40 @@
+[hellojs1]
+ Title = HelloWorld Service in JavaScript
+ Abstract = Output and Hello Wolrd string
+ Profile = urn:ogc:wps:1.0.0:buffer
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = hello.js
+ serviceType = JS
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [S]
+   Title = Name
+   Abstract = The name to display in the hello message
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <LiteralData>
+    dataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [result]
+   Title = The resulting string
+   Abstract = The string created by service.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <LiteralOutput>
+     dataType = string
+    <Default>
+    </Default>
+   </LiteralOutput>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-perl/Hello.pl
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-perl/Hello.pl	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-perl/Hello.pl	(revision 221)
@@ -0,0 +1,7 @@
+sub HelloPL {
+	my ($main_conf,$real_inputs,$real_outputs) = @_;
+	
+	$real_outputs->{"Result"}->{"value"}=$real_inputs->{"a"}->{"value"};
+	return 3;
+}
+
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-perl/cgi-env/HelloPL.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-perl/cgi-env/HelloPL.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-perl/cgi-env/HelloPL.zcfg	(revision 221)
@@ -0,0 +1,39 @@
+[HelloPL]
+ Title = Create a welcome message string.
+ Abstract = Create a welcome string.
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = Hello.pl
+ serviceType = Perl
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [a]
+   Title = Input string
+   Abstract = The name to display in the welcome message.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = My test
+   </MetaData>
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The welcome message
+   Abstract = The welcome message created by service.
+   <MetaData>
+    title = My test
+   </MetaData>
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataOutputs>
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-php/cgi-env/HelloPHP.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-php/cgi-env/HelloPHP.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-php/cgi-env/HelloPHP.zcfg	(revision 221)
@@ -0,0 +1,33 @@
+[HelloPHP]
+ Title = Display a string
+ Abstract = Display a string which contains an hello message
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = hello.php
+ serviceType = PHP
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [S]
+   Title = the string
+   Abstract = The string to add to the hellow one.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The hello string
+   Abstract = The Hello message string.
+   <LiteralOutput>
+    DataType = string
+    <Default>
+    </Default>
+   </LiteralOutput>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-php/hello.php
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-php/hello.php	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-php/hello.php	(revision 221)
@@ -0,0 +1,9 @@
+<?
+
+function HelloPHP(&$main_conf,&$inputs,&$outputs){
+  $outputs=Array();
+  $outputs["Result"]["value"]="Hello ".$inputs[S][value]." from the PHP world !!";
+  return 3;
+}
+
+?>
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-py/cgi-env/HelloPy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-py/cgi-env/HelloPy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-py/cgi-env/HelloPy.zcfg	(revision 221)
@@ -0,0 +1,47 @@
+[HelloPy]
+ Title = Create a welcome message string.
+ Abstract = Create a welcome string.
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = test_service
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [a]
+   Title = Input string
+   Abstract = The name to display in the welcome message.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = My test
+   </MetaData>
+   <LiteralData>
+    DataType = string
+    <Default>
+     UOM = meter
+    </Default>
+    <Supported>
+     UOM = meter
+    </Supported>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The welcome message
+   Abstract = The welcome message created by service.
+   <MetaData>
+    title = My test
+   </MetaData>
+   <LiteralData>
+    DataType = string
+    <Default>
+     UOM = meter
+    </Default>
+    <Supported>
+     UOM = meter
+    </Supported>
+   </LiteralData>
+ </DataOutputs>
Index: /tags/rel-1.2.0-rc1/zoo-services/hello-py/test_service.py
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/hello-py/test_service.py	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/hello-py/test_service.py	(revision 221)
@@ -0,0 +1,5 @@
+import sys
+def HelloPy(conf,inputs,outputs):
+	outputs["Result"]["value"]="Hello "+inputs["a"]["value"]+" from Python World !"
+	return 3
+
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/BoundaryPy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/BoundaryPy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/BoundaryPy.zcfg	(revision 221)
@@ -0,0 +1,56 @@
+[BoundaryPy]
+ Title = Compute boundary.
+ Abstract = A new geometry object is created and returned containing the boundary of the geometry on which the method is invoked. 
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_sp
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to conpute boundary
+   Abstract = URI to a set of GML that describes the polygon.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The geometry created
+   Abstract = The geometry containing the boundary of the geometry on which the method is invoked.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+   </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/BufferPy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/BufferPy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/BufferPy.zcfg	(revision 221)
@@ -0,0 +1,68 @@
+[BufferPy]
+ Title = Create a buffer around a polygon. 
+ Abstract = Create a buffer around a single polygon. Accepts the polygon as GML and provides GML output for the buffered feature. 
+ Profile = urn:ogc:wps:1.0.0:buffer
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_sp
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to be buffered
+   Abstract = URI to a set of GML that describes the polygon.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+     asReference = true	
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+  [BufferDistance]
+   Title = Buffer Distance
+   Abstract = Distance to be used to calculate buffer.
+   minOccurs = 0
+   maxOccurs = 1
+   <LiteralData>
+    DataType = float
+    <Default>
+     uom = meters
+     value = 1
+    </Default>
+    <Supported>
+     uom = feet
+    </Supported>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = Buffered Polygon
+   Abstract = GML stream describing the buffered polygon feature.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/CentroidPy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/CentroidPy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/CentroidPy.zcfg	(revision 221)
@@ -0,0 +1,53 @@
+[CentroidPy]
+ Title = Get the centroid of a polygon. 
+ Abstract = Compute the geometry centroid.
+ Profile = urn:ogc:wps:1.0.0:centroid
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_sp
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to get the centroid
+   Abstract = The centroid which is not necessarily within the geometry.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The Centroid
+   Abstract = JSON String / GML Entity of the centroid
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/point.xsd
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/ConvexHullPy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/ConvexHullPy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/ConvexHullPy.zcfg	(revision 221)
@@ -0,0 +1,51 @@
+[ConvexHullPy]
+ Title = Compute convex hull.
+ Abstract = A new geometry object is created and returned containing the convex hull of the geometry on which the method is invoked.
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_sp
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to compute convexhull
+   Abstract = URI to a set of GML that describes the polygon.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+   title = Mon test
+   </MetaData>
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The convex hull of the geometry
+   Abstract = The convex hull of the geometry
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataOutputs> 
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/DifferencePy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/DifferencePy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/DifferencePy.zcfg	(revision 221)
@@ -0,0 +1,75 @@
+[DifferencePy]
+ Title = Compute difference. . 
+ Abstract = Generates a new geometry which is the region of this geometry with the region of the other geometry removed.
+ Profile = urn:ogc:wps:1.0.0:difference
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_sp
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The difference between two geometries
+   Abstract = The difference between the two geometries.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+    <ComplexData>
+     <Default>
+      mimeType = text/xml
+      schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+      encoding = UTF-8
+      extension = xml
+     </Default>
+     <Supported>
+      mimeType = application/json
+      encoding = UTF-8
+      extension = js
+     </Supported>
+    </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/IntersectionPy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/IntersectionPy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/IntersectionPy.zcfg	(revision 221)
@@ -0,0 +1,75 @@
+[IntersectionPy]
+ Title = Compute intersection. 
+ Abstract = Generates a new geometry which is the region of intersection of the two geometries operated on.
+ Profile = urn:ogc:wps:1.0.0:union
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_sp
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = Intersection of the two geometries
+   Abstract = A new geometry representing the intersection or NULL if there is no intersection or an error occurs.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+    <ComplexData>
+     <Default>
+      mimeType = text/xml
+      schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+      encoding = UTF-8
+      extension = xml
+     </Default>
+     <Supported>
+      mimeType = application/json
+      encoding = UTF-8
+      extension = js
+     </Supported>
+    </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/SymDifferencePy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/SymDifferencePy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/SymDifferencePy.zcfg	(revision 221)
@@ -0,0 +1,83 @@
+[SymDifferencePy]
+ Title = Compute symmetric difference. 
+ Abstract = Generates a new geometry which is the symmetric difference of this geometry and the other geometry.
+ Profile = urn:ogc:wps:1.0.0:symmetricdifference
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_sp
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Supported>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The symmetric difference between two geometries
+   Abstract = The symmetric difference between the two geometries.
+   <MetaData>
+    Title = Symmetric Difference  
+   </MetaData>   
+    <ComplexData>
+     <Default>
+      mimeType = text/xml
+      schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+      encoding = UTF-8
+      extension = xml
+     </Default>
+     <Supported>
+      mimeType = application/json
+      encoding = UTF-8
+      extension = js
+     </Supported>
+    </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/UnionPy.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/UnionPy.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/UnionPy.zcfg	(revision 221)
@@ -0,0 +1,73 @@
+[UnionPy]
+ Title = Compute union. 
+ Abstract = Generates a new geometry which is the region of union of the two geometries operated on.
+ Profile = urn:ogc:wps:1.0.0:union
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_sp
+ serviceType = Python
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The union of two geometries
+   Abstract = The geometry representing the union of the two geometries.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+    <ComplexData>
+     <Default>
+      mimeType = text/xml
+      schema = http://schemas.opengis.net/gml/3.1.0/base/feature.xsd
+      encoding = UTF-8
+      extension = xml
+     </Default>
+     <Supported>
+      mimeType = application/json
+      encoding = UTF-8
+      extension = js
+     </Supported>
+    </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/ogr_sp.py
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/ogr_sp.py	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops-py/cgi-env/ogr_sp.py	(revision 221)
@@ -0,0 +1,187 @@
+import osgeo.ogr
+import libxml2
+import os
+import sys
+
+def createGeometryFromWFS(my_wfs_response):
+    doc=libxml2.parseMemory(my_wfs_response,len(my_wfs_response))
+    ctxt = doc.xpathNewContext()
+    res=ctxt.xpathEval("/*/*/*/*/*[local-name()='Polygon' or local-name()='MultiPolygon' or local-name()='Point' or local-name()='MultiPoint' or local-name()='MultiLineString' or local-name()='LineString' ]")
+    geometry=[]
+    try:
+        for node in res:
+            geometry_as_string=node.serialize()
+            geometry+=[osgeo.ogr.CreateGeometryFromGML(geometry_as_string)]
+    except:
+        print >> sys.stderr,"Error"
+    return geometry
+
+def extractInputs(obj):
+    if obj["mimeType"]=="application/json":
+	return [osgeo.ogr.CreateGeometryFromJson(obj["value"])]
+    else:
+	try:
+        	return createGeometryFromWFS(obj["value"])
+	except:
+		return [osgeo.ogr.CreateGeometryFromJson(obj["value"])]
+    return null
+    
+def outputResult(conf,obj,geom):
+    driverName = "GML"
+    extension = [ ".xml" , ".xsd" ]
+    if obj["mimeType"]=="application/json":
+        driverName = "GeoJSON"
+        extension = [ ".js" ]
+    drv = osgeo.ogr.GetDriverByName( driverName )
+    ds = drv.CreateDataSource( conf["main"]["tmpPath"]+"/store"+conf["lenv"]["sid"]+extension[0] )
+    lyr = ds.CreateLayer( "Result", None, osgeo.ogr.wkbUnknown )
+    field_defn = osgeo.ogr.FieldDefn( "Name", osgeo.ogr.OFTString )
+    field_defn.SetWidth( len("Result10000") )
+    lyr.CreateField ( field_defn )
+    i=0
+    while i < len(geom):
+        feat = osgeo.ogr.Feature( lyr.GetLayerDefn())
+        feat.SetField( "Name", "Result"+str(i) )
+        feat.SetGeometry(geom[i])
+        lyr.CreateFeature(feat)
+        feat.Destroy()
+        geom[i].Destroy()
+        i+=1
+    ds.Destroy()
+    obj["value"]=open(conf["main"]["tmpPath"]+"/store"+conf["lenv"]["sid"]+extension[0],"r").read()
+    os.unlink(conf["main"]["tmpPath"]+"/store"+conf["lenv"]["sid"]+extension[0])
+    if len(extension)>1:
+        os.unlink(conf["main"]["tmpPath"]+"/store"+conf["lenv"]["sid"]+extension[1])
+
+def BoundaryPy(conf,inputs,outputs):
+    geometry=extractInputs(inputs["InputPolygon"])
+    i=0
+    rgeometries=[]
+    while i < len(geometry):
+        rgeometries+=[geometry[i].GetBoundary()]
+        geometry[i].Destroy()
+        i+=1
+    outputResult(conf,outputs["Result"],rgeometries)
+    return 3
+
+def CentroidPy(conf,inputs,outputs):
+    geometry=extractInputs(inputs["InputPolygon"])
+    i=0
+    rgeometries=[]
+    while i < len(geometry):
+        if geometry[i].GetGeometryType()!=3:
+            geometry[i]=geometry[i].ConvexHull()
+        rgeometries+=[geometry[i].Centroid()]
+        geometry[i].Destroy()
+        i+=1
+    outputResult(conf,outputs["Result"],rgeometries)
+    return 3
+
+def ConvexHullPy(conf,inputs,outputs):
+    geometry=extractInputs(inputs["InputPolygon"])
+    i=0
+    rgeometries=[]
+    while i < len(geometry):
+        rgeometries+=[geometry[i].ConvexHull()]
+        geometry[i].Destroy()
+        i+=1
+    outputResult(conf,outputs["Result"],rgeometries)
+    return 3
+
+def BufferPy(conf,inputs,outputs):
+    try:
+        bdist=float(inputs["BufferDistance"]["value"])
+    except:
+        bdist=10
+    geometry=extractInputs(inputs["InputPolygon"])
+    i=0
+    rgeometries=[]
+    while i < len(geometry):
+        rgeometries+=[geometry[i].Buffer(bdist)]
+        geometry[i].Destroy()
+        i+=1
+    outputResult(conf,outputs["Result"],rgeometries)
+    i=0
+    return 3
+
+def UnionPy(conf,inputs,outputs):
+    geometry1=extractInputs(inputs["InputEntity1"])
+    geometry2=extractInputs(inputs["InputEntity2"])
+    rgeometries=[]
+    i=0
+    while i < len(geometry1):
+        j=0
+        while j < len(geometry2):
+            tres=geometry1[i].Union(geometry2[j])
+            if not(tres.IsEmpty()):
+                rgeometries+=[tres]
+            j+=1
+        geometry1[i].Destroy()
+        i+=1
+    i=0
+    while i < len(geometry2):
+        geometry2[i].Destroy()
+        i+=1
+    outputResult(conf,outputs["Result"],rgeometries)
+    return 3
+
+def IntersectionPy(conf,inputs,outputs):
+    geometry1=extractInputs(inputs["InputEntity1"])
+    geometry2=extractInputs(inputs["InputEntity2"])
+    rgeometries=[]
+    i=0
+    while i < len(geometry1):
+        j=0
+        while j < len(geometry2):
+            tres=geometry1[i].Intersection(geometry2[j])
+            if not(tres.IsEmpty()):
+                rgeometries+=[tres]
+            j+=1
+        geometry1[i].Destroy()
+        i+=1
+    i=0
+    while i < len(geometry2):
+        geometry2[i].Destroy()
+        i+=1
+    outputResult(conf,outputs["Result"],rgeometries)
+    return 3
+
+def DifferencePy(conf,inputs,outputs):
+    geometry1=extractInputs(inputs["InputEntity1"])
+    geometry2=extractInputs(inputs["InputEntity2"])
+    rgeometries=[]
+    i=0
+    while i < len(geometry1):
+        j=0
+        while j < len(geometry2):
+            tres=geometry1[i].Difference(geometry2[j])
+            if not(tres.IsEmpty()):
+                rgeometries+=[tres]
+            j+=1
+        geometry1[i].Destroy()
+        i+=1
+    i=0
+    while i < len(geometry2):
+        geometry2[i].Destroy()
+        i+=1
+    outputResult(conf,outputs["Result"],rgeometries)
+    return 3
+
+def SymDifferencePy(conf,inputs,outputs):
+    geometry1=extractInputs(inputs["InputEntity1"])
+    geometry2=extractInputs(inputs["InputEntity2"])
+    rgeometries=[]
+    i=0
+    while i < len(geometry1):
+        j=0
+        while j < len(geometry2):
+            rgeometries+=[geometry1[i].SymmetricDifference(geometry2[j])]
+            j+=1
+        geometry1[i].Destroy()
+        i+=1
+    i=0
+    while i < len(geometry2):
+        geometry2[i].Destroy()
+        i+=1
+    outputResult(conf,outputs["Result"],rgeometries)
+    return 3
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/Makefile	(revision 221)
@@ -0,0 +1,10 @@
+CFLAGS=-I../../../zoo-kernel/ -I./ `geos-config --cflags` `xml2-config --cflags`  `gdal-config --cflags`  -DLINUX_FREE_ISSUE #-DDEBUG
+# if JS_ENABLED flag is set to true in your ZOO-Kernel Makefile then you'll have
+# uncomment the following line
+# JS_LDFLAGS=../../../zoo-kernel/ulinet.o ../../../zoo-kernel/service_internal_js.o
+
+cgi-env/ogr_service.zo: service.c
+	g++ ${CFLAGS} -shared -fpic -o cgi-env/ogr_service.zo ./service.c ../../../zoo-kernel/service_internal.o ${JS_LDFLAGS} `xml2-config --libs` `gdal-config --libs` `geos-config --libs`
+
+clean:
+	rm -f cgi-env/ogr_service.zo
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Boundary.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Boundary.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Boundary.zcfg	(revision 221)
@@ -0,0 +1,60 @@
+[Boundary]
+ Title = Compute boundary.
+ Abstract = A new geometry object is created and returned containing the boundary of the geometry on which the method is invoked. 
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to compute boundary
+   Abstract = URI to a set of GML that describes the polygon.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The geometry created
+   Abstract = The geometry containing the boundary of the geometry on which the method is invoked.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     extension = xml
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+     extension = js
+    </Supported>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     extension = xml
+    </Supported>
+   </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Buffer.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Buffer.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Buffer.zcfg	(revision 221)
@@ -0,0 +1,76 @@
+[Buffer]
+ Title = Create a buffer around a polygon. 
+ Abstract = Create a buffer around a single polygon. Accepts the polygon as GML and provides GML output for the buffered feature. 
+ Profile = urn:ogc:wps:1.0.0:buffer
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to be buffered
+   Abstract = URI to a set of GML that describes the polygon.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     asReference = true	
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+    </Supported>
+   </ComplexData>
+  [BufferDistance]
+   Title = Buffer Distance
+   Abstract = Distance to be used to calculate buffer.
+   minOccurs = 0
+   maxOccurs = 1
+   <LiteralData>
+    DataType = float
+    <Default>
+     uom = meters
+     value = 10
+    </Default>
+    <Supported>
+     uom = feet
+    </Supported>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = Buffered Polygon
+   Abstract = GML stream describing the buffered polygon feature.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     extension = xml
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     extension = xml
+    </Supported>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+     extension = js
+    </Supported>
+   </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Centroid.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Centroid.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Centroid.zcfg	(revision 221)
@@ -0,0 +1,55 @@
+[Centroid]
+ Title = Get the centroid of a polygon. 
+ Abstract = Compute the geometry centroid.
+ Profile = urn:ogc:wps:1.0.0:centroid
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to get the centroid
+   Abstract = The centroid which is not necessarily within the geometry.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The Centroid
+   Abstract = JSON String / GML Entity of the centroid
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/point.xsd
+     extension = xml
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+     extension = js
+    </Supported>
+   </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/ConvexHull.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/ConvexHull.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/ConvexHull.zcfg	(revision 221)
@@ -0,0 +1,54 @@
+[ConvexHull]
+ Title = Compute convex hull.
+ Abstract = A new geometry object is created and returned containing the convex hull of the geometry on which the method is invoked.
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to compute convexhull
+   Abstract = URI to a set of GML that describes the polygon.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+   title = Mon test
+   </MetaData>
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The convex hull of the geometry
+   Abstract = The convex hull of the geometry
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     extension = xml
+    </Default>
+    <Supported>
+     mimeType = application/json
+     encoding = UTF-8
+     extension = js
+    </Supported>
+   </ComplexData>
+ </DataOutputs> 
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Difference.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Difference.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Difference.zcfg	(revision 221)
@@ -0,0 +1,75 @@
+[Difference]
+ Title = Compute difference. . 
+ Abstract = Generates a new geometry which is the region of this geometry with the region of the other geometry removed.
+ Profile = urn:ogc:wps:1.0.0:difference
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The difference between two geometries
+   Abstract = The difference between the two geometries.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+    <ComplexData>
+     <Default>
+      mimeType = text/xml
+      encoding = UTF-8
+      schema = http://fooa/gml/3.1.0/polygon.xsd
+      extension = xml
+     </Default>
+     <Supported>
+      mimeType = application/json
+      encoding = UTF-8
+      extension = js
+     </Supported>
+    </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Distance.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Distance.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Distance.zcfg	(revision 221)
@@ -0,0 +1,71 @@
+[Distance]
+ Title = Compute the distance between two geometries
+ Abstract = Compute the distance between two geometries
+ Profile = urn:ogc:wps:1.0.0:buffer
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Distance]
+   Title = The distance between two geometries
+   Abstract = The shortest distance between the two geometries.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+    <LiteralData>
+     DataType = float
+     <Default>
+      uom = meters
+     </Default>
+     <Supported>
+      uom = feet
+     </Supported>
+    </LiteralData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/GetArea.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/GetArea.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/GetArea.zcfg	(revision 221)
@@ -0,0 +1,50 @@
+[GetArea]
+ Title = Compute geometry area.
+ Abstract = Computes the area for a geometry
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputPolygon]
+   Title = Polygon to compute are
+   Abstract = URI to a set of GML that describes the polygon.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+   title = Mon test
+   </MetaData>
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Area]
+   Title = Computed Area
+   Abstract = The Computed Area Value
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <LiteralData>
+    DataType = float
+    <Default>
+     uom = degree
+    </Default>
+    <Supported>
+     uom = meter
+    </Supported>
+   </LiteralData>
+ </DataOutputs> 
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Intersection.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Intersection.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Intersection.zcfg	(revision 221)
@@ -0,0 +1,75 @@
+[Intersection]
+ Title = Compute intersection. 
+ Abstract = Generates a new geometry which is the region of intersection of the two geometries operated on.
+ Profile = urn:ogc:wps:1.0.0:union
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = Intersection of geometries
+   Abstract = A new geometry representing the intersection or NULL if there is no intersection or an error occurs.
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+    <ComplexData>
+     <Default>
+      mimeType = text/xml
+      encoding = UTF-8
+      schema = http://fooa/gml/3.1.0/polygon.xsd
+      extension = xml
+     </Default>
+     <Supported>
+      mimeType = application/json
+      encoding = UTF-8
+      extension = js
+     </Supported>
+    </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/SymDifference.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/SymDifference.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/SymDifference.zcfg	(revision 221)
@@ -0,0 +1,75 @@
+[SymDifference]
+ Title = Compute symmetric difference. 
+ Abstract = Generates a new geometry which is the symmetric difference of this geometry and the other geometry.
+ Profile = urn:ogc:wps:1.0.0:symmetricdifference
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The resulting geometry
+   Abstract = The symmetric difference of two geometries
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+    <ComplexData>
+     <Default>
+      mimeType = text/xml
+      schema = http://fooa/gml/3.1.0/polygon.xsd
+      encoding = UTF-8
+      extension = xml
+     </Default>
+     <Supported>
+      mimeType = application/json
+      encoding = UTF-8
+      extension = js
+     </Supported>
+    </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Union.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Union.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/cgi-env/Union.zcfg	(revision 221)
@@ -0,0 +1,75 @@
+[Union]
+ Title = Compute union. 
+ Abstract = Generates a new geometry which is the region of union of the two geometries operated on.
+ Profile = urn:ogc:wps:1.0.0:union
+ processVersion = 2
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = ogr_service.zo
+ serviceType = C
+ <MetaData>
+   title = Demo
+ </MetaData>
+ <DataInputs>
+  [InputEntity1]
+   Title = the first geometry 
+   Abstract = the first geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+  [InputEntity2]
+   Title = the other geometry
+   Abstract = the other geometry to compare against.
+   minOccurs = 1
+   maxOccurs = 1
+   <MetaData>
+    title = Mon test  
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+     encoding = UTF-8
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = base64
+     schema = http://fooa/gml/3.1.0/polygon.xsd
+    </Supported>
+   </ComplexData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = The union of two geometries
+   Abstract = The geometry representing the union of the two geometries.
+   <MetaData>
+    title = Mon test  
+   </MetaData>
+    <ComplexData>
+     <Default>
+      mimeType = text/xml
+      schema = http://fooa/gml/3.1.0/polygon.xsd
+      encoding = UTF-8
+      extension = xml
+     </Default>
+     <Supported>
+      mimeType = application/json
+      encoding = UTF-8
+      extension = js
+     </Supported>
+    </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/fr_FR.utf8.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/fr_FR.utf8.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/fr_FR.utf8.po	(revision 221)
@@ -0,0 +1,320 @@
+# French translations for PACKAGE package.
+# Copyright (C) 2010 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# root <gerald.fenoy@geolabs.fr>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: zoo-services\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-28 12:52+0200\n"
+"PO-Revision-Date: 2010-09-28 12:55+0200\n"
+"Last-Translator: root <gerald.fenoy@geolabs.fr>\n"
+"Language-Team: French\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: locale/.cache/my_service_string_to_translate.c:1
+msgid "Compute boundary."
+msgstr "Calcul le contour."
+
+#: locale/.cache/my_service_string_to_translate.c:2
+msgid "Polygon to compute boundary"
+msgstr "Le polygon à utiliser pour calculer le contour"
+
+#: locale/.cache/my_service_string_to_translate.c:3
+msgid "The geometry created"
+msgstr "La géométrie créée"
+
+#: locale/.cache/my_service_string_to_translate.c:4
+msgid ""
+"A new geometry object is created and returned containing the boundary of the "
+"geometry on which the method is invoked. "
+msgstr ""
+"Un nouvel objet géographique contenant le contour de la géométrie passée en "
+"paramètre est créé et retourné. "
+
+#: locale/.cache/my_service_string_to_translate.c:5
+#: locale/.cache/my_service_string_to_translate.c:12
+#: locale/.cache/my_service_string_to_translate.c:25
+#: locale/.cache/my_service_string_to_translate.c:47
+#: locale/.cache/my_service_string_to_translate.c:62
+msgid "URI to a set of GML that describes the polygon."
+msgstr "Le lien vers un fichier GML décrivant un polygone."
+
+#: locale/.cache/my_service_string_to_translate.c:6
+msgid ""
+"The geometry containing the boundary of the geometry on which the method is "
+"invoked."
+msgstr ""
+"La géométrie contenant le contour de la géométrie passée en paramètre."
+
+#: locale/.cache/my_service_string_to_translate.c:7
+msgid "Create a buffer around a polygon. "
+msgstr "Crée une zone tampon atour du polygone."
+
+#: locale/.cache/my_service_string_to_translate.c:8
+msgid "Polygon to be buffered"
+msgstr "Le Polygone à utiliser"
+
+#: locale/.cache/my_service_string_to_translate.c:9
+msgid "Buffer Distance"
+msgstr "La distance pour créer la zone tampon"
+
+#: locale/.cache/my_service_string_to_translate.c:10
+msgid "Buffered Polygon"
+msgstr "La zone tampon correspondant au polygone "
+
+#: locale/.cache/my_service_string_to_translate.c:11
+msgid ""
+"Create a buffer around a single polygon. Accepts the polygon as GML and "
+"provides GML output for the buffered feature. "
+msgstr ""
+"Crée une zone tampon autour d'un polygone. Prend en entrée un GML et fournit une "
+"chaîne GeoJSON de la zone tampon. "
+
+#: locale/.cache/my_service_string_to_translate.c:13
+msgid "Distance to be used to calculate buffer."
+msgstr "La distance à utiliser pour créer la zone tampon."
+
+#: locale/.cache/my_service_string_to_translate.c:14
+msgid "GML stream describing the buffered polygon feature."
+msgstr "Une chaîne GeoJSON contenant la zone tampon."
+
+#: locale/.cache/my_service_string_to_translate.c:15
+msgid "Get the centroid of a polygon. "
+msgstr "Renvoit le centroid d'un polygone"
+
+#: locale/.cache/my_service_string_to_translate.c:16
+msgid "Polygon to get the centroid"
+msgstr "Le polygone à utiliser pour calculer le centroid"
+
+#: locale/.cache/my_service_string_to_translate.c:17
+msgid "The Centroid"
+msgstr "Le centroid"
+
+#: locale/.cache/my_service_string_to_translate.c:18
+msgid "Compute the geometry centroid."
+msgstr "Calcul le centroid d'une géométrie"
+
+#: locale/.cache/my_service_string_to_translate.c:19
+msgid "The centroid which is not necessarily within the geometry."
+msgstr "Le centroid qui n'est pas nécessairement dans la géométrie."
+
+#: locale/.cache/my_service_string_to_translate.c:20
+msgid "JSON String / GML Entity of the centroid"
+msgstr "La chaîne GeoJSON ou l'entité GML du centroid"
+
+#: locale/.cache/my_service_string_to_translate.c:21
+msgid "Compute convex hull."
+msgstr "Calcul de la partie convexe"
+
+#: locale/.cache/my_service_string_to_translate.c:22
+msgid "Polygon to compute convexhull"
+msgstr "Polygone pour calculer la partie convexe"
+
+#: locale/.cache/my_service_string_to_translate.c:23
+#: locale/.cache/my_service_string_to_translate.c:26
+msgid "The convex hull of the geometry"
+msgstr "La partie convexe de la géométrie"
+
+#: locale/.cache/my_service_string_to_translate.c:24
+msgid ""
+"A new geometry object is created and returned containing the convex hull of "
+"the geometry on which the method is invoked."
+msgstr ""
+"Un nouvel objet géographique est créé et retourné. Il contient la partie convexe de la "
+"géométrie sur laquelle on a appliqué la méthode."
+
+#: locale/.cache/my_service_string_to_translate.c:27
+msgid "Compute difference. . "
+msgstr "Calcul de la différence."
+
+#: locale/.cache/my_service_string_to_translate.c:28
+#: locale/.cache/my_service_string_to_translate.c:36
+#: locale/.cache/my_service_string_to_translate.c:50
+#: locale/.cache/my_service_string_to_translate.c:66
+#: locale/.cache/my_service_string_to_translate.c:74
+msgid "the first geometry "
+msgstr "La première géométrie"
+
+#: locale/.cache/my_service_string_to_translate.c:29
+#: locale/.cache/my_service_string_to_translate.c:37
+#: locale/.cache/my_service_string_to_translate.c:51
+#: locale/.cache/my_service_string_to_translate.c:67
+#: locale/.cache/my_service_string_to_translate.c:75
+msgid "the other geometry"
+msgstr "L'autre géométrie"
+
+#: locale/.cache/my_service_string_to_translate.c:30
+msgid "The difference between two geometries"
+msgstr "La différence entre les deux géométries"
+
+#: locale/.cache/my_service_string_to_translate.c:31
+msgid ""
+"Generates a new geometry which is the region of this geometry with the "
+"region of the other geometry removed."
+msgstr ""
+"Génère une nouvelle géométrie qui contient la région de la géométrie dont on a "
+"supprimé l'autre géométrie."
+
+
+#: locale/.cache/my_service_string_to_translate.c:32
+#: locale/.cache/my_service_string_to_translate.c:40
+#: locale/.cache/my_service_string_to_translate.c:54
+#: locale/.cache/my_service_string_to_translate.c:70
+#: locale/.cache/my_service_string_to_translate.c:78
+msgid "the first geometry to compare against."
+msgstr "La première géométrie utilisée pour la comparaison"
+
+#: locale/.cache/my_service_string_to_translate.c:33
+#: locale/.cache/my_service_string_to_translate.c:41
+#: locale/.cache/my_service_string_to_translate.c:55
+#: locale/.cache/my_service_string_to_translate.c:71
+#: locale/.cache/my_service_string_to_translate.c:79
+msgid "the other geometry to compare against."
+msgstr "L'autre géométrie à comparer."
+
+#: locale/.cache/my_service_string_to_translate.c:34
+msgid "The difference between the two geometries."
+msgstr "La différence entre les deux géométries."
+
+#: locale/.cache/my_service_string_to_translate.c:35
+msgid "Calcul la distance entre deux entites geographique. "
+msgstr "Calcul de la distance entre deux objets géographiques."
+
+#: locale/.cache/my_service_string_to_translate.c:38
+msgid "The distance between two geometries"
+msgstr "La distance entre deux géométries"
+
+#: locale/.cache/my_service_string_to_translate.c:39
+msgid "Calcul de la distance entre deux entites geographique. "
+msgstr "Calcul de la distance entre deux entités géographiques."
+
+#: locale/.cache/my_service_string_to_translate.c:42
+msgid "The shortest distance between the two geometries."
+msgstr "La plus courte distance entre de géométries."
+
+#: locale/.cache/my_service_string_to_translate.c:43
+msgid "Compute geometry area."
+msgstr "Calcul l'aire de la géométrie."
+
+#: locale/.cache/my_service_string_to_translate.c:44
+msgid "Polygon to compute are"
+msgstr "Le polygone à utiliser"
+
+#: locale/.cache/my_service_string_to_translate.c:45
+msgid "Computed Area"
+msgstr "L'aire calculée"
+
+#: locale/.cache/my_service_string_to_translate.c:46
+msgid "Computes the area for a geometry"
+msgstr "Calcul de l'aire d'une géométrie"
+
+#: locale/.cache/my_service_string_to_translate.c:48
+msgid "The Computed Area Value"
+msgstr "La valeur de l'aire calculée"
+
+#: locale/.cache/my_service_string_to_translate.c:49
+msgid "Compute intersection. "
+msgstr "Calcul de l'intersection."
+
+#: locale/.cache/my_service_string_to_translate.c:52
+msgid "Intersection of geometries"
+msgstr "L'intersection des géométries."
+
+#: locale/.cache/my_service_string_to_translate.c:53
+msgid ""
+"Generates a new geometry which is the region of intersection of the two "
+"geometries operated on."
+msgstr ""
+"Crée une nouvelle géométrie qui représente l'intersection des deux géométries avec "
+"lesquelles le service est utilisé."
+
+#: locale/.cache/my_service_string_to_translate.c:56
+msgid ""
+"A new geometry representing the intersection or NULL if there is no "
+"intersection or an error occurs."
+msgstr ""
+"Une nouvelle géométrie représentant l'intersection ou NULL si l'intersection est vide "
+"si une erreur s'est produite."
+
+#: locale/.cache/my_service_string_to_translate.c:57
+msgid "Douglas-Peucker like algorithm"
+msgstr "Algorythme de simplification de type Douglas-Peucker"
+
+#: locale/.cache/my_service_string_to_translate.c:58
+msgid "Polygon to simplify"
+msgstr "Le polygone à simplifier"
+
+#: locale/.cache/my_service_string_to_translate.c:59
+msgid "Tolerance to use."
+msgstr "La tolérance à utliser."
+
+#: locale/.cache/my_service_string_to_translate.c:60
+msgid "The simplified geometry"
+msgstr "La géométrie simplifiée"
+
+#: locale/.cache/my_service_string_to_translate.c:61
+msgid ""
+"Simplifies a geometry, ensuring that the result is a valid geometry having "
+"the same dimension and number of components as the input. The simplification "
+"uses a maximum distance difference algorithm similar to the one used in the "
+"Douglas-Peucker algorithm."
+msgstr ""
+"Simplifie une géométrie, en s'assurant que le résultat soit une géométrie valide "
+"ayant la même dimension et le même nombre de composants que la donnée d'entrée. "
+"La simplification utilise un algorythme similaire à l'algorythme Douglas-Peucker."
+
+#: locale/.cache/my_service_string_to_translate.c:63
+msgid "The approximation tolerance to use."
+msgstr "La valeur approximative de la tolérance à utiliser."
+
+#: locale/.cache/my_service_string_to_translate.c:64
+msgid ""
+"The result has the same number of shells and holes (rings) as the input, in "
+"the same order. The result rings touch at no more than the number of "
+"touching point in the input (although they may touch at fewer points)."
+msgstr ""
+"Le résultat a le même nombre composant que l'entrée, dans le même ordre. "
+
+#: locale/.cache/my_service_string_to_translate.c:65
+msgid "Compute symmetric difference. "
+msgstr "Calcul de la différence symétrique."
+
+#: locale/.cache/my_service_string_to_translate.c:68
+msgid "The resulting geometry"
+msgstr "La géométrie résultat"
+
+#: locale/.cache/my_service_string_to_translate.c:69
+msgid ""
+"Generates a new geometry which is the symmetric difference of this geometry "
+"and the other geometry."
+msgstr ""
+"Crée une nouvelle géométrie qui est la difference symétrique des géométries."
+
+#: locale/.cache/my_service_string_to_translate.c:72
+msgid "The symmetric difference of two geometries"
+msgstr "La différence symmétrique de deux géométries"
+
+#: locale/.cache/my_service_string_to_translate.c:73
+msgid "Compute union. "
+msgstr "Cacul l'union."
+
+#: locale/.cache/my_service_string_to_translate.c:76
+msgid "The union of two geometries"
+msgstr "L'union de deux géométries"
+
+#: locale/.cache/my_service_string_to_translate.c:77
+msgid ""
+"Generates a new geometry which is the region of union of the two geometries "
+"operated on."
+msgstr ""
+"Génère une nouvelle géométrie qui est l'union des deux géométrie passées en "
+"paramètre."
+
+#: locale/.cache/my_service_string_to_translate.c:80
+msgid "The geometry representing the union of the two geometries."
+msgstr "La géométrie représentant l'union des deux géométries."
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/ja_JP.utf8.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/ja_JP.utf8.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/ja_JP.utf8.po	(revision 221)
@@ -0,0 +1,308 @@
+# Japanese translations for PACKAGE package.
+# Copyright (C) 2010 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the ZOO Services package.
+# Daisuke YOSHIDA <yoshida@la.tezuka-gu.ac.jp>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: zoo-services\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-28 12:52+0200\n"
+"PO-Revision-Date: 2010-09-28 12:55+0200\n"
+"Last-Translator: Daisuke YOSHIDA <yoshida@la.tezuka-gu.ac.jp>\n"
+"Language-Team: Japanese\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: locale/.cache/my_service_string_to_translate.c:1
+msgid "Compute boundary."
+msgstr "境界線を計算．"
+
+#: locale/.cache/my_service_string_to_translate.c:2
+msgid "Polygon to compute boundary"
+msgstr "境界線を計算するためのポリゴン．"
+
+#: locale/.cache/my_service_string_to_translate.c:3
+msgid "The geometry created"
+msgstr "ジオメトリが作成される．"
+
+#: locale/.cache/my_service_string_to_translate.c:4
+msgid ""
+"A new geometry object is created and returned containing the boundary of the "
+"geometry on which the method is invoked. "
+msgstr "新しいジオメトリオブジェクトは作成され，メソッド上に返答された境界面を含むジオメトリが，呼び出される．"
+
+
+#: locale/.cache/my_service_string_to_translate.c:5
+#: locale/.cache/my_service_string_to_translate.c:12
+#: locale/.cache/my_service_string_to_translate.c:25
+#: locale/.cache/my_service_string_to_translate.c:47
+#: locale/.cache/my_service_string_to_translate.c:62
+msgid "URI to a set of GML that describes the polygon."
+msgstr "ポリゴンが記述されているGMLセットへのURI．"
+
+#: locale/.cache/my_service_string_to_translate.c:6
+msgid ""
+"The geometry containing the boundary of the geometry on which the method is "
+"invoked."
+msgstr "メソッド上の地質境界面を含んでいるジオメトリを起動．"
+
+
+#: locale/.cache/my_service_string_to_translate.c:7
+msgid "Create a buffer around a polygon. "
+msgstr "ポリゴン周辺のバッファを作成．"
+
+#: locale/.cache/my_service_string_to_translate.c:8
+msgid "Polygon to be buffered"
+msgstr "バッファリングするポリゴン．"
+
+#: locale/.cache/my_service_string_to_translate.c:9
+msgid "Buffer Distance"
+msgstr "バッファ距離．"
+
+#: locale/.cache/my_service_string_to_translate.c:10
+msgid "Buffered Polygon"
+msgstr "バッファリングされたポリゴン． "
+
+#: locale/.cache/my_service_string_to_translate.c:11
+msgid ""
+"Create a buffer around a single polygon. Accepts the polygon as GML and "
+"provides GML output for the buffered feature. "
+msgstr "シングルポリゴン周辺のバッファの作成．GMLとしてポリゴンを許可し，バッファリングされたフィーチャーのためのGMLを提供．"
+
+#: locale/.cache/my_service_string_to_translate.c:13
+msgid "Distance to be used to calculate buffer."
+msgstr "バッファを計算するために使われる距離．"
+
+#: locale/.cache/my_service_string_to_translate.c:14
+msgid "GML stream describing the buffered polygon feature."
+msgstr "バッファリングされたポリゴンフィーチャーを記述するGML．"
+
+#: locale/.cache/my_service_string_to_translate.c:15
+msgid "Get the centroid of a polygon. "
+msgstr "ポリゴンの中心点を取得．"
+
+#: locale/.cache/my_service_string_to_translate.c:16
+msgid "Polygon to get the centroid"
+msgstr "中心点を取得するためのポリゴン．"
+
+#: locale/.cache/my_service_string_to_translate.c:17
+msgid "The Centroid"
+msgstr "中心点．"
+
+#: locale/.cache/my_service_string_to_translate.c:18
+msgid "Compute the geometry centroid."
+msgstr "ジオメトリの中心点を計算．"
+
+#: locale/.cache/my_service_string_to_translate.c:19
+msgid "The centroid which is not necessarily within the geometry."
+msgstr "ジオメトリに含まれる重要ではない中心点．"
+
+#: locale/.cache/my_service_string_to_translate.c:20
+msgid "JSON String / GML Entity of the centroid"
+msgstr "中心点のJSON文字列／GMLエンティティ．"
+
+#: locale/.cache/my_service_string_to_translate.c:21
+msgid "Compute convex hull."
+msgstr "凸包を計算．"
+
+#: locale/.cache/my_service_string_to_translate.c:22
+msgid "Polygon to compute convex hull"
+msgstr "凸包を計算するポリゴン．"
+
+#: locale/.cache/my_service_string_to_translate.c:23
+#: locale/.cache/my_service_string_to_translate.c:26
+msgid "The convex hull of the geometry"
+msgstr "ジオメトリの凸包．"
+
+#: locale/.cache/my_service_string_to_translate.c:24
+msgid ""
+"A new geometry object is created and returned containing the convex hull of "
+"the geometry on which the method is invoked."
+msgstr "新しいジオメトリオブジェクトは作成されメッソド上に返答され，凸包を含むジオメトリが呼び出される．"
+
+
+#: locale/.cache/my_service_string_to_translate.c:27
+msgid "Compute difference. . "
+msgstr "差分の計算．"
+
+#: locale/.cache/my_service_string_to_translate.c:28
+#: locale/.cache/my_service_string_to_translate.c:36
+#: locale/.cache/my_service_string_to_translate.c:50
+#: locale/.cache/my_service_string_to_translate.c:66
+#: locale/.cache/my_service_string_to_translate.c:74
+msgid "the first geometry "
+msgstr "最初のジオメトリ．"
+
+#: locale/.cache/my_service_string_to_translate.c:29
+#: locale/.cache/my_service_string_to_translate.c:37
+#: locale/.cache/my_service_string_to_translate.c:51
+#: locale/.cache/my_service_string_to_translate.c:67
+#: locale/.cache/my_service_string_to_translate.c:75
+msgid "the other geometry"
+msgstr "その他のジオメトリ．"
+
+#: locale/.cache/my_service_string_to_translate.c:30
+msgid "The difference between two geometries"
+msgstr "２つのジオメトリの差。"
+
+#: locale/.cache/my_service_string_to_translate.c:31
+msgid ""
+"Generates a new geometry which is the region of this geometry with the "
+"region of the other geometry removed."
+msgstr "他のジオメトリを削除した領域に，このジオメトリの領域の新しいジオメトリ"
+"を作成"
+
+
+#: locale/.cache/my_service_string_to_translate.c:32
+#: locale/.cache/my_service_string_to_translate.c:40
+#: locale/.cache/my_service_string_to_translate.c:54
+#: locale/.cache/my_service_string_to_translate.c:70
+#: locale/.cache/my_service_string_to_translate.c:78
+msgid "the first geometry to compare against."
+msgstr "対比比較する最初のジオメトリ．"
+
+#: locale/.cache/my_service_string_to_translate.c:33
+#: locale/.cache/my_service_string_to_translate.c:41
+#: locale/.cache/my_service_string_to_translate.c:55
+#: locale/.cache/my_service_string_to_translate.c:71
+#: locale/.cache/my_service_string_to_translate.c:79
+msgid "the other geometry to compare against."
+msgstr "対比比較するその他のジオメトリ．"
+
+#: locale/.cache/my_service_string_to_translate.c:34
+msgid "The difference between the two geometries."
+msgstr "2つのジオメトリの差分．"
+
+#: locale/.cache/my_service_string_to_translate.c:35
+msgid "Calcul la distance entre deux entites geographique. "
+msgstr "Calcul de la distance entre deux objets géographiques."
+
+#: locale/.cache/my_service_string_to_translate.c:38
+msgid "The distance between two geometries"
+msgstr "２つのジオメトリの距離．"
+
+#: locale/.cache/my_service_string_to_translate.c:39
+msgid "Calcul de la distance entre deux entites geographique. "
+msgstr "Calcul de la distance entre deux entités géographiques."
+
+#: locale/.cache/my_service_string_to_translate.c:42
+msgid "The shortest distance between the two geometries."
+msgstr "二つのジオメトリの最短距離."
+
+#: locale/.cache/my_service_string_to_translate.c:43
+msgid "Compute geometry area."
+msgstr "ジオメトリ面積の計算."
+
+#: locale/.cache/my_service_string_to_translate.c:44
+msgid "Polygon to compute area"
+msgstr "面積計算するためのポリゴン．"
+
+#: locale/.cache/my_service_string_to_translate.c:45
+msgid "Computed Area"
+msgstr "面積計算．"
+
+#: locale/.cache/my_service_string_to_translate.c:46
+msgid "Computes the area for a geometry"
+msgstr "ジオメトリの面積を計算する．"
+
+#: locale/.cache/my_service_string_to_translate.c:48
+msgid "The Computed Area Value"
+msgstr "計算された面積の値．"
+
+#: locale/.cache/my_service_string_to_translate.c:49
+msgid "Compute intersection. "
+msgstr "交点を計算する．"
+
+#: locale/.cache/my_service_string_to_translate.c:52
+msgid "Intersection of geometries"
+msgstr "ジオメトリの交点．"
+
+#: locale/.cache/my_service_string_to_translate.c:53
+msgid ""
+"Generates a new geometry which is the region of intersection of the two "
+"geometries operated on."
+msgstr "操作された２つのジオメトリの交点の領域の新しいジオメトリを作成．"
+
+
+#: locale/.cache/my_service_string_to_translate.c:56
+msgid ""
+"A new geometry representing the intersection or NULL if there is no "
+"intersection or an error occurs."
+msgstr "もし交点、またはエラーが起こっていない場合、交点またはNULLを表す新しいジオメトリ．"
+
+#: locale/.cache/my_service_string_to_translate.c:57
+msgid "Douglas-Peucker like algorithm"
+msgstr "Douglas-Peucker的アルゴリズム．"
+
+#: locale/.cache/my_service_string_to_translate.c:58
+msgid "Polygon to simplify"
+msgstr "単純化するためのポリゴン．"
+
+#: locale/.cache/my_service_string_to_translate.c:59
+msgid "Tolerance to use."
+msgstr "使用する許容値．"
+
+#: locale/.cache/my_service_string_to_translate.c:60
+msgid "The simplified geometry"
+msgstr "単純化されたジオメトリ．"
+
+#: locale/.cache/my_service_string_to_translate.c:61
+msgid ""
+"Simplifies a geometry, ensuring that the result is a valid geometry having "
+"the same dimension and number of components as the input. The simplification "
+"uses a maximum distance difference algorithm similar to the one used in the "
+"Douglas-Peucker algorithm."
+msgstr ""
+"ジオメトリを単純化"
+"結果がインプットとして同じ次元とコンポーネント数が等しい有効なジオメトリか確認．"
+"単純化では，Douglas-Peuckerアルゴリズムと類似した最大距離を使うアルゴリズムを使用．"
+
+#: locale/.cache/my_service_string_to_translate.c:63
+msgid "The approximation tolerance to use."
+msgstr "使用する許容値の概要．"
+
+#: locale/.cache/my_service_string_to_translate.c:64
+msgid ""
+"The result has the same number of shells and holes (rings) as the input, in "
+"the same order. The result rings touch at no more than the number of "
+"touching point in the input (although they may touch at fewer points)."
+msgstr "結果はインプットとしてshells とholes (rings）が共に等しい数，順序になる．"
+"結果のリングはインプットの中のタッチングポイントの数まで達しない．（しかし，より少ないポイントをタッチするかもしれない．"
+#: locale/.cache/my_service_string_to_translate.c:65
+msgid "Compute symmetric difference. "
+msgstr "対照的な差を計算．"
+
+#: locale/.cache/my_service_string_to_translate.c:68
+msgid "The resulting geometry"
+msgstr "ジオメトリの結果．"
+
+#: locale/.cache/my_service_string_to_translate.c:69
+msgid ""
+"Generates a new geometry which is the symmetric difference of this geometry "
+"and the other geometry."
+msgstr "このジオメトリと他のジオメトリで，対照的に異なった新しいジオメトリの作成"
+
+#: locale/.cache/my_service_string_to_translate.c:72
+msgid "The symmetric difference of two geometries"
+msgstr "2つのジオメトリの対照的違い．"
+
+#: locale/.cache/my_service_string_to_translate.c:73
+msgid "Compute union. "
+msgstr "結合の計算."
+
+#: locale/.cache/my_service_string_to_translate.c:76
+msgid "The union of two geometries"
+msgstr "2つのジオメトリの結合"
+
+#: locale/.cache/my_service_string_to_translate.c:77
+msgid ""
+"Generates a new geometry which is the region of union of the two geometries "
+"operated on."
+msgstr "操作された２つのジオメトリを結合した領域の新しいジオメトリの作成"
+
+#: locale/.cache/my_service_string_to_translate.c:80
+msgid "The geometry representing the union of the two geometries."
+msgstr "２つのジオメトリの結合を表すジオメトリ"
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/messages.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/messages.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/locale/po/messages.po	(revision 221)
@@ -0,0 +1,299 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: zoo-services\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-28 12:52+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: GÃ©rald Fenoy <gerald.fenoy@geolabs.fr>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: locale/.cache/my_service_string_to_translate.c:1
+msgid "Compute boundary."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:2
+msgid "Polygon to compute boundary"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:3
+msgid "The geometry created"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:4
+msgid ""
+"A new geometry object is created and returned containing the boundary of the "
+"geometry on which the method is invoked. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:5
+#: locale/.cache/my_service_string_to_translate.c:12
+#: locale/.cache/my_service_string_to_translate.c:25
+#: locale/.cache/my_service_string_to_translate.c:47
+#: locale/.cache/my_service_string_to_translate.c:62
+msgid "URI to a set of GML that describes the polygon."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:6
+msgid ""
+"The geometry containing the boundary of the geometry on which the method is "
+"invoked."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:7
+msgid "Create a buffer around a polygon. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:8
+msgid "Polygon to be buffered"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:9
+msgid "Buffer Distance"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:10
+msgid "Buffered Polygon"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:11
+msgid ""
+"Create a buffer around a single polygon. Accepts the polygon as GML and "
+"provides GML output for the buffered feature. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:13
+msgid "Distance to be used to calculate buffer."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:14
+msgid "GML stream describing the buffered polygon feature."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:15
+msgid "Get the centroid of a polygon. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:16
+msgid "Polygon to get the centroid"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:17
+msgid "The Centroid"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:18
+msgid "Compute the geometry centroid."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:19
+msgid "The centroid which is not necessarily within the geometry."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:20
+msgid "JSON String / GML Entity of the centroid"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:21
+msgid "Compute convex hull."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:22
+msgid "Polygon to compute convexhull"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:23
+#: locale/.cache/my_service_string_to_translate.c:26
+msgid "The convex hull of the geometry"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:24
+msgid ""
+"A new geometry object is created and returned containing the convex hull of "
+"the geometry on which the method is invoked."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:27
+msgid "Compute difference. . "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:28
+#: locale/.cache/my_service_string_to_translate.c:36
+#: locale/.cache/my_service_string_to_translate.c:50
+#: locale/.cache/my_service_string_to_translate.c:66
+#: locale/.cache/my_service_string_to_translate.c:74
+msgid "the first geometry "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:29
+#: locale/.cache/my_service_string_to_translate.c:37
+#: locale/.cache/my_service_string_to_translate.c:51
+#: locale/.cache/my_service_string_to_translate.c:67
+#: locale/.cache/my_service_string_to_translate.c:75
+msgid "the other geometry"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:30
+msgid "The difference between two geometries"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:31
+msgid ""
+"Generates a new geometry which is the region of this geometry with the "
+"region of the other geometry removed."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:32
+#: locale/.cache/my_service_string_to_translate.c:40
+#: locale/.cache/my_service_string_to_translate.c:54
+#: locale/.cache/my_service_string_to_translate.c:70
+#: locale/.cache/my_service_string_to_translate.c:78
+msgid "the first geometry to compare against."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:33
+#: locale/.cache/my_service_string_to_translate.c:41
+#: locale/.cache/my_service_string_to_translate.c:55
+#: locale/.cache/my_service_string_to_translate.c:71
+#: locale/.cache/my_service_string_to_translate.c:79
+msgid "the other geometry to compare against."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:34
+msgid "The difference between the two geometries."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:35
+msgid "Calcul la distance entre deux entites geographique. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:38
+msgid "The distance between two geometries"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:39
+msgid "Calcul de la distance entre deux entites geographique. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:42
+msgid "The shortest distance between the two geometries."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:43
+msgid "Compute geometry area."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:44
+msgid "Polygon to compute are"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:45
+msgid "Computed Area"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:46
+msgid "Computes the area for a geometry"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:48
+msgid "The Computed Area Value"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:49
+msgid "Compute intersection. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:52
+msgid "Intersection of geometries"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:53
+msgid ""
+"Generates a new geometry which is the region of intersection of the two "
+"geometries operated on."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:56
+msgid ""
+"A new geometry representing the intersection or NULL if there is no "
+"intersection or an error occurs."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:57
+msgid "Douglas-Peucker like algorithm"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:58
+msgid "Polygon to simplify"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:59
+msgid "Tolerance to use."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:60
+msgid "The simplified geometry"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:61
+msgid ""
+"Simplifies a geometry, ensuring that the result is a valid geometry having "
+"the same dimension and number of components as the input. The simplification "
+"uses a maximum distance difference algorithm similar to the one used in the "
+"Douglas-Peucker algorithm."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:63
+msgid "The approximation tolerance to use."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:64
+msgid ""
+"The result has the same number of shells and holes (rings) as the input, in "
+"the same order. The result rings touch at no more than the number of "
+"touching point in the input (although they may touch at fewer points)."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:65
+msgid "Compute symmetric difference. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:68
+msgid "The resulting geometry"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:69
+msgid ""
+"Generates a new geometry which is the symmetric difference of this geometry "
+"and the other geometry."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:72
+msgid "The symmetric difference of two geometries"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:73
+msgid "Compute union. "
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:76
+msgid "The union of two geometries"
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:77
+msgid ""
+"Generates a new geometry which is the region of union of the two geometries "
+"operated on."
+msgstr ""
+
+#: locale/.cache/my_service_string_to_translate.c:80
+msgid "The geometry representing the union of the two geometries."
+msgstr ""
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/makefile.vc
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/makefile.vc	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/makefile.vc	(revision 221)
@@ -0,0 +1,12 @@
+ZOODIR=../../../zoo-kernel
+THIRDSDIR=../../../thirds
+!INCLUDE $(ZOODIR)/nmake.opt
+CFLAGS=-I$(GEODIR)/include -I$(TPATH)/include -I$(ZOODIR) -I./ -DLINUX_FREE_ISSUE -DWIN32 #-DDEBUG
+CPP=cl /TP 
+
+cgi-env/ogr_service.zo: service.c
+	$(CPP) $(CFLAGS) /c service.c
+	link /dll /out:cgi-env/ogr_service.zo ../../../zoo-kernel/service_internal.obj ./service.obj -L$(TOOLS)/lib/libssl32.dll.a $(GEODIR)/lib/libxml2.lib $(GEODIR)/lib/gdal_i.lib $(GEODIR)/lib/geos_c_i.lib $(TPATH)/lib/libeay32.dll.a $(TPATH)/lib/libcrypto.a $(TPATH)/lib/libssl32.dll.a $(LIBINTL_CPATH)/lib/libintl.lib 
+
+clean:
+	erase cgi-env\ogr_service.*
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/service.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/service.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/base-vect-ops/service.c	(revision 221)
@@ -0,0 +1,621 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright 2008-2009 GeoLabs SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "cpl_conv.h"
+#include "ogr_api.h"
+#include "ogr_geometry.h"
+#include "geos_c.h"
+#include "service.h"
+#include "service_internal.h"
+
+extern "C" {
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+
+  void printExceptionReportResponse(maps*,map*);
+  char *base64(const char *input, int length);
+
+  OGRGeometryH createGeometryFromGML(maps* conf,char* inputStr){
+    xmlInitParser();
+    xmlDocPtr doc = xmlParseMemory(inputStr,strlen(inputStr));
+    xmlChar *xmlbuff;
+    int buffersize;
+    xmlXPathContextPtr xpathCtx;
+    xmlXPathObjectPtr xpathObj;
+    char * xpathExpr="/*/*/*/*/*[local-name()='Polygon' or local-name()='MultiPolygon']";
+    xpathCtx = xmlXPathNewContext(doc);
+    xpathObj = xmlXPathEvalExpression(BAD_CAST xpathExpr,xpathCtx);
+    if(!xpathObj->nodesetval){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse Input Polygon"));
+      setMapInMaps(conf,"lenv","code","InvalidParameterValue");
+      return NULL;
+    }
+    int size = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
+    /**
+     * Create a temporary XML document
+     */
+    xmlDocPtr ndoc = xmlNewDoc(BAD_CAST "1.0");
+    /**
+     * Only one polygon should be provided so we use it as the root node.
+     */
+    for(int k=size-1;k>=0;k--){ 
+      xmlDocSetRootElement(ndoc, xpathObj->nodesetval->nodeTab[k]);
+    }
+    xmlDocDumpFormatMemory(ndoc, &xmlbuff, &buffersize, 1);
+    char *tmp=(char*)calloc((xmlStrlen(xmlStrstr(xmlbuff,BAD_CAST "?>"))-1),sizeof(char));
+    sprintf(tmp,"%s",xmlStrstr(xmlbuff,BAD_CAST "?>")+2);
+    xmlXPathFreeObject(xpathObj);
+    xmlXPathFreeContext(xpathCtx);
+    xmlFree(xmlbuff);
+    xmlFreeDoc(doc);
+    xmlFreeDoc(ndoc);
+#ifndef WIN32
+    xmlCleanupParser();
+#endif
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print\n Loading the geometry from GML string ...");
+#endif
+    OGRGeometryH res=OGR_G_CreateFromGML(tmp);
+    free(tmp);
+    if(res==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to call OGR_G_CreatFromGML"));
+      return NULL;
+    }
+    else
+      return res;
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int Simplify(maps*& conf,maps*& inputs,maps*& outputs){
+    maps* cursor=inputs;
+    OGRGeometryH geometry,res;
+    double tolerance;
+    map* tmp0=getMapFromMaps(cursor,"Tolerance","value");
+    if(tmp0==NULL){
+      tolerance=atof("2.0");
+    }
+    else
+      tolerance=atof(tmp0->value);
+#ifdef DEBUG
+    fprintf(stderr,"Tolerance for Simplify %f",tolerance);
+#endif
+    map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
+    if(!tmp){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse the input geometry from InputPolygon"));
+      return SERVICE_FAILED;
+    }
+    map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
+    if(tmp1!=NULL){
+      if(strncmp(tmp1->value,"text/js",7)==0 ||
+	 strncmp(tmp1->value,"application/json",16)==0)
+        geometry=OGR_G_CreateGeometryFromJson(tmp->value);
+      else
+        geometry=createGeometryFromGML(conf,tmp->value);
+    }
+    else{
+      setMapInMaps(conf,"lenv","message",_ss("Unable to find any geometry for InputPolygon"));
+      return SERVICE_FAILED;
+    }
+    if(geometry==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse the input geometry from InputPolygon"));
+      return SERVICE_FAILED;
+    }
+#ifdef DEBUG
+    fprintf(stderr,"Create GEOSGeometry object");
+#endif
+    GEOSGeometry* ggeometry=((OGRGeometry *) geometry)->exportToGEOS();
+    GEOSGeometry* gres=GEOSTopologyPreserveSimplify(ggeometry,tolerance);
+    res=(OGRGeometryH)OGRGeometryFactory::createFromGEOS(gres);
+    tmp1=getMapFromMaps(outputs,"Result","mimeType");
+    if(tmp1!=NULL){
+      if(strncmp(tmp1->value,"text/js",7)==0 ||
+	 strncmp(tmp1->value,"application/json",16)==0){
+	char *tmpS=OGR_G_ExportToJson(res);
+	setMapInMaps(outputs,"Result","value",tmpS);
+#ifndef WIN32
+	setMapInMaps(outputs,"Result","mimeType","text/plain");
+	setMapInMaps(outputs,"Result","encoding","UTF-8");
+	free(tmpS);
+#endif
+      }
+      else{
+	char *tmpS=OGR_G_ExportToGML(res);
+	setMapInMaps(outputs,"Result","value",tmpS);
+#ifndef WIN32
+	setMapInMaps(outputs,"Result","mimeType","text/xml");
+	setMapInMaps(outputs,"Result","encoding","UTF-8");
+	setMapInMaps(outputs,"Result","schema","http://fooa/gml/3.1.0/polygon.xsd");
+	free(tmpS);
+#endif
+      }
+    }else{
+      char *tmpS=OGR_G_ExportToJson(res);
+      setMapInMaps(outputs,"Result","value",tmpS);
+#ifndef WIN32
+      setMapInMaps(outputs,"Result","mimeType","text/plain");
+      setMapInMaps(outputs,"Result","encoding","UTF-8");
+      free(tmpS);
+#endif
+    }
+    outputs->next=NULL;
+    //GEOSFree(ggeometry);
+    //GEOSFree(gres);
+    OGR_G_DestroyGeometry(res);
+    OGR_G_DestroyGeometry(geometry);
+    return SERVICE_SUCCEEDED;
+  }
+
+
+  int applyOne(maps*& conf,maps*& inputs,maps*& outputs,OGRGeometryH (*myFunc)(OGRGeometryH),char* schema){
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print\n");
+#endif
+    maps* cursor=inputs;
+    OGRGeometryH geometry,res;
+#ifdef DEBUG
+    dumpMaps(cursor);
+#endif
+    map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
+    if(!tmp){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse the input geometry from InputPolygon"));
+      return SERVICE_FAILED;
+    }
+#ifdef DEBUG
+    fprintf(stderr,"Service internal print \n");
+    dumpMaps(inputs);
+    fprintf(stderr,"/Service internal print \n");
+#endif
+    map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
+#ifdef DEBUG
+    fprintf(stderr,"Service internal print \n");
+    dumpMap(tmp1);
+    fprintf(stderr,"/Service internal print \n");
+#endif
+    if(tmp1!=NULL){
+      if(strncmp(tmp1->value,"text/js",7)==0 ||
+	 strncmp(tmp1->value,"application/json",7)==0)
+        geometry=OGR_G_CreateGeometryFromJson(tmp->value);
+      else
+        geometry=createGeometryFromGML(conf,tmp->value);
+    }
+    else
+      geometry=createGeometryFromGML(conf,tmp->value);
+    if(geometry==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse the input geometry from InputPolygon"));
+      return SERVICE_FAILED;
+    }
+    res=(*myFunc)(geometry);
+#ifdef DEBUG
+    fprintf(stderr,"Service internal print \n");
+    dumpMaps(outputs);
+    fprintf(stderr,"/Service internal print \n");
+#endif
+    map *tmp_2=getMapFromMaps(outputs,"Result","mimeType");
+#ifdef DEBUG
+    fprintf(stderr,"Service internal print \n");
+    dumpMap(tmp_2);
+    fprintf(stderr,"/Service internal print \n");
+#endif
+    if(tmp_2!=NULL){
+      if(strncmp(tmp_2->value,"text/js",7)==0 ||
+	 strncmp(tmp_2->value,"application/json",16)==0){
+	char *tmpS=OGR_G_ExportToJson(res);
+	setMapInMaps(outputs,"Result","value",tmpS);
+#ifndef WIN32
+	setMapInMaps(outputs,"Result","mimeType","text/plain");
+	setMapInMaps(outputs,"Result","encoding","UTF-8");
+	free(tmpS);
+#endif
+      }
+      else{
+	char *tmpS=OGR_G_ExportToGML(res);
+	setMapInMaps(outputs,"Result","value",tmpS);
+#ifndef WIN32
+	setMapInMaps(outputs,"Result","mimeType","text/xml");
+	setMapInMaps(outputs,"Result","encoding","UTF-8");
+	setMapInMaps(outputs,"Result","schema",schema);
+	free(tmpS);
+#endif
+      }
+    }else{
+      char *tmpS=OGR_G_ExportToJson(res);
+      setMapInMaps(outputs,"Result","value",tmpS);
+#ifndef WIN32
+      setMapInMaps(outputs,"Result","mimeType","text/plain");
+      setMapInMaps(outputs,"Result","encoding","UTF-8");
+      free(tmpS);
+#endif
+    }
+    //outputs->next=NULL;
+#ifdef DEBUG
+    dumpMaps(outputs);
+    fprintf(stderr,"\nService internal print\n===\n");
+#endif
+    OGR_G_DestroyGeometry(res);
+    OGR_G_DestroyGeometry(geometry);
+    //CPLFree(res);
+    //CPLFree(geometry);
+#ifdef DEBUG
+    fprintf(stderr,"Service internal print \n");
+    dumpMaps(outputs);
+    fprintf(stderr,"/Service internal print \n");
+#endif
+    return SERVICE_SUCCEEDED;
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+int Buffer(maps*& conf,maps*& inputs,maps*& outputs){
+   OGRGeometryH geometry,res;
+   map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
+   if(tmp==NULL){
+     setMapInMaps(conf,"lenv","message",_ss("Unable to fetch input geometry"));
+     return SERVICE_FAILED;
+   }else
+     if(strlen(tmp->value)<=0){
+       setMapInMaps(conf,"lenv","message",_ss("Unable to fetch input geometry"));
+       return SERVICE_FAILED;
+     }
+   map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
+   if(strncmp(tmp1->value,"application/json",16)==0)
+     geometry=OGR_G_CreateGeometryFromJson(tmp->value);
+   else
+     geometry=createGeometryFromGML(conf,tmp->value);
+   if(geometry==NULL){
+     setMapInMaps(conf,"lenv","message",_ss("Unable to parse input geometry"));
+     return SERVICE_FAILED;
+   }
+   double bufferDistance;
+   tmp=getMapFromMaps(inputs,"BufferDistance","value");
+   if(tmp==NULL){
+     bufferDistance=atof("10.0");
+   }
+   else
+     bufferDistance=atof(tmp->value);
+   res=OGR_G_Buffer(geometry,bufferDistance,30);
+   dumpMap(tmp);
+   tmp1=getMapFromMaps(outputs,"Result","mimeType");
+   dumpMap(tmp);
+   if(strncmp(tmp1->value,"application/json",16)==0){
+     char *tmpS=OGR_G_ExportToJson(res);
+     setMapInMaps(outputs,"Result","value",tmpS);
+     dumpMap(tmp);
+#ifndef WIN32
+     setMapInMaps(outputs,"Result","mimeType","text/plain");
+     setMapInMaps(outputs,"Result","encoding","UTF-8");
+     free(tmpS);
+#endif
+   }
+   else{
+     char *tmpS=OGR_G_ExportToGML(res);
+     setMapInMaps(outputs,"Result","value",tmpS);
+     dumpMap(tmp);
+#ifndef WIN32
+     free(tmpS);
+     setMapInMaps(outputs,"Result","mimeType","text/xml");
+     setMapInMaps(outputs,"Result","encoding","UTF-8");
+     setMapInMaps(outputs,"Result","schema","http://fooa/gml/3.1.0/polygon.xsd");
+#endif
+   }
+   //outputs->next=NULL;
+   OGR_G_DestroyGeometry(geometry);
+   OGR_G_DestroyGeometry(res);
+   return SERVICE_SUCCEEDED;
+}
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int Boundary(maps*& conf,maps*& inputs,maps*& outputs){
+    return applyOne(conf,inputs,outputs,&OGR_G_GetBoundary,"http://fooa/gml/3.1.0/polygon.xsd");
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int ConvexHull(maps*& conf,maps*& inputs,maps*& outputs){
+    return applyOne(conf,inputs,outputs,&OGR_G_ConvexHull,"http://fooa/gml/3.1.0/polygon.xsd");
+  }
+
+
+  OGRGeometryH MY_OGR_G_Centroid(OGRGeometryH hTarget){
+    OGRGeometryH res;
+    res=OGR_G_CreateGeometryFromJson("{\"type\": \"Point\", \"coordinates\": [0,0] }");
+    OGRwkbGeometryType gtype=OGR_G_GetGeometryType(hTarget);
+    if(gtype!=wkbPolygon){
+      hTarget=OGR_G_ConvexHull(hTarget);
+    }
+    int c=OGR_G_Centroid(hTarget,res);
+    return res;
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int Centroid(maps*& conf,maps*& inputs,maps*& outputs){
+    return applyOne(conf,inputs,outputs,&MY_OGR_G_Centroid,"http://fooa/gml/3.1.0/point.xsd");
+  }
+
+  int applyTwo(maps*& conf,maps*& inputs,maps*& outputs,OGRGeometryH (*myFunc)(OGRGeometryH,OGRGeometryH)){
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print1\n");
+    fflush(stderr);
+    fprintf(stderr,"\nService internal print1\n");
+    dumpMaps(inputs);
+    fprintf(stderr,"\nService internal print1\n");
+#endif
+
+    maps* cursor=inputs;
+    OGRGeometryH geometry1,geometry2;
+    OGRGeometryH res;
+    {
+      map* tmp=getMapFromMaps(inputs,"InputEntity1","value");
+      map* tmp1=getMapFromMaps(inputs,"InputEntity1","mimeType");
+      if(tmp1!=NULL){
+        if(strncmp(tmp1->value,"application/json",16)==0)
+      	  geometry1=OGR_G_CreateGeometryFromJson(tmp->value);
+	else
+	  geometry1=createGeometryFromGML(conf,tmp->value);
+      }
+      else
+      	geometry1=createGeometryFromGML(conf,tmp->value);
+    }
+    if(geometry1==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse input geometry for InputEntity1."));
+#ifdef DEBUG
+      fprintf(stderr,"SERVICE FAILED !\n");
+#endif
+      return SERVICE_FAILED;
+    }
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print1 InputEntity1\n");
+#endif
+    {
+      map* tmp=getMapFromMaps(inputs,"InputEntity2","value");
+      map* tmp1=getMapFromMaps(inputs,"InputEntity2","mimeType");
+#ifdef DEBUG
+      fprintf(stderr,"MY MAP \n[%s] - %i\n",tmp1->value,strncmp(tmp1->value,"application/json",16));
+      //dumpMap(tmp);
+      fprintf(stderr,"MY MAP\n");
+      fprintf(stderr,"\nService internal print1 InputEntity2\n");
+#endif
+      if(tmp1!=NULL){
+        if(strncmp(tmp1->value,"application/json",16)==0){
+#ifdef DEBUG
+	  fprintf(stderr,"\nService internal print1 InputEntity2 as JSON\n");
+#endif
+      	  geometry2=OGR_G_CreateGeometryFromJson(tmp->value);
+	}
+	else{
+#ifdef DEBUG
+	  fprintf(stderr,"\nService internal print1 InputEntity2 as GML\n");
+#endif
+	  geometry2=createGeometryFromGML(conf,tmp->value);
+	}
+      }
+      else
+      	geometry2=createGeometryFromGML(conf,tmp->value);
+#ifdef DEBUG
+      fprintf(stderr,"\nService internal print1 InputEntity2 PreFinal\n");
+#endif
+    }
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print1 InputEntity2 Final\n");
+#endif
+    if(geometry2==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse input geometry for InputEntity2."));
+#ifdef DEBUG
+      fprintf(stderr,"SERVICE FAILED !\n");
+#endif
+      return SERVICE_FAILED;
+    }
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print1\n");
+#endif
+    res=(*myFunc)(geometry1,geometry2);
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print1\n");
+#endif    
+    /* nuova parte */
+    map* tmp2=getMapFromMaps(outputs,"Result","mimeType");
+    if(strncmp(tmp2->value,"application/json",16)==0){
+      char *tmpS=OGR_G_ExportToJson(res);
+      setMapInMaps(outputs,"Result","value",tmpS);
+#ifndef WIN32
+      setMapInMaps(outputs,"Result","mimeType","text/plain");
+      setMapInMaps(outputs,"Result","encoding","UTF-8");
+      free(tmpS);
+#endif
+    }
+    else{
+      char *tmpS=OGR_G_ExportToGML(res);
+      setMapInMaps(outputs,"Result","value",tmpS);
+#ifndef WIN32
+      setMapInMaps(outputs,"Result","mimeType","text/xml");
+      setMapInMaps(outputs,"Result","encoding","UTF-8");
+      setMapInMaps(outputs,"Result","schema","http://fooa/gml/3.1.0/polygon.xsd");
+      free(tmpS);
+#endif
+    }
+    
+    /* vecchia da togliere */
+    /*
+    char *tmpS=OGR_G_ExportToJson(res);
+    setMapInMaps(outputs,"Result","value",tmpS);
+    setMapInMaps(outputs,"Result","mimeType","text/plain");
+    setMapInMaps(outputs,"Result","encoding","UTF-8");
+    free(tmpS);
+    */
+    OGR_G_DestroyGeometry(geometry1);
+    OGR_G_DestroyGeometry(geometry2);
+    OGR_G_DestroyGeometry(res);
+    return SERVICE_SUCCEEDED;
+  }
+  
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int Difference(maps*& conf,maps*& inputs,maps*& outputs){
+    return applyTwo(conf,inputs,outputs,&OGR_G_Difference);
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int SymDifference(maps*& conf,maps*& inputs,maps*& outputs){
+    return applyTwo(conf,inputs,outputs,&OGR_G_SymmetricDifference);
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int Intersection(maps*& conf,maps*& inputs,maps*& outputs){
+    return applyTwo(conf,inputs,outputs,&OGR_G_Intersection);
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int Union(maps*& conf,maps*& inputs,maps*& outputs){
+    return applyTwo(conf,inputs,outputs,&OGR_G_Union);
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int Distance(maps*& conf,maps*& inputs,maps*& outputs){
+#ifdef DEBUG
+    fprintf(stderr,"\nService internal print1\n");
+#endif
+    fflush(stderr);
+    maps* cursor=inputs;
+    OGRGeometryH geometry1,geometry2;
+    double res;
+    {
+      map* tmp=getMapFromMaps(inputs,"InputEntity1","value");
+      map* tmp1=getMapFromMaps(inputs,"InputEntity1","mimeType");
+#ifdef DEBUG
+      fprintf(stderr,"MY MAP\n");
+      dumpMap(tmp1);
+      dumpMaps(inputs);
+      fprintf(stderr,"MY MAP\n");
+#endif
+      if(tmp1!=NULL){
+        if(strncmp(tmp1->value,"application/json",16)==0)
+      	  geometry1=OGR_G_CreateGeometryFromJson(tmp->value);
+	else
+	  geometry1=createGeometryFromGML(conf,tmp->value);
+      }
+      else
+      	geometry1=createGeometryFromGML(conf,tmp->value);
+    }
+    if(geometry1==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse input geometry for InputEntity1."));
+      fprintf(stderr,"SERVICE FAILED !\n");
+      return SERVICE_FAILED;
+    }
+    {
+      map* tmp=getMapFromMaps(inputs,"InputEntity2","value");
+      map* tmp1=getMapFromMaps(inputs,"InputEntity2","mimeType");
+#ifdef DEBUG
+      fprintf(stderr,"MY MAP\n");
+      dumpMap(tmp1);
+      dumpMaps(inputs);
+      fprintf(stderr,"MY MAP\n");
+#endif
+      if(tmp1!=NULL){
+        if(strncmp(tmp1->value,"application/json",16)==0)
+      	  geometry2=OGR_G_CreateGeometryFromJson(tmp->value);
+	else
+	  geometry2=createGeometryFromGML(conf,tmp->value);
+      }
+      else
+      	geometry2=createGeometryFromGML(conf,tmp->value);
+    }
+    if(geometry2==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse input geometry for InputEntity2."));
+      fprintf(stderr,"SERVICE FAILED !\n");
+      return SERVICE_FAILED;
+    }
+    res=OGR_G_Distance(geometry1,geometry2);    
+    char tmpres[100];
+    sprintf(tmpres,"%f",res);
+    setMapInMaps(outputs,"Distance","value",tmpres);
+    setMapInMaps(outputs,"Distance","dataType","float");
+#ifdef DEBUG
+    dumpMaps(outputs);
+    fprintf(stderr,"\nService internal print\n===\n");
+#endif
+    return SERVICE_SUCCEEDED;
+  }
+
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int GetArea(maps*& conf,maps*& inputs,maps*& outputs){
+    fprintf(stderr,"GETAREA \n");
+    double res;
+    /**
+     * Extract Geometry from the InputPolygon value
+     */
+    OGRGeometryH geometry;
+    map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
+    if(tmp==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse input geometry from InputPolygon"));
+      return SERVICE_FAILED;
+    }
+    fprintf(stderr,"geometry creation %s \n",tmp->value);
+    geometry=createGeometryFromGML(conf,tmp->value);
+    if(geometry==NULL){
+      setMapInMaps(conf,"lenv","message",_ss("Unable to parse input geometry from InputPolygon"));
+      return SERVICE_FAILED;
+    }
+    fprintf(stderr,"geometry created %s \n",tmp->value);
+    res=OGR_G_GetArea(geometry);
+    fprintf(stderr,"area %d \n",res);
+    /**
+     * Filling the outputs
+     */
+    char tmp1[100];
+    sprintf(tmp1,"%f",res);
+    setMapInMaps(outputs,"Area","value",tmp1);
+    setMapInMaps(outputs,"Area","dataType","float");
+#ifdef DEBUG
+    dumpMaps(outputs);
+#endif
+    return SERVICE_SUCCEEDED;
+  }
+
+}
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/Makefile	(revision 221)
@@ -0,0 +1,26 @@
+GDAL_SRC=./gdal_src/
+
+include $(GDAL_SRC)GDALmake.opt
+
+CPPFLAGS        :=      $(GDAL_INCLUDE) -I$(GDAL_ROOT)/frmts $(CPPFLAGS)
+DEP_LIBS        =       $(EXE_DEP_LIBS) $(XTRAOBJ)
+BIN_LIST = cgi-env/ogr2ogr_service.zo
+
+default:        $(BIN_LIST)
+
+all: default
+
+lib-depend:
+	(cd $(GDAL_SRC)/gcore ; $(MAKE) )
+	(cd $(GDAL_SRC)/port ; $(MAKE) )
+
+CFLAGS=-DZOO_SERVICE -I../../../zoo-kernel/
+
+#cgi-env/ogr2ogr_service.zo: service.c
+#	g++  -DZOO_SERVICE ${CFLAGS} -shared -fpic -o cgi-env/ogr2ogr_service.zo ./service.c -lgdal
+
+cgi-env/ogr2ogr_service.zo: service.c $(DEP_LIBS)
+	g++ $(CFLAGS) $(CPPFLAGS) -shared -fpic $< `gdal-config --libs` -o $@
+
+clean:
+	rm -f cgi-env/*zo
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/cgi-env/Ogr2Ogr.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/cgi-env/Ogr2Ogr.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/cgi-env/Ogr2Ogr.zcfg	(revision 221)
@@ -0,0 +1,62 @@
+[Ogr2Ogr]
+ Title = Convert vector data from one format to another. 
+ Abstract = Converts vector data between different formats.
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceType = C
+ serviceProvider = ogr2ogr_service.zo
+ <MetaData>
+   Title = My Demo
+ </MetaData>
+ <DataInputs>
+  [F]
+   Title = Format of the output data
+   Abstract = Select the output format.
+   minOccurs = 0
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+     value = ESRI ShapeFile
+    </Default>
+    <Supported>
+    </Supported>
+   </LiteralData>
+  [InputDSN]
+   Title = The input data source name
+   Abstract = The input data source name to use as source for convertion.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+    <Supported>
+    </Supported>
+   </LiteralData>
+  [OutputDSN]
+   Title = The output data source name
+   Abstract = The output data name.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+    <Supported>
+    </Supported>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [OutputedDataSourceName]
+   Title = The resulting converted file
+   Abstract = The file name resulting of the convertion
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+    <Supported>
+    </Supported>
+   </LiteralData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/makefile.vc
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/makefile.vc	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/makefile.vc	(revision 221)
@@ -0,0 +1,11 @@
+GEODIR=c:/OSGeo4W/
+TOOLS=c:/Users/djay/GeoLabs/tools/
+CFLAGS=-I$(GEODIR)/include -I$(TOOLS)/include -I../../../zoo-kernel/ -I./ -DGDAL_1_5_0 -DZOO_SERVICE -DLINUX_FREE_ISSUE -DDEBUG
+CPP=cl /TP 
+
+cgi-env/ogr2ogr_service.zo: service.c
+	$(CPP) $(CFLAGS) /c service.c
+	link /dll /out:cgi-env/ogr2ogr_service.zo ../../../zoo-kernel/service_internal.obj ./service.obj -L$(TOOLS)/lib/libssl32.dll.a $(GEODIR)/lib/libxml2.lib $(GEODIR)/lib/gdal_i.lib $(TOOLS)/lib/libeay32.dll.a $(TOOLS)/lib/libcrypto.a $(TOOLS)/lib/libssl32.dll.a 
+
+clean:
+	rm -f cgi-env/ogr_service.zso
Index: /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/service.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/service.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/ogr/ogr2ogr/service.c	(revision 221)
@@ -0,0 +1,1237 @@
+/******************************************************************************
+ * $Id: ogr2ogr.cpp 15473 2008-10-07 20:59:24Z warmerdam $
+ *
+ * Project:  OpenGIS Simple Features Reference Implementation
+ * Purpose:  Simple client for translating between formats.
+ * Author:   Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 1999, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "ogrsf_frmts.h"
+#include "ogr_p.h"
+#include "cpl_conv.h"
+#include "cpl_string.h"
+#include "ogr_api.h"
+#ifdef ZOO_SERVICE
+#include "service.h"
+#endif
+
+CPL_CVSID("$Id: ogr2ogr.cpp 15473 2008-10-07 20:59:24Z warmerdam $");
+
+#ifdef ZOO_SERVICE
+extern "C" {
+#endif
+
+static void Usage();
+
+static int TranslateLayer( OGRDataSource *poSrcDS, 
+                           OGRLayer * poSrcLayer,
+                           OGRDataSource *poDstDS,
+                           char ** papszLSCO,
+                           const char *pszNewLayerName,
+                           int bTransform, 
+                           OGRSpatialReference *poOutputSRS,
+                           OGRSpatialReference *poSourceSRS,
+                           char **papszSelFields,
+                           int bAppend, int eGType,
+                           int bOverwrite,
+                           double dfMaxSegmentLength);
+
+static int bSkipFailures = FALSE;
+static int nGroupTransactions = 200;
+static int bPreserveFID = FALSE;
+static int nFIDToFetch = OGRNullFID;
+
+/************************************************************************/
+/*                                main()                                */
+/************************************************************************/
+
+#ifdef ZOO_SERVICE
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+int Ogr2Ogr(maps*& conf,maps*& inputs,maps*& outputs)
+#else
+int main( int nArgc, char ** papszArgv )
+#endif
+{
+    const char  *pszFormat = "ESRI Shapefile";
+    const char  *pszDataSource = NULL;
+    const char  *pszDestDataSource = NULL;
+    const char  *pszwebDestData = NULL;
+    char        **papszLayers = NULL;
+    char        **papszDSCO = NULL, **papszLCO = NULL;
+    int         bTransform = FALSE;
+    int         bAppend = FALSE, bUpdate = FALSE, bOverwrite = FALSE;
+    const char  *pszOutputSRSDef = NULL;
+    const char  *pszSourceSRSDef = NULL;
+    OGRSpatialReference *poOutputSRS = NULL;
+    OGRSpatialReference *poSourceSRS = NULL;
+    const char  *pszNewLayerName = NULL;
+    const char  *pszWHERE = NULL;
+    OGRGeometry *poSpatialFilter = NULL;
+    const char  *pszSelect;
+    char        **papszSelFields = NULL;
+    const char  *pszSQLStatement = NULL;
+    int         eGType = -2;
+    double       dfMaxSegmentLength = 0;
+
+    /* Check strict compilation and runtime library version as we use C++ API */
+    if (! GDAL_CHECK_VERSION("ogr2ogr"))
+#ifdef ZOO_SERVICE
+	{
+		fprintf(stderr,"Not correct version of the gdal library\n");
+		setMapInMaps(conf,"lenv","message","Unable to check gdal version for ogr2ogr_service.zo");
+		return SERVICE_FAILED;
+	}
+#else
+        exit(1);
+#endif
+/* -------------------------------------------------------------------- */
+/*      Register format(s).                                             */
+/* -------------------------------------------------------------------- */
+    OGRRegisterAll();
+
+#ifdef ZOO_SERVICE
+    map *tmpMap=NULL;
+    char dataPath[1024];
+    tmpMap=getMapFromMaps(conf,"main","dataPath");
+    if(tmpMap!=NULL)
+      sprintf(dataPath,"%s",tmpMap->value);
+    tmpMap=NULL;
+    char tempPath[1024];
+    tmpMap=getMapFromMaps(conf,"main","tmpPath");
+    if(tmpMap!=NULL){
+      sprintf(tempPath,"%s",tmpMap->value);
+    }
+    
+    tmpMap=NULL;
+    char serverAddress[1024];
+    tmpMap=getMapFromMaps(conf,"main","serverAddress");
+    if(tmpMap!=NULL){
+      sprintf(serverAddress,"%s",tmpMap->value);
+    }
+    
+    tmpMap=NULL;
+    char tmpurl[1024];
+    tmpMap=getMapFromMaps(conf,"main","tmpurl");
+    if(tmpMap!=NULL){
+      sprintf(tmpurl,"%s",tmpMap->value);
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"F","value");
+    if(tmpMap!=NULL){
+      pszFormat=tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"DSCO","value");
+    if(tmpMap!=NULL){
+	  papszDSCO = CSLAddString(papszDSCO, tmpMap->value );
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"LCO","value");
+    if(tmpMap!=NULL){
+	  papszLCO = CSLAddString(papszLCO, tmpMap->value );
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"preserve_fid","value");
+    if(tmpMap!=NULL){
+	  bPreserveFID = TRUE;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"skipfailure","value");
+    if(tmpMap!=NULL){
+	  bPreserveFID = TRUE;
+	  bSkipFailures = TRUE;
+	  nGroupTransactions = 1; /* #2409 */
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"append","value");
+    if(tmpMap!=NULL){
+	  bAppend = TRUE;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"overwrite","value");
+    if(tmpMap!=NULL){
+	  bOverwrite = TRUE;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"update","value");
+    if(tmpMap!=NULL){
+	  bUpdate = TRUE;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"fid","value");
+    if(tmpMap!=NULL){
+	  nFIDToFetch = atoi(tmpMap->value);
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"sql","value");
+    if(tmpMap!=NULL){
+	  pszSQLStatement = tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"nln","value");
+    if(tmpMap!=NULL){
+	  pszNewLayerName = tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"nlt","value");
+    if(tmpMap!=NULL){
+	  pszNewLayerName = tmpMap->value;
+	  if( EQUAL(tmpMap->value,"NONE") )
+		  eGType = wkbNone;
+	  else if( EQUAL(tmpMap->value,"GEOMETRY") )
+		  eGType = wkbUnknown;
+	  else if( EQUAL(tmpMap->value,"POINT") )
+		  eGType = wkbPoint;
+	  else if( EQUAL(tmpMap->value,"LINESTRING") )
+		  eGType = wkbLineString;
+	  else if( EQUAL(tmpMap->value,"POLYGON") )
+		  eGType = wkbPolygon;
+	  else if( EQUAL(tmpMap->value,"GEOMETRYCOLLECTION") )
+		  eGType = wkbGeometryCollection;
+	  else if( EQUAL(tmpMap->value,"MULTIPOINT") )
+		  eGType = wkbMultiPoint;
+	  else if( EQUAL(tmpMap->value,"MULTILINESTRING") )
+		  eGType = wkbMultiLineString;
+	  else if( EQUAL(tmpMap->value,"MULTIPOLYGON") )
+		  eGType = wkbMultiPolygon;
+	  else if( EQUAL(tmpMap->value,"GEOMETRY25D") )
+		  eGType = wkbUnknown | wkb25DBit;
+	  else if( EQUAL(tmpMap->value,"POINT25D") )
+		  eGType = wkbPoint25D;
+	  else if( EQUAL(tmpMap->value,"LINESTRING25D") )
+		  eGType = wkbLineString25D;
+	  else if( EQUAL(tmpMap->value,"POLYGON25D") )
+		  eGType = wkbPolygon25D;
+	  else if( EQUAL(tmpMap->value,"GEOMETRYCOLLECTION25D") )
+		  eGType = wkbGeometryCollection25D;
+	  else if( EQUAL(tmpMap->value,"MULTIPOINT25D") )
+		  eGType = wkbMultiPoint25D;
+	  else if( EQUAL(tmpMap->value,"MULTILINESTRING25D") )
+		  eGType = wkbMultiLineString25D;
+	  else if( EQUAL(tmpMap->value,"MULTIPOLYGON25D") )
+		  eGType = wkbMultiPolygon25D;
+	  else	  
+	  {
+		  fprintf( stderr, "-nlt %s: type not recognised.\n", 
+			  tmpMap->value );
+		  exit( 1 );
+	  }
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"tg","value");
+    if(tmpMap!=NULL){
+	  nGroupTransactions = atoi(tmpMap->value);
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"s_srs","value");
+    if(tmpMap!=NULL){
+	  pszSourceSRSDef = tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"a_srs","value");
+    if(tmpMap!=NULL){
+	  pszOutputSRSDef = tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"t_srs","value");
+    if(tmpMap!=NULL){
+	  pszOutputSRSDef = tmpMap->value;
+	  bTransform = TRUE;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"SPAT","value");
+    if(tmpMap!=NULL){
+      char *tmp=tmpMap->value;
+      char *t=strtok(tmp,",");
+      int cnt=0;
+      double dfULX, dfULY, dfLRX, dfLRY;
+      while(t!=NULL){
+        switch(cnt){
+        case 0:
+          dfULX = atof(t);
+          break;
+        case 1:
+          dfULY = atof(t);
+          break;
+        case 2:
+          dfLRX = atof(t);
+          break;
+        case 3:
+          dfLRY = atof(t);
+          break;
+        }
+        fprintf(stderr,"%s\n\n",t);
+        fprintf(stderr,"%f - %f - %f - %f\n\n",dfULX,dfULY,dfLRX,dfLRY);
+        t=strtok(NULL,",");
+        cnt++;
+      }
+
+      OGRLinearRing  oRing;
+      
+      oRing.addPoint( dfULX, dfULY );
+      oRing.addPoint( dfULX, dfLRY );
+      oRing.addPoint( dfLRX, dfLRY );
+      oRing.addPoint( dfLRX, dfULY );
+      oRing.addPoint( dfULX, dfULY );
+      poSpatialFilter = new OGRPolygon();
+      ((OGRPolygon *) poSpatialFilter)->addRing( &oRing );
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"where","value");
+    if(tmpMap!=NULL){
+	  pszWHERE = tmpMap->value;
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"select","value");
+    if(tmpMap!=NULL){
+	  pszSelect = tmpMap->value;
+	  papszSelFields = CSLTokenizeStringComplex(pszSelect, " ,", 
+		  FALSE, FALSE );
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"segmentize","value");
+    if(tmpMap!=NULL){
+	  dfMaxSegmentLength = atof(tmpMap->value);
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"segmentize","value");
+    if(tmpMap!=NULL){
+	  dfMaxSegmentLength = atof(tmpMap->value);
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"InputDSN","value");
+    if(tmpMap!=NULL){
+      pszDataSource=(char*)malloc(sizeof(char)*(strlen(dataPath)+strlen(tmpMap->value)+1));
+      sprintf((char*)pszDataSource,"%s/%s",dataPath,tmpMap->value);
+    }
+
+    tmpMap=NULL;
+    tmpMap=getMapFromMaps(inputs,"OutputDSN","value");
+    if(tmpMap!=NULL){
+      pszDestDataSource=(char*)malloc(sizeof(char)*(strlen(tempPath)+strlen(tmpMap->value)+4));
+      sprintf((char*)pszDestDataSource,"%s/%s",tempPath,tmpMap->value/*,ext*/);
+      pszwebDestData=(char*)malloc(sizeof(char)*(strlen(serverAddress)+strlen(tmpurl)+strlen(tmpMap->value)+4));
+      sprintf((char*)pszwebDestData,"%s%s/%s",serverAddress,tmpurl,tmpMap->value/*,ext*/);
+    }
+
+#else
+/* -------------------------------------------------------------------- */
+/*      Processing command line arguments.                              */
+/* -------------------------------------------------------------------- */
+    nArgc = OGRGeneralCmdLineProcessor( nArgc, &papszArgv, 0 );
+    
+    if( nArgc < 1 )
+        exit( -nArgc );
+
+    for( int iArg = 1; iArg < nArgc; iArg++ )
+    {
+        if( EQUAL(papszArgv[iArg], "--utility_version") )
+        {
+				printf("%s was compiled against GDAL %s and is running against GDAL %s\n",
+                   papszArgv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
+            return 0;
+        }
+        else if( EQUAL(papszArgv[iArg],"-f") && iArg < nArgc-1 )
+        {
+            pszFormat = papszArgv[++iArg];
+        }
+        else if( EQUAL(papszArgv[iArg],"-dsco") && iArg < nArgc-1 )
+        {
+            papszDSCO = CSLAddString(papszDSCO, papszArgv[++iArg] );
+        }
+        else if( EQUAL(papszArgv[iArg],"-lco") && iArg < nArgc-1 )
+        {
+            papszLCO = CSLAddString(papszLCO, papszArgv[++iArg] );
+        }
+        else if( EQUAL(papszArgv[iArg],"-preserve_fid") )
+        {
+            bPreserveFID = TRUE;
+        }
+        else if( EQUALN(papszArgv[iArg],"-skip",5) )
+        {
+            bSkipFailures = TRUE;
+            nGroupTransactions = 1; /* #2409 */
+        }
+        else if( EQUAL(papszArgv[iArg],"-append") )
+        {
+            bAppend = TRUE;
+        }
+        else if( EQUAL(papszArgv[iArg],"-overwrite") )
+        {
+            bOverwrite = TRUE;
+        }
+        else if( EQUAL(papszArgv[iArg],"-update") )
+        {
+            bUpdate = TRUE;
+        }
+        else if( EQUAL(papszArgv[iArg],"-fid") && papszArgv[iArg+1] != NULL )
+        {
+            nFIDToFetch = atoi(papszArgv[++iArg]);
+        }
+        else if( EQUAL(papszArgv[iArg],"-sql") && papszArgv[iArg+1] != NULL )
+        {
+            pszSQLStatement = papszArgv[++iArg];
+        }
+        else if( EQUAL(papszArgv[iArg],"-nln") && iArg < nArgc-1 )
+        {
+            pszNewLayerName = papszArgv[++iArg];
+        }
+        else if( EQUAL(papszArgv[iArg],"-nlt") && iArg < nArgc-1 )
+        {
+            if( EQUAL(papszArgv[iArg+1],"NONE") )
+                eGType = wkbNone;
+            else if( EQUAL(papszArgv[iArg+1],"GEOMETRY") )
+                eGType = wkbUnknown;
+            else if( EQUAL(papszArgv[iArg+1],"POINT") )
+                eGType = wkbPoint;
+            else if( EQUAL(papszArgv[iArg+1],"LINESTRING") )
+                eGType = wkbLineString;
+            else if( EQUAL(papszArgv[iArg+1],"POLYGON") )
+                eGType = wkbPolygon;
+            else if( EQUAL(papszArgv[iArg+1],"GEOMETRYCOLLECTION") )
+                eGType = wkbGeometryCollection;
+            else if( EQUAL(papszArgv[iArg+1],"MULTIPOINT") )
+                eGType = wkbMultiPoint;
+            else if( EQUAL(papszArgv[iArg+1],"MULTILINESTRING") )
+                eGType = wkbMultiLineString;
+            else if( EQUAL(papszArgv[iArg+1],"MULTIPOLYGON") )
+                eGType = wkbMultiPolygon;
+            else if( EQUAL(papszArgv[iArg+1],"GEOMETRY25D") )
+                eGType = wkbUnknown | wkb25DBit;
+            else if( EQUAL(papszArgv[iArg+1],"POINT25D") )
+                eGType = wkbPoint25D;
+            else if( EQUAL(papszArgv[iArg+1],"LINESTRING25D") )
+                eGType = wkbLineString25D;
+            else if( EQUAL(papszArgv[iArg+1],"POLYGON25D") )
+                eGType = wkbPolygon25D;
+            else if( EQUAL(papszArgv[iArg+1],"GEOMETRYCOLLECTION25D") )
+                eGType = wkbGeometryCollection25D;
+            else if( EQUAL(papszArgv[iArg+1],"MULTIPOINT25D") )
+                eGType = wkbMultiPoint25D;
+            else if( EQUAL(papszArgv[iArg+1],"MULTILINESTRING25D") )
+                eGType = wkbMultiLineString25D;
+            else if( EQUAL(papszArgv[iArg+1],"MULTIPOLYGON25D") )
+                eGType = wkbMultiPolygon25D;
+            else
+            {
+                fprintf( stderr, "-nlt %s: type not recognised.\n", 
+                         papszArgv[iArg+1] );
+                exit( 1 );
+            }
+            iArg++;
+        }
+        else if( (EQUAL(papszArgv[iArg],"-tg") ||
+                  EQUAL(papszArgv[iArg],"-gt")) && iArg < nArgc-1 )
+        {
+            nGroupTransactions = atoi(papszArgv[++iArg]);
+        }
+        else if( EQUAL(papszArgv[iArg],"-s_srs") && iArg < nArgc-1 )
+        {
+            pszSourceSRSDef = papszArgv[++iArg];
+        }
+        else if( EQUAL(papszArgv[iArg],"-a_srs") && iArg < nArgc-1 )
+        {
+            pszOutputSRSDef = papszArgv[++iArg];
+        }
+        else if( EQUAL(papszArgv[iArg],"-t_srs") && iArg < nArgc-1 )
+        {
+            pszOutputSRSDef = papszArgv[++iArg];
+            bTransform = TRUE;
+        }
+        else if( EQUAL(papszArgv[iArg],"-spat") 
+                 && papszArgv[iArg+1] != NULL 
+                 && papszArgv[iArg+2] != NULL 
+                 && papszArgv[iArg+3] != NULL 
+                 && papszArgv[iArg+4] != NULL )
+        {
+            OGRLinearRing  oRing;
+
+            oRing.addPoint( atof(papszArgv[iArg+1]), atof(papszArgv[iArg+2]) );
+            oRing.addPoint( atof(papszArgv[iArg+1]), atof(papszArgv[iArg+4]) );
+            oRing.addPoint( atof(papszArgv[iArg+3]), atof(papszArgv[iArg+4]) );
+            oRing.addPoint( atof(papszArgv[iArg+3]), atof(papszArgv[iArg+2]) );
+            oRing.addPoint( atof(papszArgv[iArg+1]), atof(papszArgv[iArg+2]) );
+
+            poSpatialFilter = new OGRPolygon();
+            ((OGRPolygon *) poSpatialFilter)->addRing( &oRing );
+            iArg += 4;
+        }
+        else if( EQUAL(papszArgv[iArg],"-where") && papszArgv[iArg+1] != NULL )
+        {
+            pszWHERE = papszArgv[++iArg];
+        }
+        else if( EQUAL(papszArgv[iArg],"-select") && papszArgv[iArg+1] != NULL)
+        {
+            pszSelect = papszArgv[++iArg];
+            papszSelFields = CSLTokenizeStringComplex(pszSelect, " ,", 
+                                                      FALSE, FALSE );
+        }
+        else if( EQUAL(papszArgv[iArg],"-segmentize") && iArg < nArgc-1 )
+        {
+            dfMaxSegmentLength = atof(papszArgv[++iArg]);
+        }
+        else if( papszArgv[iArg][0] == '-' )
+        {
+            Usage();
+        }
+        else if( pszDestDataSource == NULL )
+            pszDestDataSource = papszArgv[iArg];
+        else if( pszDataSource == NULL )
+            pszDataSource = papszArgv[iArg];
+        else
+            papszLayers = CSLAddString( papszLayers, papszArgv[iArg] );
+    }
+#endif
+
+    if( pszDataSource == NULL )
+#ifdef ZOO_SERVICE
+	{
+#endif
+	  Usage();
+#ifdef ZOO_SERVICE
+	  setMapInMaps(conf,"lenv","message","Wrong parameter");
+	  return SERVICE_FAILED;
+	}
+#endif
+
+/* -------------------------------------------------------------------- */
+/*      Open data source.                                               */
+/* -------------------------------------------------------------------- */
+    OGRDataSource       *poDS;
+        
+    poDS = OGRSFDriverRegistrar::Open( pszDataSource, FALSE );
+
+/* -------------------------------------------------------------------- */
+/*      Report failure                                                  */
+/* -------------------------------------------------------------------- */
+    if( poDS == NULL )
+    {
+        OGRSFDriverRegistrar    *poR = OGRSFDriverRegistrar::GetRegistrar();
+        
+        fprintf( stderr, "FAILURE:\n"
+                "Unable to open datasource `%s' with the following drivers.\n",
+                pszDataSource );
+
+        for( int iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ )
+        {
+            fprintf( stderr, "  -> %s\n", poR->GetDriver(iDriver)->GetName() );
+        }
+#ifdef ZOO_SERVICE
+	char tmp[1024];
+	sprintf(tmp,"Unable to open datasource `%s' with the following drivers.",pszDataSource);
+	setMapInMaps(conf,"lenv","message",tmp);
+	return SERVICE_FAILED;
+#else
+        exit( 1 );
+#endif
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Try opening the output datasource as an existing, writable      */
+/* -------------------------------------------------------------------- */
+    OGRDataSource       *poODS;
+    
+    if( bUpdate )
+    {
+        poODS = OGRSFDriverRegistrar::Open( pszDestDataSource, TRUE );
+        if( poODS == NULL )
+        {
+            fprintf( stderr, "FAILURE:\n"
+                    "Unable to open existing output datasource `%s'.\n",
+                    pszDestDataSource );
+#ifdef ZOO_SERVICE
+	    char tmp[1024];
+	    sprintf(tmp,"Unable to open existing output datasource `%s'.",pszDestDataSource);
+	    setMapInMaps(conf,"lenv","message",tmp);
+	    return SERVICE_FAILED;
+#else
+        exit( 1 );
+#endif
+        }
+
+        if( CSLCount(papszDSCO) > 0 )
+        {
+            fprintf( stderr, "WARNING: Datasource creation options ignored since an existing datasource\n"
+                    "         being updated.\n" );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Find the output driver.                                         */
+/* -------------------------------------------------------------------- */
+    else
+    {
+        OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar();
+        OGRSFDriver          *poDriver = NULL;
+        int                  iDriver;
+
+        for( iDriver = 0;
+             iDriver < poR->GetDriverCount() && poDriver == NULL;
+             iDriver++ )
+        {
+            if( EQUAL(poR->GetDriver(iDriver)->GetName(),pszFormat) )
+            {
+                poDriver = poR->GetDriver(iDriver);
+            }
+        }
+
+        if( poDriver == NULL )
+        {
+            fprintf( stderr, "Unable to find driver `%s'.\n", pszFormat );
+            fprintf( stderr,  "The following drivers are available:\n" );
+        
+            for( iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ )
+            {
+                fprintf( stderr,  "  -> `%s'\n", poR->GetDriver(iDriver)->GetName() );
+            }
+#ifdef ZOO_SERVICE
+	    char tmp[1024];
+	    sprintf(tmp,"Unable to find driver `%s'.",pszFormat);
+	    setMapInMaps(conf,"lenv","message",tmp);
+	    return SERVICE_FAILED;
+#else
+            exit( 1 );
+#endif
+        }
+
+        if( !poDriver->TestCapability( ODrCCreateDataSource ) )
+        {
+            fprintf( stderr,  "%s driver does not support data source creation.\n",
+                    pszFormat );
+#ifdef ZOO_SERVICE
+	    char tmp[1024];
+	    sprintf(tmp,"%s driver does not support data source creation.",pszFormat);
+	    setMapInMaps(conf,"lenv","message",tmp);
+	    return SERVICE_FAILED;
+#else
+            exit( 1 );
+#endif
+        }
+
+/* -------------------------------------------------------------------- */
+/*      Create the output data source.                                  */
+/* -------------------------------------------------------------------- */
+        poODS = poDriver->CreateDataSource( pszDestDataSource, papszDSCO );
+        if( poODS == NULL )
+        {
+            fprintf( stderr,  "%s driver failed to create %s\n", 
+                    pszFormat, pszDestDataSource );
+#ifdef ZOO_SERVICE
+	    char tmp[1024];
+	    sprintf(tmp,"%s driver failed to create %s",pszFormat, pszDestDataSource);
+	    setMapInMaps(conf,"lenv","message",tmp);
+	    return SERVICE_FAILED;
+#else
+            exit( 1 );
+#endif
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Parse the output SRS definition if possible.                    */
+/* -------------------------------------------------------------------- */
+    if( pszOutputSRSDef != NULL )
+    {
+        poOutputSRS = new OGRSpatialReference();
+        if( poOutputSRS->SetFromUserInput( pszOutputSRSDef ) != OGRERR_NONE )
+        {
+            fprintf( stderr,  "Failed to process SRS definition: %s\n", 
+                    pszOutputSRSDef );
+#ifdef ZOO_SERVICE
+	    char tmp[1024];
+	    sprintf(tmp,"Failed to process SRS definition: %s",pszOutputSRSDef);
+	    setMapInMaps(conf,"lenv","message",tmp);
+	    return SERVICE_FAILED;
+#else
+            exit( 1 );
+#endif
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Parse the source SRS definition if possible.                    */
+/* -------------------------------------------------------------------- */
+    if( pszSourceSRSDef != NULL )
+    {
+        poSourceSRS = new OGRSpatialReference();
+        if( poSourceSRS->SetFromUserInput( pszSourceSRSDef ) != OGRERR_NONE )
+        {
+            fprintf( stderr,  "Failed to process SRS definition: %s\n", 
+                    pszSourceSRSDef );
+#ifdef ZOO_SERVICE
+	    char tmp[1024];
+	    sprintf(tmp,"Failed to process SRS definition: %s",pszOutputSRSDef);
+	    setMapInMaps(conf,"lenv","message",tmp);
+	    return SERVICE_FAILED;
+#else
+            exit( 1 );
+#endif
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Special case for -sql clause.  No source layers required.       */
+/* -------------------------------------------------------------------- */
+    if( pszSQLStatement != NULL )
+    {
+        OGRLayer *poResultSet;
+
+        if( pszWHERE != NULL )
+            fprintf( stderr,  "-where clause ignored in combination with -sql.\n" );
+        if( CSLCount(papszLayers) > 0 )
+            fprintf( stderr,  "layer names ignored in combination with -sql.\n" );
+        
+        poResultSet = poDS->ExecuteSQL( pszSQLStatement, poSpatialFilter, 
+                                        NULL );
+
+        if( poResultSet != NULL )
+        {
+            if( !TranslateLayer( poDS, poResultSet, poODS, papszLCO, 
+                                 pszNewLayerName, bTransform, poOutputSRS,
+                                 poSourceSRS, papszSelFields, bAppend, eGType,
+                                 bOverwrite, dfMaxSegmentLength ))
+            {
+                CPLError( CE_Failure, CPLE_AppDefined, 
+                          "Terminating translation prematurely after failed\n"
+                          "translation from sql statement." );
+
+                exit( 1 );
+            }
+            poDS->ReleaseResultSet( poResultSet );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Process each data source layer.                                 */
+/* -------------------------------------------------------------------- */
+    for( int iLayer = 0; 
+         pszSQLStatement == NULL && iLayer < poDS->GetLayerCount(); 
+         iLayer++ )
+    {
+        OGRLayer        *poLayer = poDS->GetLayer(iLayer);
+
+        if( poLayer == NULL )
+        {
+            fprintf( stderr, "FAILURE: Couldn't fetch advertised layer %d!\n",
+                    iLayer );
+#ifdef ZOO_SERVICE
+	    char tmp[1024];
+	    sprintf(tmp,"Couldn't fetch advertised layer %d!",iLayer);
+	    setMapInMaps(conf,"lenv","message",tmp);
+	    return SERVICE_FAILED;
+#else
+	    exit( 1 );
+#endif
+        }
+
+        if( CSLCount(papszLayers) == 0
+            || CSLFindString( papszLayers,
+                              poLayer->GetLayerDefn()->GetName() ) != -1 )
+        {
+            if( pszWHERE != NULL )
+                poLayer->SetAttributeFilter( pszWHERE );
+            
+            if( poSpatialFilter != NULL )
+                poLayer->SetSpatialFilter( poSpatialFilter );
+            
+            if( !TranslateLayer( poDS, poLayer, poODS, papszLCO, 
+                                 pszNewLayerName, bTransform, poOutputSRS,
+                                 poSourceSRS, papszSelFields, bAppend, eGType,
+                                 bOverwrite, dfMaxSegmentLength ) 
+                && !bSkipFailures )
+            {
+                CPLError( CE_Failure, CPLE_AppDefined, 
+                          "Terminating translation prematurely after failed\n"
+                          "translation of layer %s (use -skipfailures to skip errors)\n", 
+                          poLayer->GetLayerDefn()->GetName() );
+
+#ifdef ZOO_SERVICE
+		char tmp[1024];
+		sprintf(tmp,"Terminating translation prematurely after failed of layer %s",poLayer->GetLayerDefn()->GetName() );
+		setMapInMaps(conf,"lenv","message",tmp);
+		return SERVICE_FAILED;
+#else
+                exit( 1 );
+#endif
+            }
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Close down.                                                     */
+/* -------------------------------------------------------------------- */
+    delete poOutputSRS;
+    delete poSourceSRS;
+    delete poODS;
+    delete poDS;
+
+    CSLDestroy(papszSelFields);
+#ifndef ZOO_SERVICE
+	CSLDestroy( papszArgv );
+#endif
+    CSLDestroy( papszLayers );
+    CSLDestroy( papszDSCO );
+    CSLDestroy( papszLCO );
+
+    OGRCleanupAll();
+
+#ifdef DBMALLOC
+    malloc_dump(1);
+#endif
+    
+#ifdef ZOO_SERVICE
+	outputs=(maps*)malloc(sizeof(maps*));
+    outputs->name="GeneratedFile";
+    outputs->content=createMap("value",(char*)pszwebDestData);
+    addMapToMap(&outputs->content,createMap("dataType","string"));
+    outputs->next=NULL;
+    return SERVICE_SUCCEEDED;
+#else
+	return 0;
+#endif
+}
+
+/************************************************************************/
+/*                               Usage()                                */
+/************************************************************************/
+
+static void Usage()
+
+{
+    OGRSFDriverRegistrar        *poR = OGRSFDriverRegistrar::GetRegistrar();
+
+#ifdef ZOO_SERVICE
+	fprintf(stderr,
+#else
+	printf(
+#endif
+		"Usage: ogr2ogr [--help-general] [-skipfailures] [-append] [-update] [-gt n]\n"
+		"               [-select field_list] [-where restricted_where] \n"
+		"               [-sql <sql statement>] \n" 
+		"               [-spat xmin ymin xmax ymax] [-preserve_fid] [-fid FID]\n"
+		"               [-a_srs srs_def] [-t_srs srs_def] [-s_srs srs_def]\n"
+		"               [-f format_name] [-overwrite] [[-dsco NAME=VALUE] ...]\n"
+		"               [-segmentize max_dist]\n"
+		"               dst_datasource_name src_datasource_name\n"
+		"               [-lco NAME=VALUE] [-nln name] [-nlt type] [layer [layer ...]]\n"
+		"\n"
+		" -f format_name: output file format name, possible values are:\n");
+    
+    for( int iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ )
+    {
+        OGRSFDriver *poDriver = poR->GetDriver(iDriver);
+
+        if( poDriver->TestCapability( ODrCCreateDataSource ) )
+            printf( "     -f \"%s\"\n", poDriver->GetName() );
+    }
+
+#ifdef ZOO_SERVICE
+	fprintf(stderr,
+#else
+	printf(
+#endif
+		" -append: Append to existing layer instead of creating new if it exists\n"
+		" -overwrite: delete the output layer and recreate it empty\n"
+		" -update: Open existing output datasource in update mode\n"
+		" -select field_list: Comma-delimited list of fields from input layer to\n"
+		"                     copy to the new layer (defaults to all)\n" 
+		" -where restricted_where: Attribute query (like SQL WHERE)\n" 
+		" -sql statement: Execute given SQL statement and save result.\n"
+		" -skipfailures: skip features or layers that fail to convert\n"
+		" -gt n: group n features per transaction (default 200)\n"
+		" -spat xmin ymin xmax ymax: spatial query extents\n"
+		" -segmentize max_dist: maximum distance between 2 nodes.\n"
+		"                       Used to create intermediate points\n"
+		" -dsco NAME=VALUE: Dataset creation option (format specific)\n"
+		" -lco  NAME=VALUE: Layer creation option (format specific)\n"
+		" -nln name: Assign an alternate name to the new layer\n"
+		" -nlt type: Force a geometry type for new layer.  One of NONE, GEOMETRY,\n"
+		"      POINT, LINESTRING, POLYGON, GEOMETRYCOLLECTION, MULTIPOINT,\n"
+		"      MULTIPOLYGON, or MULTILINESTRING.  Add \"25D\" for 3D layers.\n"
+		"      Default is type of source layer.\n" );
+
+#ifdef ZOO_SERVICE
+	fprintf(stderr,
+#else
+	printf(
+#endif
+		" -a_srs srs_def: Assign an output SRS\n"
+		" -t_srs srs_def: Reproject/transform to this SRS on output\n"
+		" -s_srs srs_def: Override source SRS\n"
+		"\n" 
+		" Srs_def can be a full WKT definition (hard to escape properly),\n"
+		" or a well known definition (ie. EPSG:4326) or a file with a WKT\n"
+		" definition.\n" );
+
+
+#ifndef ZOO_SERVICE
+	exit( 1 );
+#endif
+}
+
+/************************************************************************/
+/*                           TranslateLayer()                           */
+/************************************************************************/
+
+static int TranslateLayer( OGRDataSource *poSrcDS, 
+                           OGRLayer * poSrcLayer,
+                           OGRDataSource *poDstDS,
+                           char **papszLCO,
+                           const char *pszNewLayerName,
+                           int bTransform, 
+                           OGRSpatialReference *poOutputSRS,
+                           OGRSpatialReference *poSourceSRS,
+                           char **papszSelFields,
+                           int bAppend, int eGType, int bOverwrite,
+                           double dfMaxSegmentLength)
+		
+{
+    OGRLayer    *poDstLayer;
+    OGRFeatureDefn *poFDefn;
+    OGRErr      eErr;
+    int         bForceToPolygon = FALSE;
+    int         bForceToMultiPolygon = FALSE;
+
+    if( pszNewLayerName == NULL )
+        pszNewLayerName = poSrcLayer->GetLayerDefn()->GetName();
+
+    if( wkbFlatten(eGType) == wkbPolygon )
+        bForceToPolygon = TRUE;
+    else if( wkbFlatten(eGType) == wkbMultiPolygon )
+        bForceToMultiPolygon = TRUE;
+
+/* -------------------------------------------------------------------- */
+/*      Setup coordinate transformation if we need it.                  */
+/* -------------------------------------------------------------------- */
+    OGRCoordinateTransformation *poCT = NULL;
+
+    if( bTransform )
+    {
+        if( poSourceSRS == NULL )
+            poSourceSRS = poSrcLayer->GetSpatialRef();
+
+        if( poSourceSRS == NULL )
+        {
+            fprintf( stderr, "Can't transform coordinates, source layer has no\n"
+                    "coordinate system.  Use -s_srs to set one.\n" );
+#ifdef ZOO_SERVICE
+            return SERVICE_FAILED;
+#else
+            exit( 1 );
+#endif
+        }
+
+        CPLAssert( NULL != poSourceSRS );
+        CPLAssert( NULL != poOutputSRS );
+
+        poCT = OGRCreateCoordinateTransformation( poSourceSRS, poOutputSRS );
+        if( poCT == NULL )
+        {
+            char        *pszWKT = NULL;
+
+            fprintf( stderr, "Failed to create coordinate transformation between the\n"
+                   "following coordinate systems.  This may be because they\n"
+                   "are not transformable, or because projection services\n"
+                   "(PROJ.4 DLL/.so) could not be loaded.\n" );
+            
+            poSourceSRS->exportToPrettyWkt( &pszWKT, FALSE );
+            fprintf( stderr,  "Source:\n%s\n", pszWKT );
+            
+            poOutputSRS->exportToPrettyWkt( &pszWKT, FALSE );
+            fprintf( stderr,  "Target:\n%s\n", pszWKT );
+#ifdef ZOO_SERVICE
+            return SERVICE_FAILED;
+#else
+            exit( 1 );
+#endif
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      Get other info.                                                 */
+/* -------------------------------------------------------------------- */
+    poFDefn = poSrcLayer->GetLayerDefn();
+    
+    if( poOutputSRS == NULL )
+        poOutputSRS = poSrcLayer->GetSpatialRef();
+
+/* -------------------------------------------------------------------- */
+/*      Find the layer.                                                 */
+/* -------------------------------------------------------------------- */
+    int iLayer = -1;
+    poDstLayer = NULL;
+
+    for( iLayer = 0; iLayer < poDstDS->GetLayerCount(); iLayer++ )
+    {
+        OGRLayer        *poLayer = poDstDS->GetLayer(iLayer);
+
+        if( poLayer != NULL 
+            && EQUAL(poLayer->GetLayerDefn()->GetName(),pszNewLayerName) )
+        {
+            poDstLayer = poLayer;
+            break;
+        }
+    }
+    
+/* -------------------------------------------------------------------- */
+/*      If the user requested overwrite, and we have the layer in       */
+/*      question we need to delete it now so it will get recreated      */
+/*      (overwritten).                                                  */
+/* -------------------------------------------------------------------- */
+    if( poDstLayer != NULL && bOverwrite )
+    {
+        if( poDstDS->DeleteLayer( iLayer ) != OGRERR_NONE )
+        {
+            fprintf( stderr, 
+                     "DeleteLayer() failed when overwrite requested.\n" );
+            return FALSE;
+        }
+        poDstLayer = NULL;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      If the layer does not exist, then create it.                    */
+/* -------------------------------------------------------------------- */
+    if( poDstLayer == NULL )
+    {
+        if( eGType == -2 )
+            eGType = poFDefn->GetGeomType();
+
+        if( !poDstDS->TestCapability( ODsCCreateLayer ) )
+        {
+            fprintf( stderr, 
+              "Layer %s not found, and CreateLayer not supported by driver.", 
+                     pszNewLayerName );
+            return FALSE;
+        }
+
+        CPLErrorReset();
+
+        poDstLayer = poDstDS->CreateLayer( pszNewLayerName, poOutputSRS,
+                                           (OGRwkbGeometryType) eGType, 
+                                           papszLCO );
+
+        if( poDstLayer == NULL )
+            return FALSE;
+
+        bAppend = FALSE;
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Otherwise we will append to it, if append was requested.        */
+/* -------------------------------------------------------------------- */
+    else if( !bAppend )
+    {
+        fprintf( stderr, "FAILED: Layer %s already exists, and -append not specified.\n"
+                "        Consider using -append, or -overwrite.\n",
+                pszNewLayerName );
+        return FALSE;
+    }
+    else
+    {
+        if( CSLCount(papszLCO) > 0 )
+        {
+            fprintf( stderr, "WARNING: Layer creation options ignored since an existing layer is\n"
+                    "         being appended to.\n" );
+        }
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Add fields.  Default to copy all field.                         */
+/*      If only a subset of all fields requested, then output only      */
+/*      the selected fields, and in the order that they were            */
+/*      selected.                                                       */
+/* -------------------------------------------------------------------- */
+    int         iField;
+
+    if (papszSelFields && !bAppend )
+    {
+        for( iField=0; papszSelFields[iField] != NULL; iField++)
+        {
+            int iSrcField = poFDefn->GetFieldIndex(papszSelFields[iField]);
+            if (iSrcField >= 0)
+                poDstLayer->CreateField( poFDefn->GetFieldDefn(iSrcField) );
+            else
+            {
+                fprintf( stderr, "Field '%s' not found in source layer.\n", 
+                        papszSelFields[iField] );
+                if( !bSkipFailures )
+                    return FALSE;
+            }
+        }
+    }
+    else if( !bAppend )
+    {
+        for( iField = 0; iField < poFDefn->GetFieldCount(); iField++ )
+            poDstLayer->CreateField( poFDefn->GetFieldDefn(iField) );
+    }
+
+/* -------------------------------------------------------------------- */
+/*      Transfer features.                                              */
+/* -------------------------------------------------------------------- */
+    OGRFeature  *poFeature;
+    int         nFeaturesInTransaction = 0;
+    
+    poSrcLayer->ResetReading();
+
+    if( nGroupTransactions )
+        poDstLayer->StartTransaction();
+
+    while( TRUE )
+    {
+        OGRFeature      *poDstFeature = NULL;
+
+        if( nFIDToFetch != OGRNullFID )
+        {
+            // Only fetch feature on first pass.
+            if( nFeaturesInTransaction == 0 )
+                poFeature = poSrcLayer->GetFeature(nFIDToFetch);
+            else
+                poFeature = NULL;
+        }
+        else
+            poFeature = poSrcLayer->GetNextFeature();
+        
+        if( poFeature == NULL )
+            break;
+
+        if( ++nFeaturesInTransaction == nGroupTransactions )
+        {
+            poDstLayer->CommitTransaction();
+            poDstLayer->StartTransaction();
+            nFeaturesInTransaction = 0;
+        }
+
+        CPLErrorReset();
+        poDstFeature = OGRFeature::CreateFeature( poDstLayer->GetLayerDefn() );
+
+        if( poDstFeature->SetFrom( poFeature, TRUE ) != OGRERR_NONE )
+        {
+            if( nGroupTransactions )
+                poDstLayer->CommitTransaction();
+            
+            CPLError( CE_Failure, CPLE_AppDefined,
+                      "Unable to translate feature %ld from layer %s.\n",
+                      poFeature->GetFID(), poFDefn->GetName() );
+            
+            OGRFeature::DestroyFeature( poFeature );
+            OGRFeature::DestroyFeature( poDstFeature );
+            return FALSE;
+        }
+
+        if( bPreserveFID )
+            poDstFeature->SetFID( poFeature->GetFID() );
+
+#ifndef GDAL_1_5_0
+        if (poDstFeature->GetGeometryRef() != NULL && dfMaxSegmentLength > 0)
+            poDstFeature->GetGeometryRef()->segmentize(dfMaxSegmentLength);
+#endif
+
+        if( poCT && poDstFeature->GetGeometryRef() != NULL )
+        {
+            eErr = poDstFeature->GetGeometryRef()->transform( poCT );
+            if( eErr != OGRERR_NONE )
+            {
+                if( nGroupTransactions )
+                    poDstLayer->CommitTransaction();
+
+                fprintf( stderr, "Failed to reproject feature %d (geometry probably out of source or destination SRS).\n", 
+                        (int) poFeature->GetFID() );
+                if( !bSkipFailures )
+                {
+                    OGRFeature::DestroyFeature( poFeature );
+                    OGRFeature::DestroyFeature( poDstFeature );
+                    return FALSE;
+                }
+            }
+        }
+
+        if( poDstFeature->GetGeometryRef() != NULL && bForceToPolygon )
+        {
+            poDstFeature->SetGeometryDirectly( 
+                OGRGeometryFactory::forceToPolygon(
+                    poDstFeature->StealGeometry() ) );
+        }
+                    
+        if( poDstFeature->GetGeometryRef() != NULL && bForceToMultiPolygon )
+        {
+            poDstFeature->SetGeometryDirectly( 
+                OGRGeometryFactory::forceToMultiPolygon(
+                    poDstFeature->StealGeometry() ) );
+        }
+                    
+        OGRFeature::DestroyFeature( poFeature );
+
+        CPLErrorReset();
+        if( poDstLayer->CreateFeature( poDstFeature ) != OGRERR_NONE 
+            && !bSkipFailures )
+        {
+            if( nGroupTransactions )
+                poDstLayer->RollbackTransaction();
+
+            OGRFeature::DestroyFeature( poDstFeature );
+            return FALSE;
+        }
+
+        OGRFeature::DestroyFeature( poDstFeature );
+    }
+
+    if( nGroupTransactions )
+        poDstLayer->CommitTransaction();
+
+/* -------------------------------------------------------------------- */
+/*      Cleaning                                                        */
+/* -------------------------------------------------------------------- */
+    delete poCT;
+
+    return TRUE;
+}
+
+#ifdef ZOO_SERVICE
+}
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-services/openoffice/cgi-env/Exporter.py
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/openoffice/cgi-env/Exporter.py	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/openoffice/cgi-env/Exporter.py	(revision 221)
@@ -0,0 +1,97 @@
+#
+# Author : Gérald FENOY
+#
+# Copyright 2008-2009 GeoLabs SARL. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+import uno
+import getopt, sys
+
+from unohelper import Base, systemPathToFileUrl, absolutize
+
+from com.sun.star.beans import PropertyValue
+from com.sun.star.script import CannotConvertException
+from com.sun.star.lang import IllegalArgumentException
+from com.sun.star.task import ErrorCodeIOException
+from com.sun.star.io import IOException, XOutputStream
+
+class OutputStream( Base, XOutputStream ):
+    def __init__( self ):
+        self.closed = 0
+    def closeOutput(self):
+        self.closed = 1
+    def writeBytes( self, seq ):
+        sys.stdout.write( seq.value )
+    def flush( self ):
+        pass
+
+def OdtConverter(conf,inputs,outputs):
+	# get the uno component context from the PyUNO runtime  
+	localContext = uno.getComponentContext()
+
+	# create the UnoUrlResolver 
+	# on a single line
+	resolver = 	localContext.ServiceManager.createInstanceWithContext	("com.sun.star.bridge.UnoUrlResolver", localContext )
+
+	# connect to the running office                                 
+	ctx = resolver.resolve( conf["oo"]["server"].replace("::","=")+";urp;StarOffice.ComponentContext" )
+	smgr = ctx.ServiceManager
+
+	# get the central desktop object
+	desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
+
+	# get the file name
+	adressDoc=systemPathToFileUrl(conf["main"]["dataPath"]+"/"+inputs["InputDoc"]["value"])
+
+	propFich=PropertyValue("Hidden", 0, True, 0),
+
+	myDocument=0
+	try:
+	    myDocument = desktop.loadComponentFromURL(adressDoc,"_blank",0,propFich)
+	except CannotConvertException, e:
+	    print >> sys.stderr,  'Impossible de convertir le fichier pour les raisons suivantes : \n'
+	    print >> sys.stderr,  e
+	    sys.exit(0)
+	except IllegalArgumentException, e:
+	    print >> sys.stderr,  'Impossible de convertir le fichier pour les 	raisons suivantes : \n'
+	    print >> sys.stderr,  e
+	    sys.exit(0)
+
+	outputDoc=systemPathToFileUrl(conf["main"]["tmpPath"]+"/"+inputs["OutputDoc"]["value"])
+
+	tmp=inputs["OutputDoc"]["value"].split('.');
+
+	outputFormat={"pdf": "writer_pdf_Export", "html": "HTML (StarWriter)","odt": "writer8","doc": "MS Word 97","rtf": "Rich Text Format"}
+
+	for i in range(len(outputFormat)) :
+	    if tmp[1]==outputFormat.keys()[i] :
+	        filterName=outputFormat[tmp[1]]
+	        prop1Fich = (
+	            PropertyValue( "FilterName" , 0, filterName , 0 ),
+		        PropertyValue( "Overwrite" , 0, True , 0 )
+	        )
+	        break
+
+	myDocument.storeToURL(outputDoc,prop1Fich)
+	myDocument.close(True)
+	ctx.ServiceManager
+	outputs["OutputedDocument"]={"value": inputs["OutputDoc"]["value"],"dataType": "string"}
+	return 3
Index: /tags/rel-1.2.0-rc1/zoo-services/openoffice/cgi-env/OdtConverter.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/openoffice/cgi-env/OdtConverter.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/openoffice/cgi-env/OdtConverter.zcfg	(revision 221)
@@ -0,0 +1,43 @@
+[OdtConverter]
+ Title = Convert raster data from one format to another. 
+ Abstract = Converts raster data between different formats.
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceType = Python
+ serviceProvider = Exporter
+ <MetaData>
+   title = My Demo
+ </MetaData>
+ <DataInputs>
+  [InputDoc]
+   Title = The input data source name
+   Abstract = The input data source name to use as source for convertion.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+   </LiteralData>
+  [OutputDoc]
+   Title = The output data source name
+   Abstract = The output data source name to use as source for convertion.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [OutputedDoc]
+   Title = The resulting converted file
+   Abstract = The file name resulting of the convertion
+   <LiteralData>
+    DataType = string
+    <Default>
+    </Default>	
+   </LiteralData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/utils/status/Makefile
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/utils/status/Makefile	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/utils/status/Makefile	(revision 221)
@@ -0,0 +1,10 @@
+CFLAGS=-I../../../zoo-kernel/ -I./ `xslt-config --cflags` `xml2-config --cflags` -DLINUX_FREE_ISSUE #-DDEBUG
+# if JS_ENABLED flag is set to true in your ZOO-Kernel Makefile then you'll have
+# uncomment the following line
+# JS_LDFLAGS=../../../zoo-kernel/ulinet.o ../../../zoo-kernel/service_internal_js.o
+
+cgi-env/wps_status.zo: service.c
+	g++ ${CFLAGS} -shared -fpic -o cgi-env/wps_status.zo ./service.c ../../../zoo-kernel/service_internal.o ${JS_LDFLAGS} `xml2-config --libs` `xslt-config --libs`
+
+clean:
+	rm -f cgi-env/wps_status.zo
Index: /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/GetStatus.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/GetStatus.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/GetStatus.zcfg	(revision 221)
@@ -0,0 +1,43 @@
+[GetStatus]
+ Title = Produce an updated ExecuteResponse document. 
+ Abstract = Create an ExecuteResponse document from a sid (Service ID), it will use the niternal ZOO Kernel mechanisms to access the current status from a running Service and update the percentCompleted from the original backup file used by the ZOO Kernel when running a Service in background. 
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = wps_status.zo
+ serviceType = C
+ <MetaData>
+   title = Demo GetStatus request
+ </MetaData>
+ <DataInputs>
+  [sid]
+   Title = Service ID
+   Abstract = The ZOO Service ID of the ZOO Service we want to get the current status.
+   minOccurs = 1
+   maxOccurs = 1
+   <LiteralData>
+    DataType = integer
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = ExecuteResponse document
+   Abstract = The resulting ExecuteResponse document.
+   <MetaData>
+    title = Demo XSL use case
+   </MetaData>   
+   <ComplexData>
+    <Default>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd
+    </Default>
+    <Supported>
+     mimeType = text/xml
+     encoding = UTF-8
+     schema = http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd
+    </Supported>
+   </ComplexData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/longProcess.zcfg
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/longProcess.zcfg	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/longProcess.zcfg	(revision 221)
@@ -0,0 +1,36 @@
+[longProcess]
+ Title = Demo long process. 
+ Abstract = This service doesn't do anything except taking its time, it demonstrates how to use the updateStatus function from your ZOO Service. 
+ processVersion = 1
+ storeSupported = true
+ statusSupported = true
+ serviceProvider = wps_status.zo
+ serviceType = C
+ <MetaData>
+   title = Demo GetStatus request
+ </MetaData>
+ <DataInputs>
+  [sid]
+   Title = Service ID
+   Abstract = A ZOO Service ID (unused).
+   minOccurs = 0
+   maxOccurs = 1
+   <LiteralData>
+    dataType = integer
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataInputs>
+ <DataOutputs>
+  [Result]
+   Title = ExecuteResponse document
+   Abstract = The resulting ExecuteResponse document.
+   <MetaData>
+    title = Demo XSL use case
+   </MetaData>   
+   <LiteralData>
+    dataType = string
+    <Default>
+    </Default>
+   </LiteralData>
+ </DataOutputs>  
Index: /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/updateStatus.xsl
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/updateStatus.xsl	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/utils/status/cgi-env/updateStatus.xsl	(revision 221)
@@ -0,0 +1,22 @@
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:ows="http://www.opengis.net/ows/1.1"
+                xmlns:wps="http://www.opengis.net/wps/1.0.0"
+                xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <xsl:output method="xml"/>
+  <xsl:param name="value" select="string('-1')"/>
+
+  <xsl:template match="@*|node()">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()"/>
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template match="/wps:ExecuteResponse/wps:Status/wps:ProcessStarted/@percentCompleted">
+    <xsl:attribute name="percentCompleted">
+      <xsl:value-of select="$value"/>
+    </xsl:attribute>
+  </xsl:template>
+
+</xsl:stylesheet>
Index: /tags/rel-1.2.0-rc1/zoo-services/utils/status/locale/po/fr_FR.utf8.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/utils/status/locale/po/fr_FR.utf8.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/utils/status/locale/po/fr_FR.utf8.po	(revision 221)
@@ -0,0 +1,91 @@
+# French translations for PACKAGE package.
+# Copyright (C) 2010 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# root <gerald.fenoy@geolabs.fr>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: zoo-services\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-30 03:46+0200\n"
+"PO-Revision-Date: 2010-09-30 03:50+0200\n"
+"Last-Translator: root <gerald.fenoy@geolabs.fr>\n"
+"Language-Team: French\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: ../.cache/my_service_string_to_translate.c:1
+msgid "Produce an updated ExecuteResponse document. "
+msgstr ""
+"Produit un document ExecuteResponse à jour par rapport à l'exécution d'un service en "
+"tâche de fond."
+
+#: ../.cache/my_service_string_to_translate.c:2
+#: ../.cache/my_service_string_to_translate.c:8
+msgid "Service ID"
+msgstr "identifiant de service"
+
+#: ../.cache/my_service_string_to_translate.c:3
+#: ../.cache/my_service_string_to_translate.c:9
+msgid "ExecuteResponse document"
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:4
+msgid ""
+"Create an ExecuteResponse document from a sid (Service ID), it will use the "
+"niternal ZOO Kernel mechanisms to access the current status from a running "
+"Service and update the percentCompleted from the original backup file used "
+"by the ZOO Kernel when running a Service in background. "
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:5
+msgid ""
+"The ZOO Service ID of the ZOO Service we want to get the current status."
+msgstr ""
+"L'identifiant de Service ZOO dont ont veut obetnir le status."
+
+#: ../.cache/my_service_string_to_translate.c:6
+#: ../.cache/my_service_string_to_translate.c:12
+msgid "The resulting ExecuteResponse document."
+msgstr "Le document ExecuteResponse resultant."
+
+#: ../.cache/my_service_string_to_translate.c:7
+msgid "Demo long process. "
+msgstr "Service de démonstration."
+
+#: ../.cache/my_service_string_to_translate.c:10
+msgid ""
+"This service doesn't do anything except taking its time, it demonstrates how "
+"to use the updateStatus function from your ZOO Service. "
+msgstr ""
+"Ce service se contente de prendre son temps, il permet de montrer comment utiliser "
+"la fonction updateStatus depuis vos services ZOO."
+
+#: ../.cache/my_service_string_to_translate.c:11
+msgid "A ZOO Service ID (unused)."
+msgstr "Un identifiant de Service ZOO."
+
+#: ../.cache/my_service_string_to_translate.c:94
+#, c-format
+msgid ""
+"GetStatus was unable to use the tmpPath value set in main.cfg file as "
+"directory %s."
+msgstr ""
+"Le service GetStatus n'a pas été en mesure d'accéder au répertoire correspondant à la "
+"variable tmpPath définie dans le fichier main.cfg comme : %s."
+
+#: ../.cache/my_service_string_to_translate.c:100
+#, c-format
+msgid "GetStatus was unable to find any cache file for Service ID %s."
+msgstr "Le service GetStatus n'a pas été en mesure de trouver un fichier pour l'identifiant de Servicer ZOO %s."
+
+#: ../.cache/my_service_string_to_translate.c:126
+#, c-format
+msgid ""
+"ZOO GetStatus Service was unable to parse the cache xml file available for "
+"the Service ID %s."
+msgstr ""
+"Le Service ZOO GetStatus n'a pas été en mesure de charger le fichier xml pour "
+"l'identifiant %s."
Index: /tags/rel-1.2.0-rc1/zoo-services/utils/status/locale/po/messages.po
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/utils/status/locale/po/messages.po	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/utils/status/locale/po/messages.po	(revision 221)
@@ -0,0 +1,82 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: zoo-services\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-30 03:46+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../.cache/my_service_string_to_translate.c:1
+msgid "Produce an updated ExecuteResponse document. "
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:2
+#: ../.cache/my_service_string_to_translate.c:8
+msgid "Service ID"
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:3
+#: ../.cache/my_service_string_to_translate.c:9
+msgid "ExecuteResponse document"
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:4
+msgid ""
+"Create an ExecuteResponse document from a sid (Service ID), it will use the "
+"niternal ZOO Kernel mechanisms to access the current status from a running "
+"Service and update the percentCompleted from the original backup file used "
+"by the ZOO Kernel when running a Service in background. "
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:5
+msgid ""
+"The ZOO Service ID of the ZOO Service we want to get the current status."
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:6
+#: ../.cache/my_service_string_to_translate.c:12
+msgid "The resulting ExecuteResponse document."
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:7
+msgid "Demo long process. "
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:10
+msgid ""
+"This service doesn't do anything except taking its time, it demonstrates how "
+"to use the updateStatus function from your ZOO Service. "
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:11
+msgid "A ZOO Service ID (unused)."
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:94
+#, c-format
+msgid ""
+"GetStatus was unable to use the tmpPath value set in main.cfg file as "
+"directory %s."
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:100
+#, c-format
+msgid "GetStatus was unable to find any cache file for Service ID %s."
+msgstr ""
+
+#: ../.cache/my_service_string_to_translate.c:126
+#, c-format
+msgid ""
+"ZOO GetStatus Service was unable to parse the cache xml file available for "
+"the Service ID %s."
+msgstr ""
Index: /tags/rel-1.2.0-rc1/zoo-services/utils/status/makefile.vc
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/utils/status/makefile.vc	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/utils/status/makefile.vc	(revision 221)
@@ -0,0 +1,12 @@
+ZOODIR=../../../zoo-kernel
+THIRDSDIR=../../../thirds
+!INCLUDE $(ZOODIR)/nmake.opt
+CFLAGS=-DWIN32 -I$(THIRDSDIR)/dirent-win32 -I$(GEODIR)/include -I$(TPATH)/include -I$(ZOODIR)/ -I./ -DLINUX_FREE_ISSUE -DDEBUG
+CPP=cl /TP 
+
+cgi-env/wps_status.zo: service.c
+	$(CPP) $(CFLAGS) /c service.c
+	link /dll /out:cgi-env/wps_status.zo $(ZOODIR)/service_internal.obj $(ZOODIR)/dirent.obj ./service.obj -L$(TPATH)/lib/libssl32.dll.a $(TPATH)/lib/libxslt.lib $(TPATH)/lib/libxml2.lib $(TPATH)/lib/libeay32.dll.a $(TPATH)/lib/libcrypto.a $(TOOLS)/lib/libssl32.dll.a $(LIBINTL_CPATH)/lib/libintl.lib
+
+clean:
+	del /f cgi-env\wps*
Index: /tags/rel-1.2.0-rc1/zoo-services/utils/status/service.c
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-services/utils/status/service.c	(revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-services/utils/status/service.c	(revision 221)
@@ -0,0 +1,151 @@
+/**
+ * Author : Gérald FENOY
+ *
+ * Copyright 2008-2009 GeoLabs SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "service.h"
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+extern "C" {
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+
+#include <dirent.h>
+#include "service_internal.h"
+
+  /**
+   * GetStatus ZOO Service :
+   * This service is used in the ZOO-Project to get information about Services
+   * running as background tasks. The service will first get the XML document 
+   * cached by the ZOO-Kernel before calling effectively the Service, then 
+   * will access the shared memory space created by the Kernel to extract the 
+   * current status of the running Service. Using a simple XSL file it will 
+   * finally produce the final ExecuteResponse including the updated 
+   * percentCompleted attribute of the ProcessStarted node of the cached
+   * document if any (so if the Service is currently running) else it will 
+   * return the final ExecuteResponse stored on the Server file system.
+   */
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int GetStatus(maps*& conf,maps*& inputs,maps*& outputs){
+    const char *params[2 + 1];
+    int xmlLoadExtDtdDefaultValue;
+    map* tmpMap=NULL,*tmpMmap=NULL, *tmpTmap=NULL;
+    tmpMap=getMapFromMaps(inputs,"sid","value");
+    tmpTmap=getMapFromMaps(conf,"main","tmpPath");
+    tmpMmap=getMapFromMaps(conf,"main","dataPath");
+    xmlInitParser();
+    struct dirent *dp;
+    DIR *dirp = opendir(tmpTmap->value);
+    char fileName[1024],xslFileName[1024];
+    int hasFile=-1;
+    if(dirp!=NULL){
+      char tmp[128];
+      sprintf(tmp,"_%s.xml",tmpMap->value);
+      while ((dp = readdir(dirp)) != NULL)
+	if(strstr(dp->d_name,tmp)!=0){
+	  sprintf(fileName,"%s/%s",tmpTmap->value,dp->d_name);
+	  hasFile=1;
+	}
+    }else{
+      char tmp[1024];
+      snprintf(tmp,1024,_ss("GetStatus was unable to use the tmpPath value set in main.cfg file as directory %s."),tmpTmap->value);
+      setMapInMaps(conf,"lenv","message",tmp);
+      return SERVICE_FAILED;
+    }
+    if(hasFile<0){
+      char tmp[1024];
+      snprintf(tmp,1024,_ss("GetStatus was unable to find any cache file for Service ID %s."),tmpMap->value);
+      setMapInMaps(conf,"lenv","message",tmp);
+      return SERVICE_FAILED;
+    }
+    sprintf(xslFileName,"%s/updateStatus.xsl",tmpMmap->value);
+    xmlSubstituteEntitiesDefault(1);
+    xmlLoadExtDtdDefaultValue = 0;
+    xsltStylesheetPtr cur = NULL;
+    xmlDocPtr doc, res;
+    cur = xsltParseStylesheetFile(BAD_CAST xslFileName);
+    doc = xmlParseFile(fileName);
+    if(cur!=NULL && doc!=NULL){
+      params[0]="value";
+      params[1]=getStatus(atoi(tmpMap->value));
+      params[2]=NULL;
+      res = xsltApplyStylesheet(cur, doc, params);
+      xmlChar *xmlbuff;
+      int buffersize;
+      xmlDocDumpFormatMemory(res, &xmlbuff, &buffersize, 1);
+      setMapInMaps(outputs,"Result","value",(char*)xmlbuff);
+      setMapInMaps(outputs,"Result","mimeType","text/xml");
+      setMapInMaps(outputs,"Result","encoding","UTF-8");
+      xmlFree(xmlbuff);
+    }
+    else{
+      char tmp[1024];
+      sprintf(tmp,_ss("ZOO GetStatus Service was unable to parse the cache xml file available for the Service ID %s."),tmpMap->value);
+      setMapInMaps(conf,"lenv","message",tmp);
+      return SERVICE_FAILED;
+    }
+    return SERVICE_SUCCEEDED;
+  }
+
+
+  /**
+   * longProcess ZOO Service :
+   * Simple Service which just loop over 100 times then return a welcome message
+   * string, at each step the service will sleep for one second.
+   */
+#ifdef WIN32
+  __declspec(dllexport)
+#endif
+  int longProcess(maps*& conf,maps*& inputs,maps*& outputs){
+    int i=0;
+    while(i<100){
+      char tmp[4];
+      sprintf(tmp,"%i",i);
+      map* tmpMap=NULL;
+      tmpMap=getMapFromMaps(conf,"lenv","sid");
+      if(tmpMap!=NULL)
+	fprintf(stderr,"Status %s %s\n",tmpMap->value,tmp);
+      setMapInMaps(conf,"lenv","status",tmp);
+      updateStatus(conf);
+#ifndef WIN32
+      sleep(1);
+#else
+      Sleep(1000);
+#endif
+      i+=5;
+    }
+    setMapInMaps(outputs,"Result","value","\"Running long process successfully\"");
+    return SERVICE_SUCCEEDED;
+  }
+
+}
