worldstone: add -s for statistical profiling
[minix.git] / lib / libcurses / EXAMPLES / view.c
blob74df2a83d21ff20f797751856a4d967ad3b162f2
1 /*
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
9 * handling.
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
24 * partial repaint.
26 * $Id: view.c,v 1.2 2007/05/28 15:01:58 blymn Exp $
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 */
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))
57 #define my_pair 1
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 */
66 static void finish(int sig);
67 static void show_all(const char *tag);
69 static int shift = 0;
70 static bool try_color = FALSE;
72 static char *fname;
73 static CURSES_CH_T **my_lines;
74 static CURSES_CH_T **lptr;
75 static unsigned num_lines;
77 static void usage(void)
79 static const char *msg[] = {
80 "Usage: view [options] file"
81 ,""
82 ,"Options:"
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"
88 #endif
89 #ifdef TRACE
90 ," -t trace screen updates"
91 ," -T NUM specify trace mask"
92 #endif
94 size_t n;
95 for (n = 0; n < SIZEOF(msg); n++)
96 fprintf(stderr, "%s\n", msg[n]);
97 exit( 1 );
100 static int ch_len(CURSES_CH_T * src)
102 int result = 0;
104 #ifdef HAVE_WCHAR
105 while (getcchar(src++, NULL, NULL, NULL, NULL) > 0)
106 result++;
107 #else
108 while (*src++)
109 result++;
110 #endif
111 return result;
115 * Allocate a string into an array of chtype's. If UTF-8 mode is
116 * active, translate the string accordingly.
118 static CURSES_CH_T * ch_dup(char *src)
120 unsigned len = strlen(src);
121 CURSES_CH_T *dst = typeMalloc(CURSES_CH_T, len + 1);
122 unsigned j, k;
123 #ifdef HAVE_WCHAR
124 wchar_t wstr[CCHARW_MAX + 1];
125 wchar_t wch;
126 int l = 0;
127 mbstate_t state;
128 size_t rc;
129 int width;
130 #endif
132 #ifdef HAVE_WCHAR
133 mbrtowc( NULL, NULL, 1, &state );
134 #endif
135 for (j = k = 0; j < len; j++) {
136 #ifdef HAVE_WCHAR
137 rc = mbrtowc(&wch, src + j, len - j, &state);
138 #ifdef DEBUG
139 syslog( LOG_INFO, "[ch_dup]mbrtowc() returns %d", rc );
140 #endif /* DEBUG */
141 if (rc == (size_t) -1 || rc == (size_t) -2)
142 break;
143 j += rc - 1;
144 if ((width = wcwidth(wch)) < 0)
145 break;
146 if ((width > 0 && l > 0) || l == CCHARW_MAX) {
147 wstr[l] = L'\0';
148 l = 0;
149 if (setcchar(dst + k, wstr, 0, 0, NULL) != OK)
150 break;
151 ++k;
153 if (width == 0 && l == 0)
154 wstr[l++] = L' ';
155 wstr[l++] = wch;
156 #ifdef DEBUG
157 syslog( LOG_INFO, "[ch_dup]wch=%x", wch );
158 #endif /* DEBUG */
159 #else
160 dst[k++] = src[j];
161 #endif
163 #ifdef HAVE_WCHAR
164 if (l > 0) {
165 wstr[l] = L'\0';
166 if (setcchar(dst + k, wstr, 0, 0, NULL) == OK)
167 ++k;
169 setcchar(dst + k, L"", 0, 0, NULL);
170 #else
171 dst[k] = 0;
172 #endif
173 return dst;
176 int main(int argc, char *argv[])
178 int MAXLINES = 1000;
179 FILE *fp;
180 char buf[BUFSIZ];
181 int i;
182 int my_delay = 0;
183 CURSES_CH_T **olptr;
184 int length = 0;
185 int value = 0;
186 bool done = FALSE;
187 bool got_number = FALSE;
188 const char *my_label = "Input";
189 #ifdef HAVE_WCHAR
190 cchar_t icc;
191 #endif /* HAVE_WCHAR */
193 setlocale(LC_ALL, "");
195 (void) signal(SIGINT, finish); /* arrange interrupts to terminate */
197 while ((i = getopt(argc, argv, "cin:rtT:")) != EOF) {
198 switch (i) {
199 case 'c':
200 try_color = TRUE;
201 break;
202 case 'i':
203 signal(SIGINT, SIG_IGN);
204 signal(SIGQUIT, SIG_IGN);
205 signal(SIGTERM, SIG_IGN);
206 break;
207 case 'n':
208 if ((MAXLINES = atoi(optarg)) < 1)
209 usage();
210 break;
211 #ifdef TRACE
212 case 'T':
213 trace(atoi(optarg));
214 break;
215 case 't':
216 trace(TRACE_CALLS);
217 break;
218 #endif
219 default:
220 usage();
223 if (optind + 1 != argc)
224 usage();
226 if ((my_lines = typeMalloc(CURSES_CH_T *, MAXLINES + 2)) == 0)
227 usage();
229 fname = argv[optind];
230 if ((fp = fopen(fname, "r")) == 0) {
231 perror(fname);
232 exit( 1 );
235 /* slurp the file */
236 num_lines = 0;
237 for (lptr = &my_lines[0]; (lptr - my_lines) < MAXLINES; lptr++) {
238 char temp[BUFSIZ], *s, *d;
239 int col;
241 if (fgets(buf, sizeof(buf), fp) == 0)
242 break;
244 /* convert tabs so that shift will work properly */
245 for (s = buf, d = temp, col = 0; (*d = *s) != '\0'; s++) {
246 if (*d == '\n') {
247 *d = '\0';
248 break;
249 } else if (*d == '\t') {
250 col = (col | 7) + 1;
251 while ((d - temp) != col)
252 *d++ = ' ';
253 } else
254 #ifdef HAVE_WCHAR
255 col++, d++;
256 #else
257 if (isprint(UChar(*d))) {
258 col++;
259 d++;
260 } else {
261 sprintf(d, "\\%03o", UChar(*s));
262 d += strlen(d);
263 col = (d - temp);
265 #endif
267 *lptr = ch_dup(temp);
268 num_lines++;
270 (void) fclose(fp);
271 length = lptr - my_lines;
273 (void) initscr(); /* initialize the curses library */
274 keypad(stdscr, TRUE); /* enable keyboard mapping */
275 (void) nonl(); /* tell curses not to do NL->CR/NL on output */
276 (void) cbreak(); /* take input chars one at a time, no wait for \n */
277 (void) noecho(); /* don't echo input */
278 nodelay(stdscr, TRUE);
279 idlok(stdscr, TRUE); /* allow use of insert/delete line */
281 if (try_color) {
282 if (has_colors()) {
283 start_color();
284 init_pair(my_pair, COLOR_WHITE, COLOR_BLUE);
285 bkgd(COLOR_PAIR(my_pair));
286 } else {
287 try_color = FALSE;
291 lptr = my_lines;
292 while (!done) {
293 int n;
294 #ifdef HAVE_WCHAR
295 wint_t c = 0;
296 int ret;
297 #else
298 int c = 0;
299 #endif /* HAVE_WCHAR */
301 if (!got_number)
302 show_all(my_label);
304 n = 0;
305 for (;;) {
306 c = 0;
307 #ifdef HAVE_WCHAR
308 ret = get_wch( &c );
309 if ( ret == ERR ) {
310 if (!my_delay)
311 napms(50);
312 continue;
314 #ifdef DEBUG
315 else if ( ret == KEY_CODE_YES )
316 syslog( LOG_INFO, "[main]Func key(%x)", c );
317 else
318 syslog( LOG_INFO, "[main]c=%x", c );
319 #endif /* DEBUG */
320 #else
321 c = getch();
322 #ifdef DEBUG
323 syslog( LOG_INFO, "[main]c='%c'", c );
324 #endif /* DEBUG */
325 #endif /* HAVE_WCHAR */
326 if ((c < 127) && isdigit(c)) {
327 if (!got_number) {
328 mvprintw(0, 0, "Count: ");
329 clrtoeol();
331 addch(c);
332 value = 10 * value + (c - '0');
333 got_number = TRUE;
334 } else
335 break;
337 if (got_number && value) {
338 n = value;
339 } else {
340 n = 1;
343 #ifdef HAVE_WCHAR
344 if (ret != ERR)
345 my_label = key_name( c );
346 else
347 if (!my_delay)
348 napms(50);
349 #else
350 if (c != ERR)
351 my_label = keyname(c);
352 #endif /* HAVE_WCHAR */
353 switch (c) {
354 case KEY_DOWN:
355 #ifdef HAVE_WCHAR
356 case L'n':
357 #else
358 case 'n':
359 #endif /* HAVE_WCHAR */
360 olptr = lptr;
361 for (i = 0; i < n; i++)
362 if ((lptr - my_lines) < (length - LINES + 1))
363 lptr++;
364 else
365 break;
366 wscrl(stdscr, lptr - olptr);
367 break;
369 case KEY_UP:
370 #ifdef HAVE_WCHAR
371 case L'p':
372 #else
373 case 'p':
374 #endif /* HAVE_WCHAR */
375 olptr = lptr;
376 for (i = 0; i < n; i++)
377 if (lptr > my_lines)
378 lptr--;
379 else
380 break;
381 wscrl(stdscr, lptr - olptr);
382 break;
384 #ifdef HAVE_WCHAR
385 case L'h':
386 #else
387 case 'h':
388 #endif /* HAVE_WCHAR */
389 case KEY_HOME:
390 lptr = my_lines;
391 break;
393 #ifdef HAVE_WCHAR
394 case L'e':
395 #else
396 case 'e':
397 #endif /* HAVE_WCHAR */
398 case KEY_END:
399 if (length > LINES)
400 lptr = my_lines + length - LINES + 1;
401 else
402 lptr = my_lines;
403 break;
405 #ifdef HAVE_WCHAR
406 case L'r':
407 #else
408 case 'r':
409 #endif /* HAVE_WCHAR */
410 case KEY_RIGHT:
411 shift += n;
412 break;
414 #ifdef HAVE_WCHAR
415 case L'l':
416 #else
417 case 'l':
418 #endif /* HAVE_WCHAR */
419 case KEY_LEFT:
420 shift -= n;
421 if (shift < 0) {
422 shift = 0;
423 beep();
425 break;
427 #ifdef HAVE_WCHAR
428 case L'q':
429 #else
430 case 'q':
431 #endif /* HAVE_WCHAR */
432 done = TRUE;
433 break;
435 #ifdef KEY_RESIZE
436 case KEY_RESIZE:
437 //refresh();
438 break;
439 #endif
440 #ifdef HAVE_WCHAR
441 case L's':
442 #else
443 case 's':
444 #endif /* HAVE_WCHAR */
445 if (got_number) {
446 halfdelay(my_delay = n);
447 } else {
448 nodelay(stdscr, FALSE);
449 my_delay = -1;
451 break;
452 #ifdef HAVE_WCHAR
453 case L' ':
454 #else
455 case ' ':
456 #endif /* HAVE_WCHAR */
457 nodelay(stdscr, TRUE);
458 my_delay = 0;
459 break;
460 #ifndef HAVE_WCHAR
461 case ERR:
462 if (!my_delay)
463 napms(50);
464 break;
465 #endif /* HAVE_WCHAR */
466 default:
467 beep();
468 break;
470 if (c >= KEY_MIN || (c > 0 && !isdigit(c))) {
471 got_number = FALSE;
472 value = 0;
476 finish(0); /* we're done */
479 static void finish(int sig)
481 endwin();
482 exit(sig != 0 ? 1 : 0 );
485 static void show_all(const char *tag)
487 int i;
488 char temp[BUFSIZ];
489 CURSES_CH_T *s;
490 time_t this_time;
492 sprintf(temp, "%s (%3dx%3d) col %d ", tag, LINES, COLS, shift);
493 i = strlen(temp);
494 sprintf(temp + i, "view %.*s", (int) (sizeof(temp) - 7 - i), fname);
495 move(0, 0);
496 printw("%.*s", COLS, temp);
497 clrtoeol();
498 this_time = time((time_t *) 0);
499 strcpy(temp, ctime(&this_time));
500 if ((i = strlen(temp)) != 0) {
501 temp[--i] = 0;
502 if (move(0, COLS - i - 2) != ERR)
503 printw(" %s", temp);
506 scrollok(stdscr, FALSE); /* prevent screen from moving */
507 for (i = 1; i < LINES; i++) {
508 move(i, 0);
509 printw("%3ld:", (long) (lptr + i - my_lines));
510 clrtoeol();
511 if ((s = lptr[i - 1]) != 0) {
512 if (i < num_lines) {
513 int len = ch_len(s);
514 if (len > shift) {
515 #ifdef HAVE_WCHAR
516 add_wchstr(s + shift);
517 #else
518 addchstr(s + shift);
519 #endif
524 setscrreg(1, LINES - 1);
525 scrollok(stdscr, TRUE);
526 refresh();