From 9f927ee86806e3cdd0500b35ab5b9f6c164b9470 Mon Sep 17 00:00:00 2001 From: Moresteck Date: Wed, 3 Jul 2024 16:13:54 +0000 Subject: [PATCH] Added sorting - .exes are considered zip archives, will be analyzed - added optional subdirectory distinction - added readme --- README.md | 9 +++ .../jaranalyzer/FilenameComparator.java | 72 +++++++++++++++++++ .../uk/betacraft/jaranalyzer/JarAnalyzer.java | 33 +++++---- 3 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 README.md create mode 100644 src/main/java/uk/betacraft/jaranalyzer/FilenameComparator.java diff --git a/README.md b/README.md new file mode 100644 index 0000000..8041be3 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# jar-analyzer +Usage: + + java -Dtz= -jar jar-analyzer.jar /path/to/dir/or/jar/to/analyze + +`-Dtz=` is optional, and if not used, the program will assume the jar contents are UTC. + +### TODO +- add printing in tree view as an option \ No newline at end of file diff --git a/src/main/java/uk/betacraft/jaranalyzer/FilenameComparator.java b/src/main/java/uk/betacraft/jaranalyzer/FilenameComparator.java new file mode 100644 index 0000000..a2d7149 --- /dev/null +++ b/src/main/java/uk/betacraft/jaranalyzer/FilenameComparator.java @@ -0,0 +1,72 @@ +package uk.betacraft.jaranalyzer; + +import java.io.File; +import java.math.BigInteger; +import java.util.Comparator; +import java.util.regex.Pattern; +/** + * Taken from https://stackoverflow.com/a/48946762 + */ +public final class FilenameComparator implements Comparator { + private static final Pattern NUMBERS = + Pattern.compile("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)"); + + @Override public final int compare(File o1, File o2) { + // Optional "NULLS LAST" semantics: + if (o1 == null || o2 == null) + return o1 == null ? o2 == null ? 0 : -1 : 1; + + String name1 = o1.getName(); + String name2 = o2.getName(); + + // Fix Notch's inconsistent versioning breaking order... TODO: find a better way of doing this if possible + if ((name1.equals("b1.0.jar") && name2.contains("b1.0_01.jar")) + || (name1.equals("b1.7.jar") && name2.contains("b1.7_01.jar"))) + return -1; + else if ((name2.equals("b1.0.jar") && name1.contains("b1.0_01.jar")) + || (name2.equals("b1.7.jar") && name1.contains("b1.7_01.jar"))) + return 1; + else if ((name1.equals("b1.0_01.jar") && name2.contains("b1.0.2.jar")) + || (name1.equals("b1.7_01.jar") && name2.contains("b1.7.2.jar")) + || (name1.equals("b1.7_01.jar") && name2.contains("b1.7.3.jar"))) + return -1; + else if ((name2.equals("b1.0_01.jar") && name1.contains("b1.0.2.jar")) + || (name2.equals("b1.7_01.jar") && name1.contains("b1.7.2.jar")) + || (name2.equals("b1.7_01.jar") && name1.contains("b1.7.3.jar"))) + return 1; + + // Splitting both input strings by the above patterns + String[] split1 = NUMBERS.split(name1); + String[] split2 = NUMBERS.split(name2); + + for (int i = 0; i < Math.min(split1.length, split2.length); i++) { + char c1 = split1[i].charAt(0); + char c2 = split2[i].charAt(0); + int cmp = 0; + + // If both segments start with a digit, sort them numerically using + // BigInteger to stay safe + if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9') + cmp = new BigInteger(split1[i]).compareTo(new BigInteger(split2[i])); + + // Place 1.x.jar before 1.x.y.jar + if (c1 == '.' && (split2[i].equals(".jar") || split2[i].equals(".exe") || split2[i].equals(".zip"))) + cmp = 1; + else if (c2 == '.' && (split1[i].equals(".jar") || split1[i].equals(".exe") || split1[i].equals(".zip"))) + cmp = -1; + + // If we haven't sorted numerically before, or if numeric sorting yielded + // equality (e.g 007 and 7) then sort lexicographically + else if (cmp == 0) + cmp = split1[i].compareTo(split2[i]); + + // Abort once some prefix has unequal ordering + if (cmp != 0) + return cmp; + } + + // If we reach this, then both strings have equally ordered prefixes, but + // maybe one string is longer than the other (i.e. has more segments) + return split1.length - split2.length; + } +} diff --git a/src/main/java/uk/betacraft/jaranalyzer/JarAnalyzer.java b/src/main/java/uk/betacraft/jaranalyzer/JarAnalyzer.java index 8c94955..3dc5b23 100644 --- a/src/main/java/uk/betacraft/jaranalyzer/JarAnalyzer.java +++ b/src/main/java/uk/betacraft/jaranalyzer/JarAnalyzer.java @@ -7,6 +7,7 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.util.Arrays; import java.util.Enumeration; import java.util.TimeZone; import java.util.jar.JarEntry; @@ -16,6 +17,7 @@ public class JarAnalyzer { // assuming UTC by default private static final String tzID = System.getProperty("tz", "UTC"); + private static final boolean printSubdirsDistinctly = Boolean.parseBoolean(System.getProperty("subdirs", "false")); private static TimeZone tz = TimeZone.getTimeZone(tzID); public static void main(String[] args) { @@ -31,27 +33,32 @@ public static void main(String[] args) { } } - public static boolean isJarZip(File f) { - return !f.isDirectory() && (f.getName().toLowerCase().endsWith(".jar") || f.getName().toLowerCase().endsWith(".zip")); + public static boolean isZipArchive(File f) { + String fname = f.getName().toLowerCase(); + + // accept jar, zip, and additionally exe (servers before 1.9 have EXE counterparts) + return !f.isDirectory() && (fname.endsWith(".jar") || fname.endsWith(".zip") || fname.endsWith(".exe")); } + public static long depth = -1; + public static void analyze(File folder) { - if (isJarZip(folder)) { + if (isZipArchive(folder)) { printInfoJar(folder); } else if (folder.isDirectory()) { - System.out.println(" ========= FOLDER: " + folder.getName()); + + if (printSubdirsDistinctly) + System.out.println(" ========= FOLDER: " + folder.getName()); File[] files = folder.listFiles(); - for (File file : files) { - if (file.isDirectory()) { - analyze(file); - } else { - printInfoJar(file); - } - } + Arrays.sort(files, new FilenameComparator()); + + for (File file : files) + analyze(file); - System.out.println(" ========= END: " + folder.getName()); + if (printSubdirsDistinctly) + System.out.println(" ========= END: " + folder.getName()); } } @@ -93,7 +100,7 @@ public static void printInfoJar(File f) { System.out.printf("%-60.60s %-60.60s%n", relativePath, formattedDateTime); } catch (ZipException zipException) { - // happens on 1.9+ jars - avoid it as there's nothing that can be done + // disregard non-ZIP or corrupted files if ("zip END header not found".equals(zipException.getMessage())) return;