X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/61c7112146ed7cc27b0a921843efdaaa7878d449..2c4a8bb6222b88ff96fbf25372179646ce15f706:/Console.cpp?ds=sidebyside diff --git a/Console.cpp b/Console.cpp index 93df749..2f24e58 100644 --- a/Console.cpp +++ b/Console.cpp @@ -1,5 +1,5 @@ -/* Cycript - Optimizing JavaScript Compiler/Runtime - * Copyright (C) 2009-2015 Jay Freeman (saurik) +/* Cycript - The Truly Universal Scripting Language + * Copyright (C) 2009-2016 Jay Freeman (saurik) */ /* GNU Affero General Public License, Version 3 {{{ */ @@ -68,6 +68,7 @@ #include #endif +#include "Code.hpp" #include "Driver.hpp" #include "Error.hpp" #include "Highlight.hpp" @@ -219,7 +220,9 @@ static void sigint(int) { case Parsing: longjmp(ctrlc_, 1); case Running: +#ifndef __ANDROID__ CYCancel(); +#endif return; case Sending: return; @@ -246,11 +249,23 @@ void Setup(CYOutput &out, CYDriver &driver, CYOptions &options, bool lower) { driver.Replace(options); } -static CYUTF8String Run(CYPool &pool, int client, CYUTF8String code) { - const char *json; - uint32_t size; +class CYRemote { + public: + virtual CYUTF8String Run(CYPool &pool, CYUTF8String code) = 0; + + inline CYUTF8String Run(CYPool &pool, const std::string &code) { + return Run(pool, CYUTF8String(code.c_str(), code.size())); + } +}; + +class CYLocalRemote : + public CYRemote +{ + public: + virtual CYUTF8String Run(CYPool &pool, CYUTF8String code) { + const char *json; + uint32_t size; - if (client == -1) { mode_ = Running; #ifdef CY_EXECUTE json = CYExecute(CYGetJSContext(), pool, code); @@ -262,63 +277,124 @@ static CYUTF8String Run(CYPool &pool, int client, CYUTF8String code) { size = 0; else size = strlen(json); - } else { + + return CYUTF8String(json, size); + } +}; + +class CYSocketRemote : + public CYRemote +{ + private: + int socket_; + + public: + CYSocketRemote(const char *host, const char *port) { + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = 0; + + struct addrinfo *infos; + _syscall(getaddrinfo(host, port, &hints, &infos)); + + _assert(infos != NULL); try { + for (struct addrinfo *info(infos); info != NULL; info = info->ai_next) { + socket_ = _syscall(socket(info->ai_family, info->ai_socktype, info->ai_protocol)); try { + _syscall(connect(socket_, info->ai_addr, info->ai_addrlen)); + break; + } catch (...) { + _syscall(close(socket_)); + throw; + } + } + } catch (...) { + freeaddrinfo(infos); + throw; + } + } + + virtual CYUTF8String Run(CYPool &pool, CYUTF8String code) { + const char *json; + uint32_t size; + mode_ = Sending; size = code.size; - _assert(CYSendAll(client, &size, sizeof(size))); - _assert(CYSendAll(client, code.data, code.size)); + _assert(CYSendAll(socket_, &size, sizeof(size))); + _assert(CYSendAll(socket_, code.data, code.size)); mode_ = Waiting; - _assert(CYRecvAll(client, &size, sizeof(size))); - if (size == _not(uint32_t)) + _assert(CYRecvAll(socket_, &size, sizeof(size))); + if (size == _not(uint32_t)) { + size = 0; json = NULL; - else { + } else { char *temp(new(pool) char[size + 1]); - _assert(CYRecvAll(client, temp, size)); + _assert(CYRecvAll(socket_, temp, size)); temp[size] = '\0'; json = temp; } mode_ = Working; + + return CYUTF8String(json, size); } +}; - return CYUTF8String(json, size); -} +void InjectLibrary(pid_t, std::ostream &stream, int, const char *const []); -static CYUTF8String Run(CYPool &pool, int client, const std::string &code) { - return Run(pool, client, CYUTF8String(code.c_str(), code.size())); -} +class CYInjectRemote : + public CYRemote +{ + private: + int pid_; + + public: + CYInjectRemote(int pid) : + pid_(pid) + { + // XXX: wait + } + + virtual CYUTF8String Run(CYPool &pool, CYUTF8String code) { + std::ostringstream stream; + const char *args[2] = {"-e", code.data}; + InjectLibrary(pid_, stream, 2, args); + std::string json(stream.str()); + if (!json.empty() && json[json.size() - 1] == '\n') + json.resize(json.size() - 1); + return CYUTF8String(strdup(json.c_str()), json.size()); + } +}; static std::ostream *out_; -static void Output(CYUTF8String json, std::ostream *out, bool expand = false) { +static void Output(CYUTF8String json, std::ostream *out, bool reparse = false) { + CYPool pool; + + if (reparse) do { + CYStream stream(json.data, json.data + json.size); + CYDriver driver(pool, stream); + if (driver.Parse(CYMarkExpression)) + break; + std::stringbuf str; + CYOptions options; + CYOutput out(str, options); + out.pretty_ = true; + out << *driver.context_; + std::string data(str.str()); + json = CYPoolUTF8String(pool, data); + if (json.size == 0) + json.data = NULL; + } while (false); + const char *data(json.data); size_t size(json.size); - if (data == NULL || out == NULL) + if (size == 0 || out == NULL) return; - if (!expand || - data[0] != '@' && data[0] != '"' && data[0] != '\'' || - data[0] == '@' && data[1] != '"' && data[1] != '\'' - ) - CYLexerHighlight(data, size, *out); - else for (size_t i(0); i != size; ++i) - if (data[i] != '\\') - *out << data[i]; - else switch(data[++i]) { - case '\0': goto done; - case '\\': *out << '\\'; break; - case '\'': *out << '\''; break; - case '"': *out << '"'; break; - case 'b': *out << '\b'; break; - case 'f': *out << '\f'; break; - case 'n': *out << '\n'; break; - case 'r': *out << '\r'; break; - case 't': *out << '\t'; break; - case 'v': *out << '\v'; break; - default: *out << '\\'; --i; break; - } - - done: + CYLexerHighlight(data, size, *out); *out << std::endl; } @@ -326,10 +402,10 @@ int (*append_history$)(int, const char *); static std::string command_; -static int client_; +static CYRemote *remote_; static CYUTF8String Run(CYPool &pool, const std::string &code) { - return Run(pool, client_, code); + return remote_->Run(pool, code); } static char **Complete(const char *word, int start, int end) { @@ -361,7 +437,7 @@ class History { if (*character == '\x01') *character = '\n'; } - ~History() { + ~History() { try { for (HIST_ENTRY *history((history_set_pos(0), current_history())); history; history = next_history()) for (char *character(history->line); *character; ++character) if (*character == '\n') *character = '\x01'; @@ -373,9 +449,15 @@ class History { } else { _assert(write_history(histfile_.c_str()) == 0); } - } + } catch (const CYException &error) { + CYPool pool; + std::cout << error.PoolCString(pool) << std::endl; + } } void operator +=(std::string command) { + if (HIST_ENTRY *entry = history_get(where_history())) + if (command == entry->line) + return; add_history(command.c_str()); ++histlines_; } @@ -389,17 +471,25 @@ static Type_ *CYmemrchr(Type_ *data, Type_ value, size_t size) { return NULL; } -static int CYConsoleKeyBypass(int count, int key) { - rl_point = rl_end; - rl_insert(count, '\n'); - return rl_newline(count, key); +static void _lblcall(int (*command)(int, int), int count, int key) { + int last(_rl_last_c_pos); + // rl_rubout crashes in _rl_erase_at_end_of_line if _rl_last_c_pos != 0 + if (command == &rl_rubout) + _rl_last_c_pos = 0; + for (int i(0); i != count; ++i) + if (command(1, key) != 0) + _assert(false); + _rl_last_c_pos = last; } static int CYConsoleKeyReturn(int count, int key) { if (rl_point != rl_end) { - if (memchr(rl_line_buffer, '\n', rl_end) == NULL) - return CYConsoleKeyBypass(count, key); + if (memchr(rl_line_buffer, '\n', rl_end) == NULL) { + _lblcall(&rl_newline, count, key); + return 0; + } + insert: char *before(CYmemrchr(rl_line_buffer, '\n', rl_point)); if (before == NULL) before = rl_line_buffer; @@ -410,23 +500,22 @@ static int CYConsoleKeyReturn(int count, int key) { int adjust(rl_line_buffer + space - 1 - before); if (space == rl_point && adjust != 0) - rl_rubout(adjust, '\b'); + _lblcall(&rl_rubout, adjust, '\b'); - rl_insert(count, '\n'); + _lblcall(&rl_insert, count, '\n'); if (adjust != 0) - rl_insert(adjust, ' '); + _lblcall(&rl_insert, adjust, ' '); return 0; } - rl_insert(count, '\n'); - bool done(false); if (rl_line_buffer[0] == '?') done = true; else { std::string command(rl_line_buffer, rl_end); - std::istringstream stream(command); + command += '\n'; + std::stringbuf stream(command); size_t last(std::string::npos); for (size_t i(0); i != std::string::npos; i = command.find('\n', i + 1)) @@ -444,13 +533,17 @@ static int CYConsoleKeyReturn(int count, int key) { done = true; } - if (done) - return rl_newline(count, key); - return 0; + if (done) { + _lblcall(&rl_newline, count, key); + return 0; + } + + // XXX: this was the most obvious fix, but is seriously dumb + goto insert; } static int CYConsoleKeyUp(int count, int key) { - while (count-- != 0) { + for (; count != 0; --count) { char *after(CYmemrchr(rl_line_buffer, '\n', rl_point)); if (after == NULL) { if (int value = rl_get_previous_history(1, key)) @@ -473,7 +566,7 @@ static int CYConsoleKeyUp(int count, int key) { } static int CYConsoleKeyDown(int count, int key) { - while (count-- != 0) { + for (; count != 0; --count) { char *after(static_cast(memchr(rl_line_buffer + rl_point, '\n', rl_end - rl_point))); if (after == NULL) { int where(where_history()); @@ -514,25 +607,34 @@ static int CYConsoleLineBegin(int count, int key) { static int CYConsoleLineEnd(int count, int key) { while (rl_point != rl_end && rl_line_buffer[rl_point] != '\n') ++rl_point; + if (rl_point != rl_end && rl_editing_mode == 0) + --rl_point; return 0; } static int CYConsoleKeyBack(int count, int key) { - while (count-- != 0) { + for (; count != 0; --count) { + if (rl_point == 0) + return 1; + char *before(CYmemrchr(rl_line_buffer, '\n', rl_point)); - if (before == NULL) - return rl_rubout(count, key); + if (before == NULL) { + int adjust(std::min(count, rl_point)); + _lblcall(&rl_rubout, adjust, key); + count -= adjust - 1; + continue; + } int start(before + 1 - rl_line_buffer); if (start == rl_point) rubout: { - rl_rubout(1, key); + _lblcall(&rl_rubout, 1, key); continue; } for (int i(start); i != rl_point; ++i) if (rl_line_buffer[i] != ' ') goto rubout; - rl_rubout((rl_point - start) % 4 ?: 4, key); + _lblcall(&rl_rubout, (rl_point - start) % 4 ?: 4, key); } return 0; @@ -546,30 +648,52 @@ static int CYConsoleKeyTab(int count, int key) { for (int i(start); i != rl_point; ++i) if (rl_line_buffer[i] != ' ') goto complete; - return rl_insert(4 - (rl_point - start) % 4, ' '); + _lblcall(&rl_insert, 4 - (rl_point - start) % 4, ' '); + return 0; } -static void CYConsoleRemapBind(rl_command_func_t *from, rl_command_func_t *to) { - char **keyseqs(rl_invoking_keyseqs(from)); +static void CYConsoleRemapBind(Keymap map, rl_command_func_t *from, rl_command_func_t *to) { + char **keyseqs(rl_invoking_keyseqs_in_map(from, map)); if (keyseqs == NULL) return; for (char **keyseq(keyseqs); *keyseq != NULL; ++keyseq) { - rl_bind_keyseq(*keyseq, to); + rl_bind_keyseq_in_map(*keyseq, to, map); free(*keyseq); } free(keyseqs); } +static void CYConsoleRemapKeys(Keymap map) { + CYConsoleRemapBind(map, &rl_beg_of_line, &CYConsoleLineBegin); + CYConsoleRemapBind(map, &rl_end_of_line, &CYConsoleLineEnd); + + CYConsoleRemapBind(map, &rl_get_previous_history, &CYConsoleKeyUp); + CYConsoleRemapBind(map, &rl_get_next_history, &CYConsoleKeyDown); + + CYConsoleRemapBind(map, &rl_rubout, &CYConsoleKeyBack); + CYConsoleRemapBind(map, &rl_complete, &CYConsoleKeyTab); +} + static void CYConsolePrepTerm(int meta) { rl_prep_terminal(meta); - CYConsoleRemapBind(&rl_beg_of_line, &CYConsoleLineBegin); - CYConsoleRemapBind(&rl_end_of_line, &CYConsoleLineEnd); - CYConsoleRemapBind(&rl_rubout, &CYConsoleKeyBack); + CYConsoleRemapKeys(emacs_standard_keymap); + CYConsoleRemapKeys(emacs_meta_keymap); + CYConsoleRemapKeys(emacs_ctlx_keymap); + CYConsoleRemapKeys(vi_insertion_keymap); + CYConsoleRemapKeys(vi_movement_keymap); +} + +static void CYOutputRun(const std::string &code, bool reparse = false) { + CYPool pool; + Output(Run(pool, code), &std::cout, reparse); } static void Console(CYOptions &options) { std::string basedir; +#ifdef __ANDROID__ + basedir = "/data/local/tmp"; +#else if (const char *home = getenv("HOME")) basedir = home; else { @@ -580,6 +704,7 @@ static void Console(CYOptions &options) { passwd = getpwuid(getuid()); basedir = passwd->pw_dir; } +#endif basedir += "/.cycript"; mkdir(basedir.c_str(), 0700); @@ -591,41 +716,28 @@ static void Console(CYOptions &options) { bool bypass(false); bool debug(false); - bool expand(false); bool lower(true); + bool reparse(false); out_ = &std::cout; rl_completer_word_break_characters = break_; rl_attempted_completion_function = &Complete; - rl_bind_key(TAB, &CYConsoleKeyTab); - - rl_redisplay_function = CYDisplayUpdate; - rl_prep_term_function = CYConsolePrepTerm; - -#if defined (__MSDOS__) - rl_bind_keyseq("\033[0A", &CYConsoleKeyUp); - rl_bind_keyseq("\033[0D", &CYConsoleKeyDown); -#endif - rl_bind_keyseq("\033[A", &CYConsoleKeyUp); - rl_bind_keyseq("\033[B", &CYConsoleKeyDown); - rl_bind_keyseq("\033OA", &CYConsoleKeyUp); - rl_bind_keyseq("\033OB", &CYConsoleKeyDown); -#if defined (__MINGW32__) - rl_bind_keyseq("\340H", &CYConsoleKeyUp); - rl_bind_keyseq("\340P", &CYConsoleKeyDown); - rl_bind_keyseq("\\000H", &CYConsoleKeyUp); - rl_bind_keyseq("\\000P", &CYConsoleKeyDown); -#endif + if (cur_term != NULL) { + rl_redisplay_function = CYDisplayUpdate; + rl_prep_term_function = CYConsolePrepTerm; + } - struct sigaction action; - sigemptyset(&action.sa_mask); - action.sa_handler = &sigint; - action.sa_flags = 0; - sigaction(SIGINT, &action, NULL); + CYOutputRun(""); for (;;) { + struct sigaction action; + sigemptyset(&action.sa_mask); + action.sa_handler = &sigint; + action.sa_flags = 0; + sigaction(SIGINT, &action, NULL); + if (setjmp(ctrlc_) != 0) { mode_ = Working; *out_ << std::endl; @@ -633,8 +745,8 @@ static void Console(CYOptions &options) { } if (bypass) { - rl_bind_key('\r', &CYConsoleKeyBypass); - rl_bind_key('\n', &CYConsoleKeyBypass); + rl_bind_key('\r', &rl_newline); + rl_bind_key('\n', &rl_newline); } else { rl_bind_key('\r', &CYConsoleKeyReturn); rl_bind_key('\n', &CYConsoleKeyReturn); @@ -651,11 +763,9 @@ static void Console(CYOptions &options) { std::string command(line); free(line); - _assert(!command.empty()); - _assert(command[command.size() - 1] == '\n'); - command.resize(command.size() - 1); if (command.empty()) continue; + history += command; if (command[0] == '?') { std::string data(command.substr(1)); @@ -673,23 +783,22 @@ static void Console(CYOptions &options) { *out_ << "done." << std::endl; } else if (data == "exit") { return; - } else if (data == "expand") { - expand = !expand; - *out_ << "expand == " << (expand ? "true" : "false") << std::endl; } else if (data == "lower") { lower = !lower; *out_ << "lower == " << (lower ? "true" : "false") << std::endl; + } else if (data == "reparse") { + reparse = !reparse; + *out_ << "reparse == " << (reparse ? "true" : "false") << std::endl; } - history += command; continue; } std::string code; if (bypass) code = command; - else { - std::istringstream stream(command); + else try { + std::stringbuf stream(command); CYPool pool; CYDriver driver(pool, stream); @@ -717,7 +826,6 @@ static void Console(CYOptions &options) { std::cerr << " | "; std::cerr << error->message_ << std::endl; - history += command; break; } @@ -732,23 +840,22 @@ static void Console(CYOptions &options) { Setup(out, driver, options, lower); out << *driver.script_; code = str.str(); + } catch (const CYException &error) { + CYPool pool; + std::cout << error.PoolCString(pool) << std::endl; + continue; } - history += command; - if (debug) { std::cout << "cy= "; CYLexerHighlight(code.c_str(), code.size(), std::cout); std::cout << std::endl; } - CYPool pool; - Output(Run(pool, client_, code), &std::cout, expand); + CYOutputRun(code, reparse); } } -void InjectLibrary(pid_t, int, const char *const []); - static uint64_t CYGetTime() { #ifdef __APPLE__ return mach_absolute_time(); @@ -767,13 +874,13 @@ int Main(int argc, char * const argv[], char const * const envp[]) { append_history$ = (int (*)(int, const char *)) (dlsym(RTLD_DEFAULT, "append_history")); -#ifdef CY_ATTACH pid_t pid(_not(pid_t)); -#endif const char *host(NULL); const char *port(NULL); + const char *argv0(argv[0]); + optind = 1; for (;;) { @@ -781,18 +888,14 @@ int Main(int argc, char * const argv[], char const * const envp[]) { "c" "g:" "n:" -#ifdef CY_ATTACH "p:" -#endif "r:" "s" , (const struct option[]) { {NULL, no_argument, NULL, 'c'}, {NULL, required_argument, NULL, 'g'}, {NULL, required_argument, NULL, 'n'}, -#ifdef CY_ATTACH {NULL, required_argument, NULL, 'p'}, -#endif {NULL, required_argument, NULL, 'r'}, {NULL, no_argument, NULL, 's'}, {0, 0, 0, 0}}, NULL)); @@ -805,9 +908,7 @@ int Main(int argc, char * const argv[], char const * const envp[]) { case '?': fprintf(stderr, "usage: cycript [-c]" -#ifdef CY_ATTACH " [-p ]" -#endif " [-r ]" " [