remove amend
Yank all the code to install OTA packages out of the recovery binary itself. Now packages are installed by a binary included in the package (run as a child of recovery), so we can make improvements in the installation process without waiting for a new release to use them.
This commit is contained in:
		| @@ -9,7 +9,6 @@ ifeq ($(TARGET_ARCH),arm) | ||||
| LOCAL_SRC_FILES := \ | ||||
| 	recovery.c \ | ||||
| 	bootloader.c \ | ||||
| 	commands.c \ | ||||
| 	firmware.c \ | ||||
| 	install.c \ | ||||
| 	roots.c \ | ||||
| @@ -32,14 +31,13 @@ LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) | ||||
|  | ||||
| LOCAL_MODULE_TAGS := eng | ||||
|  | ||||
| LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt | ||||
| LOCAL_STATIC_LIBRARIES := libminzip libunz libmtdutils libmincrypt | ||||
| LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils | ||||
| LOCAL_STATIC_LIBRARIES += libstdc++ libc | ||||
|  | ||||
| include $(BUILD_EXECUTABLE) | ||||
|  | ||||
| include $(commands_recovery_local_path)/minui/Android.mk | ||||
| include $(commands_recovery_local_path)/amend/Android.mk | ||||
| include $(commands_recovery_local_path)/minzip/Android.mk | ||||
| include $(commands_recovery_local_path)/mtdutils/Android.mk | ||||
| include $(commands_recovery_local_path)/tools/Android.mk | ||||
|   | ||||
| @@ -1,51 +0,0 @@ | ||||
| # 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 \ | ||||
| 	execute.c | ||||
|  | ||||
| amend_test_files := \ | ||||
| 	test_symtab.c \ | ||||
| 	test_commands.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) | ||||
| @@ -1,33 +0,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. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include "amend.h" | ||||
| #include "lexer.h" | ||||
| #include "parser.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; | ||||
| } | ||||
| @@ -1,25 +0,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_H_ | ||||
| #define AMEND_H_ | ||||
|  | ||||
| #include "ast.h" | ||||
| #include "execute.h" | ||||
|  | ||||
| const AmCommandList *parseAmendScript(const char *buf, size_t bufLen); | ||||
|  | ||||
| #endif  // AMEND_H_ | ||||
							
								
								
									
										198
									
								
								amend/ast.c
									
									
									
									
									
								
							
							
						
						
									
										198
									
								
								amend/ast.c
									
									
									
									
									
								
							| @@ -1,198 +0,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. | ||||
|  */ | ||||
|  | ||||
| #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]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										165
									
								
								amend/ast.h
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								amend/ast.h
									
									
									
									
									
								
							| @@ -1,165 +0,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_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_ | ||||
							
								
								
									
										229
									
								
								amend/commands.c
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								amend/commands.c
									
									
									
									
									
								
							| @@ -1,229 +0,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. | ||||
|  */ | ||||
|  | ||||
| #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[]) | ||||
| { | ||||
|     if (entry != NULL && entry->argType == CMD_ARGS_WORDS && | ||||
|             (argc == 0 || (argc > 0 && argv != 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); | ||||
|     } | ||||
| bail: | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int | ||||
| callBooleanCommandInternal(CommandEntry *entry, bool arg) | ||||
| { | ||||
|     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); | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| int | ||||
| callCommand(Command *cmd, int argc, const char *argv[]) | ||||
| { | ||||
|     return callCommandInternal((CommandEntry *)cmd, argc, argv); | ||||
| } | ||||
|  | ||||
| int | ||||
| callBooleanCommand(Command *cmd, bool arg) | ||||
| { | ||||
|     return callBooleanCommandInternal((CommandEntry *)cmd, arg); | ||||
| } | ||||
|  | ||||
| int | ||||
| callFunctionInternal(CommandEntry *entry, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen) | ||||
| { | ||||
|     if (entry != NULL && entry->argType == CMD_ARGS_WORDS && | ||||
|             (argc == 0 || (argc > 0 && argv != NULL))) | ||||
|     { | ||||
|         if (result != 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); | ||||
|         } | ||||
|     } | ||||
| 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); | ||||
| } | ||||
| @@ -1,74 +0,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. | ||||
|  */ | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #ifndef AMEND_COMMANDS_H_ | ||||
| #define AMEND_COMMANDS_H_ | ||||
|  | ||||
| /* Invoke a command. | ||||
|  * | ||||
|  * 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[]); | ||||
|  | ||||
| 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); | ||||
|  | ||||
| /* | ||||
|  * Function management | ||||
|  */ | ||||
|  | ||||
| typedef int (*FunctionHook)(const char *name, void *cookie, | ||||
|                                 int argc, const char *argv[], | ||||
|                                 char **result, size_t *resultLen); | ||||
|  | ||||
| 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); | ||||
|  | ||||
| #endif  // AMEND_COMMANDS_H_ | ||||
							
								
								
									
										315
									
								
								amend/execute.c
									
									
									
									
									
								
							
							
						
						
									
										315
									
								
								amend/execute.c
									
									
									
									
									
								
							| @@ -1,315 +0,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. | ||||
|  */ | ||||
|  | ||||
| #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; | ||||
| } | ||||
| @@ -1,25 +0,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_ | ||||
| @@ -1,43 +0,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_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_ | ||||
							
								
								
									
										299
									
								
								amend/lexer.l
									
									
									
									
									
								
							
							
						
						
									
										299
									
								
								amend/lexer.l
									
									
									
									
									
								
							| @@ -1,299 +0,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. | ||||
|  */ | ||||
|  | ||||
| %{ | ||||
|     #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; | ||||
| } | ||||
							
								
								
									
										189
									
								
								amend/main.c
									
									
									
									
									
								
							
							
						
						
									
										189
									
								
								amend/main.c
									
									
									
									
									
								
							| @@ -1,189 +0,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. | ||||
|  */ | ||||
|  | ||||
| #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); | ||||
|     } | ||||
| #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; | ||||
| } | ||||
| @@ -1,24 +0,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_ | ||||
							
								
								
									
										430
									
								
								amend/parser_y.y
									
									
									
									
									
								
							
							
						
						
									
										430
									
								
								amend/parser_y.y
									
									
									
									
									
								
							| @@ -1,430 +0,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. | ||||
|  */ | ||||
|  | ||||
| %{ | ||||
| #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 */ | ||||
							
								
								
									
										338
									
								
								amend/register.c
									
									
									
									
									
								
							
							
						
						
									
										338
									
								
								amend/register.c
									
									
									
									
									
								
							| @@ -1,338 +0,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. | ||||
|  */ | ||||
|  | ||||
| #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; \ | ||||
|     } while (false) | ||||
|  | ||||
| #define CHECK_FN() \ | ||||
|     do { \ | ||||
|         CHECK_WORDS(); \ | ||||
|         assert(result != NULL);            \ | ||||
|         if (result == NULL) return -1;     \ | ||||
|     } while (false) | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Command definitions | ||||
|  */ | ||||
|  | ||||
| /* assert <boolexpr> | ||||
|  */ | ||||
| static int | ||||
| cmd_assert(const char *name, void *cookie, int argc, const char *argv[]) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_BOOL(); | ||||
|  | ||||
|     /* 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[]) | ||||
| { | ||||
|     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[]) | ||||
| { | ||||
|     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[]) | ||||
| { | ||||
|     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[]) | ||||
| { | ||||
|     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; | ||||
|  | ||||
|     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) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|  | ||||
|     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) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|  | ||||
|     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) | ||||
| { | ||||
|     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]; | ||||
|     } | ||||
|  | ||||
| //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) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|  | ||||
|     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) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|  | ||||
|     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; | ||||
| } | ||||
| @@ -1,23 +0,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_ | ||||
							
								
								
									
										132
									
								
								amend/symtab.c
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								amend/symtab.c
									
									
									
									
									
								
							| @@ -1,132 +0,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. | ||||
|  */ | ||||
|  | ||||
| #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; | ||||
| } | ||||
| @@ -1,34 +0,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_SYMTAB_H_ | ||||
| #define AMEND_SYMTAB_H_ | ||||
|  | ||||
| typedef struct SymbolTable SymbolTable; | ||||
|  | ||||
| SymbolTable *createSymbolTable(void); | ||||
|  | ||||
| void deleteSymbolTable(SymbolTable *tab); | ||||
|  | ||||
| /* symbol and cookie must be non-NULL. | ||||
|  */ | ||||
| int addToSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags, | ||||
|         const void *cookie); | ||||
|  | ||||
| void *findInSymbolTable(SymbolTable *tab, const char *symbol, | ||||
|         unsigned int flags); | ||||
|  | ||||
| #endif  // AMEND_SYMTAB_H_ | ||||
| @@ -1,465 +0,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. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #undef NDEBUG | ||||
| #include <assert.h> | ||||
| #include "commands.h" | ||||
|  | ||||
| static struct { | ||||
|     bool called; | ||||
|     const char *name; | ||||
|     void *cookie; | ||||
|     int argc; | ||||
|     const char **argv; | ||||
|     int returnValue; | ||||
|     char *functionResult; | ||||
| } gTestCommandState; | ||||
|  | ||||
| static int | ||||
| testCommand(const char *name, void *cookie, int argc, const char *argv[]) | ||||
| { | ||||
|     gTestCommandState.called = true; | ||||
|     gTestCommandState.name = name; | ||||
|     gTestCommandState.cookie = cookie; | ||||
|     gTestCommandState.argc = argc; | ||||
|     gTestCommandState.argv = argv; | ||||
|     return gTestCommandState.returnValue; | ||||
| } | ||||
|  | ||||
| static int | ||||
| testFunction(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen) | ||||
| { | ||||
|     gTestCommandState.called = true; | ||||
|     gTestCommandState.name = name; | ||||
|     gTestCommandState.cookie = cookie; | ||||
|     gTestCommandState.argc = argc; | ||||
|     gTestCommandState.argv = argv; | ||||
|     if (result != NULL) { | ||||
|         *result = gTestCommandState.functionResult; | ||||
|         if (resultLen != NULL) { | ||||
|             *resultLen = strlen(*result); | ||||
|         } | ||||
|     } | ||||
|     return gTestCommandState.returnValue; | ||||
| } | ||||
|  | ||||
| static int | ||||
| test_commands() | ||||
| { | ||||
|     Command *cmd; | ||||
|     int ret; | ||||
|     CommandArgumentType argType; | ||||
|  | ||||
|     ret = commandInit(); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Make sure we can't initialize twice. | ||||
|      */ | ||||
|     ret = commandInit(); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Try calling with some bad values. | ||||
|      */ | ||||
|     ret = registerCommand(NULL, CMD_ARGS_UNKNOWN, NULL, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = registerCommand("hello", CMD_ARGS_UNKNOWN, NULL, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = registerCommand("hello", CMD_ARGS_WORDS, NULL, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     cmd = findCommand(NULL); | ||||
|     assert(cmd == NULL); | ||||
|  | ||||
|     argType = getCommandArgumentType(NULL); | ||||
|     assert((int)argType < 0); | ||||
|  | ||||
|     ret = callCommand(NULL, -1, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = callBooleanCommand(NULL, false); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Register some commands. | ||||
|      */ | ||||
|     ret = registerCommand("one", CMD_ARGS_WORDS, testCommand, | ||||
|             &gTestCommandState); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = registerCommand("two", CMD_ARGS_WORDS, testCommand, | ||||
|             &gTestCommandState); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = registerCommand("bool", CMD_ARGS_BOOLEAN, testCommand, | ||||
|             &gTestCommandState); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Make sure that all of those commands exist and that their | ||||
|      * argument types are correct. | ||||
|      */ | ||||
|     cmd = findCommand("one"); | ||||
|     assert(cmd != NULL); | ||||
|     argType = getCommandArgumentType(cmd); | ||||
|     assert(argType == CMD_ARGS_WORDS); | ||||
|  | ||||
|     cmd = findCommand("two"); | ||||
|     assert(cmd != NULL); | ||||
|     argType = getCommandArgumentType(cmd); | ||||
|     assert(argType == CMD_ARGS_WORDS); | ||||
|  | ||||
|     cmd = findCommand("bool"); | ||||
|     assert(cmd != NULL); | ||||
|     argType = getCommandArgumentType(cmd); | ||||
|     assert(argType == CMD_ARGS_BOOLEAN); | ||||
|  | ||||
|     /* Make sure that no similar commands exist. | ||||
|      */ | ||||
|     cmd = findCommand("on"); | ||||
|     assert(cmd == NULL); | ||||
|  | ||||
|     cmd = findCommand("onee"); | ||||
|     assert(cmd == NULL); | ||||
|  | ||||
|     /* Make sure that a double insertion fails. | ||||
|      */ | ||||
|     ret = registerCommand("one", CMD_ARGS_WORDS, testCommand, | ||||
|             &gTestCommandState); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Make sure that bad args fail. | ||||
|      */ | ||||
|     cmd = findCommand("one"); | ||||
|     assert(cmd != NULL); | ||||
|  | ||||
|     ret = callCommand(cmd, -1, NULL);   // argc must be non-negative | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = callCommand(cmd, 1, NULL);    // argv can't be NULL if argc > 0 | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Make sure that you can't make a boolean call on a regular command. | ||||
|      */ | ||||
|     cmd = findCommand("one"); | ||||
|     assert(cmd != NULL); | ||||
|  | ||||
|     ret = callBooleanCommand(cmd, false); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Make sure that you can't make a regular call on a boolean command. | ||||
|      */ | ||||
|     cmd = findCommand("bool"); | ||||
|     assert(cmd != NULL); | ||||
|  | ||||
|     ret = callCommand(cmd, 0, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Set up some arguments. | ||||
|      */ | ||||
|     int argc = 4; | ||||
|     const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" }; | ||||
|  | ||||
|     /* Make a call and make sure that it occurred. | ||||
|      */ | ||||
|     cmd = findCommand("one"); | ||||
|     assert(cmd != NULL); | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 25; | ||||
|     ret = callCommand(cmd, argc, argv); | ||||
| //xxx also try calling with a null argv element (should fail) | ||||
|     assert(ret == 25); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "one") == 0); | ||||
|     assert(gTestCommandState.cookie == &gTestCommandState); | ||||
|     assert(gTestCommandState.argc == argc); | ||||
|     assert(gTestCommandState.argv == argv); | ||||
|  | ||||
|     /* Make a boolean call and make sure that it occurred. | ||||
|      */ | ||||
|     cmd = findCommand("bool"); | ||||
|     assert(cmd != NULL); | ||||
|  | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 12; | ||||
|     ret = callBooleanCommand(cmd, false); | ||||
|     assert(ret == 12); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "bool") == 0); | ||||
|     assert(gTestCommandState.cookie == &gTestCommandState); | ||||
|     assert(gTestCommandState.argc == 0); | ||||
|     assert(gTestCommandState.argv == NULL); | ||||
|  | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 13; | ||||
|     ret = callBooleanCommand(cmd, true); | ||||
|     assert(ret == 13); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "bool") == 0); | ||||
|     assert(gTestCommandState.cookie == &gTestCommandState); | ||||
|     assert(gTestCommandState.argc == 1); | ||||
|     assert(gTestCommandState.argv == NULL); | ||||
|  | ||||
|     /* Smoke test commandCleanup(). | ||||
|      */ | ||||
|     commandCleanup(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| test_functions() | ||||
| { | ||||
|     Function *fn; | ||||
|     int ret; | ||||
|  | ||||
|     ret = commandInit(); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Try calling with some bad values. | ||||
|      */ | ||||
|     ret = registerFunction(NULL, NULL, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = registerFunction("hello", NULL, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     fn = findFunction(NULL); | ||||
|     assert(fn == NULL); | ||||
|  | ||||
|     ret = callFunction(NULL, -1, NULL, NULL, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Register some functions. | ||||
|      */ | ||||
|     ret = registerFunction("one", testFunction, &gTestCommandState); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = registerFunction("two", testFunction, &gTestCommandState); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = registerFunction("three", testFunction, &gTestCommandState); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Make sure that all of those functions exist. | ||||
|      * argument types are correct. | ||||
|      */ | ||||
|     fn = findFunction("one"); | ||||
|     assert(fn != NULL); | ||||
|  | ||||
|     fn = findFunction("two"); | ||||
|     assert(fn != NULL); | ||||
|  | ||||
|     fn = findFunction("three"); | ||||
|     assert(fn != NULL); | ||||
|  | ||||
|     /* Make sure that no similar functions exist. | ||||
|      */ | ||||
|     fn = findFunction("on"); | ||||
|     assert(fn == NULL); | ||||
|  | ||||
|     fn = findFunction("onee"); | ||||
|     assert(fn == NULL); | ||||
|  | ||||
|     /* Make sure that a double insertion fails. | ||||
|      */ | ||||
|     ret = registerFunction("one", testFunction, &gTestCommandState); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Make sure that bad args fail. | ||||
|      */ | ||||
|     fn = findFunction("one"); | ||||
|     assert(fn != NULL); | ||||
|  | ||||
|     // argc must be non-negative | ||||
|     ret = callFunction(fn, -1, NULL, (char **)1, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     // argv can't be NULL if argc > 0 | ||||
|     ret = callFunction(fn, 1, NULL, (char **)1, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     // result can't be NULL | ||||
|     ret = callFunction(fn, 0, NULL, NULL, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Set up some arguments. | ||||
|      */ | ||||
|     int argc = 4; | ||||
|     const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" }; | ||||
|  | ||||
|     /* Make a call and make sure that it occurred. | ||||
|      */ | ||||
|     char *functionResult; | ||||
|     size_t functionResultLen; | ||||
|     fn = findFunction("one"); | ||||
|     assert(fn != NULL); | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 25; | ||||
|     gTestCommandState.functionResult = "1234"; | ||||
|     functionResult = NULL; | ||||
|     functionResultLen = 55; | ||||
|     ret = callFunction(fn, argc, argv, | ||||
|             &functionResult, &functionResultLen); | ||||
| //xxx also try calling with a null resultLen arg (should succeed) | ||||
| //xxx also try calling with a null argv element (should fail) | ||||
|     assert(ret == 25); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "one") == 0); | ||||
|     assert(gTestCommandState.cookie == &gTestCommandState); | ||||
|     assert(gTestCommandState.argc == argc); | ||||
|     assert(gTestCommandState.argv == argv); | ||||
|     assert(strcmp(functionResult, "1234") == 0); | ||||
|     assert(functionResultLen == strlen(functionResult)); | ||||
|  | ||||
|     /* Smoke test commandCleanup(). | ||||
|      */ | ||||
|     commandCleanup(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| test_interaction() | ||||
| { | ||||
|     Command *cmd; | ||||
|     Function *fn; | ||||
|     int ret; | ||||
|  | ||||
|     ret = commandInit(); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Register some commands. | ||||
|      */ | ||||
|     ret = registerCommand("one", CMD_ARGS_WORDS, testCommand, (void *)0xc1); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = registerCommand("two", CMD_ARGS_WORDS, testCommand, (void *)0xc2); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Register some functions, one of which shares a name with a command. | ||||
|      */ | ||||
|     ret = registerFunction("one", testFunction, (void *)0xf1); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = registerFunction("three", testFunction, (void *)0xf3); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Look up each of the commands, and make sure no command exists | ||||
|      * with the name used only by our function. | ||||
|      */ | ||||
|     cmd = findCommand("one"); | ||||
|     assert(cmd != NULL); | ||||
|  | ||||
|     cmd = findCommand("two"); | ||||
|     assert(cmd != NULL); | ||||
|  | ||||
|     cmd = findCommand("three"); | ||||
|     assert(cmd == NULL); | ||||
|  | ||||
|     /* Look up each of the functions, and make sure no function exists | ||||
|      * with the name used only by our command. | ||||
|      */ | ||||
|     fn = findFunction("one"); | ||||
|     assert(fn != NULL); | ||||
|  | ||||
|     fn = findFunction("two"); | ||||
|     assert(fn == NULL); | ||||
|  | ||||
|     fn = findFunction("three"); | ||||
|     assert(fn != NULL); | ||||
|  | ||||
|     /* Set up some arguments. | ||||
|      */ | ||||
|     int argc = 4; | ||||
|     const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" }; | ||||
|  | ||||
|     /* Call the overlapping command and make sure that the cookie is correct. | ||||
|      */ | ||||
|     cmd = findCommand("one"); | ||||
|     assert(cmd != NULL); | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 123; | ||||
|     ret = callCommand(cmd, argc, argv); | ||||
|     assert(ret == 123); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "one") == 0); | ||||
|     assert((int)gTestCommandState.cookie == 0xc1); | ||||
|     assert(gTestCommandState.argc == argc); | ||||
|     assert(gTestCommandState.argv == argv); | ||||
|  | ||||
|     /* Call the overlapping function and make sure that the cookie is correct. | ||||
|      */ | ||||
|     char *functionResult; | ||||
|     size_t functionResultLen; | ||||
|     fn = findFunction("one"); | ||||
|     assert(fn != NULL); | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 125; | ||||
|     gTestCommandState.functionResult = "5678"; | ||||
|     functionResult = NULL; | ||||
|     functionResultLen = 66; | ||||
|     ret = callFunction(fn, argc, argv, &functionResult, &functionResultLen); | ||||
|     assert(ret == 125); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "one") == 0); | ||||
|     assert((int)gTestCommandState.cookie == 0xf1); | ||||
|     assert(gTestCommandState.argc == argc); | ||||
|     assert(gTestCommandState.argv == argv); | ||||
|     assert(strcmp(functionResult, "5678") == 0); | ||||
|     assert(functionResultLen == strlen(functionResult)); | ||||
|  | ||||
|     /* Clean up. | ||||
|      */ | ||||
|     commandCleanup(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| test_cmd_fn() | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = test_commands(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_commands() failed: %d\n", ret); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     ret = test_functions(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_functions() failed: %d\n", ret); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     ret = test_interaction(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_interaction() failed: %d\n", ret); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -1,146 +0,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. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #undef NDEBUG | ||||
| #include <assert.h> | ||||
| #include "symtab.h" | ||||
|  | ||||
| int | ||||
| test_symtab() | ||||
| { | ||||
|     SymbolTable *tab; | ||||
|     void *cookie; | ||||
|     int ret; | ||||
|  | ||||
|     /* Test creation */ | ||||
|     tab = createSymbolTable(); | ||||
|     assert(tab != NULL); | ||||
|  | ||||
|     /* Smoke-test deletion */ | ||||
|     deleteSymbolTable(tab); | ||||
|  | ||||
|  | ||||
|     tab = createSymbolTable(); | ||||
|     assert(tab != NULL); | ||||
|  | ||||
|  | ||||
|     /* table parameter must be non-NULL. */ | ||||
|     ret = addToSymbolTable(NULL, NULL, 0, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* symbol parameter must be non-NULL. */ | ||||
|     ret = addToSymbolTable(tab, NULL, 0, NULL); | ||||
|     assert(ret < 0); | ||||
|      | ||||
|     /* cookie parameter must be non-NULL. */ | ||||
|     ret = addToSymbolTable(tab, "null", 0, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|  | ||||
|     /* table parameter must be non-NULL. */ | ||||
|     cookie = findInSymbolTable(NULL, NULL, 0); | ||||
|     assert(cookie == NULL); | ||||
|  | ||||
|     /* symbol parameter must be non-NULL. */ | ||||
|     cookie = findInSymbolTable(tab, NULL, 0); | ||||
|     assert(cookie == NULL); | ||||
|  | ||||
|  | ||||
|     /* Try some actual inserts. | ||||
|      */ | ||||
|     ret = addToSymbolTable(tab, "one", 0, (void *)1); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = addToSymbolTable(tab, "two", 0, (void *)2); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = addToSymbolTable(tab, "three", 0, (void *)3); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Try some lookups. | ||||
|      */ | ||||
|     cookie = findInSymbolTable(tab, "one", 0); | ||||
|     assert((int)cookie == 1); | ||||
|  | ||||
|     cookie = findInSymbolTable(tab, "two", 0); | ||||
|     assert((int)cookie == 2); | ||||
|  | ||||
|     cookie = findInSymbolTable(tab, "three", 0); | ||||
|     assert((int)cookie == 3); | ||||
|  | ||||
|     /* Try to insert something that's already there. | ||||
|      */ | ||||
|     ret = addToSymbolTable(tab, "one", 0, (void *)1111); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Make sure that the failed duplicate insert didn't | ||||
|      * clobber the original cookie value. | ||||
|      */ | ||||
|     cookie = findInSymbolTable(tab, "one", 0); | ||||
|     assert((int)cookie == 1); | ||||
|  | ||||
|     /* Try looking up something that isn't there. | ||||
|      */ | ||||
|     cookie = findInSymbolTable(tab, "FOUR", 0); | ||||
|     assert(cookie == NULL); | ||||
|  | ||||
|     /* Try looking up something that's similar to an existing entry. | ||||
|      */ | ||||
|     cookie = findInSymbolTable(tab, "on", 0); | ||||
|     assert(cookie == NULL); | ||||
|  | ||||
|     cookie = findInSymbolTable(tab, "onee", 0); | ||||
|     assert(cookie == NULL); | ||||
|  | ||||
|     /* Test flags. | ||||
|      * Try inserting something with a different flag. | ||||
|      */ | ||||
|     ret = addToSymbolTable(tab, "ten", 333, (void *)10); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Make sure it's there. | ||||
|      */ | ||||
|     cookie = findInSymbolTable(tab, "ten", 333); | ||||
|     assert((int)cookie == 10); | ||||
|  | ||||
|     /* Make sure it's not there when looked up with a different flag. | ||||
|      */ | ||||
|     cookie = findInSymbolTable(tab, "ten", 0); | ||||
|     assert(cookie == NULL); | ||||
|  | ||||
|     /* Try inserting something that has the same name as something | ||||
|      * with a different flag. | ||||
|      */ | ||||
|     ret = addToSymbolTable(tab, "one", 333, (void *)11); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Make sure the new entry exists. | ||||
|      */ | ||||
|     cookie = findInSymbolTable(tab, "one", 333); | ||||
|     assert((int)cookie == 11); | ||||
|  | ||||
|     /* Make sure the old entry still has the right value. | ||||
|      */ | ||||
|     cookie = findInSymbolTable(tab, "one", 0); | ||||
|     assert((int)cookie == 1); | ||||
|  | ||||
|     /* Try deleting again, now that there's stuff in the table. | ||||
|      */ | ||||
|     deleteSymbolTable(tab); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -1 +0,0 @@ | ||||
| I am a jelly donut. | ||||
| @@ -1,2 +0,0 @@ | ||||
| This is a sample no-op test, which does at least serve to verify that the | ||||
| test harness is working. | ||||
| @@ -1,17 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| echo 'I am a jelly donut.' | ||||
| @@ -1 +0,0 @@ | ||||
|  EOF | ||||
| @@ -1 +0,0 @@ | ||||
| Test to make sure that an empty file is accepted properly. | ||||
| @@ -1,17 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| amend --debug-lex input | ||||
| @@ -1,13 +0,0 @@ | ||||
|  IDENTIFIER<this_identifier_is_not_assert> EOL | ||||
|  IDENTIFIER<NEITHER_IS_THIS_123> EOL | ||||
|  IDENTIFIER<but_the_next_one_is> EOL | ||||
|  IDENTIFIER<assert> EOL | ||||
|  IDENTIFIER<next_one_is_not_an_identifier> EOL | ||||
| line 6: unexpected character at '1' | ||||
|  EOF | ||||
| line 1: unexpected character at '"' | ||||
|  EOF | ||||
| line 1: unexpected character at '=' | ||||
|  EOF | ||||
| line 1: unexpected character at '9' | ||||
|  EOF | ||||
| @@ -1 +0,0 @@ | ||||
| Test to make sure that simple command names are tokenized properly. | ||||
| @@ -1,6 +0,0 @@ | ||||
| this_identifier_is_not_assert | ||||
| NEITHER_IS_THIS_123 | ||||
| but_the_next_one_is | ||||
| assert | ||||
| next_one_is_not_an_identifier | ||||
| 12not_an_identifier | ||||
| @@ -1 +0,0 @@ | ||||
| "quoted" | ||||
| @@ -1 +0,0 @@ | ||||
| == | ||||
| @@ -1 +0,0 @@ | ||||
| 99 | ||||
| @@ -1,20 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| amend --debug-lex input | ||||
| amend --debug-lex input2 | ||||
| amend --debug-lex input3 | ||||
| amend --debug-lex input4 | ||||
| @@ -1,5 +0,0 @@ | ||||
|  IDENTIFIER<comment_on_this_line> EOL | ||||
|  IDENTIFIER<none_on_this_one> EOL | ||||
|  EOL | ||||
|  EOL | ||||
|  EOF | ||||
| @@ -1 +0,0 @@ | ||||
| Test to make sure that comments are stripped out. | ||||
| @@ -1,4 +0,0 @@ | ||||
| comment_on_this_line # this is a "comment" (with / a bunch) # \\ of stuff \ | ||||
| none_on_this_one | ||||
| # beginning of line | ||||
|                          # preceded by whitespace | ||||
| @@ -1,17 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| amend --debug-lex input | ||||
| @@ -1,13 +0,0 @@ | ||||
|  IDENTIFIER<test> WORD<string> EOL | ||||
|  IDENTIFIER<test> WORD<string with spaces> EOL | ||||
|  IDENTIFIER<test> WORD<string with "escaped" quotes> EOL | ||||
|  IDENTIFIER<test> WORD<string with \escaped\ backslashes> EOL | ||||
|  IDENTIFIER<test> WORD<string with # a comment character> EOL | ||||
|  EOF | ||||
|  EOL | ||||
|  IDENTIFIER<test1>line 2: unterminated string at ' | ||||
| ' | ||||
|  ??? <0> | ||||
|  EOL | ||||
|  IDENTIFIER<test1>line 2: illegal escape at '\n' | ||||
|  ??? <0> | ||||
| @@ -1 +0,0 @@ | ||||
| Test to make sure that quoted strings are tokenized properly. | ||||
| @@ -1,5 +0,0 @@ | ||||
| test "string" | ||||
| test "string with spaces" | ||||
| test "string with \"escaped\" quotes" | ||||
| test "string with \\escaped\\ backslashes" | ||||
| test "string with # a comment character" | ||||
| @@ -1,2 +0,0 @@ | ||||
| # This should fail | ||||
| test1 "unterminated string | ||||
| @@ -1,2 +0,0 @@ | ||||
| # This should fail | ||||
| test1 "string with illegal escape \n in the middle" | ||||
| @@ -1,19 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| amend --debug-lex input | ||||
| amend --debug-lex input2 | ||||
| amend --debug-lex input3 | ||||
| @@ -1,6 +0,0 @@ | ||||
|  IDENTIFIER<test> WORD<this> WORD<has> WORD<a> WORD<bunch> WORD<of> WORD<BARE> WORD<ALPHA> WORD<WORDS> EOL | ||||
|  IDENTIFIER<test> WORD<12> WORD<this> WORD<has(some> WORD<)> WORD<ALPHANUMER1C> WORD<and> WORD<\\> WORD<whatever> WORD<characters> EOL | ||||
|  IDENTIFIER<test> WORD<this> WORD<has> WORD<mixed> WORD<bare> WORD<and quoted> WORD<words> EOL | ||||
|  IDENTIFIER<test> WORD<what> WORD<about> WORD<quotesin the middle?> EOL | ||||
|  IDENTIFIER<test> WORD<"""shouldn't> WORD<be> WORD<a> WORD<quoted> WORD<string> EOL | ||||
|  EOF | ||||
| @@ -1 +0,0 @@ | ||||
| Test to make sure that argument words are tokenized properly. | ||||
| @@ -1,5 +0,0 @@ | ||||
| test this has a bunch of BARE ALPHA WORDS | ||||
| test 12 this has(some ) ALPHANUMER1C and \\ whatever characters | ||||
| test this has mixed bare "and quoted" words | ||||
| test what about quotes"in the middle?" | ||||
| test \"\"\"shouldn't be a quoted string | ||||
| @@ -1,2 +0,0 @@ | ||||
| # This should fail | ||||
| test1 "unterminated string | ||||
| @@ -1,2 +0,0 @@ | ||||
| # This should fail | ||||
| test1 "string with illegal escape \n in the middle" | ||||
| @@ -1,17 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| amend --debug-lex input | ||||
| @@ -1,11 +0,0 @@ | ||||
|  IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<112345oldhashvalue1234123> EOL | ||||
|  IDENTIFIER<mark> WORD<SYS:> WORD<dirty> EOL | ||||
|  IDENTIFIER<copy_dir> WORD<PKG:android-files> WORD<SYS:> EOL | ||||
|  IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> EOL | ||||
|  IDENTIFIER<mark> WORD<SYS:> WORD<clean> EOL | ||||
|  IDENTIFIER<done> EOL | ||||
|  IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> , STRING<blah> ) == STRING<112345oldhashvalue1234123> EOL | ||||
|  IDENTIFIER<assert> STRING<true> == STRING<false> EOL | ||||
|  IDENTIFIER<assert> IDENTIFIER<one> ( STRING<abc> , IDENTIFIER<two> ( STRING<def> ) ) == STRING<five> EOL | ||||
|  IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> || IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> EOL | ||||
|  EOF | ||||
| @@ -1 +0,0 @@ | ||||
| An input script similar to one that will actually be used in practice. | ||||
| @@ -1,10 +0,0 @@ | ||||
| assert hash_dir("SYS:") == "112345oldhashvalue1234123" | ||||
| mark SYS: dirty | ||||
| copy_dir "PKG:android-files" SYS: | ||||
| assert hash_dir("SYS:") == "667890newhashvalue6678909" | ||||
| mark SYS: clean | ||||
| done | ||||
| assert hash_dir("SYS:", "blah") == "112345oldhashvalue1234123" | ||||
| assert "true" == "false" | ||||
| assert one("abc", two("def")) == "five" | ||||
| assert hash_dir("SYS:") == "667890newhashvalue6678909" || hash_dir("SYS:") == "667890newhashvalue6678909" | ||||
| @@ -1,17 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| amend --debug-lex input | ||||
| @@ -1,74 +0,0 @@ | ||||
| command "assert" { | ||||
|     STRING EQ { | ||||
|         FUNCTION hash_dir ( | ||||
|             "SYS:" | ||||
|         ) | ||||
|         "112345oldhashvalue1234123" | ||||
|     } | ||||
| } | ||||
| command "mark" { | ||||
|     "SYS:" | ||||
|     "dirty" | ||||
| } | ||||
| command "copy_dir" { | ||||
|     "PKG:android-files" | ||||
|     "SYS:" | ||||
| } | ||||
| command "assert" { | ||||
|     STRING EQ { | ||||
|         FUNCTION hash_dir ( | ||||
|             "SYS:" | ||||
|         ) | ||||
|         "667890newhashvalue6678909" | ||||
|     } | ||||
| } | ||||
| command "mark" { | ||||
|     "SYS:" | ||||
|     "clean" | ||||
| } | ||||
| command "done" { | ||||
| } | ||||
| command "assert" { | ||||
|     STRING EQ { | ||||
|         FUNCTION hash_dir ( | ||||
|             "SYS:" | ||||
|             "blah" | ||||
|         ) | ||||
|         "112345oldhashvalue1234123" | ||||
|     } | ||||
| } | ||||
| command "assert" { | ||||
|     STRING EQ { | ||||
|         "true" | ||||
|         "false" | ||||
|     } | ||||
| } | ||||
| command "assert" { | ||||
|     STRING NE { | ||||
|         FUNCTION matches ( | ||||
|             FUNCTION hash_dir ( | ||||
|                 "SYS:" | ||||
|             ) | ||||
|             "667890newhashvalue6678909" | ||||
|             "999999newhashvalue6678909" | ||||
|         ) | ||||
|         "" | ||||
|     } | ||||
| } | ||||
| command "assert" { | ||||
|     BOOLEAN OR { | ||||
|         STRING EQ { | ||||
|             FUNCTION hash_dir ( | ||||
|                 "SYS:" | ||||
|             ) | ||||
|             "667890newhashvalue6678909" | ||||
|         } | ||||
|         STRING EQ { | ||||
|             FUNCTION hash_dir ( | ||||
|                 "SYS:" | ||||
|             ) | ||||
|             "999999newhashvalue6678909" | ||||
|         } | ||||
|     } | ||||
| } | ||||
| amend: Parse successful. | ||||
| @@ -1 +0,0 @@ | ||||
| An input script similar to one that will actually be used in practice. | ||||
| @@ -1,10 +0,0 @@ | ||||
| assert hash_dir("SYS:") == "112345oldhashvalue1234123" | ||||
| mark SYS: dirty | ||||
| copy_dir "PKG:android-files" SYS: | ||||
| assert hash_dir("SYS:") == "667890newhashvalue6678909" | ||||
| mark SYS: clean | ||||
| done | ||||
| assert hash_dir("SYS:", "blah") == "112345oldhashvalue1234123" | ||||
| assert "true" == "false" | ||||
| assert matches(hash_dir("SYS:"), "667890newhashvalue6678909", "999999newhashvalue6678909") != "" | ||||
| assert hash_dir("SYS:") == "667890newhashvalue6678909" || hash_dir("SYS:") == "999999newhashvalue6678909" | ||||
| @@ -1,17 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| amend --debug-ast input | ||||
| @@ -1,150 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| # Set up prog to be the path of this script, including following symlinks, | ||||
| # and set up progdir to be the fully-qualified pathname of its directory. | ||||
| prog="$0" | ||||
| while [ -h "${prog}" ]; do | ||||
|     newProg=`/bin/ls -ld "${prog}"` | ||||
|     newProg=`expr "${newProg}" : ".* -> \(.*\)$"` | ||||
|     if expr "x${newProg}" : 'x/' >/dev/null; then | ||||
|         prog="${newProg}" | ||||
|     else | ||||
|         progdir=`dirname "${prog}"` | ||||
|         prog="${progdir}/${newProg}" | ||||
|     fi | ||||
| done | ||||
| oldwd=`pwd` | ||||
| progdir=`dirname "${prog}"` | ||||
| cd "${progdir}" | ||||
| progdir=`pwd` | ||||
| prog="${progdir}"/`basename "${prog}"` | ||||
|  | ||||
| info="info.txt" | ||||
| run="run" | ||||
| expected="expected.txt" | ||||
| output="out.txt" | ||||
| skip="SKIP" | ||||
|  | ||||
| dev_mode="no" | ||||
| if [ "x$1" = "x--dev" ]; then | ||||
|     dev_mode="yes" | ||||
|     shift | ||||
| fi | ||||
|  | ||||
| update_mode="no" | ||||
| if [ "x$1" = "x--update" ]; then | ||||
|     update_mode="yes" | ||||
|     shift | ||||
| fi | ||||
|  | ||||
| usage="no" | ||||
| if [ "x$1" = "x--help" ]; then | ||||
|     usage="yes" | ||||
| else | ||||
|     if [ "x$1" = "x" ]; then | ||||
|         testdir=`basename "$oldwd"` | ||||
|     else | ||||
|         testdir="$1" | ||||
|     fi | ||||
|  | ||||
|     if [ '!' -d "$testdir" ]; then | ||||
|         td2=`echo ${testdir}-*` | ||||
|         if [ '!' -d "$td2" ]; then | ||||
|             echo "${testdir}: no such test directory" 1>&2 | ||||
|             usage="yes" | ||||
|         fi | ||||
|         testdir="$td2" | ||||
|     fi | ||||
| fi | ||||
|  | ||||
| if [ "$usage" = "yes" ]; then | ||||
|     prog=`basename $prog` | ||||
|     ( | ||||
|         echo "usage:" | ||||
|         echo "  $prog --help             Print this message." | ||||
|         echo "  $prog testname           Run test normally." | ||||
|         echo "  $prog --dev testname     Development mode (dump to stdout)." | ||||
|         echo "  $prog --update testname  Update mode (replace expected.txt)." | ||||
|         echo "  Omitting the test name uses the current directory as the test." | ||||
|     ) 1>&2 | ||||
|     exit 1 | ||||
| fi | ||||
|  | ||||
| td_info="$testdir"/"$info" | ||||
| td_run="$testdir"/"$run" | ||||
| td_expected="$testdir"/"$expected" | ||||
| td_skip="$testdir"/"$skip" | ||||
|  | ||||
| if [ -r "$td_skip" ]; then | ||||
|     exit 2 | ||||
| fi | ||||
|  | ||||
| tmpdir=/tmp/test-$$ | ||||
|  | ||||
| if [ '!' '(' -r "$td_info" -a -r "$td_run" -a -r "$td_expected" ')' ]; then | ||||
|     echo "${testdir}: missing files" 1>&2 | ||||
|     exit 1 | ||||
| fi | ||||
|  | ||||
| # copy the test to a temp dir and run it | ||||
|  | ||||
| echo "${testdir}: running..." 1>&2 | ||||
|  | ||||
| rm -rf "$tmpdir" | ||||
| cp -Rp "$testdir" "$tmpdir" | ||||
| cd "$tmpdir" | ||||
| chmod 755 "$run" | ||||
|  | ||||
| #PATH="${progdir}/../build/bin:${PATH}" | ||||
|  | ||||
| good="no" | ||||
| if [ "$dev_mode" = "yes" ]; then | ||||
|     "./$run" 2>&1 | ||||
|     echo "exit status: $?" 1>&2 | ||||
|     good="yes" | ||||
| elif [ "$update_mode" = "yes" ]; then | ||||
|     "./$run" >"${progdir}/$td_expected" 2>&1 | ||||
|     good="yes" | ||||
| else | ||||
|     "./$run" >"$output" 2>&1 | ||||
|     cmp -s "$expected" "$output" | ||||
|     if [ "$?" = "0" ]; then | ||||
|         # output == expected | ||||
|         good="yes" | ||||
|         echo "$testdir"': succeeded!' 1>&2 | ||||
|     fi | ||||
| fi | ||||
|  | ||||
| if [ "$good" = "yes" ]; then | ||||
|     cd "$oldwd" | ||||
|     rm -rf "$tmpdir" | ||||
|     exit 0 | ||||
| fi | ||||
|  | ||||
| ( | ||||
|     echo "${testdir}: FAILED!" | ||||
|     echo ' ' | ||||
|     echo '#################### info' | ||||
|     cat "$info" | sed 's/^/# /g' | ||||
|     echo '#################### diffs' | ||||
|     diff -u "$expected" "$output" | ||||
|     echo '####################' | ||||
|     echo ' ' | ||||
|     echo "files left in $tmpdir" | ||||
| ) 1>&2 | ||||
|  | ||||
| exit 1 | ||||
| @@ -1,69 +0,0 @@ | ||||
| #!/bin/bash | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| # Set up prog to be the path of this script, including following symlinks, | ||||
| # and set up progdir to be the fully-qualified pathname of its directory. | ||||
| prog="$0" | ||||
| while [ -h "${prog}" ]; do | ||||
|     newProg=`/bin/ls -ld "${prog}"` | ||||
|     newProg=`expr "${newProg}" : ".* -> \(.*\)$"` | ||||
|     if expr "x${newProg}" : 'x/' >/dev/null; then | ||||
|         prog="${newProg}" | ||||
|     else | ||||
|         progdir=`dirname "${prog}"` | ||||
|         prog="${progdir}/${newProg}" | ||||
|     fi | ||||
| done | ||||
| oldwd=`pwd` | ||||
| progdir=`dirname "${prog}"` | ||||
| cd "${progdir}" | ||||
| progdir=`pwd` | ||||
| prog="${progdir}"/`basename "${prog}"` | ||||
|  | ||||
| passed=0 | ||||
| skipped=0 | ||||
| skipNames="" | ||||
| failed=0 | ||||
| failNames="" | ||||
|  | ||||
| for i in *; do | ||||
|     if [ -d "$i" -a -r "$i" ]; then | ||||
|         ./one-test "$i" | ||||
|         status=$? | ||||
|         if [ "$status" = "0" ]; then | ||||
|             ((passed += 1)) | ||||
|         elif [ "$status" = "2" ]; then | ||||
|             ((skipped += 1)) | ||||
|             skipNames="$skipNames $i" | ||||
|         else | ||||
|             ((failed += 1)) | ||||
|             failNames="$failNames $i" | ||||
|         fi | ||||
|     fi | ||||
| done | ||||
|  | ||||
| echo "passed:  $passed test(s)" | ||||
| echo "skipped: $skipped test(s)" | ||||
|  | ||||
| for i in $skipNames; do | ||||
|     echo "skipped: $i" | ||||
| done | ||||
|  | ||||
| echo "failed:  $failed test(s)" | ||||
|      | ||||
| for i in $failNames; do | ||||
|     echo "failed: $i" | ||||
| done | ||||
							
								
								
									
										1075
									
								
								commands.c
									
									
									
									
									
								
							
							
						
						
									
										1075
									
								
								commands.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										28
									
								
								commands.h
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								commands.h
									
									
									
									
									
								
							| @@ -1,28 +0,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 RECOVERY_COMMANDS_H_ | ||||
| #define RECOVERY_COMMANDS_H_ | ||||
|  | ||||
| #include "minzip/Zip.h" | ||||
|  | ||||
| typedef struct { | ||||
|     ZipArchive *package; | ||||
| } RecoveryCommandContext; | ||||
|  | ||||
| int register_update_commands(RecoveryCommandContext *ctx); | ||||
|  | ||||
| #endif  // RECOVERY_COMMANDS_H_ | ||||
							
								
								
									
										105
									
								
								install.c
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								install.c
									
									
									
									
									
								
							| @@ -22,7 +22,6 @@ | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "amend/amend.h" | ||||
| #include "common.h" | ||||
| #include "install.h" | ||||
| #include "mincrypt/rsa.h" | ||||
| @@ -35,85 +34,9 @@ | ||||
| #include "verifier.h" | ||||
| #include "firmware.h" | ||||
|  | ||||
| #define ASSUMED_UPDATE_SCRIPT_NAME  "META-INF/com/google/android/update-script" | ||||
| #define ASSUMED_UPDATE_BINARY_NAME  "META-INF/com/google/android/update-binary" | ||||
| #define PUBLIC_KEYS_FILE "/res/keys" | ||||
|  | ||||
| static const ZipEntry * | ||||
| find_update_script(ZipArchive *zip) | ||||
| { | ||||
| //TODO: Get the location of this script from the MANIFEST.MF file | ||||
|     return mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME); | ||||
| } | ||||
|  | ||||
| static int read_data(ZipArchive *zip, const ZipEntry *entry, | ||||
|         char** ppData, int* pLength) { | ||||
|     int len = (int)mzGetZipEntryUncompLen(entry); | ||||
|     if (len <= 0) { | ||||
|         LOGE("Bad data length %d\n", len); | ||||
|         return -1; | ||||
|     } | ||||
|     char *data = malloc(len + 1); | ||||
|     if (data == NULL) { | ||||
|         LOGE("Can't allocate %d bytes for data\n", len + 1); | ||||
|         return -2; | ||||
|     } | ||||
|     bool ok = mzReadZipEntry(zip, entry, data, len); | ||||
|     if (!ok) { | ||||
|         LOGE("Error while reading data\n"); | ||||
|         free(data); | ||||
|         return -3; | ||||
|     } | ||||
|     data[len] = '\0';     // not necessary, but just to be safe | ||||
|     *ppData = data; | ||||
|     if (pLength) { | ||||
|         *pLength = len; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) | ||||
| { | ||||
|     /* Read the entire script into a buffer. | ||||
|      */ | ||||
|     int script_len; | ||||
|     char* script_data; | ||||
|     if (read_data(zip, update_script_entry, &script_data, &script_len) < 0) { | ||||
|         LOGE("Can't read update script\n"); | ||||
|         return INSTALL_ERROR; | ||||
|     } | ||||
|  | ||||
|     /* Parse the script.  Note that the script and parse tree are never freed. | ||||
|      */ | ||||
|     const AmCommandList *commands = parseAmendScript(script_data, script_len); | ||||
|     if (commands == NULL) { | ||||
|         LOGE("Syntax error in update script\n"); | ||||
|         return INSTALL_ERROR; | ||||
|     } else { | ||||
|         UnterminatedString name = mzGetZipEntryFileName(update_script_entry); | ||||
|         LOGI("Parsed %.*s\n", name.len, name.str); | ||||
|     } | ||||
|  | ||||
|     /* Execute the script. | ||||
|      */ | ||||
|     int ret = execCommandList((ExecContext *)1, commands); | ||||
|     if (ret != 0) { | ||||
|         int num = ret; | ||||
|         char *line = NULL, *next = script_data; | ||||
|         while (next != NULL && ret-- > 0) { | ||||
|             line = next; | ||||
|             next = memchr(line, '\n', script_data + script_len - line); | ||||
|             if (next != NULL) *next++ = '\0'; | ||||
|         } | ||||
|         LOGE("Failure at line %d:\n%s\n", num, next ? line : "(not found)"); | ||||
|         return INSTALL_ERROR; | ||||
|     } | ||||
|  | ||||
|     LOGI("Installation complete.\n"); | ||||
|     return INSTALL_SUCCESS; | ||||
| } | ||||
|  | ||||
| // The update binary ask us to install a firmware file on reboot.  Set | ||||
| // that up.  Takes ownership of type and filename. | ||||
| static int | ||||
| @@ -252,11 +175,9 @@ try_update_binary(const char *path, ZipArchive *zip) { | ||||
|     char* firmware_type = NULL; | ||||
|     char* firmware_filename = NULL; | ||||
|  | ||||
|     char buffer[81]; | ||||
|     char buffer[1024]; | ||||
|     FILE* from_child = fdopen(pipefd[0], "r"); | ||||
|     while (fgets(buffer, sizeof(buffer), from_child) != NULL) { | ||||
|         LOGI("read: %s", buffer); | ||||
|  | ||||
|         char* command = strtok(buffer, " \n"); | ||||
|         if (command == NULL) { | ||||
|             continue; | ||||
| @@ -331,30 +252,8 @@ handle_update_package(const char *path, ZipArchive *zip, | ||||
|     ui_print("Installing update...\n"); | ||||
|  | ||||
|     int result = try_update_binary(path, zip); | ||||
|     if (result == INSTALL_SUCCESS || result == INSTALL_ERROR) { | ||||
|         register_package_root(NULL, NULL);  // Unregister package root | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     // if INSTALL_CORRUPT is returned, this package doesn't have an | ||||
|     // update binary.  Fall back to the older mechanism of looking for | ||||
|     // an update script. | ||||
|  | ||||
|     const ZipEntry *script_entry; | ||||
|     script_entry = find_update_script(zip); | ||||
|     if (script_entry == NULL) { | ||||
|         LOGE("Can't find update script\n"); | ||||
|         return INSTALL_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     if (register_package_root(zip, path) < 0) { | ||||
|         LOGE("Can't register package root\n"); | ||||
|         return INSTALL_ERROR; | ||||
|     } | ||||
|  | ||||
|     int ret = handle_update_script(zip, script_entry); | ||||
|     register_package_root(NULL, NULL);  // Unregister package root | ||||
|     return ret; | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| // Reads a file containing one or more public keys as produced by | ||||
|   | ||||
							
								
								
									
										29
									
								
								recovery.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								recovery.c
									
									
									
									
									
								
							| @@ -29,7 +29,6 @@ | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "bootloader.h" | ||||
| #include "commands.h" | ||||
| #include "common.h" | ||||
| #include "cutils/properties.h" | ||||
| #include "firmware.h" | ||||
| @@ -258,23 +257,6 @@ finish_recovery(const char *send_intent) | ||||
|     sync();  // For good measure. | ||||
| } | ||||
|  | ||||
| #define TEST_AMEND 0 | ||||
| #if TEST_AMEND | ||||
| static void | ||||
| test_amend() | ||||
| { | ||||
|     extern int test_symtab(void); | ||||
|     extern int test_cmd_fn(void); | ||||
|     int ret; | ||||
|     LOGD("Testing symtab...\n"); | ||||
|     ret = test_symtab(); | ||||
|     LOGD("  returned %d\n", ret); | ||||
|     LOGD("Testing cmd_fn...\n"); | ||||
|     ret = test_cmd_fn(); | ||||
|     LOGD("  returned %d\n", ret); | ||||
| } | ||||
| #endif  // TEST_AMEND | ||||
|  | ||||
| static int | ||||
| erase_root(const char *root) | ||||
| { | ||||
| @@ -288,7 +270,7 @@ static void | ||||
| prompt_and_wait() | ||||
| { | ||||
|     char* headers[] = { "Android system recovery <" | ||||
|                           EXPAND(RECOVERY_API_VERSION) ">", | ||||
|                           EXPAND(RECOVERY_API_VERSION) "e>", | ||||
|                         "", | ||||
|                         "Use trackball to highlight;", | ||||
|                         "click to select.", | ||||
| @@ -445,15 +427,6 @@ main(int argc, char **argv) | ||||
|     property_list(print_property, NULL); | ||||
|     fprintf(stderr, "\n"); | ||||
|  | ||||
| #if TEST_AMEND | ||||
|     test_amend(); | ||||
| #endif | ||||
|  | ||||
|     RecoveryCommandContext ctx = { NULL }; | ||||
|     if (register_update_commands(&ctx)) { | ||||
|         LOGE("Can't install update commands\n"); | ||||
|     } | ||||
|  | ||||
|     int status = INSTALL_SUCCESS; | ||||
|  | ||||
|     if (update_package != NULL) { | ||||
|   | ||||
| @@ -14,11 +14,6 @@ | ||||
|  | ||||
| LOCAL_PATH := $(call my-dir) | ||||
|  | ||||
| include $(CLEAR_VARS) | ||||
| LOCAL_MODULE := make-update-script | ||||
| LOCAL_SRC_FILES := make-update-script.c | ||||
| include $(BUILD_HOST_EXECUTABLE) | ||||
|  | ||||
| ifneq ($(TARGET_SIMULATOR),true) | ||||
|  | ||||
| include $(CLEAR_VARS) | ||||
|   | ||||
| @@ -1,233 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2008 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 "private/android_filesystem_config.h" | ||||
|  | ||||
| #include <dirent.h> | ||||
| #include <limits.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| /* | ||||
|  * Recursively walk the directory tree at <sysdir>/<subdir>, writing | ||||
|  * script commands to set permissions and create symlinks. | ||||
|  * Assume the contents already have the specified default permissions, | ||||
|  * so only output commands if they need to be changed from the defaults. | ||||
|  * | ||||
|  * Note that permissions are set by fs_config(), which uses a lookup table of | ||||
|  * Android permissions.  They are not drawn from the build host filesystem. | ||||
|  */ | ||||
| static void walk_files( | ||||
|         const char *sysdir, const char *subdir, | ||||
|         unsigned default_uid, unsigned default_gid, | ||||
|         unsigned default_dir_mode, unsigned default_file_mode) { | ||||
|     const char *sep = strcmp(subdir, "") ? "/" : ""; | ||||
|  | ||||
|     char fn[PATH_MAX]; | ||||
|     unsigned dir_uid = 0, dir_gid = 0, dir_mode = 0; | ||||
|     snprintf(fn, PATH_MAX, "system%s%s", sep, subdir); | ||||
|     fs_config(fn, 1, &dir_uid, &dir_gid, &dir_mode); | ||||
|  | ||||
|     snprintf(fn, PATH_MAX, "%s%s%s", sysdir, sep, subdir); | ||||
|     DIR *dir = opendir(fn); | ||||
|     if (dir == NULL) { | ||||
|         perror(fn); | ||||
|         exit(1); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * We can use "set_perm" and "set_perm_recursive" to set file permissions | ||||
|      * (owner, group, and file mode) for individual files and entire subtrees. | ||||
|      * We want to use set_perm_recursive efficiently to avoid setting the | ||||
|      * permissions of every single file in the system image individually. | ||||
|      * | ||||
|      * What we do is recursively set our entire subtree to the permissions | ||||
|      * used by the first file we encounter, and then use "set_perm" to adjust | ||||
|      * the permissions of subsequent files which don't match the first one. | ||||
|      * This is bad if the first file is an outlier, but it generally works. | ||||
|      * Subdirectories can do the same thing recursively if they're different. | ||||
|      */ | ||||
|  | ||||
|     int is_first = 1; | ||||
|     const struct dirent *e; | ||||
|     while ((e = readdir(dir))) { | ||||
|         // Skip over "." and ".." entries | ||||
|         if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue; | ||||
|  | ||||
|         if (e->d_type == DT_LNK) {  // Symlink | ||||
|  | ||||
|             // Symlinks don't really have permissions, so this is orthogonal. | ||||
|             snprintf(fn, PATH_MAX, "%s/%s%s%s", sysdir, subdir, sep, e->d_name); | ||||
|             int len = readlink(fn, fn, PATH_MAX - 1); | ||||
|             if (len <= 0) { | ||||
|                 perror(fn); | ||||
|                 exit(1); | ||||
|             } | ||||
|             fn[len] = '\0'; | ||||
|             printf("symlink %s SYSTEM:%s%s%s\n", fn, subdir, sep, e->d_name); | ||||
|  | ||||
|         } else if (e->d_type == DT_DIR) {  // Subdirectory | ||||
|  | ||||
|             // Use the parent directory as the model for default permissions. | ||||
|             // We haven't seen a file, so just make up some file defaults. | ||||
|             if (is_first && ( | ||||
|                     dir_mode != default_dir_mode || | ||||
|                     dir_uid != default_uid || dir_gid != default_gid)) { | ||||
|                 default_uid = dir_uid; | ||||
|                 default_gid = dir_gid; | ||||
|                 default_dir_mode = dir_mode; | ||||
|                 default_file_mode = dir_mode & default_file_mode & 0666; | ||||
|                 printf("set_perm_recursive %d %d 0%o 0%o SYSTEM:%s\n", | ||||
|                          default_uid, default_gid, | ||||
|                          default_dir_mode, default_file_mode, | ||||
|                          subdir); | ||||
|             } | ||||
|  | ||||
|             is_first = 0; | ||||
|  | ||||
|             // Recursively handle the subdirectory. | ||||
|             // Note, the recursive call handles the directory's own permissions. | ||||
|             snprintf(fn, PATH_MAX, "%s%s%s", subdir, sep, e->d_name); | ||||
|             walk_files(sysdir, fn, | ||||
|                     default_uid, default_gid, | ||||
|                     default_dir_mode, default_file_mode); | ||||
|  | ||||
|         } else {  // Ordinary file | ||||
|  | ||||
|             // Get the file's desired permissions. | ||||
|             unsigned file_uid = 0, file_gid = 0, file_mode = 0; | ||||
|             snprintf(fn, PATH_MAX, "system/%s%s%s", subdir, sep, e->d_name); | ||||
|             fs_config(fn, 0, &file_uid, &file_gid, &file_mode); | ||||
|  | ||||
|             // If this is the first file, its mode gets to become the default. | ||||
|             if (is_first && ( | ||||
|                     dir_mode != default_dir_mode || | ||||
|                     file_mode != default_file_mode || | ||||
|                     dir_uid != default_uid || file_uid != default_uid || | ||||
|                     dir_gid != default_gid || file_gid != default_gid)) { | ||||
|                 default_uid = dir_uid; | ||||
|                 default_gid = dir_gid; | ||||
|                 default_dir_mode = dir_mode; | ||||
|                 default_file_mode = file_mode; | ||||
|                 printf("set_perm_recursive %d %d 0%o 0%o SYSTEM:%s\n", | ||||
|                          default_uid, default_gid, | ||||
|                          default_dir_mode, default_file_mode, | ||||
|                          subdir); | ||||
|             } | ||||
|  | ||||
|             is_first = 0; | ||||
|  | ||||
|             // Otherwise, override this file if it doesn't match the defaults. | ||||
|             if (file_mode != default_file_mode || | ||||
|                 file_uid != default_uid || file_gid != default_gid) { | ||||
|                 printf("set_perm %d %d 0%o SYSTEM:%s%s%s\n", | ||||
|                          file_uid, file_gid, file_mode, | ||||
|                          subdir, sep, e->d_name); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Set the directory's permissions directly, if they never got set. | ||||
|     if (dir_mode != default_dir_mode || | ||||
|         dir_uid != default_uid || dir_gid != default_gid) { | ||||
|         printf("set_perm %d %d 0%o SYSTEM:%s\n", | ||||
|                 dir_uid, dir_gid, dir_mode, subdir); | ||||
|     } | ||||
|  | ||||
|     closedir(dir); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Generate the update script (in "Amend", see commands/recovery/commands.c) | ||||
|  * for the complete-reinstall OTA update packages the build system makes. | ||||
|  * | ||||
|  * The generated script makes a variety of sanity checks about the device, | ||||
|  * erases and reinstalls system files, and sets file permissions appropriately. | ||||
|  */ | ||||
| int main(int argc, char *argv[]) { | ||||
|     if (argc != 3) { | ||||
|         fprintf(stderr, "usage: %s systemdir android-info.txt >update-script\n", | ||||
|                 argv[0]); | ||||
|         return 2; | ||||
|     } | ||||
|  | ||||
|     // ensure basic recovery script language compatibility | ||||
|     printf("assert compatible_with(\"0.2\") == \"true\"\n"); | ||||
|  | ||||
|     // if known, make sure the device name is correct | ||||
|     const char *device = getenv("TARGET_DEVICE"); | ||||
|     if (device != NULL) { | ||||
|         printf("assert getprop(\"ro.product.device\") == \"%s\" || " | ||||
|                 "getprop(\"ro.build.product\") == \"%s\"\n", device, device); | ||||
|     } | ||||
|  | ||||
|     // scan android-info.txt to enforce compatibility with the target system | ||||
|     FILE *fp = fopen(argv[2], "r"); | ||||
|     if (fp == NULL) { | ||||
|         perror(argv[2]); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     // The lines we're looking for look like: | ||||
|     //     version-bootloader=x.yy.zzzz|x.yy.zzzz|... | ||||
|     // or: | ||||
|     //     require version-bootloader=x.yy.zzzz|x.yy.zzzz|... | ||||
|     char line[256]; | ||||
|     while (fgets(line, sizeof(line), fp)) { | ||||
|         const char *name = strtok(line, "="), *value = strtok(NULL, "|\n"); | ||||
|         if (value != NULL && | ||||
|             (!strcmp(name, "version-bootloader") || | ||||
|              !strcmp(name, "require version-bootloader"))) { | ||||
|             printf("assert getprop(\"ro.bootloader\") == \"%s\"", value); | ||||
|  | ||||
|             while ((value = strtok(NULL, "|\n")) != NULL) { | ||||
|               printf(" || getprop(\"ro.bootloader\") == \"%s\"", value); | ||||
|             } | ||||
|             printf("\n"); | ||||
|         } | ||||
|         // We also used to check version-baseband, but we update radio.img | ||||
|         // ourselves, so there's no need. | ||||
|     } | ||||
|  | ||||
|     // erase the boot sector first, so if the update gets interrupted, | ||||
|     // the system will reboot into the recovery partition and start over. | ||||
|     printf("format BOOT:\n"); | ||||
|  | ||||
|     // write the radio image (actually just loads it into RAM for now) | ||||
|     printf("show_progress 0.1 0\n"); | ||||
|     printf("write_radio_image PACKAGE:radio.img\n"); | ||||
|  | ||||
|     // erase and reinstall the system image | ||||
|     printf("show_progress 0.5 0\n"); | ||||
|     printf("format SYSTEM:\n"); | ||||
|     printf("copy_dir PACKAGE:system SYSTEM:\n"); | ||||
|  | ||||
|     // walk the files in the system image, set their permissions, etc. | ||||
|     // use -1 for default values to force permissions to be set explicitly. | ||||
|     walk_files(argv[1], "", -1, -1, -1, -1); | ||||
|  | ||||
|     // as the last step, write the boot sector. | ||||
|     printf("show_progress 0.2 0\n"); | ||||
|     printf("write_raw_image PACKAGE:boot.img BOOT:\n"); | ||||
|  | ||||
|     // after the end of the script, the radio will be written to cache | ||||
|     // leave some space in the progress bar for this operation | ||||
|     printf("show_progress 0.2 10\n"); | ||||
|     return 0; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user