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#ifdef NCURSES_UI
16#include "../ui/ui_ncurses_internal.h"
17#include "ui_backend.h"
18#include <ncursesw/ncurses.h>
19#include <ncursesw/panel.h>
20#endif
21#include <common.h>
22#include <ctype.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <iso646.h>
26#include <regex.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/mman.h>
32#include <sys/param.h>
33#include <sys/stat.h>
34#include <termios.h>
35#include <time.h>
36#include <unistd.h>
37
38/** @brief read the next characater from the virtual file
39 @ingroup view_engine
40 @details Line numbers are tracked when reading forward and stored in a line
41 table for quick access when moving backwards.
42 When reading forward, the End of Data (EOD) flag is set when the
43 position is equal to the file size, and cleared when the position or reading
44 direction changes.
45 @note view_engine.c line numbering
46 The macro, get_next_char() reads lines delimited by '\n', advancing the
47 line number index at the end of each record. The line number index
48 is one record ahead for most operations, including line numbering as lines
49 are displayed. Nevertheless, when providing a line number index to
50 get_line(), you must use the zero origin index, which is one less than
51 the line number index
52 */
53#define get_next_char()
54 {
55 c = 0;
56 do {
57 if (view->file_pos == view->file_size) {
58 view->f_eod = true;
59 break;
60 } else
61 view->f_eod = false;
62 c = view->buf[view->file_pos++];
63 } while (c == 0x0d);
64 if (c == '\n')
65 increment_ln(view);
66 }
67/** @brief read the previous characater from the virtual file
68 @ingroup view_engine
69 There is no need to track line numbers when moving backwards as they
70 are stored in the line table and accessed as needed.
71 When reading in reverse, the Beginning of Data (BOD) flag is set when
72 the file position is zero, and cleared when the position or reading direction
73 changes.
74 Carriage-returns are ignored as they should be.
75 */
76#define get_prev_char()
77 {
78 c = 0;
79 do {
80 if (view->file_pos == 0) {
81 view->f_bod = true;
82 break;
83 } else
84 view->f_bod = false;
85 c = view->buf[--view->file_pos];
86 } while (c == 0x0d);
87 }
88
89#define confirm()
90 {
91 if ((c = get_cmd_char(view, &n_cmd)) == 'y' || c == 'Y')
92 ans = true;
93 else if (c == 'n' || c == 'N')
94 ans = false;
95 }
96
97#define _Perror(msg)
98 {
99 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
100 strnz__cpy(em1, msg, MAXLEN - 1);
101 display_error(em0, em1, nullptr, nullptr);
102 }
103
104#define _Refresh(view)
105 {
106 pad_refresh(view);
107 }
108
110FILE *dbgfp;
111int view_file(Init *);
112int view_cmd_processor(Init *);
113int get_cmd_char(View *, off_t *);
114int get_cmd_arg(View *, char *);
115void build_prompt(View *);
116void cat_file(View *);
117void lp(View *, char *);
118void go_to_mark(View *, uint);
119void go_to_eof(View *);
120int go_to_line(View *, off_t);
121void go_to_percent(View *, uint);
122void go_to_position(View *, off_t);
123bool search(View *, int, char *);
124void next_page(View *);
125void prev_page(View *);
126void scroll_down(View *, uint);
127void scroll_up(View *, uint);
128void get_line(View *, off_t);
129int fmt_line(View *);
130void log_split_lines(View *);
131void log_cc_buf(View *);
132void log_stripped_line_out(View *);
133void log_strnz(char *, uint);
134void display_line(View *);
135void display_line_eod(View *);
136void display_split_line(View *);
137void view_display_page(View *);
138void parse_ansi_str(char *, attr_t *, ushort *);
139void view_display_help(Init *);
140int display_prompt(View *, char *);
141int write_view_buffer(Init *, bool);
142bool enter_file_spec(Init *, char *);
143int a_toi(char *, bool *);
144void increment_ln(View *);
145void initialize_line_table(View *);
146void destroy_line_table(View *);
147int pad_refresh(View *);
148void sync_ln(View *);
149off_t line_number(View *, off_t);
151
152/** @brief Start view
153 @ingroup view_engine
154 @param init Pointer to the Init structure containing initialization
155 parameters and state for the view application. This structure is used to pass
156 necessary information and maintain state across different functions within
157 the view application.
158 @return Returns 0 on successful completion of the view application, or a
159 non-zero value if an error occurs during initialization or execution.
160 */
161int view_file(Init *init) {
162 View *view = init->view;
163 if (view->argc < 1) {
164 view->curr_argc = -1;
165 view->argc = 0;
166 view->next_file_spec_ptr = "-";
167 } else
168 view->next_file_spec_ptr = view->argv[0];
169 while (view->curr_argc < view->argc) {
170 if (view->next_file_spec_ptr == nullptr ||
171 *view->next_file_spec_ptr == '\0') {
172 break;
173 }
175 view->next_file_spec_ptr = nullptr;
177 if (view_init_input(init, view->cur_file_str) == 0) {
178 if (view->buf) {
179 view->f_eod = 0;
180 view->f_bod = 0;
181 view->maxcol = 0;
182 view->page_top_pos = 0;
183 view->page_top_ln_no = 0;
184 view->page_bot_ln_no = 0;
185 view->ln_max_pos = 0;
186 view->page_bot_pos = 0;
187 view->file_pos = 0;
189 if (!view->f_full_screen)
192 next_page(view);
195 munmap(view->buf, view->file_size);
196 }
197 } else {
198 view->curr_argc++;
199 if (view->curr_argc < view->argc) {
201 }
202 }
203 }
205 return 0;
206}
207/** @brief Main Command Processing Loop for View
208 @ingroup view_engine
209 @param init Pointer to the Init structure containing initialization
210 parameters and state for the view application. This structure is used to pass
211 necessary information and maintain state across different functions within
212 the view application.
213*/
214int view_cmd_processor(Init *init) {
215 char tmp_str[MAXLEN];
216 int tfd;
217 int c;
218 uint shift = 0;
219 int search_cmd = 0;
220 int prev_search_cmd = 0;
221 int rc;
222 uint i;
223 bool ans = true;
224 ssize_t bytes_written;
225 char *e;
226 char shell_cmd_spec[MAXLEN];
227 off_t n_cmd = 0L;
228 off_t prev_file_pos;
229 uint max_pmincol;
230 View *view = init->view;
231 UiSurface *sfc = view->sfc;
232 view->cmd[0] = '\0';
233 while (1) {
234 if (view->f_redisplay_page) {
236 view->f_redisplay_page = false;
237 }
238 c = view->next_cmd_char;
239 view->next_cmd_char = 0;
240 if (!c) {
241 build_prompt(view);
243 c = get_cmd_char(view, &n_cmd);
244 if (c >= '0' && c <= '9') {
245 tmp_str[0] = (char)c;
246 tmp_str[1] = '\0';
247 c = get_cmd_arg(view, tmp_str);
248 }
249 }
250 switch (c) {
251
252 case 'd': /** 'd' Breakpoint for Debugging */
253 break;
254 case Ctrl('L'): /**< Ctrl('L') or KEY_RESIZE - Handle terminal resize */
255 case KEY_RESIZE:
257#ifdef DEBUG_RESIZE
258 ssnprintf(em0, MAXLEN - 1,
259 "%s:%d view->page_top_ln_no=%d, resized to lines: %d, cols: %d\n",
260 __FILE__, __LINE__, view->page_top_ln_no, view->lines, view->cols);
261 write_cmenu_log(em0);
262#endif
263 if (view->f_full_screen)
265 else
267 view->f_redisplay_page = true;
268 continue;
269 case KEY_ALTHOME: /**< KEY_ALTHOME - horizontal scroll to the first
270 column */
271 view->pmincol = 0;
272 break;
273 case KEY_ALTEND: /**< KEY_ALTEND horizontal scroll to the last
274 * column
275 */
276 if (view->maxcol > view->cols)
277 view->pmincol = view->maxcol - view->cols;
278 else
279 view->pmincol = 0;
280 break;
281 case 'h': /**< 'h', Ctrl('H'), KEY_LEFT, KEY_BACKSPACE - Horizontal
282 scroll left by two thirds of the page width */
283 case KEY_LEFT:
284 case KEY_BACKSPACE:
285 if (n_cmd > 0)
286 view->h_shift = (uint)n_cmd;
287 else if (n_cmd == 0) {
288 if (view->h_shift > 0)
289 n_cmd = view->h_shift;
290 else
291 n_cmd = 1;
292 }
293 shift = (uint)n_cmd;
294 if (view->pmincol - shift > 0)
295 view->pmincol -= shift;
296 else
297 view->pmincol = 0;
298 break;
299 case 'l': /**< 'l', KEY_RIGHT - Horizontal scroll right by two thirds of the page width */
300 case KEY_RIGHT:
301 if (n_cmd > 0)
302 view->h_shift = n_cmd;
303 else if (n_cmd == 0) {
304 if (view->h_shift > 0)
305 n_cmd = view->h_shift;
306 else
307 n_cmd = 1;
308 }
309 shift = (uint)n_cmd;
310 max_pmincol = (view->maxcol > view->cols) ? (view->maxcol - view->cols) : 0;
311 if (view->pmincol + shift < max_pmincol)
312 view->pmincol += shift;
313 else
314 view->pmincol = max_pmincol;
315 break;
316 case 'k': /** 'k', KEY_UP - Scroll up one line */
317 case KEY_UP:
318 if (n_cmd <= 0)
319 n_cmd = 1;
320 scroll_up(view, (uint)n_cmd);
321 break;
322 /** 'j', KEY_DOWN, KEY_ENTER - scroll down one line */
323 case 'j':
324 case '\n':
325 case KEY_DOWN:
326 case KEY_ENTER:
327 if (n_cmd <= 0)
328 n_cmd = 1;
329 scroll_down(view, (uint)n_cmd);
330 break;
331 /** Ctrl('B'), KEY_PPAGE - Previous Page */
332 case KEY_PPAGE:
333 case Ctrl('B'):
334 prev_page(view);
335 break;
336 /** Ctrl('F'), KEY_NPAGE Next Page */
337 case KEY_NPAGE:
338 case Ctrl('F'):
339 view->ln_no++;
340 next_page(view);
341 break;
342 /** '!', Execute Shell Command from within C-Menu View */
343 case '!':
344 if (view->f_displaying_help)
345 break;
346 if (get_cmd_arg(view, "!") == 0) {
347 if (!view->f_is_pipe) {
350 str_subc(shell_cmd_spec, view->cmd_arg, '%',
351 view->cur_file_str, MAXLEN - 1);
352 } else
353 strnz__cpy(shell_cmd_spec, view->cmd_arg, MAXLEN - 1);
354 full_screen_shell(shell_cmd_spec);
355 if (!view->f_is_pipe) {
357 return 0;
358 }
359 }
360 break;
361 /** '+', Set Startup Command */
362 case '+':
363 if (get_cmd_arg(view, "Startup Command:") == 0)
365 break;
366 /** '-', Change View Settings */
367 case '-':
368 display_prompt(view, "(i, n, s, t, w, or h)->");
369 c = get_cmd_char(view, &n_cmd);
370 c = S_TOLOWER(c);
371 switch (c) {
372 /** -i ignore_case in search */
373 /** -n line numbers */
374 /** -s squeeze multiple blank lines */
375 /** -t n set tab stop columns */
376 /** -w wrap long lines */
377 /** -h display help */
378 case 'i':
379 display_prompt(view, "Ignore Case in search (Y or N)->");
380 confirm();
381 if (ans)
382 view->f_ignore_case = true;
383 else if (c == 'n' || c == 'N')
384 view->f_ignore_case = false;
385 break;
386 /** -n line numbers */
387 case 'n':
388 display_prompt(view, "Line Numbering (Y or N)->");
389 confirm();
390 if (view->f_ln == ans)
391 break;
392 view->f_ln = ans;
393 view->f_redisplay_page = true;
395 break;
396 /** -s Squeeze Multiple Blank Lines */
397 case 's':
399 view, "view->f_squeeze Multiple Blank lines (Y or N)->");
400 confirm();
401 view->f_squeeze = ans;
402 break;
403 case 'w':
405 view, "Line Wrapping (Y or N)->");
406 if ((c = get_cmd_char(view, &n_cmd)) == 'y' || c == 'Y')
407 ans = true;
408 else if (c == 'n' || c == 'N')
409 ans = false;
410 if (view->wrap == ans)
411 break;
412 view->wrap = ans;
413 view->f_redisplay_page = true;
415 break;
416 /** -t n Set Tab Stop Columns */
417 case 't':
418 sprintf(tmp_str,
419 "Tabstop Colums Currently %d:", view->tab_stop);
420 i = 0;
421 if (get_cmd_arg(view, tmp_str) == 0)
422 i = atoi(view->cmd_arg);
423 if (i >= 1 && i <= 12) {
424 view->tab_stop = i;
425 view->f_redisplay_page = true;
426 } else
427 Perror("Tab stops not changed");
428 break;
429 /** -h Display Help */
430 case KEY_F01:
431 case 'h':
432 if (!view->f_displaying_help) {
434 view = init->view;
435 }
436 view->next_cmd_char = '-';
437 break;
438 default:
439 break;
440 }
441 break;
442 /** 'n' - Repeat Previous Search */
443 case 'n':
444 if (view->f_search_complete) {
445 Perror("Search complete, no more matches");
446 break;
447 }
448 if (prev_search_cmd == 0) {
449 Perror("No previouis search");
450 break;
451 }
452 if (prev_search_cmd == '/') {
453 view->cury = 0;
455 } else {
456 view->cury = view->scroll_lines + 1;
458 }
459 rc = search(view, prev_search_cmd, prev_regex_pattern);
460 if (rc == false) {
461 Perror("No matches found");
462 break;
463 }
464 break;
465 /** '/' or '?' - Search Forward fromk top of page */
466 case '/':
467 view->f_search_complete = false;
468 strnz__cpy(tmp_str, " Forward:", MAXLEN - 1);
469 search_cmd = c;
470 c = get_cmd_arg(view, tmp_str);
471 if (c == KEY_F09 || c == '\033')
472 break;
473 if (c == KEY_ENTER) {
474 view->cury = 0;
475 view->f_first_iter = true;
478 rc = search(view, search_cmd, view->cmd_arg);
479 if (rc == false) {
480 Perror("No matches found");
481 break;
482 }
483 prev_search_cmd = search_cmd;
485 }
486 break;
487 /** '?' - Search Backward */
488 case '?':
489 view->f_search_complete = false;
490 strnz__cpy(tmp_str, " Backward:", MAXLEN - 1);
491 search_cmd = c;
492 c = get_cmd_arg(view, tmp_str);
493 if (c == KEY_F09 || c == '\033')
494 break;
495 if (c == '\n') {
496 view->cury = view->scroll_lines;
497 view->f_first_iter = true;
500 rc = search(view, search_cmd, view->cmd_arg);
501 if (rc == false) {
502 Perror("No matches found");
503 break;
504 }
505 prev_search_cmd = search_cmd;
507 }
508 break;
509 /** 'o' - Open a File */
510 case 'o':
511 if (get_cmd_arg(view, "File name:") == 0) {
512 strtok(view->cmd_arg, " ");
513 view->next_file_spec_ptr = strdup(view->cmd_arg);
514 view->f_redisplay_page = true;
515 return 0;
516 }
517 break;
518 case 'G': /** 'G' - Go to the Beginning of the Document or line */
519 case KEY_HOME:
520 if (n_cmd > 0) {
521 n_cmd -= 1;
522 go_to_line(view, n_cmd);
523 } else
524 go_to_eof(view);
525 break;
526 /** 'H' or KEY_F01 - Display Help Information */
527 case 'H':
528 case KEY_F01:
529 if (!view->f_displaying_help) {
531 view = init->view;
532 }
533 break;
534 /** 'm' - Set a Mark at the Current Position */
535 case 'm':
536 display_prompt(view, "Mark label (A-Z)->");
537 c = get_cmd_char(view, &n_cmd);
538 if (c == '@' || c == KEY_F09 || c == '\033')
539 if (c >= 'A' && c <= 'Z')
540 c += ' ';
541 if (c < 'a' || c > 'z')
542 Perror("Not (a-z)");
543 else
544 view->mark_tbl[c - 'a'] = view->page_top_pos;
545 break;
546 /** 'M' - Go to a Mark */
547 case 'M':
548 display_prompt(view, "Goto mark (A-Z)->");
549 c = get_cmd_char(view, &n_cmd);
550 if (c == '@' || c == KEY_F09 || c == '\033')
551 break;
552 if (c >= 'A' && c <= 'Z')
553 c += ' ';
554 if (c < 'a' || c > 'z')
555 Perror("Not (A-Z)");
556 else
557 go_to_mark(view, c);
558 break;
559 /** 'N' - Close Current File and Open Next File */
560 case 'N':
561 if (n_cmd <= 0)
562 n_cmd = 1;
563 if (view->curr_argc + n_cmd >= view->argc) {
564 Perror("no more files");
565 view->curr_argc = view->argc - 1;
566 } else {
567 view->curr_argc++;
568 if (view->curr_argc < view->argc)
570 return 0;
571 }
572 break;
573 /** 'p' or '%' - Go to a Percent of the File */
574 case 'p':
575 case '%':
576 if (n_cmd < 0)
577 go_to_line(view, 1);
578 if (n_cmd >= 100)
579 go_to_eof(view);
580 else
581 go_to_percent(view, n_cmd);
582 break;
583 /** Ctrl('Z') - Send File to Print Queue with Notation */
584 case Ctrl('Z'):
585 get_cmd_arg(view, "Enter Notation:");
586 strnz__cpy(tmp_str, "/tmp/view-XXXXXX", MAXLEN - 1);
587 tfd = mkstemp(tmp_str);
589 if (tfd == -1) {
590 Perror("Unable to create temporary file");
591 break;
592 }
593 strnz__cpy(shell_cmd_spec, "echo ", MAXLEN - 5);
594 strnz__cat(shell_cmd_spec, view->cmd_arg, MAXLEN - 5);
595 strnz__cat(shell_cmd_spec, view->tmp_file_name_ptr, MAXLEN - 5);
596 shell(shell_cmd_spec);
597 strnz__cpy(shell_cmd_spec, "cat ", MAXLEN - 5);
598 strnz__cat(shell_cmd_spec, view->cmd_arg, MAXLEN - 5);
599 strnz__cat(shell_cmd_spec, ">>", MAXLEN - 5);
600 strnz__cat(shell_cmd_spec, view->tmp_file_name_ptr, MAXLEN - 5);
601 shell(shell_cmd_spec);
602 lp(view, view->cur_file_str);
603 shell(shell_cmd_spec);
604 ssnprintf(shell_cmd_spec, (size_t)(MAXLEN - 5), "rm %s",
606 strnz__cpy(shell_cmd_spec, "rm ", MAXLEN - 5);
607 strnz__cat(shell_cmd_spec, view->tmp_file_name_ptr, MAXLEN - 5);
608 shell(shell_cmd_spec);
610 view->f_redisplay_page = true;
611 unlink(tmp_str);
612 break;
613 /** 'P' Print Current File */
614 case Ctrl('P'):
615 lp(view, view->cur_file_str);
616 view->f_redisplay_page = true;
617 break;
618 /** 'P' or KEY_F09 or ESC - Close Current File and Open Next */
619 case 'P':
620 if (n_cmd <= 0)
621 n_cmd = 1;
622 if (view->curr_argc - n_cmd < 0) {
623 Perror("No previous file");
624 view->curr_argc = 0;
625 } else {
626 view->curr_argc--;
627 if (view->curr_argc >= 0)
629 return 0;
630 }
631 break;
632 /** 'q' or 'Q' or KEY_F09 or ESC - Quit the Application */
633 case 'q':
634 case 'Q':
635 case KEY_F09:
636 case '\033':
638 view->curr_argc = view->argc;
639 view->next_file_spec_ptr = nullptr;
640 return 0;
641 /** 'v' - Open Current File in Editor */
642 case 'v':
643 if (init->editor[0] == 0) {
644 e = getenv("DEFAULTEDITOR");
645 if (e == nullptr || *e == '\0')
647 else
649 }
651 "View doesn't support editing current buffer directly",
652 MAXLEN - 1);
653 strnz__cpy(em1, "Would you like to write the buffer to a file?",
654 MAXLEN - 1);
655 strnz__cpy(em2, "Enter Y for yes or any other key to cancel.",
656 MAXLEN - 1);
657 rc = display_error(em0, em1, em2, nullptr);
658 if (rc != 'y' && rc != 'Y')
659 break;
660 if (!enter_file_spec(init, view->out_spec)) {
661 view->f_redisplay_page = true;
662 break;
663 }
664 prev_file_pos = view->page_top_pos;
665 bytes_written = write_view_buffer(init, view->f_strip_ansi);
666 if (bytes_written == 0) {
667 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__,
668 __LINE__ - 2);
669 strnz__cpy(em1, "0 bytes written", MAXLEN - 1);
670 strerror_r(errno, em1, MAXLEN - 1);
671 display_error(em0, em1, nullptr, nullptr);
672 break;
673 }
675 strnz__cpy(shell_cmd_spec, init->editor, MAXLEN - 5);
676 strnz__cat(shell_cmd_spec, " ", MAXLEN - 5);
677 strnz__cat(shell_cmd_spec, view->in_spec, MAXLEN - 5);
678 full_screen_shell(shell_cmd_spec);
680 prev_file_pos;
681
683 view->f_redisplay_page = true;
684 return 0;
685 /** 'w' - Write the current buffer to file */
686 case 'w':
687 if (!enter_file_spec(init, view->out_spec)) {
688 view->f_redisplay_page = true;
689 break;
690 }
691 // prev_file_pos = view->page_top_pos;
692 bytes_written = write_view_buffer(init, view->f_strip_ansi);
693 if (bytes_written == 0) {
694 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__,
695 __LINE__ - 2);
696 strnz__cpy(em1, "0 bytes written", MAXLEN - 1);
697 strerror_r(errno, em1, MAXLEN - 1);
698 display_error(em0, em1, nullptr, nullptr);
699 break;
700 }
701 display_prompt(view, tmp_str);
702 view->f_redisplay_page = true;
703 break;
704 case CT_VIEW:
705 break;
706 /** 'V' - Display Version Information */
707 case 'V':
708 ssnprintf(em0, MAXLEN - 1, "C-Menu Version: %s", CM_VERSION);
709 display_error(em0, em1, nullptr, nullptr);
710 break;
711 default:
712 break;
713 }
714 view->cmd_arg[0] = '\0';
715 }
716}
717/** @brief Get Command Character 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 n Pointer to an off_t variable where the numeric argument entered by
723 the user will be stored. If the user enters a numeric argument, it will be
724 converted to an off_t value and stored in this variable for use by the
725 calling function.
726 @return Returns the command character entered by the user, or a special
727 value if a mouse event is detected. The function handles user input,
728 including editing keys, and updates the command argument buffer accordingly.
729 If the user enters a numeric argument, it is validated based on the context
730 of the command being executed.
731*/
732int get_cmd_char(View *view, off_t *n) {
733 int c = 0, i = 0;
734 char *d, *s;
735 uint prevx;
736 bool once = false;
737 char cmd_str[33];
738 cmd_str[0] = '\0';
739 UiSurface *sfc = view->sfc;
740 UiEvent event;
741 pad_refresh(view);
744 while (1) {
745 if (i == 1 && !once) {
746 once = true;
747 view->curx = 0;
752 }
754 event.y = event.x = -1;
755 c = ui_get_event_no_mouse(sfc, CMDLN, &event);
756 switch (c) {
757 case KEY_MOUSE:
758 break;
759 case KEY_RESIZE:
760 case '\n':
761 case '\r':
762 break;
763 case 'q':
764 case KEY_F09:
765 build_prompt(view);
767 c = KEY_F09;
768 return c;
769 case '\b':
770 case KEY_BACKSPACE:
771 if (i > 0) {
772 s = &cmd_str[i];
773 d = &cmd_str[--i];
774 view->curx--;
775 prevx = view->curx;
776 while (*s != '\0') {
778 *d++ = *s++;
779 }
780 *d = '\0';
783 view->curx = prevx;
785 }
786 continue;
787 case KEY_DC:
788 if (i < 32 && cmd_str[i] != '\0') {
789 s = &cmd_str[i + 1];
790 d = &cmd_str[i];
791 while (*s != '\0') {
793 *d++ = *s++;
794 }
795 *d = '\0';
799 }
800 continue;
801 case KEY_SRIGHT:
802 if (i < 32 && cmd_str[i] != '\0') {
803 i++;
804 ui_getyx(sfc, CMDLN, &view->cury, &view->curx);
805 if (view->curx < view->cols - 1) {
806 view->curx++;
808 }
809 }
810 continue;
811 case KEY_SLEFT:
812 if (i > 0) {
813 i--;
814 ui_getyx(sfc, CMDLN, &view->cury, &view->curx);
815 if (view->curx > 0) {
816 view->curx--;
818 }
819 }
820 continue;
821 default:
822 if (c < '0' || c > '9' || i == 32)
823 break;
824 cmd_str[i++] = (char)c;
825 cmd_str[i] = '\0';
827 view->curx++;
828 continue;
829 }
830 if (c < '0' || c > '9' || i == 32)
831 break;
832 }
833 if (cmd_str[0] == '\0') {
834 *n = 0;
835 view->cmd_arg[0] = '\0';
838 return (c);
839 }
840 *n = atol(cmd_str);
841 view->cmd_arg[0] = '\0';
845 return (c);
846}
847/** @brief Get Command Argument from User Input
848 @ingroup view_engine
849 @param view Pointer to the View structure containing the state and
850 parameters of the view application. This structure is used to access and
851 modify the state of the application as needed.
852 @param prompt A string containing the prompt to be displayed to the user
853 when requesting input for the command argument. This prompt is shown on
854 the command line to guide the user in providing the necessary input for
855 the command being executed.
856 @return Returns the command character entered by the user, or a special
857 value if a mouse event is detected. The command argument entered by the
858 user is stored in the view->cmd_arg buffer for use by the calling
859 function. The function handles user input, including editing keys, and
860 updates the command argument buffer accordingly. If the user enters a
861 numeric argument, it is validated based on the context of the command
862 being executed.
863*/
864int get_cmd_arg(View *view, char *prompt) {
865 int c;
866 char prompt_s[MAXLEN];
867 UiSurface *sfc = view->sfc;
868 uint prompt_l = strnz__cpy(prompt_s, prompt, min(MAXLEN - 1, view->cols - 4));
869 if (view->cmd_arg[0] != '\0')
870 return 0;
872 if (prompt_l > 1) {
874 ui_waddstr(sfc, CMDLN, prompt_s);
876 }
877 view->curx = prompt_l;
879 pad_refresh(view);
884 uint flin = view->cmd_line;
885 uint fcol = prompt_l + 1;
886 uint flen = view->cols - prompt_l;
887 c = cf_accept(sfc, CMDLN, view->cmd_arg, flin, fcol, flen);
888 return c;
889}
890/** @brief Build Prompt String
891 * @ingroup view_engine
892 @param view Pointer to the View structure containing the state and
893 parameters of the view application. This structure is used to access and
894 modify the state of the application as needed.
895 @details
896 This function constructs a prompt string that provides information about
897 the current state of the view application. The prompt includes details such
898 as the file name, current column, file number, file position, and whether
899 the end of the document has been reached. The constructed prompt string is
900 stored in the view->prompt_str buffer for display to the user.
901 @note The prompt string is built based on the current state of the view
902 application, and segments that are not relevant will be omitted. Less
903 relevant segments will be omitted if the length of the prompt string
904 exceeds more than half the available space. The length of the prompt
905 string has a hard limit of view->cols - 4.
906 */
907void build_prompt(View *view) {
908 char tmp_str[MAXLEN];
909 uint prompt_maxlen = min(MAXLEN - 1, view->cols - 4);
910 uint prompt_l = 0;
911 // ----------------< File Name >----------------
912 if (view->f_is_pipe)
913 strnz__cpy(view->prompt_str, "stdin", prompt_maxlen);
914 else
915 strnz__cpy(view->prompt_str, view->file_name, prompt_maxlen);
916 // ----------------< Columns >----------------
917 if (view->pmincol > 0) {
918 sprintf(tmp_str, "Col %d of %d", view->pmincol, view->maxcol);
919 if (view->prompt_str[0] != '\0')
920 strnz__cat(view->prompt_str, "|", prompt_maxlen);
921 strnz__cat(view->prompt_str, tmp_str, prompt_maxlen);
922 }
923 // ----------------< File Number >----------------
924 if (view->argc > 1) {
925 prompt_l = (uint)strlen(view->prompt_str);
926 if (prompt_l > (view->cols - 4) / 2)
927 return;
928 if (view->argc > 0) {
929 sprintf(tmp_str, "File %d of %d", view->curr_argc + 1, view->argc);
930 if (view->prompt_str[0] != '\0') {
931 strnz__cat(view->prompt_str, "|", prompt_maxlen);
932 strnz__cat(view->prompt_str, tmp_str, prompt_maxlen);
933 }
934 }
935 }
936 // ----------------< File Position >----------------
937 prompt_l = (uint)strlen(view->prompt_str);
938 if (prompt_l > (view->cols - 4) / 2)
939 return;
941 if (view->page_top_pos == NULL_POSITION)
942 view->page_top_pos = view->file_size;
943 view->page_bot_pos = view->ln_tbl[view->page_bot_ln_no + 1];
944 if (view->page_bot_pos == NULL_POSITION)
945 view->page_bot_pos = view->file_size;
946 sprintf(tmp_str, "Pos %zd-%zd", view->page_top_pos, view->page_bot_pos);
947 if (view->prompt_str[0] != '\0') {
948 strnz__cat(view->prompt_str, "|", prompt_maxlen);
949 strnz__cat(view->prompt_str, tmp_str, prompt_maxlen);
950 }
951 if (!view->f_is_pipe) {
952 if (view->file_size > 0) {
953 sprintf(tmp_str, " of %zd", view->file_size);
954 strnz__cat(view->prompt_str, tmp_str, prompt_maxlen);
955 }
956 }
957 // ----------------< (End) >----------------
958 prompt_l = (uint)strlen(view->prompt_str);
959 if (prompt_l > (view->cols - 4) / 2)
960 return;
961 if (view->f_eod) {
962 if (view->prompt_str[0] != '\0')
963 strnz__cat(view->prompt_str, " ", prompt_maxlen);
964 strnz__cat(view->prompt_str, "(End)", prompt_maxlen);
965 if (view->curr_argc + 1 < view->argc) {
966 base_name(tmp_str, view->argv[view->curr_argc + 1]);
967 strnz__cpy(view->prompt_str, " Next File: ", prompt_maxlen);
968 strnz__cat(view->prompt_str, tmp_str, prompt_maxlen);
969 }
970 }
971}
972/** @brief Write buffer contents to files
973 @ingroup view_engine
974 @param init data structure
975 @param f_strip_ansi strip ANSI escape sequences
976 @details
977 */
978int write_view_buffer(Init *init, bool f_strip_ansi) {
979 ssize_t bytes_written = 0;
980 View *view = init->view;
981 int rc;
982 size_t l;
983 char tmp_line_s[PAD_COLS];
984 if (!f_strip_ansi) {
985 strnz__cpy(em0, "Would you like to strip ansi escape sequences?",
986 MAXLEN - 1);
987 strnz__cpy(em1, "Enter Y for yes or any other key to cancel.",
988 MAXLEN - 1);
989 rc = answer_yn(nullptr, em0, em1, nullptr);
990 if (rc == 'y' || rc == 'Y')
991 f_strip_ansi = true;
992 else
993 f_strip_ansi = false;
994 }
996 /** write the buffer */
997 view->out_fd = open(view->out_spec, O_CREAT | O_TRUNC | O_WRONLY, 0644);
998 if (view->out_fd == -1) {
999 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
1000 strnz__cpy(em1, "fwrite ", MAXLEN - 1);
1002 strerror_r(errno, em2, MAXLEN - 1);
1004 return false;
1005 }
1006 bytes_written = 0;
1007 while (!view->f_eod) {
1008 off_t ln_no = 0;
1009 get_line(view, ln_no);
1010 if (f_strip_ansi)
1011 strip_ansi(tmp_line_s, view->line_in_s);
1012 else
1013 strnz__cpy(tmp_line_s, view->line_in_s, MAXLEN - 1);
1014 l = strnlf(tmp_line_s, PAD_COLS - 1);
1015 bytes_written += write(view->out_fd, tmp_line_s, l);
1016 if (ln_no >= view->ln_tbl_size - 1)
1017 break;
1018 }
1019 close(view->out_fd);
1021 return bytes_written;
1022}
1023/** @brief Concatenate File to Standard Output
1024 @ingroup view_engine
1025 */
1026void cat_file(View *view) {
1027 int c;
1028 while (1) {
1029 get_next_char();
1030 if (view->f_eod)
1031 break;
1032 putchar(c);
1033 }
1034}
1035/** @brief Send File to Print Queue
1036 @ingroup view_engine
1037 @param view Pointer to the View structure containing the state and
1038 @param PrintFile - file to print */
1039void lp(View *view, char *PrintFile) {
1040 char *print_cmd_ptr;
1041 char shell_cmd_spec[MAXLEN];
1042 print_cmd_ptr = getenv("PRINTCMD");
1043 if (print_cmd_ptr == nullptr || *print_cmd_ptr == '\0')
1044 print_cmd_ptr = PRINTCMD;
1045 ssnprintf(shell_cmd_spec, MAXLEN - 1, "%s %s", print_cmd_ptr, PrintFile);
1046 display_prompt(view, shell_cmd_spec);
1047 shell(shell_cmd_spec);
1048}
1049/** @defgroup view_navigation View Navigation
1050 @brief Navigation functions for the view application
1051 */
1052/** @brief Go to Mark
1053 @ingroup view_navigation
1054 @param view Pointer to the View structure containing the state and
1055 parameters of the view application. This structure is used to access and
1056 modify the state of the application as needed.
1057 @param c The character representing the mark to go to. This character is
1058 typically a lowercase letter (a-z) corresponding to a mark that has been
1059 set previously in the view application. The function will attempt to
1060 navigate to the position associated with this mark, allowing the user to
1061 quickly jump to specific locations in the file based on the marks they
1062 have set. If the mark is not set, an error message will be displayed to
1063 the user.
1064 */
1065void go_to_mark(View *view, uint c) {
1066 if (c == '\'')
1067 view->file_pos = view->mark_tbl[(NMARKS - 1)];
1068 else
1069 view->file_pos = view->mark_tbl[c - 'a'];
1070 if (view->file_pos == NULL_POSITION)
1071 Perror("Mark not set");
1072 else
1074}
1075/** @brief Search for Regular Expression Pattern
1076 @ingroup view_navigation
1077 @param view Pointer to View Structure
1078 @param search_cmd Search Command Character ('/' or '?')
1079 @param regex_pattern Regular Expression Pattern to Search For
1080 @returns true if a match is found and displayed, false if the search
1081 completes without finding a match or if an error occurs
1082 @details The search performs extended regular expression matching, ignoring
1083 ANSI sequences and Unicode characters. Matches are highlighted on the
1084 screen, and the search continues until the page is full or the end of the
1085 file is reached. If the search wraps around the file, a message is
1086 displayed indicating that the search is complete.
1087 The search state is maintained in the view structure, allowing for
1088 repeat searches and tracking of the current search line. This
1089 function highlights all matches in the current ncurses pad, including
1090 those not displayed on the screen, and tracks the first and last match
1091 columns for prompt display.
1092 ANSI sequences and Unicode characters are stripped before
1093 matching, so matching corresponds to the visual display */
1094bool search(View *view, int search_cmd, char *regex_pattern) {
1095 char tmp_str[MAXLEN];
1096 int REG_FLAGS = 0;
1097 regmatch_t pmatch[1];
1098 regex_t compiled_regex;
1099 int reti;
1100 uint line_offset;
1101 uint line_len;
1102 uint match_idx;
1103 uint match_len;
1104 mbstate_t mbstate;
1105 memset(&mbstate, 0, sizeof(mbstate));
1106 wchar_t wstr[2] = {L'\0', L'\0'};
1107 attr_t attr;
1108 ushort cpx;
1109 UiCell cc = {0};
1110 off_t prev_ln_no;
1111 bool f_page = false;
1112 UiSurface *sfc = view->sfc;
1113 if (*regex_pattern == '\0')
1114 return false;
1115 if (view->f_ignore_case)
1116 REG_FLAGS = REG_ICASE | REG_EXTENDED;
1117 else
1118 REG_FLAGS = REG_EXTENDED;
1119 reti = regcomp(&compiled_regex, regex_pattern, REG_FLAGS);
1120 if (reti) {
1121 Perror("Invalid pattern");
1122 return false;
1123 }
1124 bool rc = false;
1125 while (1) {
1126 /** initialize iteration */
1127 if (search_cmd == '/') {
1128 if (view->srch_curr_ln_no == view->file_size)
1129 view->srch_curr_ln_no = 0;
1130 } else {
1131 if (view->srch_curr_ln_no == 0)
1133 }
1134 if (view->srch_curr_ln_no == view->srch_beg_ln_no) {
1135 if (view->f_first_iter == true) {
1136 view->f_first_iter = false;
1137 view->f_search_complete = false;
1138 if (search_cmd == '/')
1139 view->cury = 0;
1140 else
1141 view->cury = view->scroll_lines + 1;
1142 } else {
1143 view->f_search_complete = true;
1144 goto cleanup;
1145 }
1146 }
1147 view->ln_no = view->srch_curr_ln_no;
1148 sync_ln(view);
1149 /** get line to scan */
1150 if (search_cmd == '/') {
1151 if (view->cury == view->scroll_lines)
1152 goto cleanup;
1153 prev_ln_no = view->srch_curr_ln_no;
1156 } else {
1157 if (view->cury == 0)
1158 goto cleanup;
1160 prev_ln_no = view->srch_curr_ln_no;
1162 }
1163 fmt_line(view);
1164 reti = regexec(&compiled_regex, view->stripped_line_out,
1165 compiled_regex.re_nsub + 1, pmatch, REG_FLAGS);
1166 if (reti == REG_NOMATCH) {
1167 if (f_page) {
1168 /** non-matching page filler */
1169 if (search_cmd == '?')
1170 view->cury -= 2;
1171 display_line(view);
1172 if ((search_cmd == '/' && view->cury == view->scroll_lines) ||
1173 (search_cmd == '?' && view->cury == 1)) {
1174 break;
1175 }
1176 }
1177 view->srch_curr_ln_no += (search_cmd == '/') ? 1 : -1;
1178 continue;
1179 }
1180 if (reti) {
1181 char err_str[MAXLEN];
1182 regerror(reti, &compiled_regex, err_str, sizeof(err_str));
1183 strnz__cpy(tmp_str, "Regex match failed: ", MAXLEN - 1);
1184 strnz__cat(tmp_str, err_str, MAXLEN - 1);
1185 Perror(tmp_str);
1186 rc = false; /* Set status */
1187 goto cleanup;
1188 }
1189 rc = true;
1190 /** Display matching lines */
1191 if (!f_page) {
1192 if (search_cmd == '/') {
1193 view->page_top_ln_no = prev_ln_no;
1195 } else {
1196 view->page_bot_ln_no = view->ln_no;
1197 ui_cursor_move(sfc, PAD, 0, 0);
1198 }
1200 f_page = true;
1201 }
1202 if (search_cmd == '?')
1203 view->cury -= 2;
1204 bool f_first_match = true;
1205 view->first_match_x = -1;
1206 view->last_match_x = 0;
1207 line_len = strlen(view->stripped_line_out);
1208 line_offset = 0;
1209 while (1) {
1210 match_idx = line_offset + pmatch[0].rm_so;
1211 match_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1212 for (uint i = match_idx; i < match_idx + match_len; i++) {
1213 cc = view->cmplx_buf[i];
1214#ifdef UAL_UI
1215 ui_getcchar(&cc, wstr, &attr, &cpx, nullptr);
1216 cpx = cp_nt_rev;
1217 ui_setcchar(&cc, wstr, attr, cpx, nullptr);
1218#else
1220#endif
1221 view->cmplx_buf[i] = cc;
1222 }
1223 if (f_first_match) {
1224 f_first_match = false;
1225 view->first_match_x = pmatch[0].rm_so;
1226 }
1227 view->last_match_x = line_offset + pmatch[0].rm_eo;
1228 line_offset += pmatch[0].rm_eo;
1229 if (line_offset >= line_len)
1230 break;
1231 view->line_out_p = view->stripped_line_out + line_offset;
1232 reti = regexec(&compiled_regex, view->line_out_p,
1233 compiled_regex.re_nsub + 1, pmatch, REG_FLAGS);
1234 if (reti == REG_NOMATCH)
1235 break;
1236 if (reti) {
1237 char msgbuf[100];
1238 regerror(reti, &compiled_regex, msgbuf, sizeof(msgbuf));
1239 sprintf(tmp_str, "Regex match failed: %s", msgbuf);
1240 Perror(tmp_str);
1241 rc = false; /* Set status */
1242 goto cleanup;
1243 }
1244 if (search_cmd == '/') {
1245 if (view->cury == view->scroll_lines - 1) {
1246 break;
1247 }
1248 } else if (view->cury == 1) {
1249 break;
1250 }
1251 }
1252 display_line(view);
1253 view->srch_curr_ln_no += (search_cmd == '/') ? 1 : -1;
1254 }
1255cleanup:
1256 regfree(&compiled_regex);
1257 view->ln_no = view->srch_curr_ln_no;
1258 return rc;
1259}
1260
1261/*--------------------------------------------------------------
1262 Navigation
1263 *--------------------------------------------------------------- */
1264/** @brief display previous page
1265 @ingroup view_navigation
1266 @param view data structure
1267 @details Displays the previous page starting at (view->page_top_ln_no -
1268 view->scroll_lines).
1269 */
1270void prev_page(View *view) {
1271 off_t ln_no;
1272 if (view->page_top_ln_no == 0)
1273 return;
1274 view->cury = 0;
1275 view->ln_no = view->page_top_ln_no;
1276 uint scroll, avail, scroll_this_line;
1277 if (view->wrap) {
1278 scroll = view->scroll_lines;
1279 ln_no = view->page_top_ln_no;
1280 while (scroll > 0) {
1281 get_line(view, ln_no);
1282 fmt_line(view);
1283 view->page_top_sl = (view->cur.sl_cnt > 1);
1284 if (view->page_top_sl == false) {
1285 scroll--;
1286 if (ln_no > 0)
1287 ln_no--;
1288 continue;
1289 }
1290 if (ln_no == view->page_top_ln_no && ln_no != view->ln_no_max) {
1292 if (view->cur.sl_idx == 0) {
1293 if (ln_no > 0)
1294 ln_no--;
1295 continue;
1296 }
1297 avail = view->cur.sl_idx;
1298 } else
1299 avail = view->cur.sl_cnt;
1300 scroll_this_line = min(scroll, avail);
1301 scroll -= scroll_this_line;
1302 if (ln_no == view->page_top_ln_no && ln_no != view->ln_no_max)
1303 view->cur.sl_idx -= scroll_this_line;
1304 else
1305 view->cur.sl_idx = view->cur.sl_cnt - scroll_this_line;
1306 if (ln_no > 0)
1307 ln_no--;
1308 }
1309 ln_no++;
1312 view->page_top_ln_no = ln_no;
1313 view->ln_no = view->page_top_ln_no;
1314 view->page_top_sl = (view->page_top_sl_cnt > 1);
1315 } else {
1316 view->ln_no = view->page_top_ln_no;
1317 view->cury = 0;
1318 if (view->ln_no - view->scroll_lines >= 0)
1319 view->ln_no -= view->scroll_lines;
1320 else
1321 view->ln_no = 0;
1322 view->page_top_ln_no = view->ln_no;
1323 }
1324 next_page(view);
1325}
1326/** @brief Advance to Next Page
1327 @ingroup view_navigation
1328 @param view data structure
1329 @details Advances from view->page_bot_pos to view the next page of
1330 content. view->page_bot_pos must be set properly when calling this
1331 function. If the current bottom position of the page is at the end of the
1332 file, the function returns without making any changes. Otherwise, it
1333 resets the maximum column and current line position to the top of the
1334 page, updates the file position to the current bottom position of the
1335 page, and sets the top position and line number of the page accordingly.
1336 Finally, it calls the function to display the new page content.
1337*/
1338void next_page(View *view) {
1339
1340 view->file_pos = view->ln_tbl[view->ln_no];
1341 if (view->file_pos == view->file_size)
1342 return;
1343 view->maxcol = 0;
1344 view->cury = 0;
1345 view->page_top_ln_no = view->ln_no;
1347}
1348/** @brief Display Current Page
1349 @ingroup view_display
1350 @param view data structure
1351 */
1352void view_display_page(View *view) {
1353 view->cury = 0;
1354 view->page_top_sl = false;
1355 view->page_bot_sl = false;
1356 UiSurface *sfc = view->sfc;
1357 ui_cursor_move(sfc, PAD, 0, 0);
1358 if (view->f_ln)
1360 view->ln_no = view->page_top_ln_no;
1361 view->page_top_ln_no = 0;
1362 view->page_bot_ln_no = 0;
1363 while (view->cury < view->scroll_lines) {
1364 get_line(view, view->ln_no);
1365 if (view->f_eod)
1366 break;
1367 fmt_line(view);
1368 if (view->wrap && view->cury == 0)
1370 display_line(view);
1372 view->ln_no++;
1373 }
1374 if (view->f_eod)
1376 view->ln_no--;
1377 view->page_bot_end_pos = view->file_pos - 1;
1378 view->page_bot_pos = view->file_pos;
1379 view->page_bot_ln_no = view->ln_no;
1380}
1381/** @brief Display Line on Pad
1382 @ingroup view_display
1383 param View *view data structure
1384 @details This function displays a single line of text on the ncurses
1385 pad.
1386 If line numbering is enabled (view->f_ln), it is formatted and
1387 displayed at the beginning of the line with the specified attributes and
1388 color pair.
1389 @details Because get_next_char calls increment_ln upon encountering a
1390 line feed and increment_ln advances view->ln_no after updating the line
1391 table, the line number displayed is one greater than the index to the
1392 line table. That means the line counter begins with 1, while the table
1393 origin is 0.
1394 */
1395void display_line(View *view) {
1396 char ln_s[16];
1397
1398 UiSurface *sfc = view->sfc;
1399 if (view->wrap && view->cur.sl_cnt > 0) {
1400 if (view->cur.sl_idx < view->cur.sl_cnt) {
1402 return;
1403 }
1404 }
1405 if (view->cury > view->scroll_lines - 1)
1406 view->cury = view->scroll_lines - 1;
1407 if (view->f_ln) {
1408 ssnprintf(ln_s, 8, "%7jd", view->ln_no);
1411 ui_mvwaddstr(sfc, LNNO, view->cury, 0, ln_s);
1412 }
1415#ifdef UAL_UI
1416 ui_mvwadd_wchstr(sfc, PAD, view->cury, 0, view->cmplx_buf);
1417#else
1418 ui_mvwadd_wchstr(sfc, PAD, view->cury, 0, (struct nccell *)view->cmplx_buf);
1419#endif
1420 if (view->cury == 0)
1421 view->page_top_ln_no = view->ln_no;
1422 if (view->cury == view->scroll_lines - 1)
1423 view->page_bot_ln_no = view->ln_no;
1424 view->cury++;
1425}
1426/** @brief Display Split Line on Pad
1427 @ingroup view_display
1428 @param view data structure
1429 @details This function is used to display a line that has been split into
1430 multiple segments due to wrapping. It iterates through the segments of the
1431 current line (stored in view->cur.sl_cc and view->cur.sl_cells) and
1432 displays each segment on the pad, along with the corresponding line number
1433 if line numbering is enabled. The function also updates the top and bottom
1434 line numbers of the page based on the current position in the split line.
1435 */
1436void display_split_line(View *view) {
1437 char ln_s[16];
1438 UiSurface *sfc = view->sfc;
1439 if (view->cury > view->scroll_lines - 1)
1440 view->cury = view->scroll_lines - 1;
1441
1442 for (uint i = view->cur.sl_idx; i < view->cur.sl_cnt; i++) {
1443 if (i == 0) {
1444 if (view->f_ln) {
1445 ssnprintf(ln_s, 8, "%7jd", view->ln_no);
1448 ui_mvwaddstr(sfc, LNNO, view->cury, 0, ln_s);
1449 }
1450 } else {
1451 if (view->f_ln) {
1454 }
1455 }
1459 if (view->cury == 0) {
1460 view->page_top_ln_no = view->ln_no;
1461 view->page_top_sl = true;
1462 view->page_top_sl_idx = i;
1464 }
1465 if (view->cury == view->scroll_lines - 1) {
1466 view->page_bot_ln_no = view->ln_no;
1467 view->page_bot_sl = true;
1468 view->page_bot_sl_idx = i;
1470 view->page_bot_ln_no = view->ln_no;
1471 }
1472 view->cury++;
1473 if (view->cury == view->scroll_lines)
1474 break;
1475 }
1476}
1477/** @brief Display End of Data
1478 @ingroup view_display
1479 @param view data structure
1480 @details This function is called when the end of the file is reached and
1481 there are no more lines to display. It clears the remaining lines in the
1482 pad and line number window (if line numbering is enabled) to ensure that
1483 no residual content from previous lines is displayed.
1484 */
1485void display_line_eod(View *view) {
1486 UiSurface *sfc = view->sfc;
1487 if (view->f_ln) {
1490 }
1493 view->page_bot_ln_no = view->ln_no;
1494}
1495/** @brief Scroll Down by n Lines
1496 @ingroup view_navigation
1497 @param view Pointer to the View structure containing the state and
1498 parameters of the view application. This structure is used to access and
1499 modify the state of the application as needed.
1500 @param n The number of lines to scroll down. This parameter specifies how
1501 many lines the view should move down in the file, effectively advancing
1502 the display by n lines. The function will handle scrolling, updating the
1503 current line number, and refreshing the display accordingly.
1504 */
1505void scroll_down(View *view, uint n) {
1506 uint scroll, scroll_this_line, avail;
1507 off_t ln_no;
1508 UiSurface *sfc = view->sfc;
1509 view->f_bod = false;
1510 if (view->wrap) {
1511 // Set Top Line State
1512 scroll = n;
1513 ln_no = view->page_top_ln_no;
1514 while (scroll > 0) {
1515 get_line(view, ln_no);
1516 if (view->f_eod)
1517 break;
1518 fmt_line(view);
1519 view->page_top_sl = (view->cur.sl_cnt > 1);
1520 if (view->page_top_sl == false) {
1521 ln_no++;
1522 scroll--;
1523 continue;
1524 }
1525 if (ln_no == view->page_top_ln_no)
1527 avail = view->cur.sl_cnt - 1 - view->cur.sl_idx;
1528 scroll_this_line = min(scroll, avail);
1529 scroll -= scroll_this_line;
1530 if (ln_no == view->page_top_ln_no)
1531 view->cur.sl_idx += scroll_this_line;
1532 else
1533 view->cur.sl_idx += scroll_this_line - 1;
1534 if (scroll != 0)
1535 ln_no++;
1536 }
1539 view->page_top_ln_no = ln_no;
1540 view->page_top_sl = (view->cur.sl_cnt > 1);
1541 // Set Bottom Line State
1542 scroll = n;
1543 ln_no = view->page_bot_ln_no;
1544 while (scroll > 0) {
1545 get_line(view, ln_no);
1546 if (view->f_eod)
1547 break;
1548 fmt_line(view);
1549 view->page_bot_sl = (view->cur.sl_cnt > 1);
1550 if (view->page_bot_sl == false) {
1551 ln_no++;
1552 scroll--;
1553 continue;
1554 }
1555 if (ln_no == view->page_bot_ln_no)
1557 avail = view->cur.sl_cnt - 1 - view->cur.sl_idx;
1558 if (avail == 0) {
1559 ln_no++;
1560 continue;
1561 }
1562 scroll_this_line = min(scroll, avail);
1563 scroll -= scroll_this_line;
1564 if (ln_no == view->page_bot_ln_no)
1565 view->cur.sl_idx += scroll_this_line;
1566 else
1567 view->cur.sl_idx += scroll_this_line - 1;
1570 view->page_bot_ln_no = ln_no;
1571 view->page_bot_sl = (view->cur.sl_cnt > 1);
1572 view->ln_no = view->page_bot_ln_no;
1573 if (view->f_ln)
1574 ui_wscrl(sfc, LNNO, 1);
1575 ui_wscrl(sfc, PAD, scroll_this_line);
1576 view->cury = view->scroll_lines - scroll_this_line;
1578 display_line(view);
1579 if (scroll != 0)
1580 ln_no++;
1581 }
1582 } else {
1583 view->ln_no = view->page_bot_ln_no;
1584 if (view->ln_no >= view->ln_no_max)
1585 return;
1586 if (n > view->scroll_lines) {
1587 if (view->f_ln) {
1590 }
1591 ui_cursor_move(sfc, PAD, 0, 0);
1593 } else {
1594 if (view->f_ln)
1595 ui_wscrl(sfc, LNNO, n);
1596 ui_wscrl(sfc, PAD, n);
1597 if (n < view->scroll_lines)
1598 view->cury = view->scroll_lines - n;
1599 }
1600 view->page_top_ln_no += n;
1601 scroll = n;
1602 while (scroll > 0) {
1603 if (view->ln_no >= view->ln_no_max)
1604 break;
1605 view->ln_no++;
1606 get_line(view, view->ln_no);
1607 if (view->f_eod)
1608 break;
1609 fmt_line(view);
1611 display_line(view);
1612 if (view->cury == view->scroll_lines)
1613 break;
1614 }
1615 view->page_bot_ln_no = view->ln_no;
1616 }
1617 return;
1618}
1619/** @brief Scroll Up by n Lines
1620 @ingroup view_navigation
1621 @param view Pointer to the View structure containing the state and
1622 parameters of the view application. This structure is used to access and
1623 modify the state of the application as needed.
1624 @param n The number of lines to scroll up. This parameter specifies how
1625 many lines the view should move up in the file, effectively moving the
1626 display up by n lines. The function will handle scrolling, updating the
1627 current line number, and refreshing the display accordingly.
1628 */
1629void scroll_up(View *view, uint n) {
1630 uint scroll, avail, scroll_this_line;
1631 off_t ln_no;
1632 view->f_eod = false;
1633 if (view->page_top_ln_no == 0) {
1634 if (view->wrap) {
1635 if (view->page_top_sl == false || view->page_top_sl_idx == 0)
1636 return;
1637 } else
1638 return;
1639 }
1640 UiSurface *sfc = view->sfc;
1641 if (n > view->scroll_lines) {
1642 if (view->f_ln) {
1645 }
1646 ui_cursor_move(sfc, PAD, 0, 0);
1648 } else {
1649 if (view->f_ln)
1650 ui_wscrl(sfc, LNNO, -n);
1651 ui_wscrl(sfc, PAD, -n);
1652 }
1653 ui_cursor_move(sfc, PAD, 0, 0);
1654 if (view->wrap) {
1655 scroll = n;
1656 ln_no = view->page_top_ln_no;
1657 while (scroll > 0) {
1658 get_line(view, ln_no);
1659 if (view->f_bod)
1660 break;
1661 fmt_line(view);
1662 view->page_top_sl = (view->cur.sl_cnt > 1);
1663 if (view->page_top_sl == false) {
1664 scroll--;
1665 if (ln_no > 0)
1666 ln_no--;
1667 continue;
1668 }
1669 if (ln_no == view->page_top_ln_no) {
1671 if (view->cur.sl_idx == 0) {
1672 if (ln_no > 0)
1673 ln_no--;
1674 continue;
1675 }
1676 avail = view->cur.sl_idx;
1677 } else
1678 avail = view->cur.sl_cnt;
1679 scroll_this_line = min(scroll, avail);
1680 scroll -= scroll_this_line;
1681 if (ln_no == view->page_top_ln_no)
1682 view->cur.sl_idx -= scroll_this_line;
1683 else
1684 view->cur.sl_idx = view->cur.sl_cnt - scroll_this_line;
1685 if (scroll == 0)
1686 break;
1687 }
1690 view->page_top_ln_no = ln_no;
1691 view->page_top_sl = (view->page_top_sl_cnt > 1);
1692
1693 view->cury = 0;
1694 scroll = n;
1695 while (scroll > 0) {
1696 if (view->ln_no != view->page_top_ln_no) {
1697 get_line(view, view->ln_no);
1698 if (view->f_eod)
1699 break;
1700 fmt_line(view);
1701 }
1702 display_line(view);
1703 scroll--;
1704 view->ln_no++;
1705 }
1706 view->ln_no--;
1707
1708 // Set Bottom Line State
1709 scroll = n;
1710 ln_no = view->page_bot_ln_no;
1711 while (scroll > 0) {
1712 get_line(view, ln_no);
1713 if (view->f_bod)
1714 break;
1715 fmt_line(view);
1716 view->page_bot_sl = (view->cur.sl_cnt > 1);
1717 if (view->page_bot_sl == false) {
1718 scroll--;
1719 if (ln_no > 0)
1720 ln_no--;
1721 continue;
1722 }
1723 if (ln_no == view->page_bot_ln_no) {
1725 if (view->cur.sl_idx == 0) {
1726 ln_no--;
1727 continue;
1728 }
1729 avail = view->cur.sl_idx;
1730 } else
1731 avail = view->cur.sl_cnt;
1732 scroll_this_line = min(scroll, avail);
1733 scroll -= scroll_this_line;
1734 if (ln_no == view->page_bot_ln_no)
1735 view->cur.sl_idx -= scroll_this_line;
1736 else
1737 view->cur.sl_idx = view->cur.sl_cnt - scroll_this_line;
1738 if (ln_no > 0)
1739 ln_no--;
1740 }
1741 ln_no++;
1744 view->page_bot_ln_no = ln_no;
1745 view->page_bot_sl = (view->cur.sl_cnt > 1);
1746 } else {
1747 view->cury = 0;
1748 scroll = n;
1749 while (scroll > 0) {
1750 view->ln_no = view->page_top_ln_no - scroll;
1751 if (view->ln_no < 0)
1752 view->ln_no = 0;
1753 get_line(view, view->ln_no);
1754 if (view->f_eod)
1755 break;
1756 fmt_line(view);
1757 display_line(view);
1758 scroll--;
1759 view->ln_no++;
1760 }
1761 view->ln_no--;
1762 }
1763 return;
1764}
1765void get_line(View *view, off_t line) {
1766 char c;
1767 char *line_in_p;
1768
1769 view->ln_no = line;
1770 view->file_pos = view->ln_tbl[view->ln_no];
1771 view->f_eod = false;
1772 get_next_char();
1773 if (view->f_eod) {
1774 view->ln_no = line;
1775 return;
1776 }
1777 line_in_p = view->line_in_s;
1778 view->line_in_beg_p = view->line_in_s;
1780 while (1) {
1781 if (c == '\n')
1782 break;
1783 if (line_in_p >= view->line_in_end_p)
1784 break;
1785 *line_in_p++ = c;
1786 get_next_char();
1787 if (view->f_eod) {
1788 view->ln_no = line;
1789 return;
1790 }
1791 }
1792 *line_in_p = '\0';
1793 if (view->f_squeeze) {
1794 while (1) {
1795 get_next_char();
1796 if (c != '\n')
1797 break;
1798 if (view->f_eod)
1799 break;
1800 }
1801 get_prev_char();
1802 if (view->f_eod) {
1803 view->ln_no = line;
1804 return;
1805 }
1806 }
1807 view->ln_no = line;
1808 return;
1809}
1810/** @brief Go to End of File
1811 @ingroup view_navigation
1812 @param view data structure
1813 */
1814void go_to_eof(View *view) {
1815 view->file_pos = view->file_size;
1816 sync_ln(view);
1817 view->ln_no--;
1818 view->ln_no_max = view->ln_no;
1819 if (view->wrap) {
1820 view->page_top_ln_no = view->ln_no;
1821 view->f_eod = true;
1822 prev_page(view);
1823 return;
1824 }
1825 if (view->ln_no > view->scroll_lines)
1826 view->ln_no -= view->scroll_lines - 1;
1827 // else
1828 // view->page_top_ln_no = 0;
1829 view->page_top_ln_no = view->ln_no;
1830 // view->page_top_pos = view->ln_tbl[view->ln_no];
1831 // view->page_bot_pos = view->page_top_pos;
1832 // view->file_pos = view->page_top_pos;
1833 view->cury = 0;
1834 next_page(view);
1835}
1836/** @brief Go to Percent of File
1837 @ingroup view_navigation
1838 @param view data structure
1839 @param percent of file
1840*/
1841void go_to_percent(View *view, uint percent) {
1842 if (view->file_size < 0) {
1843 Perror("Cannot determine file length");
1844 return;
1845 }
1846 view->file_pos = (percent * view->file_size) / 100;
1847 view->ln_no = line_number(view, view->file_pos);
1848 view->file_pos = view->ln_tbl[view->ln_no];
1849 sync_ln(view);
1850 if (view->ln_no > view->scroll_lines)
1851 view->page_top_ln_no = view->ln_no - view->scroll_lines;
1852 else
1853 view->page_top_ln_no = 0;
1856 view->file_pos = view->page_top_pos;
1857 next_page(view);
1858}
1859/** @brief Go to Specific Line
1860 @ingroup view_navigation
1861 @param view data structure
1862 @param line_idx line number to go to (1-based index)
1863 @returns 0 on success, EOF if line index is out of bounds or end of data
1864 is reached
1865 */
1866int go_to_line(View *view, off_t line_idx) {
1867 if (line_idx < 0 || line_idx > view->ln_tbl_size - 1) {
1868 Perror("Line number out of bounds");
1869 return EOF;
1870 }
1871 view->ln_no = line_idx;
1872 view->file_pos = view->ln_tbl[view->ln_no];
1873 sync_ln(view);
1874 view->page_top_pos = view->file_pos;
1875 view->page_bot_pos = view->file_pos;
1876 // view->file_pos = view->page_top_pos;
1877 next_page(view);
1878 return 0;
1879}
1880/** @brief Go to Specific File Position
1881 @ingroup view_navigation
1882 @param view data Structure
1883 @param go_to_pos
1884*/
1885void go_to_position(View *view, off_t go_to_pos) {
1886 view->ln_no = line_number(view, go_to_pos);
1887 view->file_pos = view->ln_tbl[view->ln_no];
1888 sync_ln(view);
1889 next_page(view);
1890}
1891/** @brief Get Line Number for a Given File Position
1892 @ingroup view_navigation
1893 @param view data structure
1894 @param target file position
1895 @returns line number corresponding to the given file position
1896 @details This function performs a binary search on the line table (view->ln_tbl)
1897 to find the line number corresponding to the specified file position (target).
1898 It returns the index of the line in the line table that is closest to the target
1899 position without exceeding it. If the target position is not found, it returns
1900 the previous line number.
1901 */
1902off_t line_number(View *view, off_t position) {
1903 off_t low = 0;
1904 off_t high = view->ln_tbl_size - 1;
1905 off_t prev_ln_no = 0;
1906 while (low <= high) {
1907 off_t guess = low + (high - low) / 2;
1908 if (view->ln_tbl[guess] == position)
1909 return guess;
1910 if (view->ln_tbl[guess] < position) {
1911 prev_ln_no = guess;
1912 low = guess + 1;
1913 } else
1914 high = guess - 1;
1915 }
1916 return prev_ln_no;
1917}
1918/** @brief Initialize Line Table
1919 @ingroup view_navigation
1920 @param view data structure
1921 @details The line table is initialized with a specified increment size
1922 (LINE_TBL_INCR). Memory is allocated for the line table, and the first
1923 entry is set to 0, indicating the file position of the first line. The
1924 line index (view->ln_no) is initialized to 0.
1925 */
1926void initialize_line_table(View *view) {
1928 view->ln_tbl = (off_t *)calloc(view->ln_tbl_size, sizeof(off_t));
1929 if (view->ln_tbl == nullptr) {
1930 Perror("Memory allocation failed");
1931 exit(EXIT_FAILURE);
1932 }
1933 view->ln_max_pos = 0;
1934 view->ln_tbl[0] = 0;
1935 view->ln_no = 0;
1936}
1937/** @brief Destroy Line Table
1938 @ingroup view_navigation
1939 @param view data structure
1940 @details This function frees the memory allocated for the line table and
1941 resets the relevant fields in the View structure. It ensures that the line
1942 table is properly cleaned up when it is no longer needed, preventing memory
1943 leaks.
1944 */
1945void destroy_line_table(View *view) {
1946 if (view->ln_tbl == nullptr)
1947 return;
1948 free(view->ln_tbl);
1949 view->ln_tbl = nullptr;
1950 view->ln_tbl_size = 0;
1951 view->ln_max_pos = 0;
1952 view->ln_no = 0;
1953}
1954/** @brief Increment Line Index and Update Line Table
1955 @ingroup view_navigation
1956 @param view data structure
1957 @details This function is called when a line feed character is
1958 encountered while reading the file. It increments the line index
1959 (view->ln_no) and checks if the current file position exceeds the maximum
1960 position recorded in the line table. If it does, it updates the line
1961 table with the new file position. If the line index exceeds the current
1962 size of the line table, the table is resized by allocating more memory.
1963 */
1964void increment_ln(View *view) {
1965 // view->ln_tbl[0] is set to 0 in initialize_line_table
1966 // view->ln_tbl[1] is the second line
1967 view->ln_no++;
1968 if (view->file_pos <= view->ln_max_pos)
1969 return;
1970 if (view->ln_no > view->ln_tbl_size - 1) {
1972 view->ln_tbl =
1973 (off_t *)realloc(view->ln_tbl, view->ln_tbl_size * sizeof(off_t));
1974 if (view->ln_tbl == nullptr) {
1975 Perror("Memory allocation failed");
1976 exit(EXIT_FAILURE);
1977 }
1978 }
1979 view->ln_tbl_cnt = view->ln_no;
1980 view->ln_max_pos = view->file_pos;
1981 view->ln_tbl[view->ln_no] = view->file_pos;
1982}
1983/** @brief Synchronize Line Table with Current File Position
1984 @ingroup view_navigation
1985 @param view data Structure
1986 @details The line table (view->ln_tbl) is an array that stores the file
1987 position of each line. The index (view->ln_no + 1) corresponds to the
1988 current line number. (the line number table is 0-based, while line
1989 numbering starts at 1).
1990 @details If the line or position requested is not in the line table, this
1991 function reads forward to sycn.
1992 If the line or positione requested is behind the current line
1993 table index, the line index will be decremented it matches the file
1994 position.
1995 */
1996void sync_ln(View *view) {
1997 int c = 0;
1998 off_t idx;
1999 off_t target_pos;
2000 if (view->ln_tbl[view->ln_no] == view->file_pos)
2001 return;
2002 target_pos = view->file_pos;
2003 view->file_pos = view->ln_tbl[view->ln_tbl_cnt];
2004 if (view->file_pos < target_pos) {
2005 view->ln_no = view->ln_tbl_cnt;
2006 while (view->ln_max_pos < target_pos) {
2007 get_next_char();
2008 if (view->f_eod)
2009 return;
2010 }
2011 } else if (view->ln_tbl[view->ln_no] > target_pos) {
2012 idx = view->ln_no - 1;
2013 while (view->ln_tbl[idx] > target_pos)
2014 idx--;
2015 view->ln_no = idx;
2016 view->file_pos = view->ln_tbl[view->ln_no];
2017 } else {
2018 view->ln_no = view->ln_tbl_cnt;
2019 view->file_pos = view->ln_tbl[view->ln_no];
2020 }
2021}
2022/*------------------------------------------------------------
2023 END NAVIGATION
2024 BEGIN DISPLAY
2025 ------------------------------------------------------------*/
2026/** @defgroup view_display Manage View Display
2027 @brief Manage the View Display
2028 */
2029/** @brief Refresh Pad and Line Number Window
2030 @ingroup view_display
2031 @param view data structure
2032 @returns OK on success, ERR on failure
2033*/
2034int pad_refresh(View *view) {
2035#ifdef UAL_UI
2036 int rc = 0;
2037 // touchwin(view->sfc->mwin[WIN2]);
2038 // rc = pnoutrefresh(view->sfc->mwin[WIN2], view->pminrow, view->pmincol,
2039 // view->sminrow,
2040 // view->smincol, view->smaxrow, view->smaxcol);
2041 rc = prefresh(view->sfc->mwin[PAD],
2042 view->pminrow,
2043 view->pmincol,
2044 view->sminrow,
2045 view->smincol,
2046 view->smaxrow,
2047 view->smaxcol);
2048 if (rc == ERR) {
2049 ssnprintf(em0, MAXLEN - 1, "%s:%d prefresh(view->sfc->mwin[WIN2], pminrow=%d, pmincol=%d, smaxrow=%d, smaxcol=%d) returned %d\n",
2050 __FILE__, __LINE__ - 1, view->pminrow, view->pmincol, view->smaxrow, view->smaxcol, rc);
2051 Perror(em0);
2052 }
2053#else
2054 (void)view;
2055#endif
2057 return 0;
2058}
2059
2060/** @brief Format Line for Display
2061 @ingroup view_display
2062 @param view pointer to View structure containing line input and output
2063 buffers
2064 @return length of formatted line in characters
2065 @details This function processes the input line from view->line_in_s,
2066 handling ANSI escape sequences for text attributes and colors, as well as
2067 multi-byte characters. It converts the input line into a formatted line
2068 suitable for display in the terminal, storing the result in
2069 view->cmplx_buf and view->stripped_line_out. The function returns the
2070 length of the formatted line in characters, which may be used for
2071 tracking the maximum column width of the displayed content.
2072 */
2073int fmt_line(View *view) {
2074 char ansi_tok[MAXLEN];
2075 uint i = 0, j = 0, x = 0;
2076 uint len = 0;
2077 uint tab_spaces = 0;
2078 uint char_width;
2079 attr_t attr = WA_NORMAL;
2080 ushort cpx = cp_nt;
2081 UiCell cc = {0};
2082 wchar_t wstr[2] = {L'\0', L'\0'};
2083 char *in_str = view->line_in_s;
2084 uint sl_cols = 0;
2085 uint sl_cells = 0;
2086 uint word_cells = 0;
2087 UiCell *cmplx_buf = view->cmplx_buf;
2088 view->cur.sl_idx = 0;
2089 view->cur.sl_cnt = 0;
2090 view->cur.sl_cols[0] = 0;
2091 view->cur.sl_cells[0] = 0;
2092 view->cur.sl_s[0] = nullptr;
2093 view->cur.sl_cc[0] = nullptr;
2094 char *sl_s = view->stripped_line_out;
2095 UiCell *sl_cc = view->cmplx_buf;
2097 mbstate_t mbstate;
2098 memset(&mbstate, 0, sizeof(mbstate));
2099 uint word_cols = 0;
2100 uint sl_maxlen = PAD_COLS - 1;
2101 if (view->f_eod)
2102 return 0;
2103 if (view->wrap)
2104 sl_maxlen = view->cols;
2105 if (view->f_ln)
2106 sl_maxlen -= view->ln_win_cols;
2107 memset(view->stripped_line_out, 0, sizeof(view->stripped_line_out));
2108 while (in_str[i] != '\0') { // line
2109 while (1) { // ANSI SGR, Character, and Word
2110 if (in_str[i] == '\033') { // ANSI SGR
2111 if (in_str[i + 1] == '[') {
2112 len = strcspn(&in_str[i], "mK ") + 1;
2113 memcpy(ansi_tok, &in_str[i], len + 1);
2114 ansi_tok[len] = '\0';
2115 if (ansi_tok[0] == '\0') {
2116 if (i + 2 < MAXLEN)
2117 i += 2;
2118 continue;
2119 }
2120 if (len == 0 || in_str[i + len - 1] == ' ') {
2121 i += 2;
2122 continue;
2123 } else if (in_str[i + len - 1] == 'K') {
2124 i += len;
2125 continue;
2126 }
2127 parse_ansi_str(ansi_tok, &attr, &cpx);
2128 i += len;
2129 } else {
2130 i++;
2131 continue;
2132 }
2133 } else { // Character and Word
2134 if (in_str[i] == ' ' || in_str[i] == '-') {
2135 if (view->wrap && (sl_cols + word_cols) > (sl_maxlen - 1))
2136 break;
2137 (in_str[i] == '-') ? (wstr[0] = L'-') : (wstr[0] = L' ');
2138 wstr[1] = L'\0';
2139 ui_setcchar(&cc, wstr, attr, cpx, nullptr);
2140 view->stripped_line_out[x++] = in_str[i];
2141 cmplx_buf[j++] = cc;
2142 if (view->wrap) {
2143 sl_cols += word_cols + 1;
2144 word_cols = 0;
2145 sl_cells += word_cells + 1;
2146 word_cells = 0;
2147 }
2148 i++;
2149 continue;
2150 }
2151 if (in_str[i] == '\0') {
2152 if (view->wrap) {
2153 if (sl_cols + word_cols + 1 > sl_maxlen - 1)
2154 break;
2155 sl_cols += word_cols;
2156 word_cols = 0;
2157 sl_cells += word_cells;
2158 word_cells = 0;
2159 }
2160 break;
2161 }
2162 if (in_str[i] == '\t') {
2163 tab_spaces = view->tab_stop - ((sl_cols + word_cols) % view->tab_stop);
2164 if (view->wrap && sl_cols + word_cols + tab_spaces > sl_maxlen - 1)
2165 break;
2166 wstr[0] = L' ';
2167 wstr[1] = L'\0';
2168 ui_setcchar(&cc, wstr, attr, cpx, nullptr);
2169 for (uint z = 0; z < tab_spaces; z++) {
2170 view->stripped_line_out[x++] = ' ';
2171 cmplx_buf[j++] = cc;
2172 }
2173 if (view->wrap) {
2174 sl_cols += word_cols + tab_spaces;
2175 word_cols = 0;
2176 sl_cells += word_cells + tab_spaces;
2177 word_cells = 0;
2178 }
2179 i++;
2180 continue;
2181 }
2182 wstr[1] = L'\0';
2183 len = mbrtowc(wstr, &in_str[i], MB_CUR_MAX, &mbstate);
2184 if (len <= 0) {
2185 wstr[0] = L'?';
2186 wstr[1] = L'\0';
2187 len = 1;
2188 }
2189 char_width = wcwidth(wstr[0]);
2190 view->stripped_line_out[x++] = in_str[i];
2191 if (char_width > 1)
2192 for (uint n = 1; n < char_width; n++)
2193 view->stripped_line_out[x++] = ' ';
2194 ui_setcchar(&cc, wstr, attr, cpx, nullptr);
2195 cmplx_buf[j++] = cc;
2196 i += len;
2197 if (view->wrap) {
2198 word_cols += char_width;
2199 word_cells++;
2200 }
2201 continue;
2202 } // END Character and Word
2203 } // END WHILE - ANSI SGR, Character, and Word
2204 if (view->wrap) { // handle line wrapping
2205 if (word_cols > 0) { // break line, add last word to next line
2206 if (sl_cols <= sl_maxlen) {
2207 view->cur.sl_s[view->cur.sl_idx] = sl_s;
2208 view->cur.sl_cc[view->cur.sl_idx] = sl_cc;
2209 view->cur.sl_cols[view->cur.sl_idx] = sl_cols;
2210 view->cur.sl_cells[view->cur.sl_idx] = sl_cells;
2211 view->cur.sl_idx++;
2212 sl_s = &sl_s[sl_cols];
2213 sl_cc = &sl_cc[sl_cells];
2214 sl_cols > view->maxcol ? view->maxcol = sl_cols : 0;
2215 sl_cols = word_cols;
2216 sl_cells = word_cells;
2217 word_cols = 0;
2218 word_cells = 0;
2219 }
2220 }
2221 while (sl_cols > sl_maxlen - 1) { // split long lines
2222 uint safe_cells = 0;
2223 uint safe_cols = 0;
2224 while (safe_cols < sl_maxlen && safe_cells < sl_cells) {
2225 wchar_t wstr_chk[CCHARW_MAX];
2226 attr_t attr_chk;
2227 ushort cpx_chk;
2228 ui_getcchar(&sl_cc[safe_cells], wstr_chk, &attr_chk, &cpx_chk, nullptr);
2229 uint cw = wcwidth(wstr_chk[0]);
2230 if (safe_cols + cw > sl_maxlen)
2231 break;
2232 safe_cols += cw;
2233 safe_cells++;
2234 }
2235 if (safe_cells == 0) {
2236 safe_cells = 1;
2237 safe_cols = sl_maxlen;
2238 }
2239 view->cur.sl_s[view->cur.sl_idx] = sl_s;
2240 view->cur.sl_cc[view->cur.sl_idx] = sl_cc;
2241 view->cur.sl_cols[view->cur.sl_idx] = safe_cols;
2242 view->cur.sl_cells[view->cur.sl_idx] = safe_cells;
2243 view->cur.sl_idx++;
2244 sl_s = &sl_s[safe_cells];
2245 sl_cc = &sl_cc[safe_cells];
2246 sl_cols > view->maxcol ? view->maxcol = sl_cols : 0;
2247 sl_cols -= safe_cols;
2248 sl_cells -= safe_cells;
2249 word_cols = 0;
2250 word_cells = 0;
2251 } // END WHILE - split long lines
2252 } // END IF - handle line wrapping
2253 j > view->maxcol ? view->maxcol = j : 0;
2254 } // END WHILE - line
2255 if (view->wrap) { // finish and commit wrap state
2256 if (view->cur.sl_idx > 0) {
2257 view->cur.sl_s[view->cur.sl_idx] = sl_s;
2258 view->cur.sl_cc[view->cur.sl_idx] = sl_cc;
2259 view->cur.sl_cols[view->cur.sl_idx] = sl_cols;
2260 view->cur.sl_cells[view->cur.sl_idx] = sl_cells;
2261 view->cur.sl_idx++;
2262 view->cur.sl_cc[view->cur.sl_idx] = nullptr;
2263 view->cur.sl_s[view->cur.sl_idx] = nullptr;
2264 view->cur.sl_cnt = view->cur.sl_idx;
2265 view->cur.sl_idx = 0;
2266 }
2267 view->cur.sl_ln_no = view->ln_no;
2268 }
2269#ifdef DEBUG_WRAP
2270 // log_split_lines(view);
2271 log_cc_buf(view);
2272 // log_stripped_line_out(view);
2273#endif
2274 //-------------------------------------------------------------------------
2275 wstr[0] = '\0';
2276 wstr[1] = '\0';
2277 ui_setcchar(&cc, wstr, WA_NORMAL, cpx, nullptr);
2278 cmplx_buf[j] = cc;
2279 view->stripped_line_out[x] = '\0';
2280 return j;
2281}
2282/** @brief Log Stripped Line Output
2283 @ingroup view_display
2284 @param view pointer to View structure containing stripped line output
2285 buffer
2286 @details This function logs the contents of the stripped line output
2287 buffer (view->stripped_line_out) for debugging purposes. It iterates
2288 through the split lines (if any) and logs each line to the cmenu log.
2289 */
2290void log_stripped_line_out(View *view) {
2291 char tmp_str[PAD_COLS];
2292
2294 write_cmenu_log("stripped_line_out");
2295 for (uint k = 0; k < view->cur.sl_cnt; k++) {
2296 memset(tmp_str, 0, sizeof(tmp_str));
2297 for (uint c = 0; c < view->cur.sl_cols[k]; c++)
2298 tmp_str[c] = view->cur.sl_s[k][c];
2299 write_cmenu_log(tmp_str);
2300 }
2301}
2302/** @brief Log Complex Character Buffer
2303 @ingroup view_display
2304 @param view pointer to View structure containing complex character buffer
2305 (view->cmplx_buf)
2306 @details This function logs the contents of the complex character buffer
2307 (view->cmplx_buf) for debugging purposes. It iterates through the split
2308 lines (if any) and logs each line to the cmenu log, converting wide
2309 characters to their corresponding single-byte representation for easier
2310 viewing.
2311 */
2312void log_cc_buf(View *view) {
2313 char tmp_str[PAD_COLS];
2314
2316 write_cmenu_log("cc_buf");
2317 for (uint k = 0; k < view->cur.sl_cnt; k++) {
2318 memset(tmp_str, 0, sizeof(tmp_str));
2319 for (uint c = 0; c < view->cur.sl_cells[k]; c++) {
2320 wchar_t wstr[CCHARW_MAX];
2321 attr_t attr;
2322 ushort cpx;
2323 ui_getcchar(&view->cur.sl_cc[k][c], wstr, &attr, &cpx, nullptr);
2324 if (wstr[0] == L'\0')
2325 tmp_str[c] = ' ';
2326 else
2327 tmp_str[c] = (char)wstr[0];
2328 }
2329 write_cmenu_log(tmp_str);
2330 }
2331}
2332/** @brief Log String with Specified Length
2333 @ingroup view_display
2334 @param str pointer to the string to log
2335 @param len length of the string to log
2336 @details This function logs a specified number of characters from a given
2337 string to the cmenu log. It copies the specified length of the string into
2338 a temporary buffer and then writes it to the log.
2339 */
2340void log_strnz(char *str, uint len) {
2341 char tmp_str[MAXLEN];
2342 strnz__cpy(tmp_str, str, len);
2343 write_cmenu_log(tmp_str);
2344}
2345/** @brief Log Split Lines
2346 @ingroup view_display
2347 @param view pointer to View structure containing split line information
2348 @details This function logs the contents of the split lines stored in the
2349 view structure for debugging purposes. It iterates through the split lines
2350 and logs each line to the cmenu log, converting wide characters to their
2351 corresponding single-byte representation for easier viewing.
2352 */
2353void log_split_lines(View *view) {
2354 char tmp_str[MAXLEN];
2355
2356 for (uint k = 0; k < view->cur.sl_cnt; k++) {
2357 if (view->cur.sl_cols[k] > 0) {
2358 memset(tmp_str, 0, sizeof(tmp_str));
2359 char *s = view->cur.sl_s[k];
2360 char *d = tmp_str;
2361 char *e = view->cur.sl_s[k] + view->cur.sl_cols[k];
2362 while (s < e)
2363 *d++ = *s++;
2364 *d = '\0';
2365 write_cmenu_log(tmp_str);
2366 }
2367 }
2368}
2369//----------------------------------------------------------------------------------
2370/** @brief Parse ANSI SGR Escape Sequence
2371 @ingroup view_display
2372 @param ansi_str is the ANSI escape sequence string to parse
2373 @param attr is a pointer to an attr_t variable where the parsed
2374 attributes will be stored
2375 @param cpx is a pointer to an int variable where the parsed color pair
2376 index will be stored
2377 @details This function parses ANSI escape sequences, which it converts
2378 to color pairs and attributes used to construct complex character cells
2379 or extended grapheme clusters.
2380 @note ANSI Select Graphics Rendition (SGR), ECMA-48, (ISO/IEC 6429)
2381 @note Extends Color Pair and Color arrays as necessary
2382 @verbatim
2383
2384 SGR specification types:
2385
2386 RGB:
2387
2388 foreground \033[38;2;r;g;bm
2389 background \033[48;2;r;g;bm
2390
2391 Where r, g, b are the red, green, and blue color components (0-255)
2392
2393 XTERM 256-color:
2394
2395 foreground \033[38;5;xm
2396 background \033[48;5;xm
2397
2398 Where x is the 256-color index (0-255)
2399 uses xterm256_idx_to_rgb() to convert the 256-color index to RGB
2400
2401 8-color:
2402
2403 foreground \033[3cm
2404 background \033[4cm
2405
2406 Where c is the color code (0 for black, 1 for red, 2 for green, 3
2407 for yellow, 4 for blue, 5 for magenta, 6 for cyan, 7 for white).
2408
2409 Attributes:
2410
2411 \033[am
2412
2413 Where a is the attribute code (1 for bold, 2 for dim, 3 for italic,
2414 4 for underline, 5 for blink, 7 for reverse, 8 for invis). The function
2415 also supports resetting attributes and colors to default using \033[0m.
2416
2417 see also: xterm256_idx_to_rgb(), ui_add_color_rgb(), extended_pair_content(),
2418 ui_add_pair()
2419
2420 @endverbatim
2421*/
2422void parse_ansi_str(char *ansi_str, attr_t *attr, ushort *cpx) {
2423 char *tok;
2424 char t0, t1;
2425 char tstr[3];
2426 uint len, x_idx;
2427 uint fg, bg;
2428 uint fg_clr, bg_clr;
2429 char *ansi_p = ansi_str + 2;
2430 ui_pair_content(*cpx, &fg_clr, &bg_clr);
2431 fg = fg_clr;
2432 bg = bg_clr;
2433 RGB rgb;
2434 tok = strtok((char *)ansi_p, ";m");
2435 bool a_toi_error = false;
2436 while (1) {
2437 if (tok == nullptr || *tok == '\0')
2438 break;
2439 len = strlen(tok);
2440 if (len == 2) {
2441 t0 = tok[0];
2442 t1 = tok[1];
2443 if (t0 == '3' || t0 == '4') {
2444 if (t1 == '8') {
2445 tok = strtok(nullptr, ";m");
2446 if (tok != nullptr) {
2447 if (*tok == '5') {
2448 tok = strtok(nullptr, ";m");
2449 if (tok != nullptr) {
2450 x_idx = a_toi(tok, &a_toi_error);
2451 rgb = xterm256_idx_to_rgb(x_idx);
2452 }
2453 } else if (*tok == '2') {
2454 tok = strtok(nullptr, ";m");
2455 rgb.r = a_toi(tok, &a_toi_error);
2456 tok = strtok(nullptr, ";m");
2457 rgb.g = a_toi(tok, &a_toi_error);
2458 tok = strtok(nullptr, ";m");
2459 rgb.b = a_toi(tok, &a_toi_error);
2460 }
2461 }
2462 if (t0 == '3')
2463 fg_clr = ui_add_color_rgb(&rgb);
2464 else if (t0 == '4')
2465 bg_clr = ui_add_color_rgb(&rgb);
2466 } else if (t1 == '9') {
2467 if (t0 == '3')
2468 fg_clr = CLR_NT_FG;
2469 else if (t0 == '4')
2470 bg_clr = CLR_NT_BG;
2471 } else if (t1 >= '0' && t1 <= '7') {
2472 if (t0 == '3') {
2473 tstr[0] = t1;
2474 tstr[1] = '\0';
2475 x_idx = a_toi(tstr, &a_toi_error);
2476 rgb = xterm256_idx_to_rgb(x_idx);
2477 fg_clr = ui_add_color_rgb(&rgb);
2478 } else if (t0 == '4') {
2479 tstr[0] = t1;
2480 tstr[1] = '\0';
2481 x_idx = a_toi(tstr, &a_toi_error);
2482 rgb = xterm256_idx_to_rgb(x_idx);
2483 bg_clr = ui_add_color_rgb(&rgb);
2484 }
2485 }
2486 } else if (t0 == '0') {
2487 *tok = t1;
2488 len = 1;
2489 }
2490 }
2491 if (len == 1) {
2492 if (*tok == '0') {
2493 *attr = WA_NORMAL;
2494 fg_clr = CLR_NT_FG;
2495 bg_clr = CLR_NT_BG;
2496 } else {
2497 switch (a_toi(tok, &a_toi_error)) {
2498 case 1:
2499 *attr |= WA_BOLD;
2500 break;
2501 case 2:
2502 *attr |= WA_DIM;
2503 break;
2504 case 3:
2505 *attr |= WA_ITALIC;
2506 break;
2507 case 4:
2508 *attr |= WA_UNDERLINE;
2509 break;
2510 case 5:
2511 *attr |= WA_BLINK;
2512 break;
2513 case 7:
2514 *attr |= WA_REVERSE;
2515 break;
2516 case 8:
2517 *attr |= WA_INVIS;
2518 break;
2519 default:
2520 break;
2521 }
2522 }
2523 } else if (len == 0) {
2524 *attr = WA_NORMAL;
2525 fg_clr = CLR_NT_FG;
2526 bg_clr = CLR_NT_BG;
2527 }
2528 tok = strtok(nullptr, ";m");
2529 }
2530 if (!a_toi_error && (fg_clr != fg || bg_clr != bg))
2531 *cpx = ui_add_pair(fg_clr, bg_clr);
2532 return;
2533}
2534/** @brief Display Command Line Prompt
2535 @ingroup view_display
2536 @param view is the current view data structure
2537 @param s is the prompt string */
2538int display_prompt(View *view, char *s) {
2539 char message_str[PAD_COLS + 1];
2540 uint l;
2541 UiSurface *sfc = view->sfc;
2542 l = strnz__cpy(message_str, s, PAD_COLS);
2544 if (l != 0) {
2547 ui_mvwaddstr(sfc, CMDLN, view->cmd_line, 0, " ");
2548 ui_mvwaddstr(sfc, CMDLN, view->cmd_line, 1, message_str);
2549 ui_waddstr(sfc, CMDLN, " ");
2551 ui_getyx(sfc, CMDLN, &view->cmd_line, &view->curx);
2553 }
2554 return (view->curx);
2555}
2556/** @brief Display View Help File
2557 @ingroup view_display
2558 @param init is the current initialization data structure.
2559 @details The current View context is set aside by assigning the view
2560 structure to "view_save" while the help file is displayed using a new,
2561 separate view structure.
2562 The help file is specified by the VIEW_HELP_FILE macro can be set
2563 to a default help file path or overridden by the user through an
2564 environment variable.
2565 After the help file is closed, the original view is restored and
2566 the page is redisplayed.
2567 It may be necessary to reassign view after calling this function
2568 because the init->view pointer is temporarily set to nullptr during the
2569 help file display, and the original view is restored afterward.
2570 The default screen size for help can be set in the code below. If
2571 set to 0, popup_view will determine reasonable maximal size based on the
2572 terminal dimensions.
2573 The help file may contain Unicode characters and ANSI escape
2574 sequences for formatting, which will be properly handled and displayed by
2575 popup_view. */
2576void view_display_help(Init *init) {
2577 char tmp_str[MAXLEN];
2578 uint eargc = 0;
2579 char *eargv[MAXARGS];
2580 View *view = init->view;
2581 if (view->f_help_spec || view->help_spec[0] != '\0')
2582 strnz__cpy(tmp_str, view->help_spec, MAXLEN - 1);
2583 else {
2584 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
2585 strnz__cat(tmp_str, "/", MAXLEN - 1);
2587 }
2588 eargv[eargc++] = strdup("view");
2589 eargv[eargc++] = strdup("-N");
2590 eargv[eargc++] = strdup(tmp_str);
2591 eargv[eargc] = nullptr;
2592 init->lines = 48;
2593 init->cols = 72;
2594 init->begy = 0;
2595 init->begx = 0;
2596 strnz__cpy(init->title, "View Help", MAXLEN - 1);
2597 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
2598 init->begx);
2599 destroy_argv(eargc, eargv);
2600 init->view->f_redisplay_page = true;
2601}
2602/*------------------------------------------------------------
2603 END DISPLAY
2604 ------------------------------------------------------------*/
2605/** @brief use form to enter a file specification
2606 @ingroup view_engine
2607 @param init data structure
2608 @param file_spec - pointer to file specification
2609 the file_spec
2610 @returns true if successful
2611 @details the user must provide a character array large enough to
2612 hold file_spec without overflowing
2613 */
2614bool enter_file_spec(Init *init, char *file_spec) {
2615 char earg_str[MAXLEN];
2616 char *eargv[MAXARGS];
2617 uint eargc;
2618 char tmp_dir[MAXLEN];
2619 char tmp_str[MAXLEN];
2620 char tmp_spec[MAXLEN];
2621 int rc = 0;
2622 FILE *tmp_fp;
2623 View *view = init->view;
2624 strnz__cpy(tmp_dir, init->mapp_home, MAXLEN - 1);
2625 strnz__cat(tmp_dir, "/tmp", MAXLEN - 1);
2626 expand_tilde(tmp_dir, MAXLEN - 1);
2627 if (!mk_dir(tmp_dir)) {
2628 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
2629 strnz__cpy(em1, "Unable to ", MAXLEN - 1);
2630 strnz__cat(em1, "mkdir", MAXLEN - 1);
2631 strnz__cat(em1, tmp_dir, MAXLEN - 1);
2632 strerror_r(errno, em2, MAXLEN - 1);
2634 return false;
2635 }
2636 while (rc == false) {
2637 strnz__cpy(tmp_spec, tmp_dir, MAXLEN - 1);
2638 strnz__cat(tmp_spec, "/tmp_XXXXXX", MAXLEN - 1);
2639 view->in_fd = mkstemp(tmp_spec);
2640 if (view->in_fd == -1) {
2641 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
2642 strnz__cpy(em1, "unable to ", MAXLEN - 1);
2643 strnz__cat(em1, "mkstemp ", MAXLEN - 1);
2644 strnz__cat(em1, tmp_spec, MAXLEN - 1);
2645 strerror_r(errno, em2, MAXLEN - 1);
2646 display_error(em0, em1, nullptr, nullptr);
2647 return false;
2648 }
2649 /** call form to get file_name
2650 write the name to a temporary file */
2651
2652 strnz__cpy(earg_str, "form -d file_name.f -o ", MAXLEN - 1);
2653 strnz__cat(earg_str, tmp_spec, MAXLEN - 1);
2654 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
2655 rc = popup_form(init, eargc, eargv, view->begy + view->lines - 7, 4);
2656 destroy_argv(eargc, eargv);
2658 if (rc == FA_CANCEL || rc == 'q' || rc == 'Q' || rc == KEY_F09)
2659 return false;
2660 close(view->in_fd);
2661 tmp_fp = fopen(tmp_spec, "r");
2662 if (tmp_fp == nullptr) {
2663 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
2664 strnz__cpy(em1, "unable to ", MAXLEN - 1);
2665 strnz__cat(em1, "fopen ", MAXLEN - 1);
2666 strnz__cat(em1, tmp_spec, MAXLEN - 1);
2667 strerror_r(errno, em2, MAXLEN - 1);
2669 return false;
2670 }
2671 fgets(tmp_str, MAXLEN - 1, tmp_fp);
2672 strnz(tmp_str, MAXLEN - 1);
2673 fclose(tmp_fp);
2674 unlink(tmp_spec);
2675 if (!verify_spec_arg(file_spec, tmp_str, "~/menuapp/tmp", ".",
2676 S_WCOK | S_QUIET)) {
2677 ssnprintf(em0, MAXLEN - 1, "Unable to open %s for writing",
2678 tmp_str);
2679 strnz__cpy(em1, "Try again? y (yes) or n (no) ", MAXLEN - 1);
2680 rc = display_error(em0, em1, nullptr, nullptr);
2681 if (rc == 'y' || rc == 'Y')
2682 continue;
2683
2684 } else
2685 return true;
2686 }
2687 return true;
2688}
@ FA_CANCEL
Definition form.h:33
#define PRINTCMD
Definition common.h:47
int popup_form(Init *, int, char **, uint, uint)
instantiate a form popup window
Definition popups.c:113
#define DEFAULTEDITOR
Definition common.h:37
int popup_view(Init *, int, char **, uint, uint, uint, uint)
instantiate a view popup window
Definition popups.c:145
#define VIEW_HELP_FILE
Definition common.h:35
size_t rtrim(char *)
Trims trailing spaces from string s in place.
Definition futil.c:331
#define KEY_ALTEND
Definition cm.h:560
#define KEY_ALTHOME
Definition cm.h:557
#define MAX_ARGS
Definition cm.h:48
#define MAXARGS
Definition cm.h:50
#define S_TOLOWER(c)
Definition cm.h:144
@ CLR_NT_FG
Definition cm.h:198
@ CLR_NT_BG
Definition cm.h:199
#define min(x, y)
min macro evaluates two expressions, returning least result
Definition cm.h:91
#define S_QUIET
Definition cm.h:328
#define S_WCOK
Definition cm.h:327
#define Ctrl(c)
Definition cm.h:54
#define CM_VERSION
Definition version.h:7
@ CT_VIEW
Definition menu.h:47
void ui_render()
Definition ui_ncurses.c:800
int ui_wclrtobot(UiSurface *s, uint w)
int ui_mvwadd_wchstr(UiSurface *s, uint w, uint y, uint x, const UiCell *cell)
int ui_cursor_move(UiSurface *s, uint w, uint y, uint x)
Definition ui_ncurses.c:727
void ui_getyx(UiSurface *s, uint w, uint *lines, uint *cols)
Definition ui_ncurses.c:668
int ui_bkgdset(UiSurface *s, uint w, const UiCell *cell)
Definition ui_ncurses.c:760
int ui_mvwaddstr(UiSurface *s, uint w, uint y, uint x, const char *text)
int ui_wadd_wchnstr(UiSurface *s, uint w, const UiCell *cell, uint m)
int ui_add_color_rgb(RGB *rgb)
Definition ui_ncurses.c:138
int ui_mvwadd_wchnstr(UiSurface *s, uint w, uint y, uint x, const UiCell *cell, uint m)
int ui_get_event_no_mouse(UiSurface *surface, uint w, UiEvent *ev)
int ui_mvwaddch(UiSurface *s, uint w, uint y, uint x, const char c)
uint16_t attr_t
Definition ui_backend.h:256
void ui_surface_destroy(UiSurface *s)
Definition ui_ncurses.c:377
void ui_restore_wins()
#define CCHARW_MAX
Definition ui_backend.h:248
void ui_get_screen_size(uint *lines, uint *cols)
Definition ui_ncurses.c:692
int ui_waddstr(UiSurface *s, uint w, const char *text)
int ui_wclrtoeol(UiSurface *s, uint w)
int ui_wscrl(UiSurface *s, uint w, int rows)
int ui_curs_set(int visibility)
Definition ui_ncurses.c:737
struct nccell UiCell
Definition ui_backend.h:250
int ui_mvwadd_wch(UiSurface *s, uint w, uint y, uint x, const UiCell *cell)
int ui_cursor_enable_yx(UiSurface *s, uint w, uint y, uint x, bool visible)
Disable (visible = false) or enable (visibile = true) the cursor at a specified position on the surfa...
Definition ui_ncurses.c:703
#define LINE_TBL_INCR
Definition view.h:48
#define NMARKS
Definition view.h:35
#define PAD_COLS
Definition view.h:41
#define NULL_POSITION
Definition view.h:38
#define KEY_F01
#define KEY_F09
#define KEY_RIGHT
#define KEY_DC
#define WA_NORMAL
#define WA_BLINK
#define KEY_BACKSPACE
#define KEY_DOWN
#define KEY_SLEFT
#define WA_ITALIC
#define KEY_PPAGE
#define WA_DIM
#define WA_INVIS
#define WA_BOLD
#define KEY_RESIZE
#define KEY_NPAGE
#define WA_REVERSE
#define KEY_SRIGHT
#define KEY_ENTER
#define WA_UNDERLINE
#define KEY_MOUSE
#define KEY_LEFT
#define KEY_HOME
#define KEY_UP
int ui_pair_content(uint pair, uint *fg, uint *bg)
Definition ui_ncurses.c:194
uint ui_add_pair(uint fg, uint bg)
Definition ui_ncurses.c:105
int ui_getcchar(const UiCell *uc, wchar_t *wstr, attr_t *attrs, uint16_t *pair, void *opts)
Definition ui_ncurses.c:784
int ui_setcchar(cchar_t *wch, const wchar_t *wc, const attr_t attrs, short pair, const void *opts)
Definition ui_ncurses.c:792
#define MAXLEN
Definition curskeys.c:15
char prev_regex_pattern[MAXLEN]
FILE * dbgfp
char err_msg[MAXLEN]
void get_line(View *, off_t)
#define confirm()
Definition view_engine.c:89
int cf_accept(UiSurface *sfc, uint w, char *accept_s, uint flin, uint fcol, uint flen)
Accepts input for a field in a C-Menu window.
Definition cm_fields.c:41
UiCell cell_sp
Definition dwin.c:120
ushort cp_nt
Definition dwin.c:151
UiCell cell_nt
Definition dwin.c:94
char em1[MAXLEN]
Definition dwin.c:143
char em2[MAXLEN]
Definition dwin.c:144
RGB xterm256_idx_to_rgb(uint idx)
Convert XTerm 256 color index to RGB.
Definition dwin.c:274
char em0[MAXLEN]
Definition dwin.c:142
UiCell cell_ran
Definition dwin.c:103
UiCell cell_nt_rev
Definition dwin.c:95
void view_full_screen_resize(Init *)
Resize the full screen view and its components.
Definition init_view.c:97
void view_boxwin_resize(Init *)
Resize the current window and its box.
Definition init_view.c:178
int border_title(UiSurface *sfc, char *title)
Draw a box with a title around the specified window.
Definition dwin.c:676
int answer_yn(char *msg0, char *msg1, char *msg2, char *msg3)
Accept a single letter answer.
Definition dwin.c:715
int Perror(char *emsg_str)
Display a simple error message window or print to stderr.
Definition dwin.c:841
int display_error(char *msg0, char *msg1, char *msg2, char *msg3)
Display an error message window or print to stderr.
Definition dwin.c:778
int shell(char *)
Execute a shell command.
Definition exec.c:74
int full_screen_shell(char *)
Execute a shell command in full screen mode.
Definition exec.c:58
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition futil.c:537
void write_cmenu_log(char *)
Write message to C-Menu log file without timestamp.
Definition futil.c:1684
size_t strnz(char *, size_t)
terminates string at New Line, Carriage Return, or max_len
Definition futil.c:608
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:413
int str_to_args(char **, char *, uint)
Converts a string into an array of argument strings.
Definition futil.c:433
bool expand_tilde(char *, uint)
Replaces "~/" in string with the user's home directory.
Definition futil.c:963
size_t strip_ansi(char *, char *)
Strips ANSI SGR escape sequences (ending in 'm') from string s to d.
Definition futil.c:822
bool mk_dir(char *dir)
If directory doesn't exist, make it.
Definition futil.c:1303
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition futil.c:566
size_t strnlf(char *, size_t)
terminates string with line feed
Definition futil.c:626
int a_toi(char *, bool *)
a safer alternative to atoi() for converting ASCII strings to integers.
Definition futil.c:762
bool str_subc(char *, char *, char, char *, uint)
Replaces "ReplaceChr" in "s" with "Withstr" in "d" won't copy more than "l" bytes to "d" Replaces all...
Definition futil.c:683
int destroy_argv(uint argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:487
bool base_name(char *, char *)
Returns the base name of a file specification.
Definition futil.c:1106
int view_init_input(Init *init, char *file_name)
Initialize the input for the C-Menu View.
Definition init_view.c:279
bool verify_spec_arg(char *, char *, char *, char *, uint)
Verify file specification argument.
Definition mem.c:369
int view_file(Init *)
Start view.
bool enter_file_spec(Init *, char *)
use form to enter a file specification
int write_view_buffer(Init *, bool)
Write buffer contents to files.
void lp(View *, char *)
Send File to Print Queue.
#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:76
void cat_file(View *)
Concatenate File to Standard Output.
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 from User Input.
void build_prompt(View *)
Build Prompt String.
#define get_next_char()
read the next characater from the virtual file
Definition view_engine.c:53
void sync_ln(View *)
Synchronize Line Table with Current File Position.
void go_to_percent(View *, uint)
Go to Percent of File.
off_t line_number(View *, off_t)
Get Line Number for a Given File Position.
void scroll_up(View *, uint)
Scroll Up by n Lines.
void go_to_eof(View *)
Go to End of File.
void prev_page(View *)
display previous page
void go_to_position(View *, off_t)
Go to Specific File Position.
void go_to_mark(View *, uint)
Go to Mark.
void scroll_down(View *, uint)
Scroll Down by n Lines.
void initialize_line_table(View *)
Initialize Line Table.
bool search(View *, int, char *)
Search for Regular Expression Pattern.
void destroy_line_table(View *)
Destroy Line Table.
int go_to_line(View *, off_t)
Go to Specific Line.
void next_page(View *)
Advance to Next Page.
void increment_ln(View *)
Increment Line Index and Update Line Table.
void log_cc_buf(View *)
Log Complex Character Buffer.
void log_split_lines(View *)
Log Split Lines.
void display_line(View *)
Display Line on Padparam View *view data structure.
int pad_refresh(View *)
Refresh Pad and Line Number Window.
void display_line_eod(View *)
Display End of Data.
int display_prompt(View *, char *)
Display Command Line Prompt.
void log_strnz(char *, uint)
Log String with Specified Length.
int fmt_line(View *)
Format Line for Display.
void display_split_line(View *)
Display Split Line on Pad.
void view_display_help(Init *)
Display View Help File.
void view_display_page(View *)
Display Current Page.
void log_stripped_line_out(View *)
Log Stripped Line Output.
void parse_ansi_str(char *, attr_t *, ushort *)
Parse ANSI SGR Escape Sequence.
char title[MAXLEN]
Definition common.h:129
char mapp_help[MAXLEN]
Definition common.h:149
int begx
Definition common.h:118
int cols
Definition common.h:116
char mapp_home[MAXLEN]
Definition common.h:147
int begy
Definition common.h:117
View * view
Definition common.h:189
int lines
Definition common.h:115
char editor[MAXLEN]
Definition common.h:172
off_t sl_ln_no
Definition view.h:52
uint sl_cnt
Definition view.h:58
uint sl_cols[MAXLEN]
Definition view.h:55
uint sl_idx
Definition view.h:57
UiCell * sl_cc[MAXLEN]
Definition view.h:54
uint sl_cells[MAXLEN]
Definition view.h:56
char * sl_s[MAXLEN]
Definition view.h:53
char in_spec[MAXLEN]
Definition view.h:152
off_t page_top_ln_no
Definition view.h:169
off_t srch_curr_ln_no
Definition view.h:171
int argc
Definition view.h:82
char ** argv
Definition view.h:83
bool f_search_complete
Definition view.h:119
int next_cmd_char
Definition view.h:109
uint page_bot_sl_idx
Definition view.h:206
char * buf
Definition view.h:183
uint maxcol
Definition view.h:137
bool wrap
Definition view.h:201
char * line_out_p
Definition view.h:130
char * line_in_end_p
Definition view.h:132
int in_fd
Definition view.h:175
off_t srch_curr_pos
Definition view.h:167
off_t page_top_pos
Definition view.h:164
off_t file_pos
Definition view.h:162
bool page_bot_sl
Definition view.h:204
uint lines
Definition view.h:72
off_t ln_no
Definition view.h:194
char prompt_str[MAXLEN]
Definition view.h:80
off_t prev_file_pos
Definition view.h:163
bool f_bod
Definition view.h:111
bool f_first_iter
Definition view.h:118
char line_in_s[PAD_COLS]
Definition view.h:126
char help_spec[MAXLEN]
Definition view.h:154
bool f_redisplay_page
Definition view.h:116
char * file_spec_ptr
Definition view.h:158
char * next_file_spec_ptr
Definition view.h:159
bool f_is_pipe
Definition view.h:114
bool f_ignore_case
Definition view.h:85
bool f_help_spec
Definition view.h:157
char cmd[MAXLEN]
Definition view.h:78
bool page_top_sl
Definition view.h:203
off_t srch_beg_ln_no
Definition view.h:172
off_t * ln_tbl
Definition view.h:197
bool f_full_screen
Definition view.h:120
bool f_ln
Definition view.h:193
bool f_strip_ansi
Definition view.h:88
bool f_eod
Definition view.h:112
off_t mark_tbl[NMARKS]
Definition view.h:173
off_t ln_no_max
Definition view.h:195
UiCell cmplx_buf[PAD_COLS]
Definition view.h:129
uint cmd_line
Definition view.h:136
off_t ln_max_pos
Definition view.h:200
char stripped_line_out[PAD_COLS]
Definition view.h:128
uint page_top_sl_cnt
Definition view.h:207
off_t srch_beg_pos
Definition view.h:168
off_t ln_tbl_cnt
Definition view.h:199
uint ln_win_cols
Definition view.h:192
uint first_match_x
Definition view.h:148
char * tmp_file_name_ptr
Definition view.h:160
uint curx
Definition view.h:134
off_t file_size
Definition view.h:161
uint scroll_lines
Definition view.h:135
bool f_squeeze
Definition view.h:87
char cur_file_str[MAXLEN]
Definition view.h:125
char line_out_s[PAD_COLS]
Definition view.h:127
uint cury
Definition view.h:133
char out_spec[MAXLEN]
Definition view.h:153
int tab_stop
Definition view.h:107
uint page_bot_sl_cnt
Definition view.h:208
char title[MAXLEN]
Definition view.h:81
uint cols
Definition view.h:73
off_t page_bot_pos
Definition view.h:165
off_t page_bot_ln_no
Definition view.h:170
uint begy
Definition view.h:74
bool f_displaying_help
Definition view.h:117
int h_shift
Definition view.h:108
int curr_argc
Definition view.h:104
int out_fd
Definition view.h:177
uint pmincol
Definition view.h:139
uint page_top_sl_idx
Definition view.h:205
off_t page_bot_end_pos
Definition view.h:166
UiSurface * sfc
Definition view.h:101
SplitLine cur
Definition view.h:202
char file_name[MAXLEN]
Definition view.h:115
uint last_match_x
Definition view.h:150
off_t ln_tbl_size
Definition view.h:198
char * line_in_beg_p
Definition view.h:131
char cmd_arg[MAXLEN]
Definition view.h:106
Definition nccell.h:3
uint64_t channels
Definition nccell.h:8