1 /* radare - LGPL - Copyright 2008-2018 - pancake */
2
3 #include <r_cons.h>
4 #include <ctype.h>
5
6 #define I(x) r_cons_singleton ()->x
7
8 // Display the content of a file in the hud
r_cons_hud_file(const char * f)9 R_API char *r_cons_hud_file(const char *f) {
10 char *s = r_file_slurp (f, NULL);
11 if (s) {
12 char *ret = r_cons_hud_string (s);
13 free (s);
14 return ret;
15 }
16 return NULL;
17 }
18
19 // Display a buffer in the hud (splitting it line-by-line and ignoring
20 // the lines starting with # )
r_cons_hud_string(const char * s)21 R_API char *r_cons_hud_string(const char *s) {
22 if (!r_cons_is_interactive ()) {
23 eprintf ("Hud mode requires scr.interactive=true.\n");
24 return NULL;
25 }
26 char *os, *track, *ret, *o = strdup (s);
27 if (!o) {
28 return NULL;
29 }
30 r_str_replace_ch (o, '\r', 0, true);
31 r_str_replace_ch (o, '\t', 0, true);
32 RList *fl = r_list_new ();
33 int i;
34 if (!fl) {
35 free (o);
36 return NULL;
37 }
38 fl->free = free;
39 for (os = o, i = 0; o[i]; i++) {
40 if (o[i] == '\n') {
41 o[i] = 0;
42 if (*os && *os != '#') {
43 track = strdup (os);
44 if (!r_list_append (fl, track)) {
45 free (track);
46 break;
47 }
48 }
49 os = o + i + 1;
50 }
51 }
52 ret = r_cons_hud (fl, NULL);
53 free (o);
54 r_list_free (fl);
55 return ret;
56 }
57
58 /* Match a filter on a line. A filter can contain multiple words
59 separated by spaces, which are all matched *in any order* over the target
60 entry. If all words are present, the function returns true.
61 The mask is a character buffer which is filled by 'x' to mark those characters
62 that match the filter */
__matchString(char * entry,char * filter,char * mask,const int mask_size)63 static bool __matchString(char *entry, char *filter, char *mask, const int mask_size) {
64 char *p, *current_token = filter;
65 const char *filter_end = filter + strlen (filter);
66 char *ansi_filtered = strdup (entry);
67 int *cps;
68 r_str_ansi_filter (ansi_filtered, NULL, &cps, -1);
69 entry = ansi_filtered;
70 // first we separate the filter in words (include the terminator char
71 // to avoid special handling of the last token)
72 for (p = filter; p <= filter_end; p++) {
73 if (*p == ' ' || *p == '\0') {
74 const char *next_match, *entry_ptr = entry;
75 char old_char = *p;
76 int token_len;
77
78 // Ignoring consecutive spaces
79 if (p == current_token) {
80 current_token++;
81 continue;
82 }
83 *p = 0;
84 token_len = strlen (current_token);
85 // look for all matches of the current_token in this entry
86 while ((next_match = r_str_casestr (entry_ptr, current_token))) {
87 int real_pos, filtered_pos = next_match - entry;
88 int end_pos = cps[filtered_pos + token_len];
89 for (real_pos = cps[filtered_pos];
90 real_pos < end_pos && real_pos < mask_size;
91 real_pos = cps[++filtered_pos]) {
92 mask[real_pos] = 'x';
93 }
94 entry_ptr += token_len;
95 }
96 *p = old_char;
97 if (entry_ptr == entry) {
98 // the word is not present in the target
99 free (cps);
100 free (ansi_filtered);
101 return false;
102 }
103 current_token = p + 1;
104 }
105 }
106 free (cps);
107 free (ansi_filtered);
108 return true;
109 }
110
111
hud_filter(RList * list,char * user_input,int top_entry_n,int * current_entry_n,char ** selected_entry)112 static RList *hud_filter(RList *list, char *user_input, int top_entry_n, int *current_entry_n, char **selected_entry) {
113 RListIter *iter;
114 char *current_entry;
115 char mask[HUD_BUF_SIZE];
116 char *p, *x;
117 int j, rows;
118 (void) r_cons_get_size (&rows);
119 int counter = 0;
120 bool first_line = true;
121 RList *res = r_list_newf (free);
122 r_list_foreach (list, iter, current_entry) {
123 memset (mask, 0, HUD_BUF_SIZE);
124 if (*user_input && !__matchString (current_entry, user_input, mask, HUD_BUF_SIZE)) {
125 continue;
126 }
127 if (++counter == rows + top_entry_n) {
128 break;
129 }
130 // if the user scrolled down the list, do not print the first entries
131 if (!top_entry_n || *current_entry_n >= top_entry_n) {
132 // remove everything after a tab (in ??, it contains the commands)
133 x = strchr (current_entry, '\t');
134 if (x) {
135 *x = 0;
136 }
137 p = strdup (current_entry);
138 // if the filter is empty, print the entry and move on
139 if (!user_input[0]) {
140 r_list_append (res, r_str_newf (" %c %s", first_line? '-': ' ', p));
141 } else {
142 // otherwise we need to emphasize the matching part
143 if (I (context->color_mode)) {
144 int last_color_change = 0;
145 int last_mask = 0;
146 char *str = r_str_newf (" %c ", first_line? '-': ' ');
147 // Instead of printing one char at the time
148 // (which would be slow), we group substrings of the same color
149 for (j = 0; p[j] && j < HUD_BUF_SIZE; j++) {
150 if (mask[j] != last_mask) {
151 char tmp = p[j];
152 p[j] = 0;
153 if (mask[j]) {
154 str = r_str_appendf (str, Color_RESET "%s", p + last_color_change);
155 } else {
156 str = r_str_appendf (str, Color_GREEN "%s", p + last_color_change);
157 }
158 p[j] = tmp;
159 last_color_change = j;
160 last_mask = mask[j];
161 }
162 }
163 if (last_mask) {
164 str = r_str_appendf (str, Color_GREEN "%s"Color_RESET, p + last_color_change);
165 } else {
166 str = r_str_appendf (str, Color_RESET "%s", p + last_color_change);
167 }
168 r_list_append (res, str);
169 } else {
170 // Otherwise we print the matching characters uppercase
171 for (j = 0; p[j]; j++) {
172 if (mask[j]) {
173 p[j] = toupper ((unsigned char) p[j]);
174 }
175 }
176 r_list_append (res, r_str_newf (" %c %s", first_line? '-': ' ', p));
177 }
178 }
179 // Clean up and restore the tab character (if any)
180 free (p);
181 if (x) {
182 *x = '\t';
183 }
184 if (first_line) {
185 *selected_entry = current_entry;
186 }
187 first_line = false;
188 }
189 (*current_entry_n)++;
190
191 }
192 return res;
193 }
194
mht_free_kv(HtPPKv * kv)195 static void mht_free_kv(HtPPKv *kv) {
196 free (kv->key);
197 r_list_free (kv->value);
198 }
199
200 // Display a list of entries in the hud, filtered and emphasized based on the user input.
201
202 #define HUD_CACHE 0
r_cons_hud(RList * list,const char * prompt)203 R_API char *r_cons_hud(RList *list, const char *prompt) {
204 char user_input[HUD_BUF_SIZE + 1];
205 char *selected_entry = NULL;
206 RListIter *iter;
207
208 HtPP *ht = ht_pp_new (NULL, (HtPPKvFreeFunc)mht_free_kv, (HtPPCalcSizeV)strlen);
209 RLineHud *hud = (RLineHud*) R_NEW (RLineHud);
210 hud->activate = 0;
211 hud->vi = 0;
212 I(line)->echo = false;
213 I(line)->hud = hud;
214 user_input [0] = 0;
215 user_input[HUD_BUF_SIZE] = 0;
216 hud->top_entry_n = 0;
217 r_cons_show_cursor (false);
218 r_cons_enable_mouse (false);
219 r_cons_clear ();
220
221 // Repeat until the user exits the hud
222 for (;;) {
223 r_cons_gotoxy (0, 0);
224 hud->current_entry_n = 0;
225
226 if (hud->top_entry_n < 0) {
227 hud->top_entry_n = 0;
228 }
229 selected_entry = NULL;
230 if (prompt && *prompt) {
231 r_cons_printf (">> %s\n", prompt);
232 }
233 r_cons_printf ("%d> %s|\n", hud->top_entry_n, user_input);
234 char *row;
235 RList *filtered_list = NULL;
236
237 bool found = false;
238 filtered_list = ht_pp_find (ht, user_input, &found);
239 if (!found) {
240 filtered_list = hud_filter (list, user_input,
241 hud->top_entry_n, &(hud->current_entry_n), &selected_entry);
242 #if HUD_CACHE
243 ht_pp_insert (ht, user_input, filtered_list);
244 #endif
245 }
246 r_list_foreach (filtered_list, iter, row) {
247 r_cons_printf ("%s\n", row);
248 }
249 if (!filtered_list->length) { // hack to remove garbage value when list is empty
250 printf ("%s", R_CONS_CLEAR_LINE);
251 }
252 #if !HUD_CACHE
253 r_list_free (filtered_list);
254 #endif
255 r_cons_visual_flush ();
256 (void) r_line_readline ();
257 r_str_ncpy (user_input, I(line)->buffer.data, HUD_BUF_SIZE);
258
259 if (!hud->activate) {
260 hud->top_entry_n = 0;
261 if (hud->current_entry_n >= 1 ) {
262 if (selected_entry) {
263 R_FREE (I(line)->hud);
264 I(line)->echo = true;
265 r_cons_enable_mouse (false);
266 r_cons_show_cursor (true);
267 r_cons_set_raw (false);
268 return strdup (selected_entry);
269 }
270 } else {
271 goto _beach;
272 }
273 }
274 }
275 _beach:
276 R_FREE (I(line)->hud);
277 I(line)->echo = true;
278 r_cons_show_cursor (true);
279 r_cons_enable_mouse (false);
280 r_cons_set_raw (false);
281 ht_pp_free (ht);
282 return NULL;
283 }
284
285 // Display the list of files in a directory
r_cons_hud_path(const char * path,int dir)286 R_API char *r_cons_hud_path(const char *path, int dir) {
287 char *tmp, *ret = NULL;
288 RList *files;
289 if (path) {
290 path = r_str_trim_head_ro (path);
291 tmp = strdup (*path ? path : "./");
292 } else {
293 tmp = strdup ("./");
294 }
295 files = r_sys_dir (tmp);
296 if (files) {
297 ret = r_cons_hud (files, tmp);
298 if (ret) {
299 tmp = r_str_append (tmp, "/");
300 tmp = r_str_append (tmp, ret);
301 free (ret);
302 ret = r_file_abspath (tmp);
303 free (tmp);
304 tmp = ret;
305 if (r_file_is_directory (tmp)) {
306 ret = r_cons_hud_path (tmp, dir);
307 free (tmp);
308 tmp = ret;
309 }
310 }
311 r_list_free (files);
312 } else {
313 eprintf ("No files found\n");
314 }
315 if (!ret) {
316 free (tmp);
317 return NULL;
318 }
319 return tmp;
320 }
321
r_cons_message(const char * msg)322 R_API char *r_cons_message(const char *msg) {
323 int len = strlen (msg);
324 int rows, cols = r_cons_get_size (&rows);
325 r_cons_clear ();
326 r_cons_gotoxy ((cols - len) / 2, rows / 2);
327 r_cons_println (msg);
328 r_cons_flush ();
329 r_cons_gotoxy (0, rows - 2);
330 r_cons_any_key (NULL);
331 return NULL;
332 }
333