summaryrefslogtreecommitdiff
path: root/couchjs/c_src
diff options
context:
space:
mode:
Diffstat (limited to 'couchjs/c_src')
-rw-r--r--couchjs/c_src/SConscript54
-rw-r--r--couchjs/c_src/http.c382
-rw-r--r--couchjs/c_src/http.h12
-rw-r--r--couchjs/c_src/main.c334
-rw-r--r--couchjs/c_src/sm.h13
-rw-r--r--couchjs/c_src/sm170.c378
-rw-r--r--couchjs/c_src/sm180.c387
-rw-r--r--couchjs/c_src/sm185.c401
-rw-r--r--couchjs/c_src/utf8.c18
-rw-r--r--couchjs/c_src/util.c235
-rw-r--r--couchjs/c_src/util.h34
11 files changed, 1674 insertions, 574 deletions
diff --git a/couchjs/c_src/SConscript b/couchjs/c_src/SConscript
index cfce5605..b014e2ec 100644
--- a/couchjs/c_src/SConscript
+++ b/couchjs/c_src/SConscript
@@ -19,7 +19,10 @@ def require_lib(name):
print 'Could not find required library', name
Exit(1)
-env = Environment(CCFLAGS='-g -O2 -DXP_UNIX')
+def runcmd(cmd):
+ return commands.getstatusoutput(cmd)
+
+env = Environment(CC="c++", CCFLAGS='-g -O2 -DXP_UNIX')
if os.uname()[0] == 'SunOS':
env['CC'] = '/usr/sfw/bin/gcc'
@@ -33,12 +36,15 @@ if os.uname()[0] == 'FreeBSD':
env['LIB_COMPAT'] = 'compat'
if os.path.exists('/usr/bin/pkg-config'):
- (status, output) = commands.getstatusoutput("/usr/bin/pkg-config mozilla-js --cflags")
- if status == 0:
- env['CCFLAGS'] += output
- (status, output) = commands.getstatusoutput("/usr/bin/pkg-config mozilla-js --libs-only-L")
- if status == 0:
- env.Append(LINKFLAGS=output)
+ for pkg in ["mozilla-js-185", "mozilla-js"]:
+ (s1, output) = runcmd("/usr/bin/pkg-config %s --cflags" % pkg)
+ if s1 == 0:
+ env.Append(CCFLAGS=" " + output)
+ (s2, output) = runcmd("/usr/bin/pkg-config %s --libs-only-L" % pkg)
+ if s2 == 0:
+ env.Append(LINKFLAGS=output)
+ if s1 == 0 or s2 == 0:
+ break
if not env.GetOption('clean'):
conf = Configure(env, config_h='config.h')
@@ -46,12 +52,13 @@ if not env.GetOption('clean'):
require_lib('m')
require_lib('pthread')
require_lib('curl')
+ require_lib('nspr4')
## check for SpiderMonkey development header
- if conf.CheckHeader('js/jsapi.h'):
- jsapi = 'js/jsapi.h'
- elif conf.CheckHeader('mozjs/jsapi.h'):
+ if conf.CheckHeader('mozjs/jsapi.h'):
jsapi = 'mozjs/jsapi.h'
+ elif conf.CheckHeader('js/jsapi.h'):
+ jsapi = 'js/jsapi.h'
elif conf.CheckHeader('jsapi.h'):
jsapi = 'jsapi.h'
else:
@@ -60,16 +67,25 @@ if not env.GetOption('clean'):
Exit(1)
## check for SpiderMonkey library as libjs or libmozjs
- if not conf.CheckLibWithHeader('mozjs', jsapi, 'c', autoadd=1):
- if not conf.CheckLibWithHeader('js', jsapi, 'c', autoadd=1):
- print 'Could not find JS library.', \
- 'Is Mozilla SpiderMonkey installed?'
- Exit(1)
+ if not conf.CheckLibWithHeader('mozjs185-1.0', jsapi, 'c', autoadd=1):
+ if not conf.CheckLibWithHeader('mozjs', jsapi, 'c', autoadd=1):
+ if not conf.CheckLibWithHeader('js', jsapi, 'c', autoadd=1):
+ print 'Could not find JS library.', \
+ 'Is Mozilla SpiderMonkey installed?'
+ Exit(1)
- ## SpiderMonkey 1.8 has this callback we use for memory management
- if conf.CheckDeclaration('JS_SetOperationCallback', '#include <%s>' % jsapi):
- conf.Define('USE_JS_SETOPCB')
+ ## Detect the version of SpiderMonkey we're using
+ jsheader = "#include <%s>" % jsapi
+ versions = [
+ ("JS_NewCompartmentAndGlobalObject", "SM185"),
+ ("JS_ThrowStopIteration", "SM180"),
+ ("JS_GetStringCharsAndLength", "SM170")
+ ]
+ for func, vsn in versions:
+ if conf.CheckDeclaration(func, jsheader):
+ conf.Define(vsn)
+ break
env = conf.Finish()
-env.Program('couchjs', ['main.c', 'http.c', 'utf8.c'])
+env.Program('couchjs', ['main.c', 'http.c', 'utf8.c', 'util.c'])
diff --git a/couchjs/c_src/http.c b/couchjs/c_src/http.c
index b781f0ef..aa21515c 100644
--- a/couchjs/c_src/http.c
+++ b/couchjs/c_src/http.c
@@ -13,29 +13,87 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include "config.h"
-#ifdef HAVE_JS_JSAPI_H
-#include <js/jsapi.h>
-#elif HAVE_MOZJS_JSAPI_H
-#include <mozjs/jsapi.h>
-#else
-#include <jsapi.h>
-#endif
+#include "sm.h"
+#include "utf8.h"
+
+// Soft dependency on cURL bindings because they're
+// only used when running the JS tests from the
+// command line which is rare.
+#ifndef HAVE_LIBCURL
+
+void
+http_check_enabled()
+{
+ fprintf(stderr, "HTTP API was disabled at compile time.\n");
+ exit(3);
+}
+
+
+JSBool
+http_ctor(JSContext* cx, JSObject* req)
+{
+ return JS_FALSE;
+}
+
+
+JSBool
+http_dtor(JSContext* cx, JSObject* req)
+{
+ return JS_FALSE;
+}
+
+
+JSBool
+http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc)
+{
+ return JS_FALSE;
+}
+
+
+JSBool
+http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val)
+{
+ return JS_FALSE;
+}
+
+
+JSBool
+http_send(JSContext* cx, JSObject* req, jsval body)
+{
+ return JS_FALSE;
+}
+
+
+int
+http_status(JSContext* cx, JSObject* req)
+{
+ return -1;
+}
+
+#else
#include <curl/curl.h>
-#include "utf8.h"
-#ifdef XP_WIN
+void
+http_check_enabled()
+{
+ return;
+}
+
+
// Map some of the string function names to things which exist on Windows
+#ifdef XP_WIN
#define strcasecmp _strcmpi
#define strncasecmp _strnicmp
#define snprintf _snprintf
#endif
+
typedef struct curl_slist CurlHeaders;
+
typedef struct {
int method;
char* url;
@@ -43,7 +101,9 @@ typedef struct {
jsint last_status;
} HTTPData;
-char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL};
+
+const char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL};
+
#define GET 0
#define HEAD 1
@@ -52,14 +112,17 @@ char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", NULL};
#define DELETE 4
#define COPY 5
+
static JSBool
go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t blen);
+
static JSString*
str_from_binary(JSContext* cx, char* data, size_t length);
-static JSBool
-constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+
+JSBool
+http_ctor(JSContext* cx, JSObject* req)
{
HTTPData* http = NULL;
JSBool ret = JS_FALSE;
@@ -76,7 +139,7 @@ constructor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
http->req_headers = NULL;
http->last_status = -1;
- if(!JS_SetPrivate(cx, obj, http))
+ if(!JS_SetPrivate(cx, req, http))
{
JS_ReportError(cx, "Failed to set private CouchHTTP data.");
goto error;
@@ -92,90 +155,76 @@ success:
return ret;
}
-static void
-destructor(JSContext* cx, JSObject* obj)
+
+void
+http_dtor(JSContext* cx, JSObject* obj)
{
HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
- if(!http)
- {
- fprintf(stderr, "Unable to destroy invalid CouchHTTP instance.\n");
- }
- else
- {
+ if(http) {
if(http->url) free(http->url);
if(http->req_headers) curl_slist_free_all(http->req_headers);
free(http);
}
}
-static JSBool
-open(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+
+JSBool
+http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc)
{
- HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req);
char* method = NULL;
- char* url = NULL;
- JSBool ret = JS_FALSE;
int methid;
+ JSBool ret = JS_FALSE;
- if(!http)
- {
+ if(!http) {
JS_ReportError(cx, "Invalid CouchHTTP instance.");
goto done;
}
- if(argv[0] == JSVAL_VOID)
- {
+ if(mth == JSVAL_VOID) {
JS_ReportError(cx, "You must specify a method.");
goto done;
}
- method = enc_string(cx, argv[0], NULL);
- if(!method)
- {
+ method = enc_string(cx, mth, NULL);
+ if(!method) {
JS_ReportError(cx, "Failed to encode method.");
goto done;
}
- for(methid = 0; METHODS[methid] != NULL; methid++)
- {
+ for(methid = 0; METHODS[methid] != NULL; methid++) {
if(strcasecmp(METHODS[methid], method) == 0) break;
}
- if(methid > COPY)
- {
+ if(methid > COPY) {
JS_ReportError(cx, "Invalid method specified.");
goto done;
}
http->method = methid;
- if(argv[1] == JSVAL_VOID)
- {
+ if(url == JSVAL_VOID) {
JS_ReportError(cx, "You must specify a URL.");
goto done;
}
- if(http->url)
- {
+ if(http->url != NULL) {
free(http->url);
http->url = NULL;
}
- http->url = enc_string(cx, argv[1], NULL);
- if(!http->url)
- {
+ http->url = enc_string(cx, url, NULL);
+ if(http->url == NULL) {
JS_ReportError(cx, "Failed to encode URL.");
goto done;
}
- if(argv[2] != JSVAL_VOID && argv[2] != JSVAL_FALSE)
- {
- JS_ReportError(cx, "Synchronous flag must be false if specified.");
+ if(snc != JSVAL_FALSE) {
+ JS_ReportError(cx, "Synchronous flag must be false.");
goto done;
}
- if(http->req_headers)
- {
+ if(http->req_headers) {
curl_slist_free_all(http->req_headers);
http->req_headers = NULL;
}
@@ -190,42 +239,42 @@ done:
return ret;
}
-static JSBool
-setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+
+JSBool
+http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val)
{
- HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req);
char* keystr = NULL;
char* valstr = NULL;
char* hdrbuf = NULL;
size_t hdrlen = -1;
JSBool ret = JS_FALSE;
- if(!http)
- {
+ if(!http) {
JS_ReportError(cx, "Invalid CouchHTTP instance.");
goto done;
}
- if(argv[0] == JSVAL_VOID)
+ if(name == JSVAL_VOID)
{
JS_ReportError(cx, "You must speciy a header name.");
goto done;
}
- keystr = enc_string(cx, argv[0], NULL);
+ keystr = enc_string(cx, name, NULL);
if(!keystr)
{
JS_ReportError(cx, "Failed to encode header name.");
goto done;
}
- if(argv[1] == JSVAL_VOID)
+ if(val == JSVAL_VOID)
{
JS_ReportError(cx, "You must specify a header value.");
goto done;
}
- valstr = enc_string(cx, argv[1], NULL);
+ valstr = enc_string(cx, val, NULL);
if(!valstr)
{
JS_ReportError(cx, "Failed to encode header value.");
@@ -234,8 +283,7 @@ setheader(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
hdrlen = strlen(keystr) + strlen(valstr) + 3;
hdrbuf = (char*) malloc(hdrlen * sizeof(char));
- if(!hdrbuf)
- {
+ if(!hdrbuf) {
JS_ReportError(cx, "Failed to allocate header buffer.");
goto done;
}
@@ -249,121 +297,50 @@ done:
if(keystr) free(keystr);
if(valstr) free(valstr);
if(hdrbuf) free(hdrbuf);
-
return ret;
}
-static JSBool
-sendreq(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+JSBool
+http_send(JSContext* cx, JSObject* req, jsval body)
{
- HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
- char* body = NULL;
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req);
+ char* bodystr = NULL;
size_t bodylen = 0;
JSBool ret = JS_FALSE;
- if(!http)
- {
+ if(!http) {
JS_ReportError(cx, "Invalid CouchHTTP instance.");
goto done;
}
- if(argv[0] != JSVAL_VOID && argv[0] != JS_GetEmptyStringValue(cx))
- {
- body = enc_string(cx, argv[0], &bodylen);
- if(!body)
- {
+ if(body != JSVAL_VOID && body != JS_GetEmptyStringValue(cx)) {
+ bodystr = enc_string(cx, body, &bodylen);
+ if(!bodystr) {
JS_ReportError(cx, "Failed to encode body.");
goto done;
}
}
- ret = go(cx, obj, http, body, bodylen);
+ ret = go(cx, req, http, bodystr, bodylen);
done:
- if(body) free(body);
+ if(bodystr) free(bodystr);
return ret;
}
-static JSBool
-status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+int
+http_status(JSContext* cx, JSObject* req)
{
- HTTPData* http = (HTTPData*) JS_GetPrivate(cx, obj);
+ HTTPData* http = (HTTPData*) JS_GetPrivate(cx, req);
- if(!http)
- {
+ if(!http) {
JS_ReportError(cx, "Invalid CouchHTTP instance.");
return JS_FALSE;
}
- if(INT_FITS_IN_JSVAL(http->last_status))
- {
- *vp = INT_TO_JSVAL(http->last_status);
- return JS_TRUE;
- }
- else
- {
- JS_ReportError(cx, "INTERNAL: Invalid last_status");
- return JS_FALSE;
- }
-}
-
-JSClass CouchHTTPClass = {
- "CouchHTTP",
- JSCLASS_HAS_PRIVATE
- | JSCLASS_CONSTRUCT_PROTOTYPE
- | JSCLASS_HAS_RESERVED_SLOTS(2),
- JS_PropertyStub,
- JS_PropertyStub,
- JS_PropertyStub,
- JS_PropertyStub,
- JS_EnumerateStub,
- JS_ResolveStub,
- JS_ConvertStub,
- destructor,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-JSPropertySpec CouchHTTPProperties[] = {
- {"status", 0, JSPROP_READONLY, status, NULL},
- {0, 0, 0, 0, 0}
-};
-
-JSFunctionSpec CouchHTTPFunctions[] = {
- {"_open", open, 3, 0, 0},
- {"_setRequestHeader", setheader, 2, 0, 0},
- {"_send", sendreq, 1, 0, 0},
- {0, 0, 0, 0, 0}
-};
-
-JSObject*
-install_http(JSContext* cx, JSObject* glbl)
-{
- JSObject* klass = NULL;
- HTTPData* http = NULL;
-
- klass = JS_InitClass(
- cx,
- glbl,
- NULL,
- &CouchHTTPClass,
- constructor,
- 0,
- CouchHTTPProperties,
- CouchHTTPFunctions,
- NULL,
- NULL
- );
-
- if(!klass)
- {
- fprintf(stderr, "Failed to initialize CouchHTTP class.\n");
- return NULL;
- }
-
- return klass;
+ return http->last_status;
}
-
// Curl Helpers
typedef struct {
@@ -373,6 +350,7 @@ typedef struct {
char* sendbuf;
size_t sendlen;
size_t sent;
+ int sent_once;
char* recvbuf;
size_t recvlen;
size_t read;
@@ -404,13 +382,13 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
state.sendbuf = body;
state.sendlen = bodylen;
state.sent = 0;
+ state.sent_once = 0;
state.recvbuf = NULL;
state.recvlen = 0;
state.read = 0;
- if(HTTP_HANDLE == NULL)
- {
+ if(HTTP_HANDLE == NULL) {
HTTP_HANDLE = curl_easy_init();
curl_easy_setopt(HTTP_HANDLE, CURLOPT_READFUNCTION, send_body);
curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKFUNCTION,
@@ -425,14 +403,12 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
"CouchHTTP Client - Relax");
}
- if(!HTTP_HANDLE)
- {
+ if(!HTTP_HANDLE) {
JS_ReportError(cx, "Failed to initialize cURL handle.");
goto done;
}
- if(http->method < 0 || http->method > COPY)
- {
+ if(http->method < 0 || http->method > COPY) {
JS_ReportError(cx, "INTERNAL: Unknown method.");
goto done;
}
@@ -442,27 +418,21 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 0);
- if(http->method == HEAD)
- {
+ if(http->method == HEAD) {
curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 1);
curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
- }
- else if(http->method == POST || http->method == PUT)
- {
+ } else if(http->method == POST || http->method == PUT) {
curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 1);
curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
}
- if(body && bodylen)
- {
+ if(body && bodylen) {
curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen);
- }
- else
- {
+ } else {
curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0);
}
- //curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1);
+ // curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1);
curl_easy_setopt(HTTP_HANDLE, CURLOPT_URL, http->url);
curl_easy_setopt(HTTP_HANDLE, CURLOPT_HTTPHEADER, http->req_headers);
@@ -471,39 +441,32 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEHEADER, &state);
curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEDATA, &state);
- if(curl_easy_perform(HTTP_HANDLE) != 0)
- {
+ if(curl_easy_perform(HTTP_HANDLE) != 0) {
JS_ReportError(cx, "Failed to execute HTTP request: %s", ERRBUF);
goto done;
}
- if(!state.resp_headers)
- {
+ if(!state.resp_headers) {
JS_ReportError(cx, "Failed to recieve HTTP headers.");
goto done;
}
tmp = OBJECT_TO_JSVAL(state.resp_headers);
if(!JS_DefineProperty(
- cx,
- obj,
+ cx, obj,
"_headers",
tmp,
- NULL,
- NULL,
+ NULL, NULL,
JSPROP_READONLY
- ))
- {
+ )) {
JS_ReportError(cx, "INTERNAL: Failed to set response headers.");
goto done;
}
- if(state.recvbuf) // Is good enough?
- {
+ if(state.recvbuf) {
state.recvbuf[state.read] = '\0';
jsbody = dec_string(cx, state.recvbuf, state.read+1);
- if(!jsbody)
- {
+ if(!jsbody) {
// If we can't decode the body as UTF-8 we forcefully
// convert it to a string by just forcing each byte
// to a jschar.
@@ -516,22 +479,17 @@ go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
}
}
tmp = STRING_TO_JSVAL(jsbody);
- }
- else
- {
+ } else {
tmp = JS_GetEmptyStringValue(cx);
}
if(!JS_DefineProperty(
- cx,
- obj,
+ cx, obj,
"responseText",
tmp,
- NULL,
- NULL,
+ NULL, NULL,
JSPROP_READONLY
- ))
- {
+ )) {
JS_ReportError(cx, "INTERNAL: Failed to set responseText.");
goto done;
}
@@ -549,15 +507,20 @@ send_body(void *ptr, size_t size, size_t nmem, void *data)
CurlState* state = (CurlState*) data;
size_t length = size * nmem;
size_t towrite = state->sendlen - state->sent;
- if(towrite == 0)
- {
+
+ // Assume this is cURL trying to resend a request that
+ // failed.
+ if(towrite == 0 && state->sent_once == 0) {
+ state->sent_once = 1;
return 0;
+ } else if(towrite == 0) {
+ state->sent = 0;
+ state->sent_once = 0;
+ towrite = state->sendlen;
}
if(length < towrite) towrite = length;
- //fprintf(stderr, "%lu %lu %lu %lu\n", state->bodyused, state->bodyread, length, towrite);
-
memcpy(ptr, state->sendbuf + state->sent, towrite);
state->sent += towrite;
@@ -581,15 +544,12 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data)
char code[4];
char* header = (char*) ptr;
size_t length = size * nmem;
- size_t index = 0;
JSString* hdr = NULL;
jsuint hdrlen;
jsval hdrval;
- if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0)
- {
- if(length < 12)
- {
+ if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0) {
+ if(length < 12) {
return CURLE_WRITE_ERROR;
}
@@ -598,8 +558,7 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data)
state->http->last_status = atoi(code);
state->resp_headers = JS_NewArrayObject(state->cx, 0, NULL);
- if(!state->resp_headers)
- {
+ if(!state->resp_headers) {
return CURLE_WRITE_ERROR;
}
@@ -607,26 +566,22 @@ recv_header(void *ptr, size_t size, size_t nmem, void *data)
}
// We get a notice at the \r\n\r\n after headers.
- if(length <= 2)
- {
+ if(length <= 2) {
return length;
}
// Append the new header to our array.
hdr = dec_string(state->cx, header, length);
- if(!hdr)
- {
+ if(!hdr) {
return CURLE_WRITE_ERROR;
}
- if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen))
- {
+ if(!JS_GetArrayLength(state->cx, state->resp_headers, &hdrlen)) {
return CURLE_WRITE_ERROR;
}
hdrval = STRING_TO_JSVAL(hdr);
- if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval))
- {
+ if(!JS_SetElement(state->cx, state->resp_headers, hdrlen, &hdrval)) {
return CURLE_WRITE_ERROR;
}
@@ -640,21 +595,19 @@ recv_body(void *ptr, size_t size, size_t nmem, void *data)
size_t length = size * nmem;
char* tmp = NULL;
- if(!state->recvbuf)
- {
+ if(!state->recvbuf) {
state->recvlen = 4096;
state->read = 0;
- state->recvbuf = JS_malloc(state->cx, state->recvlen);
+ state->recvbuf = (char*) JS_malloc(state->cx, state->recvlen);
}
- if(!state->recvbuf)
- {
+ if(!state->recvbuf) {
return CURLE_WRITE_ERROR;
}
// +1 so we can add '\0' back up in the go function.
while(length+1 > state->recvlen - state->read) state->recvlen *= 2;
- tmp = JS_realloc(state->cx, state->recvbuf, state->recvlen);
+ tmp = (char*) JS_realloc(state->cx, state->recvbuf, state->recvlen);
if(!tmp) return CURLE_WRITE_ERROR;
state->recvbuf = tmp;
@@ -672,8 +625,7 @@ str_from_binary(JSContext* cx, char* data, size_t length)
if(!conv) return NULL;
- for(i = 0; i < length; i++)
- {
+ for(i = 0; i < length; i++) {
conv[i] = (jschar) data[i];
}
@@ -682,3 +634,5 @@ str_from_binary(JSContext* cx, char* data, size_t length)
return ret;
}
+
+#endif /* HAVE_CURL */
diff --git a/couchjs/c_src/http.h b/couchjs/c_src/http.h
index b5f8c70f..75a0d561 100644
--- a/couchjs/c_src/http.h
+++ b/couchjs/c_src/http.h
@@ -13,6 +13,14 @@
#ifndef COUCH_JS_HTTP_H
#define COUCH_JS_HTTP_H
-JSObject* install_http(JSContext* cx, JSObject* global);
+#include "sm.h"
-#endif \ No newline at end of file
+void http_check_enabled();
+JSBool http_ctor(JSContext* cx, JSObject* req);
+void http_dtor(JSContext* cx, JSObject* req);
+JSBool http_open(JSContext* cx, JSObject* req, jsval mth, jsval url, jsval snc);
+JSBool http_set_hdr(JSContext* cx, JSObject* req, jsval name, jsval val);
+JSBool http_send(JSContext* cx, JSObject* req, jsval body);
+int http_status(JSContext* cx, JSObject* req);
+
+#endif
diff --git a/couchjs/c_src/main.c b/couchjs/c_src/main.c
index 25acaf55..209bb023 100644
--- a/couchjs/c_src/main.c
+++ b/couchjs/c_src/main.c
@@ -10,336 +10,12 @@
// License for the specific language governing permissions and limitations under
// the License.
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
#include "config.h"
-#ifdef HAVE_JS_JSAPI_H
-#include <js/jsapi.h>
-#elif HAVE_MOZJS_JSAPI_H
-#include <mozjs/jsapi.h>
-#else
-#include <jsapi.h>
-#endif
-
-#include "utf8.h"
-#include "http.h"
-int gExitCode = 0;
-
-#ifdef JS_THREADSAFE
-#define SETUP_REQUEST(cx) \
- JS_SetContextThread(cx); \
- JS_BeginRequest(cx);
-#define FINISH_REQUEST(cx) \
- JS_EndRequest(cx); \
- JS_ClearContextThread(cx);
+#if defined(SM185)
+#include "sm185.c"
+#elif defined(SM180)
+#include "sm180.c"
#else
-#define SETUP_REQUEST(cx)
-#define FINISH_REQUEST(cx)
+#include "sm170.c"
#endif
-
-static JSBool
-evalcx(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- JSString *str;
- JSObject *sandbox;
- JSContext *subcx;
- const jschar *src;
- size_t srclen;
- JSBool ret = JS_FALSE;
- jsval v;
-
- sandbox = NULL;
- if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox))
- {
- return JS_FALSE;
- }
-
- subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L);
- if(!subcx)
- {
- JS_ReportOutOfMemory(cx);
- return JS_FALSE;
- }
-
- SETUP_REQUEST(subcx);
-
- src = JS_GetStringChars(str);
- srclen = JS_GetStringLength(str);
-
- if(!sandbox)
- {
- sandbox = JS_NewObject(subcx, NULL, NULL, NULL);
- if(!sandbox || !JS_InitStandardClasses(subcx, sandbox)) goto done;
- }
-
- if(srclen == 0)
- {
- *rval = OBJECT_TO_JSVAL(sandbox);
- }
- else
- {
- JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, rval);
- }
-
- ret = JS_TRUE;
-
-done:
- FINISH_REQUEST(subcx);
- JS_DestroyContext(subcx);
- return ret;
-}
-
-static JSBool
-gc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- JS_GC(cx);
- return JS_TRUE;
-}
-
-static JSBool
-print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- uintN i;
- char *bytes;
-
- for(i = 0; i < argc; i++)
- {
- bytes = enc_string(cx, argv[i], NULL);
- if(!bytes) return JS_FALSE;
-
- fprintf(stdout, "%s%s", i ? " " : "", bytes);
- JS_free(cx, bytes);
- }
-
- fputc('\n', stdout);
- fflush(stdout);
- return JS_TRUE;
-}
-
-static JSBool
-quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- JS_ConvertArguments(cx, argc, argv, "/ i", &gExitCode);
- return JS_FALSE;
-}
-
-static char*
-readfp(JSContext* cx, FILE* fp, size_t* buflen)
-{
- char* bytes = NULL;
- char* tmp = NULL;
- size_t used = 0;
- size_t byteslen = 256;
- size_t readlen = 0;
-
- bytes = JS_malloc(cx, byteslen);
- if(bytes == NULL) return NULL;
-
- while((readlen = js_fgets(bytes+used, byteslen-used, stdin)) > 0)
- {
- used += readlen;
-
- if(bytes[used-1] == '\n')
- {
- bytes[used-1] = '\0';
- break;
- }
-
- // Double our buffer and read more.
- byteslen *= 2;
- tmp = JS_realloc(cx, bytes, byteslen);
- if(!tmp)
- {
- JS_free(cx, bytes);
- return NULL;
- }
- bytes = tmp;
- }
-
- *buflen = used;
- return bytes;
-}
-
-static JSBool
-readline(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- jschar *chars;
- JSString *str;
- char* bytes;
- char* tmp;
- size_t byteslen;
-
- /* GC Occasionally */
- JS_MaybeGC(cx);
-
- bytes = readfp(cx, stdin, &byteslen);
- if(!bytes) return JS_FALSE;
-
- /* Treat the empty string specially */
- if(byteslen == 0)
- {
- *rval = JS_GetEmptyStringValue(cx);
- JS_free(cx, bytes);
- return JS_TRUE;
- }
-
- /* Shrink the buffer to the real size */
- tmp = JS_realloc(cx, bytes, byteslen);
- if(!tmp)
- {
- JS_free(cx, bytes);
- return JS_FALSE;
- }
- bytes = tmp;
-
- str = dec_string(cx, bytes, byteslen);
- JS_free(cx, bytes);
-
- if(!str) return JS_FALSE;
-
- *rval = STRING_TO_JSVAL(str);
-
- return JS_TRUE;
-}
-
-static JSBool
-seal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
- JSObject *target;
- JSBool deep = JS_FALSE;
-
- if (!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
- return JS_FALSE;
- if (!target)
- return JS_TRUE;
- return JS_SealObject(cx, target, deep);
-}
-
-static void
-execute_script(JSContext *cx, 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(cx, obj, filename, file);
- if(script)
- {
- JS_ExecuteScript(cx, obj, script, &result);
- JS_DestroyScript(cx, script);
- }
-}
-
-static void
-printerror(JSContext *cx, const char *mesg, JSErrorReport *report)
-{
- if(!report || !JSREPORT_IS_WARNING(report->flags))
- {
- fprintf(stderr, "%s\n", mesg);
- }
-}
-
-static JSFunctionSpec global_functions[] = {
- {"evalcx", evalcx, 0, 0, 0},
- {"gc", gc, 0, 0, 0},
- {"print", print, 0, 0, 0},
- {"quit", quit, 0, 0, 0},
- {"readline", readline, 0, 0, 0},
- {"seal", seal, 0, 0, 0},
- {0, 0, 0, 0, 0}
-};
-
-static JSClass global_class = {
- "GlobalClass",
- JSCLASS_GLOBAL_FLAGS,
- JS_PropertyStub,
- JS_PropertyStub,
- JS_PropertyStub,
- JS_PropertyStub,
- JS_EnumerateStub,
- JS_ResolveStub,
- JS_ConvertStub,
- JS_FinalizeStub,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-int
-main(int argc, const char * argv[])
-{
- JSRuntime* rt = NULL;
- JSContext* cx = NULL;
- JSObject* global = NULL;
- JSFunctionSpec* sp = NULL;
- int i = 0;
-
- rt = JS_NewRuntime(64L * 1024L * 1024L);
- if (!rt) return 1;
-
- cx = JS_NewContext(rt, 8L * 1024L);
- if (!cx) return 1;
-
- JS_SetErrorReporter(cx, printerror);
- JS_ToggleOptions(cx, JSOPTION_XML);
-
- SETUP_REQUEST(cx);
-
- global = JS_NewObject(cx, &global_class, NULL, NULL);
- if (!global) return 1;
- if (!JS_InitStandardClasses(cx, global)) return 1;
-
- for(sp = global_functions; sp->name != NULL; sp++)
- {
- if(!JS_DefineFunction(cx, global,
- sp->name, sp->call, sp->nargs, sp->flags))
- {
- fprintf(stderr, "Failed to create function: %s\n", sp->name);
- return 1;
- }
- }
-
- if(!install_http(cx, global))
- {
- return 1;
- }
-
- JS_SetGlobalObject(cx, global);
-
- if(argc > 2)
- {
- fprintf(stderr, "incorrect number of arguments\n\n");
- fprintf(stderr, "usage: %s <scriptfile>\n", argv[0]);
- return 2;
- }
-
- if(argc == 0)
- {
- execute_script(cx, global, NULL);
- }
- else
- {
- execute_script(cx, global, argv[1]);
- }
-
- FINISH_REQUEST(cx);
-
- JS_DestroyContext(cx);
- JS_DestroyRuntime(rt);
- JS_ShutDown();
-
- return gExitCode;
-}
diff --git a/couchjs/c_src/sm.h b/couchjs/c_src/sm.h
new file mode 100644
index 00000000..a40e054d
--- /dev/null
+++ b/couchjs/c_src/sm.h
@@ -0,0 +1,13 @@
+#ifndef COUCHJS_SM_H
+#define COUCHJS_SM_H
+
+#include "config.h"
+#ifdef HAVE_JS_JSAPI_H
+#include <js/jsapi.h>
+#elif HAVE_MOZJS_JSAPI_H
+#include <mozjs/jsapi.h>
+#else
+#include <jsapi.h>
+#endif
+
+#endif // included sm.h
diff --git a/couchjs/c_src/sm170.c b/couchjs/c_src/sm170.c
new file mode 100644
index 00000000..44cb5870
--- /dev/null
+++ b/couchjs/c_src/sm170.c
@@ -0,0 +1,378 @@
+// 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 "http.h"
+#include "sm.h"
+#include "utf8.h"
+#include "util.h"
+
+
+#ifdef JS_THREADSAFE
+#define SETUP_REQUEST(cx) \
+ JS_SetContextThread(cx); \
+ JS_BeginRequest(cx);
+#define FINISH_REQUEST(cx) \
+ JS_EndRequest(cx); \
+ JS_ClearContextThread(cx);
+#else
+#define SETUP_REQUEST(cx)
+#define FINISH_REQUEST(cx)
+#endif
+
+
+static JSBool
+req_ctor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ return http_ctor(cx, obj);
+}
+
+
+static void
+req_dtor(JSContext* cx, JSObject* obj)
+{
+ http_dtor(cx, obj);
+}
+
+
+static JSBool
+req_open(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ JSBool ret = JS_FALSE;
+
+ if(argc == 2) {
+ ret = http_open(cx, obj, argv[0], argv[1], JSVAL_FALSE);
+ } else if(argc == 3) {
+ ret = http_open(cx, obj, argv[0], argv[1], argv[2]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.open");
+ }
+
+ *rval = JSVAL_VOID;
+ return ret;
+}
+
+
+static JSBool
+req_set_hdr(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ JSBool ret = JS_FALSE;
+ if(argc == 2) {
+ ret = http_set_hdr(cx, obj, argv[0], argv[1]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.set_header");
+ }
+
+ *rval = JSVAL_VOID;
+ return ret;
+}
+
+
+static JSBool
+req_send(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ JSBool ret = JS_FALSE;
+ if(argc == 1) {
+ ret = http_send(cx, obj, argv[0]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.send");
+ }
+
+ *rval = JSVAL_VOID;
+ return ret;
+}
+
+
+static JSBool
+req_status(JSContext* cx, JSObject* obj, jsval idval, jsval* rval)
+{
+ int status = http_status(cx, obj);
+ if(status < 0)
+ return JS_FALSE;
+
+ if(INT_FITS_IN_JSVAL(status)) {
+ *rval = INT_TO_JSVAL(status);
+ return JS_TRUE;
+ } else {
+ JS_ReportError(cx, "Invalid HTTP status.");
+ return JS_FALSE;
+ }
+}
+
+
+static JSBool
+evalcx(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+ JSString *str;
+ JSObject *sandbox;
+ JSContext *subcx;
+ const jschar *src;
+ size_t srclen;
+ JSBool ret = JS_FALSE;
+
+ sandbox = NULL;
+ if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox)) {
+ return JS_FALSE;
+ }
+
+ subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L);
+ if(!subcx) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+
+ SETUP_REQUEST(subcx);
+
+ src = JS_GetStringChars(str);
+ srclen = JS_GetStringLength(str);
+
+ if(!sandbox) {
+ sandbox = JS_NewObject(subcx, NULL, NULL, NULL);
+ if(!sandbox || !JS_InitStandardClasses(subcx, sandbox)) {
+ goto done;
+ }
+ }
+
+ if(srclen == 0) {
+ *rval = OBJECT_TO_JSVAL(sandbox);
+ } else {
+ JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, rval);
+ }
+
+ ret = JS_TRUE;
+
+done:
+ FINISH_REQUEST(subcx);
+ JS_DestroyContext(subcx);
+ return ret;
+}
+
+
+static JSBool
+gc(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ JS_GC(cx);
+ *rval = JSVAL_VOID;
+ return JS_TRUE;
+}
+
+
+static JSBool
+print(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ couch_print(cx, argc, argv);
+ *rval = JSVAL_VOID;
+ return JS_TRUE;
+}
+
+
+static JSBool
+quit(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ int exit_code = 0;
+ JS_ConvertArguments(cx, argc, argv, "/i", &exit_code);
+ exit(exit_code);
+}
+
+
+static JSBool
+readline(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ JSString* line;
+
+ /* GC Occasionally */
+ JS_MaybeGC(cx);
+
+ line = couch_readline(cx, stdin);
+ if(line == NULL) return JS_FALSE;
+
+ *rval = STRING_TO_JSVAL(line);
+ return JS_TRUE;
+}
+
+
+static JSBool
+seal(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ JSObject *target;
+ JSBool deep = JS_FALSE;
+
+ if(!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
+ return JS_FALSE;
+
+ if(!target) {
+ *rval = JSVAL_VOID;
+ return JS_TRUE;
+ }
+
+ if(JS_SealObject(cx, obj, deep) != JS_TRUE)
+ return JS_FALSE;
+
+ *rval = JSVAL_VOID;
+ return JS_TRUE;
+}
+
+
+JSClass CouchHTTPClass = {
+ "CouchHTTP",
+ JSCLASS_HAS_PRIVATE
+ | JSCLASS_CONSTRUCT_PROTOTYPE
+ | JSCLASS_HAS_RESERVED_SLOTS(2),
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ req_dtor,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+
+JSPropertySpec CouchHTTPProperties[] = {
+ {"status", 0, JSPROP_READONLY, req_status, NULL},
+ {0, 0, 0, 0, 0}
+};
+
+
+JSFunctionSpec CouchHTTPFunctions[] = {
+ {"_open", req_open, 3, 0, 0},
+ {"_setRequestHeader", req_set_hdr, 2, 0, 0},
+ {"_send", req_send, 1, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+
+static JSClass global_class = {
+ "GlobalClass",
+ JSCLASS_GLOBAL_FLAGS,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+
+static JSFunctionSpec global_functions[] = {
+ {"evalcx", evalcx, 0, 0, 0},
+ {"gc", gc, 0, 0, 0},
+ {"print", print, 0, 0, 0},
+ {"quit", quit, 0, 0, 0},
+ {"readline", readline, 0, 0, 0},
+ {"seal", seal, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+
+int
+main(int argc, const char* argv[])
+{
+ JSRuntime* rt = NULL;
+ JSContext* cx = NULL;
+ JSObject* global = NULL;
+ JSObject* klass = NULL;
+ JSScript* script;
+ JSString* scriptsrc;
+ jschar* schars;
+ size_t slen;
+ jsval sroot;
+ jsval result;
+
+ couch_args* args = couch_parse_args(argc, argv);
+
+ rt = JS_NewRuntime(64L * 1024L * 1024L);
+ if(rt == NULL)
+ return 1;
+
+ cx = JS_NewContext(rt, args->stack_size);
+ if(cx == NULL)
+ return 1;
+
+ JS_SetErrorReporter(cx, couch_error);
+ JS_ToggleOptions(cx, JSOPTION_XML);
+
+ SETUP_REQUEST(cx);
+
+ global = JS_NewObject(cx, &global_class, NULL, NULL);
+ if(global == NULL)
+ return 1;
+
+ JS_SetGlobalObject(cx, global);
+
+ if(!JS_InitStandardClasses(cx, global))
+ return 1;
+
+ if(couch_load_funcs(cx, global, global_functions) != JS_TRUE)
+ return 1;
+
+ if(args->use_http) {
+ http_check_enabled();
+
+ klass = JS_InitClass(
+ cx, global,
+ NULL,
+ &CouchHTTPClass, req_ctor,
+ 0,
+ CouchHTTPProperties, CouchHTTPFunctions,
+ NULL, NULL
+ );
+
+ if(!klass)
+ {
+ fprintf(stderr, "Failed to initialize CouchHTTP class.\n");
+ exit(2);
+ }
+ }
+
+ // Convert script source to jschars.
+ scriptsrc = dec_string(cx, args->script, strlen(args->script));
+ if(!scriptsrc)
+ return 1;
+
+ schars = JS_GetStringChars(scriptsrc);
+ slen = JS_GetStringLength(scriptsrc);
+
+ // Root it so GC doesn't collect it.
+ sroot = STRING_TO_JSVAL(scriptsrc);
+ if(JS_AddRoot(cx, &sroot) != JS_TRUE) {
+ fprintf(stderr, "Internal root error.\n");
+ return 1;
+ }
+
+ // Compile and run
+ script = JS_CompileUCScript(cx, global, schars, slen, args->script_name, 1);
+ if(!script) {
+ fprintf(stderr, "Failed to compile script.\n");
+ return 1;
+ }
+
+ JS_ExecuteScript(cx, global, script, &result);
+
+ // Warning message if we don't remove it.
+ JS_RemoveRoot(cx, &sroot);
+
+ FINISH_REQUEST(cx);
+ JS_DestroyContext(cx);
+ JS_DestroyRuntime(rt);
+ JS_ShutDown();
+
+ return 0;
+}
diff --git a/couchjs/c_src/sm180.c b/couchjs/c_src/sm180.c
new file mode 100644
index 00000000..7dc2a974
--- /dev/null
+++ b/couchjs/c_src/sm180.c
@@ -0,0 +1,387 @@
+// 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 "http.h"
+#include "sm.h"
+#include "utf8.h"
+#include "util.h"
+
+
+#define SETUP_REQUEST(cx) \
+ JS_SetContextThread(cx); \
+ JS_BeginRequest(cx);
+#define FINISH_REQUEST(cx) \
+ JS_EndRequest(cx); \
+ JS_ClearContextThread(cx);
+
+
+static JSBool
+req_ctor(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+{
+ return http_ctor(cx, obj);
+}
+
+
+static void
+req_dtor(JSContext* cx, JSObject* obj)
+{
+ http_dtor(cx, obj);
+}
+
+
+static JSBool
+req_open(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ jsval* argv = JS_ARGV(cx, vp);
+ JSBool ret = JS_FALSE;
+
+ if(argc == 2) {
+ ret = http_open(cx, obj, argv[0], argv[1], JSVAL_FALSE);
+ } else if(argc == 3) {
+ ret = http_open(cx, obj, argv[0], argv[1], argv[2]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.open");
+ }
+
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return ret;
+}
+
+
+static JSBool
+req_set_hdr(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ jsval* argv = JS_ARGV(cx, vp);
+ JSBool ret = JS_FALSE;
+
+ if(argc == 2) {
+ ret = http_set_hdr(cx, obj, argv[0], argv[1]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.set_header");
+ }
+
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return ret;
+}
+
+
+static JSBool
+req_send(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ jsval* argv = JS_ARGV(cx, vp);
+ JSBool ret = JS_FALSE;
+
+ if(argc == 1) {
+ ret = http_send(cx, obj, argv[0]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.send");
+ }
+
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return ret;
+}
+
+
+static JSBool
+req_status(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
+{
+ int status = http_status(cx, obj);
+ if(status < 0)
+ return JS_FALSE;
+
+ if(INT_FITS_IN_JSVAL(status)) {
+ JS_SET_RVAL(cx, vp, INT_TO_JSVAL(status));
+ return JS_TRUE;
+ } else {
+ JS_ReportError(cx, "Invalid HTTP status.");
+ return JS_FALSE;
+ }
+}
+
+
+static JSBool
+evalcx(JSContext *cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ JSString *str;
+ JSObject *sandbox;
+ JSContext *subcx;
+ const jschar *src;
+ size_t srclen;
+ jsval rval;
+ JSBool ret = JS_FALSE;
+
+ sandbox = NULL;
+ if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox)) {
+ return JS_FALSE;
+ }
+
+ subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L);
+ if(!subcx) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+
+ SETUP_REQUEST(subcx);
+
+ src = JS_GetStringChars(str);
+ srclen = JS_GetStringLength(str);
+
+ if(!sandbox) {
+ sandbox = JS_NewObject(subcx, NULL, NULL, NULL);
+ if(!sandbox || !JS_InitStandardClasses(subcx, sandbox)) {
+ goto done;
+ }
+ }
+
+ if(srclen == 0) {
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(sandbox));
+ } else {
+ JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, &rval);
+ JS_SET_RVAL(cx, vp, rval);
+ }
+
+ ret = JS_TRUE;
+
+done:
+ FINISH_REQUEST(subcx);
+ JS_DestroyContext(subcx);
+ return ret;
+}
+
+
+static JSBool
+gc(JSContext* cx, uintN argc, jsval* vp)
+{
+ JS_GC(cx);
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return JS_TRUE;
+}
+
+
+static JSBool
+print(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ couch_print(cx, argc, argv);
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return JS_TRUE;
+}
+
+
+static JSBool
+quit(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ int exit_code = 0;
+ JS_ConvertArguments(cx, argc, argv, "/i", &exit_code);
+ exit(exit_code);
+}
+
+
+static JSBool
+readline(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSString* line;
+
+ /* GC Occasionally */
+ JS_MaybeGC(cx);
+
+ line = couch_readline(cx, stdin);
+ if(line == NULL) return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(line));
+ return JS_TRUE;
+}
+
+
+static JSBool
+seal(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ JSObject *target;
+ JSBool deep = JS_FALSE;
+
+ if(!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
+ return JS_FALSE;
+
+ if(!target) {
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return JS_TRUE;
+ }
+
+ if(JS_SealObject(cx, target, deep) != JS_TRUE)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return JS_TRUE;
+}
+
+
+JSClass CouchHTTPClass = {
+ "CouchHTTP",
+ JSCLASS_HAS_PRIVATE
+ | JSCLASS_CONSTRUCT_PROTOTYPE
+ | JSCLASS_HAS_RESERVED_SLOTS(2),
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ req_dtor,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+
+JSPropertySpec CouchHTTPProperties[] = {
+ {"status", 0, JSPROP_READONLY, req_status, NULL},
+ {0, 0, 0, 0, 0}
+};
+
+
+JSFunctionSpec CouchHTTPFunctions[] = {
+ JS_FS("_open", (JSNative) req_open, 3, JSFUN_FAST_NATIVE, 0),
+ JS_FS("_setRequestHeader", (JSNative) req_set_hdr, 2, JSFUN_FAST_NATIVE, 0),
+ JS_FS("_send", (JSNative) req_send, 1, JSFUN_FAST_NATIVE, 0),
+ JS_FS_END
+};
+
+
+static JSClass global_class = {
+ "GlobalClass",
+ JSCLASS_GLOBAL_FLAGS,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+
+static JSFunctionSpec global_functions[] = {
+ JS_FS("evalcx", (JSNative) evalcx, 0, JSFUN_FAST_NATIVE, 0),
+ JS_FS("gc", (JSNative) gc, 0, JSFUN_FAST_NATIVE, 0),
+ JS_FS("print", (JSNative) print, 0, JSFUN_FAST_NATIVE, 0),
+ JS_FS("quit", (JSNative) quit, 0, JSFUN_FAST_NATIVE, 0),
+ JS_FS("readline", (JSNative) readline, 0, JSFUN_FAST_NATIVE, 0),
+ JS_FS("seal", (JSNative) seal, 0, JSFUN_FAST_NATIVE, 0),
+ JS_FS_END
+};
+
+
+int
+main(int argc, const char* argv[])
+{
+ JSRuntime* rt = NULL;
+ JSContext* cx = NULL;
+ JSObject* global = NULL;
+ JSObject* klass = NULL;
+ JSScript* script;
+ JSString* scriptsrc;
+ jschar* schars;
+ size_t slen;
+ jsval sroot;
+ jsval result;
+
+ couch_args* args = couch_parse_args(argc, argv);
+
+ rt = JS_NewRuntime(64L * 1024L * 1024L);
+ if(rt == NULL)
+ return 1;
+
+ cx = JS_NewContext(rt, args->stack_size);
+ if(cx == NULL)
+ return 1;
+
+ JS_SetErrorReporter(cx, couch_error);
+ JS_ToggleOptions(cx, JSOPTION_XML);
+
+ SETUP_REQUEST(cx);
+
+ global = JS_NewObject(cx, &global_class, NULL, NULL);
+ if(global == NULL)
+ return 1;
+
+ JS_SetGlobalObject(cx, global);
+
+ if(!JS_InitStandardClasses(cx, global))
+ return 1;
+
+ if(couch_load_funcs(cx, global, global_functions) != JS_TRUE)
+ return 1;
+
+ if(args->use_http) {
+ http_check_enabled();
+
+ klass = JS_InitClass(
+ cx, global,
+ NULL,
+ &CouchHTTPClass, req_ctor,
+ 0,
+ CouchHTTPProperties, CouchHTTPFunctions,
+ NULL, NULL
+ );
+
+ if(!klass)
+ {
+ fprintf(stderr, "Failed to initialize CouchHTTP class.\n");
+ exit(2);
+ }
+ }
+
+ // Convert script source to jschars.
+ scriptsrc = dec_string(cx, args->script, strlen(args->script));
+ if(!scriptsrc)
+ return 1;
+
+ schars = JS_GetStringChars(scriptsrc);
+ slen = JS_GetStringLength(scriptsrc);
+
+ // Root it so GC doesn't collect it.
+ sroot = STRING_TO_JSVAL(scriptsrc);
+ if(JS_AddRoot(cx, &sroot) != JS_TRUE) {
+ fprintf(stderr, "Internal root error.\n");
+ return 1;
+ }
+
+ // Compile and run
+ script = JS_CompileUCScript(cx, global, schars, slen, args->script_name, 1);
+ if(!script) {
+ fprintf(stderr, "Failed to compile script.\n");
+ return 1;
+ }
+
+ JS_ExecuteScript(cx, global, script, &result);
+
+ // Warning message if we don't remove it.
+ JS_RemoveRoot(cx, &sroot);
+
+ FINISH_REQUEST(cx);
+ JS_DestroyContext(cx);
+ JS_DestroyRuntime(rt);
+ JS_ShutDown();
+
+ return 0;
+}
diff --git a/couchjs/c_src/sm185.c b/couchjs/c_src/sm185.c
new file mode 100644
index 00000000..9815f15c
--- /dev/null
+++ b/couchjs/c_src/sm185.c
@@ -0,0 +1,401 @@
+// 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 "http.h"
+#include "sm.h"
+#include "utf8.h"
+#include "util.h"
+
+
+#define SETUP_REQUEST(cx) \
+ JS_SetContextThread(cx); \
+ JS_BeginRequest(cx);
+#define FINISH_REQUEST(cx) \
+ JS_EndRequest(cx); \
+ JS_ClearContextThread(cx);
+
+
+static JSClass global_class = {
+ "GlobalClass",
+ JSCLASS_GLOBAL_FLAGS,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_StrictPropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+
+static JSBool
+req_ctor(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSBool ret;
+ JSObject* obj = JS_NewObjectForConstructor(cx, vp);
+ if(!obj) {
+ JS_ReportError(cx, "Failed to create CouchHTTP instance.\n");
+ return JS_FALSE;
+ }
+ ret = http_ctor(cx, obj);
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
+ return ret;
+}
+
+
+static void
+req_dtor(JSContext* cx, JSObject* obj)
+{
+ http_dtor(cx, obj);
+}
+
+
+static JSBool
+req_open(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ jsval* argv = JS_ARGV(cx, vp);
+ JSBool ret = JS_FALSE;
+
+ if(argc == 2) {
+ ret = http_open(cx, obj, argv[0], argv[1], JSVAL_FALSE);
+ } else if(argc == 3) {
+ ret = http_open(cx, obj, argv[0], argv[1], argv[2]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.open");
+ }
+
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return ret;
+}
+
+
+static JSBool
+req_set_hdr(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ jsval* argv = JS_ARGV(cx, vp);
+ JSBool ret = JS_FALSE;
+
+ if(argc == 2) {
+ ret = http_set_hdr(cx, obj, argv[0], argv[1]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.set_header");
+ }
+
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return ret;
+}
+
+
+static JSBool
+req_send(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ jsval* argv = JS_ARGV(cx, vp);
+ JSBool ret = JS_FALSE;
+
+ if(argc == 1) {
+ ret = http_send(cx, obj, argv[0]);
+ } else {
+ JS_ReportError(cx, "Invalid call to CouchHTTP.send");
+ }
+
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return ret;
+}
+
+
+static JSBool
+req_status(JSContext* cx, JSObject* obj, jsid pid, jsval* vp)
+{
+ int status = http_status(cx, obj);
+ if(status < 0)
+ return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, INT_TO_JSVAL(status));
+ return JS_TRUE;
+}
+
+
+static JSBool
+evalcx(JSContext *cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ JSString* str;
+ JSObject* sandbox;
+ JSObject* global;
+ JSContext* subcx;
+ JSCrossCompartmentCall* call = NULL;
+ const jschar* src;
+ size_t srclen;
+ jsval rval;
+ JSBool ret = JS_FALSE;
+
+ sandbox = NULL;
+ if(!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sandbox)) {
+ return JS_FALSE;
+ }
+
+ subcx = JS_NewContext(JS_GetRuntime(cx), 8L * 1024L);
+ if(!subcx) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+
+ SETUP_REQUEST(subcx);
+
+ src = JS_GetStringCharsAndLength(cx, str, &srclen);
+
+ // Re-use the compartment associated with the main context,
+ // rather than creating a new compartment */
+ global = JS_GetGlobalObject(cx);
+ if(global == NULL) goto done;
+ call = JS_EnterCrossCompartmentCall(subcx, global);
+
+ if(!sandbox) {
+ sandbox = JS_NewGlobalObject(subcx, &global_class);
+ if(!sandbox || !JS_InitStandardClasses(subcx, sandbox)) {
+ goto done;
+ }
+ }
+
+ if(srclen == 0) {
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(sandbox));
+ } else {
+ JS_EvaluateUCScript(subcx, sandbox, src, srclen, NULL, 0, &rval);
+ JS_SET_RVAL(cx, vp, rval);
+ }
+
+ ret = JS_TRUE;
+
+done:
+ JS_LeaveCrossCompartmentCall(call);
+ FINISH_REQUEST(subcx);
+ JS_DestroyContext(subcx);
+ return ret;
+}
+
+
+static JSBool
+gc(JSContext* cx, uintN argc, jsval* vp)
+{
+ JS_GC(cx);
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return JS_TRUE;
+}
+
+
+static JSBool
+print(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ couch_print(cx, argc, argv);
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return JS_TRUE;
+}
+
+
+static JSBool
+quit(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ int exit_code = 0;
+ JS_ConvertArguments(cx, argc, argv, "/i", &exit_code);
+ exit(exit_code);
+}
+
+
+static JSBool
+readline(JSContext* cx, uintN argc, jsval* vp)
+{
+ JSString* line;
+
+ /* GC Occasionally */
+ JS_MaybeGC(cx);
+
+ line = couch_readline(cx, stdin);
+ if(line == NULL) return JS_FALSE;
+
+ JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(line));
+ return JS_TRUE;
+}
+
+
+static JSBool
+seal(JSContext* cx, uintN argc, jsval* vp)
+{
+ jsval* argv = JS_ARGV(cx, vp);
+ JSObject *target;
+ JSBool deep = JS_FALSE;
+ JSBool ret;
+
+ if(!JS_ConvertArguments(cx, argc, argv, "o/b", &target, &deep))
+ return JS_FALSE;
+
+ if(!target) {
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return JS_TRUE;
+ }
+
+
+ ret = deep ? JS_DeepFreezeObject(cx, target) : JS_FreezeObject(cx, target);
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+ return ret;
+}
+
+
+JSClass CouchHTTPClass = {
+ "CouchHTTP",
+ JSCLASS_HAS_PRIVATE
+ | JSCLASS_CONSTRUCT_PROTOTYPE
+ | JSCLASS_HAS_RESERVED_SLOTS(2),
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_StrictPropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ req_dtor,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+
+JSPropertySpec CouchHTTPProperties[] = {
+ {"status", 0, JSPROP_READONLY, req_status, NULL},
+ {0, 0, 0, 0, 0}
+};
+
+
+JSFunctionSpec CouchHTTPFunctions[] = {
+ JS_FS("_open", req_open, 3, 0),
+ JS_FS("_setRequestHeader", req_set_hdr, 2, 0),
+ JS_FS("_send", req_send, 1, 0),
+ JS_FS_END
+};
+
+
+static JSFunctionSpec global_functions[] = {
+ JS_FS("evalcx", evalcx, 0, 0),
+ JS_FS("gc", gc, 0, 0),
+ JS_FS("print", print, 0, 0),
+ JS_FS("quit", quit, 0, 0),
+ JS_FS("readline", readline, 0, 0),
+ JS_FS("seal", seal, 0, 0),
+ JS_FS_END
+};
+
+
+int
+main(int argc, const char* argv[])
+{
+ JSRuntime* rt = NULL;
+ JSContext* cx = NULL;
+ JSObject* global = NULL;
+ JSCrossCompartmentCall *call = NULL;
+ JSObject* klass = NULL;
+ JSObject* script;
+ JSString* scriptsrc;
+ const jschar* schars;
+ size_t slen;
+ jsval sroot;
+ jsval result;
+
+ couch_args* args = couch_parse_args(argc, argv);
+
+ rt = JS_NewRuntime(64L * 1024L * 1024L);
+ if(rt == NULL)
+ return 1;
+
+ cx = JS_NewContext(rt, args->stack_size);
+ if(cx == NULL)
+ return 1;
+
+ JS_SetErrorReporter(cx, couch_error);
+ JS_ToggleOptions(cx, JSOPTION_XML);
+
+ SETUP_REQUEST(cx);
+
+ global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
+ if(global == NULL)
+ return 1;
+
+ call = JS_EnterCrossCompartmentCall(cx, global);
+
+ JS_SetGlobalObject(cx, global);
+
+ if(!JS_InitStandardClasses(cx, global))
+ return 1;
+
+ if(couch_load_funcs(cx, global, global_functions) != JS_TRUE)
+ return 1;
+
+ if(args->use_http) {
+ http_check_enabled();
+
+ klass = JS_InitClass(
+ cx, global,
+ NULL,
+ &CouchHTTPClass, req_ctor,
+ 0,
+ CouchHTTPProperties, CouchHTTPFunctions,
+ NULL, NULL
+ );
+
+ if(!klass)
+ {
+ fprintf(stderr, "Failed to initialize CouchHTTP class.\n");
+ exit(2);
+ }
+ }
+
+ // Convert script source to jschars.
+ scriptsrc = dec_string(cx, args->script, strlen(args->script));
+ if(!scriptsrc)
+ return 1;
+
+ schars = JS_GetStringCharsAndLength(cx, scriptsrc, &slen);
+
+ // Root it so GC doesn't collect it.
+ sroot = STRING_TO_JSVAL(scriptsrc);
+ if(JS_AddValueRoot(cx, &sroot) != JS_TRUE) {
+ fprintf(stderr, "Internal root error.\n");
+ return 1;
+ }
+
+ // Compile and run
+ script = JS_CompileUCScript(cx, global, schars, slen, args->script_name, 1);
+ if(!script) {
+ fprintf(stderr, "Failed to compile script.\n");
+ return 1;
+ }
+
+ JS_ExecuteScript(cx, global, script, &result);
+
+ // Warning message if we don't remove it.
+ JS_RemoveValueRoot(cx, &sroot);
+
+ JS_LeaveCrossCompartmentCall(call);
+ FINISH_REQUEST(cx);
+ JS_DestroyContext(cx);
+ JS_DestroyRuntime(rt);
+ JS_ShutDown();
+
+ return 0;
+}
diff --git a/couchjs/c_src/utf8.c b/couchjs/c_src/utf8.c
index 57928ba9..ace6badb 100644
--- a/couchjs/c_src/utf8.c
+++ b/couchjs/c_src/utf8.c
@@ -11,13 +11,7 @@
// the License.
#include "config.h"
-#ifdef HAVE_JS_JSAPI_H
-#include <js/jsapi.h>
-#elif HAVE_MOZJS_JSAPI_H
-#include <mozjs/jsapi.h>
-#else
-#include <jsapi.h>
-#endif
+#include "sm.h"
static int
enc_char(uint8 *utf8Buffer, uint32 ucs4Char)
@@ -128,7 +122,7 @@ char*
enc_string(JSContext* cx, jsval arg, size_t* buflen)
{
JSString* str = NULL;
- jschar* src = NULL;
+ const jschar* src = NULL;
char* bytes = NULL;
size_t srclen = 0;
size_t byteslen = 0;
@@ -136,12 +130,16 @@ enc_string(JSContext* cx, jsval arg, size_t* buflen)
str = JS_ValueToString(cx, arg);
if(!str) goto error;
+#ifdef SM185
+ src = JS_GetStringCharsAndLength(cx, str, &srclen);
+#else
src = JS_GetStringChars(str);
srclen = JS_GetStringLength(str);
+#endif
if(!enc_charbuf(src, srclen, NULL, &byteslen)) goto error;
- bytes = JS_malloc(cx, (byteslen) + 1);
+ bytes = (char*) JS_malloc(cx, (byteslen) + 1);
bytes[byteslen] = 0;
if(!enc_charbuf(src, srclen, bytes, &byteslen)) goto error;
@@ -273,7 +271,7 @@ dec_string(JSContext* cx, const char* bytes, size_t byteslen)
if(!dec_charbuf(bytes, byteslen, NULL, &charslen)) goto error;
- chars = JS_malloc(cx, (charslen + 1) * sizeof(jschar));
+ chars = (jschar*) JS_malloc(cx, (charslen + 1) * sizeof(jschar));
if(!chars) return NULL;
chars[charslen] = 0;
diff --git a/couchjs/c_src/util.c b/couchjs/c_src/util.c
new file mode 100644
index 00000000..f5ef3e82
--- /dev/null
+++ b/couchjs/c_src/util.c
@@ -0,0 +1,235 @@
+// 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 <string.h>
+
+#include "util.h"
+#include "utf8.h"
+
+
+char*
+slurp_file(char* buf, const char* file)
+{
+ FILE* fp;
+ char fbuf[16384];
+ char* tmp;
+ size_t nread = 0;
+ size_t buflen = 0;
+
+ if(strcmp(file, "-") == 0) {
+ fp = stdin;
+ } else {
+ fp = fopen(file, "r");
+ if(fp == NULL) {
+ fprintf(stderr, "Failed to read file: %s\n", file);
+ exit(3);
+ }
+ }
+
+ while((nread = fread(fbuf, 1, 16384, fp)) > 0) {
+ if(buf == NULL) {
+ buflen = nread;
+ buf = (char*) malloc(nread + 1);
+ if(buf == NULL) {
+ fprintf(stderr, "Out of memory.\n");
+ exit(3);
+ }
+ memcpy(buf, fbuf, buflen);
+ buf[buflen] = '\0';
+ } else {
+ buflen = strlen(buf);
+ tmp = (char*) malloc(buflen + nread + 1);
+ if(tmp == NULL) {
+ fprintf(stderr, "Out of memory.\n");
+ exit(3);
+ }
+ memcpy(tmp, buf, buflen);
+ memcpy(tmp+buflen, fbuf, nread);
+ tmp[buflen+nread] = '\0';
+ free(buf);
+ buf = tmp;
+ }
+ }
+ return buf;
+}
+
+couch_args*
+couch_parse_args(int argc, const char* argv[])
+{
+ couch_args* args;
+ int i = 1;
+
+ args = (couch_args*) malloc(sizeof(couch_args));
+ if(args == NULL)
+ return NULL;
+
+ memset(args, '\0', sizeof(couch_args));
+ args->stack_size = 8L * 1024L;
+
+ while(i < argc) {
+ if(strcmp("--http", argv[i]) == 0) {
+ args->use_http = 1;
+ } else if(strcmp("--stack-size", argv[i]) == 0) {
+ args->stack_size = atoi(argv[i+1]);
+ if(args->stack_size <= 0) {
+ fprintf(stderr, "Invalid stack size.\n");
+ exit(2);
+ }
+ } else {
+ args->script = slurp_file(args->script, argv[i]);
+ if(args->script_name == NULL) {
+ if(strcmp(argv[i], "-") == 0) {
+ args->script_name = "<stdin>";
+ } else {
+ args->script_name = argv[i];
+ }
+ } else {
+ args->script_name = "<multiple_files>";
+ }
+ }
+ i++;
+ }
+
+ if(args->script_name == NULL || args->script == NULL) {
+ fprintf(stderr, "No script provided.\n");
+ exit(3);
+ }
+
+ return args;
+}
+
+
+int
+couch_fgets(char* buf, int size, FILE* fp)
+{
+ int n, i, c;
+
+ if(size <= 0) return -1;
+ n = size - 1;
+
+ for(i = 0; i < n && (c = getc(fp)) != EOF; i++) {
+ buf[i] = c;
+ if(c == '\n') {
+ i++;
+ break;
+ }
+ }
+
+ buf[i] = '\0';
+ return i;
+}
+
+
+JSString*
+couch_readline(JSContext* cx, FILE* fp)
+{
+ JSString* str;
+ char* bytes = NULL;
+ char* tmp = NULL;
+ size_t used = 0;
+ size_t byteslen = 256;
+ size_t readlen = 0;
+
+ bytes = (char*) JS_malloc(cx, byteslen);
+ if(bytes == NULL) return NULL;
+
+ while((readlen = couch_fgets(bytes+used, byteslen-used, fp)) > 0) {
+ used += readlen;
+
+ if(bytes[used-1] == '\n') {
+ bytes[used-1] = '\0';
+ break;
+ }
+
+ // Double our buffer and read more.
+ byteslen *= 2;
+ tmp = (char*) JS_realloc(cx, bytes, byteslen);
+ if(!tmp) {
+ JS_free(cx, bytes);
+ return NULL;
+ }
+
+ bytes = tmp;
+ }
+
+ // Treat empty strings specially
+ if(used == 0) {
+ JS_free(cx, bytes);
+ return JSVAL_TO_STRING(JS_GetEmptyStringValue(cx));
+ }
+
+ // Shring the buffer to the actual data size
+ tmp = (char*) JS_realloc(cx, bytes, used);
+ if(!tmp) {
+ JS_free(cx, bytes);
+ return NULL;
+ }
+ bytes = tmp;
+ byteslen = used;
+
+ str = dec_string(cx, bytes, byteslen);
+ JS_free(cx, bytes);
+ return str;
+}
+
+
+JSObject*
+couch_readfile(JSContext* cx, FILE* fp)
+{
+ return NULL;
+}
+
+
+void
+couch_print(JSContext* cx, uintN argc, jsval* argv)
+{
+ char *bytes;
+ uintN i;
+
+ for(i = 0; i < argc; i++)
+ {
+ bytes = enc_string(cx, argv[i], NULL);
+ if(!bytes) return;
+
+ fprintf(stdout, "%s%s", i ? " " : "", bytes);
+ JS_free(cx, bytes);
+ }
+
+ fputc('\n', stdout);
+ fflush(stdout);
+}
+
+
+void
+couch_error(JSContext* cx, const char* mesg, JSErrorReport* report)
+{
+ if(!report || !JSREPORT_IS_WARNING(report->flags))
+ {
+ fprintf(stderr, "[couchjs] %s\n", mesg);
+ }
+}
+
+
+JSBool
+couch_load_funcs(JSContext* cx, JSObject* obj, JSFunctionSpec* funcs)
+{
+ JSFunctionSpec* f;
+ for(f = funcs; f->name != NULL; f++) {
+ if(!JS_DefineFunction(cx, obj, f->name, f->call, f->nargs, f->flags)) {
+ fprintf(stderr, "Failed to create function: %s\n", f->name);
+ return JS_FALSE;
+ }
+ }
+ return JS_TRUE;
+}
+
diff --git a/couchjs/c_src/util.h b/couchjs/c_src/util.h
new file mode 100644
index 00000000..54ccdaa5
--- /dev/null
+++ b/couchjs/c_src/util.h
@@ -0,0 +1,34 @@
+// 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 COUCHJS_UTIL_H
+#define COUCHJS_UTIL_H
+
+#include "sm.h"
+
+typedef struct {
+ int use_http;
+ int stack_size;
+ const char* script_name;
+ char* script;
+} couch_args;
+
+void couch_usage();
+couch_args* couch_parse_args(int argc, const char* argv[]);
+int couch_fgets(char* buf, int size, FILE* fp);
+JSString* couch_readline(JSContext* cx, FILE* fp);
+void couch_print(JSContext* cx, uintN argc, jsval* argv);
+void couch_error(JSContext* cx, const char* mesg, JSErrorReport* report);
+JSBool couch_load_funcs(JSContext* cx, JSObject* obj, JSFunctionSpec* funcs);
+
+
+#endif // Included util.h