edify extensions for OTA package installation, part 2
Adds more edify functions for OTAs:
is_mounted getprop apply_patch apply_patch_check apply_patch_space
write_raw_image write_firmware_image package_extract_file
This allows us to install radios, hboots, boot images, and install
incremental OTA packages.
Fixes a couple of dumb bugs in edify itself:
- we were doubling the size of the function table each time it was
*not* full, rather than each time it was full
- "no such function" errors weren't visible to the parser, so they
didn't prevent execution of the script.
This commit is contained in:
+1
-1
@@ -283,7 +283,7 @@ static int fn_size = 0;
|
|||||||
NamedFunction* fn_table = NULL;
|
NamedFunction* fn_table = NULL;
|
||||||
|
|
||||||
void RegisterFunction(const char* name, Function fn) {
|
void RegisterFunction(const char* name, Function fn) {
|
||||||
if (fn_entries <= fn_size) {
|
if (fn_entries >= fn_size) {
|
||||||
fn_size = fn_size*2 + 1;
|
fn_size = fn_size*2 + 1;
|
||||||
fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction));
|
fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction));
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -153,10 +153,11 @@ int main(int argc, char** argv) {
|
|||||||
buffer[size] = '\0';
|
buffer[size] = '\0';
|
||||||
|
|
||||||
Expr* root;
|
Expr* root;
|
||||||
|
int error_count = 0;
|
||||||
yy_scan_bytes(buffer, size);
|
yy_scan_bytes(buffer, size);
|
||||||
int error = yyparse(&root);
|
int error = yyparse(&root, &error_count);
|
||||||
printf("parse returned %d\n", error);
|
printf("parse returned %d; %d errors encountered\n", error, error_count);
|
||||||
if (error == 0) {
|
if (error == 0 || error_count > 0) {
|
||||||
char* result = Evaluate(NULL, root);
|
char* result = Evaluate(NULL, root);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
char* errmsg = GetError();
|
char* errmsg = GetError();
|
||||||
|
|||||||
+6
-4
@@ -25,8 +25,8 @@
|
|||||||
extern int gLine;
|
extern int gLine;
|
||||||
extern int gColumn;
|
extern int gColumn;
|
||||||
|
|
||||||
void yyerror(Expr** root, const char* s);
|
void yyerror(Expr** root, int* error_count, const char* s);
|
||||||
int yyparse(Expr** root);
|
int yyparse(Expr** root, int* error_count);
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
@@ -45,6 +45,7 @@ int yyparse(Expr** root);
|
|||||||
%type <args> arglist
|
%type <args> arglist
|
||||||
|
|
||||||
%parse-param {Expr** root}
|
%parse-param {Expr** root}
|
||||||
|
%parse-param {int* error_count}
|
||||||
%error-verbose
|
%error-verbose
|
||||||
|
|
||||||
/* declarations in increasing order of precedence */
|
/* declarations in increasing order of precedence */
|
||||||
@@ -86,7 +87,7 @@ expr: STRING {
|
|||||||
if ($$->fn == NULL) {
|
if ($$->fn == NULL) {
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
|
snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
|
||||||
yyerror(root, buffer);
|
yyerror(root, error_count, buffer);
|
||||||
YYERROR;
|
YYERROR;
|
||||||
}
|
}
|
||||||
$$->name = $1;
|
$$->name = $1;
|
||||||
@@ -113,9 +114,10 @@ arglist: /* empty */ {
|
|||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
void yyerror(Expr** root, const char* s) {
|
void yyerror(Expr** root, int* error_count, const char* s) {
|
||||||
if (strlen(s) == 0) {
|
if (strlen(s) == 0) {
|
||||||
s = "syntax error";
|
s = "syntax error";
|
||||||
}
|
}
|
||||||
printf("line %d col %d: %s\n", gLine, gColumn, s);
|
printf("line %d col %d: %s\n", gLine, gColumn, s);
|
||||||
|
++*error_count;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ handle_firmware_update(char* type, char* filename) {
|
|||||||
return INSTALL_ERROR;
|
return INSTALL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("type is [%s]\n", type);
|
LOGI("type is %s; size is %d; file is %s\n",
|
||||||
|
type, (int)st_data.st_size, filename);
|
||||||
|
|
||||||
char* data = malloc(st_data.st_size);
|
char* data = malloc(st_data.st_size);
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
|
|||||||
+2
-1
@@ -13,7 +13,8 @@ include $(CLEAR_VARS)
|
|||||||
|
|
||||||
LOCAL_SRC_FILES := $(updater_src_files)
|
LOCAL_SRC_FILES := $(updater_src_files)
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := libedify libmtdutils libminzip libz
|
LOCAL_STATIC_LIBRARIES := libapplypatch libedify libmtdutils libminzip libz
|
||||||
|
LOCAL_STATIC_LIBRARIES += libmincrypt libbz
|
||||||
LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
|
LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
|
||||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
|
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
|
||||||
|
|
||||||
|
|||||||
+272
-3
@@ -24,6 +24,8 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "cutils/misc.h"
|
||||||
|
#include "cutils/properties.h"
|
||||||
#include "edify/expr.h"
|
#include "edify/expr.h"
|
||||||
#include "minzip/DirUtil.h"
|
#include "minzip/DirUtil.h"
|
||||||
#include "mtdutils/mounts.h"
|
#include "mtdutils/mounts.h"
|
||||||
@@ -40,6 +42,7 @@ char* ErrorAbort(void* cookie, char* format, ...) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// mount(type, location, mount_point)
|
// mount(type, location, mount_point)
|
||||||
//
|
//
|
||||||
// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem
|
// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem
|
||||||
@@ -104,6 +107,36 @@ done:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// is_mounted(mount_point)
|
||||||
|
char* IsMountedFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
|
char* result = NULL;
|
||||||
|
if (argc != 1) {
|
||||||
|
return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc);
|
||||||
|
}
|
||||||
|
char* mount_point;
|
||||||
|
if (ReadArgs(cookie, argv, 1, &mount_point) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (strlen(mount_point) == 0) {
|
||||||
|
ErrorAbort(cookie, "mount_point argument to unmount() can't be empty");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
scan_mounted_volumes();
|
||||||
|
const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
|
||||||
|
if (vol == NULL) {
|
||||||
|
result = strdup("");
|
||||||
|
} else {
|
||||||
|
result = mount_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (result != mount_point) free(mount_point);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
char* UnmountFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
char* UnmountFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
char* result = NULL;
|
char* result = NULL;
|
||||||
if (argc != 1) {
|
if (argc != 1) {
|
||||||
@@ -132,6 +165,8 @@ done:
|
|||||||
if (result != mount_point) free(mount_point);
|
if (result != mount_point) free(mount_point);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// format(type, location)
|
// format(type, location)
|
||||||
//
|
//
|
||||||
// type="MTD" location=partition
|
// type="MTD" location=partition
|
||||||
@@ -192,6 +227,7 @@ done:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
char** paths = malloc(argc * sizeof(char*));
|
char** paths = malloc(argc * sizeof(char*));
|
||||||
int i;
|
int i;
|
||||||
@@ -222,6 +258,7 @@ char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
|||||||
return strdup(buffer);
|
return strdup(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
|
return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
|
||||||
@@ -243,8 +280,9 @@ char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
|||||||
return strdup("");
|
return strdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// package_extract package_path destination_path
|
// package_extract_dir(package_path, destination_path)
|
||||||
char* PackageExtractFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
char* PackageExtractDirFn(const char* name, void* cookie,
|
||||||
|
int argc, Expr* argv[]) {
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
|
return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
|
||||||
}
|
}
|
||||||
@@ -265,6 +303,42 @@ char* PackageExtractFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
|||||||
return strdup(success ? "t" : "");
|
return strdup(success ? "t" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// package_extract_file(package_path, destination_path)
|
||||||
|
char* PackageExtractFileFn(const char* name, void* cookie,
|
||||||
|
int argc, Expr* argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc);
|
||||||
|
}
|
||||||
|
char* zip_path;
|
||||||
|
char* dest_path;
|
||||||
|
if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL;
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip;
|
||||||
|
const ZipEntry* entry = mzFindZipEntry(za, zip_path);
|
||||||
|
if (entry == NULL) {
|
||||||
|
fprintf(stderr, "%s: no %s in package\n", name, zip_path);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* f = fopen(dest_path, "wb");
|
||||||
|
if (f == NULL) {
|
||||||
|
fprintf(stderr, "%s: can't open %s for write: %s\n",
|
||||||
|
name, dest_path, strerror(errno));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
success = mzExtractZipEntryToFile(za, entry, fileno(f));
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
done:
|
||||||
|
free(zip_path);
|
||||||
|
free(dest_path);
|
||||||
|
return strdup(success ? "t" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// symlink target src1 src2 ...
|
// symlink target src1 src2 ...
|
||||||
char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
@@ -289,6 +363,7 @@ char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
|||||||
return strdup("");
|
return strdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
char* result = NULL;
|
char* result = NULL;
|
||||||
bool recursive = (strcmp(name, "set_perm_recursive") == 0);
|
bool recursive = (strcmp(name, "set_perm_recursive") == 0);
|
||||||
@@ -356,15 +431,209 @@ done:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char* GetPropFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
|
if (argc != 1) {
|
||||||
|
return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc);
|
||||||
|
}
|
||||||
|
char* key;
|
||||||
|
key = Evaluate(cookie, argv[0]);
|
||||||
|
if (key == NULL) return NULL;
|
||||||
|
|
||||||
|
char value[PROPERTY_VALUE_MAX];
|
||||||
|
property_get(key, value, "");
|
||||||
|
free(key);
|
||||||
|
|
||||||
|
return strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool write_raw_image_cb(const unsigned char* data,
|
||||||
|
int data_len, void* ctx) {
|
||||||
|
int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
|
||||||
|
if (r == data_len) return true;
|
||||||
|
fprintf(stderr, "%s\n", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write_raw_image(file, partition)
|
||||||
|
char* WriteRawImageFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
|
char* result = NULL;
|
||||||
|
|
||||||
|
char* partition;
|
||||||
|
char* filename;
|
||||||
|
if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(partition) == 0) {
|
||||||
|
ErrorAbort(cookie, "partition argument to %s can't be empty", name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (strlen(filename) == 0) {
|
||||||
|
ErrorAbort(cookie, "file argument to %s can't be empty", name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtd_scan_partitions();
|
||||||
|
const MtdPartition* mtd = mtd_find_partition_by_name(partition);
|
||||||
|
if (mtd == NULL) {
|
||||||
|
fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
|
||||||
|
result = strdup("");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
MtdWriteContext* ctx = mtd_write_partition(mtd);
|
||||||
|
if (ctx == NULL) {
|
||||||
|
fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
|
||||||
|
name, partition);
|
||||||
|
result = strdup("");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
FILE* f = fopen(filename, "rb");
|
||||||
|
if (f == NULL) {
|
||||||
|
fprintf(stderr, "%s: can't open %s: %s\n",
|
||||||
|
name, filename, strerror(errno));
|
||||||
|
result = strdup("");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
char* buffer = malloc(BUFSIZ);
|
||||||
|
int read;
|
||||||
|
while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
|
||||||
|
int wrote = mtd_write_data(ctx, buffer, read);
|
||||||
|
success = success && (wrote == read);
|
||||||
|
if (!success) {
|
||||||
|
fprintf(stderr, "mtd_write_data to %s failed: %s\n",
|
||||||
|
partition, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
printf("%s %s partition from %s\n",
|
||||||
|
success ? "wrote" : "failed to write", partition, filename);
|
||||||
|
|
||||||
|
result = success ? partition : strdup("");
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (result != partition) free(partition);
|
||||||
|
free(filename);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write_firmware_image(file, partition)
|
||||||
|
//
|
||||||
|
// partition is "radio" or "hboot"
|
||||||
|
// file is not used until after updater exits
|
||||||
|
//
|
||||||
|
// TODO: this should live in some HTC-specific library
|
||||||
|
char* WriteFirmwareImageFn(const char* name, void* cookie,
|
||||||
|
int argc, Expr* argv[]) {
|
||||||
|
char* result = NULL;
|
||||||
|
|
||||||
|
char* partition;
|
||||||
|
char* filename;
|
||||||
|
if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(partition) == 0) {
|
||||||
|
ErrorAbort(cookie, "partition argument to %s can't be empty", name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (strlen(filename) == 0) {
|
||||||
|
ErrorAbort(cookie, "file argument to %s can't be empty", name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* cmd = ((UpdaterInfo*)cookie)->cmd_pipe;
|
||||||
|
fprintf(cmd, "firmware %s %s\n", partition, filename);
|
||||||
|
|
||||||
|
printf("will write %s firmware from %s\n", partition, filename);
|
||||||
|
result = partition;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (result != partition) free(partition);
|
||||||
|
free(filename);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern int applypatch(int argc, char** argv);
|
||||||
|
|
||||||
|
// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...)
|
||||||
|
// apply_patch_check(file, sha1, ...)
|
||||||
|
// apply_patch_space(bytes)
|
||||||
|
char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) {
|
||||||
|
printf("in applypatchfn (%s)\n", name);
|
||||||
|
|
||||||
|
char* prepend = NULL;
|
||||||
|
if (strstr(name, "check") != NULL) {
|
||||||
|
prepend = "-c";
|
||||||
|
} else if (strstr(name, "space") != NULL) {
|
||||||
|
prepend = "-s";
|
||||||
|
}
|
||||||
|
|
||||||
|
char** args = ReadVarArgs(cookie, argc, argv);
|
||||||
|
if (args == NULL) return NULL;
|
||||||
|
|
||||||
|
// insert the "program name" argv[0] and a copy of the "prepend"
|
||||||
|
// string (if any) at the start of the args.
|
||||||
|
|
||||||
|
int extra = 1 + (prepend != NULL ? 1 : 0);
|
||||||
|
char** temp = malloc((argc+extra) * sizeof(char*));
|
||||||
|
memcpy(temp+extra, args, argc * sizeof(char*));
|
||||||
|
temp[0] = strdup("updater");
|
||||||
|
if (prepend) {
|
||||||
|
temp[1] = strdup(prepend);
|
||||||
|
}
|
||||||
|
free(args);
|
||||||
|
args = temp;
|
||||||
|
argc += extra;
|
||||||
|
|
||||||
|
printf("calling applypatch\n");
|
||||||
|
fflush(stdout);
|
||||||
|
int result = applypatch(argc, args);
|
||||||
|
printf("applypatch returned %d\n", result);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < argc; ++i) {
|
||||||
|
free(args[i]);
|
||||||
|
}
|
||||||
|
free(args);
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case 0: return strdup("t");
|
||||||
|
case 1: return strdup("");
|
||||||
|
default: return ErrorAbort(cookie, "applypatch couldn't parse args");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void RegisterInstallFunctions() {
|
void RegisterInstallFunctions() {
|
||||||
RegisterFunction("mount", MountFn);
|
RegisterFunction("mount", MountFn);
|
||||||
|
RegisterFunction("is_mounted", IsMountedFn);
|
||||||
RegisterFunction("unmount", UnmountFn);
|
RegisterFunction("unmount", UnmountFn);
|
||||||
RegisterFunction("format", FormatFn);
|
RegisterFunction("format", FormatFn);
|
||||||
RegisterFunction("show_progress", ShowProgressFn);
|
RegisterFunction("show_progress", ShowProgressFn);
|
||||||
RegisterFunction("delete", DeleteFn);
|
RegisterFunction("delete", DeleteFn);
|
||||||
RegisterFunction("delete_recursive", DeleteFn);
|
RegisterFunction("delete_recursive", DeleteFn);
|
||||||
RegisterFunction("package_extract", PackageExtractFn);
|
RegisterFunction("package_extract_dir", PackageExtractDirFn);
|
||||||
|
RegisterFunction("package_extract_file", PackageExtractFileFn);
|
||||||
RegisterFunction("symlink", SymlinkFn);
|
RegisterFunction("symlink", SymlinkFn);
|
||||||
RegisterFunction("set_perm", SetPermFn);
|
RegisterFunction("set_perm", SetPermFn);
|
||||||
RegisterFunction("set_perm_recursive", SetPermFn);
|
RegisterFunction("set_perm_recursive", SetPermFn);
|
||||||
|
|
||||||
|
RegisterFunction("getprop", GetPropFn);
|
||||||
|
RegisterFunction("write_raw_image", WriteRawImageFn);
|
||||||
|
RegisterFunction("write_firmware_image", WriteFirmwareImageFn);
|
||||||
|
|
||||||
|
RegisterFunction("apply_patch", ApplyPatchFn);
|
||||||
|
RegisterFunction("apply_patch_check", ApplyPatchFn);
|
||||||
|
RegisterFunction("apply_patch_space", ApplyPatchFn);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -80,10 +80,11 @@ int main(int argc, char** argv) {
|
|||||||
// Parse the script.
|
// Parse the script.
|
||||||
|
|
||||||
Expr* root;
|
Expr* root;
|
||||||
|
int error_count = 0;
|
||||||
yy_scan_string(script);
|
yy_scan_string(script);
|
||||||
int error = yyparse(&root);
|
int error = yyparse(&root, &error_count);
|
||||||
if (error != 0) {
|
if (error != 0 || error_count > 0) {
|
||||||
fprintf(stderr, "%d parse errors\n", error);
|
fprintf(stderr, "%d parse errors\n", error_count);
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user