]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/view.c
ncurses 5.6 - patch 20080816
[ncurses.git] / test / view.c
1 /****************************************************************************
2  * Copyright (c) 1998-2007,2008 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  * view.c -- a silly little viewer program
30  *
31  * written by Eric S. Raymond <esr@snark.thyrsus.com> December 1994
32  * to test the scrolling code in ncurses.
33  *
34  * modified by Thomas Dickey <dickey@clark.net> July 1995 to demonstrate
35  * the use of 'resizeterm()', and May 2000 to illustrate wide-character
36  * handling.
37  *
38  * Takes a filename argument.  It's a simple file-viewer with various
39  * scroll-up and scroll-down commands.
40  *
41  * n    -- scroll one line forward
42  * p    -- scroll one line back
43  *
44  * Either command accepts a numeric prefix interpreted as a repeat count.
45  * Thus, typing `5n' should scroll forward 5 lines in the file.
46  *
47  * The way you can tell this is working OK is that, in the trace file,
48  * there should be one scroll operation plus a small number of line
49  * updates, as opposed to a whole-page update.  This means the physical
50  * scroll operation worked, and the refresh() code only had to do a
51  * partial repaint.
52  *
53  * $Id: view.c,v 1.68 2008/08/03 11:37:07 tom Exp $
54  */
55
56 #include <test.priv.h>
57
58 #include <time.h>
59
60 #undef CTRL                     /* conflict on AIX 5.2 with <sys/ioctl.h> */
61
62 #if HAVE_TERMIOS_H
63 # include <termios.h>
64 #else
65 # include <sgtty.h>
66 #endif
67
68 #if !defined(sun) || !HAVE_TERMIOS_H
69 # if HAVE_SYS_IOCTL_H
70 #  include <sys/ioctl.h>
71 # endif
72 #endif
73
74 #define my_pair 1
75
76 /* This is needed to compile 'struct winsize' */
77 #if NEED_PTEM_H
78 #include <sys/stream.h>
79 #include <sys/ptem.h>
80 #endif
81
82 static RETSIGTYPE finish(int sig) GCC_NORETURN;
83 static void show_all(const char *tag);
84
85 #if defined(SIGWINCH) && defined(TIOCGWINSZ) && HAVE_RESIZE_TERM
86 #define CAN_RESIZE 1
87 #else
88 #define CAN_RESIZE 0
89 #endif
90
91 #if CAN_RESIZE
92 static RETSIGTYPE adjust(int sig);
93 static int interrupted;
94 #endif
95
96 static bool waiting = FALSE;
97 static int shift = 0;
98 static bool try_color = FALSE;
99
100 static char *fname;
101 static NCURSES_CH_T **vec_lines;
102 static NCURSES_CH_T **lptr;
103 static int num_lines;
104
105 static void
106 usage(void)
107 {
108     static const char *msg[] =
109     {
110         "Usage: view [options] file"
111         ,""
112         ,"Options:"
113         ," -c       use color if terminal supports it"
114         ," -i       ignore INT, QUIT, TERM signals"
115         ," -n NUM   specify maximum number of lines (default 1000)"
116 #if defined(KEY_RESIZE)
117         ," -r       use old-style sigwinch handler rather than KEY_RESIZE"
118 #endif
119 #ifdef TRACE
120         ," -t       trace screen updates"
121         ," -T NUM   specify trace mask"
122 #endif
123     };
124     size_t n;
125     for (n = 0; n < SIZEOF(msg); n++)
126         fprintf(stderr, "%s\n", msg[n]);
127     ExitProgram(EXIT_FAILURE);
128 }
129
130 static int
131 ch_len(NCURSES_CH_T * src)
132 {
133     int result = 0;
134 #if USE_WIDEC_SUPPORT
135 #endif
136
137 #if USE_WIDEC_SUPPORT
138     while (getcchar(src++, NULL, NULL, NULL, NULL) > 0)
139         result++;
140 #else
141     while (*src++)
142         result++;
143 #endif
144     return result;
145 }
146
147 /*
148  * Allocate a string into an array of chtype's.  If UTF-8 mode is
149  * active, translate the string accordingly.
150  */
151 static NCURSES_CH_T *
152 ch_dup(char *src)
153 {
154     unsigned len = strlen(src);
155     NCURSES_CH_T *dst = typeMalloc(NCURSES_CH_T, len + 1);
156     unsigned j, k;
157 #if USE_WIDEC_SUPPORT
158     wchar_t wstr[CCHARW_MAX + 1];
159     wchar_t wch;
160     int l = 0;
161     mbstate_t state;
162     size_t rc;
163     int width;
164 #endif
165
166 #if USE_WIDEC_SUPPORT
167     memset(&state, 0, sizeof(state));
168 #endif
169     for (j = k = 0; j < len; j++) {
170 #if USE_WIDEC_SUPPORT
171         rc = mbrtowc(&wch, src + j, len - j, &state);
172         if (rc == (size_t) -1 || rc == (size_t) -2)
173             break;
174         j += rc - 1;
175         if ((width = wcwidth(wch)) < 0)
176             break;
177         if ((width > 0 && l > 0) || l == CCHARW_MAX) {
178             wstr[l] = L'\0';
179             l = 0;
180             if (setcchar(dst + k, wstr, 0, 0, NULL) != OK)
181                 break;
182             ++k;
183         }
184         if (width == 0 && l == 0)
185             wstr[l++] = L' ';
186         wstr[l++] = wch;
187 #else
188         dst[k++] = src[j];
189 #endif
190     }
191 #if USE_WIDEC_SUPPORT
192     if (l > 0) {
193         wstr[l] = L'\0';
194         if (setcchar(dst + k, wstr, 0, 0, NULL) == OK)
195             ++k;
196     }
197     wstr[0] = L'\0';
198     setcchar(dst + k, wstr, 0, 0, NULL);
199 #else
200     dst[k] = 0;
201 #endif
202     return dst;
203 }
204
205 int
206 main(int argc, char *argv[])
207 {
208     int MAXLINES = 1000;
209     FILE *fp;
210     char buf[BUFSIZ];
211     int i;
212     int my_delay = 0;
213     NCURSES_CH_T **olptr;
214     int value = 0;
215     bool done = FALSE;
216     bool got_number = FALSE;
217 #if CAN_RESIZE
218     bool nonposix_resize = FALSE;
219 #endif
220     const char *my_label = "Input";
221
222     setlocale(LC_ALL, "");
223
224 #ifndef NCURSES_VERSION
225     /*
226      * We know ncurses will catch SIGINT if we don't establish our own handler.
227      * Other versions of curses may/may not catch it.
228      */
229     (void) signal(SIGINT, finish);      /* arrange interrupts to terminate */
230 #endif
231
232     while ((i = getopt(argc, argv, "cin:rtT:")) != -1) {
233         switch (i) {
234         case 'c':
235             try_color = TRUE;
236             break;
237         case 'i':
238             CATCHALL(SIG_IGN);
239             break;
240         case 'n':
241             if ((MAXLINES = atoi(optarg)) < 1 ||
242                 (MAXLINES + 2) <= 1)
243                 usage();
244             break;
245 #if CAN_RESIZE
246         case 'r':
247             nonposix_resize = TRUE;
248             break;
249 #endif
250 #ifdef TRACE
251         case 'T':
252             trace((unsigned) atoi(optarg));
253             break;
254         case 't':
255             trace(TRACE_CALLS);
256             break;
257 #endif
258         default:
259             usage();
260         }
261     }
262     if (optind + 1 != argc)
263         usage();
264
265     if ((vec_lines = typeMalloc(NCURSES_CH_T *, MAXLINES + 2)) == 0)
266         usage();
267
268     fname = argv[optind];
269     if ((fp = fopen(fname, "r")) == 0) {
270         perror(fname);
271         ExitProgram(EXIT_FAILURE);
272     }
273 #if CAN_RESIZE
274     if (nonposix_resize)
275         (void) signal(SIGWINCH, adjust);        /* arrange interrupts to resize */
276 #endif
277
278     /* slurp the file */
279     for (lptr = &vec_lines[0]; (lptr - vec_lines) < MAXLINES; lptr++) {
280         char temp[BUFSIZ], *s, *d;
281         int col;
282
283         if (fgets(buf, sizeof(buf), fp) == 0)
284             break;
285
286         /* convert tabs so that shift will work properly */
287         for (s = buf, d = temp, col = 0; (*d = *s) != '\0'; s++) {
288             if (*d == '\n') {
289                 *d = '\0';
290                 break;
291             } else if (*d == '\t') {
292                 col = (col | 7) + 1;
293                 while ((d - temp) != col)
294                     *d++ = ' ';
295             } else
296 #if USE_WIDEC_SUPPORT
297                 col++, d++;
298 #else
299             if (isprint(UChar(*d))) {
300                 col++;
301                 d++;
302             } else {
303                 sprintf(d, "\\%03o", UChar(*s));
304                 d += strlen(d);
305                 col = (d - temp);
306             }
307 #endif
308         }
309         *lptr = ch_dup(temp);
310     }
311     (void) fclose(fp);
312     num_lines = lptr - vec_lines;
313
314     (void) initscr();           /* initialize the curses library */
315     keypad(stdscr, TRUE);       /* enable keyboard mapping */
316     (void) nonl();              /* tell curses not to do NL->CR/NL on output */
317     (void) cbreak();            /* take input chars one at a time, no wait for \n */
318     (void) noecho();            /* don't echo input */
319     nodelay(stdscr, TRUE);
320     idlok(stdscr, TRUE);        /* allow use of insert/delete line */
321
322     if (try_color) {
323         if (has_colors()) {
324             start_color();
325             init_pair(my_pair, COLOR_WHITE, COLOR_BLUE);
326             bkgd(COLOR_PAIR(my_pair));
327         } else {
328             try_color = FALSE;
329         }
330     }
331
332     lptr = vec_lines;
333     while (!done) {
334         int n, c;
335
336         if (!got_number)
337             show_all(my_label);
338
339         n = 0;
340         for (;;) {
341 #if CAN_RESIZE
342             if (interrupted) {
343                 adjust(0);
344                 my_label = "interrupt";
345             }
346 #endif
347             waiting = TRUE;
348             c = getch();
349             waiting = FALSE;
350             if ((c < 127) && isdigit(c)) {
351                 if (!got_number) {
352                     mvprintw(0, 0, "Count: ");
353                     clrtoeol();
354                 }
355                 addch(UChar(c));
356                 value = 10 * value + (c - '0');
357                 got_number = TRUE;
358             } else
359                 break;
360         }
361         if (got_number && value) {
362             n = value;
363         } else {
364             n = 1;
365         }
366
367         if (c != ERR)
368             my_label = keyname(c);
369         switch (c) {
370         case KEY_DOWN:
371         case 'n':
372             olptr = lptr;
373             for (i = 0; i < n; i++)
374                 if ((lptr - vec_lines) < (num_lines - LINES + 1))
375                     lptr++;
376                 else
377                     break;
378             scrl(lptr - olptr);
379             break;
380
381         case KEY_UP:
382         case 'p':
383             olptr = lptr;
384             for (i = 0; i < n; i++)
385                 if (lptr > vec_lines)
386                     lptr--;
387                 else
388                     break;
389             scrl(lptr - olptr);
390             break;
391
392         case 'h':
393         case KEY_HOME:
394             lptr = vec_lines;
395             break;
396
397         case 'e':
398         case KEY_END:
399             if (num_lines > LINES)
400                 lptr = vec_lines + num_lines - LINES + 1;
401             else
402                 lptr = vec_lines;
403             break;
404
405         case 'r':
406         case KEY_RIGHT:
407             shift += n;
408             break;
409
410         case 'l':
411         case KEY_LEFT:
412             shift -= n;
413             if (shift < 0) {
414                 shift = 0;
415                 beep();
416             }
417             break;
418
419         case 'q':
420             done = TRUE;
421             break;
422
423 #ifdef KEY_RESIZE
424         case KEY_RESIZE:        /* ignore this; ncurses will repaint */
425             break;
426 #endif
427         case 's':
428             if (got_number) {
429                 halfdelay(my_delay = n);
430             } else {
431                 nodelay(stdscr, FALSE);
432                 my_delay = -1;
433             }
434             break;
435         case ' ':
436             nodelay(stdscr, TRUE);
437             my_delay = 0;
438             break;
439         case ERR:
440             if (!my_delay)
441                 napms(50);
442             break;
443         default:
444             beep();
445             break;
446         }
447         if (c >= KEY_MIN || (c > 0 && !isdigit(c))) {
448             got_number = FALSE;
449             value = 0;
450         }
451     }
452
453     finish(0);                  /* we're done */
454 }
455
456 static RETSIGTYPE
457 finish(int sig)
458 {
459     endwin();
460 #if NO_LEAKS
461     if (vec_lines != 0) {
462         int n;
463         for (n = 0; n < num_lines; ++n) {
464             free(vec_lines[n]);
465         }
466         free(vec_lines);
467     }
468 #endif
469     ExitProgram(sig != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
470 }
471
472 #if CAN_RESIZE
473 /*
474  * This uses functions that are "unsafe", but it seems to work on SunOS and
475  * Linux.  Usually:  the "unsafe" refers to the functions that POSIX lists
476  * which may be called from a signal handler.  Those do not include buffered
477  * I/O, which is used for instance in wrefresh().  To be really portable, you
478  * should use the KEY_RESIZE return (which relies on ncurses' sigwinch
479  * handler).
480  *
481  * The 'wrefresh(curscr)' is needed to force the refresh to start from the top
482  * of the screen -- some xterms mangle the bitmap while resizing.
483  */
484 static RETSIGTYPE
485 adjust(int sig)
486 {
487     if (waiting || sig == 0) {
488         struct winsize size;
489
490         if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) {
491             resize_term(size.ws_row, size.ws_col);
492             wrefresh(curscr);   /* Linux needs this */
493             show_all(sig ? "SIGWINCH" : "interrupt");
494         }
495         interrupted = FALSE;
496     } else {
497         interrupted = TRUE;
498     }
499     (void) signal(SIGWINCH, adjust);    /* some systems need this */
500 }
501 #endif /* CAN_RESIZE */
502
503 static void
504 show_all(const char *tag)
505 {
506     int i;
507     char temp[BUFSIZ];
508     NCURSES_CH_T *s;
509     time_t this_time;
510
511 #if CAN_RESIZE
512     sprintf(temp, "%.20s (%3dx%3d) col %d ", tag, LINES, COLS, shift);
513     i = strlen(temp);
514     if ((i + 7) < (int) sizeof(temp))
515         sprintf(temp + i, "view %.*s", (int) (sizeof(temp) - 7 - i), fname);
516 #else
517     (void) tag;
518     sprintf(temp, "view %.*s", (int) sizeof(temp) - 7, fname);
519 #endif
520     move(0, 0);
521     printw("%.*s", COLS, temp);
522     clrtoeol();
523     this_time = time((time_t *) 0);
524     strcpy(temp, ctime(&this_time));
525     if ((i = strlen(temp)) != 0) {
526         temp[--i] = 0;
527         if (move(0, COLS - i - 2) != ERR)
528             printw("  %s", temp);
529     }
530
531     scrollok(stdscr, FALSE);    /* prevent screen from moving */
532     for (i = 1; i < LINES; i++) {
533         move(i, 0);
534         printw("%3ld:", (long) (lptr + i - vec_lines));
535         clrtoeol();
536         if ((s = lptr[i - 1]) != 0) {
537             int len = ch_len(s);
538             if (len > shift) {
539 #if USE_WIDEC_SUPPORT
540                 add_wchstr(s + shift);
541 #else
542                 addchstr(s + shift);
543 #endif
544             }
545 #if defined(NCURSES_VERSION) || defined(HAVE_WCHGAT)
546             if (try_color)
547                 wchgat(stdscr, -1, A_NORMAL, my_pair, NULL);
548 #endif
549         }
550     }
551     setscrreg(1, LINES - 1);
552     scrollok(stdscr, TRUE);
553     refresh();
554 }