JSValueRef function_raw_read_stdin(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef args[], JSValueRef *exception) { char buf[1024 + 1]; size_t n = fread(buf, 1, config.is_tty ? 1 : 1024, stdin); if (n > 0) { buf[n] = '\0'; return c_string_to_value(ctx, buf); } return JSValueMakeNull(ctx); }
JSValueRef function_list_files(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef args[], JSValueRef *exception) { if (argc == 1 && JSValueGetType(ctx, args[0]) == kJSTypeString) { char *path = value_to_c_string(ctx, args[0]); size_t capacity = 32; size_t count = 0; JSValueRef *paths = malloc(capacity * sizeof(paths)); DIR *d; struct dirent *dir; d = opendir(path); size_t path_len = strlen(path); if (path_len && path[path_len - 1] == '/') { path[--path_len] = 0; } if (d) { while ((dir = readdir(d)) != NULL) { if (strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..")) { char *buf = malloc((path_len + strlen(dir->d_name) + 2)); sprintf(buf, "%s/%s", path, dir->d_name); paths[count++] = c_string_to_value(ctx, buf); free(buf); if (count == capacity) { capacity *= 2; paths = realloc(paths, capacity * sizeof(paths)); } } } closedir(d); } free(path); JSValueRef rv = JSObjectMakeArray(ctx, count, paths, NULL); free(paths); return rv; } return JSValueMakeNull(ctx); }
JSValueRef function_file_input_stream_open(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef args[], JSValueRef *exception) { if (argc == 1 && JSValueGetType(ctx, args[0]) == kJSTypeString) { char *path = value_to_c_string(ctx, args[0]); uint64_t descriptor = file_open_read(path); free(path); char *descriptor_str = descriptor_int_to_str(descriptor); JSValueRef rv = c_string_to_value(ctx, descriptor_str); free(descriptor_str); return rv; } return JSValueMakeNull(ctx); }
JSValueRef function_read_password(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef args[], JSValueRef *exception) { if (argc == 1 && JSValueGetType(ctx, args[0]) == kJSTypeString) { char *prompt = value_to_c_string(ctx, args[0]); char *pass = getpass(prompt); JSValueRef rv; if (pass) { rv = c_string_to_value(ctx, pass); memset(pass, 0, strlen(pass)); } else { rv = JSValueMakeNull(ctx); } free(prompt); return rv; } return JSValueMakeNull(ctx); }
JSValueRef function_file_writer_open(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef args[], JSValueRef *exception) { if (argc == 3 && JSValueGetType(ctx, args[0]) == kJSTypeString && JSValueGetType(ctx, args[1]) == kJSTypeBoolean) { char *path = value_to_c_string(ctx, args[0]); bool append = JSValueToBoolean(ctx, args[1]); char *encoding = value_to_c_string(ctx, args[2]); uint64_t descriptor = ufile_open_write(path, append, encoding); free(path); free(encoding); char *descriptor_str = descriptor_int_to_str(descriptor); JSValueRef rv = c_string_to_value(ctx, descriptor_str); free(descriptor_str); return rv; } return JSValueMakeNull(ctx); }
JSValueRef timeout_data_to_js_value(JSContextRef ctx, struct timeout_data_t *timeout_data) { char *id_str = timeout_id_to_str(timeout_data->id); JSValueRef rv = c_string_to_value(ctx, id_str); free(id_str); return rv; }
JSValueRef function_fstat(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef args[], JSValueRef *exception) { if (argc == 1 && JSValueGetType(ctx, args[0]) == kJSTypeString) { char *path = value_to_c_string(ctx, args[0]); struct stat file_stat; int retval = lstat(path, &file_stat); if (retval == 0) { JSObjectRef result = JSObjectMake(ctx, NULL, NULL); char *type = "unknown"; if (S_ISDIR(file_stat.st_mode)) { type = "directory"; } else if (S_ISREG(file_stat.st_mode)) { type = "file"; } else if (S_ISLNK(file_stat.st_mode)) { type = "symbolic-link"; } else if (S_ISSOCK(file_stat.st_mode)) { type = "socket"; } else if (S_ISFIFO(file_stat.st_mode)) { type = "fifo"; } else if (S_ISCHR(file_stat.st_mode)) { type = "character-special"; } else if (S_ISBLK(file_stat.st_mode)) { type = "block-special"; } JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("type"), c_string_to_value(ctx, type), kJSPropertyAttributeReadOnly, NULL); double device_id = (double) file_stat.st_rdev; if (device_id) { JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("device-id"), JSValueMakeNumber(ctx, device_id), kJSPropertyAttributeReadOnly, NULL); } double file_number = (double) file_stat.st_ino; if (file_number) { JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("file-number"), JSValueMakeNumber(ctx, file_number), kJSPropertyAttributeReadOnly, NULL); } JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("permissions"), JSValueMakeNumber(ctx, (double) (ACCESSPERMS & file_stat.st_mode)), kJSPropertyAttributeReadOnly, NULL); JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("reference-count"), JSValueMakeNumber(ctx, (double) file_stat.st_nlink), kJSPropertyAttributeReadOnly, NULL); JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("uid"), JSValueMakeNumber(ctx, (double) file_stat.st_uid), kJSPropertyAttributeReadOnly, NULL); struct passwd *uid_passwd = getpwuid(file_stat.st_uid); if (uid_passwd) { JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("uname"), c_string_to_value(ctx, uid_passwd->pw_name), kJSPropertyAttributeReadOnly, NULL); } JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("gid"), JSValueMakeNumber(ctx, (double) file_stat.st_gid), kJSPropertyAttributeReadOnly, NULL); struct group *gid_group = getgrgid(file_stat.st_gid); if (gid_group) { JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("gname"), c_string_to_value(ctx, gid_group->gr_name), kJSPropertyAttributeReadOnly, NULL); } JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("file-size"), JSValueMakeNumber(ctx, (double) file_stat.st_size), kJSPropertyAttributeReadOnly, NULL); #ifdef __APPLE__ #define birthtime(x) x.st_birthtime #else #define birthtime(x) x.st_ctime #endif JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("created"), JSValueMakeNumber(ctx, 1000 * birthtime(file_stat)), kJSPropertyAttributeReadOnly, NULL); JSObjectSetProperty(ctx, result, JSStringCreateWithUTF8CString("modified"), JSValueMakeNumber(ctx, 1000 * file_stat.st_mtime), kJSPropertyAttributeReadOnly, NULL); return result; } free(path); } return JSValueMakeNull(ctx); }
int main(int argc, char **argv) { struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"legal", no_argument, NULL, 'l'}, {"verbose", no_argument, NULL, 'v'}, {"quiet", no_argument, NULL, 'q'}, {"repl", no_argument, NULL, 'r'}, {"static-fns", no_argument, NULL, 's'}, {"elide-asserts", no_argument, NULL, 'a'}, {"cache", required_argument, NULL, 'k'}, {"eval", required_argument, NULL, 'e'}, {"theme", required_argument, NULL, 't'}, {"classpath", required_argument, NULL, 'c'}, {"auto-cache", no_argument, NULL, 'K'}, {"init", required_argument, NULL, 'i'}, {"main", required_argument, NULL, 'm'}, // development options {"javascript", no_argument, NULL, 'j'}, {"out", required_argument, NULL, 'o'}, {0, 0, 0, 0} }; int opt, option_index; while ((opt = getopt_long(argc, argv, "h?lvrsak:je:t:c:o:Ki:qm:", long_options, &option_index)) != -1) { switch (opt) { case 'h': usage(argv[0]); exit(0); case 'l': legal(); return 0; case 'v': verbose = true; break; case 'q': quiet = true; break; case 'r': repl = true; break; case 's': static_fns = true; break; case 'a': elide_asserts = true; break; case 'k': cache_path = argv[optind - 1]; break; case 'K': cache_path = ".planck_cache"; { char *path_copy = strdup(cache_path); char *dir = dirname(path_copy); if (mkdir_p(dir) < 0) { fprintf(stderr, "Could not create %s: %s\n", cache_path, strerror(errno)); } free(path_copy); } break; case 'j': javascript = true; break; case 'e': num_scripts += 1; scripts = realloc(scripts, num_scripts * sizeof(struct script)); scripts[num_scripts - 1].type = "text"; scripts[num_scripts - 1].expression = true; scripts[num_scripts - 1].source = argv[optind - 1]; break; case 'i': num_scripts += 1; scripts = realloc(scripts, num_scripts * sizeof(struct script)); scripts[num_scripts - 1].type = "path"; scripts[num_scripts - 1].expression = false; scripts[num_scripts - 1].source = argv[optind - 1]; break; case 'm': main_ns_name = argv[optind - 1]; case 't': theme = argv[optind - 1]; break; case 'c': { char *classpath = argv[optind - 1]; char *source = strtok(classpath, ":"); while (source != NULL) { char *type = "src"; if (str_has_suffix(source, ".jar") == 0) { type = "jar"; } num_src_paths += 1; src_paths = realloc(src_paths, num_src_paths * sizeof(struct src_path)); src_paths[num_src_paths - 1].type = type; src_paths[num_src_paths - 1].path = strdup(source); source = strtok(NULL, ":"); } break; } case 'o': out_path = argv[optind - 1]; break; case '?': usage(argv[0]); exit(1); default: printf("unhandled argument: %c\n", opt); } } int num_rest_args = 0; char **rest_args = NULL; if (optind < argc) { num_rest_args = argc - optind; rest_args = malloc((argc - optind) * sizeof(char*)); int i = 0; while (optind < argc) { rest_args[i++] = argv[optind++]; } } if (num_scripts == 0 && main_ns_name == NULL && num_rest_args == 0) { repl = true; } if (main_ns_name != NULL && repl) { printf("Only one main-opt can be specified."); } JSGlobalContextRef ctx = JSGlobalContextCreate(NULL); JSStringRef nameRef = JSStringCreateWithUTF8CString("planck"); JSGlobalContextSetName(ctx, nameRef); evaluate_script(ctx, "var global = this;", "<init>"); register_global_function(ctx, "AMBLY_IMPORT_SCRIPT", function_import_script); bootstrap(ctx, out_path); register_global_function(ctx, "PLANCK_CONSOLE_LOG", function_console_log); register_global_function(ctx, "PLANCK_CONSOLE_ERROR", function_console_error); evaluate_script(ctx, "var console = {};"\ "console.log = PLANCK_CONSOLE_LOG;"\ "console.error = PLANCK_CONSOLE_ERROR;", "<init>"); evaluate_script(ctx, "var PLANCK_VERSION = \"" PLANCK_VERSION "\";", "<init>"); // require app namespaces evaluate_script(ctx, "goog.require('planck.repl');", "<init>"); // without this things won't work evaluate_script(ctx, "var window = global;", "<init>"); register_global_function(ctx, "PLANCK_READ_FILE", function_read_file); register_global_function(ctx, "PLANCK_LOAD", function_load); register_global_function(ctx, "PLANCK_LOAD_DEPS_CLJS_FILES", function_load_deps_cljs_files); register_global_function(ctx, "PLANCK_CACHE", function_cache); register_global_function(ctx, "PLANCK_EVAL", function_eval); register_global_function(ctx, "PLANCK_GET_TERM_SIZE", function_get_term_size); register_global_function(ctx, "PLANCK_PRINT_FN", function_print_fn); register_global_function(ctx, "PLANCK_PRINT_ERR_FN", function_print_err_fn); register_global_function(ctx, "PLANCK_SET_EXIT_VALUE", function_set_exit_value); is_tty = isatty(STDIN_FILENO) == 1; register_global_function(ctx, "PLANCK_RAW_READ_STDIN", function_raw_read_stdin); register_global_function(ctx, "PLANCK_RAW_WRITE_STDOUT", function_raw_write_stdout); register_global_function(ctx, "PLANCK_RAW_FLUSH_STDOUT", function_raw_flush_stdout); register_global_function(ctx, "PLANCK_RAW_WRITE_STDERR", function_raw_write_stderr); register_global_function(ctx, "PLANCK_RAW_FLUSH_STDERR", function_raw_flush_stderr); { JSValueRef arguments[num_rest_args]; for (int i = 0; i < num_rest_args; i++) { arguments[i] = c_string_to_value(ctx, rest_args[i]); } JSValueRef args_ref = JSObjectMakeArray(ctx, num_rest_args, arguments, NULL); JSValueRef global_obj = JSContextGetGlobalObject(ctx); JSStringRef prop = JSStringCreateWithUTF8CString("PLANCK_INITIAL_COMMAND_LINE_ARGS"); JSObjectSetProperty(ctx, JSValueToObject(ctx, global_obj, NULL), prop, args_ref, kJSPropertyAttributeNone, NULL); JSStringRelease(prop); } evaluate_script(ctx, "cljs.core.set_print_fn_BANG_.call(null,PLANCK_PRINT_FN);", "<init>"); evaluate_script(ctx, "cljs.core.set_print_err_fn_BANG_.call(null,PLANCK_PRINT_ERR_FN);", "<init>"); char *elide_script = str_concat("cljs.core._STAR_assert_STAR_ = ", elide_asserts ? "false" : "true"); evaluate_script(ctx, elide_script, "<init>"); free(elide_script); { JSValueRef arguments[4]; arguments[0] = JSValueMakeBoolean(ctx, repl); arguments[1] = JSValueMakeBoolean(ctx, verbose); JSValueRef cache_path_ref = NULL; if (cache_path != NULL) { JSStringRef cache_path_str = JSStringCreateWithUTF8CString(cache_path); cache_path_ref = JSValueMakeString(ctx, cache_path_str); } arguments[2] = cache_path_ref; arguments[3] = JSValueMakeBoolean(ctx, static_fns); JSValueRef ex = NULL; JSObjectCallAsFunction(ctx, get_function(ctx, "planck.repl", "init"), JSContextGetGlobalObject(ctx), 4, arguments, &ex); debug_print_value("planck.repl/init", ctx, ex); } if (repl) { evaluate_source(ctx, "text", "(require '[planck.repl :refer-macros [apropos dir find-doc doc source pst]])", true, false, "cljs.user", "dumb"); } evaluate_script(ctx, "goog.provide('cljs.user');", "<init>"); evaluate_script(ctx, "goog.require('cljs.core');", "<init>"); evaluate_script(ctx, "cljs.core._STAR_assert_STAR_ = true;", "<init>"); // Process init arguments for (int i = 0; i < num_scripts; i++) { // TODO: exit if not successfull evaluate_source(ctx, scripts[i].type, scripts[i].source, scripts[i].expression, false, NULL, theme); } // Process main arguments if (main_ns_name != NULL) { run_main_in_ns(ctx, main_ns_name, num_rest_args, rest_args); } else if (!repl && num_rest_args > 0) { char *path = rest_args[0]; struct script script; if (strcmp(path, "-") == 0) { char *source = read_all(stdin); script.type = "text"; script.source = source; script.expression = false; } else { script.type = "path"; script.source = path; script.expression = false; } evaluate_source(ctx, script.type, script.source, script.expression, false, NULL, theme); } else if (repl) { if (!quiet) { banner(); } char *home = getenv("HOME"); char *history_path = NULL; if (home != NULL) { char history_name[] = ".planck_history"; int len = strlen(home) + strlen(history_name) + 2; history_path = malloc(len * sizeof(char)); snprintf(history_path, len, "%s/%s", home, history_name); linenoiseHistoryLoad(history_path); } char *prompt = javascript ? " > " : " => "; char *line; while ((line = linenoise(prompt)) != NULL) { if (javascript) { JSValueRef res = evaluate_script(ctx, line, "<stdin>"); print_value("", ctx, res); } else { evaluate_source(ctx, "text", line, true, true, "cljs.user", theme); } linenoiseHistoryAdd(line); if (history_path != NULL) { linenoiseHistorySave(history_path); } free(line); } } return exit_value; }