1 #ifdef _WIN32
2 
3 #include <windows.h>
4 #include <conio.h>
5 
6 #else
7 
8 #include <unistd.h> // for close()
9 #include <fcntl.h> // for open()
10 #include <sys/ioctl.h>
11 
12 #endif
13 
14 #include "libtorrent/config.hpp"
15 
16 #include "print.hpp"
17 
18 #include <cstdlib> // for atoi
19 #include <cstring> // for strlen
20 #include <cmath>
21 #include <algorithm> // for std::min
22 #include <iterator> // for back_inserter
23 
esc(char const * code)24 char const* esc(char const* code)
25 {
26 	// this is a silly optimization
27 	// to avoid copying of strings
28 	enum { num_strings = 200 };
29 	static char buf[num_strings][20];
30 	static int round_robin = 0;
31 	char* ret = buf[round_robin];
32 	++round_robin;
33 	if (round_robin >= num_strings) round_robin = 0;
34 	ret[0] = '\033';
35 	ret[1] = '[';
36 	int i = 2;
37 	int j = 0;
38 	while (code[j]) ret[i++] = code[j++];
39 	ret[i++] = 'm';
40 	ret[i++] = 0;
41 	return ret;
42 }
43 
to_string(int v,int width)44 std::string to_string(int v, int width)
45 {
46 	char buf[100];
47 	std::snprintf(buf, sizeof(buf), "%*d", width, v);
48 	return buf;
49 }
50 
add_suffix_float(double val,char const * suffix)51 std::string add_suffix_float(double val, char const* suffix)
52 {
53 	if (val < 0.001)
54 	{
55 		std::string ret;
56 		ret.resize(4 + 2, ' ');
57 		if (suffix) ret.resize(4 + 2 + strlen(suffix), ' ');
58 		return ret;
59 	}
60 
61 	const char* prefix[] = {"kB", "MB", "GB", "TB", "PB"};
62 	const int num_prefix = sizeof(prefix) / sizeof(const char*);
63 	int i = 0;
64 	for (; i < num_prefix - 1; ++i)
65 	{
66 		val /= 1000.;
67 		if (std::fabs(val) < 1000.) break;
68 	}
69 	char ret[100];
70 	std::snprintf(ret, sizeof(ret), "%4.*f%s%s", val < 99 ? 1 : 0, val, prefix[i], suffix ? suffix : "");
71 	return ret;
72 }
73 
color(std::string const & s,color_code c)74 std::string color(std::string const& s, color_code c)
75 {
76 	if (c == col_none) return s;
77 	if (std::count(s.begin(), s.end(), ' ') == int(s.size())) return s;
78 
79 	char buf[1024];
80 	std::snprintf(buf, sizeof(buf), "\x1b[3%dm%s\x1b[39m", c, s.c_str());
81 	return buf;
82 }
83 
progress_bar(int progress,int width,color_code c,char fill,char bg,std::string caption,int flags)84 std::string const& progress_bar(int progress, int width, color_code c
85 	, char fill, char bg, std::string caption, int flags)
86 {
87 	static std::string bar;
88 	bar.clear();
89 	bar.reserve(size_t(width + 10));
90 
91 	auto const progress_chars = static_cast<std::size_t>((progress * width + 500) / 1000);
92 
93 	if (caption.empty())
94 	{
95 		char code[10];
96 		std::snprintf(code, sizeof(code), "\x1b[3%dm", c);
97 		bar = code;
98 		std::fill_n(std::back_inserter(bar), progress_chars, fill);
99 		std::fill_n(std::back_inserter(bar), width - progress_chars, bg);
100 		bar += esc("39");
101 	}
102 	else
103 	{
104 		// foreground color (depends a bit on background color)
105 		color_code tc = col_black;
106 		if (c == col_black || c == col_blue)
107 			tc = col_white;
108 
109 		caption.resize(size_t(width), ' ');
110 
111 #ifdef _WIN32
112 		char const* background = "40";
113 #else
114 		char const* background = "48;5;238";
115 #endif
116 
117 		char str[256];
118 		if (flags & progress_invert)
119 			std::snprintf(str, sizeof(str), "\x1b[%sm\x1b[37m%s\x1b[4%d;3%dm%s\x1b[49;39m"
120 				, background, caption.substr(0, progress_chars).c_str(), c, tc
121 				, caption.substr(progress_chars).c_str());
122 		else
123 			std::snprintf(str, sizeof(str), "\x1b[4%d;3%dm%s\x1b[%sm\x1b[37m%s\x1b[49;39m"
124 				, c, tc, caption.substr(0, progress_chars).c_str(), background
125 				, caption.substr(progress_chars).c_str());
126 		bar = str;
127 	}
128 	return bar;
129 }
130 
get_piece(lt::bitfield const & p,int index)131 int get_piece(lt::bitfield const& p, int index)
132 {
133 	if (index < 0 || index >= p.size()) return 0;
134 	return p.get_bit(index) ? 1 : 0;
135 }
136 
piece_bar(lt::bitfield const & p,int width)137 std::string const& piece_bar(lt::bitfield const& p, int width)
138 {
139 #ifdef _WIN32
140 	int const table_size = 5;
141 #else
142 	int const table_size = 18;
143 	width *= 2; // we only print one character for every two "slots"
144 #endif
145 
146 	double const piece_per_char = p.size() / double(width);
147 	static std::string bar;
148 	bar.clear();
149 	bar.reserve(width * 6);
150 	bar += "[";
151 	if (p.size() == 0)
152 	{
153 		for (int i = 0; i < width; ++i) bar += ' ';
154 		bar += "]";
155 		return bar;
156 	}
157 
158 	// the [piece, piece + pieces_per_char) range is the pieces that are represented by each character
159 	double piece = 0;
160 
161 	// we print two blocks at a time, so calculate the color in pair
162 #ifndef _WIN32
163 	int color[2];
164 	int last_color[2] = { -1, -1};
165 #endif
166 
167 	for (int i = 0; i < width; ++i, piece += piece_per_char)
168 	{
169 		int num_pieces = 0;
170 		int num_have = 0;
171 		int end = (std::max)(int(piece + piece_per_char), int(piece) + 1);
172 		for (int k = int(piece); k < end; ++k, ++num_pieces)
173 			if (p[k]) ++num_have;
174 		int const c = int(std::ceil(num_have / float((std::max)(num_pieces, 1)) * (table_size - 1)));
175 
176 #ifndef _WIN32
177 		color[i & 1] = c;
178 
179 		if ((i & 1) == 1)
180 		{
181 			// now, print color[0] and [1]
182 			// bg determines whether we're settings foreground or background color
183 			static int const bg[] = { 38, 48};
184 			for (int k = 0; k < 2; ++k)
185 			{
186 				if (color[k] != last_color[k])
187 				{
188 					char buf[40];
189 					std::snprintf(buf, sizeof(buf), "\x1b[%d;5;%dm", bg[k & 1], 232 + color[k]);
190 					last_color[k] = color[k];
191 					bar += buf;
192 				}
193 			}
194 			bar += "\u258C";
195 		}
196 #else
197 		static char const table[] = {' ', '\xb0', '\xb1', '\xb2', '\xdb'};
198 		bar += table[c];
199 #endif
200 	}
201 	bar += esc("0");
202 	bar += "]";
203 	return bar;
204 }
205 
206 #ifndef _WIN32
207 // this function uses the block characters that splits up the glyph in 4
208 // segments and provide all combinations of a segment lit or not. This allows us
209 // to print 4 pieces per character.
piece_matrix(lt::bitfield const & p,int width,int * height)210 std::string piece_matrix(lt::bitfield const& p, int width, int* height)
211 {
212 	// print two rows of pieces at a time
213 	int piece = 0;
214 	++*height;
215 	std::string ret;
216 	ret.reserve((p.size() + width * 2 - 1) / width / 2 * 4);
217 	while (piece < p.size())
218 	{
219 		for (int i = 0; i < width; ++i)
220 		{
221 			// each character has 4 pieces. store them in a byte to use for lookups
222 			int const c = get_piece(p, piece)
223 				| (get_piece(p, piece+1) << 1)
224 				| (get_piece(p, width*2+piece) << 2)
225 				| (get_piece(p, width*2+piece+1) << 3);
226 
227 			// we have 4 bits, 16 different combinations
228 			static char const* const chars[] =
229 			{
230 				" ",      // no bit is set             0000
231 				"\u2598", // upper left                0001
232 				"\u259d", // upper right               0010
233 				"\u2580", // both top bits             0011
234 				"\u2596", // lower left                0100
235 				"\u258c", // both left bits            0101
236 				"\u259e", // upper right, lower left   0110
237 				"\u259b", // left and upper sides      0111
238 				"\u2597", // lower right               1000
239 				"\u259a", // lower right, upper left   1001
240 				"\u2590", // right side                1010
241 				"\u259c", // lower right, top side     1011
242 				"\u2584", // both lower bits           1100
243 				"\u2599", // both lower, top left      1101
244 				"\u259f", // both lower, top right     1110
245 				"\x1b[7m \x1b[27m" // all bits are set (full block)
246 			};
247 
248 			ret += chars[c];
249 			piece += 2;
250 		}
251 		ret += "\x1b[K\n";
252 		++*height;
253 		piece += width * 2; // skip another row, as we've already printed it
254 	}
255 	return ret;
256 }
257 #else
258 // on MS-DOS terminals, we only have block characters for upper half and lower
259 // half. This lets us print two pieces per character.
piece_matrix(lt::bitfield const & p,int width,int * height)260 std::string piece_matrix(lt::bitfield const& p, int width, int* height)
261 {
262 	// print two rows of pieces at a time
263 	int piece = 0;
264 	++*height;
265 	std::string ret;
266 	ret.reserve((p.size() + width * 2 - 1) / width);
267 	while (piece < p.size())
268 	{
269 		for (int i = 0; i < width; ++i)
270 		{
271 			// each character has 8 pieces. store them in a byte to use for lookups
272 			// the ordering of these bits
273 			int const c = get_piece(p, piece)
274 				| (get_piece(p, width*2+piece) << 1);
275 
276 			static char const* const chars[] =
277 			{
278 				" ",    // no piece     00
279 				"\xdf", // top piece    01
280 				"\xdc", // bottom piece 10
281 				"\xdb"  // both pieces  11
282 			};
283 
284 			ret += chars[c];
285 			++piece;
286 		}
287 		ret += '\n';
288 		++*height;
289 		piece += width * 2; // skip another row, as we've already printed it
290 	}
291 	return ret;
292 }
293 #endif
294 
set_cursor_pos(int x,int y)295 void set_cursor_pos(int x, int y)
296 {
297 #ifdef _WIN32
298 	HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
299 	COORD c = {SHORT(x), SHORT(y)};
300 	SetConsoleCursorPosition(out, c);
301 #else
302 	std::printf("\033[%d;%dH", y + 1, x + 1);
303 #endif
304 }
305 
clear_screen()306 void clear_screen()
307 {
308 #ifdef _WIN32
309 	HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
310 
311 	COORD c = {0, 0};
312 	CONSOLE_SCREEN_BUFFER_INFO si;
313 	GetConsoleScreenBufferInfo(out, &si);
314 	DWORD n;
315 	FillConsoleOutputCharacter(out, ' ', si.dwSize.X * si.dwSize.Y, c, &n);
316 	FillConsoleOutputAttribute(out, 0x7, si.dwSize.X * si.dwSize.Y, c, &n);
317 #else
318 	std::printf("\033[2J");
319 #endif
320 }
321 
clear_rows(int y1,int y2)322 void clear_rows(int y1, int y2)
323 {
324 	if (y1 > y2) return;
325 
326 #ifdef _WIN32
327 	HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
328 
329 	COORD c = {0, SHORT(y1)};
330 	SetConsoleCursorPosition(out, c);
331 	CONSOLE_SCREEN_BUFFER_INFO si;
332 	GetConsoleScreenBufferInfo(out, &si);
333 	DWORD n;
334 	int num_chars = si.dwSize.X * (std::min)(si.dwSize.Y - y1, y2 - y1);
335 	FillConsoleOutputCharacter(out, ' ', num_chars, c, &n);
336 	FillConsoleOutputAttribute(out, 0x7, num_chars, c, &n);
337 #else
338 	for (int i = y1; i < y2; ++i)
339 		std::printf("\033[%d;1H\033[2K", i + 1);
340 #endif
341 }
342 
terminal_size()343 std::pair<int, int> terminal_size()
344 {
345 	int width = 80;
346 	int height = 50;
347 #ifdef _WIN32
348 	HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
349 	CONSOLE_SCREEN_BUFFER_INFO coninfo;
350 	if (GetConsoleScreenBufferInfo(out, &coninfo))
351 	{
352 		width = coninfo.dwSize.X;
353 		height = coninfo.srWindow.Bottom - coninfo.srWindow.Top;
354 #else
355 	int tty = open("/dev/tty", O_RDONLY);
356 	if (tty < 0)
357 	{
358 		width = 190;
359 		height = 100;
360 		return {width, height};
361 	}
362 	winsize size;
363 	int ret = ioctl(tty, TIOCGWINSZ, reinterpret_cast<char*>(&size));
364 	close(tty);
365 	if (ret == 0)
366 	{
367 		width = size.ws_col;
368 		height = size.ws_row;
369 #endif
370 
371 		if (width < 64)
372 			width = 64;
373 		if (height < 25)
374 			height = 25;
375 	}
376 	else
377 	{
378 		width = 190;
379 		height = 100;
380 	}
381 	return {width, height};
382 }
383 
384 #ifdef _WIN32
385 void apply_ansi_code(WORD* attributes, bool* reverse, bool* support_chaining, int code)
386 {
387 	static const WORD color_table[8] =
388 	{
389 		0, // black
390 		FOREGROUND_RED, // red
391 		FOREGROUND_GREEN, // green
392 		FOREGROUND_RED | FOREGROUND_GREEN, // yellow
393 		FOREGROUND_BLUE, // blue
394 		FOREGROUND_RED | FOREGROUND_BLUE, // magenta
395 		FOREGROUND_BLUE | FOREGROUND_GREEN, // cyan
396 		FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE // white
397 	};
398 
399 	enum
400 	{
401 		foreground_mask = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
402 		background_mask = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY
403 	};
404 
405 	static const int fg_mask[2] = {foreground_mask, background_mask};
406 	static const int bg_mask[2] = {background_mask, foreground_mask};
407 	static const int fg_shift[2] = { 0, 4};
408 	static const int bg_shift[2] = { 4, 0};
409 
410 	// default foreground
411 	if (code == 39) code = 37;
412 
413 	// default background
414 	if (code == 49) code = 40;
415 
416 	if (code == 0)
417 	{
418 		// reset
419 		*attributes = color_table[7];
420 		*reverse = false;
421 		*support_chaining = true;
422 	}
423 	else if (code == 1)
424 	{
425 		// intensity
426 		*attributes |= *reverse ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
427 		*support_chaining = true;
428 	}
429 	else if (code == 7)
430 	{
431 		// reverse video
432 		*support_chaining = true;
433 		if (*reverse) return;
434 		*reverse = true;
435 		int fg_col = *attributes & foreground_mask;
436 		int bg_col = (*attributes & background_mask) >> 4;
437 		*attributes &= ~(foreground_mask + background_mask);
438 		*attributes |= fg_col << 4;
439 		*attributes |= bg_col;
440 	}
441 	else if (code >= 30 && code <= 37)
442 	{
443 		// foreground color
444 		*attributes &= ~fg_mask[*reverse];
445 		*attributes |= color_table[code - 30] << fg_shift[*reverse];
446 		*support_chaining = true;
447 	}
448 	else if (code >= 40 && code <= 47)
449 	{
450 		// background color
451 		*attributes &= ~bg_mask[*reverse];
452 		*attributes |= color_table[code - 40] << bg_shift[*reverse];
453 		*support_chaining = true;
454 	}
455 }
456 #endif
457 void print(char const* buf)
458 {
459 #ifdef _WIN32
460 	HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
461 
462 	WORD current_attributes = 7;
463 	bool reverse = false;
464 	SetConsoleTextAttribute(out, current_attributes);
465 
466 	char const* start = buf;
467 	DWORD written;
468 	while (*buf != 0)
469 	{
470 		if (*buf == '\033' && buf[1] == '[')
471 		{
472 			WriteFile(out, start, DWORD(buf - start), &written, nullptr);
473 			buf += 2; // skip escape and '['
474 			start = buf;
475 			if (*buf == 0) break;
476 			if (*start == 'K')
477 			{
478 				// this means clear the rest of the line.
479 				CONSOLE_SCREEN_BUFFER_INFO sbi;
480 				if (GetConsoleScreenBufferInfo(out, &sbi))
481 				{
482 					COORD const pos = sbi.dwCursorPosition;
483 					int const width = sbi.dwSize.X;
484 					int const run = width - pos.X;
485 					DWORD n;
486 					FillConsoleOutputAttribute(out, 0x7, run, pos, &n);
487 					FillConsoleOutputCharacter(out, ' ', run, pos, &n);
488 				}
489 				++buf;
490 				start = buf;
491 				continue;
492 			}
493 			else if (*start == 'J')
494 			{
495 				// clear rest of screen
496 				CONSOLE_SCREEN_BUFFER_INFO sbi;
497 				if (GetConsoleScreenBufferInfo(out, &sbi))
498 				{
499 					COORD pos = sbi.dwCursorPosition;
500 					int width = sbi.dwSize.X;
501 					int run = (width - pos.X) + width * (sbi.dwSize.Y - pos.Y - 1);
502 					DWORD n;
503 					FillConsoleOutputAttribute(out, 0x7, run, pos, &n);
504 					FillConsoleOutputCharacter(out, ' ', run, pos, &n);
505 				}
506 				++buf;
507 				start = buf;
508 				continue;
509 			}
510 one_more:
511 			while (*buf != 'm' && *buf != ';' && *buf != 0) ++buf;
512 
513 			// this is where we handle reset, color and reverse codes
514 			if (*buf == 0) break;
515 			int code = atoi(start);
516 			bool support_chaining = false;
517 			apply_ansi_code(&current_attributes, &reverse, &support_chaining, code);
518 			if (support_chaining)
519 			{
520 				if (*buf == ';')
521 				{
522 					++buf;
523 					start = buf;
524 					goto one_more;
525 				}
526 			}
527 			else
528 			{
529 				// ignore codes with multiple fields for now
530 				while (*buf != 'm' && *buf != 0) ++buf;
531 			}
532 			SetConsoleTextAttribute(out, current_attributes);
533 			++buf; // skip 'm'
534 			start = buf;
535 		}
536 		else
537 		{
538 			++buf;
539 		}
540 	}
541 	WriteFile(out, start, DWORD(buf - start), &written, nullptr);
542 
543 #else
544 	fputs(buf, stdout);
545 #endif
546 }
547