Restore applypatch/updater/edify for non-A/B OTA support
Google deleted this source from bootable/recovery outright (commit c7ebad5f, "rm -rf non-AB code", 2024-04-05) — confirmed present at android-14, absent at android-16.0.0_r4. The runtime side (install.cpp's SetUpNonAbUpdateCommands) and build-side packaging (ota_from_target_files.py's GenerateNonAbOtaPackage) both survived; only the source that builds the update-binary executable itself was removed. Sourced from TeamWin's android_bootable_recovery (android-14.1), the only living copy found, with install/ZipUtil.cpp, get_args.cpp, and set_metadata.cpp intentionally left out (only wired into TeamWin's modified install.cpp, which PawletOS isn't adopting — twrpinstall carries its own independent copies). Kept as its own repo, not folded into a recovery fork, so it's buildable independent of whichever recovery UI ends up in use.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2017 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package {
|
||||
default_applicable_licenses: ["external_update_binary_edify_license"],
|
||||
}
|
||||
|
||||
// external_update_binary is a standalone repo (not nested inside
|
||||
// bootable/recovery), so this declares its own license module instead of
|
||||
// depending on bootable/recovery's internal "bootable_recovery_license" name.
|
||||
license {
|
||||
name: "external_update_binary_edify_license",
|
||||
visibility: [":__subpackages__"],
|
||||
license_kinds: [
|
||||
"SPDX-license-identifier-Apache-2.0",
|
||||
],
|
||||
license_text: [
|
||||
"//external/update_binary:NOTICE",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libedify",
|
||||
|
||||
host_supported: true,
|
||||
vendor_available: true,
|
||||
recovery_available: true,
|
||||
|
||||
srcs: [
|
||||
"expr.cpp",
|
||||
"lexer.ll",
|
||||
"parser.yy",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-Wno-deprecated-register",
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
|
||||
export_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
|
||||
local_include_dirs: [
|
||||
"include",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"libotautil",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
# Copyright 2009 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
edify_src_files := \
|
||||
lexer.ll \
|
||||
parser.yy \
|
||||
expr.cpp
|
||||
|
||||
#
|
||||
# Build the host-side command line tool (host executable)
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(edify_src_files) \
|
||||
edify_parser.cpp
|
||||
|
||||
LOCAL_CFLAGS := -Werror
|
||||
LOCAL_CPPFLAGS := -g -O0
|
||||
LOCAL_MODULE := edify_parser
|
||||
LOCAL_YACCFLAGS := -v
|
||||
LOCAL_CPPFLAGS += -Wno-unused-parameter
|
||||
LOCAL_CPPFLAGS += -Wno-deprecated-register
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
|
||||
LOCAL_STATIC_LIBRARIES += libbase
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
#
|
||||
# Build the device-side library (static library)
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(edify_src_files)
|
||||
|
||||
LOCAL_CFLAGS := -Werror
|
||||
LOCAL_CPPFLAGS := -Wno-unused-parameter
|
||||
LOCAL_CPPFLAGS += -Wno-deprecated-register
|
||||
LOCAL_MODULE := libedify
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
|
||||
LOCAL_STATIC_LIBRARIES += libbase
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
edify
|
||||
=====
|
||||
|
||||
Update scripts (from donut onwards) are written in a new little
|
||||
scripting language ("edify") that is superficially somewhat similar to
|
||||
the old one ("amend"). This is a brief overview of the new language.
|
||||
|
||||
- The entire script is a single expression.
|
||||
|
||||
- All expressions are string-valued.
|
||||
|
||||
- String literals appear in double quotes. \n, \t, \", and \\ are
|
||||
understood, as are hexadecimal escapes like \x4a.
|
||||
|
||||
- String literals consisting of only letters, numbers, colons,
|
||||
underscores, slashes, and periods don't need to be in double quotes.
|
||||
|
||||
- The following words are reserved:
|
||||
|
||||
if then else endif
|
||||
|
||||
They have special meaning when unquoted. (In quotes, they are just
|
||||
string literals.)
|
||||
|
||||
- When used as a boolean, the empty string is "false" and all other
|
||||
strings are "true".
|
||||
|
||||
- All functions are actually macros (in the Lisp sense); the body of
|
||||
the function can control which (if any) of the arguments are
|
||||
evaluated. This means that functions can act as control
|
||||
structures.
|
||||
|
||||
- Operators (like "&&" and "||") are just syntactic sugar for builtin
|
||||
functions, so they can act as control structures as well.
|
||||
|
||||
- ";" is a binary operator; evaluating it just means to first evaluate
|
||||
the left side, then the right. It can also appear after any
|
||||
expression.
|
||||
|
||||
- Comments start with "#" and run to the end of the line.
|
||||
|
||||
|
||||
|
||||
Some examples:
|
||||
|
||||
- There's no distinction between quoted and unquoted strings; the
|
||||
quotes are only needed if you want characters like whitespace to
|
||||
appear in the string. The following expressions all evaluate to the
|
||||
same string.
|
||||
|
||||
"a b"
|
||||
a + " " + b
|
||||
"a" + " " + "b"
|
||||
"a\x20b"
|
||||
a + "\x20b"
|
||||
concat(a, " ", "b")
|
||||
"concat"(a, " ", "b")
|
||||
|
||||
As shown in the last example, function names are just strings,
|
||||
too. They must be string *literals*, however. This is not legal:
|
||||
|
||||
("con" + "cat")(a, " ", b) # syntax error!
|
||||
|
||||
|
||||
- The ifelse() builtin takes three arguments: it evaluates exactly
|
||||
one of the second and third, depending on whether the first one is
|
||||
true. There is also some syntactic sugar to make expressions that
|
||||
look like if/else statements:
|
||||
|
||||
# these are all equivalent
|
||||
ifelse(something(), "yes", "no")
|
||||
if something() then yes else no endif
|
||||
if something() then "yes" else "no" endif
|
||||
|
||||
The else part is optional.
|
||||
|
||||
if something() then "yes" endif # if something() is false,
|
||||
# evaluates to false
|
||||
|
||||
ifelse(condition(), "", abort()) # abort() only called if
|
||||
# condition() is false
|
||||
|
||||
The last example is equivalent to:
|
||||
|
||||
assert(condition())
|
||||
|
||||
|
||||
- The && and || operators can be used similarly; they evaluate their
|
||||
second argument only if it's needed to determine the truth of the
|
||||
expression. Their value is the value of the last-evaluated
|
||||
argument:
|
||||
|
||||
file_exists("/data/system/bad") && delete("/data/system/bad")
|
||||
|
||||
file_exists("/data/system/missing") || create("/data/system/missing")
|
||||
|
||||
get_it() || "xxx" # returns value of get_it() if that value is
|
||||
# true, otherwise returns "xxx"
|
||||
|
||||
|
||||
- The purpose of ";" is to simulate imperative statements, of course,
|
||||
but the operator can be used anywhere. Its value is the value of
|
||||
its right side:
|
||||
|
||||
concat(a;b;c, d, e;f) # evaluates to "cdf"
|
||||
|
||||
A more useful example might be something like:
|
||||
|
||||
ifelse(condition(),
|
||||
(first_step(); second_step();), # second ; is optional
|
||||
alternative_procedure())
|
||||
+425
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "edify/expr.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "otautil/error_code.h"
|
||||
|
||||
// Functions should:
|
||||
//
|
||||
// - return a malloc()'d string
|
||||
// - if Evaluate() on any argument returns nullptr, return nullptr.
|
||||
|
||||
static bool BooleanString(const std::string& s) {
|
||||
return !s.empty();
|
||||
}
|
||||
|
||||
bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result) {
|
||||
if (result == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Value> v(expr->fn(expr->name.c_str(), state, expr->argv));
|
||||
if (!v) {
|
||||
return false;
|
||||
}
|
||||
if (v->type != Value::Type::STRING) {
|
||||
ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type);
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = v->data;
|
||||
return true;
|
||||
}
|
||||
|
||||
Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr) {
|
||||
return expr->fn(expr->name.c_str(), state, expr->argv);
|
||||
}
|
||||
|
||||
Value* StringValue(const char* str) {
|
||||
if (str == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new Value(Value::Type::STRING, str);
|
||||
}
|
||||
|
||||
Value* StringValue(const std::string& str) {
|
||||
return StringValue(str.c_str());
|
||||
}
|
||||
|
||||
Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
if (argv.empty()) {
|
||||
return StringValue("");
|
||||
}
|
||||
std::string result;
|
||||
for (size_t i = 0; i < argv.size(); ++i) {
|
||||
std::string str;
|
||||
if (!Evaluate(state, argv[i], &str)) {
|
||||
return nullptr;
|
||||
}
|
||||
result += str;
|
||||
}
|
||||
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
if (argv.size() != 2 && argv.size() != 3) {
|
||||
state->errmsg = "ifelse expects 2 or 3 arguments";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string cond;
|
||||
if (!Evaluate(state, argv[0], &cond)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!cond.empty()) {
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else if (argv.size() == 3) {
|
||||
return EvaluateValue(state, argv[2]);
|
||||
}
|
||||
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string msg;
|
||||
if (!argv.empty() && Evaluate(state, argv[0], &msg)) {
|
||||
state->errmsg += msg;
|
||||
} else {
|
||||
state->errmsg += "called abort()";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
for (size_t i = 0; i < argv.size(); ++i) {
|
||||
std::string result;
|
||||
if (!Evaluate(state, argv[i], &result)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (result.empty()) {
|
||||
int len = argv[i]->end - argv[i]->start;
|
||||
state->errmsg = "assert failed: " + state->script.substr(argv[i]->start, len);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
Value* SleepFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string val;
|
||||
if (!Evaluate(state, argv[0], &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int v;
|
||||
if (!android::base::ParseInt(val.c_str(), &v, 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
sleep(v);
|
||||
|
||||
return StringValue(val);
|
||||
}
|
||||
|
||||
Value* StdoutFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
for (size_t i = 0; i < argv.size(); ++i) {
|
||||
std::string v;
|
||||
if (!Evaluate(state, argv[i], &v)) {
|
||||
return nullptr;
|
||||
}
|
||||
fputs(v.c_str(), stdout);
|
||||
}
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
Value* LogicalAndFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string left;
|
||||
if (!Evaluate(state, argv[0], &left)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (BooleanString(left)) {
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
return StringValue("");
|
||||
}
|
||||
}
|
||||
|
||||
Value* LogicalOrFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string left;
|
||||
if (!Evaluate(state, argv[0], &left)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!BooleanString(left)) {
|
||||
return EvaluateValue(state, argv[1]);
|
||||
} else {
|
||||
return StringValue(left);
|
||||
}
|
||||
}
|
||||
|
||||
Value* LogicalNotFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string val;
|
||||
if (!Evaluate(state, argv[0], &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return StringValue(BooleanString(val) ? "" : "t");
|
||||
}
|
||||
|
||||
Value* SubstringFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string needle;
|
||||
if (!Evaluate(state, argv[0], &needle)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string haystack;
|
||||
if (!Evaluate(state, argv[1], &haystack)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string result = (haystack.find(needle) != std::string::npos) ? "t" : "";
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string left;
|
||||
if (!Evaluate(state, argv[0], &left)) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string right;
|
||||
if (!Evaluate(state, argv[1], &right)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* result = (left == right) ? "t" : "";
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* InequalityFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::string left;
|
||||
if (!Evaluate(state, argv[0], &left)) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string right;
|
||||
if (!Evaluate(state, argv[1], &right)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* result = (left != right) ? "t" : "";
|
||||
return StringValue(result);
|
||||
}
|
||||
|
||||
Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
std::unique_ptr<Value> left(EvaluateValue(state, argv[0]));
|
||||
if (!left) {
|
||||
return nullptr;
|
||||
}
|
||||
return EvaluateValue(state, argv[1]);
|
||||
}
|
||||
|
||||
Value* LessThanIntFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
if (argv.size() != 2) {
|
||||
state->errmsg = "less_than_int expects 2 arguments";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> args;
|
||||
if (!ReadArgs(state, argv, &args)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Parse up to at least long long or 64-bit integers.
|
||||
int64_t l_int;
|
||||
if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
|
||||
state->errmsg = "failed to parse int in " + args[0];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int64_t r_int;
|
||||
if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
|
||||
state->errmsg = "failed to parse int in " + args[1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return StringValue(l_int < r_int ? "t" : "");
|
||||
}
|
||||
|
||||
Value* GreaterThanIntFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
if (argv.size() != 2) {
|
||||
state->errmsg = "greater_than_int expects 2 arguments";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> args;
|
||||
if (!ReadArgs(state, argv, &args)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Parse up to at least long long or 64-bit integers.
|
||||
int64_t l_int;
|
||||
if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
|
||||
state->errmsg = "failed to parse int in " + args[0];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int64_t r_int;
|
||||
if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
|
||||
state->errmsg = "failed to parse int in " + args[1];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return StringValue(l_int > r_int ? "t" : "");
|
||||
}
|
||||
|
||||
Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
return StringValue(name);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// the function table
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
static std::unordered_map<std::string, Function> fn_table;
|
||||
|
||||
void RegisterFunction(const std::string& name, Function fn) {
|
||||
fn_table[name] = fn;
|
||||
}
|
||||
|
||||
Function FindFunction(const std::string& name) {
|
||||
if (fn_table.find(name) == fn_table.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return fn_table[name];
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterBuiltins() {
|
||||
RegisterFunction("ifelse", IfElseFn);
|
||||
RegisterFunction("abort", AbortFn);
|
||||
RegisterFunction("assert", AssertFn);
|
||||
RegisterFunction("concat", ConcatFn);
|
||||
RegisterFunction("is_substring", SubstringFn);
|
||||
RegisterFunction("stdout", StdoutFn);
|
||||
RegisterFunction("sleep", SleepFn);
|
||||
|
||||
RegisterFunction("less_than_int", LessThanIntFn);
|
||||
RegisterFunction("greater_than_int", GreaterThanIntFn);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// convenience methods for functions
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// Evaluate the expressions in argv, and put the results of strings in args. If any expression
|
||||
// evaluates to nullptr, return false. Return true on success.
|
||||
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::string>* args) {
|
||||
return ReadArgs(state, argv, args, 0, argv.size());
|
||||
}
|
||||
|
||||
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::string>* args, size_t start, size_t len) {
|
||||
if (args == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (start + len > argv.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = start; i < start + len; ++i) {
|
||||
std::string var;
|
||||
if (!Evaluate(state, argv[i], &var)) {
|
||||
args->clear();
|
||||
return false;
|
||||
}
|
||||
args->push_back(var);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Evaluate the expressions in argv, and put the results of Value* in args. If any expression
|
||||
// evaluate to nullptr, return false. Return true on success.
|
||||
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::unique_ptr<Value>>* args) {
|
||||
return ReadValueArgs(state, argv, args, 0, argv.size());
|
||||
}
|
||||
|
||||
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len) {
|
||||
if (args == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (len == 0 || start + len > argv.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = start; i < start + len; ++i) {
|
||||
std::unique_ptr<Value> v(EvaluateValue(state, argv[i]));
|
||||
if (!v) {
|
||||
args->clear();
|
||||
return false;
|
||||
}
|
||||
args->push_back(std::move(v));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use printf-style arguments to compose an error message to put into
|
||||
// *state. Returns nullptr.
|
||||
Value* ErrorAbort(State* state, const char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
android::base::StringAppendV(&state->errmsg, format, ap);
|
||||
va_end(ap);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) {
|
||||
std::string err_message;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
android::base::StringAppendV(&err_message, format, ap);
|
||||
va_end(ap);
|
||||
// Ensure that there's exactly one line break at the end of the error message.
|
||||
state->errmsg = android::base::Trim(err_message) + "\n";
|
||||
state->cause_code = cause_code;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
State::State(const std::string& script, UpdaterInterface* interface)
|
||||
: script(script), updater(interface), error_code(kNoError), cause_code(kNoCause) {}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _EXPRESSION_H
|
||||
#define _EXPRESSION_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "edify/updater_interface.h"
|
||||
|
||||
// Forward declaration to avoid including "otautil/error_code.h".
|
||||
enum ErrorCode : int;
|
||||
enum CauseCode : int;
|
||||
|
||||
struct State {
|
||||
State(const std::string& script, UpdaterInterface* cookie);
|
||||
|
||||
// The source of the original script.
|
||||
const std::string& script;
|
||||
|
||||
// A pointer to app-specific data; the libedify doesn't use this value.
|
||||
UpdaterInterface* updater;
|
||||
|
||||
// The error message (if any) returned if the evaluation aborts.
|
||||
// Should be empty initially, will be either empty or a string that
|
||||
// Evaluate() returns.
|
||||
std::string errmsg;
|
||||
|
||||
// error code indicates the type of failure (e.g. failure to update system image)
|
||||
// during the OTA process.
|
||||
ErrorCode error_code;
|
||||
|
||||
// cause code provides more detailed reason of an OTA failure (e.g. fsync error)
|
||||
// in addition to the error code.
|
||||
CauseCode cause_code;
|
||||
|
||||
bool is_retry = false;
|
||||
};
|
||||
|
||||
struct Value {
|
||||
enum class Type {
|
||||
STRING = 1,
|
||||
BLOB = 2,
|
||||
};
|
||||
|
||||
Value(Type type, std::string str) : type(type), data(std::move(str)) {}
|
||||
|
||||
Type type;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
struct Expr;
|
||||
|
||||
using Function = Value* (*)(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
|
||||
struct Expr {
|
||||
Function fn;
|
||||
std::string name;
|
||||
std::vector<std::unique_ptr<Expr>> argv;
|
||||
int start, end;
|
||||
|
||||
Expr(Function fn, const std::string& name, int start, int end) :
|
||||
fn(fn),
|
||||
name(name),
|
||||
start(start),
|
||||
end(end) {}
|
||||
};
|
||||
|
||||
// Evaluate the input expr, return the resulting Value.
|
||||
Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr);
|
||||
|
||||
// Evaluate the input expr, assert that it is a string, and update the result parameter. This
|
||||
// function returns true if the evaluation succeeds. This is a convenience function for older
|
||||
// functions that want to deal only with strings.
|
||||
bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result);
|
||||
|
||||
// Glue to make an Expr out of a literal.
|
||||
Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
|
||||
// Functions corresponding to various syntactic sugar operators.
|
||||
// ("concat" is also available as a builtin function, to concatenate
|
||||
// more than two strings.)
|
||||
Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* LogicalAndFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* LogicalOrFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* LogicalNotFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* SubstringFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* InequalityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
|
||||
// Global builtins, registered by RegisterBuiltins().
|
||||
Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
|
||||
|
||||
// Register a new function. The same Function may be registered under
|
||||
// multiple names, but a given name should only be used once.
|
||||
void RegisterFunction(const std::string& name, Function fn);
|
||||
|
||||
// Register all the builtins.
|
||||
void RegisterBuiltins();
|
||||
|
||||
// Find the Function for a given name; return NULL if no such function
|
||||
// exists.
|
||||
Function FindFunction(const std::string& name);
|
||||
|
||||
// --- convenience functions for use in functions ---
|
||||
|
||||
// Evaluate the expressions in argv, and put the results of strings in args. If any expression
|
||||
// evaluates to nullptr, return false. Return true on success.
|
||||
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::string>* args);
|
||||
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::string>* args, size_t start, size_t len);
|
||||
|
||||
// Evaluate the expressions in argv, and put the results of Value* in args. If any
|
||||
// expression evaluate to nullptr, return false. Return true on success.
|
||||
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::unique_ptr<Value>>* args);
|
||||
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len);
|
||||
|
||||
// Use printf-style arguments to compose an error message to put into
|
||||
// *state. Returns NULL.
|
||||
Value* ErrorAbort(State* state, const char* format, ...)
|
||||
__attribute__((format(printf, 2, 3), deprecated));
|
||||
|
||||
// ErrorAbort has an optional (but recommended) argument 'cause_code'. If the cause code
|
||||
// is set, it will be logged into last_install and provides reason of OTA failures.
|
||||
Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
// Copying the string into a Value.
|
||||
Value* StringValue(const char* str);
|
||||
|
||||
Value* StringValue(const std::string& str);
|
||||
|
||||
int ParseString(const std::string& str, std::unique_ptr<Expr>* root, int* error_count);
|
||||
|
||||
#endif // _EXPRESSION_H
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
struct ZipArchive;
|
||||
typedef ZipArchive* ZipArchiveHandle;
|
||||
|
||||
class UpdaterRuntimeInterface;
|
||||
|
||||
class UpdaterInterface {
|
||||
public:
|
||||
virtual ~UpdaterInterface() = default;
|
||||
|
||||
// Writes the message to command pipe, adds a new line in the end.
|
||||
virtual void WriteToCommandPipe(const std::string_view message, bool flush = false) const = 0;
|
||||
|
||||
// Sends over the message to recovery to print it on the screen.
|
||||
virtual void UiPrint(const std::string_view message) const = 0;
|
||||
|
||||
// Given the name of the block device, returns |name| for updates on the device; or the file path
|
||||
// to the fake block device for simulations.
|
||||
virtual std::string FindBlockDeviceName(const std::string_view name) const = 0;
|
||||
|
||||
virtual UpdaterRuntimeInterface* GetRuntime() const = 0;
|
||||
virtual ZipArchiveHandle GetPackageHandle() const = 0;
|
||||
virtual std::string GetResult() const = 0;
|
||||
virtual uint8_t* GetMappedPackageAddress() const = 0;
|
||||
virtual size_t GetMappedPackageLength() const = 0;
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
// This class serves as the base to updater runtime. It wraps the runtime dependent functions; and
|
||||
// updates on device and host simulations can have different implementations. e.g. block devices
|
||||
// during host simulation merely a temporary file. With this class, the caller side in registered
|
||||
// updater's functions will stay the same for both update and simulation.
|
||||
class UpdaterRuntimeInterface {
|
||||
public:
|
||||
virtual ~UpdaterRuntimeInterface() = default;
|
||||
|
||||
// Returns true if it's a runtime instance for simulation.
|
||||
virtual bool IsSimulator() const = 0;
|
||||
|
||||
// Returns the value of system property |key|. If the property doesn't exist, returns
|
||||
// |default_value|.
|
||||
virtual std::string GetProperty(const std::string_view key,
|
||||
const std::string_view default_value) const = 0;
|
||||
|
||||
// Given the name of the block device, returns |name| for updates on the device; or the file path
|
||||
// to the fake block device for simulations.
|
||||
virtual std::string FindBlockDeviceName(const std::string_view name) const = 0;
|
||||
|
||||
// Mounts the |location| on |mount_point|. Returns 0 on success.
|
||||
virtual int Mount(const std::string_view location, const std::string_view mount_point,
|
||||
const std::string_view fs_type, const std::string_view mount_options) = 0;
|
||||
|
||||
// Returns true if |mount_point| is mounted.
|
||||
virtual bool IsMounted(const std::string_view mount_point) const = 0;
|
||||
|
||||
// Unmounts the |mount_point|. Returns a pair of results with the first value indicating
|
||||
// if the |mount_point| is mounted, and the second value indicating the result of umount(2).
|
||||
virtual std::pair<bool, int> Unmount(const std::string_view mount_point) = 0;
|
||||
|
||||
// Reads |filename| and puts its value to |content|.
|
||||
virtual bool ReadFileToString(const std::string_view filename, std::string* content) const = 0;
|
||||
|
||||
// Updates the content of |filename| with |content|.
|
||||
virtual bool WriteStringToFile(const std::string_view content,
|
||||
const std::string_view filename) const = 0;
|
||||
|
||||
// Wipes the first |len| bytes of block device in |filename|.
|
||||
virtual int WipeBlockDevice(const std::string_view filename, size_t len) const = 0;
|
||||
|
||||
// Starts a child process and runs the program with |args|. Uses vfork(2) if |is_vfork| is true.
|
||||
virtual int RunProgram(const std::vector<std::string>& args, bool is_vfork) const = 0;
|
||||
|
||||
// Runs tune2fs with arguments |args|.
|
||||
virtual int Tune2Fs(const std::vector<std::string>& args) const = 0;
|
||||
|
||||
// Dynamic partition related functions.
|
||||
virtual bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) = 0;
|
||||
virtual bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) = 0;
|
||||
virtual bool UpdateDynamicPartitions(const std::string_view op_list_value) = 0;
|
||||
|
||||
// On devices supports A/B, add current slot suffix to arg. Otherwise, return |arg| as is.
|
||||
virtual std::string AddSlotSuffix(const std::string_view arg) const = 0;
|
||||
};
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
%{
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "edify/expr.h"
|
||||
#include "yydefs.h"
|
||||
#include "parser.h"
|
||||
|
||||
int gLine = 1;
|
||||
int gColumn = 1;
|
||||
int gPos = 0;
|
||||
|
||||
std::string string_buffer;
|
||||
|
||||
#define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \
|
||||
gColumn+=yyleng; gPos+=yyleng;} while(0)
|
||||
|
||||
%}
|
||||
|
||||
%x STR
|
||||
|
||||
%option noinput
|
||||
%option nounput
|
||||
%option noyywrap
|
||||
|
||||
%%
|
||||
|
||||
|
||||
\" {
|
||||
BEGIN(STR);
|
||||
string_buffer.clear();
|
||||
yylloc.start = gPos;
|
||||
++gColumn;
|
||||
++gPos;
|
||||
}
|
||||
|
||||
<STR>{
|
||||
\" {
|
||||
++gColumn;
|
||||
++gPos;
|
||||
BEGIN(INITIAL);
|
||||
yylval.str = strdup(string_buffer.c_str());
|
||||
yylloc.end = gPos;
|
||||
return STRING;
|
||||
}
|
||||
|
||||
\\n { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\n'); }
|
||||
\\t { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\t'); }
|
||||
\\\" { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\"'); }
|
||||
\\\\ { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\\'); }
|
||||
|
||||
\\x[0-9a-fA-F]{2} {
|
||||
gColumn += yyleng;
|
||||
gPos += yyleng;
|
||||
int val;
|
||||
sscanf(yytext+2, "%x", &val);
|
||||
string_buffer.push_back(static_cast<char>(val));
|
||||
}
|
||||
|
||||
\n {
|
||||
++gLine;
|
||||
++gPos;
|
||||
gColumn = 1;
|
||||
string_buffer.push_back(yytext[0]);
|
||||
}
|
||||
|
||||
. {
|
||||
++gColumn;
|
||||
++gPos;
|
||||
string_buffer.push_back(yytext[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if ADVANCE; return IF;
|
||||
then ADVANCE; return THEN;
|
||||
else ADVANCE; return ELSE;
|
||||
endif ADVANCE; return ENDIF;
|
||||
|
||||
[a-zA-Z0-9_:/.]+ {
|
||||
ADVANCE;
|
||||
yylval.str = strdup(yytext);
|
||||
return STRING;
|
||||
}
|
||||
|
||||
\&\& ADVANCE; return AND;
|
||||
\|\| ADVANCE; return OR;
|
||||
== ADVANCE; return EQ;
|
||||
!= ADVANCE; return NE;
|
||||
|
||||
[+(),!;] ADVANCE; return yytext[0];
|
||||
|
||||
[ \t]+ ADVANCE;
|
||||
|
||||
(#.*)?\n gPos += yyleng; ++gLine; gColumn = 1;
|
||||
|
||||
. return BAD;
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
%{
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
|
||||
#include "edify/expr.h"
|
||||
#include "yydefs.h"
|
||||
#include "parser.h"
|
||||
|
||||
extern int gLine;
|
||||
extern int gColumn;
|
||||
|
||||
void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s);
|
||||
int yyparse(std::unique_ptr<Expr>* root, int* error_count);
|
||||
|
||||
struct yy_buffer_state;
|
||||
void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
|
||||
struct yy_buffer_state* yy_scan_string(const char* yystr);
|
||||
|
||||
// Convenience function for building expressions with a fixed number
|
||||
// of arguments.
|
||||
static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
|
||||
va_list v;
|
||||
va_start(v, count);
|
||||
Expr* e = new Expr(fn, "(operator)", loc.start, loc.end);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
e->argv.emplace_back(va_arg(v, Expr*));
|
||||
}
|
||||
va_end(v);
|
||||
return e;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%locations
|
||||
|
||||
%union {
|
||||
char* str;
|
||||
Expr* expr;
|
||||
std::vector<std::unique_ptr<Expr>>* args;
|
||||
}
|
||||
|
||||
%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
|
||||
%token <str> STRING BAD
|
||||
%type <expr> expr
|
||||
%type <args> arglist
|
||||
|
||||
%destructor { delete $$; } expr
|
||||
%destructor { delete $$; } arglist
|
||||
|
||||
%parse-param {std::unique_ptr<Expr>* root}
|
||||
%parse-param {int* error_count}
|
||||
%define parse.error verbose
|
||||
|
||||
/* declarations in increasing order of precedence */
|
||||
%left ';'
|
||||
%left ','
|
||||
%left OR
|
||||
%left AND
|
||||
%left EQ NE
|
||||
%left '+'
|
||||
%right '!'
|
||||
|
||||
%%
|
||||
|
||||
input: expr { root->reset($1); }
|
||||
;
|
||||
|
||||
expr: STRING {
|
||||
$$ = new Expr(Literal, $1, @$.start, @$.end);
|
||||
}
|
||||
| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
|
||||
| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
|
||||
| expr ';' expr { $$ = Build(SequenceFn, @$, 2, $1, $3); }
|
||||
| error ';' expr { $$ = $3; $$->start=@$.start; $$->end=@$.end; }
|
||||
| expr '+' expr { $$ = Build(ConcatFn, @$, 2, $1, $3); }
|
||||
| expr EQ expr { $$ = Build(EqualityFn, @$, 2, $1, $3); }
|
||||
| expr NE expr { $$ = Build(InequalityFn, @$, 2, $1, $3); }
|
||||
| expr AND expr { $$ = Build(LogicalAndFn, @$, 2, $1, $3); }
|
||||
| expr OR expr { $$ = Build(LogicalOrFn, @$, 2, $1, $3); }
|
||||
| '!' expr { $$ = Build(LogicalNotFn, @$, 1, $2); }
|
||||
| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); }
|
||||
| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
|
||||
| STRING '(' arglist ')' {
|
||||
Function fn = FindFunction($1);
|
||||
if (fn == nullptr) {
|
||||
std::string msg = "unknown function \"" + std::string($1) + "\"";
|
||||
yyerror(root, error_count, msg.c_str());
|
||||
YYERROR;
|
||||
}
|
||||
$$ = new Expr(fn, $1, @$.start, @$.end);
|
||||
$$->argv = std::move(*$3);
|
||||
}
|
||||
;
|
||||
|
||||
arglist: /* empty */ {
|
||||
$$ = new std::vector<std::unique_ptr<Expr>>;
|
||||
}
|
||||
| expr {
|
||||
$$ = new std::vector<std::unique_ptr<Expr>>;
|
||||
$$->emplace_back($1);
|
||||
}
|
||||
| arglist ',' expr {
|
||||
UNUSED($1);
|
||||
$$->push_back(std::unique_ptr<Expr>($3));
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s) {
|
||||
if (strlen(s) == 0) {
|
||||
s = "syntax error";
|
||||
}
|
||||
printf("line %d col %d: %s\n", gLine, gColumn, s);
|
||||
++*error_count;
|
||||
}
|
||||
|
||||
int ParseString(const std::string& str, std::unique_ptr<Expr>* root, int* error_count) {
|
||||
yy_switch_to_buffer(yy_scan_string(str.c_str()));
|
||||
return yyparse(root, error_count);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _YYDEFS_H_
|
||||
#define _YYDEFS_H_
|
||||
|
||||
#define YYLTYPE YYLTYPE
|
||||
typedef struct {
|
||||
int start, end;
|
||||
} YYLTYPE;
|
||||
|
||||
#define YYLLOC_DEFAULT(Current, Rhs, N) \
|
||||
do { \
|
||||
if (N) { \
|
||||
(Current).start = YYRHSLOC(Rhs, 1).start; \
|
||||
(Current).end = YYRHSLOC(Rhs, N).end; \
|
||||
} else { \
|
||||
(Current).start = YYRHSLOC(Rhs, 0).start; \
|
||||
(Current).end = YYRHSLOC(Rhs, 0).end; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int yylex();
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user