1 /* radare2 - LGPL - Copyright 2008-2019 - pancake */
2 
3 #include <r_cons.h>
4 #include <r_regex.h>
5 #include <r_util.h>
6 #include "pager_private.h"
7 
r_cons_more_str(const char * str,const char * exitkeys)8 R_API int r_cons_more_str(const char *str, const char *exitkeys) {
9 	static bool inHelp = false;
10 	static const char *r_cons_more_help = \
11 		" space    - page up\n"
12 		" j        - line down\n"
13 		" /        - search in buffer\n"
14 		" _        - enter the hud mode\n"
15 		" n        - next search result\n"
16 		" q        - quit\n"
17 		" ?        - show this help\n"
18 		"\n";
19 	int lines_count = 0;
20 	RRegex *rx = NULL;
21 	int w, h, ch, to, ui = 1, from = 0, i;
22 	const char *sreg;
23 	RList **mla;
24 
25 	if (!str || !*str) {
26 		return 0;
27 	}
28 	// rcons kills str after flushing the buffer, so we must keep a copy
29 	char *ostr = strdup (str);
30 	if (!ostr) {
31 		return 0;
32 	}
33 	char *p = strdup (str);
34 	if (!p) {
35 		free (ostr);
36 		return 0;
37 	}
38 	int *lines = pager_splitlines (p, &lines_count);
39 	if (lines_count < 1) {
40 		mla = NULL;
41 	} else {
42 		mla = calloc (lines_count, sizeof (RList *));
43 		if (!mla) {
44 			free (p);
45 			free (ostr);
46 			free (lines);
47 			return 0;
48 		}
49 	}
50 	for (i = 0; i < lines_count; i++) {
51 		mla[i] = r_list_new ();
52 	}
53 	r_cons_set_raw (true);
54 	r_cons_show_cursor (false);
55 	r_cons_reset ();
56 	h = 0;
57 	while (ui) {
58 		w = r_cons_get_size (&h);
59 		to = R_MIN (lines_count, from + h);
60 		if (from + 3 > lines_count) {
61 			from = lines_count - 3;
62 		}
63 		if (from < 0) {
64 			from = 0;
65 		}
66 		pager_printpage (p, lines, mla, from, to, w);
67 		ch = r_cons_readchar ();
68 		if (exitkeys && strchr (exitkeys, ch)) {
69 			for (i = 0; i < lines_count; i++) {
70 				r_list_free (mla[i]);
71 			}
72 			free (p);
73 			free (mla);
74 			free (ostr);
75 			free (lines);
76 			return ch;
77 		}
78 		ch = r_cons_arrow_to_hjkl (ch);
79 		switch (ch) {
80 		case '_':
81 			r_cons_hud_string (ostr);
82 			break;
83 		case '?':
84 			if (!inHelp) {
85 				inHelp = true;
86 				r_cons_more_str (r_cons_more_help, NULL);
87 				inHelp = false;
88 			}
89 			break;
90 		case ' ': from += h; break;
91 		case -1: // EOF
92 		case '\x03': // ^C
93 		case 'q': ui = 0; break;
94 		case '\r':
95 		case '\n':
96 		case 'j': from++; break;
97 		case 'J': from+=h; break;
98 		case '/': 	/* search */
99 			r_cons_reset_colors ();
100 			r_line_set_prompt ("/");
101 			sreg = r_line_readline ();
102 			from = R_MIN (lines_count - 1, from);
103 			/* repeat last search if empty string is provided */
104 			if (sreg[0]) { /* prepare for a new search */
105 				if (rx) {
106 					r_regex_free (rx);
107 				}
108 				rx = r_regex_new (sreg, "");
109 			} else { /* we got an empty string */
110 				from = pager_next_match (from, mla, lines_count);
111 				break;
112 			}
113 			if (!rx) {
114 				break;
115 			}
116 			/* find all occurrences */
117 			if (pager_all_matches (p, rx, mla, lines, lines_count)) {
118 				from = pager_next_match (from, mla, lines_count);
119 			}
120 			break;
121 		case 'n': 	/* next match */
122 			/* search already performed */
123 			if (rx) {
124 				from = pager_next_match (from, mla, lines_count);
125 			}
126 			break;
127 		}
128 	}
129 	for (i = 0; i < lines_count; i++) {
130 		r_list_free (mla[i]);
131 	}
132 	free (mla);
133 	r_regex_free (rx);
134 	free (lines);
135 	free (p);
136 	r_cons_reset_colors ();
137 	r_cons_set_raw (false);
138 	r_cons_show_cursor (true);
139 	free (ostr);
140 	return 0;
141 }
142 
r_cons_more(void)143 R_API void r_cons_more(void) {
144 	(void)r_cons_more_str (r_cons_singleton ()->context->buffer, NULL);
145 }
146