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