2 * view.c -- a silly little viewer program
4 * written by Eric S. Raymond <esr@snark.thyrsus.com> December 1994
5 * to test the scrolling code in ncurses.
7 * modified by Thomas Dickey <dickey@clark.net> July 1995 to demonstrate
8 * the use of 'resizeterm()', and May 2000 to illustrate wide-character
11 * Takes a filename argument. It's a simple file-viewer with various
12 * scroll-up and scroll-down commands.
14 * n -- scroll one line forward
15 * p -- scroll one line back
17 * Either command accepts a numeric prefix interpreted as a repeat count.
18 * Thus, typing `5n' should scroll forward 5 lines in the file.
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
26 * $Id: view.c,v 1.31 2000/09/02 18:14:52 tom Exp $
29 #include <test.priv.h>
41 #if !defined(sun) || !HAVE_TERMIOS_H
43 # include <sys/ioctl.h>
47 /* This is needed to compile 'struct winsize' */
49 #include <sys/stream.h>
53 static RETSIGTYPE finish(int sig) GCC_NORETURN;
54 static void show_all(void);
56 #if defined(SIGWINCH) && defined(TIOCGWINSZ) && HAVE_RESIZETERM
63 static RETSIGTYPE adjust(int sig);
64 static int interrupted;
69 static int utf8_mode = FALSE;
72 static chtype **lines;
78 static const char *msg[] =
80 "Usage: view [options] file"
83 ," -n NUM specify maximum number of lines (default 1000)"
84 #if defined(KEY_RESIZE)
85 ," -r use experimental KEY_RESIZE rather than our own handler"
88 ," -t trace screen updates"
89 ," -T NUM specify trace mask"
91 ," -u translate UTF-8 data"
94 for (n = 0; n < SIZEOF(msg); n++)
95 fprintf(stderr, "%s\n", msg[n]);
109 * Allocate a string into an array of chtype's. If UTF-8 mode is
110 * active, translate the string accordingly.
115 unsigned len = strlen(src);
116 chtype *dst = typeMalloc(chtype, len + 1);
118 unsigned utf_count = 0;
119 unsigned utf_char = 0;
121 #define UCS_REPL 0xfffd
123 for (j = k = 0; j < len; j++) {
125 unsigned c = src[j] & 0xff;
126 /* Combine UTF-8 into Unicode */
128 /* We received an ASCII character */
130 dst[k++] = UCS_REPL; /* prev. sequence incomplete */
133 } else if (c < 0xc0) {
134 /* We received a continuation byte */
136 dst[k++] = UCS_REPL; /* ... unexpectedly */
138 if (!utf_char && !((c & 0x7f) >> (7 - utf_count))) {
141 /* characters outside UCS-2 become UCS_REPL */
142 if (utf_char > 0x03ff) {
143 /* value would be >0xffff */
147 utf_char |= (c & 0x3f);
154 /* We received a sequence start byte */
156 dst[k++] = UCS_REPL; /* prev. sequence incomplete */
159 utf_char = (c & 0x1f);
161 utf_char = UCS_REPL; /* overlong sequence */
162 } else if (c < 0xf0) {
164 utf_char = (c & 0x0f);
165 } else if (c < 0xf8) {
167 utf_char = (c & 0x07);
168 } else if (c < 0xfc) {
170 utf_char = (c & 0x03);
171 } else if (c < 0xfe) {
173 utf_char = (c & 0x01);
188 main(int argc, char *argv[])
198 bool use_resize = TRUE;
201 while ((i = getopt(argc, argv, "n:rtT:u")) != EOF) {
204 if ((MAXLINES = atoi(optarg)) < 1)
227 if (optind + 1 != argc)
230 if ((lines = typeMalloc(chtype *, MAXLINES + 2)) == 0)
233 fname = argv[optind];
234 if ((fp = fopen(fname, "r")) == 0) {
239 (void) signal(SIGINT, finish); /* arrange interrupts to terminate */
242 (void) signal(SIGWINCH, adjust); /* arrange interrupts to resize */
246 for (lptr = &lines[0]; (lptr - lines) < MAXLINES; lptr++) {
247 char temp[BUFSIZ], *s, *d;
250 if (fgets(buf, sizeof(buf), fp) == 0)
253 /* convert tabs so that shift will work properly */
254 for (s = buf, d = temp, col = 0; (*d = *s) != '\0'; s++) {
258 } else if (*d == '\t') {
260 while ((d - temp) != col)
262 } else if (isprint(*d) || utf8_mode) {
266 sprintf(d, "\\%03o", *s & 0xff);
271 *lptr = ch_dup(temp);
274 length = lptr - lines;
276 (void) initscr(); /* initialize the curses library */
277 keypad(stdscr, TRUE); /* enable keyboard mapping */
278 (void) nonl(); /* tell curses not to do NL->CR/NL on output */
279 (void) cbreak(); /* take input chars one at a time, no wait for \n */
280 (void) noecho(); /* don't echo input */
281 idlok(stdscr, TRUE); /* allow use of insert/delete line */
300 if ((c < 127) && isdigit(c)) {
302 mvprintw(0, 0, "Count: ");
306 n = 10 * n + (c - '0');
311 if (!got_number && n == 0)
318 for (i = 0; i < n; i++)
319 if ((lptr - lines) < (length - LINES + 1))
323 wscrl(stdscr, lptr - olptr);
329 for (i = 0; i < n; i++)
334 wscrl(stdscr, lptr - olptr);
345 lptr = lines + length - LINES + 1;
368 case KEY_RESIZE: /* ignore this; ncurses will repaint */
380 finish(0); /* we're done */
387 exit(sig != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
392 * This uses functions that are "unsafe", but it seems to work on SunOS and
393 * Linux. The 'wrefresh(curscr)' is needed to force the refresh to start from
394 * the top of the screen -- some xterms mangle the bitmap while resizing.
399 if (waiting || sig == 0) {
402 if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) {
403 resizeterm(size.ws_row, size.ws_col);
404 wrefresh(curscr); /* Linux needs this */
411 (void) signal(SIGWINCH, adjust); /* some systems need this */
413 #endif /* CAN_RESIZE */
423 sprintf(temp, "(%3dx%3d) col %d ", LINES, COLS, shift);
425 sprintf(temp + i, "view %.*s", (int) (sizeof(temp) - 7 - i), fname);
427 sprintf(temp, "view %.*s", (int) sizeof(temp) - 7, fname);
430 printw("%.*s", COLS, temp);
433 scrollok(stdscr, FALSE); /* prevent screen from moving */
434 for (i = 1; i < LINES; i++) {
436 printw("%3ld:", (long) (lptr + i - lines));
438 if ((s = lptr[i - 1]) != 0) {
444 setscrreg(1, LINES - 1);
445 scrollok(stdscr, TRUE);