package com.it_jaros.network_scanner; 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 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 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(); } } }