blob: 7919025d37d5c1049b67bad191c6ce086ea991f8 [file] [log] [blame]
// 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 <jsapi.h>
#include "help.h"
#include "util.h"
#include "utf8.h"
size_t
slurp_file(const char* file, char** outbuf_p)
{
FILE* fp;
char fbuf[16384];
char *buf = NULL;
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) {
buf = (char*) malloc(nread + 1);
if(buf == NULL) {
fprintf(stderr, "Out of memory.\n");
exit(3);
}
memcpy(buf, fbuf, nread);
} else {
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);
free(buf);
buf = tmp;
}
buflen += nread;
buf[buflen] = '\0';
}
*outbuf_p = buf;
return buflen + 1;
}
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 = 64L * 1024L * 1024L;
while(i < argc) {
if(strcmp("-h", argv[i]) == 0) {
DISPLAY_USAGE;
exit(0);
} else if(strcmp("-V", argv[i]) == 0) {
DISPLAY_VERSION;
exit(0);
} else if(strcmp("-H", argv[i]) == 0) {
args->use_http = 1;
} else if(strcmp("-T", argv[i]) == 0) {
args->use_test_funs = 1;
} else if(strcmp("-S", argv[i]) == 0) {
args->stack_size = atoi(argv[++i]);
if(args->stack_size <= 0) {
fprintf(stderr, "Invalid stack size.\n");
exit(2);
}
} else if(strcmp("-u", argv[i]) == 0) {
args->uri_file = argv[++i];
} else if(strcmp("--no-eval", argv[i]) == 0) {
args->no_eval = 1;
} else if(strcmp("--", argv[i]) == 0) {
i++;
break;
} else {
break;
}
i++;
}
if(i >= argc) {
DISPLAY_USAGE;
exit(3);
}
args->scripts = argv + i;
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 = 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 = 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 = 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;
}
JSString*
couch_readfile(JSContext* cx, const char* filename)
{
JSString *string;
size_t byteslen;
char *bytes;
if((byteslen = slurp_file(filename, &bytes))) {
string = dec_string(cx, bytes, byteslen);
free(bytes);
return string;
}
return NULL;
}
void
couch_print(JSContext* cx, uintN argc, jsval* argv)
{
char *bytes = NULL;
FILE *stream = stdout;
if (argc) {
if (argc > 1 && argv[1] == JSVAL_TRUE) {
stream = stderr;
}
bytes = enc_string(cx, argv[0], NULL);
if(!bytes) return;
fprintf(stream, "%s", bytes);
JS_free(cx, bytes);
}
fputc('\n', stream);
fflush(stream);
}
void
couch_error(JSContext* cx, const char* mesg, JSErrorReport* report)
{
jsval v, replace;
char* bytes;
JSObject* regexp, *stack;
jsval re_args[2];
if(!report || !JSREPORT_IS_WARNING(report->flags))
{
fprintf(stderr, "%s\n", mesg);
// Print a stack trace, if available.
if (JSREPORT_IS_EXCEPTION(report->flags) &&
JS_GetPendingException(cx, &v))
{
// Clear the exception before an JS method calls or the result is
// infinite, recursive error report generation.
JS_ClearPendingException(cx);
// Use JS regexp to indent the stack trace.
// If the regexp can't be created, don't JS_ReportError since it is
// probably not productive to wind up here again.
#ifdef SM185
if(JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "stack", &v) &&
(regexp = JS_NewRegExpObjectNoStatics(
cx, "^(?=.)", 6, JSREG_GLOB | JSREG_MULTILINE)))
#else
if(JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "stack", &v) &&
(regexp = JS_NewRegExpObject(
cx, "^(?=.)", 6, JSREG_GLOB | JSREG_MULTILINE)))
#endif
{
// Set up the arguments to ``String.replace()``
re_args[0] = OBJECT_TO_JSVAL(regexp);
re_args[1] = STRING_TO_JSVAL(JS_InternString(cx, "\t"));
// Perform the replacement
if(JS_ValueToObject(cx, v, &stack) &&
JS_GetProperty(cx, stack, "replace", &replace) &&
JS_CallFunctionValue(cx, stack, replace, 2, re_args, &v))
{
// Print the result
bytes = enc_string(cx, v, NULL);
fprintf(stderr, "Stacktrace:\n%s", bytes);
JS_free(cx, bytes);
}
}
}
}
}
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;
}