Commit 8446d016 authored by Skylot's avatar Skylot

cli: update jcommander lib

parent ab040d36
...@@ -5,7 +5,7 @@ applicationName = 'jadx' ...@@ -5,7 +5,7 @@ applicationName = 'jadx'
dependencies { dependencies {
compile(project(':jadx-core')) compile(project(':jadx-core'))
compile 'com.beust:jcommander:1.47' compile 'com.beust:jcommander:1.72'
compile 'ch.qos.logback:logback-classic:1.2.3' compile 'ch.qos.logback:logback-classic:1.2.3'
} }
......
...@@ -4,6 +4,7 @@ import org.slf4j.Logger; ...@@ -4,6 +4,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.api.JadxDecompiler; import jadx.api.JadxDecompiler;
import jadx.core.utils.exceptions.JadxArgsValidateException;
public class JadxCLI { public class JadxCLI {
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class); private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
...@@ -22,7 +23,12 @@ public class JadxCLI { ...@@ -22,7 +23,12 @@ public class JadxCLI {
static void processAndSave(JadxCLIArgs inputArgs) { static void processAndSave(JadxCLIArgs inputArgs) {
JadxDecompiler jadx = new JadxDecompiler(inputArgs.toJadxArgs()); JadxDecompiler jadx = new JadxDecompiler(inputArgs.toJadxArgs());
jadx.load(); try {
jadx.load();
} catch (JadxArgsValidateException e) {
LOG.error("Incorrect arguments: {}", e.getMessage());
System.exit(1);
}
jadx.save(); jadx.save();
if (jadx.getErrorsCount() != 0) { if (jadx.getErrorsCount() != 0) {
jadx.printErrorsReport(); jadx.printErrorsReport();
......
...@@ -10,7 +10,6 @@ import java.util.stream.Collectors; ...@@ -10,7 +10,6 @@ import java.util.stream.Collectors;
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender; import ch.qos.logback.core.Appender;
import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.JCommander; import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterDescription; import com.beust.jcommander.ParameterDescription;
...@@ -25,7 +24,7 @@ import jadx.core.utils.files.FileUtils; ...@@ -25,7 +24,7 @@ import jadx.core.utils.files.FileUtils;
public class JadxCLIArgs { public class JadxCLIArgs {
@Parameter(description = "<input file> (.dex, .apk, .jar or .class)") @Parameter(description = "<input file> (.apk, .dex, .jar or .class)")
protected List<String> files = new ArrayList<>(1); protected List<String> files = new ArrayList<>(1);
@Parameter(names = {"-d", "--output-dir"}, description = "output directory") @Parameter(names = {"-d", "--output-dir"}, description = "output directory")
...@@ -37,9 +36,6 @@ public class JadxCLIArgs { ...@@ -37,9 +36,6 @@ public class JadxCLIArgs {
@Parameter(names = {"-dr", "--output-dir-res"}, description = "output directory for resources") @Parameter(names = {"-dr", "--output-dir-res"}, description = "output directory for resources")
protected String outDirRes; protected String outDirRes;
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
@Parameter(names = {"-r", "--no-res"}, description = "do not decode resources") @Parameter(names = {"-r", "--no-res"}, description = "do not decode resources")
protected boolean skipResources = false; protected boolean skipResources = false;
...@@ -49,15 +45,16 @@ public class JadxCLIArgs { ...@@ -49,15 +45,16 @@ public class JadxCLIArgs {
@Parameter(names = {"-e", "--export-gradle"}, description = "save as android gradle project") @Parameter(names = {"-e", "--export-gradle"}, description = "save as android gradle project")
protected boolean exportAsGradleProject = false; protected boolean exportAsGradleProject = false;
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
@Parameter(names = {"--show-bad-code"}, description = "show inconsistent code (incorrectly decompiled)") @Parameter(names = {"--show-bad-code"}, description = "show inconsistent code (incorrectly decompiled)")
protected boolean showInconsistentCode = false; protected boolean showInconsistentCode = false;
@Parameter(names = {"--no-imports"}, converter = InvertedBooleanConverter.class, @Parameter(names = {"--no-imports"}, description = "disable use of imports, always write entire package name")
description = "disable use of imports, always write entire package name")
protected boolean useImports = true; protected boolean useImports = true;
@Parameter(names = "--no-replace-consts", converter = InvertedBooleanConverter.class, @Parameter(names = "--no-replace-consts", description = "don't replace constant value with matching constant field")
description = "don't replace constant value with matching constant field")
protected boolean replaceConsts = true; protected boolean replaceConsts = true;
@Parameter(names = {"--escape-unicode"}, description = "escape non latin characters in strings (with \\u)") @Parameter(names = {"--escape-unicode"}, description = "escape non latin characters in strings (with \\u)")
...@@ -90,6 +87,9 @@ public class JadxCLIArgs { ...@@ -90,6 +87,9 @@ public class JadxCLIArgs {
@Parameter(names = {"-v", "--verbose"}, description = "verbose output") @Parameter(names = {"-v", "--verbose"}, description = "verbose output")
protected boolean verbose = false; protected boolean verbose = false;
@Parameter(names = {"--version"}, description = "print jadx version")
protected boolean printVersion = false;
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true) @Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
protected boolean printHelp = false; protected boolean printHelp = false;
...@@ -99,7 +99,7 @@ public class JadxCLIArgs { ...@@ -99,7 +99,7 @@ public class JadxCLIArgs {
private boolean parse(String[] args) { private boolean parse(String[] args) {
try { try {
new JCommander(this, args); makeJCommander().parse(args);
return true; return true;
} catch (ParameterException e) { } catch (ParameterException e) {
System.err.println("Arguments parse error: " + e.getMessage()); System.err.println("Arguments parse error: " + e.getMessage());
...@@ -108,16 +108,24 @@ public class JadxCLIArgs { ...@@ -108,16 +108,24 @@ public class JadxCLIArgs {
} }
} }
private JCommander makeJCommander() {
return JCommander.newBuilder().addObject(this).build();
}
private boolean process() { private boolean process() {
if (isPrintHelp()) { if (printHelp) {
printUsage(); printUsage();
return false; return false;
} }
if (printVersion) {
System.out.println(JadxDecompiler.getVersion());
return false;
}
try { try {
if (threadsCount <= 0) { if (threadsCount <= 0) {
throw new JadxException("Threads count must be positive, got: " + threadsCount); throw new JadxException("Threads count must be positive, got: " + threadsCount);
} }
if (isVerbose()) { if (verbose) {
ch.qos.logback.classic.Logger rootLogger = ch.qos.logback.classic.Logger rootLogger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// remove INFO ThresholdFilter // remove INFO ThresholdFilter
...@@ -135,7 +143,7 @@ public class JadxCLIArgs { ...@@ -135,7 +143,7 @@ public class JadxCLIArgs {
} }
public void printUsage() { public void printUsage() {
JCommander jc = new JCommander(this); JCommander jc = makeJCommander();
// print usage in not sorted fields order (by default its sorted by description) // print usage in not sorted fields order (by default its sorted by description)
PrintStream out = System.out; PrintStream out = System.out;
out.println(); out.println();
...@@ -162,13 +170,13 @@ public class JadxCLIArgs { ...@@ -162,13 +170,13 @@ public class JadxCLIArgs {
continue; continue;
} }
StringBuilder opt = new StringBuilder(); StringBuilder opt = new StringBuilder();
opt.append(' ').append(p.getNames()); opt.append(" ").append(p.getNames());
addSpaces(opt, maxNamesLen - opt.length() + 2); addSpaces(opt, maxNamesLen - opt.length() + 2);
opt.append("- ").append(p.getDescription()); opt.append("- ").append(p.getDescription());
out.println(opt); out.println(opt);
} }
out.println("Example:"); out.println("Example:");
out.println(" jadx -d out classes.dex"); out.println(" jadx -d out classes.dex");
} }
private static void addSpaces(StringBuilder str, int count) { private static void addSpaces(StringBuilder str, int count) {
...@@ -202,13 +210,6 @@ public class JadxCLIArgs { ...@@ -202,13 +210,6 @@ public class JadxCLIArgs {
return args; return args;
} }
public static class InvertedBooleanConverter implements IStringConverter<Boolean> {
@Override
public Boolean convert(String value) {
return "false".equals(value);
}
}
public List<String> getFiles() { public List<String> getFiles() {
return files; return files;
} }
...@@ -225,10 +226,6 @@ public class JadxCLIArgs { ...@@ -225,10 +226,6 @@ public class JadxCLIArgs {
return outDirRes; return outDirRes;
} }
public boolean isPrintHelp() {
return printHelp;
}
public boolean isSkipResources() { public boolean isSkipResources() {
return skipResources; return skipResources;
} }
...@@ -241,14 +238,6 @@ public class JadxCLIArgs { ...@@ -241,14 +238,6 @@ public class JadxCLIArgs {
return threadsCount; return threadsCount;
} }
public boolean isCFGOutput() {
return cfgOutput;
}
public boolean isRawCFGOutput() {
return rawCfgOutput;
}
public boolean isFallbackMode() { public boolean isFallbackMode() {
return fallbackMode; return fallbackMode;
} }
...@@ -261,10 +250,6 @@ public class JadxCLIArgs { ...@@ -261,10 +250,6 @@ public class JadxCLIArgs {
return useImports; return useImports;
} }
public boolean isVerbose() {
return verbose;
}
public boolean isDeobfuscationOn() { public boolean isDeobfuscationOn() {
return deobfuscationOn; return deobfuscationOn;
} }
...@@ -289,6 +274,18 @@ public class JadxCLIArgs { ...@@ -289,6 +274,18 @@ public class JadxCLIArgs {
return escapeUnicode; return escapeUnicode;
} }
public boolean isEscapeUnicode() {
return escapeUnicode;
}
public boolean isCfgOutput() {
return cfgOutput;
}
public boolean isRawCfgOutput() {
return rawCfgOutput;
}
public boolean isReplaceConsts() { public boolean isReplaceConsts() {
return replaceConsts; return replaceConsts;
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<level>INFO</level> <level>INFO</level>
</filter> </filter>
<encoder> <encoder>
<pattern>%d{HH:mm:ss} %-5level - %msg%n</pattern> <pattern>%-5level - %msg%n</pattern>
</encoder> </encoder>
</appender> </appender>
......
package jadx.cli; package jadx.cli;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
public class JadxCLIArgsTest { public class JadxCLIArgsTest {
private static final Logger LOG = LoggerFactory.getLogger(JadxCLIArgsTest.class);
@Test @Test
public void testInvertedBooleanOption() { public void testInvertedBooleanOption() {
assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false)); assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false));
assertThat(parse("").isReplaceConsts(), is(true)); assertThat(parse("").isReplaceConsts(), is(true));
} }
@Test
public void testEscapeUnicodeOption() {
assertThat(parse("--escape-unicode").isEscapeUnicode(), is(true));
assertThat(parse("").isEscapeUnicode(), is(false));
}
@Test
public void testSrcOption() {
assertThat(parse("--no-src").isSkipSources(), is(true));
assertThat(parse("-s").isSkipSources(), is(true));
assertThat(parse("").isSkipSources(), is(false));
}
private JadxCLIArgs parse(String... args) { private JadxCLIArgs parse(String... args) {
JadxCLIArgs jadxArgs = new JadxCLIArgs(); JadxCLIArgs jadxArgs = new JadxCLIArgs();
boolean res = jadxArgs.processArgs(args); boolean res = jadxArgs.processArgs(args);
assertThat(res, is(true)); assertThat(res, is(true));
LOG.info("Jadx args: {}", jadxArgs.toJadxArgs());
return jadxArgs; return jadxArgs;
} }
} }
...@@ -211,4 +211,31 @@ public class JadxArgs { ...@@ -211,4 +211,31 @@ public class JadxArgs {
public void setExportAsGradleProject(boolean exportAsGradleProject) { public void setExportAsGradleProject(boolean exportAsGradleProject) {
this.exportAsGradleProject = exportAsGradleProject; this.exportAsGradleProject = exportAsGradleProject;
} }
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("JadxArgs{");
sb.append("inputFiles=").append(inputFiles);
sb.append(", outDir=").append(outDir);
sb.append(", outDirSrc=").append(outDirSrc);
sb.append(", outDirRes=").append(outDirRes);
sb.append(", threadsCount=").append(threadsCount);
sb.append(", cfgOutput=").append(cfgOutput);
sb.append(", rawCFGOutput=").append(rawCFGOutput);
sb.append(", fallbackMode=").append(fallbackMode);
sb.append(", showInconsistentCode=").append(showInconsistentCode);
sb.append(", useImports=").append(useImports);
sb.append(", isSkipResources=").append(isSkipResources);
sb.append(", isSkipSources=").append(isSkipSources);
sb.append(", isDeobfuscationOn=").append(isDeobfuscationOn);
sb.append(", isDeobfuscationForceSave=").append(isDeobfuscationForceSave);
sb.append(", useSourceNameAsClassAlias=").append(useSourceNameAsClassAlias);
sb.append(", deobfuscationMinLength=").append(deobfuscationMinLength);
sb.append(", deobfuscationMaxLength=").append(deobfuscationMaxLength);
sb.append(", escapeUnicode=").append(escapeUnicode);
sb.append(", replaceConsts=").append(replaceConsts);
sb.append(", exportAsGradleProject=").append(exportAsGradleProject);
sb.append('}');
return sb.toString();
}
} }
package jadx.api; package jadx.api;
import java.io.File; import java.io.File;
import java.util.List;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxArgsValidateException;
public class JadxArgsValidator { public class JadxArgsValidator {
private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidator.class); private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidator.class);
public static void validate(JadxArgs args) { public static void validate(JadxArgs args) {
if (args.getInputFiles().isEmpty()) { checkInputFiles(args);
throw new JadxRuntimeException("Please specify input file"); validateOutDirs(args);
if (LOG.isDebugEnabled()) {
LOG.debug("Effective jadx args: {}", args);
}
}
private static void checkInputFiles(JadxArgs args) {
List<File> inputFiles = args.getInputFiles();
if (inputFiles.isEmpty()) {
throw new JadxArgsValidateException("Please specify input file");
}
if (inputFiles.size() > 1) {
for (File inputFile : inputFiles) {
String fileName = inputFile.getName();
if (fileName.startsWith("--")) {
throw new JadxArgsValidateException("Unknown argument: " + fileName);
}
}
throw new JadxArgsValidateException("Only one input file supported");
} }
for (File file : args.getInputFiles()) { for (File file : inputFiles) {
checkFile(file); checkFile(file);
} }
validateOutDirs(args);
} }
private static void validateOutDirs(JadxArgs args) { private static void validateOutDirs(JadxArgs args) {
...@@ -71,16 +90,16 @@ public class JadxArgsValidator { ...@@ -71,16 +90,16 @@ public class JadxArgsValidator {
private static void checkFile(File file) { private static void checkFile(File file) {
if (!file.exists()) { if (!file.exists()) {
throw new JadxRuntimeException("File not found " + file.getAbsolutePath()); throw new JadxArgsValidateException("File not found " + file.getAbsolutePath());
} }
if (file.isDirectory()) { if (file.isDirectory()) {
throw new JadxRuntimeException("Expected file but found directory instead: " + file.getAbsolutePath()); throw new JadxArgsValidateException("Expected file but found directory instead: " + file.getAbsolutePath());
} }
} }
private static void checkDir(File dir) { private static void checkDir(File dir) {
if (dir != null && dir.exists() && !dir.isDirectory()) { if (dir != null && dir.exists() && !dir.isDirectory()) {
throw new JadxRuntimeException("Output directory exists as file " + dir); throw new JadxArgsValidateException("Output directory exists as file " + dir);
} }
} }
......
package jadx.core.utils.exceptions;
public class JadxArgsValidateException extends RuntimeException {
private static final long serialVersionUID = -7457621776087311909L;
public JadxArgsValidateException(String message) {
super(message);
}
public JadxArgsValidateException(String message, Throwable cause) {
super(message, cause);
}
}
...@@ -270,7 +270,7 @@ public class JadxSettingsWindow extends JDialog { ...@@ -270,7 +270,7 @@ public class JadxSettingsWindow extends JDialog {
}); });
JCheckBox cfg = new JCheckBox(); JCheckBox cfg = new JCheckBox();
cfg.setSelected(settings.isCFGOutput()); cfg.setSelected(settings.isCfgOutput());
cfg.addItemListener(new ItemListener() { cfg.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) { public void itemStateChanged(ItemEvent e) {
settings.setCfgOutput(e.getStateChange() == ItemEvent.SELECTED); settings.setCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
...@@ -279,7 +279,7 @@ public class JadxSettingsWindow extends JDialog { ...@@ -279,7 +279,7 @@ public class JadxSettingsWindow extends JDialog {
}); });
JCheckBox rawCfg = new JCheckBox(); JCheckBox rawCfg = new JCheckBox();
rawCfg.setSelected(settings.isRawCFGOutput()); rawCfg.setSelected(settings.isRawCfgOutput());
rawCfg.addItemListener(new ItemListener() { rawCfg.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) { public void itemStateChanged(ItemEvent e) {
settings.setRawCfgOutput(e.getStateChange() == ItemEvent.SELECTED); settings.setRawCfgOutput(e.getStateChange() == ItemEvent.SELECTED);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment