1997-05-15 12:00:00 +08:00
|
|
|
/*
|
|
|
|
* view.c -- a silly little viewer program
|
|
|
|
*
|
|
|
|
* written by Eric S. Raymond <esr@snark.thyrsus.com> December 1994
|
|
|
|
* to test the scrolling code in ncurses.
|
|
|
|
*
|
|
|
|
* modified by Thomas Dickey <dickey@clark.net> July 1995 to demonstrate
|
|
|
|
* the use of 'resizeterm()'.
|
|
|
|
*
|
1998-03-01 12:21:12 +08:00
|
|
|
* Takes a filename argument. It's a simple file-viewer with various
|
1997-05-15 12:00:00 +08:00
|
|
|
* scroll-up and scroll-down commands.
|
|
|
|
*
|
|
|
|
* n -- scroll one line forward
|
|
|
|
* p -- scroll one line back
|
|
|
|
*
|
|
|
|
* Either command accepts a numeric prefix interpreted as a repeat count.
|
|
|
|
* Thus, typing `5n' should scroll forward 5 lines in the file.
|
|
|
|
*
|
|
|
|
* The way you can tell this is working OK is that, in the trace file,
|
|
|
|
* there should be one scroll operation plus a small number of line
|
|
|
|
* updates, as opposed to a whole-page update. This means the physical
|
|
|
|
* scroll operation worked, and the refresh() code only had to do a
|
|
|
|
* partial repaint.
|
|
|
|
*
|
1999-10-24 12:32:42 +08:00
|
|
|
* $Id: view.c,v 1.27 1998/08/22 18:33:41 tom Exp $
|
1997-05-15 12:00:00 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <test.priv.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#if HAVE_TERMIOS_H
|
|
|
|
# include <termios.h>
|
|
|
|
#else
|
|
|
|
# include <sgtty.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(sun) || !HAVE_TERMIOS_H
|
|
|
|
# if HAVE_SYS_IOCTL_H
|
|
|
|
# include <sys/ioctl.h>
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* This is needed to compile 'struct winsize' */
|
1998-03-01 12:21:12 +08:00
|
|
|
#if NEED_PTEM_H
|
1997-05-15 12:00:00 +08:00
|
|
|
#include <sys/stream.h>
|
|
|
|
#include <sys/ptem.h>
|
|
|
|
#endif
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
static RETSIGTYPE finish(int sig) GCC_NORETURN;
|
1997-05-15 12:00:00 +08:00
|
|
|
static void show_all(void);
|
1998-03-01 12:21:12 +08:00
|
|
|
|
1997-05-15 12:00:00 +08:00
|
|
|
#if defined(SIGWINCH) && defined(TIOCGWINSZ) && defined(NCURSES_VERSION)
|
|
|
|
#define CAN_RESIZE 1
|
|
|
|
#else
|
|
|
|
#define CAN_RESIZE 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if CAN_RESIZE
|
|
|
|
static RETSIGTYPE adjust(int sig);
|
|
|
|
static int interrupted;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int waiting;
|
|
|
|
static int shift;
|
|
|
|
|
|
|
|
static char *fname;
|
1998-03-01 12:21:12 +08:00
|
|
|
static char **lines;
|
1997-05-15 12:00:00 +08:00
|
|
|
static char **lptr;
|
|
|
|
|
|
|
|
#if !HAVE_STRDUP
|
1998-03-01 12:21:12 +08:00
|
|
|
#define strdup my_strdup
|
1997-05-15 12:00:00 +08:00
|
|
|
static char *strdup (char *s)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
p = malloc(strlen(s)+1);
|
|
|
|
if (p)
|
|
|
|
strcpy(p,s);
|
|
|
|
return(p);
|
|
|
|
}
|
|
|
|
#endif /* not HAVE_STRDUP */
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
static void usage(void)
|
|
|
|
{
|
|
|
|
static const char *msg[] = {
|
|
|
|
"Usage: view [options] file"
|
|
|
|
,""
|
|
|
|
,"Options:"
|
|
|
|
," -n NUM specify maximum number of lines (default 1000)"
|
|
|
|
#if defined(KEY_RESIZE)
|
|
|
|
," -r use experimental KEY_RESIZE rather than our own handler"
|
|
|
|
#endif
|
|
|
|
#ifdef TRACE
|
|
|
|
," -t trace screen updates"
|
|
|
|
," -T NUM specify trace mask"
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
size_t n;
|
|
|
|
for (n = 0; n < SIZEOF(msg); n++)
|
|
|
|
fprintf(stderr, "%s\n", msg[n]);
|
|
|
|
exit (EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
1997-05-15 12:00:00 +08:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
1998-03-01 12:21:12 +08:00
|
|
|
int MAXLINES = 1000;
|
1997-05-15 12:00:00 +08:00
|
|
|
FILE *fp;
|
|
|
|
char buf[BUFSIZ];
|
|
|
|
int i;
|
|
|
|
char **olptr;
|
|
|
|
int done = FALSE;
|
1998-03-01 12:21:12 +08:00
|
|
|
int length = 0;
|
|
|
|
#if CAN_RESIZE
|
|
|
|
bool use_resize = TRUE;
|
|
|
|
#endif
|
1997-05-15 12:00:00 +08:00
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
while ((i = getopt(argc, argv, "n:rtT:")) != EOF) {
|
|
|
|
switch (i) {
|
|
|
|
case 'n':
|
|
|
|
if ((MAXLINES = atoi(optarg)) < 1)
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
#if CAN_RESIZE
|
|
|
|
case 'r':
|
|
|
|
use_resize = FALSE;
|
|
|
|
break;
|
|
|
|
#endif
|
1997-05-15 12:00:00 +08:00
|
|
|
#ifdef TRACE
|
1998-03-01 12:21:12 +08:00
|
|
|
case 'T':
|
|
|
|
trace(atoi(optarg));
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
trace(TRACE_CALLS);
|
|
|
|
break;
|
1997-05-15 12:00:00 +08:00
|
|
|
#endif
|
1998-03-01 12:21:12 +08:00
|
|
|
default:
|
|
|
|
usage();
|
1997-05-15 12:00:00 +08:00
|
|
|
}
|
|
|
|
}
|
1998-03-01 12:21:12 +08:00
|
|
|
if (optind + 1 != argc)
|
|
|
|
usage();
|
|
|
|
|
|
|
|
if ((lines = (char **)calloc(MAXLINES+2, sizeof(*lines))) == 0)
|
|
|
|
usage();
|
|
|
|
|
|
|
|
fname = argv[optind];
|
|
|
|
if ((fp = fopen(fname, "r")) == 0) {
|
|
|
|
perror(fname);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
(void) signal(SIGINT, finish); /* arrange interrupts to terminate */
|
|
|
|
#if CAN_RESIZE
|
1998-03-01 12:21:12 +08:00
|
|
|
if (use_resize)
|
|
|
|
(void) signal(SIGWINCH, adjust); /* arrange interrupts to resize */
|
1997-05-15 12:00:00 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* slurp the file */
|
1998-03-01 12:21:12 +08:00
|
|
|
for (lptr = &lines[0]; (lptr - lines) < MAXLINES; lptr++) {
|
1997-05-15 12:00:00 +08:00
|
|
|
char temp[BUFSIZ], *s, *d;
|
|
|
|
int col;
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
if (fgets(buf, sizeof(buf), fp) == 0)
|
|
|
|
break;
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
/* convert tabs so that shift will work properly */
|
|
|
|
for (s = buf, d = temp, col = 0; (*d = *s) != '\0'; s++) {
|
|
|
|
if (*d == '\n') {
|
|
|
|
*d = '\0';
|
|
|
|
break;
|
|
|
|
} else if (*d == '\t') {
|
|
|
|
col = (col | 7) + 1;
|
|
|
|
while ((d-temp) != col)
|
|
|
|
*d++ = ' ';
|
|
|
|
} else if (isprint(*d)) {
|
|
|
|
col++;
|
|
|
|
d++;
|
|
|
|
} else {
|
1998-03-01 12:21:12 +08:00
|
|
|
sprintf(d, "\\%03o", *s & 0xff);
|
1997-05-15 12:00:00 +08:00
|
|
|
d += strlen(d);
|
|
|
|
col = (d - temp);
|
|
|
|
}
|
|
|
|
}
|
1998-03-01 12:21:12 +08:00
|
|
|
*lptr = strdup(temp);
|
1997-05-15 12:00:00 +08:00
|
|
|
}
|
|
|
|
(void) fclose(fp);
|
1998-03-01 12:21:12 +08:00
|
|
|
length = lptr - lines;
|
|
|
|
|
|
|
|
(void) initscr(); /* initialize the curses library */
|
|
|
|
keypad(stdscr, TRUE); /* enable keyboard mapping */
|
|
|
|
(void) nonl(); /* tell curses not to do NL->CR/NL on output */
|
|
|
|
(void) cbreak(); /* take input chars one at a time, no wait for \n */
|
|
|
|
(void) noecho(); /* don't echo input */
|
|
|
|
idlok(stdscr, TRUE); /* allow use of insert/delete line */
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
lptr = lines;
|
|
|
|
while (!done) {
|
1998-03-01 12:21:12 +08:00
|
|
|
int n, c;
|
|
|
|
bool got_number;
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
show_all();
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
got_number = FALSE;
|
1997-05-15 12:00:00 +08:00
|
|
|
n = 0;
|
|
|
|
for (;;) {
|
|
|
|
#if CAN_RESIZE
|
|
|
|
if (interrupted)
|
|
|
|
adjust(0);
|
|
|
|
#endif
|
|
|
|
waiting = TRUE;
|
|
|
|
c = getch();
|
|
|
|
waiting = FALSE;
|
1998-03-01 12:21:12 +08:00
|
|
|
if ((c < 127) && isdigit(c)) {
|
|
|
|
if (!got_number) {
|
|
|
|
mvprintw(0,0, "Count: ");
|
|
|
|
clrtoeol();
|
|
|
|
}
|
|
|
|
addch(c);
|
1997-05-15 12:00:00 +08:00
|
|
|
n = 10 * n + (c - '0');
|
1998-03-01 12:21:12 +08:00
|
|
|
got_number = TRUE;
|
|
|
|
}
|
|
|
|
else
|
1997-05-15 12:00:00 +08:00
|
|
|
break;
|
|
|
|
}
|
1998-03-01 12:21:12 +08:00
|
|
|
if (!got_number && n == 0)
|
1997-05-15 12:00:00 +08:00
|
|
|
n = 1;
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
switch(c) {
|
|
|
|
case KEY_DOWN:
|
1997-05-15 12:00:00 +08:00
|
|
|
case 'n':
|
|
|
|
olptr = lptr;
|
1998-03-01 12:21:12 +08:00
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
if ((lptr - lines) < (length - LINES + 1))
|
1997-05-15 12:00:00 +08:00
|
|
|
lptr++;
|
1998-03-01 12:21:12 +08:00
|
|
|
else
|
1997-05-15 12:00:00 +08:00
|
|
|
break;
|
|
|
|
wscrl(stdscr, lptr - olptr);
|
1998-03-01 12:21:12 +08:00
|
|
|
break;
|
1997-05-15 12:00:00 +08:00
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
case KEY_UP:
|
1997-05-15 12:00:00 +08:00
|
|
|
case 'p':
|
|
|
|
olptr = lptr;
|
1998-03-01 12:21:12 +08:00
|
|
|
for (i = 0; i < n; i++)
|
1997-05-15 12:00:00 +08:00
|
|
|
if (lptr > lines)
|
|
|
|
lptr--;
|
1998-03-01 12:21:12 +08:00
|
|
|
else
|
1997-05-15 12:00:00 +08:00
|
|
|
break;
|
|
|
|
wscrl(stdscr, lptr - olptr);
|
1998-03-01 12:21:12 +08:00
|
|
|
break;
|
1997-05-15 12:00:00 +08:00
|
|
|
|
|
|
|
case 'h':
|
|
|
|
case KEY_HOME:
|
|
|
|
lptr = lines;
|
|
|
|
break;
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
case 'e':
|
|
|
|
case KEY_END:
|
|
|
|
if (length > LINES)
|
|
|
|
lptr = lines + length - LINES + 1;
|
|
|
|
else
|
|
|
|
lptr = lines;
|
|
|
|
break;
|
|
|
|
|
1997-05-15 12:00:00 +08:00
|
|
|
case 'r':
|
|
|
|
case KEY_RIGHT:
|
|
|
|
shift++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'l':
|
|
|
|
case KEY_LEFT:
|
|
|
|
if (shift)
|
1998-03-01 12:21:12 +08:00
|
|
|
shift--;
|
1997-05-15 12:00:00 +08:00
|
|
|
else
|
|
|
|
beep();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'q':
|
|
|
|
done = TRUE;
|
|
|
|
break;
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
#ifdef KEY_RESIZE
|
|
|
|
case KEY_RESIZE: /* ignore this; ncurses will repaint */
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#if CAN_RESIZE
|
|
|
|
case ERR:
|
|
|
|
break;
|
|
|
|
#endif
|
1997-05-15 12:00:00 +08:00
|
|
|
default:
|
|
|
|
beep();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-03-01 12:21:12 +08:00
|
|
|
finish(0); /* we're done */
|
1997-05-15 12:00:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static RETSIGTYPE finish(int sig)
|
|
|
|
{
|
|
|
|
endwin();
|
|
|
|
exit(sig != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if CAN_RESIZE
|
|
|
|
/*
|
|
|
|
* This uses functions that are "unsafe", but it seems to work on SunOS and
|
|
|
|
* Linux. The 'wrefresh(curscr)' is needed to force the refresh to start from
|
|
|
|
* the top of the screen -- some xterms mangle the bitmap while resizing.
|
|
|
|
*/
|
|
|
|
static RETSIGTYPE adjust(int sig)
|
|
|
|
{
|
|
|
|
if (waiting || sig == 0) {
|
|
|
|
struct winsize size;
|
|
|
|
|
|
|
|
if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) {
|
|
|
|
resizeterm(size.ws_row, size.ws_col);
|
|
|
|
wrefresh(curscr); /* Linux needs this */
|
|
|
|
show_all();
|
|
|
|
}
|
|
|
|
interrupted = FALSE;
|
|
|
|
} else {
|
|
|
|
interrupted = TRUE;
|
|
|
|
}
|
|
|
|
(void) signal(SIGWINCH, adjust); /* some systems need this */
|
|
|
|
}
|
|
|
|
#endif /* CAN_RESIZE */
|
|
|
|
|
|
|
|
static void show_all(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char temp[BUFSIZ];
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
#if CAN_RESIZE
|
|
|
|
sprintf(temp, "(%3dx%3d) col %d ", LINES, COLS, shift);
|
|
|
|
i = strlen(temp);
|
|
|
|
sprintf(temp+i, "view %.*s", (int)(sizeof(temp)-7-i), fname);
|
|
|
|
#else
|
|
|
|
sprintf(temp, "view %.*s", (int)sizeof(temp)-7, fname);
|
|
|
|
#endif
|
|
|
|
move(0,0);
|
|
|
|
printw("%.*s", COLS, temp);
|
|
|
|
clrtoeol();
|
|
|
|
|
|
|
|
scrollok(stdscr, FALSE); /* prevent screen from moving */
|
1998-03-01 12:21:12 +08:00
|
|
|
for (i = 1; i < LINES; i++) {
|
|
|
|
move(i, 0);
|
|
|
|
if ((s = lptr[i-1]) != 0 && (int)strlen(s) > shift)
|
1999-10-24 12:32:42 +08:00
|
|
|
printw("%3ld:%.*s", (long) (lptr+i-lines), COLS-4, s + shift);
|
1997-05-15 12:00:00 +08:00
|
|
|
else
|
1999-10-24 12:32:42 +08:00
|
|
|
printw("%3ld:", (long) (lptr+i-lines));
|
1997-05-15 12:00:00 +08:00
|
|
|
clrtoeol();
|
1998-03-01 12:21:12 +08:00
|
|
|
}
|
1997-05-15 12:00:00 +08:00
|
|
|
setscrreg(1, LINES-1);
|
|
|
|
scrollok(stdscr, TRUE);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* view.c ends here */
|
|
|
|
|