summaryrefslogtreecommitdiff
path: root/src/main/java/com/it_jaros/jscanner/ProgressBar.java
diff options
context:
space:
mode:
authorGravatar Matthias Jaros <jarlucmat@mailbox.org>2026-04-13 17:31:55 +0200
committerGravatar Matthias Jaros <jarlucmat@mailbox.org>2026-07-09 09:59:28 +0200
commit8b881560dd8884e8e54e1e64654f79749d9ff456 (patch)
treefaffd2348b9430bcda9591120a0aa392923e422c /src/main/java/com/it_jaros/jscanner/ProgressBar.java
parentb1bdcf02479f67a65f6176ed8a87b47925903e81 (diff)
Renamed package from network_scanner to jscanner
Diffstat (limited to 'src/main/java/com/it_jaros/jscanner/ProgressBar.java')
-rw-r--r--src/main/java/com/it_jaros/jscanner/ProgressBar.java118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/main/java/com/it_jaros/jscanner/ProgressBar.java b/src/main/java/com/it_jaros/jscanner/ProgressBar.java
new file mode 100644
index 0000000..5ea347b
--- /dev/null
+++ b/src/main/java/com/it_jaros/jscanner/ProgressBar.java
@@ -0,0 +1,118 @@
+package com.it_jaros.jscanner;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ProgressBar {
+
+ private final ScheduledExecutorService ui = Executors.newSingleThreadScheduledExecutor();
+ private final ReentrantLock lock = new ReentrantLock();
+ private final int columnWidth = ProgressBar.getColumnWidth();
+
+ private List<Progress> hosts;
+
+ private static int getColumnWidth() {
+ try {
+ Process process = new ProcessBuilder("sh", "-c", "stty size < /dev/tty").start();
+
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(process.getInputStream()))) {
+ String line = reader.readLine();
+ if (line != null && !line.isBlank()) {
+ Matcher matcher = Pattern.compile("^\\d+\\s+(\\d+)").matcher(line.trim());
+ if (matcher.matches()) {
+ return Integer.parseInt(matcher.group(1));
+ }
+ }
+ }
+ } catch (Exception ignored) {
+ // does not matter why it did not work
+ }
+
+ return 120;
+ }
+
+ public void start() {
+ this.hosts = new ArrayList<>();
+ System.out.print("Scanning targets...\n\n");
+ ui.scheduleAtFixedRate(() -> {
+ lock.lock();
+ drawProgress(false);
+ lock.unlock();
+ }, 1, 200, TimeUnit.MILLISECONDS);
+ }
+
+ private void drawProgress(boolean last) {
+ // find and print finished hosts one last time
+ for (Iterator<Progress> it = hosts.iterator(); it.hasNext();) {
+ Progress p = it.next();
+ if (p.done().get() == p.total()) {
+ it.remove();
+ drawBar(p);
+ }
+ }
+
+ // the more progressed the host the more up it is
+ hosts.sort(Comparator.comparingInt((Progress p) -> p.done().get()).reversed());
+ for (Progress progress : hosts) {
+ drawBar(progress);
+ }
+
+ if (!last) {
+ // move cursor up again
+ System.out.print("\u001b[" + hosts.size() + "A");
+ }
+ }
+
+ public void submit(Progress progress) {
+ lock.lock();
+ hosts.add(progress);
+ lock.unlock();
+ }
+
+ private void drawBar(Progress progress) {
+ System.out.print("\u001b[2K\r"); // clear whole line
+ System.out.println(createBar(
+ progress.host(),
+ progress.open().get(),
+ progress.filtered().get(),
+ progress.done().get(),
+ progress.total()));
+ }
+
+ public String createBar(String label, int open, int filtered, int done, int total) {
+ int terminalWidth = columnWidth;
+ int bracketWidth = terminalWidth / 3;
+ double pct = total == 0 ? 1.0 : (done / (double) total);
+ int percent = (int) (pct * 100);
+
+ String statsLeft = String.format("open:%d filtered:%d (%d/%d)", open, filtered, done, total);
+ String statsRight = String.format("%3d%%", percent);
+ int filled = (int) (pct * bracketWidth);
+ String brackets = "[" + "#".repeat(filled) + "-".repeat(bracketWidth - filled) + "]";
+
+ int spacesBetween = terminalWidth - bracketWidth - (label + statsLeft + statsRight).length() - 5;
+ return String.format("%s%s%s %s %s", label, " ".repeat(spacesBetween),statsLeft, brackets, statsRight);
+ }
+
+ public void stop() {
+ ui.shutdown();
+ try {
+ ui.awaitTermination(10, TimeUnit.SECONDS);
+ // we need to draw one last time for the 100% to appear
+ drawProgress(true);
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ }
+ }
+}