ROM Manager is now powered by Edify,

Change-Id: I3857aa6591b743be146d87a4e97afdc9d9c765ed
diff --git a/Android.mk b/Android.mk
index e4a3ad0..ab3a6fd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -19,6 +19,7 @@
 	extendedcommands.c \
 	nandroid.c \
     reboot.c \
+    edifyscripting.c \
     setprop.c
 
 LOCAL_MODULE := recovery
@@ -55,7 +56,7 @@
 LOCAL_STATIC_LIBRARIES += libext4_utils libz
 LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt
 
-LOCAL_STATIC_LIBRARIES += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
+LOCAL_STATIC_LIBRARIES += libedify libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
 LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
 
 LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
diff --git a/edifyscripting.c b/edifyscripting.c
new file mode 100644
index 0000000..3b0d4c9
--- /dev/null
+++ b/edifyscripting.c
@@ -0,0 +1,267 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "bootloader.h"
+#include "common.h"
+#include "cutils/properties.h"
+#include "firmware.h"
+#include "install.h"
+#include "minui/minui.h"
+#include "minzip/DirUtil.h"
+#include "roots.h"
+#include "recovery_ui.h"
+
+#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
+#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
+
+#include "extendedcommands.h"
+#include "nandroid.h"
+#include "mounts.h"
+#include "flashutils/flashutils.h"
+#include "edify/expr.h"
+#include "mtdutils/mtdutils.h"
+#include "mmcutils/mmcutils.h"
+//#include "edify/parser.h"
+
+Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char** args = ReadVarArgs(state, argc, argv);
+    if (args == NULL) {
+        return NULL;
+    }
+
+    int size = 0;
+    int i;
+    for (i = 0; i < argc; ++i) {
+        size += strlen(args[i]);
+    }
+    char* buffer = malloc(size+1);
+    size = 0;
+    for (i = 0; i < argc; ++i) {
+        strcpy(buffer+size, args[i]);
+        size += strlen(args[i]);
+        free(args[i]);
+    }
+    free(args);
+    buffer[size] = '\0';
+
+    char* line = strtok(buffer, "\n");
+    while (line) {
+        ui_print("%s\n", line);
+        line = strtok(NULL, "\n");
+    }
+
+    return StringValue(buffer);
+}
+
+Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
+    if (argc < 1) {
+        return ErrorAbort(state, "%s() expects at least 1 arg", name);
+    }
+    char** args = ReadVarArgs(state, argc, argv);
+    if (args == NULL) {
+        return NULL;
+    }
+
+    char** args2 = malloc(sizeof(char*) * (argc+1));
+    memcpy(args2, args, sizeof(char*) * argc);
+    args2[argc] = NULL;
+
+    fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
+
+    pid_t child = fork();
+    if (child == 0) {
+        execv(args2[0], args2);
+        fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
+        _exit(1);
+    }
+    int status;
+    waitpid(child, &status, 0);
+    if (WIFEXITED(status)) {
+        if (WEXITSTATUS(status) != 0) {
+            fprintf(stderr, "run_program: child exited with status %d\n",
+                    WEXITSTATUS(status));
+        }
+    } else if (WIFSIGNALED(status)) {
+        fprintf(stderr, "run_program: child terminated by signal %d\n",
+                WTERMSIG(status));
+    }
+
+    int i;
+    for (i = 0; i < argc; ++i) {
+        free(args[i]);
+    }
+    free(args);
+    free(args2);
+
+    char buffer[20];
+    sprintf(buffer, "%d", status);
+
+    return StringValue(strdup(buffer));
+}
+
+Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char* result = NULL;
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
+    }
+    
+    char *path;
+    if (ReadArgs(state, argv, 1, &path) < 0) {
+        return NULL;
+    }
+    
+    ui_print("Formatting %s...\n", path);
+    if (0 != format_volume(path)) {
+        free(path);
+        return StringValue(strdup(""));
+    }
+
+done:
+    return StringValue(strdup(path));
+}
+
+Value* BackupFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char* result = NULL;
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc);
+    }
+    char* path;
+    if (ReadArgs(state, argv, 1, &path) < 0) {
+        return NULL;
+    }
+    
+    if (0 != nandroid_backup(path))
+        return StringValue(strdup(""));
+    
+    return StringValue(strdup(path));
+}
+
+Value* RestoreFn(const char* name, State* state, int argc, Expr* argv[]) {
+    if (argc < 1) {
+        return ErrorAbort(state, "%s() expects at least 1 arg", name);
+    }
+    char** args = ReadVarArgs(state, argc, argv);
+    if (args == NULL) {
+        return NULL;
+    }
+
+    char** args2 = malloc(sizeof(char*) * (argc+1));
+    memcpy(args2, args, sizeof(char*) * argc);
+    args2[argc] = NULL;
+    
+    char* path = strdup(args2[0]);
+    int restoreboot = 1;
+    int restoresystem = 1;
+    int restoredata = 1;
+    int restorecache = 1;
+    int restoresdext = 1;
+    int i;
+    for (i = 1; i < argc; i++)
+    {
+        if (args2[i] == NULL)
+            continue;
+        if (strcmp(args2[i], "noboot") == 0)
+            restoreboot = 0;
+        else if (strcmp(args2[i], "nosystem") == 0)
+            restoresystem = 0;
+        else if (strcmp(args2[i], "nodata") == 0)
+            restoredata = 0;
+        else if (strcmp(args2[i], "nocache") == 0)
+            restorecache = 0;
+        else if (strcmp(args2[i], "nosd-ext") == 0)
+            restoresdext = 0;
+    }
+    
+    for (i = 0; i < argc; ++i) {
+        free(args[i]);
+    }
+    free(args);
+    free(args2);
+
+    if (0 != nandroid_restore(path, restoreboot, restoresystem, restoredata, restorecache, restoresdext)) {
+        free(path);
+        return StringValue(strdup(""));
+    }
+    
+    return StringValue(path);
+}
+
+Value* InstallZipFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char* result = NULL;
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc);
+    }
+    char* path;
+    if (ReadArgs(state, argv, 1, &path) < 0) {
+        return NULL;
+    }
+    
+    if (0 != install_zip(path))
+        return StringValue(strdup(""));
+    
+    return StringValue(strdup(path));
+}
+
+void RegisterRecoveryHooks() {
+    RegisterFunction("format", FormatFn);
+    RegisterFunction("ui_print", UIPrintFn);
+    RegisterFunction("run_program", RunProgramFn);
+    RegisterFunction("backup_rom", BackupFn);
+    RegisterFunction("restore_rom", RestoreFn);
+    RegisterFunction("install_zip", InstallZipFn);
+}
+
+static int hasInitializedEdify = 0;
+int run_script_from_buffer(char* script_data, int script_len, char* filename)
+{
+    if (!hasInitializedEdify) {
+        RegisterBuiltins();
+        RegisterRecoveryHooks();
+        FinishRegistration();
+        hasInitializedEdify = 1;
+    }
+
+    Expr* root;
+    int error_count = 0;
+    yy_scan_bytes(script_data, script_len);
+    int error = yyparse(&root, &error_count);
+    printf("parse returned %d; %d errors encountered\n", error, error_count);
+    if (error == 0 || error_count > 0) {
+        //ExprDump(0, root, buffer);
+
+        State state;
+        state.cookie = NULL;
+        state.script = script_data;
+        state.errmsg = NULL;
+
+        char* result = Evaluate(&state, root);
+        if (result == NULL) {
+            printf("result was NULL, message is: %s\n",
+                   (state.errmsg == NULL ? "(NULL)" : state.errmsg));
+            free(state.errmsg);
+            return -1;
+        } else {
+            printf("result is [%s]\n", result);
+        }
+    }
+    return 0;
+}
diff --git a/extendedcommands.c b/extendedcommands.c
index 7eec4c0..ad83a0a 100644
--- a/extendedcommands.c
+++ b/extendedcommands.c
@@ -37,6 +37,7 @@
 #include "nandroid.h"
 #include "mounts.h"
 #include "flashutils/flashutils.h"
+#include "edify/expr.h"
 
 int signature_check_enabled = 1;
 int script_assert_enabled = 1;
@@ -474,7 +475,7 @@
 
         for (i = 0; i < DEVICE_COUNT; i++)
         {
-            options[DEVICE_COUNT + i] = devices[i][0];
+            options[MOUNTABLE_COUNT + i] = devices[i][0];
         }
 
         options[MOUNTABLE_COUNT + DEVICE_COUNT] = "mount USB storage";
@@ -522,33 +523,52 @@
     return 0 == stat(EXTENDEDCOMMAND_SCRIPT, &file_info);
 }
 
-int run_script_from_buffer(char* script_data, int script_len, char* filename)
-{
-    ui_print("not yet implemented.\n");
-    return -1;
+int edify_main(int argc, char** argv) {
+    load_volume_table();
+    process_volumes();
+    RegisterBuiltins();
+    RegisterRecoveryHooks();
+    FinishRegistration();
 
-    /*
-    const AmCommandList *commands = parseAmendScript(script_data, script_len);
-    if (commands == NULL) {
-        printf("Syntax error in update script\n");
+    if (argc != 2) {
+        printf("edify <filename>\n");
         return 1;
-    } else {
-        printf("Parsed %.*s\n", script_len, filename);
     }
 
-    int ret = execCommandList((ExecContext *)1, commands);
-    if (ret != 0) {
-        int num = ret;
-        char *line = NULL, *next = script_data;
-        while (next != NULL && ret-- > 0) {
-            line = next;
-            next = memchr(line, '\n', script_data + script_len - line);
-            if (next != NULL) *next++ = '\0';
+    FILE* f = fopen(argv[1], "r");
+    if (f == NULL) {
+        printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
+        return 1;
+    }
+    char buffer[8192];
+    int size = fread(buffer, 1, 8191, f);
+    fclose(f);
+    buffer[size] = '\0';
+
+    Expr* root;
+    int error_count = 0;
+    yy_scan_bytes(buffer, size);
+    int error = yyparse(&root, &error_count);
+    printf("parse returned %d; %d errors encountered\n", error, error_count);
+    if (error == 0 || error_count > 0) {
+
+        //ExprDump(0, root, buffer);
+
+        State state;
+        state.cookie = NULL;
+        state.script = buffer;
+        state.errmsg = NULL;
+
+        char* result = Evaluate(&state, root);
+        if (result == NULL) {
+            printf("result was NULL, message is: %s\n",
+                   (state.errmsg == NULL ? "(NULL)" : state.errmsg));
+            free(state.errmsg);
+        } else {
+            printf("result is [%s]\n", result);
         }
-        printf("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
-        return 1;
     }
-    */
+    return 0;
 }
 
 int run_script(char* filename)
@@ -928,7 +948,7 @@
         return 0;
     }
     
-    ui_print("%s may be rfs. Checking...\n");
+    ui_print("%s may be rfs. Checking...\n", path);
     char tmp[PATH_MAX];
     sprintf(tmp, "mount -t rfs %s %s", vol->device, path);
     int ret = __system(tmp);
@@ -958,8 +978,9 @@
     ui_set_show_text(1);
     ui_print("Filesystems need to be converted to ext4.\n");
     ui_print("A backup and restore will now take place.\n");
-    ui_print("If anything goes wrong, your back will be\n");
-    ui_print("named before-ext4-convert.\n");
+    ui_print("If anything goes wrong, your backup will be\n");
+    ui_print("named before-ext4-convert. Try restoring it\n");
+    ui_print("in case of error.\n");
 
     nandroid_backup("/sdcard/clockworkmod/backup/before-ext4-convert");
     nandroid_restore("/sdcard/clockworkmod/backup/before-ext4-convert", 1, 1, 1, 1, 1);
diff --git a/recovery.c b/recovery.c
index 5e25e68..4fa4587 100644
--- a/recovery.c
+++ b/recovery.c
@@ -766,6 +766,8 @@
 	{
 	    if (strstr(argv[0], "flash_image") != NULL)
 	        return flash_image_main(argc, argv);
+	    if (strstr(argv[0], "edify") != NULL)
+	        return edify_main(argc, argv);
 	    if (strstr(argv[0], "dump_image") != NULL)
 	        return dump_image_main(argc, argv);
 	    if (strstr(argv[0], "erase_image") != NULL)