Rizin
unix-like reverse engineering framework and cli tools
hud.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2008-2018 pancake <pancake@nopcode.org>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include <rz_cons.h>
5 #include <ctype.h>
6 
7 #define I(x) rz_cons_singleton()->x
8 
9 // Display the content of a file in the hud
10 RZ_API char *rz_cons_hud_file(const char *f) {
11  char *s = rz_file_slurp(f, NULL);
12  if (s) {
13  char *ret = rz_cons_hud_string(s);
14  free(s);
15  return ret;
16  }
17  return NULL;
18 }
19 
20 // Display a buffer in the hud (splitting it line-by-line and ignoring
21 // the lines starting with # )
22 RZ_API char *rz_cons_hud_string(const char *s) {
23  if (!rz_cons_is_interactive()) {
24  eprintf("Hud mode requires scr.interactive=true.\n");
25  return NULL;
26  }
27  char *os, *track, *ret, *o = strdup(s);
28  if (!o) {
29  return NULL;
30  }
31  rz_str_replace_ch(o, '\r', 0, true);
32  rz_str_replace_ch(o, '\t', 0, true);
33  RzList *fl = rz_list_new();
34  int i;
35  if (!fl) {
36  free(o);
37  return NULL;
38  }
39  fl->free = free;
40  for (os = o, i = 0; o[i]; i++) {
41  if (o[i] == '\n') {
42  o[i] = 0;
43  if (*os && *os != '#') {
44  track = strdup(os);
45  if (!rz_list_append(fl, track)) {
46  free(track);
47  break;
48  }
49  }
50  os = o + i + 1;
51  }
52  }
53  ret = rz_cons_hud(fl, NULL);
54  free(o);
55  rz_list_free(fl);
56  return ret;
57 }
58 
59 /* Match a filter on a line. A filter can contain multiple words
60  separated by spaces, which are all matched *in any order* over the target
61  entry. If all words are present, the function returns true.
62  The mask is a character buffer which is filled by 'x' to mark those characters
63  that match the filter */
64 static bool __matchString(char *entry, char *filter, char *mask, const int mask_size) {
65  char *p, *current_token = filter;
66  const char *filter_end = filter + strlen(filter);
67  char *ansi_filtered = strdup(entry);
68  int *cps;
69  rz_str_ansi_filter(ansi_filtered, NULL, &cps, -1);
70  entry = ansi_filtered;
71  // first we separate the filter in words (include the terminator char
72  // to avoid special handling of the last token)
73  for (p = filter; p <= filter_end; p++) {
74  if (*p == ' ' || *p == '\0') {
75  const char *next_match, *entry_ptr = entry;
76  char old_char = *p;
77  int token_len;
78 
79  // Ignoring consecutive spaces
80  if (p == current_token) {
81  current_token++;
82  continue;
83  }
84  *p = 0;
85  token_len = strlen(current_token);
86  // look for all matches of the current_token in this entry
87  while ((next_match = rz_str_casestr(entry_ptr, current_token))) {
88  int real_pos, filtered_pos = next_match - entry;
89  int end_pos = cps[filtered_pos + token_len];
90  for (real_pos = cps[filtered_pos];
91  real_pos < end_pos && real_pos < mask_size;
92  real_pos = cps[++filtered_pos]) {
93  mask[real_pos] = 'x';
94  }
95  entry_ptr += token_len;
96  }
97  *p = old_char;
98  if (entry_ptr == entry) {
99  // the word is not present in the target
100  free(cps);
101  free(ansi_filtered);
102  return false;
103  }
104  current_token = p + 1;
105  }
106  }
107  free(cps);
108  free(ansi_filtered);
109  return true;
110 }
111 
112 static RzList *hud_filter(RzList *list, char *user_input, int top_entry_n, int *current_entry_n, char **selected_entry) {
113  RzListIter *iter;
114  char *current_entry;
115  char mask[HUD_BUF_SIZE];
116  char *p, *x;
117  int j, rows;
118  (void)rz_cons_get_size(&rows);
119  int counter = 0;
120  bool first_line = true;
121  RzList *res = rz_list_newf(free);
122  rz_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  rz_list_append(res, rz_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 = rz_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 = rz_str_appendf(str, Color_RESET "%s", p + last_color_change);
155  } else {
156  str = rz_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 = rz_str_appendf(str, Color_GREEN "%s" Color_RESET, p + last_color_change);
165  } else {
166  str = rz_str_appendf(str, Color_RESET "%s", p + last_color_change);
167  }
168  rz_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  rz_list_append(res, rz_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  return res;
192 }
193 
194 static void mht_free_kv(HtPPKv *kv) {
195  free(kv->key);
196  rz_list_free(kv->value);
197 }
198 
199 // Display a list of entries in the hud, filtered and emphasized based on the user input.
200 
201 #define HUD_CACHE 0
202 RZ_API char *rz_cons_hud(RzList /*<char *>*/ *list, const char *prompt) {
203  char user_input[HUD_BUF_SIZE + 1];
204  char *selected_entry = NULL;
205  RzListIter *iter;
206 
207  HtPP *ht = ht_pp_new(NULL, (HtPPKvFreeFunc)mht_free_kv, (HtPPCalcSizeV)strlen);
208  RzLineHud *hud = (RzLineHud *)RZ_NEW(RzLineHud);
209  hud->activate = 0;
210  hud->vi = 0;
211  I(line)->echo = false;
212  I(line)->hud = hud;
213  user_input[0] = 0;
214  user_input[HUD_BUF_SIZE] = 0;
215  hud->top_entry_n = 0;
216  rz_cons_show_cursor(false);
217  rz_cons_enable_mouse(false);
218  rz_cons_clear();
219 
220  // Repeat until the user exits the hud
221  for (;;) {
222  rz_cons_gotoxy(0, 0);
223  hud->current_entry_n = 0;
224 
225  if (hud->top_entry_n < 0) {
226  hud->top_entry_n = 0;
227  }
228  selected_entry = NULL;
229  if (prompt && *prompt) {
230  rz_cons_printf(">> %s\n", prompt);
231  }
232  rz_cons_printf("%d> %s|\n", hud->top_entry_n, user_input);
233  char *row;
234  RzList *filtered_list = NULL;
235 
236  bool found = false;
237  filtered_list = ht_pp_find(ht, user_input, &found);
238  if (!found) {
239  filtered_list = hud_filter(list, user_input,
240  hud->top_entry_n, &(hud->current_entry_n), &selected_entry);
241 #if HUD_CACHE
242  ht_pp_insert(ht, user_input, filtered_list);
243 #endif
244  }
245  rz_list_foreach (filtered_list, iter, row) {
246  rz_cons_printf("%s\n", row);
247  }
248  if (!filtered_list->length) { // hack to remove garbage value when list is empty
249  printf("%s", RZ_CONS_CLEAR_LINE);
250  }
251 #if !HUD_CACHE
252  rz_list_free(filtered_list);
253 #endif
255  (void)rz_line_readline();
256  rz_str_ncpy(user_input, I(line)->buffer.data, HUD_BUF_SIZE); // to search
257 
258  if (!hud->activate) {
259  hud->top_entry_n = 0;
260  if (hud->current_entry_n >= 1) {
261  if (selected_entry) {
262  RZ_FREE(I(line)->hud);
263  I(line)->echo = true;
264  rz_cons_enable_mouse(false);
265  rz_cons_show_cursor(true);
266  rz_cons_set_raw(false);
267  return strdup(selected_entry);
268  }
269  } else {
270  goto _beach;
271  }
272  }
273  }
274 _beach:
275  RZ_FREE(I(line)->hud);
276  I(line)->echo = true;
277  rz_cons_show_cursor(true);
278  rz_cons_enable_mouse(false);
279  rz_cons_set_raw(false);
280  ht_pp_free(ht);
281  return NULL;
282 }
283 
284 // Display the list of files in a directory
285 RZ_API char *rz_cons_hud_path(const char *path, int dir) {
286  char *tmp, *ret = NULL;
287  RzList *files;
288  if (path) {
290  tmp = strdup(*path ? path : "./");
291  } else {
292  tmp = strdup("./");
293  }
294  files = rz_sys_dir(tmp);
295  if (files) {
296  ret = rz_cons_hud(files, tmp);
297  if (ret) {
298  tmp = rz_str_append(tmp, "/");
299  tmp = rz_str_append(tmp, ret);
300  free(ret);
301  ret = rz_file_abspath(tmp);
302  free(tmp);
303  tmp = ret;
304  if (rz_file_is_directory(tmp)) {
305  ret = rz_cons_hud_path(tmp, dir);
306  free(tmp);
307  tmp = ret;
308  }
309  }
311  } else {
312  eprintf("No files found\n");
313  }
314  if (!ret) {
315  free(tmp);
316  return NULL;
317  }
318  return tmp;
319 }
320 
323  int len = strlen(msg);
324  int rows, cols = rz_cons_get_size(&rows);
325  rz_cons_clear();
326  rz_cons_gotoxy((cols - len) / 2, rows / 2);
328  rz_cons_flush();
329  rz_cons_gotoxy(0, rows - 2);
331 }
size_t len
Definition: 6502dis.c:15
#define mask()
lzma_index ** i
Definition: index.h:629
RZ_API void rz_cons_set_raw(bool is_raw)
Definition: cons.c:1617
RZ_API int rz_cons_get_size(int *rows)
Definition: cons.c:1446
RZ_API bool rz_cons_enable_mouse(const bool enable)
Definition: cons.c:500
RZ_API int rz_cons_printf(const char *format,...)
Definition: cons.c:1202
RZ_API bool rz_cons_is_interactive(void)
Definition: cons.c:365
RZ_API void rz_cons_visual_flush(void)
Definition: cons.c:1067
RZ_API void rz_cons_show_cursor(int cursor)
Definition: cons.c:1581
RZ_API void rz_cons_flush(void)
Definition: cons.c:959
RZ_API void rz_cons_println(const char *str)
Definition: cons.c:233
RZ_API void rz_cons_gotoxy(int x, int y)
Definition: cons.c:724
RZ_API void rz_cons_clear(void)
Definition: cons.c:787
#define RZ_API
#define NULL
Definition: cris-opc.c:27
_Use_decl_annotations_ int __cdecl printf(const char *const _Format,...)
Definition: cs_driver.c:93
static static fork const void static count static fd const char const char static newpath const char static path const char path
Definition: sflib.h:35
RZ_API const char * rz_line_readline(void)
Definition: dietline.c:913
checking print the parsed form of the magic use in n conjunction with m to debug a new magic file n before installing it n output MIME type special files
Definition: file_opts.h:46
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
RZ_API const KEY_TYPE bool * found
Definition: ht_inc.h:130
static bool __matchString(char *entry, char *filter, char *mask, const int mask_size)
Definition: hud.c:64
#define I(x)
Definition: hud.c:7
RZ_API void rz_cons_message(RZ_NONNULL const char *msg)
Definition: hud.c:321
static void mht_free_kv(HtPPKv *kv)
Definition: hud.c:194
RZ_API char * rz_cons_hud_path(const char *path, int dir)
Definition: hud.c:285
static RzList * hud_filter(RzList *list, char *user_input, int top_entry_n, int *current_entry_n, char **selected_entry)
Definition: hud.c:112
RZ_API char * rz_cons_hud_file(const char *f)
Definition: hud.c:10
RZ_API char * rz_cons_hud(RzList *list, const char *prompt)
Definition: hud.c:202
RZ_API char * rz_cons_hud_string(const char *s)
Definition: hud.c:22
RZ_API int rz_cons_any_key(const char *msg)
Definition: input.c:393
return memset(p, 0, total)
void * p
Definition: libc.cpp:67
static void list(RzEgg *egg)
Definition: rz-gg.c:52
RZ_API RZ_OWN RzList * rz_list_newf(RzListFree f)
Returns a new initialized RzList pointer and sets the free method.
Definition: list.c:248
RZ_API RZ_OWN RzList * rz_list_new(void)
Returns a new initialized RzList pointer (free method is not initialized)
Definition: list.c:235
RZ_API RZ_BORROW RzListIter * rz_list_append(RZ_NONNULL RzList *list, void *data)
Appends at the end of the list a new element.
Definition: list.c:288
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
return strdup("=SP r13\n" "=LR r14\n" "=PC r15\n" "=A0 r0\n" "=A1 r1\n" "=A2 r2\n" "=A3 r3\n" "=ZF zf\n" "=SF nf\n" "=OF vf\n" "=CF cf\n" "=SN or0\n" "gpr lr .32 56 0\n" "gpr pc .32 60 0\n" "gpr cpsr .32 64 0 ____tfiae_________________qvczn\n" "gpr or0 .32 68 0\n" "gpr tf .1 64.5 0 thumb\n" "gpr ef .1 64.9 0 endian\n" "gpr jf .1 64.24 0 java\n" "gpr qf .1 64.27 0 sticky_overflow\n" "gpr vf .1 64.28 0 overflow\n" "gpr cf .1 64.29 0 carry\n" "gpr zf .1 64.30 0 zero\n" "gpr nf .1 64.31 0 negative\n" "gpr itc .4 64.10 0 if_then_count\n" "gpr gef .4 64.16 0 great_or_equal\n" "gpr r0 .32 0 0\n" "gpr r1 .32 4 0\n" "gpr r2 .32 8 0\n" "gpr r3 .32 12 0\n" "gpr r4 .32 16 0\n" "gpr r5 .32 20 0\n" "gpr r6 .32 24 0\n" "gpr r7 .32 28 0\n" "gpr r8 .32 32 0\n" "gpr r9 .32 36 0\n" "gpr r10 .32 40 0\n" "gpr r11 .32 44 0\n" "gpr r12 .32 48 0\n" "gpr r13 .32 52 0\n" "gpr r14 .32 56 0\n" "gpr r15 .32 60 0\n" "gpr r16 .32 64 0\n" "gpr r17 .32 68 0\n")
int x
Definition: mipsasm.c:20
line
Definition: setup.py:34
static bool filter(RzParse *p, ut64 addr, RzFlag *f, RzAnalysisHint *hint, char *data, char *str, int len, bool big_endian)
Definition: filter.c:185
#define eprintf(x, y...)
Definition: rlcc.c:7
static RzSocket * s
Definition: rtr.c:28
#define rz_return_if_fail(expr)
Definition: rz_assert.h:100
#define RZ_CONS_CLEAR_LINE
Definition: rz_cons.h:593
#define Color_RESET
Definition: rz_cons.h:617
#define Color_GREEN
Definition: rz_cons.h:627
#define HUD_BUF_SIZE
Definition: rz_cons.h:499
RZ_API bool rz_file_is_directory(const char *str)
Definition: file.c:167
RZ_API char * rz_file_abspath(const char *file)
Definition: file.c:267
RZ_API RZ_OWN char * rz_file_slurp(const char *str, RZ_NULLABLE size_t *usz)
Definition: file.c:454
RZ_API char * rz_str_appendf(char *ptr, const char *fmt,...) RZ_PRINTF_CHECK(2
RZ_API char * rz_str_newf(const char *fmt,...) RZ_PRINTF_CHECK(1
RZ_API char * rz_str_append(char *ptr, const char *string)
Definition: str.c:1063
RZ_API int rz_str_ansi_filter(char *str, char **out, int **cposs, int len)
Definition: str.c:2124
RZ_API size_t rz_str_ncpy(char *dst, const char *src, size_t n)
Secure string copy with null terminator.
Definition: str.c:923
RZ_API const char * rz_str_trim_head_ro(const char *str)
Definition: str_trim.c:86
RZ_API int rz_str_replace_ch(char *s, char a, char b, bool g)
Definition: str.c:139
RZ_API const char * rz_str_casestr(const char *a, const char *b)
Definition: str.c:2757
RZ_API RzList * rz_sys_dir(const char *path)
Definition: sys.c:216
#define RZ_NONNULL
Definition: rz_types.h:64
#define RZ_NEW(x)
Definition: rz_types.h:285
#define RZ_FREE(x)
Definition: rz_types.h:369
#define toupper(c)
Definition: safe-ctype.h:147
static struct sockaddr static addrlen static backlog const void msg
Definition: sfsocketcall.h:119
#define f(i)
Definition: sha256.c:46
Definition: buffer.h:15
Definition: zipcmp.c:77
int current_entry_n
Definition: rz_cons.h:1028
int vi
Definition: rz_cons.h:1031
int top_entry_n
Definition: rz_cons.h:1029
char activate
Definition: rz_cons.h:1030
RzListFree free
Definition: rz_list.h:21
ut32 length
Definition: rz_list.h:22
int64_t counter
Definition: main.c:4
static char * prompt(const char *str, const char *txt)
Definition: vmenus.c:30