X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=test%2Fview.c;h=6e5c2411cf411d02813d3baf8fa67a89d61aba63;hp=bbcecf6bb97f51244d8aa7f0b7749323176af832;hb=52aa842907b31bb56fb5133da3f023b45bd4355f;hpb=3a9b6a3bf0269231bef7de74757a910dedd04e0c;ds=sidebyside diff --git a/test/view.c b/test/view.c index bbcecf6b..6e5c2411 100644 --- a/test/view.c +++ b/test/view.c @@ -1,3 +1,30 @@ +/**************************************************************************** + * Copyright (c) 1998-2009,2010 Free Software Foundation, Inc. * + * * + * Permission is hereby granted, free of charge, to any person obtaining a * + * copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, distribute with modifications, sublicense, and/or sell * + * copies of the Software, and to permit persons to whom the Software is * + * furnished to do so, subject to the following conditions: * + * * + * The above copyright notice and this permission notice shall be included * + * in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * + * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name(s) of the above copyright * + * holders shall not be used in advertising or otherwise to promote the * + * sale, use or other dealings in this Software without prior written * + * authorization. * + ****************************************************************************/ /* * view.c -- a silly little viewer program * @@ -5,9 +32,10 @@ * to test the scrolling code in ncurses. * * modified by Thomas Dickey July 1995 to demonstrate - * the use of 'resizeterm()'. + * the use of 'resizeterm()', and May 2000 to illustrate wide-character + * handling. * - * Takes a filename argument. It's a simple file-viewer with various + * Takes a filename argument. It's a simple file-viewer with various * scroll-up and scroll-down commands. * * n -- scroll one line forward @@ -22,20 +50,22 @@ * scroll operation worked, and the refresh() code only had to do a * partial repaint. * - * $Id: view.c,v 1.20 1997/04/26 18:16:38 tom Exp $ + * $Id: view.c,v 1.81 2010/11/14 01:06:02 tom Exp $ */ #include -#include -#include -#include +#include + +#undef CTRL /* conflict on AIX 5.2 with */ #if HAVE_TERMIOS_H # include #else +#if !defined(__MINGW32__) # include #endif +#endif #if !defined(sun) || !HAVE_TERMIOS_H # if HAVE_SYS_IOCTL_H @@ -43,18 +73,35 @@ # endif #endif +#define my_pair 1 + /* This is needed to compile 'struct winsize' */ -#if SYSTEM_LOOKS_LIKE_SCO +#if NEED_PTEM_H #include #include #endif - -#define MAXLINES 256 /* most lines we can handle */ -static void finish(int sig) GCC_NORETURN; -static void show_all(void); - -#if defined(SIGWINCH) && defined(TIOCGWINSZ) && defined(NCURSES_VERSION) +#if USE_WIDEC_SUPPORT +#if HAVE_MBTOWC && HAVE_MBLEN +#define reset_mbytes(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0)) +#define count_mbytes(buffer,length,state) mblen(buffer,length) +#define check_mbytes(wch,buffer,length,state) \ + (int) mbtowc(&wch, buffer, length) +#define state_unused +#elif HAVE_MBRTOWC && HAVE_MBRLEN +#define reset_mbytes(state) init_mb(state) +#define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state) +#define check_mbytes(wch,buffer,length,state) \ + (int) mbrtowc(&wch, buffer, length, &state) +#else +make an error +#endif +#endif /* USE_WIDEC_SUPPORT */ + +static RETSIGTYPE finish(int sig) GCC_NORETURN; +static void show_all(const char *tag); + +#if defined(SIGWINCH) && defined(TIOCGWINSZ) && HAVE_RESIZE_TERM #define CAN_RESIZE 1 #else #define CAN_RESIZE 0 @@ -62,73 +109,210 @@ static void show_all(void); #if CAN_RESIZE static RETSIGTYPE adjust(int sig); -static int interrupted; +static int interrupted; #endif -static int waiting; -static int shift; +static bool waiting = FALSE; +static int shift = 0; +static bool try_color = FALSE; -static char *fname; -static char *lines[MAXLINES]; -static char **lptr; +static char *fname; +static NCURSES_CH_T **vec_lines; +static NCURSES_CH_T **lptr; +static int num_lines; -#if !HAVE_STRDUP -static char *strdup (char *s) +static void +usage(void) { - char *p; - - p = malloc(strlen(s)+1); - if (p) - strcpy(p,s); - return(p); + static const char *msg[] = + { + "Usage: view [options] file" + ,"" + ,"Options:" + ," -c use color if terminal supports it" + ," -i ignore INT, QUIT, TERM signals" + ," -n NUM specify maximum number of lines (default 1000)" +#if defined(KEY_RESIZE) + ," -r use old-style sigwinch handler rather than KEY_RESIZE" +#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]); + ExitProgram(EXIT_FAILURE); } -#endif /* not HAVE_STRDUP */ -int main(int argc, char *argv[]) +static int +ch_len(NCURSES_CH_T * src) { -FILE *fp; -char buf[BUFSIZ]; -int i; -char **olptr; -int done = FALSE; + int result = 0; +#if USE_WIDEC_SUPPORT + int count; +#endif -#ifdef TRACE - trace(TRACE_UPDATE); +#if USE_WIDEC_SUPPORT + for (;;) { + TEST_CCHAR(src, count, { + ++result; + ++src; + } + , { + break; + }) + } +#else + while (*src++) + result++; #endif + return result; +} - if (argc != 2) { - fprintf(stderr, "usage: view file\n"); - return EXIT_FAILURE; - } else { - fname = argv[1]; - if ((fp = fopen(fname, "r")) == (FILE *)NULL) { - perror(fname); - return EXIT_FAILURE; +/* + * Allocate a string into an array of chtype's. If UTF-8 mode is + * active, translate the string accordingly. + */ +static NCURSES_CH_T * +ch_dup(char *src) +{ + unsigned len = (unsigned) strlen(src); + NCURSES_CH_T *dst = typeMalloc(NCURSES_CH_T, len + 1); + unsigned j, k; +#if USE_WIDEC_SUPPORT + wchar_t wstr[CCHARW_MAX + 1]; + wchar_t wch; + int l = 0; + size_t rc; + int width; +#ifndef state_unused + mbstate_t state; +#endif +#endif /* USE_WIDEC_SUPPORT */ + +#if USE_WIDEC_SUPPORT + reset_mbytes(state); +#endif + for (j = k = 0; j < len; j++) { +#if USE_WIDEC_SUPPORT + rc = (size_t) check_mbytes(wch, src + j, len - j, state); + if (rc == (size_t) -1 || rc == (size_t) -2) + break; + j += rc - 1; + if ((width = wcwidth(wch)) < 0) + break; + if ((width > 0 && l > 0) || l == CCHARW_MAX) { + wstr[l] = L'\0'; + l = 0; + if (setcchar(dst + k, wstr, 0, 0, NULL) != OK) + break; + ++k; } + if (width == 0 && l == 0) + wstr[l++] = L' '; + wstr[l++] = wch; +#else + dst[k++] = (chtype) UChar(src[j]); +#endif + } +#if USE_WIDEC_SUPPORT + if (l > 0) { + wstr[l] = L'\0'; + if (setcchar(dst + k, wstr, 0, 0, NULL) == OK) + ++k; } + wstr[0] = L'\0'; + setcchar(dst + k, wstr, 0, 0, NULL); +#else + dst[k] = 0; +#endif + return dst; +} - (void) signal(SIGINT, finish); /* arrange interrupts to terminate */ +int +main(int argc, char *argv[]) +{ + int MAXLINES = 1000; + FILE *fp; + char buf[BUFSIZ]; + int i; + int my_delay = 0; + NCURSES_CH_T **olptr; + int value = 0; + bool done = FALSE; + bool got_number = FALSE; #if CAN_RESIZE - (void) signal(SIGWINCH, adjust); /* arrange interrupts to resize */ + bool nonposix_resize = FALSE; +#endif + const char *my_label = "Input"; + + setlocale(LC_ALL, ""); + +#ifndef NCURSES_VERSION + /* + * We know ncurses will catch SIGINT if we don't establish our own handler. + * Other versions of curses may/may not catch it. + */ + (void) signal(SIGINT, finish); /* arrange interrupts to terminate */ #endif - (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 */ + while ((i = getopt(argc, argv, "cin:rtT:")) != -1) { + switch (i) { + case 'c': + try_color = TRUE; + break; + case 'i': + CATCHALL(SIG_IGN); + break; + case 'n': + if ((MAXLINES = atoi(optarg)) < 1 || + (MAXLINES + 2) <= 1) + usage(); + break; +#if CAN_RESIZE + case 'r': + nonposix_resize = TRUE; + break; +#endif +#ifdef TRACE + case 'T': + trace((unsigned) atoi(optarg)); + break; + case 't': + trace(TRACE_CALLS); + break; +#endif + default: + usage(); + } + } + if (optind + 1 != argc) + usage(); + + if ((vec_lines = typeCalloc(NCURSES_CH_T *, (size_t) MAXLINES + 2)) == 0) + usage(); + + assert(vec_lines != 0); + + fname = argv[optind]; + if ((fp = fopen(fname, "r")) == 0) { + perror(fname); + ExitProgram(EXIT_FAILURE); + } +#if CAN_RESIZE + if (nonposix_resize) + (void) signal(SIGWINCH, adjust); /* arrange interrupts to resize */ +#endif /* slurp the file */ - for (lptr = &lines[0]; fgets(buf, BUFSIZ, fp) != (char *)NULL; lptr++) { + for (lptr = &vec_lines[0]; (lptr - vec_lines) < MAXLINES; lptr++) { char temp[BUFSIZ], *s, *d; - int col; + int col; - if (lptr - lines >= MAXLINES) { - endwin(); - (void) fprintf(stderr, "%s: %s is too large\n", argv[0], argv[1]); - return EXIT_FAILURE; - } + if (fgets(buf, sizeof(buf), fp) == 0) + break; /* convert tabs so that shift will work properly */ for (s = buf, d = temp, col = 0; (*d = *s) != '\0'; s++) { @@ -137,161 +321,266 @@ int done = FALSE; break; } else if (*d == '\t') { col = (col | 7) + 1; - while ((d-temp) != col) + while ((d - temp) != col) *d++ = ' '; - } else if (isprint(*d)) { + } else +#if USE_WIDEC_SUPPORT + col++, d++; +#else + if (isprint(UChar(*d))) { col++; d++; } else { - sprintf(d, "\\%03o", *s); + sprintf(d, "\\%03o", UChar(*s)); d += strlen(d); - col = (d - temp); + col = (int) (d - temp); } +#endif } - *lptr = strdup(temp); + *lptr = ch_dup(temp); } (void) fclose(fp); + num_lines = (int) (lptr - vec_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 */ + nodelay(stdscr, TRUE); + idlok(stdscr, TRUE); /* allow use of insert/delete line */ + + if (try_color) { + if (has_colors()) { + start_color(); + init_pair(my_pair, COLOR_WHITE, COLOR_BLUE); + bkgd(COLOR_PAIR(my_pair)); + } else { + try_color = FALSE; + } + } - lptr = lines; + lptr = vec_lines; while (!done) { - int n, c; - bool explicit; + int n, c; - show_all(); + if (!got_number) + show_all(my_label); - explicit = FALSE; - n = 0; - for (;;) { + for (;;) { #if CAN_RESIZE - if (interrupted) + if (interrupted) { adjust(0); + my_label = "interrupt"; + } #endif waiting = TRUE; c = getch(); waiting = FALSE; - if (c < 127 && isdigit(c)) { - n = 10 * n + (c - '0'); - explicit = TRUE; + if ((c < 127) && isdigit(c)) { + if (!got_number) { + MvPrintw(0, 0, "Count: "); + clrtoeol(); + } + addch(UChar(c)); + value = 10 * value + (c - '0'); + got_number = TRUE; } else break; } - if (!explicit && n == 0) + if (got_number && value) { + n = value; + } else { n = 1; + } - switch(c) { - case KEY_DOWN: + if (c != ERR) + my_label = keyname(c); + switch (c) { + case KEY_DOWN: case 'n': olptr = lptr; - for (i = 0; i < n; i++) - if (lptr + LINES < lines + MAXLINES && lptr[LINES + 1]) + for (i = 0; i < n; i++) + if ((lptr - vec_lines) < (num_lines - LINES + 1)) lptr++; - else + else break; - wscrl(stdscr, lptr - olptr); - break; + scrl((int) (lptr - olptr)); + break; - case KEY_UP: + case KEY_UP: case 'p': olptr = lptr; - for (i = 0; i < n; i++) - if (lptr > lines) + for (i = 0; i < n; i++) + if (lptr > vec_lines) lptr--; - else + else break; - wscrl(stdscr, lptr - olptr); - break; + scrl((int) (lptr - olptr)); + break; case 'h': case KEY_HOME: - lptr = lines; + lptr = vec_lines; + break; + + case 'e': + case KEY_END: + if (num_lines > LINES) + lptr = vec_lines + num_lines - LINES + 1; + else + lptr = vec_lines; break; case 'r': case KEY_RIGHT: - shift++; + shift += n; break; case 'l': case KEY_LEFT: - if (shift) - shift--; - else + shift -= n; + if (shift < 0) { + shift = 0; beep(); + } break; case 'q': done = TRUE; break; +#ifdef KEY_RESIZE + case KEY_RESIZE: /* ignore this; ncurses will repaint */ + break; +#endif + case 's': + if (got_number) { + halfdelay(my_delay = n); + } else { + nodelay(stdscr, FALSE); + my_delay = -1; + } + break; + case ' ': + nodelay(stdscr, TRUE); + my_delay = 0; + break; + case ERR: + if (!my_delay) + napms(50); + break; default: beep(); + break; + } + if (c >= KEY_MIN || (c > 0 && !isdigit(c))) { + got_number = FALSE; + value = 0; } } - finish(0); /* we're done */ + finish(0); /* we're done */ } -static RETSIGTYPE finish(int sig) +static RETSIGTYPE +finish(int sig) { endwin(); - exit(sig != 0 ? EXIT_FAILURE : EXIT_SUCCESS); +#if NO_LEAKS + if (vec_lines != 0) { + int n; + for (n = 0; n < num_lines; ++n) { + free(vec_lines[n]); + } + free(vec_lines); + } +#endif + ExitProgram(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. + * This uses functions that are "unsafe", but it seems to work on SunOS. + * Usually: the "unsafe" refers to the functions that POSIX lists which may be + * called from a signal handler. Those do not include buffered I/O, which is + * used for instance in wrefresh(). To be really portable, you should use the + * KEY_RESIZE return (which relies on ncurses' sigwinch handler). + * + * 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) +static RETSIGTYPE +adjust(int sig) { - if (waiting || sig == 0) { + if (waiting || sig == 0) { struct winsize size; - if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) { - resizeterm(size.ws_row, size.ws_col); - beep(); - wrefresh(curscr); /* Linux needs this */ - show_all(); - } - interrupted = FALSE; - } else { - interrupted = TRUE; + if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) { + resize_term(size.ws_row, size.ws_col); + wrefresh(curscr); + show_all(sig ? "SIGWINCH" : "interrupt"); } - (void) signal(SIGWINCH, adjust); /* some systems need this */ + interrupted = FALSE; + } else { + interrupted = TRUE; + } + (void) signal(SIGWINCH, adjust); /* some systems need this */ } -#endif /* CAN_RESIZE */ +#endif /* CAN_RESIZE */ -static void show_all(void) +static void +show_all(const char *tag) { - int i; - char temp[BUFSIZ]; - char *s; + int i; + char temp[BUFSIZ]; + NCURSES_CH_T *s; + time_t this_time; #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); + sprintf(temp, "%.20s (%3dx%3d) col %d ", tag, LINES, COLS, shift); + i = (int) strlen(temp); + if ((i + 7) < (int) sizeof(temp)) { + sprintf(temp + i, "view %.*s", + (int) (sizeof(temp) - 7 - (size_t) i), + fname); + } #else - sprintf(temp, "view %.*s", (int)sizeof(temp)-7, fname); + (void) tag; + sprintf(temp, "view %.*s", (int) sizeof(temp) - 7, fname); #endif - move(0,0); - printw("%.*s", COLS, temp); - clrtoeol(); + move(0, 0); + printw("%.*s", COLS, temp); + clrtoeol(); + this_time = time((time_t *) 0); + strcpy(temp, ctime(&this_time)); + if ((i = (int) strlen(temp)) != 0) { + temp[--i] = 0; + if (move(0, COLS - i - 2) != ERR) + printw(" %s", temp); + } - scrollok(stdscr, FALSE); /* prevent screen from moving */ - for (i = 1; i < LINES; i++) { - move(i, 0); - if ((s = lptr[i-1]) != 0 && (int)strlen(s) > shift) - printw("%3d:%.*s", lptr+i-lines, COLS-4, s + shift); - else - printw("%3d:", lptr+i-lines); - clrtoeol(); - } - setscrreg(1, LINES-1); - scrollok(stdscr, TRUE); - refresh(); + scrollok(stdscr, FALSE); /* prevent screen from moving */ + for (i = 1; i < LINES; i++) { + move(i, 0); + printw("%3ld:", (long) (lptr + i - vec_lines)); + clrtoeol(); + if ((s = lptr[i - 1]) != 0) { + int len = ch_len(s); + if (len > shift) { +#if USE_WIDEC_SUPPORT + add_wchstr(s + shift); +#else + addchstr(s + shift); +#endif + } +#if defined(NCURSES_VERSION) || defined(HAVE_WCHGAT) + if (try_color) + wchgat(stdscr, -1, A_NORMAL, my_pair, NULL); +#endif + } + } + setscrreg(1, LINES - 1); + scrollok(stdscr, TRUE); + refresh(); } - -/* view.c ends here */ -