8381dc8898c48fc878e5eeb21f1a7b25f26701e4
[ncurses.git] / 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()'.
9  *
10  * Takes a filename argument.  It's a simple file-viewer with various
11  * scroll-up and scroll-down commands.
12  *
13  * n    -- scroll one line forward
14  * p    -- scroll one line back
15  *
16  * Either command accepts a numeric prefix interpreted as a repeat count.
17  * Thus, typing `5n' should scroll forward 5 lines in the file.
18  *
19  * The way you can tell this is working OK is that, in the trace file,
20  * there should be one scroll operation plus a small number of line
21  * updates, as opposed to a whole-page update.  This means the physical
22  * scroll operation worked, and the refresh() code only had to do a
23  * partial repaint.
24  *
25  * $Id: view.c,v 1.27 1998/08/22 18:33:41 tom Exp $
26  */
27
28 #include <test.priv.h>
29
30 #include <string.h>
31 #include <ctype.h>
32 #include <signal.h>
33
34 #if HAVE_TERMIOS_H
35 # include <termios.h>
36 #else
37 # include <sgtty.h>
38 #endif
39
40 #if !defined(sun) || !HAVE_TERMIOS_H
41 # if HAVE_SYS_IOCTL_H
42 #  include <sys/ioctl.h>
43 # endif
44 #endif
45
46 /* This is needed to compile 'struct winsize' */
47 #if NEED_PTEM_H
48 #include <sys/stream.h>
49 #include <sys/ptem.h>
50 #endif
51
52 static RETSIGTYPE finish(int sig) GCC_NORETURN;
53 static void show_all(void);
54
55 #if defined(SIGWINCH) && defined(TIOCGWINSZ) && defined(NCURSES_VERSION)
56 #define CAN_RESIZE 1
57 #else
58 #define CAN_RESIZE 0
59 #endif
60
61 #if CAN_RESIZE
62 static RETSIGTYPE adjust(int sig);
63 static int          interrupted;
64 #endif
65
66 static int          waiting;
67 static int          shift;
68
69 static char        *fname;
70 static char        **lines;
71 static char        **lptr;
72
73 #if !HAVE_STRDUP
74 #define strdup my_strdup
75 static char *strdup (char *s)
76 {
77   char *p;
78
79   p = malloc(strlen(s)+1);
80   if (p)
81     strcpy(p,s);
82   return(p);
83 }
84 #endif /* not HAVE_STRDUP */
85
86 static void usage(void)
87 {
88     static const char *msg[] = {
89          "Usage: view [options] file"
90         ,""
91         ,"Options:"
92         ," -n NUM   specify maximum number of lines (default 1000)"
93 #if defined(KEY_RESIZE)
94         ," -r       use experimental KEY_RESIZE rather than our own handler"
95 #endif
96 #ifdef TRACE
97         ," -t       trace screen updates"
98         ," -T NUM   specify trace mask"
99 #endif
100     };
101     size_t n;
102     for (n = 0; n < SIZEOF(msg); n++)
103         fprintf(stderr, "%s\n", msg[n]);
104     exit (EXIT_FAILURE);
105 }
106
107 int main(int argc, char *argv[])
108 {
109 int         MAXLINES = 1000;
110 FILE        *fp;
111 char        buf[BUFSIZ];
112 int         i;
113 char        **olptr;
114 int         done = FALSE;
115 int         length = 0;
116 #if CAN_RESIZE
117 bool        use_resize = TRUE;
118 #endif
119
120     while ((i = getopt(argc, argv, "n:rtT:")) != EOF) {
121         switch (i) {
122         case 'n':
123             if ((MAXLINES = atoi(optarg)) < 1)
124                 usage();
125             break;
126 #if CAN_RESIZE
127         case 'r':
128             use_resize = FALSE;
129             break;
130 #endif
131 #ifdef TRACE
132         case 'T':
133             trace(atoi(optarg));
134             break;
135         case 't':
136             trace(TRACE_CALLS);
137             break;
138 #endif
139         default:
140             usage();
141         }
142     }
143     if (optind + 1 != argc)
144         usage();
145
146     if ((lines = (char **)calloc(MAXLINES+2, sizeof(*lines))) == 0)
147         usage();
148
149     fname = argv[optind];
150     if ((fp = fopen(fname, "r")) == 0) {
151         perror(fname);
152         return EXIT_FAILURE;
153     }
154
155     (void) signal(SIGINT, finish);      /* arrange interrupts to terminate */
156 #if CAN_RESIZE
157     if (use_resize)
158         (void) signal(SIGWINCH, adjust); /* arrange interrupts to resize */
159 #endif
160
161     /* slurp the file */
162     for (lptr = &lines[0]; (lptr - lines) < MAXLINES; lptr++) {
163         char temp[BUFSIZ], *s, *d;
164         int  col;
165
166         if (fgets(buf, sizeof(buf), fp) == 0)
167             break;
168
169         /* convert tabs so that shift will work properly */
170         for (s = buf, d = temp, col = 0; (*d = *s) != '\0'; s++) {
171             if (*d == '\n') {
172                 *d = '\0';
173                 break;
174             } else if (*d == '\t') {
175                 col = (col | 7) + 1;
176                 while ((d-temp) != col)
177                     *d++ = ' ';
178             } else if (isprint(*d)) {
179                 col++;
180                 d++;
181             } else {
182                 sprintf(d, "\\%03o", *s & 0xff);
183                 d += strlen(d);
184                 col = (d - temp);
185             }
186         }
187         *lptr = strdup(temp);
188     }
189     (void) fclose(fp);
190     length = lptr - lines;
191
192     (void) initscr();      /* initialize the curses library */
193     keypad(stdscr, TRUE);  /* enable keyboard mapping */
194     (void) nonl();         /* tell curses not to do NL->CR/NL on output */
195     (void) cbreak();       /* take input chars one at a time, no wait for \n */
196     (void) noecho();       /* don't echo input */
197     idlok(stdscr, TRUE);   /* allow use of insert/delete line */
198
199     lptr = lines;
200     while (!done) {
201         int n, c;
202         bool got_number;
203
204         show_all();
205
206         got_number = FALSE;
207         n = 0;
208         for (;;) {
209 #if CAN_RESIZE
210             if (interrupted)
211                 adjust(0);
212 #endif
213             waiting = TRUE;
214             c = getch();
215             waiting = FALSE;
216             if ((c < 127) && isdigit(c)) {
217                 if (!got_number) {
218                     mvprintw(0,0, "Count: ");
219                     clrtoeol();
220                 }
221                 addch(c);
222                 n = 10 * n + (c - '0');
223                 got_number = TRUE;
224             }
225             else
226                 break;
227         }
228         if (!got_number && n == 0)
229             n = 1;
230
231         switch(c) {
232         case KEY_DOWN:
233         case 'n':
234             olptr = lptr;
235             for (i = 0; i < n; i++)
236                 if ((lptr - lines) < (length - LINES + 1))
237                     lptr++;
238                 else
239                     break;
240             wscrl(stdscr, lptr - olptr);
241             break;
242
243         case KEY_UP:
244         case 'p':
245             olptr = lptr;
246             for (i = 0; i < n; i++)
247                 if (lptr > lines)
248                     lptr--;
249                 else
250                     break;
251             wscrl(stdscr, lptr - olptr);
252             break;
253
254         case 'h':
255         case KEY_HOME:
256             lptr = lines;
257             break;
258
259         case 'e':
260         case KEY_END:
261             if (length > LINES)
262                 lptr = lines + length - LINES + 1;
263             else
264                 lptr = lines;
265             break;
266
267         case 'r':
268         case KEY_RIGHT:
269             shift++;
270             break;
271
272         case 'l':
273         case KEY_LEFT:
274             if (shift)
275                 shift--;
276             else
277                 beep();
278             break;
279
280         case 'q':
281             done = TRUE;
282             break;
283
284 #ifdef KEY_RESIZE
285         case KEY_RESIZE:        /* ignore this; ncurses will repaint */
286             break;
287 #endif
288 #if CAN_RESIZE
289         case ERR:
290             break;
291 #endif
292         default:
293             beep();
294         }
295     }
296
297     finish(0);                  /* we're done */
298 }
299
300 static RETSIGTYPE finish(int sig)
301 {
302     endwin();
303     exit(sig != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
304 }
305
306 #if CAN_RESIZE
307 /*
308  * This uses functions that are "unsafe", but it seems to work on SunOS and
309  * Linux.  The 'wrefresh(curscr)' is needed to force the refresh to start from
310  * the top of the screen -- some xterms mangle the bitmap while resizing.
311  */
312 static RETSIGTYPE adjust(int sig)
313 {
314         if (waiting || sig == 0) {
315         struct winsize size;
316
317                 if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) {
318                         resizeterm(size.ws_row, size.ws_col);
319                         wrefresh(curscr);       /* Linux needs this */
320                         show_all();
321                 }
322                 interrupted = FALSE;
323         } else {
324                 interrupted = TRUE;
325         }
326         (void) signal(SIGWINCH, adjust);        /* some systems need this */
327 }
328 #endif  /* CAN_RESIZE */
329
330 static void show_all(void)
331 {
332         int i;
333         char temp[BUFSIZ];
334         char *s;
335
336 #if CAN_RESIZE
337         sprintf(temp, "(%3dx%3d) col %d ", LINES, COLS, shift);
338         i = strlen(temp);
339         sprintf(temp+i, "view %.*s", (int)(sizeof(temp)-7-i), fname);
340 #else
341         sprintf(temp, "view %.*s", (int)sizeof(temp)-7, fname);
342 #endif
343         move(0,0);
344         printw("%.*s", COLS, temp);
345         clrtoeol();
346
347         scrollok(stdscr, FALSE); /* prevent screen from moving */
348         for (i = 1; i < LINES; i++) {
349             move(i, 0);
350             if ((s = lptr[i-1]) != 0 && (int)strlen(s) > shift)
351                 printw("%3ld:%.*s", (long) (lptr+i-lines), COLS-4, s + shift);
352             else
353                 printw("%3ld:", (long) (lptr+i-lines));
354             clrtoeol();
355         }
356         setscrreg(1, LINES-1);
357         scrollok(stdscr, TRUE);
358         refresh();
359 }
360
361 /* view.c ends here */
362