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.57 2003/05/17 21:58:43 tom Exp $
31 #include <test.priv.h>
39 #if !defined(sun) || !HAVE_TERMIOS_H
41 # 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(const char *tag
);
56 #if defined(SIGWINCH) && defined(TIOCGWINSZ) && HAVE_RESIZE_TERM
63 static RETSIGTYPE
adjust(int sig
);
64 static int interrupted
;
67 static bool waiting
= FALSE
;
69 static bool try_color
= FALSE
;
72 static NCURSES_CH_T
**my_lines
;
73 static NCURSES_CH_T
**lptr
;
78 static const char *msg
[] =
80 "Usage: view [options] file"
83 ," -c use color if terminal supports it"
84 ," -i ignore INT, QUIT, TERM signals"
85 ," -n NUM specify maximum number of lines (default 1000)"
86 #if defined(KEY_RESIZE)
87 ," -r use old-style sigwinch handler rather than KEY_RESIZE"
90 ," -t trace screen updates"
91 ," -T NUM specify trace mask"
95 for (n
= 0; n
< SIZEOF(msg
); n
++)
96 fprintf(stderr
, "%s\n", msg
[n
]);
97 ExitProgram(EXIT_FAILURE
);
101 ch_len(NCURSES_CH_T
* src
)
104 #if USE_WIDEC_SUPPORT
107 #if USE_WIDEC_SUPPORT
108 while (getcchar(src
++, NULL
, NULL
, NULL
, NULL
) > 0)
118 * Allocate a string into an array of chtype's. If UTF-8 mode is
119 * active, translate the string accordingly.
121 static NCURSES_CH_T
*
124 unsigned len
= strlen(src
);
125 NCURSES_CH_T
*dst
= typeMalloc(NCURSES_CH_T
, len
+ 1);
127 #if USE_WIDEC_SUPPORT
128 wchar_t wstr
[CCHARW_MAX
+ 1];
136 #if USE_WIDEC_SUPPORT
137 memset(&state
, 0, sizeof(state
));
139 for (j
= k
= 0; j
< len
; j
++) {
140 #if USE_WIDEC_SUPPORT
141 rc
= mbrtowc(&wch
, src
+ j
, len
- j
, &state
);
142 if (rc
== (size_t) -1 || rc
== (size_t) -2)
145 if ((width
= wcwidth(wch
)) < 0)
147 if ((width
> 0 && l
> 0) || l
== CCHARW_MAX
) {
150 if (setcchar(dst
+ k
, wstr
, 0, 0, NULL
) != OK
)
154 if (width
== 0 && l
== 0)
161 #if USE_WIDEC_SUPPORT
164 if (setcchar(dst
+ k
, wstr
, 0, 0, NULL
) == OK
)
167 setcchar(dst
+ k
, L
"", 0, 0, NULL
);
175 main(int argc
, char *argv
[])
182 NCURSES_CH_T
**olptr
;
186 bool got_number
= FALSE
;
188 bool nonposix_resize
= FALSE
;
190 const char *my_label
= "Input";
192 setlocale(LC_ALL
, "");
194 #ifndef NCURSES_VERSION
196 * We know ncurses will catch SIGINT if we don't establish our own handler.
197 * Other versions of curses may/may not catch it.
199 (void) signal(SIGINT
, finish
); /* arrange interrupts to terminate */
202 while ((i
= getopt(argc
, argv
, "cin:rtT:")) != EOF
) {
208 signal(SIGINT
, SIG_IGN
);
209 signal(SIGQUIT
, SIG_IGN
);
210 signal(SIGTERM
, SIG_IGN
);
213 if ((MAXLINES
= atoi(optarg
)) < 1)
218 nonposix_resize
= TRUE
;
233 if (optind
+ 1 != argc
)
236 if ((my_lines
= typeMalloc(NCURSES_CH_T
*, MAXLINES
+ 2)) == 0)
239 fname
= argv
[optind
];
240 if ((fp
= fopen(fname
, "r")) == 0) {
242 ExitProgram(EXIT_FAILURE
);
246 (void) signal(SIGWINCH
, adjust
); /* arrange interrupts to resize */
250 for (lptr
= &my_lines
[0]; (lptr
- my_lines
) < MAXLINES
; lptr
++) {
251 char temp
[BUFSIZ
], *s
, *d
;
254 if (fgets(buf
, sizeof(buf
), fp
) == 0)
257 /* convert tabs so that shift will work properly */
258 for (s
= buf
, d
= temp
, col
= 0; (*d
= *s
) != '\0'; s
++) {
262 } else if (*d
== '\t') {
264 while ((d
- temp
) != col
)
267 #if USE_WIDEC_SUPPORT
270 if (isprint(UChar(*d
))) {
274 sprintf(d
, "\\%03o", UChar(*s
));
280 *lptr
= ch_dup(temp
);
283 length
= lptr
- my_lines
;
285 (void) initscr(); /* initialize the curses library */
286 keypad(stdscr
, TRUE
); /* enable keyboard mapping */
287 (void) nonl(); /* tell curses not to do NL->CR/NL on output */
288 (void) cbreak(); /* take input chars one at a time, no wait for \n */
289 (void) noecho(); /* don't echo input */
290 nodelay(stdscr
, TRUE
);
291 idlok(stdscr
, TRUE
); /* allow use of insert/delete line */
296 init_pair(my_pair
, COLOR_WHITE
, COLOR_BLUE
);
297 bkgd(COLOR_PAIR(my_pair
));
315 my_label
= "interrupt";
321 if ((c
< 127) && isdigit(c
)) {
323 mvprintw(0, 0, "Count: ");
327 value
= 10 * value
+ (c
- '0');
332 if (got_number
&& value
) {
339 my_label
= keyname(c
);
344 for (i
= 0; i
< n
; i
++)
345 if ((lptr
- my_lines
) < (length
- LINES
+ 1))
349 wscrl(stdscr
, lptr
- olptr
);
355 for (i
= 0; i
< n
; i
++)
360 wscrl(stdscr
, lptr
- olptr
);
371 lptr
= my_lines
+ length
- LINES
+ 1;
395 case KEY_RESIZE
: /* ignore this; ncurses will repaint */
400 halfdelay(my_delay
= n
);
402 nodelay(stdscr
, FALSE
);
407 nodelay(stdscr
, TRUE
);
418 if (c
>= KEY_MIN
|| (c
> 0 && !isdigit(c
))) {
424 finish(0); /* we're done */
431 ExitProgram(sig
!= 0 ? EXIT_FAILURE
: EXIT_SUCCESS
);
436 * This uses functions that are "unsafe", but it seems to work on SunOS and
437 * Linux. Usually: the "unsafe" refers to the functions that POSIX lists
438 * which may be called from a signal handler. Those do not include buffered
439 * I/O, which is used for instance in wrefresh(). To be really portable, you
440 * should use the KEY_RESIZE return (which relies on ncurses' sigwinch
443 * The 'wrefresh(curscr)' is needed to force the refresh to start from the top
444 * of the screen -- some xterms mangle the bitmap while resizing.
449 if (waiting
|| sig
== 0) {
452 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &size
) == 0) {
453 resize_term(size
.ws_row
, size
.ws_col
);
454 wrefresh(curscr
); /* Linux needs this */
455 show_all(sig
? "SIGWINCH" : "interrupt");
461 (void) signal(SIGWINCH
, adjust
); /* some systems need this */
463 #endif /* CAN_RESIZE */
466 show_all(const char *tag
)
474 sprintf(temp
, "%s (%3dx%3d) col %d ", tag
, LINES
, COLS
, shift
);
476 sprintf(temp
+ i
, "view %.*s", (int) (sizeof(temp
) - 7 - i
), fname
);
478 sprintf(temp
, "view %.*s", (int) sizeof(temp
) - 7, fname
);
481 printw("%.*s", COLS
, temp
);
483 this_time
= time((time_t *) 0);
484 strcpy(temp
, ctime(&this_time
));
485 if ((i
= strlen(temp
)) != 0) {
487 if (move(0, COLS
- i
- 2) != ERR
)
491 scrollok(stdscr
, FALSE
); /* prevent screen from moving */
492 for (i
= 1; i
< LINES
; i
++) {
494 printw("%3ld:", (long) (lptr
+ i
- my_lines
));
496 if ((s
= lptr
[i
- 1]) != 0) {
499 #if USE_WIDEC_SUPPORT
500 add_wchstr(s
+ shift
);
505 #if defined(NCURSES_VERSION) || defined(HAVE_WCHGAT)
507 wchgat(stdscr
, -1, A_NORMAL
, my_pair
, NULL
);
511 setscrreg(1, LINES
- 1);
512 scrollok(stdscr
, TRUE
);