OSDN Git Service

Added XML output mode for dexdump.
authorAndy McFadden <fadden@android.com>
Tue, 5 May 2009 23:52:10 +0000 (16:52 -0700)
committerAndy McFadden <fadden@android.com>
Wed, 6 May 2009 00:05:59 +0000 (17:05 -0700)
This adds an output mode that looks similar to the "current.xml" we
generate for our public APIs.  There are a number of differences in
content.  The original ("plain") output has not been altered.

I pulled in the bad checksum handling change (internal 142686) since
it's small, has turned out to be useful, and might make the merge of
this to master slightly easier.

This also renames a buffer in the ongoing temp file variable saga.

dexdump/DexDump.c
libdex/CmdUtils.c

index a4d97eb..c5714ea 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 /*
  * The "dexdump" tool is intended to mimic "objdump".  When possible, use
  * similar command-line arguments.
  *
- * TODO: rework the output format to be more regexp-friendly
+ * TODO: rework the "plain" output format to be more regexp-friendly
+ *
+ * Differences between XML output and the "current.xml" file:
+ * - classes in same package are not all grouped together; generally speaking
+ *   nothing is sorted
+ * - no "deprecated" on fields and methods
+ * - no "value" on fields
+ * - no parameter names
+ * - no generic signatures on parameters, e.g. type="java.lang.Class&lt;?&gt;"
+ * - class shows declared fields and methods; does not show inherited fields
  */
 #include "libdex/DexFile.h"
 #include "libdex/DexCatch.h"
@@ -43,12 +53,21 @@ static const char* gProgName = "dexdump";
 static InstructionWidth* gInstrWidth;
 static InstructionFormat* gInstrFormat;
 
+typedef enum OutputFormat {
+    OUTPUT_PLAIN = 0,               /* default */
+    OUTPUT_XML,                     /* fancy */
+} OutputFormat;
+
 /* command-line options */
 struct {
     bool disassemble;
     bool showFileHeaders;
     bool showSectionHeaders;
+    bool ignoreBadChecksum;
+    OutputFormat outputFormat;
     const char* tempFileName;
+    bool exportsOnly;
+    bool verbose;
 } gOptions;
 
 /* basic info about a field or method */
@@ -67,34 +86,134 @@ static inline u2 get2LE(unsigned char const* pSrc)
 }   
 
 /*
- * Return a newly-allocated string for the "dot version" of the class
- * name for the given type descriptor. That is, The initial "L" and
- * final ";" (if any) have been removed and all occurrences of '/'
- * have been changed to '.'.
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+static const char* primitiveTypeLabel(char typeChar)
+{
+    switch (typeChar) {
+    case 'B':   return "byte";
+    case 'C':   return "char";
+    case 'D':   return "double";
+    case 'F':   return "float";
+    case 'I':   return "int";
+    case 'J':   return "long";
+    case 'S':   return "short";
+    case 'V':   return "void";
+    case 'Z':   return "boolean";
+    default:
+                return "UNKNOWN";
+    }
+}
+
+/*
+ * Converts a type descriptor to human-readable "dotted" form.  For
+ * example, "Ljava/lang/String;" becomes "java.lang.String", and
+ * "[I" becomes "int[]".  Also converts '$' to '.', which means this
+ * form can't be converted back to a descriptor.
  */
 static char* descriptorToDot(const char* str)
 {
-    size_t at = strlen(str);
+    int targetLen = strlen(str);
+    int offset = 0;
+    int arrayDepth = 0;
     char* newStr;
 
-    if (str[0] == 'L') {
-        assert(str[at - 1] == ';');
-        at -= 2; /* Two fewer chars to copy. */
-        str++; /* Skip the 'L'. */
+    /* strip leading [s; will be added to end */
+    while (targetLen > 1 && str[offset] == '[') {
+        offset++;
+        targetLen--;
     }
+    arrayDepth = offset;
 
-    newStr = malloc(at + 1); /* Add one for the '\0'. */
-    newStr[at] = '\0';
+    if (targetLen == 1) {
+        /* primitive type */
+        str = primitiveTypeLabel(str[offset]);
+        offset = 0;
+        targetLen = strlen(str);
+    } else {
+        /* account for leading 'L' and trailing ';' */
+        if (targetLen >= 2 && str[offset] == 'L' &&
+            str[offset+targetLen-1] == ';')
+        {
+            targetLen -= 2;
+            offset++;
+        }
+    }
+
+    newStr = malloc(targetLen + arrayDepth * 2 +1);
+
+    /* copy class name over */
+    int i;
+    for (i = 0; i < targetLen; i++) {
+        char ch = str[offset + i];
+        newStr[i] = (ch == '/' || ch == '$') ? '.' : ch;
+    }
 
-    while (at > 0) {
-        at--;
-        newStr[at] = (str[at] == '/') ? '.' : str[at];
+    /* add the appropriate number of brackets for arrays */
+    while (arrayDepth-- > 0) {
+        newStr[i++] = '[';
+        newStr[i++] = ']';
     }
+    newStr[i] = '\0';
+    assert(i == targetLen + arrayDepth * 2);
 
     return newStr;
 }
 
 /*
+ * Converts the class name portion of a type descriptor to human-readable
+ * "dotted" form.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* descriptorClassToDot(const char* str)
+{
+    const char* lastSlash;
+    char* newStr;
+    char* cp;
+
+    /* reduce to just the class name, trimming trailing ';' */
+    lastSlash = strrchr(str, '/');
+    if (lastSlash == NULL)
+        lastSlash = str + 1;        /* start past 'L' */
+    else
+        lastSlash++;                /* start past '/' */
+
+    newStr = strdup(lastSlash);
+    newStr[strlen(lastSlash)-1] = '\0';
+    for (cp = newStr; *cp != '\0'; cp++) {
+        if (*cp == '$')
+            *cp = '.';
+    }
+
+    return newStr;
+}
+
+/*
+ * Returns a quoted string representing the boolean value.
+ */
+static const char* quotedBool(bool val)
+{
+    if (val)
+        return "\"true\"";
+    else
+        return "\"false\"";
+}
+
+static const char* quotedVisibility(u4 accessFlags)
+{
+    if ((accessFlags & ACC_PUBLIC) != 0)
+        return "\"public\"";
+    else if ((accessFlags & ACC_PROTECTED) != 0)
+        return "\"protected\"";
+    else if ((accessFlags & ACC_PRIVATE) != 0)
+        return "\"private\"";
+    else
+        return "\"package\"";
+}
+
+/*
  * Count the number of '1' bits in a word.
  *
  * Having completed this, I'm ready for an interview at Google.
@@ -114,7 +233,6 @@ static int countOnes(u4 val)
     return count;
 }
 
-
 /*
  * Flag for use with createAccessFlagStr().
  */
@@ -310,7 +428,7 @@ void dumpClassDef(DexFile* pDexFile, int idx)
 }
 
 /*
- * Dump an interface.
+ * Dump an interface that a class declares to implement.
  */
 void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
     int i)
@@ -318,7 +436,13 @@ void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
     const char* interfaceName =
         dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
 
-    printf("    #%d              : '%s'\n", i, interfaceName);
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("    #%d              : '%s'\n", i, interfaceName);
+    } else {
+        char* dotted = descriptorToDot(interfaceName);
+        printf("<implements name=\"%s\">\n</implements>\n", dotted);
+        free(dotted);
+    }
 }
 
 /*
@@ -870,8 +994,14 @@ void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
     const DexMethodId* pMethodId;
     const char* backDescriptor;
     const char* name;
-    char* typeDescriptor;
-    char* accessStr;
+    char* typeDescriptor = NULL;
+    char* accessStr = NULL;
+
+    if (gOptions.exportsOnly &&
+        (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+    {
+        return;
+    }
 
     pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
     name = dexStringById(pDexFile, pMethodId->nameIdx);
@@ -882,22 +1012,119 @@ void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
     accessStr = createAccessFlagStr(pDexMethod->accessFlags,
                     kAccessForMethod);
 
-    printf("    #%d              : (in %s)\n", i, backDescriptor);
-    printf("      name          : '%s'\n", name);
-    printf("      type          : '%s'\n", typeDescriptor);
-    printf("      access        : 0x%04x (%s)\n",
-        pDexMethod->accessFlags, accessStr);
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("    #%d              : (in %s)\n", i, backDescriptor);
+        printf("      name          : '%s'\n", name);
+        printf("      type          : '%s'\n", typeDescriptor);
+        printf("      access        : 0x%04x (%s)\n",
+            pDexMethod->accessFlags, accessStr);
 
-    if (pDexMethod->codeOff == 0) {
-        printf("      code          : (none)\n");
-    } else {
-        printf("      code          -\n");
-        dumpCode(pDexFile, pDexMethod);
-    }
+        if (pDexMethod->codeOff == 0) {
+            printf("      code          : (none)\n");
+        } else {
+            printf("      code          -\n");
+            dumpCode(pDexFile, pDexMethod);
+        }
 
-    if (gOptions.disassemble)
-        putchar('\n');
+        if (gOptions.disassemble)
+            putchar('\n');
+    } else if (gOptions.outputFormat == OUTPUT_XML) {
+        bool constructor = (name[0] == '<');
+
+        if (constructor) {
+            char* tmp;
+
+            tmp = descriptorClassToDot(backDescriptor);
+            printf("<constructor name=\"%s\"\n", tmp);
+            free(tmp);
+
+            tmp = descriptorToDot(backDescriptor);
+            printf(" type=\"%s\"\n", tmp);
+            free(tmp);
+        } else {
+            printf("<method name=\"%s\"\n", name);
+
+            const char* returnType = strrchr(typeDescriptor, ')');
+            if (returnType == NULL) {
+                fprintf(stderr, "bad method type descriptor '%s'\n",
+                    typeDescriptor);
+                goto bail;
+            }
+
+            char* tmp = descriptorToDot(returnType+1);
+            printf(" return=\"%s\"\n", tmp);
+            free(tmp);
+
+            printf(" abstract=%s\n",
+                quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
+            printf(" native=%s\n",
+                quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
+
+            bool isSync =
+                (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
+                (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+            printf(" synchronized=%s\n", quotedBool(isSync));
+        }
+
+        printf(" static=%s\n",
+            quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
+        printf(" final=%s\n",
+            quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
+        // "deprecated=" not knowable w/o parsing annotations
+        printf(" visibility=%s\n",
+            quotedVisibility(pDexMethod->accessFlags));
+
+        printf(">\n");
+
+        /*
+         * Parameters.
+         */
+        if (typeDescriptor[0] != '(') {
+            fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
+            goto bail;
+        }
+
+        char tmpBuf[strlen(typeDescriptor)+1];      /* more than big enough */
+        int argNum = 0;
+
+        const char* base = typeDescriptor+1;
+
+        while (*base != ')') {
+            char* cp = tmpBuf;
+
+            while (*base == '[')
+                *cp++ = *base++;
 
+            if (*base == 'L') {
+                /* copy through ';' */
+                do {
+                    *cp = *base++;
+                } while (*cp++ != ';');
+            } else {
+                /* primitive char, copy it */
+                if (strchr("ZBCSIFJD", *base) == NULL) {
+                    fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+                    goto bail;
+                }
+                *cp++ = *base++;
+            }
+
+            /* null terminate and display */
+            *cp++ = '\0';
+
+            char* tmp = descriptorToDot(tmpBuf);
+            printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
+                argNum++, tmp);
+            free(tmp);
+        }
+
+        if (constructor)
+            printf("</constructor>\n");
+        else
+            printf("</method>\n");
+    }
+
+bail:
     free(typeDescriptor);
     free(accessStr);
 }
@@ -913,6 +1140,12 @@ void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
     const char* typeDescriptor;
     char* accessStr;
 
+    if (gOptions.exportsOnly &&
+        (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+    {
+        return;
+    }
+
     pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
     name = dexStringById(pDexFile, pFieldId->nameIdx);
     typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
@@ -920,11 +1153,35 @@ void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
 
     accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
 
-    printf("    #%d              : (in %s)\n", i, backDescriptor);
-    printf("      name          : '%s'\n", name);
-    printf("      type          : '%s'\n", typeDescriptor);
-    printf("      access        : 0x%04x (%s)\n",
-        pSField->accessFlags, accessStr);
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("    #%d              : (in %s)\n", i, backDescriptor);
+        printf("      name          : '%s'\n", name);
+        printf("      type          : '%s'\n", typeDescriptor);
+        printf("      access        : 0x%04x (%s)\n",
+            pSField->accessFlags, accessStr);
+    } else if (gOptions.outputFormat == OUTPUT_XML) {
+        char* tmp;
+
+        printf("<field name=\"%s\"\n", name);
+
+        tmp = descriptorToDot(typeDescriptor);
+        printf(" type=\"%s\"\n", tmp);
+        free(tmp);
+
+        printf(" transient=%s\n",
+            quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
+        printf(" volatile=%s\n",
+            quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
+        // "value=" not knowable w/o parsing annotations
+        printf(" static=%s\n",
+            quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
+        printf(" final=%s\n",
+            quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
+        // "deprecated=" not knowable w/o parsing annotations
+        printf(" visibility=%s\n",
+            quotedVisibility(pSField->accessFlags));
+        printf(">\n</field>\n");
+    }
 
     free(accessStr);
 }
@@ -934,92 +1191,158 @@ void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
  */
 void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
 {
-    const DexFieldId* pFieldId;
-    const char* backDescriptor;
-    const char* name;
-    const char* typeDescriptor;
-    char* accessStr;
-
-    pFieldId = dexGetFieldId(pDexFile, pIField->fieldIdx);
-    name = dexStringById(pDexFile, pFieldId->nameIdx);
-    typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
-    backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
-
-    accessStr = createAccessFlagStr(pIField->accessFlags, kAccessForField);
-
-    printf("    #%d              : (in %s)\n", i, backDescriptor);
-    printf("      name          : '%s'\n", name);
-    printf("      type          : '%s'\n", typeDescriptor);
-    printf("      access        : 0x%04x (%s)\n",
-        pIField->accessFlags, accessStr);
-
-    free(accessStr);
+    dumpSField(pDexFile, pIField, i);
 }
 
 /*
  * Dump the class.
+ *
+ * If "*pLastPackage" is NULL or does not match the current class' package,
+ * the value will be replaced with a newly-allocated string.
  */
-void dumpClass(DexFile* pDexFile, int idx)
+void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
 {
     const DexTypeList* pInterfaces;
     const DexClassDef* pClassDef;
-    DexClassData* pClassData;
+    DexClassData* pClassData = NULL;
     const u1* pEncodedData;
     const char* fileName;
     const char* classDescriptor;
     const char* superclassDescriptor;
-    char* accessStr;
+    char* accessStr = NULL;
     int i;
 
     pClassDef = dexGetClassDef(pDexFile, idx);
-    printf("Class #%d            -\n", idx);
+
+    if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
+        //printf("<!-- omitting non-public class %s -->\n",
+        //    classDescriptor);
+        goto bail;
+    }
 
     pEncodedData = dexGetClassData(pDexFile, pClassDef);
     pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
 
     if (pClassData == NULL) {
-        printf("Trouble reading class data\n");
-        return;
+        printf("Trouble reading class data (#%d)\n", idx);
+        goto bail;
     }
     
     classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
-    printf("  Class descriptor  : '%s'\n", classDescriptor);
+
+    /*
+     * For the XML output, show the package name.  Ideally we'd gather
+     * up the classes, sort them, and dump them alphabetically so the
+     * package name wouldn't jump around, but that's not a great plan
+     * for something that needs to run on the device.
+     */
+    if (!(classDescriptor[0] == 'L' &&
+          classDescriptor[strlen(classDescriptor)-1] == ';'))
+    {
+        /* arrays and primitives should not be defined explicitly */
+        fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
+        /* keep going? */
+    } else if (gOptions.outputFormat == OUTPUT_XML) {
+        char* mangle;
+        char* lastSlash;
+        char* cp;
+
+        mangle = strdup(classDescriptor + 1);
+        mangle[strlen(mangle)-1] = '\0';
+
+        /* reduce to just the package name */
+        lastSlash = strrchr(mangle, '/');
+        if (lastSlash != NULL) {
+            *lastSlash = '\0';
+        } else {
+            *mangle = '\0';
+        }
+
+        for (cp = mangle; *cp != '\0'; cp++) {
+            if (*cp == '/')
+                *cp = '.';
+        }
+
+        if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
+            /* start of a new package */
+            if (*pLastPackage != NULL)
+                printf("</package>\n");
+            printf("<package name=\"%s\"\n>\n", mangle);
+            free(*pLastPackage);
+            *pLastPackage = mangle;
+        } else {
+            free(mangle);
+        }
+    }
 
     accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
-    printf("  Access flags      : 0x%04x (%s)\n",
-        pClassDef->accessFlags, accessStr);
 
-    if (pClassDef->superclassIdx == kDexNoIndex)
-        superclassDescriptor = "(none)";
-    else {
+    if (pClassDef->superclassIdx == kDexNoIndex) {
+        superclassDescriptor = NULL;
+    else {
         superclassDescriptor =
             dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
-        printf("  Superclass        : '%s'\n", superclassDescriptor);
     }
 
-    printf("  Interfaces        -\n");
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("Class #%d            -\n", idx);
+        printf("  Class descriptor  : '%s'\n", classDescriptor);
+        printf("  Access flags      : 0x%04x (%s)\n",
+            pClassDef->accessFlags, accessStr);
+
+        if (superclassDescriptor != NULL)
+            printf("  Superclass        : '%s'\n", superclassDescriptor);
+
+        printf("  Interfaces        -\n");
+    } else {
+        char* tmp;
+
+        tmp = descriptorClassToDot(classDescriptor);
+        printf("<class name=\"%s\"\n", tmp);
+        free(tmp);
+
+        if (superclassDescriptor != NULL) {
+            tmp = descriptorToDot(superclassDescriptor);
+            printf(" extends=\"%s\"\n", tmp);
+            free(tmp);
+        }
+        printf(" abstract=%s\n",
+            quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
+        printf(" static=%s\n",
+            quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
+        printf(" final=%s\n",
+            quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
+        // "deprecated=" not knowable w/o parsing annotations
+        printf(" visibility=%s\n",
+            quotedVisibility(pClassDef->accessFlags));
+        printf(">\n");
+    }
     pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
     if (pInterfaces != NULL) {
         for (i = 0; i < (int) pInterfaces->size; i++)
             dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
     }
 
-    printf("  Static fields     -\n");
+    if (gOptions.outputFormat == OUTPUT_PLAIN)
+        printf("  Static fields     -\n");
     for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
         dumpSField(pDexFile, &pClassData->staticFields[i], i);
     }
 
-    printf("  Instance fields   -\n");
+    if (gOptions.outputFormat == OUTPUT_PLAIN)
+        printf("  Instance fields   -\n");
     for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
         dumpIField(pDexFile, &pClassData->instanceFields[i], i);
     }
 
-    printf("  Direct methods    -\n");
+    if (gOptions.outputFormat == OUTPUT_PLAIN)
+        printf("  Direct methods    -\n");
     for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
         dumpMethod(pDexFile, &pClassData->directMethods[i], i);
     }
 
-    printf("  Virtual methods   -\n");
+    if (gOptions.outputFormat == OUTPUT_PLAIN)
+        printf("  Virtual methods   -\n");
     for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
         dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
     }
@@ -1030,11 +1353,18 @@ void dumpClass(DexFile* pDexFile, int idx)
         fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
     else
         fileName = "unknown";
-    printf("  source_file_idx   : %d (%s)\n",
-        pClassDef->sourceFileIdx, fileName);
 
-    printf("\n");
+    if (gOptions.outputFormat == OUTPUT_PLAIN) {
+        printf("  source_file_idx   : %d (%s)\n",
+            pClassDef->sourceFileIdx, fileName);
+        printf("\n");
+    }
+
+    if (gOptions.outputFormat == OUTPUT_XML) {
+        printf("</class>\n");
+    }
 
+bail:
     free(pClassData);
     free(accessStr);
 }
@@ -1044,20 +1374,35 @@ void dumpClass(DexFile* pDexFile, int idx)
  */
 void processDexFile(const char* fileName, DexFile* pDexFile)
 {
+    char* package = NULL;
     int i;
 
-    printf("Opened '%s', DEX version '%.3s'\n", fileName,
-        pDexFile->pHeader->magic +4);
+    if (gOptions.verbose) {
+        printf("Opened '%s', DEX version '%.3s'\n", fileName,
+            pDexFile->pHeader->magic +4);
+    }
 
     if (gOptions.showFileHeaders)
         dumpFileHeader(pDexFile);
 
+    if (gOptions.outputFormat == OUTPUT_XML)
+        printf("<api>\n");
+
     for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
         if (gOptions.showSectionHeaders)
             dumpClassDef(pDexFile, i);
 
-        dumpClass(pDexFile, i);
+        dumpClass(pDexFile, i, &package);
     }
+
+    /* free the last one allocated */
+    if (package != NULL) {
+        printf("</package>\n");
+        free(package);
+    }
+
+    if (gOptions.outputFormat == OUTPUT_XML)
+        printf("</api>\n");
 }
 
 
@@ -1071,14 +1416,18 @@ int process(const char* fileName)
     bool mapped = false;
     int result = -1;
 
-    printf("Processing '%s'...\n", fileName);
+    if (gOptions.verbose)
+        printf("Processing '%s'...\n", fileName);
 
     if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0)
         goto bail;
     mapped = true;
 
-    pDexFile = dexFileParse(map.addr, map.length,
-        kDexParseVerifyChecksum | kDexParseContinueOnError);
+    int flags = kDexParseVerifyChecksum;
+    if (gOptions.ignoreBadChecksum)
+        flags |= kDexParseContinueOnError;
+
+    pDexFile = dexFileParse(map.addr, map.length, flags);
     if (pDexFile == NULL) {
         fprintf(stderr, "ERROR: DEX parse failed\n");
         goto bail;
@@ -1103,11 +1452,15 @@ bail:
 void usage(void)
 {
     fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
-    fprintf(stderr, "%s: [-d] [-f] [-h] [-t tempfile] dexfile...\n", gProgName);
+    fprintf(stderr,
+        "%s: [-d] [-f] [-h] [-i] [-l layout] [-t tempfile] dexfile...\n",
+        gProgName);
     fprintf(stderr, "\n");
     fprintf(stderr, " -d : disassemble code sections\n");
     fprintf(stderr, " -f : display summary information from file header\n");
     fprintf(stderr, " -h : display file header details\n");
+    fprintf(stderr, " -i : ignore checksum failures\n");
+    fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
     fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
 }
 
@@ -1122,9 +1475,10 @@ int main(int argc, char* const argv[])
     int ic;
 
     memset(&gOptions, 0, sizeof(gOptions));
+    gOptions.verbose = true;
 
     while (1) {
-        ic = getopt(argc, argv, "dfht:");
+        ic = getopt(argc, argv, "dfhil:t:");
         if (ic < 0)
             break;
 
@@ -1138,8 +1492,22 @@ int main(int argc, char* const argv[])
         case 'h':       // dump section headers, i.e. all meta-data
             gOptions.showSectionHeaders = true;
             break;
+        case 'i':       // continue even if checksum is bad
+            gOptions.ignoreBadChecksum = true;
+            break;
+        case 'l':       // layout
+            if (strcmp(optarg, "plain") == 0) {
+                gOptions.outputFormat = OUTPUT_PLAIN;
+            } else if (strcmp(optarg, "xml") == 0) {
+                gOptions.outputFormat = OUTPUT_XML;
+                gOptions.verbose = false;
+                gOptions.exportsOnly = true;
+            } else {
+                wantUsage = true;
+            }
+            break;
         case 't':       // temp file, used when opening compressed Jar
-            gOptions.tempFileName = argv[optind];
+            gOptions.tempFileName = optarg;
             break;
         default:
             wantUsage = true;
@@ -1169,3 +1537,4 @@ int main(int argc, char* const argv[])
 
     return 0;
 }
+
index 35ced14..7dfee87 100644 (file)
@@ -104,7 +104,7 @@ UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
 {
     UnzipToFileResult result = kUTFRGenericFailure;
     int len = strlen(fileName);
-    char tempName[32];
+    char tempNameBuf[32];
     bool removeTemp = false;
     int fd = -1;
 
@@ -125,17 +125,17 @@ UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
              * data to a temp file, the location of which varies.
              */
             if (access("/tmp", W_OK) == 0)
-                sprintf(tempName, "/tmp/dex-temp-%d", getpid());
+                sprintf(tempNameBuf, "/tmp/dex-temp-%d", getpid());
             else
-                sprintf(tempName, "/sdcard/dex-temp-%d", getpid());
+                sprintf(tempNameBuf, "/sdcard/dex-temp-%d", getpid());
 
-            tempFileName = tempName;
+            tempFileName = tempNameBuf;
         }
 
         result = dexUnzipToFile(fileName, tempFileName, quiet);
         
         if (result == kUTFRSuccess) {
-            //printf("+++ Good unzip to '%s'\n", tempName);
+            //printf("+++ Good unzip to '%s'\n", tempFileName);
             fileName = tempFileName;
             removeTemp = true;
         } else if (result == kUTFRNotZip) {
@@ -177,8 +177,10 @@ bail:
     if (fd >= 0)
         close(fd);
     if (removeTemp) {
-        if (unlink(tempName) != 0)
-            fprintf(stderr, "Warning: unable to remove temp '%s'\n", tempName);
+        if (unlink(tempFileName) != 0) {
+            fprintf(stderr, "Warning: unable to remove temp '%s'\n",
+                tempFileName);
+        }
     }
     return result;
 }