| // 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("--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 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; |
| } |