1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
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 List<Progress> hosts = new ArrayList<>();
private final ReentrantLock lock = new ReentrantLock();
private final ScheduledExecutorService ui = Executors.newSingleThreadScheduledExecutor();
private final boolean quiet;
private final int columnWidth = ProgressBar.getColumnWidth();
public ProgressBar(boolean quiet) {
this.quiet = quiet;
}
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() {
if (!quiet) {
System.out.print("Scanning targets...\n\n");
}
// although we don't print anything, we still need to
// gc all the done hosts
ui.scheduleAtFixedRate(() -> {
lock.lock();
drawProgress(false);
lock.unlock();
}, 1, 500, TimeUnit.MILLISECONDS);
}
private void drawProgress(boolean last) {
// sort -> the more progressed the host the more up it is
hosts.sort(Comparator.comparingInt((Progress p) -> p.done().get()).reversed());
// 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);
} else {
break;
}
}
for (Progress progress : hosts) {
drawBar(progress);
}
if (!last) {
// move cursor up again
if (!quiet) {
System.out.print("\u001b[" + hosts.size() + "A");
}
}
}
public void add(Progress progress) {
lock.lock();
hosts.add(progress);
lock.unlock();
}
private void drawBar(Progress progress) {
if (quiet) return;
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(1, TimeUnit.SECONDS);
// we need to draw one last time for the 100% to appear
drawProgress(true);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
}
}
|