1 /* Lazyread 2.0  -by Ryan Kulla (aka gt3) Feb 2nd 2003 */
2 /* HomePage: http://www30.brinkster.com/gt3world/myworld.html */
3 /* E-Mail: ambiod@sbcglobal.net */
4 /* License: GPL */
5 
6 #include <curses.h>
7 #include <time.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14 #include <sys/stat.h>
15 #include <errno.h>
16 #include <signal.h>
17 
18 #define BUFMAX 1024
19 #define OFF 0
20 #define ON 1
21 #define UC 1 /* upper case */
22 #define LC 2 /* lower case */
23 #define CR 13 /* carriage return */
24 #define LF 10 /* line feed */
25 
26 struct my_windows {
27     WINDOW *scrollwin;
28     WINDOW *statwin;
29 } *pstat, *pscroll;
30 
31 struct {
32     char *filename;
33     char *display_filename;
34     char *tty_name;
35     FILE *fp;
36     FILE *which;
37     unsigned long int the_file_size;
38     unsigned int piped;
39     float percent;
40     unsigned long int page_num;
41     char homedir[BUFMAX];
42     char lazy_file[BUFMAX];
43     char lazy_tmp[BUFMAX];
44     char lazy_pipe[BUFMAX];
45 } lfile = { NULL, NULL, NULL, NULL, NULL, 0, 1, 0, 1 };
46 
47 struct lazy_options {
48     unsigned long int default_speed;
49     char *special_word;
50     char *editor;
51     char *use_color;
52     unsigned int want_color;
53     unsigned int y;
54     unsigned int pos_changed;
55     unsigned int view_normal;
56     unsigned int case_change;
57     unsigned int case_type;
58     unsigned int scrollmode_chars;
59     unsigned int statusbar;
60     unsigned int auto_pause;
61     unsigned int beep_ok;
62     unsigned int reshow_statusbar;
63     unsigned int highlight;
64     unsigned int new_speed;
65 } lop = { 1000, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 };
66 
67 SCREEN *STDIN_SCREEN;
68 char *progname;
69 
70 void scan_command_line(int, char **);
71 void lazy_colors(void);
72 void scroll_it(unsigned int, int, char *);
73 void usage(void);
74 void quit_cleanly(void);
75 void get_stats(unsigned long int, unsigned long int);
76 void highlight_word(char *, unsigned int, unsigned int, unsigned int, unsigned long int, unsigned long int);
77 void user_input(unsigned int *, unsigned int, unsigned int *, unsigned long int, unsigned long int);
78 void do_options(unsigned int, int, char *);
79 unsigned int get_key(void);
80 int char_check(char *);
81 long line_nums(FILE *);
82 void change_case(char *, int);
83 void char_scroll(unsigned int);
84 void show_info(unsigned int, unsigned int, unsigned long int, unsigned long int);
85 int file_size(FILE *);
86 int check_if_pdf(char *);
87 void lesspipe(void);
88 void fmt(void);
89 void get_editor(void);
90 int start_editor(unsigned long int);
91 void strip_extra_blanks(void);
92 char *get_basename(char *);
93 char *str_trunc(char *, int);
94 void my_perror(char *);
95 void cperror(char *);
96 void catch_sigwinch(int signo);
97 void catch_sigint(int signo);
98 void signal_setup(void);
99 void create_windows(void);
100 void check_homedir(void);
101 void check_stdin(void);
102 FILE *open_tty(char *);
103 
main(int argc,char ** argv)104 int main(int argc, char **argv)
105 {
106     signal_setup();
107     progname = argv[0];
108     check_homedir();
109     get_editor();
110     scan_command_line(argc, argv);
111     unlink(lfile.lazy_file);
112     if (lfile.piped)
113         unlink(lfile.lazy_pipe);
114     clear();
115     refresh();
116     endwin();
117     return 0;
118 }
119 
check_homedir(void)120 void check_homedir(void)
121 {
122 char *home = getenv("HOME");
123 
124     if (home) {
125         snprintf(lfile.homedir, (strlen(home) + 12), "%s/.lazyread/", home);
126         if ((access(lfile.homedir, F_OK|W_OK)) != 0)
127             if (mkdir(lfile.homedir, S_IRUSR|S_IWUSR|S_IXUSR) != 0)
128                 my_perror(lfile.homedir);
129     }
130     snprintf(lfile.lazy_file, sizeof lfile.lazy_file, "%slazy_file", lfile.homedir);
131     snprintf(lfile.lazy_tmp, sizeof lfile.lazy_tmp, "%slazy_tmp", lfile.homedir);
132     if (lfile.piped)
133         snprintf(lfile.lazy_pipe, sizeof lfile.lazy_pipe, "%spiped", lfile.homedir);
134 
135 }
136 
check_stdin(void)137 void check_stdin(void)
138 {
139 /* do this before any initscr()'ing due to ncurses tty i/o handling
140    when input is piped from another program */
141 char stdin_buf[BUFMAX];
142 FILE *fp, *input, *output;
143 
144     if (lfile.piped) {
145         input = output = open_tty(lfile.tty_name);
146 
147         if (!(fp = fopen(lfile.lazy_pipe, "w")))
148             my_perror("fopen()");
149         while (fgets(stdin_buf, sizeof stdin_buf, stdin)) {
150             fprintf(fp, "%s", stdin_buf);
151         }
152         fclose(fp);
153         STDIN_SCREEN = newterm((char *)0, output, input);
154     }
155 }
156 
open_tty(char * tty_path)157 FILE *open_tty(char *tty_path)
158 {
159 FILE *fp;
160 struct stat sb;
161 
162     if (stat(tty_path, &sb) < 0)
163         my_perror(tty_path);
164     if ((sb.st_mode & S_IFMT) != S_IFCHR) {
165         errno = ENOTTY;
166         my_perror(tty_path);
167     }
168 
169     if (!(fp = fopen(tty_path, "a+")))
170         my_perror("fopen()");
171     if (fp == 0)
172         my_perror(tty_path);
173 
174     return fp;
175 }
176 
create_windows(void)177 void create_windows(void)
178 {
179     initscr();
180     if (lfile.piped) {
181         set_term(STDIN_SCREEN); /* switch to a real tty */
182         refresh();
183         endwin();
184     }
185     cbreak();
186     noecho();
187     nonl();
188     intrflush(stdscr, FALSE);
189     keypad(stdscr, TRUE);
190     curs_set(FALSE);
191     if (lop.want_color)
192         lazy_colors();
193 
194     if (!(pscroll = (struct my_windows *)malloc(sizeof(struct my_windows))))
195         cperror("malloc()");
196 
197     pscroll->scrollwin = newwin(LINES, 0, 0, 0);
198 
199     if (!(pstat = (struct my_windows *)malloc(sizeof(struct my_windows))))
200         cperror("malloc()");
201 
202     pstat->statwin = newwin(1, COLS, LINES - 1, 0);
203     refresh();
204 }
205 
scan_command_line(int argc,char ** argv)206 void scan_command_line(int argc, char **argv)
207 {
208 int optch, opt;
209 static char optstring[] = "s:p:f:w:c:navhbluxmt:";
210 char speed[20], position[3], *filename_nodashf;
211 unsigned int scroll_speed = lop.default_speed;
212 
213     if (argc < 2)
214         usage();
215     if (argv[1])
216         filename_nodashf = argv[1];
217     for (opt = 1; opt < argc; opt++) {
218         if (!strncmp(argv[opt], "-", 1))
219             break;
220         else { /* no options just filename */
221             lfile.piped = FALSE;
222             if (strlen(argv[opt]) > BUFMAX)
223                 usage();
224             if (!(lfile.filename = (char *)malloc(strlen(argv[opt])+1)))
225                 my_perror("malloc()");
226             strncpy(lfile.filename, argv[opt], strlen(argv[opt]));
227             if (!(lfile.fp = fopen(lfile.filename, "r")))
228                 my_perror("fopen()");
229         }
230     }
231 
232     while ((optch = getopt(argc, argv, optstring)) != -1)
233         switch (optch) {
234             case 's':
235                 if (!char_check(optarg))
236                     usage();
237                 strncpy(speed, optarg, 20);
238                 scroll_speed = (unsigned int)atoi(speed);
239                 break;
240             case 'p':
241                 if (!char_check(optarg))
242                     usage();
243                 strncpy(position, optarg, 3);
244                 lop.pos_changed = (unsigned int)atoi(position);
245                 break;
246             case 'f':
247                 lfile.piped = FALSE;
248                 if (strlen(optarg) > BUFMAX)
249                     usage();
250                 if (!(lfile.filename = (char *)malloc(strlen(optarg)+1)))
251                     my_perror("malloc()");
252                 strncpy(lfile.filename, optarg, strlen(optarg));
253                 if (!(lfile.fp = fopen(lfile.filename, "r")))
254                     my_perror("fopen()");
255                 break;
256             case 't':
257                 if (!(lfile.tty_name = (char *)malloc(strlen(optarg)+1)))
258                     my_perror("malloc()");
259                 strncpy(lfile.tty_name, optarg, strlen(optarg));
260                 break;
261             case 'w':
262                 if (strlen(optarg) > BUFMAX)
263                     usage();
264                 if (!(lop.special_word = (char *)malloc(strlen(optarg)+1)))
265                     my_perror("malloc()");
266                 strncpy(lop.special_word, optarg, strlen(optarg));
267                 break;
268             case 'n':
269                 lop.view_normal = TRUE;
270                 break;
271             case 'u':
272                 lop.case_type = UC;
273                 lop.case_change = TRUE;
274                 break;
275             case 'l':
276                 lop.case_type = LC;
277                 lop.case_change = TRUE;
278                 break;
279             case 'c':
280                 lop.want_color = 1;
281                 if (!(lop.use_color = (char *)malloc(strlen(optarg)+1)))
282                     my_perror("malloc");
283                 strncpy(lop.use_color, optarg, strlen(optarg));
284                 break;
285             case 'x':
286                 lop.statusbar = FALSE;
287                 break;
288             case 'v':
289                 endwin();
290                 printf("lazyread 2.0  By Ryan Kulla\n");
291                 exit(EXIT_SUCCESS);
292                 break;
293             case 'h':
294                 usage();
295                 break;
296             case 'a':
297                 lop.auto_pause = TRUE;
298                 break;
299             case 'm':
300                 lop.scrollmode_chars = TRUE;
301                 break;
302             case 'b':
303                 lop.beep_ok = TRUE;
304                 break;
305             default:
306                 usage();
307                 break;
308         }
309         do_options(scroll_speed, argc, filename_nodashf);
310 }
311 
lazy_colors(void)312 void lazy_colors(void)
313 {
314     if (has_colors() == FALSE)
315         cperror("Your terminal does not support colors");
316 
317     start_color();
318     if (!strncmp(lop.use_color, "red", 3))
319         assume_default_colors(COLOR_RED, COLOR_BLACK);
320     if (!strncmp(lop.use_color, "bgred", 5))
321         assume_default_colors(COLOR_WHITE, COLOR_RED);
322     if (!strncmp(lop.use_color, "bgblue", 6))
323         assume_default_colors(COLOR_WHITE, COLOR_BLUE);
324     if (!strncmp(lop.use_color, "blue", 4))
325         assume_default_colors(COLOR_BLUE, COLOR_BLACK);
326     if (!strncmp(lop.use_color, "green", 5))
327         assume_default_colors(COLOR_GREEN, COLOR_BLACK);
328     if (!strncmp(lop.use_color, "bggreen", 7))
329         assume_default_colors(COLOR_BLUE, COLOR_GREEN);
330     if (!strncmp(lop.use_color, "magenta", 7))
331         assume_default_colors(COLOR_MAGENTA, COLOR_BLACK);
332     if (!strncmp(lop.use_color, "bgmagenta", 9))
333         assume_default_colors(COLOR_WHITE, COLOR_MAGENTA);
334     if (!strncmp(lop.use_color, "yellow", 6))
335         assume_default_colors(COLOR_YELLOW, COLOR_BLACK);
336     if (!strncmp(lop.use_color, "bgyellow", 8))
337         assume_default_colors(COLOR_WHITE, COLOR_YELLOW);
338     if (!strncmp(lop.use_color, "bgwhite", 7))
339         assume_default_colors(COLOR_BLACK, COLOR_WHITE);
340     if (!strncmp(lop.use_color, "bgblack", 7))
341         assume_default_colors(COLOR_WHITE, COLOR_BLACK);
342 }
343 
do_options(unsigned int scroll_speed,int argc,char * filename_nodashf)344 void do_options(unsigned int scroll_speed, int argc, char *filename_nodashf)
345 {
346     if (lfile.piped) {
347         lfile.filename = lfile.lazy_pipe;
348         check_stdin();
349     }
350 
351     lesspipe();
352     fmt();
353 
354     if (!lop.view_normal)
355         strip_extra_blanks();
356 
357     create_windows();
358 
359     if (lfile.filename)
360         lfile.display_filename = str_trunc(get_basename(lfile.filename), 15);
361 
362     if (!lop.special_word)
363         lop.special_word = "lazyread";
364 
365     if (scroll_speed)
366          scroll_it(scroll_speed, argc, filename_nodashf);
367     else
368         usage();
369 }
370 
lesspipe(void)371 void lesspipe(void)
372 {
373 FILE *fp_read, *fp_write;
374 char *c, buf[BUFMAX], command[BUFMAX], lesscommand[BUFMAX], *lesspipe;
375 unsigned long int empty = 0;
376 char qfilename[BUFMAX]; /* quoted filename */
377 
378     printf("Loading..\n");
379     /* $LESSOPEN will look like:  |/usr/bin/lesspipe.sh %s */
380     lesspipe = "|/usr/bin/lesspipe.sh %s";
381     if (lesspipe) {
382         /* strip off the leading | */
383         if ((c = strchr(lesspipe, '|'))) {
384             strncpy(lesscommand, c + 1, strlen(c + 1));
385             /* redirection */
386             if (check_if_pdf(lfile.filename))
387                 strncat(lesscommand, " - > ", 5); /* pdftotext needs the - */
388             else
389                 strncat(lesscommand, " > ", 3);
390             strncat(lesscommand, lfile.lazy_file, strlen(lfile.lazy_file));
391             /* add quotes for filenames containing spaces: */
392             snprintf(qfilename, sizeof qfilename, "\"%s\"", lfile.filename);
393             snprintf(command, (strlen(lesscommand) + strlen(qfilename)), lesscommand, qfilename);
394         }
395         system(command); /* run lesspipe.sh */
396     }
397 
398     if (!(fp_write = fopen(lfile.lazy_file, "r")))
399         my_perror("fopen()");
400     if (file_size(fp_write) == 0)
401         empty = 1;
402     fclose(fp_write);
403 
404     if (empty) {
405     /* lesspipe didn't have any output, make the file ourselves */
406         if (!(fp_read = fopen(lfile.filename, "r")))
407             my_perror("fopen()");
408         if (!(fp_write = fopen(lfile.lazy_file, "w")))
409             my_perror("fopen()");
410         while (fgets(buf, sizeof buf, fp_read)) {
411             fprintf(fp_write, "%s", buf);
412         }
413         fclose(fp_read);
414         fclose(fp_write);
415     }
416 }
417 
check_if_pdf(char * filename)418 int check_if_pdf(char *filename)
419 {
420 char *c;
421 
422     if (strstr(filename, ".pdf")) {
423         if ((c = strrchr(filename, '.')))
424             if (!strncmp(c, ".pdf", 4))
425                 return 1;
426     }
427 
428     return 0;
429 }
430 
fmt(void)431 void fmt(void)
432 {
433 char command[BUFMAX];
434 
435     snprintf(command, sizeof command, "fmt -s %s > %s", lfile.lazy_file, lfile.lazy_tmp);
436     system(command);
437 
438     if (unlink(lfile.lazy_file))
439         my_perror("unlink()");
440     if (link(lfile.lazy_tmp, lfile.lazy_file) != 0)
441         my_perror("link()");
442     if (unlink(lfile.lazy_tmp))
443         my_perror("unlink()");
444 }
445 
strip_extra_blanks(void)446 void strip_extra_blanks(void)
447 {
448 FILE *fp_read, *fp_write;
449 char buf[BUFMAX];
450 unsigned int blank = 0;
451 
452     if (!(fp_read = fopen(lfile.lazy_file, "r+")))
453         my_perror("fopen()");
454     if (!(fp_write = fopen(lfile.lazy_tmp, "w")))
455         my_perror("fopen()");
456     while (fgets(buf, sizeof buf, fp_read)) {
457         /* Don't print 2+ blank lines */
458         if (buf[0] == CR || buf[0] == LF)
459             blank++;
460         else
461             blank = 0;
462         if (blank == 2) {
463             blank = 1;
464             continue;
465         }
466         fprintf(fp_write, "%s", buf);
467     }
468     fclose(fp_read);
469     fclose(fp_write);
470     if (unlink(lfile.lazy_file))
471         my_perror("unlink()");
472     if (link(lfile.lazy_tmp, lfile.lazy_file) != 0)
473         my_perror("link()");
474     if (unlink(lfile.lazy_tmp))
475         my_perror("unlink()");
476 }
477 
scroll_it(unsigned int scroll_speed,int argc,char * filename_nodashf)478 void scroll_it(unsigned int scroll_speed, int argc, char *filename_nodashf)
479 {
480 char buf[BUFMAX], *s;
481 unsigned int origspeed = scroll_speed, toggle = ON;
482 unsigned long int total_lines = 0, line = 0;
483 
484     lop.y = LINES - 2;
485     if (lop.pos_changed)
486         lop.y = lop.pos_changed;
487     if ((lop.y > LINES - 2) || (lop.y < 1))
488         usage();
489 
490     if (!lfile.piped)  { /* if they used -f */
491         if (!(lfile.fp = fopen(lfile.lazy_file, "r")))
492             cperror("fopen()");
493         lfile.which = lfile.fp;
494         total_lines = line_nums(lfile.which);
495     } else {
496         if (!(lfile.which = fopen(lfile.lazy_pipe, "r")))
497             cperror("fopen()");
498         lfile.display_filename = "piped output";
499         total_lines = line_nums(lfile.which);
500     }
501     lfile.the_file_size = file_size(lfile.which);
502 
503     if (lop.scrollmode_chars)
504         char_scroll(scroll_speed);
505     else {
506         scrollok(pscroll->scrollwin, TRUE);
507         while ((s = fgets(buf, sizeof(buf), lfile.which))) { /* scroll time */
508             flushinp();
509             line++;
510             if (lop.statusbar)
511                 if (toggle)
512                     get_stats(total_lines, line);
513             highlight_word(buf, scroll_speed, origspeed, toggle, total_lines, line);
514             if (lop.case_change)
515                 change_case(buf, lop.case_type);
516             mvwprintw(pscroll->scrollwin, lop.y, 0, "%s", buf);
517             napms(scroll_speed);
518             scroll(pscroll->scrollwin);
519             wrefresh(pscroll->scrollwin);
520             user_input(&scroll_speed, origspeed, &toggle, total_lines, line);
521         }
522         get_stats(total_lines, line); /* see stats at eof */
523         wgetch(pscroll->scrollwin);
524         fclose(lfile.which);
525     }
526 }
527 
char_scroll(unsigned int scroll_speed)528 void char_scroll(unsigned int scroll_speed)
529 {
530 unsigned int i, line = 0;
531 char buf[BUFMAX], *s;
532 unsigned int origspeed = scroll_speed, toggle = ON;
533 unsigned long int total_lines = line_nums(lfile.which);
534 
535     while ((s = fgets(buf, sizeof(buf), lfile.which))) {
536         line++;
537         highlight_word(buf, scroll_speed, origspeed, toggle, total_lines, line);
538         if (lop.case_change)
539             change_case(buf, lop.case_type);
540         if (lop.highlight) {
541             lop.highlight = FALSE;
542             wclear(pscroll->scrollwin);
543             wrefresh(pscroll->scrollwin);
544             continue;
545         }
546         for (i = 0; buf[i] != '\0'; i++) {
547             if (lop.statusbar)
548                 if (toggle)
549                     get_stats(total_lines, line);
550             if (lop.reshow_statusbar) {
551                 get_stats(total_lines, line); /* see status bar */
552                 lop.reshow_statusbar = FALSE;
553             }
554             if (lop.pos_changed)
555                 mvwprintw(pscroll->scrollwin, lop.y, i, "%c", buf[i]);
556             else
557                 mvwprintw(pscroll->scrollwin, lop.y / 2, i, "%c", buf[i]);
558             napms(scroll_speed);
559             wrefresh(pscroll->scrollwin);
560             user_input(&scroll_speed, origspeed, &toggle, total_lines, line);
561         }
562         wclear(pscroll->scrollwin);
563         wrefresh(pscroll->scrollwin);
564     }
565     get_stats(total_lines, line); /* see stats at eof */
566     wgetch(pscroll->scrollwin);
567     fclose(lfile.which);
568 }
569 
file_size(FILE * fp)570 int file_size(FILE *fp)
571 {
572 unsigned long int len = 0;
573 
574     fseek(fp, 0, SEEK_END);
575     len = ftell(fp);
576     fseek(fp, 0, SEEK_SET);
577 
578     return(len);
579 }
580 
change_case(char * buf,int choice)581 void change_case(char *buf, int choice)
582 {
583 char *p;
584 
585     for (p = buf; *p != '\0'; p++) {
586         if (choice == LC)
587             *p = tolower(*p);
588         else
589             *p = toupper(*p);
590     }
591 }
592 
highlight_word(char * buf,unsigned int scroll_speed,unsigned int origspeed,unsigned int toggle,unsigned long int total_lines,unsigned long int line)593 void highlight_word(char *buf, unsigned int scroll_speed, unsigned int origspeed, unsigned int toggle, unsigned long int total_lines, unsigned long int line)
594 {
595 unsigned int i = 0;
596 
597     /* highlight the entire line special_word is on */
598     if (strstr(buf, lop.special_word)) {
599         lop.highlight = TRUE;
600         wattrset(pscroll->scrollwin, A_BOLD);
601         if (lop.beep_ok)
602             beep();
603 
604         if (lop.scrollmode_chars) {
605             /* print again to see correctly */
606             for (i = 0; buf[i] != '\0'; i++) {
607                 if (lop.statusbar)
608                     if (toggle)
609                         get_stats(total_lines, line);
610                 if (lop.pos_changed)
611                     mvwprintw(pscroll->scrollwin, lop.y, i, "%c", buf[i]);
612                 else
613                     mvwprintw(pscroll->scrollwin, lop.y / 2, i, "%c", buf[i]);
614                 user_input(&scroll_speed, origspeed, &toggle, total_lines, line);
615                 napms(scroll_speed);
616                 wrefresh(pscroll->scrollwin);
617             }
618         }
619         if (lop.auto_pause) {
620             wrefresh(pscroll->scrollwin);
621             nodelay(stdscr, FALSE);
622             getch();
623         }
624     }
625     else
626         wattrset(pscroll->scrollwin, A_NORMAL);
627 }
628 
get_stats(unsigned long int total_lines,unsigned long int line)629 void get_stats(unsigned long int total_lines, unsigned long int line)
630 {
631 time_t now;
632 struct tm *tmptr;
633 char sdate[64];
634 
635     time(&now);
636     tmptr = localtime(&now);
637     strftime(sdate, sizeof sdate, "%a %b %d  %I:%M:%S%p", tmptr);
638 
639     lfile.percent = (((float)line / (float)total_lines) * 100);
640 
641     wbkgd(pstat->statwin, A_REVERSE);
642 
643     if ((line % LINES) == 0)
644         lfile.page_num++;
645     mvwprintw(pstat->statwin, 0, 0, "%ld/%ld - %.0f%%  Page: %ld - %s", line,
646 total_lines, lfile.percent, lfile.page_num, lfile.display_filename);
647 
648     mvwprintw(pstat->statwin, 0, COLS - 23, "%s", sdate);
649 
650     wrefresh(pstat->statwin);
651 }
652 
get_editor(void)653 void get_editor(void)
654 {
655     if (getenv("VISUAL"))
656         lop.editor = getenv("VISUAL");
657     else if (getenv("EDITOR"))
658         lop.editor = getenv("EDITOR");
659 
660    if ((access(lop.editor, F_OK)) != 0) {
661        if (!(access("/bin/vi", F_OK)))
662            lop.editor = "/bin/vi";
663        else
664            lop.editor = "None";
665     }
666 }
667 
start_editor(unsigned long int line)668 int start_editor(unsigned long int line)
669 {
670 char *editor_base, line_str[BUFMAX];
671 pid_t pid, wpid;
672 int status;
673 
674     if ((strncmp(lop.editor, "None", 4)) != 0) {
675         editor_base = get_basename(lop.editor);
676         snprintf(line_str, sizeof line_str, "+%ld", line);
677         def_prog_mode();
678 
679         pid = fork();
680         if (pid == -1) {
681            cperror("fork()");
682         } else if (pid == 0) {
683             execlp(lop.editor, editor_base, line_str, lfile.filename, NULL);
684             cperror("execlp()");
685         } else {
686             wpid = wait(&status);
687             if (wpid == -1)
688                 cperror("wait()");
689             if (wpid != pid)
690                 exit(1);
691         }
692         clear();
693         refresh();
694         reset_prog_mode();
695         refresh();
696     }
697     return 0;
698 }
699 
get_basename(char * editor)700 char *get_basename(char *editor)
701 {
702 char *c;
703 
704     if ((c = strrchr(editor, '/')))
705         return (c + 1);
706     else
707         return editor;
708 }
709 
user_input(unsigned int * scroll_speed,unsigned int origspeed,unsigned int * toggle,unsigned long int total_lines,unsigned long int line)710 void user_input(unsigned int *scroll_speed, unsigned int origspeed, unsigned
711 int *toggle, unsigned long int total_lines, unsigned long int line)
712 {
713     switch (get_key()) {
714         case 1:
715             if (*scroll_speed != 1) /* speed up */
716                 *scroll_speed = 1;
717             else {
718                 if (lop.new_speed)
719                     *scroll_speed = lop.new_speed;
720                 else
721                     *scroll_speed = origspeed; /* stop speeding */
722             }
723             break;
724         case 2:
725             quit_cleanly();
726             break;
727         case 3:
728             *scroll_speed = origspeed;
729             lop.new_speed = 0;
730             break;
731         case 4:
732             if (lop.statusbar)
733                 if (toggle)
734                     get_stats(total_lines, line);
735             nodelay(stdscr, FALSE);
736             getch();
737             break;
738         case 5:
739             if (!lop.statusbar)
740                 lop.statusbar = ON;
741             *toggle = ON;
742             break;
743         case 6:
744             if (!lop.statusbar)
745                break;
746             *toggle = OFF;
747             wbkgd(pstat->statwin, A_NORMAL);
748             wclear(pstat->statwin);
749             wrefresh(pstat->statwin);
750             break;
751         case 7:
752             wclear(pscroll->scrollwin);
753             wrefresh(pscroll->scrollwin);
754             break;
755         case 8:
756             *scroll_speed -= ((*scroll_speed * 25) / 100);
757             lop.new_speed = *scroll_speed;
758             break;
759         case 9:
760             *scroll_speed += ((*scroll_speed * 25) / 100);
761             lop.new_speed = *scroll_speed;
762             break;
763         case 10:
764             lop.auto_pause ^= 1; /* toggle */
765             break;
766         case 11:
767             if (lop.beep_ok)
768                 beep();
769             show_info(*scroll_speed, origspeed, total_lines, line);
770             break;
771         case 12:
772             start_editor(line);
773             break;
774     }
775 }
776 
get_key(void)777 unsigned int get_key(void)
778 {
779 chtype key;
780 
781     nodelay(stdscr, TRUE);
782     if ((key = getch()) != (unsigned long int)ERR)
783         if (key == 32)  return 1;
784         if (key == 'q') return 2;
785         if (key == 'o') return 3;
786         if (key == 'p') return 4;
787         if (key == 'v') return 5;
788         if (key == 'n') return 6;
789         if (key == 'c') return 7;
790         if (key == 'f') return 8;
791         if (key == 's') return 9;
792         if (key == 'a') return 10;
793         if (key == 'i') return 11;
794         if (key == 'e') return 12;
795 
796     return 0;
797 }
798 
show_info(unsigned int scroll_speed,unsigned int origspeed,unsigned long int total_lines,unsigned long int line)799 void show_info(unsigned int scroll_speed, unsigned int origspeed, unsigned long int total_lines, unsigned long int line)
800 {
801 char buf[BUFMAX], *current_dir;
802 char *header_msg = "Lazyread 2.0  [Press any key]";
803 
804     if (!getcwd(buf, BUFMAX))
805         cperror("getcwd()");
806     if (strlen(buf) > (COLS - 18))
807         current_dir = str_trunc(buf, (COLS - 18));
808     else
809         current_dir = buf;
810 
811     def_prog_mode(); /* save the old screen */
812     clear();
813     refresh();
814 
815     box(stdscr, 0, 0);
816     attrset(A_BOLD);
817     mvprintw(0, ((COLS / 2) - (strlen(header_msg) / 2)), "%s", header_msg);
818     attrset(A_NORMAL);
819     mvhline(1, 1, ACS_HLINE, COLS - 2);
820     attrset(A_BOLD); mvprintw(2, 1, "Filename: "); attrset(A_NORMAL);
821     mvprintw(2, 11, "%s", get_basename(lfile.filename));
822     attrset(A_BOLD); mvprintw(3, 1, "File Size: "); attrset(A_NORMAL);
823     mvprintw(3, 12, "%ld bytes", lfile.the_file_size);
824     attrset(A_BOLD); mvprintw(4, 1, "Current Dir: "); attrset(A_NORMAL);
825     mvprintw(4, 14, "%s%c", current_dir, strlen(current_dir) < 2 ? ' ' : '/');
826     attrset(A_BOLD); mvprintw(5, 1, "Current Line: "); attrset(A_NORMAL);
827     mvprintw(5, 15, "%ld of %ld - %.0f%% - Page: %d", line, total_lines, lfile.percent, lfile.page_num);
828     attrset(A_BOLD); mvprintw(6, 1, "Scroll Speed: "); attrset(A_NORMAL);
829     mvprintw(6, 15, "(%d Milliseconds | %.2f Second%s", scroll_speed, (float)scroll_speed / 1000, scroll_speed > 1000 ? "s)" : ")");
830     attrset(A_BOLD); mvprintw(7, 1, "Original Speed: "); attrset(A_NORMAL);
831     mvprintw(7, 17, "(%d Milliseconds | %.2f Second%s", origspeed, (float)origspeed / 1000, origspeed > 1000 ? "%s)" : ")");
832     attrset(A_BOLD); mvprintw(8, 1, "External Editor: "); attrset(A_NORMAL);
833     mvprintw(8, 18, "%s", lop.editor);
834     attrset(A_BOLD); mvprintw(9, 1, "Terminal Size: "); attrset(A_NORMAL);
835     mvprintw(9, 16, "%d Rows, %d Columns", LINES, COLS);
836     attrset(A_BOLD); mvprintw(10, 1, "Beep On Events: "); attrset(A_NORMAL);
837     mvprintw(10, 17, "%s", lop.beep_ok ? "Yes" : "No");
838     attrset(A_BOLD); mvprintw(11, 1, "Auto-Pause: "); attrset(A_NORMAL);
839     mvprintw(11, 13, "%s", lop.auto_pause ? "Yes" : "No");
840     attrset(A_BOLD); mvprintw(12, 1, "Skip Extra Blank Lines: "); attrset(A_NORMAL);
841     mvprintw(12, 25, "%s", lop.view_normal ? "No" : "Yes");
842 
843     nodelay(stdscr, FALSE);
844     getch();
845     clear();
846     refresh();
847     reset_prog_mode(); /* Return the screen */
848     refresh(); /* needed to show the screen */
849     lop.reshow_statusbar = TRUE;
850 }
851 
usage()852 void usage()
853 {
854     printf("Usage: %s [OPTIONS]\n", progname);
855     printf("-f <filename>   File to scroll.\n"
856     "-s <n>          Scroll at <n> milliseconds. Default: 1000 (1 sec).\n"
857     "-p <n>          Start text on row <n> (1 to LINES-2).\n"
858     "-w <string>     Highlight all lines that <string> appears on.\n"
859     "-l              Display in all lower case characters.\n"
860     "-u              Display in all upper case characters.\n"
861     "-c <color>      red, bgred, green, bggreen, blue, bgblue, yellow,                               bgyellow, magenta, bgmagenta, bgwhite.\n"
862     "-n              Don't take out extra blank lines. Display as is.\n"
863     "-a              Automatically pause on highlight word from -w.\n"
864     "-v              Display program version number.\n"
865     "-h              Help (This message).\n"
866     "-x              Don't show status bar.\n"
867     "-b              Allow beeping on important events.\n"
868     "-m              Scroll a character at a time mode.\n"
869     "-t <ttyname>    Name of tty your running lazyread from while piped\n"
870 
871     "\tWhile lazyread is running you can use the option keys:\n"
872     "'q' to quit.\n'p' to pause.\n'spacebar' to scroll super"
873     "fast (hit again to turn off).\n'c' to clear the current screen."
874     "\n'n' to turn off the status bar.\n'v' to turn the status bar back on.\n"
875     "'f' to speed scrolling up in 25 percent increments.\n's' to slow "
876     "scrolling speed down in 25 percent increments.\n"
877     "'o' to go back to original speed.\n'a' to toggle Auto-Pausing on/off.\n"
878     "'e' open file in your editor. Uses $VISUAL, $EDITOR or /bin/vi.\n"
879     "'i' to view detailed file/program/etc information.\n");
880 
881     exit(EXIT_SUCCESS);
882 }
883 
quit_cleanly(void)884 void quit_cleanly(void)
885 {
886     unlink(lfile.lazy_file);
887     if (lfile.piped)
888         unlink(lfile.lazy_pipe);
889     clear();
890     refresh();
891     flushinp();
892     endwin();
893     exit(EXIT_SUCCESS);
894 }
895 
char_check(char * str)896 int char_check(char *str)
897 {
898 /* checks if any non-digits are found in the input string */
899 
900     for (; *str; str++)
901         if (!isdigit(*str))
902             return 0;
903 
904     return 1;
905 }
906 
line_nums(FILE * fp)907 long line_nums(FILE *fp)
908 {
909 char buf[255];
910 long linenum = 0;
911 
912     /* add up all the lines of the file */
913     while (fgets(buf, sizeof(buf), fp))
914         ++linenum;
915 
916     rewind(fp);
917 
918     return (linenum);
919 }
920 
str_trunc(char * s,int n)921 char *str_trunc(char *s, int n)
922 {
923 char *buf;
924 
925     if (n > strlen(s))
926         n = strlen(s) + 1;
927 
928     if (!(buf = (char *)malloc(n + 1)))
929         cperror("malloc()");
930 
931     strncpy(buf, s, n);
932 
933     if (strlen(s) > strlen(buf))
934         strncat(buf, "~", 1);
935 
936     return (buf);
937 }
938 
my_perror(char * msg)939 void my_perror(char *msg)
940 {
941     perror(msg);
942     exit(EXIT_FAILURE);
943 }
944 
cperror(char * msg)945 void cperror(char *msg)
946 {
947     endwin(); /* kill the window first so we can print to stdout */
948     perror(msg);
949     quit_cleanly();
950 }
951 
catch_sigwinch(int signo)952 void catch_sigwinch(int signo)
953 {
954     /* terminal resizing functionality */
955     refresh();
956     endwin();
957     free(pscroll->scrollwin);
958     free(pstat->statwin);
959     create_windows();
960     scrollok(pscroll->scrollwin, TRUE);
961     lop.y = LINES - 2;
962 }
963 
catch_sigint(int signo)964 void catch_sigint(int signo)
965 {
966     quit_cleanly();
967 }
968 
signal_setup(void)969 void signal_setup(void)
970 {
971 struct sigaction sa_resize_old, sa_resize_new;
972 struct sigaction sa_kill_old, sa_kill_new;
973 
974     /* xterm resizing */
975     sa_resize_new.sa_handler = catch_sigwinch;
976     sigemptyset(&sa_resize_new.sa_mask);
977     sa_resize_new.sa_flags = 0;
978     sigaction(SIGWINCH, &sa_resize_new, &sa_resize_old);
979 
980     /* control-C */
981     sa_kill_new.sa_handler = catch_sigint;
982     sigemptyset(&sa_kill_new.sa_mask);
983     sa_kill_new.sa_flags = 0;
984     sigaction(SIGINT, &sa_kill_new, &sa_kill_old);
985 }
986