1 /****************************************************************************
2 * Copyright (c) 1998-2016,2017 Free Software Foundation, Inc. *
4 * Permission is hereby granted, free of charge, to any person obtaining a *
5 * copy of this software and associated documentation files (the *
6 * "Software"), to deal in the Software without restriction, including *
7 * without limitation the rights to use, copy, modify, merge, publish, *
8 * distribute, distribute with modifications, sublicense, and/or sell *
9 * copies of the Software, and to permit persons to whom the Software is *
10 * furnished to do so, subject to the following conditions: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 * Except as contained in this notice, the name(s) of the above copyright *
24 * holders shall not be used in advertising or otherwise to promote the *
25 * sale, use or other dealings in this Software without prior written *
27 ****************************************************************************/
29 * view.c -- a silly little viewer program
31 * written by Eric S. Raymond <esr@snark.thyrsus.com> December 1994
32 * to test the scrolling code in ncurses.
34 * modified by Thomas Dickey <dickey@clark.net> July 1995 to demonstrate
35 * the use of 'resizeterm()', and May 2000 to illustrate wide-character
38 * Takes a filename argument. It's a simple file-viewer with various
39 * scroll-up and scroll-down commands.
41 * n -- scroll one line forward
42 * p -- scroll one line back
44 * Either command accepts a numeric prefix interpreted as a repeat count.
45 * Thus, typing `5n' should scroll forward 5 lines in the file.
47 * The way you can tell this is working OK is that, in the trace file,
48 * there should be one scroll operation plus a small number of line
49 * updates, as opposed to a whole-page update. This means the physical
50 * scroll operation worked, and the refresh() code only had to do a
53 * $Id: view.c,v 1.111 2017/10/15 00:56:58 tom Exp $
56 #include <test.priv.h>
57 #include <widechars.h>
58 #include <popup_msg.h>
63 #undef CTRL /* conflict on AIX 5.2 with <sys/ioctl.h> */
65 static void finish(int sig) GCC_NORETURN;
70 #if !defined(__MINGW32__)
75 #if !defined(sun) || !HAVE_TERMIOS_H
77 # include <sys/ioctl.h>
83 /* This is needed to compile 'struct winsize' */
85 #include <sys/stream.h>
90 #define CTRL(x) ((x) & 0x1f)
92 #if defined(SIGWINCH) && defined(TIOCGWINSZ) && HAVE_RESIZE_TERM
99 static int interrupted;
100 static bool waiting = FALSE;
103 static int shift = 0;
104 static bool try_color = FALSE;
107 static NCURSES_CH_T **vec_lines;
108 static NCURSES_CH_T **lptr;
109 static int num_lines;
111 static void usage(void) GCC_NORETURN;
114 failed(const char *msg)
116 fprintf(stderr, "%s\n", msg);
117 ExitProgram(EXIT_FAILURE);
123 static const char *msg[] =
125 "Usage: view [options] file"
128 ," -c use color if terminal supports it"
129 ," -i ignore INT, QUIT, TERM signals"
130 #if defined(KEY_RESIZE)
131 ," -r use old-style sigwinch handler rather than KEY_RESIZE"
133 ," -s start in single-step mode, waiting for input"
135 ," -t trace screen updates"
136 ," -T NUM specify trace mask"
140 for (n = 0; n < SIZEOF(msg); n++)
141 fprintf(stderr, "%s\n", msg[n]);
142 ExitProgram(EXIT_FAILURE);
146 ch_len(NCURSES_CH_T * src)
149 #if USE_WIDEC_SUPPORT
153 #if USE_WIDEC_SUPPORT
155 TEST_CCHAR(src, count, {
171 * Allocate a string into an array of chtype's. If UTF-8 mode is
172 * active, translate the string accordingly.
174 static NCURSES_CH_T *
177 unsigned len = (unsigned) strlen(src);
178 NCURSES_CH_T *dst = typeMalloc(NCURSES_CH_T, len + 1);
180 #if USE_WIDEC_SUPPORT
181 wchar_t wstr[CCHARW_MAX + 1];
189 #endif /* USE_WIDEC_SUPPORT */
191 #if USE_WIDEC_SUPPORT
194 for (j = k = 0; j < len; j++) {
195 #if USE_WIDEC_SUPPORT
196 rc = (size_t) check_mbytes(wch, src + j, len - j, state);
197 if (rc == (size_t) -1 || rc == (size_t) -2) {
201 width = wcwidth(wch);
206 } else if ((l > 0) || (l == CCHARW_MAX)) {
209 if (setcchar(dst + k, wstr, 0, 0, NULL) != OK) {
216 dst[k++] = (chtype) UChar(src[j]);
219 #if USE_WIDEC_SUPPORT
222 if (setcchar(dst + k, wstr, 0, 0, NULL) == OK)
226 setcchar(dst + k, wstr, 0, 0, NULL);
238 if (vec_lines != 0) {
240 for (n = 0; n < num_lines; ++n) {
246 ExitProgram(sig != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
250 show_all(const char *tag)
258 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
259 "%.20s (%3dx%3d) col %d ", tag, LINES, COLS, shift);
260 i = (int) strlen(temp);
261 if ((i + 7) < (int) sizeof(temp)) {
262 _nc_SPRINTF(temp + i, _nc_SLIMIT(sizeof(temp) - (size_t) i)
264 (int) (sizeof(temp) - 7 - (size_t) i),
269 _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
270 "view %.*s", (int) sizeof(temp) - 7, fname);
273 printw("%.*s", COLS, temp);
275 this_time = time((time_t *) 0);
276 _nc_STRNCPY(temp, ctime(&this_time), (size_t) 30);
277 if ((i = (int) strlen(temp)) != 0) {
279 if (move(0, COLS - i - 2) != ERR)
283 scrollok(stdscr, FALSE); /* prevent screen from moving */
284 for (i = 1; i < LINES; i++) {
286 int actual = (int) (lptr + i - vec_lines);
287 if (actual >= num_lines) {
292 printw("%3d:", actual);
294 if ((s = lptr[i - 1]) == 0) {
299 #if USE_WIDEC_SUPPORT
300 add_wchstr(s + shift);
305 #if defined(NCURSES_VERSION) || defined(HAVE_WCHGAT)
307 wchgat(stdscr, -1, WA_NORMAL, my_pair, NULL);
310 setscrreg(1, LINES - 1);
311 scrollok(stdscr, TRUE);
317 * This uses functions that are "unsafe", but it seems to work on SunOS.
318 * Usually: the "unsafe" refers to the functions that POSIX lists which may be
319 * called from a signal handler. Those do not include buffered I/O, which is
320 * used for instance in wrefresh(). To be really portable, you should use the
321 * KEY_RESIZE return (which relies on ncurses' sigwinch handler).
323 * The 'wrefresh(curscr)' is needed to force the refresh to start from the top
324 * of the screen -- some xterms mangle the bitmap while resizing.
329 if (waiting || sig == 0) {
332 if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) {
333 resize_term(size.ws_row, size.ws_col);
335 show_all(sig ? "SIGWINCH" : "interrupt");
341 (void) signal(SIGWINCH, adjust); /* some systems need this */
343 #endif /* CAN_RESIZE */
346 read_file(const char *filename)
357 if (stat(filename, &sb) != 0
358 || (sb.st_mode & S_IFMT) != S_IFREG) {
359 failed("input is not a file");
362 if (sb.st_size == 0) {
363 failed("input is empty");
366 if ((fp = fopen(filename, "r")) == 0) {
367 failed("cannot open input-file");
370 if ((my_blob = malloc((size_t) sb.st_size + 1)) == 0) {
371 failed("cannot allocate memory for input-file");
374 len = fread(my_blob, sizeof(char), (size_t) sb.st_size, fp);
375 my_blob[sb.st_size] = '\0';
378 for (pass = 0; pass < 2; ++pass) {
379 char *base = my_blob;
381 for (j = 0; j < len; ++j) {
382 if (my_blob[j] == '\n') {
387 base = my_blob + j + 1;
392 if (base != (my_blob + j))
395 ((my_vec = typeCalloc(char *, (size_t) k + 2)) == 0)) {
396 failed("cannot allocate line-vector #1");
399 if ((vec_lines = typeCalloc(NCURSES_CH_T *, (size_t) num_lines + 2)) == 0)
400 failed("cannot allocate line-vector #2");
402 Trace(("slurp the file"));
403 for (k = 0; k < num_lines; ++k) {
404 char *buf = my_vec[k];
405 char temp[BUFSIZ], *s, *d;
408 lptr = &vec_lines[k];
410 #if USE_WIDEC_SUPPORT
411 if (lptr == vec_lines) {
412 if (!memcmp("", buf, 3)) {
417 } while ((*d++ = *s++) != '\0');
422 /* convert tabs and nonprinting chars so that shift will work properly */
423 for (s = buf, d = temp, col = 0; (*d = *s) != '\0'; s++) {
434 } else if (*d == '\t') {
436 while ((d - temp) != col)
439 #if USE_WIDEC_SUPPORT
442 if (isprint(UChar(*d))) {
446 _nc_SPRINTF(d, _nc_SLIMIT(sizeof(temp) - (d - buf))
447 "\\%03o", UChar(*s));
449 col = (int) (d - temp);
453 *lptr = ch_dup(temp);
461 main(int argc, char *argv[])
463 static const char *help[] =
466 " q,^Q,ESC - quit this program",
468 " p,<Up> - scroll the viewport up by one row",
469 " n,<Down> - scroll the viewport down by one row",
470 " l,<Left> - scroll the viewport left by one column",
471 " r,<Right> - scroll the viewport right by one column",
473 " h,<Home> - scroll the viewport to top of file",
474 " ^F,<PageDn> - scroll to the next page",
475 " ^B,<PageUp> - scroll to the previous page",
476 " e,<End> - scroll the viewport to end of file",
478 " ^L - repaint using redrawwin()",
480 " 0 through 9 - enter digits for count",
481 " s - use entered count for halfdelay() parameter",
482 " - if no entered count, stop nodelay()",
483 " <space> - begin nodelay()",
489 NCURSES_CH_T **olptr;
492 bool got_number = FALSE;
493 bool ignore_sigs = FALSE;
494 bool single_step = FALSE;
496 bool nonposix_resize = FALSE;
498 const char *my_label = "Input";
500 setlocale(LC_ALL, "");
502 while ((i = getopt(argc, argv, "cirstT:")) != -1) {
512 nonposix_resize = TRUE;
522 int tvalue = (int) strtol(optarg, &next, 0);
523 if (tvalue < 0 || (next != 0 && *next != 0))
525 trace((unsigned) tvalue);
536 if (optind + 1 != argc)
539 read_file(fname = argv[optind]);
543 (void) signal(SIGWINCH, adjust); /* arrange interrupts to resize */
546 InitAndCatch(initscr(), ignore_sigs ? SIG_IGN : finish);
547 keypad(stdscr, TRUE); /* enable keyboard mapping */
548 (void) nonl(); /* tell curses not to do NL->CR/NL on output */
549 (void) cbreak(); /* take input chars one at a time, no wait for \n */
550 (void) noecho(); /* don't echo input */
552 nodelay(stdscr, TRUE);
553 idlok(stdscr, TRUE); /* allow use of insert/delete line */
558 init_pair(my_pair, COLOR_WHITE, COLOR_BLUE);
559 bkgd((chtype) COLOR_PAIR(my_pair));
576 my_label = "interrupt";
584 if ((c < 127) && isdigit(c)) {
586 MvPrintw(0, 0, "Count: ");
590 value = 10 * value + (c - '0');
595 if (got_number && value) {
602 my_label = keyname(c);
607 for (i = 0; i < n; i++)
608 if ((lptr - vec_lines) < (num_lines - LINES + 1))
612 scrl((int) (lptr - olptr));
618 for (i = 0; i < n; i++)
619 if (lptr > vec_lines)
623 scrl((int) (lptr - olptr));
641 if (num_lines > LINES)
642 lptr = (vec_lines + num_lines - LINES + 1);
644 lptr = (vec_lines + (num_lines - 2));
650 if ((lptr - vec_lines) < (num_lines - 5))
653 lptr = (vec_lines + num_lines - 2);
659 if ((lptr - vec_lines) >= LINES)
686 case KEY_RESIZE: /* ignore this; ncurses will repaint */
692 halfdelay(my_delay = n);
694 nodelay(stdscr, FALSE);
698 nodelay(stdscr, FALSE);
703 nodelay(stdscr, TRUE);
714 popup_msg(stdscr, help);
720 if (c >= KEY_MIN || (c > 0 && !isdigit(c))) {
726 finish(0); /* we're done */