diff options
author | Andy McFadden <fadden@android.com> | 2009-05-06 10:19:16 -0700 |
---|---|---|
committer | Andy McFadden <fadden@android.com> | 2009-05-06 10:19:16 -0700 |
commit | d18aff3e3ef753a0b6725f1b0a9cd47faa5989e0 (patch) | |
tree | 93a5ce9236e90ef788342acdb63e6a03e87f6d5a /dexdump | |
parent | 39cf7d7849f7b0c55f749fc538c8b1960c16e5bb (diff) | |
parent | a2ee53bac4db4c77aa2bb31bad8a9d177fd09301 (diff) | |
download | android_dalvik-d18aff3e3ef753a0b6725f1b0a9cd47faa5989e0.tar.gz android_dalvik-d18aff3e3ef753a0b6725f1b0a9cd47faa5989e0.tar.bz2 android_dalvik-d18aff3e3ef753a0b6725f1b0a9cd47faa5989e0.zip |
merge a2ee53b and resolved conflicts...
Diffstat (limited to 'dexdump')
-rw-r--r-- | dexdump/DexDump.c | 529 |
1 files changed, 443 insertions, 86 deletions
diff --git a/dexdump/DexDump.c b/dexdump/DexDump.c index bd9123671..6fed7aaf4 100644 --- a/dexdump/DexDump.c +++ b/dexdump/DexDump.c @@ -18,7 +18,16 @@ * 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<?>" + * - class shows declared fields and methods; does not show inherited fields */ #include "libdex/DexFile.h" #include "libdex/DexCatch.h" @@ -44,6 +53,11 @@ 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 checksumOnly; @@ -52,7 +66,10 @@ struct { bool showSectionHeaders; bool ignoreBadChecksum; bool dumpRegisterMaps; + OutputFormat outputFormat; const char* tempFileName; + bool exportsOnly; + bool verbose; } gOptions; /* basic info about a field or method */ @@ -79,34 +96,134 @@ static inline u4 get4LE(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); - while (at > 0) { - at--; - newStr[at] = (str[at] == '/') ? '.' : str[at]; + /* copy class name over */ + int i; + for (i = 0; i < targetLen; i++) { + char ch = str[offset + i]; + newStr[i] = (ch == '/' || ch == '$') ? '.' : ch; + } + + /* 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. @@ -126,7 +243,6 @@ static int countOnes(u4 val) return count; } - /* * Flag for use with createAccessFlagStr(). */ @@ -322,7 +438,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) @@ -330,7 +446,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); + } } /* @@ -878,8 +1000,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); @@ -890,22 +1018,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); } @@ -921,6 +1146,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); @@ -928,11 +1159,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); } @@ -942,94 +1197,160 @@ 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. * * Note "idx" is a DexClassDef index, not a DexTypeId index. + * + * 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); } @@ -1040,11 +1361,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); } @@ -1256,10 +1584,13 @@ void dumpRegisterMaps(DexFile* pDexFile) */ 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.dumpRegisterMaps) { dumpRegisterMaps(pDexFile); @@ -1269,12 +1600,24 @@ void processDexFile(const char* fileName, DexFile* pDexFile) 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"); } @@ -1288,7 +1631,8 @@ 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; @@ -1328,7 +1672,7 @@ void usage(void) { fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n"); fprintf(stderr, - "%s: [-c] [-d] [-f] [-h] [-m] [-i] [-t tempfile] dexfile...\n", + "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n", gProgName); fprintf(stderr, "\n"); fprintf(stderr, " -c : verify checksum and exit\n"); @@ -1336,6 +1680,7 @@ void usage(void) 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, " -m : dump register maps (and nothing else)\n"); fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n"); } @@ -1351,9 +1696,10 @@ int main(int argc, char* const argv[]) int ic; memset(&gOptions, 0, sizeof(gOptions)); + gOptions.verbose = true; while (1) { - ic = getopt(argc, argv, "cdfhimt:"); + ic = getopt(argc, argv, "cdfhil:mt:"); if (ic < 0) break; @@ -1373,11 +1719,22 @@ int main(int argc, char* const argv[]) 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 'm': // dump register maps only gOptions.dumpRegisterMaps = true; break; case 't': // temp file, used when opening compressed Jar - gOptions.tempFileName = argv[optind]; + gOptions.tempFileName = optarg; break; default: wantUsage = true; |