C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
view_engine.c
Go to the documentation of this file.
1/** @file view_engine.c
2 @brief The working part of View
3 @author Bill Waller
4 Copyright (c) 2025
5 MIT License
6 billxwaller@gmail.com
7 @date 2026-02-09
8 */
9
10/**
11 @defgroup view_engine View Engine
12 @brief File mapping, user input, command processing, and display logic
13 */
14
15#include <common.h>
16#include <ctype.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <regex.h>
20#include <stdbool.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/mman.h>
25#include <sys/param.h>
26#include <sys/stat.h>
27#include <termios.h>
28#include <time.h>
29#include <unistd.h>
30
31/** @brief read the next characater from the virtual file
32 @ingroup view_engine
33 @details Line numbers are tracked when reading forward and stored in a line
34 table for quick access when moving backwards.
35 When reading forward, the End of Data (EOD) flag is set when the
36 position is equal to the file size, and cleared when the position or reading
37 direction changes.
38 Carriage-returns are ignored as they should be.
39 View uses the kernel's demand paged virtual address space to map files
40 directly into memory. This allows for efficient access to file contents
41 without the need for explicit buffering or read system calls, as the kernel
42 handles loading the necessary pages into memory on demand.
43 */
44#define get_next_char()
45 {
46 c = 0;
47 do {
48 if (view->file_pos == view->file_size) {
49 view->f_eod = true;
50 break;
51 } else
52 view->f_eod = false;
53 c = view->buf[view->file_pos++];
54 } while (c == 0x0d);
55 if (c == '\n')
56 increment_ln(view);
57 }
58/** @brief read the previous characater from the virtual file
59 @ingroup view_engine
60 There is no need to track line numbers when moving backwards as they
61 are stored in the line table and accessed as needed.
62 When reading in reverse, the Beginning of Data (BOD) flag is set when
63 the file position is zero, and cleared when the position or reading direction
64 changes.
65 Carriage-returns are ignored as they should be.
66 */
67#define get_prev_char()
68 {
69 c = 0;
70 do {
71 if (view->file_pos == 0) {
72 view->f_bod = true;
73 break;
74 } else
75 view->f_bod = false;
76 c = view->buf[--view->file_pos];
77 } while (c == 0x0d);
78 }
79
81FILE *dbgfp;
82int view_file(Init *);
83int view_cmd_processor(Init *);
84int get_cmd_char(View *, off_t *);
85int get_cmd_arg(View *, char *);
86void build_prompt(View *);
87void cat_file(View *);
88void lp(char *);
89void go_to_mark(View *, int);
90void go_to_eof(View *);
91int go_to_line(View *, off_t);
92void go_to_percent(View *, int);
93void go_to_position(View *, off_t);
94bool search(View *, int *, char *);
95void next_page(View *);
96void prev_page(View *);
97void scroll_down_n_lines(View *, int);
98void scroll_up_n_lines(View *, int);
99off_t get_next_line(View *, off_t);
100off_t get_prev_line(View *, off_t);
101off_t get_pos_next_line(View *, off_t);
102off_t get_pos_prev_line(View *, off_t);
103int fmt_line(View *);
104void display_line(View *);
105void view_display_page(View *);
106void parse_ansi_str(char *, attr_t *, int *);
107void view_display_help(Init *);
108int display_prompt(View *, char *);
109void remove_file(View *);
110int write_view_buffer(Init *, bool);
111bool enter_file_spec(Init *, char *);
112int a_toi(char *, bool *);
113void increment_ln(View *);
114void initialize_line_table(View *);
115int pad_refresh(View *);
116void sync_ln(View *);
118void view_restore_wins();
119
120/** @brief Start view
121 @ingroup view_engine
122 @param init Pointer to the Init structure containing initialization
123 parameters and state for the view application. This structure is used to pass
124 necessary information and maintain state across different functions within
125 the view application.
126 @return Returns 0 on successful completion of the view application, or a
127 non-zero value if an error occurs during initialization or execution.
128 */
129int view_file(Init *init) {
130 view = init->view;
131 if (view->argc < 1) {
132 view->curr_argc = -1;
133 view->argc = 0;
135 } else
137 while (view->curr_argc < view->argc) {
139 *view->next_file_spec_ptr == '\0') {
140 break;
141 }
146 if (view->buf) {
147 view->f_eod = 0;
148 view->f_bod = 0;
149 view->maxcol = 0;
154 view->ln = 0;
156 view->file_pos = 0;
160 munmap(view->buf, view->file_size);
161 }
162 } else {
166 }
167 }
168 }
169 return 0;
170}
171/** @brief Main Command Processing Loop for View
172 @ingroup view_engine
173 @param init Pointer to the Init structure containing initialization
174 parameters and state for the view application. This structure is used to pass
175 necessary information and maintain state across different functions within
176 the view application.
177*/
178int view_cmd_processor(Init *init) {
179 char tmp_str[MAXLEN];
180 int tfd;
181 int c;
182 int shift = 0;
183 int search_cmd = 0;
184 int prev_search_cmd = 0;
185 int rc, i;
186 ssize_t bytes_written;
187 char *e;
188 char shell_cmd_spec[MAXLEN];
189 off_t n_cmd = 0L;
190 off_t prev_file_pos;
191 int swidth;
192 int max_pmincol;
193 view = init->view;
194 view->cmd[0] = '\0';
195 while (1) {
199 }
202 if (!c) {
205 if (view->f_ln) {
206 touchwin(view->ln_win);
207 wrefresh(view->ln_win);
208 }
209 c = get_cmd_char(view, &n_cmd);
210 if (c >= '0' && c <= '9') {
211 tmp_str[0] = (char)c;
212 tmp_str[1] = '\0';
213 c = get_cmd_arg(view, tmp_str);
214 }
215 }
216 switch (c) {
217 case Ctrl(
218 'R'): /**< Ctrl('R') or KEY_RESIZE - Handle terminal resize */
219 case 'x':
220 case KEY_RESIZE:
221 getmaxyx(stdscr, view->lines, view->cols);
222#ifdef DEBUG_RESIZE
223 ssnprintf(em0, MAXLEN - 1,
224 "view->page_top_ln=%d, resized to lines: %d, cols: %d\n",
225 view->page_top_ln, view->lines, view->cols);
226 write_cmenu_log_nt(em0);
227#endif
230 } else {
232 }
234 continue;
235 case KEY_ALTHOME: /**< KEY_ALTHOME - horizontal scroll to the first
236 column */
237 view->pmincol = 0;
238 break;
239 case KEY_ALTEND: /**< KEY_ALTEND horizontal scroll to the last
240 * column
241 */
244 else
245 view->pmincol = 0;
246 break;
247 case 'h': /**< 'h', Ctrl('H'), KEY_LEFT, KEY_BACKSPACE - Horizontal
248 scroll left by two thirds of the page width */
249 case Ctrl('H'):
250 case KEY_LEFT:
251 case KEY_BACKSPACE:
252 if (n_cmd <= 0)
253 n_cmd = 1;
254 shift = (int)n_cmd;
255 // swidth = view->smaxcol - view->smincol;
256 if (view->pmincol - shift > 0)
257 view->pmincol -= shift;
258 else
259 view->pmincol = 0;
260 break;
261 case 'l': /**< 'l', 'L', KEY_RIGHT - Horizontal scroll right by two
262 thirds of the page width */
263 case 'L':
264 case KEY_RIGHT:
265 if (n_cmd <= 0)
266 n_cmd = 1;
267 shift = (int)n_cmd;
268 swidth = view->smaxcol - view->smincol + 1;
269 if (view->f_ln)
270 max_pmincol = view->maxcol - swidth + 8;
271 else
272 max_pmincol = view->maxcol - swidth;
273 if (view->pmincol + shift < max_pmincol)
274 view->pmincol += shift;
275 else
276 view->pmincol = max_pmincol;
277 break;
278 case 'k': /** 'k', 'K', KEY_UP, Ctrl('K') - Scroll up one line */
279 case 'K':
280 case KEY_UP:
281 case Ctrl('K'):
282 if (n_cmd <= 0)
283 n_cmd = 1;
285 break;
286 /** 'j', 'J', KEY_DOWN, KEY_ENTER, SPACE - scroll down one line */
287 case 'j':
288 case 'J':
289 case '\n':
290 case ' ':
291 case KEY_DOWN:
292 case KEY_ENTER:
293 if (n_cmd <= 0)
294 n_cmd = 1;
295 for (i = 0; i < n_cmd; i++) {
297 }
298 break;
299 /** 'b', 'B', Ctrl('B'), KEY_PPAGE - Previous Page */
300 case KEY_PPAGE:
301 case 'b':
302 case 'B':
303 case Ctrl('B'):
305 break;
306 /** 'f', 'F', Ctrl('F'), KEY_NPAGE Next Page */
307 case 'f':
308 case 'F':
309 case KEY_NPAGE:
310 case Ctrl('F'):
312 break;
313 /** 'g', KEY_HOME - Go to the beginning of the document */
314 case 'g':
315 case KEY_HOME:
316 view->pmincol = 0;
318 break;
319 /** KEY_LL - Go to the end of the document */
320 case KEY_LL:
322 break;
323 /** '!', Execute Shell Command from within C-Menu View */
324 case '!':
326 break;
327 if (get_cmd_arg(view, "!") == 0) {
328 if (!view->f_is_pipe) {
331 str_subc(shell_cmd_spec, view->cmd_arg, '%',
333 } else
334 strnz__cpy(shell_cmd_spec, view->cmd_arg, MAXLEN - 1);
335 full_screen_shell(shell_cmd_spec);
336 if (!view->f_is_pipe) {
338 return 0;
339 }
340 }
341 break;
342 /** '+', Set Startup Command */
343 case '+':
344 if (get_cmd_arg(view, "Startup Command:") == 0)
346 break;
347 /** '-', Change View Settings */
348 case '-':
349 display_prompt(view, "(i, n, s, t, or h)->");
350 c = get_cmd_char(view, &n_cmd);
351 c = S_TOLOWER(c);
352 switch (c) {
353 /** -i ignore_case in search */
354 /** -n line numbers */
355 /** -s squeeze multiple blank lines */
356 /** -t n set tab stop columns */
357 /** -h display help */
358 case 'i':
359 display_prompt(view, "Ignore Case in search (Y or N)->");
360 if ((c = get_cmd_char(view, &n_cmd)) == 'y' || c == 'Y')
361 view->f_ignore_case = true;
362 else if (c == 'n' || c == 'N')
363 view->f_ignore_case = false;
364 break;
365 /** -n line numbers */
366 case 'n':
367 if (view->f_ln)
368 view->f_ln = false;
369 else
370 view->f_ln = true;
374 view->maxcol = 0;
375 view->cury = 0;
377 mvwaddstr(view->pad, view->cmd_line, 0, " ");
380 break;
381 /** -s Squeeze Multiple Blank Lines */
382 case 's':
384 view, "view->f_squeeze Multiple Blank lines (Y or N)->");
385 if ((c = get_cmd_char(view, &n_cmd)) == 'y' || c == 'Y')
386 view->f_squeeze = true;
387 else if (c == 'n' || c == 'N')
388 view->f_squeeze = false;
389 break;
390 /** -t n Set Tab Stop Columns */
391 case 't':
392 sprintf(tmp_str,
393 "Tabstop Colums Currently %d:", view->tab_stop);
394 i = 0;
395 if (get_cmd_arg(view, tmp_str) == 0)
396 i = atoi(view->cmd_arg);
397 if (i >= 1 && i <= 12) {
398 view->tab_stop = i;
400 } else
401 Perror("Tab stops not changed");
402 break;
403 /** -h Display Help */
404 case 'h':
405 case KEY_F(1):
408 /** Pedantic reassignment of view pointer as the
409 init->view pointer was was reassigned during
410 view_display_help */
411 view = init->view;
412 }
414 break;
415 default:
416 break;
417 }
418 break;
419 /** ':' - Set a Prompt String */
420 case ':':
422 break;
423 /** 'n' - Repeat Previous Search */
424 case 'n':
425 if (prev_search_cmd == 0 || view->f_search_complete) {
426 Perror("No previous search or search complete");
427 break;
428 }
429 if (prev_search_cmd == '/') {
430 view->cury = 0;
432 } else {
435 }
436 search(view, &prev_search_cmd, prev_regex_pattern);
437 break;
438 /** '/' or '?' - Search Forward fromk top of page */
439 case '/':
441 strnz__cpy(tmp_str, "(forward)->", MAXLEN - 1);
442 search_cmd = c;
443 c = get_cmd_arg(view, tmp_str);
444 if (c == '\n') {
445 view->cury = 0;
446 view->f_first_iter = true;
449 search(view, &search_cmd, view->cmd_arg);
450 prev_search_cmd = search_cmd;
452 }
453 break;
454 /** '?' - Search Backward */
455 case '?':
457 strnz__cpy(tmp_str, "(backward)->", MAXLEN - 1);
458 search_cmd = c;
459 c = get_cmd_arg(view, tmp_str);
460 if (c == '\n') {
462 view->f_first_iter = true;
465 search(view, &search_cmd, view->cmd_arg);
466 prev_search_cmd = search_cmd;
468 }
469 break;
470 /** 'o' or 'O' - Open a File */
471 case 'o':
472 case 'O':
473 case 'e':
474 case 'E':
475 if (get_cmd_arg(view, "File name:") == 0) {
476 strtok(view->cmd_arg, " ");
479 return 0;
480 }
481 break;
482 /** 'g' or 'G' - Go to the End of the Document */
483 case 'G':
484 case KEY_END:
485 if (n_cmd <= 0)
487 else
488 go_to_line(view, n_cmd);
489 break;
490 /** 'H' or KEY_F(1) - Display Help Information */
491 case 'H':
492 case KEY_F(1):
495 view = init->view;
496 }
497 break;
498 /** 'm' - Set a Mark at the Current Position */
499 case 'm':
500 display_prompt(view, "Mark label (A-Z)->");
501 c = get_cmd_char(view, &n_cmd);
502 if (c == '@' || c == KEY_F(9) || c == '\033')
503 if (c >= 'A' && c <= 'Z')
504 c += ' ';
505 if (c < 'a' || c > 'z')
506 Perror("Not (a-z)");
507 else
509 break;
510 /** 'M' - Go to a Mark */
511 case 'M':
512 display_prompt(view, "Goto mark (A-Z)->");
513 c = get_cmd_char(view, &n_cmd);
514 if (c == '@' || c == KEY_F(9) || c == '\033')
515 break;
516 if (c >= 'A' && c <= 'Z')
517 c += ' ';
518 if (c < 'a' || c > 'z')
519 Perror("Not (A-Z)");
520 else
522 break;
523 /** 'N' - Close Current File and Open Next File */
524 case 'N':
525 if (n_cmd <= 0)
526 n_cmd = 1;
527 if (view->curr_argc + n_cmd >= view->argc) {
528 Perror("no more files");
530 } else {
534 return 0;
535 }
536 break;
537 /** 'p' or '%' - Go to a Percent of the File */
538 case 'p':
539 case '%':
540 if (n_cmd < 0)
542 if (n_cmd >= 100)
544 else
546 break;
547 /** Ctrl('Z') - Send File to Print Queue with Notation */
548 case Ctrl('Z'):
549 get_cmd_arg(view, "Enter Notation:");
550 strnz__cpy(tmp_str, "/tmp/view-XXXXXX", MAXLEN - 1);
551 tfd = mkstemp(tmp_str);
553 if (tfd == -1) {
554 Perror("Unable to create temporary file");
555 break;
556 }
557 strnz__cpy(shell_cmd_spec, "echo ", MAXLEN - 5);
558 strnz__cat(shell_cmd_spec, view->cmd_arg, MAXLEN - 5);
560 shell(shell_cmd_spec);
561 strnz__cpy(shell_cmd_spec, "cat ", MAXLEN - 5);
562 strnz__cat(shell_cmd_spec, view->cmd_arg, MAXLEN - 5);
563 strnz__cat(shell_cmd_spec, ">>", MAXLEN - 5);
565 shell(shell_cmd_spec);
567 shell(shell_cmd_spec);
568 ssnprintf(shell_cmd_spec, (size_t)(MAXLEN - 5), "rm %s",
570 strnz__cpy(shell_cmd_spec, "rm ", MAXLEN - 5);
572 shell(shell_cmd_spec);
575 unlink(tmp_str);
576 break;
577 /** 'P' or KEY_CATAB or KEY_PRINT - Print Current File */
578 case Ctrl('P'):
579 case KEY_CATAB:
580 case KEY_PRINT:
583 break;
584 /** 'P' or KEY_F(9) or ESC - Close Current File and Open Next */
585 case 'P':
586 if (n_cmd <= 0)
587 n_cmd = 1;
588 if (view->curr_argc - n_cmd < 0) {
589 Perror("No previous file");
590 view->curr_argc = 0;
591 } else {
593 if (view->curr_argc >= 0)
595 return 0;
596 }
597 break;
598 /** 'q' or 'Q' or KEY_F(9) or ESC - Quit the Application */
599 case 'q':
600 case 'Q':
601 case KEY_F(9):
602 case '\033':
605 return 0;
606 /** 'v' - Open Current File in Editor */
607 case 'v':
608 if (init->editor[0] == 0) {
609 e = getenv("DEFAULTEDITOR");
610 if (e == nullptr || *e == '\0')
612 else
614 }
616 "View doesn't support editing current buffer directly",
617 MAXLEN - 1);
618 strnz__cpy(em1, "Would you like to write the buffer to a file?",
619 MAXLEN - 1);
620 strnz__cpy(em2, "Enter Y for yes or any other key to cancel.",
621 MAXLEN - 1);
623 if (rc != 'y' && rc != 'Y')
624 break;
627 break;
628 }
629 prev_file_pos = view->page_top_pos;
630 bytes_written = write_view_buffer(init, view->f_strip_ansi);
631 if (bytes_written == 0) {
632 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__,
633 __LINE__ - 2);
634 strnz__cpy(em1, "0 bytes written", MAXLEN - 1);
635 strerror_r(errno, em1, MAXLEN - 1);
637 break;
638 }
640 strnz__cpy(shell_cmd_spec, init->editor, MAXLEN - 5);
641 strnz__cat(shell_cmd_spec, " ", MAXLEN - 5);
642 strnz__cat(shell_cmd_spec, view->in_spec, MAXLEN - 5);
643 full_screen_shell(shell_cmd_spec);
645 prev_file_pos;
646
649 return 0;
650 /** 'w' - Write the current buffer to file */
651 case 'w':
654 break;
655 }
656 // prev_file_pos = view->page_top_pos;
657 bytes_written = write_view_buffer(init, view->f_strip_ansi);
658 if (bytes_written == 0) {
659 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__,
660 __LINE__ - 2);
661 strnz__cpy(em1, "0 bytes written", MAXLEN - 1);
662 strerror_r(errno, em1, MAXLEN - 1);
664 break;
665 }
666 display_prompt(view, tmp_str);
668 break;
669 case CT_VIEW:
670 break;
671 /** 'V' - Display Version Information */
672 case 'V':
673 ssnprintf(em0, MAXLEN - 1, "C-Menu Version: %s", CM_VERSION);
675 break;
676 default:
677 break;
678 }
679 view->cmd_arg[0] = '\0';
680 }
681}
682/** @brief Get Command Character and Numeric Argument
683 @ingroup view_engine
684 @param view Pointer to the View structure containing the state around
685 the view application. This structure is used to access and modify the
686 state
687 @param n is used to store the numeric argument entered by the user, if
688 applicable. The function reads user input and extracts both the command
689 character and any numeric argument, allowing for commands that require a
690 numeric parameter to be processed effectively.
691 @return Returns the command character entered by the user, or a special
692 value if a mouse event is detected. The numeric argument is stored in the
693 variable pointed to by n if applicable.
694 */
695int get_cmd_char(View *view, off_t *n) {
696 int c = 0, i = 0;
697 char cmd_str[33];
698 cmd_str[0] = '\0';
699 pad_refresh(view);
700 wmove(view->cmdln_win, view->cmd_line, view->curx);
701 do {
703 if ((c >= '0' && c <= '9') && i < 32) {
704 cmd_str[i++] = (char)c;
705 cmd_str[i] = '\0';
706 } else {
707 if (c >= 256)
708 return c;
709 else
710 continue;
711 }
712 } while (c >= '0' && c <= '9');
713 *n = atol(cmd_str);
714 view->cmd_arg[0] = '\0';
715 return (c);
716}
717/** @brief Get Command Argument from User Input
718 @ingroup view_engine
719 @param view Pointer to the View structure containing the state and
720 parameters of the view application. This structure is used to access and
721 modify the state of the application as needed.
722 @param prompt A string containing the prompt to be displayed to the user
723 when requesting input for the command argument. This prompt is shown on
724 the command line to guide the user in providing the necessary input for
725 the command being executed.
726 @return Returns the command character entered by the user, or a special
727 value if a mouse event is detected. The command argument entered by the
728 user is stored in the view->cmd_arg buffer for use by the calling
729 function. The function handles user input, including editing keys, and
730 updates the command argument buffer accordingly. If the user enters a
731 numeric argument, it is validated based on the context of the command
732 being executed.
733*/
734int get_cmd_arg(View *view, char *prompt) {
735 int c;
736 int numeric_arg = false;
737 char *cmd_p;
738 char *cmd_e;
739 char prompt_s[PAD_COLS + 1];
740 char *n;
741 int prompt_l;
742 prompt_l = strnz__cpy(prompt_s, prompt, view->cols - 4);
743 if (view->cmd_arg[0] != '\0')
744 return 0;
745 cmd_p = view->cmd_arg;
746 cmd_e = view->cmd_arg + MAXLEN - 2;
747 wmove(view->cmdln_win, view->cmd_line, 0);
748 if (prompt_l == 0)
749 numeric_arg = true;
750 if (prompt_l > 1) {
751 wstandout(view->cmdln_win);
752 waddch(view->cmdln_win, ' ');
753 waddstr(view->cmdln_win, prompt_s);
754 waddch(view->cmdln_win, ' ');
755 wstandend(view->cmdln_win);
756 } else {
757 if (*prompt == ':')
758 numeric_arg = true;
759 else {
760 n = prompt;
761 if (*n >= '0' && *n <= '9') {
762 *cmd_p++ = *n;
763 *cmd_p = '\0';
764 numeric_arg = true;
765 }
766 }
767 waddstr(view->cmdln_win, prompt_s);
768 wmove(view->cmdln_win, view->cmd_line, prompt_l);
769 }
770 wclrtoeol(view->cmdln_win);
771 while (1) {
773 switch (c) {
774 /** Basic Editing Keys for Command Line */
775 case KEY_LEFT:
776 case KEY_BACKSPACE:
777 case '\b':
778 if (cmd_p > view->cmd_arg) {
779 cmd_p--;
780 if (*cmd_p < ' ' || *cmd_p == 0x7f) {
781 getyx(view->cmdln_win, view->cury, view->curx);
782 if (view->curx > 0) {
783 view->curx--;
784 wmove(view->cmdln_win, view->cmd_line, view->curx);
785 waddch(view->cmdln_win, ' ');
786 wmove(view->cmdln_win, view->cmd_line, view->curx);
787 }
788 }
789 getyx(view->cmdln_win, view->cury, view->curx);
790 if (view->curx > 0) {
791 view->curx--;
792 wmove(view->cmdln_win, view->cmd_line, view->curx);
793 waddch(view->cmdln_win, ' ');
794 wmove(view->cmdln_win, view->cmd_line, view->curx);
795 }
796 }
797 break;
798 case '\n':
799 case KEY_ENTER:
800 return c;
801 case '\033':
802 case KEY_F(9):
803 return c;
804 case KEY_MOUSE:
805 continue;
806 default:
807 *cmd_p++ = (char)c;
808 *cmd_p = '\0';
809 if ((char)c < ' ') {
810 waddch(view->cmdln_win, '^');
811 c |= '@';
812 } else if ((uchar)c == 0x7f)
813 c = '?';
814 waddch(view->cmdln_win, (char)c);
815 if (cmd_p >= cmd_e)
816 return 0;
817 if (numeric_arg && (c < '0' || c > '9'))
818 return -1;
819 break;
820 }
821 }
822 return c;
823}
824/** @brief Build Prompt String
825 @ingroup view_engine
826 @param view Pointer to the View structure containing the state and
827 parameters of the view application. This structure is used to access and
828 modify the state of the application as needed.
829 */
830void build_prompt(View *view) {
831 char tmp_str[MAXLEN];
832 if (view->f_is_pipe)
833 strnz__cpy(view->prompt_str, "stdin", MAXLEN - 1);
834 else
836 if (view->pmincol > 0) {
837 sprintf(tmp_str, "Col %d of %d", view->pmincol, view->maxcol);
838 if (view->prompt_str[0] != '\0')
840 strnz__cat(view->prompt_str, tmp_str, MAXLEN - 1);
841 }
842 if (view->argc > 0) {
843 sprintf(tmp_str, "File %d of %d", view->curr_argc + 1, view->argc);
844 if (view->prompt_str[0] != '\0') {
846 strnz__cat(view->prompt_str, tmp_str, MAXLEN - 1);
847 }
848 }
849 if (view->page_top_pos == NULL_POSITION)
850 view->page_top_pos = view->file_size;
851 sprintf(tmp_str, "Pos %zd-%zd", view->page_top_pos, view->page_bot_pos);
852 if (view->prompt_str[0] != '\0') {
854 strnz__cat(view->prompt_str, tmp_str, MAXLEN - 1);
855 }
856 if (!view->f_is_pipe) {
857 if (view->file_size > 0) {
858 sprintf(tmp_str, " of %zd", view->file_size);
859 strnz__cat(view->prompt_str, tmp_str, MAXLEN - 1);
860 }
861 }
862 if (view->f_eod) {
863 if (view->prompt_str[0] != '\0')
865 strnz__cat(view->prompt_str, "(End)", MAXLEN - 1);
866 if (view->curr_argc + 1 < view->argc) {
867 base_name(tmp_str, view->argv[view->curr_argc + 1]);
868 strnz__cpy(view->prompt_str, " Next File: ", MAXLEN - 1);
869 strnz__cat(view->prompt_str, tmp_str, MAXLEN - 1);
870 }
871 }
872}
873/** @brief Write buffer contents to files
874 @ingroup view_engine
875 @param init data structure
876 @param f_strip_ansi strip ANSI escape sequences
877 @details
878 */
879int write_view_buffer(Init *init, bool f_strip_ansi) {
880 ssize_t bytes_written = 0;
881 off_t pos;
882 View *view = init->view;
883 int rc;
884 size_t l;
885 char tmp_line_s[PAD_COLS];
886 if (!f_strip_ansi) {
887 strnz__cpy(em0, "Would you like to strip ansi escape sequences?",
888 MAXLEN - 1);
889 strnz__cpy(em1, "Enter Y for yes or any other key to cancel.",
890 MAXLEN - 1);
892 if (rc == 'y' || rc == 'Y')
893 f_strip_ansi = true;
894 else
895 f_strip_ansi = false;
896 }
898 /** write the buffer */
899 pos = 0;
900 view->out_fd = open(view->out_spec, O_CREAT | O_TRUNC | O_WRONLY, 0644);
901 if (view->out_fd == -1) {
902 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
903 strnz__cpy(em1, "fwrite ", MAXLEN - 1);
905 strerror_r(errno, em2, MAXLEN - 1);
907 return false;
908 }
909 bytes_written = 0;
910 while (!view->f_eod) {
911 pos = get_next_line(view, pos);
912 if (f_strip_ansi)
913 strip_ansi(tmp_line_s, view->line_in_s);
914 else
915 strnz__cpy(tmp_line_s, view->line_in_s, MAXLEN - 1);
916 l = strnlf(tmp_line_s, PAD_COLS - 1);
917 bytes_written += write(view->out_fd, tmp_line_s, l);
918 }
919 close(view->out_fd);
921 return bytes_written;
922}
923/** @brief Concatenate File to Standard Output
924 @ingroup view_engine
925 */
926void cat_file(View *view) {
927 int c;
928 while (1) {
930 if (view->f_eod)
931 break;
932 putchar(c);
933 }
934}
935/** @brief Send File to Print Queue
936 @ingroup view_engine
937 @param PrintFile - file to print */
938void lp(char *PrintFile) {
939 char *print_cmd_ptr;
940 char shell_cmd_spec[MAXLEN];
941 print_cmd_ptr = getenv("PRINTCMD");
942 if (print_cmd_ptr == nullptr || *print_cmd_ptr == '\0')
943 print_cmd_ptr = PRINTCMD;
944 ssnprintf(shell_cmd_spec, MAXLEN - 1, "%s %s", print_cmd_ptr, PrintFile);
945 display_prompt(view, shell_cmd_spec);
946 shell(shell_cmd_spec);
947}
948/** @defgroup view_navigation View Navigation
949 @brief Navigation functions for the view application
950 */
951/** @brief Go to Mark
952 @ingroup view_navigation
953 @param view Pointer to the View structure containing the state and
954 parameters of the view application. This structure is used to access and
955 modify the state of the application as needed.
956 @param c The character representing the mark to go to. This character is
957 typically a lowercase letter (a-z) corresponding to a mark that has been
958 set previously in the view application. The function will attempt to
959 navigate to the position associated with this mark, allowing the user to
960 quickly jump to specific locations in the file based on the marks they
961 have set. If the mark is not set, an error message will be displayed to
962 the user.
963 */
964void go_to_mark(View *view, int c) {
965 if (c == '\'')
966 view->file_pos = view->mark_tbl[(NMARKS - 1)];
967 else
968 view->file_pos = view->mark_tbl[c - 'a'];
969 if (view->file_pos == NULL_POSITION)
970 Perror("Mark not set");
971 else
973}
974/** @brief Search for Regular Expression Pattern
975 @ingroup view_navigation
976 @param view Pointer to View Structure
977 @param search_cmd Search Command Character ('/' or '?')
978 @param regex_pattern Regular Expression Pattern to Search For
979 @returns true if a match is found and displayed, false if the search
980 completes without finding a match or if an error occurs
981 @details The search performs extended regular expression matching, ignoring
982 ANSI sequences and Unicode characters. Matches are highlighted on the
983 screen, and the search continues until the page is full or the end of the
984 file is reached. If the search wraps around the file, a message is
985 displayed indicating that the search is complete.
986 The search state is maintained in the view structure, allowing for
987 repeat searches and tracking of the current search position. This
988 function highlights all matches in the current ncurses pad, including
989 those not displayed on the screen, and tracks the first and last match
990 columns for prompt display.
991 ANSI sequences and Unicode characters are stripped before
992 matching, so matching corresponds to the visual display */
993bool search(View *view, int *search_cmd, char *regex_pattern) {
994 char tmp_str[MAXLEN];
995 int REG_FLAGS = 0;
996 regmatch_t pmatch[1];
997 regex_t compiled_regex;
998 int reti;
999 int line_offset;
1000 int line_len;
1001 int match_len;
1002 off_t prev_ln;
1003 bool f_page = false;
1004 if (*regex_pattern == '\0')
1005 return false;
1006 if (view->f_ignore_case)
1007 REG_FLAGS = REG_ICASE | REG_EXTENDED;
1008 else
1009 REG_FLAGS = REG_EXTENDED;
1010 reti = regcomp(&compiled_regex, regex_pattern, REG_FLAGS);
1011 if (reti) {
1012 Perror("Invalid pattern");
1013 return false;
1014 }
1015 /** */
1016 while (1) {
1017 /** initialize iteration */
1018 if (*search_cmd == '/') {
1019 if (view->srch_curr_pos == view->file_size)
1020 view->srch_curr_pos = 0;
1021 } else {
1022 if (view->srch_curr_pos == 0)
1023 view->srch_curr_pos = view->file_size;
1024 }
1025 if (view->srch_curr_pos == view->srch_beg_pos) {
1026 if (view->f_first_iter == true) {
1027 view->f_first_iter = false;
1028 view->f_search_complete = false;
1029 if (*search_cmd == '/')
1030 view->cury = 0;
1031 else
1032 view->cury = view->scroll_lines + 1;
1033 } else {
1034 view->f_search_complete = true;
1035 return true;
1036 }
1037 }
1038 view->file_pos = view->srch_curr_pos;
1039 sync_ln(view);
1040 /** get line to scan */
1041 if (*search_cmd == '/') {
1042 if (view->cury == view->scroll_lines)
1043 return true;
1044 prev_ln = view->ln; /**< Note placement before get_next_line */
1047 } else {
1048 if (view->cury == 0)
1049 return true;
1051 prev_ln = view->ln; /**< Note placement after get_prev_line */
1053 }
1054 fmt_line(view);
1055 reti = regexec(&compiled_regex, view->stripped_line_out,
1056 compiled_regex.re_nsub + 1, pmatch, REG_FLAGS);
1057 if (reti == REG_NOMATCH) {
1058 if (f_page) {
1059 /** non-matching page filler */
1060 if (*search_cmd == '?')
1061 view->cury -= 2;
1062 display_line(view);
1063 if ((*search_cmd == '/' && view->cury == view->scroll_lines) ||
1064 (*search_cmd == '?' && view->cury == 1)) {
1065 break;
1066 }
1067 }
1068 continue;
1069 }
1070 if (reti) {
1071 char err_str[MAXLEN];
1072 regerror(reti, &compiled_regex, err_str, sizeof(err_str));
1073 strnz__cpy(tmp_str, "Regex match failed: ", MAXLEN - 1);
1074 strnz__cat(tmp_str, err_str, MAXLEN - 1);
1075 Perror(tmp_str);
1076 regfree(&compiled_regex);
1077 return false;
1078 }
1079 /** Display matching lines */
1080 if (!f_page) {
1081 if (*search_cmd == '/') {
1082 view->page_top_ln = prev_ln;
1083 wmove(view->pad, view->cury, 0);
1084 } else {
1085 view->page_bot_ln = view->ln;
1086 wmove(view->pad, 0, 0);
1087 }
1088 wclrtobot(view->pad);
1089 f_page = true;
1090 }
1091 if (*search_cmd == '?')
1092 view->cury -= 2;
1093 display_line(view);
1094 /** All matches on the current line are highlighted,
1095 including those not displayed on the screen.
1096 Track first and last match columns for prompt display. */
1097 view->first_match_x = -1;
1098 view->last_match_x = 0;
1099 line_len = strlen(view->stripped_line_out);
1100
1101 line_offset = 0;
1102 while (1) {
1103 view->curx = line_offset + pmatch[0].rm_so;
1104 match_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1105 mvwchgat(view->pad, view->cury - 1, view->curx, match_len,
1106 WA_REVERSE, cp_win, nullptr);
1107 if (view->first_match_x == -1)
1108 view->first_match_x = pmatch[0].rm_so;
1109 view->last_match_x = line_offset + pmatch[0].rm_eo;
1110 line_offset += pmatch[0].rm_eo;
1111 if (line_offset >= line_len)
1112 break;
1113 view->line_out_p = view->stripped_line_out + line_offset;
1114 reti = regexec(&compiled_regex, view->line_out_p,
1115 compiled_regex.re_nsub + 1, pmatch, REG_FLAGS);
1116 if (reti == REG_NOMATCH)
1117 break;
1118 if (reti) {
1119 char msgbuf[100];
1120 regerror(reti, &compiled_regex, msgbuf, sizeof(msgbuf));
1121 sprintf(tmp_str, "Regex match failed: %s", msgbuf);
1122 Perror(tmp_str);
1123 regfree(&compiled_regex);
1124 return false;
1125 }
1126 if (*search_cmd == '/') {
1127 if (view->cury == view->scroll_lines) {
1128 regfree(&compiled_regex);
1129 return true;
1130 }
1131 } else if (view->cury == 1) {
1132 regfree(&compiled_regex);
1133 return true;
1134 }
1135 }
1136 }
1137 view->file_pos = view->srch_curr_pos;
1138#ifdef DEBUG_SEARCH
1139 /** Statistics for debugging */
1140 ssnprintf(view->tmp_prompt_str, MAXLEN - 1,
1141 "%s|%c%s|Pos %zu-%zu|(%zd) %zu %zu", view->file_name, *search_cmd,
1142 regex_pattern, view->page_top_pos, view->page_bot_pos,
1143 view->file_size, view->srch_beg_pos, view->srch_curr_pos);
1144#else
1145 if (view->last_match_x > view->maxcol)
1147 "%s|%c%s|Match Cols %d-%d of %d-%d|(%zd%%)", view->file_name,
1148 *search_cmd, regex_pattern, view->first_match_x,
1150 (view->page_bot_pos * 100 / view->file_size));
1151 else
1153 "%s|%c%s|Pos %zu-%zu|(%zd%%)", view->file_name, *search_cmd,
1154 regex_pattern, view->page_top_pos, view->page_bot_pos,
1155 (view->page_bot_pos * 100 / view->file_size));
1156#endif
1157 regfree(&compiled_regex);
1158 return true;
1159}
1160
1161/** @defgroup view_display Manage View Display
1162 @brief Manage the View Display
1163 */
1164/** @brief Refresh Pad and Line Number Window
1165 @ingroup view_display
1166 @param view data structure
1167 @returns OK on success, ERR on failure
1168*/
1169int pad_refresh(View *view) {
1170 int rc;
1171 if (view->f_ln)
1172 rc = prefresh(view->pad, view->pminrow, view->pmincol, view->sminrow,
1173 view->smincol + 8, view->smaxrow, view->smaxcol);
1174 else
1175 rc = prefresh(view->pad, view->pminrow, view->pmincol, view->sminrow,
1176 view->smincol, view->smaxrow, view->smaxcol);
1177 if (rc == ERR)
1178 Perror("Error refreshing screen");
1179 wrefresh(view->cmdln_win);
1180 if (view->f_ln)
1181 wrefresh(view->ln_win);
1182 return rc;
1183}
1184/*--------------------------------------------------------------
1185 Navigation
1186 *--------------------------------------------------------------- */
1187/** @brief display previous page
1188 @ingroup view_navigation
1189 @param view data structure
1190 @details Displays the previous page starting at (view->page_top_ln -
1191 view->scroll_lines).
1192 */
1193void prev_page(View *view) {
1194 if (view->page_top_pos == 0)
1195 return;
1196 view->cury = 0;
1197 view->ln = view->page_top_ln;
1198 if (view->ln - view->scroll_lines >= 0)
1199 view->ln -= view->scroll_lines;
1200 else
1201 view->ln = 0;
1202 next_page(view);
1203}
1204/** @brief Advance to Next Page
1205 @ingroup view_navigation
1206 @param view data structure
1207 @details Advances from view->page_bot_pos to view the next page of
1208 content. view->page_bot_pos must be set properly when calling this
1209 function. If the current bottom position of the page is at the end of the
1210 file, the function returns without making any changes. Otherwise, it
1211 resets the maximum column and current line position to the top of the
1212 page, updates the file position to the current bottom position of the
1213 page, and sets the top position and line number of the page accordingly.
1214 Finally, it calls the function to display the new page content.
1215*/
1216void next_page(View *view) {
1217 view->file_pos = view->ln_tbl[view->ln];
1218 if (view->file_pos == view->file_size)
1219 return;
1220 view->maxcol = 0;
1221 view->cury = 0;
1222 view->page_top_ln = view->ln;
1224}
1225/** @brief Display Current Page
1226 @ingroup view_display
1227 @param view data structure
1228 */
1229void view_display_page(View *view) {
1230 int i;
1231 view->cury = 0;
1232 wmove(view->pad, 0, 0);
1233 if (view->ln_win)
1234 wmove(view->ln_win, 0, 0);
1235 view->ln = view->page_top_ln;
1236 view->page_bot_ln = view->ln;
1237 view->page_top_pos = view->ln_tbl[view->ln];
1238 view->file_pos = view->page_top_pos;
1239 view->page_bot_pos = view->file_pos;
1240 for (i = 0; i < view->scroll_lines; i++) {
1242 if (view->f_eod)
1243 break;
1244 fmt_line(view);
1245 display_line(view);
1246 }
1247 if (view->cury < view->scroll_lines) {
1248 wmove(view->ln_win, view->cury, 0);
1249 wclrtobot(view->ln_win);
1250 wmove(view->pad, view->cury, 0);
1251 wclrtobot(view->pad);
1252 }
1253 view->page_bot_ln = view->ln;
1254}
1255/** @brief Scroll N Lines
1256 @ingroup view_navigation
1257 @param view data Structure
1258 @param n number of lines to scroll
1259 */
1260void scroll_down_n_lines(View *view, int n) {
1261 int i = 0;
1262 view->f_bod = false;
1263 if (view->page_bot_pos == view->file_size)
1264 return;
1265 /** Locate New Top of Page */
1266 if (view->page_top_ln + n < view->ln) {
1267 view->page_top_ln += n;
1268 view->page_top_pos = view->ln_tbl[view->page_top_ln];
1269 } else
1270 view->page_top_ln = view->ln;
1271 /** Scroll */
1272 if (n > view->scroll_lines) {
1273 if (view->f_ln) {
1274 wmove(view->ln_win, 0, 0);
1275 wclrtobot(view->ln_win);
1276 }
1277 wmove(view->pad, 0, 0);
1278 wclrtobot(view->pad);
1279 } else {
1280 if (view->f_ln)
1281 wscrl(view->ln_win, n);
1282 wscrl(view->pad, n);
1283 if (view->cury + n < view->scroll_lines)
1284 view->cury = view->scroll_lines - n;
1285 }
1286 /** Fill in Page Bottom */
1287 wmove(view->pad, view->cury, 0);
1288 for (i = 0; i < n; i++) {
1290 view->page_bot_ln = view->ln;
1291 if (view->f_eod)
1292 break;
1293 fmt_line(view);
1294 display_line(view);
1295 }
1296}
1297/** @brief Scroll Up N Lines
1298 @ingroup view_navigation
1299 @param view data Structure
1300 @param n number of lines to scroll
1301 */
1302void scroll_up_n_lines(View *view, int n) {
1303 int i;
1304 view->page_top_pos = view->ln_tbl[view->page_top_ln];
1305 view->f_eod = false;
1306 if (view->page_top_pos == 0)
1307 return;
1308 if (view->page_top_ln - n >= 0) {
1309 view->page_top_ln -= n;
1310 } else
1311 view->page_top_ln = 1;
1312 view->ln = view->page_top_ln;
1313 view->page_top_pos = view->ln_tbl[view->page_top_ln];
1314 view->file_pos = view->page_top_pos;
1315
1316 if (view->f_ln) {
1317 wscrl(view->ln_win, -n);
1318 wnoutrefresh(view->ln_win);
1319 }
1320 wscrl(view->pad, -n);
1321 view->cury = 0;
1322 wmove(view->pad, view->cury, 0);
1323 /** Fill in Page Top */
1324 for (i = 0; i < n; i++) {
1326 if (view->f_eod)
1327 break;
1328 fmt_line(view);
1329 display_line(view);
1330 }
1331 if (view->page_bot_ln - n >= view->scroll_lines)
1332 view->page_bot_ln -= n;
1333 else
1334 view->page_bot_ln = view->scroll_lines;
1335 view->ln = view->page_bot_ln;
1336 view->page_bot_pos = view->ln_tbl[view->page_bot_ln];
1337 view->file_pos = view->page_bot_pos;
1338 return;
1339}
1340/** @brief Get Next Line from View->buf
1341 @ingroup view_navigation
1342 @param view struct
1343 @param pos buffer offset
1344 @returns file position of next line
1345 @details gets view->line_in_s
1346 */
1347off_t get_next_line(View *view, off_t pos) {
1348 char c;
1349 char *line_in_p;
1350
1351 view->file_pos = pos;
1352 view->f_eod = false;
1353 get_next_char();
1354 if (view->f_eod)
1355 return view->file_pos;
1356 line_in_p = view->line_in_s;
1357 view->line_in_beg_p = view->line_in_s;
1359 while (1) {
1360 if (c == '\n')
1361 break;
1362 if (line_in_p >= view->line_in_end_p)
1363 break;
1364 *line_in_p++ = c;
1365 get_next_char();
1366 if (view->f_eod)
1367 return view->file_pos;
1368 }
1369 *line_in_p = '\0';
1370 if (view->f_squeeze) {
1371 while (1) {
1372 get_next_char();
1373 if (c != '\n')
1374 break;
1375 if (view->f_eod)
1376 break;
1377 }
1378 get_prev_char();
1379 if (view->f_eod)
1380 return view->file_pos;
1381 }
1382 return view->file_pos;
1383}
1384/** @brief Get Previous Line from View->buf
1385 @ingroup view_navigation
1386 @param view data structure
1387 @param pos buffer offset
1388 @returns file position of previous line
1389 */
1390off_t get_prev_line(View *view, off_t pos) {
1391 pos = get_pos_prev_line(view, pos);
1392 view->file_pos = pos;
1394 return pos;
1395}
1396/** @brief Get Position of Next Line
1397 @ingroup view_navigation
1398 @param view data structure
1399 @param pos buffer offset
1400 @returns file position of next line
1401 */
1402off_t get_pos_next_line(View *view, off_t pos) {
1403 char c;
1404 if (pos == view->file_size) {
1405 view->f_eod = true;
1406 return view->file_pos;
1407 } else
1408 view->f_eod = false;
1409 view->file_pos = pos;
1410 get_next_char();
1411 if (view->f_eod)
1412 return view->file_pos;
1413 if (view->f_squeeze) {
1414 while (1) {
1415 if (c != '\n')
1416 break;
1417 get_next_char();
1418 if (view->f_eod)
1419 return view->file_pos;
1420 }
1421 get_prev_char();
1422 }
1423 while (!view->f_eod) {
1424 if (c == '\n') {
1425 break;
1426 }
1427 get_next_char();
1428 }
1429 return view->file_pos;
1430}
1431/** @brief Get Position of Previous Line
1432 @ingroup view_navigation
1433 @param view data structure
1434 @param pos buffer offset
1435 @returns file position of previous line
1436 */
1437off_t get_pos_prev_line(View *view, off_t pos) {
1438 view->file_pos = pos;
1439 if (view->file_pos == 0) {
1440 view->f_bod = true;
1441 return view->file_pos;
1442 }
1443 while (view->ln_tbl[view->ln] >= view->file_pos) {
1444 if (view->ln == 0)
1445 break;
1446 view->ln--;
1447 }
1448 view->file_pos = view->ln_tbl[view->ln];
1449 return view->file_pos;
1450}
1451/** @brief Go to Specific File Position
1452 @ingroup view_navigation
1453 @param view data Structure
1454 @param go_to_pos
1455*/
1456void go_to_position(View *view, off_t go_to_pos) {
1457 view->file_pos = go_to_pos;
1458 view->page_top_pos = view->file_pos;
1459 view->page_bot_pos = view->file_pos;
1460 sync_ln(view);
1461 next_page(view);
1462}
1463/** @brief Go to End of File
1464 @ingroup view_navigation
1465 @param view data structure
1466 */
1467void go_to_eof(View *view) {
1468 view->file_pos = view->file_size;
1469 sync_ln(view);
1470 if (view->ln > view->scroll_lines)
1471 view->ln -= view->scroll_lines;
1472 else
1473 view->page_top_ln = 0;
1474 view->page_top_ln = view->ln;
1475 view->page_top_pos = view->ln_tbl[view->ln];
1477 view->file_pos = view->page_top_pos;
1478 view->cury = 0;
1479 next_page(view);
1480}
1481/** @brief Go to Percent of File
1482 @ingroup view_navigation
1483 @param view data structure
1484 @param percent of file
1485*/
1486void go_to_percent(View *view, int percent) {
1487 if (view->file_size < 0) {
1488 Perror("Cannot determine file length");
1489 return;
1490 }
1491 view->file_pos = (percent * view->file_size) / 100;
1492 sync_ln(view);
1493 if (view->ln > view->scroll_lines)
1494 view->page_top_ln = view->ln - view->scroll_lines;
1495 else
1496 view->page_top_ln = 0;
1497 view->page_top_pos = view->ln_tbl[view->page_top_ln];
1499 view->file_pos = view->page_top_pos;
1500 next_page(view);
1501}
1502/** @brief Go to Specific Line
1503 @ingroup view_navigation
1504 @param view data structure
1505 @param line_idx line number to go to (1-based index)
1506 @returns 0 on success, EOF if line index is out of bounds or end of data
1507 is reached
1508 */
1509int go_to_line(View *view, off_t line_idx) {
1510 if (line_idx <= 1) {
1511 go_to_position(view, 0);
1512 return EOF;
1513 }
1514 sync_ln(view);
1515 view->page_top_pos = view->file_pos;
1516 view->page_bot_pos = view->file_pos;
1517 view->file_pos = view->page_top_pos;
1518 next_page(view);
1519 return 0;
1520}
1521/** @brief Initialize Line Table
1522 @ingroup view_navigation
1523 @param view data structure
1524 @details The line table is initialized with a specified increment size
1525 (LINE_TBL_INCR). Memory is allocated for the line table, and the first
1526 entry is set to 0, indicating the file position of the first line. The
1527 line index (view->ln) is initialized to 0.
1528 */
1529void initialize_line_table(View *view) {
1531 view->ln_tbl = (off_t *)malloc(view->ln_tbl_size * sizeof(off_t));
1532 if (view->ln_tbl == nullptr) {
1533 Perror("Memory allocation failed");
1534 exit(EXIT_FAILURE);
1535 }
1536 view->ln_max_pos = 0;
1537 view->ln_tbl[0] = 0;
1538 view->ln = 0;
1539}
1540/** @brief Increment Line Index and Update Line Table
1541 @ingroup view_navigation
1542 @param view data structure
1543 @details This function is called when a line feed character is
1544 encountered while reading the file. It increments the line index
1545 (view->ln) and checks if the current file position exceeds the maximum
1546 position recorded in the line table. If it does, it updates the line
1547 table with the new file position. If the line index exceeds the current
1548 size of the line table, the table is resized by allocating more memory.
1549 */
1550void increment_ln(View *view) {
1551 view->ln++;
1552 if (view->file_pos <= view->ln_max_pos)
1553 return;
1554 if (view->ln > view->ln_tbl_size - 1) {
1556 view->ln_tbl =
1557 (off_t *)realloc(view->ln_tbl, view->ln_tbl_size * sizeof(off_t));
1558 if (view->ln_tbl == nullptr) {
1559 Perror("Memory allocation failed");
1560 exit(EXIT_FAILURE);
1561 }
1562 }
1563 view->ln_tbl_cnt = view->ln;
1564 view->ln_max_pos = view->file_pos;
1565 view->ln_tbl[view->ln] = view->file_pos;
1566}
1567/** @brief Synchronize Line Table with Current File Position
1568 @ingroup view_navigation
1569 @param view data Structure
1570 @details The line table (view->ln_tbl) is an array that stores the file
1571 position of each line. The index (view->ln + 1) corresponds to the
1572 current line number. (the line number table is 0-based, while line
1573 numbering starts at 1).
1574 @details If the line or position requested is not in the line table, this
1575 function reads forward to sycn.
1576 If the line or positione requested is behind the current line
1577 table index, the line index will be decremented it matches the file
1578 position.
1579 */
1580void sync_ln(View *view) {
1581 int c = 0;
1582 off_t idx;
1583 off_t target_pos;
1584 if (view->ln_tbl[view->ln] == view->file_pos)
1585 return;
1586 target_pos = view->file_pos;
1587 view->file_pos = view->ln_tbl[view->ln_tbl_cnt];
1588 if (view->file_pos < target_pos) {
1589 view->ln = view->ln_tbl_cnt;
1590 while (view->ln_max_pos < target_pos) {
1591 get_next_char();
1592 if (view->f_eod)
1593 return;
1594 }
1595 } else if (view->ln_tbl[view->ln] > target_pos) {
1596 idx = view->ln - 1;
1597 while (view->ln_tbl[idx] > target_pos)
1598 idx--;
1599 view->ln = idx;
1600 view->file_pos = view->ln_tbl[view->ln];
1601 } else {
1602 view->ln = view->ln_tbl_cnt;
1603 view->file_pos = view->ln_tbl[view->ln];
1604 }
1605}
1606/*------------------------------------------------------------
1607 END NAVIGATION
1608 BEGIN DISPLAY
1609 ------------------------------------------------------------*/
1610/** @brief Display Line on Pad
1611 @ingroup view_display
1612 param View *view data structure
1613 @details This function displays a single line of text on the ncurses
1614 pad.
1615 If line numbering is enabled (view->f_ln), it is formatted and
1616 displayed at the beginning of the line with the specified attributes and
1617 color pair.
1618 @details Because get_next_char calls increment_ln upon encountering a
1619 line feed and increment_ln advances view->ln after updating the line
1620 table, the line number displayed is one greater than the index to the
1621 line table. That means the line counter begins with 1, while the table
1622 origin is 0.
1623 */
1624void display_line(View *view) {
1625 char ln_s[16];
1626
1627 if (view->cury < 0)
1628 view->cury = 0;
1629 if (view->cury > view->scroll_lines - 1)
1630 view->cury = view->scroll_lines - 1;
1631 if (view->f_ln) {
1632 ssnprintf(ln_s, 8, "%7jd", view->ln);
1633 wmove(view->ln_win, view->cury, 0);
1634 wclrtoeol(view->ln_win);
1635 mvwaddstr(view->ln_win, view->cury, 0, ln_s);
1636 }
1637 wmove(view->pad, view->cury, 0);
1638 wclrtoeol(view->pad);
1639 wadd_wchstr(view->pad, view->cmplx_buf);
1640 view->cury++;
1641}
1642/** @brief Format Line for Display
1643 @ingroup view_display
1644 @param view pointer to View structure containing line input and output
1645 buffers
1646 @return length of formatted line in characters
1647 @details This function processes the input line from view->line_in_s,
1648 handling ANSI escape sequences for text attributes and colors, as well as
1649 multi-byte characters. It converts the input line into a formatted line
1650 suitable for display in the terminal, storing the result in
1651 view->cmplx_buf and view->stripped_line_out. The function returns the
1652 length of the formatted line in characters, which may be used for
1653 tracking the maximum column width of the displayed content.
1654 */
1655int fmt_line(View *view) {
1656 char ansi_tok[MAXLEN];
1657 int i = 0, j = 0;
1658 int len = 0;
1659 const char *s;
1660 attr_t attr = WA_NORMAL;
1661 int cpx = cp_win;
1662 cchar_t cc = {0};
1663 wchar_t wstr[2] = {L'\0', L'\0'};
1664
1665 char *in_str = view->line_in_s;
1666 cchar_t *cmplx_buf = view->cmplx_buf;
1667
1669 mbstate_t mbstate;
1670 memset(&mbstate, 0, sizeof(mbstate));
1671 while (in_str[i] != '\0') {
1672 if (in_str[i] == '\033' && in_str[i + 1] == '[') {
1673 /** This section calls the decoder for ANSI escape sequences */
1674 len = strcspn(&in_str[i], "mK ") + 1;
1675 memcpy(ansi_tok, &in_str[i], len + 1);
1676 ansi_tok[len] = '\0';
1677 if (ansi_tok[0] == '\0') {
1678 if (i + 2 < MAXLEN)
1679 i += 2;
1680 continue;
1681 }
1682 if (len == 0 || in_str[i + len - 1] == ' ') {
1683 i += 2;
1684 continue;
1685 } else if (in_str[i + len - 1] == 'K') {
1686 i += len;
1687 continue;
1688 }
1689 parse_ansi_str(ansi_tok, &attr, &cpx);
1690 i += len;
1691 } else {
1692 /** This section converts the input string to wide characters,
1693 handling tabs and multi-byte characters, and stores the result
1694 in the complex buffer for display. ANSI escape sequences are
1695 ignored in this section since they are handled separately above.
1696 */
1697 if (in_str[i] == '\033') {
1698 i++;
1699 continue;
1700 }
1701 s = &in_str[i];
1702 if (*s == '\t') {
1703 do {
1704 wstr[0] = L' ';
1705 wstr[1] = L'\0';
1706 setcchar(&cc, wstr, attr, cpx, nullptr);
1707 view->stripped_line_out[j] = ' ';
1708 cmplx_buf[j++] = cc;
1709 } while ((j < PAD_COLS - 2) && (j % view->tab_stop != 0));
1710 i++;
1711 } else {
1712 wstr[1] = L'\0';
1713 len = mbrtowc(wstr, s, MB_CUR_MAX, &mbstate);
1714 if (len <= 0) {
1715 wstr[0] = L'?';
1716 wstr[1] = L'\0';
1717 len = 1;
1718 }
1719 if (setcchar(&cc, wstr, attr, cpx, nullptr) != ERR) {
1720 if (len > 0 && (j + len) < PAD_COLS - 1) {
1721 view->stripped_line_out[j] = *s;
1722 cmplx_buf[j++] = cc;
1723 }
1724 }
1725 i += len;
1726 }
1727 }
1728 }
1729 if (j > view->maxcol)
1730 view->maxcol = j;
1731 wstr[0] = '\0';
1732 wstr[1] = '\0';
1733 setcchar(&cc, wstr, WA_NORMAL, cpx, nullptr);
1734 cmplx_buf[j] = cc;
1735 view->stripped_line_out[j] = '\0';
1736 return j;
1737}
1738/** @brief Parse ANSI SGR Escape Sequence
1739 @ingroup view_display
1740 @param ansi_str is the ANSI escape sequence string to parse
1741 @param attr is a pointer to an attr_t variable where the parsed
1742 attributes will be stored
1743 @param cpx is a pointer to an int variable where the parsed color pair
1744 index will be stored
1745 @details This function parses an ANSI escape sequence and updates the color
1746 pair and color tables according to the attributes specified. Despite the
1747 depth of nested conditionals, with an understanding of the ANSI Select
1748 Graphics Rendition (SGR) scheme, which is defined in the ECMA-48 standard
1749 (ISO/IEC 6429), you will find that it is exceptionally simple and
1750 straightforward.
1751
1752 @verbatim
1753
1754 This function converts the following SGR specification types to the
1755 appropriate curses color pair index for use in the terminal display.
1756
1757 RGB:
1758
1759 foreground \033[38;2;r;g;bm
1760 background \033[48;2;r;g;bm
1761
1762 Where r, g, b are the red, green, and blue color components
1763 (0-255)
1764
1765 XTERM 256-color:
1766
1767 foreground \033[38;5;xm
1768 background \033[48;5;xm
1769
1770 Where x is the 256-color index (0-255)
1771
1772 uses xterm256_idx_to_rgb() to convert the 256-color index to
1773 RGB
1774
1775 8-color:
1776
1777 foreground \033[3cm
1778 background \033[4cm
1779
1780 Where c is the color code (0 for black, 1 for red, 2 for green, 3
1781 for yellow, 4 for blue, 5 for magenta, 6 for cyan, 7 for white).
1782
1783 Attributes:
1784
1785 \033[am
1786
1787 Where a is the attribute code (1 for bold, 2 for dim, 3 for italic,
1788 4 for underline, 5 for blink, 7 for reverse, 8 for invis). The function
1789 also supports resetting attributes and colors to default using \033[0m.
1790
1791 @sa xterm256_idx_to_rgb(), rgb_to_curses_clr(), extended_pair_content(),
1792 get_clr_pair()
1793
1794 @endverbatim
1795*/
1796void parse_ansi_str(char *ansi_str, attr_t *attr, int *cpx) {
1797 char *tok;
1798 char t0, t1;
1799 char tstr[3];
1800 int len, x_idx;
1801 int fg, bg;
1802 int fg_clr, bg_clr;
1803 char *ansi_p = ansi_str + 2;
1804 extended_pair_content(*cpx, &fg_clr, &bg_clr);
1805 fg = fg_clr;
1806 bg = bg_clr;
1807 RGB rgb;
1808 tok = strtok((char *)ansi_p, ";m");
1809 bool a_toi_error = false;
1810 while (1) {
1811 if (tok == nullptr || *tok == '\0')
1812 break;
1813 len = strlen(tok);
1814 if (len == 2) {
1815 t0 = tok[0];
1816 t1 = tok[1];
1817 if (t0 == '3' || t0 == '4') {
1818 if (t1 == '8') {
1819 tok = strtok(nullptr, ";m");
1820 if (tok != nullptr) {
1821 if (*tok == '5') {
1822 tok = strtok(nullptr, ";m");
1823 if (tok != nullptr) {
1824 x_idx = a_toi(tok, &a_toi_error);
1825 rgb = xterm256_idx_to_rgb(x_idx);
1826 }
1827 } else if (*tok == '2') {
1828 tok = strtok(nullptr, ";m");
1829 rgb.r = a_toi(tok, &a_toi_error);
1830 tok = strtok(nullptr, ";m");
1831 rgb.g = a_toi(tok, &a_toi_error);
1832 tok = strtok(nullptr, ";m");
1833 rgb.b = a_toi(tok, &a_toi_error);
1834 }
1835 }
1836 if (t0 == '3')
1837 fg_clr = rgb_to_curses_clr(&rgb);
1838 else if (t0 == '4')
1839 bg_clr = rgb_to_curses_clr(&rgb);
1840 } else if (t1 == '9') {
1841 if (t0 == '3')
1842 fg_clr = CLR_FG;
1843 else if (t0 == '4')
1844 bg_clr = CLR_BG;
1845 } else if (t1 >= '0' && t1 <= '7') {
1846 if (t0 == '3') {
1847 tstr[0] = t1;
1848 tstr[1] = '\0';
1849 x_idx = a_toi(tstr, &a_toi_error);
1850 rgb = xterm256_idx_to_rgb(x_idx);
1851 fg_clr = rgb_to_curses_clr(&rgb);
1852 } else if (t0 == '4') {
1853 tstr[0] = t1;
1854 tstr[1] = '\0';
1855 x_idx = a_toi(tstr, &a_toi_error);
1856 rgb = xterm256_idx_to_rgb(x_idx);
1857 bg_clr = rgb_to_curses_clr(&rgb);
1858 }
1859 }
1860 } else if (t0 == '0') {
1861 *tok = t1;
1862 len = 1;
1863 }
1864 }
1865 if (len == 1) {
1866 if (*tok == '0') {
1867 *attr = WA_NORMAL;
1868 fg_clr = CLR_FG;
1869 bg_clr = CLR_BG;
1870 } else {
1871 switch (a_toi(tok, &a_toi_error)) {
1872 case 1:
1873 *attr |= WA_BOLD;
1874 break;
1875 case 2:
1876 *attr |= WA_DIM;
1877 break;
1878 case 3:
1879 *attr |= WA_ITALIC;
1880 break;
1881 case 4:
1882 *attr |= WA_UNDERLINE;
1883 break;
1884 case 5:
1885 *attr |= WA_BLINK;
1886 break;
1887 case 7:
1888 *attr |= WA_REVERSE;
1889 break;
1890 case 8:
1891 *attr |= WA_INVIS;
1892 break;
1893 default:
1894 break;
1895 }
1896 }
1897 } else if (len == 0) {
1898 *attr = WA_NORMAL;
1899 fg_clr = CLR_FG;
1900 bg_clr = CLR_BG;
1901 }
1902 tok = strtok(nullptr, ";m");
1903 }
1904 if (!a_toi_error && (fg_clr != fg || bg_clr != bg)) {
1905 clr_pair_idx = get_clr_pair(fg_clr, bg_clr);
1906 *cpx = clr_pair_idx;
1907 }
1908 return;
1909}
1910/** @brief Display Command Line Prompt
1911 @ingroup view_display
1912 @param view is the current view data structure
1913 @param s is the prompt string */
1914int display_prompt(View *view, char *s) {
1915 char message_str[PAD_COLS + 1];
1916 int l;
1917 l = strnz__cpy(message_str, s, PAD_COLS);
1918 wmove(view->cmdln_win, view->cmd_line, 0);
1919 if (l != 0) {
1920 wclrtoeol(view->cmdln_win);
1921 wattron(view->cmdln_win, WA_REVERSE);
1922 waddstr(view->cmdln_win, " ");
1923 waddstr(view->cmdln_win, message_str);
1924 waddstr(view->cmdln_win, " ");
1925 wattroff(view->cmdln_win, WA_REVERSE);
1926 waddstr(view->cmdln_win, " ");
1927 view->curx = l + 2;
1928 wmove(view->cmdln_win, view->cmd_line, view->curx);
1929 }
1930 return (view->curx);
1931}
1932/** @brief Remove File
1933 @ingroup view_engine
1934 @param view is the current view data structure */
1935void remove_file(View *view) {
1936 char c;
1937 if (view->f_at_end_remove) {
1938 wmove(view->pad, view->cmd_line, 0);
1939 waddstr(view->pad, "Remove File (Y or N)->");
1940 wclrtoeol(view->pad);
1941 c = (char)xwgetch(view->cmdln_win, nullptr, -1);
1942 waddch(view->pad, (char)toupper(c));
1943 if (c == 'Y' || c == 'y')
1944 remove(view->cur_file_str);
1945 }
1946}
1947/** @brief Display View Help File
1948 @ingroup view_display
1949 @param init is the current initialization data structure.
1950 @details The current View context is set aside by assigning the view
1951 structure to "view_save" while the help file is displayed using a new,
1952 separate view structure.
1953 The help file is specified by the VIEW_HELP_FILE macro can be set
1954 to a default help file path or overridden by the user through an
1955 environment variable.
1956 After the help file is closed, the original view is restored and
1957 the page is redisplayed.
1958 It may be necessary to reassign view after calling this function
1959 because the init->view pointer is temporarily set to nullptr during the
1960 help file display, and the original view is restored afterward.
1961 The default screen size for help can be set in the code below. If
1962 set to 0, popup_view will determine reasonable maximal size based on the
1963 terminal dimensions.
1964 The help file may contain Unicode characters and ANSI escape
1965 sequences for formatting, which will be properly handled and displayed by
1966 popup_view. */
1967void view_display_help(Init *init) {
1968 char tmp_str[MAXLEN];
1969 int eargc = 0;
1970 char *eargv[MAXARGS];
1971 if (view->f_help_spec && view->help_spec[0] != '\0')
1973 else {
1974 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
1975 strnz__cat(tmp_str, "/", MAXLEN - 1);
1977 }
1978 eargv[eargc++] = strdup("view");
1979 eargv[eargc++] = strdup("-N");
1980 eargv[eargc++] = strdup("f");
1981 eargv[eargc++] = strdup(tmp_str);
1982 eargv[eargc] = nullptr;
1983 init->lines = 48;
1984 init->cols = 72;
1985 init->begy = 0;
1986 init->begx = 0;
1987 strnz__cpy(init->title, "View Help", MAXLEN - 1);
1988 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
1989 init->begx);
1990 destroy_argv(eargc, eargv);
1991 init->view->f_redisplay_page = true;
1992}
1993/** @brief Restore View Windows
1994 @ingroup view_display
1995 @details This function restores the content of the view windows (line
1996 number window and command line window) after they may have been
1997 overwritten by a popup or other temporary display. It uses wnoutrefresh to
1998 update the virtual screen with the content of the windows and then calls
1999 wrefresh to update the physical screen. */
2001 wnoutrefresh(view->ln_win);
2002 wrefresh(view->ln_win);
2003 wnoutrefresh(view->cmdln_win);
2004 wrefresh(view->cmdln_win);
2005}
2006/*------------------------------------------------------------
2007 END DISPLAY
2008 ------------------------------------------------------------*/
2009/** @brief use form to enter a file specification
2010 @ingroup view_engine
2011 @param init data structure
2012 @param file_spec - pointer to file specification
2013 the file_spec
2014 @returns true if successful
2015 @details the user must provide a character array large enough to
2016 hold file_spec without overflowing
2017 */
2018bool enter_file_spec(Init *init, char *file_spec) {
2019 char earg_str[MAXLEN];
2020 char *eargv[MAXARGS];
2021 int eargc;
2022 char tmp_dir[MAXLEN];
2023 char tmp_str[MAXLEN];
2024 char tmp_spec[MAXLEN];
2025 int rc = false;
2026 FILE *tmp_fp;
2027 view = init->view;
2028 strnz__cpy(tmp_dir, init->mapp_home, MAXLEN - 1);
2029 strnz__cat(tmp_dir, "/tmp", MAXLEN - 1);
2030 expand_tilde(tmp_dir, MAXLEN - 1);
2031 if (!mk_dir(tmp_dir)) {
2032 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
2033 strnz__cpy(em1, "Unable to ", MAXLEN - 1);
2034 strnz__cat(em1, "mkdir", MAXLEN - 1);
2035 strnz__cat(em1, tmp_dir, MAXLEN - 1);
2036 strerror_r(errno, em2, MAXLEN - 1);
2038 return false;
2039 }
2040 while (rc == false) {
2041 strnz__cpy(tmp_spec, tmp_dir, MAXLEN - 1);
2042 strnz__cat(tmp_spec, "/tmp_XXXXXX", MAXLEN - 1);
2043 view->in_fd = mkstemp(tmp_spec);
2044 if (view->in_fd == -1) {
2045 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
2046 strnz__cpy(em1, "unable to ", MAXLEN - 1);
2047 strnz__cat(em1, "mkstemp ", MAXLEN - 1);
2048 strnz__cat(em1, tmp_spec, MAXLEN - 1);
2049 strerror_r(errno, em2, MAXLEN - 1);
2051 return false;
2052 }
2053 /** call form to get file_name
2054 write the name to a temporary file */
2055
2056 strnz__cpy(earg_str, "form -d file_name.f -o ", MAXLEN - 1);
2057 strnz__cat(earg_str, tmp_spec, MAXLEN - 1);
2058 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
2059 rc = popup_form(init, eargc, eargv, view->begy + view->lines - 7, 4);
2060 destroy_argv(eargc, eargv);
2062 if (rc == FA_CANCEL || rc == 'q' || rc == 'Q' || rc == KEY_F(9))
2063 return false;
2064 close(view->in_fd);
2065 tmp_fp = fopen(tmp_spec, "r");
2066 if (tmp_fp == nullptr) {
2067 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
2068 strnz__cpy(em1, "unable to ", MAXLEN - 1);
2069 strnz__cat(em1, "fopen ", MAXLEN - 1);
2070 strnz__cat(em1, tmp_spec, MAXLEN - 1);
2071 strerror_r(errno, em2, MAXLEN - 1);
2073 return false;
2074 }
2075 fgets(tmp_str, MAXLEN - 1, tmp_fp);
2076 strnz(tmp_str, MAXLEN - 1);
2077 fclose(tmp_fp);
2078 unlink(tmp_spec);
2079 if (!verify_spec_arg(file_spec, tmp_str, "~/menuapp/tmp", ".",
2080 S_WCOK | S_QUIET)) {
2081 ssnprintf(em0, MAXLEN - 1, "Unable to open %s for writing",
2082 tmp_str);
2083 strnz__cpy(em1, "Try again? y (yes) or n (no) ", MAXLEN - 1);
2085 if (rc == 'y' || rc == 'Y')
2086 continue;
2087
2088 } else
2089 return true;
2090 }
2091 return true;
2092}
@ FA_CANCEL
Definition form.h:34
#define PRINTCMD
Definition common.h:44
int popup_form(Init *, int, char **, int, int)
instantiate a form popup window
Definition popups.c:113
#define DEFAULTEDITOR
Definition common.h:39
int popup_view(Init *, int, char **, int, int, int, int)
instantiate a view popup window
Definition popups.c:146
#define VIEW_HELP_FILE
Definition common.h:37
size_t rtrim(char *)
Trims trailing spaces from string s in place.
Definition futil.c:229
#define KEY_ALTEND
Definition cm.h:460
#define KEY_ALTHOME
Definition cm.h:457
#define MAX_ARGS
Definition cm.h:28
#define MAXARGS
Definition cm.h:30
unsigned char uchar
Definition cm.h:543
#define S_TOLOWER(c)
Definition cm.h:109
@ CLR_FG
Definition cm.h:141
@ CLR_BG
Definition cm.h:142
#define nullptr
Definition cm.h:25
#define S_QUIET
Definition cm.h:219
#define S_WCOK
Definition cm.h:218
#define Ctrl(c)
Definition cm.h:34
#define CM_VERSION
Definition version.h:7
@ CT_VIEW
Definition menu.h:47
#define LINE_TBL_INCR
Definition view.h:34
#define NMARKS
Definition view.h:24
char err_msg[MAXLEN]
void go_to_position(View *, long)
#define PAD_COLS
Definition view.h:30
#define NULL_POSITION
Definition view.h:27
View * view
Definition mem.c:48
#define MAXLEN
Definition curskeys.c:15
char prev_regex_pattern[MAXLEN]
Definition view_engine.c:80
FILE * dbgfp
Definition view_engine.c:81
int cp_win
Definition dwin.c:146
char em1[MAXLEN]
Definition dwin.c:142
int clr_pair_idx
Definition dwin.c:154
char em0[MAXLEN]
Definition dwin.c:141
char em2[MAXLEN]
Definition dwin.c:143
void view_win_resize(Init *, char *)
Resize the current window and its box, and update the title.
Definition init_view.c:265
int xwgetch(WINDOW *, Chyron *, int)
Wrapper for wgetch that handles signals, mouse events, checks for clicks on the chyron line,...
Definition dwin.c:1651
void restore_wins()
Restore all windows after a screen resize.
Definition dwin.c:933
void view_full_screen_resize(Init *)
Resize the full screen view and its components.
Definition init_view.c:115
RGB xterm256_idx_to_rgb(int)
Convert XTerm 256 color index to RGB color.
Definition dwin.c:366
int rgb_to_curses_clr(RGB *)
Get color index for RGB color.
Definition dwin.c:315
int get_clr_pair(int fg, int bg)
Get color pair index for foreground and background colors.
Definition dwin.c:284
int answer_yn(char *em0, char *em1, char *em2, char *em3)
Accept a single letter answer.
Definition dwin.c:1041
int Perror(char *)
Display a simple error message window or print to stderr.
Definition dwin.c:1162
int display_error(char *em0, char *em1, char *em2, char *em3)
Display an error message window or print to stderr.
Definition dwin.c:1102
int shell(char *)
Execute a shell command.
Definition exec.c:82
int full_screen_shell(char *)
Execute a shell command in full screen mode.
Definition exec.c:62
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition futil.c:435
int destroy_argv(int argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:385
size_t strnz(char *, size_t)
terminates string at New Line, Carriage Return, or max_len
Definition futil.c:506
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:311
bool expand_tilde(char *, int)
Replaces "~/" in string with the user's home directory.
Definition futil.c:904
size_t strip_ansi(char *, char *)
Strips ANSI SGR escape sequences (ending in 'm') from string s to d.
Definition futil.c:763
bool mk_dir(char *dir)
If directory doesn't exist, make it.
Definition futil.c:1181
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition futil.c:464
bool str_subc(char *, char *, char, char *, int)
Replaces "ReplaceChr" in "s" with "Withstr" in "d" won't copy more than "l" bytes to "d" Replaces all...
Definition futil.c:581
size_t strnlf(char *, size_t)
terminates string with line feed
Definition futil.c:524
int a_toi(char *, bool *)
a safer alternative to atoi() for converting ASCII strings to integers.
Definition futil.c:671
bool base_name(char *, char *)
Returns the base name of a file specification.
Definition futil.c:984
int str_to_args(char **, char *, int)
Converts a string into an array of argument strings.
Definition futil.c:331
int view_init_input(View *, char *)
Initialize the input for a C-Menu View.
Definition init_view.c:443
bool verify_spec_arg(char *, char *, char *, char *, int)
Verify file specification argument.
Definition mem.c:406
int view_file(Init *)
Start view.
void lp(char *)
Send File to Print Queue.
bool enter_file_spec(Init *, char *)
use form to enter a file specification
int write_view_buffer(Init *, bool)
Write buffer contents to files.
#define get_prev_char()
read the previous characater from the virtual fileThere is no need to track line numbers when moving ...
Definition view_engine.c:67
void cat_file(View *)
Concatenate File to Standard Output.
void remove_file(View *)
Remove File.
int get_cmd_arg(View *, char *)
Get Command Argument from User Input.
int view_cmd_processor(Init *)
Main Command Processing Loop for View.
int get_cmd_char(View *, off_t *)
Get Command Character and Numeric Argument.
void build_prompt(View *)
Build Prompt String.
#define get_next_char()
read the next characater from the virtual file
Definition view_engine.c:44
void sync_ln(View *)
Synchronize Line Table with Current File Position.
off_t get_next_line(View *, off_t)
Get Next Line from View->buf.
void go_to_eof(View *)
Go to End of File.
void prev_page(View *)
display previous page
bool search(View *, int *, char *)
Search for Regular Expression Pattern.
void initialize_line_table(View *)
Initialize Line Table.
void scroll_down_n_lines(View *, int)
Scroll N Lines.
off_t get_pos_prev_line(View *, off_t)
Get Position of Previous Line.
void scroll_up_n_lines(View *, int)
Scroll Up N Lines.
off_t get_prev_line(View *, off_t)
Get Previous Line from View->buf.
int go_to_line(View *, off_t)
Go to Specific Line.
void next_page(View *)
Advance to Next Page.
void go_to_mark(View *, int)
Go to Mark.
void increment_ln(View *)
Increment Line Index and Update Line Table.
void go_to_percent(View *, int)
Go to Percent of File.
off_t get_pos_next_line(View *, off_t)
Get Position of Next Line.
void display_line(View *)
Display Line on Padparam View *view data structure.
int pad_refresh(View *)
Refresh Pad and Line Number Window.
void view_restore_wins()
Restore View Windows.
int display_prompt(View *, char *)
Display Command Line Prompt.
int fmt_line(View *)
Format Line for Display.
void parse_ansi_str(char *, attr_t *, int *)
Parse ANSI SGR Escape Sequence.
void view_display_help(Init *)
Display View Help File.
void view_display_page(View *)
Display Current Page.
char title[MAXLEN]
Definition common.h:120
char mapp_help[MAXLEN]
Definition common.h:137
int begx
Definition common.h:109
int cols
Definition common.h:107
char mapp_home[MAXLEN]
Definition common.h:135
int begy
Definition common.h:108
View * view
Definition common.h:175
int lines
Definition common.h:106
char editor[MAXLEN]
Definition common.h:159
int r
Definition cm.h:305
int b
Definition cm.h:307
int g
Definition cm.h:306
char in_spec[MAXLEN]
Definition view.h:121
int cury
Definition view.h:102
int argc
Definition view.h:58
char ** argv
Definition view.h:59
int smaxrow
Definition view.h:113
bool f_search_complete
Definition view.h:88
int next_cmd_char
Definition view.h:78
char * buf
Definition view.h:146
char * line_out_p
Definition view.h:99
int curx
Definition view.h:103
char * line_in_end_p
Definition view.h:101
int in_fd
Definition view.h:139
off_t srch_curr_pos
Definition view.h:135
WINDOW * pad
Definition view.h:69
off_t page_top_pos
Definition view.h:133
off_t file_pos
Definition view.h:131
off_t page_top_ln
Definition view.h:163
char prompt_str[MAXLEN]
Definition view.h:56
int smincol
Definition view.h:111
off_t prev_file_pos
Definition view.h:132
bool f_bod
Definition view.h:80
bool f_first_iter
Definition view.h:87
char line_in_s[PAD_COLS]
Definition view.h:95
char help_spec[MAXLEN]
Definition view.h:123
bool f_redisplay_page
Definition view.h:85
char * file_spec_ptr
Definition view.h:127
char * next_file_spec_ptr
Definition view.h:128
bool f_is_pipe
Definition view.h:83
bool f_ignore_case
Definition view.h:61
bool f_help_spec
Definition view.h:126
char cmd[MAXLEN]
Definition view.h:54
off_t ln
Definition view.h:157
int first_match_x
Definition view.h:117
WINDOW * cmdln_win
Definition view.h:68
int smaxcol
Definition view.h:115
off_t * ln_tbl
Definition view.h:159
int cols
Definition view.h:49
bool f_full_screen
Definition view.h:89
bool f_ln
Definition view.h:156
bool f_strip_ansi
Definition view.h:64
bool f_eod
Definition view.h:81
off_t mark_tbl[NMARKS]
Definition view.h:137
WINDOW * ln_win
Definition view.h:70
int lines
Definition view.h:48
int last_match_x
Definition view.h:119
int pminrow
Definition view.h:107
off_t ln_max_pos
Definition view.h:162
char stripped_line_out[PAD_COLS]
Definition view.h:97
off_t srch_beg_pos
Definition view.h:136
off_t ln_tbl_cnt
Definition view.h:161
bool f_at_end_remove
Definition view.h:62
char * tmp_file_name_ptr
Definition view.h:129
int pmincol
Definition view.h:108
off_t file_size
Definition view.h:130
bool f_squeeze
Definition view.h:63
char tmp_prompt_str[MAXLEN]
Definition view.h:72
char cur_file_str[MAXLEN]
Definition view.h:94
char line_out_s[PAD_COLS]
Definition view.h:96
int sminrow
Definition view.h:109
cchar_t cmplx_buf[PAD_COLS]
Definition view.h:98
int cmd_line
Definition view.h:105
char out_spec[MAXLEN]
Definition view.h:122
int tab_stop
Definition view.h:77
off_t page_bot_ln
Definition view.h:164
char title[MAXLEN]
Definition view.h:57
off_t page_bot_pos
Definition view.h:134
bool f_displaying_help
Definition view.h:86
int begy
Definition view.h:50
int maxcol
Definition view.h:106
int curr_argc
Definition view.h:74
int out_fd
Definition view.h:140
int scroll_lines
Definition view.h:104
char file_name[MAXLEN]
Definition view.h:84
int begx
Definition view.h:51
off_t ln_tbl_size
Definition view.h:160
char * line_in_beg_p
Definition view.h:100
char cmd_arg[MAXLEN]
Definition view.h:76