]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/padview.c
ncurses 6.1 - patch 20190928
[ncurses.git] / test / padview.c
1 /****************************************************************************
2  * Copyright (c) 2017,2019 Free Software Foundation, Inc.                   *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /*
29  * clone of view.c, using pads
30  *
31  * $Id: padview.c,v 1.14 2019/07/13 20:41:47 tom Exp $
32  */
33
34 #include <test.priv.h>
35 #include <widechars.h>
36 #include <popup_msg.h>
37
38 #include <sys/stat.h>
39 #include <time.h>
40
41 #if HAVE_NEWPAD
42
43 static void finish(int sig) GCC_NORETURN;
44
45 #define my_pair 1
46
47 static int shift = 0;
48 static bool try_color = FALSE;
49
50 static char *fname;
51 static int num_lines;
52
53 #if USE_WIDEC_SUPPORT
54 static bool n_option = FALSE;
55 #endif
56
57 static void usage(void) GCC_NORETURN;
58
59 static void
60 failed(const char *msg)
61 {
62     endwin();
63     fprintf(stderr, "%s\n", msg);
64     ExitProgram(EXIT_FAILURE);
65 }
66
67 static void
68 finish(int sig)
69 {
70     endwin();
71     ExitProgram(sig != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
72 }
73
74 static void
75 show_all(const char *tag, WINDOW *my_pad, int my_row)
76 {
77     int i;
78     int digits;
79     char temp[BUFSIZ];
80     time_t this_time;
81
82     for (digits = 1, i = num_lines; i > 0; i /= 10) {
83         ++digits;
84     }
85
86     wattrset(stdscr, COLOR_PAIR(my_pair));
87     clear();
88
89     _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
90                 "view %.*s", (int) strlen(tag), tag);
91     i = (int) strlen(temp);
92     _nc_SPRINTF(temp + i, _nc_SLIMIT(sizeof(temp) - (size_t) i)
93                 " %.*s", (int) sizeof(temp) - i - 2, fname);
94     mvprintw(0, 0, "%.*s", COLS, temp);
95     this_time = time((time_t *) 0);
96     _nc_STRNCPY(temp, ctime(&this_time), (size_t) 30);
97     if ((i = (int) strlen(temp)) != 0) {
98         temp[--i] = 0;
99         mvprintw(0, COLS - i - 2, "  %s", temp);
100     }
101
102     for (i = 1; i < LINES; i++) {
103         int actual = my_row + i;
104         if (actual > num_lines) {
105             break;
106         }
107         mvprintw(i, 0, "%*d:", digits, actual);
108     }
109     wnoutrefresh(stdscr);
110     pnoutrefresh(my_pad, my_row, shift, 1, digits + 1, LINES - 1, COLS - 1);
111     doupdate();
112 }
113
114 static WINDOW *
115 read_file(const char *filename)
116 {
117     FILE *fp;
118     int pass;
119     int k;
120     int height, width;
121     size_t j;
122     size_t len;
123     struct stat sb;
124     char *my_blob;
125     char **my_vec = 0;
126     WINDOW *my_pad;
127
128     if (stat(filename, &sb) != 0
129         || (sb.st_mode & S_IFMT) != S_IFREG) {
130         failed("input is not a file");
131     }
132
133     if (sb.st_size == 0) {
134         failed("input is empty");
135     }
136
137     if ((fp = fopen(filename, "r")) == 0) {
138         failed("cannot open input-file");
139     }
140
141     if ((my_blob = malloc((size_t) sb.st_size + 1)) == 0) {
142         failed("cannot allocate memory for input-file");
143     }
144
145     len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp);
146     my_blob[sb.st_size] = '\0';
147     fclose(fp);
148
149     for (pass = 0; pass < 2; ++pass) {
150         char *base = my_blob;
151         k = 0;
152         for (j = 0; j < len; ++j) {
153             if (my_blob[j] == '\n') {
154                 if (pass) {
155                     my_vec[k] = base;
156                     my_blob[j] = '\0';
157                 }
158                 base = my_blob + j + 1;
159                 ++k;
160             }
161         }
162         num_lines = k;
163         if (base != (my_blob + j))
164             ++num_lines;
165         if (!pass &&
166             ((my_vec = typeCalloc(char *, (size_t) k + 2)) == 0)) {
167             failed("cannot allocate line-vector #1");
168         }
169     }
170
171 #if USE_WIDEC_SUPPORT
172     if (!memcmp("\357\273\277", my_blob, 3)) {
173         char *s = my_blob + 3;
174         char *d = my_blob;
175         Trace(("trim BOM"));
176         do {
177         } while ((*d++ = *s++) != '\0');
178     }
179 #endif
180
181     height = num_lines;
182     width = (int) strlen(my_vec[0]);
183     for (k = 1; my_vec[k]; ++k) {
184         int check = (int) (my_vec[k] - my_vec[k - 1]);
185         if (width < check)
186             width = check;
187     }
188     width = (width + 1) * 5;
189     my_pad = newpad(height, width);
190     if (my_pad == 0)
191         failed("cannot allocate pad workspace");
192     if (try_color) {
193         wattrset(my_pad, COLOR_PAIR(my_pair));
194         wbkgd(my_pad, (chtype) (' ' | COLOR_PAIR(my_pair)));
195     }
196
197     /*
198      * Use the curses library for rendering, including tab-conversion.
199      */
200     Trace(("slurp the file"));
201     for (k = 0; my_vec[k]; ++k) {
202         char *s;
203 #if USE_WIDEC_SUPPORT
204         char *last = my_vec[k] + (int) strlen(my_vec[k]);
205         wchar_t wch[2];
206         size_t rc;
207 #ifndef state_unused
208         mbstate_t state;
209 #endif
210 #endif /* USE_WIDEC_SUPPORT */
211
212         wmove(my_pad, k, 0);
213 #if USE_WIDEC_SUPPORT
214         wch[1] = 0;
215         reset_mbytes(state);
216 #endif
217         for (s = my_vec[k]; *s != '\0'; ++s) {
218 #if USE_WIDEC_SUPPORT
219             if (!n_option) {
220                 rc = (size_t) check_mbytes(wch[0], s, (size_t) (last - s), state);
221                 if ((long) rc == -1 || (long) rc == -2) {
222                     break;
223                 }
224                 s += rc - 1;
225                 waddwstr(my_pad, wch);
226             } else
227 #endif
228                 waddch(my_pad, *s & 0xff);
229         }
230     }
231
232     free(my_vec);
233     free(my_blob);
234
235     return my_pad;
236 }
237
238 static void
239 usage(void)
240 {
241     static const char *msg[] =
242     {
243         "Usage: view [options] file"
244         ,""
245         ,"Options:"
246         ," -c       use color if terminal supports it"
247         ," -i       ignore INT, QUIT, TERM signals"
248 #if USE_WIDEC_SUPPORT
249         ," -n       use waddch (bytes) rather then wadd_wch (wide-chars)"
250 #endif
251         ," -s       start in single-step mode, waiting for input"
252 #ifdef TRACE
253         ," -t       trace screen updates"
254         ," -T NUM   specify trace mask"
255 #endif
256     };
257     size_t n;
258     for (n = 0; n < SIZEOF(msg); n++)
259         fprintf(stderr, "%s\n", msg[n]);
260     ExitProgram(EXIT_FAILURE);
261 }
262
263 int
264 main(int argc, char *argv[])
265 {
266     static const char *help[] =
267     {
268         "Commands:",
269         "  q,^Q,ESC       - quit this program",
270         "",
271         "  p,<Up>         - scroll the viewport up by one row",
272         "  n,<Down>       - scroll the viewport down by one row",
273         "  l,<Left>       - scroll the viewport left by one column",
274         "  r,<Right>      - scroll the viewport right by one column",
275         "  <,>            - scroll the viewport left/right by 8 columns",
276         "",
277         "  h,<Home>       - scroll the viewport to top of file",
278         "  ^F,<PageDn>    - scroll to the next page",
279         "  ^B,<PageUp>    - scroll to the previous page",
280         "  e,<End>        - scroll the viewport to end of file",
281         "",
282         "  ^L             - repaint using redrawwin()",
283         "",
284         "  0 through 9    - enter digits for count",
285         "  s              - use entered count for halfdelay() parameter",
286         "                 - if no entered count, stop nodelay()",
287         "  <space>        - begin nodelay()",
288         0
289     };
290
291     int i;
292     int my_delay = 0;
293     WINDOW *my_pad;
294     int my_row = 0;
295     int value = 0;
296     bool done = FALSE;
297     bool got_number = FALSE;
298     bool ignore_sigs = FALSE;
299     bool single_step = FALSE;
300     const char *my_label = "Input";
301
302     setlocale(LC_ALL, "");
303
304     while ((i = getopt(argc, argv, "cinstT:")) != -1) {
305         switch (i) {
306         case 'c':
307             try_color = TRUE;
308             break;
309         case 'i':
310             ignore_sigs = TRUE;
311             break;
312 #if USE_WIDEC_SUPPORT
313         case 'n':
314             n_option = TRUE;
315             break;
316 #endif
317         case 's':
318             single_step = TRUE;
319             break;
320 #ifdef TRACE
321         case 'T':
322             {
323                 char *next = 0;
324                 int tvalue = (int) strtol(optarg, &next, 0);
325                 if (tvalue < 0 || (next != 0 && *next != 0))
326                     usage();
327                 trace((unsigned) tvalue);
328             }
329             break;
330         case 't':
331             trace(TRACE_CALLS);
332             break;
333 #endif
334         default:
335             usage();
336         }
337     }
338     if (optind + 1 != argc)
339         usage();
340
341     InitAndCatch(initscr(), ignore_sigs ? SIG_IGN : finish);
342     keypad(stdscr, TRUE);       /* enable keyboard mapping */
343     (void) nonl();              /* tell curses not to do NL->CR/NL on output */
344     (void) cbreak();            /* take input chars one at a time, no wait for \n */
345     (void) noecho();            /* don't echo input */
346     if (!single_step)
347         nodelay(stdscr, TRUE);
348     idlok(stdscr, TRUE);        /* allow use of insert/delete line */
349
350     if (try_color) {
351         if (has_colors()) {
352             start_color();
353             init_pair(my_pair, COLOR_WHITE, COLOR_BLUE);
354             bkgd((chtype) (' ' | COLOR_PAIR(my_pair)));
355         } else {
356             try_color = FALSE;
357         }
358     }
359
360     /*
361      * Do this after starting color, otherwise the pad's background will be
362      * uncolored after the ncurses 6.1.20181208 fixes.
363      */
364     my_pad = read_file(fname = argv[optind]);
365
366     my_row = 0;
367     while (!done) {
368         int n, c;
369
370         if (!got_number)
371             show_all(my_label, my_pad, my_row);
372
373         for (;;) {
374             c = getch();
375             if ((c < 127) && isdigit(c)) {
376                 if (!got_number) {
377                     MvPrintw(0, 0, "Count: ");
378                     clrtoeol();
379                 }
380                 addch(UChar(c));
381                 value = 10 * value + (c - '0');
382                 got_number = TRUE;
383             } else
384                 break;
385         }
386         if (got_number && value) {
387             n = value;
388         } else {
389             n = 1;
390         }
391
392         if (c != ERR)
393             my_label = keyname(c);
394         switch (c) {
395         case KEY_DOWN:
396         case 'n':
397             for (i = 0; i < n; i++)
398                 if (my_row < (num_lines - LINES + 1))
399                     my_row++;
400                 else
401                     break;
402             break;
403
404         case KEY_UP:
405         case 'p':
406             for (i = 0; i < n; i++)
407                 if (my_row > 0)
408                     my_row--;
409                 else
410                     break;
411             break;
412
413         case 'h':
414             /* FALLTHRU */
415         case KEY_HOME:
416             my_row = 0;
417             break;
418
419         case '<':
420             if ((shift -= 8) < 0)
421                 shift = 0;
422             break;
423         case '>':
424             shift += 8;
425             break;
426
427         case 'e':
428             /* FALLTHRU */
429         case KEY_END:
430             if (num_lines > LINES)
431                 my_row = (num_lines - LINES + 1);
432             else
433                 my_row = (num_lines - 2);
434             break;
435
436         case CTRL('F'):
437             /* FALLTHRU */
438         case KEY_NPAGE:
439             for (i = 0; i < n; i++) {
440                 if (my_row < (num_lines - 5))
441                     my_row += (LINES - 1);
442                 else
443                     my_row = (num_lines - 2);
444             }
445             break;
446
447         case CTRL('B'):
448             /* FALLTHRU */
449         case KEY_PPAGE:
450             for (i = 0; i < n; i++) {
451                 if (my_row >= LINES)
452                     my_row -= (LINES - 1);
453                 else
454                     my_row = 0;
455             }
456             break;
457
458         case 'r':
459         case KEY_RIGHT:
460             shift += n;
461             break;
462
463         case 'l':
464         case KEY_LEFT:
465             shift -= n;
466             if (shift < 0) {
467                 shift = 0;
468                 beep();
469             }
470             break;
471
472         case 'q':
473         case QUIT:
474         case ESCAPE:
475             done = TRUE;
476             break;
477
478 #ifdef KEY_RESIZE
479         case KEY_RESIZE:        /* ignore this; ncurses will repaint */
480             break;
481 #endif
482         case 's':
483 #if HAVE_HALFDELAY
484             if (got_number) {
485                 halfdelay(my_delay = n);
486             } else {
487                 nodelay(stdscr, FALSE);
488                 my_delay = -1;
489             }
490 #else
491             nodelay(stdscr, FALSE);
492             my_delay = -1;
493 #endif
494             break;
495         case ' ':
496             nodelay(stdscr, TRUE);
497             my_delay = 0;
498             break;
499         case CTRL('L'):
500             redrawwin(stdscr);
501             break;
502         case ERR:
503             if (!my_delay)
504                 napms(50);
505             break;
506         case HELP_KEY_1:
507             popup_msg(stdscr, help);
508             break;
509         default:
510             beep();
511             break;
512         }
513         if (c >= KEY_MIN || (c > 0 && !isdigit(c))) {
514             got_number = FALSE;
515             value = 0;
516         }
517     }
518
519     finish(0);                  /* we're done */
520 }
521 #else
522 int
523 main(void)
524 {
525     printf("This program requires the curses pad functions\n");
526     ExitProgram(EXIT_FAILURE);
527 }
528 #endif