+
+ return CYUTF8String(json, size);
+ }
+};
+
+void InjectLibrary(pid_t, std::ostream &stream, int, const char *const []);
+
+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 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 (size == 0 || out == NULL)
+ return;
+
+ CYLexerHighlight(data, size, *out);
+ *out << std::endl;
+}
+
+int (*append_history$)(int, const char *);
+
+static std::string command_;
+
+static CYRemote *remote_;
+
+static CYUTF8String Run(CYPool &pool, const std::string &code) {
+ return remote_->Run(pool, code);
+}
+
+static char **Complete(const char *word, int start, int end) {
+ rl_attempted_completion_over = ~0;
+ std::string line(rl_line_buffer, start);
+ char **values(CYComplete(word, command_ + line, &Run));
+ mode_ = Parsing;
+ return values;
+}
+
+// need char *, not const char *
+static char name_[] = "cycript";
+static char break_[] = " \t\n\"\\'`@><=;|&{(" ")}" ".:[]";
+
+class History {
+ private:
+ std::string histfile_;
+ size_t histlines_;
+
+ public:
+ History(std::string histfile) :
+ histfile_(histfile),
+ histlines_(0)
+ {
+ read_history(histfile_.c_str());
+
+ for (HIST_ENTRY *history((history_set_pos(0), current_history())); history; history = next_history())
+ for (char *character(history->line); *character; ++character)
+ if (*character == '\x01') *character = '\n';
+ }
+
+ ~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';
+
+ if (append_history$ != NULL) {
+ int fd(_syscall(open(histfile_.c_str(), O_CREAT | O_WRONLY, 0600)));
+ _syscall(close(fd));
+ _assert((*append_history$)(histlines_, histfile_.c_str()) == 0);
+ } 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_;
+ }
+};
+
+template <typename Type_>
+static Type_ *CYmemrchr(Type_ *data, Type_ value, size_t size) {
+ while (size != 0)
+ if (data[--size] == value)
+ return data + size;
+ return NULL;
+}
+
+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) {
+ _lblcall(&rl_newline, count, key);
+ return 0;
+ }
+
+ insert:
+ char *before(CYmemrchr(rl_line_buffer, '\n', rl_point));
+ if (before == NULL)
+ before = rl_line_buffer;
+
+ int space(before + 1 - rl_line_buffer);
+ while (space != rl_point && rl_line_buffer[space] == ' ')
+ ++space;
+
+ int adjust(rl_line_buffer + space - 1 - before);
+ if (space == rl_point && adjust != 0)
+ _lblcall(&rl_rubout, adjust, '\b');
+
+ _lblcall(&rl_insert, count, '\n');
+ if (adjust != 0)
+ _lblcall(&rl_insert, adjust, ' ');
+
+ return 0;