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