1 /* radare - LGPL - Copyright 2009-2019 - pancake */
2 
3 #include <r_cons.h>
4 #include <r_util/r_assert.h>
5 #define I r_cons_singleton ()
6 
7 #if __WINDOWS__
__fill_tail(int cols,int lines)8 static void __fill_tail(int cols, int lines) {
9 	lines++;
10 	if (lines > 0) {
11 		char white[1024];
12 		cols = R_MIN (cols, sizeof (white));
13 		memset (white, ' ', cols - 1);
14 		lines--;
15 		white[cols] = '\n';
16 		while (lines-- > 0) {
17 			write (1, white, cols);
18 		}
19 	}
20 }
21 
r_cons_w32_clear(void)22 R_API void r_cons_w32_clear(void) {
23 	static HANDLE hStdout = NULL;
24 	static CONSOLE_SCREEN_BUFFER_INFO csbi;
25 	COORD startCoords;
26 	DWORD dummy;
27 	if (I->vtmode) {
28 		r_cons_strcat (Color_RESET R_CONS_CLEAR_SCREEN);
29 		return;
30 	}
31 	if (I->is_wine == 1) {
32 		write (1, "\033[0;0H\033[0m\033[2J", 6 + 4 + 4);
33 	}
34 	if (!hStdout) {
35 		hStdout = GetStdHandle (STD_OUTPUT_HANDLE);
36 	}
37 	GetConsoleScreenBufferInfo (hStdout, &csbi);
38 	startCoords = (COORD) {
39 		csbi.srWindow.Left,
40 		csbi.srWindow.Top
41 	};
42 	DWORD nLength = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
43 	FillConsoleOutputCharacter (hStdout, ' ',
44 		nLength, startCoords, &dummy);
45 	FillConsoleOutputAttribute (hStdout,
46 		FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
47 		nLength, startCoords, &dummy);
48 }
49 
r_cons_w32_gotoxy(int fd,int x,int y)50 R_API void r_cons_w32_gotoxy(int fd, int x, int y) {
51 	static HANDLE hStdout = NULL;
52 	static HANDLE hStderr = NULL;
53 	HANDLE *hConsole = fd == 1 ? &hStdout : &hStderr;
54 	COORD coord;
55 	coord.X = x;
56 	coord.Y = y;
57 	if (I->vtmode) {
58 		r_cons_printf ("\x1b[%d;%dH", y, x);
59 		return;
60 	}
61 	if (I->is_wine == 1) {
62 		write (fd, "\x1b[0;0H", 6);
63 	}
64 	if (!*hConsole) {
65 		*hConsole = GetStdHandle (fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
66 	}
67 	CONSOLE_SCREEN_BUFFER_INFO info;
68 	GetConsoleScreenBufferInfo (*hConsole, &info);
69 	coord.X += info.srWindow.Left;
70 	coord.Y += info.srWindow.Top;
71 	SetConsoleCursorPosition (*hConsole, coord);
72 }
73 
wrapline(const char * s,int len)74 static int wrapline(const char *s, int len) {
75 	int l = 0, n = 0;
76 	for (; n < len; ) {
77 		l = r_str_len_utf8char (s+n, (len-n));
78 		n += l;
79 	}
80 	return n - ((n > len) ? l : 1);
81 }
82 
83 // Dupe from canvas.c
utf8len_fixed(const char * s,int n)84 static int utf8len_fixed(const char *s, int n) {
85 	int i = 0, j = 0, fullwidths = 0;
86 	while (s[i] && n > 0) {
87 		if ((s[i] & 0xc0) != 0x80) {
88 			j++;
89 			if (r_str_char_fullwidth (s + i, n)) {
90 				fullwidths++;
91 			}
92 		}
93 		n--;
94 		i++;
95 	}
96 	return j + fullwidths;
97 }
98 
bytes_utf8len(const char * s,int n)99 static int bytes_utf8len(const char *s, int n) {
100 	int ret = 0;
101 	while (n > 0) {
102 		int sz = r_str_utf8_charsize (s);
103 		ret += sz;
104 		s += sz;
105 		n--;
106 	}
107 	return ret;
108 }
109 
r_cons_w32_hprint(DWORD hdl,const char * ptr,int len,bool vmode)110 static int r_cons_w32_hprint(DWORD hdl, const char *ptr, int len, bool vmode) {
111 	HANDLE hConsole = GetStdHandle (hdl);
112 	int fd = hdl == STD_OUTPUT_HANDLE ? 1 : 2;
113 	int esc = 0;
114 	int bg = 0, fg = 1|2|4|8;
115 	const char *ptr_end, *str = ptr;
116 	int ret = 0;
117 	int inv = 0;
118 	int linelen = 0;
119 	int ll = 0;
120 	int raw_ll = 0;
121 	int lines, cols = r_cons_get_size (&lines);
122 	if (I->is_wine == -1) {
123 		I->is_wine = r_file_is_directory ("/proc")? 1: 0;
124 	}
125 	if (len < 0) {
126 		len = strlen ((const char *)ptr);
127 	}
128 	ptr_end = ptr + len;
129 	if (ptr && hConsole)
130 	for (; *ptr && ptr < ptr_end; ptr++) {
131 		if (ptr[0] == 0xa) {
132 			raw_ll = (size_t)(ptr - str);
133 			ll = utf8len_fixed (str, raw_ll);
134 			lines--;
135 			if (vmode && lines < 1) {
136 				break;
137 			}
138 			if (raw_ll < 1) {
139 				continue;
140 			}
141 			if (vmode) {
142 				/* only chop columns if necessary */
143 				if (ll + linelen >= cols) {
144 					// chop line if too long
145 					ll = (cols - linelen) - 1;
146 					if (ll < 0) {
147 						continue;
148 					}
149 				}
150 			}
151 			if (ll > 0) {
152 				raw_ll = bytes_utf8len (str, ll);
153 				write (fd, str, raw_ll);
154 				linelen += ll;
155 			}
156 			esc = 0;
157 			str = ptr + 1;
158 			if (vmode) {
159 				int wlen = cols - linelen;
160 				char white[1024];
161 				if (wlen > 0 && wlen < sizeof (white)) {
162 					memset (white, ' ', sizeof (white));
163 					write (fd, white, wlen-1);
164 				}
165 			}
166 			write (fd, "\n\r", 2);
167 			// reset colors for next line
168 			SetConsoleTextAttribute (hConsole, 1 | 2 | 4 | 8);
169 			linelen = 0;
170 			continue;
171 		}
172 		if (ptr[0] == 0x1b) {
173 			raw_ll = (size_t)(ptr - str);
174 			ll = utf8len_fixed (str, raw_ll);
175 			if (str[0] == '\n') {
176 				str++;
177 				ll--;
178 				if (vmode) {
179 					int wlen = cols - linelen - 1;
180 					char white[1024];
181 					//wlen = 5;
182 					if (wlen > 0) {
183 						memset (white, ' ', sizeof (white));
184 						write (fd, white, wlen);
185 					}
186 				}
187 				write (fd, "\n\r", 2);
188 				//write (fd, "\r\n", 2);
189 				//lines--;
190 				linelen = 0;
191 			}
192 			if (vmode) {
193 				if (linelen + ll >= cols) {
194 					// chop line if too long
195 					ll = (cols - linelen) - 1;
196 					if (ll > 0) {
197 						// fix utf8 len here
198 						ll = wrapline ((const char*)str, cols - linelen - 1);
199 					}
200 				}
201 			}
202 			if (ll > 0) {
203 				raw_ll = bytes_utf8len (str, ll);
204 				write (fd, str, raw_ll);
205 				linelen += ll;
206 			}
207 			esc = 1;
208 			str = ptr + 1;
209 			continue;
210 		}
211 		if (esc == 1) {
212 			// \x1b[2J
213 			if (ptr[0] != '[') {
214 				eprintf ("Oops invalid escape char\n");
215 				esc = 0;
216 				str = ptr + 1;
217 				continue;
218 			}
219 			esc = 2;
220 			continue;
221 		} else if (esc == 2) {
222 			const char *ptr2 = NULL;
223 			int x, y, i, state = 0;
224 			for (i = 0; ptr[i] && state >= 0; i++) {
225 				switch (state) {
226 				case 0:
227 					if (ptr[i] == ';') {
228 						y = atoi ((const char *)ptr);
229 						state = 1;
230 						ptr2 = (const char *)ptr+i+1;
231 					} else if (ptr[i] >='0' && ptr[i]<='9') {
232 						// ok
233 					} else {
234 						state = -1; // END FAIL
235 					}
236 					break;
237 				case 1:
238 					if (ptr[i]=='H') {
239 						x = atoi (ptr2);
240 						state = -2; // END OK
241 					} else if (ptr[i] >='0' && ptr[i]<='9') {
242 						// ok
243 					} else {
244 						state = -1; // END FAIL
245 					}
246 					break;
247 				}
248 			}
249 			if (state == -2) {
250 				r_cons_w32_gotoxy (fd, x, y);
251 				ptr += i;
252 				str = ptr; // + i-2;
253 				continue;
254 			}
255 			bool bright = false;
256 			if (ptr[0]=='0' && ptr[1] == ';' && ptr[2]=='0') {
257 				// \x1b[0;0H
258 				/** clear screen if gotoxy **/
259 				if (vmode) {
260 					// fill row here
261 					__fill_tail (cols, lines);
262 				}
263 				r_cons_w32_gotoxy (fd, 0, 0);
264 				lines = 0;
265 				esc = 0;
266 				ptr += 3;
267 				str = ptr + 1;
268 				continue;
269 			} else if (ptr[0]=='2'&&ptr[1]=='J') {
270 				r_cons_w32_clear ();
271 				esc = 0;
272 				ptr = ptr + 1;
273 				str = ptr + 1;
274 				continue;
275 			} else if (ptr[0]=='0'&&(ptr[1]=='m' || ptr [1]=='K')) {
276 				SetConsoleTextAttribute (hConsole, 1|2|4|8);
277 				fg = 1|2|4|8;
278 				bg = 0;
279 				inv = 0;
280 				esc = 0;
281 				ptr++;
282 				str = ptr + 1;
283 				continue;
284 				// reset color
285 			} else if (ptr[0]=='2'&&ptr[1]=='7'&&ptr[2]=='m') {
286 				SetConsoleTextAttribute (hConsole, bg|fg);
287 				inv = 0;
288 				esc = 0;
289 				ptr = ptr + 2;
290 				str = ptr + 1;
291 				continue;
292 				// invert off
293 			} else if (ptr[0]=='7'&&ptr[1]=='m') {
294 				SetConsoleTextAttribute (hConsole, bg|fg|COMMON_LVB_REVERSE_VIDEO);
295 				inv = COMMON_LVB_REVERSE_VIDEO;
296 				esc = 0;
297 				ptr = ptr + 1;
298 				str = ptr + 1;
299 				continue;
300 				// invert
301 			} else if ((ptr[0] == '3' || (bright = ptr[0] == '9')) && (ptr[2] == 'm' || ptr[2] == ';')) {
302 				switch (ptr[1]) {
303 				case '0': // BLACK
304 					fg = 0;
305 					break;
306 				case '1': // RED
307 					fg = 4;
308 					break;
309 				case '2': // GREEN
310 					fg = 2;
311 					break;
312 				case '3': // YELLOW
313 					fg = 2|4;
314 					break;
315 				case '4': // BLUE
316 					fg = 1;
317 					break;
318 				case '5': // MAGENTA
319 					fg = 1|4;
320 					break;
321 				case '6': // CYAN
322 					fg = 1|2;
323 					break;
324 				case '7': // WHITE
325 					fg = 1|2|4;
326 					break;
327 				case '8': // ???
328 				case '9':
329 					break;
330 				}
331 				if (bright) {
332 					fg |= 8;
333 				}
334 				SetConsoleTextAttribute (hConsole, bg|fg|inv);
335 				esc = 0;
336 				ptr = ptr + 2;
337 				str = ptr + 1;
338 				continue;
339 			} else if ((ptr[0] == '4' && ptr[2] == 'm')
340 			           || (bright = ptr[0] == '1' && ptr[1] == '0' && ptr[3] == 'm')) {
341 				/* background color */
342 				ut8 col = bright ? ptr[2] : ptr[1];
343 				switch (col) {
344 				case '0': // BLACK
345 					bg = 0x0;
346 					break;
347 				case '1': // RED
348 					bg = 0x40;
349 					break;
350 				case '2': // GREEN
351 					bg = 0x20;
352 					break;
353 				case '3': // YELLOW
354 					bg = 0x20|0x40;
355 					break;
356 				case '4': // BLUE
357 					bg = 0x10;
358 					break;
359 				case '5': // MAGENTA
360 					bg = 0x10|0x40;
361 					break;
362 				case '6': // CYAN
363 					bg = 0x10|0x20;
364 					break;
365 				case '7': // WHITE
366 					bg = 0x10|0x20|0x40;
367 					break;
368 				case '8': // ???
369 				case '9':
370 					break;
371 				}
372 				if (bright) {
373 					bg |= 0x80;
374 				}
375 				SetConsoleTextAttribute (hConsole, bg|fg|inv);
376 				esc = 0;
377 				ptr = ptr + (bright ? 3 : 2);
378 				str = ptr + 1;
379 				continue;
380 			}
381 		}
382 		ret++;
383 	}
384 	if (vmode) {
385 		/* fill partial line */
386 		int wlen = cols - linelen - 1;
387 		if (wlen > 0) {
388 			char white[1024];
389 			memset (white, ' ', sizeof (white));
390 			write (fd, white, wlen);
391 		}
392 		/* fill tail */
393 		__fill_tail (cols, lines);
394 	} else {
395 		int ll = (size_t)(ptr - str);
396 		if (ll > 0) {
397 			write (fd, str, ll);
398 			linelen += ll;
399 		}
400 	}
401 	return ret;
402 }
403 
r_cons_w32_print(const char * ptr,int len,bool vmode)404 R_API int r_cons_w32_print(const char *ptr, int len, bool vmode) {
405 	return r_cons_w32_hprint (STD_OUTPUT_HANDLE, ptr, len, vmode);
406 }
407 
r_cons_win_vhprintf(DWORD hdl,bool vmode,const char * fmt,va_list ap)408 R_API int r_cons_win_vhprintf(DWORD hdl, bool vmode, const char *fmt, va_list ap) {
409 	va_list ap2;
410 	int ret = -1;
411 	FILE *con = hdl == STD_OUTPUT_HANDLE ? stdout : stderr;
412 	if (!strchr (fmt, '%')) {
413 		size_t len = strlen (fmt);
414 		if (I->vtmode) {
415 			return fwrite (fmt, 1, len, con);
416 		}
417 		return r_cons_w32_hprint (hdl, fmt, len, vmode);
418 	}
419 	va_copy (ap2, ap);
420 	int num_chars = vsnprintf (NULL, 0, fmt, ap2);
421 	num_chars++;
422 	char *buf = calloc (1, num_chars);
423 	if (buf) {
424 		(void)vsnprintf (buf, num_chars, fmt, ap);
425 		if (I->vtmode) {
426 			ret = fwrite (buf, 1, num_chars - 1, con);
427 		} else {
428 			ret = r_cons_w32_hprint (hdl, buf, num_chars - 1, vmode);
429 		}
430 		free (buf);
431 	}
432 	va_end (ap2);
433 	return ret;
434 }
435 
r_cons_win_printf(bool vmode,const char * fmt,...)436 R_API int r_cons_win_printf(bool vmode, const char *fmt, ...) {
437 	va_list ap;
438 	int ret;
439 	r_return_val_if_fail (fmt, -1);
440 
441 	va_start (ap, fmt);
442 	ret = r_cons_win_vhprintf (STD_OUTPUT_HANDLE, vmode, fmt, ap);
443 	va_end (ap);
444 	return ret;
445 }
446 
r_cons_win_eprintf(bool vmode,const char * fmt,...)447 R_API int r_cons_win_eprintf(bool vmode, const char *fmt, ...) {
448 	va_list ap;
449 	int ret;
450 	r_return_val_if_fail (fmt, -1);
451 
452 	va_start (ap, fmt);
453 	ret = r_cons_win_vhprintf (STD_ERROR_HANDLE, vmode, fmt, ap);
454 	va_end (ap);
455 	return ret;
456 }
457 #endif
458