Commit | Line | Data |
---|---|---|
ec215ba5 LM |
1 | /* depend: */ |
2 | /* cflags: */ | |
97efd1ff | 3 | /* linker: cmd.o debug.o tui.o -lcurses */ |
bfc65d24 | 4 | /* winlnk: cmd.o debug.o tui.o -lpdcurses */ |
ec215ba5 | 5 | |
3321993d | 6 | #include <assert.h> |
ec215ba5 | 7 | #include <stdlib.h> |
bff06943 | 8 | #include <string.h> |
53cd5b25 | 9 | #include <unistd.h> |
ec215ba5 LM |
10 | |
11 | #include "cmd.h" | |
4bbdea75 | 12 | #include "debug.h" |
ec215ba5 LM |
13 | #include "tui.h" |
14 | ||
39699220 LM |
15 | /* static variables */ |
16 | char *progname = NULL; | |
17 | char *version = "0.1"; | |
18 | ||
1d9c8261 ML |
19 | char *input = NULL; |
20 | char *command = NULL; | |
08f3bcd3 | 21 | char **lines = NULL; |
53cd5b25 | 22 | int second = 0; |
1d9c8261 | 23 | |
ff9767eb LM |
24 | #define MAX_LENGTH 48 |
25 | ||
08f3bcd3 | 26 | void display (char **lines, int xmax, int ymax, int keep_skip) |
ec215ba5 LM |
27 | { |
28 | int stop = 0; | |
29 | ||
3321993d | 30 | assert (lines); |
08f3bcd3 | 31 | |
dfbd663a | 32 | #ifdef __PDCURSES__ |
ec215ba5 | 33 | statusmsg ("Press 'q' or escape to quit"); |
1d9c8261 ML |
34 | #else |
35 | statusmsg ("Press 'q' or double-escape to quit"); | |
36 | #endif | |
37 | ||
08f3bcd3 LM |
38 | static int skip; |
39 | if (!keep_skip) { | |
40 | skip = 0; | |
41 | } | |
42 | ||
ec215ba5 LM |
43 | while (!stop) { |
44 | clsbody (); | |
45 | int i = 0; | |
46 | int eol = 0; | |
bff06943 | 47 | for (i = 0; i < ymax - 1; i++) { |
ec215ba5 LM |
48 | if (lines [skip + i] == NULL) { |
49 | break; | |
50 | } | |
51 | if (eol) { | |
52 | bodymsg ("\n"); | |
53 | } | |
54 | bodymsg (lines[skip + i]); | |
55 | eol = (lines[skip + i][xmax - 1 ] == '\0'); | |
56 | } | |
bff06943 LM |
57 | if (lines [skip + i] != NULL) { |
58 | if (eol) { | |
59 | bodymsg ("\n"); | |
60 | } | |
61 | char *str = (char *) calloc (1, xmax + 1); | |
62 | strcpy (str, lines[skip + i]); | |
63 | if (str[xmax - 1] != '\0') { | |
64 | VERBOSE (DEBUG, fprintf (stdout, "last line too long\n")); | |
65 | strcpy (str + xmax - 3, ">>"); | |
66 | } | |
67 | bodymsg (str); | |
68 | free (str); | |
69 | } | |
70 | ||
53cd5b25 LM |
71 | if (second > 0) { |
72 | DoExit (); | |
73 | break; | |
74 | } | |
ec215ba5 | 75 | int key = waitforkey (); |
bff06943 | 76 | VERBOSE (DEBUG, fprintf (stdout, "key pressed: 0x%04x (%c)\n", key, (key >= ' ') && (key < 256) ? key : '.')); |
ec215ba5 | 77 | switch (key) { |
4424d941 | 78 | case ':': |
c068c797 | 79 | switch (waitforkey ()) { |
4424d941 | 80 | case ':': |
1d9c8261 ML |
81 | DoExit (); |
82 | /* fallthrough */ | |
83 | case 'f': | |
84 | case 'h': | |
85 | stop = 1; | |
86 | break; | |
87 | } | |
88 | break; | |
c068c797 LM |
89 | case KEY_ESC: |
90 | #ifndef __PDCURSES__ | |
91 | if (waitforkey () != KEY_ESC) { | |
92 | break; | |
93 | } | |
c068c797 | 94 | #endif /* __PDCURSES__ */ |
3321993d | 95 | /* fallthrough */ |
1d9c8261 ML |
96 | case 'q': |
97 | DoExit (); | |
98 | stop = 1; | |
ec215ba5 LM |
99 | break; |
100 | case 'i': | |
101 | case KEY_UP: | |
102 | skip = (skip > 0) ? skip - 1 : skip; | |
103 | break; | |
104 | case 'k': | |
105 | case KEY_DOWN: | |
106 | skip = (lines[skip + 1] != NULL) ? skip + 1 : skip; | |
107 | break; | |
dfbd663a LM |
108 | #ifdef __PDCURSES__ |
109 | case ALT_F: | |
110 | case ALT_H: | |
1d9c8261 | 111 | stop = 1; |
ec215ba5 | 112 | break; |
1d9c8261 | 113 | #endif |
ec215ba5 LM |
114 | } |
115 | } | |
116 | ||
117 | rmstatus(); | |
ec215ba5 LM |
118 | } |
119 | ||
1d9c8261 | 120 | void process (void) |
ec215ba5 | 121 | { |
ec215ba5 LM |
122 | int xmax, ymax; |
123 | WINDOW *wbody = bodywin (); | |
124 | getmaxyx (wbody, ymax, xmax); | |
125 | ||
1d9c8261 | 126 | char *buffer = (char *)-1; |
8ffb744c | 127 | static int is_stdin = 0; |
1d9c8261 ML |
128 | if (command) { |
129 | buffer = exec_cmd (command); | |
130 | } else if (input) { | |
131 | buffer = load_file (input); | |
132 | } else if (!lines) { | |
8ffb744c LM |
133 | if (!is_stdin) { |
134 | buffer = read_stdin (); | |
135 | is_stdin = 1; | |
136 | } | |
1d9c8261 ML |
137 | } |
138 | ||
ec215ba5 | 139 | if (buffer == NULL) { |
ff9767eb LM |
140 | char msg[MAX_LENGTH + 13 + 2 + 1] = {0}; |
141 | sprintf (msg, "%s: %s", command ? "can't execute" : input ? "can't load" : "can't read", command ? command : input ? input : "stdin"); | |
c22e49bd | 142 | VERBOSE (WARNING, fprintf (stderr, "%s\n", msg)); |
ff9767eb LM |
143 | errormsg (msg); |
144 | return; | |
ec215ba5 LM |
145 | } |
146 | ||
08f3bcd3 LM |
147 | free_lines (lines); |
148 | lines = split_lines (buffer, xmax); | |
149 | free (buffer); | |
150 | display (lines, xmax, ymax, 0); | |
53cd5b25 LM |
151 | |
152 | if (second > 0) { | |
153 | VERBOSE (DEBUG, fprintf (stdout, "sleeping for %d''\n", second)); | |
154 | sleep (second); | |
155 | cleanup (); | |
156 | exit (0); | |
ec215ba5 | 157 | } |
ec215ba5 LM |
158 | } |
159 | ||
08f3bcd3 LM |
160 | void follow (void) |
161 | { | |
162 | int xmax, ymax; | |
163 | WINDOW *wbody = bodywin (); | |
164 | getmaxyx (wbody, ymax, xmax); | |
165 | ||
166 | display (lines, xmax, ymax, 1); | |
167 | } | |
168 | ||
5a4f7374 ML |
169 | void about (void) |
170 | { | |
f446a2a7 LM |
171 | char *message = "CMore: a curses more\n \nThis program is a pager with a text user interface.\n- To move displayed text or in menu, use arrow keys or vim keys (i, j, k, l).\n- To access menus, use Alt-f of Alt-h.\n- To quit displayed text, press Escape or 'q'."; |
172 | ||
5a4f7374 ML |
173 | int xmax, ymax; |
174 | WINDOW *wbody = bodywin (); | |
175 | getmaxyx (stdscr, ymax, xmax); | |
176 | ||
177 | wmove (stdscr, ymax / 4, xmax / 4); | |
178 | WINDOW *whelp = winputbox (stdscr, ymax / 2, xmax / 2); | |
179 | ||
f446a2a7 LM |
180 | int i, j; |
181 | for (i = 1; i < ymax / 2 - 1; i++) { | |
182 | char c[2] = {0}; | |
183 | for (j = 1; j < xmax / 2 - 1; j++) { | |
184 | *c = *message++; | |
185 | if ((*c == '\n') && (j == 1)) { | |
186 | *c = *message++; | |
187 | } | |
188 | if ((*c == '\n') || (*c == '\0')) { | |
189 | break; | |
190 | } | |
191 | wmove (whelp, i, j); | |
192 | waddstr (whelp, c); | |
193 | } | |
194 | if (*c == '\0') { | |
195 | break; | |
196 | } | |
197 | } | |
5a4f7374 ML |
198 | wrefresh (whelp); |
199 | ||
200 | waitforkey (); | |
201 | ||
202 | delwin (whelp); | |
164c3ff9 | 203 | touchwin (wbody); |
5a4f7374 ML |
204 | wrefresh (wbody); |
205 | } | |
206 | ||
cb16b4c1 LM |
207 | void chcmd (void) |
208 | { | |
ff9767eb | 209 | char buffer[MAX_LENGTH + 1] = {0}; |
17e0870c ML |
210 | char *desc[] = {"New command", NULL}; |
211 | char *buf[] = {buffer, NULL}; | |
ff9767eb LM |
212 | |
213 | if (command) { | |
214 | strncpy (buffer, command, MAX_LENGTH); | |
215 | } | |
216 | int c = getstrings (desc, buf, MAX_LENGTH); | |
8ffb744c LM |
217 | |
218 | if ((c == '\n') && (strlen (buffer) > 0)) { | |
219 | free (command); | |
220 | free (input); | |
221 | command = strdup (buffer); | |
222 | input = NULL; | |
223 | process (); | |
224 | } | |
cb16b4c1 LM |
225 | } |
226 | ||
227 | void chfile (void) | |
228 | { | |
ff9767eb | 229 | char buffer[MAX_LENGTH + 1] = {0}; |
8ffb744c LM |
230 | char *desc[] = {"New file", NULL}; |
231 | char *buf[] = {buffer, NULL}; | |
ff9767eb LM |
232 | |
233 | if (input) { | |
234 | strncpy (buffer, input, MAX_LENGTH); | |
235 | } | |
236 | int c = getstrings (desc, buf, MAX_LENGTH); | |
8ffb744c LM |
237 | |
238 | if ((c == '\n') && (strlen (buffer) > 0)) { | |
239 | free (command); | |
240 | free (input); | |
241 | command = NULL; | |
242 | input = strdup (buffer); | |
243 | process (); | |
244 | } | |
cb16b4c1 LM |
245 | } |
246 | ||
ec215ba5 LM |
247 | menu SubMenu0[] = |
248 | { | |
cb16b4c1 LM |
249 | { "Command", chcmd, "Change command"}, |
250 | { "File", chfile, "Change file"}, | |
08f3bcd3 | 251 | { "Display", follow, "Continue"}, |
cb16b4c1 | 252 | { "Refresh", process, "Refresh"}, |
ca818a82 | 253 | { "Quit", DoExit, "Terminate program" }, |
ec215ba5 LM |
254 | { "", (FUNC)0, "" } |
255 | }; | |
256 | ||
257 | void sub0 (void) | |
258 | { | |
259 | domenu (SubMenu0); | |
260 | } | |
261 | ||
262 | menu SubMenu1[] = | |
263 | { | |
5a4f7374 | 264 | { "About", about, "About..." }, |
ec215ba5 LM |
265 | { "", (FUNC)0, "" } |
266 | }; | |
267 | ||
268 | void sub1 (void) | |
269 | { | |
270 | domenu (SubMenu1); | |
271 | } | |
272 | ||
273 | menu MainMenu[] = | |
274 | { | |
275 | { "File", sub0, "File menu" }, | |
276 | { "Help", sub1, "Help menu" }, | |
277 | { "", (FUNC)0, "" } /* always add this as the last item! */ | |
278 | }; | |
279 | ||
4bbdea75 LM |
280 | /* help message */ |
281 | int usage (int ret) | |
282 | { | |
283 | FILE *fd = ret ? stderr : stdout; | |
53cd5b25 | 284 | fprintf (fd, "usage: %s [-c command] [-f file] [-h] [-s sec] [-v]\n", progname); |
4bbdea75 LM |
285 | fprintf (fd, " -c: command\n"); |
286 | fprintf (fd, " -i: input file\n"); | |
287 | fprintf (fd, " -h: help message\n"); | |
53cd5b25 | 288 | fprintf (fd, " -s: stoptime\n"); |
4bbdea75 LM |
289 | fprintf (fd, " -v: verbose level (%d)\n", verbose); |
290 | ||
291 | return ret; | |
292 | } | |
293 | ||
294 | /* main function */ | |
ec215ba5 LM |
295 | int main (int argc, char *argv[]) |
296 | { | |
4bbdea75 LM |
297 | |
298 | /* get basename */ | |
299 | char *pt = progname = argv[0]; | |
300 | while (*pt) { | |
301 | if ((*pt == '/') || (*pt == '\\')) { | |
302 | progname = pt + 1; | |
303 | } | |
304 | pt++; | |
305 | } | |
ec215ba5 | 306 | |
4bbdea75 LM |
307 | /* process argument */ |
308 | while (argc-- > 1) { | |
309 | char *arg = *(++argv); | |
310 | if (arg[0] != '-') { | |
311 | VERBOSE (ERROR, fprintf (stderr, "%s: invalid option -- %s\n", progname, arg)); | |
312 | return usage (1); | |
313 | } | |
314 | char c = arg[1]; | |
315 | switch (c) { | |
316 | case 'c': | |
317 | arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL; | |
8ffb744c LM |
318 | if (arg) { |
319 | free (command); | |
320 | free (input); | |
321 | command = strdup (arg); | |
322 | input = NULL; | |
4bbdea75 | 323 | } else { |
8ffb744c | 324 | VERBOSE (ERROR, fprintf (stderr, "%s: error for command\n", progname)); |
4bbdea75 LM |
325 | return usage (1); |
326 | } | |
327 | break; | |
328 | case 'f': | |
329 | arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL; | |
8ffb744c LM |
330 | if (arg) { |
331 | free (command); | |
332 | free (input); | |
333 | command = NULL; | |
334 | input = strdup (arg); | |
4bbdea75 | 335 | } else { |
8ffb744c | 336 | VERBOSE (ERROR, fprintf (stderr, "%s: error for file\n", progname)); |
4bbdea75 LM |
337 | return usage (1); |
338 | } | |
339 | break; | |
53cd5b25 LM |
340 | case 's': |
341 | arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL; | |
342 | if (arg == NULL) { | |
343 | VERBOSE (ERROR, fprintf (stderr, "%s: missing number of second\n", progname)); | |
344 | return usage (1); | |
345 | } | |
346 | second = atoi (arg); | |
347 | break; | |
4bbdea75 LM |
348 | case 'v': |
349 | arg = (arg[2]) ? arg + 2 : (--argc > 0) ? *(++argv) : NULL; | |
350 | if (arg == NULL) { | |
351 | VERBOSE (ERROR, fprintf (stderr, "%s: missing verbose level\n", progname)); | |
352 | return usage (1); | |
353 | } | |
354 | verbose = atoi (arg); | |
355 | break; | |
356 | case 'h': | |
357 | default: | |
358 | return usage (c != 'h'); | |
359 | } | |
360 | } | |
361 | ||
362 | char titre[256] = {0}; | |
1d9c8261 | 363 | sprintf (titre, "Application: %s", command ? command : input ? input : "stdin"); |
ec215ba5 | 364 | |
1d9c8261 | 365 | startmenu (MainMenu, titre, process); |
ec215ba5 LM |
366 | |
367 | return 0; | |
368 | } | |
369 | ||
288d672d | 370 | /* test: cmore.exe -c 2>&1 | grep error */ |
c22e49bd | 371 | /* test: echo :fi | cmore.exe -v 1 -c notAnExistingCommand 2>&1 | grep can\'t */ |
288d672d | 372 | /* test: cmore.exe -f 2>&1 | grep error */ |
c22e49bd | 373 | /* test: echo :fi | cmore.exe -v 1 -f notAnExistingFile 2>&1 | grep can\'t */ |
53cd5b25 LM |
374 | /* test: cmore.exe -h | grep usage */ |
375 | /* test: cmore.exe -Z 2>&1 | grep usage */ | |
376 | /* test: cmore.exe -v 2>&1 | grep missing */ | |
377 | /* test: cmore.exe -s 2>&1 | grep missing */ | |
378 | /* test: cmore.exe _ 2>&1 | grep invalid */ | |
c22e49bd | 379 | /* test: cat tui.c | cmore.exe -v 3 -s 2*/ |
288d672d LM |
380 | /* test: (sleep 1; echo -n q) | cmore.exe -c 'ip addr' */ |
381 | /* test: (sleep 1; echo -ne '\e\e') | cmore.exe -f tui.c */ | |
e99309bc | 382 | /* test: (sleep 1; echo -ne '\ex'; sleep 1; echo -ne '\e\e') | cmore.exe -f tui.c */ |
3321993d | 383 | /* test: (sleep 1; echo -n :x; sleep 1; echo -n ::) | cmore.exe -f tui.c */ |
288d672d | 384 | /* test: (sleep 1; echo -n kkkkk; sleep 1; echo -n i; sleep 1; echo -n q) | cmore.exe -c 'ip addr' */ |
c22e49bd ML |
385 | /* test: (sleep 1; echo -n q) | cmore.exe -c 'echo -n a; for i in $(seq 1 '$(( $COLUMNS*($LINES-4)-2 ))'); do echo -n .; done; echo -n z' -v 3 | grep -q 'last line too long' */ |
386 | /* test: (sleep 1; echo -n q) | cmore.exe -c 'echo -n a; for i in $(seq 1 '$(( $COLUMNS*($LINES-4)-3 ))'); do echo -n .; done; echo -n z' -v 3 | grep -q 'last line too long'; test $? -eq 1 */ | |
c2074f6c ML |
387 | /* test: (sleep 1; echo :f; sleep 1; echo i) | cmore.exe -c 'ip addr' */ |
388 | /* test: (sleep 1; echo :fkkk; sleep 1; echo :fiii) | cmore.exe -c 'ip addr' */ | |
389 | /* test: (sleep 1; echo :fjljjkkikkkk) | cmore.exe -c 'ip addr' */ | |
390 | /* test: (sleep 1; echo :flk; sleep 1; echo xji) | cmore.exe -c 'ip addr' */ | |
ca818a82 LM |
391 | /* test: (sleep 1; echo :fllk; sleep 1; echo; sleep 1; echo :fi) | cmore.exe -c 'ip addr' */ |
392 | /* test: (sleep 1; echo -n :fjjkll; sleep 1; echo -n :; sleep 1; echo -ne '\e'; sleep 1; echo -n kq) | cmore.exe -c 'ip addr' */ | |
c22e49bd | 393 | |
c2074f6c | 394 | /* test: (sleep 1; echo :fk; sleep 1; echo date; sleep 1; echo :fi) | cmore.exe -c 'ip addr' */ |
ca818a82 | 395 | /* test: (sleep 1; echo :fk; sleep 1; echo -ne '\e\e'; sleep 1; echo i) | cmore.exe -c 'ip addr' */ |
c22e49bd | 396 | /* test: (sleep 1; echo :fk; echo not_a_command; sleep 1; echo i) | cmore.exe -c 'ip addr' -v 3 2>&1 | grep -q "can't execute" */ |
c2074f6c | 397 | /* test: (sleep 1; echo :fkk; sleep 1; echo; sleep 1; echo ii) | cmore.exe -c 'ip addr' */ |
3321993d | 398 | /* test: (sleep 1; echo :fk; sleep 1; echo -e '\elljll\eroute'; sleep 1; echo q) | cmore.exe -c 'ip addr' */ |
ca818a82 | 399 | /* test: (sleep 1; echo :fk; sleep 1; echo -e '\elljll\nroute'; sleep 1; echo q) | cmore.exe -c 'ip addr' */ |
3321993d ML |
400 | /* test: (sleep 1; echo :fk; sleep 1; echo -en '\ek'; sleep 1; echo i) | cmore.exe -c 'ip addr' */ |
401 | /* test: (sleep 1; echo :fk; sleep 1; echo -e '\eidate'; sleep 1; echo q) | cmore.exe -c 'ip addr' */ | |
ca818a82 | 402 | /* test: (sleep 1; echo :fk; sleep 1; echo -e '\elll\e\t-j x\b'; sleep 1; echo q) | cmore.exe -c 'ip addr' */ |
c22e49bd ML |
403 | |
404 | /* test: (sleep 1; echo :fkk; sleep 1; echo tui.c; sleep 1; echo :fii) | cmore.exe -f tui.h */ | |
c2074f6c | 405 | /* test: (sleep 1; echo :fkk; sleep 1; echo cmore.c; sleep 1; echo :fii) | cmore.exe -c 'ip addr' */ |
c22e49bd | 406 | /* test: (sleep 1; echo :fkk; echo not_a_file; sleep 1; echo ii) | cmore.exe -c 'ip addr' -v 3 2>&1 | grep -q "can't load" */ |
c2074f6c | 407 | /* test: (sleep 1; echo :fkk; sleep 1; echo; sleep 1; echo ii) | cmore.exe -c 'ip addr' */ |
288d672d | 408 | |
ec215ba5 | 409 | /* vim: set ts=4 sw=4 et: */ |