You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2009/11/26 20:29:36 UTC

svn commit: r884671 - in /couchdb/trunk: ./ src/couchdb/ src/couchdb/priv/ src/couchdb/priv/couch_js/ src/couchdb/priv/icu_driver/ src/couchdb/priv/spawnkillable/ test/etap/

Author: davisp
Date: Thu Nov 26 19:29:35 2009
New Revision: 884671

URL: http://svn.apache.org/viewvc?rev=884671&view=rev
Log:
Move all C code to src/couchdb/priv

Shuffling bits around to conform to Erlang conventions.


Added:
    couchdb/trunk/src/couchdb/priv/couch_js/
    couchdb/trunk/src/couchdb/priv/couch_js/couch_js.c
    couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.c
    couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.h
    couchdb/trunk/src/couchdb/priv/icu_driver/
    couchdb/trunk/src/couchdb/priv/icu_driver/couch_icu_driver.c
    couchdb/trunk/src/couchdb/priv/spawnkillable/
    couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable.sh
    couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c
    couchdb/trunk/test/etap/002-icu-driver.t
Removed:
    couchdb/trunk/src/couchdb/couch_erl_driver.c
    couchdb/trunk/src/couchdb/couch_js.c
    couchdb/trunk/src/couchdb/curlhelper.c
    couchdb/trunk/src/couchdb/curlhelper.h
    couchdb/trunk/src/couchdb/priv/couchspawnkillable.sh
    couchdb/trunk/src/couchdb/priv/couchspawnkillable_win.c
    couchdb/trunk/test/etap/002-erl-driver.t
Modified:
    couchdb/trunk/.gitignore
    couchdb/trunk/license.skip
    couchdb/trunk/src/couchdb/Makefile.am
    couchdb/trunk/src/couchdb/couch_util.erl
    couchdb/trunk/src/couchdb/priv/Makefile.am

Modified: couchdb/trunk/.gitignore
URL: http://svn.apache.org/viewvc/couchdb/trunk/.gitignore?rev=884671&r1=884670&r2=884671&view=diff
==============================================================================
--- couchdb/trunk/.gitignore (original)
+++ couchdb/trunk/.gitignore Thu Nov 26 19:29:35 2009
@@ -49,12 +49,14 @@
 etc/logrotate.d/couchdb
 src/couchdb/.libs/*
 src/couchdb/couch.app
-src/couchdb/couch_erl_driver.la
 src/couchdb/couchjs
 src/couchdb/edoc-info
 src/couchdb/erlang.png
 src/couchdb/stylesheet.css
 src/couchdb/priv/.deps/
+src/couchdb/priv/.libs/
+src/couchdb/priv/couch_icu_driver.la
+src/couchdb/priv/couchjs
 src/couchdb/priv/couchspawnkillable
 test/local.ini
 test/etap/test_util.erl

Modified: couchdb/trunk/license.skip
URL: http://svn.apache.org/viewvc/couchdb/trunk/license.skip?rev=884671&r1=884670&r2=884671&view=diff
==============================================================================
--- couchdb/trunk/license.skip (original)
+++ couchdb/trunk/license.skip Thu Nov 26 19:29:35 2009
@@ -53,17 +53,19 @@
 ^share/www/script/test/lorem*
 ^src/Makefile
 ^src/Makefile.in
-^src/couchdb/*.beam
+^src/couchdb/.*beam
 ^src/couchdb/couch.app.tpl.in
 ^src/couchdb/.deps/*
-^src/couchdb/.libs/*
+^src/couchdb/couch.app*
 ^src/couchdb/Makefile
 ^src/couchdb/Makefile.in
-^src/couchdb/*.o
 ^src/couchdb/priv/.deps/*
+^src/couchdb/priv/.*o
 ^src/couchdb/priv/Makefile
 ^src/couchdb/priv/Makefile.in
+^src/couchdb/priv/couchjs
 ^src/couchdb/priv/couchspawnkillable
+^src/couchdb/priv/couch_icu_driver.la
 ^src/couchdb/priv/stat_descriptions.cfg
 ^src/erlang-oauth/*
 ^src/etap/*

Modified: couchdb/trunk/src/couchdb/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/Makefile.am?rev=884671&r1=884670&r2=884671&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/Makefile.am (original)
+++ couchdb/trunk/src/couchdb/Makefile.am Thu Nov 26 19:29:35 2009
@@ -12,38 +12,12 @@
 
 SUBDIRS = priv
 
-ICU_LOCAL_FLAGS = $(ICU_LOCAL_CFLAGS) $(ICU_LOCAL_LDFLAGS)
-if WINDOWS
-ICU_LOCAL_LIBS=-licuuc -licudt -licuin
-else
-ICU_LOCAL_LIBS=-licuuc -licudata -licui18n
-endif
-
 # devdocdir = $(localdocdir)/developer/couchdb
 couchlibdir = $(localerlanglibdir)/couch-$(version)
-couchprivdir = $(couchlibdir)/priv
-couchprivlibdir = $(couchlibdir)/priv/lib
 couchincludedir = $(couchlibdir)/include
 couchebindir = $(couchlibdir)/ebin
 
-couchprivlib_LTLIBRARIES = couch_erl_driver.la
-couch_erl_driver_la_SOURCES = couch_erl_driver.c
-couch_erl_driver_la_LDFLAGS = -module -avoid-version $(ICU_LOCAL_FLAGS)
-couch_erl_driver_la_CFLAGS = $(ICU_LOCAL_FLAGS)
-couch_erl_driver_la_LIBADD = $(ICU_LOCAL_LIBS)
-
-locallibbin_PROGRAMS = couchjs
-couchjs_SOURCES = couch_js.c curlhelper.c curlhelper.h
-couchjs_LDFLAGS = $(CURL_LDFLAGS)
-couchjs_CFLAGS = $(CURL_CFLAGS)
-couchjs_LDADD = $(CURL_LDFLAGS) @JSLIB@
-
-if WINDOWS
-couch_erl_driver_la_LDFLAGS += -no-undefined
-endif
-
 couchinclude_DATA = couch_db.hrl
-
 couchebin_DATA = $(compiled_files)
 
 # dist_devdoc_DATA = $(doc_base) $(doc_modules)
@@ -219,20 +193,3 @@
 %.beam: %.erl couch_db.hrl
 	$(ERLC) $(ERLC_FLAGS) ${TEST} $<;
 
-install-data-hook:
-	if test -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver"; then \
-	    rm -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver.so"; \
-	    cd "$(DESTDIR)$(couchprivlibdir)" && \
-	        $(LN_S) couch_erl_driver couch_erl_driver.so; \
-	fi
-if WINDOWS
-	$(INSTALL) $(ICU_LOCAL_BIN)/icuuc42.dll $(bindir)
-	$(INSTALL) $(ICU_LOCAL_BIN)/icudt42.dll $(bindir)
-	$(INSTALL) $(ICU_LOCAL_BIN)/icuin42.dll $(bindir)
-	$(INSTALL) $(JS_LIB_BINARY) $(bindir)
-endif
-
-uninstall-local:
-	if test -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver"; then \
-	    rm -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver.so"; \
-	fi

Modified: couchdb/trunk/src/couchdb/couch_util.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_util.erl?rev=884671&r1=884670&r2=884671&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_util.erl (original)
+++ couchdb/trunk/src/couchdb/couch_util.erl Thu Nov 26 19:29:35 2009
@@ -39,11 +39,11 @@
     end.
 
 start_driver(LibDir) ->
-    case erl_ddll:load_driver(LibDir, "couch_erl_driver") of
+    case erl_ddll:load_driver(LibDir, "couch_icu_driver") of
     ok ->
         ok;
     {error, already_loaded} ->
-        ok = erl_ddll:reload_driver(LibDir, "couch_erl_driver");
+        ok = erl_ddll:reload_driver(LibDir, "couch_icu_driver");
     {error, Error} ->
         exit(erl_ddll:format_error(Error))
     end.
@@ -191,7 +191,7 @@
 drv_port() ->
     case get(couch_drv_port) of
     undefined ->
-        Port = open_port({spawn, "couch_erl_driver"}, []),
+        Port = open_port({spawn, "couch_icu_driver"}, []),
         put(couch_drv_port, Port),
         Port;
     Port ->

Modified: couchdb/trunk/src/couchdb/priv/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/Makefile.am?rev=884671&r1=884670&r2=884671&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/priv/Makefile.am (original)
+++ couchdb/trunk/src/couchdb/priv/Makefile.am Thu Nov 26 19:29:35 2009
@@ -12,6 +12,7 @@
 
 couchlibdir = $(localerlanglibdir)/couch-$(version)
 couchprivdir = $(couchlibdir)/priv
+couchprivlibdir = $(couchlibdir)/priv/lib
 
 EXTRA_DIST = \
 	couchspawnkillable.sh \
@@ -26,21 +27,36 @@
 	cp $< $@
 
 if WINDOWS
-couchspawnkillable_SOURCES = couchspawnkillable_win.c
+couchspawnkillable_SOURCES = spawnkillable/couchspawnkillable_win.c
 endif
 
 if !WINDOWS
-couchspawnkillable: couchspawnkillable.sh
+couchspawnkillable: spawnkillable/couchspawnkillable.sh
 	cp $< $@
 endif
 
-if WINDOWS
-install-data-hook:
 # libtool and automake have defeated markh.  For each of our executables
 # we end up with 2 copies - one directly in the 'target' folder (eg, 'priv')
 # and another - the correct one - in .libs.  The former doesn't work but is 
 # what gets installed for 'couchspawnkillable' - but the correct one for
 # couchjs.exe *does* get copied.  *shrug*  So just clobber it with the 
-# correct one here... See bug COUCHDB-439
-	$(INSTALL) .libs/couchspawnkillable.exe "$(DESTDIR)$(couchprivdir)/couchspawnkillable.exe"
+# correct one as the last step. See bug COUCHDB-439
+install-data-hook:
+	if test -f "$(DESTDIR)$(couchprivlibdir)/couch_icu_driver"; then \
+	    rm -f "$(DESTDIR)$(couchprivlibdir)/couch_icu_driver.so"; \
+	    cd "$(DESTDIR)$(couchprivlibdir)" && \
+	        $(LN_S) couch_icu_driver couch_icu_driver.so; \
+	fi
+if WINDOWS
+	$(INSTALL) $(ICU_LOCAL_BIN)/icuuc42.dll $(bindir)
+	$(INSTALL) $(ICU_LOCAL_BIN)/icudt42.dll $(bindir)
+	$(INSTALL) $(ICU_LOCAL_BIN)/icuin42.dll $(bindir)
+	$(INSTALL) $(JS_LIB_BINARY) $(bindir)
+	$(INSTALL) .libs/couchspawnkillable.exe \
+		"$(DESTDIR)$(couchprivdir)/couchspawnkillable.exe"
 endif
+
+uninstall-local:
+	if test -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver"; then \
+	    rm -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver.so"; \
+	fi

Added: couchdb/trunk/src/couchdb/priv/couch_js/couch_js.c
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_js/couch_js.c?rev=884671&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_js/couch_js.c (added)
+++ couchdb/trunk/src/couchdb/priv/couch_js/couch_js.c Thu Nov 26 19:29:35 2009
@@ -0,0 +1,1286 @@
+/*
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "curlhelper.h"
+#include <jsapi.h>
+#include <curl/curl.h>
+#include "config.h"
+
+#ifndef CURLOPT_COPYPOSTFIELDS
+   #define CURLOPT_COPYPOSTFIELDS 10165
+#endif
+
+int gExitCode = 0;
+int gStackChunkSize = 8L * 1024L;
+
+int
+EncodeChar(uint8 *utf8Buffer, uint32 ucs4Char) {
+    int utf8Length = 1;
+
+    if (ucs4Char < 0x80) {
+        *utf8Buffer = (uint8)ucs4Char;
+    } else {
+        int i;
+        uint32 a = ucs4Char >> 11;
+        utf8Length = 2;
+        while (a) {
+            a >>= 5;
+            utf8Length++;
+        }
+        i = utf8Length;
+        while (--i) {
+            utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);
+            ucs4Char >>= 6;
+        }
+        *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
+    }
+    return utf8Length;
+}
+
+JSBool
+EncodeString(const jschar *src, size_t srclen, char *dst, size_t *dstlenp) {
+    size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen;
+    jschar c, c2;
+    uint32 v;
+    uint8 utf8buf[6];
+
+    if (!dst)
+        dstlen = origDstlen = (size_t) -1;
+
+    while (srclen) {
+        c = *src++;
+        srclen--;
+        if ((c >= 0xDC00) && (c <= 0xDFFF))
+            goto badSurrogate;
+        if (c < 0xD800 || c > 0xDBFF) {
+            v = c;
+        } else {
+            if (srclen < 1)
+                goto bufferTooSmall;
+            c2 = *src++;
+            srclen--;
+            if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
+                c = c2;
+                goto badSurrogate;
+            }
+            v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
+        }
+        if (v < 0x0080) {
+            /* no encoding necessary - performance hack */
+            if (!dstlen)
+                goto bufferTooSmall;
+            if (dst)
+                *dst++ = (char) v;
+            utf8Len = 1;
+        } else {
+            utf8Len = EncodeChar(utf8buf, v);
+            if (utf8Len > dstlen)
+                goto bufferTooSmall;
+            if (dst) {
+                for (i = 0; i < utf8Len; i++)
+                    *dst++ = (char) utf8buf[i];
+            }
+        }
+        dstlen -= utf8Len;
+    }
+    *dstlenp = (origDstlen - dstlen);
+    return JS_TRUE;
+
+badSurrogate:
+    *dstlenp = (origDstlen - dstlen);
+    return JS_FALSE;
+
+bufferTooSmall:
+    *dstlenp = (origDstlen - dstlen);
+    return JS_FALSE;
+}
+
+static uint32
+DecodeChar(const uint8 *utf8Buffer, int utf8Length) {
+    uint32 ucs4Char;
+    uint32 minucs4Char;
+    /* from Unicode 3.1, non-shortest form is illegal */
+    static const uint32 minucs4Table[] = {
+        0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000
+    };
+
+    if (utf8Length == 1) {
+        ucs4Char = *utf8Buffer;
+    } else {
+        ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
+        minucs4Char = minucs4Table[utf8Length-2];
+        while (--utf8Length) {
+            ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
+        }
+        if (ucs4Char < minucs4Char ||
+            ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) {
+            ucs4Char = 0xFFFD;
+        }
+    }
+    return ucs4Char;
+}
+
+JSBool
+DecodeString(const char *src, size_t srclen, jschar *dst, size_t *dstlenp) {
+    uint32 v;
+    size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen;
+
+    if (!dst)
+        dstlen = origDstlen = (size_t) -1;
+
+    while (srclen) {
+        v = (uint8) *src;
+        n = 1;
+        if (v & 0x80) {
+            while (v & (0x80 >> n))
+                n++;
+            if (n > srclen)
+                goto bufferTooSmall;
+            if (n == 1 || n > 6)
+                goto badCharacter;
+            for (j = 1; j < n; j++) {
+                if ((src[j] & 0xC0) != 0x80)
+                    goto badCharacter;
+            }
+            v = DecodeChar((const uint8 *) src, n);
+            if (v >= 0x10000) {
+                v -= 0x10000;
+                if (v > 0xFFFFF || dstlen < 2) {
+                    *dstlenp = (origDstlen - dstlen);
+                    return JS_FALSE;
+                }
+                if (dstlen < 2)
+                    goto bufferTooSmall;
+                if (dst) {
+                    *dst++ = (jschar)((v >> 10) + 0xD800);
+                    v = (jschar)((v & 0x3FF) + 0xDC00);
+                }
+                dstlen--;
+            }
+        }
+        if (!dstlen)
+            goto bufferTooSmall;
+        if (dst)
+            *dst++ = (jschar) v;
+        dstlen--;
+        offset += n;
+        src += n;
+        srclen -= n;
+    }
+    *dstlenp = (origDstlen - dstlen);
+    return JS_TRUE;
+
+badCharacter:
+    *dstlenp = (origDstlen - dstlen);
+    return JS_FALSE;
+
+bufferTooSmall:
+    *dstlenp = (origDstlen - dstlen);
+    return JS_FALSE;
+}
+
+static JSBool
+EvalInContext(JSContext *context, JSObject *obj, uintN argc, jsval *argv,
+              jsval *rval) {
+    JSString *str;
+    JSObject *sandbox;
+    JSContext *sub_context;
+    const jschar *src;
+    size_t srclen;
+    JSBool ok;
+    jsval v;
+
+    sandbox = NULL;
+    if (!JS_ConvertArguments(context, argc, argv, "S / o", &str, &sandbox))
+        return JS_FALSE;
+
+    sub_context = JS_NewContext(JS_GetRuntime(context), gStackChunkSize);
+    if (!sub_context) {
+        JS_ReportOutOfMemory(context);
+        return JS_FALSE;
+    }
+
+#ifdef USE_JS_SETOPCB
+    JS_SetContextThread(sub_context);
+    JS_BeginRequest(sub_context);
+#endif
+
+    src = JS_GetStringChars(str);
+    srclen = JS_GetStringLength(str);
+
+    if (!sandbox) {
+        sandbox = JS_NewObject(sub_context, NULL, NULL, NULL);
+        if (!sandbox || !JS_InitStandardClasses(sub_context, sandbox)) {
+            ok = JS_FALSE;
+            goto out;
+        }
+    }
+
+    if (srclen == 0) {
+        *rval = OBJECT_TO_JSVAL(sandbox);
+        ok = JS_TRUE;
+    } else {
+        ok = JS_EvaluateUCScript(sub_context, sandbox, src, srclen, NULL, 0,
+                                 rval);
+        ok = JS_TRUE;
+    }
+
+out:
+#ifdef USE_JS_SETOPCB
+    JS_EndRequest(sub_context);
+    JS_ClearContextThread(sub_context);
+#endif
+
+    JS_DestroyContext(sub_context);
+    return ok;
+}
+
+static JSBool
+GC(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+    JS_GC(context);
+    return JS_TRUE;
+}
+
+static JSBool
+Print(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+    uintN i;
+    size_t cl, bl;
+    JSString *str;
+    jschar *chars;
+    char *bytes;
+
+    for (i = 0; i < argc; i++) {
+        str = JS_ValueToString(context, argv[i]);
+        if (!str)
+            return JS_FALSE;
+        chars = JS_GetStringChars(str);
+        cl = JS_GetStringLength(str);
+        if (!EncodeString(chars, cl, NULL, &bl))
+            return JS_FALSE;
+        bytes = JS_malloc(context, bl + 1);
+        bytes[bl] = '\0';
+        if (!EncodeString(chars, cl, bytes, &bl)) {
+            JS_free(context, bytes);
+            return JS_FALSE;
+        }
+        fprintf(stdout, "%s%s", i ? " " : "", bytes);
+        JS_free(context, bytes);
+    }
+
+    fputc('\n', stdout);
+    fflush(stdout);
+    return JS_TRUE;
+}
+
+static JSBool
+Quit(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+    JS_ConvertArguments(context, argc, argv, "/ i", &gExitCode);
+    return JS_FALSE;
+}
+
+static JSBool
+ReadLine(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+    char *bytes, *tmp;
+    jschar *chars;
+    size_t bufsize, byteslen, charslen, readlen;
+    JSString *str;
+
+    JS_MaybeGC(context);
+
+    byteslen = 0;
+    bufsize = 256;
+    bytes = JS_malloc(context, bufsize);
+    if (!bytes)
+        return JS_FALSE;
+
+    while ((readlen = js_fgets(bytes + byteslen, bufsize - byteslen, stdin)) > 0) {
+        byteslen += readlen;
+
+        /* Are we done? */
+        if (bytes[byteslen - 1] == '\n') {
+            bytes[byteslen - 1] = '\0';
+            break;
+        }
+
+        /* Else, grow our buffer for another pass */
+        tmp = JS_realloc(context, bytes, bufsize * 2);
+        if (!tmp) {
+            JS_free(context, bytes);
+            return JS_FALSE;
+        }
+
+        bufsize *= 2;
+        bytes = tmp;
+    }
+
+    /* Treat the empty string specially */
+    if (byteslen == 0) {
+        *rval = JS_GetEmptyStringValue(context);
+        JS_free(context, bytes);
+        return JS_TRUE;
+    }
+
+    /* Shrink the buffer to the real size */
+    tmp = JS_realloc(context, bytes, byteslen);
+    if (!tmp) {
+        JS_free(context, bytes);
+        return JS_FALSE;
+    }
+    bytes = tmp;
+
+    /* Decode the string from UTF-8 */
+    if (!DecodeString(bytes, byteslen, NULL, &charslen)) {
+        JS_free(context, bytes);
+        return JS_FALSE;
+    }
+    chars = JS_malloc(context, (charslen + 1) * sizeof(jschar));
+    if (!DecodeString(bytes, byteslen, chars, &charslen)) {
+        JS_free(context, bytes);
+        JS_free(context, chars);
+        return JS_FALSE;
+    }
+    JS_free(context, bytes);
+    chars[charslen] = '\0';
+
+    /* Initialize a JSString object */
+    str = JS_NewUCString(context, chars, charslen - 1);
+    if (!str) {
+        JS_free(context, chars);
+        return JS_FALSE;
+    }
+
+    *rval = STRING_TO_JSVAL(str);
+    return JS_TRUE;
+}
+
+static JSBool
+Seal(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+    JSObject *target;
+    JSBool deep = JS_FALSE;
+
+    if (!JS_ConvertArguments(context, argc, argv, "o/b", &target, &deep))
+        return JS_FALSE;
+    if (!target)
+        return JS_TRUE;
+    return JS_SealObject(context, target, deep);
+}
+
+static void
+ExecuteScript(JSContext *context, JSObject *obj, const char *filename) {
+    FILE *file;
+    JSScript *script;
+    jsval result;
+
+    if (!filename || strcmp(filename, "-") == 0) {
+        file = stdin;
+    } else {
+        file = fopen(filename, "r");
+        if (!file) {
+            fprintf(stderr, "could not open script file %s\n", filename);
+            gExitCode = 1;
+            return;
+        }
+    }
+
+    script = JS_CompileFileHandle(context, obj, filename, file);
+    if (script) {
+        JS_ExecuteScript(context, obj, script, &result);
+        JS_DestroyScript(context, script);
+    }
+}
+
+static uint32 gBranchCount = 0;
+
+#ifdef USE_JS_SETOPCB
+static JSBool
+OperationCallback(JSContext *context)
+{
+    if ((++gBranchCount & 0x3fff) == 1) {
+        JS_MaybeGC(context);
+    }
+    return JS_TRUE;
+}
+#else
+static JSBool
+BranchCallback(JSContext *context, JSScript *script) {
+    if ((++gBranchCount & 0x3fff) == 1) {
+        JS_MaybeGC(context);
+    }
+    return JS_TRUE;
+}
+#endif
+
+static void
+PrintError(JSContext *context, const char *message, JSErrorReport *report) {
+    if (!report || !JSREPORT_IS_WARNING(report->flags))
+        fprintf(stderr, "%s\n", message);
+}
+
+JSBool ThrowError(JSContext *cx, const char *message)
+{
+    void *mark;
+    jsval *args;
+    jsval exc;
+
+    printf("%s\n",message);
+
+    args = JS_PushArguments(cx, &mark, "s", message);
+    if (args) {
+        if (JS_CallFunctionName(cx, JS_GetGlobalObject(cx),
+                                "Error", 1, args, &exc))
+            JS_SetPendingException(cx, exc);
+        JS_PopArguments(cx, mark);
+    }
+
+    return JS_FALSE;
+}
+
+typedef struct buffer_counter {
+ Buffer buffer;
+ int pos;
+}* BufferCount;
+
+size_t curl_read(void *ptr, size_t size, size_t nmemb, void *stream) {
+  int readlength, spaceleft, i;
+  char* databuffer = (char*)ptr;
+  Buffer b = ((BufferCount)stream)->buffer;
+  int* pos = &(((BufferCount)stream)->pos);
+
+  if( size == 0 || nmemb == 0) {
+    return 0;
+  }
+
+  if((b->count - *pos) == 0) {
+    return 0;
+  }
+
+  readlength = size*nmemb;
+  spaceleft = b->count - *pos;
+
+  if(readlength < spaceleft) {
+    copy_Buffer(b,databuffer,*pos,readlength);
+    *(pos) += readlength;
+    return readlength;
+  } else {
+    copy_Buffer(b,databuffer,*pos,spaceleft);
+    *(pos) += spaceleft;
+    return spaceleft;
+  }
+}
+
+size_t curl_write(void *ptr, size_t size, size_t nmemb, void *stream) {
+  char *data, *tmp;
+  Buffer b;
+  if( size == 0 || nmemb == 0 )
+    return 0;
+
+  data = (char *)ptr;
+  b = (Buffer)stream;
+
+  append_Buffer(b,data,size*nmemb);
+
+  return size*nmemb;
+}
+
+// This uses MALLOC dont forget to free
+char* JSValToChar(JSContext* context, jsval* arg) {
+  char *c, *tmp;
+  JSString *jsmsg;
+  size_t len;
+  int i;
+  if(!JSVAL_IS_STRING(*arg)) {
+    return NULL;
+  }
+
+  jsmsg = JS_ValueToString(context,*arg);
+  len = JS_GetStringLength(jsmsg);
+  tmp = JS_GetStringBytes(jsmsg);
+
+  c = (char*)malloc(len+1);
+  c[len] = '\0';
+
+  for(i = 0;i < len;i++) {
+    c[i] = tmp[i];
+  }
+
+  return c;
+}
+
+JSBool BufferToJSVal(JSContext *context, Buffer b, jsval *rval) {
+  char* c;
+  JSString *str;
+
+  // Important for char* to be JS_malloced, otherwise js wont let you use it in the NewString method
+  c = JS_malloc(context, b->count * sizeof(char));
+  copy_Buffer(b,c,0,b->count);
+
+
+  /* Initialize a JSString object */
+  str = JS_NewString(context, c, b->count);
+
+  if (!str) {
+    JS_free(context, c);
+    return JS_FALSE;
+  }
+
+  // Set Return Value
+  *rval = STRING_TO_JSVAL(str);
+  if(rval == NULL) {
+    return JS_FALSE;
+  }
+  return JS_TRUE;
+}
+
+struct curl_slist* generateCurlHeaders(JSContext* context,jsval* arg) {
+  // If arg is an object then we go the header-hash route else return NULL
+
+  if(!JSVAL_IS_NULL(*arg)) {
+
+    struct curl_slist *slist = NULL;
+    JSObject* header_obj;
+    JSObject* iterator;
+    jsval *jsProperty;
+    jsval *jsValue;
+    jsid *jsId;
+    Buffer bTmp;
+    char* jsPropertyName, *jsPropertyValue;
+
+    // If we fail to convert arg2 to an object. Error!
+    if(!JS_ValueToObject(context,*arg,&header_obj)) {
+      return NULL;
+    }
+
+    iterator = JS_NewPropertyIterator(context,header_obj);
+
+    jsProperty = JS_malloc(context,sizeof(jsval));
+    jsValue = JS_malloc(context,sizeof(jsval));
+    jsId = JS_malloc(context,sizeof(jsid));
+
+    while(JS_NextProperty(context,iterator,jsId) == JS_TRUE) {
+
+      if(*jsId == JSVAL_VOID) {
+        break;
+      }
+
+      // TODO: Refactor this maybe make a JSValAppendBuffer method b/c that is what you really want to do.
+
+      bTmp = init_Buffer();
+      JS_IdToValue(context,*jsId,jsProperty);
+      jsPropertyName = JSValToChar(context,jsProperty);
+
+      // TODO: Remove strlen =/
+      append_Buffer(bTmp,jsPropertyName,strlen(jsPropertyName));
+      append_Buffer(bTmp,": ",2);
+
+      JS_GetProperty(context,header_obj,jsPropertyName,jsValue);
+      jsPropertyValue = JSValToChar(context,jsValue);
+      // TODO: Remove strlen =/
+      append_Buffer(bTmp,jsPropertyValue,strlen(jsPropertyValue));
+      append_Buffer(bTmp,"",1);
+
+      slist = curl_slist_append(slist,bTmp->data);
+
+      free_Buffer(bTmp);
+      free(jsPropertyValue);
+      free(jsPropertyName);
+    }
+
+    JS_free(context,jsProperty);
+    JS_free(context,jsValue);
+    JS_free(context,jsId);
+
+    return slist;
+
+  } else {
+    return NULL;
+  }
+}
+
+static JSBool
+GetHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+  CURL* handle;
+  Buffer b;
+  char *url;
+  size_t charslen, readlen;
+  struct curl_slist *slist;
+  int exitcode;
+
+  // Run GC
+  JS_MaybeGC(context);
+
+  // Init Curl
+  if((handle = curl_easy_init()) == NULL) {
+    return JS_FALSE;
+  }
+
+  // Get URL
+  url = JSValToChar(context,argv);
+  if( url == NULL ) {
+    return ThrowError(context,"Unable to convert url (argument 0) to a string");
+  }
+
+  b = init_Buffer(); // Allocate buffer that will store the get resultant
+
+  // Configuration
+  curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
+  curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
+  curl_easy_setopt(handle,CURLOPT_URL,url);
+  curl_easy_setopt(handle,CURLOPT_HTTPGET,1);
+  curl_easy_setopt(handle,CURLOPT_FOLLOWLOCATION,1);
+  curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
+  curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
+
+  slist = generateCurlHeaders(context,argv+1);
+  if(slist != NULL) {
+    curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
+  }
+
+  // Perform
+  if((exitcode = curl_easy_perform(handle)) != 0) {
+    if(slist != NULL) {
+      curl_slist_free_all(slist);
+    }
+    curl_easy_cleanup(handle);
+    free(url);
+    free_Buffer(b);
+    return JS_FALSE;
+  }
+
+  free(url);
+  if(slist != NULL) {
+    curl_slist_free_all(slist);
+  }
+
+  /* Treat the empty string specially */
+  if (b->count == 0) {
+    free_Buffer(b);
+    *rval = JS_GetEmptyStringValue(context);
+    curl_easy_cleanup(handle);
+    return JS_TRUE;
+  }
+
+  /* Shrink the buffer to the real size  and store its value in rval */
+  shrink_Buffer(b);
+  BufferToJSVal(context,b,rval);
+
+  // Free Buffer
+  free_Buffer(b);
+
+  if(rval == NULL) {
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  JS_MaybeGC(context);
+
+  curl_easy_cleanup(handle);
+
+  return JS_TRUE;
+}
+
+static JSBool
+HeadHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+  CURL* handle;
+  Buffer b;
+  char *url;
+  size_t charslen, readlen;
+  struct curl_slist *slist;
+  int exitcode;
+
+  // Run GC
+  JS_MaybeGC(context);
+
+  // Init Curl
+  if((handle = curl_easy_init()) == NULL) {
+    return JS_FALSE;
+  }
+
+  // Get URL
+  url = JSValToChar(context,argv);
+  if( url == NULL ) {
+    return ThrowError(context,"Unable to convert url (argument 0) to a string");
+  }
+
+  b = init_Buffer(); // Allocate buffer that will store the get resultant
+
+  // Configuration
+  // curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
+  // curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
+  curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
+  curl_easy_setopt(handle,CURLOPT_URL,url);
+  curl_easy_setopt(handle,CURLOPT_HTTPGET,0);
+  curl_easy_setopt(handle,CURLOPT_NOBODY,1);
+  curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
+  curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
+
+  slist = generateCurlHeaders(context,argv+1);
+  if(slist != NULL) {
+    curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
+  }
+
+  // fprintf(stderr, "about to run HEAD request\n");
+
+  // Perform
+  if((exitcode = curl_easy_perform(handle)) != 0) {
+    if(slist != NULL) {
+      curl_slist_free_all(slist);
+    }
+    curl_easy_cleanup(handle);
+    free(url);
+    free_Buffer(b);
+    return JS_FALSE;
+  }
+  // fprintf(stderr, "ran ok HEAD request\n");
+
+  free(url);
+  if(slist != NULL) {
+    curl_slist_free_all(slist);
+  }
+
+  /* Treat the empty string specially */
+  if (b->count == 0) {
+    free_Buffer(b);
+    *rval = JS_GetEmptyStringValue(context);
+    curl_easy_cleanup(handle);
+    return JS_TRUE;
+  }
+
+  /* Shrink the buffer to the real size  and store its value in rval */
+  shrink_Buffer(b);
+  BufferToJSVal(context,b,rval);
+
+  // Free Buffer
+  free_Buffer(b);
+
+  if(rval == NULL) {
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  JS_MaybeGC(context);
+
+  curl_easy_cleanup(handle);
+
+  return JS_TRUE;
+}
+
+
+static JSBool
+PostHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+  CURL* handle;
+  Buffer b;
+  char *url, *body;
+  size_t charslen, readlen;
+  struct curl_slist *slist;
+  int exitcode;
+
+  // Run GC
+  JS_MaybeGC(context);
+
+  // Init Curl
+  if((handle = curl_easy_init()) == NULL) {
+    return JS_FALSE;
+  }
+
+  // Get URL
+  if((url = JSValToChar(context,argv)) == NULL) {
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  // Initialize buffer
+  b = init_Buffer();
+
+  curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);    // function that recieves data
+  curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);                 // buffer to write the data to
+  curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
+  curl_easy_setopt(handle,CURLOPT_URL,url);                     // url
+  curl_easy_setopt(handle,CURLOPT_POST,1);                  // Set Op. to post
+  curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);                // No Progress Meter
+  curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4); // only ipv4
+
+  if((body = JSValToChar(context,argv+1)) == NULL) {            // Convert arg1 to a string
+    free(url);
+    free_Buffer(b);
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  curl_easy_setopt(handle,CURLOPT_POSTFIELDSIZE,strlen(body));
+  curl_easy_setopt(handle,CURLOPT_POSTFIELDS,body);         // Curl wants '\0' terminated, we oblige
+
+  slist = generateCurlHeaders(context,argv+2); // Initialize Headers
+  if(slist != NULL) {
+    curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
+  }
+
+  if((exitcode = curl_easy_perform(handle)) != 0) {             // Perform
+    curl_slist_free_all(slist);
+    free(body);
+    free(url);
+    free_Buffer(b);
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  free(body);
+  free(url);
+  curl_slist_free_all(slist);
+
+  // Convert response back to javascript value and then clean
+  BufferToJSVal(context,b,rval);
+  free_Buffer(b);
+  curl_easy_cleanup(handle);
+
+  JS_MaybeGC(context);
+
+  if( rval == NULL ) {
+    return JS_FALSE;
+  }
+
+  return JS_TRUE;
+}
+
+#define CLEAN \
+  free_Buffer(b); \
+  free_Buffer(b_data->buffer); \
+  free(b_data);                \
+  free(url)
+
+static JSBool
+PutHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
+
+  Buffer b;
+  BufferCount b_data;
+  char *url, *data;
+  size_t charslen, readlen;
+  JSObject* header_obj;
+  CURL* handle;
+  struct curl_slist *slist;
+  int exitcode;
+
+  // Run GC
+  JS_MaybeGC(context);
+
+  // Get URL
+  url = JSValToChar(context,argv);
+
+  // Allocate buffer that will store the get resultant
+  b = init_Buffer();
+
+  // Allocate data buffer and move data into them
+  b_data = (BufferCount)malloc(sizeof(Buffer) + sizeof(int));
+  b_data->buffer = init_Buffer();
+  b_data->pos = 0;
+
+  data = JSValToChar(context,(argv+1));
+  readlen = strlen(data);
+
+
+
+  // TODO: remove strlen
+  append_Buffer(b_data->buffer,data,readlen);
+
+  free(data);
+
+  // Init Curl
+
+  if((handle = curl_easy_init()) == NULL) {
+    CLEAN;
+    return JS_FALSE;
+  }
+
+  // Configuration
+  curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
+  curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
+  curl_easy_setopt(handle,CURLOPT_READFUNCTION,curl_read);
+  curl_easy_setopt(handle,CURLOPT_READDATA,b_data);
+  curl_easy_setopt(handle,CURLOPT_URL,url);
+  curl_easy_setopt(handle,CURLOPT_UPLOAD,1);
+  curl_easy_setopt(handle,CURLOPT_INFILESIZE,readlen);
+
+
+
+  // Curl structure
+  slist = generateCurlHeaders(context,argv+2);
+  if(slist != NULL) {
+    curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
+  }
+
+  // Little Things
+  // No progress meter
+  curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
+  // Use only ipv4
+  curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
+
+
+
+  // Perform
+  if((exitcode = curl_easy_perform(handle)) != 0) {
+    if(slist != NULL)
+      curl_slist_free_all(slist);
+    curl_easy_cleanup(handle);
+    CLEAN;
+    return JS_FALSE;
+  }
+
+  if(slist != NULL)
+    curl_slist_free_all(slist);
+  free_Buffer(b_data->buffer);
+  free(b_data);
+  free(url);
+
+  /* Treat the empty string specially */
+  if (b->count == 0) {
+    *rval = JS_GetEmptyStringValue(context);
+    curl_easy_cleanup(handle);
+    free_Buffer(b);
+    return JS_TRUE;
+  }
+
+  /* Shrink the buffer to the real size */
+  shrink_Buffer(b);
+
+  BufferToJSVal(context,b,rval);
+
+  free_Buffer(b);
+
+  if(rval == NULL) {
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  JS_MaybeGC(context);
+
+  curl_easy_cleanup(handle);
+
+  return JS_TRUE;
+}
+
+static JSBool
+DelHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+  Buffer b;
+  char *url;
+  size_t charslen, readlen;
+  char header_name[7];
+  CURL* handle;
+  int exitcode;
+  struct curl_slist *slist = NULL;
+
+  strcpy(header_name,"DELETE");
+
+  // Run GC
+  JS_MaybeGC(context);
+
+  // Get URL
+  url = JSValToChar(context,argv);
+
+  // Allocate buffer that will store the del resultant
+  b = init_Buffer();
+
+  // Init Curl
+  if((handle = curl_easy_init()) == NULL) {
+    free_Buffer(b);
+    return JS_FALSE;
+  }
+
+  // Configuration
+  curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
+  curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
+  curl_easy_setopt(handle,CURLOPT_URL,url);
+  curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name);
+  curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
+  curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
+
+  // Curl structure
+  if((slist = generateCurlHeaders(context,argv+1)) != NULL) {
+    curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
+  }
+
+  // Perform
+  if((exitcode = curl_easy_perform(handle)) != 0) {
+    if(slist != NULL)
+      curl_slist_free_all(slist);
+    curl_easy_cleanup(handle);
+    free(url);
+    free_Buffer(b);
+    return JS_FALSE;
+  }
+
+  if(slist != NULL)
+    curl_slist_free_all(slist);
+  free(url);
+
+  /* Treat the empty string specially */
+  if (b->count == 0) {
+    *rval = JS_GetEmptyStringValue(context);
+    curl_easy_cleanup(handle);
+    free_Buffer(b);
+    return JS_TRUE;
+  }
+
+  /* Shrink the buffer to the real size */
+  shrink_Buffer(b);
+
+  BufferToJSVal(context,b,rval);
+
+  if(rval == NULL) {
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  JS_MaybeGC(context);
+
+  curl_easy_cleanup(handle);
+
+  return JS_TRUE;
+}
+
+static JSBool
+CopyHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+  Buffer b;
+  char *url;
+  size_t charslen, readlen;
+  char header_name[5];
+  CURL* handle;
+  int exitcode;
+  struct curl_slist *slist = NULL;
+
+  strcpy(header_name,"COPY");
+
+  // Run GC
+  JS_MaybeGC(context);
+
+  // Get URL
+  url = JSValToChar(context,argv);
+
+  // Allocate buffer that will store the del resultant
+  b = init_Buffer();
+
+  // Init Curl
+  if((handle = curl_easy_init()) == NULL) {
+    free_Buffer(b);
+    return JS_FALSE;
+  }
+
+  // Configuration
+  curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
+  curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
+  curl_easy_setopt(handle,CURLOPT_URL,url);
+  curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name);
+  curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
+  curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
+
+  // Curl structure
+  if((slist = generateCurlHeaders(context,argv+1)) != NULL) {
+    curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
+  }
+
+  // Perform
+  if((exitcode = curl_easy_perform(handle)) != 0) {
+    if(slist != NULL)
+      curl_slist_free_all(slist);
+    curl_easy_cleanup(handle);
+    free(url);
+    free_Buffer(b);
+    return JS_FALSE;
+  }
+
+  if(slist != NULL)
+    curl_slist_free_all(slist);
+  free(url);
+
+  /* Treat the empty string specially */
+  if (b->count == 0) {
+    *rval = JS_GetEmptyStringValue(context);
+    curl_easy_cleanup(handle);
+    free_Buffer(b);
+    return JS_TRUE;
+  }
+
+  /* Shrink the buffer to the real size */
+  shrink_Buffer(b);
+
+  BufferToJSVal(context,b,rval);
+
+  if(rval == NULL) {
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  JS_MaybeGC(context);
+
+  curl_easy_cleanup(handle);
+
+  return JS_TRUE;
+}
+
+static JSBool
+MoveHttp(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
+  Buffer b;
+  char *url;
+  size_t charslen, readlen;
+  char header_name[5];
+  CURL* handle;
+  struct curl_slist *slist = NULL;
+  int exitcode;
+
+  strcpy(header_name,"MOVE");
+
+  // Run GC
+  JS_MaybeGC(context);
+
+  // Get URL
+  url = JSValToChar(context,argv);
+
+  // Allocate buffer that will store the del resultant
+  b = init_Buffer();
+
+  // Init Curl
+  if((handle = curl_easy_init()) == NULL) {
+    free_Buffer(b);
+    return JS_FALSE;
+  }
+
+  // Configuration
+  curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEDATA,b);
+  curl_easy_setopt(handle,CURLOPT_HEADERFUNCTION,curl_write);
+  curl_easy_setopt(handle,CURLOPT_WRITEHEADER,b);
+  curl_easy_setopt(handle,CURLOPT_URL,url);
+  curl_easy_setopt(handle,CURLOPT_CUSTOMREQUEST,header_name);
+  curl_easy_setopt(handle,CURLOPT_NOPROGRESS,1);
+  curl_easy_setopt(handle,CURLOPT_IPRESOLVE,CURL_IPRESOLVE_V4);
+
+  // Curl structure
+  if((slist = generateCurlHeaders(context,argv+1)) != NULL) {
+    curl_easy_setopt(handle,CURLOPT_HTTPHEADER,slist);
+  }
+
+  // Perform
+  if((exitcode = curl_easy_perform(handle)) != 0) {
+    if(slist != NULL)
+      curl_slist_free_all(slist);
+    curl_easy_cleanup(handle);
+    free(url);
+    free_Buffer(b);
+    return JS_FALSE;
+  }
+
+  if(slist != NULL)
+    curl_slist_free_all(slist);
+  free(url);
+
+  /* Treat the empty string specially */
+  if (b->count == 0) {
+    *rval = JS_GetEmptyStringValue(context);
+    curl_easy_cleanup(handle);
+    free_Buffer(b);
+    return JS_TRUE;
+  }
+
+  /* Shrink the buffer to the real size */
+  shrink_Buffer(b);
+
+  BufferToJSVal(context,b,rval);
+
+  if(rval == NULL) {
+    curl_easy_cleanup(handle);
+    return JS_FALSE;
+  }
+
+  JS_MaybeGC(context);
+
+  curl_easy_cleanup(handle);
+
+  return JS_TRUE;
+}
+
+int
+main(int argc, const char * argv[]) {
+    JSRuntime *runtime;
+    JSContext *context;
+    JSObject *global;
+
+    runtime = JS_NewRuntime(64L * 1024L * 1024L);
+    if (!runtime)
+        return 1;
+    context = JS_NewContext(runtime, gStackChunkSize);
+    if (!context)
+        return 1;
+    /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=477187 */
+    JS_SetErrorReporter(context, PrintError);
+#ifdef USE_JS_SETOPCB
+    JS_SetContextThread(context);
+    JS_BeginRequest(context);
+    JS_SetOperationCallback(context, OperationCallback);
+#else
+    JS_SetBranchCallback(context, BranchCallback);
+    JS_ToggleOptions(context, JSOPTION_NATIVE_BRANCH_CALLBACK);
+#endif
+    JS_ToggleOptions(context, JSOPTION_XML);
+
+    global = JS_NewObject(context, NULL, NULL, NULL);
+    if (!global)
+        return 1;
+    if (!JS_InitStandardClasses(context, global))
+        return 1;
+    if (!JS_DefineFunction(context, global, "evalcx", EvalInContext, 0, 0)
+     || !JS_DefineFunction(context, global, "gc", GC, 0, 0)
+     || !JS_DefineFunction(context, global, "print", Print, 0, 0)
+     || !JS_DefineFunction(context, global, "quit", Quit, 0, 0)
+     || !JS_DefineFunction(context, global, "readline", ReadLine, 0, 0)
+     || !JS_DefineFunction(context, global, "seal", Seal, 0, 0)
+     || !JS_DefineFunction(context, global, "gethttp", GetHttp, 1, 0)
+     || !JS_DefineFunction(context, global, "headhttp", HeadHttp, 1, 0)
+     || !JS_DefineFunction(context, global, "posthttp", PostHttp, 2, 0)
+     || !JS_DefineFunction(context, global, "puthttp", PutHttp, 2, 0)
+     || !JS_DefineFunction(context, global, "delhttp", DelHttp, 1, 0)
+     || !JS_DefineFunction(context, global, "movehttp", MoveHttp, 1, 0)
+     || !JS_DefineFunction(context, global, "copyhttp", CopyHttp, 1, 0))
+        return 1;
+
+    if (argc != 2) {
+        fprintf(stderr, "incorrect number of arguments\n\n");
+        fprintf(stderr, "usage: %s <scriptfile>\n", argv[0]);
+        return 2;
+    }
+
+    ExecuteScript(context, global, argv[1]);
+
+#ifdef USE_JS_SETOPCB
+    JS_EndRequest(context);
+    JS_ClearContextThread(context);
+#endif
+
+    JS_DestroyContext(context);
+    JS_DestroyRuntime(runtime);
+    JS_ShutDown();
+
+    return gExitCode;
+}

Added: couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.c
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.c?rev=884671&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.c (added)
+++ couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.c Thu Nov 26 19:29:35 2009
@@ -0,0 +1,255 @@
+/*
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "curlhelper.h"
+
+#define TRUE 1
+#define FALSE 0
+
+Buffer init_Buffer() {
+  Buffer b;
+
+  if((b = (Buffer)malloc(sizeof(char*) + sizeof(int)*2)) == NULL) {
+    return NULL;
+  }
+
+  b->count = 0;
+  b->capacity = 50;
+
+  if(b->data = (char*)malloc(sizeof(char)*b->capacity)) {
+    return b;
+  } else {
+    return NULL;
+  }
+}
+
+void free_Buffer(Buffer b) {
+  if(b == NULL)
+    return;
+  if(b->data != NULL)
+    free(b->data);
+  free(b);
+}
+
+int append_Buffer(Buffer b, char* c, int length) {
+  int capacity_changed;
+  int new_count;
+  int i;
+
+  capacity_changed = FALSE;
+  new_count = b->count + length;
+
+  if(new_count > b->capacity) {
+    capacity_changed = TRUE;
+    b->capacity *= 2;
+  }
+
+  while(new_count > b->capacity) {
+    b->capacity *= 2;
+  }
+
+  if(capacity_changed) {
+    if((b->data = (char*)realloc(b->data,b->capacity)) == NULL) {
+      return FALSE;
+    }
+  }
+
+  for(i = 0;i < length;i++) {
+    *(b->data + b->count + i) = *(c + i);
+  }
+
+  b->count = new_count;
+
+  return TRUE;
+}
+
+char* toString_Buffer(Buffer b) {
+  char* result;
+  int i;
+
+  if((result = (char*)malloc(sizeof(char)*(b->count+1))) == NULL) {
+    return NULL;
+  }
+
+  result[b->count] = '\0';
+
+  for(i = 0;i < b->count;i++) {
+    result[i] = b->data[i];
+  }
+
+  return result;
+}
+
+int print_Buffer(Buffer b) {
+  char* c;
+
+  if((c = toString_Buffer(b)) == NULL) {
+    return FALSE;
+  }
+
+  printf("%s\n",c);
+
+  free(c);
+
+  return TRUE;
+}
+
+
+int shrink_Buffer(Buffer b) {
+  b->capacity = b->count;
+
+  if((b->data = realloc(b->data,sizeof(char)*b->capacity)) == NULL) {
+    return FALSE;
+  } else {
+    return TRUE;
+  }
+}
+
+void copy_Buffer(Buffer b, char* c, int pos, int length) {
+  int i;
+  if((pos + length) > b->count)
+    return;
+
+  for(i = 0; i < length;i++) {
+    *(c + i) = *(b->data + pos + i);
+  }
+}
+
+
+List init_List(int capacity) {
+  List l;
+  if(capacity < 5)
+    capacity = 5;
+
+  if((l = (List)malloc(sizeof(void**)+sizeof(int)*2)) == NULL) {
+    return NULL;
+  }
+
+  l->count = 0;
+  l->capacity = capacity;
+
+  if((l->elements = (void**)malloc(sizeof(void*)*l->capacity)) == NULL) {
+    return NULL;
+  }
+
+  return l;
+}
+
+void free_List(List l) {
+  if(l == NULL)
+    return;
+  if(l->elements != NULL)
+    free(l->elements);
+  free(l);
+}
+
+void* get_List(List l, int pos) {
+  if(pos > (l->count - 1)) {
+    return NULL;
+  }
+  return *(l->elements + pos);
+}
+
+void* pull_List(List l) {
+  void* r = *(l->elements);
+
+  int i;
+
+  for(i = 1; i < (l->count-1);i++) {
+    l->elements[i] = l->elements[i+1];
+  }
+  l->count -= 1;
+  return r;
+}
+
+int set_List(List l, int pos, void* ptr) {
+  if(pos > (l->count - 1)) {
+    return FALSE;
+  }
+
+  *(l->elements + pos) = ptr;
+
+  return TRUE;
+}
+
+int append_List(List l, void* ptr, int length) {
+  int capacity_changed;
+  int new_count;
+  int i;
+
+  capacity_changed = FALSE;
+  new_count = l->count + length;
+
+  if(new_count > l->capacity) {
+    capacity_changed = TRUE;
+    l->capacity *= 2;
+  }
+
+  while(new_count > l->capacity) {
+    l->capacity *= 2;
+  }
+
+  if(capacity_changed) {
+    if((l->elements = (void*)realloc(l->elements,l->capacity)) == NULL) {
+      return FALSE;
+    }
+  }
+
+  for(i = 0;i < length;i++) {
+    *(l->elements + l->count + i) = (void *)((char *)ptr + i);
+  }
+
+  l->count = new_count;
+
+  return TRUE;
+}
+
+int push_List(List l, void* ptr, int length) {
+  int capacity_changed;
+  int new_count;
+  int i;
+
+  capacity_changed = FALSE;
+  new_count = l->count + length;
+
+  if(new_count > l->capacity) {
+    capacity_changed = TRUE;
+    l->capacity *= 2;
+  }
+
+  while(new_count > l->capacity) {
+    l->capacity *= 2;
+  }
+
+  if(capacity_changed) {
+    if((l->elements = (void*)realloc(l->elements,l->capacity)) == NULL) {
+      return FALSE;
+    }
+  }
+
+  for(i = 0;i < length;i++) {
+    *(l->elements + l->count + i) = *(l->elements + i);
+  }
+
+  for(i = 0;i < length;i++) {
+    *(l->elements + i) = (void *)((char *)ptr+i);
+  }
+
+  l->count = new_count;
+
+  return TRUE;
+}

Added: couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.h
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.h?rev=884671&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.h (added)
+++ couchdb/trunk/src/couchdb/priv/couch_js/curlhelper.h Thu Nov 26 19:29:35 2009
@@ -0,0 +1,49 @@
+/*
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+*/
+
+#ifndef CURLHELPER_H
+#define CURLHELPER_H
+
+typedef struct {
+  char* data;
+  int count;
+  int capacity;
+}* Buffer;
+
+Buffer init_Buffer();
+void free_Buffer(Buffer b);
+int append_Buffer(Buffer b,char* c,int length);
+// WARNING USES MALLOC DONT FORGET TO FREE
+char* toString_Buffer(Buffer b);
+int print_Buffer(Buffer b);
+int shrink_Buffer(Buffer b);
+void copy_Buffer(Buffer b, char* c, int pos, int length);
+
+
+typedef struct {
+  void** elements;
+  int count;
+  int capacity;
+}* List;
+
+List init_List(int capacity);
+void free_List(List l);
+void* get_List(List l, int pos);
+void* pull_List(List l);
+int set_List(List l, int pos, void* ptr);
+int append_List(List l, void* ptr, int length);
+int push_List(List l, void* ptr, int length);
+
+#endif

Added: couchdb/trunk/src/couchdb/priv/icu_driver/couch_icu_driver.c
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/icu_driver/couch_icu_driver.c?rev=884671&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/icu_driver/couch_icu_driver.c (added)
+++ couchdb/trunk/src/couchdb/priv/icu_driver/couch_icu_driver.c Thu Nov 26 19:29:35 2009
@@ -0,0 +1,177 @@
+/*
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+*/
+
+// This file is the C port driver for Erlang. It provides a low overhead
+// means of calling into C code, however coding errors in this module can
+// crash the entire Erlang server.
+
+#ifdef DARWIN
+#define U_HIDE_DRAFT_API 1
+#define U_DISABLE_RENAMING 1
+#endif
+
+#include "erl_driver.h"
+#include "unicode/ucol.h"
+#include "unicode/ucasemap.h"
+#ifndef WIN32
+#include <string.h> // for memcpy
+#endif
+
+typedef struct {
+    ErlDrvPort port;
+    UCollator* collNoCase;
+    UCollator* coll;
+} couch_drv_data;
+
+static void couch_drv_stop(ErlDrvData data)
+{
+    couch_drv_data* pData = (couch_drv_data*)data;
+    if (pData->coll) {
+        ucol_close(pData->coll);
+    }
+    if (pData->collNoCase) {
+        ucol_close(pData->collNoCase);
+    }
+    driver_free((char*)pData);
+}
+
+static ErlDrvData couch_drv_start(ErlDrvPort port, char *buff)
+{
+    UErrorCode status = U_ZERO_ERROR;
+    couch_drv_data* pData = (couch_drv_data*)driver_alloc(sizeof(couch_drv_data));
+
+    if (pData == NULL)
+        return ERL_DRV_ERROR_GENERAL;
+
+    pData->port = port;
+
+    pData->coll = ucol_open("", &status);
+    if (U_FAILURE(status)) {
+        couch_drv_stop((ErlDrvData)pData);
+        return ERL_DRV_ERROR_GENERAL;
+    }
+
+    pData->collNoCase = ucol_open("", &status);
+    if (U_FAILURE(status)) {
+        couch_drv_stop((ErlDrvData)pData);
+        return ERL_DRV_ERROR_GENERAL;
+    }
+
+    ucol_setAttribute(pData->collNoCase, UCOL_STRENGTH, UCOL_PRIMARY, &status);
+    if (U_FAILURE(status)) {
+        couch_drv_stop((ErlDrvData)pData);
+        return ERL_DRV_ERROR_GENERAL;
+    }
+
+    return (ErlDrvData)pData;
+}
+
+static int return_control_result(void* pLocalResult, int localLen, char **ppRetBuf, int returnLen)
+{
+    if (*ppRetBuf == NULL || localLen > returnLen) {
+        *ppRetBuf = (char*)driver_alloc_binary(localLen);
+        if(*ppRetBuf == NULL) {
+            return -1;
+        }
+    }
+    memcpy(*ppRetBuf, pLocalResult, localLen);
+    return localLen;
+}
+
+static int couch_drv_control(ErlDrvData drv_data, unsigned int command, char *pBuf,
+             int bufLen, char **rbuf, int rlen)
+{
+
+    couch_drv_data* pData = (couch_drv_data*)drv_data;
+    switch(command) {
+    case 0: // COLLATE
+    case 1: // COLLATE_NO_CASE:
+        {
+        UErrorCode status = U_ZERO_ERROR;
+        int collResult;
+        char response;
+        UCharIterator iterA;
+        UCharIterator iterB;
+        int32_t length;
+
+        // 2 strings are in the buffer, consecutively
+        // The strings begin first with a 32 bit integer byte length, then the actual
+        // string bytes follow.
+
+        // first 32bits are the length
+        memcpy(&length, pBuf, sizeof(length));
+        pBuf += sizeof(length);
+
+        // point the iterator at it.
+        uiter_setUTF8(&iterA, pBuf, length);
+
+        pBuf += length; // now on to string b
+
+        // first 32bits are the length
+        memcpy(&length, pBuf, sizeof(length));
+        pBuf += sizeof(length);
+
+        // point the iterator at it.
+        uiter_setUTF8(&iterB, pBuf, length);
+
+        if (command == 0) // COLLATE
+          collResult = ucol_strcollIter(pData->coll, &iterA, &iterB, &status);
+        else              // COLLATE_NO_CASE
+          collResult = ucol_strcollIter(pData->collNoCase, &iterA, &iterB, &status);
+
+        if (collResult < 0)
+          response = 0; //lt
+        else if (collResult > 0)
+          response = 2; //gt
+        else
+          response = 1; //eq
+
+        return return_control_result(&response, sizeof(response), rbuf, rlen);
+        }
+
+    default:
+        return -1;
+    }
+}
+
+ErlDrvEntry couch_driver_entry = {
+        NULL,               /* F_PTR init, N/A */
+        couch_drv_start,    /* L_PTR start, called when port is opened */
+        couch_drv_stop,     /* F_PTR stop, called when port is closed */
+        NULL,               /* F_PTR output, called when erlang has sent */
+        NULL,               /* F_PTR ready_input, called when input descriptor ready */
+        NULL,               /* F_PTR ready_output, called when output descriptor ready */
+        "couch_icu_driver", /* char *driver_name, the argument to open_port */
+        NULL,               /* F_PTR finish, called when unloaded */
+        NULL,               /* Not used */
+        couch_drv_control,  /* F_PTR control, port_command callback */
+        NULL,               /* F_PTR timeout, reserved */
+        NULL,               /* F_PTR outputv, reserved */
+        NULL,               /* F_PTR ready_async */
+        NULL,               /* F_PTR flush */
+        NULL,               /* F_PTR call */
+        NULL,               /* F_PTR event */
+        ERL_DRV_EXTENDED_MARKER,
+        ERL_DRV_EXTENDED_MAJOR_VERSION,
+        ERL_DRV_EXTENDED_MINOR_VERSION,
+        ERL_DRV_FLAG_USE_PORT_LOCKING,
+        NULL,               /* Reserved -- Used by emulator internally */
+        NULL,               /* F_PTR process_exit */
+};
+
+DRIVER_INIT(couch_icu_driver) /* must match name in driver_entry */
+{
+        return &couch_driver_entry;
+}

Added: couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable.sh
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable.sh?rev=884671&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable.sh (added)
+++ couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable.sh Thu Nov 26 19:29:35 2009
@@ -0,0 +1,20 @@
+#! /bin/sh -e
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# The purpose of this script is to echo an OS specific command before launching
+# the actual process. This provides a way for Erlang to hard-kill its external
+# processes.
+
+echo "kill -9 $$"
+exec $*

Added: couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c?rev=884671&view=auto
==============================================================================
--- couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c (added)
+++ couchdb/trunk/src/couchdb/priv/spawnkillable/couchspawnkillable_win.c Thu Nov 26 19:29:35 2009
@@ -0,0 +1,139 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Do what 2 lines of shell script in couchspawnkillable does...
+// * Create a new suspended process with the same (duplicated) standard 
+//   handles as us.
+// * Write a line to stdout, consisting of the path to ourselves, plus
+//   '--kill {pid}' where {pid} is the PID of the newly created process.
+// * Un-suspend the new process.
+// * Terminate
+
+// Later, couch will call us with --kill and the PID, so we dutifully
+// terminate the specified PID.
+
+#include <stdlib.h>
+#include "windows.h"
+
+char *get_child_cmdline(int argc, char **argv)
+{
+    // make a new command-line, but skipping me.
+    // XXX - todo - spaces etc in args???
+    int i;
+    char *p, *cmdline;
+    int nchars = 0;
+    int nthis = 1;
+    for (i=1;i<argc;i++)
+        nchars += strlen(argv[i])+1;
+    cmdline = p = malloc(nchars+1);
+    if (!cmdline)
+        return NULL;
+    for (i=1;i<argc;i++) {
+        nthis = strlen(argv[i]);
+        strncpy(p, argv[i], nthis);
+        p[nthis] = ' ';
+        p += nthis+1;
+    }
+    // Replace the last space we added above with a '\0'
+    cmdline[nchars-1] = '\0';
+    return cmdline;
+}
+
+// create the child process, returning 0, or the exit-code we will
+// terminate with.
+int create_child(int argc, char **argv, PROCESS_INFORMATION *pi)
+{
+    char buf[1024];
+    DWORD dwcreate;
+    STARTUPINFO si;
+    char *cmdline;
+    if (argc < 2)
+        return 1;
+    cmdline = get_child_cmdline(argc, argv);
+    if (!cmdline)
+        return 2;
+
+    memset(&si, 0, sizeof(si));
+    si.cb = sizeof(si);
+    // depending on how *our* parent is started, we may or may not have
+    // a valid stderr stream - so although we try and duplicate it, only
+    // failing to duplicate stdin and stdout are considered fatal.
+    if (!DuplicateHandle(GetCurrentProcess(),
+                       GetStdHandle(STD_INPUT_HANDLE),
+                       GetCurrentProcess(),
+                       &si.hStdInput,
+                       0,
+                       TRUE, // inheritable
+                       DUPLICATE_SAME_ACCESS) ||
+       !DuplicateHandle(GetCurrentProcess(),
+                       GetStdHandle(STD_OUTPUT_HANDLE),
+                       GetCurrentProcess(),
+                       &si.hStdOutput,
+                       0,
+                       TRUE, // inheritable
+                       DUPLICATE_SAME_ACCESS)) {
+        return 3;
+    }
+    DuplicateHandle(GetCurrentProcess(),
+                   GetStdHandle(STD_ERROR_HANDLE),
+                   GetCurrentProcess(),
+                   &si.hStdError,
+                   0,
+                   TRUE, // inheritable
+                   DUPLICATE_SAME_ACCESS);
+
+    si.dwFlags = STARTF_USESTDHANDLES;
+    dwcreate = CREATE_SUSPENDED;
+    if (!CreateProcess( NULL, cmdline,
+                        NULL,
+                        NULL,
+                        TRUE, // inherit handles
+                        dwcreate,
+                        NULL, // environ
+                        NULL, // cwd
+                        &si,
+                        pi))
+        return 4;
+    return 0;
+}
+
+// and here we go...
+int main(int argc, char **argv)
+{
+    char out_buf[1024];
+    int rc;
+    DWORD cbwritten;
+    PROCESS_INFORMATION pi;
+    if (argc==3 && strcmp(argv[1], "--kill")==0) {
+        HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, atoi(argv[2]));
+        if (!h)
+            return 1;
+        if (!TerminateProcess(h, 0))
+            return 2;
+        CloseHandle(h);
+        return 0;
+    }
+    // spawn the new suspended process
+    rc = create_child(argc, argv, &pi);
+    if (rc)
+        return rc;
+    // Write the 'terminate' command, which includes this PID, back to couch.
+    // *sob* - what about spaces etc?
+    sprintf_s(out_buf, sizeof(out_buf), "%s --kill %d\n", 
+              argv[0], pi.dwProcessId);
+    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), out_buf, strlen(out_buf), 
+              &cbwritten, NULL);
+    // Let the child process go...
+    ResumeThread(pi.hThread);
+    // and that is all - we can die...
+    return 0;
+}

Added: couchdb/trunk/test/etap/002-icu-driver.t
URL: http://svn.apache.org/viewvc/couchdb/trunk/test/etap/002-icu-driver.t?rev=884671&view=auto
==============================================================================
--- couchdb/trunk/test/etap/002-icu-driver.t (added)
+++ couchdb/trunk/test/etap/002-icu-driver.t Thu Nov 26 19:29:35 2009
@@ -0,0 +1,33 @@
+#!/usr/bin/env escript
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+
+main(_) ->
+    test_util:init_code_path(),
+    etap:plan(3),
+    etap:is(
+        couch_util:start_driver("src/couchdb/priv/.libs"),
+        ok,
+        "Started couch_icu_driver."
+    ),
+    etap:is(
+        couch_util:collate(<<"foo">>, <<"bar">>),
+        1,
+        "Can collate stuff"
+    ),
+    etap:is(
+        couch_util:collate(<<"A">>, <<"aa">>),
+        -1,
+        "Collate's non-ascii style."
+    ),
+    etap:end_tests().