add more test (3)
[cmore.git] / cmore.c
CommitLineData
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 */
16char *progname = NULL;
17char *version = "0.1";
18
1d9c8261
ML
19char *input = NULL;
20char *command = NULL;
08f3bcd3 21char **lines = NULL;
53cd5b25 22int second = 0;
1d9c8261 23
ff9767eb
LM
24#define MAX_LENGTH 48
25
08f3bcd3 26void 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 120void 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
160void 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
169void 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
207void 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
227void 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
247menu 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
257void sub0 (void)
258{
259 domenu (SubMenu0);
260}
261
262menu SubMenu1[] =
263{
5a4f7374 264 { "About", about, "About..." },
ec215ba5
LM
265 { "", (FUNC)0, "" }
266};
267
268void sub1 (void)
269{
270 domenu (SubMenu1);
271}
272
273menu 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 */
281int 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
295int 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: */