Commit 23580ca2 authored by The Android Open Source Project's avatar The Android Open Source Project
Browse files

Initial Contribution

parents
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
commands_recovery_local_path := $(LOCAL_PATH)
ifneq ($(TARGET_SIMULATOR),true)
LOCAL_SRC_FILES := \
recovery.c \
bootloader.c \
commands.c \
firmware.c \
install.c \
roots.c \
ui.c \
verifier.c
LOCAL_SRC_FILES += test_roots.c
LOCAL_MODULE := recovery
LOCAL_FORCE_STATIC_EXECUTABLE := true
# This binary is in the recovery ramdisk, which is otherwise a copy of root.
# It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses
# a (redundant) copy of the binary in /system/bin for user builds.
# TODO: Build the ramdisk image in a more principled way.
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libcutils
LOCAL_STATIC_LIBRARIES += libstdc++ libc
# Specify a C-includable file containing the OTA public keys.
# This is built in config/Makefile.
# *** THIS IS A TOTAL HACK; EXECUTABLES MUST NOT CHANGE BETWEEN DIFFERENT
# PRODUCTS/BUILD TYPES. ***
# TODO: make recovery read the keys from an external file.
RECOVERY_INSTALL_OTA_KEYS_INC := \
$(call intermediates-dir-for,PACKAGING,ota_keys_inc)/keys.inc
# Let install.c say #include "keys.inc"
LOCAL_C_INCLUDES += $(dir $(RECOVERY_INSTALL_OTA_KEYS_INC))
include $(BUILD_EXECUTABLE)
# Depend on the generated keys.inc containing the OTA public keys.
$(intermediates)/install.o: $(RECOVERY_INSTALL_OTA_KEYS_INC)
include $(commands_recovery_local_path)/minui/Android.mk
endif # !TARGET_SIMULATOR
include $(commands_recovery_local_path)/amend/Android.mk
include $(commands_recovery_local_path)/minzip/Android.mk
include $(commands_recovery_local_path)/mtdutils/Android.mk
commands_recovery_local_path :=
# Copyright 2007 The Android Open Source Project
#
LOCAL_PATH := $(call my-dir)
amend_src_files := \
amend.c \
lexer.l \
parser_y.y \
ast.c \
symtab.c \
commands.c \
permissions.c \
execute.c
amend_test_files := \
test_symtab.c \
test_commands.c \
test_permissions.c
# "-x c" forces the lex/yacc files to be compiled as c;
# the build system otherwise forces them to be c++.
amend_cflags := -Wall -x c
#
# Build the host-side command line tool
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(amend_src_files) \
$(amend_test_files) \
register.c \
main.c
LOCAL_CFLAGS := $(amend_cflags) -g -O0
LOCAL_MODULE := amend
LOCAL_YACCFLAGS := -v
include $(BUILD_HOST_EXECUTABLE)
#
# Build the device-side library
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(amend_src_files)
LOCAL_SRC_FILES += $(amend_test_files)
LOCAL_CFLAGS := $(amend_cflags)
LOCAL_MODULE := libamend
include $(BUILD_STATIC_LIBRARY)
/*
* Copyright (C) 2007 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 <stdlib.h>
#include "amend.h"
#include "lexer.h"
extern const AmCommandList *gCommands;
const AmCommandList *
parseAmendScript(const char *buf, size_t bufLen)
{
setLexerInputBuffer(buf, bufLen);
int ret = yyparse();
if (ret != 0) {
return NULL;
}
return gCommands;
}
/*
* Copyright (C) 2007 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 AMEND_H_
#define AMEND_H_
#include "ast.h"
#include "execute.h"
const AmCommandList *parseAmendScript(const char *buf, size_t bufLen);
#endif // AMEND_H_
/*
* Copyright (C) 2007 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 <stdio.h>
#include "ast.h"
static const char gSpaces[] =
" "
" "
" "
" "
" "
" "
" ";
const int gSpacesMax = sizeof(gSpaces) - 1;
static const char *
pad(int level)
{
level *= 4;
if (level > gSpacesMax) {
level = gSpacesMax;
}
return gSpaces + gSpacesMax - level;
}
void dumpBooleanValue(int level, const AmBooleanValue *booleanValue);
void dumpStringValue(int level, const AmStringValue *stringValue);
void
dumpBooleanExpression(int level, const AmBooleanExpression *booleanExpression)
{
const char *op;
bool unary = false;
switch (booleanExpression->op) {
case AM_BOP_NOT:
op = "NOT";
unary = true;
break;
case AM_BOP_EQ:
op = "EQ";
break;
case AM_BOP_NE:
op = "NE";
break;
case AM_BOP_AND:
op = "AND";
break;
case AM_BOP_OR:
op = "OR";
break;
default:
op = "??";
break;
}
printf("%sBOOLEAN %s {\n", pad(level), op);
dumpBooleanValue(level + 1, booleanExpression->arg1);
if (!unary) {
dumpBooleanValue(level + 1, booleanExpression->arg2);
}
printf("%s}\n", pad(level));
}
void
dumpFunctionArguments(int level, const AmFunctionArguments *functionArguments)
{
int i;
for (i = 0; i < functionArguments->argc; i++) {
dumpStringValue(level, &functionArguments->argv[i]);
}
}
void
dumpFunctionCall(int level, const AmFunctionCall *functionCall)
{
printf("%sFUNCTION %s (\n", pad(level), functionCall->name);
dumpFunctionArguments(level + 1, functionCall->args);
printf("%s)\n", pad(level));
}
void
dumpStringValue(int level, const AmStringValue *stringValue)
{
switch (stringValue->type) {
case AM_SVAL_LITERAL:
printf("%s\"%s\"\n", pad(level), stringValue->u.literal);
break;
case AM_SVAL_FUNCTION:
dumpFunctionCall(level, stringValue->u.function);
break;
default:
printf("%s<UNKNOWN SVAL TYPE %d>\n", pad(level), stringValue->type);
break;
}
}
void
dumpStringComparisonExpression(int level,
const AmStringComparisonExpression *stringComparisonExpression)
{
const char *op;
switch (stringComparisonExpression->op) {
case AM_SOP_LT:
op = "LT";
break;
case AM_SOP_LE:
op = "LE";
break;
case AM_SOP_GT:
op = "GT";
break;
case AM_SOP_GE:
op = "GE";
break;
case AM_SOP_EQ:
op = "EQ";
break;
case AM_SOP_NE:
op = "NE";
break;
default:
op = "??";
break;
}
printf("%sSTRING %s {\n", pad(level), op);
dumpStringValue(level + 1, stringComparisonExpression->arg1);
dumpStringValue(level + 1, stringComparisonExpression->arg2);
printf("%s}\n", pad(level));
}
void
dumpBooleanValue(int level, const AmBooleanValue *booleanValue)
{
switch (booleanValue->type) {
case AM_BVAL_EXPRESSION:
dumpBooleanExpression(level, &booleanValue->u.expression);
break;
case AM_BVAL_STRING_COMPARISON:
dumpStringComparisonExpression(level,
&booleanValue->u.stringComparison);
break;
default:
printf("%s<UNKNOWN BVAL TYPE %d>\n", pad(1), booleanValue->type);
break;
}
}
void
dumpWordList(const AmWordList *wordList)
{
int i;
for (i = 0; i < wordList->argc; i++) {
printf("%s\"%s\"\n", pad(1), wordList->argv[i]);
}
}
void
dumpCommandArguments(const AmCommandArguments *commandArguments)
{
if (commandArguments->booleanArgs) {
dumpBooleanValue(1, commandArguments->u.b);
} else {
dumpWordList(commandArguments->u.w);
}
}
void
dumpCommand(const AmCommand *command)
{
printf("command \"%s\" {\n", command->name);
dumpCommandArguments(command->args);
printf("}\n");
}
void
dumpCommandList(const AmCommandList *commandList)
{
int i;
for (i = 0; i < commandList->commandCount; i++) {
dumpCommand(commandList->commands[i]);
}
}
/*
* Copyright (C) 2007 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 AMEND_AST_H_
#define AMEND_AST_H_
#include "commands.h"
typedef struct AmStringValue AmStringValue;
typedef struct {
int argc;
AmStringValue *argv;
} AmFunctionArguments;
/* An internal structure used only by the parser;
* will not appear in the output AST.
xxx try to move this into parser.h
*/
typedef struct AmFunctionArgumentBuilder AmFunctionArgumentBuilder;
struct AmFunctionArgumentBuilder {
AmFunctionArgumentBuilder *next;
AmStringValue *arg;
int argCount;
};
typedef struct AmWordListBuilder AmWordListBuilder;
struct AmWordListBuilder {
AmWordListBuilder *next;
const char *word;
int wordCount;
};
typedef struct {
const char *name;
Function *fn;
AmFunctionArguments *args;
} AmFunctionCall;
/* <string-value> ::=
* <literal-string> |
* <function-call>
*/
struct AmStringValue {
unsigned int line;
enum {
AM_SVAL_LITERAL,
AM_SVAL_FUNCTION,
} type;
union {
const char *literal;
//xxx inline instead of using pointers
AmFunctionCall *function;
} u;
};
/* <string-comparison-expression> ::=
* <string-value> <string-comparison-operator> <string-value>
*/
typedef struct {
unsigned int line;
enum {
AM_SOP_LT,
AM_SOP_LE,
AM_SOP_GT,
AM_SOP_GE,
AM_SOP_EQ,
AM_SOP_NE,
} op;
AmStringValue *arg1;
AmStringValue *arg2;
} AmStringComparisonExpression;
/* <boolean-expression> ::=
* ! <boolean-value> |
* <boolean-value> <binary-boolean-operator> <boolean-value>
*/
typedef struct AmBooleanValue AmBooleanValue;
typedef struct {
unsigned int line;
enum {
AM_BOP_NOT,
AM_BOP_EQ,
AM_BOP_NE,
AM_BOP_AND,
AM_BOP_OR,
} op;
AmBooleanValue *arg1;
AmBooleanValue *arg2;
} AmBooleanExpression;
/* <boolean-value> ::=
* <boolean-expression> |
* <string-comparison-expression>
*/
struct AmBooleanValue {
unsigned int line;
enum {
AM_BVAL_EXPRESSION,
AM_BVAL_STRING_COMPARISON,
} type;
union {
AmBooleanExpression expression;
AmStringComparisonExpression stringComparison;
} u;
};
typedef struct {
unsigned int line;
int argc;
const char **argv;
} AmWordList;
typedef struct {
bool booleanArgs;
union {
AmWordList *w;
AmBooleanValue *b;
} u;
} AmCommandArguments;
typedef struct {
unsigned int line;
const char *name;
Command *cmd;
AmCommandArguments *args;
} AmCommand;
typedef struct {
AmCommand **commands;
int commandCount;
int arraySize;
} AmCommandList;
void dumpCommandList(const AmCommandList *commandList);
#endif // AMEND_AST_H_
/*
* Copyright (C) 2007 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "symtab.h"
#include "commands.h"
#if 1
#define TRACE(...) printf(__VA_ARGS__)
#else
#define TRACE(...) /**/
#endif
typedef enum {
CMD_TYPE_UNKNOWN = -1,
CMD_TYPE_COMMAND = 0,
CMD_TYPE_FUNCTION
} CommandType;
typedef struct {
const char *name;
void *cookie;
CommandType type;
CommandArgumentType argType;
CommandHook hook;
} CommandEntry;
static struct {
SymbolTable *symbolTable;
bool commandStateInitialized;
} gCommandState;
int
commandInit()
{
if (gCommandState.commandStateInitialized) {
return -1;
}
gCommandState.symbolTable = createSymbolTable();
if (gCommandState.symbolTable == NULL) {
return -1;
}
gCommandState.commandStateInitialized = true;
return 0;
}
void
commandCleanup()
{
if (gCommandState.commandStateInitialized) {
gCommandState.commandStateInitialized = false;
deleteSymbolTable(gCommandState.symbolTable);
gCommandState.symbolTable = NULL;
//xxx need to free the entries and names in the symbol table
}
}
static int
registerCommandInternal(const char *name, CommandType type,
CommandArgumentType argType, CommandHook hook, void *cookie)
{
CommandEntry *entry;
if (!gCommandState.commandStateInitialized) {
return -1;
}
if (name == NULL || hook == NULL) {
return -1;
}
if (type != CMD_TYPE_COMMAND && type != CMD_TYPE_FUNCTION) {
return -1;
}
if (argType != CMD_ARGS_BOOLEAN && argType != CMD_ARGS_WORDS) {
return -1;
}
entry = (CommandEntry *)malloc(sizeof(CommandEntry));
if (entry != NULL) {
entry->name = strdup(name);
if (entry->name != NULL) {
int ret;
entry->cookie = cookie;
entry->type = type;
entry->argType = argType;
entry->hook = hook;
ret = addToSymbolTable(gCommandState.symbolTable,
entry->name, entry->type, entry);
if (ret == 0) {
return 0;
}
}
free(entry);
}
return -1;
}
int
registerCommand(const char *name,
CommandArgumentType argType, CommandHook hook, void *cookie)
{
return registerCommandInternal(name,
CMD_TYPE_COMMAND, argType, hook, cookie);
}
int
registerFunction(const char *name, FunctionHook hook, void *cookie)
{
return registerCommandInternal(name,
CMD_TYPE_FUNCTION, CMD_ARGS_WORDS, (CommandHook)hook, cookie);
}
Command *
findCommand(const char *name)
{
return (Command *)findInSymbolTable(gCommandState.symbolTable,
name, CMD_TYPE_COMMAND);
}
Function *
findFunction(const char *name)
{
return (Function *)findInSymbolTable(gCommandState.symbolTable,
name, CMD_TYPE_FUNCTION);
}
CommandArgumentType
getCommandArgumentType(Command *cmd)
{
CommandEntry *entry = (CommandEntry *)cmd;
if (entry != NULL) {
return entry->argType;
}
return CMD_ARGS_UNKNOWN;
}
static int
callCommandInternal(CommandEntry *entry, int argc, const char *argv[],
PermissionRequestList *permissions)
{
if (entry != NULL && entry->argType == CMD_ARGS_WORDS &&
(argc == 0 || (argc > 0 && argv != NULL)))
{
if (permissions == NULL) {
int i;
for (i = 0; i < argc; i++) {
if (argv[i] == NULL) {
goto bail;
}
}
}
TRACE("calling command %s\n", entry->name);
return entry->hook(entry->name, entry->cookie, argc, argv, permissions);
//xxx if permissions, make sure the entry has added at least one element.
}
bail:
return -1;
}
static int
callBooleanCommandInternal(CommandEntry *entry, bool arg,
PermissionRequestList *permissions)
{
if (entry != NULL && entry->argType == CMD_ARGS_BOOLEAN) {
TRACE("calling boolean command %s\n", entry->name);
return entry->hook(entry->name, entry->cookie, arg ? 1 : 0, NULL,
permissions);
//xxx if permissions, make sure the entry has added at least one element.
}
return -1;
}
int
callCommand(Command *cmd, int argc, const char *argv[])
{
return callCommandInternal((CommandEntry *)cmd, argc, argv, NULL);
}
int
callBooleanCommand(Command *cmd, bool arg)
{
return callBooleanCommandInternal((CommandEntry *)cmd, arg, NULL);
}
int
getCommandPermissions(Command *cmd, int argc, const char *argv[],
PermissionRequestList *permissions)
{
if (permissions != NULL) {
return callCommandInternal((CommandEntry *)cmd, argc, argv,
permissions);
}
return -1;
}
int
getBooleanCommandPermissions(Command *cmd, bool arg,
PermissionRequestList *permissions)
{
if (permissions != NULL) {
return callBooleanCommandInternal((CommandEntry *)cmd, arg,
permissions);
}
return -1;
}
int
callFunctionInternal(CommandEntry *entry, int argc, const char *argv[],
char **result, size_t *resultLen, PermissionRequestList *permissions)
{
if (entry != NULL && entry->argType == CMD_ARGS_WORDS &&
(argc == 0 || (argc > 0 && argv != NULL)))
{
if ((permissions == NULL && result != NULL) ||
(permissions != NULL && result == NULL))
{
if (permissions == NULL) {
/* This is the actual invocation of the function,
* which means that none of the arguments are allowed
* to be NULL.
*/
int i;
for (i = 0; i < argc; i++) {
if (argv[i] == NULL) {
goto bail;
}
}
}
TRACE("calling function %s\n", entry->name);
return ((FunctionHook)entry->hook)(entry->name, entry->cookie,
argc, argv, result, resultLen, permissions);
//xxx if permissions, make sure the entry has added at least one element.
}
}
bail:
return -1;
}
int
callFunction(Function *fn, int argc, const char *argv[],
char **result, size_t *resultLen)
{
return callFunctionInternal((CommandEntry *)fn, argc, argv,
result, resultLen, NULL);
}
int
getFunctionPermissions(Function *fn, int argc, const char *argv[],
PermissionRequestList *permissions)
{
if (permissions != NULL) {
return callFunctionInternal((CommandEntry *)fn, argc, argv,
NULL, NULL, permissions);
}
return -1;
}
/*
* Copyright (C) 2007 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 AMEND_COMMANDS_H_
#define AMEND_COMMANDS_H_
#include "permissions.h"
/* Invoke or dry-run a command. If "permissions" is non-NULL,
* the hook should fill it out with the list of files and operations that
* it would need to complete its operation. If "permissions" is NULL,
* the hook should do the actual work specified by its arguments.
*
* When a command is called with non-NULL "permissions", some arguments
* may be NULL. A NULL argument indicates that the argument is actually
* the output of another function, so is not known at permissions time.
* The permissions of leaf-node functions (those that have only literal
* strings as arguments) will get appended to the permissions of the
* functions that call them. However, to be completely safe, functions
* that receive a NULL argument should request the broadest-possible
* permissions for the range of the input argument.
*
* When a boolean command is called, "argc" is the boolean value and
* "argv" is NULL.
*/
typedef int (*CommandHook)(const char *name, void *cookie,
int argc, const char *argv[],
PermissionRequestList *permissions);
int commandInit(void);
void commandCleanup(void);
/*
* Command management
*/
struct Command;
typedef struct Command Command;
typedef enum {
CMD_ARGS_UNKNOWN = -1,
CMD_ARGS_BOOLEAN = 0,
CMD_ARGS_WORDS
} CommandArgumentType;
int registerCommand(const char *name,
CommandArgumentType argType, CommandHook hook, void *cookie);
Command *findCommand(const char *name);
CommandArgumentType getCommandArgumentType(Command *cmd);
int callCommand(Command *cmd, int argc, const char *argv[]);
int callBooleanCommand(Command *cmd, bool arg);
int getCommandPermissions(Command *cmd, int argc, const char *argv[],
PermissionRequestList *permissions);
int getBooleanCommandPermissions(Command *cmd, bool arg,
PermissionRequestList *permissions);
/*
* Function management
*/
typedef int (*FunctionHook)(const char *name, void *cookie,
int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions);
struct Function;
typedef struct Function Function;
int registerFunction(const char *name, FunctionHook hook, void *cookie);
Function *findFunction(const char *name);
int callFunction(Function *fn, int argc, const char *argv[],
char **result, size_t *resultLen);
int getFunctionPermissions(Function *fn, int argc, const char *argv[],
PermissionRequestList *permissions);
#endif // AMEND_COMMANDS_H_
/*
* Copyright (C) 2007 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#undef NDEBUG
#include <assert.h>
#include "ast.h"
#include "execute.h"
typedef struct {
int c;
const char **v;
} StringList;
static int execBooleanValue(ExecContext *ctx,
const AmBooleanValue *booleanValue, bool *result);
static int execStringValue(ExecContext *ctx, const AmStringValue *stringValue,
const char **result);
static int
execBooleanExpression(ExecContext *ctx,
const AmBooleanExpression *booleanExpression, bool *result)
{
int ret;
bool arg1, arg2;
bool unary;
assert(ctx != NULL);
assert(booleanExpression != NULL);
assert(result != NULL);
if (ctx == NULL || booleanExpression == NULL || result == NULL) {
return -__LINE__;
}
if (booleanExpression->op == AM_BOP_NOT) {
unary = true;
} else {
unary = false;
}
ret = execBooleanValue(ctx, booleanExpression->arg1, &arg1);
if (ret != 0) return ret;
if (!unary) {
ret = execBooleanValue(ctx, booleanExpression->arg2, &arg2);
if (ret != 0) return ret;
} else {
arg2 = false;
}
switch (booleanExpression->op) {
case AM_BOP_NOT:
*result = !arg1;
break;
case AM_BOP_EQ:
*result = (arg1 == arg2);
break;
case AM_BOP_NE:
*result = (arg1 != arg2);
break;
case AM_BOP_AND:
*result = (arg1 && arg2);
break;
case AM_BOP_OR:
*result = (arg1 || arg2);
break;
default:
return -__LINE__;
}
return 0;
}
static int
execFunctionArguments(ExecContext *ctx,
const AmFunctionArguments *functionArguments, StringList *result)
{
int ret;
assert(ctx != NULL);
assert(functionArguments != NULL);
assert(result != NULL);
if (ctx == NULL || functionArguments == NULL || result == NULL) {
return -__LINE__;
}
result->c = functionArguments->argc;
result->v = (const char **)malloc(result->c * sizeof(const char *));
if (result->v == NULL) {
result->c = 0;
return -__LINE__;
}
int i;
for (i = 0; i < functionArguments->argc; i++) {
ret = execStringValue(ctx, &functionArguments->argv[i], &result->v[i]);
if (ret != 0) {
result->c = 0;
free(result->v);
//TODO: free the individual args, if we're responsible for them.
result->v = NULL;
return ret;
}
}
return 0;
}
static int
execFunctionCall(ExecContext *ctx, const AmFunctionCall *functionCall,
const char **result)
{
int ret;
assert(ctx != NULL);
assert(functionCall != NULL);
assert(result != NULL);
if (ctx == NULL || functionCall == NULL || result == NULL) {
return -__LINE__;
}
StringList args;
ret = execFunctionArguments(ctx, functionCall->args, &args);
if (ret != 0) {
return ret;
}
ret = callFunction(functionCall->fn, args.c, args.v, (char **)result, NULL);
if (ret != 0) {
return ret;
}
//TODO: clean up args
return 0;
}
static int
execStringValue(ExecContext *ctx, const AmStringValue *stringValue,
const char **result)
{
int ret;
assert(ctx != NULL);
assert(stringValue != NULL);
assert(result != NULL);
if (ctx == NULL || stringValue == NULL || result == NULL) {
return -__LINE__;
}
switch (stringValue->type) {
case AM_SVAL_LITERAL:
*result = strdup(stringValue->u.literal);
break;
case AM_SVAL_FUNCTION:
ret = execFunctionCall(ctx, stringValue->u.function, result);
if (ret != 0) {
return ret;
}
break;
default:
return -__LINE__;
}
return 0;
}
static int
execStringComparisonExpression(ExecContext *ctx,
const AmStringComparisonExpression *stringComparisonExpression,
bool *result)
{
int ret;
assert(ctx != NULL);
assert(stringComparisonExpression != NULL);
assert(result != NULL);
if (ctx == NULL || stringComparisonExpression == NULL || result == NULL) {
return -__LINE__;
}
const char *arg1, *arg2;
ret = execStringValue(ctx, stringComparisonExpression->arg1, &arg1);
if (ret != 0) {
return ret;
}
ret = execStringValue(ctx, stringComparisonExpression->arg2, &arg2);
if (ret != 0) {
return ret;
}
int cmp = strcmp(arg1, arg2);
switch (stringComparisonExpression->op) {
case AM_SOP_LT:
*result = (cmp < 0);
break;
case AM_SOP_LE:
*result = (cmp <= 0);
break;
case AM_SOP_GT:
*result = (cmp > 0);
break;
case AM_SOP_GE:
*result = (cmp >= 0);
break;
case AM_SOP_EQ:
*result = (cmp == 0);
break;
case AM_SOP_NE:
*result = (cmp != 0);
break;
default:
return -__LINE__;
break;
}
return 0;
}
static int
execBooleanValue(ExecContext *ctx, const AmBooleanValue *booleanValue,
bool *result)
{
int ret;
assert(ctx != NULL);
assert(booleanValue != NULL);
assert(result != NULL);
if (ctx == NULL || booleanValue == NULL || result == NULL) {
return -__LINE__;
}
switch (booleanValue->type) {
case AM_BVAL_EXPRESSION:
ret = execBooleanExpression(ctx, &booleanValue->u.expression, result);
break;
case AM_BVAL_STRING_COMPARISON:
ret = execStringComparisonExpression(ctx,
&booleanValue->u.stringComparison, result);
break;
default:
ret = -__LINE__;
break;
}
return ret;
}
static int
execCommand(ExecContext *ctx, const AmCommand *command)
{
int ret;
assert(ctx != NULL);
assert(command != NULL);
if (ctx == NULL || command == NULL) {
return -__LINE__;
}
CommandArgumentType argType;
argType = getCommandArgumentType(command->cmd);
switch (argType) {
case CMD_ARGS_BOOLEAN:
{
bool bVal;
ret = execBooleanValue(ctx, command->args->u.b, &bVal);
if (ret == 0) {
ret = callBooleanCommand(command->cmd, bVal);
}
}
break;
case CMD_ARGS_WORDS:
{
AmWordList *words = command->args->u.w;
ret = callCommand(command->cmd, words->argc, words->argv);
}
break;
default:
ret = -__LINE__;
break;
}
return ret;
}
int
execCommandList(ExecContext *ctx, const AmCommandList *commandList)
{
int i;
for (i = 0; i < commandList->commandCount; i++) {
int ret = execCommand(ctx, commandList->commands[i]);
if (ret != 0) {
int line = commandList->commands[i]->line;
return line > 0 ? line : ret;
}
}
return 0;
}
/*
* Copyright (C) 2007 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 AMEND_EXECUTE_H_
#define AMEND_EXECUTE_H_
typedef struct ExecContext ExecContext;
/* Returns 0 on success, otherwise the line number that failed. */
int execCommandList(ExecContext *ctx, const AmCommandList *commandList);
#endif // AMEND_EXECUTE_H_
/*
* Copyright (C) 2007 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 AMEND_LEXER_H_
#define AMEND_LEXER_H_
#define AMEND_LEXER_BUFFER_INPUT 1
void yyerror(const char *msg);
int yylex(void);
#if AMEND_LEXER_BUFFER_INPUT
void setLexerInputBuffer(const char *buf, size_t buflen);
#else
#include <stdio.h>
void yyset_in(FILE *in_str);
#endif
const char *tokenToString(int token);
typedef enum {
AM_UNKNOWN_ARGS,
AM_WORD_ARGS,
AM_BOOLEAN_ARGS,
} AmArgumentType;
void setLexerArgumentType(AmArgumentType type);
int getLexerLineNumber(void);
#endif // AMEND_LEXER_H_
/*
* Copyright (C) 2007 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 <stdio.h>
#include <stdlib.h>
#include "ast.h"
#include "lexer.h"
#include "parser.h"
const char *tokenToString(int token)
{
static char scratch[128];
switch (token) {
case TOK_AND:
return "&&";
case TOK_OR:
return "||";
case TOK_EQ:
return "==";
case TOK_NE:
return "!=";
case TOK_GE:
return ">=";
case TOK_LE:
return "<=";
case TOK_EOF:
return "EOF";
case TOK_EOL:
return "EOL\n";
case TOK_STRING:
snprintf(scratch, sizeof(scratch),
"STRING<%s>", yylval.literalString);
return scratch;
case TOK_IDENTIFIER:
snprintf(scratch, sizeof(scratch), "IDENTIFIER<%s>",
yylval.literalString);
return scratch;
case TOK_WORD:
snprintf(scratch, sizeof(scratch), "WORD<%s>",
yylval.literalString);
return scratch;
default:
if (token > ' ' && token <= '~') {
scratch[0] = (char)token;
scratch[1] = '\0';
} else {
snprintf(scratch, sizeof(scratch), "??? <%d>", token);
}
return scratch;
}
}
typedef struct {
char *value;
char *nextc;
unsigned int alloc_size;
} AmString;
static int addCharToString(AmString *str, char c)
{
if ((unsigned int)(str->nextc - str->value) >= str->alloc_size) {
char *new_value;
unsigned int new_size;
new_size = (str->alloc_size + 1) * 2;
if (new_size < 64) {
new_size = 64;
}
new_value = (char *)realloc(str->value, new_size);
if (new_value == NULL) {
yyerror("out of memory");
return -1;
}
str->nextc = str->nextc - str->value + new_value;
str->value = new_value;
str->alloc_size = new_size;
}
*str->nextc++ = c;
return 0;
}
static int setString(AmString *str, const char *p)
{
str->nextc = str->value;
while (*p != '\0') {
//TODO: add the whole string at once
addCharToString(str, *p++);
}
return addCharToString(str, '\0');
}
static AmString gStr = { NULL, NULL, 0 };
static int gLineNumber = 1;
static AmArgumentType gArgumentType = AM_UNKNOWN_ARGS;
static const char *gErrorMessage = NULL;
#if AMEND_LEXER_BUFFER_INPUT
static const char *gInputBuffer;
static const char *gInputBufferNext;
static const char *gInputBufferEnd;
# define YY_INPUT(buf, result, max_size) \
do { \
int nbytes = gInputBufferEnd - gInputBufferNext; \
if (nbytes > 0) { \
if (nbytes > max_size) { \
nbytes = max_size; \
} \
memcpy(buf, gInputBufferNext, nbytes); \
gInputBufferNext += nbytes; \
result = nbytes; \
} else { \
result = YY_NULL; \
} \
} while (false)
#endif // AMEND_LEXER_BUFFER_INPUT
%}
%option noyywrap
%x QUOTED_STRING BOOLEAN WORDS
ident [a-zA-Z_][a-zA-Z_0-9]*
word [^ \t\r\n"]+
%%
/* This happens at the beginning of each call to yylex().
*/
if (gArgumentType == AM_WORD_ARGS) {
BEGIN(WORDS);
} else if (gArgumentType == AM_BOOLEAN_ARGS) {
BEGIN(BOOLEAN);
}
/*xxx require everything to be 7-bit-clean, printable characters */
<INITIAL>{
{ident}/[ \t\r\n] {
/* The only token we recognize in the initial
* state is an identifier followed by whitespace.
*/
setString(&gStr, yytext);
yylval.literalString = gStr.value;
return TOK_IDENTIFIER;
}
}
<BOOLEAN>{
{ident} {
/* Non-quoted identifier-style string */
setString(&gStr, yytext);
yylval.literalString = gStr.value;
return TOK_IDENTIFIER;
}
"&&" return TOK_AND;
"||" return TOK_OR;
"==" return TOK_EQ;
"!=" return TOK_NE;
">=" return TOK_GE;
"<=" return TOK_LE;
[<>()!,] return yytext[0];
}
/* Double-quoted string handling */
<WORDS,BOOLEAN>\" {
/* Initial quote */
gStr.nextc = gStr.value;
BEGIN(QUOTED_STRING);
}
<QUOTED_STRING>{
\" {
/* Closing quote */
BEGIN(INITIAL);
addCharToString(&gStr, '\0');
yylval.literalString = gStr.value;
if (gArgumentType == AM_WORD_ARGS) {
return TOK_WORD;
} else {
return TOK_STRING;
}
}
<<EOF>> |
\n {
/* Unterminated string */
yyerror("unterminated string");
return TOK_ERROR;
}
\\\" {
/* Escaped quote */
addCharToString(&gStr, '"');
}
\\\\ {
/* Escaped backslash */
addCharToString(&gStr, '\\');
}
\\. {
/* No other escapes allowed. */
gErrorMessage = "illegal escape";
return TOK_ERROR;
}
[^\\\n\"]+ {
/* String contents */
char *p = yytext;
while (*p != '\0') {
/* TODO: add the whole string at once */
addCharToString(&gStr, *p++);
}
}
}
<WORDS>{
/*xxx look out for backslashes; escape backslashes and quotes */
/*xxx if a quote is right against a char, we should append */
{word} {
/* Whitespace-separated word */
setString(&gStr, yytext);
yylval.literalString = gStr.value;
return TOK_WORD;
}
}
<INITIAL,WORDS,BOOLEAN>{
\n {
/* Count lines */
gLineNumber++;
gArgumentType = AM_UNKNOWN_ARGS;
BEGIN(INITIAL);
return TOK_EOL;
}
/*xxx backslashes to extend lines? */
/* Skip whitespace and comments.
*/
[ \t\r]+ ;
#.* ;
. {
/* Fail on anything we didn't expect. */
gErrorMessage = "unexpected character";
return TOK_ERROR;
}
}
%%
void
yyerror(const char *msg)
{
if (!strcmp(msg, "syntax error") && gErrorMessage != NULL) {
msg = gErrorMessage;
gErrorMessage = NULL;
}
fprintf(stderr, "line %d: %s at '%s'\n", gLineNumber, msg, yytext);
}
#if AMEND_LEXER_BUFFER_INPUT
void
setLexerInputBuffer(const char *buf, size_t buflen)
{
gLineNumber = 1;
gInputBuffer = buf;
gInputBufferNext = gInputBuffer;
gInputBufferEnd = gInputBuffer + buflen;
}
#endif // AMEND_LEXER_BUFFER_INPUT
void
setLexerArgumentType(AmArgumentType type)
{
gArgumentType = type;
}
int
getLexerLineNumber(void)
{
return gLineNumber;
}
/*
* Copyright (C) 2007 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ast.h"
#include "lexer.h"
#include "parser.h"
#include "register.h"
#include "execute.h"
void
lexTest()
{
int token;
do {
token = yylex();
if (token == 0) {
printf(" EOF");
fflush(stdout);
break;
} else {
printf(" %s", tokenToString(token));
fflush(stdout);
if (token == TOK_IDENTIFIER) {
if (strcmp(yylval.literalString, "assert") == 0) {
setLexerArgumentType(AM_BOOLEAN_ARGS);
} else {
setLexerArgumentType(AM_WORD_ARGS);
}
do {
token = yylex();
printf(" %s", tokenToString(token));
fflush(stdout);
} while (token != TOK_EOL && token != TOK_EOF && token != 0);
} else if (token != TOK_EOL) {
fprintf(stderr, "syntax error: expected identifier\n");
break;
}
}
} while (token != 0);
printf("\n");
}
void
usage()
{
printf("usage: amend [--debug-lex|--debug-ast] [<filename>]\n");
exit(1);
}
extern const AmCommandList *gCommands;
int
main(int argc, char *argv[])
{
FILE *inputFile = NULL;
bool debugLex = false;
bool debugAst = false;
const char *fileName = NULL;
int err;
#if 1
extern int test_symtab(void);
int ret = test_symtab();
if (ret != 0) {
fprintf(stderr, "test_symtab() failed: %d\n", ret);
exit(ret);
}
extern int test_cmd_fn(void);
ret = test_cmd_fn();
if (ret != 0) {
fprintf(stderr, "test_cmd_fn() failed: %d\n", ret);
exit(ret);
}
extern int test_permissions(void);
ret = test_permissions();
if (ret != 0) {
fprintf(stderr, "test_permissions() failed: %d\n", ret);
exit(ret);
}
#endif
argc--;
argv++;
while (argc > 0) {
if (strcmp("--debug-lex", argv[0]) == 0) {
debugLex = true;
} else if (strcmp("--debug-ast", argv[0]) == 0) {
debugAst = true;
} else if (argv[0][0] == '-') {
fprintf(stderr, "amend: Unknown option \"%s\"\n", argv[0]);
usage();
} else {
fileName = argv[0];
}
argc--;
argv++;
}
if (fileName != NULL) {
inputFile = fopen(fileName, "r");
if (inputFile == NULL) {
fprintf(stderr, "amend: Can't open input file '%s'\n", fileName);
usage();
}
}
commandInit();
//xxx clean up
err = registerUpdateCommands();
if (err < 0) {
fprintf(stderr, "amend: Error registering commands: %d\n", err);
exit(-err);
}
err = registerUpdateFunctions();
if (err < 0) {
fprintf(stderr, "amend: Error registering functions: %d\n", err);
exit(-err);
}
#if AMEND_LEXER_BUFFER_INPUT
if (inputFile == NULL) {
fprintf(stderr, "amend: No input file\n");
usage();
}
char *fileData;
int fileDataLen;
fseek(inputFile, 0, SEEK_END);
fileDataLen = ftell(inputFile);
rewind(inputFile);
if (fileDataLen < 0) {
fprintf(stderr, "amend: Can't get file length\n");
exit(2);
} else if (fileDataLen == 0) {
printf("amend: Empty input file\n");
exit(0);
}
fileData = (char *)malloc(fileDataLen + 1);
if (fileData == NULL) {
fprintf(stderr, "amend: Can't allocate %d bytes\n", fileDataLen + 1);
exit(2);
}
size_t nread = fread(fileData, 1, fileDataLen, inputFile);
if (nread != (size_t)fileDataLen) {
fprintf(stderr, "amend: Didn't read %d bytes, only %zd\n", fileDataLen,
nread);
exit(2);
}
fileData[fileDataLen] = '\0';
setLexerInputBuffer(fileData, fileDataLen);
#else
if (inputFile == NULL) {
inputFile = stdin;
}
yyset_in(inputFile);
#endif
if (debugLex) {
lexTest();
} else {
int ret = yyparse();
if (ret != 0) {
fprintf(stderr, "amend: Parse failed (%d)\n", ret);
exit(2);
} else {
if (debugAst) {
dumpCommandList(gCommands);
}
printf("amend: Parse successful.\n");
ret = execCommandList((ExecContext *)1, gCommands);
if (ret != 0) {
fprintf(stderr, "amend: Execution failed (%d)\n", ret);
exit(3);
}
printf("amend: Execution successful.\n");
}
}
return 0;
}
/*
* Copyright (C) 2007 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 AMEND_PARSER_H_
#define AMEND_PARSER_H_
#include "parser_y.h"
int yyparse(void);
#endif // AMEND_PARSER_H_
/*
* Copyright (C) 2007 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.
*/
%{
#undef NDEBUG
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include "ast.h"
#include "lexer.h"
#include "commands.h"
void yyerror(const char *msg);
int yylex(void);
#define STRING_COMPARISON(out, a1, sop, a2) \
do { \
out = (AmBooleanValue *)malloc(sizeof(AmBooleanValue)); \
if (out == NULL) { \
YYABORT; \
} \
out->type = AM_BVAL_STRING_COMPARISON; \
out->u.stringComparison.op = sop; \
out->u.stringComparison.arg1 = a1; \
out->u.stringComparison.arg2 = a2; \
} while (false)
#define BOOLEAN_EXPRESSION(out, a1, bop, a2) \
do { \
out = (AmBooleanValue *)malloc(sizeof(AmBooleanValue)); \
if (out == NULL) { \
YYABORT; \
} \
out->type = AM_BVAL_EXPRESSION; \
out->u.expression.op = bop; \
out->u.expression.arg1 = a1; \
out->u.expression.arg2 = a2; \
} while (false)
AmCommandList *gCommands = NULL;
%}
%start lines
%union {
char *literalString;
AmFunctionArgumentBuilder *functionArgumentBuilder;
AmFunctionArguments *functionArguments;
AmFunctionCall *functionCall;
AmStringValue *stringValue;
AmBooleanValue *booleanValue;
AmWordListBuilder *wordListBuilder;
AmCommandArguments *commandArguments;
AmCommand *command;
AmCommandList *commandList;
}
%token TOK_AND TOK_OR TOK_EQ TOK_NE TOK_GE TOK_LE TOK_EOF TOK_EOL TOK_ERROR
%token <literalString> TOK_STRING TOK_IDENTIFIER TOK_WORD
%type <commandList> lines
%type <command> command line
%type <functionArgumentBuilder> function_arguments
%type <functionArguments> function_arguments_or_empty
%type <functionCall> function_call
%type <literalString> function_name
%type <stringValue> string_value
%type <booleanValue> boolean_expression
%type <wordListBuilder> word_list
%type <commandArguments> arguments
/* Operator precedence, weakest to strongest.
* Same as C/Java precedence.
*/
%left TOK_OR
%left TOK_AND
%left TOK_EQ TOK_NE
%left '<' '>' TOK_LE TOK_GE
%right '!'
%%
lines : /* empty */
{
$$ = (AmCommandList *)malloc(sizeof(AmCommandList));
if ($$ == NULL) {
YYABORT;
}
gCommands = $$;
$$->arraySize = 64;
$$->commandCount = 0;
$$->commands = (AmCommand **)malloc(
sizeof(AmCommand *) * $$->arraySize);
if ($$->commands == NULL) {
YYABORT;
}
}
| lines line
{
if ($2 != NULL) {
if ($1->commandCount >= $1->arraySize) {
AmCommand **newArray;
newArray = (AmCommand **)realloc($$->commands,
sizeof(AmCommand *) * $$->arraySize * 2);
if (newArray == NULL) {
YYABORT;
}
$$->commands = newArray;
$$->arraySize *= 2;
}
$1->commands[$1->commandCount++] = $2;
}
}
;
line : line_ending
{
$$ = NULL; /* ignore blank lines */
}
| command arguments line_ending
{
$$ = $1;
$$->args = $2;
setLexerArgumentType(AM_UNKNOWN_ARGS);
}
;
command : TOK_IDENTIFIER
{
Command *cmd = findCommand($1);
if (cmd == NULL) {
fprintf(stderr, "Unknown command \"%s\"\n", $1);
YYABORT;
}
$$ = (AmCommand *)malloc(sizeof(AmCommand));
if ($$ == NULL) {
YYABORT;
}
$$->line = getLexerLineNumber();
$$->name = strdup($1);
if ($$->name == NULL) {
YYABORT;
}
$$->args = NULL;
CommandArgumentType argType = getCommandArgumentType(cmd);
if (argType == CMD_ARGS_BOOLEAN) {
setLexerArgumentType(AM_BOOLEAN_ARGS);
} else {
setLexerArgumentType(AM_WORD_ARGS);
}
$$->cmd = cmd;
}
;
line_ending :
TOK_EOL
| TOK_EOF
;
arguments : boolean_expression
{
$$ = (AmCommandArguments *)malloc(
sizeof(AmCommandArguments));
if ($$ == NULL) {
YYABORT;
}
$$->booleanArgs = true;
$$->u.b = $1;
}
| word_list
{
/* Convert the builder list into an array.
* Do it in reverse order; the words were pushed
* onto the list in LIFO order.
*/
AmWordList *w = (AmWordList *)malloc(sizeof(AmWordList));
if (w == NULL) {
YYABORT;
}
if ($1 != NULL) {
AmWordListBuilder *words = $1;
w->argc = words->wordCount;
w->argv = (const char **)malloc(w->argc *
sizeof(char *));
if (w->argv == NULL) {
YYABORT;
}
int i;
for (i = w->argc; words != NULL && i > 0; --i) {
AmWordListBuilder *f = words;
w->argv[i-1] = words->word;
words = words->next;
free(f);
}
assert(i == 0);
assert(words == NULL);
} else {
w->argc = 0;
w->argv = NULL;
}
$$ = (AmCommandArguments *)malloc(
sizeof(AmCommandArguments));
if ($$ == NULL) {
YYABORT;
}
$$->booleanArgs = false;
$$->u.w = w;
}
;
word_list : /* empty */
{ $$ = NULL; }
| word_list TOK_WORD
{
if ($1 == NULL) {
$$ = (AmWordListBuilder *)malloc(
sizeof(AmWordListBuilder));
if ($$ == NULL) {
YYABORT;
}
$$->next = NULL;
$$->wordCount = 1;
} else {
$$ = (AmWordListBuilder *)malloc(
sizeof(AmWordListBuilder));
if ($$ == NULL) {
YYABORT;
}
$$->next = $1;
$$->wordCount = $$->next->wordCount + 1;
}
$$->word = strdup($2);
if ($$->word == NULL) {
YYABORT;
}
}
;
boolean_expression :
'!' boolean_expression
{
$$ = (AmBooleanValue *)malloc(sizeof(AmBooleanValue));
if ($$ == NULL) {
YYABORT;
}
$$->type = AM_BVAL_EXPRESSION;
$$->u.expression.op = AM_BOP_NOT;
$$->u.expression.arg1 = $2;
$$->u.expression.arg2 = NULL;
}
/* TODO: if both expressions are literals, evaluate now */
| boolean_expression TOK_AND boolean_expression
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_AND, $3); }
| boolean_expression TOK_OR boolean_expression
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_OR, $3); }
| boolean_expression TOK_EQ boolean_expression
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_EQ, $3); }
| boolean_expression TOK_NE boolean_expression
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_NE, $3); }
| '(' boolean_expression ')'
{ $$ = $2; }
/* TODO: if both strings are literals, evaluate now */
| string_value '<' string_value
{ STRING_COMPARISON($$, $1, AM_SOP_LT, $3); }
| string_value '>' string_value
{ STRING_COMPARISON($$, $1, AM_SOP_GT, $3); }
| string_value TOK_EQ string_value
{ STRING_COMPARISON($$, $1, AM_SOP_EQ, $3); }
| string_value TOK_NE string_value
{ STRING_COMPARISON($$, $1, AM_SOP_NE, $3); }
| string_value TOK_LE string_value
{ STRING_COMPARISON($$, $1, AM_SOP_LE, $3); }
| string_value TOK_GE string_value
{ STRING_COMPARISON($$, $1, AM_SOP_GE, $3); }
;
string_value :
TOK_IDENTIFIER
{
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
if ($$ == NULL) {
YYABORT;
}
$$->type = AM_SVAL_LITERAL;
$$->u.literal = strdup($1);
if ($$->u.literal == NULL) {
YYABORT;
}
}
| TOK_STRING
{
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
if ($$ == NULL) {
YYABORT;
}
$$->type = AM_SVAL_LITERAL;
$$->u.literal = strdup($1);
if ($$->u.literal == NULL) {
YYABORT;
}
}
| function_call
{
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
if ($$ == NULL) {
YYABORT;
}
$$->type = AM_SVAL_FUNCTION;
$$->u.function = $1;
}
;
/* We can't just say
* TOK_IDENTIFIER '(' function_arguments_or_empty ')'
* because parsing function_arguments_or_empty will clobber
* the underlying string that yylval.literalString points to.
*/
function_call :
function_name '(' function_arguments_or_empty ')'
{
Function *fn = findFunction($1);
if (fn == NULL) {
fprintf(stderr, "Unknown function \"%s\"\n", $1);
YYABORT;
}
$$ = (AmFunctionCall *)malloc(sizeof(AmFunctionCall));
if ($$ == NULL) {
YYABORT;
}
$$->name = $1;
if ($$->name == NULL) {
YYABORT;
}
$$->fn = fn;
$$->args = $3;
}
;
function_name :
TOK_IDENTIFIER
{
$$ = strdup($1);
}
;
function_arguments_or_empty :
/* empty */
{
$$ = (AmFunctionArguments *)malloc(
sizeof(AmFunctionArguments));
if ($$ == NULL) {
YYABORT;
}
$$->argc = 0;
$$->argv = NULL;
}
| function_arguments
{
AmFunctionArgumentBuilder *args = $1;
assert(args != NULL);
/* Convert the builder list into an array.
* Do it in reverse order; the args were pushed
* onto the list in LIFO order.
*/
$$ = (AmFunctionArguments *)malloc(
sizeof(AmFunctionArguments));
if ($$ == NULL) {
YYABORT;
}
$$->argc = args->argCount;
$$->argv = (AmStringValue *)malloc(
$$->argc * sizeof(AmStringValue));
if ($$->argv == NULL) {
YYABORT;
}
int i;
for (i = $$->argc; args != NULL && i > 0; --i) {
AmFunctionArgumentBuilder *f = args;
$$->argv[i-1] = *args->arg;
args = args->next;
free(f->arg);
free(f);
}
assert(i == 0);
assert(args == NULL);
}
;
function_arguments :
string_value
{
$$ = (AmFunctionArgumentBuilder *)malloc(
sizeof(AmFunctionArgumentBuilder));
if ($$ == NULL) {
YYABORT;
}
$$->next = NULL;
$$->argCount = 1;
$$->arg = $1;
}
| function_arguments ',' string_value
{
$$ = (AmFunctionArgumentBuilder *)malloc(
sizeof(AmFunctionArgumentBuilder));
if ($$ == NULL) {
YYABORT;
}
$$->next = $1;
$$->argCount = $$->next->argCount + 1;
$$->arg = $3;
}
;
/* xxx this whole tool needs to be hardened */
/*
* Copyright (C) 2007 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 <stdlib.h>
#include <string.h>
#include "permissions.h"
int
initPermissionRequestList(PermissionRequestList *list)
{
if (list != NULL) {
list->requests = NULL;
list->numRequests = 0;
list->requestsAllocated = 0;
return 0;
}
return -1;
}
int
addPermissionRequestToList(PermissionRequestList *list,
const char *path, bool recursive, unsigned int permissions)
{
if (list == NULL || list->numRequests < 0 ||
list->requestsAllocated < list->numRequests || path == NULL)
{
return -1;
}
if (list->numRequests == list->requestsAllocated) {
int newSize;
PermissionRequest *newRequests;
newSize = list->requestsAllocated * 2;
if (newSize < 16) {
newSize = 16;
}
newRequests = (PermissionRequest *)realloc(list->requests,
newSize * sizeof(PermissionRequest));
if (newRequests == NULL) {
return -2;
}
list->requests = newRequests;
list->requestsAllocated = newSize;
}
PermissionRequest *req;
req = &list->requests[list->numRequests++];
req->path = strdup(path);
if (req->path == NULL) {
list->numRequests--;
return -3;
}
req->recursive = recursive;
req->requested = permissions;
req->allowed = 0;
return 0;
}
void
freePermissionRequestListElements(PermissionRequestList *list)
{
if (list != NULL && list->numRequests >= 0 &&
list->requestsAllocated >= list->numRequests)
{
int i;
for (i = 0; i < list->numRequests; i++) {
free((void *)list->requests[i].path);
}
free(list->requests);
initPermissionRequestList(list);
}
}
/*
* Global permission table
*/
static struct {
Permission *permissions;
int numPermissionEntries;
int allocatedPermissionEntries;
bool permissionStateInitialized;
} gPermissionState = {
#if 1
NULL, 0, 0, false
#else
.permissions = NULL,
.numPermissionEntries = 0,
.allocatedPermissionEntries = 0,
.permissionStateInitialized = false
#endif
};
int
permissionInit()
{
if (gPermissionState.permissionStateInitialized) {
return -1;
}
gPermissionState.permissions = NULL;
gPermissionState.numPermissionEntries = 0;
gPermissionState.allocatedPermissionEntries = 0;
gPermissionState.permissionStateInitialized = true;
//xxx maybe add an "namespace root gets no permissions" fallback by default
return 0;
}
void
permissionCleanup()
{
if (gPermissionState.permissionStateInitialized) {
gPermissionState.permissionStateInitialized = false;
if (gPermissionState.permissions != NULL) {
int i;
for (i = 0; i < gPermissionState.numPermissionEntries; i++) {
free((void *)gPermissionState.permissions[i].path);
}
free(gPermissionState.permissions);
}
}
}
int
getPermissionCount()
{
if (gPermissionState.permissionStateInitialized) {
return gPermissionState.numPermissionEntries;
}
return -1;
}
const Permission *
getPermissionAt(int index)
{
if (!gPermissionState.permissionStateInitialized) {
return NULL;
}
if (index < 0 || index >= gPermissionState.numPermissionEntries) {
return NULL;
}
return &gPermissionState.permissions[index];
}
int
getAllowedPermissions(const char *path, bool recursive,
unsigned int *outAllowed)
{
if (!gPermissionState.permissionStateInitialized) {
return -2;
}
if (outAllowed == NULL) {
return -1;
}
*outAllowed = 0;
if (path == NULL) {
return -1;
}
//TODO: implement this for real.
recursive = false;
*outAllowed = PERMSET_ALL;
return 0;
}
int
countPermissionConflicts(PermissionRequestList *requests, bool updateAllowed)
{
if (!gPermissionState.permissionStateInitialized) {
return -2;
}
if (requests == NULL || requests->requests == NULL ||
requests->numRequests < 0 ||
requests->requestsAllocated < requests->numRequests)
{
return -1;
}
int conflicts = 0;
int i;
for (i = 0; i < requests->numRequests; i++) {
PermissionRequest *req;
unsigned int allowed;
int ret;
req = &requests->requests[i];
ret = getAllowedPermissions(req->path, req->recursive, &allowed);
if (ret < 0) {
return ret;
}
if ((req->requested & ~allowed) != 0) {
conflicts++;
}
if (updateAllowed) {
req->allowed = allowed;
}
}
return conflicts;
}
int
registerPermissionSet(int count, Permission *set)
{
if (!gPermissionState.permissionStateInitialized) {
return -2;
}
if (count < 0 || (count > 0 && set == NULL)) {
return -1;
}
if (count == 0) {
return 0;
}
if (gPermissionState.numPermissionEntries + count >=
gPermissionState.allocatedPermissionEntries)
{
Permission *newList;
int newSize;
newSize = (gPermissionState.allocatedPermissionEntries + count) * 2;
if (newSize < 16) {
newSize = 16;
}
newList = (Permission *)realloc(gPermissionState.permissions,
newSize * sizeof(Permission));
if (newList == NULL) {
return -3;
}
gPermissionState.permissions = newList;
gPermissionState.allocatedPermissionEntries = newSize;
}
Permission *p = &gPermissionState.permissions[
gPermissionState.numPermissionEntries];
int i;
for (i = 0; i < count; i++) {
*p = set[i];
//TODO: cache the strlen of the path
//TODO: normalize; strip off trailing /
p->path = strdup(p->path);
if (p->path == NULL) {
/* If we can't add all of the entries, we don't
* add any of them.
*/
Permission *pp = &gPermissionState.permissions[
gPermissionState.numPermissionEntries];
while (pp != p) {
free((void *)pp->path);
pp++;
}
return -4;
}
p++;
}
gPermissionState.numPermissionEntries += count;
return 0;
}
/*
* Copyright (C) 2007 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 AMEND_PERMISSIONS_H_
#define AMEND_PERMISSIONS_H_
#include <stdbool.h>
#define PERM_NONE (0)
#define PERM_STAT (1<<0)
#define PERM_READ (1<<1)
#define PERM_WRITE (1<<2) // including create, delete, mkdir, rmdir
#define PERM_CHMOD (1<<3)
#define PERM_CHOWN (1<<4)
#define PERM_CHGRP (1<<5)
#define PERM_SETUID (1<<6)
#define PERM_SETGID (1<<7)
#define PERMSET_READ (PERM_STAT | PERM_READ)
#define PERMSET_WRITE (PERMSET_READ | PERM_WRITE)
#define PERMSET_ALL \
(PERM_STAT | PERM_READ | PERM_WRITE | PERM_CHMOD | \
PERM_CHOWN | PERM_CHGRP | PERM_SETUID | PERM_SETGID)
typedef struct {
unsigned int requested;
unsigned int allowed;
const char *path;
bool recursive;
} PermissionRequest;
typedef struct {
PermissionRequest *requests;
int numRequests;
int requestsAllocated;
} PermissionRequestList;
/* Properly clear out a PermissionRequestList.
*
* @return 0 if list is non-NULL, negative otherwise.
*/
int initPermissionRequestList(PermissionRequestList *list);
/* Add a permission request to the list, allocating more space
* if necessary.
*
* @return 0 on success or a negative value on failure.
*/
int addPermissionRequestToList(PermissionRequestList *list,
const char *path, bool recursive, unsigned int permissions);
/* Free anything allocated by addPermissionRequestToList(). The caller
* is responsible for freeing the actual PermissionRequestList.
*/
void freePermissionRequestListElements(PermissionRequestList *list);
/*
* Global permission table
*/
typedef struct {
const char *path;
unsigned int allowed;
} Permission;
int permissionInit(void);
void permissionCleanup(void);
/* Returns the allowed permissions for the path in "outAllowed".
* Returns 0 if successful, negative if a parameter or global state
* is bad.
*/
int getAllowedPermissions(const char *path, bool recursive,
unsigned int *outAllowed);
/* More-recently-registered permissions override older permissions.
*/
int registerPermissionSet(int count, Permission *set);
/* Check to make sure that each request is allowed.
*
* @param requests The list of permission requests
* @param updateAllowed If true, update the "allowed" field in each
* element of the list
* @return the number of requests that were denied, or negative if
* an error occurred.
*/
int countPermissionConflicts(PermissionRequestList *requests,
bool updateAllowed);
/* Inspection/testing/debugging functions
*/
int getPermissionCount(void);
const Permission *getPermissionAt(int index);
#endif // AMEND_PERMISSIONS_H_
/*
* Copyright (C) 2007 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#undef NDEBUG
#include <assert.h>
#include "commands.h"
#include "register.h"
#define UNUSED(p) ((void)(p))
#define CHECK_BOOL() \
do { \
assert(argv == NULL); \
if (argv != NULL) return -1; \
assert(argc == true || argc == false); \
if (argc != true && argc != false) return -1; \
} while (false)
#define CHECK_WORDS() \
do { \
assert(argc >= 0); \
if (argc < 0) return -1; \
assert(argc == 0 || argv != NULL); \
if (argc != 0 && argv == NULL) return -1; \
if (permissions != NULL) { \
int CW_I_; \
for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \
assert(argv[CW_I_] != NULL); \
if (argv[CW_I_] == NULL) return -1; \
} \
} \
} while (false)
#define CHECK_FN() \
do { \
CHECK_WORDS(); \
if (permissions != NULL) { \
assert(result == NULL); \
if (result != NULL) return -1; \
} else { \
assert(result != NULL); \
if (result == NULL) return -1; \
} \
} while (false)
#define NO_PERMS(perms) \
do { \
PermissionRequestList *NP_PRL_ = (perms); \
if (NP_PRL_ != NULL) { \
int NP_RET_ = addPermissionRequestToList(NP_PRL_, \
"", false, PERM_NONE); \
if (NP_RET_ < 0) { \
/* Returns from the calling function. \
*/ \
return NP_RET_; \
} \
} \
} while (false)
/*
* Command definitions
*/
/* assert <boolexpr>
*/
static int
cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_BOOL();
NO_PERMS(permissions);
/* If our argument is false, return non-zero (failure)
* If our argument is true, return zero (success)
*/
if (argc) {
return 0;
} else {
return 1;
}
}
/* format <root>
*/
static int
cmd_format(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx
return -1;
}
/* copy_dir <srcdir> <dstdir>
*/
static int
cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx
return -1;
}
/* mark <resource> dirty|clean
*/
static int
cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx when marking, save the top-level hash at the mark point
// so we can retry on failure. Otherwise the hashes won't match,
// or someone could intentionally dirty the FS to force a downgrade
//xxx
return -1;
}
/* done
*/
static int
cmd_done(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx
return -1;
}
int
registerUpdateCommands()
{
int ret;
ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, NULL);
if (ret < 0) return ret;
ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL);
if (ret < 0) return ret;
ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL);
if (ret < 0) return ret;
ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, NULL);
if (ret < 0) return ret;
ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, NULL);
if (ret < 0) return ret;
//xxx some way to fix permissions
//xxx could have "installperms" commands that build the fs_config list
//xxx along with a "commitperms", and any copy_dir etc. needs to see
// a commitperms before it will work
return 0;
}
/*
* Function definitions
*/
/* update_forced()
*
* Returns "true" if some system setting has determined that
* the update should happen no matter what.
*/
static int
fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
NO_PERMS(permissions);
if (argc != 0) {
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
name, argc);
return 1;
}
//xxx check some global or property
bool force = true;
if (force) {
*result = strdup("true");
} else {
*result = strdup("");
}
if (resultLen != NULL) {
*resultLen = strlen(*result);
}
return 0;
}
/* get_mark(<resource>)
*
* Returns the current mark associated with the provided resource.
*/
static int
fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
NO_PERMS(permissions);
if (argc != 1) {
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
name, argc);
return 1;
}
//xxx look up the value
*result = strdup("");
if (resultLen != NULL) {
*resultLen = strlen(*result);
}
return 0;
}
/* hash_dir(<path-to-directory>)
*/
static int
fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
int ret = -1;
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
const char *dir;
if (argc != 1) {
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
name, argc);
return 1;
} else {
dir = argv[0];
}
if (permissions != NULL) {
if (dir == NULL) {
/* The argument is the result of another function.
* Assume the worst case, where the function returns
* the root.
*/
dir = "/";
}
ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
} else {
//xxx build and return the string
*result = strdup("hashvalue");
if (resultLen != NULL) {
*resultLen = strlen(*result);
}
ret = 0;
}
return ret;
}
/* matches(<str>, <str1> [, <strN>...])
* If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
* otherwise returns "".
*
* E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
*/
static int
fn_matches(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
NO_PERMS(permissions);
if (argc < 2) {
fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
name, argc);
return 1;
}
int i;
for (i = 1; i < argc; i++) {
if (strcmp(argv[0], argv[i]) == 0) {
*result = strdup(argv[0]);
if (resultLen != NULL) {
*resultLen = strlen(*result);
}
return 0;
}
}
*result = strdup("");
if (resultLen != NULL) {
*resultLen = 1;
}
return 0;
}
/* concat(<str>, <str1> [, <strN>...])
* Returns the concatenation of all strings.
*/
static int
fn_concat(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
NO_PERMS(permissions);
size_t totalLen = 0;
int i;
for (i = 0; i < argc; i++) {
totalLen += strlen(argv[i]);
}
char *s = (char *)malloc(totalLen + 1);
if (s == NULL) {
return -1;
}
s[totalLen] = '\0';
for (i = 0; i < argc; i++) {
//TODO: keep track of the end to avoid walking the string each time
strcat(s, argv[i]);
}
*result = s;
if (resultLen != NULL) {
*resultLen = strlen(s);
}
return 0;
}
int
registerUpdateFunctions()
{
int ret;
ret = registerFunction("update_forced", fn_update_forced, NULL);
if (ret < 0) return ret;
ret = registerFunction("get_mark", fn_get_mark, NULL);
if (ret < 0) return ret;
ret = registerFunction("hash_dir", fn_hash_dir, NULL);
if (ret < 0) return ret;
ret = registerFunction("matches", fn_matches, NULL);
if (ret < 0) return ret;
ret = registerFunction("concat", fn_concat, NULL);
if (ret < 0) return ret;
return 0;
}
/*
* Copyright (C) 2007 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 AMEND_REGISTER_H_
#define AMEND_REGISTER_H_
int registerUpdateCommands(void);
int registerUpdateFunctions(void);
#endif // AMEND_REGISTER_H_
/*
* Copyright (C) 2007 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 <stdlib.h>
#include <string.h>
#include "symtab.h"
#define DEFAULT_TABLE_SIZE 16
typedef struct {
char *symbol;
const void *cookie;
unsigned int flags;
} SymbolTableEntry;
struct SymbolTable {
SymbolTableEntry *table;
int numEntries;
int maxSize;
};
SymbolTable *
createSymbolTable()
{
SymbolTable *tab;
tab = (SymbolTable *)malloc(sizeof(SymbolTable));
if (tab != NULL) {
tab->numEntries = 0;
tab->maxSize = DEFAULT_TABLE_SIZE;
tab->table = (SymbolTableEntry *)malloc(
tab->maxSize * sizeof(SymbolTableEntry));
if (tab->table == NULL) {
free(tab);
tab = NULL;
}
}
return tab;
}
void
deleteSymbolTable(SymbolTable *tab)
{
if (tab != NULL) {
while (tab->numEntries > 0) {
free(tab->table[--tab->numEntries].symbol);
}
free(tab->table);
}
}
void *
findInSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags)
{
int i;
if (tab == NULL || symbol == NULL) {
return NULL;
}
// TODO: Sort the table and binary search
for (i = 0; i < tab->numEntries; i++) {
if (strcmp(tab->table[i].symbol, symbol) == 0 &&
tab->table[i].flags == flags)
{
return (void *)tab->table[i].cookie;
}
}
return NULL;
}
int
addToSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags,
const void *cookie)
{
if (tab == NULL || symbol == NULL || cookie == NULL) {
return -1;
}
/* Make sure that this symbol isn't already in the table.
*/
if (findInSymbolTable(tab, symbol, flags) != NULL) {
return -2;
}
/* Make sure there's enough space for the new entry.
*/
if (tab->numEntries == tab->maxSize) {
SymbolTableEntry *newTable;
int newSize;
newSize = tab->numEntries * 2;
if (newSize < DEFAULT_TABLE_SIZE) {
newSize = DEFAULT_TABLE_SIZE;
}
newTable = (SymbolTableEntry *)realloc(tab->table,
newSize * sizeof(SymbolTableEntry));
if (newTable == NULL) {
return -1;
}
tab->maxSize = newSize;
tab->table = newTable;
}
/* Insert the new entry.
*/
symbol = strdup(symbol);
if (symbol == NULL) {
return -1;
}
// TODO: Sort the table
tab->table[tab->numEntries].symbol = (char *)symbol;
tab->table[tab->numEntries].cookie = cookie;
tab->table[tab->numEntries].flags = flags;
tab->numEntries++;
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment