auto import from //depot/cupcake/@135843
This commit is contained in:
		
							
								
								
									
										60
									
								
								Android.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								Android.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| LOCAL_PATH := $(call my-dir) | ||||
| include $(CLEAR_VARS) | ||||
|  | ||||
| commands_recovery_local_path := $(LOCAL_PATH) | ||||
|  | ||||
| ifneq ($(TARGET_SIMULATOR),true) | ||||
| ifeq ($(TARGET_ARCH),arm) | ||||
|  | ||||
| LOCAL_SRC_FILES := \ | ||||
| 	recovery.c \ | ||||
| 	bootloader.c \ | ||||
| 	commands.c \ | ||||
| 	firmware.c \ | ||||
| 	install.c \ | ||||
| 	roots.c \ | ||||
| 	ui.c \ | ||||
| 	verifier.c | ||||
|  | ||||
| LOCAL_SRC_FILES += test_roots.c | ||||
|  | ||||
| LOCAL_MODULE := recovery | ||||
|  | ||||
| LOCAL_FORCE_STATIC_EXECUTABLE := true | ||||
|  | ||||
| # This binary is in the recovery ramdisk, which is otherwise a copy of root. | ||||
| # It gets copied there in config/Makefile.  LOCAL_MODULE_TAGS suppresses | ||||
| # a (redundant) copy of the binary in /system/bin for user builds. | ||||
| # TODO: Build the ramdisk image in a more principled way. | ||||
|  | ||||
| LOCAL_MODULE_TAGS := eng | ||||
|  | ||||
| LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt | ||||
| LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libcutils | ||||
| LOCAL_STATIC_LIBRARIES += libstdc++ libc | ||||
|  | ||||
| # Specify a C-includable file containing the OTA public keys. | ||||
| # This is built in config/Makefile. | ||||
| # *** THIS IS A TOTAL HACK; EXECUTABLES MUST NOT CHANGE BETWEEN DIFFERENT | ||||
| #     PRODUCTS/BUILD TYPES. *** | ||||
| # TODO: make recovery read the keys from an external file. | ||||
| RECOVERY_INSTALL_OTA_KEYS_INC := \ | ||||
| 	$(call intermediates-dir-for,PACKAGING,ota_keys_inc)/keys.inc | ||||
| # Let install.c say #include "keys.inc" | ||||
| LOCAL_C_INCLUDES += $(dir $(RECOVERY_INSTALL_OTA_KEYS_INC)) | ||||
|  | ||||
| include $(BUILD_EXECUTABLE) | ||||
|  | ||||
| # Depend on the generated keys.inc containing the OTA public keys. | ||||
| $(intermediates)/install.o: $(RECOVERY_INSTALL_OTA_KEYS_INC) | ||||
|  | ||||
| include $(commands_recovery_local_path)/minui/Android.mk | ||||
|  | ||||
| endif   # TARGET_ARCH == arm | ||||
| endif	# !TARGET_SIMULATOR | ||||
|  | ||||
| include $(commands_recovery_local_path)/amend/Android.mk | ||||
| include $(commands_recovery_local_path)/minzip/Android.mk | ||||
| include $(commands_recovery_local_path)/mtdutils/Android.mk | ||||
| include $(commands_recovery_local_path)/tools/Android.mk | ||||
| commands_recovery_local_path := | ||||
							
								
								
									
										53
									
								
								amend/Android.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								amend/Android.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| # Copyright 2007 The Android Open Source Project | ||||
| # | ||||
|  | ||||
| LOCAL_PATH := $(call my-dir) | ||||
|  | ||||
| amend_src_files := \ | ||||
| 	amend.c \ | ||||
| 	lexer.l \ | ||||
| 	parser_y.y \ | ||||
| 	ast.c \ | ||||
| 	symtab.c \ | ||||
| 	commands.c \ | ||||
| 	permissions.c \ | ||||
| 	execute.c | ||||
|  | ||||
| amend_test_files := \ | ||||
| 	test_symtab.c \ | ||||
| 	test_commands.c \ | ||||
| 	test_permissions.c | ||||
|  | ||||
| # "-x c" forces the lex/yacc files to be compiled as c; | ||||
| # the build system otherwise forces them to be c++. | ||||
| amend_cflags := -Wall -x c | ||||
|  | ||||
| # | ||||
| # Build the host-side command line tool | ||||
| # | ||||
| include $(CLEAR_VARS) | ||||
|  | ||||
| LOCAL_SRC_FILES := \ | ||||
| 		$(amend_src_files) \ | ||||
| 		$(amend_test_files) \ | ||||
| 		register.c \ | ||||
| 		main.c | ||||
|  | ||||
| LOCAL_CFLAGS := $(amend_cflags) -g -O0 | ||||
| LOCAL_MODULE := amend | ||||
| LOCAL_YACCFLAGS := -v | ||||
|  | ||||
| include $(BUILD_HOST_EXECUTABLE) | ||||
|  | ||||
| # | ||||
| # Build the device-side library | ||||
| # | ||||
| include $(CLEAR_VARS) | ||||
|  | ||||
| LOCAL_SRC_FILES := $(amend_src_files) | ||||
| LOCAL_SRC_FILES += $(amend_test_files) | ||||
|  | ||||
| LOCAL_CFLAGS := $(amend_cflags) | ||||
| LOCAL_MODULE := libamend | ||||
|  | ||||
| include $(BUILD_STATIC_LIBRARY) | ||||
							
								
								
									
										32
									
								
								amend/amend.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								amend/amend.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include "amend.h" | ||||
| #include "lexer.h" | ||||
|  | ||||
| extern const AmCommandList *gCommands; | ||||
|  | ||||
| const AmCommandList * | ||||
| parseAmendScript(const char *buf, size_t bufLen) | ||||
| { | ||||
|     setLexerInputBuffer(buf, bufLen); | ||||
|     int ret = yyparse(); | ||||
|     if (ret != 0) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return gCommands; | ||||
| } | ||||
							
								
								
									
										25
									
								
								amend/amend.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								amend/amend.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * 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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								amend/ast.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| /* | ||||
|  * 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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								amend/ast.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | ||||
| /* | ||||
|  * 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_ | ||||
							
								
								
									
										273
									
								
								amend/commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								amend/commands.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,273 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include "symtab.h" | ||||
| #include "commands.h" | ||||
|  | ||||
| #if 1 | ||||
| #define TRACE(...)  printf(__VA_ARGS__) | ||||
| #else | ||||
| #define TRACE(...)  /**/ | ||||
| #endif | ||||
|  | ||||
| typedef enum { | ||||
|     CMD_TYPE_UNKNOWN = -1, | ||||
|     CMD_TYPE_COMMAND = 0, | ||||
|     CMD_TYPE_FUNCTION | ||||
| } CommandType; | ||||
|  | ||||
| typedef struct { | ||||
|     const char *name; | ||||
|     void *cookie; | ||||
|     CommandType type; | ||||
|     CommandArgumentType argType; | ||||
|     CommandHook hook; | ||||
| } CommandEntry; | ||||
|  | ||||
| static struct { | ||||
|     SymbolTable *symbolTable; | ||||
|     bool commandStateInitialized; | ||||
| } gCommandState; | ||||
|  | ||||
| int | ||||
| commandInit() | ||||
| { | ||||
|     if (gCommandState.commandStateInitialized) { | ||||
|         return -1; | ||||
|     } | ||||
|     gCommandState.symbolTable = createSymbolTable(); | ||||
|     if (gCommandState.symbolTable == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|     gCommandState.commandStateInitialized = true; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void | ||||
| commandCleanup() | ||||
| { | ||||
|     if (gCommandState.commandStateInitialized) { | ||||
|         gCommandState.commandStateInitialized = false; | ||||
|         deleteSymbolTable(gCommandState.symbolTable); | ||||
|         gCommandState.symbolTable = NULL; | ||||
| //xxx need to free the entries and names in the symbol table | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int | ||||
| registerCommandInternal(const char *name, CommandType type, | ||||
|         CommandArgumentType argType, CommandHook hook, void *cookie) | ||||
| { | ||||
|     CommandEntry *entry; | ||||
|  | ||||
|     if (!gCommandState.commandStateInitialized) { | ||||
|         return -1; | ||||
|     } | ||||
|     if (name == NULL || hook == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|     if (type != CMD_TYPE_COMMAND && type != CMD_TYPE_FUNCTION) { | ||||
|         return -1; | ||||
|     } | ||||
|     if (argType != CMD_ARGS_BOOLEAN && argType != CMD_ARGS_WORDS) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     entry = (CommandEntry *)malloc(sizeof(CommandEntry)); | ||||
|     if (entry != NULL) { | ||||
|         entry->name = strdup(name); | ||||
|         if (entry->name != NULL) { | ||||
|             int ret; | ||||
|  | ||||
|             entry->cookie = cookie; | ||||
|             entry->type = type; | ||||
|             entry->argType = argType; | ||||
|             entry->hook = hook; | ||||
|             ret = addToSymbolTable(gCommandState.symbolTable, | ||||
|                         entry->name, entry->type, entry); | ||||
|             if (ret == 0) { | ||||
|                 return 0; | ||||
|             } | ||||
|         } | ||||
|         free(entry); | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| int | ||||
| registerCommand(const char *name, | ||||
|         CommandArgumentType argType, CommandHook hook, void *cookie) | ||||
| { | ||||
|     return registerCommandInternal(name, | ||||
|             CMD_TYPE_COMMAND, argType, hook, cookie); | ||||
| } | ||||
|  | ||||
| int | ||||
| registerFunction(const char *name, FunctionHook hook, void *cookie) | ||||
| { | ||||
|     return registerCommandInternal(name, | ||||
|             CMD_TYPE_FUNCTION, CMD_ARGS_WORDS, (CommandHook)hook, cookie); | ||||
| } | ||||
|  | ||||
| Command * | ||||
| findCommand(const char *name) | ||||
| { | ||||
|     return (Command *)findInSymbolTable(gCommandState.symbolTable, | ||||
|             name, CMD_TYPE_COMMAND); | ||||
| } | ||||
|  | ||||
| Function * | ||||
| findFunction(const char *name) | ||||
| { | ||||
|     return (Function *)findInSymbolTable(gCommandState.symbolTable, | ||||
|             name, CMD_TYPE_FUNCTION); | ||||
| } | ||||
|  | ||||
| CommandArgumentType | ||||
| getCommandArgumentType(Command *cmd) | ||||
| { | ||||
|     CommandEntry *entry = (CommandEntry *)cmd; | ||||
|  | ||||
|     if (entry != NULL) { | ||||
|         return entry->argType; | ||||
|     } | ||||
|     return CMD_ARGS_UNKNOWN; | ||||
| } | ||||
|  | ||||
| static int | ||||
| callCommandInternal(CommandEntry *entry, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     if (entry != NULL && entry->argType == CMD_ARGS_WORDS && | ||||
|             (argc == 0 || (argc > 0 && argv != NULL))) | ||||
|     { | ||||
|         if (permissions == NULL) { | ||||
|             int i; | ||||
|             for (i = 0; i < argc; i++) { | ||||
|                 if (argv[i] == NULL) { | ||||
|                     goto bail; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         TRACE("calling command %s\n", entry->name); | ||||
|         return entry->hook(entry->name, entry->cookie, argc, argv, permissions); | ||||
| //xxx if permissions, make sure the entry has added at least one element. | ||||
|     } | ||||
| bail: | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int | ||||
| callBooleanCommandInternal(CommandEntry *entry, bool arg, | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     if (entry != NULL && entry->argType == CMD_ARGS_BOOLEAN) { | ||||
|         TRACE("calling boolean command %s\n", entry->name); | ||||
|         return entry->hook(entry->name, entry->cookie, arg ? 1 : 0, NULL, | ||||
|                 permissions); | ||||
| //xxx if permissions, make sure the entry has added at least one element. | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| int | ||||
| callCommand(Command *cmd, int argc, const char *argv[]) | ||||
| { | ||||
|     return callCommandInternal((CommandEntry *)cmd, argc, argv, NULL); | ||||
| } | ||||
|  | ||||
| int | ||||
| callBooleanCommand(Command *cmd, bool arg) | ||||
| { | ||||
|     return callBooleanCommandInternal((CommandEntry *)cmd, arg, NULL); | ||||
| } | ||||
|  | ||||
| int | ||||
| getCommandPermissions(Command *cmd, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     if (permissions != NULL) { | ||||
|         return callCommandInternal((CommandEntry *)cmd, argc, argv, | ||||
|                 permissions); | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| int | ||||
| getBooleanCommandPermissions(Command *cmd, bool arg, | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     if (permissions != NULL) { | ||||
|         return callBooleanCommandInternal((CommandEntry *)cmd, arg, | ||||
|                 permissions); | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| int | ||||
| callFunctionInternal(CommandEntry *entry, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen, PermissionRequestList *permissions) | ||||
| { | ||||
|     if (entry != NULL && entry->argType == CMD_ARGS_WORDS && | ||||
|             (argc == 0 || (argc > 0 && argv != NULL))) | ||||
|     { | ||||
|         if ((permissions == NULL && result != NULL) || | ||||
|                 (permissions != NULL && result == NULL)) | ||||
|         { | ||||
|             if (permissions == NULL) { | ||||
|                 /* This is the actual invocation of the function, | ||||
|                  * which means that none of the arguments are allowed | ||||
|                  * to be NULL. | ||||
|                  */ | ||||
|                 int i; | ||||
|                 for (i = 0; i < argc; i++) { | ||||
|                     if (argv[i] == NULL) { | ||||
|                         goto bail; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             TRACE("calling function %s\n", entry->name); | ||||
|             return ((FunctionHook)entry->hook)(entry->name, entry->cookie, | ||||
|                     argc, argv, result, resultLen, permissions); | ||||
| //xxx if permissions, make sure the entry has added at least one element. | ||||
|         } | ||||
|     } | ||||
| bail: | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| int | ||||
| callFunction(Function *fn, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen) | ||||
| { | ||||
|     return callFunctionInternal((CommandEntry *)fn, argc, argv, | ||||
|             result, resultLen, NULL); | ||||
| } | ||||
|  | ||||
| int | ||||
| getFunctionPermissions(Function *fn, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     if (permissions != NULL) { | ||||
|         return callFunctionInternal((CommandEntry *)fn, argc, argv, | ||||
|                 NULL, NULL, permissions); | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
							
								
								
									
										96
									
								
								amend/commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								amend/commands.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #ifndef AMEND_COMMANDS_H_ | ||||
| #define AMEND_COMMANDS_H_ | ||||
|  | ||||
| #include "permissions.h" | ||||
|  | ||||
| /* Invoke or dry-run a command.  If "permissions" is non-NULL, | ||||
|  * the hook should fill it out with the list of files and operations that | ||||
|  * it would need to complete its operation.  If "permissions" is NULL, | ||||
|  * the hook should do the actual work specified by its arguments. | ||||
|  * | ||||
|  * When a command is called with non-NULL "permissions", some arguments | ||||
|  * may be NULL.  A NULL argument indicates that the argument is actually | ||||
|  * the output of another function, so is not known at permissions time. | ||||
|  * The permissions of leaf-node functions (those that have only literal | ||||
|  * strings as arguments) will get appended to the permissions of the | ||||
|  * functions that call them.  However, to be completely safe, functions | ||||
|  * that receive a NULL argument should request the broadest-possible | ||||
|  * permissions for the range of the input argument. | ||||
|  * | ||||
|  * When a boolean command is called, "argc" is the boolean value and | ||||
|  * "argv" is NULL. | ||||
|  */ | ||||
| typedef int (*CommandHook)(const char *name, void *cookie, | ||||
|                                 int argc, const char *argv[], | ||||
|                                 PermissionRequestList *permissions); | ||||
|  | ||||
| int commandInit(void); | ||||
| void commandCleanup(void); | ||||
|  | ||||
| /* | ||||
|  * Command management | ||||
|  */ | ||||
|  | ||||
| struct Command; | ||||
| typedef struct Command Command; | ||||
|  | ||||
| typedef enum { | ||||
|     CMD_ARGS_UNKNOWN = -1, | ||||
|     CMD_ARGS_BOOLEAN = 0, | ||||
|     CMD_ARGS_WORDS | ||||
| } CommandArgumentType; | ||||
|  | ||||
| int registerCommand(const char *name, | ||||
|         CommandArgumentType argType, CommandHook hook, void *cookie); | ||||
|  | ||||
| Command *findCommand(const char *name); | ||||
|  | ||||
| CommandArgumentType getCommandArgumentType(Command *cmd); | ||||
|  | ||||
| int callCommand(Command *cmd, int argc, const char *argv[]); | ||||
| int callBooleanCommand(Command *cmd, bool arg); | ||||
|  | ||||
| int getCommandPermissions(Command *cmd, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions); | ||||
| int getBooleanCommandPermissions(Command *cmd, bool arg, | ||||
|         PermissionRequestList *permissions); | ||||
|  | ||||
| /* | ||||
|  * Function management | ||||
|  */ | ||||
|  | ||||
| typedef int (*FunctionHook)(const char *name, void *cookie, | ||||
|                                 int argc, const char *argv[], | ||||
|                                 char **result, size_t *resultLen, | ||||
|                                 PermissionRequestList *permissions); | ||||
|  | ||||
| struct Function; | ||||
| typedef struct Function Function; | ||||
|  | ||||
| int registerFunction(const char *name, FunctionHook hook, void *cookie); | ||||
|  | ||||
| Function *findFunction(const char *name); | ||||
|  | ||||
| int callFunction(Function *fn, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen); | ||||
|  | ||||
| int getFunctionPermissions(Function *fn, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions); | ||||
|  | ||||
| #endif  // AMEND_COMMANDS_H_ | ||||
							
								
								
									
										315
									
								
								amend/execute.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								amend/execute.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,315 @@ | ||||
| /* | ||||
|  * 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; | ||||
| } | ||||
							
								
								
									
										25
									
								
								amend/execute.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								amend/execute.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * 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_ | ||||
							
								
								
									
										43
									
								
								amend/lexer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								amend/lexer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /* | ||||
|  * 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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								amend/lexer.l
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,299 @@ | ||||
| /* | ||||
|  * 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; | ||||
| } | ||||
							
								
								
									
										195
									
								
								amend/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								amend/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,195 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include "ast.h" | ||||
| #include "lexer.h" | ||||
| #include "parser.h" | ||||
| #include "register.h" | ||||
| #include "execute.h" | ||||
|  | ||||
| void | ||||
| lexTest() | ||||
| { | ||||
|     int token; | ||||
|     do { | ||||
|         token = yylex(); | ||||
|         if (token == 0) { | ||||
|             printf(" EOF"); | ||||
|             fflush(stdout); | ||||
|             break; | ||||
|         } else { | ||||
|             printf(" %s", tokenToString(token)); | ||||
|             fflush(stdout); | ||||
|             if (token == TOK_IDENTIFIER) { | ||||
|                 if (strcmp(yylval.literalString, "assert") == 0) { | ||||
|                     setLexerArgumentType(AM_BOOLEAN_ARGS); | ||||
|                 } else { | ||||
|                     setLexerArgumentType(AM_WORD_ARGS); | ||||
|                 } | ||||
|                 do { | ||||
|                     token = yylex(); | ||||
|                     printf(" %s", tokenToString(token)); | ||||
|                     fflush(stdout); | ||||
|                 } while (token != TOK_EOL && token != TOK_EOF && token != 0); | ||||
|             } else if (token != TOK_EOL) { | ||||
|                 fprintf(stderr, "syntax error: expected identifier\n"); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } while (token != 0); | ||||
|     printf("\n"); | ||||
| } | ||||
|  | ||||
| void | ||||
| usage() | ||||
| { | ||||
|     printf("usage: amend [--debug-lex|--debug-ast] [<filename>]\n"); | ||||
|     exit(1); | ||||
| } | ||||
|  | ||||
| extern const AmCommandList *gCommands; | ||||
| int | ||||
| main(int argc, char *argv[]) | ||||
| { | ||||
|     FILE *inputFile = NULL; | ||||
|     bool debugLex = false; | ||||
|     bool debugAst = false; | ||||
|     const char *fileName = NULL; | ||||
|     int err; | ||||
|  | ||||
| #if 1 | ||||
|     extern int test_symtab(void); | ||||
|     int ret = test_symtab(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_symtab() failed: %d\n", ret); | ||||
|         exit(ret); | ||||
|     } | ||||
|     extern int test_cmd_fn(void); | ||||
|     ret = test_cmd_fn(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_cmd_fn() failed: %d\n", ret); | ||||
|         exit(ret); | ||||
|     } | ||||
|     extern int test_permissions(void); | ||||
|     ret = test_permissions(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_permissions() failed: %d\n", ret); | ||||
|         exit(ret); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     argc--; | ||||
|     argv++; | ||||
|     while (argc > 0) { | ||||
|         if (strcmp("--debug-lex", argv[0]) == 0) { | ||||
|             debugLex = true; | ||||
|         } else if (strcmp("--debug-ast", argv[0]) == 0) { | ||||
|             debugAst = true; | ||||
|         } else if (argv[0][0] == '-') { | ||||
|             fprintf(stderr, "amend: Unknown option \"%s\"\n", argv[0]); | ||||
|             usage(); | ||||
|         } else { | ||||
|             fileName = argv[0]; | ||||
|         } | ||||
|         argc--; | ||||
|         argv++; | ||||
|     } | ||||
|  | ||||
|     if (fileName != NULL) { | ||||
|         inputFile = fopen(fileName, "r"); | ||||
|         if (inputFile == NULL) { | ||||
|             fprintf(stderr, "amend: Can't open input file '%s'\n", fileName); | ||||
|             usage(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     commandInit(); | ||||
| //xxx clean up | ||||
|  | ||||
|     err = registerUpdateCommands(); | ||||
|     if (err < 0) { | ||||
|         fprintf(stderr, "amend: Error registering commands: %d\n", err); | ||||
|         exit(-err); | ||||
|     } | ||||
|     err = registerUpdateFunctions(); | ||||
|     if (err < 0) { | ||||
|         fprintf(stderr, "amend: Error registering functions: %d\n", err); | ||||
|         exit(-err); | ||||
|     } | ||||
|  | ||||
| #if AMEND_LEXER_BUFFER_INPUT | ||||
|     if (inputFile == NULL) { | ||||
|         fprintf(stderr, "amend: No input file\n"); | ||||
|         usage(); | ||||
|     } | ||||
|     char *fileData; | ||||
|     int fileDataLen; | ||||
|     fseek(inputFile, 0, SEEK_END); | ||||
|     fileDataLen = ftell(inputFile); | ||||
|     rewind(inputFile); | ||||
|     if (fileDataLen < 0) { | ||||
|         fprintf(stderr, "amend: Can't get file length\n"); | ||||
|         exit(2); | ||||
|     } else if (fileDataLen == 0) { | ||||
|         printf("amend: Empty input file\n"); | ||||
|         exit(0); | ||||
|     } | ||||
|     fileData = (char *)malloc(fileDataLen + 1); | ||||
|     if (fileData == NULL) { | ||||
|         fprintf(stderr, "amend: Can't allocate %d bytes\n", fileDataLen + 1); | ||||
|         exit(2); | ||||
|     } | ||||
|     size_t nread = fread(fileData, 1, fileDataLen, inputFile); | ||||
|     if (nread != (size_t)fileDataLen) { | ||||
|         fprintf(stderr, "amend: Didn't read %d bytes, only %zd\n", fileDataLen, | ||||
|                 nread); | ||||
|         exit(2); | ||||
|     } | ||||
|     fileData[fileDataLen] = '\0'; | ||||
|     setLexerInputBuffer(fileData, fileDataLen); | ||||
| #else | ||||
|     if (inputFile == NULL) { | ||||
|         inputFile = stdin; | ||||
|     } | ||||
|     yyset_in(inputFile); | ||||
| #endif | ||||
|  | ||||
|     if (debugLex) { | ||||
|         lexTest(); | ||||
|     } else { | ||||
|         int ret = yyparse(); | ||||
|         if (ret != 0) { | ||||
|             fprintf(stderr, "amend: Parse failed (%d)\n", ret); | ||||
|             exit(2); | ||||
|         } else { | ||||
|             if (debugAst) { | ||||
|                 dumpCommandList(gCommands); | ||||
|             } | ||||
| printf("amend: Parse successful.\n"); | ||||
|             ret = execCommandList((ExecContext *)1, gCommands); | ||||
|             if (ret != 0) { | ||||
|                 fprintf(stderr, "amend: Execution failed (%d)\n", ret); | ||||
|                 exit(3); | ||||
|             } | ||||
| printf("amend: Execution successful.\n"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										24
									
								
								amend/parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								amend/parser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| /* | ||||
|  * 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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										430
									
								
								amend/parser_y.y
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,430 @@ | ||||
| /* | ||||
|  * 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 */ | ||||
							
								
								
									
										270
									
								
								amend/permissions.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								amend/permissions.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,270 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include "permissions.h" | ||||
|  | ||||
| int | ||||
| initPermissionRequestList(PermissionRequestList *list) | ||||
| { | ||||
|     if (list != NULL) { | ||||
|         list->requests = NULL; | ||||
|         list->numRequests = 0; | ||||
|         list->requestsAllocated = 0; | ||||
|         return 0; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| int | ||||
| addPermissionRequestToList(PermissionRequestList *list, | ||||
|         const char *path, bool recursive, unsigned int permissions) | ||||
| { | ||||
|     if (list == NULL || list->numRequests < 0 || | ||||
|             list->requestsAllocated < list->numRequests || path == NULL) | ||||
|     { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (list->numRequests == list->requestsAllocated) { | ||||
|         int newSize; | ||||
|         PermissionRequest *newRequests; | ||||
|  | ||||
|         newSize = list->requestsAllocated * 2; | ||||
|         if (newSize < 16) { | ||||
|             newSize = 16; | ||||
|         } | ||||
|         newRequests = (PermissionRequest *)realloc(list->requests, | ||||
|                 newSize * sizeof(PermissionRequest)); | ||||
|         if (newRequests == NULL) { | ||||
|             return -2; | ||||
|         } | ||||
|         list->requests = newRequests; | ||||
|         list->requestsAllocated = newSize; | ||||
|     } | ||||
|  | ||||
|     PermissionRequest *req; | ||||
|     req = &list->requests[list->numRequests++]; | ||||
|     req->path = strdup(path); | ||||
|     if (req->path == NULL) { | ||||
|         list->numRequests--; | ||||
|         return -3; | ||||
|     } | ||||
|     req->recursive = recursive; | ||||
|     req->requested = permissions; | ||||
|     req->allowed = 0; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void | ||||
| freePermissionRequestListElements(PermissionRequestList *list) | ||||
| { | ||||
|     if (list != NULL && list->numRequests >= 0 && | ||||
|             list->requestsAllocated >= list->numRequests) | ||||
|     { | ||||
|         int i; | ||||
|         for (i = 0; i < list->numRequests; i++) { | ||||
|             free((void *)list->requests[i].path); | ||||
|         } | ||||
|         free(list->requests); | ||||
|         initPermissionRequestList(list); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Global permission table | ||||
|  */ | ||||
|  | ||||
| static struct { | ||||
|     Permission *permissions; | ||||
|     int numPermissionEntries; | ||||
|     int allocatedPermissionEntries; | ||||
|     bool permissionStateInitialized; | ||||
| } gPermissionState = { | ||||
| #if 1 | ||||
|     NULL, 0, 0, false | ||||
| #else | ||||
|     .permissions = NULL, | ||||
|     .numPermissionEntries = 0, | ||||
|     .allocatedPermissionEntries = 0, | ||||
|     .permissionStateInitialized = false | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| int | ||||
| permissionInit() | ||||
| { | ||||
|     if (gPermissionState.permissionStateInitialized) { | ||||
|         return -1; | ||||
|     } | ||||
|     gPermissionState.permissions = NULL; | ||||
|     gPermissionState.numPermissionEntries = 0; | ||||
|     gPermissionState.allocatedPermissionEntries = 0; | ||||
|     gPermissionState.permissionStateInitialized = true; | ||||
| //xxx maybe add an "namespace root gets no permissions" fallback by default | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void | ||||
| permissionCleanup() | ||||
| { | ||||
|     if (gPermissionState.permissionStateInitialized) { | ||||
|         gPermissionState.permissionStateInitialized = false; | ||||
|         if (gPermissionState.permissions != NULL) { | ||||
|             int i; | ||||
|             for (i = 0; i < gPermissionState.numPermissionEntries; i++) { | ||||
|                 free((void *)gPermissionState.permissions[i].path); | ||||
|             } | ||||
|             free(gPermissionState.permissions); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| int | ||||
| getPermissionCount() | ||||
| { | ||||
|     if (gPermissionState.permissionStateInitialized) { | ||||
|         return gPermissionState.numPermissionEntries; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| const Permission * | ||||
| getPermissionAt(int index) | ||||
| { | ||||
|     if (!gPermissionState.permissionStateInitialized) { | ||||
|         return NULL; | ||||
|     } | ||||
|     if (index < 0 || index >= gPermissionState.numPermissionEntries) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return &gPermissionState.permissions[index]; | ||||
| } | ||||
|  | ||||
| int | ||||
| getAllowedPermissions(const char *path, bool recursive, | ||||
|         unsigned int *outAllowed) | ||||
| { | ||||
|     if (!gPermissionState.permissionStateInitialized) { | ||||
|         return -2; | ||||
|     } | ||||
|     if (outAllowed == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|     *outAllowed = 0; | ||||
|     if (path == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|     //TODO: implement this for real. | ||||
|     recursive = false; | ||||
|     *outAllowed = PERMSET_ALL; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| countPermissionConflicts(PermissionRequestList *requests, bool updateAllowed) | ||||
| { | ||||
|     if (!gPermissionState.permissionStateInitialized) { | ||||
|         return -2; | ||||
|     } | ||||
|     if (requests == NULL || requests->requests == NULL || | ||||
|             requests->numRequests < 0 || | ||||
|             requests->requestsAllocated < requests->numRequests) | ||||
|     { | ||||
|         return -1; | ||||
|     } | ||||
|     int conflicts = 0; | ||||
|     int i; | ||||
|     for (i = 0; i < requests->numRequests; i++) { | ||||
|         PermissionRequest *req; | ||||
|         unsigned int allowed; | ||||
|         int ret; | ||||
|  | ||||
|         req = &requests->requests[i]; | ||||
|         ret = getAllowedPermissions(req->path, req->recursive, &allowed); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|         if ((req->requested & ~allowed) != 0) { | ||||
|             conflicts++; | ||||
|         } | ||||
|         if (updateAllowed) { | ||||
|             req->allowed = allowed; | ||||
|         } | ||||
|     } | ||||
|     return conflicts; | ||||
| } | ||||
|  | ||||
| int | ||||
| registerPermissionSet(int count, Permission *set) | ||||
| { | ||||
|     if (!gPermissionState.permissionStateInitialized) { | ||||
|         return -2; | ||||
|     } | ||||
|     if (count < 0 || (count > 0 && set == NULL)) { | ||||
|         return -1; | ||||
|     } | ||||
|     if (count == 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (gPermissionState.numPermissionEntries + count >= | ||||
|             gPermissionState.allocatedPermissionEntries) | ||||
|     { | ||||
|         Permission *newList; | ||||
|         int newSize; | ||||
|  | ||||
|         newSize = (gPermissionState.allocatedPermissionEntries + count) * 2; | ||||
|         if (newSize < 16) { | ||||
|             newSize = 16; | ||||
|         } | ||||
|         newList = (Permission *)realloc(gPermissionState.permissions, | ||||
|                 newSize * sizeof(Permission)); | ||||
|         if (newList == NULL) { | ||||
|             return -3; | ||||
|         } | ||||
|         gPermissionState.permissions = newList; | ||||
|         gPermissionState.allocatedPermissionEntries = newSize; | ||||
|     } | ||||
|  | ||||
|     Permission *p = &gPermissionState.permissions[ | ||||
|                         gPermissionState.numPermissionEntries]; | ||||
|     int i; | ||||
|     for (i = 0; i < count; i++) { | ||||
|         *p = set[i]; | ||||
|         //TODO: cache the strlen of the path | ||||
|         //TODO: normalize; strip off trailing / | ||||
|         p->path = strdup(p->path); | ||||
|         if (p->path == NULL) { | ||||
|             /* If we can't add all of the entries, we don't | ||||
|              * add any of them. | ||||
|              */ | ||||
|             Permission *pp = &gPermissionState.permissions[ | ||||
|                                 gPermissionState.numPermissionEntries]; | ||||
|             while (pp != p) { | ||||
|                 free((void *)pp->path); | ||||
|                 pp++; | ||||
|             } | ||||
|             return -4; | ||||
|         } | ||||
|         p++; | ||||
|     } | ||||
|     gPermissionState.numPermissionEntries += count; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										111
									
								
								amend/permissions.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								amend/permissions.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #ifndef AMEND_PERMISSIONS_H_ | ||||
| #define AMEND_PERMISSIONS_H_ | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #define PERM_NONE   (0) | ||||
| #define PERM_STAT   (1<<0) | ||||
| #define PERM_READ   (1<<1) | ||||
| #define PERM_WRITE  (1<<2)  // including create, delete, mkdir, rmdir | ||||
| #define PERM_CHMOD  (1<<3) | ||||
| #define PERM_CHOWN  (1<<4) | ||||
| #define PERM_CHGRP  (1<<5) | ||||
| #define PERM_SETUID (1<<6) | ||||
| #define PERM_SETGID (1<<7) | ||||
|  | ||||
| #define PERMSET_READ (PERM_STAT | PERM_READ) | ||||
| #define PERMSET_WRITE (PERMSET_READ | PERM_WRITE) | ||||
|  | ||||
| #define PERMSET_ALL \ | ||||
|     (PERM_STAT | PERM_READ | PERM_WRITE | PERM_CHMOD | \ | ||||
|     PERM_CHOWN | PERM_CHGRP | PERM_SETUID | PERM_SETGID) | ||||
|  | ||||
| typedef struct { | ||||
|     unsigned int requested; | ||||
|     unsigned int allowed; | ||||
|     const char *path; | ||||
|     bool recursive; | ||||
| } PermissionRequest; | ||||
|  | ||||
| typedef struct { | ||||
|     PermissionRequest *requests; | ||||
|     int numRequests; | ||||
|     int requestsAllocated; | ||||
| } PermissionRequestList; | ||||
|  | ||||
| /* Properly clear out a PermissionRequestList. | ||||
|  * | ||||
|  * @return 0 if list is non-NULL, negative otherwise. | ||||
|  */ | ||||
| int initPermissionRequestList(PermissionRequestList *list); | ||||
|  | ||||
| /* Add a permission request to the list, allocating more space | ||||
|  * if necessary. | ||||
|  * | ||||
|  * @return 0 on success or a negative value on failure. | ||||
|  */ | ||||
| int addPermissionRequestToList(PermissionRequestList *list, | ||||
|         const char *path, bool recursive, unsigned int permissions); | ||||
|  | ||||
| /* Free anything allocated by addPermissionRequestToList().  The caller | ||||
|  * is responsible for freeing the actual PermissionRequestList. | ||||
|  */ | ||||
| void freePermissionRequestListElements(PermissionRequestList *list); | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Global permission table | ||||
|  */ | ||||
|  | ||||
| typedef struct { | ||||
|     const char *path; | ||||
|     unsigned int allowed; | ||||
| } Permission; | ||||
|  | ||||
| int permissionInit(void); | ||||
| void permissionCleanup(void); | ||||
|  | ||||
| /* Returns the allowed permissions for the path in "outAllowed". | ||||
|  * Returns 0 if successful, negative if a parameter or global state | ||||
|  * is bad. | ||||
|  */ | ||||
| int getAllowedPermissions(const char *path, bool recursive, | ||||
|         unsigned int *outAllowed); | ||||
|  | ||||
| /* More-recently-registered permissions override older permissions. | ||||
|  */ | ||||
| int registerPermissionSet(int count, Permission *set); | ||||
|  | ||||
| /* Check to make sure that each request is allowed. | ||||
|  * | ||||
|  * @param requests The list of permission requests | ||||
|  * @param updateAllowed If true, update the "allowed" field in each | ||||
|  *                      element of the list | ||||
|  * @return the number of requests that were denied, or negative if | ||||
|  *         an error occurred. | ||||
|  */ | ||||
| int countPermissionConflicts(PermissionRequestList *requests, | ||||
|         bool updateAllowed); | ||||
|  | ||||
| /* Inspection/testing/debugging functions | ||||
|  */ | ||||
| int getPermissionCount(void); | ||||
| const Permission *getPermissionAt(int index); | ||||
|  | ||||
| #endif  // AMEND_PERMISSIONS_H_ | ||||
							
								
								
									
										394
									
								
								amend/register.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										394
									
								
								amend/register.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,394 @@ | ||||
| /* | ||||
|  * Copyright (C) 2007 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #undef NDEBUG | ||||
| #include <assert.h> | ||||
| #include "commands.h" | ||||
|  | ||||
| #include "register.h" | ||||
|  | ||||
| #define UNUSED(p)   ((void)(p)) | ||||
|  | ||||
| #define CHECK_BOOL() \ | ||||
|     do { \ | ||||
|         assert(argv == NULL); \ | ||||
|         if (argv != NULL) return -1; \ | ||||
|         assert(argc == true || argc == false); \ | ||||
|         if (argc != true && argc != false) return -1; \ | ||||
|     } while (false) | ||||
|  | ||||
| #define CHECK_WORDS() \ | ||||
|     do { \ | ||||
|         assert(argc >= 0); \ | ||||
|         if (argc < 0) return -1; \ | ||||
|         assert(argc == 0 || argv != NULL); \ | ||||
|         if (argc != 0 && argv == NULL) return -1; \ | ||||
|         if (permissions != NULL) { \ | ||||
|             int CW_I_; \ | ||||
|             for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \ | ||||
|                 assert(argv[CW_I_] != NULL); \ | ||||
|                 if (argv[CW_I_] == NULL) return -1; \ | ||||
|             } \ | ||||
|         } \ | ||||
|     } while (false) | ||||
|  | ||||
| #define CHECK_FN() \ | ||||
|     do { \ | ||||
|         CHECK_WORDS(); \ | ||||
|         if (permissions != NULL) { \ | ||||
|             assert(result == NULL); \ | ||||
|             if (result != NULL) return -1; \ | ||||
|         } else { \ | ||||
|             assert(result != NULL); \ | ||||
|             if (result == NULL) return -1; \ | ||||
|         } \ | ||||
|     } while (false) | ||||
|  | ||||
| #define NO_PERMS(perms) \ | ||||
|     do { \ | ||||
|         PermissionRequestList *NP_PRL_ = (perms); \ | ||||
|         if (NP_PRL_ != NULL) { \ | ||||
|             int NP_RET_ = addPermissionRequestToList(NP_PRL_, \ | ||||
|                     "", false, PERM_NONE); \ | ||||
|             if (NP_RET_ < 0) { \ | ||||
|                 /* Returns from the calling function. \ | ||||
|                  */ \ | ||||
|                 return NP_RET_; \ | ||||
|             } \ | ||||
|         } \ | ||||
|     } while (false) | ||||
|  | ||||
| /* | ||||
|  * Command definitions | ||||
|  */ | ||||
|  | ||||
| /* assert <boolexpr> | ||||
|  */ | ||||
| static int | ||||
| cmd_assert(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_BOOL(); | ||||
|     NO_PERMS(permissions); | ||||
|  | ||||
|     /* If our argument is false, return non-zero (failure) | ||||
|      * If our argument is true, return zero (success) | ||||
|      */ | ||||
|     if (argc) { | ||||
|         return 0; | ||||
|     } else { | ||||
|         return 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* format <root> | ||||
|  */ | ||||
| static int | ||||
| cmd_format(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_WORDS(); | ||||
| //xxx | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| /* copy_dir <srcdir> <dstdir> | ||||
|  */ | ||||
| static int | ||||
| cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_WORDS(); | ||||
| //xxx | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| /* mark <resource> dirty|clean | ||||
|  */ | ||||
| static int | ||||
| cmd_mark(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_WORDS(); | ||||
| //xxx when marking, save the top-level hash at the mark point | ||||
| //    so we can retry on failure.  Otherwise the hashes won't match, | ||||
| //    or someone could intentionally dirty the FS to force a downgrade | ||||
| //xxx | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| /* done | ||||
|  */ | ||||
| static int | ||||
| cmd_done(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_WORDS(); | ||||
| //xxx | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| int | ||||
| registerUpdateCommands() | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
| //xxx some way to fix permissions | ||||
| //xxx could have "installperms" commands that build the fs_config list | ||||
| //xxx along with a "commitperms", and any copy_dir etc. needs to see | ||||
| //    a commitperms before it will work | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Function definitions | ||||
|  */ | ||||
|  | ||||
| /* update_forced() | ||||
|  * | ||||
|  * Returns "true" if some system setting has determined that | ||||
|  * the update should happen no matter what. | ||||
|  */ | ||||
| static int | ||||
| fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen, | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|     NO_PERMS(permissions); | ||||
|  | ||||
|     if (argc != 0) { | ||||
|         fprintf(stderr, "%s: wrong number of arguments (%d)\n", | ||||
|                 name, argc); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     //xxx check some global or property | ||||
|     bool force = true; | ||||
|     if (force) { | ||||
|         *result = strdup("true"); | ||||
|     } else { | ||||
|         *result = strdup(""); | ||||
|     } | ||||
|     if (resultLen != NULL) { | ||||
|         *resultLen = strlen(*result); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* get_mark(<resource>) | ||||
|  * | ||||
|  * Returns the current mark associated with the provided resource. | ||||
|  */ | ||||
| static int | ||||
| fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen, | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|     NO_PERMS(permissions); | ||||
|  | ||||
|     if (argc != 1) { | ||||
|         fprintf(stderr, "%s: wrong number of arguments (%d)\n", | ||||
|                 name, argc); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     //xxx look up the value | ||||
|     *result = strdup(""); | ||||
|     if (resultLen != NULL) { | ||||
|         *resultLen = strlen(*result); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* hash_dir(<path-to-directory>) | ||||
|  */ | ||||
| static int | ||||
| fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen, | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     int ret = -1; | ||||
|  | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|  | ||||
|     const char *dir; | ||||
|     if (argc != 1) { | ||||
|         fprintf(stderr, "%s: wrong number of arguments (%d)\n", | ||||
|                 name, argc); | ||||
|         return 1; | ||||
|     } else { | ||||
|         dir = argv[0]; | ||||
|     } | ||||
|  | ||||
|     if (permissions != NULL) { | ||||
|         if (dir == NULL) { | ||||
|             /* The argument is the result of another function. | ||||
|              * Assume the worst case, where the function returns | ||||
|              * the root. | ||||
|              */ | ||||
|             dir = "/"; | ||||
|         } | ||||
|         ret = addPermissionRequestToList(permissions, dir, true, PERM_READ); | ||||
|     } else { | ||||
| //xxx build and return the string | ||||
|         *result = strdup("hashvalue"); | ||||
|         if (resultLen != NULL) { | ||||
|             *resultLen = strlen(*result); | ||||
|         } | ||||
|         ret = 0; | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* matches(<str>, <str1> [, <strN>...]) | ||||
|  * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>, | ||||
|  * otherwise returns "". | ||||
|  * | ||||
|  * E.g., assert matches(hash_dir("/path"), "hash1", "hash2") | ||||
|  */ | ||||
| static int | ||||
| fn_matches(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen, | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|     NO_PERMS(permissions); | ||||
|  | ||||
|     if (argc < 2) { | ||||
|         fprintf(stderr, "%s: not enough arguments (%d < 2)\n", | ||||
|                 name, argc); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     int i; | ||||
|     for (i = 1; i < argc; i++) { | ||||
|         if (strcmp(argv[0], argv[i]) == 0) { | ||||
|             *result = strdup(argv[0]); | ||||
|             if (resultLen != NULL) { | ||||
|                 *resultLen = strlen(*result); | ||||
|             } | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     *result = strdup(""); | ||||
|     if (resultLen != NULL) { | ||||
|         *resultLen = 1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* concat(<str>, <str1> [, <strN>...]) | ||||
|  * Returns the concatenation of all strings. | ||||
|  */ | ||||
| static int | ||||
| fn_concat(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen, | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     UNUSED(name); | ||||
|     UNUSED(cookie); | ||||
|     CHECK_FN(); | ||||
|     NO_PERMS(permissions); | ||||
|  | ||||
|     size_t totalLen = 0; | ||||
|     int i; | ||||
|     for (i = 0; i < argc; i++) { | ||||
|         totalLen += strlen(argv[i]); | ||||
|     } | ||||
|  | ||||
|     char *s = (char *)malloc(totalLen + 1); | ||||
|     if (s == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|     s[totalLen] = '\0'; | ||||
|     for (i = 0; i < argc; i++) { | ||||
|         //TODO: keep track of the end to avoid walking the string each time | ||||
|         strcat(s, argv[i]); | ||||
|     } | ||||
|     *result = s; | ||||
|     if (resultLen != NULL) { | ||||
|         *resultLen = strlen(s); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| registerUpdateFunctions() | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = registerFunction("update_forced", fn_update_forced, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     ret = registerFunction("get_mark", fn_get_mark, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     ret = registerFunction("hash_dir", fn_hash_dir, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     ret = registerFunction("matches", fn_matches, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     ret = registerFunction("concat", fn_concat, NULL); | ||||
|     if (ret < 0) return ret; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										23
									
								
								amend/register.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								amend/register.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| /* | ||||
|  * 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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								amend/symtab.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| /* | ||||
|  * 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; | ||||
| } | ||||
							
								
								
									
										34
									
								
								amend/symtab.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								amend/symtab.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /* | ||||
|  * 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_ | ||||
							
								
								
									
										538
									
								
								amend/test_commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										538
									
								
								amend/test_commands.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,538 @@ | ||||
| /* | ||||
|  * 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; | ||||
|     PermissionRequestList *permissions; | ||||
|     int returnValue; | ||||
|     char *functionResult; | ||||
| } gTestCommandState; | ||||
|  | ||||
| static int | ||||
| testCommand(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         PermissionRequestList *permissions) | ||||
| { | ||||
|     gTestCommandState.called = true; | ||||
|     gTestCommandState.name = name; | ||||
|     gTestCommandState.cookie = cookie; | ||||
|     gTestCommandState.argc = argc; | ||||
|     gTestCommandState.argv = argv; | ||||
|     gTestCommandState.permissions = permissions; | ||||
|     return gTestCommandState.returnValue; | ||||
| } | ||||
|  | ||||
| static int | ||||
| testFunction(const char *name, void *cookie, int argc, const char *argv[], | ||||
|         char **result, size_t *resultLen, PermissionRequestList *permissions) | ||||
| { | ||||
|     gTestCommandState.called = true; | ||||
|     gTestCommandState.name = name; | ||||
|     gTestCommandState.cookie = cookie; | ||||
|     gTestCommandState.argc = argc; | ||||
|     gTestCommandState.argv = argv; | ||||
|     gTestCommandState.permissions = permissions; | ||||
|     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; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)1; | ||||
|     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); | ||||
|     assert(gTestCommandState.permissions == NULL); | ||||
|  | ||||
|     /* 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; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)1; | ||||
|     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); | ||||
|     assert(gTestCommandState.permissions == NULL); | ||||
|  | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 13; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)1; | ||||
|     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); | ||||
|     assert(gTestCommandState.permissions == NULL); | ||||
|  | ||||
|     /* Try looking up permissions. | ||||
|      */ | ||||
|     PermissionRequestList permissions; | ||||
|     cmd = findCommand("one"); | ||||
|     assert(cmd != NULL); | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 27; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)1; | ||||
|     argv[1] = NULL; // null out an arg, which should be ok | ||||
|     ret = getCommandPermissions(cmd, argc, argv, &permissions); | ||||
|     assert(ret == 27); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "one") == 0); | ||||
|     assert(gTestCommandState.cookie == &gTestCommandState); | ||||
|     assert(gTestCommandState.argc == argc); | ||||
|     assert(gTestCommandState.argv == argv); | ||||
|     assert(gTestCommandState.permissions == &permissions); | ||||
|  | ||||
|     /* Boolean command permissions | ||||
|      */ | ||||
|     cmd = findCommand("bool"); | ||||
|     assert(cmd != NULL); | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 55; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)1; | ||||
|     // argv[1] is still NULL | ||||
|     ret = getBooleanCommandPermissions(cmd, true, &permissions); | ||||
|     assert(ret == 55); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "bool") == 0); | ||||
|     assert(gTestCommandState.cookie == &gTestCommandState); | ||||
|     assert(gTestCommandState.argc == 1); | ||||
|     assert(gTestCommandState.argv == NULL); | ||||
|     assert(gTestCommandState.permissions == &permissions); | ||||
|  | ||||
|  | ||||
|     /* 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"; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)1; | ||||
|     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(gTestCommandState.permissions == NULL); | ||||
|     assert(strcmp(functionResult, "1234") == 0); | ||||
|     assert(functionResultLen == strlen(functionResult)); | ||||
|  | ||||
|     /* Try looking up permissions. | ||||
|      */ | ||||
|     PermissionRequestList permissions; | ||||
|     fn = findFunction("one"); | ||||
|     assert(fn != NULL); | ||||
|     memset(&gTestCommandState, 0, sizeof(gTestCommandState)); | ||||
|     gTestCommandState.called = false; | ||||
|     gTestCommandState.returnValue = 27; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)1; | ||||
|     argv[1] = NULL; // null out an arg, which should be ok | ||||
|     ret = getFunctionPermissions(fn, argc, argv, &permissions); | ||||
|     assert(ret == 27); | ||||
|     assert(gTestCommandState.called); | ||||
|     assert(strcmp(gTestCommandState.name, "one") == 0); | ||||
|     assert(gTestCommandState.cookie == &gTestCommandState); | ||||
|     assert(gTestCommandState.argc == argc); | ||||
|     assert(gTestCommandState.argv == argv); | ||||
|     assert(gTestCommandState.permissions == &permissions); | ||||
|  | ||||
|     /* 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; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)1; | ||||
|     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); | ||||
|     assert(gTestCommandState.permissions == NULL); | ||||
|  | ||||
|     /* 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"; | ||||
|     gTestCommandState.permissions = (PermissionRequestList *)2; | ||||
|     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(gTestCommandState.permissions == NULL); | ||||
|     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; | ||||
| } | ||||
							
								
								
									
										347
									
								
								amend/test_permissions.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										347
									
								
								amend/test_permissions.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,347 @@ | ||||
| /* | ||||
|  * 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 "permissions.h" | ||||
|  | ||||
| static int | ||||
| test_permission_list() | ||||
| { | ||||
|     PermissionRequestList list; | ||||
|     int ret; | ||||
|     int numRequests; | ||||
|  | ||||
|     /* Bad parameter | ||||
|      */ | ||||
|     ret = initPermissionRequestList(NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Good parameter | ||||
|      */ | ||||
|     ret = initPermissionRequestList(&list); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Bad parameters | ||||
|      */ | ||||
|     ret = addPermissionRequestToList(NULL, NULL, false, 0); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = addPermissionRequestToList(&list, NULL, false, 0); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Good parameters | ||||
|      */ | ||||
|     numRequests = 0; | ||||
|  | ||||
|     ret = addPermissionRequestToList(&list, "one", false, 1); | ||||
|     assert(ret == 0); | ||||
|     numRequests++; | ||||
|  | ||||
|     ret = addPermissionRequestToList(&list, "two", false, 2); | ||||
|     assert(ret == 0); | ||||
|     numRequests++; | ||||
|  | ||||
|     ret = addPermissionRequestToList(&list, "three", false, 3); | ||||
|     assert(ret == 0); | ||||
|     numRequests++; | ||||
|  | ||||
|     ret = addPermissionRequestToList(&list, "recursive", true, 55); | ||||
|     assert(ret == 0); | ||||
|     numRequests++; | ||||
|  | ||||
|     /* Validate the list | ||||
|      */ | ||||
|     assert(list.requests != NULL); | ||||
|     assert(list.numRequests == numRequests); | ||||
|     assert(list.numRequests <= list.requestsAllocated); | ||||
|     bool sawOne = false; | ||||
|     bool sawTwo = false; | ||||
|     bool sawThree = false; | ||||
|     bool sawRecursive = false; | ||||
|     int i; | ||||
|     for (i = 0; i < list.numRequests; i++) { | ||||
|         PermissionRequest *req = &list.requests[i]; | ||||
|         assert(req->allowed == 0); | ||||
|  | ||||
|         /* Order isn't guaranteed, so we have to switch every time. | ||||
|          */ | ||||
|         if (strcmp(req->path, "one") == 0) { | ||||
|             assert(!sawOne); | ||||
|             assert(req->requested == 1); | ||||
|             assert(!req->recursive); | ||||
|             sawOne = true; | ||||
|         } else if (strcmp(req->path, "two") == 0) { | ||||
|             assert(!sawTwo); | ||||
|             assert(req->requested == 2); | ||||
|             assert(!req->recursive); | ||||
|             sawTwo = true; | ||||
|         } else if (strcmp(req->path, "three") == 0) { | ||||
|             assert(!sawThree); | ||||
|             assert(req->requested == 3); | ||||
|             assert(!req->recursive); | ||||
|             sawThree = true; | ||||
|         } else if (strcmp(req->path, "recursive") == 0) { | ||||
|             assert(!sawRecursive); | ||||
|             assert(req->requested == 55); | ||||
|             assert(req->recursive); | ||||
|             sawRecursive = true; | ||||
|         } else { | ||||
|             assert(false); | ||||
|         } | ||||
|     } | ||||
|     assert(sawOne); | ||||
|     assert(sawTwo); | ||||
|     assert(sawThree); | ||||
|     assert(sawRecursive); | ||||
|  | ||||
|     /* Smoke test the teardown | ||||
|      */ | ||||
|     freePermissionRequestListElements(&list); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| test_permission_table() | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     /* Test the global permissions table. | ||||
|      * Try calling functions without initializing first. | ||||
|      */ | ||||
|     ret = registerPermissionSet(0, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = countPermissionConflicts((PermissionRequestList *)16, false); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = getPermissionCount(); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     const Permission *p; | ||||
|     p = getPermissionAt(0); | ||||
|     assert(p == NULL); | ||||
|  | ||||
|     /* Initialize. | ||||
|      */ | ||||
|     ret = permissionInit(); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Make sure we can't initialize twice. | ||||
|      */ | ||||
|     ret = permissionInit(); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Test the inspection functions. | ||||
|      */ | ||||
|     ret = getPermissionCount(); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     p = getPermissionAt(-1); | ||||
|     assert(p == NULL); | ||||
|  | ||||
|     p = getPermissionAt(0); | ||||
|     assert(p == NULL); | ||||
|  | ||||
|     p = getPermissionAt(1); | ||||
|     assert(p == NULL); | ||||
|  | ||||
|     /* Test registerPermissionSet(). | ||||
|      * Try some bad parameter values. | ||||
|      */ | ||||
|     ret = registerPermissionSet(-1, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = registerPermissionSet(1, NULL); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Register some permissions. | ||||
|      */ | ||||
|     Permission p1; | ||||
|     p1.path = "one"; | ||||
|     p1.allowed = 1; | ||||
|     ret = registerPermissionSet(1, &p1); | ||||
|     assert(ret == 0); | ||||
|     ret = getPermissionCount(); | ||||
|     assert(ret == 1); | ||||
|  | ||||
|     Permission p2[2]; | ||||
|     p2[0].path = "two"; | ||||
|     p2[0].allowed = 2; | ||||
|     p2[1].path = "three"; | ||||
|     p2[1].allowed = 3; | ||||
|     ret = registerPermissionSet(2, p2); | ||||
|     assert(ret == 0); | ||||
|     ret = getPermissionCount(); | ||||
|     assert(ret == 3); | ||||
|  | ||||
|     ret = registerPermissionSet(0, NULL); | ||||
|     assert(ret == 0); | ||||
|     ret = getPermissionCount(); | ||||
|     assert(ret == 3); | ||||
|  | ||||
|     p1.path = "four"; | ||||
|     p1.allowed = 4; | ||||
|     ret = registerPermissionSet(1, &p1); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Make sure the table looks correct. | ||||
|      * Order is important;  more-recent additions | ||||
|      * should appear at higher indices. | ||||
|      */ | ||||
|     ret = getPermissionCount(); | ||||
|     assert(ret == 4); | ||||
|  | ||||
|     int i; | ||||
|     for (i = 0; i < ret; i++) { | ||||
|         const Permission *p; | ||||
|         p = getPermissionAt(i); | ||||
|         assert(p != NULL); | ||||
|         assert(p->allowed == (unsigned int)(i + 1)); | ||||
|         switch (i) { | ||||
|         case 0: | ||||
|             assert(strcmp(p->path, "one") == 0); | ||||
|             break; | ||||
|         case 1: | ||||
|             assert(strcmp(p->path, "two") == 0); | ||||
|             break; | ||||
|         case 2: | ||||
|             assert(strcmp(p->path, "three") == 0); | ||||
|             break; | ||||
|         case 3: | ||||
|             assert(strcmp(p->path, "four") == 0); | ||||
|             break; | ||||
|         default: | ||||
|             assert(!"internal error"); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     p = getPermissionAt(ret); | ||||
|     assert(p == NULL); | ||||
|  | ||||
|     /* Smoke test the teardown | ||||
|      */ | ||||
|     permissionCleanup(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| test_allowed_permissions() | ||||
| { | ||||
|     int ret; | ||||
|     int numPerms; | ||||
|  | ||||
|     /* Make sure these fail before initialization. | ||||
|      */ | ||||
|     ret = countPermissionConflicts((PermissionRequestList *)1, false); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     ret = getAllowedPermissions((const char *)1, false, (unsigned int *)1); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Initialize. | ||||
|      */ | ||||
|     ret = permissionInit(); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Make sure countPermissionConflicts() fails with bad parameters. | ||||
|      */ | ||||
|     ret = countPermissionConflicts(NULL, false); | ||||
|     assert(ret < 0); | ||||
|  | ||||
|     /* Register a set of permissions. | ||||
|      */ | ||||
|     Permission perms[] = { | ||||
|         { "/", PERM_NONE }, | ||||
|         { "/stat", PERM_STAT }, | ||||
|         { "/read", PERMSET_READ }, | ||||
|         { "/write", PERMSET_WRITE }, | ||||
|         { "/.stat", PERM_STAT }, | ||||
|         { "/.stat/.read", PERMSET_READ }, | ||||
|         { "/.stat/.read/.write", PERMSET_WRITE }, | ||||
|         { "/.stat/.write", PERMSET_WRITE }, | ||||
|     }; | ||||
|     numPerms = sizeof(perms) / sizeof(perms[0]); | ||||
|     ret = registerPermissionSet(numPerms, perms); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Build a permission request list. | ||||
|      */ | ||||
|     PermissionRequestList list; | ||||
|     ret = initPermissionRequestList(&list); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = addPermissionRequestToList(&list, "/stat", false, PERM_STAT); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = addPermissionRequestToList(&list, "/read", false, PERM_READ); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = addPermissionRequestToList(&list, "/write", false, PERM_WRITE); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     //TODO: cover more cases once the permission stuff has been implemented | ||||
|  | ||||
|     /* All of the requests in the list should be allowed. | ||||
|      */ | ||||
|     ret = countPermissionConflicts(&list, false); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     /* Add a request that will be denied. | ||||
|      */ | ||||
|     ret = addPermissionRequestToList(&list, "/stat", false, 1<<31 | PERM_STAT); | ||||
|     assert(ret == 0); | ||||
|  | ||||
|     ret = countPermissionConflicts(&list, false); | ||||
|     assert(ret == 1); | ||||
|  | ||||
|     //TODO: more tests | ||||
|  | ||||
|     permissionCleanup(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| test_permissions() | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = test_permission_list(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_permission_list() failed: %d\n", ret); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     ret = test_permission_table(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_permission_table() failed: %d\n", ret); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     ret = test_allowed_permissions(); | ||||
|     if (ret != 0) { | ||||
|         fprintf(stderr, "test_permission_table() failed: %d\n", ret); | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										146
									
								
								amend/test_symtab.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								amend/test_symtab.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| /* | ||||
|  * 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
									
								
								amend/tests/001-nop/expected.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/001-nop/expected.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| I am a jelly donut. | ||||
							
								
								
									
										2
									
								
								amend/tests/001-nop/info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								amend/tests/001-nop/info.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| This is a sample no-op test, which does at least serve to verify that the | ||||
| test harness is working. | ||||
							
								
								
									
										17
									
								
								amend/tests/001-nop/run
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								amend/tests/001-nop/run
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #!/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.' | ||||
							
								
								
									
										0
									
								
								amend/tests/002-lex-empty/SKIP
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								amend/tests/002-lex-empty/SKIP
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								amend/tests/002-lex-empty/expected.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/002-lex-empty/expected.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
|  EOF | ||||
							
								
								
									
										1
									
								
								amend/tests/002-lex-empty/info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/002-lex-empty/info.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Test to make sure that an empty file is accepted properly. | ||||
							
								
								
									
										0
									
								
								amend/tests/002-lex-empty/input
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								amend/tests/002-lex-empty/input
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										17
									
								
								amend/tests/002-lex-empty/run
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								amend/tests/002-lex-empty/run
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #!/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 | ||||
							
								
								
									
										13
									
								
								amend/tests/003-lex-command/expected.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								amend/tests/003-lex-command/expected.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
|  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
									
								
								amend/tests/003-lex-command/info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/003-lex-command/info.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Test to make sure that simple command names are tokenized properly. | ||||
							
								
								
									
										6
									
								
								amend/tests/003-lex-command/input
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								amend/tests/003-lex-command/input
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| 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
									
								
								amend/tests/003-lex-command/input2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/003-lex-command/input2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| "quoted" | ||||
							
								
								
									
										1
									
								
								amend/tests/003-lex-command/input3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/003-lex-command/input3
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| == | ||||
							
								
								
									
										1
									
								
								amend/tests/003-lex-command/input4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/003-lex-command/input4
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| 99 | ||||
							
								
								
									
										20
									
								
								amend/tests/003-lex-command/run
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								amend/tests/003-lex-command/run
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| #!/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 | ||||
							
								
								
									
										5
									
								
								amend/tests/004-lex-comment/expected.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								amend/tests/004-lex-comment/expected.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
|  IDENTIFIER<comment_on_this_line> EOL | ||||
|  IDENTIFIER<none_on_this_one> EOL | ||||
|  EOL | ||||
|  EOL | ||||
|  EOF | ||||
							
								
								
									
										1
									
								
								amend/tests/004-lex-comment/info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/004-lex-comment/info.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Test to make sure that comments are stripped out. | ||||
							
								
								
									
										4
									
								
								amend/tests/004-lex-comment/input
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								amend/tests/004-lex-comment/input
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| comment_on_this_line # this is a "comment" (with / a bunch) # \\ of stuff \ | ||||
| none_on_this_one | ||||
| # beginning of line | ||||
|                          # preceded by whitespace | ||||
							
								
								
									
										17
									
								
								amend/tests/004-lex-comment/run
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								amend/tests/004-lex-comment/run
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #!/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 | ||||
							
								
								
									
										13
									
								
								amend/tests/005-lex-quoted-string/expected.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								amend/tests/005-lex-quoted-string/expected.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
|  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
									
								
								amend/tests/005-lex-quoted-string/info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/005-lex-quoted-string/info.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Test to make sure that quoted strings are tokenized properly. | ||||
							
								
								
									
										5
									
								
								amend/tests/005-lex-quoted-string/input
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								amend/tests/005-lex-quoted-string/input
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| test "string" | ||||
| test "string with spaces" | ||||
| test "string with \"escaped\" quotes" | ||||
| test "string with \\escaped\\ backslashes" | ||||
| test "string with # a comment character" | ||||
							
								
								
									
										2
									
								
								amend/tests/005-lex-quoted-string/input2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								amend/tests/005-lex-quoted-string/input2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| # This should fail | ||||
| test1 "unterminated string | ||||
							
								
								
									
										2
									
								
								amend/tests/005-lex-quoted-string/input3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								amend/tests/005-lex-quoted-string/input3
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| # This should fail | ||||
| test1 "string with illegal escape \n in the middle" | ||||
							
								
								
									
										19
									
								
								amend/tests/005-lex-quoted-string/run
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								amend/tests/005-lex-quoted-string/run
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| #!/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 | ||||
							
								
								
									
										0
									
								
								amend/tests/006-lex-words/SKIP
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								amend/tests/006-lex-words/SKIP
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										6
									
								
								amend/tests/006-lex-words/expected.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								amend/tests/006-lex-words/expected.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
|  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
									
								
								amend/tests/006-lex-words/info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/006-lex-words/info.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| Test to make sure that argument words are tokenized properly. | ||||
							
								
								
									
										5
									
								
								amend/tests/006-lex-words/input
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								amend/tests/006-lex-words/input
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| 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 | ||||
							
								
								
									
										2
									
								
								amend/tests/006-lex-words/input2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								amend/tests/006-lex-words/input2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| # This should fail | ||||
| test1 "unterminated string | ||||
							
								
								
									
										2
									
								
								amend/tests/006-lex-words/input3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								amend/tests/006-lex-words/input3
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| # This should fail | ||||
| test1 "string with illegal escape \n in the middle" | ||||
							
								
								
									
										17
									
								
								amend/tests/006-lex-words/run
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								amend/tests/006-lex-words/run
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #!/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 | ||||
							
								
								
									
										11
									
								
								amend/tests/007-lex-real-script/expected.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								amend/tests/007-lex-real-script/expected.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
|  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
									
								
								amend/tests/007-lex-real-script/info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/007-lex-real-script/info.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| An input script similar to one that will actually be used in practice. | ||||
							
								
								
									
										10
									
								
								amend/tests/007-lex-real-script/input
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								amend/tests/007-lex-real-script/input
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| 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" | ||||
							
								
								
									
										17
									
								
								amend/tests/007-lex-real-script/run
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								amend/tests/007-lex-real-script/run
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #!/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 | ||||
							
								
								
									
										74
									
								
								amend/tests/008-parse-real-script/expected.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								amend/tests/008-parse-real-script/expected.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| 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
									
								
								amend/tests/008-parse-real-script/info.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								amend/tests/008-parse-real-script/info.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| An input script similar to one that will actually be used in practice. | ||||
							
								
								
									
										10
									
								
								amend/tests/008-parse-real-script/input
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								amend/tests/008-parse-real-script/input
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| 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" | ||||
							
								
								
									
										17
									
								
								amend/tests/008-parse-real-script/run
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								amend/tests/008-parse-real-script/run
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| #!/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 | ||||
							
								
								
									
										0
									
								
								amend/tests/XXX-long-token/SKIP
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								amend/tests/XXX-long-token/SKIP
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								amend/tests/XXX-stack-overflow/SKIP
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								amend/tests/XXX-stack-overflow/SKIP
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										150
									
								
								amend/tests/one-test
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										150
									
								
								amend/tests/one-test
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| #!/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 | ||||
							
								
								
									
										69
									
								
								amend/tests/run-all-tests
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										69
									
								
								amend/tests/run-all-tests
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| #!/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 | ||||
							
								
								
									
										267
									
								
								bootloader.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								bootloader.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,267 @@ | ||||
| /* | ||||
|  * 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 "bootloader.h" | ||||
| #include "common.h" | ||||
| #include "mtdutils/mtdutils.h" | ||||
| #include "roots.h" | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
|  | ||||
| static const char *CACHE_NAME = "CACHE:"; | ||||
| static const char *MISC_NAME = "MISC:"; | ||||
| static const int MISC_PAGES = 3;         // number of pages to save | ||||
| static const int MISC_COMMAND_PAGE = 1;  // bootloader command is this page | ||||
|  | ||||
| #ifdef LOG_VERBOSE | ||||
| static void dump_data(const char *data, int len) { | ||||
|     int pos; | ||||
|     for (pos = 0; pos < len; ) { | ||||
|         printf("%05x: %02x", pos, data[pos]); | ||||
|         for (++pos; pos < len && (pos % 24) != 0; ++pos) { | ||||
|             printf(" %02x", data[pos]); | ||||
|         } | ||||
|         printf("\n"); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| int get_bootloader_message(struct bootloader_message *out) { | ||||
|     size_t write_size; | ||||
|     const MtdPartition *part = get_root_mtd_partition(MISC_NAME); | ||||
|     if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { | ||||
|         LOGE("Can't find %s\n", MISC_NAME); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     MtdReadContext *read = mtd_read_partition(part); | ||||
|     if (read == NULL) { | ||||
|         LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     const ssize_t size = write_size * MISC_PAGES; | ||||
|     char data[size]; | ||||
|     ssize_t r = mtd_read_data(read, data, size); | ||||
|     if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno)); | ||||
|     mtd_read_close(read); | ||||
|     if (r != size) return -1; | ||||
|  | ||||
| #ifdef LOG_VERBOSE | ||||
|     printf("\n--- get_bootloader_message ---\n"); | ||||
|     dump_data(data, size); | ||||
|     printf("\n"); | ||||
| #endif | ||||
|  | ||||
|     memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out)); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int set_bootloader_message(const struct bootloader_message *in) { | ||||
|     size_t write_size; | ||||
|     const MtdPartition *part = get_root_mtd_partition(MISC_NAME); | ||||
|     if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { | ||||
|         LOGE("Can't find %s\n", MISC_NAME); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     MtdReadContext *read = mtd_read_partition(part); | ||||
|     if (read == NULL) { | ||||
|         LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     ssize_t size = write_size * MISC_PAGES; | ||||
|     char data[size]; | ||||
|     ssize_t r = mtd_read_data(read, data, size); | ||||
|     if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno)); | ||||
|     mtd_read_close(read); | ||||
|     if (r != size) return -1; | ||||
|  | ||||
|     memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in)); | ||||
|  | ||||
| #ifdef LOG_VERBOSE | ||||
|     printf("\n--- set_bootloader_message ---\n"); | ||||
|     dump_data(data, size); | ||||
|     printf("\n"); | ||||
| #endif | ||||
|  | ||||
|     MtdWriteContext *write = mtd_write_partition(part); | ||||
|     if (write == NULL) { | ||||
|         LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|     if (mtd_write_data(write, data, size) != size) { | ||||
|         LOGE("Can't write %s\n(%s)\n", MISC_NAME, strerror(errno)); | ||||
|         mtd_write_close(write); | ||||
|         return -1; | ||||
|     } | ||||
|     if (mtd_write_close(write)) { | ||||
|         LOGE("Can't finish %s\n(%s)\n", MISC_NAME, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     LOGI("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : ""); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Update Image | ||||
|  * | ||||
|  * - will be stored in the "cache" partition | ||||
|  * - bad blocks will be ignored, like boot.img and recovery.img | ||||
|  * - the first block will be the image header (described below) | ||||
|  * - the size is in BYTES, inclusive of the header | ||||
|  * - offsets are in BYTES from the start of the update header | ||||
|  * - two raw bitmaps will be included, the "busy" and "fail" bitmaps | ||||
|  * - for dream, the bitmaps will be 320x480x16bpp RGB565 | ||||
|  */ | ||||
|  | ||||
| #define UPDATE_MAGIC       "MSM-RADIO-UPDATE" | ||||
| #define UPDATE_MAGIC_SIZE  16 | ||||
| #define UPDATE_VERSION     0x00010000 | ||||
|  | ||||
| struct update_header { | ||||
|     unsigned char MAGIC[UPDATE_MAGIC_SIZE]; | ||||
|  | ||||
|     unsigned version; | ||||
|     unsigned size; | ||||
|  | ||||
|     unsigned image_offset; | ||||
|     unsigned image_length; | ||||
|  | ||||
|     unsigned bitmap_width; | ||||
|     unsigned bitmap_height; | ||||
|     unsigned bitmap_bpp; | ||||
|  | ||||
|     unsigned busy_bitmap_offset; | ||||
|     unsigned busy_bitmap_length; | ||||
|  | ||||
|     unsigned fail_bitmap_offset; | ||||
|     unsigned fail_bitmap_length; | ||||
| }; | ||||
|  | ||||
| int write_update_for_bootloader( | ||||
|         const char *update, int update_length, | ||||
|         int bitmap_width, int bitmap_height, int bitmap_bpp, | ||||
|         const char *busy_bitmap, const char *fail_bitmap) { | ||||
|     if (ensure_root_path_unmounted(CACHE_NAME)) { | ||||
|         LOGE("Can't unmount %s\n", CACHE_NAME); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     const MtdPartition *part = get_root_mtd_partition(CACHE_NAME); | ||||
|     if (part == NULL) { | ||||
|         LOGE("Can't find %s\n", CACHE_NAME); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     MtdWriteContext *write = mtd_write_partition(part); | ||||
|     if (write == NULL) { | ||||
|         LOGE("Can't open %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* Write an invalid (zero) header first, to disable any previous | ||||
|      * update and any other structured contents (like a filesystem), | ||||
|      * and as a placeholder for the amount of space required. | ||||
|      */ | ||||
|  | ||||
|     struct update_header header; | ||||
|     memset(&header, 0, sizeof(header)); | ||||
|     const ssize_t header_size = sizeof(header); | ||||
|     if (mtd_write_data(write, (char*) &header, header_size) != header_size) { | ||||
|         LOGE("Can't write header to %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         mtd_write_close(write); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* Write each section individually block-aligned, so we can write | ||||
|      * each block independently without complicated buffering. | ||||
|      */ | ||||
|  | ||||
|     memcpy(&header.MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE); | ||||
|     header.version = UPDATE_VERSION; | ||||
|     header.size = header_size; | ||||
|  | ||||
|     header.image_offset = mtd_erase_blocks(write, 0); | ||||
|     header.image_length = update_length; | ||||
|     if ((int) header.image_offset == -1 || | ||||
|         mtd_write_data(write, update, update_length) != update_length) { | ||||
|         LOGE("Can't write update to %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         mtd_write_close(write); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     header.bitmap_width = bitmap_width; | ||||
|     header.bitmap_height = bitmap_height; | ||||
|     header.bitmap_bpp = bitmap_bpp; | ||||
|  | ||||
|     int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height; | ||||
|  | ||||
|     header.busy_bitmap_offset = mtd_erase_blocks(write, 0); | ||||
|     header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0; | ||||
|     if ((int) header.busy_bitmap_offset == -1 || | ||||
|         mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) { | ||||
|         LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         mtd_write_close(write); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     header.fail_bitmap_offset = mtd_erase_blocks(write, 0); | ||||
|     header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0; | ||||
|     if ((int) header.fail_bitmap_offset == -1 || | ||||
|         mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) { | ||||
|         LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         mtd_write_close(write); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* Write the header last, after all the blocks it refers to, so that | ||||
|      * when the magic number is installed everything is valid. | ||||
|      */ | ||||
|  | ||||
|     if (mtd_write_close(write)) { | ||||
|         LOGE("Can't finish writing %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     write = mtd_write_partition(part); | ||||
|     if (write == NULL) { | ||||
|         LOGE("Can't reopen %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (mtd_write_data(write, (char*) &header, header_size) != header_size) { | ||||
|         LOGE("Can't rewrite header to %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         mtd_write_close(write); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (mtd_erase_blocks(write, 0) != (off_t) header.image_offset) { | ||||
|         LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         mtd_write_close(write); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (mtd_write_close(write)) { | ||||
|         LOGE("Can't finish header of %s\n(%s)\n", CACHE_NAME, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										59
									
								
								bootloader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								bootloader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| #ifndef _RECOVERY_BOOTLOADER_H | ||||
| #define _RECOVERY_BOOTLOADER_H | ||||
|  | ||||
| /* Bootloader Message | ||||
|  * | ||||
|  * This structure describes the content of a block in flash | ||||
|  * that is used for recovery and the bootloader to talk to | ||||
|  * each other. | ||||
|  * | ||||
|  * The command field is updated by linux when it wants to | ||||
|  * reboot into recovery or to update radio or bootloader firmware. | ||||
|  * It is also updated by the bootloader when firmware update | ||||
|  * is complete (to boot into recovery for any final cleanup) | ||||
|  * | ||||
|  * The status field is written by the bootloader after the | ||||
|  * completion of an "update-radio" or "update-hboot" command. | ||||
|  * | ||||
|  * The recovery field is only written by linux and used | ||||
|  * for the system to send a message to recovery or the | ||||
|  * other way around. | ||||
|  */ | ||||
| struct bootloader_message { | ||||
|     char command[32]; | ||||
|     char status[32]; | ||||
|     char recovery[1024]; | ||||
| }; | ||||
|  | ||||
| /* Read and write the bootloader command from the "misc" partition. | ||||
|  * These return zero on success. | ||||
|  */ | ||||
| int get_bootloader_message(struct bootloader_message *out); | ||||
| int set_bootloader_message(const struct bootloader_message *in); | ||||
|  | ||||
| /* Write an update to the cache partition for update-radio or update-hboot. | ||||
|  * Note, this destroys any filesystem on the cache partition! | ||||
|  * The expected bitmap format is 240x320, 16bpp (2Bpp), RGB 5:6:5. | ||||
|  */ | ||||
| int write_update_for_bootloader( | ||||
|         const char *update, int update_len, | ||||
|         int bitmap_width, int bitmap_height, int bitmap_bpp, | ||||
|         const char *busy_bitmap, const char *error_bitmap); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										1148
									
								
								commands.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1148
									
								
								commands.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										28
									
								
								commands.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								commands.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * 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_ | ||||
							
								
								
									
										94
									
								
								common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								common.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /* | ||||
|  * 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_COMMON_H | ||||
| #define RECOVERY_COMMON_H | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| // Initialize the graphics system. | ||||
| void ui_init(); | ||||
|  | ||||
| // Use KEY_* codes from <linux/input.h> or KEY_DREAM_* from "minui/minui.h". | ||||
| int ui_wait_key();            // waits for a key/button press, returns the code | ||||
| int ui_key_pressed(int key);  // returns >0 if the code is currently pressed | ||||
| int ui_text_visible();        // returns >0 if text log is currently visible | ||||
| void ui_clear_key_queue(); | ||||
|  | ||||
| // Write a message to the on-screen log shown with Alt-L (also to stderr). | ||||
| // The screen is small, and users may need to report these messages to support, | ||||
| // so keep the output short and not too cryptic. | ||||
| void ui_print(const char *fmt, ...); | ||||
|  | ||||
| // Display some header text followed by a menu of items, which appears | ||||
| // at the top of the screen (in place of any scrolling ui_print() | ||||
| // output, if necessary). | ||||
| void ui_start_menu(char** headers, char** items); | ||||
| // Set the menu highlight to the given index, and return it (capped to | ||||
| // the range [0..numitems). | ||||
| int ui_menu_select(int sel); | ||||
| // End menu mode, resetting the text overlay so that ui_print() | ||||
| // statements will be displayed. | ||||
| void ui_end_menu(); | ||||
|  | ||||
| // Set the icon (normally the only thing visible besides the progress bar). | ||||
| enum { | ||||
|   BACKGROUND_ICON_NONE, | ||||
|   BACKGROUND_ICON_UNPACKING, | ||||
|   BACKGROUND_ICON_INSTALLING, | ||||
|   BACKGROUND_ICON_ERROR, | ||||
|   BACKGROUND_ICON_FIRMWARE_INSTALLING, | ||||
|   BACKGROUND_ICON_FIRMWARE_ERROR, | ||||
|   NUM_BACKGROUND_ICONS | ||||
| }; | ||||
| void ui_set_background(int icon); | ||||
|  | ||||
| // Get a malloc'd copy of the screen image showing (only) the specified icon. | ||||
| // Also returns the width, height, and bits per pixel of the returned image. | ||||
| // TODO: Use some sort of "struct Bitmap" here instead of all these variables? | ||||
| char *ui_copy_image(int icon, int *width, int *height, int *bpp); | ||||
|  | ||||
| // Show a progress bar and define the scope of the next operation: | ||||
| //   portion - fraction of the progress bar the next operation will use | ||||
| //   seconds - expected time interval (progress bar moves at this minimum rate) | ||||
| void ui_show_progress(float portion, int seconds); | ||||
| void ui_set_progress(float fraction);  // 0.0 - 1.0 within the defined scope | ||||
|  | ||||
| // Default allocation of progress bar segments to operations | ||||
| static const int VERIFICATION_PROGRESS_TIME = 60; | ||||
| static const float VERIFICATION_PROGRESS_FRACTION = 0.5; | ||||
| static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; | ||||
| static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; | ||||
|  | ||||
| // Show a rotating "barberpole" for ongoing operations.  Updates automatically. | ||||
| void ui_show_indeterminate_progress(); | ||||
|  | ||||
| // Hide and reset the progress bar. | ||||
| void ui_reset_progress(); | ||||
|  | ||||
| #define LOGE(...) ui_print("E:" __VA_ARGS__) | ||||
| #define LOGW(...) fprintf(stderr, "W:" __VA_ARGS__) | ||||
| #define LOGI(...) fprintf(stderr, "I:" __VA_ARGS__) | ||||
|  | ||||
| #if 0 | ||||
| #define LOGV(...) fprintf(stderr, "V:" __VA_ARGS__) | ||||
| #define LOGD(...) fprintf(stderr, "D:" __VA_ARGS__) | ||||
| #else | ||||
| #define LOGV(...) do {} while (0) | ||||
| #define LOGD(...) do {} while (0) | ||||
| #endif | ||||
|  | ||||
| #endif  // RECOVERY_COMMON_H | ||||
							
								
								
									
										8
									
								
								etc/META-INF/com/google/android/update-script
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								etc/META-INF/com/google/android/update-script
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| assert compatible_with("0.1") == "true" | ||||
| assert file_contains("SYSTEM:build.prop", "ro.product.device=dream") == "true" || file_contains("SYSTEM:build.prop", "ro.build.product=dream") == "true" | ||||
| assert file_contains("RECOVERY:default.prop", "ro.product.device=dream") == "true" || file_contains("RECOVERY:default.prop", "ro.build.product=dream") == "true" | ||||
| assert getprop("ro.product.device") == "dream" | ||||
| format BOOT: | ||||
| format SYSTEM: | ||||
| copy_dir PACKAGE:system SYSTEM: | ||||
| write_raw_image PACKAGE:boot.img BOOT: | ||||
							
								
								
									
										33
									
								
								etc/init.rc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								etc/init.rc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
|  | ||||
| on init | ||||
|     export PATH /sbin | ||||
|     export ANDROID_ROOT /system | ||||
|     export ANDROID_DATA /data | ||||
|     export EXTERNAL_STORAGE /sdcard | ||||
|  | ||||
|     symlink /system/etc /etc | ||||
|  | ||||
|     mkdir /sdcard | ||||
|     mkdir /system | ||||
|     mkdir /data | ||||
|     mkdir /cache | ||||
|     mount /tmp /tmp tmpfs | ||||
|  | ||||
| on boot | ||||
|  | ||||
|     ifup lo | ||||
|     hostname localhost | ||||
|     domainname localdomain | ||||
|  | ||||
|     class_start default | ||||
|  | ||||
|  | ||||
| service recovery /sbin/recovery | ||||
|  | ||||
| service adbd /sbin/adbd recovery | ||||
|  | ||||
| on property:persist.service.adb.enable=1 | ||||
|     start adbd | ||||
|  | ||||
| on property:persist.service.adb.enable=0 | ||||
|     stop adbd | ||||
							
								
								
									
										127
									
								
								firmware.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								firmware.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| /* | ||||
|  * 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 "bootloader.h" | ||||
| #include "common.h" | ||||
| #include "firmware.h" | ||||
| #include "roots.h" | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <string.h> | ||||
| #include <sys/reboot.h> | ||||
|  | ||||
| static const char *update_type = NULL; | ||||
| static const char *update_data = NULL; | ||||
| static int update_length = 0; | ||||
|  | ||||
| int remember_firmware_update(const char *type, const char *data, int length) { | ||||
|     if (update_type != NULL || update_data != NULL) { | ||||
|         LOGE("Multiple firmware images\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     update_type = type; | ||||
|     update_data = data; | ||||
|     update_length = length; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Bootloader / Recovery Flow | ||||
|  * | ||||
|  * On every boot, the bootloader will read the bootloader_message | ||||
|  * from flash and check the command field.  The bootloader should | ||||
|  * deal with the command field not having a 0 terminator correctly | ||||
|  * (so as to not crash if the block is invalid or corrupt). | ||||
|  * | ||||
|  * The bootloader will have to publish the partition that contains | ||||
|  * the bootloader_message to the linux kernel so it can update it. | ||||
|  * | ||||
|  * if command == "boot-recovery" -> boot recovery.img | ||||
|  * else if command == "update-radio" -> update radio image (below) | ||||
|  * else if command == "update-hboot" -> update hboot image (below) | ||||
|  * else -> boot boot.img (normal boot) | ||||
|  * | ||||
|  * Radio/Hboot Update Flow | ||||
|  * 1. the bootloader will attempt to load and validate the header | ||||
|  * 2. if the header is invalid, status="invalid-update", goto #8 | ||||
|  * 3. display the busy image on-screen | ||||
|  * 4. if the update image is invalid, status="invalid-radio-image", goto #8 | ||||
|  * 5. attempt to update the firmware (depending on the command) | ||||
|  * 6. if successful, status="okay", goto #8 | ||||
|  * 7. if failed, and the old image can still boot, status="failed-update" | ||||
|  * 8. write the bootloader_message, leaving the recovery field | ||||
|  *    unchanged, updating status, and setting command to | ||||
|  *    "boot-recovery" | ||||
|  * 9. reboot | ||||
|  * | ||||
|  * The bootloader will not modify or erase the cache partition. | ||||
|  * It is recovery's responsibility to clean up the mess afterwards. | ||||
|  */ | ||||
|  | ||||
| int maybe_install_firmware_update(const char *send_intent) { | ||||
|     if (update_data == NULL || update_length == 0) return 0; | ||||
|  | ||||
|     /* We destroy the cache partition to pass the update image to the | ||||
|      * bootloader, so all we can really do afterwards is wipe cache and reboot. | ||||
|      * Set up this instruction now, in case we're interrupted while writing. | ||||
|      */ | ||||
|  | ||||
|     struct bootloader_message boot; | ||||
|     memset(&boot, 0, sizeof(boot)); | ||||
|     strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); | ||||
|     strlcpy(boot.recovery, "recovery\n--wipe_cache\n", sizeof(boot.command)); | ||||
|     if (send_intent != NULL) { | ||||
|         strlcat(boot.recovery, "--send_intent=", sizeof(boot.recovery)); | ||||
|         strlcat(boot.recovery, send_intent, sizeof(boot.recovery)); | ||||
|         strlcat(boot.recovery, "\n", sizeof(boot.recovery)); | ||||
|     } | ||||
|     if (set_bootloader_message(&boot)) return -1; | ||||
|  | ||||
|     int width = 0, height = 0, bpp = 0; | ||||
|     char *busy_image = ui_copy_image( | ||||
|         BACKGROUND_ICON_FIRMWARE_INSTALLING, &width, &height, &bpp); | ||||
|     char *fail_image = ui_copy_image( | ||||
|         BACKGROUND_ICON_FIRMWARE_ERROR, &width, &height, &bpp); | ||||
|  | ||||
|     ui_print("Writing %s image...\n", update_type); | ||||
|     if (write_update_for_bootloader( | ||||
|             update_data, update_length, | ||||
|             width, height, bpp, busy_image, fail_image)) { | ||||
|         LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno)); | ||||
|         format_root_device("CACHE:");  // Attempt to clean cache up, at least. | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     free(busy_image); | ||||
|     free(fail_image); | ||||
|  | ||||
|     /* The update image is fully written, so now we can instruct the bootloader | ||||
|      * to install it.  (After doing so, it will come back here, and we will | ||||
|      * wipe the cache and reboot into the system.) | ||||
|      */ | ||||
|     snprintf(boot.command, sizeof(boot.command), "update-%s", update_type); | ||||
|     if (set_bootloader_message(&boot)) { | ||||
|         format_root_device("CACHE:"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     reboot(RB_AUTOBOOT); | ||||
|  | ||||
|     // Can't reboot?  WTF? | ||||
|     LOGE("Can't reboot\n"); | ||||
|     return -1; | ||||
| } | ||||
							
								
								
									
										32
									
								
								firmware.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								firmware.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| #ifndef _RECOVERY_FIRMWARE_H | ||||
| #define _RECOVERY_FIRMWARE_H | ||||
|  | ||||
| /* Save a radio or bootloader update image for later installation. | ||||
|  * The type should be one of "hboot" or "radio". | ||||
|  * Takes ownership of type and data.  Returns nonzero on error. | ||||
|  */ | ||||
| int remember_firmware_update(const char *type, const char *data, int length); | ||||
|  | ||||
| /* If an update was saved, reboot into the bootloader now to install it. | ||||
|  * Returns 0 if no radio image was defined, nonzero on error, | ||||
|  * doesn't return at all on success... | ||||
|  */ | ||||
| int maybe_install_firmware_update(const char *send_intent); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										186
									
								
								install.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								install.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| /* | ||||
|  * 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 <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #include "amend/amend.h" | ||||
| #include "common.h" | ||||
| #include "install.h" | ||||
| #include "mincrypt/rsa.h" | ||||
| #include "minui/minui.h" | ||||
| #include "minzip/SysUtil.h" | ||||
| #include "minzip/Zip.h" | ||||
| #include "mtdutils/mounts.h" | ||||
| #include "mtdutils/mtdutils.h" | ||||
| #include "roots.h" | ||||
| #include "verifier.h" | ||||
|  | ||||
| /* List of public keys */ | ||||
| static const RSAPublicKey keys[] = { | ||||
| #include "keys.inc" | ||||
| }; | ||||
|  | ||||
| #define ASSUMED_UPDATE_SCRIPT_NAME  "META-INF/com/google/android/update-script" | ||||
|  | ||||
| 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, *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; | ||||
|     } | ||||
|  | ||||
|     ui_print("Installation complete.\n"); | ||||
|     return INSTALL_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int | ||||
| handle_update_package(const char *path, ZipArchive *zip) | ||||
| { | ||||
|     // Give verification half the progress bar... | ||||
|     ui_print("Verifying update package...\n"); | ||||
|     ui_show_progress( | ||||
|             VERIFICATION_PROGRESS_FRACTION, | ||||
|             VERIFICATION_PROGRESS_TIME); | ||||
|  | ||||
|     if (!verify_jar_signature(zip, keys, sizeof(keys) / sizeof(keys[0]))) { | ||||
|         LOGE("Verification failed\n"); | ||||
|         return INSTALL_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     // Update should take the rest of the progress bar. | ||||
|     ui_print("Installing update...\n"); | ||||
|  | ||||
|     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; | ||||
| } | ||||
|  | ||||
| int | ||||
| install_package(const char *root_path) | ||||
| { | ||||
|     ui_set_background(BACKGROUND_ICON_INSTALLING); | ||||
|     ui_print("Finding update package...\n"); | ||||
|     ui_show_indeterminate_progress(); | ||||
|     LOGI("Update location: %s\n", root_path); | ||||
|  | ||||
|     if (ensure_root_path_mounted(root_path) != 0) { | ||||
|         LOGE("Can't mount %s\n", root_path); | ||||
|         return INSTALL_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     char path[PATH_MAX] = ""; | ||||
|     if (translate_root_path(root_path, path, sizeof(path)) == NULL) { | ||||
|         LOGE("Bad path %s\n", root_path); | ||||
|         return INSTALL_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     ui_print("Opening update package...\n"); | ||||
|     LOGI("Update file path: %s\n", path); | ||||
|  | ||||
|     /* Try to open the package. | ||||
|      */ | ||||
|     ZipArchive zip; | ||||
|     int err = mzOpenZipArchive(path, &zip); | ||||
|     if (err != 0) { | ||||
|         LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad"); | ||||
|         return INSTALL_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     /* Verify and install the contents of the package. | ||||
|      */ | ||||
|     int status = handle_update_package(path, &zip); | ||||
|     mzCloseZipArchive(&zip); | ||||
|     return status; | ||||
| } | ||||
							
								
								
									
										25
									
								
								install.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								install.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * 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_INSTALL_H_ | ||||
| #define RECOVERY_INSTALL_H_ | ||||
|  | ||||
| #include "common.h" | ||||
|  | ||||
| enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT }; | ||||
| int install_package(const char *root_path); | ||||
|  | ||||
| #endif  // RECOVERY_INSTALL_H_ | ||||
							
								
								
									
										12
									
								
								minui/Android.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								minui/Android.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| LOCAL_PATH := $(call my-dir) | ||||
| include $(CLEAR_VARS) | ||||
|  | ||||
| LOCAL_SRC_FILES := graphics.c events.c resources.c | ||||
|  | ||||
| LOCAL_C_INCLUDES +=\ | ||||
|     external/libpng\ | ||||
|     external/zlib | ||||
|  | ||||
| LOCAL_MODULE := libminui | ||||
|  | ||||
| include $(BUILD_STATIC_LIBRARY) | ||||
							
								
								
									
										82
									
								
								minui/events.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								minui/events.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| /* | ||||
|  * 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 <fcntl.h> | ||||
| #include <dirent.h> | ||||
| #include <sys/poll.h> | ||||
|  | ||||
| #include <linux/input.h> | ||||
|  | ||||
| #include "minui.h" | ||||
|  | ||||
| #define MAX_DEVICES 16 | ||||
|  | ||||
| static struct pollfd ev_fds[MAX_DEVICES]; | ||||
| static unsigned ev_count = 0; | ||||
|  | ||||
| int ev_init(void) | ||||
| { | ||||
|     DIR *dir; | ||||
|     struct dirent *de; | ||||
|     int fd; | ||||
|  | ||||
|     dir = opendir("/dev/input"); | ||||
|     if(dir != 0) { | ||||
|         while((de = readdir(dir))) { | ||||
| //            fprintf(stderr,"/dev/input/%s\n", de->d_name); | ||||
|             if(strncmp(de->d_name,"event",5)) continue; | ||||
|             fd = openat(dirfd(dir), de->d_name, O_RDONLY); | ||||
|             if(fd < 0) continue; | ||||
|  | ||||
|             ev_fds[ev_count].fd = fd; | ||||
|             ev_fds[ev_count].events = POLLIN; | ||||
|             ev_count++; | ||||
|             if(ev_count == MAX_DEVICES) break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void ev_exit(void) | ||||
| { | ||||
|     while (ev_count > 0) { | ||||
|         close(ev_fds[--ev_count].fd); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int ev_get(struct input_event *ev, unsigned dont_wait) | ||||
| { | ||||
|     int r; | ||||
|     unsigned n; | ||||
|  | ||||
|     do { | ||||
|         r = poll(ev_fds, ev_count, dont_wait ? 0 : -1); | ||||
|  | ||||
|         if(r > 0) { | ||||
|             for(n = 0; n < ev_count; n++) { | ||||
|                 if(ev_fds[n].revents & POLLIN) { | ||||
|                     r = read(ev_fds[n].fd, ev, sizeof(*ev)); | ||||
|                     if(r == sizeof(*ev)) return 0; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } while(dont_wait == 0); | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
							
								
								
									
										214
									
								
								minui/font_10x18.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								minui/font_10x18.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | ||||
| struct { | ||||
|   unsigned width; | ||||
|   unsigned height; | ||||
|   unsigned cwidth; | ||||
|   unsigned cheight; | ||||
|   unsigned char rundata[]; | ||||
| } font = { | ||||
|   .width = 960, | ||||
|   .height = 18, | ||||
|   .cwidth = 10, | ||||
|   .cheight = 18, | ||||
|   .rundata = { | ||||
| 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x55,0x82,0x06,0x82,0x02,0x82,0x10,0x82, | ||||
| 0x11,0x83,0x08,0x82,0x0a,0x82,0x04,0x82,0x46,0x82,0x08,0x82,0x07,0x84,0x06, | ||||
| 0x84,0x0a,0x81,0x03,0x88,0x04,0x84,0x04,0x88,0x04,0x84,0x06,0x84,0x1e,0x81, | ||||
| 0x0e,0x81,0x0a,0x84,0x06,0x84,0x07,0x82,0x05,0x85,0x07,0x84,0x04,0x86,0x04, | ||||
| 0x88,0x02,0x88,0x04,0x84,0x04,0x82,0x04,0x82,0x02,0x88,0x05,0x86,0x01,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04, | ||||
| 0x86,0x06,0x84,0x04,0x86,0x06,0x84,0x04,0x88,0x02,0x82,0x04,0x82,0x02,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02, | ||||
| 0x88,0x03,0x86,0x0e,0x86,0x06,0x82,0x11,0x82,0x10,0x82,0x18,0x82,0x0f,0x84, | ||||
| 0x0d,0x82,0x1c,0x82,0x09,0x84,0x7f,0x16,0x84,0x05,0x82,0x05,0x84,0x07,0x83, | ||||
| 0x02,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x03,0x86,0x04, | ||||
| 0x83,0x02,0x82,0x03,0x82,0x01,0x82,0x07,0x82,0x09,0x82,0x06,0x82,0x3e,0x82, | ||||
| 0x04,0x84,0x06,0x83,0x06,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x03, | ||||
| 0x82,0x09,0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82, | ||||
| 0x1c,0x82,0x0e,0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x05,0x84,0x04, | ||||
| 0x82,0x02,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82, | ||||
| 0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x03,0x82,0x02,0x82, | ||||
| 0x03,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x02, | ||||
| 0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82, | ||||
| 0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x03,0x82,0x08,0x82,0x0c, | ||||
| 0x82,0x05,0x84,0x11,0x82,0x0f,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82, | ||||
| 0x1c,0x82,0x0b,0x82,0x7f,0x15,0x82,0x08,0x82,0x08,0x82,0x05,0x82,0x01,0x82, | ||||
| 0x01,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x02,0x82,0x01, | ||||
| 0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x07,0x82, | ||||
| 0x08,0x82,0x08,0x82,0x3d,0x82,0x03,0x82,0x02,0x82,0x04,0x84,0x05,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x06,0x83,0x03,0x82,0x08,0x82,0x04,0x81,0x09,0x82, | ||||
| 0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x1a,0x82,0x10,0x82,0x06,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02, | ||||
| 0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83, | ||||
| 0x02,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02, | ||||
| 0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x08,0x82,0x0c,0x82,0x04,0x82,0x02,0x82, | ||||
| 0x11,0x82,0x0e,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82,0x0b,0x82,0x0b, | ||||
| 0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x05,0x82, | ||||
| 0x02,0x83,0x1a,0x82,0x07,0x81,0x02,0x81,0x07,0x82,0x01,0x82,0x02,0x82,0x01, | ||||
| 0x82,0x05,0x82,0x01,0x84,0x04,0x82,0x01,0x82,0x07,0x82,0x08,0x82,0x08,0x82, | ||||
| 0x06,0x82,0x02,0x82,0x06,0x82,0x28,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01, | ||||
| 0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x84,0x03,0x82,0x08,0x82, | ||||
| 0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x19,0x82,0x12,0x82,0x05, | ||||
| 0x82,0x04,0x82,0x02,0x82,0x02,0x84,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82, | ||||
| 0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04, | ||||
| 0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83,0x02,0x83, | ||||
| 0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82,0x05,0x82,0x04,0x82,0x02,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08, | ||||
| 0x82,0x04,0x82,0x09,0x82,0x0b,0x82,0x03,0x82,0x04,0x82,0x20,0x82,0x18,0x82, | ||||
| 0x0e,0x82,0x10,0x82,0x0b,0x82,0x0b,0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45, | ||||
| 0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x88,0x01,0x82,0x01,0x82,0x06,0x83, | ||||
| 0x01,0x82,0x04,0x84,0x08,0x81,0x08,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06, | ||||
| 0x82,0x28,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x08,0x82,0x04,0x82, | ||||
| 0x01,0x82,0x03,0x82,0x08,0x82,0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04, | ||||
| 0x82,0x18,0x82,0x06,0x88,0x06,0x82,0x04,0x82,0x04,0x82,0x02,0x82,0x01,0x85, | ||||
| 0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02, | ||||
| 0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82, | ||||
| 0x02,0x82,0x04,0x82,0x08,0x88,0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02, | ||||
| 0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82, | ||||
| 0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x84,0x06, | ||||
| 0x84,0x08,0x82,0x05,0x82,0x09,0x82,0x0b,0x82,0x2b,0x82,0x18,0x82,0x0e,0x82, | ||||
| 0x10,0x82,0x1c,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x26, | ||||
| 0x82,0x11,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x09,0x82,0x06,0x82,0x12,0x82, | ||||
| 0x0a,0x82,0x06,0x84,0x07,0x82,0x27,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0b, | ||||
| 0x82,0x07,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01,0x83,0x04,0x82,0x01,0x83, | ||||
| 0x08,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x83,0x07,0x83,0x05, | ||||
| 0x82,0x16,0x82,0x08,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82, | ||||
| 0x02,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08, | ||||
| 0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82, | ||||
| 0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82, | ||||
| 0x0a,0x82,0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01, | ||||
| 0x82,0x04,0x84,0x06,0x84,0x08,0x82,0x05,0x82,0x0a,0x82,0x0a,0x82,0x23,0x85, | ||||
| 0x03,0x82,0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x84,0x04,0x86,0x05, | ||||
| 0x85,0x01,0x81,0x02,0x82,0x01,0x83,0x05,0x84,0x09,0x84,0x02,0x82,0x03,0x82, | ||||
| 0x06,0x82,0x05,0x81,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x83,0x06,0x84,0x04, | ||||
| 0x82,0x01,0x83,0x06,0x83,0x01,0x82,0x02,0x82,0x01,0x84,0x04,0x86,0x03,0x86, | ||||
| 0x04,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x03,0x87,0x05,0x82,0x08,0x82,0x08,0x82,0x26,0x82, | ||||
| 0x11,0x82,0x01,0x82,0x04,0x86,0x07,0x82,0x05,0x83,0x12,0x82,0x0a,0x82,0x04, | ||||
| 0x88,0x02,0x88,0x0c,0x88,0x10,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0a,0x82, | ||||
| 0x06,0x83,0x04,0x82,0x03,0x82,0x03,0x83,0x02,0x82,0x03,0x83,0x02,0x82,0x07, | ||||
| 0x82,0x06,0x84,0x05,0x82,0x02,0x83,0x05,0x83,0x07,0x83,0x04,0x82,0x18,0x82, | ||||
| 0x06,0x82,0x04,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x86,0x04, | ||||
| 0x82,0x08,0x82,0x04,0x82,0x02,0x86,0x04,0x86,0x04,0x82,0x02,0x84,0x02,0x88, | ||||
| 0x05,0x82,0x0a,0x82,0x03,0x85,0x05,0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02, | ||||
| 0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x03,0x82,0x05,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x03, | ||||
| 0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x08,0x82,0x08,0x82, | ||||
| 0x06,0x82,0x0a,0x82,0x0a,0x82,0x22,0x82,0x03,0x82,0x02,0x83,0x02,0x82,0x04, | ||||
| 0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x02,0x82,0x05,0x82,0x06,0x82, | ||||
| 0x03,0x83,0x02,0x83,0x02,0x82,0x06,0x82,0x0b,0x82,0x02,0x82,0x02,0x82,0x07, | ||||
| 0x82,0x05,0x88,0x02,0x83,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82, | ||||
| 0x04,0x82,0x02,0x83,0x03,0x83,0x02,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06, | ||||
| 0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82, | ||||
| 0x03,0x82,0x04,0x82,0x08,0x82,0x02,0x84,0x09,0x82,0x09,0x84,0x23,0x82,0x11, | ||||
| 0x82,0x01,0x82,0x06,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x01,0x82,0x11,0x82, | ||||
| 0x0a,0x82,0x06,0x84,0x07,0x82,0x26,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x08, | ||||
| 0x83,0x09,0x82,0x03,0x82,0x03,0x82,0x09,0x82,0x02,0x82,0x04,0x82,0x05,0x82, | ||||
| 0x06,0x82,0x02,0x82,0x05,0x83,0x01,0x82,0x17,0x82,0x16,0x82,0x06,0x82,0x05, | ||||
| 0x82,0x01,0x82,0x01,0x82,0x02,0x88,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05, | ||||
| 0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82, | ||||
| 0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x86,0x04,0x82,0x04,0x82,0x02, | ||||
| 0x86,0x09,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82, | ||||
| 0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09,0x82,0x27, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82, | ||||
| 0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02, | ||||
| 0x82,0x01,0x82,0x08,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82, | ||||
| 0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x07, | ||||
| 0x82,0x0a,0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82, | ||||
| 0x04,0x84,0x04,0x82,0x04,0x82,0x07,0x82,0x06,0x82,0x08,0x82,0x08,0x82,0x26, | ||||
| 0x82,0x0f,0x88,0x05,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x02,0x82,0x01,0x82, | ||||
| 0x0d,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06,0x82,0x26,0x82,0x05,0x82,0x04, | ||||
| 0x82,0x05,0x82,0x07,0x82,0x0c,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82, | ||||
| 0x05,0x82,0x05,0x82,0x04,0x82,0x08,0x82,0x18,0x82,0x14,0x82,0x07,0x82,0x05, | ||||
| 0x82,0x01,0x84,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05, | ||||
| 0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82, | ||||
| 0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02, | ||||
| 0x82,0x02,0x82,0x0a,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82, | ||||
| 0x01,0x82,0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09, | ||||
| 0x82,0x22,0x87,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x88, | ||||
| 0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02, | ||||
| 0x84,0x09,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x08,0x86,0x05, | ||||
| 0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82, | ||||
| 0x05,0x82,0x05,0x82,0x04,0x82,0x06,0x82,0x07,0x82,0x08,0x82,0x08,0x82,0x26, | ||||
| 0x82,0x10,0x82,0x01,0x82,0x07,0x82,0x01,0x82,0x04,0x82,0x01,0x83,0x02,0x82, | ||||
| 0x03,0x83,0x0f,0x82,0x08,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x25,0x82,0x07, | ||||
| 0x82,0x02,0x82,0x06,0x82,0x06,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x09,0x82, | ||||
| 0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04,0x82,0x08,0x82,0x19,0x82,0x05, | ||||
| 0x88,0x05,0x82,0x08,0x82,0x05,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x02,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82,0x03,0x82,0x03,0x82, | ||||
| 0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02, | ||||
| 0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x03,0x82,0x09,0x82,0x05,0x82, | ||||
| 0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x06, | ||||
| 0x82,0x06,0x82,0x08,0x82,0x0c,0x82,0x08,0x82,0x21,0x82,0x04,0x82,0x02,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,0x82,0x06,0x82,0x03, | ||||
| 0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x85,0x08,0x82,0x05,0x82, | ||||
| 0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82,0x06,0x82,0x04,0x82, | ||||
| 0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05, | ||||
| 0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x38,0x82,0x01,0x82,0x04,0x82,0x01,0x82, | ||||
| 0x01,0x82,0x04,0x84,0x01,0x82,0x01,0x82,0x03,0x82,0x10,0x82,0x08,0x82,0x30, | ||||
| 0x83,0x06,0x82,0x07,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x08,0x82,0x04,0x82, | ||||
| 0x07,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04, | ||||
| 0x82,0x03,0x81,0x04,0x82,0x1a,0x82,0x10,0x82,0x10,0x82,0x08,0x82,0x04,0x82, | ||||
| 0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08, | ||||
| 0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82, | ||||
| 0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02, | ||||
| 0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x02,0x84,0x02,0x82,0x03,0x82,0x03,0x82, | ||||
| 0x04,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x05,0x83,0x02,0x83,0x03, | ||||
| 0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x09,0x82,0x0c,0x82,0x08,0x82,0x21,0x82, | ||||
| 0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a, | ||||
| 0x82,0x07,0x85,0x04,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x02,0x82, | ||||
| 0x07,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82, | ||||
| 0x06,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x04,0x84,0x04, | ||||
| 0x82,0x04,0x82,0x04,0x82,0x09,0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x82, | ||||
| 0x01,0x82,0x05,0x86,0x04,0x82,0x01,0x82,0x01,0x82,0x01,0x83,0x01,0x84,0x10, | ||||
| 0x82,0x06,0x82,0x1d,0x83,0x11,0x83,0x05,0x82,0x09,0x84,0x07,0x82,0x05,0x82, | ||||
| 0x09,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04, | ||||
| 0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x06,0x83,0x07,0x83,0x09,0x82, | ||||
| 0x0e,0x82,0x0a,0x82,0x06,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03, | ||||
| 0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x09,0x82, | ||||
| 0x02,0x83,0x02,0x82,0x04,0x82,0x05,0x82,0x06,0x82,0x01,0x82,0x04,0x82,0x04, | ||||
| 0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82, | ||||
| 0x03,0x82,0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x06, | ||||
| 0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82, | ||||
| 0x05,0x82,0x05,0x82,0x09,0x82,0x0d,0x82,0x07,0x82,0x21,0x82,0x04,0x82,0x02, | ||||
| 0x83,0x02,0x82,0x04,0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x03,0x82, | ||||
| 0x04,0x82,0x06,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x03, | ||||
| 0x82,0x06,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x03,0x82, | ||||
| 0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x07,0x82,0x04, | ||||
| 0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x02,0x83,0x05,0x82,0x05,0x88,0x03,0x82, | ||||
| 0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x0a,0x82,0x08,0x82,0x08,0x82,0x26, | ||||
| 0x82,0x1c,0x82,0x06,0x82,0x02,0x83,0x03,0x84,0x02,0x82,0x10,0x82,0x04,0x82, | ||||
| 0x1e,0x83,0x11,0x83,0x05,0x82,0x0a,0x82,0x05,0x88,0x02,0x88,0x04,0x84,0x09, | ||||
| 0x82,0x05,0x84,0x06,0x84,0x05,0x82,0x09,0x84,0x06,0x84,0x07,0x83,0x07,0x83, | ||||
| 0x0a,0x81,0x0e,0x81,0x0b,0x82,0x07,0x85,0x03,0x82,0x04,0x82,0x02,0x86,0x06, | ||||
| 0x84,0x04,0x86,0x04,0x88,0x02,0x82,0x0a,0x84,0x01,0x81,0x02,0x82,0x04,0x82, | ||||
| 0x02,0x88,0x04,0x83,0x05,0x82,0x04,0x82,0x02,0x88,0x02,0x82,0x04,0x82,0x02, | ||||
| 0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x0a,0x85,0x03,0x82,0x04,0x82,0x04,0x84, | ||||
| 0x07,0x82,0x07,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05, | ||||
| 0x82,0x05,0x88,0x03,0x86,0x09,0x82,0x03,0x86,0x22,0x85,0x01,0x81,0x02,0x82, | ||||
| 0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x85,0x05,0x82,0x07,0x86,0x03, | ||||
| 0x82,0x04,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x88,0x02,0x82, | ||||
| 0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x83,0x06, | ||||
| 0x83,0x01,0x82,0x03,0x82,0x08,0x86,0x06,0x84,0x05,0x83,0x01,0x82,0x05,0x82, | ||||
| 0x06,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x83,0x01,0x82,0x03,0x87,0x06, | ||||
| 0x84,0x05,0x82,0x05,0x84,0x7f,0x15,0x83,0x7f,0x14,0x83,0x7f,0x5e,0x82,0x7f, | ||||
| 0x05,0x89,0x47,0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x4e, | ||||
| 0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a,0x82,0x04,0x82,0x17,0x82,0x03,0x82, | ||||
| 0x34,0x82,0x0e,0x82,0x48,0x82,0x04,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a, | ||||
| 0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x49,0x82,0x02,0x82, | ||||
| 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0c,0x86,0x19,0x85,0x35,0x82,0x0e,0x82,0x4a, | ||||
| 0x84,0x3f, | ||||
| 0x00, | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										312
									
								
								minui/graphics.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								minui/graphics.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,312 @@ | ||||
| /* | ||||
|  * 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 <unistd.h> | ||||
|  | ||||
| #include <fcntl.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <linux/fb.h> | ||||
| #include <linux/kd.h> | ||||
|  | ||||
| #include <pixelflinger/pixelflinger.h> | ||||
|  | ||||
| #include "font_10x18.h" | ||||
| #include "minui.h" | ||||
|  | ||||
| typedef struct { | ||||
|     GGLSurface texture; | ||||
|     unsigned cwidth; | ||||
|     unsigned cheight; | ||||
|     unsigned ascent; | ||||
| } GRFont; | ||||
|  | ||||
| static GRFont *gr_font = 0; | ||||
| static GGLContext *gr_context = 0; | ||||
| static GGLSurface gr_font_texture; | ||||
| static GGLSurface gr_framebuffer[2]; | ||||
| static GGLSurface gr_mem_surface; | ||||
| static unsigned gr_active_fb = 0; | ||||
|  | ||||
| static int gr_fb_fd = -1; | ||||
| static int gr_vt_fd = -1; | ||||
|  | ||||
| static struct fb_var_screeninfo vi; | ||||
|  | ||||
| static int get_framebuffer(GGLSurface *fb) | ||||
| { | ||||
|     int fd; | ||||
|     struct fb_fix_screeninfo fi; | ||||
|     void *bits; | ||||
|  | ||||
|     fd = open("/dev/graphics/fb0", O_RDWR); | ||||
|     if (fd < 0) { | ||||
|         perror("cannot open fb0"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { | ||||
|         perror("failed to get fb0 info"); | ||||
|         close(fd); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { | ||||
|         perror("failed to get fb0 info"); | ||||
|         close(fd); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||||
|     if (bits == MAP_FAILED) { | ||||
|         perror("failed to mmap framebuffer"); | ||||
|         close(fd); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     fb->version = sizeof(*fb); | ||||
|     fb->width = vi.xres; | ||||
|     fb->height = vi.yres; | ||||
|     fb->stride = vi.xres; | ||||
|     fb->data = bits; | ||||
|     fb->format = GGL_PIXEL_FORMAT_RGB_565; | ||||
|  | ||||
|     fb++; | ||||
|  | ||||
|     fb->version = sizeof(*fb); | ||||
|     fb->width = vi.xres; | ||||
|     fb->height = vi.yres; | ||||
|     fb->stride = vi.xres; | ||||
|     fb->data = (void*) (((unsigned) bits) + vi.yres * vi.xres * 2); | ||||
|     fb->format = GGL_PIXEL_FORMAT_RGB_565; | ||||
|  | ||||
|     return fd; | ||||
| } | ||||
|  | ||||
| static void get_memory_surface(GGLSurface* ms) { | ||||
|   ms->version = sizeof(*ms); | ||||
|   ms->width = vi.xres; | ||||
|   ms->height = vi.yres; | ||||
|   ms->stride = vi.xres; | ||||
|   ms->data = malloc(vi.xres * vi.yres * 2); | ||||
|   ms->format = GGL_PIXEL_FORMAT_RGB_565; | ||||
| } | ||||
|  | ||||
| static void set_active_framebuffer(unsigned n) | ||||
| { | ||||
|     if (n > 1) return; | ||||
|     vi.yres_virtual = vi.yres * 2; | ||||
|     vi.yoffset = n * vi.yres; | ||||
|     if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { | ||||
|         perror("active fb swap failed"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void gr_flip(void) | ||||
| { | ||||
|     GGLContext *gl = gr_context; | ||||
|  | ||||
|     /* swap front and back buffers */ | ||||
|     gr_active_fb = (gr_active_fb + 1) & 1; | ||||
|  | ||||
|     /* copy data from the in-memory surface to the buffer we're about | ||||
|      * to make active. */ | ||||
|     memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data, | ||||
|            vi.xres * vi.yres * 2); | ||||
|  | ||||
|     /* inform the display driver */ | ||||
|     set_active_framebuffer(gr_active_fb); | ||||
| } | ||||
|  | ||||
| void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) | ||||
| { | ||||
|     GGLContext *gl = gr_context; | ||||
|     GGLint color[4]; | ||||
|     color[0] = ((r << 8) | r) + 1; | ||||
|     color[1] = ((g << 8) | g) + 1; | ||||
|     color[2] = ((b << 8) | b) + 1; | ||||
|     color[3] = ((a << 8) | a) + 1; | ||||
|     gl->color4xv(gl, color); | ||||
| } | ||||
|  | ||||
| int gr_measure(const char *s) | ||||
| { | ||||
|     return gr_font->cwidth * strlen(s); | ||||
| } | ||||
|  | ||||
| int gr_text(int x, int y, const char *s) | ||||
| { | ||||
|     GGLContext *gl = gr_context; | ||||
|     GRFont *font = gr_font; | ||||
|     unsigned off; | ||||
|  | ||||
|     y -= font->ascent; | ||||
|  | ||||
|     gl->bindTexture(gl, &font->texture); | ||||
|     gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); | ||||
|     gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); | ||||
|     gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); | ||||
|     gl->enable(gl, GGL_TEXTURE_2D); | ||||
|  | ||||
|     while((off = *s++)) { | ||||
|         off -= 32; | ||||
|         if (off < 96) { | ||||
|             gl->texCoord2i(gl, (off * font->cwidth) - x, 0 - y); | ||||
|             gl->recti(gl, x, y, x + font->cwidth, y + font->cheight); | ||||
|         } | ||||
|         x += font->cwidth; | ||||
|     } | ||||
|  | ||||
|     return x; | ||||
| } | ||||
|  | ||||
| void gr_fill(int x, int y, int w, int h) | ||||
| { | ||||
|     GGLContext *gl = gr_context; | ||||
|     gl->disable(gl, GGL_TEXTURE_2D); | ||||
|     gl->recti(gl, x, y, w, h); | ||||
| } | ||||
|  | ||||
| void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) { | ||||
|     if (gr_context == NULL) { | ||||
|         return; | ||||
|     } | ||||
|     GGLContext *gl = gr_context; | ||||
|  | ||||
|     gl->bindTexture(gl, (GGLSurface*) source); | ||||
|     gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); | ||||
|     gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); | ||||
|     gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); | ||||
|     gl->enable(gl, GGL_TEXTURE_2D); | ||||
|     gl->texCoord2i(gl, sx - dx, sy - dy); | ||||
|     gl->recti(gl, dx, dy, dx + w, dy + h); | ||||
| } | ||||
|  | ||||
| unsigned int gr_get_width(gr_surface surface) { | ||||
|     if (surface == NULL) { | ||||
|         return 0; | ||||
|     } | ||||
|     return ((GGLSurface*) surface)->width; | ||||
| } | ||||
|  | ||||
| unsigned int gr_get_height(gr_surface surface) { | ||||
|     if (surface == NULL) { | ||||
|         return 0; | ||||
|     } | ||||
|     return ((GGLSurface*) surface)->height; | ||||
| } | ||||
|  | ||||
| static void gr_init_font(void) | ||||
| { | ||||
|     GGLSurface *ftex; | ||||
|     unsigned char *bits, *rle; | ||||
|     unsigned char *in, data; | ||||
|  | ||||
|     gr_font = calloc(sizeof(*gr_font), 1); | ||||
|     ftex = &gr_font->texture; | ||||
|  | ||||
|     bits = malloc(font.width * font.height); | ||||
|  | ||||
|     ftex->version = sizeof(*ftex); | ||||
|     ftex->width = font.width; | ||||
|     ftex->height = font.height; | ||||
|     ftex->stride = font.width; | ||||
|     ftex->data = (void*) bits; | ||||
|     ftex->format = GGL_PIXEL_FORMAT_A_8; | ||||
|  | ||||
|     in = font.rundata; | ||||
|     while((data = *in++)) { | ||||
|         memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f); | ||||
|         bits += (data & 0x7f); | ||||
|     } | ||||
|  | ||||
|     gr_font->cwidth = font.cwidth; | ||||
|     gr_font->cheight = font.cheight; | ||||
|     gr_font->ascent = font.cheight - 2; | ||||
| } | ||||
|  | ||||
| int gr_init(void) | ||||
| { | ||||
|     gglInit(&gr_context); | ||||
|     GGLContext *gl = gr_context; | ||||
|  | ||||
|     gr_init_font(); | ||||
|     gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC); | ||||
|     if (gr_vt_fd < 0) { | ||||
|         // This is non-fatal; post-Cupcake kernels don't have tty0. | ||||
|         perror("can't open /dev/tty0"); | ||||
|     } else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) { | ||||
|         // However, if we do open tty0, we expect the ioctl to work. | ||||
|         perror("failed KDSETMODE to KD_GRAPHICS on tty0"); | ||||
|         gr_exit(); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     gr_fb_fd = get_framebuffer(gr_framebuffer); | ||||
|     if (gr_fb_fd < 0) { | ||||
|         gr_exit(); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     get_memory_surface(&gr_mem_surface); | ||||
|  | ||||
|     fprintf(stderr, "framebuffer: fd %d (%d x %d)\n", | ||||
|             gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height); | ||||
|  | ||||
|         /* start with 0 as front (displayed) and 1 as back (drawing) */ | ||||
|     gr_active_fb = 0; | ||||
|     set_active_framebuffer(0); | ||||
|     gl->colorBuffer(gl, &gr_mem_surface); | ||||
|  | ||||
|  | ||||
|     gl->activeTexture(gl, 0); | ||||
|     gl->enable(gl, GGL_BLEND); | ||||
|     gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void gr_exit(void) | ||||
| { | ||||
|     close(gr_fb_fd); | ||||
|     gr_fb_fd = -1; | ||||
|  | ||||
|     free(gr_mem_surface.data); | ||||
|  | ||||
|     ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT); | ||||
|     close(gr_vt_fd); | ||||
|     gr_vt_fd = -1; | ||||
| } | ||||
|  | ||||
| int gr_fb_width(void) | ||||
| { | ||||
|     return gr_framebuffer[0].width; | ||||
| } | ||||
|  | ||||
| int gr_fb_height(void) | ||||
| { | ||||
|     return gr_framebuffer[0].height; | ||||
| } | ||||
|  | ||||
| gr_pixel *gr_fb_data(void) | ||||
| { | ||||
|     return (unsigned short *) gr_mem_surface.data; | ||||
| } | ||||
							
								
								
									
										70
									
								
								minui/minui.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								minui/minui.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* | ||||
|  * 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 _MINUI_H_ | ||||
| #define _MINUI_H_ | ||||
|  | ||||
| typedef void* gr_surface; | ||||
| typedef unsigned short gr_pixel; | ||||
|  | ||||
| int gr_init(void); | ||||
| void gr_exit(void); | ||||
|  | ||||
| int gr_fb_width(void); | ||||
| int gr_fb_height(void); | ||||
| gr_pixel *gr_fb_data(void); | ||||
| void gr_flip(void); | ||||
|  | ||||
| void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); | ||||
| void gr_fill(int x, int y, int w, int h); | ||||
| int gr_text(int x, int y, const char *s); | ||||
| int gr_measure(const char *s); | ||||
|  | ||||
| void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy); | ||||
| unsigned int gr_get_width(gr_surface surface); | ||||
| unsigned int gr_get_height(gr_surface surface); | ||||
|  | ||||
| // input event structure, include <linux/input.h> for the definition. | ||||
| // see http://www.mjmwired.net/kernel/Documentation/input/ for info. | ||||
| struct input_event; | ||||
|  | ||||
| // Dream-specific key codes | ||||
| #define KEY_DREAM_HOME        102  // = KEY_HOME | ||||
| #define KEY_DREAM_RED         107  // = KEY_END | ||||
| #define KEY_DREAM_VOLUMEDOWN  114  // = KEY_VOLUMEDOWN | ||||
| #define KEY_DREAM_VOLUMEUP    115  // = KEY_VOLUMEUP | ||||
| #define KEY_DREAM_SYM         127  // = KEY_COMPOSE | ||||
| #define KEY_DREAM_MENU        139  // = KEY_MENU | ||||
| #define KEY_DREAM_BACK        158  // = KEY_BACK | ||||
| #define KEY_DREAM_FOCUS       211  // = KEY_HP (light touch on camera) | ||||
| #define KEY_DREAM_CAMERA      212  // = KEY_CAMERA | ||||
| #define KEY_DREAM_AT          215  // = KEY_EMAIL | ||||
| #define KEY_DREAM_GREEN       231 | ||||
| #define KEY_DREAM_FATTOUCH    258  // = BTN_2 ??? | ||||
| #define KEY_DREAM_BALL        272  // = BTN_MOUSE | ||||
| #define KEY_DREAM_TOUCH       330  // = BTN_TOUCH | ||||
|  | ||||
| int ev_init(void); | ||||
| void ev_exit(void); | ||||
| int ev_get(struct input_event *ev, unsigned dont_wait); | ||||
|  | ||||
| // Resources | ||||
|  | ||||
| // Returns 0 if no error, else negative. | ||||
| int res_create_surface(const char* name, gr_surface* pSurface); | ||||
| void res_free_surface(gr_surface surface); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										54
									
								
								minui/mkfont.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								minui/mkfont.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| int main(int argc, char *argv) | ||||
| { | ||||
|     unsigned n; | ||||
|     unsigned char *x; | ||||
|     unsigned m; | ||||
|     unsigned run_val; | ||||
|     unsigned run_count; | ||||
|   | ||||
|     n = gimp_image.width * gimp_image.height; | ||||
|     m = 0; | ||||
|     x = gimp_image.pixel_data; | ||||
|  | ||||
|     printf("struct {\n"); | ||||
|     printf("  unsigned width;\n"); | ||||
|     printf("  unsigned height;\n"); | ||||
|     printf("  unsigned cwidth;\n"); | ||||
|     printf("  unsigned cheight;\n"); | ||||
|     printf("  unsigned char rundata[];\n"); | ||||
|     printf("} font = {\n"); | ||||
|     printf("  .width = %d,\n  .height = %d,\n  .cwidth = %d,\n  .cheight = %d,\n", gimp_image.width, gimp_image.height, | ||||
|            gimp_image.width / 96, gimp_image.height); | ||||
|     printf("  .rundata = {\n"); | ||||
|     | ||||
|     run_val = (*x ? 0 : 255); | ||||
|     run_count = 1; | ||||
|     n--; | ||||
|     x+=3; | ||||
|  | ||||
|     while(n-- > 0) { | ||||
|         unsigned val = (*x ? 0 : 255); | ||||
|         x+=3; | ||||
|         if((val == run_val) && (run_count < 127)) { | ||||
|             run_count++; | ||||
|         } else { | ||||
| eject: | ||||
|             printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00)); | ||||
|             run_val = val; | ||||
|             run_count = 1; | ||||
|             m += 5; | ||||
|             if(m >= 75) { | ||||
|                 printf("\n"); | ||||
|                 m = 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00)); | ||||
|     printf("\n0x00,"); | ||||
|     printf("\n"); | ||||
|     printf("  }\n};\n"); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										202
									
								
								minui/resources.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								minui/resources.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
| /* | ||||
|  * 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 <unistd.h> | ||||
|  | ||||
| #include <fcntl.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <linux/fb.h> | ||||
| #include <linux/kd.h> | ||||
|  | ||||
| #include <pixelflinger/pixelflinger.h> | ||||
|  | ||||
| #include "minui.h" | ||||
|  | ||||
| // File signature for BMP files. | ||||
| // The letters 'BM' as a little-endian unsigned short. | ||||
|  | ||||
| #define BMP_SIGNATURE 0x4d42 | ||||
|  | ||||
| typedef struct { | ||||
|     // constant, value should equal BMP_SIGNATURE | ||||
|     unsigned short  bfType; | ||||
|     // size of the file in bytes. | ||||
|     unsigned long   bfSize; | ||||
|     // must always be set to zero. | ||||
|     unsigned short  bfReserved1; | ||||
|     // must always be set to zero. | ||||
|     unsigned short  bfReserved2; | ||||
|     // offset from the beginning of the file to the bitmap data. | ||||
|     unsigned long   bfOffBits; | ||||
|  | ||||
|     // The BITMAPINFOHEADER: | ||||
|     // size of the BITMAPINFOHEADER structure, in bytes. | ||||
|     unsigned long   biSize; | ||||
|     // width of the image, in pixels. | ||||
|     unsigned long   biWidth; | ||||
|     // height of the image, in pixels. | ||||
|     unsigned long   biHeight; | ||||
|     // number of planes of the target device, must be set to 1. | ||||
|     unsigned short  biPlanes; | ||||
|     // number of bits per pixel. | ||||
|     unsigned short  biBitCount; | ||||
|     // type of compression, zero means no compression. | ||||
|     unsigned long   biCompression; | ||||
|     // size of the image data, in bytes. If there is no compression, | ||||
|     // it is valid to set this member to zero. | ||||
|     unsigned long   biSizeImage; | ||||
|     // horizontal pixels per meter on the designated targer device, | ||||
|     // usually set to zero. | ||||
|     unsigned long   biXPelsPerMeter; | ||||
|     // vertical pixels per meter on the designated targer device, | ||||
|     // usually set to zero. | ||||
|     unsigned long   biYPelsPerMeter; | ||||
|     // number of colors used in the bitmap, if set to zero the | ||||
|     // number of colors is calculated using the biBitCount member. | ||||
|     unsigned long   biClrUsed; | ||||
|     // number of color that are 'important' for the bitmap, | ||||
|     // if set to zero, all colors are important. | ||||
|     unsigned long   biClrImportant; | ||||
| } __attribute__((packed)) BitMapFileHeader; | ||||
|  | ||||
| int res_create_surface(const char* name, gr_surface* pSurface) { | ||||
|     char resPath[256]; | ||||
|     BitMapFileHeader header; | ||||
|     GGLSurface* surface = NULL; | ||||
|     int result = 0; | ||||
|      | ||||
|     snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.bmp", name); | ||||
|     resPath[sizeof(resPath)-1] = '\0'; | ||||
|     int fd = open(resPath, O_RDONLY); | ||||
|     if (fd == -1) { | ||||
|         result = -1; | ||||
|         goto exit; | ||||
|     } | ||||
|     size_t bytesRead = read(fd, &header, sizeof(header)); | ||||
|     if (bytesRead != sizeof(header)) { | ||||
|         result = -2; | ||||
|         goto exit; | ||||
|     } | ||||
|     if (header.bfType != BMP_SIGNATURE) { | ||||
|         result = -3; // Not a legal header | ||||
|         goto exit; | ||||
|     } | ||||
|     if (header.biPlanes != 1) { | ||||
|         result = -4; | ||||
|         goto exit; | ||||
|     } | ||||
|     if (!(header.biBitCount == 24 || header.biBitCount == 32)) { | ||||
|         result = -5; | ||||
|         goto exit; | ||||
|     } | ||||
|     if (header.biCompression != 0) { | ||||
|         result = -6; | ||||
|         goto exit; | ||||
|     } | ||||
|     size_t width = header.biWidth; | ||||
|     size_t height = header.biHeight; | ||||
|     size_t stride = 4 * width; | ||||
|     size_t pixelSize = stride * height; | ||||
|      | ||||
|     surface = malloc(sizeof(GGLSurface) + pixelSize); | ||||
|     if (surface == NULL) { | ||||
|         result = -7; | ||||
|         goto exit; | ||||
|     } | ||||
|     unsigned char* pData = (unsigned char*) (surface + 1); | ||||
|     surface->version = sizeof(GGLSurface); | ||||
|     surface->width = width; | ||||
|     surface->height = height; | ||||
|     surface->stride = width; /* Yes, pixels, not bytes */ | ||||
|     surface->data = pData; | ||||
|     surface->format = (header.biBitCount == 24) ? | ||||
|             GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888; | ||||
|  | ||||
|     // Source pixel bytes are stored B G R {A} | ||||
|      | ||||
|     lseek(fd, header.bfOffBits, SEEK_SET); | ||||
|     size_t y; | ||||
|     if (header.biBitCount == 24) { // RGB | ||||
|         size_t inputStride = (((3 * width + 3) >> 2) << 2); | ||||
|         for (y = 0; y < height; y++) { | ||||
|             unsigned char* pRow = pData + (height - (y + 1)) * stride; | ||||
|             bytesRead = read(fd,  pRow, inputStride); | ||||
|             if (bytesRead != inputStride) { | ||||
|                 result = -8; | ||||
|                 goto exit; | ||||
|             } | ||||
|             int x; | ||||
|             for(x = width - 1; x >= 0; x--) { | ||||
|                 int sx = x * 3; | ||||
|                 int dx = x * 4; | ||||
|                 unsigned char b = pRow[sx]; | ||||
|                 unsigned char g = pRow[sx + 1]; | ||||
|                 unsigned char r = pRow[sx + 2]; | ||||
|                 unsigned char a = 0xff; | ||||
|                 pRow[dx    ] = r; // r | ||||
|                 pRow[dx + 1] = g; // g | ||||
|                 pRow[dx + 2] = b; // b; | ||||
|                 pRow[dx + 3] = a; | ||||
|             } | ||||
|         } | ||||
|     } else { // RGBA | ||||
|         for (y = 0; y < height; y++) { | ||||
|             unsigned char* pRow = pData + (height - (y + 1)) * stride; | ||||
|             bytesRead = read(fd,  pRow, stride); | ||||
|             if (bytesRead != stride) { | ||||
|                 result = -9; | ||||
|                 goto exit; | ||||
|             } | ||||
|             size_t x; | ||||
|             for(x = 0; x < width; x++) { | ||||
|                 size_t xx = x * 4; | ||||
|                 unsigned char b = pRow[xx]; | ||||
|                 unsigned char g = pRow[xx + 1]; | ||||
|                 unsigned char r = pRow[xx + 2]; | ||||
|                 unsigned char a = pRow[xx + 3]; | ||||
|                 pRow[xx    ] = r; | ||||
|                 pRow[xx + 1] = g; | ||||
|                 pRow[xx + 2] = b; | ||||
|                 pRow[xx + 3] = a; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     *pSurface = (gr_surface) surface; | ||||
|  | ||||
| exit: | ||||
|     if (fd >= 0) { | ||||
|         close(fd); | ||||
|     } | ||||
|     if (result < 0) { | ||||
|         if (surface) { | ||||
|             free(surface); | ||||
|         } | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| void res_free_surface(gr_surface surface) { | ||||
|     GGLSurface* pSurface = (GGLSurface*) surface; | ||||
|     if (pSurface) { | ||||
|         free(pSurface); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								minzip/Android.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								minzip/Android.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| LOCAL_PATH := $(call my-dir) | ||||
| include $(CLEAR_VARS) | ||||
|  | ||||
| LOCAL_SRC_FILES := \ | ||||
| 	Hash.c \ | ||||
| 	SysUtil.c \ | ||||
| 	DirUtil.c \ | ||||
| 	Inlines.c \ | ||||
| 	Zip.c | ||||
|  | ||||
| LOCAL_C_INCLUDES += \ | ||||
| 	external/zlib \ | ||||
| 	external/safe-iop/include | ||||
| 	 | ||||
| LOCAL_MODULE := libminzip | ||||
|  | ||||
| LOCAL_CFLAGS += -Wall | ||||
|  | ||||
| include $(BUILD_STATIC_LIBRARY) | ||||
							
								
								
									
										357
									
								
								minzip/Bits.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								minzip/Bits.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,357 @@ | ||||
| /* | ||||
|  * Copyright 2006 The Android Open Source Project | ||||
|  * | ||||
|  * Some handy functions for manipulating bits and bytes. | ||||
|  */ | ||||
| #ifndef _MINZIP_BITS | ||||
| #define _MINZIP_BITS | ||||
|  | ||||
| #include "inline_magic.h" | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| /* | ||||
|  * Get 1 byte.  (Included to make the code more legible.) | ||||
|  */ | ||||
| INLINE unsigned char get1(unsigned const char* pSrc) | ||||
| { | ||||
|     return *pSrc; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get 2 big-endian bytes. | ||||
|  */ | ||||
| INLINE unsigned short get2BE(unsigned char const* pSrc) | ||||
| { | ||||
|     unsigned short result; | ||||
|  | ||||
|     result = *pSrc++ << 8; | ||||
|     result |= *pSrc++; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get 4 big-endian bytes. | ||||
|  */ | ||||
| INLINE unsigned int get4BE(unsigned char const* pSrc) | ||||
| { | ||||
|     unsigned int result; | ||||
|  | ||||
|     result = *pSrc++ << 24; | ||||
|     result |= *pSrc++ << 16; | ||||
|     result |= *pSrc++ << 8; | ||||
|     result |= *pSrc++; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get 8 big-endian bytes. | ||||
|  */ | ||||
| INLINE unsigned long long get8BE(unsigned char const* pSrc) | ||||
| { | ||||
|     unsigned long long result; | ||||
|  | ||||
|     result = (unsigned long long) *pSrc++ << 56; | ||||
|     result |= (unsigned long long) *pSrc++ << 48; | ||||
|     result |= (unsigned long long) *pSrc++ << 40; | ||||
|     result |= (unsigned long long) *pSrc++ << 32; | ||||
|     result |= (unsigned long long) *pSrc++ << 24; | ||||
|     result |= (unsigned long long) *pSrc++ << 16; | ||||
|     result |= (unsigned long long) *pSrc++ << 8; | ||||
|     result |= (unsigned long long) *pSrc++; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get 2 little-endian bytes. | ||||
|  */ | ||||
| INLINE unsigned short get2LE(unsigned char const* pSrc) | ||||
| { | ||||
|     unsigned short result; | ||||
|  | ||||
|     result = *pSrc++; | ||||
|     result |= *pSrc++ << 8; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get 4 little-endian bytes. | ||||
|  */ | ||||
| INLINE unsigned int get4LE(unsigned char const* pSrc) | ||||
| { | ||||
|     unsigned int result; | ||||
|  | ||||
|     result = *pSrc++; | ||||
|     result |= *pSrc++ << 8; | ||||
|     result |= *pSrc++ << 16; | ||||
|     result |= *pSrc++ << 24; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get 8 little-endian bytes. | ||||
|  */ | ||||
| INLINE unsigned long long get8LE(unsigned char const* pSrc) | ||||
| { | ||||
|     unsigned long long result; | ||||
|  | ||||
|     result = (unsigned long long) *pSrc++; | ||||
|     result |= (unsigned long long) *pSrc++ << 8; | ||||
|     result |= (unsigned long long) *pSrc++ << 16; | ||||
|     result |= (unsigned long long) *pSrc++ << 24; | ||||
|     result |= (unsigned long long) *pSrc++ << 32; | ||||
|     result |= (unsigned long long) *pSrc++ << 40; | ||||
|     result |= (unsigned long long) *pSrc++ << 48; | ||||
|     result |= (unsigned long long) *pSrc++ << 56; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Grab 1 byte and advance the data pointer. | ||||
|  */ | ||||
| INLINE unsigned char read1(unsigned const char** ppSrc) | ||||
| { | ||||
|     return *(*ppSrc)++; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Grab 2 big-endian bytes and advance the data pointer. | ||||
|  */ | ||||
| INLINE unsigned short read2BE(unsigned char const** ppSrc) | ||||
| { | ||||
|     unsigned short result; | ||||
|  | ||||
|     result = *(*ppSrc)++ << 8; | ||||
|     result |= *(*ppSrc)++; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Grab 4 big-endian bytes and advance the data pointer. | ||||
|  */ | ||||
| INLINE unsigned int read4BE(unsigned char const** ppSrc) | ||||
| { | ||||
|     unsigned int result; | ||||
|  | ||||
|     result = *(*ppSrc)++ << 24; | ||||
|     result |= *(*ppSrc)++ << 16; | ||||
|     result |= *(*ppSrc)++ << 8; | ||||
|     result |= *(*ppSrc)++; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get 8 big-endian bytes. | ||||
|  */ | ||||
| INLINE unsigned long long read8BE(unsigned char const** ppSrc) | ||||
| { | ||||
|     unsigned long long result; | ||||
|  | ||||
|     result = (unsigned long long) *(*ppSrc)++ << 56; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 48; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 40; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 32; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 24; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 16; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 8; | ||||
|     result |= (unsigned long long) *(*ppSrc)++; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Grab 2 little-endian bytes and advance the data pointer. | ||||
|  */ | ||||
| INLINE unsigned short read2LE(unsigned char const** ppSrc) | ||||
| { | ||||
|     unsigned short result; | ||||
|  | ||||
|     result = *(*ppSrc)++; | ||||
|     result |= *(*ppSrc)++ << 8; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Grab 4 little-endian bytes and advance the data pointer. | ||||
|  */ | ||||
| INLINE unsigned int read4LE(unsigned char const** ppSrc) | ||||
| { | ||||
|     unsigned int result; | ||||
|  | ||||
|     result = *(*ppSrc)++; | ||||
|     result |= *(*ppSrc)++ << 8; | ||||
|     result |= *(*ppSrc)++ << 16; | ||||
|     result |= *(*ppSrc)++ << 24; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get 8 little-endian bytes. | ||||
|  */ | ||||
| INLINE unsigned long long read8LE(unsigned char const** ppSrc) | ||||
| { | ||||
|     unsigned long long result; | ||||
|  | ||||
|     result = (unsigned long long) *(*ppSrc)++; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 8; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 16; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 24; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 32; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 40; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 48; | ||||
|     result |= (unsigned long long) *(*ppSrc)++ << 56; | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Skip over a UTF-8 string. | ||||
|  */ | ||||
| INLINE void skipUtf8String(unsigned char const** ppSrc) | ||||
| { | ||||
|     unsigned int length = read4BE(ppSrc); | ||||
|  | ||||
|     (*ppSrc) += length; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Read a UTF-8 string into a fixed-size buffer, and null-terminate it. | ||||
|  * | ||||
|  * Returns the length of the original string. | ||||
|  */ | ||||
| INLINE int readUtf8String(unsigned char const** ppSrc, char* buf, size_t bufLen) | ||||
| { | ||||
|     unsigned int length = read4BE(ppSrc); | ||||
|     size_t copyLen = (length < bufLen) ? length : bufLen-1; | ||||
|  | ||||
|     memcpy(buf, *ppSrc, copyLen); | ||||
|     buf[copyLen] = '\0'; | ||||
|  | ||||
|     (*ppSrc) += length; | ||||
|     return length; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Read a UTF-8 string into newly-allocated storage, and null-terminate it. | ||||
|  * | ||||
|  * Returns the string and its length.  (The latter is probably unnecessary | ||||
|  * for the way we're using UTF8.) | ||||
|  */ | ||||
| INLINE char* readNewUtf8String(unsigned char const** ppSrc, size_t* pLength) | ||||
| { | ||||
|     unsigned int length = read4BE(ppSrc); | ||||
|     char* buf; | ||||
|  | ||||
|     buf = (char*) malloc(length+1); | ||||
|  | ||||
|     memcpy(buf, *ppSrc, length); | ||||
|     buf[length] = '\0'; | ||||
|  | ||||
|     (*ppSrc) += length; | ||||
|  | ||||
|     *pLength = length; | ||||
|     return buf; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Set 1 byte.  (Included to make the code more legible.) | ||||
|  */ | ||||
| INLINE void set1(unsigned char* buf, unsigned char val) | ||||
| { | ||||
|     *buf = (unsigned char)(val); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Set 2 big-endian bytes. | ||||
|  */ | ||||
| INLINE void set2BE(unsigned char* buf, unsigned short val) | ||||
| { | ||||
|     *buf++ = (unsigned char)(val >> 8); | ||||
|     *buf = (unsigned char)(val); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Set 4 big-endian bytes. | ||||
|  */ | ||||
| INLINE void set4BE(unsigned char* buf, unsigned int val) | ||||
| { | ||||
|     *buf++ = (unsigned char)(val >> 24); | ||||
|     *buf++ = (unsigned char)(val >> 16); | ||||
|     *buf++ = (unsigned char)(val >> 8); | ||||
|     *buf = (unsigned char)(val); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Set 8 big-endian bytes. | ||||
|  */ | ||||
| INLINE void set8BE(unsigned char* buf, unsigned long long val) | ||||
| { | ||||
|     *buf++ = (unsigned char)(val >> 56); | ||||
|     *buf++ = (unsigned char)(val >> 48); | ||||
|     *buf++ = (unsigned char)(val >> 40); | ||||
|     *buf++ = (unsigned char)(val >> 32); | ||||
|     *buf++ = (unsigned char)(val >> 24); | ||||
|     *buf++ = (unsigned char)(val >> 16); | ||||
|     *buf++ = (unsigned char)(val >> 8); | ||||
|     *buf = (unsigned char)(val); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Set 2 little-endian bytes. | ||||
|  */ | ||||
| INLINE void set2LE(unsigned char* buf, unsigned short val) | ||||
| { | ||||
|     *buf++ = (unsigned char)(val); | ||||
|     *buf = (unsigned char)(val >> 8); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Set 4 little-endian bytes. | ||||
|  */ | ||||
| INLINE void set4LE(unsigned char* buf, unsigned int val) | ||||
| { | ||||
|     *buf++ = (unsigned char)(val); | ||||
|     *buf++ = (unsigned char)(val >> 8); | ||||
|     *buf++ = (unsigned char)(val >> 16); | ||||
|     *buf = (unsigned char)(val >> 24); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Set 8 little-endian bytes. | ||||
|  */ | ||||
| INLINE void set8LE(unsigned char* buf, unsigned long long val) | ||||
| { | ||||
|     *buf++ = (unsigned char)(val); | ||||
|     *buf++ = (unsigned char)(val >> 8); | ||||
|     *buf++ = (unsigned char)(val >> 16); | ||||
|     *buf++ = (unsigned char)(val >> 24); | ||||
|     *buf++ = (unsigned char)(val >> 32); | ||||
|     *buf++ = (unsigned char)(val >> 40); | ||||
|     *buf++ = (unsigned char)(val >> 48); | ||||
|     *buf = (unsigned char)(val >> 56); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Stuff a UTF-8 string into the buffer. | ||||
|  */ | ||||
| INLINE void setUtf8String(unsigned char* buf, const unsigned char* str) | ||||
| { | ||||
|     unsigned int strLen = strlen((const char*)str); | ||||
|  | ||||
|     set4BE(buf, strLen); | ||||
|     memcpy(buf + sizeof(unsigned int), str, strLen); | ||||
| } | ||||
|  | ||||
| #endif /*_MINZIP_BITS*/ | ||||
							
								
								
									
										280
									
								
								minzip/DirUtil.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								minzip/DirUtil.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | ||||
| /* | ||||
|  * 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 <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
| #include <errno.h> | ||||
| #include <dirent.h> | ||||
| #include <limits.h> | ||||
|  | ||||
| #include "DirUtil.h" | ||||
|  | ||||
| typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus; | ||||
|  | ||||
| static DirStatus | ||||
| getPathDirStatus(const char *path) | ||||
| { | ||||
|     struct stat st; | ||||
|     int err; | ||||
|  | ||||
|     err = stat(path, &st); | ||||
|     if (err == 0) { | ||||
|         /* Something's there; make sure it's a directory. | ||||
|          */ | ||||
|         if (S_ISDIR(st.st_mode)) { | ||||
|             return DDIR; | ||||
|         } | ||||
|         errno = ENOTDIR; | ||||
|         return DILLEGAL; | ||||
|     } else if (errno != ENOENT) { | ||||
|         /* Something went wrong, or something in the path | ||||
|          * is bad.  Can't do anything in this situation. | ||||
|          */ | ||||
|         return DILLEGAL; | ||||
|     } | ||||
|     return DMISSING; | ||||
| } | ||||
|  | ||||
| int | ||||
| dirCreateHierarchy(const char *path, int mode, | ||||
|         const struct utimbuf *timestamp, bool stripFileName) | ||||
| { | ||||
|     DirStatus ds; | ||||
|  | ||||
|     /* Check for an empty string before we bother | ||||
|      * making any syscalls. | ||||
|      */ | ||||
|     if (path[0] == '\0') { | ||||
|         errno = ENOENT; | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* Allocate a path that we can modify; stick a slash on | ||||
|      * the end to make things easier. | ||||
|      */ | ||||
|     size_t pathLen = strlen(path); | ||||
|     char *cpath = (char *)malloc(pathLen + 2); | ||||
|     if (cpath == NULL) { | ||||
|         errno = ENOMEM; | ||||
|         return -1; | ||||
|     } | ||||
|     memcpy(cpath, path, pathLen); | ||||
|     if (stripFileName) { | ||||
|         /* Strip everything after the last slash. | ||||
|          */ | ||||
|         char *c = cpath + pathLen - 1; | ||||
|         while (c != cpath && *c != '/') { | ||||
|             c--; | ||||
|         } | ||||
|         if (c == cpath) { | ||||
| //xxx test this path | ||||
|             /* No directory component.  Act like the path was empty. | ||||
|              */ | ||||
|             errno = ENOENT; | ||||
|             free(cpath); | ||||
|             return -1; | ||||
|         } | ||||
|         c[1] = '\0';    // Terminate after the slash we found. | ||||
|     } else { | ||||
|         /* Make sure that the path ends in a slash. | ||||
|          */ | ||||
|         cpath[pathLen] = '/'; | ||||
|         cpath[pathLen + 1] = '\0'; | ||||
|     } | ||||
|  | ||||
|     /* See if it already exists. | ||||
|      */ | ||||
|     ds = getPathDirStatus(cpath); | ||||
|     if (ds == DDIR) { | ||||
|         return 0; | ||||
|     } else if (ds == DILLEGAL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* Walk up the path from the root and make each level. | ||||
|      * If a directory already exists, no big deal. | ||||
|      */ | ||||
|     char *p = cpath; | ||||
|     while (*p != '\0') { | ||||
|         /* Skip any slashes, watching out for the end of the string. | ||||
|          */ | ||||
|         while (*p != '\0' && *p == '/') { | ||||
|             p++; | ||||
|         } | ||||
|         if (*p == '\0') { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         /* Find the end of the next path component. | ||||
|          * We know that we'll see a slash before the NUL, | ||||
|          * because we added it, above. | ||||
|          */ | ||||
|         while (*p != '/') { | ||||
|             p++; | ||||
|         } | ||||
|         *p = '\0'; | ||||
|  | ||||
|         /* Check this part of the path and make a new directory | ||||
|          * if necessary. | ||||
|          */ | ||||
|         ds = getPathDirStatus(cpath); | ||||
|         if (ds == DILLEGAL) { | ||||
|             /* Could happen if some other process/thread is | ||||
|              * messing with the filesystem. | ||||
|              */ | ||||
|             free(cpath); | ||||
|             return -1; | ||||
|         } else if (ds == DMISSING) { | ||||
|             int err; | ||||
|  | ||||
|             err = mkdir(cpath, mode); | ||||
|             if (err != 0) { | ||||
|                 free(cpath); | ||||
|                 return -1; | ||||
|             } | ||||
|             if (timestamp != NULL && utime(cpath, timestamp)) { | ||||
|                 free(cpath); | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|         // else, this directory already exists. | ||||
|          | ||||
|         /* Repair the path and continue. | ||||
|          */ | ||||
|         *p = '/'; | ||||
|     } | ||||
|     free(cpath); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| dirUnlinkHierarchy(const char *path) | ||||
| { | ||||
|     struct stat st; | ||||
|     DIR *dir; | ||||
|     struct dirent *de; | ||||
|     int fail = 0; | ||||
|  | ||||
|     /* is it a file or directory? */ | ||||
|     if (lstat(path, &st) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* a file, so unlink it */ | ||||
|     if (!S_ISDIR(st.st_mode)) { | ||||
|         return unlink(path); | ||||
|     } | ||||
|  | ||||
|     /* a directory, so open handle */ | ||||
|     dir = opendir(path); | ||||
|     if (dir == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* recurse over components */ | ||||
|     errno = 0; | ||||
|     while ((de = readdir(dir)) != NULL) { | ||||
| //TODO: don't blow the stack | ||||
|         char dn[PATH_MAX]; | ||||
|         if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { | ||||
|             continue; | ||||
|         } | ||||
|         snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); | ||||
|         if (dirUnlinkHierarchy(dn) < 0) { | ||||
|             fail = 1; | ||||
|             break; | ||||
|         } | ||||
|         errno = 0; | ||||
|     } | ||||
|     /* in case readdir or unlink_recursive failed */ | ||||
|     if (fail || errno < 0) { | ||||
|         int save = errno; | ||||
|         closedir(dir); | ||||
|         errno = save; | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* close directory handle */ | ||||
|     if (closedir(dir) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* delete target directory */ | ||||
|     return rmdir(path); | ||||
| } | ||||
|  | ||||
| int | ||||
| dirSetHierarchyPermissions(const char *path, | ||||
|         int uid, int gid, int dirMode, int fileMode) | ||||
| { | ||||
|     struct stat st; | ||||
|     if (lstat(path, &st)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* ignore symlinks */ | ||||
|     if (S_ISLNK(st.st_mode)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /* directories and files get different permissions */ | ||||
|     if (chown(path, uid, gid) || | ||||
|         chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* recurse over directory components */ | ||||
|     if (S_ISDIR(st.st_mode)) { | ||||
|         DIR *dir = opendir(path); | ||||
|         if (dir == NULL) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         errno = 0; | ||||
|         const struct dirent *de; | ||||
|         while (errno == 0 && (de = readdir(dir)) != NULL) { | ||||
|             if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             char dn[PATH_MAX]; | ||||
|             snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); | ||||
|             if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) { | ||||
|                 errno = 0; | ||||
|             } else if (errno == 0) { | ||||
|                 errno = -1; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (errno != 0) { | ||||
|             int save = errno; | ||||
|             closedir(dir); | ||||
|             errno = save; | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         if (closedir(dir)) { | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										51
									
								
								minzip/DirUtil.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								minzip/DirUtil.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| /* | ||||
|  * 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 MINZIP_DIRUTIL_H_ | ||||
| #define MINZIP_DIRUTIL_H_ | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <utime.h> | ||||
|  | ||||
| /* Like "mkdir -p", try to guarantee that all directories | ||||
|  * specified in path are present, creating as many directories | ||||
|  * as necessary.  The specified mode is passed to all mkdir | ||||
|  * calls;  no modifications are made to umask. | ||||
|  * | ||||
|  * If stripFileName is set, everything after the final '/' | ||||
|  * is stripped before creating the directory hierarchy. | ||||
|  * | ||||
|  * If timestamp is non-NULL, new directories will be timestamped accordingly. | ||||
|  * | ||||
|  * Returns 0 on success; returns -1 (and sets errno) on failure | ||||
|  * (usually if some element of path is not a directory). | ||||
|  */ | ||||
| int dirCreateHierarchy(const char *path, int mode, | ||||
|         const struct utimbuf *timestamp, bool stripFileName); | ||||
|  | ||||
| /* rm -rf <path> | ||||
|  */ | ||||
| int dirUnlinkHierarchy(const char *path); | ||||
|  | ||||
| /* chown -R <uid>:<gid> <path> | ||||
|  * chmod -R <mode> <path> | ||||
|  * | ||||
|  * Sets directories to <dirMode> and files to <fileMode>.  Skips symlinks. | ||||
|  */ | ||||
| int dirSetHierarchyPermissions(const char *path, | ||||
|          int uid, int gid, int dirMode, int fileMode); | ||||
|  | ||||
| #endif  // MINZIP_DIRUTIL_H_ | ||||
							
								
								
									
										390
									
								
								minzip/Hash.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								minzip/Hash.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,390 @@ | ||||
| /* | ||||
|  * Copyright 2006 The Android Open Source Project | ||||
|  * | ||||
|  * Hash table.  The dominant calls are add and lookup, with removals | ||||
|  * happening very infrequently.  We use probing, and don't worry much | ||||
|  * about tombstone removal. | ||||
|  */ | ||||
| #include <stdlib.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| #define LOG_TAG "minzip" | ||||
| #include "Log.h" | ||||
| #include "Hash.h" | ||||
|  | ||||
| /* table load factor, i.e. how full can it get before we resize */ | ||||
| //#define LOAD_NUMER  3       // 75% | ||||
| //#define LOAD_DENOM  4 | ||||
| #define LOAD_NUMER  5       // 62.5% | ||||
| #define LOAD_DENOM  8 | ||||
| //#define LOAD_NUMER  1       // 50% | ||||
| //#define LOAD_DENOM  2 | ||||
|  | ||||
| /* | ||||
|  * Compute the capacity needed for a table to hold "size" elements. | ||||
|  */ | ||||
| size_t mzHashSize(size_t size) { | ||||
|     return (size * LOAD_DENOM) / LOAD_NUMER +1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Round up to the next highest power of 2. | ||||
|  * | ||||
|  * Found on http://graphics.stanford.edu/~seander/bithacks.html. | ||||
|  */ | ||||
| unsigned int roundUpPower2(unsigned int val) | ||||
| { | ||||
|     val--; | ||||
|     val |= val >> 1; | ||||
|     val |= val >> 2; | ||||
|     val |= val >> 4; | ||||
|     val |= val >> 8; | ||||
|     val |= val >> 16; | ||||
|     val++; | ||||
|  | ||||
|     return val; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Create and initialize a hash table. | ||||
|  */ | ||||
| HashTable* mzHashTableCreate(size_t initialSize, HashFreeFunc freeFunc) | ||||
| { | ||||
|     HashTable* pHashTable; | ||||
|  | ||||
|     assert(initialSize > 0); | ||||
|  | ||||
|     pHashTable = (HashTable*) malloc(sizeof(*pHashTable)); | ||||
|     if (pHashTable == NULL) | ||||
|         return NULL; | ||||
|  | ||||
|     pHashTable->tableSize = roundUpPower2(initialSize); | ||||
|     pHashTable->numEntries = pHashTable->numDeadEntries = 0; | ||||
|     pHashTable->freeFunc = freeFunc; | ||||
|     pHashTable->pEntries = | ||||
|         (HashEntry*) calloc((size_t)pHashTable->tableSize, sizeof(HashTable)); | ||||
|     if (pHashTable->pEntries == NULL) { | ||||
|         free(pHashTable); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     return pHashTable; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Clear out all entries. | ||||
|  */ | ||||
| void mzHashTableClear(HashTable* pHashTable) | ||||
| { | ||||
|     HashEntry* pEnt; | ||||
|     int i; | ||||
|  | ||||
|     pEnt = pHashTable->pEntries; | ||||
|     for (i = 0; i < pHashTable->tableSize; i++, pEnt++) { | ||||
|         if (pEnt->data == HASH_TOMBSTONE) { | ||||
|             // nuke entry | ||||
|             pEnt->data = NULL; | ||||
|         } else if (pEnt->data != NULL) { | ||||
|             // call free func then nuke entry | ||||
|             if (pHashTable->freeFunc != NULL) | ||||
|                 (*pHashTable->freeFunc)(pEnt->data); | ||||
|             pEnt->data = NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pHashTable->numEntries = 0; | ||||
|     pHashTable->numDeadEntries = 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Free the table. | ||||
|  */ | ||||
| void mzHashTableFree(HashTable* pHashTable) | ||||
| { | ||||
|     if (pHashTable == NULL) | ||||
|         return; | ||||
|     mzHashTableClear(pHashTable); | ||||
|     free(pHashTable->pEntries); | ||||
|     free(pHashTable); | ||||
| } | ||||
|  | ||||
| #ifndef NDEBUG | ||||
| /* | ||||
|  * Count up the number of tombstone entries in the hash table. | ||||
|  */ | ||||
| static int countTombStones(HashTable* pHashTable) | ||||
| { | ||||
|     int i, count; | ||||
|  | ||||
|     for (count = i = 0; i < pHashTable->tableSize; i++) { | ||||
|         if (pHashTable->pEntries[i].data == HASH_TOMBSTONE) | ||||
|             count++; | ||||
|     } | ||||
|     return count; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Resize a hash table.  We do this when adding an entry increased the | ||||
|  * size of the table beyond its comfy limit. | ||||
|  * | ||||
|  * This essentially requires re-inserting all elements into the new storage. | ||||
|  * | ||||
|  * If multiple threads can access the hash table, the table's lock should | ||||
|  * have been grabbed before issuing the "lookup+add" call that led to the | ||||
|  * resize, so we don't have a synchronization problem here. | ||||
|  */ | ||||
| static bool resizeHash(HashTable* pHashTable, int newSize) | ||||
| { | ||||
|     HashEntry* pNewEntries; | ||||
|     int i; | ||||
|  | ||||
|     assert(countTombStones(pHashTable) == pHashTable->numDeadEntries); | ||||
|     //LOGI("before: dead=%d\n", pHashTable->numDeadEntries); | ||||
|  | ||||
|     pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashTable)); | ||||
|     if (pNewEntries == NULL) | ||||
|         return false; | ||||
|  | ||||
|     for (i = 0; i < pHashTable->tableSize; i++) { | ||||
|         void* data = pHashTable->pEntries[i].data; | ||||
|         if (data != NULL && data != HASH_TOMBSTONE) { | ||||
|             int hashValue = pHashTable->pEntries[i].hashValue; | ||||
|             int newIdx; | ||||
|  | ||||
|             /* probe for new spot, wrapping around */ | ||||
|             newIdx = hashValue & (newSize-1); | ||||
|             while (pNewEntries[newIdx].data != NULL) | ||||
|                 newIdx = (newIdx + 1) & (newSize-1); | ||||
|  | ||||
|             pNewEntries[newIdx].hashValue = hashValue; | ||||
|             pNewEntries[newIdx].data = data; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     free(pHashTable->pEntries); | ||||
|     pHashTable->pEntries = pNewEntries; | ||||
|     pHashTable->tableSize = newSize; | ||||
|     pHashTable->numDeadEntries = 0; | ||||
|  | ||||
|     assert(countTombStones(pHashTable) == 0); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Look up an entry. | ||||
|  * | ||||
|  * We probe on collisions, wrapping around the table. | ||||
|  */ | ||||
| void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item, | ||||
|     HashCompareFunc cmpFunc, bool doAdd) | ||||
| { | ||||
|     HashEntry* pEntry; | ||||
|     HashEntry* pEnd; | ||||
|     void* result = NULL; | ||||
|  | ||||
|     assert(pHashTable->tableSize > 0); | ||||
|     assert(item != HASH_TOMBSTONE); | ||||
|     assert(item != NULL); | ||||
|  | ||||
|     /* jump to the first entry and probe for a match */ | ||||
|     pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; | ||||
|     pEnd = &pHashTable->pEntries[pHashTable->tableSize]; | ||||
|     while (pEntry->data != NULL) { | ||||
|         if (pEntry->data != HASH_TOMBSTONE && | ||||
|             pEntry->hashValue == itemHash && | ||||
|             (*cmpFunc)(pEntry->data, item) == 0) | ||||
|         { | ||||
|             /* match */ | ||||
|             //LOGD("+++ match on entry %d\n", pEntry - pHashTable->pEntries); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         pEntry++; | ||||
|         if (pEntry == pEnd) {     /* wrap around to start */ | ||||
|             if (pHashTable->tableSize == 1) | ||||
|                 break;      /* edge case - single-entry table */ | ||||
|             pEntry = pHashTable->pEntries; | ||||
|         } | ||||
|  | ||||
|         //LOGI("+++ look probing %d...\n", pEntry - pHashTable->pEntries); | ||||
|     } | ||||
|  | ||||
|     if (pEntry->data == NULL) { | ||||
|         if (doAdd) { | ||||
|             pEntry->hashValue = itemHash; | ||||
|             pEntry->data = item; | ||||
|             pHashTable->numEntries++; | ||||
|  | ||||
|             /* | ||||
|              * We've added an entry.  See if this brings us too close to full. | ||||
|              */ | ||||
|             if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM | ||||
|                 > pHashTable->tableSize * LOAD_NUMER) | ||||
|             { | ||||
|                 if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) { | ||||
|                     /* don't really have a way to indicate failure */ | ||||
|                     LOGE("Dalvik hash resize failure\n"); | ||||
|                     abort(); | ||||
|                 } | ||||
|                 /* note "pEntry" is now invalid */ | ||||
|             } else { | ||||
|                 //LOGW("okay %d/%d/%d\n", | ||||
|                 //    pHashTable->numEntries, pHashTable->tableSize, | ||||
|                 //    (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM); | ||||
|             } | ||||
|  | ||||
|             /* full table is bad -- search for nonexistent never halts */ | ||||
|             assert(pHashTable->numEntries < pHashTable->tableSize); | ||||
|             result = item; | ||||
|         } else { | ||||
|             assert(result == NULL); | ||||
|         } | ||||
|     } else { | ||||
|         result = pEntry->data; | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Remove an entry from the table. | ||||
|  * | ||||
|  * Does NOT invoke the "free" function on the item. | ||||
|  */ | ||||
| bool mzHashTableRemove(HashTable* pHashTable, unsigned int itemHash, void* item) | ||||
| { | ||||
|     HashEntry* pEntry; | ||||
|     HashEntry* pEnd; | ||||
|  | ||||
|     assert(pHashTable->tableSize > 0); | ||||
|  | ||||
|     /* jump to the first entry and probe for a match */ | ||||
|     pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; | ||||
|     pEnd = &pHashTable->pEntries[pHashTable->tableSize]; | ||||
|     while (pEntry->data != NULL) { | ||||
|         if (pEntry->data == item) { | ||||
|             //LOGI("+++ stepping on entry %d\n", pEntry - pHashTable->pEntries); | ||||
|             pEntry->data = HASH_TOMBSTONE; | ||||
|             pHashTable->numEntries--; | ||||
|             pHashTable->numDeadEntries++; | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         pEntry++; | ||||
|         if (pEntry == pEnd) {     /* wrap around to start */ | ||||
|             if (pHashTable->tableSize == 1) | ||||
|                 break;      /* edge case - single-entry table */ | ||||
|             pEntry = pHashTable->pEntries; | ||||
|         } | ||||
|  | ||||
|         //LOGI("+++ del probing %d...\n", pEntry - pHashTable->pEntries); | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Execute a function on every entry in the hash table. | ||||
|  * | ||||
|  * If "func" returns a nonzero value, terminate early and return the value. | ||||
|  */ | ||||
| int mzHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg) | ||||
| { | ||||
|     int i, val; | ||||
|  | ||||
|     for (i = 0; i < pHashTable->tableSize; i++) { | ||||
|         HashEntry* pEnt = &pHashTable->pEntries[i]; | ||||
|  | ||||
|         if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) { | ||||
|             val = (*func)(pEnt->data, arg); | ||||
|             if (val != 0) | ||||
|                 return val; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Look up an entry, counting the number of times we have to probe. | ||||
|  * | ||||
|  * Returns -1 if the entry wasn't found. | ||||
|  */ | ||||
| int countProbes(HashTable* pHashTable, unsigned int itemHash, const void* item, | ||||
|     HashCompareFunc cmpFunc) | ||||
| { | ||||
|     HashEntry* pEntry; | ||||
|     HashEntry* pEnd; | ||||
|     int count = 0; | ||||
|  | ||||
|     assert(pHashTable->tableSize > 0); | ||||
|     assert(item != HASH_TOMBSTONE); | ||||
|     assert(item != NULL); | ||||
|  | ||||
|     /* jump to the first entry and probe for a match */ | ||||
|     pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)]; | ||||
|     pEnd = &pHashTable->pEntries[pHashTable->tableSize]; | ||||
|     while (pEntry->data != NULL) { | ||||
|         if (pEntry->data != HASH_TOMBSTONE && | ||||
|             pEntry->hashValue == itemHash && | ||||
|             (*cmpFunc)(pEntry->data, item) == 0) | ||||
|         { | ||||
|             /* match */ | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         pEntry++; | ||||
|         if (pEntry == pEnd) {     /* wrap around to start */ | ||||
|             if (pHashTable->tableSize == 1) | ||||
|                 break;      /* edge case - single-entry table */ | ||||
|             pEntry = pHashTable->pEntries; | ||||
|         } | ||||
|  | ||||
|         count++; | ||||
|     } | ||||
|     if (pEntry->data == NULL) | ||||
|         return -1; | ||||
|  | ||||
|     return count; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Evaluate the amount of probing required for the specified hash table. | ||||
|  * | ||||
|  * We do this by running through all entries in the hash table, computing | ||||
|  * the hash value and then doing a lookup. | ||||
|  * | ||||
|  * The caller should lock the table before calling here. | ||||
|  */ | ||||
| void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc, | ||||
|     HashCompareFunc cmpFunc) | ||||
| { | ||||
|     int numEntries, minProbe, maxProbe, totalProbe; | ||||
|     HashIter iter; | ||||
|  | ||||
|     numEntries = maxProbe = totalProbe = 0; | ||||
|     minProbe = 65536*32767; | ||||
|  | ||||
|     for (mzHashIterBegin(pHashTable, &iter); !mzHashIterDone(&iter); | ||||
|         mzHashIterNext(&iter)) | ||||
|     { | ||||
|         const void* data = (const void*)mzHashIterData(&iter); | ||||
|         int count; | ||||
|              | ||||
|         count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc); | ||||
|  | ||||
|         numEntries++; | ||||
|  | ||||
|         if (count < minProbe) | ||||
|             minProbe = count; | ||||
|         if (count > maxProbe) | ||||
|             maxProbe = count; | ||||
|         totalProbe += count; | ||||
|     } | ||||
|  | ||||
|     LOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n", | ||||
|         minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize, | ||||
|         (float) totalProbe / (float) numEntries); | ||||
| } | ||||
							
								
								
									
										186
									
								
								minzip/Hash.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								minzip/Hash.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| /* | ||||
|  * Copyright 2007 The Android Open Source Project | ||||
|  * | ||||
|  * General purpose hash table, used for finding classes, methods, etc. | ||||
|  * | ||||
|  * When the number of elements reaches 3/4 of the table's capacity, the | ||||
|  * table will be resized. | ||||
|  */ | ||||
| #ifndef _MINZIP_HASH | ||||
| #define _MINZIP_HASH | ||||
|  | ||||
| #include "inline_magic.h" | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| /* compute the hash of an item with a specific type */ | ||||
| typedef unsigned int (*HashCompute)(const void* item); | ||||
|  | ||||
| /* | ||||
|  * Compare a hash entry with a "loose" item after their hash values match. | ||||
|  * Returns { <0, 0, >0 } depending on ordering of items (same semantics | ||||
|  * as strcmp()). | ||||
|  */ | ||||
| typedef int (*HashCompareFunc)(const void* tableItem, const void* looseItem); | ||||
|  | ||||
| /* | ||||
|  * This function will be used to free entries in the table.  This can be | ||||
|  * NULL if no free is required, free(), or a custom function. | ||||
|  */ | ||||
| typedef void (*HashFreeFunc)(void* ptr); | ||||
|  | ||||
| /* | ||||
|  * Used by mzHashForeach(). | ||||
|  */ | ||||
| typedef int (*HashForeachFunc)(void* data, void* arg); | ||||
|  | ||||
| /* | ||||
|  * One entry in the hash table.  "data" values are expected to be (or have | ||||
|  * the same characteristics as) valid pointers.  In particular, a NULL | ||||
|  * value for "data" indicates an empty slot, and HASH_TOMBSTONE indicates | ||||
|  * a no-longer-used slot that must be stepped over during probing. | ||||
|  * | ||||
|  * Attempting to add a NULL or tombstone value is an error. | ||||
|  * | ||||
|  * When an entry is released, we will call (HashFreeFunc)(entry->data). | ||||
|  */ | ||||
| typedef struct HashEntry { | ||||
|     unsigned int hashValue; | ||||
|     void* data; | ||||
| } HashEntry; | ||||
|  | ||||
| #define HASH_TOMBSTONE ((void*) 0xcbcacccd)     // invalid ptr value | ||||
|  | ||||
| /* | ||||
|  * Expandable hash table. | ||||
|  * | ||||
|  * This structure should be considered opaque. | ||||
|  */ | ||||
| typedef struct HashTable { | ||||
|     int         tableSize;          /* must be power of 2 */ | ||||
|     int         numEntries;         /* current #of "live" entries */ | ||||
|     int         numDeadEntries;     /* current #of tombstone entries */ | ||||
|     HashEntry*  pEntries;           /* array on heap */ | ||||
|     HashFreeFunc freeFunc; | ||||
| } HashTable; | ||||
|  | ||||
| /* | ||||
|  * Create and initialize a HashTable structure, using "initialSize" as | ||||
|  * a basis for the initial capacity of the table.  (The actual initial | ||||
|  * table size may be adjusted upward.)  If you know exactly how many | ||||
|  * elements the table will hold, pass the result from mzHashSize() in.) | ||||
|  * | ||||
|  * Returns "false" if unable to allocate the table. | ||||
|  */ | ||||
| HashTable* mzHashTableCreate(size_t initialSize, HashFreeFunc freeFunc); | ||||
|  | ||||
| /* | ||||
|  * Compute the capacity needed for a table to hold "size" elements.  Use | ||||
|  * this when you know ahead of time how many elements the table will hold. | ||||
|  * Pass this value into mzHashTableCreate() to ensure that you can add | ||||
|  * all elements without needing to reallocate the table. | ||||
|  */ | ||||
| size_t mzHashSize(size_t size); | ||||
|  | ||||
| /* | ||||
|  * Clear out a hash table, freeing the contents of any used entries. | ||||
|  */ | ||||
| void mzHashTableClear(HashTable* pHashTable); | ||||
|  | ||||
| /* | ||||
|  * Free a hash table. | ||||
|  */ | ||||
| void mzHashTableFree(HashTable* pHashTable); | ||||
|  | ||||
| /* | ||||
|  * Get #of entries in hash table. | ||||
|  */ | ||||
| INLINE int mzHashTableNumEntries(HashTable* pHashTable) { | ||||
|     return pHashTable->numEntries; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get total size of hash table (for memory usage calculations). | ||||
|  */ | ||||
| INLINE int mzHashTableMemUsage(HashTable* pHashTable) { | ||||
|     return sizeof(HashTable) + pHashTable->tableSize * sizeof(HashEntry); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Look up an entry in the table, possibly adding it if it's not there. | ||||
|  * | ||||
|  * If "item" is not found, and "doAdd" is false, NULL is returned. | ||||
|  * Otherwise, a pointer to the found or added item is returned.  (You can | ||||
|  * tell the difference by seeing if return value == item.) | ||||
|  * | ||||
|  * An "add" operation may cause the entire table to be reallocated. | ||||
|  */ | ||||
| void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item, | ||||
|     HashCompareFunc cmpFunc, bool doAdd); | ||||
|  | ||||
| /* | ||||
|  * Remove an item from the hash table, given its "data" pointer.  Does not | ||||
|  * invoke the "free" function; just detaches it from the table. | ||||
|  */ | ||||
| bool mzHashTableRemove(HashTable* pHashTable, unsigned int hash, void* item); | ||||
|  | ||||
| /* | ||||
|  * Execute "func" on every entry in the hash table. | ||||
|  * | ||||
|  * If "func" returns a nonzero value, terminate early and return the value. | ||||
|  */ | ||||
| int mzHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg); | ||||
|  | ||||
| /* | ||||
|  * An alternative to mzHashForeach(), using an iterator. | ||||
|  * | ||||
|  * Use like this: | ||||
|  *   HashIter iter; | ||||
|  *   for (mzHashIterBegin(hashTable, &iter); !mzHashIterDone(&iter); | ||||
|  *       mzHashIterNext(&iter)) | ||||
|  *   { | ||||
|  *       MyData* data = (MyData*)mzHashIterData(&iter); | ||||
|  *   } | ||||
|  */ | ||||
| typedef struct HashIter { | ||||
|     void*       data; | ||||
|     HashTable*  pHashTable; | ||||
|     int         idx; | ||||
| } HashIter; | ||||
| INLINE void mzHashIterNext(HashIter* pIter) { | ||||
|     int i = pIter->idx +1; | ||||
|     int lim = pIter->pHashTable->tableSize; | ||||
|     for ( ; i < lim; i++) { | ||||
|         void* data = pIter->pHashTable->pEntries[i].data; | ||||
|         if (data != NULL && data != HASH_TOMBSTONE) | ||||
|             break; | ||||
|     } | ||||
|     pIter->idx = i; | ||||
| } | ||||
| INLINE void mzHashIterBegin(HashTable* pHashTable, HashIter* pIter) { | ||||
|     pIter->pHashTable = pHashTable; | ||||
|     pIter->idx = -1; | ||||
|     mzHashIterNext(pIter); | ||||
| } | ||||
| INLINE bool mzHashIterDone(HashIter* pIter) { | ||||
|     return (pIter->idx >= pIter->pHashTable->tableSize); | ||||
| } | ||||
| INLINE void* mzHashIterData(HashIter* pIter) { | ||||
|     assert(pIter->idx >= 0 && pIter->idx < pIter->pHashTable->tableSize); | ||||
|     return pIter->pHashTable->pEntries[pIter->idx].data; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Evaluate hash table performance by examining the number of times we | ||||
|  * have to probe for an entry. | ||||
|  * | ||||
|  * The caller should lock the table beforehand. | ||||
|  */ | ||||
| typedef unsigned int (*HashCalcFunc)(const void* item); | ||||
| void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc, | ||||
|     HashCompareFunc cmpFunc); | ||||
|  | ||||
| #endif /*_MINZIP_HASH*/ | ||||
							
								
								
									
										25
									
								
								minzip/Inlines.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								minzip/Inlines.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
|  | ||||
| /* Make sure that non-inlined versions of INLINED-marked functions | ||||
|  * exist so that debug builds (which don't generally do inlining) | ||||
|  * don't break. | ||||
|  */ | ||||
| #define MINZIP_GENERATE_INLINES 1 | ||||
| #include "Bits.h" | ||||
| #include "Hash.h" | ||||
| #include "SysUtil.h" | ||||
| #include "Zip.h" | ||||
							
								
								
									
										207
									
								
								minzip/Log.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								minzip/Log.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | ||||
| // | ||||
| // Copyright 2005 The Android Open Source Project | ||||
| // | ||||
| // C/C++ logging functions.  See the logging documentation for API details. | ||||
| // | ||||
| // We'd like these to be available from C code (in case we import some from | ||||
| // somewhere), so this has a C interface. | ||||
| // | ||||
| // The output will be correct when the log file is shared between multiple | ||||
| // threads and/or multiple processes so long as the operating system | ||||
| // supports O_APPEND.  These calls have mutex-protected data structures | ||||
| // and so are NOT reentrant.  Do not use LOG in a signal handler. | ||||
| // | ||||
| #ifndef _MINZIP_LOG_H | ||||
| #define _MINZIP_LOG_H | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| // --------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|  * Normally we strip LOGV (VERBOSE messages) from release builds. | ||||
|  * You can modify this (for example with "#define LOG_NDEBUG 0" | ||||
|  * at the top of your source file) to change that behavior. | ||||
|  */ | ||||
| #ifndef LOG_NDEBUG | ||||
| #ifdef NDEBUG | ||||
| #define LOG_NDEBUG 1 | ||||
| #else | ||||
| #define LOG_NDEBUG 0 | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * This is the local tag used for the following simplified | ||||
|  * logging macros.  You can change this preprocessor definition | ||||
|  * before using the other macros to change the tag. | ||||
|  */ | ||||
| #ifndef LOG_TAG | ||||
| #define LOG_TAG NULL | ||||
| #endif | ||||
|  | ||||
| // --------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|  * Simplified macro to send a verbose log message using the current LOG_TAG. | ||||
|  */ | ||||
| #ifndef LOGV | ||||
| #if LOG_NDEBUG | ||||
| #define LOGV(...)   ((void)0) | ||||
| #else | ||||
| #define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #define CONDITION(cond)     (__builtin_expect((cond)!=0, 0)) | ||||
|  | ||||
| #ifndef LOGV_IF | ||||
| #if LOG_NDEBUG | ||||
| #define LOGV_IF(cond, ...)   ((void)0) | ||||
| #else | ||||
| #define LOGV_IF(cond, ...) \ | ||||
|     ( (CONDITION(cond)) \ | ||||
|     ? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ | ||||
|     : (void)0 ) | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #define LOGVV LOGV | ||||
| #define LOGVV_IF LOGV_IF | ||||
|  | ||||
| /* | ||||
|  * Simplified macro to send a debug log message using the current LOG_TAG. | ||||
|  */ | ||||
| #ifndef LOGD | ||||
| #define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) | ||||
| #endif | ||||
|  | ||||
| #ifndef LOGD_IF | ||||
| #define LOGD_IF(cond, ...) \ | ||||
|     ( (CONDITION(cond)) \ | ||||
|     ? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ | ||||
|     : (void)0 ) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Simplified macro to send an info log message using the current LOG_TAG. | ||||
|  */ | ||||
| #ifndef LOGI | ||||
| #define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) | ||||
| #endif | ||||
|  | ||||
| #ifndef LOGI_IF | ||||
| #define LOGI_IF(cond, ...) \ | ||||
|     ( (CONDITION(cond)) \ | ||||
|     ? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ | ||||
|     : (void)0 ) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Simplified macro to send a warning log message using the current LOG_TAG. | ||||
|  */ | ||||
| #ifndef LOGW | ||||
| #define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) | ||||
| #endif | ||||
|  | ||||
| #ifndef LOGW_IF | ||||
| #define LOGW_IF(cond, ...) \ | ||||
|     ( (CONDITION(cond)) \ | ||||
|     ? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ | ||||
|     : (void)0 ) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Simplified macro to send an error log message using the current LOG_TAG. | ||||
|  */ | ||||
| #ifndef LOGE | ||||
| #define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) | ||||
| #endif | ||||
|  | ||||
| #ifndef LOGE_IF | ||||
| #define LOGE_IF(cond, ...) \ | ||||
|     ( (CONDITION(cond)) \ | ||||
|     ? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ | ||||
|     : (void)0 ) | ||||
| #endif | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Conditional based on whether the current LOG_TAG is enabled at | ||||
|  * verbose priority. | ||||
|  */ | ||||
| #ifndef IF_LOGV | ||||
| #if LOG_NDEBUG | ||||
| #define IF_LOGV() if (false) | ||||
| #else | ||||
| #define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG) | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Conditional based on whether the current LOG_TAG is enabled at | ||||
|  * debug priority. | ||||
|  */ | ||||
| #ifndef IF_LOGD | ||||
| #define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Conditional based on whether the current LOG_TAG is enabled at | ||||
|  * info priority. | ||||
|  */ | ||||
| #ifndef IF_LOGI | ||||
| #define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Conditional based on whether the current LOG_TAG is enabled at | ||||
|  * warn priority. | ||||
|  */ | ||||
| #ifndef IF_LOGW | ||||
| #define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Conditional based on whether the current LOG_TAG is enabled at | ||||
|  * error priority. | ||||
|  */ | ||||
| #ifndef IF_LOGE | ||||
| #define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG) | ||||
| #endif | ||||
|  | ||||
| // --------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|  * Basic log message macro. | ||||
|  * | ||||
|  * Example: | ||||
|  *  LOG(LOG_WARN, NULL, "Failed with error %d", errno); | ||||
|  * | ||||
|  * The second argument may be NULL or "" to indicate the "global" tag. | ||||
|  * | ||||
|  * Non-gcc probably won't have __FUNCTION__.  It's not vital.  gcc also | ||||
|  * offers __PRETTY_FUNCTION__, which is rather more than we need. | ||||
|  */ | ||||
| #ifndef LOG | ||||
| #define LOG(priority, tag, ...) \ | ||||
|     LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Log macro that allows you to specify a number for the priority. | ||||
|  */ | ||||
| #ifndef LOG_PRI | ||||
| #define LOG_PRI(priority, tag, ...) \ | ||||
|     printf(tag ": " __VA_ARGS__) | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * Conditional given a desired logging priority and tag. | ||||
|  */ | ||||
| #ifndef IF_LOG | ||||
| #define IF_LOG(priority, tag) \ | ||||
|     if (1) | ||||
| #endif | ||||
|  | ||||
| #endif // _MINZIP_LOG_H | ||||
							
								
								
									
										212
									
								
								minzip/SysUtil.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								minzip/SysUtil.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | ||||
| /* | ||||
|  * Copyright 2006 The Android Open Source Project | ||||
|  * | ||||
|  * System utilities. | ||||
|  */ | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <unistd.h> | ||||
| #include <string.h> | ||||
| #include <sys/mman.h> | ||||
| #include <limits.h> | ||||
| #include <errno.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| #define LOG_TAG "minzip" | ||||
| #include "Log.h" | ||||
| #include "SysUtil.h" | ||||
|  | ||||
| /* | ||||
|  * Having trouble finding a portable way to get this.  sysconf(_SC_PAGE_SIZE) | ||||
|  * seems appropriate, but we don't have that on the device.  Some systems | ||||
|  * have getpagesize(2), though the linux man page has some odd cautions. | ||||
|  */ | ||||
| #define DEFAULT_PAGE_SIZE   4096 | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Create an anonymous shared memory segment large enough to hold "length" | ||||
|  * bytes.  The actual segment may be larger because mmap() operates on | ||||
|  * page boundaries (usually 4K). | ||||
|  */ | ||||
| static void* sysCreateAnonShmem(size_t length) | ||||
| { | ||||
|     void* ptr; | ||||
|  | ||||
|     ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, | ||||
|             MAP_SHARED | MAP_ANON, -1, 0); | ||||
|     if (ptr == MAP_FAILED) { | ||||
|         LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length, | ||||
|             strerror(errno)); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     return ptr; | ||||
| } | ||||
|  | ||||
| static int getFileStartAndLength(int fd, off_t *start_, size_t *length_) | ||||
| { | ||||
|     off_t start, end; | ||||
|     size_t length; | ||||
|  | ||||
|     assert(start_ != NULL); | ||||
|     assert(length_ != NULL); | ||||
|  | ||||
|     start = lseek(fd, 0L, SEEK_CUR); | ||||
|     end = lseek(fd, 0L, SEEK_END); | ||||
|     (void) lseek(fd, start, SEEK_SET); | ||||
|  | ||||
|     if (start == (off_t) -1 || end == (off_t) -1) { | ||||
|         LOGE("could not determine length of file\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     length = end - start; | ||||
|     if (length == 0) { | ||||
|         LOGE("file is empty\n"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     *start_ = start; | ||||
|     *length_ = length; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Pull the contents of a file into an new shared memory segment.  We grab | ||||
|  * everything from fd's current offset on. | ||||
|  * | ||||
|  * We need to know the length ahead of time so we can allocate a segment | ||||
|  * of sufficient size. | ||||
|  */ | ||||
| int sysLoadFileInShmem(int fd, MemMapping* pMap) | ||||
| { | ||||
|     off_t start; | ||||
|     size_t length, actual; | ||||
|     void* memPtr; | ||||
|  | ||||
|     assert(pMap != NULL); | ||||
|  | ||||
|     if (getFileStartAndLength(fd, &start, &length) < 0) | ||||
|         return -1; | ||||
|  | ||||
|     memPtr = sysCreateAnonShmem(length); | ||||
|     if (memPtr == NULL) | ||||
|         return -1; | ||||
|  | ||||
|     actual = read(fd, memPtr, length); | ||||
|     if (actual != length) { | ||||
|         LOGE("only read %d of %d bytes\n", (int) actual, (int) length); | ||||
|         sysReleaseShmem(pMap); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     pMap->baseAddr = pMap->addr = memPtr; | ||||
|     pMap->baseLength = pMap->length = length; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Map a file (from fd's current offset) into a shared, read-only memory | ||||
|  * segment.  The file offset must be a multiple of the page size. | ||||
|  * | ||||
|  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero | ||||
|  * value and does not disturb "pMap". | ||||
|  */ | ||||
| int sysMapFileInShmem(int fd, MemMapping* pMap) | ||||
| { | ||||
|     off_t start; | ||||
|     size_t length; | ||||
|     void* memPtr; | ||||
|  | ||||
|     assert(pMap != NULL); | ||||
|  | ||||
|     if (getFileStartAndLength(fd, &start, &length) < 0) | ||||
|         return -1; | ||||
|  | ||||
|     memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start); | ||||
|     if (memPtr == MAP_FAILED) { | ||||
|         LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length, | ||||
|             fd, (int) start, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     pMap->baseAddr = pMap->addr = memPtr; | ||||
|     pMap->baseLength = pMap->length = length; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Map part of a file (from fd's current offset) into a shared, read-only | ||||
|  * memory segment. | ||||
|  * | ||||
|  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero | ||||
|  * value and does not disturb "pMap". | ||||
|  */ | ||||
| int sysMapFileSegmentInShmem(int fd, off_t start, long length, | ||||
|     MemMapping* pMap) | ||||
| { | ||||
|     off_t dummy; | ||||
|     size_t fileLength, actualLength; | ||||
|     off_t actualStart; | ||||
|     int adjust; | ||||
|     void* memPtr; | ||||
|  | ||||
|     assert(pMap != NULL); | ||||
|  | ||||
|     if (getFileStartAndLength(fd, &dummy, &fileLength) < 0) | ||||
|         return -1; | ||||
|  | ||||
|     if (start + length > (long)fileLength) { | ||||
|         LOGW("bad segment: st=%d len=%ld flen=%d\n", | ||||
|             (int) start, length, (int) fileLength); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* adjust to be page-aligned */ | ||||
|     adjust = start % DEFAULT_PAGE_SIZE; | ||||
|     actualStart = start - adjust; | ||||
|     actualLength = length + adjust; | ||||
|  | ||||
|     memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED, | ||||
|                 fd, actualStart); | ||||
|     if (memPtr == MAP_FAILED) { | ||||
|         LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", | ||||
|             (int) actualLength, fd, (int) actualStart, strerror(errno)); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     pMap->baseAddr = memPtr; | ||||
|     pMap->baseLength = actualLength; | ||||
|     pMap->addr = (char*)memPtr + adjust; | ||||
|     pMap->length = length; | ||||
|  | ||||
|     LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n", | ||||
|         (int) start, (int) length, | ||||
|         pMap->baseAddr, (int) pMap->baseLength, | ||||
|         pMap->addr, (int) pMap->length); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Release a memory mapping. | ||||
|  */ | ||||
| void sysReleaseShmem(MemMapping* pMap) | ||||
| { | ||||
|     if (pMap->baseAddr == NULL && pMap->baseLength == 0) | ||||
|         return; | ||||
|  | ||||
|     if (munmap(pMap->baseAddr, pMap->baseLength) < 0) { | ||||
|         LOGW("munmap(%p, %d) failed: %s\n", | ||||
|             pMap->baseAddr, (int)pMap->baseLength, strerror(errno)); | ||||
|     } else { | ||||
|         LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength); | ||||
|         pMap->baseAddr = NULL; | ||||
|         pMap->baseLength = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										61
									
								
								minzip/SysUtil.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								minzip/SysUtil.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| /* | ||||
|  * Copyright 2006 The Android Open Source Project | ||||
|  * | ||||
|  * System utilities. | ||||
|  */ | ||||
| #ifndef _MINZIP_SYSUTIL | ||||
| #define _MINZIP_SYSUTIL | ||||
|  | ||||
| #include "inline_magic.h" | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| /* | ||||
|  * Use this to keep track of mapped segments. | ||||
|  */ | ||||
| typedef struct MemMapping { | ||||
|     void*   addr;           /* start of data */ | ||||
|     size_t  length;         /* length of data */ | ||||
|  | ||||
|     void*   baseAddr;       /* page-aligned base address */ | ||||
|     size_t  baseLength;     /* length of mapping */ | ||||
| } MemMapping; | ||||
|  | ||||
| /* copy a map */ | ||||
| INLINE void sysCopyMap(MemMapping* dst, const MemMapping* src) { | ||||
|     *dst = *src; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Load a file into a new shared memory segment.  All data from the current | ||||
|  * offset to the end of the file is pulled in. | ||||
|  * | ||||
|  * The segment is read-write, allowing VM fixups.  (It should be modified | ||||
|  * to support .gz/.zip compressed data.) | ||||
|  * | ||||
|  * On success, "pMap" is filled in, and zero is returned. | ||||
|  */ | ||||
| int sysLoadFileInShmem(int fd, MemMapping* pMap); | ||||
|  | ||||
| /* | ||||
|  * Map a file (from fd's current offset) into a shared, | ||||
|  * read-only memory segment. | ||||
|  * | ||||
|  * On success, "pMap" is filled in, and zero is returned. | ||||
|  */ | ||||
| int sysMapFileInShmem(int fd, MemMapping* pMap); | ||||
|  | ||||
| /* | ||||
|  * Like sysMapFileInShmem, but on only part of a file. | ||||
|  */ | ||||
| int sysMapFileSegmentInShmem(int fd, off_t start, long length, | ||||
|     MemMapping* pMap); | ||||
|  | ||||
| /* | ||||
|  * Release the pages associated with a shared memory segment. | ||||
|  * | ||||
|  * This does not free "pMap"; it just releases the memory. | ||||
|  */ | ||||
| void sysReleaseShmem(MemMapping* pMap); | ||||
|  | ||||
| #endif /*_MINZIP_SYSUTIL*/ | ||||
							
								
								
									
										1098
									
								
								minzip/Zip.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1098
									
								
								minzip/Zip.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										206
									
								
								minzip/Zip.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								minzip/Zip.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | ||||
| /* | ||||
|  * Copyright 2006 The Android Open Source Project | ||||
|  * | ||||
|  * Simple Zip archive support. | ||||
|  */ | ||||
| #ifndef _MINZIP_ZIP | ||||
| #define _MINZIP_ZIP | ||||
|  | ||||
| #include "inline_magic.h" | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <utime.h> | ||||
|  | ||||
| #include "Hash.h" | ||||
| #include "SysUtil.h" | ||||
|  | ||||
| /* | ||||
|  * One entry in the Zip archive.  Treat this as opaque -- use accessors below. | ||||
|  * | ||||
|  * TODO: we're now keeping the pages mapped so we don't have to copy the | ||||
|  * filename.  We can change the accessors to retrieve the various pieces | ||||
|  * directly from the source file instead of copying them out, for a very | ||||
|  * slight speed hit and a modest reduction in memory usage. | ||||
|  */ | ||||
| typedef struct ZipEntry { | ||||
|     unsigned int fileNameLen; | ||||
|     const char*  fileName;       // not null-terminated | ||||
|     long         offset; | ||||
|     long         compLen; | ||||
|     long         uncompLen; | ||||
|     int          compression; | ||||
|     long         modTime; | ||||
|     long         crc32; | ||||
|     int          versionMadeBy; | ||||
|     long         externalFileAttributes; | ||||
| } ZipEntry; | ||||
|  | ||||
| /* | ||||
|  * One Zip archive.  Treat as opaque. | ||||
|  */ | ||||
| typedef struct ZipArchive { | ||||
|     int         fd; | ||||
|     unsigned int numEntries; | ||||
|     ZipEntry*   pEntries; | ||||
|     HashTable*  pHash;          // maps file name to ZipEntry | ||||
|     MemMapping  map; | ||||
| } ZipArchive; | ||||
|  | ||||
| /* | ||||
|  * Represents a non-NUL-terminated string, | ||||
|  * which is how entry names are stored. | ||||
|  */ | ||||
| typedef struct { | ||||
|     const char *str; | ||||
|     size_t len; | ||||
| } UnterminatedString; | ||||
|  | ||||
| /* | ||||
|  * Open a Zip archive. | ||||
|  * | ||||
|  * On success, returns 0 and populates "pArchive".  Returns nonzero errno | ||||
|  * value on failure. | ||||
|  */ | ||||
| int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive); | ||||
|  | ||||
| /* | ||||
|  * Close archive, releasing resources associated with it. | ||||
|  * | ||||
|  * Depending on the implementation this could unmap pages used by classes | ||||
|  * stored in a Jar.  This should only be done after unloading classes. | ||||
|  */ | ||||
| void mzCloseZipArchive(ZipArchive* pArchive); | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Find an entry in the Zip archive, by name. | ||||
|  */ | ||||
| const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive, | ||||
|         const char* entryName); | ||||
|  | ||||
| /* | ||||
|  * Get the number of entries in the Zip archive. | ||||
|  */ | ||||
| INLINE unsigned int mzZipEntryCount(const ZipArchive* pArchive) { | ||||
|     return pArchive->numEntries; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get an entry by index.  Returns NULL if the index is out-of-bounds. | ||||
|  */ | ||||
| INLINE const ZipEntry* | ||||
| mzGetZipEntryAt(const ZipArchive* pArchive, unsigned int index) | ||||
| { | ||||
|     if (index < pArchive->numEntries) { | ||||
|         return pArchive->pEntries + index; | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Get the index number of an entry in the archive. | ||||
|  */ | ||||
| INLINE unsigned int | ||||
| mzGetZipEntryIndex(const ZipArchive *pArchive, const ZipEntry *pEntry) { | ||||
|     return pEntry - pArchive->pEntries; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Simple accessors. | ||||
|  */ | ||||
| INLINE UnterminatedString mzGetZipEntryFileName(const ZipEntry* pEntry) { | ||||
|     UnterminatedString ret; | ||||
|     ret.str = pEntry->fileName; | ||||
|     ret.len = pEntry->fileNameLen; | ||||
|     return ret; | ||||
| } | ||||
| INLINE long mzGetZipEntryOffset(const ZipEntry* pEntry) { | ||||
|     return pEntry->offset; | ||||
| } | ||||
| INLINE long mzGetZipEntryUncompLen(const ZipEntry* pEntry) { | ||||
|     return pEntry->uncompLen; | ||||
| } | ||||
| INLINE long mzGetZipEntryModTime(const ZipEntry* pEntry) { | ||||
|     return pEntry->modTime; | ||||
| } | ||||
| INLINE long mzGetZipEntryCrc32(const ZipEntry* pEntry) { | ||||
|     return pEntry->crc32; | ||||
| } | ||||
| bool mzIsZipEntrySymlink(const ZipEntry* pEntry); | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Type definition for the callback function used by | ||||
|  * mzProcessZipEntryContents(). | ||||
|  */ | ||||
| typedef bool (*ProcessZipEntryContentsFunction)(const unsigned char *data, | ||||
|     int dataLen, void *cookie); | ||||
|  | ||||
| /* | ||||
|  * Stream the uncompressed data through the supplied function, | ||||
|  * passing cookie to it each time it gets called.  processFunction | ||||
|  * may be called more than once. | ||||
|  * | ||||
|  * If processFunction returns false, the operation is abandoned and | ||||
|  * mzProcessZipEntryContents() immediately returns false. | ||||
|  * | ||||
|  * This is useful for calculating the hash of an entry's uncompressed contents. | ||||
|  */ | ||||
| bool mzProcessZipEntryContents(const ZipArchive *pArchive, | ||||
|     const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction, | ||||
|     void *cookie); | ||||
|  | ||||
| /* | ||||
|  * Read an entry into a buffer allocated by the caller. | ||||
|  */ | ||||
| bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, | ||||
|         char* buf, int bufLen); | ||||
|  | ||||
| /* | ||||
|  * Check the CRC on this entry; return true if it is correct. | ||||
|  * May do other internal checks as well. | ||||
|  */ | ||||
| bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry); | ||||
|  | ||||
| /* | ||||
|  * Inflate and write an entry to a file. | ||||
|  */ | ||||
| bool mzExtractZipEntryToFile(const ZipArchive *pArchive, | ||||
|     const ZipEntry *pEntry, int fd); | ||||
|  | ||||
| /* | ||||
|  * Inflate all entries under zipDir to the directory specified by | ||||
|  * targetDir, which must exist and be a writable directory. | ||||
|  * | ||||
|  * The immediate children of zipDir will become the immediate | ||||
|  * children of targetDir; e.g., if the archive contains the entries | ||||
|  * | ||||
|  *     a/b/c/one | ||||
|  *     a/b/c/two | ||||
|  *     a/b/c/d/three | ||||
|  * | ||||
|  * and mzExtractRecursive(a, "a/b/c", "/tmp", ...) is called, the resulting | ||||
|  * files will be | ||||
|  * | ||||
|  *     /tmp/one | ||||
|  *     /tmp/two | ||||
|  *     /tmp/d/three | ||||
|  * | ||||
|  * flags is zero or more of the following: | ||||
|  * | ||||
|  *     MZ_EXTRACT_FILES_ONLY - only unpack files, not directories or symlinks | ||||
|  *     MZ_EXTRACT_DRY_RUN - don't do anything, but do invoke the callback | ||||
|  * | ||||
|  * If timestamp is non-NULL, file timestamps will be set accordingly. | ||||
|  * | ||||
|  * If callback is non-NULL, it will be invoked with each unpacked file. | ||||
|  * | ||||
|  * Returns true on success, false on failure. | ||||
|  */ | ||||
| enum { MZ_EXTRACT_FILES_ONLY = 1, MZ_EXTRACT_DRY_RUN = 2 }; | ||||
| bool mzExtractRecursive(const ZipArchive *pArchive, | ||||
|         const char *zipDir, const char *targetDir, | ||||
|         int flags, const struct utimbuf *timestamp, | ||||
|         void (*callback)(const char *fn, void*), void *cookie); | ||||
|  | ||||
| #endif /*_MINZIP_ZIP*/ | ||||
							
								
								
									
										26
									
								
								minzip/inline_magic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								minzip/inline_magic.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| /* | ||||
|  * 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 MINZIP_INLINE_MAGIC_H_ | ||||
| #define MINZIP_INLINE_MAGIC_H_ | ||||
|  | ||||
| #ifndef MINZIP_GENERATE_INLINES | ||||
| #define INLINE extern __inline__ | ||||
| #else | ||||
| #define INLINE | ||||
| #endif | ||||
|  | ||||
| #endif  // MINZIP_INLINE_MAGIC_H_ | ||||
							
								
								
									
										23
									
								
								mtdutils/Android.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								mtdutils/Android.mk
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| ifneq ($(TARGET_SIMULATOR),true) | ||||
| ifeq ($(TARGET_ARCH),arm) | ||||
|  | ||||
| LOCAL_PATH := $(call my-dir) | ||||
| include $(CLEAR_VARS) | ||||
|  | ||||
| LOCAL_SRC_FILES := \ | ||||
| 	mtdutils.c \ | ||||
| 	mounts.c | ||||
|  | ||||
| LOCAL_MODULE := libmtdutils | ||||
|  | ||||
| include $(BUILD_STATIC_LIBRARY) | ||||
|  | ||||
| include $(CLEAR_VARS) | ||||
| LOCAL_SRC_FILES := flash_image.c | ||||
| LOCAL_MODULE := flash_image | ||||
| LOCAL_STATIC_LIBRARIES := libmtdutils | ||||
| LOCAL_SHARED_LIBRARIES := libcutils libc | ||||
| include $(BUILD_EXECUTABLE) | ||||
|  | ||||
| endif	# TARGET_ARCH == arm | ||||
| endif	# !TARGET_SIMULATOR | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user