blob: c37c41f2faed517b674d57bbd8230c19467951f1 [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 <sstream>
#include <jsapi.h>
#include <js/Initialization.h>
#include <js/CharacterEncoding.h>
#include <js/Conversions.h>
#include <mozilla/Unused.h>
#include "help.h"
#include "util.h"
std::string
js_to_string(JSContext* cx, JS::HandleValue val)
{
JS::AutoSaveExceptionState exc_state(cx);
JS::RootedString sval(cx);
sval = val.toString();
JS::UniqueChars chars(JS_EncodeStringToUTF8(cx, sval));
if(!chars) {
JS_ClearPendingException(cx);
return std::string();
}
return chars.get();
}
bool
js_to_string(JSContext* cx, JS::HandleValue val, std::string& str)
{
if(!val.isString()) {
return false;
}
if(JS_GetStringLength(val.toString()) == 0) {
str = "";
return true;
}
std::string conv = js_to_string(cx, val);
if(!conv.size()) {
return false;
}
str = conv;
return true;
}
JSString*
string_to_js(JSContext* cx, const std::string& raw)
{
JS::UTF8Chars utf8(raw.c_str(), raw.size());
JS::UniqueTwoByteChars utf16;
size_t len;
utf16.reset(JS::UTF8CharsToNewTwoByteCharsZ(cx, utf8, &len).get());
if(!utf16) {
return nullptr;
}
JSString* ret = JS_NewUCString(cx, utf16.get(), len);
if(ret) {
// JS_NewUCString took ownership on success. We shift
// the resulting pointer into Unused to silence the
// compiler warning.
mozilla::Unused << utf16.release();
}
return ret;
}
size_t
couch_readfile(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 = new char[nread + 1];
if(buf == NULL) {
fprintf(stderr, "Out of memory.\n");
exit(3);
}
memcpy(buf, fbuf, nread);
} else {
tmp = new char[buflen + nread + 1];
if(tmp == NULL) {
fprintf(stderr, "Out of memory.\n");
exit(3);
}
memcpy(tmp, buf, buflen);
memcpy(tmp+buflen, fbuf, nread);
delete buf;
buf = tmp;
}
buflen += nread;
buf[buflen] = '\0';
}
*outbuf_p = buf;
return buflen ;
}
couch_args*
couch_parse_args(int argc, const char* argv[])
{
couch_args* args;
int i = 1;
args = new couch_args();
if(args == NULL)
return NULL;
args->eval = 0;
args->use_http = 0;
args->use_test_funs = 0;
args->stack_size = 64L * 1024L * 1024L;
args->scripts = nullptr;
args->uri_file = nullptr;
args->uri = nullptr;
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("--eval", argv[i]) == 0) {
args->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 oldbyteslen = 256;
size_t readlen = 0;
bytes = static_cast<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.
oldbyteslen = byteslen;
byteslen *= 2;
tmp = static_cast<char*>(JS_realloc(cx, bytes, oldbyteslen, byteslen));
if(!tmp) {
JS_free(cx, bytes);
return NULL;
}
bytes = tmp;
}
// Treat empty strings specially
if(used == 0) {
JS_free(cx, bytes);
return JS_NewStringCopyZ(cx, nullptr);
}
// Shrink the buffer to the actual data size
tmp = static_cast<char*>(JS_realloc(cx, bytes, byteslen, used));
if(!tmp) {
JS_free(cx, bytes);
return NULL;
}
bytes = tmp;
byteslen = used;
str = string_to_js(cx, std::string(tmp));
JS_free(cx, bytes);
return str;
}
void
couch_print(JSContext* cx, JS::HandleValue obj, bool use_stderr)
{
FILE* stream = stdout;
if(use_stderr) {
stream = stderr;
}
std::string val = js_to_string(cx, obj);
fprintf(stream, "%s\n", val.c_str());
fflush(stream);
}
void
couch_error(JSContext* cx, JSErrorReport* report)
{
if(!report) {
return;
}
if(JSREPORT_IS_WARNING(report->flags)) {
return;
}
std::ostringstream msg;
msg << "error: " << report->message().c_str();
mozilla::Maybe<JSAutoCompartment> ac;
JS::RootedValue exc(cx);
JS::RootedObject exc_obj(cx);
JS::RootedObject stack_obj(cx);
JS::RootedString stack_str(cx);
JS::RootedValue stack_val(cx);
if(!JS_GetPendingException(cx, &exc)) {
goto done;
}
// Clear the exception before an JS method calls or the result is
// infinite, recursive error report generation.
JS_ClearPendingException(cx);
exc_obj.set(exc.toObjectOrNull());
stack_obj.set(JS::ExceptionStackOrNull(exc_obj));
if(!stack_obj) {
// Compilation errors don't have a stack
msg << " at ";
if(report->filename) {
msg << report->filename;
} else {
msg << "<unknown>";
}
if(report->lineno) {
msg << ':' << report->lineno << ':' << report->column;
}
goto done;
}
if(!JS::BuildStackString(cx, stack_obj, &stack_str, 2)) {
goto done;
}
stack_val.set(JS::StringValue(stack_str));
msg << std::endl << std::endl << js_to_string(cx, stack_val).c_str();
done:
msg << std::endl;
fprintf(stderr, "%s", msg.str().c_str());
}
void
couch_oom(JSContext* cx, void* data)
{
fprintf(stderr, "out of memory\n");
exit(1);
}
bool
couch_load_funcs(JSContext* cx, JS::HandleObject obj, JSFunctionSpec* funcs)
{
JSFunctionSpec* f;
for(f = funcs; f->name != NULL; f++) {
if(!JS_DefineFunction(cx, obj, f->name, f->call.op, f->nargs, f->flags)) {
fprintf(stderr, "Failed to create function: %s\n", f->name);
return false;
}
}
return true;
}