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