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