Home | History | Annotate | Line # | Download | only in EXAMPLES
view.c revision 1.1.2.1
      1 /*
      2  * view.c -- a silly little viewer program
      3  *
      4  * written by Eric S. Raymond <esr (at) snark.thyrsus.com> December 1994
      5  * to test the scrolling code in ncurses.
      6  *
      7  * modified by Thomas Dickey <dickey (at) clark.net> July 1995 to demonstrate
      8  * the use of 'resizeterm()', and May 2000 to illustrate wide-character
      9  * handling.
     10  *
     11  * Takes a filename argument.  It's a simple file-viewer with various
     12  * scroll-up and scroll-down commands.
     13  *
     14  * n	-- scroll one line forward
     15  * p	-- scroll one line back
     16  *
     17  * Either command accepts a numeric prefix interpreted as a repeat count.
     18  * Thus, typing `5n' should scroll forward 5 lines in the file.
     19  *
     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
     24  * partial repaint.
     25  *
     26  * $Id: view.c,v 1.1.2.1 2007/01/21 12:05:54 blymn Exp $
     27  */
     28 
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <sys/types.h>
     32 #include <signal.h>
     33 #ifdef NCURSES
     34 #define _XOPEN_SOURCE_EXTENDED
     35 #include <ncurses.h>
     36 #include <term.h>
     37 #else
     38 #include <curses.h>
     39 #endif /* NCURSES */
     40 #include <locale.h>
     41 #include <assert.h>
     42 #include <ctype.h>
     43 #include <termios.h>
     44 #include <util.h>
     45 #include <unistd.h>
     46 #ifdef HAVE_WCHAR
     47 #include <wchar.h>
     48 #endif /* HAVE_WCHAR */
     49 #ifdef DEBUG
     50 #include <syslog.h>
     51 #endif /* DEBUG */
     52 
     53 #define UChar(c)    ((unsigned char)(c))
     54 #define SIZEOF(table)	(sizeof(table)/sizeof(table[0]))
     55 #define typeMalloc(type,n) (type *) malloc((n) * sizeof(type))
     56 
     57 #define my_pair 1
     58 
     59 #undef CURSES_CH_T
     60 #ifdef HAVE_WCHAR
     61 #define CURSES_CH_T cchar_t
     62 #else
     63 #define CURSES_CH_T chtype
     64 #endif /* HAVE_WCHAR */
     65 
     66 static void finish(int sig);
     67 static void show_all(const char *tag);
     68 
     69 static int shift = 0;
     70 static bool try_color = FALSE;
     71 
     72 static char *fname;
     73 static CURSES_CH_T **my_lines;
     74 static CURSES_CH_T **lptr;
     75 
     76 static void usage(void)
     77 {
     78     static const char *msg[] = {
     79 	    "Usage: view [options] file"
     80 	    ,""
     81 	    ,"Options:"
     82 	    ," -c       use color if terminal supports it"
     83 	    ," -i       ignore INT, QUIT, TERM signals"
     84 	    ," -n NUM   specify maximum number of lines (default 1000)"
     85 #if defined(KEY_RESIZE)
     86 	    ," -r       use old-style sigwinch handler rather than KEY_RESIZE"
     87 #endif
     88 #ifdef TRACE
     89 	    ," -t       trace screen updates"
     90 	    ," -T NUM   specify trace mask"
     91 #endif
     92     };
     93     size_t n;
     94     for (n = 0; n < SIZEOF(msg); n++)
     95 	    fprintf(stderr, "%s\n", msg[n]);
     96     exit( 1 );
     97 }
     98 
     99 static int ch_len(CURSES_CH_T * src)
    100 {
    101     int result = 0;
    102 
    103 #ifdef HAVE_WCHAR
    104     while (getcchar(src++, NULL, NULL, NULL, NULL) > 0)
    105 	    result++;
    106 #else
    107     while (*src++)
    108 	result++;
    109 #endif
    110     return result;
    111 }
    112 
    113 /*
    114  * Allocate a string into an array of chtype's.  If UTF-8 mode is
    115  * active, translate the string accordingly.
    116  */
    117 static CURSES_CH_T * ch_dup(char *src)
    118 {
    119     unsigned len = strlen(src);
    120     CURSES_CH_T *dst = typeMalloc(CURSES_CH_T, len + 1);
    121     unsigned j, k;
    122 #ifdef HAVE_WCHAR
    123     wchar_t wstr[CCHARW_MAX + 1];
    124     wchar_t wch;
    125     int l = 0;
    126     mbstate_t state;
    127     size_t rc;
    128     int width;
    129 #endif
    130 
    131 #ifdef HAVE_WCHAR
    132     mbrtowc( NULL, NULL, 1, &state );
    133 #endif
    134     for (j = k = 0; j < len; j++) {
    135 #ifdef HAVE_WCHAR
    136 	    rc = mbrtowc(&wch, src + j, len - j, &state);
    137 #ifdef DEBUG
    138         syslog( LOG_INFO, "[ch_dup]mbrtowc() returns %d", rc );
    139 #endif /* DEBUG */
    140 	    if (rc == (size_t) -1 || rc == (size_t) -2)
    141 	        break;
    142 	    j += rc - 1;
    143 	    if ((width = wcwidth(wch)) < 0)
    144 	        break;
    145 	    if ((width > 0 && l > 0) || l == CCHARW_MAX) {
    146 	        wstr[l] = L'\0';
    147 	        l = 0;
    148 	        if (setcchar(dst + k, wstr, 0, 0, NULL) != OK)
    149 		        break;
    150 	        ++k;
    151 	    }
    152 	    if (width == 0 && l == 0)
    153 	        wstr[l++] = L' ';
    154 	    wstr[l++] = wch;
    155 #ifdef DEBUG
    156         syslog( LOG_INFO, "[ch_dup]wch=%x", wch );
    157 #endif /* DEBUG */
    158 #else
    159 	    dst[k++] = src[j];
    160 #endif
    161     }
    162 #ifdef HAVE_WCHAR
    163     if (l > 0) {
    164 	    wstr[l] = L'\0';
    165 	    if (setcchar(dst + k, wstr, 0, 0, NULL) == OK)
    166 	        ++k;
    167     }
    168     setcchar(dst + k, L"", 0, 0, NULL);
    169 #else
    170     dst[k] = 0;
    171 #endif
    172     return dst;
    173 }
    174 
    175 int main(int argc, char *argv[])
    176 {
    177     int MAXLINES = 1000;
    178     FILE *fp;
    179     char buf[BUFSIZ];
    180     int i;
    181     int my_delay = 0;
    182     CURSES_CH_T **olptr;
    183     int length = 0;
    184     int value = 0;
    185     bool done = FALSE;
    186     bool got_number = FALSE;
    187     const char *my_label = "Input";
    188 #ifdef HAVE_WCHAR
    189     cchar_t icc;
    190 #endif /* HAVE_WCHAR */
    191 
    192     setlocale(LC_ALL, "");
    193 
    194     (void) signal(SIGINT, finish);	/* arrange interrupts to terminate */
    195 
    196     while ((i = getopt(argc, argv, "cin:rtT:")) != EOF) {
    197 	    switch (i) {
    198 	        case 'c':
    199 	            try_color = TRUE;
    200 	            break;
    201 	        case 'i':
    202 	            signal(SIGINT, SIG_IGN);
    203 	            signal(SIGQUIT, SIG_IGN);
    204 	            signal(SIGTERM, SIG_IGN);
    205 	            break;
    206 	        case 'n':
    207 	            if ((MAXLINES = atoi(optarg)) < 1)
    208 		        usage();
    209 	            break;
    210 #ifdef TRACE
    211 	        case 'T':
    212 	            trace(atoi(optarg));
    213 	            break;
    214 	        case 't':
    215 	            trace(TRACE_CALLS);
    216 	            break;
    217 #endif
    218 	        default:
    219 	            usage();
    220 	    }
    221     }
    222     if (optind + 1 != argc)
    223 	    usage();
    224 
    225     if ((my_lines = typeMalloc(CURSES_CH_T *, MAXLINES + 2)) == 0)
    226 	    usage();
    227 
    228     fname = argv[optind];
    229     if ((fp = fopen(fname, "r")) == 0) {
    230 	    perror(fname);
    231 	    exit( 1 );
    232     }
    233 
    234     /* slurp the file */
    235     for (lptr = &my_lines[0]; (lptr - my_lines) < MAXLINES; lptr++) {
    236 	    char temp[BUFSIZ], *s, *d;
    237 	    int col;
    238 
    239 	    if (fgets(buf, sizeof(buf), fp) == 0)
    240 	        break;
    241 
    242 	    /* convert tabs so that shift will work properly */
    243 	    for (s = buf, d = temp, col = 0; (*d = *s) != '\0'; s++) {
    244 	        if (*d == '\n') {
    245 		        *d = '\0';
    246 		        break;
    247 	        } else if (*d == '\t') {
    248 		        col = (col | 7) + 1;
    249 		        while ((d - temp) != col)
    250 		            *d++ = ' ';
    251 	        } else
    252 #ifdef HAVE_WCHAR
    253 		        col++, d++;
    254 #else
    255 	            if (isprint(UChar(*d))) {
    256 		            col++;
    257 		            d++;
    258 	            } else {
    259 		            sprintf(d, "\\%03o", UChar(*s));
    260 		            d += strlen(d);
    261 		            col = (d - temp);
    262 	            }
    263 #endif
    264 	    }
    265 	    *lptr = ch_dup(temp);
    266     }
    267     (void) fclose(fp);
    268     length = lptr - my_lines;
    269 
    270     (void) initscr();		/* initialize the curses library */
    271     keypad(stdscr, TRUE);	/* enable keyboard mapping */
    272     (void) nonl();	 /* tell curses not to do NL->CR/NL on output */
    273     (void) cbreak(); /* take input chars one at a time, no wait for \n */
    274     (void) noecho();		/* don't echo input */
    275     nodelay(stdscr, TRUE);
    276     idlok(stdscr, TRUE);	/* allow use of insert/delete line */
    277 
    278     if (try_color) {
    279 	    if (has_colors()) {
    280 	        start_color();
    281 	        init_pair(my_pair, COLOR_WHITE, COLOR_BLUE);
    282 	        bkgd(COLOR_PAIR(my_pair));
    283 	    } else {
    284 	        try_color = FALSE;
    285 	    }
    286     }
    287 
    288     lptr = my_lines;
    289     while (!done) {
    290 	    int n;
    291 #ifdef HAVE_WCHAR
    292         wint_t c = 0;
    293         int ret;
    294 #else
    295         int c = 0;
    296 #endif /* HAVE_WCHAR */
    297 
    298 	    if (!got_number)
    299 	        show_all(my_label);
    300 
    301 	    n = 0;
    302 	    for (;;) {
    303             c = 0;
    304 #ifdef HAVE_WCHAR
    305             ret = get_wch( &c );
    306             if ( ret == ERR ) {
    307 	            if (!my_delay)
    308 		            napms(50);
    309                 continue;
    310             }
    311 #ifdef DEBUG
    312             else if ( ret == KEY_CODE_YES )
    313                 syslog( LOG_INFO, "[main]Func key(%x)", c );
    314             else
    315                 syslog( LOG_INFO, "[main]c=%x", c );
    316 #endif /* DEBUG */
    317 #else
    318 	        c = getch();
    319 #ifdef DEBUG
    320             syslog( LOG_INFO, "[main]c='%c'", c );
    321 #endif /* DEBUG */
    322 #endif /* HAVE_WCHAR */
    323 	        if ((c < 127) && isdigit(c)) {
    324 		        if (!got_number) {
    325 		            mvprintw(0, 0, "Count: ");
    326 		            clrtoeol();
    327 		        }
    328 		        addch(c);
    329 		        value = 10 * value + (c - '0');
    330 		        got_number = TRUE;
    331 	        } else
    332 		        break;
    333 	    }
    334 	    if (got_number && value) {
    335 	        n = value;
    336 	    } else {
    337 	        n = 1;
    338 	    }
    339 
    340 #ifdef HAVE_WCHAR
    341 	    if (ret != ERR)
    342             my_label = key_name( c );
    343         else
    344 	        if (!my_delay)
    345 		        napms(50);
    346 #else
    347 	    if (c != ERR)
    348 	        my_label = keyname(c);
    349 #endif /* HAVE_WCHAR */
    350 	    switch (c) {
    351 	        case KEY_DOWN:
    352 #ifdef HAVE_WCHAR
    353             case L'n':
    354 #else
    355 	        case 'n':
    356 #endif /* HAVE_WCHAR */
    357 	            olptr = lptr;
    358 	            for (i = 0; i < n; i++)
    359 		            if ((lptr - my_lines) < (length - LINES + 1))
    360 		                lptr++;
    361 		            else
    362 		                break;
    363 	            wscrl(stdscr, lptr - olptr);
    364 	            break;
    365 
    366 	        case KEY_UP:
    367 #ifdef HAVE_WCHAR
    368             case L'p':
    369 #else
    370 	        case 'p':
    371 #endif /* HAVE_WCHAR */
    372 	            olptr = lptr;
    373 	            for (i = 0; i < n; i++)
    374 		            if (lptr > my_lines)
    375 		                lptr--;
    376 		            else
    377 		                break;
    378 	            wscrl(stdscr, lptr - olptr);
    379 	            break;
    380 
    381 #ifdef HAVE_WCHAR
    382             case L'h':
    383 #else
    384 	        case 'h':
    385 #endif /* HAVE_WCHAR */
    386 	        case KEY_HOME:
    387 	            lptr = my_lines;
    388 	            break;
    389 
    390 #ifdef HAVE_WCHAR
    391             case L'e':
    392 #else
    393 	        case 'e':
    394 #endif /* HAVE_WCHAR */
    395 	        case KEY_END:
    396 	            if (length > LINES)
    397 		            lptr = my_lines + length - LINES + 1;
    398 	            else
    399 		            lptr = my_lines;
    400 	            break;
    401 
    402 #ifdef HAVE_WCHAR
    403             case L'r':
    404 #else
    405 	        case 'r':
    406 #endif /* HAVE_WCHAR */
    407 	        case KEY_RIGHT:
    408 	            shift += n;
    409 	            break;
    410 
    411 #ifdef HAVE_WCHAR
    412             case L'l':
    413 #else
    414 	        case 'l':
    415 #endif /* HAVE_WCHAR */
    416 	        case KEY_LEFT:
    417 	            shift -= n;
    418 	            if (shift < 0) {
    419 		            shift = 0;
    420 		            beep();
    421 	            }
    422 	            break;
    423 
    424 #ifdef HAVE_WCHAR
    425             case L'q':
    426 #else
    427 	        case 'q':
    428 #endif /* HAVE_WCHAR */
    429 	            done = TRUE;
    430 	            break;
    431 
    432 #ifdef KEY_RESIZE
    433 	        case KEY_RESIZE:
    434                 //refresh();
    435 	            break;
    436 #endif
    437 #ifdef HAVE_WCHAR
    438 	        case L's':
    439 #else
    440             case 's':
    441 #endif /* HAVE_WCHAR */
    442 	            if (got_number) {
    443 		            halfdelay(my_delay = n);
    444 	            } else {
    445 		            nodelay(stdscr, FALSE);
    446 		            my_delay = -1;
    447 	            }
    448 	            break;
    449 #ifdef HAVE_WCHAR
    450             case L' ':
    451 #else
    452 	        case ' ':
    453 #endif /* HAVE_WCHAR */
    454 	            nodelay(stdscr, TRUE);
    455 	            my_delay = 0;
    456 	            break;
    457 #ifndef HAVE_WCHAR
    458 	        case ERR:
    459 	            if (!my_delay)
    460 		            napms(50);
    461 	            break;
    462 #endif /* HAVE_WCHAR */
    463 	        default:
    464 	            beep();
    465 	            break;
    466 	    }
    467 	    if (c >= KEY_MIN || (c > 0 && !isdigit(c))) {
    468 	        got_number = FALSE;
    469 	        value = 0;
    470 	    }
    471     }
    472 
    473     finish(0);			/* we're done */
    474 }
    475 
    476 static void finish(int sig)
    477 {
    478     endwin();
    479     exit(sig != 0 ?  1 : 0 );
    480 }
    481 
    482 static void show_all(const char *tag)
    483 {
    484     int i;
    485     char temp[BUFSIZ];
    486     CURSES_CH_T *s;
    487     time_t this_time;
    488 
    489     sprintf(temp, "%s (%3dx%3d) col %d ", tag, LINES, COLS, shift);
    490     i = strlen(temp);
    491     sprintf(temp + i, "view %.*s", (int) (sizeof(temp) - 7 - i), fname);
    492     move(0, 0);
    493     printw("%.*s", COLS, temp);
    494     clrtoeol();
    495     this_time = time((time_t *) 0);
    496     strcpy(temp, ctime(&this_time));
    497     if ((i = strlen(temp)) != 0) {
    498 	    temp[--i] = 0;
    499 	    if (move(0, COLS - i - 2) != ERR)
    500 	        printw("  %s", temp);
    501     }
    502 
    503     scrollok(stdscr, FALSE);	/* prevent screen from moving */
    504     for (i = 1; i < LINES; i++) {
    505 	    move(i, 0);
    506 	    printw("%3ld:", (long) (lptr + i - my_lines));
    507 	    clrtoeol();
    508 	    if ((s = lptr[i - 1]) != 0) {
    509 	        int len = ch_len(s);
    510 	        if (len > shift) {
    511 #ifdef HAVE_WCHAR
    512 		        add_wchstr(s + shift);
    513 #else
    514 		        addchstr(s + shift);
    515 #endif
    516 	        }
    517 	    }
    518     }
    519     setscrreg(1, LINES - 1);
    520     scrollok(stdscr, TRUE);
    521     refresh();
    522 }
    523