]> git.saurik.com Git - cycript.git/blobdiff - Application.cpp
Changed console commands to start with ? and added ?expand to unescape strings.
[cycript.git] / Application.cpp
index ae638a6c5d833ff9c3c6092d2ec2194626225328..25f624efb3bf8426660fcacfae29897cfe72bf05 100644 (file)
@@ -1,3 +1,42 @@
+/* Cycript - Remove Execution Server and Disassembler
+ * Copyright (C) 2009  Jay Freeman (saurik)
+*/
+
+/* Modified BSD License {{{ */
+/*
+ *        Redistribution and use in source and binary
+ * forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the
+ *    above copyright notice, this list of conditions
+ *    and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the
+ *    above copyright notice, this list of conditions
+ *    and the following disclaimer in the documentation
+ *    and/or other materials provided with the
+ *    distribution.
+ * 3. The name of the author may not be used to endorse
+ *    or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/* }}} */
+
 #define _GNU_SOURCE
 
 #include <substrate.h>
 
 #include "Cycript.tab.hh"
 
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
 static jmp_buf ctrlc_;
 
-void sigint(int) {
+static void sigint(int) {
     longjmp(ctrlc_, 1);
 }
 
-void Run(const char *code, FILE *fout) {
-    JSStringRef script(JSStringCreateWithUTF8CString(code));
-
-    JSContextRef context(CYGetJSContext());
-
-    JSValueRef exception(NULL);
-    JSValueRef result(JSEvaluateScript(context, script, NULL, NULL, 0, &exception));
-    JSStringRelease(script);
-
-    if (exception != NULL)
-        result = exception;
-
-    if (!JSValueIsUndefined(context, result)) {
-        CYPool pool;
-        const char *json;
-
-        json = CYPoolJSONString(pool, context, result);
+void Run(int socket, const char *data, size_t size, FILE *fout, bool expand = false) {
+    CYPool pool;
+
+    const char *json;
+    if (socket == -1) {
+        json = CYExecute(pool, data);
+        if (json != NULL)
+            size = strlen(json);
+    } else {
+        CYSendAll(socket, &size, sizeof(size));
+        CYSendAll(socket, data, size);
+        CYRecvAll(socket, &size, sizeof(size));
+        if (size == _not(size_t))
+            json = NULL;
+        else {
+            char *temp(new(pool) char[size + 1]);
+            CYRecvAll(socket, temp, size);
+            temp[size] = '\0';
+            json = temp;
+        }
+    }
 
-        if (fout != NULL) {
+    if (json != NULL && fout != NULL) {
+        if (!expand || json[0] != '"' && json[0] != '\'')
             fputs(json, fout);
-            fputs("\n", fout);
-            fflush(fout);
-        }
+        else for (size_t i(0); i != size; ++i)
+            if (json[i] != '\\')
+                fputc(json[i], fout);
+            else switch(json[++i]) {
+                case '\0': goto done;
+                case '\\': fputc('\\', fout); break;
+                case '\'': fputc('\'', fout); break;
+                case '"': fputc('"', fout); break;
+                case 'b': fputc('\b', fout); break;
+                case 'f': fputc('\f', fout); break;
+                case 'n': fputc('\n', fout); break;
+                case 'r': fputc('\r', fout); break;
+                case 't': fputc('\t', fout); break;
+                case 'v': fputc('\v', fout); break;
+                default: fputc('\\', fout); --i; break;
+            }
+
+      done:
+        fputs("\n", fout);
+        fflush(fout);
     }
 }
 
-void Console() {
+void Run(int socket, std::string &code, FILE *fout, bool expand = false) {
+    Run(socket, code.c_str(), code.size(), fout, expand);
+}
+
+static void Console(int socket) {
     bool bypass(false);
     bool debug(false);
+    bool expand(false);
 
     FILE *fout(stdout);
 
@@ -88,7 +159,7 @@ void Console() {
 
         if (!extra) {
             extra = true;
-            if (line[0] == '\\') {
+            if (line[0] == '?') {
                 std::string data(line + 1);
                 if (data == "bypass") {
                     bypass = !bypass;
@@ -98,6 +169,10 @@ void Console() {
                     debug = !debug;
                     fprintf(fout, "debug == %s\n", debug ? "true" : "false");
                     fflush(fout);
+                } else if (data == "expand") {
+                    expand = !expand;
+                    fprintf(fout, "expand == %s\n", expand ? "true" : "false");
+                    fflush(fout);
                 }
                 add_history(line);
                 goto restart;
@@ -120,10 +195,24 @@ void Console() {
             driver.size_ = command.size();
 
             if (parser.parse() != 0 || !driver.errors_.empty()) {
-                for (CYDriver::Errors::const_iterator i(driver.errors_.begin()); i != driver.errors_.end(); ++i) {
-                    cy::position begin(i->location_.begin);
+                for (CYDriver::Errors::const_iterator error(driver.errors_.begin()); error != driver.errors_.end(); ++error) {
+                    cy::position begin(error->location_.begin);
                     if (begin.line != lines.size() || begin.column - 1 != lines.back().size()) {
-                        std::cerr << i->message_ << std::endl;
+                        cy::position end(error->location_.end);
+                        if (begin.line != lines.size()) {
+                            std::cerr << "  | ";
+                            std::cerr << lines[begin.line - 1] << std::endl;
+                        }
+                        std::cerr << "  | ";
+                        for (size_t i(0); i != begin.column - 1; ++i)
+                            std::cerr << '.';
+                        if (begin.line != end.line)
+                            std::cerr << '^';
+                        else for (size_t i(0), e(end.column - begin.column); i != e; ++i)
+                            std::cerr << '^';
+                        std::cerr << std::endl;
+                        std::cerr << "  | ";
+                        std::cerr << error->message_ << std::endl;
                         add_history(command.c_str());
                         goto restart;
                     }
@@ -139,9 +228,13 @@ void Console() {
             if (driver.source_ == NULL)
                 goto restart;
 
-            std::ostringstream str;
-            driver.source_->Show(str);
-            code = str.str();
+            if (socket != -1)
+                code = command;
+            else {
+                std::ostringstream str;
+                driver.source_->Show(str);
+                code = str.str();
+            }
         }
 
         add_history(command.c_str());
@@ -149,14 +242,14 @@ void Console() {
         if (debug)
             std::cout << code << std::endl;
 
-        Run(code.c_str(), fout);
+        Run(socket, code, fout, expand);
     }
 
     fputs("\n", fout);
     fflush(fout);
 }
 
-void *Map(const char *path, size_t *psize) {
+static void *Map(const char *path, size_t *psize) {
     int fd;
     _syscall(fd = open(path, O_RDONLY));
 
@@ -173,18 +266,61 @@ void *Map(const char *path, size_t *psize) {
     return base;
 }
 
-int main(int argc, const char *argv[]) {
+int main(int argc, char *argv[]) {
+    pid_t pid(_not(pid_t));
+
+    for (;;) switch (getopt(argc, argv, "p:")) {
+        case -1:
+            goto getopt;
+        case '?':
+            fprintf(stderr, "usage: cycript [-p <pid>] [<script> [<arg>...]]\n");
+            return 1;
+
+        case 'p': {
+            size_t size(strlen(optarg));
+            char *end;
+            pid = strtoul(optarg, &end, 0);
+            if (optarg + size != end) {
+                fprintf(stderr, "invalid pid for -p\n");
+                return 1;
+            }
+        } break;
+    } getopt:;
+
     const char *script;
 
-    if (argc == 1)
+    if (optind < argc - 1 && pid != _not(pid_t)) {
+        fprintf(stderr, "-p cannot set argv\n");
+        return 1;
+    }
+
+    if (optind == argc)
         script = NULL;
     else {
-        CYSetArgs(argc - 1, argv + 1);
-        script = argv[1];
+        // XXX: const_cast?! wtf gcc :(
+        CYSetArgs(argc - optind - 1, const_cast<const char **>(argv + optind + 1));
+        script = argv[optind];
+        if (strcmp(script, "-") == 0)
+            script = NULL;
     }
 
-    if (script == NULL || strcmp(script, "-") == 0)
-        Console();
+    int socket;
+
+    if (pid == _not(pid_t))
+        socket = -1;
+    else {
+        socket = _syscall(::socket(PF_UNIX, SOCK_STREAM, 0));
+
+        struct sockaddr_un address;
+        memset(&address, 0, sizeof(address));
+        address.sun_family = AF_UNIX;
+        sprintf(address.sun_path, "/tmp/.s.cy.%u", pid);
+
+        _syscall(connect(socket, reinterpret_cast<sockaddr *>(&address), SUN_LEN(&address)));
+    }
+
+    if (script == NULL)
+        Console(socket);
     else {
         CYDriver driver(script);
         cy::parser parser(driver);
@@ -195,7 +331,11 @@ int main(int argc, const char *argv[]) {
 
         if (size >= 2 && start[0] == '#' && start[1] == '!') {
             start += 2;
-            while (start != end && *start++ != '\n');
+
+            if (void *line = memchr(start, '\n', end - start))
+                start = reinterpret_cast<char *>(line);
+            else
+                start = end;
         }
 
         driver.data_ = start;
@@ -204,13 +344,15 @@ int main(int argc, const char *argv[]) {
         if (parser.parse() != 0 || !driver.errors_.empty()) {
             for (CYDriver::Errors::const_iterator i(driver.errors_.begin()); i != driver.errors_.end(); ++i)
                 std::cerr << i->location_.begin << ": " << i->message_ << std::endl;
-        } else if (driver.source_ != NULL) {
-            std::ostringstream str;
-            driver.source_->Show(str);
-            std::string code(str.str());
-            std::cout << code << std::endl;
-            Run(code.c_str(), stdout);
-        }
+        } else if (driver.source_ != NULL)
+            if (socket != -1)
+                Run(socket, start, end - start, stdout);
+            else {
+                std::ostringstream str;
+                driver.source_->Show(str);
+                std::string code(str.str());
+                Run(socket, code, stdout);
+            }
     }
 
     return 0;