1 /* ################################################################### */
2 /* Copyright 2015, Pierre Gentile (p.gen.progs@gmail.com) */
4 /* This Source Code Form is subject to the terms of the Mozilla Public */
5 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
6 /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 /* ################################################################### */
21 #if (defined(__sun) && defined(__SVR4)) || defined(_AIX)
28 #include <sys/ioctl.h>
30 #include <sys/param.h>
46 /* ***************** */
47 /* Extern variables. */
48 /* ***************** */
50 extern ll_t
*tst_search_list
;
52 /* ***************** */
53 /* Global variables. */
54 /* ***************** */
56 word_t
*word_a
; /* array containing words data (size: count). */
57 long count
= 0; /* number of words read from stdin. */
58 long current
; /* index the current selection under the cursor). */
59 long new_current
; /* final cur. position, (used in search function). */
60 long prev_current
; /* prev. position stored when using direct access. */
62 long *line_nb_of_word_a
; /* array containing the line number (from 0) *
63 | of each word read. */
64 long *first_word_in_line_a
; /* array containing the index of the first *
65 | word of each lines. */
66 long *shift_right_sym_pos_a
; /* screen column number of the right *
67 scrolling symbol if any when in line or *
70 int forgotten_timer
= -1;
73 int daccess_timer
= -1;
74 int search_timer
= -1;
76 search_mode_t search_mode
= NONE
;
77 search_mode_t old_search_mode
= NONE
;
79 int help_mode
= 0; /* 1 if help is displayed else 0. */
81 int marked
= -1; /* Index of the marked word or -1. */
85 int (*my_isprint
)(int);
86 int (*my_isempty
)(const unsigned char *);
88 /* UTF-8 useful symbols. */
89 /* """"""""""""""""""""" */
90 /* clang-format off */
91 char * left_arrow
= "\xe2\x86\x90"; /* ← leftwards arrow. */
92 char * up_arrow
= "\xe2\x86\x91"; /* ↑ upwards arrow. */
93 char * right_arrow
= "\xe2\x86\x92"; /* → rightwards arrow. */
94 char * down_arrow
= "\xe2\x86\x93"; /* ↓ downwards arrow. */
95 char * vertical_bar
= "\xe2\x94\x82"; /* │ box drawings light vertical. */
96 char * shift_left_sym
= "\xe2\x97\x80"; /* ◀ black left-pointing triangle. */
97 char * shift_right_sym
= "\xe2\x96\xb6"; /* ▶ black right-pointing triangle. */
98 char * sbar_line
= "\xe2\x94\x82"; /* │ box drawings light vertical. */
99 char * hbar_line
= "\xe2\x94\x80"; /* ─ box drawings light horizontal. */
100 char * hbar_left
= "\xe2\x97\x80"; /* ◀ black left-pointing triangle. */
101 char * hbar_right
= "\xe2\x96\xb6"; /* ▶ black right-pointing triangle. */
102 char * sbar_top
= "\xe2\x94\x90"; /* ┐ box drawings light down and l. */
103 char * sbar_down
= "\xe2\x94\x98"; /* ┘ box drawings light up and l. */
104 char * hbar_begin
= "\xe2\x94\x94"; /* └ box drawings light up and r. */
105 char * hbar_end
= "\xe2\x94\x98"; /* ┘ box drawings light up and l. */
106 char * sbar_curs
= "\xe2\x94\x83"; /* ┃ box drawings heavy vertical. */
107 char * hbar_curs
= "\xe2\x94\x81"; /* ━ box drawings heavy horizontal. */
108 char * sbar_arr_up
= "\xe2\x96\xb2"; /* ▲ black up pointing triangle. */
109 char * sbar_arr_down
= "\xe2\x96\xbc"; /* ▼ black down pointing triangle. */
110 char * msg_arr_down
= "\xe2\x96\xbc"; /* ▼ black down pointing triangle. */
111 /* clang-format on */
113 /* Mouse tracking. */
114 /* """"""""""""""" */
118 /* Variables used to manage the direct access entries. */
119 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
122 int daccess_stack_head
;
124 /* Variables used for fuzzy and substring searching. */
125 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
126 long matches_count
; /* Number of all matching words. */
128 long *matching_words_a
; /* Array containing the index of all matching *
131 long matching_words_a_size
; /* Allocated size for the previous array. */
133 long best_matches_count
; /* Number of matching words whose matching *
134 | part form a consecutive suite of matching *
137 long *best_matching_words_a
; /* Array containing the index of matching *
138 | words containing a consecutive suite of *
139 | matching glyphs. */
141 long best_matching_words_a_size
; /* Allocated size for the previous array. */
143 long *alt_matching_words_a
= NULL
; /* Alternate array to contain only the *
144 | matching candidates having potentially *
145 | a starting/ending pattern. */
147 /* Variables used in signal handlers. */
148 /* """""""""""""""""""""""""""""""""" */
149 volatile sig_atomic_t got_winch
= 0;
150 volatile sig_atomic_t got_winch_alrm
= 0;
151 volatile sig_atomic_t got_forgotten_alrm
= 0;
152 volatile sig_atomic_t got_help_alrm
= 0;
153 volatile sig_atomic_t got_daccess_alrm
= 0;
154 volatile sig_atomic_t got_search_alrm
= 0;
155 volatile sig_atomic_t got_timeout_tick
= 0;
156 volatile sig_atomic_t got_sigpipe
= 0;
157 volatile sig_atomic_t got_sigsegv
= 0;
158 volatile sig_atomic_t got_sigterm
= 0;
159 volatile sig_atomic_t got_sighup
= 0;
161 /* Variables used when a timeout is set (option -x). */
162 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
164 char *timeout_word
; /* printed word when the timeout type is WORD. */
165 char *timeout_seconds
; /* string containing the number of remaining *
167 int quiet_timeout
= 0; /* 1 when we want no message to be displayed. */
169 /* *************** */
170 /* Help functions. */
171 /* *************** */
173 /* ===================== */
174 /* Help message display. */
175 /* ===================== */
177 help(win_t
*win
, term_t
*term
, long last_line
)
179 int index
; /* used to identify the objects long the help line. */
180 int line
= 0; /* number of windows lines used by the help line. */
181 int len
= 0; /* length of the help line. */
182 int max_col
; /* when to split the help line. */
183 int entries_nb
; /* number of help entries to display. */
188 char *str
; /* string to be displayed for an object in the help line. */
189 int len
; /* screen space taken by of one of these objects. */
190 char attr
; /* r=reverse, n=normal, b=bold, uu=underlined. */
193 char *arrows
= concat(left_arrow
,
199 struct entry_s entries
[] = {
200 { "Move:", 5, 'r' }, { "Mouse:", 6, 'u' }, { "B1", 2, 'b' },
201 { ",", 1, 'n' }, { "B3", 2, 'b' }, { ",", 1, 'n' },
202 { "Wheel", 5, 'b' }, { " ", 1, 'n' }, { "Keyb:", 5, 'u' },
203 { "hjkl", 4, 'b' }, { ",", 1, 'n' }, { arrows
, 4, 'b' },
204 { ",", 1, 'n' }, { "PgUp", 4, 'b' }, { "/", 1, 'n' },
205 { "Dn", 2, 'b' }, { "... ", 4, 'n' }, { "Abort:", 6, 'r' },
206 { "q", 1, 'b' }, { ",", 1, 'n' }, { "^C", 2, 'b' },
207 { " ", 1, 'n' }, { "Find:", 5, 'r' }, { "/", 1, 'b' },
208 { "\"\'", 2, 'b' }, { "~*", 2, 'b' }, { "=^", 2, 'b' },
209 { ",", 1, 'n' }, { "SP", 2, 'b' }, { ",", 1, 'n' },
210 { "nN", 2, 'b' }, { " ", 1, 'n' }, { "Select:", 7, 'r' },
211 { "CR", 2, 'b' }, { ",", 1, 'n' }, { "D-click", 7, 'b' },
212 { " ", 1, 'n' }, { " ", 1, 'n' }, { "Mark:", 5, 'r' },
213 { "mM", 2, 'b' }, { " ", 1, 'n' }, { "Tag:", 4, 'r' },
214 { "t", 1, 'b' }, { ",", 1, 'n' }, { "r", 1, 'b' },
215 { ",", 1, 'n' }, { "c", 1, 'b' }, { " ", 1, 'n' },
216 { "T", 1, 'b' }, { "...", 3, 'n' }, { "T", 1, 'b' },
217 { ",", 1, 'n' }, { "Z", 1, 'b' }, { "...", 3, 'n' },
218 { "Z", 1, 'b' }, { ",", 1, 'n' }, { "R", 1, 'b' },
219 { "...", 3, 'n' }, { "R", 1, 'b' }, { ",", 1, 'n' },
220 { "C", 1, 'b' }, { "...", 3, 'n' }, { "C", 1, 'b' },
221 { " ", 1, 'n' }, { "U", 1, 'b' }, { ",", 1, 'n' },
222 { "^T", 2, 'b' }, { " ", 1, 'n' }, { "Cancel:", 7, 'r' },
226 entries_nb
= sizeof(entries
) / sizeof(struct entry_s
);
228 /* Save the position of the terminal cursor so that it can be */
229 /* put back there after printing of the help line. */
230 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
231 (void)tputs(TPARM1(save_cursor
), 1, outch
);
233 /* Determine when to split the help line if necessary. */
234 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
235 if (win
->sb_column
> 0)
236 max_col
= term
->ncolumns
> win
->sb_column
? win
->sb_column
- 1
237 : term
->ncolumns
- 1;
240 max_col
= term
->ncolumns
- 1;
242 /* Print the different objects forming the help line. */
243 /* A new line is added each time the next entry does not fit in the */
244 /* width of the window. This is done as long as there is space left in */
246 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
247 for (index
= 0; index
< entries_nb
; index
++)
249 if (entries
[index
].len
>= max_col
)
252 len
+= entries
[index
].len
;
257 /* Exit early if we do not have enough space. */
258 /* '''''''''''''''''''''''''''''''''''''''''' */
259 if (line
> last_line
|| line
== win
->max_lines
)
262 len
-= entries
[index
].len
;
264 /* Fill the rest on the line with space, preserving the scroll bar. */
265 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
266 for (i
= len
; i
< max_col
; i
++)
267 fputc_safe(' ', stdout
);
269 len
= entries
[index
].len
;
270 fputc_safe('\n', stdout
);
273 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
274 switch (entries
[index
].attr
)
276 case 'b': /* bold. */
278 (void)tputs(TPARM1(enter_bold_mode
), 1, outch
);
280 case 'u': /* underline. */
281 if (term
->has_underline
)
282 (void)tputs(TPARM1(enter_underline_mode
), 1, outch
);
284 case 'r': /* reverse. */
285 if (term
->has_reverse
)
286 (void)tputs(TPARM1(enter_reverse_mode
), 1, outch
);
287 else if (term
->has_standout
)
288 (void)tputs(TPARM1(enter_standout_mode
), 1, outch
);
290 case 'n': /* no attributes. */
291 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
294 fputs_safe(entries
[index
].str
, stdout
);
295 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
298 /* Fill the remaining space with spaces. */
299 /* """"""""""""""""""""""""""""""""""""" */
300 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
301 for (i
= len
; i
< max_col
; i
++)
302 fputc_safe(' ', stdout
);
304 /* Put back the cursor to its saved position. */
305 /* """""""""""""""""""""""""""""""""""""""""" */
306 (void)tputs(TPARM1(restore_cursor
), 1, outch
);
311 /* *********************************** */
312 /* Attributes string parsing function. */
313 /* *********************************** */
315 /* =========================================================== */
316 /* Allocation and initialization of a new attribute structure. */
317 /* =========================================================== */
323 attr
= xmalloc(sizeof(attrib_t
));
325 attr
->is_set
= UNSET
;
328 attr
->bold
= (signed char)-1;
329 attr
->dim
= (signed char)-1;
330 attr
->reverse
= (signed char)-1;
331 attr
->standout
= (signed char)-1;
332 attr
->underline
= (signed char)-1;
333 attr
->italic
= (signed char)-1;
334 attr
->invis
= (signed char)-1;
335 attr
->blink
= (signed char)-1;
340 /* ================================= */
341 /* Decode attributes toggles if any. */
351 /* Returns 0 if some unexpected. */
352 /* toggle is found else 0. */
353 /* ================================= */
355 decode_attr_toggles(char *s
, attrib_t
*attr
)
359 attr
->bold
= (signed char)0;
360 attr
->dim
= (signed char)0;
361 attr
->reverse
= (signed char)0;
362 attr
->standout
= (signed char)0;
363 attr
->underline
= (signed char)0;
364 attr
->italic
= (signed char)0;
365 attr
->invis
= (signed char)0;
366 attr
->blink
= (signed char)0;
373 attr
->bold
= (signed char)1;
377 attr
->dim
= (signed char)1;
381 attr
->reverse
= (signed char)1;
385 attr
->standout
= (signed char)1;
389 attr
->underline
= (signed char)1;
393 attr
->italic
= (signed char)1;
397 attr
->invis
= (signed char)1;
401 attr
->blink
= (signed char)1;
413 /* =============================================================*/
414 /* Parse attributes in str in the form [fg][/bg][[,.+]toggles] */
416 /* fg and bg are short representing a color value */
417 /* toggles is an array of toggles (see decode_attr_toggles) */
418 /* Returns 1 on success else 0. */
419 /* attr will be filled by the function. */
420 /* =============================================================*/
422 parse_attr(char *str
, attrib_t
*attr
, short colors
)
426 char s1
[12] = { (char)0 }; /* For the colors. */
427 char s2
[9] = { (char)0 }; /* For the attributes. */
428 short d1
= -1, d2
= -1; /* colors. */
432 /* 11: 4 type+colon,2x3 for colors, 1 for slash. */
433 /* 8 : max size for the concatenation of attributes. */
434 /* ''''''''''''''''''''''''''''''''''''''''''''''''' */
435 n
= sscanf(str
, "%11[^,.+]%*[,.+]%8s%c", s1
, s2
, &c
);
437 if (n
== 0 || c
!= '\0')
440 if ((pos
= strchr(s1
, '/')))
442 if (pos
== s1
) /* s1 starts with a / */
445 if (sscanf(s1
+ 1, "%hd", &d2
) == 0)
454 else if (sscanf(s1
, "%hd/%hd", &d1
, &d2
) < 2)
460 else if (d1
< 0 || d2
< 0)
463 else /* no / in the first string. */
466 if (sscanf(s1
, "%hd", &d1
) == 0)
469 if (n
== 2 || decode_attr_toggles(s1
, attr
) == 0)
474 if (colors
== 0) /* Monochrome. */
481 attr
->fg
= d1
< colors
? d1
: -1;
482 attr
->bg
= d2
< colors
? d2
: -1;
486 rc
= decode_attr_toggles(s2
, attr
);
494 /* ============================================== */
495 /* Set the terminal attributes according to attr. */
496 /* ============================================== */
498 apply_attr(term_t
*term
, attrib_t attr
)
501 set_foreground_color(term
, attr
.fg
);
504 set_background_color(term
, attr
.bg
);
506 if (attr
.bold
> (signed char)0)
507 (void)tputs(TPARM1(enter_bold_mode
), 1, outch
);
509 if (attr
.dim
> (signed char)0)
510 (void)tputs(TPARM1(enter_dim_mode
), 1, outch
);
512 if (attr
.reverse
> (signed char)0)
513 (void)tputs(TPARM1(enter_reverse_mode
), 1, outch
);
515 if (attr
.standout
> (signed char)0)
516 (void)tputs(TPARM1(enter_standout_mode
), 1, outch
);
518 if (attr
.underline
> (signed char)0)
519 (void)tputs(TPARM1(enter_underline_mode
), 1, outch
);
521 if (attr
.italic
> (signed char)0)
522 (void)tputs(TPARM1(enter_italics_mode
), 1, outch
);
524 if (attr
.invis
> (signed char)0)
525 (void)tputs(TPARM1(enter_secure_mode
), 1, outch
);
527 if (attr
.blink
> (signed char)0)
528 (void)tputs(TPARM1(enter_blink_mode
), 1, outch
);
531 /* ********************* */
532 /* ini parsing function. */
533 /* ********************* */
535 /* ===================================================== */
536 /* Callback function called when parsing each non-header */
537 /* line of the ini file. */
538 /* Returns 0 if OK, 1 if not. */
539 /* ===================================================== */
552 int has_colors
= term
->colors
> 7;
554 if (strcmp(section
, "colors") == 0)
556 attrib_t v
= { UNSET
,
559 /* bold */ (signed char)-1,
560 /* dim */ (signed char)-1,
561 /* reverse */ (signed char)-1,
562 /* standout */ (signed char)-1,
563 /* underline */ (signed char)-1,
564 /* italic */ (signed char)-1,
565 /* invis */ (signed char)-1,
566 /* blink */ (signed char)-1 };
568 #define CHECK_ATTR(x) \
569 else if (strcmp(name, #x) == 0) \
571 error = !parse_attr(value, &v, term->colors); \
576 if (win->x##_attr.is_set != FORCED) \
578 win->x##_attr.is_set = SET; \
580 win->x##_attr.fg = v.fg; \
582 win->x##_attr.bg = v.bg; \
583 if (v.bold >= (signed char)0) \
584 win->x##_attr.bold = v.bold; \
585 if (v.dim >= (signed char)0) \
586 win->x##_attr.dim = v.dim; \
587 if (v.reverse >= (signed char)0) \
588 win->x##_attr.reverse = v.reverse; \
589 if (v.standout >= (signed char)0) \
590 win->x##_attr.standout = v.standout; \
591 if (v.underline >= (signed char)0) \
592 win->x##_attr.underline = v.underline; \
593 if (v.italic >= (signed char)0) \
594 win->x##_attr.italic = v.italic; \
595 if (v.invis >= (signed char)0) \
596 win->x##_attr.invis = v.invis; \
597 if (v.blink >= (signed char)0) \
598 win->x##_attr.blink = v.blink; \
603 #define CHECK_ATT_ATTR(x, y) \
604 else if (strcmp(name, #x #y) == 0) \
606 error = !parse_attr(value, &v, term->colors); \
611 if (win->x##_attr[y - 1].is_set != FORCED) \
613 win->x##_attr[y - 1].is_set = SET; \
615 win->x##_attr[y - 1].fg = v.fg; \
617 win->x##_attr[y - 1].bg = v.bg; \
618 if (v.bold >= (signed char)0) \
619 win->x##_attr[y - 1].bold = v.bold; \
620 if (v.dim >= (signed char)0) \
621 win->x##_attr[y - 1].dim = v.dim; \
622 if (v.reverse >= (signed char)0) \
623 win->x##_attr[y - 1].reverse = v.reverse; \
624 if (v.standout >= (signed char)0) \
625 win->x##_attr[y - 1].standout = v.standout; \
626 if (v.underline >= (signed char)0) \
627 win->x##_attr[y - 1].underline = v.underline; \
628 if (v.italic >= (signed char)0) \
629 win->x##_attr[y - 1].italic = v.italic; \
630 if (v.invis >= (signed char)0) \
631 win->x##_attr[y - 1].invis = v.invis; \
632 if (v.blink >= (signed char)0) \
633 win->x##_attr[y - 1].blink = v.blink; \
638 /* [colors] section. */
639 /* """"""""""""""""" */
642 if (strcmp(name
, "method") == 0)
644 if (strcmp(value
, "classic") == 0)
645 term
->color_method
= CLASSIC
;
646 else if (strcmp(value
, "ansi") == 0)
647 term
->color_method
= ANSI
;
655 /* clang-format off */
660 CHECK_ATTR(search_field
)
661 CHECK_ATTR(search_text
)
662 CHECK_ATTR(match_field
)
663 CHECK_ATTR(match_text
)
664 CHECK_ATTR(match_err_field
)
665 CHECK_ATTR(match_err_text
)
669 CHECK_ATTR(cursor_on_tag
)
671 CHECK_ATT_ATTR(special
, 1)
672 CHECK_ATT_ATTR(special
, 2)
673 CHECK_ATT_ATTR(special
, 3)
674 CHECK_ATT_ATTR(special
, 4)
675 CHECK_ATT_ATTR(special
, 5)
676 CHECK_ATT_ATTR(special
, 6)
677 CHECK_ATT_ATTR(special
, 7)
678 CHECK_ATT_ATTR(special
, 8)
679 CHECK_ATT_ATTR(special
, 9)
680 /* clang-format on */
683 else if (strcmp(section
, "window") == 0)
687 /* [window] section. */
688 /* """"""""""""""""" */
689 if (strcmp(name
, "lines") == 0)
691 if ((error
= !(sscanf(value
, "%d", &v
) == 1 && v
>= 0)))
694 win
->asked_max_lines
= v
;
697 else if (strcmp(section
, "limits") == 0)
701 /* [limits] section. */
702 /* """"""""""""""""" */
703 if (strcmp(name
, "word_length") == 0)
705 if ((error
= !(sscanf(value
, "%ld", &v
) == 1 && v
> 0)))
708 limits
->word_length
= v
;
710 else if (strcmp(name
, "words") == 0)
712 if ((error
= !(sscanf(value
, "%ld", &v
) == 1 && v
> 0)))
717 else if (strcmp(name
, "columns") == 0)
719 if ((error
= !(sscanf(value
, "%ld", &v
) == 1 && v
> 0)))
725 else if (strcmp(section
, "timers") == 0)
729 /* [timers] section. */
730 /* """"""""""""""""" */
731 if (strcmp(name
, "help") == 0)
733 if ((error
= !(sscanf(value
, "%d", &v
) == 1 && v
> 0)))
738 else if (strcmp(name
, "forgotten") == 0)
740 if ((error
= !(sscanf(value
, "%d", &v
) == 1 && v
> 0)))
743 timers
->forgotten
= v
;
745 else if (strcmp(name
, "window") == 0)
747 if ((error
= !(sscanf(value
, "%d", &v
) == 1 && v
> 0)))
752 else if (strcmp(name
, "direct_access") == 0)
754 if ((error
= !(sscanf(value
, "%d", &v
) == 1 && v
> 0)))
757 timers
->direct_access
= v
;
759 else if (strcmp(name
, "search") == 0)
761 if ((error
= !(sscanf(value
, "%d", &v
) == 1 && v
> 0)))
767 else if (strcmp(section
, "mouse") == 0)
771 /* [mouse] section. */
772 /* """"""""""""""" */
773 if (strcmp(name
, "double_click_delay") == 0)
775 if ((error
= !(sscanf(value
, "%d", &v
) == 1 && v
> 0)))
778 mouse
->double_click_delay
= v
;
781 else if (strcmp(section
, "misc") == 0)
783 /* [misc] section. */
784 /* """"""""""""""" */
785 if (strcmp(name
, "default_search_method") == 0)
787 if (misc
->default_search_method
== NONE
)
789 if (strcmp(value
, "prefix") == 0)
790 misc
->default_search_method
= PREFIX
;
791 else if (strcmp(value
, "fuzzy") == 0)
792 misc
->default_search_method
= FUZZY
;
793 else if (strcmp(value
, "substring") == 0)
794 misc
->default_search_method
= SUBSTRING
;
804 /* ======================================================================== */
805 /* Load an .ini format file. */
806 /* filename - path to a file. */
807 /* report - callback can return non-zero to stop, the callback error code */
808 /* returned from this function. */
809 /* return - return 0 on success. */
811 /* This function is public domain. No copyright is claimed. */
812 /* Jon Mayo April 2011. */
813 /* ======================================================================== */
815 ini_load(const char *filename
,
822 int (*report
)(win_t
*win
,
833 char value
[256] = "";
834 char section
[128] = "";
840 /* If the filename is empty we skip this phase and use the */
841 /* default values. */
842 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
843 if (filename
== NULL
)
846 /* We do that if the file is not readable as well. */
847 /* """"""""""""""""""""""""""""""""""""""""""""""" */
848 f
= fopen_safe(filename
, "r");
850 return 0; /* Returns success as the presence of this file *
855 /* Skip blank lines. */
856 /* """"""""""""""""" */
857 while (fscanf(f
, "%*[\n]") == 1)
863 if (fscanf(f
, " [%127[^];\n]]", section
) == 1)
869 if ((cnt
= fscanf(f
, " %63[^=;\n] = %255[^;\n]", name
, value
)))
874 for (s
= name
+ strlen(name
) - 1; s
> name
&& isspace(*s
); s
--)
877 for (s
= value
+ strlen(value
) - 1; s
> value
&& isspace(*s
); s
--)
880 /* Callback function calling. */
881 /* """""""""""""""""""""""""" */
883 report(win
, term
, limits
, timers
, misc
, mouse
, section
, name
, value
);
889 if (fscanf(f
, " ;%*[^\n]"))
891 /* To silence the compiler about unused results. */
894 /* Skip blank lines. */
895 /* """"""""""""""""" */
896 while (fscanf(f
, "%*[\n]") == 1)
908 "Invalid entry found: %s=%s in %s.\n",
916 /* ======================================================= */
917 /* Return the full path on the configuration file supposed */
918 /* to be in the home directory of the user. */
919 /* NULL is returned if the built path is too large. */
920 /* ======================================================= */
922 make_ini_path(char *name
, char *base
)
929 /* Set the prefix of the path from the environment */
930 /* base can be "HOME" or "PWD". */
931 /* """"""""""""""""""""""""""""""""""""""""""""""" */
937 path_max
= pathconf(".", _PC_PATH_MAX
);
938 len
= strlen(home
) + strlen(name
) + 3;
941 path_max
= 4096; /* POSIX minimal value. path_max >= 4096. */
948 conf
= strrchr(name
, '/');
955 snprintf(path
, len
, "%s/.%s", home
, conf
);
963 /* ********************************* */
964 /* Functions used when sorting tags. */
965 /* ********************************* */
967 /* =================================================================== */
968 /* Compare the pin order of two pinned word in the output list. */
969 /* Returns -1 if the pinning value is less than the second, 0 if it is */
970 /* equal to the second and 1 if it is greater than the second. */
971 /* =================================================================== */
973 tag_comp(void const *a
, void const *b
)
975 output_t
*oa
= (output_t
*)a
;
976 output_t
*ob
= (output_t
*)b
;
978 if (oa
->order
== ob
->order
)
981 return (oa
->order
< ob
->order
) ? -1 : 1;
984 /* ========================================================= */
985 /* Swap the values of two selected words in the output list. */
986 /* ========================================================= */
988 tag_swap(void **a
, void **b
)
990 output_t
*oa
= (output_t
*)*a
;
991 output_t
*ob
= (output_t
*)*b
;
996 tmp_str
= oa
->output_str
;
997 oa
->output_str
= ob
->output_str
;
998 ob
->output_str
= tmp_str
;
1000 tmp_order
= oa
->order
;
1001 oa
->order
= ob
->order
;
1002 ob
->order
= tmp_order
;
1005 /* ****************** */
1006 /* Utility functions. */
1007 /* ****************** */
1009 /* =================================================================== */
1010 /* Create a new element to be added to the tst_search_list used by the */
1011 /* search mechanism. */
1012 /* Return the newly created element. */
1013 /* =================================================================== */
1017 sub_tst_t
*elem
= xmalloc(sizeof(sub_tst_t
));
1021 elem
->array
= xmalloc(elem
->size
* sizeof(tst_node_t
));
1026 /* ========================================= */
1027 /* Emit a small (visual) beep warn the user. */
1028 /* ========================================= */
1030 my_beep(toggle_t
*toggles
)
1032 struct timespec ts
, rem
;
1034 if (!toggles
->visual_bell
)
1035 fputc_safe('\a', stdout
);
1040 (void)tputs(TPARM1(cursor_visible
), 1, outch
);
1043 ts
.tv_nsec
= 200000000; /* 0.2s */
1046 rc
= nanosleep(&ts
, &rem
);
1048 while (rc
< 0 && errno
== EINTR
)
1051 rc
= nanosleep(&rem
, &rem
);
1054 (void)tputs(TPARM1(cursor_invisible
), 1, outch
);
1058 /* ================================================================== */
1059 /* Integer verification constraint for ctxopt. */
1060 /* Return 1 if par is the representation of an integer else return 0. */
1061 /* ================================================================== */
1063 check_integer_constraint(int nb_args
, char **args
, char *value
, char *par
)
1065 if (!is_integer(value
))
1067 fprintf(stderr
, "The argument of %s is not an integer: %s", par
, value
);
1073 /* ======================================================================= */
1074 /* Update the bitmap associated with a word. The bits set to 1 in this */
1075 /* bitmap indicate the positions of the UFT-8 glyphs of the search buffer */
1078 /* The disp_word function will use it to display these special characters. */
1080 /* mode is the search method. */
1081 /* data contains information about the search buffer. */
1082 /* affinity determines if we must only consider matches that occur at */
1083 /* the start, the end or if we just don't care. */
1084 /* ======================================================================= */
1086 update_bitmaps(search_mode_t mode
,
1087 search_data_t
*data
,
1088 bitmap_affinity_t affinity
)
1090 long i
, j
, n
; /* work variables. */
1092 long bm_len
; /* number of chars taken by the bit mask. */
1094 char *start
; /* pointer on the position of the matching position *
1095 | of the last search buffer glyph in the word. */
1097 char *bm
; /* the word's current bitmap. */
1099 char *str
; /* copy of the current word put in lower case. */
1100 char *str_orig
; /* original version of the word. */
1102 char *sb_orig
= data
->buf
; /* sb: search buffer. */
1104 long *o
= data
->utf8_off_a
; /* array of the offsets of the search *
1106 long *l
= data
->utf8_len_a
; /* array of the lengths in bytes of *
1107 | the search buffer glyphs. */
1108 long last
= data
->utf8_len
- 1; /* offset of the last glyph in the *
1111 best_matches_count
= 0;
1113 if (mode
== FUZZY
|| mode
== SUBSTRING
)
1117 long badness
= 0; /* number of 0s between two 1s. */
1119 first_glyph
= xmalloc(5);
1123 sb
= xstrdup(sb_orig
); /* sb initially points to sb_orig. */
1124 utf8_strtolower(sb
, sb_orig
);
1129 for (i
= 0; i
< matches_count
; i
++)
1131 long lmg
; /* position of the last matching glyph of the search buffer *
1134 n
= matching_words_a
[i
];
1136 str_orig
= xstrdup(word_a
[n
].str
+ daccess
.flength
);
1138 /* We need to remove the trailing spaces to use the */
1139 /* following algorithm. */
1140 /* .len holds the original length in bytes of the word. */
1141 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
1142 rtrim(str_orig
, " \t", 0);
1144 bm_len
= (word_a
[n
].mb
- daccess
.flength
) / CHAR_BIT
+ 1;
1145 bm
= word_a
[n
].bitmap
;
1147 /* In fuzzy search mode str are converted in lower case letters */
1148 /* for comparison reason. */
1149 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1152 str
= xstrdup(str_orig
);
1153 utf8_strtolower(str
, str_orig
);
1161 /* Start points to the first UTF-8 glyph of the word. */
1162 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
1163 while ((size_t)(start
- str
) < word_a
[n
].len
- daccess
.flength
)
1165 /* Reset the bitmap. */
1166 /* """"""""""""""""" */
1167 memset(bm
, '\0', bm_len
);
1169 /* Compare the glyph pointed to by start to the last glyph of */
1170 /* the search buffer, the aim is to point to the first */
1171 /* occurrence of the last glyph of it. */
1172 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1173 if (memcmp(start
, sb
+ o
[last
], l
[last
]) == 0)
1175 char *p
; /* Pointer to the beginning of an UTF-8 glyph in *
1176 | the potential lowercase version of the word. */
1178 long sg
; /* Index going from lmg backward to 0 of the tested *
1179 | glyphs of the search buffer (searched glyph). */
1183 /* There is only one glyph in the search buffer, we can */
1185 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
1187 if (affinity
!= END_AFFINITY
)
1191 /* If the search buffer contains more than one glyph, we need */
1192 /* to search the first combination which match the buffer in */
1194 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1196 j
= last
; /* j counts the number of glyphs in the search buffer *
1197 | not found in the word. */
1199 /* Proceed backwards from the position of last glyph of the */
1200 /* search to check if all the previous glyphs can be fond before */
1201 /* in the word. If not try to find the next position of this */
1202 /* last glyph in the word. */
1203 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1205 while (j
> 0 && (p
= utf8_prev(str
, p
)) != NULL
)
1207 if (memcmp(p
, sb
+ o
[j
- 1], l
[j
- 1]) == 0)
1212 else if (mode
== SUBSTRING
)
1218 /* All the glyphs have been found. */
1219 /* """"""""""""""""""""""""""""""" */
1223 if (affinity
!= END_AFFINITY
)
1229 start
= utf8_next(start
);
1238 /* We know that the first non blank glyph is part of the pattern, */
1239 /* so highlight it if it is not and suppresses the highlighting */
1240 /* of the next occurrence that must be here because this word has */
1241 /* already been filtered by select_starting_pattern(). */
1242 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1243 if (affinity
== START_AFFINITY
)
1248 /* Skip leading spaces and tabs. */
1249 /* """"""""""""""""""""""""""""" */
1250 for (i
= 0; i
< word_a
[n
].mb
; i
++)
1251 if (!isblank(*(word_a
[n
].str
+ daccess
.flength
+ i
)))
1254 first_glyph
= utf8_strprefix(first_glyph
,
1259 if (!BIT_ISSET(word_a
[n
].bitmap
, i
))
1263 BIT_ON(word_a
[n
].bitmap
, i
);
1265 ptr1
= word_a
[n
].str
+ i
;
1267 while ((ptr2
= utf8_next(ptr1
)) != NULL
)
1269 if (memcmp(ptr2
, first_glyph
, utf8_len
) == 0)
1271 if (BIT_ISSET(word_a
[n
].bitmap
, i
))
1273 BIT_OFF(word_a
[n
].bitmap
, i
);
1287 /* Compute the number of 'holes' in the bitmap to determine the */
1288 /* badness of a match. Th goal is to put the cursor on the word */
1289 /* with the smallest badness. */
1290 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1295 while (utf8_index
< word_a
[n
].mb
1296 && !BIT_ISSET(word_a
[n
].bitmap
, utf8_index
))
1299 while (utf8_index
< word_a
[n
].mb
)
1301 if (!BIT_ISSET(word_a
[n
].bitmap
, utf8_index
))
1306 /* Stop here if all the possible bits has been checked as they */
1307 /* cannot be more numerous than the number of UTF-8 glyphs in */
1308 /* the search buffer. */
1309 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1310 if (j
== data
->utf8_len
)
1318 if (search_mode
== FUZZY
)
1320 /* When the badness is zero (best match), add the word position. */
1321 /* at the end of a special array which will be used to move the. */
1322 /* cursor among this category of words. */
1323 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1326 if (best_matches_count
== best_matching_words_a_size
)
1328 best_matching_words_a
= xrealloc(best_matching_words_a
,
1329 (best_matching_words_a_size
+ 16)
1331 best_matching_words_a_size
+= 16;
1334 best_matching_words_a
[best_matches_count
] = n
;
1336 best_matches_count
++;
1346 else if (mode
== PREFIX
)
1348 for (i
= 0; i
< matches_count
; i
++)
1350 n
= matching_words_a
[i
];
1351 bm
= word_a
[n
].bitmap
;
1352 bm_len
= (word_a
[n
].mb
- daccess
.flength
) / CHAR_BIT
+ 1;
1354 memset(bm
, '\0', bm_len
);
1356 for (j
= 0; j
<= last
; j
++)
1362 /* ========================================================= */
1363 /* Find the next word index in the list of matching words */
1364 /* using the bisection search algorithm. */
1365 /* Set the value of index to -1 and returns -1 if not found. */
1366 /* ========================================================= */
1368 find_next_matching_word(long *array
, long nb
, long value
, long *index
)
1370 /* Use the cached value when possible. */
1371 /* """"""""""""""""""""""""""""""""""" */
1372 if (*index
>= 0 && *index
< nb
- 1 && array
[*index
] == value
)
1373 return (array
[++(*index
)]);
1377 long left
= 0, right
= nb
;
1379 /* Bisection search. */
1380 /* ''''''''''''''''' */
1381 while (left
< right
)
1383 long middle
= (left
+ right
) / 2;
1385 if (value
< array
[middle
])
1394 return array
[*index
];
1397 if (value
> array
[nb
- 1])
1404 return array
[*index
];
1411 /* ========================================================== */
1412 /* Find the previous word index in the list of matching words */
1413 /* using the bisection search algorithm. */
1414 /* set the value of index to -1 and returns -1 if not found. */
1415 /* ========================================================== */
1417 find_prev_matching_word(long *array
, long nb
, long value
, long *index
)
1419 /* Use the cached value when possible. */
1420 /* """"""""""""""""""""""""""""""""""" */
1421 if (*index
> 0 && array
[*index
] == value
)
1422 return (array
[--(*index
)]);
1426 /* Bisection search. */
1427 /* ''''''''''''''''' */
1429 long left
= 0, right
= nb
;
1431 while (left
< right
)
1433 long middle
= (left
+ right
) / 2;
1435 if (array
[middle
] == value
)
1439 *index
= middle
- 1;
1440 return array
[*index
];
1447 if (value
< array
[middle
])
1456 return array
[*index
];
1467 /* ============================================================= */
1468 /* Remove all traces of matched words and redisplay the windows. */
1469 /* ============================================================= */
1471 clean_matches(search_data_t
*search_data
, long size
)
1475 /* Clean the list of lists data-structure containing the search levels */
1476 /* Note that the first element of the list is never deleted. */
1477 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1478 if (tst_search_list
!= NULL
)
1480 ll_node_t
*fuzzy_node
;
1481 sub_tst_t
*sub_tst_data
;
1483 fuzzy_node
= tst_search_list
->tail
;
1485 while (tst_search_list
->len
> 1)
1487 sub_tst_data
= (sub_tst_t
*)(fuzzy_node
->data
);
1489 free(sub_tst_data
->array
);
1492 ll_delete(tst_search_list
, tst_search_list
->tail
);
1493 fuzzy_node
= tst_search_list
->tail
;
1495 sub_tst_data
= (sub_tst_t
*)(fuzzy_node
->data
);
1496 sub_tst_data
->count
= 0;
1499 search_data
->err
= 0;
1501 /* Clean the search buffer. */
1502 /* """""""""""""""""""""""" */
1503 memset(search_data
->buf
, '\0', size
- daccess
.flength
);
1505 search_data
->len
= 0;
1506 search_data
->utf8_len
= 0;
1507 search_data
->only_ending
= 0;
1508 search_data
->only_starting
= 0;
1510 /* Clean the match flags and bitmaps. */
1511 /* """""""""""""""""""""""""""""""""" */
1512 for (i
= 0; i
< matches_count
; i
++)
1514 long n
= matching_words_a
[i
];
1516 word_a
[n
].is_matching
= 0;
1518 memset(word_a
[n
].bitmap
,
1520 (word_a
[n
].mb
- daccess
.flength
) / CHAR_BIT
+ 1);
1526 /* *************************** */
1527 /* Terminal utility functions. */
1528 /* *************************** */
1530 /* ===================================================================== */
1531 /* outch is a function version of putchar that can be passed to tputs as */
1532 /* a routine to call. */
1533 /* ===================================================================== */
1545 /* =============================================== */
1546 /* Set the terminal in non echo/non canonical mode */
1547 /* wait for at least one byte, no timeout. */
1548 /* =============================================== */
1550 setup_term(int const fd
,
1551 struct termios
*old_in_attrs
,
1552 struct termios
*new_in_attrs
)
1556 /* Save the terminal parameters and configure it in row mode. */
1557 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1558 tcgetattr(fd
, old_in_attrs
);
1560 new_in_attrs
->c_iflag
= 0;
1561 new_in_attrs
->c_oflag
= old_in_attrs
->c_oflag
;
1562 new_in_attrs
->c_cflag
= old_in_attrs
->c_cflag
;
1563 new_in_attrs
->c_lflag
= old_in_attrs
->c_lflag
& ~(ICANON
| ECHO
| ISIG
);
1565 new_in_attrs
->c_cc
[VMIN
] = 1; /* wait for at least 1 byte. */
1566 new_in_attrs
->c_cc
[VTIME
] = 0; /* no timeout. */
1568 error
= tcsetattr_safe(fd
, TCSANOW
, new_in_attrs
);
1577 /* ====================================== */
1578 /* Set the terminal in its previous mode. */
1579 /* ====================================== */
1581 restore_term(int const fd
, struct termios
*old_in_attrs
)
1585 error
= tcsetattr_safe(fd
, TCSANOW
, old_in_attrs
);
1594 /* ============================================== */
1595 /* Get the terminal numbers of lines and columns */
1596 /* Assume that the TIOCGWINSZ, ioctl is available */
1597 /* Defaults to 80x24. */
1598 /* ============================================== */
1600 get_terminal_size(int * const r
, int * const c
, term_t
*term
)
1606 if (ioctl(0, TIOCGWINSZ
, &ws
) == 0)
1611 if (*r
> 0 && *c
> 0)
1615 *r
= tigetnum("lines");
1616 *c
= tigetnum("cols");
1618 if (*r
<= 0 || *c
<= 0)
1625 /* =========================================================== */
1626 /* Gets the cursor position in the terminal. */
1627 /* Assume that the Escape sequence ESC [ 6 n is available. */
1628 /* Retries up to 64 times in case of system call interruption. */
1629 /* Returns 1 on success and 0 on error. */
1630 /* =========================================================== */
1632 get_cursor_position(int * const r
, int * const c
)
1634 char buf
[32] = { 0 };
1641 int ask
; /* Number of asked characters. */
1642 int got
; /* Number of characters obtained. */
1644 int buf_size
= sizeof(buf
);
1646 /* Report cursor location. */
1647 /* """"""""""""""""""""""" */
1648 while ((v
= write(STDOUT_FILENO
, "\x1b[6n", 4)) == -1 && attempts
)
1666 /* Read the response: ESC [ rows ; cols R. */
1667 /* """"""""""""""""""""""""""""""""""""""" */
1672 ask
= buf_size
- 1 - (s
- buf
);
1673 got
= read(STDIN_FILENO
, s
, ask
);
1675 if (got
< 0 && errno
== EINTR
)
1681 } while (strchr(buf
, 'R') == NULL
);
1685 if (buf
[0] != 0x1b || buf
[1] != '[')
1688 if (sscanf(buf
+ 2, "%d;%d", r
, c
) != 2)
1694 /* ====================================================== */
1695 /* Returns 1 if a string is empty or only made of spaces. */
1696 /* Non UTF-8 version. */
1697 /* ====================================================== */
1699 isempty_non_utf8(const unsigned char *s
)
1703 if (*s
!= ' ' && *s
!= '\t')
1710 /* ====================================================== */
1711 /* Returns 1 if a string is empty or only made of spaces. */
1712 /* UTF-8 version. */
1713 /* ====================================================== */
1715 isempty_utf8(const unsigned char *s
)
1721 if (*s
== ' ' || *s
== '\t') /* Normal ASCII spaces. */
1724 if (*s
< 0xc2) /* Not an UTF-8 space -> return FALSE. */
1727 /* Scanning for a potential non UTF-8 spaces scanning. */
1728 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
1729 if ((c
= *(s
+ 1)) != '\0')
1731 if (*s
== 0xc2 && (c
== 0x85 || c
== 0xa0))
1734 goto next
; /* Unnamed control character or NO-BREAK SPACE. */
1737 if ((d
= *(s
+ 2)) == '\0')
1740 if (*s
== 0xe1 && c
== 0x9a && d
== 0x80)
1743 goto next
; /* OGHAM SPACE MARK. */
1746 if (*s
== 0xe1 && c
== 0xa0 && d
== 0x8e)
1749 goto next
; /* MONGOLIAN VOWEL SEPARATOR. */
1752 if (*s
== 0xe2 && c
== 0x80 && d
== 0x80)
1755 goto next
; /* EN QUAD. */
1758 if (*s
== 0xe2 && c
== 0x80 && d
== 0x81)
1761 goto next
; /* EM QUAD. */
1764 if (*s
== 0xe2 && c
== 0x80 && d
== 0x82)
1767 goto next
; /* EN SPACE. */
1769 if (*s
== 0xe2 && c
== 0x80 && d
== 0x83)
1772 goto next
; /* EM SPACE. */
1775 if (*s
== 0xe2 && c
== 0x80 && d
== 0x84)
1778 goto next
; /* THREE-PER-EM SPACE. */
1781 if (*s
== 0xe2 && c
== 0x80 && d
== 0x85)
1784 goto next
; /* FOUR-PER-EM SPACE. */
1787 if (*s
== 0xe2 && c
== 0x80 && d
== 0x86)
1790 goto next
; /* SIX-PER-EM SPACE. */
1792 if (*s
== 0xe2 && c
== 0x80 && d
== 0x87)
1795 goto next
; /* FIGURE SPACE. */
1798 if (*s
== 0xe2 && c
== 0x80 && d
== 0x88)
1801 goto next
; /* PUNCTUATION SPACE. */
1804 if (*s
== 0xe2 && c
== 0x80 && d
== 0x89)
1807 goto next
; /* THIN SPACE. */
1810 if (*s
== 0xe2 && c
== 0x80 && d
== 0x8a)
1813 goto next
; /* HAIR SPACE. */
1816 if (*s
== 0xe2 && c
== 0x80 && d
== 0xa8)
1819 goto next
; /* LINE SEPARATOR. */
1822 if (*s
== 0xe2 && c
== 0x80 && d
== 0xa9)
1825 goto next
; /* PARAGRAPH SEPARATOR. */
1828 if (*s
== 0xe2 && c
== 0x80 && d
== 0xaf)
1831 goto next
; /* NARROW NO-BREAK SPACE. */
1834 if (*s
== 0xe2 && c
== 0x81 && d
== 0x9f)
1837 goto next
; /* MEDIUM MATHEMATICAL SPACE. */
1840 if (*s
== 0xe3 && c
== 0x80 && d
== 0x80)
1843 goto next
; /* IDEOGRAPHIC SPACE. */
1854 /* ======================================================================== */
1855 /* Parse a regular expression based selector. */
1856 /* The string to parse is bounded by a delimiter so we must parse something */
1857 /* like: <delim><regex string><delim> as in /a.*b/ by example. */
1859 /* str (in) delimited string to parse */
1860 /* regex_list (out) regex list to modify. */
1861 /* ======================================================================== */
1863 parse_regex_selector_part(char *str
, ll_t
**regex_list
)
1867 /* Remove the last character of str (the delimiter).*/
1868 /* """""""""""""""""""""""""""""""""""""""""""""""" */
1869 str
[strlen(str
) - 1] = '\0';
1871 /* Ignore the first character of str (the delimiter). */
1872 /* compile it and add a compiled regex in a dedicated list. */
1873 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1874 regex
= xmalloc(sizeof(regex_t
));
1875 if (regcomp(regex
, str
+ 1, REG_EXTENDED
| REG_NOSUB
) == 0)
1877 if (*regex_list
== NULL
)
1878 *regex_list
= ll_new();
1880 ll_append(*regex_list
, regex
);
1884 /* ================================================================= */
1885 /* Compare two elements of type attr_elem_t. */
1886 /* The comparison key is the first member of the structure which is */
1887 /* of type attrib_t. */
1888 /* Returns 0 is the two elements are equals otherwise returns 1. */
1889 /* ================================================================= */
1891 attr_elem_cmp(void const *a
, void const *b
)
1893 const attr_elem_t
*ai
= a
;
1894 const attr_elem_t
*bi
= b
;
1896 if (ai
->attr
->fg
!= bi
->attr
->fg
)
1898 if (ai
->attr
->bg
!= bi
->attr
->bg
)
1900 if (ai
->attr
->bold
!= bi
->attr
->bold
)
1902 if (ai
->attr
->dim
!= bi
->attr
->dim
)
1904 if (ai
->attr
->reverse
!= bi
->attr
->reverse
)
1906 if (ai
->attr
->standout
!= bi
->attr
->standout
)
1908 if (ai
->attr
->underline
!= bi
->attr
->underline
)
1910 if (ai
->attr
->italic
!= bi
->attr
->italic
)
1912 if (ai
->attr
->invis
!= bi
->attr
->invis
)
1914 if (ai
->attr
->blink
!= bi
->attr
->blink
)
1920 /* ===================================================================== */
1921 /* Parse a column or row selector string whose syntax is defined as: */
1922 /* [<letter>]<item1>|,<item>|,... */
1923 /* <item> is <range>| <delimited regex> */
1924 /* <item> is (<range>| <delimited regex>):<attribute> if letter is a|A. */
1925 /* <range> is n1-n2 | n1 | -n2 | n1- where n1 starts with 1. */
1926 /* <delimited regex> is <char><regex><char> (e.g. /<regex>/). */
1928 /* <letter> is a|A|s|S|r|R|u|U where: */
1929 /* i|I is for 'include'. */
1930 /* e|E is for 'exclude'. */
1931 /* l|L is for 'left' alignment. */
1932 /* r|R is for 'right' alignment. */
1933 /* c|C is for 'center' alignment. */
1934 /* a|A for defining attributes for rows or columns. */
1936 /* str (in) string to parse. */
1937 /* filter (out) is INCLUDE_FILTER or EXCLUDE_FILTER according */
1939 /* unparsed (out) is empty on success and contains the unparsed */
1940 /* part on failure. */
1941 /* inc_interval_list (out) is a list of the interval of elements to */
1943 /* inc_regex_list (out) is a list of extended regular expressions of */
1944 /* elements to be included. */
1945 /* exc_interval_list (out) is a list of the interval of elements to be */
1947 /* exc_regex_list (out) is a list of regex matching elements to */
1949 /* aX_interval_list (out) is a list of the interval of elements to be */
1950 /* aligned to the left, right or centered. */
1951 /* aX_regex_list (out) is a list of regex matching elements to */
1952 /* be aligned to the left, right or centered. */
1953 /* at_interval_list (out) is a list of the interval of elements with */
1954 /* a given attribute. */
1955 /* at_regex_list (out) is a list of regex matching elements with */
1956 /* a given attribute. */
1957 /* ===================================================================== */
1959 parse_selectors(char *str
,
1962 ll_t
**inc_interval_list
,
1963 ll_t
**inc_regex_list
,
1964 ll_t
**exc_interval_list
,
1965 ll_t
**exc_regex_list
,
1966 ll_t
**al_interval_list
,
1967 ll_t
**al_regex_list
,
1968 ll_t
**ar_interval_list
,
1969 ll_t
**ar_regex_list
,
1970 ll_t
**ac_interval_list
,
1971 ll_t
**ac_regex_list
,
1972 ll_t
**at_interval_list
,
1973 ll_t
**at_regex_list
,
1974 alignment_t
*default_alignment
,
1980 long start
= 1; /* column string offset in the parsed string. */
1981 long first
, second
; /* range starting and ending values. */
1982 int l_open_range
; /* 1 if the range is left-open. */
1983 int r_open_range
; /* 1 if the range is right-open. */
1984 char *ptr
; /* pointer to the remaining string to parse. */
1985 interval_t
*interval
;
1987 char *attr_str
= NULL
;
1989 attr_elem_t
*attr_elem
;
1991 /* Replace the UTF-8 string representation in the selector by */
1992 /* their binary values. */
1993 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1994 utf8_interpret(str
, misc
->invalid_char_substitute
);
1996 /* Get the first character to see if this is */
1997 /* an additive or restrictive operation. */
1998 /* """"""""""""""""""""""""""""""""""""""""" */
1999 if (sscanf(str
, "%c", &c
) == 0)
2008 *unparsed
= xstrdup(str
);
2018 *unparsed
= xstrdup(str
);
2028 *unparsed
= xstrdup(str
);
2037 *filter
= INCLUDE_FILTER
;
2043 *filter
= EXCLUDE_FILTER
;
2056 *filter
= INCLUDE_FILTER
;
2060 case 'a': /* Attribute. */
2067 *unparsed
= strprint(str
);
2072 *filter
= INCLUDE_FILTER
;
2077 /* Set ptr to the start of the interval list to parse. */
2078 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2081 if (*ptr
== '\0' && *default_alignment
== AL_NONE
)
2085 *default_alignment
= AL_LEFT
;
2088 *default_alignment
= AL_RIGHT
;
2091 *default_alignment
= AL_CENTERED
;
2094 *unparsed
= xstrdup(str
);
2098 /* Scan the comma separated ranges. */
2099 /* '\' can be used to escape a ','. */
2100 /* """""""""""""""""""""""""""""""" */
2104 char delim1
, delim2
= '\0';
2108 l_open_range
= r_open_range
= 0;
2109 first
= second
= -1;
2112 while (*ptr
&& *ptr
!= ',')
2119 else if (*ptr
== '\\' && *(ptr
+ 1) != '\0' && *(ptr
+ 1) == ',')
2121 else if (type
== ATTR
&& *ptr
== '\\' && *(ptr
+ 1) != '\0'
2122 && *(ptr
+ 1) == ':')
2124 else if (type
== ATTR
&& *ptr
&& *ptr
== ':')
2133 /* Forbid the trailing comma (ex: xxx,). */
2134 /* """"""""""""""""""""""""""""""""""""" */
2135 if (*ptr
== ',' && *(ptr
+ 1) == '\0')
2137 *unparsed
= strprint(ptr
);
2141 /* Forbid the empty patterns (ex: xxx,,yyy). */
2142 /* """"""""""""""""""""""""""""""""""""""""" */
2145 *unparsed
= strprint(ptr
);
2149 /* Mark the end of the interval found. */
2150 /* """"""""""""""""""""""""""""""""""" */
2154 delim1
= *(str
+ start
);
2161 delim2
= *(ptr
- 1);
2162 else if (ptr
> str
+ start
+ 2)
2163 delim2
= *(ptr
- 2);
2167 if (colon
== NULL
|| colon
== str
+ start
)
2169 *unparsed
= strprint(str
+ start
);
2173 delim2
= *(colon
- 1);
2176 /* Regex ranges are not yet supported in selectors. */
2177 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2178 if (!isdigit(delim1
) && delim1
!= '-' && is_range
)
2180 *unparsed
= strprint(str
+ start
);
2187 /* Check is we have found a well described regular expression. */
2188 /* If the segment to parse contains at least three characters */
2189 /* then delim1 and delim2 point to the first and last */
2190 /* delimiter of the regular expression. */
2195 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2196 if (ptr
> str
+ start
+ 2 && delim1
== delim2
&& isgraph(delim1
)
2197 && isgraph(delim2
) && !isdigit(delim1
) && !isdigit(delim2
))
2199 /* Process the regex. */
2200 /* """""""""""""""""" */
2204 parse_regex_selector_part(str
+ start
, inc_regex_list
);
2207 parse_regex_selector_part(str
+ start
, exc_regex_list
);
2210 parse_regex_selector_part(str
+ start
, al_regex_list
);
2213 parse_regex_selector_part(str
+ start
, ar_regex_list
);
2216 parse_regex_selector_part(str
+ start
, ac_regex_list
);
2225 /* """""""""""""""" */
2229 /* parse the attribute part (after the colon) */
2230 /* """""""""""""""""""""""""""""""""""""""""" */
2231 if (!parse_attr(colon
+ 1, attr
, term
->colors
))
2233 *unparsed
= strprint(str
+ start
);
2238 /* Parse the regex part (before the colon) and add it to the */
2239 /* list of elements of type attr_elem_t. */
2240 /* In each of these elements a list of regex for the same */
2241 /* attributes is updated. */
2242 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2243 if (*at_regex_list
== NULL
) /* The list doesn't already exists. */
2245 *at_regex_list
= ll_new();
2246 attr_elem_t
*elem
= xmalloc(sizeof(attr_elem_t
));
2249 parse_regex_selector_part(str
+ start
, &elem
->list
);
2250 ll_append(*at_regex_list
, elem
);
2257 /* Update the list of regex of the attribute attr. */
2258 /* """"""""""""""""""""""""""""""""""""""""""""""" */
2259 if ((node
= ll_find(*at_regex_list
, &e
, attr_elem_cmp
)) != NULL
)
2261 ((attr_elem_t
*)(node
->data
))->attr
= attr
;
2262 parse_regex_selector_part(str
+ start
,
2263 &((attr_elem_t
*)(node
->data
))->list
);
2267 attr_elem_t
*elem
= xmalloc(sizeof(attr_elem_t
));
2270 parse_regex_selector_part(str
+ start
, &elem
->list
);
2271 ll_append(*at_regex_list
, elem
);
2278 /* Adjust the start of the new interval to read in the */
2279 /* initial string. */
2280 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2288 /* We must parse 2 numbers separated by a dash. */
2289 /* """""""""""""""""""""""""""""""""""""""""""" */
2294 if (l_open_range
== 0 && r_open_range
== 0)
2296 rc
= sscanf(str
+ start
, "%ld-%ld%n", &first
, &second
, &pos
);
2300 if (rc
!= 2 || *(str
+ start
+ pos
) != '\0')
2302 *unparsed
= strprint(str
+ start
);
2308 if (*(str
+ start
+ pos
) != ':')
2310 if (rc
!= 2 || *(str
+ start
+ pos
) != '\0')
2311 *unparsed
= strprint(str
+ start
+ pos
);
2313 *unparsed
= strprint(str
+ start
);
2317 attr_str
= xstrdup(str
+ start
+ pos
+ 1);
2320 else if (l_open_range
== 1 && r_open_range
== 0)
2322 rc
= sscanf(str
+ start
, "-%ld%n", &second
, &pos
);
2326 if (rc
!= 1 || *(str
+ start
+ pos
) != '\0')
2328 *unparsed
= strprint(str
+ start
);
2334 if (*(str
+ start
+ pos
) != ':')
2336 if (rc
!= 1 || *(str
+ start
+ pos
) != '\0')
2337 *unparsed
= strprint(str
+ start
+ pos
);
2339 *unparsed
= strprint(str
+ start
);
2343 attr_str
= xstrdup(str
+ start
+ pos
+ 1);
2348 else if (l_open_range
== 0 && r_open_range
== 1)
2350 rc
= sscanf(str
+ start
, "%ld-%n", &first
, &pos
);
2354 if (rc
!= 1 || *(str
+ start
+ pos
) != '\0')
2356 *unparsed
= strprint(str
+ start
);
2362 if (*(str
+ start
+ pos
) != ':')
2364 if (rc
!= 1 || *(str
+ start
+ pos
) != '\0')
2365 *unparsed
= strprint(str
+ start
+ pos
);
2367 *unparsed
= strprint(str
+ start
);
2371 attr_str
= xstrdup(str
+ start
+ pos
+ 1);
2377 if (first
< 1 || second
< 1)
2379 /* Both interval boundaries must be strictly positive. */
2380 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2381 *unparsed
= strprint(str
+ start
);
2385 /* Ensure that the low bound of the interval is lower or equal */
2386 /* to the high one. Swap them if needed. */
2387 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2388 interval
= interval_new();
2399 interval
->low
= first
- 1;
2400 interval
->high
= second
- 1;
2404 /* We must parse a single number. */
2405 /* """""""""""""""""""""""""""""" */
2410 rc
= sscanf(str
+ start
, "%ld%n", &first
, &pos
);
2414 if (rc
!= 1 || *(str
+ start
+ pos
) != '\0')
2416 *unparsed
= strprint(str
+ start
);
2422 if (*(str
+ start
+ pos
) != ':')
2424 if (rc
!= 1 || *(str
+ start
+ pos
) != '\0')
2425 *unparsed
= strprint(str
+ start
+ pos
);
2427 *unparsed
= strprint(str
+ start
);
2431 attr_str
= xstrdup(str
+ start
+ pos
+ 1);
2434 interval
= interval_new();
2435 interval
->low
= interval
->high
= first
- 1;
2438 /* Adjust the start of the new interval to read in the */
2439 /* initial string. */
2440 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2443 /* Add the new interval to the correct list. */
2444 /* """"""""""""""""""""""""""""""""""""""""" */
2448 if (*inc_interval_list
== NULL
)
2449 *inc_interval_list
= ll_new();
2451 ll_append(*inc_interval_list
, interval
);
2455 if (*exc_interval_list
== NULL
)
2456 *exc_interval_list
= ll_new();
2458 ll_append(*exc_interval_list
, interval
);
2462 if (*al_interval_list
== NULL
)
2463 *al_interval_list
= ll_new();
2465 ll_append(*al_interval_list
, interval
);
2469 if (*ar_interval_list
== NULL
)
2470 *ar_interval_list
= ll_new();
2472 ll_append(*ar_interval_list
, interval
);
2476 if (*ac_interval_list
== NULL
)
2477 *ac_interval_list
= ll_new();
2479 ll_append(*ac_interval_list
, interval
);
2484 if (parse_attr(attr_str
, attr
, term
->colors
))
2487 attr_elem
= xmalloc(sizeof(attr_elem_t
));
2488 attr_elem
->attr
= attr
;
2490 if (*at_interval_list
== NULL
)
2492 *at_interval_list
= ll_new();
2493 attr_elem
->list
= ll_new();
2494 ll_append(attr_elem
->list
, interval
);
2495 ll_append(*at_interval_list
, attr_elem
);
2500 if ((node
= ll_find(*at_interval_list
, attr_elem
, attr_elem_cmp
))
2504 attr_elem
= (attr_elem_t
*)node
->data
;
2506 ll_append(attr_elem
->list
, interval
);
2510 attr_elem
->list
= ll_new();
2511 ll_append(attr_elem
->list
, interval
);
2512 ll_append(*at_interval_list
, attr_elem
);
2519 *unparsed
= strprint(str
+ start
);
2527 /* If we are here, then all the intervals have be successfully parsed */
2528 /* Ensure that the unparsed string is empty. */
2529 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2530 *unparsed
= xstrdup("");
2533 /* ===================================================================== */
2534 /* Parse a commas separated sequence of regular expression. */
2535 /* Uses to align to the left, right or center some words base on regular */
2538 /* str (in) sequence of regular expression. */
2539 /* al_regex_list (out) list of RE for left-aligned words. */
2540 /* ar_regex_list (out) list of RE for left-aligned words. */
2541 /* ac_regex_list (out) list of RE for centered words. */
2542 /* default_alignment (out) new default alignment. */
2543 /* misc (in) used by utf8_interpret. */
2544 /* ===================================================================== */
2546 parse_al_selectors(char *str
,
2548 ll_t
**al_regex_list
,
2549 ll_t
**ar_regex_list
,
2550 ll_t
**ac_regex_list
,
2551 alignment_t
*default_alignment
,
2555 size_t start
= 1; /* column string offset in the parsed string. */
2556 char *ptr
; /* pointer to the remaining string to parse. */
2559 /* Replace the UTF-8 string representation in the selector by */
2560 /* their binary values. */
2561 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2562 utf8_interpret(str
, misc
->invalid_char_substitute
);
2564 /* Get the first character to see if this is */
2565 /* an additive or restrictive operation. */
2566 /* """"""""""""""""""""""""""""""""""""""""" */
2567 if (sscanf(str
, "%c", &c
) == 0)
2570 type
= *default_alignment
;
2597 /* Set ptr to the start of the interval list to parse. */
2598 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2601 if (*ptr
== '\0' && *default_alignment
== AL_NONE
)
2605 *default_alignment
= AL_LEFT
;
2608 *default_alignment
= AL_RIGHT
;
2611 *default_alignment
= AL_CENTERED
;
2614 *unparsed
= xstrdup(str
);
2618 /* Scan the comma separated ranges. */
2619 /* '\' can be used to escape a ','. */
2620 /* """""""""""""""""""""""""""""""" */
2623 char delim1
, delim2
= '\0';
2627 while (*ptr
&& *ptr
!= ',')
2629 if (*ptr
== '\\' && *(ptr
+ 1) != '\0' && *(ptr
+ 1) == ',')
2635 /* Forbid the trailing comma (ex: xxx,). */
2636 /* """"""""""""""""""""""""""""""""""""" */
2637 if (*ptr
== ',' && (*(ptr
+ 1) == '\0'))
2639 *unparsed
= xstrdup(ptr
);
2643 /* Forbid the empty patterns (ex: xxx,,yyy). */
2644 /* """"""""""""""""""""""""""""""""""""""""" */
2647 *unparsed
= xstrdup(ptr
);
2651 /* Mark the end of the interval found. */
2652 /* """"""""""""""""""""""""""""""""""" */
2656 /* If the regex contains at least three characters then delim1 */
2657 /* and delim2 point to the first and last delimiter of the */
2658 /* regular expression. E.g. /abc/ */
2662 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2663 delim1
= *(str
+ start
);
2665 delim2
= *(ptr
- 1);
2666 else if (ptr
> str
+ start
+ 2)
2667 delim2
= *(ptr
- 2);
2669 /* Forbid the empty patterns (ex: xxx,,yyy) and check is we have */
2670 /* found a well described regular expression. */
2671 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2672 if (ptr
> str
+ start
+ 2 && delim1
== delim2
&& isgraph(delim1
)
2673 && isgraph(delim2
) && !isdigit(delim1
) && !isdigit(delim2
))
2675 /* Process the regex. */
2676 /* """""""""""""""""" */
2680 parse_regex_selector_part(str
+ start
, al_regex_list
);
2683 parse_regex_selector_part(str
+ start
, ar_regex_list
);
2686 parse_regex_selector_part(str
+ start
, ac_regex_list
);
2690 /* Adjust the start of the new interval to read in the */
2691 /* initial string. */
2692 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2699 *unparsed
= xstrdup(ptr
- 1);
2704 /* If we are here, then all the intervals have be successfully parsed */
2705 /* Ensure that the unparsed string is empty. */
2706 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2707 *unparsed
= xstrdup("");
2710 /* ========================================================= */
2711 /* Parse the sed like string passed as argument to -S/-I/-E. */
2712 /* This updates the sed parameter. */
2713 /* Return 1 on success and 0 on error. */
2714 /* ========================================================= */
2716 parse_sed_like_string(sed_t
*sed
)
2719 char *first_sep_pos
;
2723 unsigned char icase
;
2726 if (strlen(sed
->pattern
) < 4)
2729 /* Get the separator (the 1st character). */
2730 /* """""""""""""""""""""""""""""""""""""" */
2731 buf
= xstrdup(sed
->pattern
);
2734 /* Space like separators are not permitted. */
2735 /* """""""""""""""""""""""""""""""""""""""" */
2739 /* Get the extended regular expression. */
2740 /* """""""""""""""""""""""""""""""""""" */
2741 if ((first_sep_pos
= strchr(buf
+ 1, sep
)) == NULL
)
2744 *first_sep_pos
= '\0';
2746 /* Get the substitution string. */
2747 /* """""""""""""""""""""""""""" */
2748 if ((last_sep_pos
= strchr(first_sep_pos
+ 1, sep
)) == NULL
)
2751 *last_sep_pos
= '\0';
2753 sed
->substitution
= xstrdup(first_sep_pos
+ 1);
2755 /* Get the global indicator (trailing g) */
2756 /* and the visual indicator (trailing v) */
2757 /* and the stop indicator (trailing s). */
2758 /* """""""""""""""""""""""""""""""""""""" */
2759 sed
->global
= sed
->visual
= icase
= (unsigned char)0;
2762 while ((c
= *(last_sep_pos
+ index
)) != '\0')
2765 sed
->global
= (unsigned char)1;
2767 sed
->visual
= (unsigned char)1;
2769 sed
->stop
= (unsigned char)1;
2771 icase
= (unsigned char)1;
2778 /* Empty regular expression ? */
2779 /* """""""""""""""""""""""""" */
2780 if (*(buf
+ 1) == '\0')
2783 /* Compile the regular expression and abort on failure. */
2784 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2785 if (regcomp(&(sed
->re
),
2787 !icase
? REG_EXTENDED
: (REG_EXTENDED
| REG_ICASE
))
2801 /* ===================================================================== */
2802 /* Utility function used by replace to expand the replacement string. */
2804 /* orig: matching part of the original string to be replaced. */
2805 /* repl: string containing the replacement directives */
2806 /* subs_a: array of ranges containing the start and end offset */
2807 /* of the remembered parts of the strings referenced */
2808 /* by the sequence \n where n is in [1,10]. */
2809 /* match_start/end: offset in orig for the current matched string */
2810 /* subs_nb: number of elements containing significant values in */
2811 /* the array described above. */
2812 /* error: set to 0 if no error in replacement string and to 1 */
2814 /* match: current match number in the original string. */
2817 /* The modified string according to the content of repl. */
2818 /* ===================================================================== */
2820 build_repl_string(char *orig
,
2829 size_t allocated
= 16;
2831 char *str
= xmalloc(allocated
);
2833 long offset
= match
* subs_nb
; /* offset of the 1st sub *
2834 | corresponding to the match. */
2841 while (!*error
&& *repl
)
2848 if (allocated
== rsize
)
2849 str
= xrealloc(str
, allocated
+= 16);
2870 if ((*repl
) - '0' <= subs_nb
)
2872 long index
= (*repl
) - '0' - 1 + offset
;
2873 long delta
= subs_a
[index
].end
- subs_a
[index
].start
;
2875 if (allocated
<= rsize
+ delta
)
2876 str
= xrealloc(str
, allocated
+= (delta
+ 16));
2878 memcpy(str
+ rsize
, orig
+ subs_a
[index
].start
, delta
);
2892 if (allocated
== rsize
)
2893 str
= xrealloc(str
, allocated
+= 16);
2903 long delta
= match_end
- match_start
;
2905 if (allocated
<= rsize
+ delta
)
2906 str
= xrealloc(str
, allocated
+= (delta
+ 16));
2908 memcpy(str
+ rsize
, orig
+ match_start
, delta
);
2917 if (allocated
== rsize
)
2918 str
= xrealloc(str
, allocated
+= 16);
2929 long delta
= match_end
- match_start
;
2931 if (allocated
<= rsize
+ delta
)
2932 str
= xrealloc(str
, allocated
+= (delta
+ 16));
2934 memcpy(str
+ rsize
, orig
+ match_start
, delta
);
2941 /* No break here, '&' must be treated as a normal */
2942 /* character when protected. */
2943 /* '''''''''''''''''''''''''''''''''''''''''''''' */
2948 if (allocated
== rsize
)
2949 str
= xrealloc(str
, allocated
+= 16);
2964 /* ====================================================================== */
2965 /* Replace the part of a string matched by an extender regular expression */
2966 /* by the substitution string. */
2967 /* The regex used must have been previously compiled. */
2969 /* orig: original string */
2970 /* sed: composite variable containing the regular expression, a */
2971 /* substitution string and various other information. */
2972 /* output: destination buffer. */
2974 /* return 1 if the replacement has been successful else 0. */
2977 /* uses the global variable word_buffer. */
2978 /* ====================================================================== */
2980 replace(char *orig
, sed_t
*sed
)
2982 size_t match_nb
= 0; /* number of matches in the original string. */
2983 int sub_nb
= 0; /* number of remembered matches in the *
2984 | original sting. */
2985 size_t target_len
= 0; /* length of the resulting string. */
2986 size_t subs_max
= 0;
2991 range_t
*matches_a
= xmalloc(strlen(orig
) * sizeof(range_t
));
2992 range_t
*subs_a
= xmalloc(10 * sizeof(range_t
));
2994 const char *p
= orig
; /* points to the end of the previous match. */
2995 regmatch_t m
[10]; /* array containing the start/end offsets *
2996 | of the matches found. */
3001 size_t match
; /* current match index. */
3002 size_t index
= 0; /* current char index in the original string. */
3003 int nomatch
= 0; /* equals to 1 when there is no more matching. */
3004 char *exp_repl
; /* expanded replacement string. */
3009 nomatch
= regexec(&sed
->re
, p
, 10, m
, 0);
3015 for (index
= 0; index
< matches_a
[0].start
; index
++)
3016 word_buffer
[target_len
++] = orig
[index
];
3018 for (match
= 0; match
< match_nb
; match
++)
3024 exp_repl
= build_repl_string(orig
,
3026 matches_a
[match
].start
,
3027 matches_a
[match
].end
,
3036 "Invalid matching group reference "
3037 "in the replacement string \"%s\".\n",
3042 len
= strlen(exp_repl
);
3044 my_strcpy(word_buffer
+ target_len
, exp_repl
);
3048 index
+= matches_a
[match
].end
- matches_a
[match
].start
;
3050 if (match
< match_nb
- 1 && sed
->global
)
3051 end
= matches_a
[match
+ 1].start
;
3056 word_buffer
[target_len
++] = orig
[index
++];
3058 word_buffer
[target_len
] = '\0';
3066 my_strcpy(word_buffer
, orig
);
3078 for (i
= 0; i
< 10; i
++)
3083 if (m
[i
].rm_so
== -1)
3086 start
= m
[i
].rm_so
+ (p
- orig
);
3087 finish
= m
[i
].rm_eo
+ (p
- orig
);
3091 matches_a
[match_nb
].start
= start
;
3092 matches_a
[match_nb
].end
= finish
;
3094 if (match_nb
> utf8_strlen(orig
))
3099 subs_a
[sub_nb
].start
= start
;
3100 subs_a
[sub_nb
].end
= finish
;
3108 p
++; /* Empty match. */
3117 /* ============================================================ */
3118 /* Remove all ANSI color codes from s and puts the result in d. */
3119 /* Memory space for d must have been allocated before. */
3120 /* ============================================================ */
3122 strip_ansi_color(char *s
, toggle_t
*toggles
, misc_t
*misc
)
3125 long len
= strlen(s
);
3129 /* Remove a sequence of \x1b[...m from s. */
3130 /* """""""""""""""""""""""""""""""""""""" */
3131 if ((*s
== 0x1b) && (*(s
+ 1) == '['))
3133 while ((*s
!= '\0') && (*s
++ != 'm'))
3136 /* Convert a single \x1b in the invalid substitute character. */
3137 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3138 else if (*s
== 0x1b)
3140 if (toggles
->blank_nonprintable
&& len
> 1)
3141 *s
++ = ' '; /* Non printable character -> ' '. */
3143 *s
++ = misc
->invalid_char_substitute
;
3146 /* No ESC char, we can move on. */
3147 /* """""""""""""""""""""""""""" */
3155 /* ================================================================== */
3156 /* Callback function used by tst_traverse to insert the index */
3157 /* of a matching word index in the sorted list of the already matched */
3159 /* Always succeeds and returns 1. */
3160 /* ================================================================== */
3162 set_matching_flag(void *elem
)
3164 ll_t
*list
= (ll_t
*)elem
;
3166 ll_node_t
*node
= list
->head
;
3172 pos
= *(size_t *)(node
->data
);
3173 if (word_a
[pos
].is_selectable
)
3174 word_a
[pos
].is_matching
= 1;
3176 insert_sorted_index(&matching_words_a
,
3177 &matching_words_a_size
,
3186 /* ======================================================================= */
3187 /* Callback function used by tst_traverse applied to tst_word so this */
3188 /* function is applied on each node of this tst. */
3190 /* Each node of this tst contains a linked list storing the indexes of */
3191 /* the words in the input flow. */
3192 /* Each position in this list is used to: */
3193 /* - mark the word at that position as matching, */
3194 /* - add this position in the sorted array matching_words_a. */
3195 /* Always succeeds and returns 1. */
3196 /* ======================================================================= */
3200 /* The data attached to the string in the tst is a linked list of */
3201 /* position of the string in the input flow, This list is naturally */
3203 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3204 ll_t
*list
= (ll_t
*)elem
;
3206 ll_node_t
*node
= list
->head
;
3212 pos
= *(long *)(node
->data
);
3214 word_a
[pos
].is_matching
= 1;
3215 insert_sorted_index(&matching_words_a
,
3216 &matching_words_a_size
,
3225 /* **************** */
3226 /* Input functions. */
3227 /* **************** */
3229 /* ===================================================================== */
3230 /* Non delay reading of a scancode. */
3231 /* Update a scancodes buffer and return its length in bytes. */
3232 /* The length of the scancode cannot reach max which must be lower of */
3233 /* equal than 63 which is the size minus one of the buffer array defined */
3235 /* ===================================================================== */
3237 get_scancode(unsigned char *s
, size_t max
)
3241 struct termios original_ts
, nowait_ts
;
3243 /* Wait until all data has been transmitted to stdin. */
3244 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
3247 if ((c
= my_fgetc(stdin
)) == EOF
)
3250 /* Initialize the string with the first byte. */
3251 /* """""""""""""""""""""""""""""""""""""""""" */
3252 memset(s
, '\0', max
);
3255 /* 0x1b (ESC) has been found, proceed to check if additional codes */
3256 /* are available. */
3257 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3258 if (c
== 0x1b || c
> 0x80)
3260 /* Save the terminal parameters and configure getchar() */
3261 /* to return immediately. */
3262 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
3263 tcgetattr(0, &original_ts
);
3264 nowait_ts
= original_ts
;
3265 nowait_ts
.c_lflag
&= ~ISIG
;
3266 nowait_ts
.c_cc
[VMIN
] = 0;
3267 nowait_ts
.c_cc
[VTIME
] = 0;
3269 /* tcsetattr_safe cannot fail here. */
3270 /* """""""""""""""""""""""""""""""" */
3271 (void)tcsetattr_safe(0, TCSADRAIN
, &nowait_ts
);
3273 /* Check if additional code is available after 0x1b. */
3274 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
3275 if ((c
= my_fgetc(stdin
)) != EOF
)
3280 while (i
< max
&& (c
= my_fgetc(stdin
)) != EOF
)
3285 /* There isn't a new code, this mean 0x1b came from ESC key. */
3286 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3289 /* Restore the save terminal parameters. */
3290 /* tcsetattr_safe cannot fail here. */
3291 /* """"""""""""""""""""""""""""""""""""" */
3292 (void)tcsetattr_safe(0, TCSADRAIN
, &original_ts
);
3294 /* Ignore EOF when a scancode contains an escape sequence. */
3295 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3302 /* ============================================================ */
3303 /* Helper function to compare to delimiters for use by ll_find. */
3304 /* ============================================================ */
3306 buffer_cmp(const void *a
, const void *b
)
3308 return strcmp((const char *)a
, (const char *)b
);
3311 /* ===================================================================== */
3312 /* Get bytes from stdin. If the first byte is the leading character of a */
3313 /* UTF-8 glyph, the following ones are also read. */
3314 /* The utf8_get_length function is used to get the number of bytes of */
3315 /* the character. */
3316 /* ===================================================================== */
3318 get_bytes(FILE *input
,
3320 ll_t
*zapped_glyphs_list
,
3321 langinfo_t
*langinfo
,
3332 /* Read the first byte. */
3333 /* """""""""""""""""""" */
3334 byte
= my_fgetc(input
);
3339 utf8_buffer
[last
++] = byte
;
3341 /* Check if we need to read more bytes to form a sequence */
3342 /* and put the number of bytes of the sequence in last. */
3343 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3344 if (langinfo
->utf8
&& ((n
= utf8_get_length(byte
)) > 1))
3346 while (last
< n
&& (byte
= my_fgetc(input
)) != EOF
3347 && (byte
& 0xc0) == 0x80)
3348 utf8_buffer
[last
++] = byte
;
3354 utf8_buffer
[last
] = '\0';
3356 /* Replace an invalid UTF-8 byte sequence by a single dot. */
3357 /* In this case the original sequence is lost (unsupported */
3359 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3360 if (langinfo
->utf8
&& utf8_validate(utf8_buffer
) != NULL
)
3362 byte
= utf8_buffer
[0] = misc
->invalid_char_substitute
;
3363 utf8_buffer
[1] = '\0';
3365 } while (ll_find(zapped_glyphs_list
, utf8_buffer
, buffer_cmp
) != NULL
);
3370 /* =======================================================================*/
3371 /* Expand the string str by replacing all its embedded special characters */
3372 /* by their corresponding escape sequence. */
3374 /* dest must be long enough to contain the expanded string. */
3376 /* Replace also UTF-8 glyphs by the substitution character if the */
3377 /* current locale if not UTF-8. */
3379 /* Return the number of resulting glyphs. */
3380 /* ====================================================================== */
3384 langinfo_t
*langinfo
,
3394 while ((c
= *(src
++)))
3396 /* UTF-8 codepoints may take more than on character. */
3397 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
3398 if ((n
= utf8_get_length(c
)) > 1)
3403 /* If the locale is UTF-8 aware, copy src into ptr. */
3404 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3409 } while (--n
&& (c
= *(src
++)));
3412 /* If not, ignore the bytes composing the UTF-8 glyph and replace */
3413 /* them with the substitution character. */
3414 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3417 /* Skip this byte. */
3418 /* ''''''''''''''' */
3419 } while (--n
&& ('\0' != *(src
++)));
3421 *(ptr
++) = misc
->invalid_char_substitute
;
3426 /* This is not a multibyte UTF-8 glyph. */
3427 /* """""""""""""""""""""""""""""""""""" */
3478 if (toggles
->blank_nonprintable
)
3479 *(ptr
++) = ' '; /* Non printable character -> ' '. */
3482 *(ptr
++) = misc
->invalid_char_substitute
;
3490 /* If the word contains only spaces, replace them */
3491 /* by underscores so that it can be seen. */
3492 /* """""""""""""""""""""""""""""""""""""""""""""" */
3493 if (toggles
->show_blank_words
&& all_spaces
)
3494 memset(dest
, misc
->blank_char_substitute
, len
);
3496 *ptr
= '\0'; /* Ensure that dest has a nul terminator. */
3501 /* ===================================================================== */
3502 /* get_word(input): return a char pointer to the next word (as a string) */
3503 /* Accept: a FILE * for the input stream. */
3504 /* Return: a char * */
3505 /* On Success: the return value will point to a nul-terminated */
3507 /* On Failure: the return value will be set to NULL. */
3508 /* ===================================================================== */
3510 get_word(FILE *input
,
3511 ll_t
*word_delims_list
,
3512 ll_t
*line_delims_list
,
3513 ll_t
*zapped_glyphs_list
,
3515 unsigned char *is_last
,
3517 langinfo_t
*langinfo
,
3524 long utf8_count
= 0; /* count chars used in current allocation. */
3525 long wordsize
; /* size of current allocation in chars. */
3526 unsigned char is_dquote
; /* double quote presence indicator. */
3527 unsigned char is_squote
; /* single quote presence indicator. */
3528 int is_special
; /* a character is special after a \ */
3530 /* Skip leading delimiters. */
3531 /* """""""""""""""""""""""" */
3533 byte
= get_bytes(input
, utf8_buffer
, zapped_glyphs_list
, langinfo
, misc
);
3535 && ll_find(word_delims_list
, utf8_buffer
, buffer_cmp
) != NULL
);
3540 /* Allocate initial word storage space. */
3541 /* """""""""""""""""""""""""""""""""""" */
3542 wordsize
= CHARSCHUNK
;
3543 temp
= xmalloc(wordsize
);
3545 /* Start stashing bytes. Stop when we meet a non delimiter or EOF. */
3546 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3556 if (utf8_count
>= limits
->word_length
)
3559 "The length of a word has reached the limit of "
3560 "%ld characters.\n",
3561 limits
->word_length
);
3566 if (byte
== '\\' && !is_special
)
3572 /* Parse special characters. */
3573 /* """"""""""""""""""""""""" */
3578 utf8_buffer
[0] = byte
= '\a';
3579 utf8_buffer
[1] = '\0';
3583 utf8_buffer
[0] = byte
= '\b';
3584 utf8_buffer
[1] = '\0';
3588 utf8_buffer
[0] = byte
= '\t';
3589 utf8_buffer
[1] = '\0';
3593 utf8_buffer
[0] = byte
= '\n';
3594 utf8_buffer
[1] = '\0';
3598 utf8_buffer
[0] = byte
= '\v';
3599 utf8_buffer
[1] = '\0';
3603 utf8_buffer
[0] = byte
= '\f';
3604 utf8_buffer
[1] = '\0';
3608 utf8_buffer
[0] = byte
= '\r';
3609 utf8_buffer
[1] = '\0';
3613 utf8_buffer
[0] = '\\';
3614 utf8_buffer
[1] = 'u';
3615 utf8_buffer
[2] = '\0';
3619 utf8_buffer
[0] = '\\';
3620 utf8_buffer
[1] = 'U';
3621 utf8_buffer
[2] = '\0';
3625 utf8_buffer
[0] = byte
= '\\';
3626 utf8_buffer
[1] = '\0';
3631 if (!misc
->ignore_quotes
)
3633 /* Manage double quotes. */
3634 /* """"""""""""""""""""" */
3635 if (byte
== '"' && !is_squote
)
3638 /* Manage single quotes. */
3639 /* """"""""""""""""""""" */
3640 if (byte
== '\'' && !is_dquote
)
3645 /* Only consider delimiters when outside quotations. */
3646 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
3647 if ((!is_dquote
&& !is_squote
)
3648 && ll_find(word_delims_list
, utf8_buffer
, buffer_cmp
) != NULL
)
3651 /* We no dot count the significant quotes. */
3652 /* """"""""""""""""""""""""""""""""""""""" */
3653 if (!misc
->ignore_quotes
&& !is_special
3654 && ((byte
== '"' && !is_squote
) || (byte
== '\'' && !is_dquote
)))
3660 /* Feed temp with the content of utf8_buffer. */
3661 /* """""""""""""""""""""""""""""""""""""""""" */
3662 while (utf8_buffer
[i
] != '\0')
3664 if (utf8_count
>= wordsize
- 1)
3665 temp
= xrealloc(temp
,
3666 wordsize
+= (utf8_count
/ CHARSCHUNK
+ 1) * CHARSCHUNK
);
3668 *(temp
+ utf8_count
++) = utf8_buffer
[i
];
3675 byte
= get_bytes(input
, utf8_buffer
, zapped_glyphs_list
, langinfo
, misc
);
3678 /* Nul-terminate the word to make it a string. */
3679 /* """"""""""""""""""""""""""""""""""""""""""" */
3680 *(temp
+ utf8_count
) = '\0';
3682 /* Replace the UTF-8 ASCII representations in the word just */
3683 /* read by their binary values. */
3684 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3685 utf8_interpret(temp
, misc
->invalid_char_substitute
);
3687 /* Skip all field delimiters before a record delimiter. */
3688 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
3689 if (ll_find(line_delims_list
, utf8_buffer
, buffer_cmp
) == NULL
)
3691 byte
= get_bytes(input
, utf8_buffer
, zapped_glyphs_list
, langinfo
, misc
);
3694 && ll_find(word_delims_list
, utf8_buffer
, buffer_cmp
) != NULL
3695 && ll_find(line_delims_list
, utf8_buffer
, buffer_cmp
) == NULL
)
3696 byte
= get_bytes(input
, utf8_buffer
, zapped_glyphs_list
, langinfo
, misc
);
3700 if (langinfo
->utf8
&& utf8_get_length(utf8_buffer
[0]) > 1)
3704 pos
= strlen(utf8_buffer
);
3706 my_ungetc(utf8_buffer
[--pos
], input
);
3709 my_ungetc(byte
, input
);
3713 /* Mark it as the last word of a record if its sequence matches a */
3714 /* record delimiter. */
3715 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3717 || ((win
->col_mode
|| win
->line_mode
|| win
->tab_mode
)
3718 && ll_find(line_delims_list
, utf8_buffer
, buffer_cmp
) != NULL
))
3723 /* Remove the ANSI color escape sequences from the word. */
3724 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3725 strip_ansi_color(temp
, toggles
, misc
);
3730 /* ================================================================ */
3731 /* Convert the 8 first colors from setf/setaf coding to setaf/setf. */
3732 /* ================================================================ */
3734 color_transcode(short color
)
3751 /* ========================================================== */
3752 /* Set a foreground color according to terminal capabilities. */
3753 /* ========================================================== */
3755 set_foreground_color(term_t
*term
, short color
)
3757 if (term
->color_method
== CLASSIC
)
3760 (void)tputs(TPARM2(set_foreground
, color
), 1, outch
);
3761 if (term
->has_setaf
)
3762 (void)tputs(TPARM2(set_a_foreground
, color_transcode(color
)), 1, outch
);
3765 else if (term
->color_method
== ANSI
)
3767 if (term
->has_setaf
)
3768 (void)tputs(TPARM2(set_a_foreground
, color
), 1, outch
);
3770 (void)tputs(TPARM2(set_foreground
, color_transcode(color
)), 1, outch
);
3774 /* ========================================================== */
3775 /* Set a background color according to terminal capabilities. */
3776 /* ========================================================== */
3778 set_background_color(term_t
*term
, short color
)
3780 if (term
->color_method
== CLASSIC
)
3783 (void)tputs(TPARM2(set_background
, color
), 1, outch
);
3784 if (term
->has_setab
)
3785 (void)tputs(TPARM2(set_a_background
, color_transcode(color
)), 1, outch
);
3788 else if (term
->color_method
== ANSI
)
3790 if (term
->has_setab
)
3791 (void)tputs(TPARM2(set_a_background
, color
), 1, outch
);
3793 (void)tputs(TPARM2(set_background
, color_transcode(color
)), 1, outch
);
3797 /* ======================================================= */
3798 /* Put a scrolling symbol at the first column of the line. */
3799 /* ======================================================= */
3801 left_margin_putp(char *s
, term_t
*term
, win_t
*win
)
3803 apply_attr(term
, win
->shift_attr
);
3805 /* We won't print this symbol when not in column mode. */
3806 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
3808 fputs_safe(s
, stdout
);
3810 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
3813 /* ====================================================== */
3814 /* Put a scrolling symbol at the last column of the line. */
3815 /* ====================================================== */
3817 right_margin_putp(char *s1
,
3819 langinfo_t
*langinfo
,
3825 apply_attr(term
, win
->bar_attr
);
3828 (void)tputs(TPARM2(column_address
, offset
+ win
->max_width
+ 1), 1, outch
);
3829 else if (term
->has_cursor_address
)
3830 (void)tputs(TPARM3(cursor_address
,
3831 term
->curs_line
+ line
- 2,
3832 offset
+ win
->max_width
+ 1),
3835 else if (term
->has_parm_right_cursor
)
3837 fputc_safe('\r', stdout
);
3838 (void)tputs(TPARM2(parm_right_cursor
, offset
+ win
->max_width
+ 1),
3846 fputc_safe('\r', stdout
);
3847 for (i
= 0; i
< offset
+ win
->max_width
+ 1; i
++)
3848 (void)tputs(TPARM1(cursor_right
), 1, outch
);
3852 fputs_safe(s1
, stdout
);
3854 fputs_safe(s2
, stdout
);
3856 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
3859 /* ==========================================================*/
3860 /* Displays an horizontal scroll bar below the window. */
3861 /* The bar if only displayed in line or column mode when the */
3862 /* line containing the cursor is truncated. */
3864 /* mark is the offset of the cursor in the bar. */
3865 /* ==========================================================*/
3867 disp_hbar(win_t
*win
, term_t
*term
, langinfo_t
*langinfo
, int pos1
, int pos2
)
3871 apply_attr(term
, win
->bar_attr
);
3873 (void)tputs(TPARM1(clr_eol
), 1, outch
);
3875 /* Draw the left symbol arrow. */
3876 /* """""""""""""""""""""""""""" */
3880 fputs_safe(hbar_begin
, stdout
);
3882 fputs_safe(hbar_left
, stdout
);
3887 fputc_safe('<', stdout
);
3889 fputc_safe('\\', stdout
);
3892 /* Draw the line in the horizontal bar. */
3893 /* """""""""""""""""""""""""""""""""""" */
3894 if (term
->has_rep
&& !langinfo
->utf8
)
3895 (void)tputs(TPARM3(repeat_char
, '-', term
->ncolumns
- 2), 1, outch
);
3905 for (i
= 0; i
< term
->ncolumns
- 3; i
++)
3906 fputs_safe(s
, stdout
);
3909 /* Draw the cursor. */
3910 /* """""""""""""""" */
3920 (void)tputs(TPARM2(column_address
, pos1
+ 1), 1, outch
);
3921 for (i
= pos1
+ 1; i
<= pos2
+ 1; i
++)
3922 fputs_safe(s
, stdout
);
3924 (void)tputs(TPARM2(column_address
, term
->ncolumns
- 2), 1, outch
);
3926 else if (term
->has_parm_right_cursor
)
3935 fputs_safe("\r", stdout
);
3936 (void)tputs(TPARM2(parm_right_cursor
, pos1
+ 1), 1, outch
);
3938 for (i
= pos1
; i
<= pos2
; i
++)
3939 fputs_safe(s
, stdout
);
3941 fputs_safe("\r", stdout
);
3942 (void)tputs(TPARM2(parm_right_cursor
, term
->ncolumns
- 2), 1, outch
);
3953 fputs_safe("\r", stdout
);
3954 (void)tputs(TPARM2(cursor_right
, 1), 1, outch
);
3955 for (i
= 0; i
< pos1
; i
++)
3956 (void)tputs(TPARM1(cursor_right
), 1, outch
);
3958 for (i
= pos1
+ 2; i
<= pos2
+ 1; i
++)
3959 fputs_safe(s
, stdout
);
3961 for (i
= pos1
; i
< term
->ncolumns
- 3 - pos2
- 2; i
++)
3962 (void)tputs(TPARM1(cursor_right
), 1, outch
);
3965 /* Draw the right symbol arrow. */
3966 /* """""""""""""""""""""""""""" */
3969 if (pos2
== term
->ncolumns
- 4)
3970 fputs_safe(hbar_end
, stdout
);
3972 fputs_safe(hbar_right
, stdout
);
3976 if (pos2
== term
->ncolumns
- 4)
3977 fputc_safe('>', stdout
);
3979 fputc_safe('/', stdout
);
3982 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
3985 /* *************** */
3986 /* Core functions. */
3987 /* *************** */
3989 /* ============================================================== */
3990 /* Split the lines of the message given to -m to a linked list of */
3992 /* Also fill the maximum screen width and the maximum number */
3993 /* of bytes of the longest line. */
3994 /* ============================================================== */
3996 get_message_lines(char *message
,
3997 ll_t
*message_lines_list
,
3998 long *message_max_width
,
3999 long *message_max_len
)
4007 *message_max_width
= 0;
4008 *message_max_len
= 0;
4011 /* For each line terminated with a EOL character. */
4012 /* """""""""""""""""""""""""""""""""""""""""""""" */
4013 while (*ptr
!= '\0' && (cr_ptr
= strchr(ptr
, '\n')) != NULL
)
4017 str
= xmalloc(cr_ptr
- ptr
+ 1);
4018 str
[cr_ptr
- ptr
] = '\0';
4019 memcpy(str
, ptr
, cr_ptr
- ptr
);
4024 ll_append(message_lines_list
, str
);
4026 /* If needed, update the message maximum width. */
4027 /* """""""""""""""""""""""""""""""""""""""""""" */
4028 n
= wcswidth((w
= utf8_strtowcs(str
)), utf8_strlen(str
));
4031 if (n
> *message_max_width
)
4032 *message_max_width
= n
;
4034 /* If needed, update the message maximum number */
4035 /* of bytes used by the longest line. */
4036 /* """""""""""""""""""""""""""""""""""""""""""" */
4037 if ((n
= (long)strlen(str
)) > *message_max_len
)
4038 *message_max_len
= n
;
4043 /* For the last line. */
4044 /* """""""""""""""""" */
4047 ll_append(message_lines_list
, xstrdup(ptr
));
4049 n
= wcswidth((w
= utf8_strtowcs(ptr
)), utf8_strlen(ptr
));
4052 if (n
> *message_max_width
)
4053 *message_max_width
= n
;
4055 /* If needed, update the message maximum number */
4056 /* of bytes used by the longest line. */
4057 /* """""""""""""""""""""""""""""""""""""""""""" */
4058 if ((n
= (long)strlen(ptr
)) > *message_max_len
)
4059 *message_max_len
= n
;
4062 ll_append(message_lines_list
, xstrdup(""));
4065 /* =================================================================== */
4066 /* Set the new start and the new end of the window structure according */
4067 /* to the current cursor position. */
4068 /* =================================================================== */
4070 set_win_start_end(win_t
*win
, long current
, long last
)
4072 long cur_line
, end_line
;
4074 cur_line
= line_nb_of_word_a
[current
];
4075 if (cur_line
== last
)
4076 win
->end
= count
- 1;
4079 /* In help mode we must not modify the windows start/end position as */
4080 /* It must be redrawn exactly as it was before. */
4081 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4084 if (cur_line
+ win
->max_lines
/ 2 + 1 <= last
)
4085 win
->end
= first_word_in_line_a
[cur_line
+ win
->max_lines
/ 2 + 1] - 1;
4087 win
->end
= first_word_in_line_a
[last
];
4090 end_line
= line_nb_of_word_a
[win
->end
];
4092 if (end_line
< win
->max_lines
)
4095 win
->start
= first_word_in_line_a
[end_line
- win
->max_lines
+ 1];
4098 /* ======================================================================== */
4099 /* Set the metadata associated with a word, its starting and ending */
4100 /* position, the line in which it is put and so on. */
4101 /* Set win.start win.end and the starting and ending position of each word. */
4102 /* This function is only called initially, when resizing the terminal and */
4103 /* potentially when the search function is used. */
4104 /* ======================================================================== */
4106 build_metadata(term_t
*term
, long count
, win_t
*win
)
4112 long word_width
; /* Number of screen positions taken by the word. */
4113 long tab_count
; /* Current number of words in the line, used in *
4117 line_nb_of_word_a
[0] = 0;
4118 first_word_in_line_a
[0] = 0;
4120 /* In column mode we need to calculate win->max_width, first initialize */
4121 /* it to 0 and increment it later in the loop. */
4122 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4126 win
->real_max_width
= 0;
4132 /* Determine the number of screen positions taken by the word. */
4133 /* Note: mbstowcs will always succeed here as word_a[i].str */
4134 /* has already been utf8_validated/repaired. */
4135 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4136 word_len
= mbstowcs(NULL
, word_a
[i
].str
, 0);
4137 word_width
= wcswidth((w
= utf8_strtowcs(word_a
[i
].str
)), word_len
);
4139 /* Manage the case where the word is larger than the terminal width. */
4140 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4141 if (word_width
>= term
->ncolumns
- 2)
4143 /* Shorten the word until it fits. */
4144 /* """"""""""""""""""""""""""""""" */
4147 word_width
= wcswidth(w
, word_len
--);
4148 } while (word_len
> 0 && word_width
>= term
->ncolumns
- 2);
4152 /* Look if there is enough remaining place on the line when not in */
4153 /* column mode. Force a break if the 'is_last' flag is set in all */
4154 /* modes or if we hit the max number of allowed columns in tab mode. */
4155 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4156 if ((!win
->col_mode
&& !win
->line_mode
4157 && (len
+ word_width
+ 1) >= term
->ncolumns
- 1)
4158 || ((win
->col_mode
|| win
->line_mode
|| win
->tab_mode
) && i
> 0
4159 && word_a
[i
- 1].is_last
)
4160 || (win
->tab_mode
&& win
->max_cols
> 0 && tab_count
>= win
->max_cols
))
4163 /* We must build another line. */
4164 /* """"""""""""""""""""""""""" */
4165 line_nb_of_word_a
[i
] = ++last
;
4166 first_word_in_line_a
[last
] = i
;
4168 word_a
[i
].start
= 0;
4170 len
= word_width
+ 1; /* Resets the current line length. */
4171 tab_count
= 1; /* Resets the current number of words *
4173 word_a
[i
].end
= word_width
- 1;
4174 word_a
[i
].mb
= word_len
;
4178 word_a
[i
].start
= len
;
4179 word_a
[i
].end
= word_a
[i
].start
+ word_width
- 1;
4180 word_a
[i
].mb
= word_len
;
4181 line_nb_of_word_a
[i
] = last
;
4183 len
+= word_width
+ 1; /* Increase line length. */
4184 tab_count
++; /* We've seen another word in the line/ */
4187 /* Update win->(real_)max_width if necessary. */
4188 /* """""""""""""""""""""""""""""""""""""""""" */
4189 if (len
> win
->max_width
)
4191 /* Update the effective line width. */
4192 /* '''''''''''''''''''''''''''''''' */
4193 if (len
> term
->ncolumns
)
4195 win
->max_width
= term
->ncolumns
- 2;
4198 win
->max_width
= len
;
4201 /* Update the real line width. */
4202 /* ''''''''''''''''''''''''''' */
4203 if (len
> win
->real_max_width
)
4204 win
->real_max_width
= len
;
4209 /* Set the left margin when in centered mode. */
4210 /* """""""""""""""""""""""""""""""""""""""""" */
4211 if (!win
->center
|| win
->max_width
> term
->ncolumns
- 2)
4214 win
->offset
= (term
->ncolumns
- 2 - win
->max_width
) / 2;
4216 /* We need to recalculate win->start and win->end here */
4217 /* because of a possible terminal resizing. */
4218 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
4219 set_win_start_end(win
, current
, last
);
4224 /* ======================================================================= */
4225 /* Helper function used by disp_word to print the cursor with the matching */
4226 /* characters of the word highlighted. */
4227 /* ======================================================================= */
4229 disp_cursor_word(long pos
, win_t
*win
, term_t
*term
, int err
)
4233 char *p
= word_a
[pos
].str
+ daccess
.flength
;
4236 /* Set the cursor attribute. */
4237 /* """"""""""""""""""""""""" */
4238 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4240 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4241 if (word_a
[pos
].tag_id
> 0)
4244 apply_attr(term
, win
->cursor_on_tag_attr
);
4246 apply_attr(term
, win
->cursor_on_tag_marked_attr
);
4251 apply_attr(term
, win
->cursor_attr
);
4253 apply_attr(term
, win
->cursor_marked_attr
);
4256 for (i
= 0; i
< word_a
[pos
].mb
- daccess
.flength
; i
++)
4258 if (BIT_ISSET(word_a
[pos
].bitmap
, i
))
4264 /* Set the buffer display attribute. */
4265 /* """"""""""""""""""""""""""""""""" */
4266 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4268 apply_attr(term
, win
->match_err_text_attr
);
4270 apply_attr(term
, win
->match_text_attr
);
4272 if (word_a
[pos
].tag_id
> 0)
4275 apply_attr(term
, win
->cursor_on_tag_attr
);
4277 apply_attr(term
, win
->cursor_on_tag_marked_attr
);
4282 apply_attr(term
, win
->cursor_attr
);
4284 apply_attr(term
, win
->cursor_marked_attr
);
4294 /* Set the search cursor attribute. */
4295 /* """""""""""""""""""""""""""""""" */
4296 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4297 if (word_a
[pos
].tag_id
> 0)
4300 apply_attr(term
, win
->cursor_on_tag_attr
);
4302 apply_attr(term
, win
->cursor_on_tag_marked_attr
);
4307 apply_attr(term
, win
->cursor_attr
);
4309 apply_attr(term
, win
->cursor_marked_attr
);
4315 fputs_safe(p
, stdout
);
4317 printf("%.*s", (int)(np
- p
), p
);
4322 /* ========================================================== */
4323 /* Helper function used by disp_word to print a matching word */
4324 /* with the matching characters of the word highlighted. */
4325 /* ========================================================== */
4327 disp_matching_word(long pos
, win_t
*win
, term_t
*term
, int is_current
, int err
)
4331 char *p
= word_a
[pos
].str
+ daccess
.flength
;
4333 unsigned char level
= 0;
4335 level
= word_a
[pos
].special_level
;
4337 /* Set the search cursor attribute. */
4338 /* """""""""""""""""""""""""""""""" */
4339 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4344 apply_attr(term
, win
->match_err_field_attr
);
4348 apply_attr(term
, win
->special_attr
[level
- 1]);
4350 apply_attr(term
, win
->match_field_attr
);
4356 apply_attr(term
, win
->search_err_field_attr
);
4358 apply_attr(term
, win
->search_field_attr
);
4361 if (word_a
[pos
].tag_id
> 0)
4362 apply_attr(term
, win
->tag_attr
);
4364 for (i
= 0; i
< word_a
[pos
].mb
- daccess
.flength
; i
++)
4366 if (BIT_ISSET(word_a
[pos
].bitmap
, i
))
4372 /* Set the buffer display attribute. */
4373 /* """"""""""""""""""""""""""""""""" */
4374 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4378 apply_attr(term
, win
->match_err_text_attr
);
4380 apply_attr(term
, win
->match_text_attr
);
4383 apply_attr(term
, win
->search_text_attr
);
4385 if (word_a
[pos
].tag_id
> 0)
4386 apply_attr(term
, win
->tag_attr
);
4395 /* Set the search cursor attribute. */
4396 /* """""""""""""""""""""""""""""""" */
4397 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4401 apply_attr(term
, win
->match_err_field_attr
);
4405 apply_attr(term
, win
->special_attr
[level
- 1]);
4407 apply_attr(term
, win
->match_field_attr
);
4413 apply_attr(term
, win
->search_err_field_attr
);
4415 apply_attr(term
, win
->search_field_attr
);
4418 if (word_a
[pos
].tag_id
> 0)
4419 apply_attr(term
, win
->tag_attr
);
4425 fputs_safe(p
, stdout
);
4427 printf("%.*s", (int)(np
- p
), p
);
4432 /* ====================================================================== */
4433 /* Display a word in, the windows. Manages the following different cases: */
4434 /* - Search mode display */
4435 /* - Cursor display */
4436 /* - Normal display */
4437 /* - Color or mono display */
4438 /* ====================================================================== */
4441 search_mode_t search_mode
,
4442 search_data_t
*search_data
,
4448 long s
= word_a
[pos
].start
;
4449 long e
= word_a
[pos
].end
;
4452 char *buffer
= search_data
->buf
;
4456 if (search_mode
!= NONE
)
4458 utf8_strprefix(tmp_word
, word_a
[pos
].str
, (long)word_a
[pos
].mb
, &p
);
4459 if (word_a
[pos
].is_numbered
)
4461 /* Set the direct access number attribute. */
4462 /* """"""""""""""""""""""""""""""""""""""" */
4463 apply_attr(term
, win
->daccess_attr
);
4467 fputs_safe(daccess
.left
, stdout
);
4468 printf("%.*s", daccess
.length
, tmp_word
+ 1);
4469 fputs_safe(daccess
.right
, stdout
);
4470 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4471 fputc_safe(' ', stdout
);
4473 else if (daccess
.length
> 0)
4475 /* Prints the leading spaces. */
4476 /* """""""""""""""""""""""""" */
4477 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4478 printf("%.*s", daccess
.flength
, tmp_word
);
4481 /* Set the search cursor attribute. */
4482 /* """""""""""""""""""""""""""""""" */
4483 if (search_data
->err
)
4484 apply_attr(term
, win
->search_err_field_attr
);
4486 apply_attr(term
, win
->search_field_attr
);
4488 /* The tab attribute must complete the attributes already set. */
4489 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4490 if (word_a
[pos
].tag_id
> 0)
4491 apply_attr(term
, win
->tag_attr
);
4493 /* Print the word part. */
4494 /* """""""""""""""""""" */
4495 fputs_safe(tmp_word
+ daccess
.flength
, stdout
);
4497 if (buffer
[0] != '\0')
4501 /* Put the cursor at the beginning of the word. */
4502 /* """""""""""""""""""""""""""""""""""""""""""" */
4503 for (i
= 0; i
< e
- s
+ 1 - daccess
.flength
; i
++)
4504 (void)tputs(TPARM1(cursor_left
), 1, outch
);
4506 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4508 /* Set the search cursor attribute. */
4509 /* """""""""""""""""""""""""""""""" */
4510 if (search_data
->err
)
4511 apply_attr(term
, win
->search_err_field_attr
);
4513 apply_attr(term
, win
->search_field_attr
);
4515 disp_matching_word(pos
, win
, term
, 1, search_data
->err
);
4520 if (daccess
.length
> 0)
4522 /* If this word is not numbered, reset the display */
4523 /* attributes before printing the leading spaces. */
4524 /* """"""""""""""""""""""""""""""""""""""""""""""" */
4525 if (!word_a
[pos
].is_numbered
)
4527 /* Print the non significant part of the word. */
4528 /* """"""""""""""""""""""""""""""""""""""""""" */
4529 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4530 printf("%.*s", daccess
.flength
- 1, word_a
[pos
].str
);
4531 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4532 fputc_safe(' ', stdout
);
4536 apply_attr(term
, win
->daccess_attr
);
4538 /* Print the non significant part of the word. */
4539 /* """"""""""""""""""""""""""""""""""""""""""" */
4540 fputs_safe(daccess
.left
, stdout
);
4541 printf("%.*s", daccess
.length
, word_a
[pos
].str
+ 1);
4542 fputs_safe(daccess
.right
, stdout
);
4543 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4544 fputc_safe(' ', stdout
);
4548 /* If we are not in search mode, display a normal cursor. */
4549 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
4550 utf8_strprefix(tmp_word
, word_a
[pos
].str
, (long)word_a
[pos
].mb
, &p
);
4551 if (word_a
[pos
].is_matching
)
4552 disp_cursor_word(pos
, win
, term
, search_data
->err
);
4555 if (word_a
[pos
].tag_id
> 0)
4558 apply_attr(term
, win
->cursor_on_tag_attr
);
4562 apply_attr(term
, win
->cursor_on_marked_attr
);
4564 apply_attr(term
, win
->cursor_on_tag_marked_attr
);
4570 apply_attr(term
, win
->cursor_attr
);
4574 apply_attr(term
, win
->cursor_on_marked_attr
);
4576 apply_attr(term
, win
->cursor_marked_attr
);
4580 fputs_safe(tmp_word
+ daccess
.flength
, stdout
);
4583 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4587 /* Display a normal word without any attribute. */
4588 /* """""""""""""""""""""""""""""""""""""""""""" */
4589 utf8_strprefix(tmp_word
, word_a
[pos
].str
, (long)word_a
[pos
].mb
, &p
);
4591 /* If words are numbered, emphasis their numbers. */
4592 /* """""""""""""""""""""""""""""""""""""""""""""" */
4593 if (word_a
[pos
].is_numbered
)
4595 apply_attr(term
, win
->daccess_attr
);
4597 fputs_safe(daccess
.left
, stdout
);
4598 printf("%.*s", daccess
.length
, tmp_word
+ 1);
4599 fputs_safe(daccess
.right
, stdout
);
4601 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4602 fputc_safe(' ', stdout
);
4604 else if (daccess
.length
> 0)
4608 /* Insert leading spaces if the word is non numbered and */
4609 /* padding for all words is set. */
4610 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
4611 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4612 if (daccess
.padding
== 'a')
4613 for (i
= 0; i
< daccess
.flength
; i
++)
4614 fputc_safe(' ', stdout
);
4617 if (!word_a
[pos
].is_selectable
)
4618 apply_attr(term
, win
->exclude_attr
);
4619 else if (word_a
[pos
].special_level
> 0)
4621 unsigned char level
= word_a
[pos
].special_level
- 1;
4623 apply_attr(term
, win
->special_attr
[level
]);
4625 else if (toggles
->taggable
&& pos
== marked
)
4626 apply_attr(term
, win
->marked_attr
);
4629 if (word_a
[pos
].iattr
!= NULL
) /* is a specific attribute set? */
4630 apply_attr(term
, *(word_a
[pos
].iattr
));
4632 apply_attr(term
, win
->include_attr
);
4635 if (word_a
[pos
].is_matching
)
4636 disp_matching_word(pos
, win
, term
, 0, search_data
->err
);
4639 if (word_a
[pos
].tag_id
> 0)
4640 apply_attr(term
, win
->tag_attr
);
4642 if ((daccess
.length
> 0 && daccess
.padding
== 'a')
4643 || word_a
[pos
].is_numbered
)
4644 fputs_safe(tmp_word
+ daccess
.flength
, stdout
);
4646 fputs_safe(tmp_word
, stdout
);
4649 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4653 /* =================================== */
4654 /* Display a message above the window. */
4655 /* =================================== */
4657 disp_message(ll_t
*message_lines_list
,
4658 long message_max_width
,
4659 long message_max_len
,
4662 langinfo_t
*langinfo
)
4671 int n
= 0; /* Counter used to display message lines. */
4672 int cut
= 0; /* Will be 1 if the message is shortened. */
4676 win
->message_lines
= 0;
4678 /* Do nothing if there is no message to display. */
4679 /* """"""""""""""""""""""""""""""""""""""""""""" */
4680 if (message_lines_list
== NULL
)
4683 /* Recalculate the number of to-be-displayed lines in the messages */
4684 /* if space is missing. */
4685 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4686 if (term
->nlines
< 3)
4689 win
->message_lines
= message_lines_list
->len
;
4690 if (win
->message_lines
> term
->nlines
- 2)
4692 win
->message_lines
= term
->nlines
- 2;
4693 win
->max_lines
= term
->nlines
- win
->message_lines
- 1;
4696 win
->message_lines
++;
4698 /* Deactivate the periodic timer to prevent the interruptions to corrupt */
4699 /* screen by altering the timing of the decoding of the terminfo */
4701 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4703 sigaddset(&mask
, SIGALRM
);
4704 sigprocmask(SIG_BLOCK
, &mask
, NULL
);
4706 node
= message_lines_list
->head
;
4707 buf
= xmalloc(message_max_len
+ 1);
4709 /* Follow the list of message lines and display each line. */
4710 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
4711 for (n
= 1; n
< win
->message_lines
; n
++)
4716 len
= utf8_strlen(line
);
4717 w
= utf8_strtowcs(line
);
4719 /* Adjust size and len if the terminal is not large enough. */
4720 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4721 size
= wcswidth(w
, len
);
4722 while (len
> 0 && size
> term
->ncolumns
)
4723 size
= wcswidth(w
, --len
);
4727 /* Compute the offset from the left screen border if -M option is set. */
4728 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4729 offset
= (term
->ncolumns
- message_max_width
- 3) / 2;
4731 if (win
->center
&& offset
> 0)
4732 for (i
= 0; i
< offset
; i
++)
4733 fputc_safe(' ', stdout
);
4735 apply_attr(term
, win
->message_attr
);
4737 /* Only print the start of a line if the screen width if too small. */
4738 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4739 utf8_strprefix(buf
, line
, len
, &size
);
4741 /* Print the line without the ending \n. */
4742 /* ''''''''''''''''''''''''''''''''''''' */
4743 if (n
> 1 && cut
&& n
== win
->message_lines
- 1)
4746 fputs_safe(msg_arr_down
, stdout
);
4748 fputc_safe('v', stdout
);
4753 /* Complete the short line with spaces until it reach the */
4754 /* message max size. */
4755 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4756 for (i
= size
; i
< message_max_width
; i
++)
4758 if (i
+ (offset
< 0 ? 0 : offset
) >= term
->ncolumns
)
4760 fputc_safe(' ', stdout
);
4763 /* Drop the attributes and print a \n. */
4764 /* ''''''''''''''''''''''''''''''''''' */
4765 if (term
->nlines
> 2)
4767 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4774 /* Add an empty line without attribute to separate the menu title */
4775 /* and the menu content. */
4776 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4781 /* Re-enable the periodic timer. */
4782 /* """"""""""""""""""""""""""""" */
4783 sigprocmask(SIG_UNBLOCK
, &mask
, NULL
);
4786 /* ============================= */
4787 /* Display the selection window. */
4788 /* ============================= */
4790 disp_lines(win_t
*win
,
4794 search_mode_t search_mode
,
4795 search_data_t
*search_data
,
4799 langinfo_t
*langinfo
)
4803 char left_margin_symbol
[5]; /* Placeholder for the arrow symbols. */
4805 long has_vbar
; /* Flag to signal the presence of the vertical bar. */
4807 int leftmost_start
= 0; /* Starting position of the leftmost selectable *
4808 | word in the window lines. */
4809 int rightmost_end
= 0; /* Ending position of the rightmost selectable *
4810 | word in the window lines. */
4812 long first_line
; /* real line # on the first line of the window. */
4814 int row1
, row2
, col
; /* Only the rows are used to detect a *
4815 | bottom-of-page scrolling, col is *
4816 | necessary but not required here. */
4820 /* Disable the periodic timer to prevent the interruptions to corrupt */
4821 /* screen by altering the timing of the decoding of the terminfo */
4823 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4825 sigaddset(&mask
, SIGALRM
);
4826 sigprocmask(SIG_BLOCK
, &mask
, NULL
);
4828 left_margin_symbol
[0] = ' ';
4829 left_margin_symbol
[1] = '\0';
4835 /* Initialize the truncated lines flag. */
4836 /* """""""""""""""""""""""""""""""""""" */
4837 win
->has_truncated_lines
= 0;
4839 /* Initialize the necessity of displaying the horizontal scroll bar or not. */
4840 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4843 /* Initialize the selectable column guard to its maximum position. */
4844 /* for the first window line. */
4845 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4846 shift_right_sym_pos_a
[line_nb_of_word_a
[win
->start
] + lines_disp
- 1] =
4849 (void)tputs(TPARM1(save_cursor
), 1, outch
);
4851 i
= win
->start
; /* Index of the first word in the window. */
4853 /* Modify the max number of displayed lines if we do not have */
4855 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4856 if (win
->max_lines
> term
->nlines
- win
->message_lines
)
4857 win
->max_lines
= term
->nlines
- win
->message_lines
;
4859 if (last_line
>= win
->max_lines
)
4864 if (win
->col_mode
|| win
->line_mode
)
4865 len
= term
->ncolumns
- 3;
4867 len
= term
->ncolumns
- 2;
4869 /* If in column mode and the sum of the columns sizes + gutters is */
4870 /* greater than the terminal width, then prepend a space to be able to */
4871 /* display the left arrow indicating that the first displayed column */
4872 /* is not the first one. */
4873 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4875 && ((win
->col_mode
|| win
->line_mode
)
4876 && win
->real_max_width
> term
->ncolumns
- 2))
4878 if (win
->first_column
> 0)
4881 strcpy(left_margin_symbol
, shift_left_sym
);
4883 strcpy(left_margin_symbol
, "<");
4885 if (!toggles
->no_hor_scrollbar
)
4889 if (!win
->has_truncated_lines
)
4890 win
->has_truncated_lines
= 1;
4893 left_margin_symbol
[0] = '\0';
4895 /* Center the display ? */
4896 /* """""""""""""""""""" */
4897 if (win
->offset
> 0)
4900 for (i
= 0; i
< win
->offset
; i
++)
4901 fputc_safe(' ', stdout
);
4904 left_margin_putp(left_margin_symbol
, term
, win
);
4906 first_line
= line_nb_of_word_a
[i
];
4908 while (len
> 1 && i
<= count
- 1)
4910 /* Display one word and the space or symbol following it. */
4911 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
4912 if (word_a
[i
].start
>= win
->first_column
4913 && word_a
[i
].end
< len
+ win
->first_column
)
4915 if (first_start
< 0)
4916 first_start
= word_a
[i
].start
;
4918 disp_word(i
, search_mode
, search_data
, term
, win
, toggles
, tmp_word
);
4920 /* Calculate the start offset of the last word of the line */
4921 /* containing the cursor in column or line mode. */
4922 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
4923 if ((win
->col_mode
|| win
->line_mode
)
4924 && line_nb_of_word_a
[current
] - first_line
+ 1 == lines_disp
)
4928 wi
= first_word_in_line_a
[lines_disp
+ first_line
- 1];
4929 while (wi
< current
&& !word_a
[wi
].is_selectable
)
4932 leftmost_start
= word_a
[wi
++].start
;
4934 if (lines_disp
+ first_line
> last_line
)
4937 wi
= first_word_in_line_a
[lines_disp
+ first_line
] - 1;
4939 while (!word_a
[wi
].is_selectable
)
4942 rightmost_end
= word_a
[wi
].end
;
4945 /* If there are more element to be displayed after the right margin */
4946 /* in column or line mode. */
4947 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4948 if ((win
->col_mode
|| win
->line_mode
) && i
< count
- 1
4949 && word_a
[i
+ 1].end
>= len
+ win
->first_column
)
4951 if (!win
->has_truncated_lines
)
4952 win
->has_truncated_lines
= 1;
4954 /* Toggle the presence of the horizontal bar if allowed and */
4955 /* if this line contains the cursor in column or line mode. */
4956 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4957 if (!toggles
->no_hor_scrollbar
4958 && line_nb_of_word_a
[current
] - first_line
+ 1 == lines_disp
)
4959 win
->has_hbar
= 1; /* the line containing the cursor is *
4960 | truncated in the window. */
4962 apply_attr(term
, win
->shift_attr
);
4965 fputs_safe(shift_right_sym
, stdout
);
4967 fputc_safe('>', stdout
);
4969 (void)tputs(TPARM1(exit_attribute_mode
), 1, outch
);
4971 /* Adjust the selectable column guard to the column just after */
4972 /* the last displayed word. */
4973 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4974 shift_right_sym_pos_a
[line_nb_of_word_a
[i
]] = word_a
[i
].end
+ 1
4978 /* If we want to display the gutter. */
4979 /* """"""""""""""""""""""""""""""""" */
4980 if (!word_a
[i
].is_last
&& win
->col_sep
4981 && (win
->tab_mode
|| win
->col_mode
))
4985 /* Make sure that we are using the right gutter character even */
4986 /* if the first displayed word is * not the first of its line. */
4987 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4988 pos
= i
- first_word_in_line_a
[line_nb_of_word_a
[i
]];
4990 if (pos
>= win
->gutter_nb
) /* Use the last gutter character. */
4991 fputs_safe(win
->gutter_a
[win
->gutter_nb
- 1], stdout
);
4993 fputs_safe(win
->gutter_a
[pos
], stdout
);
4996 /* Else just display a space. */
4997 /* """""""""""""""""""""""""" */
4998 fputc_safe(' ', stdout
);
5001 /* Mark the line as the current line, the line containing the cursor. */
5002 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5004 win
->cur_line
= lines_disp
;
5006 /* Check if we must start a new line. */
5007 /* """""""""""""""""""""""""""""""""" */
5008 if (i
== count
- 1 || word_a
[i
+ 1].start
== 0)
5010 (void)tputs(TPARM1(clr_eol
), 1, outch
);
5011 if (lines_disp
< win
->max_lines
)
5013 /* If we have more than one line to display. */
5014 /* """"""""""""""""""""""""""""""""""""""""" */
5015 if (has_vbar
&& !toggles
->no_scrollbar
5016 && (lines_disp
> 1 || i
< count
- 1))
5018 /* Store the scroll bar column position. */
5019 /* """"""""""""""""""""""""""""""""""""" */
5020 win
->sb_column
= win
->offset
+ win
->max_width
+ 1;
5022 /* Display the next element of the scrollbar. */
5023 /* """""""""""""""""""""""""""""""""""""""""" */
5024 if (line_nb_of_word_a
[i
] == 0)
5026 if (win
->max_lines
> 1)
5027 right_margin_putp(sbar_top
,
5035 right_margin_putp(sbar_arr_down
,
5043 else if (lines_disp
== 1)
5044 right_margin_putp(sbar_arr_up
,
5051 else if (line_nb_of_word_a
[i
] == last_line
)
5053 if (win
->max_lines
> 1)
5054 right_margin_putp(sbar_down
,
5062 right_margin_putp(sbar_arr_up
,
5070 else if (last_line
+ 1 > win
->max_lines
5071 && (long)((float)(line_nb_of_word_a
[current
])
5072 / (last_line
+ 1) * (win
->max_lines
- 2)
5075 right_margin_putp(sbar_curs
,
5083 right_margin_putp(sbar_line
,
5092 /* Print a newline character if we are not at the end of */
5093 /* the input nor at the end of the window. */
5094 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
5095 if (i
< count
- 1 && lines_disp
< win
->max_lines
)
5097 fputc_safe('\n', stdout
);
5099 if (win
->offset
> 0)
5102 for (i
= 0; i
< win
->offset
; i
++)
5103 fputc_safe(' ', stdout
);
5106 left_margin_putp(left_margin_symbol
, term
, win
);
5109 /* We do not increment the number of lines seen after */
5110 /* a premature end of input. */
5111 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
5117 /* Initialize the selectable column guard to its maximum position. */
5118 /* for the next window line. */
5119 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5120 shift_right_sym_pos_a
[line_nb_of_word_a
[win
->start
] + lines_disp
5121 - 1] = term
->ncolumns
;
5124 if (win
->max_lines
== 1)
5127 else if (lines_disp
== win
->max_lines
)
5129 /* The last line of the window has been displayed. */
5130 /* """"""""""""""""""""""""""""""""""""""""""""""" */
5131 if (has_vbar
&& line_nb_of_word_a
[i
] == last_line
)
5133 if (!toggles
->no_scrollbar
)
5135 if (win
->max_lines
> 1)
5136 right_margin_putp(sbar_down
,
5144 right_margin_putp(sbar_arr_up
,
5155 if (has_vbar
&& !toggles
->no_scrollbar
)
5156 right_margin_putp(sbar_arr_down
,
5167 /* These lines were not in the widows and so we have nothing to do. */
5168 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5177 /* Display the horizontal bar when needed. */
5178 /* """"""""""""""""""""""""""""""""""""""" */
5179 if (win
->col_mode
|| win
->line_mode
)
5181 /* Save again the cursor position before drawing the horizontal bar. */
5182 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5183 get_cursor_position(&row1
, &col
); /* col is not needed here. */
5187 int pos1
; /* Pos. of the cursor's start in the horizontal scroll bar. */
5188 int pos2
; /* Pos. of the cursor's end in the horizontal scroll bar. */
5190 /* Note: in the following expression, rightmost_start is always */
5191 /* greater then leftmost_start as a line containing a single word */
5192 /* cannot be truncated and have an horizontal scroll bar hence */
5193 /* win->has_hbar = 0. */
5194 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
5195 pos1
= (int)(word_a
[current
].start
- leftmost_start
)
5196 / (float)(rightmost_end
- leftmost_start
) * (term
->ncolumns
- 4);
5197 pos2
= (int)(word_a
[current
].end
- leftmost_start
)
5198 / (float)(rightmost_end
- leftmost_start
) * (term
->ncolumns
- 4);
5200 if (pos2
> term
->ncolumns
- 4)
5201 pos2
= term
->ncolumns
5202 - 4; /* just to make sure but should not happen. */
5204 fputs_safe("\n", stdout
);
5205 disp_hbar(win
, term
, langinfo
, pos1
, pos2
);
5207 /* Mark the fact that an horizontal scroll bar has been displayed */
5208 /* and its space allocated. */
5209 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5210 if (win
->hbar_displayed
== 0)
5211 win
->hbar_displayed
= 1;
5213 else if (win
->hbar_displayed
) /* The horizontal scroll bar has already *
5214 | been displayed, keep this space empty *
5215 | to not disturb the display. */
5217 fputs_safe("\n", stdout
);
5218 (void)tputs(TPARM1(clr_eol
), 1, outch
);
5221 /* Save again the cursor position again (especially the row) to detect */
5222 /* an automatic scroll up when the cursor is at the bottom of the window */
5223 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5224 get_cursor_position(&row2
, &col
);
5227 /* Update win->end, this is necessary because we only */
5228 /* call build_metadata on start and on terminal resize. */
5229 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
5235 /* We restore the cursor position saved before the display of the window. */
5236 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5237 (void)tputs(TPARM1(restore_cursor
), 1, outch
);
5239 /* Make sure the cursor is correctly moved when the horizontal scroll */
5240 /* bar is displayed and the window is at the bottom of the screen. */
5241 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5242 if (win
->col_mode
|| win
->line_mode
)
5244 if (win
->has_hbar
&& row1
== row2
) /* Screen scrolled up. */
5246 (void)tputs(TPARM1(cursor_up
), 1, outch
);
5251 /* Re-enable the periodic timer. */
5252 /* """"""""""""""""""""""""""""" */
5253 sigprocmask(SIG_UNBLOCK
, &mask
, NULL
);
5255 return lines_disp
+ (win
->hbar_displayed
? 1 : 0);
5258 /* ======================================================= */
5259 /* Signal handler. */
5260 /* Manages SIGSEGV, SIGTERM, SIGHUP, SIGWINCH and SIGALRM. */
5261 /* ======================================================= */
5267 /* Standard termination signals. */
5268 /* """"""""""""""""""""""""""""" */
5285 /* Terminal resize. */
5286 /* """""""""""""""" */
5291 /* Alarm triggered, This signal is used by the search mechanism to */
5292 /* forces a window refresh. */
5293 /* The help mechanism uses it to clear the message */
5294 /* It is also used to redisplay the window after the end of a terminal */
5296 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5298 if (timeout
.initial_value
> 0)
5299 got_timeout_tick
= 1;
5301 if (forgotten_timer
> 0)
5304 if (forgotten_timer
== 0)
5305 got_forgotten_alrm
= 1;
5310 if (help_timer
== 0 && help_mode
)
5313 if (daccess_timer
> 0)
5316 if (daccess_timer
== 0)
5317 got_daccess_alrm
= 1;
5319 if (winch_timer
> 0)
5322 if (winch_timer
== 0)
5329 if (search_timer
> 0)
5332 if (search_timer
== 0 && search_mode
!= NONE
)
5333 got_search_alrm
= 1;
5339 /* ========================================================= */
5340 /* Set new first column to display when horizontal scrolling */
5341 /* Alter win->first_column. */
5342 /* ========================================================= */
5344 set_new_first_column(win_t
*win
, term_t
*term
)
5348 if (word_a
[current
].start
< win
->first_column
)
5352 while (win
->first_column
> 0 && word_a
[current
].start
< win
->first_column
)
5354 win
->first_column
= word_a
[pos
].start
;
5358 else if (word_a
[current
].end
- win
->first_column
>= term
->ncolumns
- 3)
5360 pos
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
5362 while (!word_a
[pos
].is_last
5363 && word_a
[current
].end
- win
->first_column
>= term
->ncolumns
- 3)
5366 win
->first_column
= word_a
[pos
].start
;
5371 /* ===================================================== */
5372 /* Restrict the matches to word ending with the pattern. */
5373 /* ===================================================== */
5375 select_ending_matches(win_t
*win
,
5377 search_data_t
*search_data
,
5380 if (matches_count
> 0)
5391 /* Creation of an alternate array which will */
5392 /* contain only the candidates having potentially */
5393 /* an ending pattern, if this array becomes non */
5394 /* empty then it will replace the original array. */
5395 /* """""""""""""""""""""""""""""""""""""""""""""" */
5396 alt_matching_words_a
= xrealloc(alt_matching_words_a
,
5397 matches_count
* (sizeof(long)));
5399 for (i
= 0; i
< matches_count
; i
++)
5401 index
= matching_words_a
[i
];
5402 char *str
= word_a
[index
].str
;
5404 /* count the trailing blanks non counted in the bitmap. */
5405 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
5406 ptr
= str
+ strlen(str
);
5409 while ((ptr
= utf8_prev(str
, ptr
)) != NULL
&& isblank(*ptr
))
5415 if (ptr
== NULL
) /* str is blank or contain an invalid uft8 sequence. */
5418 /* NOTE: utf8_prev cannot return NULL in the previous loop */
5419 /* because str always contains at least one UTF-8 valid */
5420 /* sequence, so does ptr. */
5421 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
5423 /* Check the bit corresponding to the last non blank glyph */
5424 /* If set we add the index to an alternate array, if not we */
5425 /* clear the bitmap of the corresponding word. */
5426 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5427 if (BIT_ISSET(word_a
[index
].bitmap
,
5428 word_a
[index
].mb
- nb
- daccess
.flength
- 1))
5429 alt_matching_words_a
[j
++] = index
;
5432 /* Look if the end of the word potentially contain an */
5433 /* ending pattern. */
5434 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
5435 if (search_mode
== FUZZY
)
5437 utf8_len
= mblen(ptr
, 4);
5438 last_glyph
= search_data
->buf
+ search_data
->len
- utf8_len
;
5440 /* in fuzzy search mode we only look the last glyph. */
5441 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
5442 if (memcmp(ptr
, last_glyph
, utf8_len
) == 0)
5443 alt_matching_words_a
[j
++] = index
;
5445 memset(word_a
[index
].bitmap
,
5447 (word_a
[index
].mb
- daccess
.flength
) / CHAR_BIT
+ 1);
5451 /* in not fuzzy search mode use all the pattern. */
5452 /* """"""""""""""""""""""""""""""""""""""""""""" */
5453 for (nb
= 0; nb
< search_data
->utf8_len
- 1; nb
++)
5454 ptr
= utf8_prev(str
, ptr
);
5455 if (memcmp(ptr
, search_data
->buf
, search_data
->len
) == 0)
5456 alt_matching_words_a
[j
++] = index
;
5458 memset(word_a
[index
].bitmap
,
5460 (word_a
[index
].mb
- daccess
.flength
) / CHAR_BIT
+ 1);
5465 /* Swap the normal and alt array. */
5466 /* """""""""""""""""""""""""""""" */
5468 matching_words_a_size
= j
;
5470 tmp
= matching_words_a
;
5471 matching_words_a
= alt_matching_words_a
;
5472 alt_matching_words_a
= tmp
;
5476 /* Adjust the bitmap to the ending version. */
5477 /* """""""""""""""""""""""""""""""""""""""" */
5478 update_bitmaps(search_mode
, search_data
, END_AFFINITY
);
5480 current
= matching_words_a
[0];
5482 if (current
< win
->start
|| current
> win
->end
)
5483 *last_line
= build_metadata(term
, count
, win
);
5485 /* Set new first column to display. */
5486 /* """""""""""""""""""""""""""""""" */
5487 set_new_first_column(win
, term
);
5492 /* ======================================================= */
5493 /* Restrict the matches to word starting with the pattern. */
5494 /* ======================================================= */
5496 select_starting_matches(win_t
*win
,
5498 search_data_t
*search_data
,
5501 if (matches_count
> 0)
5512 alt_matching_words_a
= xrealloc(alt_matching_words_a
,
5513 matches_count
* (sizeof(long)));
5515 first_glyph
= xmalloc(5);
5517 for (i
= 0; i
< matches_count
; i
++)
5519 index
= matching_words_a
[i
];
5521 for (nb
= 0; nb
< word_a
[index
].mb
; nb
++)
5522 if (!isblank(*(word_a
[index
].str
+ daccess
.flength
+ nb
)))
5525 if (BIT_ISSET(word_a
[index
].bitmap
, nb
))
5526 alt_matching_words_a
[j
++] = index
;
5530 if (search_mode
== FUZZY
)
5532 first_glyph
= utf8_strprefix(first_glyph
,
5533 word_a
[index
].str
+ nb
+ daccess
.flength
,
5538 /* in fuzzy search mode we only look the first glyph. */
5539 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
5540 if (memcmp(search_data
->buf
, first_glyph
, utf8_len
) == 0)
5541 alt_matching_words_a
[j
++] = index
;
5543 memset(word_a
[index
].bitmap
,
5545 (word_a
[index
].mb
+ nb
- daccess
.flength
) / CHAR_BIT
+ 1);
5549 /* in not fuzzy search mode use all the pattern. */
5550 /* """"""""""""""""""""""""""""""""""""""""""""" */
5551 if (memcmp(search_data
->buf
,
5552 word_a
[index
].str
+ nb
,
5553 search_data
->len
- nb
)
5555 alt_matching_words_a
[j
++] = index
;
5557 memset(word_a
[index
].bitmap
,
5559 (word_a
[index
].mb
+ nb
- daccess
.flength
) / CHAR_BIT
+ 1);
5567 matching_words_a_size
= j
;
5569 tmp
= matching_words_a
;
5570 matching_words_a
= alt_matching_words_a
;
5571 alt_matching_words_a
= tmp
;
5575 /* Adjust the bitmap to the ending version. */
5576 /* """""""""""""""""""""""""""""""""""""""" */
5577 update_bitmaps(search_mode
, search_data
, START_AFFINITY
);
5579 current
= matching_words_a
[0];
5581 if (current
< win
->start
|| current
> win
->end
)
5582 *last_line
= build_metadata(term
, count
, win
);
5584 /* Set new first column to display. */
5585 /* """""""""""""""""""""""""""""""" */
5586 set_new_first_column(win
, term
);
5591 /* ============================= */
5592 /* Moves the cursor to the left. */
5593 /* ============================= */
5595 move_left(win_t
*win
,
5598 search_data_t
*search_data
,
5599 langinfo_t
*langinfo
,
5604 long old_current
= current
;
5605 long old_start
= win
->start
;
5606 long old_first_column
= win
->first_column
;
5607 long wi
; /* Word index. */
5613 /* Sets the new win->start and win->end if the cursor */
5614 /* is at the beginning of the windows. */
5615 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
5616 if (current
== win
->start
&& win
->start
> 0)
5618 for (wi
= win
->start
- 1; wi
>= 0 && word_a
[wi
].start
!= 0; wi
--)
5623 if (word_a
[wi
].str
!= NULL
)
5626 if (win
->end
< count
- 1)
5628 for (wi
= win
->end
+ 2; wi
< count
- 1 && word_a
[wi
].start
!= 0; wi
++)
5631 if (word_a
[wi
].str
!= NULL
)
5636 /* In column mode we need to take care of the */
5637 /* horizontal scrolling. */
5638 /* """""""""""""""""""""""""""""""""""""""""" */
5639 if (win
->col_mode
|| win
->line_mode
)
5643 if (word_a
[current
].start
== 0)
5647 len
= term
->ncolumns
- 3;
5648 pos
= first_word_in_line_a
[line_nb_of_word_a
[current
- 1]];
5650 while (word_a
[current
- 1].end
- win
->first_column
>= len
)
5652 win
->first_column
+= word_a
[pos
].end
- word_a
[pos
].start
+ 2;
5657 else if (word_a
[current
- 1].start
< win
->first_column
)
5658 win
->first_column
= word_a
[current
- 1].start
;
5664 } while (current
!= old_current
&& !word_a
[current
].is_selectable
);
5666 /* The old settings need to be restored if the */
5667 /* new current word is not selectable. */
5668 /* """"""""""""""""""""""""""""""""""""""""""" */
5669 if (!word_a
[current
].is_selectable
)
5671 current
= old_current
;
5672 win
->start
= old_start
;
5673 if (win
->col_mode
|| win
->line_mode
)
5674 win
->first_column
= old_first_column
;
5677 if (current
!= old_current
)
5678 *nl
= disp_lines(win
,
5690 /* ============================================ */
5691 /* Shift the content of the window to the left. */
5692 /* Stop if the cursor will stop to be visible. */
5693 /* ============================================ */
5695 shift_left(win_t
*win
,
5698 search_data_t
*search_data
,
5699 langinfo_t
*langinfo
,
5708 /* No lines to shift if not in lire or column mode. */
5709 /* """""""""""""""""""""""""""""""""""""""""""""""" */
5710 if (!win
->col_mode
&& !win
->line_mode
)
5713 /* Do nothing if we already are on the left side of if the line */
5714 /* is already fully displayed. */
5715 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5716 if (win
->first_column
== 0)
5719 /* Find the first word to be displayed in this line. */
5720 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
5721 pos
= first_word_in_line_a
[line
];
5723 while (pos
< count
- 1 && word_a
[pos
].start
< win
->first_column
5724 && word_a
[pos
+ 1].start
> 0)
5727 if (word_a
[pos
].start
>= win
->first_column
)
5730 /* Make sure the word under the cursor remains visible. */
5731 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
5732 len
= term
->ncolumns
- 3;
5733 if (word_a
[current
].end
>= len
+ word_a
[pos
].start
)
5736 win
->first_column
= word_a
[pos
].start
;
5738 *nl
= disp_lines(win
,
5750 /* ============================== */
5751 /* Moves the cursor to the right. */
5752 /* ============================== */
5754 move_right(win_t
*win
,
5757 search_data_t
*search_data
,
5758 langinfo_t
*langinfo
,
5763 long old_current
= current
;
5764 long old_start
= win
->start
;
5765 long old_first_column
= win
->first_column
;
5766 long wi
; /* word index */
5770 if (current
< count
- 1)
5772 /* Sets the new win->start and win->end if the cursor */
5773 /* is at the end of the windows. */
5774 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
5775 if (current
== win
->end
&& win
->start
< count
- 1
5776 && win
->end
!= count
- 1)
5778 for (wi
= win
->start
+ 1; wi
< count
- 1 && word_a
[wi
].start
!= 0; wi
++)
5782 if (word_a
[wi
].str
!= NULL
)
5785 if (win
->end
< count
- 1)
5787 for (wi
= win
->end
+ 2; wi
< count
- 1 && word_a
[wi
].start
!= 0; wi
++)
5790 if (word_a
[wi
].str
!= NULL
)
5795 /* In column mode we need to take care of the */
5796 /* horizontal scrolling. */
5797 /* """""""""""""""""""""""""""""""""""""""""" */
5798 if (win
->col_mode
|| win
->line_mode
)
5800 if (word_a
[current
].is_last
)
5801 win
->first_column
= 0;
5807 len
= term
->ncolumns
- 3;
5809 if (word_a
[current
+ 1].end
>= len
+ win
->first_column
)
5811 /* Find the first word to be displayed in this line. */
5812 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
5813 pos
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
5815 while (word_a
[pos
].start
<= win
->first_column
)
5820 /* If the new current word cannot be displayed, search */
5821 /* the first word in the line that can be displayed by */
5822 /* iterating on pos. */
5823 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
5824 while (word_a
[current
+ 1].end
- word_a
[pos
].start
>= len
)
5827 if (word_a
[pos
].start
> 0)
5828 win
->first_column
= word_a
[pos
].start
;
5836 } while (current
!= old_current
&& !word_a
[current
].is_selectable
);
5838 /* The old settings need to be restored if the */
5839 /* new current word is not selectable. */
5840 /* """"""""""""""""""""""""""""""""""""""""""" */
5841 if (!word_a
[current
].is_selectable
)
5843 current
= old_current
;
5844 win
->start
= old_start
;
5845 if (win
->col_mode
|| win
->line_mode
)
5846 win
->first_column
= old_first_column
;
5849 if (current
!= old_current
)
5850 *nl
= disp_lines(win
,
5862 /* =========================================== */
5863 /* Shift content of the window to the right. */
5864 /* Stop if the cursor will stop to be visible. */
5865 /* ============================================*/
5867 shift_right(win_t
*win
,
5870 search_data_t
*search_data
,
5871 langinfo_t
*langinfo
,
5879 /* No lines to shift if not in lire or column mode. */
5880 /* """""""""""""""""""""""""""""""""""""""""""""""" */
5881 if (!win
->col_mode
&& !win
->line_mode
)
5884 /* Do nothing if we already are on the right side and all or lines */
5885 /* are already fully displayed. */
5886 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5887 if (shift_right_sym_pos_a
[line
] == term
->ncolumns
)
5891 /* Search for at least one line not fully displayed. */
5892 /* ''''''''''''''''''''''''''''''''''''''''''''''''' */
5893 for (long l
= line_nb_of_word_a
[win
->start
];
5894 l
< line_nb_of_word_a
[win
->start
] + win
->max_lines
;
5897 if (shift_right_sym_pos_a
[l
] == term
->ncolumns
) /* fully displayed. */
5900 found
= 1; /* Use this line instead of line to calculate the shifting. */
5905 /* Do nothing if all lines are fully displayed. */
5906 /* '''''''''''''''''''''''''''''''''''''''''''' */
5911 /* Find the first word to be displayed in this line. */
5912 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
5913 pos
= first_word_in_line_a
[line
];
5915 while (pos
< count
- 1 && word_a
[pos
].start
< win
->first_column
5916 && word_a
[pos
+ 1].start
> 0)
5919 if (pos
< count
- 1 && word_a
[pos
+ 1].start
> 0)
5922 /* Make sure the word under the cursor remains visible. */
5923 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
5924 if (word_a
[pos
].start
<= word_a
[current
].start
)
5925 win
->first_column
= word_a
[pos
].start
;
5929 *nl
= disp_lines(win
,
5941 /* ================================================================== */
5942 /* Get the last word of a line after it has been formed to fit in the */
5944 /* ================================================================== */
5946 get_line_last_word(long line
, long last_line
)
5948 if (line
== last_line
)
5951 return first_word_in_line_a
[line
+ 1] - 1;
5954 /* ==================================================================== */
5955 /* Try to locate the best word in the target line when trying to move */
5956 /* the cursor upwards. */
5957 /* returns 1 if a word has been found else 0. */
5958 /* This function has the side effect to potentially change the value of */
5959 /* the variable 'current' if an adequate word is found. */
5960 /* ==================================================================== */
5962 find_best_word_upwards(long last_word
, long s
, long e
)
5968 /* Look for the first word whose start position in the line is */
5969 /* less or equal to the source word starting position. */
5970 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5972 while (word_a
[cursor
].start
> s
)
5975 /* In case no word is eligible, keep the cursor on */
5976 /* the last word. */
5977 /* """"""""""""""""""""""""""""""""""""""""""""""" */
5978 if (cursor
== last_word
&& word_a
[cursor
].start
> 0)
5981 /* Try to guess the best choice if we have multiple choices. */
5982 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5983 if (word_a
[cursor
].end
>= s
5984 && word_a
[cursor
].end
- s
>= e
- word_a
[cursor
+ 1].start
)
5988 if (cursor
< last_word
)
5989 current
= cursor
+ 1;
5994 /* If the word is not selectable, try to find a selectable word */
5996 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5997 if (!word_a
[current
].is_selectable
)
6000 while (word_a
[current
- index
].start
> 0
6001 && !word_a
[current
- index
].is_selectable
)
6004 if (word_a
[current
- index
].is_selectable
)
6012 while (current
+ index
< last_word
6013 && !word_a
[current
+ index
].is_selectable
)
6016 if (word_a
[current
+ index
].is_selectable
)
6029 /* ==================================================================== */
6030 /* Try to locate the best word in the target line when trying to move */
6031 /* the cursor downwards. */
6032 /* returns 1 if a word has been found else 0. */
6033 /* This function has the side effect to potentially change the value of */
6034 /* the variable 'current' if an adequate word is found. */
6035 /* ==================================================================== */
6037 find_best_word_downwards(long last_word
, long s
, long e
)
6043 /* Look for the first word whose start position in the line is */
6044 /* less or equal than the source word starting position. */
6045 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6047 while (word_a
[cursor
].start
> s
)
6050 /* In case no word is eligible, keep the cursor on */
6051 /* the last word. */
6052 /* """"""""""""""""""""""""""""""""""""""""""""""" */
6053 if (cursor
== last_word
&& word_a
[cursor
].start
> 0)
6056 /* Try to guess the best choice if we have multiple choices. */
6057 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6058 if (cursor
< count
- 1
6059 && word_a
[cursor
].end
- s
>= e
- word_a
[cursor
+ 1].start
)
6063 if (cursor
< count
- 1)
6065 if (cursor
< last_word
)
6066 current
= cursor
+ 1;
6071 current
= count
- 1;
6074 /* If the word is not selectable, try to find a selectable word */
6076 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6077 if (!word_a
[current
].is_selectable
)
6080 while (word_a
[current
- index
].start
> 0
6081 && !word_a
[current
- index
].is_selectable
)
6084 if (word_a
[current
- index
].is_selectable
)
6092 while (current
+ index
< last_word
6093 && !word_a
[current
+ index
].is_selectable
)
6096 if (word_a
[current
+ index
].is_selectable
)
6109 /* ========================= */
6110 /* Moves the cursor upwards. */
6111 /* ========================= */
6116 search_data_t
*search_data
,
6117 langinfo_t
*langinfo
,
6120 long first_selectable
,
6124 long line
; /* The line being processed (target line). */
6125 long start_line
; /* The first line of the window. */
6126 long cur_line
; /* The line of the cursor. */
6127 long nlines
; /* Number of line in the window. */
6128 long first_selectable_line
; /* the line containing the first *
6129 | selectable word. */
6130 long lines_skipped
; /* The number of line between the target line and the *
6131 | first line containing a selectable word in case of *
6133 long last_word
; /* The last word on the target line. */
6134 long s
, e
; /* Starting and ending terminal position of a word. */
6135 int found
; /* 1 if a line could be fond else 0. */
6137 /* Store the initial starting and ending positions of */
6138 /* the word under the cursor. */
6139 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
6140 s
= word_a
[current
].start
;
6141 e
= word_a
[current
].end
;
6143 /* Identify the line number of the first window's line */
6144 /* and the line number of the current line. */
6145 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
6146 start_line
= line_nb_of_word_a
[win
->start
];
6147 cur_line
= line_nb_of_word_a
[current
];
6148 first_selectable_line
= line_nb_of_word_a
[first_selectable
];
6151 nlines
= win
->max_lines
< last_line
+ 1 ? win
->max_lines
: last_line
+ 1;
6153 /* initialise the target line. */
6154 /* """"""""""""""""""""""""""" */
6157 /* Special case if the cursor is already in the line containing the */
6158 /* first selectable word. */
6159 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6160 if (line
== first_selectable_line
)
6162 /* we can't move the cursor up but we still can try to show the */
6163 /* more non selectable words as we can. */
6164 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
6165 if (line
<= start_line
+ nlines
- 1 - page
)
6167 /* We are scrolling one line at a time and the cursor is not in */
6168 /* the last line of the window. */
6169 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
6170 if (start_line
- page
> 0)
6171 /* There is enough remaining line to fill a window. */
6172 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
6175 /* We cannot scroll further. */
6176 /* ''''''''''''''''''''''''' */
6181 /* The cursor is already in the last line of the windows. */
6182 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''' */
6184 /* There is enough remaining line to fill a window. */
6185 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
6186 start_line
= line
- nlines
+ 1;
6188 /* We cannot scroll further. */
6189 /* ''''''''''''''''''''''''' */
6195 if (line
- page
< 0)
6197 /* Trivial case, we are already on the first page */
6198 /* just jump to the first selectable line. */
6199 /* """""""""""""""""""""""""""""""""""""""""""""" */
6200 line
= first_selectable_line
;
6201 last_word
= get_line_last_word(line
, last_line
);
6202 find_best_word_upwards(last_word
, s
, e
);
6206 /* Temporarily move up one page. */
6207 /* """"""""""""""""""""""""""""" */
6210 /* The target line cannot be before the line containing the first */
6211 /* selectable word. */
6212 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6213 if (line
< first_selectable_line
)
6215 line
= first_selectable_line
;
6216 last_word
= get_line_last_word(line
, last_line
);
6217 find_best_word_upwards(last_word
, s
, e
);
6221 /* If this is not the case, search upwards for the line with a */
6222 /* selectable word. This line is guaranteed to exist. */
6223 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6224 while (line
>= first_selectable_line
)
6226 last_word
= get_line_last_word(line
, last_line
);
6228 if (find_best_word_upwards(last_word
, s
, e
))
6241 /* Look if we need to adjust the window to follow the cursor. */
6242 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6243 if (!found
&& start_line
- page
>= 0)
6245 /* We are on the first line containing a selectable word and */
6246 /* There is enough place to scroll up a page but only scrolls */
6247 /* up if the cursor remains in the window. */
6248 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6249 if (start_line
+ nlines
- line
> page
)
6252 else if (line
< start_line
)
6254 /* The target line is above the windows. */
6255 /* """"""""""""""""""""""""""""""""""""" */
6256 if (start_line
- page
- lines_skipped
< 0)
6257 /* There isn't enough remaining lines to scroll up */
6259 /* """"""""""""""""""""""""""""""""""""""""""""""" */
6262 start_line
-= page
+ lines_skipped
;
6265 /* And set the new value of the starting word of the window. */
6266 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6267 win
->start
= first_word_in_line_a
[start_line
];
6269 /* Set the new first column to display */
6270 /* """"""""""""""""""""""""""""""""""" */
6271 set_new_first_column(win
, term
);
6273 /* Redisplay the window. */
6274 /* """"""""""""""""""""" */
6275 *nl
= disp_lines(win
,
6287 /* =========================== */
6288 /* Moves the cursor downwards. */
6289 /* =========================== */
6291 move_down(win_t
*win
,
6294 search_data_t
*search_data
,
6295 langinfo_t
*langinfo
,
6298 long last_selectable
,
6302 long line
; /* The line being processed (target line). */
6303 long start_line
; /* The first line of the window. */
6304 long cur_line
; /* The line of the cursor. */
6305 long nlines
; /* Number of line in the window. */
6306 long last_selectable_line
; /* the line containing the last *
6307 | selectable word. */
6308 long lines_skipped
; /* The number of line between the target line and the *
6309 | first line containing a selectable word in case of *
6311 long last_word
; /* The last word on the target line. */
6312 long s
, e
; /* Starting and ending terminal position of a word. */
6313 int found
; /* 1 if a line could be fond in the next page else 0. */
6315 /* Store the initial starting and ending positions of */
6316 /* the word under the cursor. */
6317 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
6318 s
= word_a
[current
].start
;
6319 e
= word_a
[current
].end
;
6321 /* Identify the line number of the first window's line */
6322 /* and the line number of the current line. */
6323 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
6324 start_line
= line_nb_of_word_a
[win
->start
];
6325 cur_line
= line_nb_of_word_a
[current
];
6326 last_selectable_line
= line_nb_of_word_a
[last_selectable
];
6329 nlines
= win
->max_lines
< last_line
+ 1 ? win
->max_lines
: last_line
+ 1;
6331 /* initialise the target line. */
6332 /* """"""""""""""""""""""""""" */
6335 /* Special case if the cursor is already in the line containing the */
6336 /* last selectable word. */
6337 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6338 if (line
== last_selectable_line
)
6340 /* we can't move the cursor down but we still can try to show the */
6341 /* more non selectable words as we can. */
6342 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
6343 if (line
>= start_line
+ page
)
6345 /* We are scrolling one line at a time and the cursor is not in */
6346 /* the first line of the window. */
6347 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
6348 if (start_line
+ page
+ nlines
- 1 <= last_line
)
6349 /* There is enough remaining line to fill a window. */
6350 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
6353 /* We cannot scroll further. */
6354 /* ''''''''''''''''''''''''' */
6355 start_line
= last_line
- nlines
+ 1;
6359 /* The cursor is already in the first line of the windows. */
6360 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
6361 if (last_line
- line
+ 1 > nlines
)
6362 /* There is enough remaining line to fill a window. */
6363 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
6366 /* We cannot scroll further. */
6367 /* ''''''''''''''''''''''''' */
6368 start_line
= last_line
- nlines
+ 1;
6373 /* The cursor is above the line containing the last selectable word. */
6374 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6375 if (last_line
- line
- page
< 0)
6377 /* Trivial case, we are already on the last page */
6378 /* just jump to the last selectable line. */
6379 /* """"""""""""""""""""""""""""""""""""""""""""" */
6380 line
= last_selectable_line
;
6381 last_word
= get_line_last_word(line
, last_line
);
6382 find_best_word_downwards(last_word
, s
, e
);
6386 /* Temporarily move down one page. */
6387 /* """"""""""""""""""""""""""""""" */
6390 /* The target line cannot be after the line containing the last */
6391 /* selectable word. */
6392 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6393 if (line
> last_selectable_line
)
6395 line
= last_selectable_line
;
6396 last_word
= get_line_last_word(line
, last_line
);
6397 find_best_word_downwards(last_word
, s
, e
);
6401 /* If this is not the case, search downwards for the line with a */
6402 /* selectable word. This line is guaranteed to exist. */
6403 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6404 while (line
<= last_selectable_line
)
6406 last_word
= get_line_last_word(line
, last_line
);
6408 if (find_best_word_downwards(last_word
, s
, e
))
6420 /* Look if we need to adjust the window to follow the cursor. */
6421 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6422 if (!found
&& start_line
+ nlines
- 1 + page
<= last_line
)
6424 /* We are on the last line containing a selectable word and */
6425 /* There is enough place to scroll down a page but only scrolls */
6426 /* down if the cursor remains in the window. */
6427 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6428 if (line
- start_line
>= page
)
6431 else if (line
> start_line
+ nlines
- 1)
6433 /* The target line is below the windows. */
6434 /* """"""""""""""""""""""""""""""""""""" */
6435 if (start_line
+ nlines
+ page
+ lines_skipped
- 1 > last_line
)
6436 /* There isn't enough remaining lines to scroll down */
6438 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
6439 start_line
= last_line
- nlines
+ 1;
6441 start_line
+= page
+ lines_skipped
;
6445 /* And set the new value of the starting word of the window. */
6446 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6447 win
->start
= first_word_in_line_a
[start_line
];
6449 /* Set the new first column to display. */
6450 /* """""""""""""""""""""""""""""""""""" */
6451 set_new_first_column(win
, term
);
6453 /* Redisplay the window. */
6454 /* """"""""""""""""""""" */
6455 *nl
= disp_lines(win
,
6467 /* ========================================= */
6468 /* Initialize some internal data structures. */
6469 /* ========================================= */
6471 init_main_ds(attrib_t
*init_attr
,
6483 /* Initial attribute settings. */
6484 /* """"""""""""""""""""""""""" */
6485 init_attr
->is_set
= UNSET
;
6488 init_attr
->bold
= (signed char)-1;
6489 init_attr
->dim
= (signed char)-1;
6490 init_attr
->reverse
= (signed char)-1;
6491 init_attr
->standout
= (signed char)-1;
6492 init_attr
->underline
= (signed char)-1;
6493 init_attr
->italic
= (signed char)-1;
6494 init_attr
->invis
= (signed char)-1;
6495 init_attr
->blink
= (signed char)-1;
6497 /* Win fields initialization. */
6498 /* """""""""""""""""""""""""" */
6500 win
->message_lines
= 0;
6501 win
->asked_max_lines
= -1;
6509 win
->first_column
= 0;
6510 win
->real_max_width
= 0;
6511 win
->sb_column
= -1;
6512 win
->hbar_displayed
= 0;
6514 win
->cursor_attr
= *init_attr
;
6515 win
->cursor_marked_attr
= *init_attr
;
6516 win
->cursor_on_marked_attr
= *init_attr
;
6517 win
->cursor_on_tag_attr
= *init_attr
;
6518 win
->cursor_on_tag_marked_attr
= *init_attr
;
6519 win
->marked_attr
= *init_attr
;
6520 win
->bar_attr
= *init_attr
;
6521 win
->shift_attr
= *init_attr
;
6522 win
->message_attr
= *init_attr
;
6523 win
->search_field_attr
= *init_attr
;
6524 win
->search_text_attr
= *init_attr
;
6525 win
->search_err_field_attr
= *init_attr
;
6526 win
->search_err_text_attr
= *init_attr
;
6527 win
->match_field_attr
= *init_attr
;
6528 win
->match_text_attr
= *init_attr
;
6529 win
->match_err_field_attr
= *init_attr
;
6530 win
->match_err_text_attr
= *init_attr
;
6531 win
->include_attr
= *init_attr
;
6532 win
->exclude_attr
= *init_attr
;
6533 win
->tag_attr
= *init_attr
;
6534 win
->daccess_attr
= *init_attr
;
6536 win
->next_tag_id
= 1; /* No word is currently tagged. */
6537 win
->sel_sep
= NULL
;
6539 for (i
= 0; i
< 9; i
++)
6540 win
->special_attr
[i
] = *init_attr
;
6542 /* Default limits initialization. */
6543 /* """""""""""""""""""""""""""""" */
6544 limits
->words
= 32767;
6546 limits
->word_length
= 512;
6548 /* Default timers in 1/10 s. */
6549 /* """"""""""""""""""""""""" */
6550 timers
->search
= 100 * FREQ
/ 10;
6551 timers
->forgotten
= -1; /* Disabled by default. */
6552 timers
->help
= 300 * FREQ
/ 10;
6553 timers
->winch
= 20 * FREQ
/ 10;
6554 timers
->direct_access
= 6 * FREQ
/ 10;
6556 /* Toggles initialization. */
6557 /* """"""""""""""""""""""" */
6558 toggles
->del_line
= 0;
6559 toggles
->enter_val_in_search
= 0;
6560 toggles
->no_scrollbar
= 0;
6561 toggles
->blank_nonprintable
= 0;
6562 toggles
->keep_spaces
= 0;
6563 toggles
->taggable
= 0;
6564 toggles
->autotag
= 0;
6565 toggles
->noautotag
= 0;
6566 toggles
->pinable
= 0;
6567 toggles
->visual_bell
= 0;
6568 toggles
->incremental_search
= 0;
6569 toggles
->no_mouse
= 0;
6570 toggles
->show_blank_words
= 0;
6571 toggles
->cols_first
= 0;
6572 toggles
->rows_first
= 0;
6574 /* Misc default values. */
6575 /* """""""""""""""""""" */
6576 misc
->default_search_method
= NONE
;
6577 misc
->ignore_quotes
= 0;
6578 misc
->invalid_char_substitute
= '.';
6579 misc
->blank_char_substitute
= '_';
6583 mouse
->button
[0] = 1;
6584 mouse
->button
[1] = 2;
6585 mouse
->button
[2] = 3;
6587 mouse
->double_click_delay
= 150;
6589 /* Set the default timeout to 0 (no expiration). */
6590 /* """"""""""""""""""""""""""""""""""""""""""""" */
6591 timeout
->initial_value
= 0;
6592 timeout
->remain
= 0;
6593 timeout
->reached
= 0;
6595 /* Initialize Direct Access settings. */
6596 /* """""""""""""""""""""""""""""""""" */
6597 daccess
->mode
= DA_TYPE_NONE
;
6598 daccess
->left
= xstrdup(" ");
6599 daccess
->right
= xstrdup(")");
6600 daccess
->alignment
= 'r';
6601 daccess
->padding
= 'a';
6602 daccess
->head
= 'k'; /* Keep by default. */
6603 daccess
->length
= -2;
6604 daccess
->flength
= 0;
6605 daccess
->offset
= 0;
6608 daccess
->ignore
= 0;
6609 daccess
->follow
= 'y';
6610 daccess
->missing
= 'y';
6611 daccess
->num_sep
= NULL
;
6612 daccess
->def_number
= -1;
6615 /* *********************************** */
6616 /* ctxopt contexts callback functions. */
6617 /* *********************************** */
6619 /* ******************************** */
6620 /* ctxopt option callback function. */
6621 /* ******************************** */
6624 help_action(char *ctx_name
,
6634 if (strcmp(ctx_name
, "Columns") == 0)
6636 else if (strcmp(ctx_name
, "Lines") == 0)
6638 else if (strcmp(ctx_name
, "Tabulations") == 0)
6640 else if (strcmp(ctx_name
, "Tagging") == 0)
6649 long_help_action(char *ctx_name
,
6659 ctxopt_disp_usage(continue_after
);
6661 printf("\nRead the manual for more information.\n");
6667 usage_action(char *ctx_name
,
6677 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
6681 lines_action(char *ctx_name
,
6691 win_t
*win
= opt_data
[0];
6694 /* No need to validate if values are numeric here, they have */
6695 /* already been validated by the check_integer_constraint filter. */
6696 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6697 sscanf(values
[0], "%d", &(win
->asked_max_lines
));
6699 win
->asked_max_lines
= 0;
6703 tab_mode_action(char *ctx_name
,
6713 win_t
*win
= opt_data
[0];
6719 /* No need to validate if values are numeric or out of range here, */
6720 /* they have already been validated by the check_integer_constraint */
6721 /* and ctxopt_range_constraint filters. */
6722 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6723 sscanf(values
[0], "%ld", &max_cols
);
6724 win
->max_cols
= max_cols
;
6733 set_pattern_action(char *ctx_name
,
6743 char **pattern
= opt_data
[0];
6744 misc_t
*misc
= opt_data
[1];
6746 *pattern
= xstrdup(values
[0]);
6747 utf8_interpret(*pattern
, misc
->invalid_char_substitute
);
6751 int_action(char *ctx_name
,
6761 char **string
= opt_data
[0];
6762 int *shell_like
= opt_data
[1];
6763 langinfo_t
*langinfo
= opt_data
[2];
6764 misc_t
*misc
= opt_data
[3];
6768 *string
= xstrdup(values
[0]);
6769 if (!langinfo
->utf8
)
6770 utf8_sanitize(*string
, misc
->invalid_char_substitute
);
6771 utf8_interpret(*string
, misc
->invalid_char_substitute
);
6778 set_string_action(char *ctx_name
,
6788 char **string
= opt_data
[0];
6789 langinfo_t
*langinfo
= opt_data
[1];
6790 misc_t
*misc
= opt_data
[2];
6792 *string
= xstrdup(values
[0]);
6793 if (!langinfo
->utf8
)
6794 utf8_sanitize(*string
, misc
->invalid_char_substitute
);
6795 utf8_interpret(*string
, misc
->invalid_char_substitute
);
6799 wide_mode_action(char *ctx_name
,
6809 win_t
*win
= opt_data
[0];
6815 center_mode_action(char *ctx_name
,
6825 win_t
*win
= opt_data
[0];
6831 columns_select_action(char *ctx_name
,
6842 ll_t
**cols_selector_list
= opt_data
[0];
6843 toggle_t
*toggles
= opt_data
[1];
6845 if (*cols_selector_list
== NULL
)
6846 *cols_selector_list
= ll_new();
6848 if (toggles
->cols_first
== 0 && toggles
->rows_first
== 0)
6849 toggles
->cols_first
= 1;
6851 for (v
= 0; v
< nb_values
; v
++)
6852 ll_append(*cols_selector_list
, xstrdup(values
[v
]));
6856 rows_select_action(char *ctx_name
,
6867 ll_t
**rows_selector_list
= opt_data
[0];
6868 win_t
*win
= opt_data
[1];
6869 toggle_t
*toggles
= opt_data
[2];
6871 if (*rows_selector_list
== NULL
)
6872 *rows_selector_list
= ll_new();
6874 if (toggles
->cols_first
== 0 && toggles
->rows_first
== 0)
6875 toggles
->rows_first
= 1;
6877 for (v
= 0; v
< nb_values
; v
++)
6878 ll_append(*rows_selector_list
, xstrdup(values
[v
]));
6880 win
->max_cols
= 0; /* Disable the window column restriction. */
6884 aligns_select_action(char *ctx_name
,
6895 ll_t
**aligns_selector_list
= opt_data
[0];
6897 if (*aligns_selector_list
== NULL
)
6898 *aligns_selector_list
= ll_new();
6900 for (v
= 0; v
< nb_values
; v
++)
6901 ll_append(*aligns_selector_list
, xstrdup(values
[v
]));
6905 toggle_action(char *ctx_name
,
6915 toggle_t
*toggles
= opt_data
[0];
6917 if (strcmp(opt_name
, "clean") == 0)
6918 toggles
->del_line
= 1;
6919 else if (strcmp(opt_name
, "keep_spaces") == 0)
6920 toggles
->keep_spaces
= 1;
6921 else if (strcmp(opt_name
, "visual_bell") == 0)
6922 toggles
->visual_bell
= 1;
6923 else if (strcmp(opt_name
, "validate_in_search_mode") == 0)
6924 toggles
->enter_val_in_search
= 1;
6925 else if (strcmp(opt_name
, "blank_nonprintable") == 0)
6926 toggles
->blank_nonprintable
= 1;
6927 else if (strcmp(opt_name
, "no_scroll_bar") == 0)
6929 toggles
->no_scrollbar
= 1;
6930 toggles
->no_hor_scrollbar
= 1;
6932 else if (strcmp(opt_name
, "no_hor_scroll_bar") == 0)
6934 if (!toggles
->no_scrollbar
)
6935 toggles
->no_hor_scrollbar
= 1;
6937 else if (strcmp(opt_name
, "auto_tag") == 0)
6938 toggles
->autotag
= 1;
6939 else if (strcmp(opt_name
, "no_auto_tag") == 0)
6940 toggles
->noautotag
= 1;
6941 else if (strcmp(opt_name
, "incremental_search") == 0)
6942 toggles
->incremental_search
= 1;
6943 else if (strcmp(opt_name
, "no_mouse") == 0)
6944 toggles
->no_mouse
= 1;
6948 invalid_char_action(char *ctx_name
,
6958 misc_t
*misc
= opt_data
[0];
6960 char ic
= *values
[0];
6963 misc
->invalid_char_substitute
= ic
;
6965 misc
->invalid_char_substitute
= '.';
6969 show_blank_action(char *ctx_name
,
6979 toggle_t
*toggles
= opt_data
[0];
6980 misc_t
*misc
= opt_data
[1];
6984 toggles
->show_blank_words
= 1;
6988 bc
= (unsigned char)*values
[0];
6991 misc
->blank_char_substitute
= bc
;
6993 misc
->blank_char_substitute
= '_';
6998 gutter_action(char *ctx_name
,
7008 win_t
*win
= opt_data
[0];
7009 langinfo_t
*langinfo
= opt_data
[1];
7010 misc_t
*misc
= opt_data
[2];
7014 /* As there is no argument, the gutter array will only contain */
7015 /* a vertical bar. */
7016 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7017 win
->gutter_a
= xmalloc(1 * sizeof(char *));
7020 win
->gutter_a
[0] = xstrdup(vertical_bar
);
7022 win
->gutter_a
[0] = xstrdup("|");
7028 /* The argument is used to feed the gutter array, each of its character */
7029 /* Will serve as gutter in a round-robin way. */
7030 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7036 gutter
= xstrdup(values
[0]);
7038 utf8_interpret(gutter
,
7039 misc
->invalid_char_substitute
); /* Guarantees a well *
7040 | formed UTF-8 string. */
7042 win
->gutter_nb
= utf8_strlen(gutter
);
7043 win
->gutter_a
= xmalloc(win
->gutter_nb
* sizeof(char *));
7047 for (i
= 0; i
< win
->gutter_nb
; i
++)
7051 mblength
= utf8_get_length(*(gutter
+ offset
));
7052 win
->gutter_a
[i
] = xcalloc(1, mblength
+ 1);
7053 memcpy(win
->gutter_a
[i
], gutter
+ offset
, mblength
);
7055 n
= wcswidth((w
= utf8_strtowcs(win
->gutter_a
[i
])), 1);
7060 fprintf(stderr
, "%s: A multi columns gutter is not allowed.\n", param
);
7061 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7067 win
->col_sep
= 1; /* Activate the gutter. */
7071 column_mode_action(char *ctx_name
,
7081 win_t
*win
= opt_data
[0];
7090 line_mode_action(char *ctx_name
,
7100 win_t
*win
= opt_data
[0];
7109 include_re_action(char *ctx_name
,
7119 int *pattern_def_include
= opt_data
[0];
7120 char **include_pattern
= opt_data
[1];
7121 misc_t
*misc
= opt_data
[2];
7123 /* Set the default behaviour if not already set. */
7124 /* """"""""""""""""""""""""""""""""""""""""""""" */
7125 if (*pattern_def_include
== -1)
7126 *pattern_def_include
= 0;
7128 if (*include_pattern
== NULL
)
7129 *include_pattern
= concat("(", values
[0], ")", (char *)0);
7131 *include_pattern
= concat(*include_pattern
,
7137 /* Replace the UTF-8 ASCII representations by their binary values in */
7138 /* inclusion patterns. */
7139 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7140 utf8_interpret(*include_pattern
, misc
->invalid_char_substitute
);
7144 exclude_re_action(char *ctx_name
,
7154 int *pattern_def_include
= opt_data
[0];
7155 char **exclude_pattern
= opt_data
[1];
7156 misc_t
*misc
= opt_data
[2];
7158 /* Set the default behaviour if not already set. */
7159 /* """"""""""""""""""""""""""""""""""""""""""""" */
7160 if (*pattern_def_include
== -1)
7161 *pattern_def_include
= 1;
7163 if (*exclude_pattern
== NULL
)
7164 *exclude_pattern
= concat("(", values
[0], ")", (char *)0);
7166 *exclude_pattern
= concat(*exclude_pattern
,
7172 /* Replace the UTF-8 ASCII representations by their binary values in */
7173 /* exclusion patterns. */
7174 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7175 utf8_interpret(*exclude_pattern
, misc
->invalid_char_substitute
);
7179 post_subst_action(char *ctx_name
,
7189 ll_t
**list
= opt_data
[0];
7190 misc_t
*misc
= opt_data
[1];
7197 for (i
= 0; i
< nb_values
; i
++)
7201 sed_node
= xmalloc(sizeof(sed_t
));
7202 sed_node
->pattern
= xstrdup(values
[i
]);
7203 utf8_interpret(sed_node
->pattern
, misc
->invalid_char_substitute
);
7205 ll_append(*list
, sed_node
);
7210 special_level_action(char *ctx_name
,
7220 char **special_pattern
= opt_data
[0];
7221 win_t
*win
= opt_data
[1];
7222 term_t
*term
= opt_data
[2];
7223 attrib_t
*init_attr
= opt_data
[3];
7224 misc_t
*misc
= opt_data
[4];
7226 attrib_t attr
= *init_attr
;
7227 char opt
= param
[strlen(param
) - 1]; /* last character of param. */
7230 special_pattern
[opt
- '1'] = xstrdup(values
[0]);
7231 utf8_interpret(special_pattern
[opt
- '1'], misc
->invalid_char_substitute
);
7233 /* Parse optional additional arguments. */
7234 /* """""""""""""""""""""""""""""""""""" */
7235 for (i
= 1; i
< nb_values
; i
++)
7237 /* Colors must respect the format: <fg color>/<bg color>. */
7238 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
7239 if (parse_attr(values
[i
], &attr
, term
->colors
))
7241 win
->special_attr
[opt
- '1'].is_set
= FORCED
;
7242 win
->special_attr
[opt
- '1'].fg
= attr
.fg
;
7243 win
->special_attr
[opt
- '1'].bg
= attr
.bg
;
7244 win
->special_attr
[opt
- '1'].bold
= attr
.bold
;
7245 win
->special_attr
[opt
- '1'].dim
= attr
.dim
;
7246 win
->special_attr
[opt
- '1'].reverse
= attr
.reverse
;
7247 win
->special_attr
[opt
- '1'].standout
= attr
.standout
;
7248 win
->special_attr
[opt
- '1'].underline
= attr
.underline
;
7249 win
->special_attr
[opt
- '1'].italic
= attr
.italic
;
7250 win
->special_attr
[opt
- '1'].invis
= attr
.invis
;
7251 win
->special_attr
[opt
- '1'].blink
= attr
.blink
;
7257 attributes_action(char *ctx_name
,
7267 win_t
*win
= opt_data
[0];
7268 term_t
*term
= opt_data
[1];
7269 attrib_t
*init_attr
= opt_data
[2];
7271 long i
, a
; /* loop index. */
7272 long offset
= 0; /* nb of chars to ship to find the attribute *
7273 | representation (prefix size). */
7276 attrib_t
*attr_to_set
= NULL
;
7278 /* Flags to check if an attribute is already set */
7279 /* """"""""""""""""""""""""""""""""""""""""""""" */
7280 int inc_attr_set
= 0; /* included words. */
7281 int exc_attr_set
= 0; /* excluded words. */
7282 int cur_attr_set
= 0; /* highlighted word (cursor). */
7283 int bar_attr_set
= 0; /* scroll bar. */
7284 int shift_attr_set
= 0; /* horizontal scrolling arrows. */
7285 int message_attr_set
= 0; /* message (title). */
7286 int tag_attr_set
= 0; /* selected (tagged) words. */
7287 int cursor_on_tag_attr_set
= 0; /* selected words under the cursor. */
7288 int sf_attr_set
= 0; /* currently searched field color. */
7289 int st_attr_set
= 0; /* currently searched text color. */
7290 int mf_attr_set
= 0; /* matching word field color. */
7291 int mt_attr_set
= 0; /* matching word text color. */
7292 int mfe_attr_set
= 0; /* matching word with error field color. */
7293 int mte_attr_set
= 0; /* matching word with error text color. */
7294 int daccess_attr_set
= 0; /* Direct access text color. */
7296 /* Information relatives to the attributes to be searched and set. */
7297 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7306 { &win
->exclude_attr
,
7307 "The exclude attribute is already set.",
7311 { &win
->include_attr
,
7312 "The include attribute is already set.",
7316 { &win
->cursor_attr
,
7317 "The cursor attribute is already set.",
7322 "The scroll bar attribute is already set.",
7327 "The shift attribute is already set.",
7331 { &win
->message_attr
,
7332 "The message attribute is already set.",
7337 "The tag attribute is already set.",
7341 { &win
->cursor_on_tag_attr
,
7342 "The cursor on tagged word attribute is already set.",
7343 &cursor_on_tag_attr_set
,
7346 { &win
->search_field_attr
,
7347 "The search field attribute is already set.",
7351 { &win
->search_text_attr
,
7352 "The search text attribute is already set.",
7356 { &win
->search_err_field_attr
,
7357 "The search with error field attribute is already set.",
7361 { &win
->search_err_text_attr
,
7362 "The search text with error attribute is already set.",
7366 { &win
->match_field_attr
,
7367 "The matching word field attribute is already set.",
7371 { &win
->match_text_attr
,
7372 "The matching word text attribute is already set.",
7376 { &win
->match_err_field_attr
,
7377 "The matching word with error field attribute is already set.",
7381 { &win
->match_err_text_attr
,
7382 "The matching word with error text attribute is already set.",
7386 { &win
->daccess_attr
,
7387 "The direct access tag attribute is already set.",
7391 { NULL
, NULL
, NULL
, NULL
, 0 }
7394 /* Parse the arguments. */
7395 /* The syntax of the attributes has already been pre validated by the */
7396 /* ctxopt_re_constraint filter. */
7397 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7398 for (a
= 0; a
< nb_values
; a
++)
7403 while (attr_infos
[i
].flag
!= NULL
)
7405 if (strncmp(values
[a
], attr_infos
[i
].prefix
, attr_infos
[i
].prefix_len
)
7408 if (*attr_infos
[i
].flag
)
7410 fprintf(stderr
, "%s: ", param
);
7411 fputs_safe(attr_infos
[i
].msg
, stderr
);
7412 fputs_safe("\n", stderr
);
7413 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7416 attr_to_set
= attr_infos
[i
].attr
;
7417 *attr_infos
[i
].flag
= 1;
7418 offset
= attr_infos
[i
].prefix_len
;
7419 break; /* We have found a matching prefix, *
7420 | no need to continue. */
7424 if (attr_infos
[i
].flag
== NULL
)
7426 fprintf(stderr
, "%s: Bad attribute prefix in %s\n", param
, values
[a
]);
7427 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7430 /* Attributes must respect the format: */
7431 /* <fg color>/<bg color>,<styles>. */
7432 /* """"""""""""""""""""""""""""""""""" */
7433 if (parse_attr(values
[a
] + offset
, &attr
, term
->colors
))
7435 attr_to_set
->is_set
= FORCED
;
7436 attr_to_set
->fg
= attr
.fg
;
7437 attr_to_set
->bg
= attr
.bg
;
7438 attr_to_set
->bold
= attr
.bold
;
7439 attr_to_set
->dim
= attr
.dim
;
7440 attr_to_set
->reverse
= attr
.reverse
;
7441 attr_to_set
->standout
= attr
.standout
;
7442 attr_to_set
->underline
= attr
.underline
;
7443 attr_to_set
->italic
= attr
.italic
;
7444 attr_to_set
->invis
= attr
.invis
;
7445 attr_to_set
->blink
= attr
.blink
;
7449 fprintf(stderr
, "%s: Bad attribute settings %s\n", param
, values
[a
]);
7450 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7456 limits_action(char *ctx_name
,
7466 limit_t
*limits
= opt_data
[0];
7475 /* Parse the arguments. */
7476 /* """""""""""""""""""" */
7477 for (l
= 0; l
< nb_values
; l
++)
7485 case 'l': /* word length. */
7486 val
= strtol(p
, &endptr
, 0);
7487 if (errno
== ERANGE
|| (val
== 0 && errno
!= 0) || endptr
== p
7490 fprintf(stderr
, "%s: Invalid word length limit. ", p
);
7492 "Using the default value: %ld\n",
7493 limits
->word_length
);
7496 limits
->word_length
= val
;
7499 case 'w': /* max number of words. */
7500 val
= strtol(p
, &endptr
, 0);
7501 if (errno
== ERANGE
|| (val
== 0 && errno
!= 0) || endptr
== p
7504 fprintf(stderr
, "%s: Invalid words number limit. ", p
);
7505 fprintf(stderr
, "Using the default value: %ld\n", limits
->words
);
7508 limits
->words
= val
;
7511 case 'c': /* max number of words. */
7512 val
= strtol(p
, &endptr
, 0);
7513 if (errno
== ERANGE
|| (val
== 0 && errno
!= 0) || endptr
== p
7516 fprintf(stderr
, "%s: Invalid columns number limit. ", p
);
7517 fprintf(stderr
, "Using the default value: %ld\n", limits
->cols
);
7524 fprintf(stderr
, "%s: Invalid limit keyword, should be l, w or c.\n", p
);
7531 copyright_action(char *ctx_name
,
7541 fputs_safe("Copyright 2015 - Pierre Gentile <p.gen.progs@gmail.com> - "
7549 version_action(char *ctx_name
,
7559 fputs_safe("Version: " VERSION
"\n", stdout
);
7565 timeout_action(char *ctx_name
,
7575 misc_t
*misc
= opt_data
[0];
7577 if (strcmp(opt_name
, "hidden_timeout") == 0)
7580 if (strprefix("current", values
[0]))
7581 timeout
.mode
= CURRENT
;
7582 else if (strprefix("quit", values
[0]))
7583 timeout
.mode
= QUIT
;
7584 else if (strprefix("word", values
[0]))
7588 timeout
.mode
= WORD
;
7589 timeout_word
= xstrdup(values
[1]);
7590 utf8_interpret(timeout_word
, misc
->invalid_char_substitute
);
7594 fprintf(stderr
, "%s: Missing timeout selected word or delay.\n", param
);
7595 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7599 if (sscanf(values
[nb_values
- 1], "%5u", &timeout
.initial_value
) == 1)
7601 timeout
.initial_value
*= FREQ
;
7602 timeout
.remain
= timeout
.initial_value
;
7606 fprintf(stderr
, "%s: Invalid timeout delay.\n", param
);
7607 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7612 tag_mode_action(char *ctx_name
,
7622 toggle_t
*toggles
= opt_data
[0];
7623 win_t
*win
= opt_data
[1];
7624 misc_t
*misc
= opt_data
[2];
7626 toggles
->taggable
= 1;
7630 win
->sel_sep
= xstrdup(values
[0]);
7631 utf8_interpret(win
->sel_sep
, misc
->invalid_char_substitute
);
7636 pin_mode_action(char *ctx_name
,
7646 toggle_t
*toggles
= opt_data
[0];
7647 win_t
*win
= opt_data
[1];
7648 misc_t
*misc
= opt_data
[2];
7650 toggles
->taggable
= 1;
7651 toggles
->pinable
= 1;
7655 win
->sel_sep
= xstrdup(values
[0]);
7656 utf8_interpret(win
->sel_sep
, misc
->invalid_char_substitute
);
7661 search_method_action(char *ctx_name
,
7671 misc_t
*misc
= opt_data
[0];
7673 if (strprefix("prefix", values
[0]))
7674 misc
->default_search_method
= PREFIX
;
7675 else if (strprefix("fuzzy", values
[0]))
7676 misc
->default_search_method
= FUZZY
;
7677 else if (strprefix("substring", values
[0]))
7678 misc
->default_search_method
= SUBSTRING
;
7681 fprintf(stderr
, "%s: Bad search method: %s\n", param
, values
[0]);
7682 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7687 auto_da_action(char *ctx_name
,
7697 char **daccess_pattern
= opt_data
[0];
7702 if (daccess
.missing
== 'y' || ((daccess
.mode
& DA_TYPE_POS
) == 0))
7704 if (*daccess_pattern
== NULL
)
7706 *daccess_pattern
= xstrdup("(.)");
7707 daccess
.mode
|= DA_TYPE_AUTO
; /* Auto. */
7710 *daccess_pattern
= concat(*daccess_pattern
, "|(.)", (char *)0);
7714 for (i
= 0; i
< nb_values
; i
++)
7718 if (*values
[i
] == '\0'
7719 && (daccess
.missing
== 'y' || ((daccess
.mode
& DA_TYPE_POS
) == 0)))
7724 if (*daccess_pattern
== NULL
)
7726 *daccess_pattern
= concat("(", value
, ")", (char *)0);
7727 daccess
.mode
|= DA_TYPE_AUTO
; /* Auto. */
7730 *daccess_pattern
= concat(*daccess_pattern
,
7737 if (daccess
.def_number
< 0)
7739 if (strcmp(param
, "-N") == 0)
7740 daccess
.def_number
= 0; /* Words are unnumbered by default. */
7742 daccess
.def_number
= 1; /* Words are numbered by default. */
7747 field_da_number_action(char *ctx_name
,
7757 daccess
.mode
|= DA_TYPE_POS
;
7761 da_options_action(char *ctx_name
,
7771 long *daccess_index
= opt_data
[0];
7772 misc_t
*misc
= opt_data
[1];
7779 /* Parse optional additional arguments. */
7780 /* The syntax of the arguments has already been pre validated by the */
7781 /* ctxopt_re_constraint filter. */
7782 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7783 for (i
= 0; i
< nb_values
; i
++)
7785 char *value
= values
[i
];
7789 case 'l': /* Left char .*/
7792 daccess
.left
= xstrdup(value
+ 2);
7793 utf8_interpret(daccess
.left
, misc
->invalid_char_substitute
);
7795 if (utf8_strlen(daccess
.left
) != 1)
7797 fprintf(stderr
, "%s: Too many characters after l:\n", param
);
7798 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7801 n
= wcswidth((w
= utf8_strtowcs(daccess
.left
)), 1);
7807 "%s: A multi columns character is not allowed "
7810 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7814 case 'r': /* Right char. */
7815 free(daccess
.right
);
7817 daccess
.right
= xstrdup(value
+ 2);
7818 utf8_interpret(daccess
.right
, misc
->invalid_char_substitute
);
7820 if (utf8_strlen(daccess
.right
) != 1)
7822 fprintf(stderr
, "%s: Too many characters after r:\n", param
);
7823 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7826 n
= wcswidth((w
= utf8_strtowcs(daccess
.right
)), 1);
7832 "%s: A multi columns character is not allowed "
7835 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7839 case 'a': /* Alignment. */
7840 if (strprefix("left", value
+ 2))
7841 daccess
.alignment
= 'l';
7842 else if (strprefix("right", value
+ 2))
7843 daccess
.alignment
= 'r';
7847 "%s: The value after a: must be "
7848 "l(eft) or r(ight)\n",
7850 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7854 case 'p': /* Padding. */
7855 if (strprefix("all", value
+ 2))
7856 daccess
.padding
= 'a';
7857 else if (strprefix("included", value
+ 2))
7858 daccess
.padding
= 'i';
7861 fprintf(stderr
, "%s: Bad value after p:\n", param
);
7862 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7866 case 'w': /* Width. */
7867 if (sscanf(value
+ 2, "%d%n", &daccess
.length
, &pos
) != 1)
7869 fprintf(stderr
, "%s: Bad value after w:\n", param
);
7870 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7872 if (value
[pos
+ 2] != '\0')
7874 fprintf(stderr
, "%s: Bad value after w:\n", param
);
7875 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7877 if (daccess
.length
<= 0 || daccess
.length
> 5)
7879 fprintf(stderr
, "%s: w sub-option must be between 1 and 5\n", param
);
7880 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7884 case 'o': /* Start offset. */
7885 if (sscanf(value
+ 2, "%zu%n+", &daccess
.offset
, &pos
) == 1)
7887 if (value
[pos
+ 2] == '+')
7891 if (value
[pos
+ 3] != '\0')
7893 fprintf(stderr
, "%s: Bad value after o:\n", param
);
7894 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7897 else if (value
[pos
+ 2] != '\0')
7899 fprintf(stderr
, "%s: Bad value after o:\n", param
);
7900 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7905 fprintf(stderr
, "%s: Bad value after o:\n", param
);
7906 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7911 case 'n': /* Number of digits to extract. */
7912 if (sscanf(value
+ 2, "%d%n", &daccess
.size
, &pos
) != 1)
7914 fprintf(stderr
, "%s: Bad value after n:\n", param
);
7915 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7917 if (value
[pos
+ 2] != '\0')
7919 fprintf(stderr
, "%s: Bad value after n:\n", param
);
7920 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7922 if (daccess
.size
<= 0 || daccess
.size
> 5)
7924 fprintf(stderr
, "n sub-option must have a value between 1 and 5.\n");
7925 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7929 case 'i': /* Number of UTF-8 glyphs to ignore after the *
7930 | selector to extract. */
7931 if (sscanf(value
+ 2, "%zu%n", &daccess
.ignore
, &pos
) != 1)
7933 fprintf(stderr
, "%s: Bad value after i:\n", param
);
7934 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7936 if (value
[pos
+ 2] != '\0')
7938 fprintf(stderr
, "%s: Bad value after i:\n", param
);
7939 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7943 case 'f': /* Follow. */
7944 if (strprefix("yes", value
+ 2))
7945 daccess
.follow
= 'y';
7946 else if (strprefix("no", value
+ 2))
7947 daccess
.follow
= 'n';
7950 fprintf(stderr
, "%s: Bad value after f:\n", param
);
7951 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7955 case 'm': /* Possibly number missing embedded numbers. */
7956 if (strprefix("yes", value
+ 2))
7957 daccess
.missing
= 'y';
7958 else if (strprefix("no", value
+ 2))
7959 daccess
.missing
= 'n';
7962 fprintf(stderr
, "%s: Bad value after m:\n", param
);
7963 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7967 case 'd': /* Decorate. */
7968 free(daccess
.num_sep
);
7970 daccess
.num_sep
= xstrdup(value
+ 2);
7971 utf8_interpret(daccess
.num_sep
, misc
->invalid_char_substitute
);
7973 if (utf8_strlen(daccess
.num_sep
) != 1)
7975 fprintf(stderr
, "%s: Too many characters after d:\n", param
);
7976 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7979 n
= wcswidth((w
= utf8_strtowcs(daccess
.num_sep
)), 1);
7985 "%s: A multi columns separator is not allowed "
7988 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
7992 case 's': /* Start index. */
7996 if (sscanf(value
+ 2, "%ld%ln", daccess_index
, &index
) == 1)
7998 if (*daccess_index
< 0 || *(value
+ 2 + index
) != '\0')
8003 fprintf(stderr
, "%s: Invalid first index after s:\n", param
);
8004 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
8009 case 'h': /* Head. */
8010 if (strprefix("trim", value
+ 2))
8012 else if (strprefix("cut", value
+ 2))
8014 else if (strprefix("keep", value
+ 2))
8018 fprintf(stderr
, "%s: Bad value after :h\n", param
);
8019 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
8025 fprintf(stderr
, "%s: Bad sub-command: %s\n", param
, value
);
8026 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
8030 if (daccess
.length
<= 0 || daccess
.length
> 5)
8031 daccess
.length
= -2; /* special value -> auto. */
8036 ignore_quotes_action(char *ctx_name
,
8046 misc_t
*misc
= opt_data
[0];
8048 misc
->ignore_quotes
= 1;
8052 forgotten_action(char *ctx_name
,
8062 ticker_t
*timers
= opt_data
[0];
8067 size_t l
= strlen(values
[0]);
8069 /* Add a 0 at the end of the parameter to multiply it by 10 */
8070 /* as the timer values must be in tenths of seconds. */
8071 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8073 p
= strcpy(p
, values
[0]);
8077 val
= strtol(p
, &endptr
, 0);
8078 if (errno
== ERANGE
|| (val
== 0 && errno
!= 0) || endptr
== p
8081 fprintf(stderr
, "%s: Invalid timeout delay.\n", values
[0]);
8082 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
8085 timers
->forgotten
= -1; /* Explicitly disable the timers. */
8087 timers
->forgotten
= (int)val
;
8093 button_action(char *ctx_name
,
8103 mouse_t
*mouse
= opt_data
[0];
8111 fprintf(stderr
, "Remapping of buttons 1 and 3 expected.\n");
8112 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
8115 int const index
[2] = { 0, 2 };
8118 mouse
->button
[0] = 0;
8119 mouse
->button
[1] = 0;
8120 mouse
->button
[2] = 0;
8122 for (int i
= 0; i
< nb_values
; i
++)
8127 val
= strtol(p
, &endptr
, 0);
8128 if (errno
== ERANGE
|| endptr
== p
|| *endptr
!= '\0' || val
< 1 || val
> 3)
8130 fprintf(stderr
, "%s: Invalid button number.\n", values
[0]);
8131 ctxopt_ctx_disp_usage(ctx_name
, exit_after
);
8133 mouse
->button
[(int)val
- 1] = ind
+ 1;
8138 double_click_action(char *ctx_name
,
8148 mouse_t
*mouse
= opt_data
[0];
8149 int *ddc
= opt_data
[1];
8152 char *p
= values
[0];
8156 val
= strtol(p
, &endptr
, 0);
8157 if (errno
== ERANGE
|| endptr
== p
|| *endptr
!= '\0')
8158 return; /* default value (150 ~ 1/6.66th second) is set in main(). */
8160 *ddc
= 1; /* disable double_click; */
8161 else if (val
>= 100 && val
<= 500)
8162 mouse
->double_click_delay
= val
;
8165 /* =================================================================== */
8166 /* Cancels a search. Called when ESC is hit or a new search session is */
8167 /* initiated and the incremental_search option is not used. */
8168 /* =================================================================== */
8170 reset_search_buffer(win_t
*win
,
8171 search_data_t
*search_data
,
8176 langinfo_t
*langinfo
,
8179 long word_real_max_size
)
8181 /* ESC key has been pressed. */
8182 /* """"""""""""""""""""""""" */
8183 search_mode_t saved_search_mode
= search_mode
;
8185 /* Cancel the search timer. */
8186 /* """""""""""""""""""""""" */
8189 search_data
->err
= 0;
8190 search_data
->only_starting
= 0;
8191 search_data
->only_ending
= 0;
8205 /* Reset the direct access selector stack. */
8206 /* """"""""""""""""""""""""""""""""""""""" */
8207 memset(daccess_stack
, '\0', 6);
8208 daccess_stack_head
= 0;
8209 daccess_timer
= timers
->direct_access
;
8211 /* Clean the potential matching words non empty list. */
8212 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
8215 if (matches_count
> 0 || saved_search_mode
!= search_mode
)
8217 clean_matches(search_data
, word_real_max_size
);
8232 /* =============================================================== */
8233 /* Detect if the current terminal belongs to the foreground group. */
8234 /* returns 1 if yes else returns 0. */
8235 /* =============================================================== */
8237 is_in_foreground_process_group(void)
8241 fd
= open("/dev/tty", O_RDONLY
);
8245 fg
= (tcgetpgrp(fd
) == getpgid(0));
8252 /* ===================================================================== */
8253 /* Get the new index of a word based on the mouse click position. */
8254 /* Returns a new index if a word could be selected or the original index */
8255 /* The error flag is set if the word was not selectable or if the user */
8256 /* did not click on a word. */
8257 /* ===================================================================== */
8259 get_clicked_index(win_t
*win
,
8265 long new_current
; /* Future new current word on success. */
8266 long clicked_line
; /* Number of the clicked line in smenu internal *
8267 | representation on the screen. */
8268 int delta
; /* Compensation due to the alignment of the first *
8269 | visible word of the selected line in the window. */
8271 /* Make sure the error indicator is initialized. */
8272 /* """"""""""""""""""""""""""""""""""""""""""""" */
8275 /* Get the internal line number of the click. */
8276 /* """""""""""""""""""""""""""""""""""""""""" */
8277 clicked_line
= line_nb_of_word_a
[win
->start
] + line_click
- term
->curs_line
;
8279 /* Ignore clicks after the last word on a line. */
8280 /* """""""""""""""""""""""""""""""""""""""""""" */
8281 if (column_click
>= shift_right_sym_pos_a
[clicked_line
])
8284 new_current
= first_word_in_line_a
[clicked_line
];
8286 if (new_current
== count
- 1)
8287 return new_current
; /* The users clicked on the last word; */
8290 int offset
; /* in column or line mode lines can be larger than the *
8291 | terminal size, in this case a space is added at the *
8292 | start of the lines to possibly put the shift arrow *
8293 | indicator, this space must be taken into account. */
8294 int found
; /* Flag to indicate if a word could have been clicked. */
8296 /* Take into account the presence of shift arrows if any. */
8297 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
8298 if (win
->has_truncated_lines
)
8303 /* Compensates for the offset of the window in the case of a centred */
8304 /* window and the additional space added by the display of truncated */
8306 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8307 column_click
-= win
->offset
;
8309 /* Forbid click on column 1 if the windows has truncated lines. */
8310 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8311 if (column_click
== 1 && offset
== 1)
8314 /* Normalize column_click. */
8315 /* """"""""""""""""""""""" */
8316 column_click
-= offset
;
8318 /* Determine the offset of the first character of the */
8319 /* first visible word in the clicked line. */
8320 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
8321 while (word_a
[new_current
].start
< win
->first_column
)
8325 if (new_current
== count
|| word_a
[new_current
].start
== 0)
8329 delta
= word_a
[new_current
].start
- win
->first_column
;
8331 /* Find the right clicked word if any. */
8332 /* """"""""""""""""""""""""""""""""""" */
8337 if ((column_click
- 1
8338 >= word_a
[new_current
].start
- win
->first_column
- delta
)
8339 && (column_click
- 1
8340 <= word_a
[new_current
].end
- win
->first_column
- delta
))
8348 if (new_current
== count
|| word_a
[new_current
].start
== 0)
8355 new_current
= current
;
8359 if (!word_a
[new_current
].is_selectable
)
8368 /* ==================================================================== */
8369 /* Get the number of the clicked shift arrow if any. */
8370 /* arrow will contain 0 if the left arrow as clicked and 1 if the right */
8371 /* arrow was clicked. */
8372 /* Returns 0 is no arrow wa clicked else 1. */
8373 /* ==================================================================== */
8375 shift_arrow_clicked(win_t
*win
,
8382 /* Get the internal line number of the click. */
8383 /* """""""""""""""""""""""""""""""""""""""""" */
8384 *clicked_line
= line_nb_of_word_a
[win
->start
] + line_click
- term
->curs_line
;
8386 if (column_click
> shift_right_sym_pos_a
[*clicked_line
])
8389 /* Compensates for the offset of the window */
8390 /* in the case of a centred window. */
8391 /* """""""""""""""""""""""""""""""""""""""" */
8392 if (column_click
== 1)
8393 *arrow
= 0; /* Left arrow. */
8394 else if (shift_right_sym_pos_a
[*clicked_line
] < term
->ncolumns
8395 && column_click
== shift_right_sym_pos_a
[*clicked_line
])
8396 *arrow
= 1; /* Right arrow. */
8403 /* ============================================================== */
8404 /* Takes a string with leading and/or trailing spaces and try to */
8405 /* left-align, right-align or center them by re-arranging blanks. */
8406 /* word IN/OUT string to work with. */
8407 /* alignment IN kind of alignments. */
8408 /* ============================================================== */
8410 align_word(word_t
*word
, alignment_t alignment
, size_t prefix
, char sp
)
8421 /* Left align the word. */
8422 /* """""""""""""""""""" */
8423 str
= xstrdup(word
->str
+ prefix
);
8426 while (str
[n
] == sp
)
8429 swap_string_parts(&str
, n
);
8431 strcpy(word
->str
+ prefix
, str
);
8437 /* Right align the word. */
8438 /* """"""""""""""""""""" */
8439 str
= xstrdup(word
->str
+ prefix
);
8440 n
= strlen(str
) - 1;
8442 while (n
&& str
[n
] == sp
)
8445 swap_string_parts(&str
, n
+ 1);
8447 strcpy(word
->str
+ prefix
, str
);
8453 /* Center the word. */
8454 /* """""""""""""""" */
8455 str
= xstrdup(word
->str
+ prefix
);
8460 while (str
[n
] == sp
)
8463 while (m
&& str
[m
] == sp
)
8473 memset(word
->str
+ prefix
, sp
, l
);
8475 strncpy(word
->str
+ prefix
+ (l
- wl
) / 2, str
+ n
, wl
);
8485 /* ================= */
8486 /* Main entry point. */
8487 /* ================= */
8489 main(int argc
, char *argv
[])
8491 /* Mapping of supported charsets and the number of bits used in them. */
8492 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8493 charsetinfo_t all_supported_charsets
[] = {
8496 { "ANSI_X3.4-1968", 7 },
8497 { "ANSI_X3.4-1986", 7 },
8502 { "ISO_646.BASIC", 7 },
8503 { "ISO_646.IRV:1991", 7 },
8504 { "ISO_646.IRV", 7 },
8515 { "ISO-8859-1", 8 },
8516 { "ISO-IR-100", 8 },
8517 { "ISO_8859-1:1987", 8 },
8518 { "ISO_8859-1", 8 },
8525 { "ISO-8859-2", 8 },
8526 { "ISO-IR-101", 8 },
8527 { "ISO_8859-2:1987", 8 },
8528 { "ISO_8859-2", 8 },
8534 { "ISO-8859-3", 8 },
8535 { "ISO-IR-109", 8 },
8536 { "ISO_8859-3:1988", 8 },
8537 { "ISO_8859-3", 8 },
8543 { "ISO-8859-4", 8 },
8544 { "ISO-IR-110", 8 },
8545 { "ISO_8859-4:1988", 8 },
8551 { "ISO-8859-5", 8 },
8552 { "ISO-IR-144", 8 },
8553 { "ISO_8859-5:1988", 8 },
8562 { "ISO-8859-6", 8 },
8563 { "ISO-IR-127", 8 },
8564 { "ISO_8859-6:1987", 8 },
8571 { "ISO-8859-7", 8 },
8572 { "ISO-IR-126", 8 },
8573 { "ISO_8859-7:2003", 8 },
8574 { "ISO_8859-7:1987", 8 },
8582 { "ISO-8859-8", 8 },
8583 { "ISO-IR-138", 8 },
8584 { "ISO_8859-8:1988", 8 },
8589 { "ISO-8859-9", 8 },
8590 { "ISO-IR-148", 8 },
8591 { "ISO_8859-9:1989", 8 },
8596 { "ISO8859-10", 8 },
8597 { "ISO-8859-10", 8 },
8598 { "ISO-IR-157", 8 },
8599 { "ISO_8859-10:1992", 8 },
8604 { "ISO8859-11", 8 },
8605 { "ISO-8859-11", 8 },
8606 { "ISO-8859-11:2001", 8 },
8607 { "ISO-IR-166", 8 },
8613 { "TIS620.2529-1", 8 },
8614 { "TIS620.2533-0", 8 },
8616 /* ISO-8859-12 was abandoned in 1997. */
8617 /* """""""""""""""""""""""""""""""""" */
8619 { "ISO8859-13", 8 },
8620 { "ISO-8859-13", 8 },
8621 { "ISO-IR-179", 8 },
8626 { "ISO8859-14", 8 },
8627 { "ISO-8859-14", 8 },
8631 { "ISO8859-15", 8 },
8632 { "ISO-8859-15", 8 },
8636 { "ISO8859-16", 8 },
8637 { "ISO-8859-16", 8 },
8638 { "ISO-IR-226", 8 },
8639 { "ISO_8859-16:2001", 8 },
8651 /* Terminal setting variables. */
8652 /* """"""""""""""""""""""""""" */
8653 struct termios new_in_attrs
;
8654 struct termios old_in_attrs
;
8656 /* Interval timers used. */
8657 /* """"""""""""""""""""" */
8658 struct itimerval periodic_itv
; /* refresh rate for the timeout counter. */
8660 int nb_rem_args
= 0; /* Remaining non analyzed command line arguments. */
8661 char **rem_args
= NULL
; /* Remaining non analyzed command line *
8662 | arguments array. */
8664 char *message
= NULL
; /* message to be displayed above the selection *
8666 ll_t
*message_lines_list
= NULL
; /* list of the lines in the message to *
8668 long message_max_width
= 0; /* total width of the message (longest line). */
8669 long message_max_len
= 0; /* max number of bytes taken by a message *
8672 char *int_string
= NULL
; /* String to be output when typing ^C. */
8673 int int_as_in_shell
= 1; /* CTRL-C mimics the shell behaviour. */
8675 FILE *input_file
; /* The name of the file passed as argument if any. */
8677 long index
; /* generic counter. */
8679 long daccess_index
= 1; /* First index of the numbered words. */
8681 char *daccess_np
= NULL
; /* direct access numbered pattern. */
8682 regex_t daccess_np_re
; /* variable to store the compiled direct access *
8683 | pattern (-N) RE. */
8685 char *daccess_up
= NULL
; /* direct access not numbered pattern. */
8686 regex_t daccess_up_re
; /* variable to store the compiled direct access *
8687 | pattern (-U) RE. */
8689 char *include_pattern
= NULL
; /* ASCII version of the include RE */
8690 char *exclude_pattern
= NULL
; /* ASCII version of the exclude RE */
8692 int pattern_def_include
= -1; /* Set to -1 until an -i or -e option is *
8693 | specified, This variable remembers if *
8694 | the words not matched will be included *
8695 | (value 1) or excluded (value 0) by *
8698 regex_t include_re
; /* variable to store the compiled include (-i) REs. */
8699 regex_t exclude_re
; /* variable to store the compiled exclude (-e) REs. */
8701 ll_t
*early_sed_list
= NULL
; /* List of sed like string representation *
8702 | of regex given after (-ES). */
8703 ll_t
*sed_list
= NULL
; /* List of sed like string representation *
8704 | of regex given after (-S). */
8705 ll_t
*include_sed_list
= NULL
; /* idem for -I. */
8706 ll_t
*exclude_sed_list
= NULL
; /* idem for -E. */
8708 ll_t
*inc_col_interval_list
= NULL
; /* Lists of included or */
8709 ll_t
*exc_col_interval_list
= NULL
; /* excluded numerical intervals */
8710 ll_t
*inc_row_interval_list
= NULL
; /* for lines and columns. */
8711 ll_t
*exc_row_interval_list
= NULL
;
8713 ll_t
*inc_col_regex_list
= NULL
; /* Same for lines and columns specified. */
8714 ll_t
*exc_col_regex_list
= NULL
; /* by regular expressions. */
8715 ll_t
*inc_row_regex_list
= NULL
;
8716 ll_t
*exc_row_regex_list
= NULL
;
8718 ll_t
*al_col_interval_list
= NULL
; /* Lists of left aligned rows/columns, */
8719 ll_t
*ar_col_interval_list
= NULL
; /* right aligned rows/columns, */
8720 ll_t
*ac_col_interval_list
= NULL
; /* centered rows/columns */
8721 ll_t
*al_row_interval_list
= NULL
;
8722 ll_t
*ar_row_interval_list
= NULL
;
8723 ll_t
*ac_row_interval_list
= NULL
;
8725 ll_t
*at_col_interval_list
= NULL
; /* list of attributes for rows and */
8726 ll_t
*at_row_interval_list
= NULL
; /* columns. */
8728 ll_t
*al_col_regex_list
= NULL
; /* Same for regular expression based */
8729 ll_t
*ar_col_regex_list
= NULL
; /* alignments. */
8730 ll_t
*ac_col_regex_list
= NULL
;
8731 ll_t
*al_row_regex_list
= NULL
;
8732 ll_t
*ar_row_regex_list
= NULL
;
8733 ll_t
*ac_row_regex_list
= NULL
;
8735 ll_t
*at_col_regex_list
= NULL
; /* list of attributes for rows and */
8736 ll_t
*at_row_regex_list
= NULL
; /* columns. */
8738 ll_t
*al_word_regex_list
= NULL
;
8739 ll_t
*ar_word_regex_list
= NULL
;
8740 ll_t
*ac_word_regex_list
= NULL
;
8742 alignment_t default_alignment
= AL_NONE
;
8744 filters_t rows_filter_type
= UNKNOWN_FILTER
;
8746 char *first_word_pattern
= NULL
; /* used by -A/-Z. */
8747 char *last_word_pattern
= NULL
;
8748 regex_t first_word_re
; /* to hold the compiled first words RE. */
8749 regex_t last_word_re
; /* to hold the compiled last words RE. */
8751 char *special_pattern
[9] = { NULL
, NULL
, NULL
, NULL
, NULL
,
8752 NULL
, NULL
, NULL
, NULL
}; /* -1 .. -9 */
8753 regex_t special_re
[9]; /* array to hold the compiled special patterns RE. */
8755 int include_visual_only
= 0; /* If set to 1, the original word which is *
8756 | read from stdin will be output even if its */
8757 int exclude_visual_only
= 0; /* visual representation was modified via *
8760 ll_t
*cols_selector_list
= NULL
; /* to store ASCII representations of */
8761 char *cols_selector
= NULL
; /* ranges add RE to select some columns *
8764 ll_t
*rows_selector_list
= NULL
; /* to store ASCII representations of */
8765 char *rows_selector
= NULL
; /* ranges and RE to select some rows *
8768 ll_t
*aligns_selector_list
= NULL
; /* to store ASCII representations of */
8769 char *aligns_selector
= NULL
; /* RE to select some rows with -al. */
8771 long wi
; /* word index. */
8773 term_t term
; /* Terminal structure. */
8775 tst_node_t
*tst_word
= NULL
; /* TST used by the search function. */
8776 tst_node_t
*tst_daccess
= NULL
; /* TST used by the direct access system. */
8778 long page
; /* Step for the vertical cursor moves. */
8779 char *word
; /* Temporary variable to work on words. */
8780 char *tmp_word
; /* Temporary variable able to contain the beginning of *
8781 | the word to be displayed. */
8783 long last_line
= 0; /* last logical line number (from 0). */
8785 limit_t limits
; /* set of various limitations. */
8786 ticker_t timers
; /* timers contents. */
8787 misc_t misc
; /* misc contents. */
8788 mouse_t mouse
; /* mouse default values. */
8789 toggle_t toggles
; /* set of binary indicators. */
8791 int old_fd0
; /* backups of the old stdin file descriptor. */
8792 int old_fd1
; /* backups of the old stdout file descriptor. */
8794 FILE *old_stdout
; /* The selected word will go there. */
8796 long nl
; /* Number of lines displayed in the window. */
8797 long line_offset
; /* Used to correctly put the cursor at the start of the *
8798 | selection window, even after a terminal vertical *
8801 long first_selectable
; /* Index of the first selectable word in the input *
8803 long last_selectable
; /* Index of the last selectable word in the input *
8806 long min_size
; /* Minimum screen width of a column in tabular mode. */
8808 long tab_max_size
; /* Maximum screen width of a column in tabular *
8810 long tab_real_max_size
; /* Maximum size in bytes of a column in tabular *
8813 long *col_real_max_size
= NULL
; /* Array of maximum sizes (bytes) of each */
8814 /* column in column mode. */
8816 long *col_max_size
= NULL
; /* Array of maximum sizes (in display cells) *
8817 | of each column in column mode. */
8819 attrib_t
**col_attrs
= NULL
; /* attributes for each column in column mode. */
8821 long word_real_max_size
= 0; /* size of the longer word after expansion. */
8822 long cols_real_max_size
= 0; /* Max real width of all columns used when *
8823 | -w and -c are both set. */
8825 long cols_max_size
= 0; /* Same as above for the columns widths */
8827 long col_index
= 0; /* Index of the current column when reading words, *
8828 | used in column mode. */
8830 long cols_number
= 0; /* Number of columns in column mode. */
8832 char *pre_selection_index
= NULL
; /* pattern used to set the initial *
8833 | cursor position. */
8835 unsigned char buffer
[64]; /* Input buffer which will contain the scancode. */
8837 search_data_t search_data
;
8838 search_data
.buf
= NULL
; /* Search buffer */
8839 search_data
.len
= 0; /* Current position in the search buffer */
8840 search_data
.utf8_len
= 0; /* Current position in the search buffer in *
8842 search_data
.err
= 0; /* reset the error indicator. */
8843 search_data
.fuzzy_err_pos
= -1; /* no last error position in search *
8846 long matching_word_cur_index
= -1; /* cache for the next/previous moves *
8847 | in the matching words array. */
8849 struct sigaction sa
; /* Signal structure. */
8851 char *iws
= NULL
, *ils
= NULL
, *zg
= NULL
; /* words/lines delimiters and *
8852 | zapped glyphs strings. */
8854 ll_t
*word_delims_list
= NULL
; /* and associated linked lists. */
8855 ll_t
*line_delims_list
= NULL
;
8856 ll_t
*zapped_glyphs_list
= NULL
;
8858 char utf8_buffer
[5]; /* buffer to store the bytes of a UTF-8 glyph *
8860 unsigned char is_last
;
8863 char *home_ini_file
; /* init file full path. */
8864 char *local_ini_file
; /* init file full path. */
8866 charsetinfo_t
*charset_ptr
;
8867 langinfo_t langinfo
;
8868 int is_supported_charset
;
8870 long line_count
= 0; /* Only used when -R is selected. */
8874 ll_node_t
*inc_interval_node
= NULL
; /* one node of this list. */
8875 ll_node_t
*exc_interval_node
= NULL
; /* one node of this list. */
8877 interval_t
*inc_interval
; /* the data in each node. */
8878 interval_t
*exc_interval
; /* the data in each node. */
8879 int row_def_selectable
; /* default selectable value. */
8881 int line_selected_by_regex
= 0;
8882 int line_excluded
= 0;
8884 char *timeout_message
;
8886 char *common_options
;
8887 char *main_options
, *main_spec_options
;
8888 char *col_options
, *col_spec_options
;
8889 char *line_options
, *line_spec_options
;
8890 char *tab_options
, *tab_spec_options
;
8891 char *tag_options
, *tag_spec_options
;
8893 /* Used to check if we can get the cursor position in the terminal. */
8894 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8895 int row
; /* absolute line position in terminal (1...) */
8896 int col
; /* absolute column position in terminal (1...) */
8898 int mouse_proto
= -1;
8900 /* Initialize some internal data structures. */
8901 /* """"""""""""""""""""""""""""""""""""""""" */
8902 init_main_ds(&init_attr
,
8912 /* direct access variable initialization. */
8913 /* """""""""""""""""""""""""""""""""""""" */
8914 daccess_stack
= xcalloc(6, 1);
8915 daccess_stack_head
= 0;
8917 /* fuzzy variables initialization. */
8918 /* """"""""""""""""""""""""""""""" */
8919 tst_search_list
= ll_new();
8920 ll_append(tst_search_list
, sub_tst_new());
8922 matching_words_a_size
= 64;
8923 matching_words_a
= xmalloc(matching_words_a_size
* sizeof(long));
8926 best_matching_words_a_size
= 16;
8927 best_matching_words_a
= xmalloc(best_matching_words_a_size
* sizeof(long));
8928 best_matches_count
= 0;
8930 /* Initialize the tag hit number which will permit to sort the */
8931 /* pinned words when displayed. */
8932 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8935 /* Columns selection variables. */
8936 /* """""""""""""""""""""""""""" */
8937 char *cols_filter
= NULL
;
8939 /* Initialize the count of tagged words. */
8940 /* """"""""""""""""""""""""""""""""""""" */
8941 long tagged_words
= 0;
8943 /* Double-click related variables. */
8944 /* """"""""""""""""""""""""""""""" */
8945 int disable_double_click
= 0;
8947 struct timespec last_click_ts
;
8949 /* Get the current locale. */
8950 /* """"""""""""""""""""""" */
8951 setlocale(LC_ALL
, "");
8952 charset
= nl_langinfo(CODESET
);
8954 /* Check if the local charset is supported. */
8955 /* """""""""""""""""""""""""""""""""""""""" */
8956 is_supported_charset
= 0;
8957 charset_ptr
= all_supported_charsets
;
8959 while (charset_ptr
->name
!= NULL
)
8961 if (my_strcasecmp(charset
, charset_ptr
->name
) == 0)
8963 is_supported_charset
= 1;
8964 langinfo
.bits
= charset_ptr
->bits
;
8970 if (!is_supported_charset
)
8972 fprintf(stderr
, "%s is not a supported charset.", charset
);
8977 /* Remember the fact that the charset is UTF-8. */
8978 /* """""""""""""""""""""""""""""""""""""""""""" */
8979 if (strcmp(charset
, "UTF-8") == 0)
8984 /* my_isprint is a function pointers that points to */
8985 /* the 7 or 8-bit version of isprint according to the */
8986 /* current locale. */
8987 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
8988 if (langinfo
.utf8
|| langinfo
.bits
== 8)
8989 my_isprint
= isprint8
;
8991 my_isprint
= isprint7
;
8993 /* my_isempty is a function pointers that points to */
8994 /* the utf8 or non_utf8 version of isprint according to the */
8995 /* current locale. */
8996 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8998 my_isempty
= isempty_utf8
;
9000 my_isempty
= isempty_non_utf8
;
9002 /* Set terminal in noncanonical, noecho mode and */
9003 /* if TERM is unset or unknown, vt100 is assumed. */
9004 /* """""""""""""""""""""""""""""""""""""""""""""" */
9005 if (getenv("TERM") == NULL
)
9006 setupterm("vt100", 1, (int *)0);
9008 setupterm((char *)0, 1, (int *)0);
9010 /* Get the number of colors if the use of colors is available */
9011 /* and authorized. */
9012 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9013 if (getenv("NO_COLOR") != NULL
)
9017 term
.colors
= tigetnum("colors");
9018 if (term
.colors
< 0)
9022 /* Ignore SIGTTIN. */
9023 /* """"""""""""""" */
9024 sigset_t sigs
, oldsigs
;
9027 sigaddset(&sigs
, SIGTTIN
);
9028 sigprocmask(SIG_BLOCK
, &sigs
, &oldsigs
);
9030 /* Temporarily set /dev/tty as stdin/stdout to get its size */
9031 /* even in a pipe. */
9032 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9036 fprintf(stderr
, "Cannot save the standard input file descriptor.\n");
9040 old_stdin
= freopen("/dev/tty", "r", stdin
);
9045 fprintf(stderr
, "Cannot save the standard output file descriptor.\n");
9049 old_stdout
= freopen("/dev/tty", "w", stdout
);
9051 if (old_stdin
== NULL
|| old_stdout
== NULL
)
9053 fprintf(stderr
, "A terminal is required to use this program.\n");
9057 /* Get the number of lines/columns of the terminal. */
9058 /* """""""""""""""""""""""""""""""""""""""""""""""" */
9059 get_terminal_size(&term
.nlines
, &term
.ncolumns
, &term
);
9061 /* Restore the old stdin and stdout. */
9062 /* """"""""""""""""""""""""""""""""" */
9063 if (dup2(old_fd0
, 0) == -1)
9065 fprintf(stderr
, "Cannot restore the standard input file descriptor.\n");
9069 if (dup2(old_fd1
, 1) == -1)
9071 fprintf(stderr
, "Cannot restore the standard output file descriptor.\n");
9078 /* Build the full path of the .ini file. */
9079 /* """"""""""""""""""""""""""""""""""""" */
9080 home_ini_file
= make_ini_path(argv
[0], "HOME");
9081 local_ini_file
= make_ini_path(argv
[0], "PWD");
9083 /* Set the attributes from the configuration file if possible. */
9084 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9085 if (ini_load(home_ini_file
,
9095 if (ini_load(local_ini_file
,
9105 free(home_ini_file
);
9106 free(local_ini_file
);
9108 /* Command line option settings using ctxopt. */
9109 /* """""""""""""""""""""""""""""""""""""""""" */
9110 ctxopt_init(argv
[0],
9111 "stop_if_non_option=No "
9112 "allow_abbreviations=No "
9113 "display_usage_on_error=Yes ");
9115 common_options
= "[*help] "
9117 "[include_re... #regex] "
9118 "[exclude_re... #regex] "
9121 "[attributes #prefix:attr...] "
9122 "[special_level_1 #...<3] "
9123 "[special_level_2 #...<3] "
9124 "[special_level_3 #...<3] "
9125 "[special_level_4 #...<3] "
9126 "[special_level_5 #...<3] "
9127 "[special_level_6 #...<3] "
9128 "[special_level_7 #...<3] "
9129 "[special_level_8 #...<3] "
9130 "[special_level_9 #...<3] "
9131 "[zapped_glyphs #bytes] "
9132 "[lines [#height]] "
9133 "[blank_nonprintable] "
9134 "[*invalid_character #invalid_char_subst] "
9138 "[word_separators #bytes] "
9140 "[no_hor_scroll_bar] "
9141 "[early_subst_all... #/regex/repl/opts] "
9142 "[post_subst_all... #/regex/repl/opts] "
9143 "[post_subst_included... #/regex/repl/opts] "
9144 "[post_subst_excluded... #/regex/repl/opts] "
9145 "[search_method #prefix|substring|fuzzy] "
9146 "[start_pattern #pattern] "
9148 "[hidden_timeout #...] "
9149 "[validate_in_search_mode] "
9152 "[incremental_search] "
9153 "[limits... #limit:value...] "
9154 "[forgotten_timeout #timeout] "
9155 "[double_click_delay #delay] "
9156 "[button_remapping #mapping...] "
9158 "[show_blank_words [#blank_char]] "; /* <- don't remove *
9161 main_spec_options
= "[*copyright] "
9164 "[da_options #prefix:attr...] "
9165 "[auto_da_number... [#regex...]] "
9166 "[auto_da_unnumber... [#regex...]] "
9167 "[field_da_number] "
9168 "[column_mode>Columns] "
9169 "[line_mode>Lines] "
9170 "[tab_mode>Tabulations [#cols]] "
9171 "[tag_mode>Tagging [#delim]] "
9172 "[pin_mode>Tagging [#delim]]";
9174 col_spec_options
= "[wide_mode] "
9175 "[columns_select... #selector...] "
9176 "[rows_select... #selector...] "
9177 "[aligns_select... #re_selector...] "
9178 "[gutter [#string]] "
9179 "[line_separators #bytes] "
9180 "[da_options #prefix:attr...] "
9181 "[auto_da_number... [#regex...]] "
9182 "[auto_da_unnumber... [#regex...]] "
9183 "[field_da_number] "
9184 "[tag_mode>Tagging [#delim]] "
9185 "[pin_mode>Tagging [#delim]] "
9186 "[force_first_column #regex] "
9187 "[force_last_column #regex]";
9189 line_spec_options
= "[rows_select... #selector...] "
9190 "[line_separators #bytes] "
9191 "[da_options #prefix:attr...] "
9192 "[auto_da_number... [#regex...]] "
9193 "[auto_da_unnumber... [#regex...]] "
9194 "[field_da_number] "
9195 "[tag_mode>Tagging [#delim]] "
9196 "[pin_mode>Tagging [#delim]] "
9197 "[force_first_column #regex] "
9198 "[force_last_column #regex]";
9200 tab_spec_options
= "[wide_mode] "
9201 "[gutter [#string]] "
9202 "[line_separators #bytes] "
9203 "[da_options #prefix:attr...] "
9204 "[auto_da_number... [#regex...]] "
9205 "[auto_da_unnumber... [#regex...]] "
9206 "[field_da_number] "
9207 "[tag_mode>Tagging [#delim]] "
9208 "[pin_mode>Tagging [#delim]] "
9209 "[force_first_column #regex] "
9210 "[force_last_column #regex]";
9212 tag_spec_options
= "[auto_tag] "
9214 "[column_mode>Columns] "
9215 "[line_mode>Lines] "
9216 "[tab_mode>Tabulations [#cols]]";
9218 main_options
= concat(common_options
, main_spec_options
, (char *)0);
9219 col_options
= concat(common_options
, col_spec_options
, (char *)0);
9220 line_options
= concat(common_options
, line_spec_options
, (char *)0);
9221 tab_options
= concat(common_options
, tab_spec_options
, (char *)0);
9222 tag_options
= concat(common_options
, tag_spec_options
, (char *)0);
9224 ctxopt_new_ctx("Main", main_options
);
9225 ctxopt_new_ctx("Columns", col_options
);
9226 ctxopt_new_ctx("Lines", line_options
);
9227 ctxopt_new_ctx("Tabulations", tab_options
);
9228 ctxopt_new_ctx("Tagging", tag_options
);
9236 /* ctxopt parameters. */
9237 /* """""""""""""""""" */
9239 ctxopt_add_opt_settings(parameters
, "help", "-h -help");
9240 ctxopt_add_opt_settings(parameters
, "long_help", "-H -long-help");
9241 ctxopt_add_opt_settings(parameters
, "usage", "-? -u -usage");
9242 ctxopt_add_opt_settings(parameters
, "copyright", "-copyright");
9243 ctxopt_add_opt_settings(parameters
, "version", "-V -version");
9244 ctxopt_add_opt_settings(parameters
,
9246 "-i -in -inc -incl -include");
9247 ctxopt_add_opt_settings(parameters
,
9249 "-e -ex -exc -excl -exclude");
9250 ctxopt_add_opt_settings(parameters
, "lines", "-n -lines -height");
9251 ctxopt_add_opt_settings(parameters
, "title", "-m -msg -message -title");
9252 ctxopt_add_opt_settings(parameters
, "int", "-! -int -int_string");
9253 ctxopt_add_opt_settings(parameters
, "attributes", "-a -attr -attributes");
9254 ctxopt_add_opt_settings(parameters
, "special_level_1", "-1 -l1 -level1");
9255 ctxopt_add_opt_settings(parameters
, "special_level_2", "-2 -l2 -level2");
9256 ctxopt_add_opt_settings(parameters
, "special_level_3", "-3 -l3 -level3");
9257 ctxopt_add_opt_settings(parameters
, "special_level_4", "-4 -l4 -level4");
9258 ctxopt_add_opt_settings(parameters
, "special_level_5", "-5 -l5 -level5");
9259 ctxopt_add_opt_settings(parameters
, "special_level_6", "-6 -l6 -level6");
9260 ctxopt_add_opt_settings(parameters
, "special_level_7", "-7 -l7 -level7");
9261 ctxopt_add_opt_settings(parameters
, "special_level_8", "-8 -l8 -level8");
9262 ctxopt_add_opt_settings(parameters
, "special_level_9", "-9 -l9 -level9");
9263 ctxopt_add_opt_settings(parameters
, "tag_mode", "-T -tm -tag -tag_mode");
9264 ctxopt_add_opt_settings(parameters
, "pin_mode", "-P -pm -pin -pin_mode");
9265 ctxopt_add_opt_settings(parameters
, "auto_tag", "-p -at -auto_tag");
9266 ctxopt_add_opt_settings(parameters
, "no_auto_tag", "-0 -noat -no_auto_tag");
9267 ctxopt_add_opt_settings(parameters
, "auto_da_number", "-N -number");
9268 ctxopt_add_opt_settings(parameters
, "auto_da_unnumber", "-U -unnumber");
9269 ctxopt_add_opt_settings(parameters
,
9271 "-F -en -embedded_number");
9272 ctxopt_add_opt_settings(parameters
, "da_options", "-D -data -options");
9273 ctxopt_add_opt_settings(parameters
, "invalid_character", "-. -dot -invalid");
9274 ctxopt_add_opt_settings(parameters
, "blank_nonprintable", "-b -blank");
9275 ctxopt_add_opt_settings(parameters
, "center_mode", "-M -middle -center");
9276 ctxopt_add_opt_settings(parameters
,
9278 "-d -restore -delete -clean "
9279 "-delete_window -clean_window");
9280 ctxopt_add_opt_settings(parameters
,
9282 "-c -col -col_mode -column");
9283 ctxopt_add_opt_settings(parameters
, "line_mode", "-l -line -line_mode");
9284 ctxopt_add_opt_settings(parameters
,
9286 "-t -tab -tab_mode -tabulate_mode");
9287 ctxopt_add_opt_settings(parameters
, "wide_mode", "-w -wide -wide_mode");
9288 ctxopt_add_opt_settings(parameters
,
9290 "-C -cs -cols -cols_select");
9291 ctxopt_add_opt_settings(parameters
,
9293 "-R -rs -rows -rows_select");
9294 ctxopt_add_opt_settings(parameters
, "aligns_select", "-al -align");
9295 ctxopt_add_opt_settings(parameters
,
9296 "force_first_column",
9297 "-A -fc -first_column");
9298 ctxopt_add_opt_settings(parameters
,
9299 "force_last_column",
9300 "-Z -lc -last_column");
9301 ctxopt_add_opt_settings(parameters
, "gutter", "-g -gutter");
9302 ctxopt_add_opt_settings(parameters
, "keep_spaces", "-k -ks -keep_spaces");
9303 ctxopt_add_opt_settings(parameters
,
9305 "-W -ws -wd -word_delimiters -word_separators");
9306 ctxopt_add_opt_settings(parameters
,
9308 "-L -ls -ld -line-delimiters -line_separators");
9309 ctxopt_add_opt_settings(parameters
, "zapped_glyphs", "-z -zap -zap-glyphs");
9310 ctxopt_add_opt_settings(parameters
,
9312 "-q -no_bar -no_scroll_bar");
9313 ctxopt_add_opt_settings(parameters
,
9314 "no_hor_scroll_bar",
9315 "-no_hbar -no_hor_scroll_bar");
9316 ctxopt_add_opt_settings(parameters
, "early_subst_all", "-ES -early_subst");
9317 ctxopt_add_opt_settings(parameters
, "post_subst_all", "-S -subst");
9318 ctxopt_add_opt_settings(parameters
,
9319 "post_subst_included",
9320 "-I -si -subst_included");
9321 ctxopt_add_opt_settings(parameters
,
9322 "post_subst_excluded",
9323 "-E -se -subst_excluded");
9324 ctxopt_add_opt_settings(parameters
, "search_method", "-/ -search_method");
9325 ctxopt_add_opt_settings(parameters
,
9327 "-s -sp -start -start_pattern");
9328 ctxopt_add_opt_settings(parameters
, "timeout", "-x -tmout -timeout");
9329 ctxopt_add_opt_settings(parameters
,
9331 "-X -htmout -hidden_timeout");
9332 ctxopt_add_opt_settings(parameters
,
9333 "validate_in_search_mode",
9334 "-r -auto_validate");
9335 ctxopt_add_opt_settings(parameters
, "visual_bell", "-v -vb -visual_bell");
9336 ctxopt_add_opt_settings(parameters
, "ignore_quotes", "-Q -ignore_quotes");
9337 ctxopt_add_opt_settings(parameters
,
9338 "incremental_search",
9339 "-is -incremental_search");
9340 ctxopt_add_opt_settings(parameters
, "limits", "-lim -limits");
9341 ctxopt_add_opt_settings(parameters
,
9342 "forgotten_timeout",
9343 "-f -forgotten_timeout -global_timeout");
9344 ctxopt_add_opt_settings(parameters
, "no_mouse", "-nm -no_mouse");
9345 ctxopt_add_opt_settings(parameters
,
9347 "-br -buttons -button_remapping");
9348 ctxopt_add_opt_settings(parameters
,
9349 "double_click_delay",
9350 "-dc -dcd -double_click -double_click_delay");
9351 ctxopt_add_opt_settings(parameters
,
9353 "-sb -sbw -show_blank_words");
9355 /* ctxopt options incompatibilities. */
9356 /* """"""""""""""""""""""""""""""""" */
9358 ctxopt_add_ctx_settings(incompatibilities
,
9360 "column_mode line_mode tab_mode");
9361 ctxopt_add_ctx_settings(incompatibilities
, "Main", "tag_mode pin_mode");
9362 ctxopt_add_ctx_settings(incompatibilities
, "Main", "help usage");
9363 ctxopt_add_ctx_settings(incompatibilities
, "Main", "timeout hidden_timeout");
9364 ctxopt_add_ctx_settings(incompatibilities
,
9366 "no_mouse button_remapping");
9367 ctxopt_add_ctx_settings(incompatibilities
,
9369 "no_mouse double_click_delay");
9371 /* ctxopt options requirements. */
9372 /* """""""""""""""""""""""""""" */
9374 ctxopt_add_ctx_settings(requirements
,
9377 "field_da_number auto_da_number auto_da_unnumber");
9378 ctxopt_add_ctx_settings(requirements
,
9381 "field_da_number auto_da_number auto_da_unnumber");
9382 ctxopt_add_ctx_settings(requirements
,
9385 "field_da_number auto_da_number auto_da_unnumber");
9386 ctxopt_add_ctx_settings(requirements
,
9389 "field_da_number auto_da_number auto_da_unnumber");
9391 /* ctxopt actions. */
9392 /* """"""""""""""" */
9394 ctxopt_add_opt_settings(actions
,
9399 ctxopt_add_opt_settings(actions
,
9404 ctxopt_add_opt_settings(actions
,
9405 "invalid_character",
9406 invalid_char_action
,
9409 ctxopt_add_opt_settings(actions
,
9410 "blank_nonprintable",
9414 ctxopt_add_opt_settings(actions
,
9419 ctxopt_add_opt_settings(actions
, "clean", toggle_action
, &toggles
, (char *)0);
9420 ctxopt_add_opt_settings(actions
,
9425 ctxopt_add_opt_settings(actions
,
9430 ctxopt_add_opt_settings(actions
,
9435 ctxopt_add_opt_settings(actions
,
9437 columns_select_action
,
9438 &cols_selector_list
,
9441 ctxopt_add_opt_settings(actions
,
9444 &rows_selector_list
,
9448 ctxopt_add_opt_settings(actions
,
9450 aligns_select_action
,
9451 &aligns_selector_list
,
9453 ctxopt_add_opt_settings(actions
,
9456 &pattern_def_include
,
9460 ctxopt_add_opt_settings(actions
,
9463 &pattern_def_include
,
9467 ctxopt_add_opt_settings(actions
,
9474 ctxopt_add_opt_settings(actions
, "help", help_action
, (char *)0);
9475 ctxopt_add_opt_settings(actions
, "long_help", long_help_action
, (char *)0);
9476 ctxopt_add_opt_settings(actions
, "usage", usage_action
, (char *)0);
9477 ctxopt_add_opt_settings(actions
,
9482 ctxopt_add_opt_settings(actions
, "lines", lines_action
, &win
, (char *)0);
9483 ctxopt_add_opt_settings(actions
,
9488 ctxopt_add_opt_settings(actions
,
9489 "no_hor_scroll_bar",
9493 ctxopt_add_opt_settings(actions
,
9496 &pre_selection_index
,
9499 ctxopt_add_opt_settings(actions
,
9506 ctxopt_add_opt_settings(actions
,
9514 ctxopt_add_opt_settings(actions
,
9515 "validate_in_search_mode",
9519 ctxopt_add_opt_settings(actions
, "copyright", copyright_action
, (char *)0);
9520 ctxopt_add_opt_settings(actions
, "version", version_action
, (char *)0);
9521 ctxopt_add_opt_settings(actions
,
9526 ctxopt_add_opt_settings(actions
,
9527 "incremental_search",
9531 ctxopt_add_opt_settings(actions
,
9536 ctxopt_add_opt_settings(actions
,
9542 ctxopt_add_opt_settings(actions
,
9548 ctxopt_add_opt_settings(actions
,
9549 "post_subst_included",
9554 ctxopt_add_opt_settings(actions
,
9555 "post_subst_excluded",
9560 ctxopt_add_opt_settings(actions
,
9562 special_level_action
,
9569 ctxopt_add_opt_settings(actions
,
9571 special_level_action
,
9578 ctxopt_add_opt_settings(actions
,
9580 special_level_action
,
9587 ctxopt_add_opt_settings(actions
,
9589 special_level_action
,
9596 ctxopt_add_opt_settings(actions
,
9598 special_level_action
,
9605 ctxopt_add_opt_settings(actions
,
9607 special_level_action
,
9614 ctxopt_add_opt_settings(actions
,
9616 special_level_action
,
9623 ctxopt_add_opt_settings(actions
,
9625 special_level_action
,
9632 ctxopt_add_opt_settings(actions
,
9634 special_level_action
,
9641 ctxopt_add_opt_settings(actions
,
9648 ctxopt_add_opt_settings(actions
, "timeout", timeout_action
, &misc
, (char *)0);
9649 ctxopt_add_opt_settings(actions
,
9654 ctxopt_add_opt_settings(actions
,
9655 "force_first_column",
9657 &first_word_pattern
,
9660 ctxopt_add_opt_settings(actions
,
9661 "force_last_column",
9666 ctxopt_add_opt_settings(actions
,
9672 ctxopt_add_opt_settings(actions
,
9678 ctxopt_add_opt_settings(actions
,
9684 ctxopt_add_opt_settings(actions
,
9691 ctxopt_add_opt_settings(actions
,
9698 ctxopt_add_opt_settings(actions
,
9700 search_method_action
,
9703 ctxopt_add_opt_settings(actions
,
9708 ctxopt_add_opt_settings(actions
,
9713 ctxopt_add_opt_settings(actions
,
9715 field_da_number_action
,
9717 ctxopt_add_opt_settings(actions
,
9723 ctxopt_add_opt_settings(actions
,
9725 ignore_quotes_action
,
9728 ctxopt_add_opt_settings(actions
, "limits", limits_action
, &limits
, (char *)0);
9729 ctxopt_add_opt_settings(actions
,
9730 "forgotten_timeout",
9734 ctxopt_add_opt_settings(actions
,
9739 ctxopt_add_opt_settings(actions
,
9740 "double_click_delay",
9741 double_click_action
,
9743 &disable_double_click
,
9745 ctxopt_add_opt_settings(actions
,
9750 ctxopt_add_opt_settings(actions
,
9757 /* ctxopt constraints. */
9758 /* """"""""""""""""""" */
9760 ctxopt_add_opt_settings(constraints
,
9762 ctxopt_re_constraint
,
9764 ctxopt_add_opt_settings(constraints
,
9766 ctxopt_re_constraint
,
9768 ctxopt_add_opt_settings(constraints
, "lines", check_integer_constraint
, "");
9770 ctxopt_add_opt_settings(constraints
,
9772 check_integer_constraint
,
9774 ctxopt_add_opt_settings(constraints
,
9776 ctxopt_range_constraint
,
9778 ctxopt_add_opt_settings(constraints
,
9779 "double_click_delay",
9780 check_integer_constraint
,
9783 /* Evaluation order. */
9784 /* """"""""""""""""" */
9786 ctxopt_add_opt_settings(after
,
9788 "auto_da_number auto_da_unnumber");
9790 ctxopt_add_opt_settings(after
,
9792 "field_da_number auto_da_number auto_da_unnumber");
9794 /* In help settings. */
9795 /* """"""""""""""""" */
9797 ctxopt_add_opt_settings(visible_in_help
, "copyright", "no");
9799 /* Command line options analysis. */
9800 /* """""""""""""""""""""""""""""" */
9801 ctxopt_analyze(argc
- 1, argv
+ 1, &nb_rem_args
, &rem_args
);
9803 /* Command line options evaluation. */
9804 /* """""""""""""""""""""""""""""""" */
9807 /* Check remaining non analyzed command line arguments. */
9808 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
9809 if (nb_rem_args
== 1)
9811 input_file
= fopen_safe(rem_args
[0], "r");
9812 if (input_file
== NULL
)
9815 "The file \"%s\" does not exist or cannot be read.\n",
9817 ctxopt_ctx_disp_usage(NULL
, exit_after
);
9818 exit(EXIT_FAILURE
); /* Avoid a compiler warning. */
9821 else if (nb_rem_args
== 0)
9825 fprintf(stderr
, "Extra arguments detected:\n");
9827 fprintf(stderr
, "%s", rem_args
[1]);
9828 for (int i
= 2; i
< nb_rem_args
; i
++)
9829 fprintf(stderr
, ", %s", rem_args
[i
]);
9830 fprintf(stderr
, ".\n");
9832 ctxopt_ctx_disp_usage(NULL
, exit_after
);
9833 exit(EXIT_FAILURE
); /* Avoid a compiler warning. */
9836 /* Free the memory used internally by ctxopt. */
9837 /* """""""""""""""""""""""""""""""""""""""""" */
9838 ctxopt_free_memory();
9840 /* If we did not impose the number of columns, use the whole */
9841 /* terminal width. */
9842 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9843 if (win
.tab_mode
&& !win
.max_cols
)
9846 /* Force the padding mode to all when words are numbered and not in */
9847 /* col/line/tab_mode. */
9848 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9849 if (daccess
.padding
!= 'a' && (win
.col_mode
|| win
.line_mode
|| win
.tab_mode
))
9850 daccess
.padding
= 'a';
9854 term
.color_method
= ANSI
; /* We default to setaf/setbf to set colors. */
9855 term
.curs_line
= term
.curs_column
= 0;
9860 str
= tigetstr("cuu1");
9861 term
.has_cursor_up
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9862 str
= tigetstr("cud1");
9863 term
.has_cursor_down
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9864 str
= tigetstr("cub1");
9865 term
.has_cursor_left
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9866 str
= tigetstr("cuf1");
9867 term
.has_cursor_right
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9868 str
= tigetstr("cup");
9869 term
.has_cursor_address
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9870 str
= tigetstr("sc");
9871 term
.has_save_cursor
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9872 str
= tigetstr("rc");
9873 term
.has_restore_cursor
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9874 str
= tigetstr("setf");
9875 term
.has_setf
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9876 str
= tigetstr("setb");
9877 term
.has_setb
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9878 str
= tigetstr("setaf");
9879 term
.has_setaf
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9880 str
= tigetstr("setab");
9881 term
.has_setab
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9882 str
= tigetstr("hpa");
9883 term
.has_hpa
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9884 str
= tigetstr("cuf");
9885 term
.has_parm_right_cursor
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9886 str
= tigetstr("bold");
9887 term
.has_bold
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9888 str
= tigetstr("dim");
9889 term
.has_dim
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9890 str
= tigetstr("rev");
9891 term
.has_reverse
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9892 str
= tigetstr("smul");
9893 term
.has_underline
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9894 str
= tigetstr("smso");
9895 term
.has_standout
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9896 str
= tigetstr("sitm");
9897 term
.has_italic
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9898 str
= tigetstr("invis");
9899 term
.has_invis
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9900 str
= tigetstr("blink");
9901 term
.has_blink
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9902 str
= tigetstr("kmous");
9903 term
.has_kmous
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9904 str
= tigetstr("rep");
9905 term
.has_rep
= (str
== (char *)-1 || str
== NULL
) ? 0 : 1;
9908 if (!term
.has_cursor_up
|| !term
.has_cursor_down
|| !term
.has_cursor_left
9909 || !term
.has_cursor_right
|| !term
.has_save_cursor
9910 || !term
.has_restore_cursor
)
9913 "The terminal does not have the required cursor "
9914 "management capabilities.\n");
9919 word_buffer
= xcalloc(1, daccess
.flength
+ limits
.word_length
+ 1);
9921 /* default_search_method is not set in the command line nor in a config */
9922 /* file, set it to fuzzy. */
9923 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9924 if (misc
.default_search_method
== NONE
)
9925 misc
.default_search_method
= FUZZY
;
9927 /* If some attributes were not set, set their default values. */
9928 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9929 if (term
.colors
> 7)
9931 int const special_def_attr
[9] = { 1, 2, 3, 5, 6, 7, 7, 7, 7 };
9933 if (!win
.cursor_attr
.is_set
)
9935 if (term
.has_reverse
)
9936 win
.cursor_attr
.reverse
= 1;
9937 else if (term
.has_standout
)
9938 win
.cursor_attr
.standout
= 1;
9940 win
.cursor_attr
.is_set
= SET
;
9943 if (!win
.cursor_marked_attr
.is_set
)
9946 win
.cursor_marked_attr
.dim
= 1;
9947 else if (term
.has_bold
)
9948 win
.cursor_marked_attr
.bold
= 1;
9950 if (term
.has_reverse
)
9951 win
.cursor_marked_attr
.reverse
= 1;
9952 else if (term
.has_standout
)
9953 win
.cursor_marked_attr
.standout
= 1;
9955 win
.cursor_marked_attr
.is_set
= SET
;
9958 if (!win
.cursor_on_marked_attr
.is_set
)
9961 win
.cursor_on_marked_attr
.bold
= 1;
9962 if (term
.has_italic
)
9963 win
.cursor_on_marked_attr
.italic
= 1;
9965 if (term
.has_reverse
)
9966 win
.cursor_on_marked_attr
.reverse
= 1;
9967 else if (term
.has_standout
)
9968 win
.cursor_on_marked_attr
.standout
= 1;
9970 win
.cursor_on_marked_attr
.is_set
= SET
;
9973 if (!win
.cursor_on_tag_attr
.is_set
)
9975 if (term
.has_reverse
)
9976 win
.cursor_on_tag_attr
.reverse
= 1;
9978 if (term
.has_underline
)
9979 win
.cursor_on_tag_attr
.underline
= 1;
9981 win
.cursor_on_tag_attr
.fg
= 2;
9983 win
.cursor_on_tag_attr
.is_set
= SET
;
9986 if (!win
.cursor_on_tag_marked_attr
.is_set
)
9989 win
.cursor_on_tag_marked_attr
.dim
= 1;
9991 if (term
.has_reverse
)
9992 win
.cursor_on_tag_marked_attr
.reverse
= 1;
9994 if (term
.has_underline
)
9995 win
.cursor_on_tag_marked_attr
.underline
= 1;
9997 win
.cursor_on_tag_marked_attr
.is_set
= SET
;
10000 if (!win
.marked_attr
.is_set
)
10002 if (term
.has_standout
)
10003 win
.marked_attr
.standout
= 1;
10006 win
.marked_attr
.bold
= 1;
10008 win
.marked_attr
.fg
= 2;
10009 win
.marked_attr
.bg
= 0;
10011 win
.marked_attr
.is_set
= SET
;
10014 if (!win
.bar_attr
.is_set
)
10016 win
.bar_attr
.fg
= 2;
10017 win
.bar_attr
.is_set
= SET
;
10020 if (!win
.shift_attr
.is_set
)
10022 win
.shift_attr
.fg
= 2;
10023 win
.shift_attr
.is_set
= SET
;
10026 if (!win
.message_attr
.is_set
)
10029 win
.message_attr
.bold
= 1;
10030 else if (term
.has_reverse
)
10031 win
.message_attr
.reverse
= 1;
10034 win
.message_attr
.fg
= 0;
10035 win
.message_attr
.bg
= 7;
10038 win
.message_attr
.is_set
= SET
;
10041 if (!win
.search_field_attr
.is_set
)
10043 win
.search_field_attr
.bg
= 5;
10044 win
.search_field_attr
.is_set
= SET
;
10047 if (!win
.search_text_attr
.is_set
)
10049 win
.search_text_attr
.fg
= 0;
10050 win
.search_text_attr
.bg
= 6;
10052 win
.search_text_attr
.is_set
= SET
;
10055 if (!win
.search_err_field_attr
.is_set
)
10057 win
.search_err_field_attr
.bg
= 1;
10058 win
.search_err_field_attr
.is_set
= SET
;
10061 if (!win
.search_err_text_attr
.is_set
)
10063 if (term
.has_reverse
)
10064 win
.search_err_text_attr
.reverse
= 1;
10066 win
.search_err_text_attr
.fg
= 1;
10067 win
.search_err_text_attr
.is_set
= SET
;
10070 if (!win
.match_field_attr
.is_set
)
10072 win
.match_field_attr
.is_set
= SET
;
10075 if (!win
.match_text_attr
.is_set
)
10077 win
.match_text_attr
.fg
= 5;
10078 win
.match_text_attr
.is_set
= SET
;
10081 if (!win
.match_err_field_attr
.is_set
)
10083 win
.match_err_field_attr
.is_set
= SET
;
10086 if (!win
.match_err_text_attr
.is_set
)
10088 win
.match_err_text_attr
.fg
= 1;
10089 win
.match_err_text_attr
.is_set
= SET
;
10092 if (!win
.exclude_attr
.is_set
)
10094 win
.exclude_attr
.fg
= 6;
10096 win
.exclude_attr
.is_set
= SET
;
10099 /* This attribute should complete the attributes already set. */
10100 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10101 if (!win
.tag_attr
.is_set
)
10103 if (term
.has_underline
)
10104 win
.tag_attr
.underline
= 1;
10105 else if (term
.has_bold
)
10106 win
.tag_attr
.bold
= 1;
10108 win
.tag_attr
.fg
= 2;
10110 win
.tag_attr
.is_set
= SET
;
10113 if (!win
.daccess_attr
.is_set
)
10116 win
.daccess_attr
.bold
= 1;
10118 win
.daccess_attr
.is_set
= SET
;
10121 for (index
= 0; index
< 9; index
++)
10123 if (!win
.special_attr
[index
].is_set
)
10125 win
.special_attr
[index
].fg
= special_def_attr
[index
];
10126 win
.special_attr
[index
].is_set
= SET
;
10132 if (!win
.cursor_attr
.is_set
)
10134 if (term
.has_reverse
)
10135 win
.cursor_attr
.reverse
= 1;
10137 win
.cursor_attr
.is_set
= SET
;
10140 if (!win
.cursor_on_tag_attr
.is_set
)
10142 if (term
.has_reverse
)
10143 win
.cursor_on_tag_attr
.reverse
= 1;
10145 if (term
.has_underline
)
10146 win
.cursor_on_tag_attr
.underline
= 1;
10147 else if (term
.has_bold
)
10148 win
.cursor_on_tag_attr
.bold
= 1;
10150 win
.cursor_on_tag_attr
.is_set
= SET
;
10153 if (!win
.bar_attr
.is_set
)
10156 win
.bar_attr
.bold
= 1;
10158 win
.bar_attr
.is_set
= SET
;
10161 if (!win
.shift_attr
.is_set
)
10163 if (term
.has_reverse
)
10164 win
.shift_attr
.reverse
= 1;
10166 win
.shift_attr
.is_set
= SET
;
10169 if (!win
.message_attr
.is_set
)
10172 win
.message_attr
.bold
= 1;
10173 else if (term
.has_reverse
)
10174 win
.message_attr
.reverse
= 1;
10176 win
.message_attr
.is_set
= SET
;
10179 if (!win
.search_field_attr
.is_set
)
10181 if (term
.has_reverse
)
10182 win
.search_field_attr
.reverse
= 1;
10184 win
.search_field_attr
.is_set
= SET
;
10187 if (!win
.search_text_attr
.is_set
)
10190 win
.search_text_attr
.bold
= 1;
10192 win
.search_text_attr
.is_set
= SET
;
10195 if (!win
.search_err_field_attr
.is_set
)
10198 win
.search_err_field_attr
.bold
= 1;
10200 win
.search_err_field_attr
.is_set
= SET
;
10203 if (!win
.search_err_text_attr
.is_set
)
10205 if (term
.has_reverse
)
10206 win
.search_err_text_attr
.reverse
= 1;
10208 win
.search_err_text_attr
.is_set
= SET
;
10211 if (!win
.match_field_attr
.is_set
)
10214 win
.match_field_attr
.bold
= 1;
10215 else if (term
.has_reverse
)
10216 win
.match_field_attr
.reverse
= 1;
10218 win
.match_field_attr
.is_set
= SET
;
10221 if (!win
.match_text_attr
.is_set
)
10223 if (term
.has_reverse
)
10224 win
.match_text_attr
.reverse
= 1;
10225 else if (term
.has_bold
)
10226 win
.match_text_attr
.bold
= 1;
10228 win
.match_text_attr
.is_set
= SET
;
10231 if (!win
.exclude_attr
.is_set
)
10234 win
.exclude_attr
.dim
= 1;
10235 else if (term
.has_italic
)
10236 win
.exclude_attr
.italic
= 1;
10237 else if (term
.has_bold
)
10238 win
.exclude_attr
.bold
= 1;
10240 win
.exclude_attr
.is_set
= SET
;
10243 if (!win
.tag_attr
.is_set
)
10245 if (term
.has_underline
)
10246 win
.tag_attr
.underline
= 1;
10247 else if (term
.has_standout
)
10248 win
.tag_attr
.standout
= 1;
10249 else if (term
.has_reverse
)
10250 win
.tag_attr
.reverse
= 1;
10252 win
.tag_attr
.is_set
= SET
;
10255 if (!win
.daccess_attr
.is_set
)
10258 win
.daccess_attr
.bold
= 1;
10260 win
.daccess_attr
.is_set
= SET
;
10263 for (index
= 0; index
< 9; index
++)
10265 if (!win
.special_attr
[index
].is_set
)
10268 win
.special_attr
[index
].bold
= 1;
10269 else if (term
.has_standout
)
10270 win
.special_attr
[index
].standout
= 1;
10272 win
.special_attr
[index
].is_set
= SET
;
10277 /* Initialize and arm the global (forgotten) timer. */
10278 /* """""""""""""""""""""""""""""""""""""""""""""""" */
10279 forgotten_timer
= timers
.forgotten
;
10281 /* Initialize the timeout message when the x/X option is set. */
10282 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10283 if (!quiet_timeout
&& timeout
.initial_value
> 0)
10285 switch (timeout
.mode
)
10288 timeout_message
= xstrdup(
10289 "[ s before quitting without selecting anything]");
10293 timeout_message
= xstrdup(
10294 "[ s before selecting the current highlighted word]");
10299 char *s
= "[ s before selecting the word \"";
10301 timeout_message
= xcalloc(1, 4 + strlen(s
) + strlen(timeout_word
));
10303 strcpy(timeout_message
, s
);
10304 strcat(timeout_message
, timeout_word
);
10305 strcat(timeout_message
, "\"]");
10311 /* The other cases are impossible due to options analysis. */
10312 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
10313 timeout_message
= xstrdup(" "); /* Just in case. */
10316 timeout_seconds
= xcalloc(1, 6);
10317 sprintf(timeout_seconds
, "%5u", timeout
.initial_value
/ FREQ
);
10318 memcpy(timeout_message
+ 1, timeout_seconds
, 5);
10320 message_lines_list
= ll_new();
10326 get_message_lines(message
,
10327 message_lines_list
,
10328 &message_max_width
,
10330 ll_append(message_lines_list
, timeout_message
);
10332 if ((len
= strlen(timeout_message
)) > message_max_len
)
10333 message_max_len
= message_max_width
= len
;
10337 ll_append(message_lines_list
, timeout_message
);
10338 message_max_len
= message_max_width
= strlen(timeout_message
);
10343 message_lines_list
= ll_new();
10344 get_message_lines(message
,
10345 message_lines_list
,
10346 &message_max_width
,
10350 /* Force the maximum number of window's line if -n is used. */
10351 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10352 if (term
.nlines
<= win
.message_lines
)
10354 win
.message_lines
= term
.nlines
- 1;
10357 else if (win
.asked_max_lines
>= 0)
10359 if (win
.asked_max_lines
== 0)
10360 win
.max_lines
= term
.nlines
- win
.message_lines
- 1;
10363 if (win
.asked_max_lines
> term
.nlines
- win
.message_lines
)
10364 win
.max_lines
= term
.nlines
- win
.message_lines
- 1;
10366 win
.max_lines
= win
.asked_max_lines
;
10369 else /* -n was not used. Set win.asked_max_lines to its default value. */
10370 win
.asked_max_lines
= win
.max_lines
;
10372 /* Allocate the memory for our words structures. */
10373 /* """"""""""""""""""""""""""""""""""""""""""""" */
10374 word_a
= xmalloc(WORDSCHUNK
* sizeof(word_t
));
10376 /* Fill an array of word_t elements obtained from stdin. */
10377 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
10378 tab_real_max_size
= 0;
10382 /* Parse the list of glyphs to be zapped (option -z). */
10383 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
10384 zapped_glyphs_list
= ll_new();
10391 utf8_len
= mblen(zg_ptr
, 4);
10393 while (utf8_len
!= 0)
10395 tmp
= xmalloc(utf8_len
+ 1);
10396 memcpy(tmp
, zg_ptr
, utf8_len
);
10397 tmp
[utf8_len
] = '\0';
10398 ll_append(zapped_glyphs_list
, tmp
);
10400 zg_ptr
+= utf8_len
;
10401 utf8_len
= mblen(zg_ptr
, 4);
10405 /* Parse the word separators string (option -W). If it is empty then */
10406 /* the standard delimiters (space, tab and EOL) are used. Each of its */
10407 /* UTF-8 sequences are stored in a linked list. */
10408 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10409 word_delims_list
= ll_new();
10413 ll_append(word_delims_list
, " ");
10414 ll_append(word_delims_list
, "\t");
10415 ll_append(word_delims_list
, "\n");
10420 char *iws_ptr
= iws
;
10423 utf8_len
= mblen(iws_ptr
, 4);
10425 while (utf8_len
!= 0)
10427 tmp
= xmalloc(utf8_len
+ 1);
10428 memcpy(tmp
, iws_ptr
, utf8_len
);
10429 tmp
[utf8_len
] = '\0';
10430 ll_append(word_delims_list
, tmp
);
10432 iws_ptr
+= utf8_len
;
10433 utf8_len
= mblen(iws_ptr
, 4);
10437 /* Parse the line separators string (option -L). If it is empty then */
10438 /* the standard delimiter (newline) is used. Each of its UTF-8 */
10439 /* sequences are stored in a linked list. */
10440 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10441 line_delims_list
= ll_new();
10443 /* A default line separator is set to '\n' except in tab_mode */
10444 /* where it should be explicitly set. */
10445 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10446 if (ils
== NULL
&& !win
.tab_mode
)
10447 ll_append(line_delims_list
, "\n");
10451 char *ils_ptr
= ils
;
10454 utf8_len
= mblen(ils_ptr
, 4);
10456 while (utf8_len
!= 0)
10458 tmp
= xmalloc(utf8_len
+ 1);
10459 memcpy(tmp
, ils_ptr
, utf8_len
);
10460 tmp
[utf8_len
] = '\0';
10461 ll_append(line_delims_list
, tmp
);
10463 /* Add this record delimiter as a word delimiter. */
10464 /* """""""""""""""""""""""""""""""""""""""""""""" */
10465 if (ll_find(word_delims_list
, tmp
, buffer_cmp
) == NULL
)
10466 ll_append(word_delims_list
, tmp
);
10468 ils_ptr
+= utf8_len
;
10469 utf8_len
= mblen(ils_ptr
, 4);
10473 /* Initialize the first chunks of the arrays which will contain the */
10474 /* maximum length of each column in column mode. */
10475 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10478 long ci
; /* Column index. */
10480 col_real_max_size
= xmalloc(COLSCHUNK
* sizeof(long));
10481 col_max_size
= xmalloc(COLSCHUNK
* sizeof(long));
10483 for (ci
= 0; ci
< COLSCHUNK
; ci
++)
10484 col_real_max_size
[ci
] = col_max_size
[ci
] = 0;
10486 col_index
= cols_number
= 0;
10489 /* Compile the regular expression patterns. */
10490 /* """""""""""""""""""""""""""""""""""""""" */
10492 && regcomp(&daccess_np_re
, daccess_np
, REG_EXTENDED
| REG_NOSUB
) != 0)
10494 fprintf(stderr
, "%s: Bad regular expression.\n", daccess_np
);
10496 exit(EXIT_FAILURE
);
10500 && regcomp(&daccess_up_re
, daccess_up
, REG_EXTENDED
| REG_NOSUB
) != 0)
10502 fprintf(stderr
, "%s: Bad regular expression.\n", daccess_up
);
10504 exit(EXIT_FAILURE
);
10507 if (include_pattern
!= NULL
10508 && regcomp(&include_re
, include_pattern
, REG_EXTENDED
| REG_NOSUB
) != 0)
10510 fprintf(stderr
, "%s: Bad regular expression.\n", include_pattern
);
10512 exit(EXIT_FAILURE
);
10515 if (exclude_pattern
!= NULL
10516 && regcomp(&exclude_re
, exclude_pattern
, REG_EXTENDED
| REG_NOSUB
) != 0)
10518 fprintf(stderr
, "%s: Bad regular expression.\n", exclude_pattern
);
10520 exit(EXIT_FAILURE
);
10523 if (first_word_pattern
!= NULL
10524 && regcomp(&first_word_re
, first_word_pattern
, REG_EXTENDED
| REG_NOSUB
)
10527 fprintf(stderr
, "%s: Bad regular expression.\n", first_word_pattern
);
10529 exit(EXIT_FAILURE
);
10532 if (last_word_pattern
!= NULL
10533 && regcomp(&last_word_re
, last_word_pattern
, REG_EXTENDED
| REG_NOSUB
)
10536 fprintf(stderr
, "%s: Bad regular expression.\n", last_word_pattern
);
10538 exit(EXIT_FAILURE
);
10541 for (index
= 0; index
< 9; index
++)
10543 if (special_pattern
[index
] != NULL
10544 && regcomp(&special_re
[index
],
10545 special_pattern
[index
],
10546 REG_EXTENDED
| REG_NOSUB
)
10549 fprintf(stderr
, "%s: Bad regular expression.\n", special_pattern
[index
]);
10551 exit(EXIT_FAILURE
);
10555 /* Parse the post-processing patterns and extract its values. */
10556 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10557 if (early_sed_list
!= NULL
)
10559 ll_node_t
*node
= early_sed_list
->head
;
10561 while (node
!= NULL
)
10563 if (!parse_sed_like_string((sed_t
*)(node
->data
)))
10566 "Bad -ES argument. Must be something like: "
10567 "/regex/repl_string/[g][v][s][i].\n");
10569 exit(EXIT_FAILURE
);
10576 if (sed_list
!= NULL
)
10578 ll_node_t
*node
= sed_list
->head
;
10580 while (node
!= NULL
)
10582 if (!parse_sed_like_string((sed_t
*)(node
->data
)))
10585 "Bad -S argument. Must be something like: "
10586 "/regex/repl_string/[g][v][s][i].\n");
10588 exit(EXIT_FAILURE
);
10590 if ((!include_visual_only
|| !exclude_visual_only
)
10591 && ((sed_t
*)(node
->data
))->visual
)
10593 include_visual_only
= 1;
10594 exclude_visual_only
= 1;
10601 if (include_sed_list
!= NULL
)
10603 ll_node_t
*node
= include_sed_list
->head
;
10605 while (node
!= NULL
)
10607 if (!parse_sed_like_string((sed_t
*)(node
->data
)))
10610 "Bad -I argument. Must be something like: "
10611 "/regex/repl_string/[g][v][s][i].\n");
10613 exit(EXIT_FAILURE
);
10615 if (!include_visual_only
&& ((sed_t
*)(node
->data
))->visual
)
10616 include_visual_only
= 1;
10622 if (exclude_sed_list
!= NULL
)
10624 ll_node_t
*node
= exclude_sed_list
->head
;
10626 while (node
!= NULL
)
10628 if (!parse_sed_like_string((sed_t
*)(node
->data
)))
10631 "Bad -E argument. Must be something like: "
10632 "/regex/repl_string/[g][v][s][i].\n");
10634 exit(EXIT_FAILURE
);
10636 if (!exclude_visual_only
&& ((sed_t
*)(node
->data
))->visual
)
10637 exclude_visual_only
= 1;
10643 /* Parse the row selection string if any. */
10644 /* """""""""""""""""""""""""""""""""""""" */
10645 if (rows_selector_list
!= NULL
)
10647 ll_node_t
*node_selector
= rows_selector_list
->head
;
10649 filters_t filter_type
;
10652 rows_filter_type
= UNKNOWN_FILTER
;
10653 while (node_selector
!= NULL
)
10657 rows_selector
= node_selector
->data
;
10659 parse_selectors(rows_selector
,
10662 &inc_row_interval_list
,
10663 &inc_row_regex_list
,
10664 &exc_row_interval_list
,
10665 &exc_row_regex_list
,
10666 &al_row_interval_list
,
10667 &al_row_regex_list
,
10668 &ar_row_interval_list
,
10669 &ar_row_regex_list
,
10670 &ac_row_interval_list
,
10671 &ac_row_regex_list
,
10672 &at_row_interval_list
,
10673 &at_row_regex_list
,
10674 &default_alignment
,
10679 if (*unparsed
!= '\0')
10682 "Bad or not allowed row selection argument. "
10683 "Unparsed part: %s\n",
10686 exit(EXIT_FAILURE
);
10689 if (rows_filter_type
== UNKNOWN_FILTER
)
10690 rows_filter_type
= filter_type
;
10692 node_selector
= node_selector
->next
;
10696 optimize_an_interval_list(inc_row_interval_list
);
10697 optimize_an_interval_list(exc_row_interval_list
);
10698 optimize_an_interval_list(al_row_interval_list
);
10699 optimize_an_interval_list(ar_row_interval_list
);
10700 optimize_an_interval_list(ac_row_interval_list
);
10702 if (at_row_interval_list
!= NULL
)
10704 node
= at_row_interval_list
->head
;
10708 optimize_an_interval_list(elem
->list
);
10714 /* Parse the column selection string if any. */
10715 /* """"""""""""""""""""""""""""""""""""""""" */
10716 if (cols_selector_list
!= NULL
)
10718 filters_t filter_type
, cols_filter_type
;
10721 ll_node_t
*node_selector
= cols_selector_list
->head
;
10724 cols_filter
= xmalloc(limits
.cols
);
10726 cols_filter_type
= UNKNOWN_FILTER
;
10727 while (node_selector
!= NULL
)
10731 cols_selector
= node_selector
->data
;
10733 parse_selectors(cols_selector
,
10736 &inc_col_interval_list
,
10737 &inc_col_regex_list
,
10738 &exc_col_interval_list
,
10739 &exc_col_regex_list
,
10740 &al_col_interval_list
,
10741 &al_col_regex_list
,
10742 &ar_col_interval_list
,
10743 &ar_col_regex_list
,
10744 &ac_col_interval_list
,
10745 &ac_col_regex_list
,
10746 &at_col_interval_list
,
10747 &at_col_regex_list
,
10748 &default_alignment
,
10753 if (*unparsed
!= '\0')
10756 "Bad or not allowed column selection argument. "
10757 "Unparsed part: %s\n",
10760 exit(EXIT_FAILURE
);
10763 optimize_an_interval_list(inc_col_interval_list
);
10764 optimize_an_interval_list(exc_col_interval_list
);
10766 if (cols_filter_type
== UNKNOWN_FILTER
)
10767 cols_filter_type
= filter_type
;
10769 /* Only initialize the whole set when -C is encountered for the */
10771 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10772 if (cols_filter_type
== INCLUDE_FILTER
)
10773 memset(cols_filter
, SOFT_EXCLUDE_MARK
, limits
.cols
);
10775 memset(cols_filter
, SOFT_INCLUDE_MARK
, limits
.cols
);
10777 /* Process the explicitly included columns intervals. */
10778 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
10779 if (inc_col_interval_list
!= NULL
)
10780 for (node
= inc_col_interval_list
->head
; node
; node
= node
->next
)
10784 if (data
->low
>= limits
.cols
)
10787 if (data
->high
>= limits
.cols
)
10788 data
->high
= limits
.cols
- 1;
10790 memset(cols_filter
+ data
->low
,
10792 data
->high
- data
->low
+ 1);
10795 /* Process the explicitly excluded column intervals. */
10796 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
10797 if (exc_col_interval_list
!= NULL
)
10798 for (node
= exc_col_interval_list
->head
; node
; node
= node
->next
)
10802 if (data
->low
>= limits
.cols
)
10805 if (data
->high
>= limits
.cols
)
10806 data
->high
= limits
.cols
- 1;
10808 memset(cols_filter
+ data
->low
,
10810 data
->high
- data
->low
+ 1);
10813 node_selector
= node_selector
->next
;
10818 optimize_an_interval_list(al_col_interval_list
);
10819 optimize_an_interval_list(ar_col_interval_list
);
10820 optimize_an_interval_list(ac_col_interval_list
);
10822 if (at_col_interval_list
!= NULL
)
10824 node
= at_col_interval_list
->head
;
10828 optimize_an_interval_list(elem
->list
);
10834 /* parse the alignment selector list if any. */
10835 /* """"""""""""""""""""""""""""""""""""""""" */
10836 if (aligns_selector_list
!= NULL
)
10838 ll_node_t
*node_selector
= aligns_selector_list
->head
;
10840 while (node_selector
!= NULL
)
10844 aligns_selector
= node_selector
->data
;
10846 parse_al_selectors(aligns_selector
,
10848 &al_word_regex_list
,
10849 &ar_word_regex_list
,
10850 &ac_word_regex_list
,
10851 &default_alignment
,
10854 if (*unparsed
!= '\0')
10857 "Bad alignment selection argument. Unparsed part: %s\n",
10860 exit(EXIT_FAILURE
);
10865 node_selector
= node_selector
->next
;
10869 /* Initialize the useful values needed to walk through */
10870 /* the rows intervals. */
10871 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
10872 if (rows_filter_type
== INCLUDE_FILTER
)
10873 row_def_selectable
= SOFT_EXCLUDE_MARK
;
10874 else if (rows_filter_type
== EXCLUDE_FILTER
)
10875 row_def_selectable
= SOFT_INCLUDE_MARK
;
10878 if (pattern_def_include
== 0)
10879 row_def_selectable
= SOFT_EXCLUDE_MARK
;
10881 row_def_selectable
= SOFT_INCLUDE_MARK
;
10884 /* Set the head of the interval list. */
10885 /* """""""""""""""""""""""""""""""""" */
10886 if (inc_row_interval_list
)
10887 inc_interval_node
= inc_row_interval_list
->head
;
10889 inc_interval_node
= NULL
;
10891 if (exc_row_interval_list
)
10892 exc_interval_node
= exc_row_interval_list
->head
;
10894 exc_interval_node
= NULL
;
10896 /* And get the first interval.*/
10897 /* """""""""""""""""""""""""" */
10898 if (inc_interval_node
)
10899 inc_interval
= (interval_t
*)inc_interval_node
->data
;
10901 inc_interval
= NULL
;
10903 if (exc_interval_node
)
10904 exc_interval
= (interval_t
*)exc_interval_node
->data
;
10906 exc_interval
= NULL
;
10909 /* Get and process the input stream words. */
10910 /* In this pass, the different actions will occur: */
10911 /* - A new word is read from stdin */
10912 /* - A new SOL and or EOL is possibly set */
10913 /* - A special level is possibly affected to the word just read */
10914 /* - The -R is taken into account */
10915 /* - The first part of the -C option is done */
10916 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10917 while ((word
= get_word(input_file
,
10920 zapped_glyphs_list
,
10932 unsigned char special_level
;
10933 int row_inc_matched
= 0;
10939 /* Early substitution. */
10940 /* """"""""""""""""""" */
10941 if (early_sed_list
!= NULL
)
10945 node
= early_sed_list
->head
;
10947 while (node
!= NULL
)
10949 tmp
= xstrdup(word
);
10950 if (replace(word
, (sed_t
*)(node
->data
)))
10954 word
= xstrdup(word_buffer
);
10956 if (((sed_t
*)(node
->data
))->stop
)
10960 *word_buffer
= '\0';
10969 /* Manipulates the is_last flag word indicator to make this word */
10970 /* the first or last one of the current line in column/line/tab mode. */
10971 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10972 if (win
.col_mode
|| win
.line_mode
|| win
.tab_mode
)
10974 if (first_word_pattern
!= NULL
10975 && regexec(&first_word_re
, word
, (int)0, NULL
, 0) == 0)
10978 if (last_word_pattern
!= NULL
&& !is_last
10979 && regexec(&last_word_re
, word
, (int)0, NULL
, 0) == 0)
10983 /* Check if the word is special. */
10984 /* """"""""""""""""""""""""""""" */
10986 for (index
= 0; index
< 9; index
++)
10988 if (special_pattern
[index
] != NULL
10989 && regexec(&special_re
[index
], word
, (int)0, NULL
, 0) == 0)
10991 special_level
= index
+ 1;
10996 /* Default selectable state. */
10997 /* """"""""""""""""""""""""" */
10998 selectable
= SOFT_INCLUDE_MARK
;
11000 /* For each new line check if the line is in the current */
11001 /* interval or if we need to get the next interval if any .*/
11002 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
11005 if (count
> 0 && word_a
[count
- 1].is_last
)
11007 /* We are in a new line, reset the flag indicating that we are on */
11008 /* a line selected by a regular expression and the flag saying */
11009 /* that the whole line has been excluded. */
11010 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
11011 line_selected_by_regex
= 0;
11014 /* And also reset the flag telling that the row has been explicitly */
11015 /* removed from the selectable list of words. */
11016 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
11017 row_inc_matched
= 0;
11019 /* Increment the line counter used to see if we are an include or */
11020 /* exclude set of lines. */
11021 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
11024 /* Look if we need to use the next interval of the list. */
11025 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''' */
11026 if (inc_interval_node
&& line_count
> inc_interval
->high
)
11028 inc_interval_node
= inc_interval_node
->next
;
11029 if (inc_interval_node
)
11030 inc_interval
= (interval_t
*)inc_interval_node
->data
;
11033 if (exc_interval_node
&& line_count
> exc_interval
->high
)
11035 exc_interval_node
= exc_interval_node
->next
;
11036 if (exc_interval_node
)
11037 exc_interval
= (interval_t
*)exc_interval_node
->data
;
11041 /* Look if the line is in an excluded or included line. */
11042 /* The included line intervals are only checked if the word didn't */
11043 /* belong to an excluded line interval before. */
11044 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11045 if (exc_interval
&& line_count
>= exc_interval
->low
11046 && line_count
<= exc_interval
->high
)
11047 selectable
= EXCLUDE_MARK
;
11049 if (selectable
!= EXCLUDE_MARK
&& inc_interval
11050 && line_count
>= inc_interval
->low
11051 && line_count
<= inc_interval
->high
)
11053 selectable
= INCLUDE_MARK
;
11055 /* As the raw has been explicitly selected, record that so than */
11056 /* we can distinguish that from the implicit selection. */
11057 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
11058 row_inc_matched
= 1;
11062 /* Check if the all the words in the current row must be included or */
11063 /* excluded from the selectable set of words. */
11064 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11065 if (selectable
!= EXCLUDE_MARK
)
11067 /* Look in the excluded list of regular expressions. */
11068 /* ''''''''''''''''''''''''''''''''''''''''''''''''' */
11069 if (exc_row_regex_list
!= NULL
)
11073 ll_node_t
*row_regex_node
= exc_row_regex_list
->head
;
11075 while (row_regex_node
!= NULL
)
11077 row_re
= row_regex_node
->data
;
11078 if (regexec(row_re
, word
, (int)0, NULL
, 0) == 0)
11080 long c
= count
- 1;
11082 /* Mark all the next words of the line as excluded. */
11083 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
11084 line_selected_by_regex
= 1;
11087 /* Mark all the previous words of the line as excluded. */
11088 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
11089 while (c
>= 0 && !word_a
[c
].is_last
)
11091 word_a
[c
].is_selectable
= EXCLUDE_MARK
;
11095 /* Mark the current word as not excluded. */
11096 /* '''''''''''''''''''''''''''''''''''''' */
11097 selectable
= EXCLUDE_MARK
;
11099 /* No need to continue as the line is already marked as */
11101 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
11105 row_regex_node
= row_regex_node
->next
;
11109 /* If the line has not yet been excluded and the list of explicitly */
11110 /* include regular expressions is not empty then give them a chance. */
11111 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11112 if (selectable
!= EXCLUDE_MARK
&& inc_row_regex_list
!= NULL
)
11116 ll_node_t
*row_regex_node
= inc_row_regex_list
->head
;
11118 while (row_regex_node
!= NULL
)
11120 row_re
= row_regex_node
->data
;
11121 if (regexec(row_re
, word
, (int)0, NULL
, 0) == 0)
11123 long c
= count
- 1;
11125 while (c
>= 0 && !word_a
[c
].is_last
)
11127 /* Do not include an already excluded word. */
11128 /* """""""""""""""""""""""""""""""""""""""" */
11129 if (word_a
[c
].is_selectable
)
11130 word_a
[c
].is_selectable
= INCLUDE_MARK
;
11135 /* Mark all the next words of the line as included. */
11136 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
11137 line_selected_by_regex
= 1;
11139 /* Mark all the previous words of the line as included. */
11140 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
11141 selectable
= INCLUDE_MARK
;
11144 row_regex_node
= row_regex_node
->next
;
11149 /* If the line contains a word that matched a regex which determines */
11150 /* the inclusion of exclusion of this line, then use the regex */
11151 /* selection flag to determine the inclusion/exclusion of the future */
11152 /* words in the line. */
11153 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11155 selectable
= EXCLUDE_MARK
;
11158 if (line_selected_by_regex
)
11159 selectable
= (row_def_selectable
== EXCLUDE_MARK
) ? SOFT_EXCLUDE_MARK
11162 /* Check if the current word is matching an include or exclude */
11164 /* Only do it if if hasn't be explicitly deselected before. */
11165 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11166 if (selectable
!= EXCLUDE_MARK
)
11168 /* Check if the word will be excluded in the list of selectable */
11169 /* words or not. */
11170 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
11171 if (exclude_pattern
!= NULL
11172 && regexec(&exclude_re
, word
, (int)0, NULL
, 0) == 0)
11173 selectable
= EXCLUDE_MARK
;
11175 if (selectable
!= 0 && !line_selected_by_regex
)
11177 /* Check if the word will be included in the list of selectable */
11178 /* words or not. */
11179 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
11180 if (include_pattern
!= NULL
)
11182 if (regexec(&include_re
, word
, (int)0, NULL
, 0) == 0)
11183 selectable
= INCLUDE_MARK
;
11184 else if (!row_inc_matched
)
11185 selectable
= row_def_selectable
;
11187 else if (rows_selector
&& !row_inc_matched
)
11188 selectable
= row_def_selectable
;
11195 /* In column mode we must manage the allocation space for some */
11196 /* column's related data structures and check if some limits ave not */
11197 /* been reached. */
11198 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11206 if (col_index
> cols_number
)
11208 /* Check the limits. */
11209 /* ''''''''''''''''' */
11210 if (col_index
== limits
.cols
)
11213 "The number of columns has reached the limit of %ld.\n",
11216 exit(EXIT_FAILURE
);
11221 /* Look if we need to enlarge the arrays indexed by the */
11222 /* number of columns. */
11223 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
11224 if (cols_number
% COLSCHUNK
== 0)
11226 long ci
; /* column index */
11228 col_real_max_size
= xrealloc(col_real_max_size
,
11229 (cols_number
+ COLSCHUNK
)
11232 col_max_size
= xrealloc(col_max_size
,
11233 (cols_number
+ COLSCHUNK
) * sizeof(long));
11235 /* Initialize the max size for the new columns. */
11236 /* '''''''''''''''''''''''''''''''''''''''''''' */
11237 for (ci
= 0; ci
< COLSCHUNK
; ci
++)
11239 col_real_max_size
[cols_number
+ ci
] = 0;
11240 col_max_size
[cols_number
+ ci
] = 0;
11246 /* We must now check if the word matches a RE that */
11247 /* exclude the whole column. */
11248 /* """"""""""""""""""""""""""""""""""""""""""""""" */
11249 if (cols_selector
!= NULL
)
11251 long ci
; /* column index. */
11255 if (cols_filter
[col_index
- 1] == EXCLUDE_MARK
)
11256 selectable
= EXCLUDE_MARK
;
11259 if (exc_col_regex_list
!= NULL
)
11261 /* Some columns must be excluded by regex. */
11262 /* ''''''''''''''''''''''''''''''''''''''' */
11263 ll_node_t
*col_regex_node
= exc_col_regex_list
->head
;
11265 while (col_regex_node
!= NULL
)
11267 col_re
= col_regex_node
->data
;
11269 if (regexec(col_re
, word
, (int)0, NULL
, 0) == 0)
11271 cols_filter
[col_index
- 1] = EXCLUDE_MARK
;
11272 selectable
= EXCLUDE_MARK
;
11274 /* Mark non selectable the items above in the column. */
11275 /* '''''''''''''''''''''''''''''''''''''''''''''''''' */
11277 for (wi
= 0; wi
< count
; wi
++)
11279 if (ci
== col_index
- 1)
11280 word_a
[wi
].is_selectable
= EXCLUDE_MARK
;
11282 if (word_a
[wi
].is_last
)
11290 col_regex_node
= col_regex_node
->next
;
11294 if (inc_col_regex_list
!= NULL
)
11296 /* Some columns must be included by regex. */
11297 /* ''''''''''''''''''''''''''''''''''''''' */
11298 ll_node_t
*col_regex_node
= inc_col_regex_list
->head
;
11300 while (col_regex_node
!= NULL
)
11302 col_re
= col_regex_node
->data
;
11304 if (regexec(col_re
, word
, (int)0, NULL
, 0) == 0)
11306 cols_filter
[col_index
- 1] = INCLUDE_MARK
;
11310 col_regex_node
= col_regex_node
->next
;
11317 /* Initialize the alignment information of each column to be 'left'. */
11318 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11319 col_attrs
= xmalloc(cols_number
* sizeof(attrib_t
*));
11320 for (long ci
= 0; ci
< cols_number
; ci
++)
11321 col_attrs
[ci
] = NULL
;
11323 /* Store some known values in the current word's structure. */
11324 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11325 word_a
[count
].start
= word_a
[count
].end
= 0;
11327 word_a
[count
].str
= word
;
11328 word_a
[count
].is_selectable
= selectable
;
11330 word_a
[count
].special_level
= special_level
;
11331 word_a
[count
].is_matching
= 0;
11332 word_a
[count
].is_numbered
= 0;
11333 word_a
[count
].tag_order
= 0;
11334 word_a
[count
].tag_id
= 0;
11335 word_a
[count
].iattr
= NULL
;
11337 if (win
.col_mode
|| win
.line_mode
|| win
.tab_mode
)
11339 /* Set the last word in line indicator when in */
11340 /* column/line/tab mode. */
11341 /* ''''''''''''''''''''''''''''''''''''''''''' */
11342 if (is_first
&& count
> 0)
11343 word_a
[count
- 1].is_last
= 1;
11344 word_a
[count
].is_last
= is_last
;
11349 word_a
[count
].is_last
= 0;
11351 /* One more word... */
11352 /* """""""""""""""" */
11353 if (count
+ 1 >= limits
.words
)
11356 "The number of read words has reached the limit of %ld.\n",
11359 exit(EXIT_FAILURE
);
11364 if (count
% WORDSCHUNK
== 0)
11365 word_a
= xrealloc(word_a
, (count
+ WORDSCHUNK
) * sizeof(word_t
));
11368 /* Early exit if there is no input or if no word is selected. */
11369 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11371 exit(EXIT_FAILURE
);
11373 /* Ignore SIGINT */
11374 /* """"""""""""" */
11375 sigaddset(&sigs
, SIGINT
);
11376 sigprocmask(SIG_BLOCK
, &sigs
, &oldsigs
);
11378 /* The last word is always the last of its line. */
11379 /* """"""""""""""""""""""""""""""""""""""""""""" */
11380 if (win
.col_mode
|| win
.line_mode
|| win
.tab_mode
)
11381 word_a
[count
- 1].is_last
= 1;
11383 /* Second pass to modify the word according to all/include/exclude */
11384 /* regular expressions and the columns settings set in the previous pass. */
11385 /* This must be done separately because in the first pass, some word */
11386 /* could have been marked as excluded before the currently processed word */
11387 /* (second part of the -C option) */
11388 /* In this pass the following actions will also be done: */
11389 /* - Finish the work on columns. */
11390 /* - Possibly modify the word according to -S/-I/-E arguments */
11391 /* - Replace unprintable characters in the word by mnemonics */
11392 /* - Remember the max size of the words/columns/tabs */
11393 /* - Insert the word in a TST (Ternary Search Tree) index to facilitate */
11394 /* word search (each node pf the TST will contain an UTF-8 glyph). */
11395 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11397 for (wi
= 0; wi
< count
; wi
++)
11399 char *unaltered_word
;
11406 char *expanded_word
;
11409 /* If the column section argument is set, then adjust the final */
11410 /* selectable attribute according to the already set words and column */
11411 /* selectable flag contents. */
11412 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11413 if (cols_selector_list
!= NULL
)
11415 if (cols_filter
[col_index
] == EXCLUDE_MARK
)
11416 word_a
[wi
].is_selectable
= EXCLUDE_MARK
;
11417 else if (word_a
[wi
].is_selectable
!= EXCLUDE_MARK
)
11419 switch (cols_filter
[col_index
])
11422 word_a
[wi
].is_selectable
= INCLUDE_MARK
;
11425 case SOFT_EXCLUDE_MARK
:
11426 if (word_a
[wi
].is_selectable
== SOFT_EXCLUDE_MARK
11427 || word_a
[wi
].is_selectable
== SOFT_INCLUDE_MARK
)
11428 word_a
[wi
].is_selectable
= EXCLUDE_MARK
;
11430 word_a
[wi
].is_selectable
= INCLUDE_MARK
;
11433 case SOFT_INCLUDE_MARK
:
11434 if (word_a
[wi
].is_selectable
== SOFT_EXCLUDE_MARK
)
11435 word_a
[wi
].is_selectable
= EXCLUDE_MARK
;
11437 word_a
[wi
].is_selectable
= INCLUDE_MARK
;
11443 word
= &word_a
[wi
];
11445 /* Make sure that daccess.length >= daccess.size */
11446 /* with DA_TYPE_POS. */
11447 /* """"""""""""""""""""""""""""""""""""""""""""" */
11448 if (daccess
.mode
!= DA_TYPE_NONE
)
11450 if ((daccess
.mode
& DA_TYPE_POS
) && daccess
.size
> 0
11451 && daccess
.size
> daccess
.length
)
11452 daccess
.length
= daccess
.size
;
11454 /* Auto determination of the length of the selector */
11455 /* with DA_TYPE_AUTO. */
11456 /* """""""""""""""""""""""""""""""""""""""""""""""" */
11457 if ((daccess
.mode
& DA_TYPE_AUTO
) && daccess
.length
== -2)
11459 long n
= count
+ daccess_index
- 1;
11461 daccess
.length
= 0;
11470 /* Set the full length of the prefix in case of numbering. */
11471 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
11472 if (daccess
.length
> 0)
11473 daccess
.flength
= 3 + daccess
.length
;
11475 if (word
->is_selectable
!= EXCLUDE_MARK
11476 && word
->is_selectable
!= SOFT_EXCLUDE_MARK
)
11479 char *tmp
= xmalloc(strlen(word
->str
) + 4 + daccess
.length
);
11480 long *word_pos
= xmalloc(sizeof(long));
11483 if (!my_isempty(word
->str
))
11489 /* Check if the word is eligible to the numbering process. */
11490 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
11491 if (daccess_up
== NULL
&& daccess_np
== NULL
)
11493 if (daccess
.mode
& DA_TYPE_POS
)
11500 if (daccess_up
!= NULL
11501 && !!regexec(&daccess_up_re
, word
->str
, (int)0, NULL
, 0) == 0)
11505 if (daccess_np
!= NULL
11506 && !!regexec(&daccess_np_re
, word
->str
, (int)0, NULL
, 0) == 0)
11509 may_number
= daccess
.def_number
;
11517 if ((daccess
.mode
& DA_TYPE_POS
) && !word
->is_numbered
11518 && daccess
.size
> 0
11519 && (daccess
.offset
+ daccess
.size
+ daccess
.ignore
)
11520 <= utf8_strlen(word
->str
))
11522 unsigned selector_value
; /* numerical value of the *
11523 | extracted selector. */
11524 long selector_offset
; /* offset in byte to the selector *
11526 char *ptr
; /* points just after the selector *
11528 long plus_offset
; /* points to the first occurrence *
11529 | of a number in word->str after *
11530 | the offset given. */
11532 selector_offset
= utf8_offset(word
->str
, daccess
.offset
);
11536 plus_offset
= strcspn(word
->str
+ selector_offset
,
11539 if (plus_offset
+ daccess
.size
+ daccess
.ignore
11540 <= strlen(word
->str
))
11541 selector_offset
+= plus_offset
;
11544 ptr
= word
->str
+ selector_offset
;
11545 selector
= xstrndup(ptr
, daccess
.size
);
11547 /* read the embedded number and, if correct, format */
11548 /* it according to daccess.alignment. */
11549 /* """""""""""""""""""""""""""""""""""""""""""""""" */
11550 if (sscanf(selector
, "%u", &selector_value
) == 1)
11552 sprintf(selector
, "%u", selector_value
);
11556 daccess
.alignment
== 'l' ? -daccess
.length
11560 /* Overwrite the end of the word to erase */
11561 /* the selector. */
11562 /* """""""""""""""""""""""""""""""""""""" */
11565 + utf8_offset(ptr
+ daccess
.size
, daccess
.ignore
));
11567 /* Modify the word according to the 'h' directive */
11569 /* """""""""""""""""""""""""""""""""""""""""""""" */
11570 if (daccess
.head
== 'c')
11571 /* h:c is present cut the leading characters */
11572 /* before the selector. */
11573 /* ''''''''''''''''''''''''''''''''''''''''' */
11574 memmove(word
->str
, ptr
, strlen(ptr
) + 1);
11575 else if (daccess
.head
== 't')
11577 /* h:t is present trim the leading characters */
11578 /* before the selector if they are ' ' or '\t'. */
11579 /* '''''''''''''''''''''''''''''''''''''''''''' */
11580 char *p
= word
->str
;
11582 while (p
!= ptr
&& (*p
== ' ' || *p
== '\t'))
11586 memmove(word
->str
, ptr
, strlen(ptr
) + 1);
11589 ltrim(selector
, " ");
11590 rtrim(selector
, " ", 0);
11592 tst_daccess
= tst_insert(tst_daccess
,
11593 utf8_strtowcs(selector
),
11596 if (daccess
.follow
== 'y')
11597 daccess_index
= selector_value
+ 1;
11599 word
->is_numbered
= 1;
11604 /* Try to number this word if it is still non numbered and */
11605 /* the -N/-U option is given. */
11606 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
11607 if (!word
->is_numbered
&& (daccess
.mode
& DA_TYPE_AUTO
))
11611 daccess
.alignment
== 'l' ? -daccess
.length
11615 selector
= xstrdup(tmp
+ 1);
11616 ltrim(selector
, " ");
11617 rtrim(selector
, " ", 0);
11619 /* Insert it in the tst tree containing the selector's */
11621 /* ''''''''''''''''''''''''''''''''''''''''''''''''''' */
11622 tst_daccess
= tst_insert(tst_daccess
,
11623 utf8_strtowcs(selector
),
11629 word
->is_numbered
= 1;
11633 /* Fill the space taken by the numbering by space if the word */
11634 /* is not numbered. */
11635 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11636 if (daccess
.length
> 0 && !word
->is_numbered
)
11638 for (i
= 0; i
< daccess
.flength
; i
++)
11642 /* Make sure that the 2 character after this placeholder */
11643 /* are initialized. */
11644 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
11645 if (daccess
.length
> 0)
11647 tmp
[1 + daccess
.length
] = ' ';
11648 tmp
[2 + daccess
.length
] = ' ';
11651 else if (daccess
.length
> 0)
11653 /* Make sure that the prefix of empty word is blank */
11654 /* as they may be display in column mode. */
11655 /* """""""""""""""""""""""""""""""""""""""""""""""" */
11656 for (i
= 0; i
< daccess
.flength
; i
++)
11660 if (daccess
.length
> 0)
11662 my_strcpy(tmp
+ daccess
.flength
, word
->str
);
11671 /* Should we also add space at the beginning of excluded words? */
11672 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11673 if (daccess
.padding
== 'a')
11675 char *tmp
= xmalloc(strlen(word
->str
) + 4 + daccess
.length
);
11676 for (i
= 0; i
< daccess
.flength
; i
++)
11678 my_strcpy(tmp
+ daccess
.flength
, word
->str
);
11687 daccess
.length
= 0;
11690 /* Save the original word. */
11691 /* """"""""""""""""""""""" */
11692 unaltered_word
= xstrdup(word
->str
);
11694 /* Possibly modify the word according to -S/-I/-E arguments. */
11695 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11697 ll_node_t
*node
= NULL
;
11700 /* Manage the -S case. */
11701 /* """"""""""""""""""" */
11702 if (sed_list
!= NULL
)
11704 node
= sed_list
->head
;
11706 while (node
!= NULL
)
11708 tmp
= xstrndup(word
->str
, daccess
.flength
);
11709 if (replace(word
->str
+ daccess
.flength
, (sed_t
*)(node
->data
)))
11713 memmove(word_buffer
+ daccess
.flength
,
11715 strlen(word_buffer
) + 1);
11716 memmove(word_buffer
, tmp
, daccess
.flength
);
11718 word
->str
= xstrdup(word_buffer
);
11720 if (((sed_t
*)(node
->data
))->stop
)
11724 *word_buffer
= '\0';
11731 /* Manage the -I/-E case. */
11732 /* """""""""""""""""""""" */
11733 if ((word
->is_selectable
== INCLUDE_MARK
11734 || word
->is_selectable
== SOFT_INCLUDE_MARK
)
11735 && include_sed_list
!= NULL
)
11736 node
= include_sed_list
->head
;
11737 else if ((word
->is_selectable
== EXCLUDE_MARK
11738 || word
->is_selectable
== SOFT_EXCLUDE_MARK
)
11739 && exclude_sed_list
!= NULL
)
11740 node
= exclude_sed_list
->head
;
11744 *word_buffer
= '\0';
11746 while (node
!= NULL
)
11748 tmp
= xstrndup(word
->str
, daccess
.flength
);
11749 if (replace(word
->str
+ daccess
.flength
, (sed_t
*)(node
->data
)))
11753 memmove(word_buffer
+ daccess
.flength
,
11755 strlen(word_buffer
) + 1);
11756 memmove(word_buffer
, tmp
, daccess
.flength
);
11758 word
->str
= xstrdup(word_buffer
);
11760 if (((sed_t
*)(node
->data
))->stop
)
11763 *word_buffer
= '\0';
11770 /* A substitution leading to an empty word is invalid in column mode. */
11771 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11776 if (daccess
.padding
== 'a')
11777 len
= daccess
.flength
;
11781 if (*(word
->str
+ len
) == '\0')
11782 exit(EXIT_FAILURE
);
11785 /* Alter the word just read be replacing special chars by their */
11786 /* escaped equivalents. */
11787 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11788 word_len
= strlen(word
->str
);
11790 expanded_word
= xmalloc(5 * word_len
+ 1);
11791 len
= expand(word
->str
, expanded_word
, &langinfo
, &toggles
, &misc
);
11793 /* Update it if needed. */
11794 /* '''''''''''''''''''' */
11795 if (strcmp(expanded_word
, word
->str
) != 0)
11799 word
->str
= xstrdup(expanded_word
);
11802 free(expanded_word
);
11806 /* Update the max values of col_real_max_size[col_index] */
11807 /* and col_max_size[col_index]. */
11808 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
11809 if ((s
= (long)word_len
) > col_real_max_size
[col_index
])
11811 col_real_max_size
[col_index
] = s
;
11813 /* Also update the real max size of all columns seen. */
11814 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
11815 if (s
> cols_real_max_size
)
11816 cols_real_max_size
= s
;
11819 s
= (long)mbstowcs(NULL
, word
->str
, 0);
11820 s
= wcswidth((tmpw
= utf8_strtowcs(word
->str
)), s
);
11823 if (s
> col_max_size
[col_index
])
11825 col_max_size
[col_index
] = s
;
11827 /* Also update the max size of all columns seen. */
11828 /* """"""""""""""""""""""""""""""""""""""""""""" */
11829 if (s
> cols_max_size
)
11832 /* Update the size of the longest expanded word. */
11833 /* """"""""""""""""""""""""""""""""""""""""""""" */
11834 word_real_max_size
= cols_real_max_size
+ 1;
11836 else if (win
.tab_mode
)
11838 /* Store the new max number of bytes in a word */
11839 /* and update the size of the longest expanded word. */
11840 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
11841 if ((long)word_len
> tab_real_max_size
)
11842 word_real_max_size
= tab_real_max_size
= (long)word_len
;
11844 /* Store the new max word width. */
11845 /* """"""""""""""""""""""""""""" */
11846 size
= (long)mbstowcs(NULL
, word
->str
, 0);
11848 if ((size
= wcswidth((tmpw
= utf8_strtowcs(word
->str
)), size
))
11850 tab_max_size
= size
;
11854 else if (word_real_max_size
< word_len
)
11855 /* Update the size of the longest expanded word. */
11856 /* """"""""""""""""""""""""""""""""""""""""""""" */
11857 word_real_max_size
= word_len
;
11859 /* When the visual only flag is set, we keep the unaltered word so */
11860 /* that it can be restored even if its visual and searchable */
11861 /* representation may have been altered by the previous code. */
11862 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11864 /* Record the length of the word in bytes. This information will be */
11865 /* used if the -k option (keep spaces ) is not set. */
11866 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11867 word
->len
= strlen(word
->str
);
11869 /* Save the non modified word in .orig if it has been altered. */
11870 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11871 if ((strcmp(word
->str
, unaltered_word
) != 0)
11872 && ((word
->is_selectable
&& include_visual_only
)
11873 || (!word
->is_selectable
&& exclude_visual_only
)))
11875 word
->orig
= unaltered_word
;
11880 free(unaltered_word
);
11885 if (word_a
[wi
].is_last
)
11892 /* Set the minimum width of a column (-w and -t or -c option). */
11893 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11898 if (win
.max_cols
> 0)
11899 min_size
= (term
.ncolumns
- 2) / win
.max_cols
- 1;
11901 if (min_size
< tab_max_size
)
11902 min_size
= tab_max_size
;
11904 word_real_max_size
= min_size
+ tab_real_max_size
- tab_max_size
;
11906 else /* Column mode. */
11908 min_size
= (term
.ncolumns
- 2) / cols_number
;
11909 if (min_size
< cols_max_size
)
11910 min_size
= cols_max_size
;
11912 word_real_max_size
= cols_real_max_size
;
11916 /* Third (compress) pass: remove all empty word and words containing */
11917 /* only spaces when not in column mode. */
11918 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11924 for (wi
= 0; wi
< count
- offset
; wi
++)
11928 while (wi
+ offset
< count
)
11930 if (daccess
.padding
== 'a' || word_a
[wi
+ offset
].is_numbered
)
11931 len
= daccess
.flength
;
11935 if (!my_isempty(word_a
[wi
+ offset
].str
+ len
))
11938 /* Keep non selectable empty words to allow special effects. */
11939 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
11940 if (word_a
[wi
+ offset
].is_selectable
== SOFT_EXCLUDE_MARK
11941 || word_a
[wi
+ offset
].is_selectable
== EXCLUDE_MARK
)
11948 word_a
[wi
] = word_a
[wi
+ offset
];
11954 exit(EXIT_FAILURE
);
11956 /* Allocate the space for the satellites arrays. */
11957 /* """"""""""""""""""""""""""""""""""""""""""""" */
11958 line_nb_of_word_a
= xmalloc(count
* sizeof(long));
11959 first_word_in_line_a
= xmalloc(count
* sizeof(long));
11960 shift_right_sym_pos_a
= xmalloc(count
* sizeof(long));
11963 /* When in column or tabulating mode, we need to adjust the length of */
11964 /* all the words by adding the right number of spaces so that they will */
11965 /* be aligned correctly. In column mode the size of each column is */
11966 /* variable; in tabulate mode it is constant. */
11967 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11972 /* Sets all columns to the same size when -w and -c are both set. */
11973 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11975 for (col_index
= 0; col_index
< cols_number
; col_index
++)
11977 col_max_size
[col_index
] = cols_max_size
;
11978 col_real_max_size
[col_index
] = cols_real_max_size
;
11981 /* Total space taken by all the columns plus the gutter. */
11982 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
11984 win
.real_max_width
= 0;
11985 for (col_index
= 0; col_index
< cols_number
; col_index
++)
11987 if (win
.max_width
+ col_max_size
[col_index
] + 1 <= term
.ncolumns
- 2)
11988 win
.max_width
+= col_max_size
[col_index
] + 1;
11990 win
.real_max_width
+= col_max_size
[col_index
] + 1;
11994 for (wi
= 0; wi
< count
; wi
++)
12001 interval_t
*interval
;
12003 /* Does this word matched by one of the alignment regex? */
12004 /* If yes, then add the current column number to the list of the */
12005 /* corresponding column_alignment list */
12006 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12007 ll_t
*regex_list_a
[3] = { al_col_regex_list
,
12009 ac_col_regex_list
};
12010 ll_t
**interval_list_a
[3] = { &al_col_interval_list
,
12011 &ar_col_interval_list
,
12012 &ac_col_interval_list
};
12014 for (int i
= 0; i
< 3; i
++) /* For each regex list. */
12016 if (regex_list_a
[i
] == NULL
)
12019 node
= regex_list_a
[i
]->head
;
12020 while (node
) /* For each RE in the list. */
12022 re
= *(regex_t
*)(node
->data
);
12023 if (regexec(&re
, word_a
[wi
].str
+ daccess
.flength
, (int)0, NULL
, 0)
12026 int already_aligned
= 0;
12028 /* We have a match. */
12029 /* '''''''''''''''' */
12030 interval
= xmalloc(sizeof(interval_t
));
12031 interval
->low
= interval
->high
= col_index
;
12033 /* Look if the column has already been inserted in another */
12034 /* interval list. */
12035 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
12036 for (int j
= 0; j
< 3; j
++)
12041 /* Quick continuation. */
12042 /* ''''''''''''''''''' */
12043 if (i
== j
|| *interval_list_a
[j
] == NULL
)
12046 n
= (*interval_list_a
[j
])->head
;
12049 inter
= *(interval_t
*)(n
->data
);
12050 if (col_index
>= inter
.low
&& col_index
<= inter
.high
)
12052 already_aligned
= 1; /* This column is already aligned. */
12059 if (!already_aligned
)
12061 /* Append a new interval containing the current column number */
12062 /* in the interval list matching the regex list. */
12063 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
12064 if (*interval_list_a
[i
] == NULL
)
12065 *interval_list_a
[i
] = ll_new();
12067 ll_append(*interval_list_a
[i
], interval
);
12074 /* Process regex based attributes and fill the attribute columns */
12075 /* list accordingly. */
12076 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12077 if (at_col_regex_list
!= NULL
)
12081 ll_node_t
*regex_node
;
12082 ll_node_t
*interval_node
;
12084 node
= at_col_regex_list
->head
;
12090 regex_node
= elem
->list
->head
;
12093 re
= *(regex_t
*)(regex_node
->data
);
12094 if (regexec(&re
, word_a
[wi
].str
+ daccess
.flength
, (int)0, NULL
, 0)
12097 /* We have a match. */
12098 /* '''''''''''''''' */
12099 attr_elem_t
*new_elem
;
12101 if (col_attrs
[col_index
] != NULL
)
12104 new_elem
= xmalloc(sizeof(attr_elem_t
));
12105 new_elem
->attr
= attr
;
12107 interval
= xmalloc(sizeof(interval_t
));
12108 interval
->low
= interval
->high
= col_index
;
12110 col_attrs
[col_index
] = attr
;
12112 if (at_col_interval_list
== NULL
)
12113 at_col_interval_list
= ll_new();
12115 if ((interval_node
= ll_find(at_col_interval_list
,
12120 ll_append(((attr_elem_t
*)(interval_node
->data
))->list
,
12125 new_elem
->list
= ll_new();
12126 ll_append(new_elem
->list
, interval
);
12127 ll_append(at_col_interval_list
, new_elem
);
12130 regex_node
= regex_node
->next
;
12136 s1
= (long)strlen(word_a
[wi
].str
);
12137 word_width
= mbstowcs(NULL
, word_a
[wi
].str
, 0);
12138 s2
= wcswidth((w
= utf8_strtowcs(word_a
[wi
].str
)), word_width
);
12141 /* Use the 0x05 character as a placeholder to preserve the internal */
12142 /* spaces of the word if there are any. This value has been chosen */
12143 /* because it cannot be part of a UTF-8 sequence and is very unlikely */
12144 /* to be part of a normal word from the input stream. */
12145 /* This placeholder will be removed during the alignment phase. */
12146 /*"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12147 temp
= xcalloc(1, col_real_max_size
[col_index
] + s1
- s2
+ 1);
12148 memset(temp
, 0x05, col_max_size
[col_index
] + s1
- s2
);
12149 memcpy(temp
, word_a
[wi
].str
, s1
);
12150 temp
[col_real_max_size
[col_index
] + s1
- s2
] = '\0';
12151 free(word_a
[wi
].str
);
12152 word_a
[wi
].str
= temp
;
12154 if (word_a
[wi
].is_last
)
12159 optimize_an_interval_list(al_col_interval_list
);
12160 optimize_an_interval_list(ar_col_interval_list
);
12161 optimize_an_interval_list(ac_col_interval_list
);
12163 else if (win
.tab_mode
)
12167 if (tab_max_size
< min_size
)
12169 tab_max_size
= min_size
;
12170 if (tab_max_size
> tab_real_max_size
)
12171 tab_real_max_size
= tab_max_size
;
12174 for (wi
= 0; wi
< count
; wi
++)
12180 s1
= (long)strlen(word_a
[wi
].str
);
12181 word_width
= mbstowcs(NULL
, word_a
[wi
].str
, 0);
12182 s2
= wcswidth((w
= utf8_strtowcs(word_a
[wi
].str
)), word_width
);
12184 temp
= xcalloc(1, tab_real_max_size
+ s1
- s2
+ 1);
12185 memset(temp
, ' ', tab_max_size
+ s1
- s2
);
12186 memcpy(temp
, word_a
[wi
].str
, s1
);
12187 temp
[tab_real_max_size
+ s1
- s2
] = '\0';
12188 free(word_a
[wi
].str
);
12189 word_a
[wi
].str
= temp
;
12193 /* Fifth pass: transforms the remaining SOFT_EXCLUDE_MARKs with */
12194 /* EXCLUDE_MARKs. */
12195 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12196 for (wi
= 0; wi
< count
; wi
++)
12202 if (word_a
[wi
].is_selectable
== SOFT_EXCLUDE_MARK
)
12203 word_a
[wi
].is_selectable
= EXCLUDE_MARK
;
12205 /* If the word is selectable insert it in the TST tree */
12206 /* with its associated index in the input stream. */
12207 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
12208 if (word_a
[wi
].is_selectable
)
12210 data
= xmalloc(sizeof(long));
12213 /* Create a wide characters string from the word screen */
12214 /* representation to be able to store in in the TST. */
12215 /* Note that the direct access selector,if any, is not */
12217 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
12218 if (word_a
[wi
].is_numbered
)
12219 w
= utf8_strtowcs(word_a
[wi
].str
+ daccess
.flength
);
12221 w
= utf8_strtowcs(word_a
[wi
].str
);
12223 /* If we didn't already encounter this word, then create a new */
12224 /* entry in the TST for it and store its index in its list. */
12225 /* Otherwise, add its index in its index list. */
12226 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12227 if (tst_word
&& (list
= tst_search(tst_word
, w
)) != NULL
)
12228 ll_append(list
, data
);
12232 ll_append(list
, data
);
12233 tst_word
= tst_insert(tst_word
, w
, list
);
12239 /* Sixth pass: Apply alignment rules in column modes. */
12240 /* The column alignments, based on regular expressions, have already */
12241 /* been processed in the fourth pass which converted this information */
12242 /* by adding new ranges to the three lists of column ranges. */
12244 /* It remains to interpret these lists of column intervals and the lists */
12245 /* of intervals and regular expressions for the rows. */
12247 /* To do this, a working table (aligned_a) is created and reset for */
12248 /* each row to store the statistics of alignments already processed, */
12249 /* taking into account the previous result and the order in which the */
12250 /* row and column alignment requests were made. */
12251 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12254 long row_index
= 0;
12255 interval_t interval
;
12257 alignment_t alignment
; /* Future value of the word alignment. */
12258 alignment_t word_alignment
; /* Specific word alignment. */
12259 alignment_t row_alignment
; /* current row word alignments. */
12265 ll_node_t
*cur_word_node_a
[3];
12267 ll_t
*col_interval_list_a
[3] = { al_col_interval_list
,
12268 ar_col_interval_list
,
12269 ac_col_interval_list
};
12271 ll_node_t
*cur_col_node_a
[3] = {
12272 al_col_interval_list
!= NULL
? al_col_interval_list
->head
: NULL
,
12273 ar_col_interval_list
!= NULL
? ar_col_interval_list
->head
: NULL
,
12274 ac_col_interval_list
!= NULL
? ac_col_interval_list
->head
: NULL
12277 ll_node_t
*cur_row_interval_node_a
[3] = {
12278 al_row_interval_list
!= NULL
? al_row_interval_list
->head
: NULL
,
12279 ar_row_interval_list
!= NULL
? ar_row_interval_list
->head
: NULL
,
12280 ac_row_interval_list
!= NULL
? ac_row_interval_list
->head
: NULL
12283 ll_node_t
*cur_row_regex_node_a
[3];
12285 alignment_t
const alignment_a
[3] = { AL_LEFT
, AL_RIGHT
, AL_CENTERED
};
12287 char *aligned_a
; /* Array of indicators used to remember that a word *
12288 | has been aligned with -al in a row. */
12290 /* Initialize each chars of aligned_a with No ('N'). */
12291 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
12292 aligned_a
= xmalloc(cols_number
);
12294 for (int i
= 0; i
< cols_number
; i
++)
12295 aligned_a
[i
] = 'N';
12297 row_alignment
= AL_NONE
;
12299 for (wi
= 0; wi
< count
; wi
++)
12301 word_alignment
= AL_NONE
;
12303 if (row_alignment
!= AL_NONE
)
12304 alignment
= row_alignment
;
12306 alignment
= default_alignment
;
12308 str
= xstrdup(word_a
[wi
].str
+ daccess
.flength
);
12309 tstr
= xstrdup(str
);
12311 rtrim(tstr
, "\x05", 0);
12312 ltrim(tstr
, "\x05");
12314 /* First check if the current word is matched by a word specified */
12315 /* regular expression set by the * alignment option. */
12316 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12318 cur_word_node_a
[0] = al_word_regex_list
!= NULL
? al_word_regex_list
->head
12321 cur_word_node_a
[1] = ar_word_regex_list
!= NULL
? ar_word_regex_list
->head
12324 cur_word_node_a
[2] = ac_word_regex_list
!= NULL
? ac_word_regex_list
->head
12327 cur_row_regex_node_a
[0] = al_row_regex_list
!= NULL
12328 ? al_row_regex_list
->head
12331 cur_row_regex_node_a
[1] = ar_row_regex_list
!= NULL
12332 ? ar_row_regex_list
->head
12335 cur_row_regex_node_a
[2] = ac_row_regex_list
!= NULL
12336 ? ac_row_regex_list
->head
12339 /* Process the word alignment regex lists. */
12340 /* """"""""""""""""""""""""""""""""""""""" */
12341 for (int i
= 0; i
< 3; i
++)
12343 while (word_alignment
== AL_NONE
&& cur_word_node_a
[i
] != NULL
)
12347 re
= (regex_t
*)(cur_word_node_a
[i
]->data
);
12349 if (regexec(re
, tstr
, (int)0, NULL
, 0) == 0)
12351 word_alignment
= alignment_a
[i
];
12353 /* Mark this word as aligned in this row. */
12354 /* """""""""""""""""""""""""""""""""""""" */
12355 aligned_a
[col_index
] = 'Y';
12357 break; /* Early exit of the while loop. */
12360 cur_word_node_a
[i
] = cur_word_node_a
[i
]->next
;
12364 /* Process the alignment lists for columns and increment */
12365 /* their current pointers when needed. */
12366 /* The current interval pointer cur_col_node_a[i] is only */
12367 /* modified to point to the next one of the list is the */
12368 /* current column is greater than the max column in the */
12369 /* pointed interval. */
12370 /* An already aligned column will not be realigned. */
12371 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12372 for (int i
= 0; i
< 3; i
++)
12374 if (cur_col_node_a
[i
] != NULL
)
12376 interval
= *(interval_t
*)(cur_col_node_a
[i
]->data
);
12377 if (aligned_a
[col_index
] == 'N' && col_index
>= interval
.low
12378 && col_index
<= interval
.high
)
12380 /* Tell that the word is already aligned when column */
12381 /* alignments have precedence over row alignments. */
12382 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
12383 if (toggles
.cols_first
)
12384 aligned_a
[col_index
] = 'Y';
12386 alignment
= alignment_a
[i
];
12388 else if (col_index
> interval
.high
)
12389 cur_col_node_a
[i
] = cur_col_node_a
[i
]->next
;
12391 if (word_a
[wi
].is_last
&& col_interval_list_a
[i
] != NULL
)
12392 cur_col_node_a
[i
] = col_interval_list_a
[i
]->head
;
12395 /* Process row interval and regex alignment lists. */
12396 /* """"""""""""""""""""""""""""""""""""""""""""""" */
12397 for (int i
= 0; i
< 3; i
++)
12399 /* Lists of intervals. */
12400 /* ''''''''''''''''''' */
12401 if (cur_row_interval_node_a
[i
] != NULL
)
12403 interval
= *(interval_t
*)(cur_row_interval_node_a
[i
]->data
);
12404 if (row_alignment
== AL_NONE
&& row_index
>= interval
.low
12405 && row_index
<= interval
.high
)
12407 row_alignment
= alignment_a
[i
];
12408 if (aligned_a
[col_index
] == 'N')
12409 alignment
= alignment_a
[i
];
12411 else if (row_index
> interval
.high
)
12412 cur_row_interval_node_a
[i
] = cur_row_interval_node_a
[i
]->next
;
12415 /* Lists of regular expression. */
12416 /* '''''''''''''''''''''''''''' */
12417 if (cur_row_regex_node_a
[i
] != NULL
)
12419 while (cur_row_regex_node_a
[i
] != NULL
)
12423 re
= (regex_t
*)(cur_row_regex_node_a
[i
]->data
);
12425 if (row_alignment
== AL_NONE
12426 && regexec(re
, tstr
, (int)0, NULL
, 0) == 0)
12428 row_alignment
= alignment_a
[i
];
12429 if (aligned_a
[col_index
] == 'N')
12430 alignment
= alignment_a
[i
];
12432 /* Also aligns the previous words in the line to the */
12433 /* right, left or centre. */
12434 /* ''''''''''''''''''''''''''''''''''''''''''''''''' */
12436 while (j
> 0 && !word_a
[j
- 1].is_last
)
12440 /* Do not realign words already aligned with -al */
12441 /* of if already aligned using a column RE when */
12442 /* column alignments have precedence. */
12443 /* ''''''''''''''''''''''''''''''''''''''''''''' */
12444 if (aligned_a
[col_index
- (wi
- j
)] == 'N')
12445 align_word(&word_a
[j
], alignment_a
[i
], daccess
.flength
, 0x05);
12448 break; /* Early exit of the while loop. */
12450 else if (toggles
.rows_first
&& row_alignment
!= AL_NONE
)
12451 alignment
= row_alignment
;
12453 cur_row_regex_node_a
[i
] = cur_row_regex_node_a
[i
]->next
;
12458 /* Force a word alignment if it is set for this word. */
12459 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
12460 if (word_alignment
!= AL_NONE
)
12461 alignment
= word_alignment
;
12463 /* Do the alignment. */
12464 /* """"""""""""""""" */
12465 align_word(&word_a
[wi
], alignment
, daccess
.flength
, 0x05);
12467 /* Adjusts things before a row change. */
12468 /* """"""""""""""""""""""""""""""""""" */
12469 if (word_a
[wi
].is_last
|| wi
== count
- 1) /* About to start a new row? */
12473 /* Re-initialize the array with No ('N'). */
12474 /* as this is a new row. */
12475 /* """""""""""""""""""""""""""""""""""""" */
12476 for (int i
= 0; i
< cols_number
; i
++)
12478 aligned_a
[i
] = 'N';
12480 /* We can restore the spaces which are not part of the word */
12481 /* now that the row is fully processed. */
12482 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12484 strrep(word_a
[wi
- i
].str
+ daccess
.flength
, 0x05, ' ');
12487 col_index
= 0; /* Restart the columns counter. */
12488 row_alignment
= AL_NONE
;
12498 /* Seventh pass: sets default attributes. */
12499 /* """""""""""""""""""""""""""""""""""""" */
12502 if (at_row_interval_list
!= NULL
|| at_col_interval_list
!= NULL
12503 || at_row_regex_list
!= NULL
|| at_col_regex_list
!= NULL
)
12505 long row_index
= 0;
12506 interval_t
*interval
;
12509 attr_elem_t
*attr_elem
;
12510 ll_node_t
*attr_elem_node
;
12516 for (wi
= 0; wi
< count
; wi
++)
12518 if (word_a
[wi
].iattr
!= NULL
)
12521 if (at_row_interval_list
!= NULL
)
12523 attr_elem_node
= at_row_interval_list
->head
;
12524 while (attr_elem_node
!= NULL
)
12526 attr_elem
= attr_elem_node
->data
;
12527 attr
= attr_elem
->attr
;
12528 list
= attr_elem
->list
; /* Cannot be null by construction. */
12533 interval
= node
->data
;
12534 if (row_index
>= interval
->low
&& row_index
<= interval
->high
)
12536 word_a
[wi
].iattr
= attr
;
12537 goto early_row_exit
;
12541 attr_elem_node
= attr_elem_node
->next
;
12546 if (at_col_interval_list
!= NULL
)
12548 attr_elem_node
= at_col_interval_list
->head
;
12549 while (attr_elem_node
!= NULL
)
12551 attr_elem
= attr_elem_node
->data
;
12552 attr
= attr_elem
->attr
;
12553 list
= attr_elem
->list
; /* Cannot be null by construction. */
12557 interval
= node
->data
;
12558 if (col_index
>= interval
->low
&& col_index
<= interval
->high
)
12560 if (word_a
[wi
].iattr
== NULL
|| toggles
.cols_first
)
12562 if (col_attrs
[col_index
] == NULL
)
12564 word_a
[wi
].iattr
= attr
;
12565 col_attrs
[col_index
] = attr
;
12568 word_a
[wi
].iattr
= col_attrs
[col_index
];
12570 goto early_col_exit
;
12575 attr_elem_node
= attr_elem_node
->next
;
12580 if (at_row_regex_list
!= NULL
)
12582 attr_elem_node
= at_row_regex_list
->head
;
12583 while (attr_elem_node
!= NULL
)
12585 attr_elem
= attr_elem_node
->data
;
12586 attr
= attr_elem
->attr
;
12587 list
= attr_elem
->list
; /* Cannot be null by construction. */
12591 re
= *(regex_t
*)(node
->data
);
12593 word_a
[wi
].str
+ daccess
.flength
,
12600 while (wi
> 0 && !word_a
[wi
- 1].is_last
)
12605 if (toggles
.cols_first
&& col_attrs
[col_index
] != NULL
)
12606 word_a
[wi
].iattr
= col_attrs
[col_index
];
12608 word_a
[wi
].iattr
= attr
;
12612 if (word_a
[wi
].is_last
)
12619 attr_elem_node
= attr_elem_node
->next
;
12623 /* Adjusts things before a row change. */
12624 /* """"""""""""""""""""""""""""""""""" */
12625 if (word_a
[wi
].is_last
12626 || wi
== count
- 1) /* About to start a new row? */
12630 col_index
= 0; /* Restart the columns counter. */
12638 /* The word after the last one is set to NULL. */
12639 /* """"""""""""""""""""""""""""""""""""""""""" */
12640 word_a
[count
].str
= NULL
;
12642 /* We can now allocate the space for our tmp_word work variable */
12643 /* augmented by the number of tabulation columns. This is not */
12644 /* optimal but the loss is tiny and we have the guarantee that */
12645 /* enough place will be allocated. */
12646 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12647 tmp_word
= xcalloc(1, word_real_max_size
+ tab_max_size
+ 1);
12649 search_data
.utf8_off_a
= xmalloc(word_real_max_size
* sizeof(long));
12650 search_data
.utf8_len_a
= xmalloc(word_real_max_size
* sizeof(long));
12652 win
.start
= 0; /* index of the first element in the *
12653 | words array to be displayed. */
12655 /* We can now build the first metadata. */
12656 /* """""""""""""""""""""""""""""""""""" */
12657 last_line
= build_metadata(&term
, count
, &win
);
12659 /* Adjust the max number of lines in the windows */
12660 /* if it has not be explicitly set. */
12661 /* """"""""""""""""""""""""""""""""""""""""""""" */
12662 if (line_nb_of_word_a
[count
- 1] < win
.max_lines
)
12663 win
.max_lines
= win
.asked_max_lines
= line_nb_of_word_a
[count
- 1] + 1;
12665 /* Index of the selected element in the array words */
12666 /* The string can be: */
12667 /* "last" The string "last" put the cursor on the last word */
12668 /* n a number put the cursor on the word n */
12669 /* /pref /+a regexp put the cursor on the first */
12670 /* word matching the prefix "pref" */
12671 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12673 for (wi
= 0; wi
< count
; wi
++)
12677 if (daccess
.padding
== 'a' || word_a
[wi
].is_numbered
)
12678 len
= daccess
.flength
;
12682 word_a
[wi
].bitmap
= xcalloc(1, (word_a
[wi
].mb
- len
) / CHAR_BIT
+ 1);
12685 /* Find the first selectable word (if any) in the input stream. */
12686 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12687 first_selectable
= 0;
12688 while (first_selectable
< count
&& !word_a
[first_selectable
].is_selectable
)
12689 first_selectable
++;
12691 /* If not found, abort. */
12692 /* """""""""""""""""""" */
12693 if (first_selectable
== count
)
12695 fprintf(stderr
, "No selectable word found.\n");
12697 exit(EXIT_FAILURE
);
12700 /* Else find the last selectable word in the input stream. */
12701 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
12702 last_selectable
= count
- 1;
12703 while (last_selectable
> 0 && !word_a
[last_selectable
].is_selectable
)
12706 if (pre_selection_index
== NULL
)
12707 /* Option -s was not used. */
12708 /* """"""""""""""""""""""" */
12709 current
= first_selectable
;
12710 else if (*pre_selection_index
== '/')
12712 /* A regular expression is expected. */
12713 /* """"""""""""""""""""""""""""""""" */
12716 if (regcomp(&re
, pre_selection_index
+ 1, REG_EXTENDED
| REG_NOSUB
) != 0)
12718 fprintf(stderr
, "%s: Invalid regular expression.\n", pre_selection_index
);
12720 exit(EXIT_FAILURE
);
12727 for (index
= first_selectable
; index
<= last_selectable
; index
++)
12729 if (!word_a
[index
].is_selectable
)
12732 if (word_a
[index
].orig
!= NULL
)
12733 word
= word_a
[index
].orig
;
12735 word
= word_a
[index
].str
;
12737 if (regexec(&re
, word
, (int)0, NULL
, 0) == 0)
12745 /* Insert the index in the search array. */
12746 /* """"""""""""""""""""""""""""""""""""" */
12747 insert_sorted_index(&matching_words_a
,
12748 &matching_words_a_size
,
12755 current
= first_selectable
;
12758 else if (*pre_selection_index
== '=') /* exact search. */
12760 /* An exact match is expected. */
12761 /* """"""""""""""""""""""""""" */
12767 list
= tst_search(tst_word
, w
= utf8_strtowcs(pre_selection_index
+ 1));
12771 current
= *(long *)(node
->data
);
12775 /* Insert the index in the search array. */
12776 /* """"""""""""""""""""""""""""""""""""" */
12777 insert_sorted_index(&matching_words_a
,
12778 &matching_words_a_size
,
12780 *(long *)(node
->data
));
12785 current
= first_selectable
;
12789 else if (*pre_selection_index
!= '\0')
12791 /* A prefix string or an index is expected. */
12792 /* """""""""""""""""""""""""""""""""""""""" */
12794 char *ptr
= pre_selection_index
;
12798 /* An index is expected. */
12799 /* """"""""""""""""""""" */
12802 if (sscanf(ptr
, "%ld%n", ¤t
, &len
) == 1 && len
== (int)strlen(ptr
))
12804 /* We got an index (numeric value). */
12805 /* """""""""""""""""""""""""""""""" */
12807 current
= first_selectable
;
12809 if (current
>= count
)
12810 current
= count
- 1;
12812 if (!word_a
[current
].is_selectable
)
12814 if (current
> last_selectable
)
12815 current
= last_selectable
;
12816 else if (current
< first_selectable
)
12817 current
= first_selectable
;
12819 while (current
> first_selectable
&& !word_a
[current
].is_selectable
)
12823 else if (*ptr
== '\0' || strcmp(ptr
, "last") == 0)
12824 /* We got a special index (empty or last). */
12825 /* """"""""""""""""""""""""""""""""""""""" */
12826 current
= last_selectable
;
12829 fprintf(stderr
, "%s: Invalid index.\n", ptr
);
12831 exit(EXIT_FAILURE
);
12838 /* A prefix is expected. */
12839 /* """"""""""""""""""""" */
12840 for (new_current
= first_selectable
; new_current
< count
; new_current
++)
12843 if (strprefix(word_a
[new_current
].str
, ptr
)
12844 && word_a
[new_current
].is_selectable
)
12848 current
= new_current
;
12852 /* Insert the index in the search array. */
12853 /* """"""""""""""""""""""""""""""""""""" */
12854 insert_sorted_index(&matching_words_a
,
12855 &matching_words_a_size
,
12862 current
= first_selectable
;
12866 current
= first_selectable
;
12868 /* We now need to adjust the 'start'/'end' fields of the */
12869 /* structure 'win'. */
12870 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
12871 set_win_start_end(&win
, current
, last_line
);
12873 /* Re-associates /dev/tty with stdin and stdout. */
12874 /* """"""""""""""""""""""""""""""""""""""""""""" */
12875 if (freopen("/dev/tty", "r", stdin
) == NULL
)
12877 fprintf(stderr
, "Unable to associate /dev/tty with stdin.\n");
12878 exit(EXIT_FAILURE
);
12882 old_stdout
= fdopen(old_fd1
, "w");
12884 setbuf(old_stdout
, NULL
);
12886 if (freopen("/dev/tty", "w", stdout
) == NULL
)
12888 fprintf(stderr
, "Unable to associate /dev/tty with stdout.\n");
12889 exit(EXIT_FAILURE
);
12892 setvbuf(stdout
, NULL
, _IONBF
, 0);
12894 /* Make sure smenu runs in foreground. */
12895 /* """"""""""""""""""""""""""""""""""" */
12896 if (!is_in_foreground_process_group())
12898 fprintf(stderr
, "smenu cannot be launched in background.\n");
12899 exit(EXIT_FAILURE
);
12902 /* Set the characteristics of the terminal. */
12903 /* """""""""""""""""""""""""""""""""""""""" */
12904 setup_term(fileno(stdin
), &old_in_attrs
, &new_in_attrs
);
12906 if (!get_cursor_position(&row
, &col
))
12909 "The terminal does not have the capability to report "
12910 "the cursor position.\n");
12911 restore_term(fileno(stdin
), &old_in_attrs
);
12913 exit(EXIT_FAILURE
);
12916 /* Initialize the search buffer with tab_real_max_size+1 NULs */
12917 /* It will never be reallocated, only cleared. */
12918 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12919 search_data
.buf
= xcalloc(1, word_real_max_size
+ 1 - daccess
.flength
);
12921 /* Hide the cursor. */
12922 /* """""""""""""""" */
12923 (void)tputs(TPARM1(cursor_invisible
), 1, outch
);
12925 /* Force the display to start at a beginning of line. */
12926 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
12927 get_cursor_position(&term
.curs_line
, &term
.curs_column
);
12928 if (term
.curs_column
> 1)
12931 /* Display the words window and its title for the first time. */
12932 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12933 disp_message(message_lines_list
,
12940 /* Before displaying the word windows for the first time when in */
12941 /* column or line mode, we need to ensure that the word under the */
12942 /* cursor will be visible by setting the number of the first column */
12943 /* to be displayed. */
12944 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12945 if (win
.col_mode
|| win
.line_mode
)
12950 len
= term
.ncolumns
- 3;
12952 /* Adjust win.first_column if the cursor is not visible. */
12953 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
12954 pos
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
12956 while (word_a
[current
].end
- word_a
[pos
].start
>= len
)
12959 win
.first_column
= word_a
[pos
].start
;
12962 /* Save the initial cursor line and column, here only the line is */
12963 /* interesting us. This will tell us if we are in need to compensate */
12964 /* a terminal automatic scrolling. */
12965 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12966 get_cursor_position(&term
.curs_line
, &term
.curs_column
);
12968 nl
= disp_lines(&win
,
12979 /* Assert the presence of an early display of the horizontal bar. */
12980 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12982 win
.hbar_displayed
= 1;
12984 /* Determine the number of lines to move the cursor up if the window */
12985 /* display needed a terminal scrolling. */
12986 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
12987 if (nl
+ term
.curs_line
- 1 > term
.nlines
)
12988 line_offset
= term
.curs_line
+ nl
- term
.nlines
;
12992 /* Set the cursor to the first line of the window. */
12993 /* """"""""""""""""""""""""""""""""""""""""""""""" */
12995 long i
; /* generic index in this block. */
12997 for (i
= 1; i
< line_offset
; i
++)
12998 (void)tputs(TPARM1(cursor_up
), 1, outch
);
13001 /* Enable the reporting of the mouse events. */
13002 /* """"""""""""""""""""""""""""""""""""""""" */
13003 if (!toggles
.no_mouse
)
13005 if (term
.has_kmous
&& strncmp(tigetstr("kmous"), "\x1b[<", 3) == 0)
13006 mouse_trk_on
= "\x1b[?1005l\x1b[?1000;1006h\x1b[?2004h";
13008 mouse_trk_on
= "\x1b[?1005l\x1b[?1000;1015;1006h\x1b[?2004h";
13010 mouse_trk_off
= "\x1b[?1000;1015;1006l\x1b[?2004l";
13012 printf("%s", mouse_trk_on
);
13015 /* Save again the cursor current line and column positions so that we */
13016 /* will be able to put the terminal cursor back here. */
13017 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13018 get_cursor_position(&term
.curs_line
, &term
.curs_column
);
13020 /* Arm the periodic timer. */
13021 /* """"""""""""""""""""""" */
13022 periodic_itv
.it_value
.tv_sec
= 0;
13023 periodic_itv
.it_value
.tv_usec
= TCK
;
13024 periodic_itv
.it_interval
.tv_sec
= 0;
13025 periodic_itv
.it_interval
.tv_usec
= TCK
;
13026 setitimer(ITIMER_REAL
, &periodic_itv
, NULL
);
13028 /* Signal management. */
13029 /* """""""""""""""""" */
13030 void sig_handler(int s
);
13032 sa
.sa_handler
= sig_handler
;
13034 sigemptyset(&sa
.sa_mask
);
13035 sigaction(SIGWINCH
, &sa
, NULL
);
13036 sigaction(SIGALRM
, &sa
, NULL
);
13037 sigaction(SIGTERM
, &sa
, NULL
);
13038 sigaction(SIGHUP
, &sa
, NULL
);
13039 sigaction(SIGSEGV
, &sa
, NULL
);
13040 sigaction(SIGPIPE
, &sa
, NULL
);
13046 int sc
= 0; /* scancode */
13048 /* Manage the case of a broken pipe by exiting failure and restoring */
13049 /* the terminal and the cursor. */
13050 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13053 (void)tputs(TPARM1(carriage_return
), 1, outch
);
13054 (void)tputs(TPARM1(cursor_normal
), 1, outch
);
13055 restore_term(fileno(stdin
), &old_in_attrs
);
13057 exit(128 + SIGPIPE
);
13060 /* Manage a segmentation fault by exiting with failure and restoring */
13061 /* the terminal and the cursor. */
13062 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13065 fputs_safe("SIGSEGV received!\n", stderr
);
13066 (void)tputs(TPARM1(carriage_return
), 1, outch
);
13067 (void)tputs(TPARM1(cursor_normal
), 1, outch
);
13068 restore_term(fileno(stdin
), &old_in_attrs
);
13070 exit(128 + SIGSEGV
);
13073 /* Manage the hangup and termination signal by exiting with failure */
13074 /* and restoring the terminal and the cursor. */
13075 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13076 if (got_sigterm
|| got_sighup
)
13078 fputs_safe("Interrupted!\n", stderr
);
13079 (void)tputs(TPARM1(carriage_return
), 1, outch
);
13080 (void)tputs(TPARM1(cursor_normal
), 1, outch
);
13081 restore_term(fileno(stdin
), &old_in_attrs
);
13084 exit(128 + SIGTERM
);
13086 exit(128 + SIGHUP
);
13089 /* If this alarm is triggered, then gracefully exit. */
13090 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
13091 if (got_forgotten_alrm
)
13093 (void)tputs(TPARM1(carriage_return
), 1, outch
);
13094 (void)tputs(TPARM1(cursor_normal
), 1, outch
);
13095 restore_term(fileno(stdin
), &old_in_attrs
);
13097 exit(EXIT_SUCCESS
);
13100 /* If this alarm is triggered, then redisplay the window */
13101 /* to remove the help message and disable this timer. */
13102 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
13107 /* Calculate the new metadata and draw the window again. */
13108 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
13109 last_line
= build_metadata(&term
, count
, &win
);
13112 nl
= disp_lines(&win
,
13124 /* Reset the direct access selector if the direct access alarm rang. */
13125 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13126 if (got_daccess_alrm
)
13128 got_daccess_alrm
= 0;
13129 memset(daccess_stack
, '\0', 6);
13130 daccess_stack_head
= 0;
13132 daccess_timer
= timers
.direct_access
;
13135 if (got_search_alrm
)
13137 got_search_alrm
= 0;
13139 /* Calculate the new metadata and draw the window again. */
13140 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
13141 last_line
= build_metadata(&term
, count
, &win
);
13143 search_mode
= NONE
;
13145 nl
= disp_lines(&win
,
13160 got_winch_alrm
= 0;
13161 winch_timer
= timers
.winch
; /* Rearm the refresh timer. */
13164 /* If the timeout is set then decrement its remaining value */
13165 /* Upon expiration of this alarm, we trigger a content update */
13166 /* of the window. */
13167 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13168 if (got_winch_alrm
)
13170 long i
; /* generic index in this block */
13171 int nlines
, ncolumns
;
13173 int original_message_lines
;
13175 got_winch_alrm
= 0; /* Reset the flag signaling the need for a refresh. */
13176 winch_timer
= -1; /* Disarm the timer used for this refresh. */
13178 if (message_lines_list
!= NULL
&& message_lines_list
->len
> 0)
13179 original_message_lines
= message_lines_list
->len
+ 1;
13181 original_message_lines
= 0;
13183 get_terminal_size(&nlines
, &ncolumns
, &term
);
13185 /* Update term with the new number of lines and columns */
13186 /* of the real terminal. */
13187 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
13188 term
.nlines
= nlines
;
13189 term
.ncolumns
= ncolumns
;
13191 /* Reset the number of lines if the terminal has enough lines. */
13192 /* message_lines_list->len+1 is used here instead of */
13193 /* win.message_lines because win.message_lines may have been */
13194 /* altered by a previous scrolling and not yet recalculated. */
13195 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13196 if (term
.nlines
<= original_message_lines
)
13198 win
.message_lines
= term
.nlines
- 1;
13203 win
.message_lines
= original_message_lines
;
13205 if (win
.max_lines
< term
.nlines
- win
.message_lines
)
13207 if (win
.asked_max_lines
== 0)
13208 win
.max_lines
= term
.nlines
- win
.message_lines
;
13211 if (win
.asked_max_lines
> term
.nlines
- win
.message_lines
)
13212 win
.max_lines
= term
.nlines
- win
.message_lines
;
13214 win
.max_lines
= win
.asked_max_lines
;
13218 win
.max_lines
= term
.nlines
- win
.message_lines
;
13221 /* Erase the visible part of the displayed window. */
13222 /* """"""""""""""""""""""""""""""""""""""""""""""" */
13223 for (i
= 0; i
< win
.message_lines
; i
++)
13225 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13226 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13227 (void)tputs(TPARM1(cursor_up
), 1, outch
);
13230 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13231 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13232 (void)tputs(TPARM1(save_cursor
), 1, outch
);
13234 get_cursor_position(&line
, &column
);
13236 for (i
= 1; i
< nl
+ win
.message_lines
; i
++)
13238 if (line
+ i
>= nlines
)
13239 break; /* We have reached the last terminal line. */
13241 (void)tputs(TPARM1(cursor_down
), 1, outch
);
13242 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13243 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13245 (void)tputs(TPARM1(restore_cursor
), 1, outch
);
13247 /* Get new cursor position. */
13248 /* """""""""""""""""""""""" */
13249 get_cursor_position(&term
.curs_line
, &term
.curs_column
);
13251 /* Calculate the new metadata and draw the window again. */
13252 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
13253 last_line
= build_metadata(&term
, count
, &win
);
13255 if (win
.col_mode
|| win
.line_mode
)
13260 len
= term
.ncolumns
- 3;
13262 /* Adjust win.first_column if the cursor is no more visible. */
13263 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13264 pos
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
13266 while (word_a
[current
].end
- word_a
[pos
].start
>= len
)
13269 win
.first_column
= word_a
[pos
].start
;
13272 /* Keep a line available for an eventual horizontal scroll bar. */
13273 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13274 if (win
.col_mode
|| win
.line_mode
)
13276 if (win
.asked_max_lines
== 0
13277 || win
.max_lines
> term
.nlines
- win
.message_lines
)
13279 win
.max_lines
= term
.nlines
- win
.message_lines
- 1;
13281 (void)tputs(TPARM3(cursor_address
, win
.max_lines
, 0), 1, outch
);
13282 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13283 (void)tputs(TPARM3(cursor_address
, 0, 0), 1, outch
);
13286 else if (win
.asked_max_lines
> term
.nlines
- win
.message_lines
)
13287 win
.max_lines
= term
.nlines
- win
.message_lines
- 1;
13289 win
.max_lines
= win
.asked_max_lines
;
13292 disp_message(message_lines_list
,
13299 nl
= disp_lines(&win
,
13310 /* Determine the number of lines to move the cursor up if the window */
13311 /* display needed a terminal scrolling. */
13312 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13313 if (nl
+ win
.message_lines
+ term
.curs_line
> term
.nlines
)
13314 line_offset
= term
.curs_line
+ nl
+ win
.message_lines
- term
.nlines
;
13318 /* Set the cursor to the first line of the window. */
13319 /* """"""""""""""""""""""""""""""""""""""""""""""" */
13320 for (i
= 1; i
< line_offset
; i
++)
13321 (void)tputs(TPARM1(cursor_up
), 1, outch
);
13323 /* Get new cursor position. */
13324 /* """""""""""""""""""""""" */
13325 get_cursor_position(&term
.curs_line
, &term
.curs_column
);
13327 /* Short-circuit the loop. */
13328 /* """"""""""""""""""""""" */
13332 /* and possibly set its reached value. */
13333 /* The counter is frozen in search and help mode. */
13334 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13335 if (timeout
.initial_value
&& search_mode
== NONE
&& !help_mode
13336 && got_timeout_tick
)
13339 char *timeout_string
;
13341 got_timeout_tick
= 0;
13345 if (!quiet_timeout
&& timeout
.remain
% FREQ
== 0)
13347 sprintf(timeout_seconds
, "%5u", timeout
.remain
/ FREQ
);
13349 (char *)(((ll_node_t
*)(message_lines_list
->tail
))->data
);
13350 memcpy(timeout_string
+ 1, timeout_seconds
, 5);
13352 /* Erase the current window. */
13353 /* """"""""""""""""""""""""" */
13354 for (i
= 0; i
< win
.message_lines
; i
++)
13356 (void)tputs(TPARM1(cursor_up
), 1, outch
);
13357 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13358 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13361 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13362 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13364 /* Display the words window and its title for the first time. */
13365 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13366 disp_message(message_lines_list
,
13373 /* The timeout has expired. */
13374 /* """""""""""""""""""""""" */
13375 if (timeout
.remain
== 0)
13376 timeout
.reached
= 1;
13379 if (timeout
.reached
)
13381 if (timeout
.mode
== QUIT
)
13383 else if (timeout
.mode
== CURRENT
|| timeout
.mode
== WORD
)
13387 /* Pressed keys scancodes processing. */
13388 /* """""""""""""""""""""""""""""""""" */
13389 page
= 1; /* Default number of lines to do down/up *
13390 | with PgDn/PgUp. */
13392 sc
= get_scancode(buffer
, 63);
13394 if (sc
&& winch_timer
< 0) /* Do not allow input when a window *
13395 | refresh is scheduled. */
13397 /* Rearm the timer named "forgotten". */
13398 /* """""""""""""""""""""""""""""""""" */
13399 forgotten_timer
= timers
.forgotten
;
13401 if (timeout
.initial_value
&& buffer
[0] != 0x0d && buffer
[0] != 'q'
13402 && buffer
[0] != 'Q' && buffer
[0] != 3)
13404 char *timeout_string
;
13406 /* Reset the timeout to its initial value. */
13407 /* """"""""""""""""""""""""""""""""""""""" */
13408 timeout
.remain
= timeout
.initial_value
;
13410 if (!quiet_timeout
)
13412 sprintf(timeout_seconds
, "%5u", timeout
.initial_value
/ FREQ
);
13414 (char *)(((ll_node_t
*)(message_lines_list
->tail
))->data
);
13415 memcpy(timeout_string
+ 1, timeout_seconds
, 5);
13417 /* Clear the message. */
13418 /* """""""""""""""""" */
13419 for (long i
= 0; i
< win
.message_lines
; i
++)
13421 (void)tputs(TPARM1(cursor_up
), 1, outch
);
13422 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13423 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13426 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13427 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13429 /* Display the words window and its title for the first time. */
13430 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13431 disp_message(message_lines_list
,
13439 setitimer(ITIMER_REAL
, &periodic_itv
, NULL
);
13442 if (search_mode
== NONE
&& help_mode
&& buffer
[0] != '?')
13450 case 0x01: /* ^A */
13451 if (search_mode
!= NONE
)
13456 case 0x1a: /* ^Z */
13457 if (search_mode
!= NONE
)
13462 case 0x1b: /* ESC */
13464 /* Ignore mouse pastes when bracketed pastes is enabled. */
13465 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
13466 if (memcmp("\x1b[200~", buffer
, 6) == 0)
13469 char eb
[6] = { 0 };
13471 /* Consume stdin until a closing bracket is found. */
13472 /* ''''''''''''''''''''''''''''''''''''''''''''''' */
13475 /* Fast reading until an ESC or the end of input is found. */
13476 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
13477 while ((c
= my_fgetc(stdin
)) != EOF
&& c
!= 0x1b)
13478 ; /* Null action. */
13480 /* Exits the loop early if the first ESC character starting */
13481 /* the ending bracket has already be consumed while reading */
13482 /* the content of buffer. */
13483 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13487 /* Read the 5 next characters to look for the */
13488 /* ending bracket "[201~". */
13489 /* """""""""""""""""""""""""""""""""""""""""" */
13491 if (memcmp("[201~", eb
, 5) == 0)
13496 /* An escape sequence or a UTF-8 sequence has been pressed. */
13497 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13498 if (memcmp("\x1bOH", buffer
, 3) == 0
13499 || memcmp("\x1bk", buffer
, 2) == 0
13500 || memcmp("\x1b[H", buffer
, 3) == 0
13501 || memcmp("\x1b[1~", buffer
, 4) == 0
13502 || memcmp("\x1b[7~", buffer
, 4) == 0)
13504 /* HOME key has been pressed. */
13505 /* """""""""""""""""""""""""" */
13506 if (search_mode
!= NONE
)
13509 search_data
.only_starting
= 1;
13510 search_data
.only_ending
= 0;
13511 select_starting_matches(&win
, &term
, &search_data
, &last_line
);
13515 /* Find the first selectable word. */
13516 /* """"""""""""""""""""""""""""""" */
13517 current
= win
.start
;
13519 while (current
< win
.end
&& !word_a
[current
].is_selectable
)
13522 /* In column mode we need to take care of the */
13523 /* horizontal scrolling. */
13524 /* """""""""""""""""""""""""""""""""""""""""" */
13525 if ((win
.col_mode
|| win
.line_mode
)
13526 && word_a
[current
].end
< win
.first_column
)
13527 win
.first_column
= word_a
[current
].start
;
13530 nl
= disp_lines(&win
,
13543 if (memcmp("\x1b[1;5H", buffer
, 6) == 0
13544 || memcmp("\x1b[7^", buffer
, 4) == 0)
13545 /* CTRL HOME key has been pressed. */
13546 /* """"""""""""""""""""""""""""""" */
13549 if (memcmp("\x1bOF", buffer
, 3) == 0
13550 || memcmp("\x1bj", buffer
, 2) == 0
13551 || memcmp("\x1b[F", buffer
, 3) == 0
13552 || memcmp("\x1b[4~", buffer
, 4) == 0
13553 || memcmp("\x1b[8~", buffer
, 4) == 0)
13555 /* END key has been pressed. */
13556 /* """"""""""""""""""""""""" */
13557 if (search_mode
!= NONE
)
13561 if (matches_count
> 0 && search_mode
!= PREFIX
)
13563 search_data
.only_starting
= 0;
13564 search_data
.only_ending
= 1;
13565 select_ending_matches(&win
, &term
, &search_data
, &last_line
);
13570 /* Find the last selectable word. */
13571 /* """""""""""""""""""""""""""""" */
13574 while (current
> win
.start
&& !word_a
[current
].is_selectable
)
13577 /* In column mode we need to take care of the */
13578 /* horizontal scrolling. */
13579 /* """""""""""""""""""""""""""""""""""""""""" */
13580 if (win
.col_mode
|| win
.line_mode
)
13581 set_new_first_column(&win
, &term
);
13584 nl
= disp_lines(&win
,
13597 if (memcmp("\x1b[1;5F", buffer
, 6) == 0
13598 || memcmp("\x1b[8^", buffer
, 4) == 0)
13599 /* CTRL END key has been pressed. */
13600 /* """""""""""""""""""""""""""""" */
13603 if (memcmp("\x1bOD", buffer
, 3) == 0
13604 || memcmp("\x1b[D", buffer
, 3) == 0)
13605 /* Left arrow key has been pressed. */
13606 /* """""""""""""""""""""""""""""""" */
13609 if (memcmp("\x1bOC", buffer
, 3) == 0
13610 || memcmp("\x1b[C", buffer
, 3) == 0)
13611 /* Right arrow key has been pressed. */
13612 /* """"""""""""""""""""""""""""""""" */
13615 if (memcmp("\x1bOA", buffer
, 3) == 0
13616 || memcmp("\x1b[A", buffer
, 3) == 0)
13617 /* Up arrow key has been pressed. */
13618 /* """""""""""""""""""""""""""""" */
13621 if (memcmp("\x1bOB", buffer
, 3) == 0
13622 || memcmp("\x1b[B", buffer
, 3) == 0)
13623 /* Down arrow key has been pressed. */
13624 /* """""""""""""""""""""""""""""""" */
13627 if (memcmp("\x1b[I", buffer
, 3) == 0
13628 || memcmp("\x1b[5~", buffer
, 4) == 0)
13629 /* PgUp key has been pressed. */
13630 /* """""""""""""""""""""""""" */
13633 if (memcmp("\x1b[G", buffer
, 3) == 0
13634 || memcmp("\x1b[6~", buffer
, 4) == 0)
13635 /* PgDn key has been pressed. */
13636 /* """""""""""""""""""""""""" */
13639 if (memcmp("\x1b[L", buffer
, 3) == 0
13640 || memcmp("\x1b[2~", buffer
, 4) == 0)
13641 /* Ins key has been pressed. */
13642 /* """"""""""""""""""""""""" */
13645 if (memcmp("\x1b[3~", buffer
, 4) == 0)
13646 /* Del key has been pressed. */
13647 /* """"""""""""""""""""""""" */
13650 if (memcmp("\x1b[1;2F", buffer
, 6) == 0
13651 || memcmp("\x1b[1;5C", buffer
, 6) == 0
13652 || memcmp("\x1b[8$", buffer
, 4) == 0
13653 || memcmp("\x1bOc", buffer
, 3) == 0)
13654 /* SHIFT END or CTRL -> has been pressed. */
13655 /* """""""""""""""""""""""""""""""""""""" */
13658 if (memcmp("\x1b[1;2H", buffer
, 6) == 0
13659 || memcmp("\x1b[1;5D", buffer
, 6) == 0
13660 || memcmp("\x1b[7$", buffer
, 4) == 0
13661 || memcmp("\x1bOd", buffer
, 3) == 0)
13662 /* SHIFT HOME or CTRL <- key has been pressed. */
13663 /* """"""""""""""""""""""""""""""""""""""""""" */
13666 if (!toggles
.no_mouse
)
13668 double res
= 5000; /* 5 s, arbitrary value. */
13669 struct timespec res_ts
;
13671 /* The minimal resolution for double-click must be 1/10 s. */
13672 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
13673 if (clock_getres(CLOCK_MONOTONIC
, &res_ts
) == -1)
13674 disable_double_click
= 1;
13676 res
= 1000.0 * res_ts
.tv_sec
+ 1e-6 * res_ts
.tv_nsec
;
13679 disable_double_click
= 1;
13681 /* Detect the mouse protocol. */
13682 /* """""""""""""""""""""""""" */
13683 if (memcmp("\x1b[<", buffer
, 3) == 0)
13685 mouse_proto
= MOUSE1006
;
13689 if (memcmp("\x1b[M", buffer
, 3) == 0)
13691 mouse_proto
= MOUSE1000
;
13695 if (memcmp("\x1b[32;", buffer
, 5) == 0
13696 || memcmp("\x1b[33;", buffer
, 5) == 0
13697 || memcmp("\x1b[34;", buffer
, 5) == 0
13698 || memcmp("\x1b[96;", buffer
, 5) == 0
13699 || memcmp("\x1b[97;", buffer
, 5) == 0)
13701 mouse_proto
= MOUSE1015
;
13706 if (buffer
[0] == 0x1b && buffer
[1] == '\0')
13708 /* ESC key has been pressed. */
13709 /* """"""""""""""""""""""""" */
13710 reset_search_buffer(&win
,
13719 word_real_max_size
);
13721 /* Unmark the marked word. */
13722 /* """"""""""""""""""""""" */
13723 if (toggles
.taggable
&& marked
>= 0)
13729 /* Else ignore key. */
13736 /* q or Q of ^C has been pressed. */
13737 /* """""""""""""""""""""""""""""" */
13739 if (search_mode
!= NONE
&& buffer
[0] != 3)
13740 goto special_cmds_when_searching
;
13743 long i
; /* Generic index in this block. */
13745 for (i
= 0; i
< win
.message_lines
; i
++)
13746 (void)tputs(TPARM1(cursor_up
), 1, outch
);
13748 if (toggles
.del_line
)
13750 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13751 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13752 (void)tputs(TPARM1(save_cursor
), 1, outch
);
13754 for (i
= 1; i
< nl
+ win
.message_lines
; i
++)
13756 (void)tputs(TPARM1(cursor_down
), 1, outch
);
13757 (void)tputs(TPARM1(clr_eol
), 1, outch
);
13758 (void)tputs(TPARM1(clr_bol
), 1, outch
);
13760 (void)tputs(TPARM1(restore_cursor
), 1, outch
);
13764 for (i
= 1; i
< nl
+ win
.message_lines
; i
++)
13765 (void)tputs(TPARM1(cursor_down
), 1, outch
);
13770 /* Disable the reporting of the mouse events. */
13771 /* """""""""""""""""""""""""""""""""""""""""" */
13772 if (!toggles
.no_mouse
)
13773 printf("%s", mouse_trk_off
);
13775 /* Restore the visibility of the cursor. */
13776 /* """"""""""""""""""""""""""""""""""""" */
13777 (void)tputs(TPARM1(cursor_normal
), 1, outch
);
13779 if (buffer
[0] == 3) /* ^C */
13781 if (int_string
!= NULL
)
13782 fprintf(old_stdout
, "%s", int_string
);
13784 /* Set the cursor at the start on the line an restore the */
13785 /* original terminal state before exiting. */
13786 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
13787 (void)tputs(TPARM1(carriage_return
), 1, outch
);
13788 restore_term(fileno(stdin
), &old_in_attrs
);
13790 if (int_as_in_shell
)
13791 exit(128 + SIGINT
);
13794 restore_term(fileno(stdin
), &old_in_attrs
);
13796 exit(EXIT_SUCCESS
);
13799 /* Form feed (^L) is a traditional method to redraw a screen. */
13800 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
13801 if (current
< win
.start
|| current
> win
.end
)
13802 last_line
= build_metadata(&term
, count
, &win
);
13804 nl
= disp_lines(&win
,
13818 /* n or the space bar has been pressed. */
13819 /* """""""""""""""""""""""""""""""""""" */
13820 if (search_mode
!= NONE
)
13821 goto special_cmds_when_searching
;
13823 if (matches_count
> 0)
13825 long pos
= find_next_matching_word(matching_words_a
,
13828 &matching_word_cur_index
);
13832 if (current
< win
.start
|| current
> win
.end
)
13833 last_line
= build_metadata(&term
, count
, &win
);
13835 /* Set new first column to display. */
13836 /* """""""""""""""""""""""""""""""" */
13837 set_new_first_column(&win
, &term
);
13839 nl
= disp_lines(&win
,
13853 /* N has been pressed.*/
13854 /* """"""""""""""""""" */
13855 if (search_mode
!= NONE
)
13856 goto special_cmds_when_searching
;
13858 if (matches_count
> 0)
13860 long pos
= find_prev_matching_word(matching_words_a
,
13863 &matching_word_cur_index
);
13868 if (current
< win
.start
|| current
> win
.end
)
13869 last_line
= build_metadata(&term
, count
, &win
);
13871 /* Set new first column to display. */
13872 /* """""""""""""""""""""""""""""""" */
13873 set_new_first_column(&win
, &term
);
13875 nl
= disp_lines(&win
,
13888 /* s has been pressed. */
13889 /* """"""""""""""""""" */
13890 if (search_mode
!= NONE
)
13891 goto special_cmds_when_searching
;
13893 if (matches_count
> 0)
13897 if (best_matches_count
> 0)
13898 pos
= find_next_matching_word(best_matching_words_a
,
13899 best_matches_count
,
13901 &matching_word_cur_index
);
13903 pos
= find_next_matching_word(matching_words_a
,
13906 &matching_word_cur_index
);
13911 if (current
< win
.start
|| current
> win
.end
)
13912 last_line
= build_metadata(&term
, count
, &win
);
13914 /* Set new first column to display. */
13915 /* """""""""""""""""""""""""""""""" */
13916 set_new_first_column(&win
, &term
);
13918 nl
= disp_lines(&win
,
13932 /* S has been pressed. */
13933 /* """"""""""""""""""" */
13934 if (search_mode
!= NONE
)
13935 goto special_cmds_when_searching
;
13937 if (matches_count
> 0)
13941 if (best_matches_count
> 0)
13942 pos
= find_prev_matching_word(best_matching_words_a
,
13943 best_matches_count
,
13945 &matching_word_cur_index
);
13947 pos
= find_prev_matching_word(matching_words_a
,
13950 &matching_word_cur_index
);
13956 if (current
< win
.start
|| current
> win
.end
)
13957 last_line
= build_metadata(&term
, count
, &win
);
13959 /* Set new first column to display. */
13960 /* """""""""""""""""""""""""""""""" */
13961 set_new_first_column(&win
, &term
);
13963 nl
= disp_lines(&win
,
13976 case 0x0d: /* CR */
13978 /* <Enter> has been pressed. */
13979 /* """"""""""""""""""""""""" */
13983 output_t
*output_node
;
13988 long i
; /* Generic index in this block. */
13990 if (help_mode
|| marked
>= 0)
13992 marked
= -1; /* Disable the marked mode unconditionally. */
13993 nl
= disp_lines(&win
,
14005 if (search_mode
!= NONE
)
14007 /* Cancel the search timer. */
14008 /* """""""""""""""""""""""" */
14011 search_mode
= NONE
;
14012 search_data
.only_starting
= 0;
14013 search_data
.only_ending
= 0;
14015 nl
= disp_lines(&win
,
14026 if (!toggles
.enter_val_in_search
)
14030 if (toggles
.del_line
)
14032 for (i
= 0; i
< win
.message_lines
; i
++)
14033 (void)tputs(TPARM1(cursor_up
), 1, outch
);
14035 (void)tputs(TPARM1(clr_eol
), 1, outch
);
14036 (void)tputs(TPARM1(clr_bol
), 1, outch
);
14037 (void)tputs(TPARM1(save_cursor
), 1, outch
);
14039 for (i
= 1; i
< nl
+ win
.message_lines
; i
++)
14041 (void)tputs(TPARM1(cursor_down
), 1, outch
);
14042 (void)tputs(TPARM1(clr_eol
), 1, outch
);
14043 (void)tputs(TPARM1(clr_bol
), 1, outch
);
14046 (void)tputs(TPARM1(restore_cursor
), 1, outch
);
14050 for (i
= 1; i
< nl
; i
++)
14051 (void)tputs(TPARM1(cursor_down
), 1, outch
);
14055 /* When a timeout of type WORD is set, prints the specified word */
14056 /* else prints the current selected entries. */
14057 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
14058 if (timeout
.initial_value
> 0 && timeout
.remain
== 0
14059 && timeout
.mode
== WORD
)
14060 fprintf(old_stdout
, "%s", timeout_word
);
14066 if (toggles
.taggable
)
14068 ll_t
*output_list
= ll_new();
14071 /* When using -P, updates the tagging order of this word to */
14072 /* make sure that the output will be correctly sorted. */
14073 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
14074 if (word_a
[current
].tag_id
== 0 && toggles
.pinable
)
14075 word_a
[current
].tag_order
= tag_nb
++;
14077 for (wi
= 0; wi
< count
; wi
++)
14079 if (word_a
[wi
].tag_id
> 0 || wi
== current
)
14081 /* If the -p option is not used we do not take into */
14082 /* account an untagged word under the cursor if at least */
14083 /* on word is tagged. */
14084 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
14085 if (wi
== current
&& tagged_words
> 0 && !toggles
.autotag
14086 && word_a
[wi
].tag_id
== 0)
14089 /* In tagged mode, do not automatically tag the word */
14090 /* under the cursor if toggles.noautotag is set and no */
14091 /* word are tagged. */
14092 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
14093 if (tagged_words
== 0 && toggles
.taggable
14094 && toggles
.noautotag
)
14097 /* Chose the original string to print if the current one */
14098 /* has been altered by a possible expansion. */
14099 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
14100 output_node
= xmalloc(sizeof(output_t
));
14102 if (word_a
[wi
].orig
!= NULL
)
14103 str
= word_a
[wi
].orig
;
14105 str
= word_a
[wi
].str
;
14107 if (word_a
[wi
].is_numbered
&& daccess
.num_sep
)
14109 num_str
= xstrndup(str
+ 1, daccess
.length
);
14111 ltrim(num_str
, " ");
14112 rtrim(num_str
, " ", 0);
14114 output_node
->output_str
= concat(num_str
,
14116 str
+ daccess
.flength
,
14122 output_node
->output_str
= xstrdup(str
+ daccess
.flength
);
14124 output_node
->order
= word_a
[wi
].tag_order
;
14126 /* Trim the spaces if -k is not given. */
14127 /* """"""""""""""""""""""""""""""""""" */
14128 if (!toggles
.keep_spaces
)
14130 ltrim(output_node
->output_str
, " \t");
14131 rtrim(output_node
->output_str
, " \t", 0);
14134 ll_append(output_list
, output_node
);
14138 /* If nothing is selected, exist without printing anything. */
14139 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
14140 if (output_list
->head
== NULL
)
14143 /* If -P is in use, then sort the output list. */
14144 /* """"""""""""""""""""""""""""""""""""""""""" */
14145 if (toggles
.pinable
)
14146 ll_sort(output_list
, tag_comp
, tag_swap
);
14148 /* And print them. */
14149 /* """"""""""""""" */
14150 node
= output_list
->head
;
14151 while (node
->next
!= NULL
)
14153 str
= ((output_t
*)(node
->data
))->output_str
;
14155 fprintf(old_stdout
, "%s", str
);
14156 width
+= wcswidth((w
= utf8_strtowcs(str
)), 65535);
14161 if (win
.sel_sep
!= NULL
)
14163 fprintf(old_stdout
, "%s", win
.sel_sep
);
14164 width
+= wcswidth((w
= utf8_strtowcs(win
.sel_sep
)), 65535);
14169 fprintf(old_stdout
, " ");
14176 str
= ((output_t
*)(node
->data
))->output_str
;
14177 fprintf(old_stdout
, "%s", str
);
14178 width
+= wcswidth((w
= utf8_strtowcs(str
)), 65535);
14185 /* Chose the original string to print if the current one has */
14186 /* been altered by a possible expansion. */
14187 /* Once this made, print it. */
14188 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
14189 if (word_a
[current
].orig
!= NULL
)
14190 str
= word_a
[current
].orig
;
14192 str
= word_a
[current
].str
;
14194 if (word_a
[current
].is_numbered
&& daccess
.num_sep
)
14196 num_str
= xstrndup(str
+ 1, daccess
.length
);
14198 ltrim(num_str
, " ");
14199 rtrim(num_str
, " ", 0);
14201 output_str
= concat(num_str
,
14203 str
+ daccess
.flength
,
14209 output_str
= str
+ daccess
.flength
;
14211 /* Trim the spaces if -k is not given. */
14212 /* """"""""""""""""""""""""""""""""""" */
14213 if (!toggles
.keep_spaces
)
14215 ltrim(output_str
, " \t");
14216 rtrim(output_str
, " \t", 0);
14219 width
= wcswidth((w
= utf8_strtowcs(output_str
)), 65535);
14222 /* And print it. */
14223 /* """"""""""""" */
14224 fprintf(old_stdout
, "%s", output_str
);
14227 /* If the output stream is a terminal. */
14228 /* """"""""""""""""""""""""""""""""""" */
14229 if (isatty(old_fd1
))
14231 /* width is (in term of terminal columns) the size of the */
14232 /* string to be displayed. */
14234 /* With that information, count the number of terminal lines */
14236 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
14237 extra_lines
= width
/ term
.ncolumns
;
14239 /* Clean the printed line and all the extra lines used. */
14240 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
14241 (void)tputs(TPARM1(delete_line
), 1, outch
);
14243 for (i
= 0; i
< extra_lines
; i
++)
14245 (void)tputs(TPARM1(cursor_up
), 1, outch
);
14246 (void)tputs(TPARM1(clr_eol
), 1, outch
);
14247 (void)tputs(TPARM1(clr_bol
), 1, outch
);
14254 /* Disable mouse reporting. */
14255 /* '''''''''''''''''''''''' */
14256 if (!toggles
.no_mouse
)
14257 printf("%s", mouse_trk_off
);
14259 /* Restore the visibility of the cursor. */
14260 /* """"""""""""""""""""""""""""""""""""" */
14261 (void)tputs(TPARM1(cursor_normal
), 1, outch
);
14263 /* Set the cursor at the start on the line an restore the */
14264 /* original terminal state before exiting. */
14265 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
14266 (void)tputs(TPARM1(carriage_return
), 1, outch
);
14267 restore_term(fileno(stdin
), &old_in_attrs
);
14269 exit(EXIT_SUCCESS
);
14273 /* Go to the start of the line. */
14274 /* """""""""""""""""""""""""""" */
14275 search_mode
= NONE
;
14277 /* Fall through. */
14278 /* """"""""""""" */
14281 if (search_mode
== NONE
)
14283 if (win
.col_mode
|| win
.line_mode
)
14287 pos
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
14289 search_mode
= NONE
;
14291 /* Find the first selectable word. */
14292 /* """"""""""""""""""""""""""""""" */
14293 while (!word_a
[pos
].is_last
)
14295 if (word_a
[pos
].is_selectable
)
14307 if (word_a
[pos
].is_last
&& word_a
[pos
].is_selectable
)
14310 set_new_first_column(&win
, &term
);
14312 nl
= disp_lines(&win
,
14325 goto special_cmds_when_searching
;
14330 /* Cursor Left key has been pressed. */
14331 /* """"""""""""""""""""""""""""""""" */
14332 search_mode
= NONE
;
14334 /* Fall through. */
14335 /* """"""""""""" */
14338 if (search_mode
== NONE
)
14348 goto special_cmds_when_searching
;
14352 /* shift the window to the left if possible. */
14353 /* """"""""""""""""""""""""""""""""""""""""" */
14355 if (search_mode
== NONE
)
14364 line_nb_of_word_a
[current
]);
14366 goto special_cmds_when_searching
;
14371 /* Go to the end of the line. */
14372 /* """""""""""""""""""""""""" */
14373 search_mode
= NONE
;
14375 /* Fall through. */
14376 /* """"""""""""" */
14379 if (search_mode
== NONE
)
14381 if (win
.col_mode
|| win
.line_mode
)
14387 search_mode
= NONE
;
14389 /* Find the first selectable word. */
14390 /* """"""""""""""""""""""""""""""" */
14391 while (!word_a
[pos
].is_last
)
14393 if (word_a
[pos
].is_selectable
)
14399 if (word_a
[pos
].is_selectable
)
14402 set_new_first_column(&win
, &term
);
14404 nl
= disp_lines(&win
,
14417 goto special_cmds_when_searching
;
14422 /* Right key has been pressed. */
14423 /* """"""""""""""""""""""""""" */
14424 search_mode
= NONE
;
14426 /* Fall through. */
14427 /* """"""""""""" */
14430 if (search_mode
== NONE
)
14440 goto special_cmds_when_searching
;
14444 /* shift the window to the left if possible. */
14445 /* """"""""""""""""""""""""""""""""""""""""" */
14447 if (search_mode
== NONE
)
14456 line_nb_of_word_a
[current
]);
14458 goto special_cmds_when_searching
;
14463 /* ^K key has been pressed. */
14464 /* """""""""""""""""""""""" */
14468 if (search_mode
!= NONE
)
14469 goto special_cmds_when_searching
;
14472 /* PgUp key has been pressed. */
14473 /* """""""""""""""""""""""""" */
14474 page
= win
.max_lines
;
14477 /* Cursor Up key has been pressed. */
14478 /* """"""""""""""""""""""""""""""" */
14479 search_mode
= NONE
;
14481 /* Fall through. */
14482 /* """"""""""""" */
14485 if (search_mode
== NONE
)
14497 goto special_cmds_when_searching
;
14502 /* Go to the first selectable word. */
14503 /* """""""""""""""""""""""""""""""" */
14506 search_mode
= NONE
;
14508 /* Find the first selectable word. */
14509 /* """"""""""""""""""""""""""""""" */
14510 while (!word_a
[current
].is_selectable
)
14513 if (current
< win
.start
|| current
> win
.end
)
14514 last_line
= build_metadata(&term
, count
, &win
);
14516 /* In column mode we need to take care of the */
14517 /* horizontal scrolling. */
14518 /* """""""""""""""""""""""""""""""""""""""""" */
14519 if (win
.col_mode
|| win
.line_mode
)
14523 /* Adjust win.first_column if the cursor is */
14524 /* no more visible. */
14525 /* """""""""""""""""""""""""""""""""""""""" */
14526 pos
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
14528 while (word_a
[current
].end
- word_a
[pos
].start
>= term
.ncolumns
- 3)
14531 win
.first_column
= word_a
[pos
].start
;
14534 nl
= disp_lines(&win
,
14547 /* ^J key has been pressed. */
14548 /* """""""""""""""""""""""" */
14552 if (search_mode
!= NONE
)
14553 goto special_cmds_when_searching
;
14556 /* PgDn key has been pressed. */
14557 /* """""""""""""""""""""""""" */
14558 page
= win
.max_lines
;
14561 /* Cursor Down key has been pressed. */
14562 /* """"""""""""""""""""""""""""""""" */
14563 search_mode
= NONE
;
14565 /* Fall through. */
14566 /* """"""""""""" */
14569 if (search_mode
== NONE
)
14581 goto special_cmds_when_searching
;
14586 /* Go to the last selectable word. */
14587 /* """"""""""""""""""""""""""""""" */
14588 current
= count
- 1;
14590 search_mode
= NONE
;
14592 /* Find the first selectable word. */
14593 /* """"""""""""""""""""""""""""""" */
14594 while (!word_a
[current
].is_selectable
)
14597 if (current
< win
.start
|| current
> win
.end
)
14598 last_line
= build_metadata(&term
, count
, &win
);
14600 /* In column mode we need to take care of the */
14601 /* horizontal scrolling. */
14602 /* """""""""""""""""""""""""""""""""""""""""" */
14603 if (win
.col_mode
|| win
.line_mode
)
14607 /* Adjust win.first_column if the cursor is no more visible. */
14608 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
14609 pos
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
14611 while (word_a
[current
].end
- word_a
[pos
].start
>= term
.ncolumns
- 3)
14614 win
.first_column
= word_a
[pos
].start
;
14617 nl
= disp_lines(&win
,
14630 /* Generic search method according the misc settings. */
14631 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
14632 if (misc
.default_search_method
== PREFIX
)
14633 goto prefix_method
;
14634 else if (misc
.default_search_method
== SUBSTRING
)
14635 goto substring_method
;
14636 else if (misc
.default_search_method
== FUZZY
)
14643 /* ~ or * key has been pressed */
14644 /* (start of a fuzzy search session). */
14645 /* """""""""""""""""""""""""""""""""" */
14648 if (search_mode
== NONE
)
14650 if (!toggles
.incremental_search
)
14651 reset_search_buffer(&win
,
14660 word_real_max_size
);
14662 /* Set the search timer. */
14663 /* """"""""""""""""""""" */
14664 search_timer
= timers
.search
; /* default 10 s. */
14666 search_mode
= FUZZY
;
14668 if (old_search_mode
!= FUZZY
)
14670 old_search_mode
= FUZZY
;
14671 clean_matches(&search_data
, word_real_max_size
);
14674 nl
= disp_lines(&win
,
14686 goto special_cmds_when_searching
;
14692 /* ' or " key has been pressed */
14693 /* (start of a substring search session). */
14694 /* """""""""""""""""""""""""""""""""""""" */
14697 if (search_mode
== NONE
)
14699 if (!toggles
.incremental_search
)
14700 reset_search_buffer(&win
,
14709 word_real_max_size
);
14711 /* Set the search timer. */
14712 /* """"""""""""""""""""" */
14713 search_timer
= timers
.search
; /* default 10 s. */
14715 search_mode
= SUBSTRING
;
14717 if (old_search_mode
!= SUBSTRING
)
14719 old_search_mode
= SUBSTRING
;
14720 clean_matches(&search_data
, word_real_max_size
);
14723 nl
= disp_lines(&win
,
14735 goto special_cmds_when_searching
;
14741 /* ^ or = key has been pressed */
14742 /* (start of a prefix search session). */
14743 /* """"""""""""""""""""""""""""""""""" */
14746 if (search_mode
== NONE
)
14748 if (!toggles
.incremental_search
)
14749 reset_search_buffer(&win
,
14758 word_real_max_size
);
14760 /* Set the search timer. */
14761 /* """"""""""""""""""""" */
14762 search_timer
= timers
.search
; /* default 10 s. */
14764 search_mode
= PREFIX
;
14766 if (old_search_mode
!= PREFIX
)
14768 old_search_mode
= PREFIX
;
14769 clean_matches(&search_data
, word_real_max_size
);
14772 nl
= disp_lines(&win
,
14785 goto special_cmds_when_searching
;
14790 /* The INS key has been pressed to tag a word if */
14791 /* tagging is enabled. */
14792 /* """"""""""""""""""""""""""""""""""""""""""""" */
14793 if (toggles
.taggable
&& word_a
[current
].tag_id
== 0)
14795 word_a
[current
].tag_id
= win
.next_tag_id
++;
14798 if (toggles
.pinable
)
14799 word_a
[current
].tag_order
= tag_nb
++;
14801 nl
= disp_lines(&win
,
14815 /* The DEL key has been pressed to untag a word if */
14816 /* tagging is enabled. */
14817 /* """"""""""""""""""""""""""""""""""""""""""""""" */
14818 if (toggles
.taggable
&& word_a
[current
].tag_id
> 0)
14820 word_a
[current
].tag_id
= 0;
14823 /* We do not try to change tag_nb here to guaranty that */
14824 /* tag_nb will be greater than all those already stored */
14825 /* in all word_a[*].tag_order. */
14826 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
14828 nl
= disp_lines(&win
,
14842 /* t has been pressed to tag/untag a word if */
14843 /* tagging is enabled. */
14844 /* """"""""""""""""""""""""""""""""""""""""" */
14845 if (search_mode
== NONE
)
14847 if (toggles
.taggable
)
14849 if (word_a
[current
].tag_id
> 0)
14851 word_a
[current
].tag_id
= 0;
14856 word_a
[current
].tag_id
= win
.next_tag_id
++;
14859 if (toggles
.pinable
)
14860 word_a
[current
].tag_order
= tag_nb
++;
14863 nl
= disp_lines(&win
,
14876 goto special_cmds_when_searching
;
14880 /* u has been pressed to untag a word if */
14881 /* tagging is enabled. */
14882 /* """"""""""""""""""""""""""""""""""""" */
14883 if (search_mode
== NONE
)
14885 if (toggles
.taggable
)
14887 if (word_a
[current
].tag_id
> 0)
14889 word_a
[current
].tag_id
= 0;
14892 nl
= disp_lines(&win
,
14906 goto special_cmds_when_searching
;
14910 /* (CTRL-t) Remove all tags. */
14911 /* """"""""""""""""""""""""" */
14912 if (search_mode
== NONE
)
14914 if (toggles
.taggable
&& (win
.next_tag_id
> 1 || marked
>= 0))
14917 win
.next_tag_id
= 1;
14920 for (wi
= 0; wi
< count
; wi
++)
14922 word_a
[wi
].tag_id
= 0;
14923 word_a
[wi
].tag_order
= 0;
14926 nl
= disp_lines(&win
,
14939 goto special_cmds_when_searching
;
14944 /* Tag/untag all the words in the current column. */
14945 /* """""""""""""""""""""""""""""""""""""""""""""" */
14946 if (search_mode
== NONE
)
14948 if (toggles
.taggable
|| toggles
.pinable
)
14950 long col
, cur_col
, marked_col
;
14958 /* Get the current column number. */
14959 /* """""""""""""""""""""""""""""" */
14961 - first_word_in_line_a
[line_nb_of_word_a
[current
]] + 1;
14963 /* Determine the loop values according to the existence of */
14964 /* a marked word and is value. */
14965 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
14973 marked_col
= marked
14974 - first_word_in_line_a
[line_nb_of_word_a
[marked
]]
14977 /* Ignore the marked word is its is not on the same column */
14978 /* as the current word. */
14979 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
14980 if (cur_col
== marked_col
)
14982 if (marked
<= current
)
14984 first
= first_word_in_line_a
[line_nb_of_word_a
[marked
]];
14989 first
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
14992 /* Pre-increment tag_nb with is maximum mulue as */
14993 /* it will be decremented in the following loop when */
14994 /* marked > current */
14995 /* ''''''''''''''''''''''''''''''''''''''''''''''''' */
14996 tag_nb
+= marked
- current
+ 1;
15003 /* Tag from first to last. */
15004 /* """"""""""""""""""""""" */
15008 for (wi
= first
; wi
<= last
; wi
++)
15011 if (col
== cur_col
&& word_a
[wi
].is_selectable
15012 && word_a
[wi
].tag_id
== 0)
15014 word_a
[wi
].tag_id
= win
.next_tag_id
;
15017 if (toggles
.pinable
)
15019 if (marked
<= current
)
15020 word_a
[wi
].tag_order
= tag_nb
++;
15022 word_a
[wi
].tag_order
= tag_nb
--;
15028 /* Time to go back to column 1? */
15029 /* """""""""""""""""""""""""""" */
15030 if (word_a
[wi
].is_last
)
15037 if (marked
> current
)
15038 tag_nb
+= marked
- current
+ 1;
15042 nl
= disp_lines(&win
,
15055 goto special_cmds_when_searching
;
15059 /* Allow to tag part of a column, the first invocation marks */
15060 /* the starting word and the second marks the words in between. */
15061 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
15062 if (search_mode
== NONE
)
15064 /* Mark the first word is not already marked. */
15065 /* """""""""""""""""""""""""""""""""""""""""" */
15069 if (!toggles
.taggable
&& !toggles
.pinable
)
15078 goto special_cmds_when_searching
;
15083 /* Tag/untag all the words in the current line. */
15084 /* """""""""""""""""""""""""""""""""""""""""""" */
15085 if (search_mode
== NONE
)
15087 if (toggles
.taggable
|| toggles
.pinable
)
15093 if (!win
.col_mode
&& !win
.line_mode
)
15098 marked_line
= line_nb_of_word_a
[marked
];
15099 if (marked_line
== line_nb_of_word_a
[current
])
15101 if (marked
<= current
)
15111 /* Pre-increment tag_nb with is maximum mulue as */
15112 /* it will be decremented in the following loop when */
15113 /* marked > current */
15114 /* ''''''''''''''''''''''''''''''''''''''''''''''''' */
15115 tag_nb
+= marked
- current
+ 1;
15123 first
= first_word_in_line_a
[line_nb_of_word_a
[current
]];
15124 if (line_nb_of_word_a
[current
] == line_nb_of_word_a
[count
- 1])
15127 last
= first_word_in_line_a
[line_nb_of_word_a
[current
] + 1]
15131 /* Tag from first to last. */
15132 /* """"""""""""""""""""""" */
15138 if (word_a
[wi
].is_selectable
)
15140 if (word_a
[wi
].tag_id
== 0)
15142 word_a
[wi
].tag_id
= win
.next_tag_id
;
15145 if (toggles
.pinable
)
15147 if (marked
<= current
)
15148 word_a
[wi
].tag_order
= tag_nb
++;
15150 word_a
[wi
].tag_order
= tag_nb
--;
15157 } while (wi
<= last
);
15162 if (marked
> current
)
15163 tag_nb
+= marked
- current
+ 1;
15167 nl
= disp_lines(&win
,
15180 goto special_cmds_when_searching
;
15184 /* Make sure that all the words in the current line */
15185 /* are not tagged. */
15186 /* """""""""""""""""""""""""""""""""""""""""""""""" */
15187 if (search_mode
== NONE
)
15189 if (!win
.col_mode
&& !win
.line_mode
)
15192 if (!toggles
.taggable
&& !toggles
.pinable
)
15201 goto special_cmds_when_searching
;
15206 /* Mark the current word (ESC clears the mark). */
15207 /* """""""""""""""""""""""""""""""""""""""""""" */
15208 if (search_mode
== NONE
)
15210 if (toggles
.taggable
)
15214 nl
= disp_lines(&win
,
15227 goto special_cmds_when_searching
;
15232 /* unmark the current word (ESC clears the mark). */
15233 /* """""""""""""""""""""""""""""""""""""""""""""" */
15234 if (search_mode
== NONE
)
15236 if (toggles
.taggable
)
15240 nl
= disp_lines(&win
,
15253 goto special_cmds_when_searching
;
15258 /* T has been pressed to tag all matching words resulting */
15259 /* from a previous search if tagging is enabled. */
15260 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
15261 if (search_mode
== NONE
)
15263 if (toggles
.taggable
)
15267 /* Is words have been matched by a recent search, tag them. */
15268 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
15269 if (matches_count
> 0 && marked
== -1)
15273 for (i
= 0; i
< matches_count
; i
++)
15275 wi
= matching_words_a
[i
];
15277 if (word_a
[wi
].tag_id
== 0)
15279 word_a
[wi
].tag_id
= win
.next_tag_id
;
15282 if (toggles
.pinable
)
15283 word_a
[wi
].tag_order
= tag_nb
++;
15291 else /* Tag word between the marked and current words. */
15300 if (marked
<= current
)
15310 /* Pre-increment tag_nb with is maximum mulue as */
15311 /* it will be decremented in the following loop when */
15312 /* marked > current */
15313 /* ''''''''''''''''''''''''''''''''''''''''''''''''' */
15314 tag_nb
+= marked
- current
+ 1;
15319 for (wi
= first
; wi
<= last
; wi
++)
15321 if (!word_a
[wi
].is_selectable
)
15324 if (word_a
[wi
].tag_id
== 0)
15326 word_a
[wi
].tag_id
= win
.next_tag_id
;
15329 if (toggles
.pinable
)
15331 if (marked
<= current
)
15332 word_a
[wi
].tag_order
= tag_nb
++;
15334 word_a
[wi
].tag_order
= tag_nb
--;
15344 if (marked
> current
)
15345 tag_nb
+= marked
- current
+ 1;
15351 nl
= disp_lines(&win
,
15364 goto special_cmds_when_searching
;
15367 adaptative_tag_to_mark
:
15369 /* Z has been pressed to tag consecutive word in a given zone . */
15370 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
15371 if (search_mode
== NONE
)
15373 if (toggles
.taggable
)
15377 else if (marked
== current
)
15379 else if (marked
>= 0 && win
.col_mode
)
15382 current
- first_word_in_line_a
[line_nb_of_word_a
[current
]]
15385 marked
- first_word_in_line_a
[line_nb_of_word_a
[marked
]] + 1;
15386 if (cur_col
== mark_col
)
15388 else if (line_nb_of_word_a
[current
]
15389 == line_nb_of_word_a
[marked
])
15394 else if (marked
>= 0)
15397 && line_nb_of_word_a
[current
] == line_nb_of_word_a
[marked
])
15403 nl
= disp_lines(&win
,
15415 goto special_cmds_when_searching
;
15419 /* U has been pressed to undo the last tagging operation. */
15420 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
15421 if (search_mode
== NONE
)
15423 if (toggles
.taggable
)
15425 for (wi
= 0; wi
< count
; wi
++)
15427 if (!word_a
[wi
].is_selectable
)
15430 if (word_a
[wi
].tag_id
> 0)
15432 if (word_a
[wi
].tag_id
== win
.next_tag_id
- 1)
15434 word_a
[wi
].tag_id
= 0;
15440 if (win
.next_tag_id
> 1)
15443 nl
= disp_lines(&win
,
15456 goto special_cmds_when_searching
;
15469 /* A digit has been pressed to build a number to be used for */
15470 /* A direct access to a words if direct access is enabled. */
15471 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
15473 if (search_mode
== NONE
&& daccess
.mode
!= DA_TYPE_NONE
)
15478 /* Set prev_current to the initial current word to be */
15479 /* able to return here if the first direct access fails. */
15480 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
15481 if (daccess_stack_head
== 0)
15482 prev_current
= current
;
15484 if (daccess_stack_head
== daccess
.length
)
15487 daccess_stack
[daccess_stack_head
] = buffer
[0];
15488 daccess_stack_head
++;
15489 w
= utf8_strtowcs(daccess_stack
);
15490 pos
= tst_search(tst_daccess
, w
);
15497 if (current
< win
.start
|| current
> win
.end
)
15498 last_line
= build_metadata(&term
, count
, &win
);
15500 /* Set new first column to display. */
15501 /* """""""""""""""""""""""""""""""" */
15502 set_new_first_column(&win
, &term
);
15504 nl
= disp_lines(&win
,
15517 if (current
!= prev_current
)
15519 current
= prev_current
;
15521 if (current
< win
.start
|| current
> win
.end
)
15522 last_line
= build_metadata(&term
, count
, &win
);
15524 /* Set new first column to display. */
15525 /* """""""""""""""""""""""""""""""" */
15526 set_new_first_column(&win
, &term
);
15528 nl
= disp_lines(&win
,
15541 daccess_timer
= timers
.direct_access
;
15544 goto special_cmds_when_searching
;
15548 case 0x08: /* ^H */
15549 case 0x7f: /* BS */
15550 /* backspace/CTRL-H management. */
15551 /* """""""""""""""""""""""""""" */
15555 if (daccess_stack_head
> 0)
15556 daccess_stack
[--daccess_stack_head
] = '\0';
15558 if (search_mode
!= NONE
)
15560 if (search_data
.utf8_len
> 0)
15564 prev
= utf8_prev(search_data
.buf
,
15565 search_data
.buf
+ search_data
.len
- 1);
15567 /* Reset the error indicator if we erase the first non */
15568 /* matching element of the search buffer. */
15569 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
15570 if (search_mode
!= FUZZY
15571 || search_data
.utf8_len
== search_data
.fuzzy_err_pos
- 1)
15573 search_data
.err
= 0;
15574 search_data
.fuzzy_err_pos
= -1;
15576 search_data
.utf8_len
--;
15580 *(utf8_next(prev
)) = '\0';
15581 search_data
.len
= prev
- search_data
.buf
+ 1;
15585 *search_data
.buf
= '\0';
15586 search_data
.len
= 0;
15588 for (i
= 0; i
< matches_count
; i
++)
15590 long n
= matching_words_a
[i
];
15592 word_a
[n
].is_matching
= 0;
15594 memset(word_a
[n
].bitmap
,
15596 (word_a
[n
].mb
- daccess
.flength
) / CHAR_BIT
+ 1);
15601 nl
= disp_lines(&win
,
15616 if (search_data
.err
)
15618 search_data
.err
= 0;
15619 search_data
.fuzzy_err_pos
= -1;
15621 nl
= disp_lines(&win
,
15634 if (search_data
.utf8_len
> 0)
15635 goto special_cmds_when_searching
;
15638 /* When there is only one glyph in the search list in */
15639 /* FUZZY and SUBSTRING mode then all is already done except */
15640 /* the cleanup of the first level of the tst_search_list. */
15641 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
15642 if (search_mode
!= PREFIX
)
15644 sub_tst_t
*sub_tst_data
;
15647 node
= tst_search_list
->tail
;
15648 sub_tst_data
= (sub_tst_t
*)(node
->data
);
15650 search_data
.err
= 0;
15652 sub_tst_data
->count
= 0;
15663 if (search_mode
== NONE
)
15665 help(&win
, &term
, last_line
);
15668 /* Arm the help timer. */
15669 /* """"""""""""""""""" */
15670 help_timer
= timers
.help
; /* default 30 s. */
15673 goto special_cmds_when_searching
;
15679 unsigned int iCb
, iCx
, iCy
;
15680 unsigned char cCb
, cCx
, cCy
;
15684 int button
, old_button
;
15689 long old_current
= current
;
15691 struct timespec actual_click_ts
;
15693 switch (mouse_proto
)
15696 if (sscanf((char *)buffer
+ 3,
15703 goto ignore_mouse_event
;
15708 column_click
= iCx
;
15710 /* Only consider button click (not release) events. */
15711 /* """""""""""""""""""""""""""""""""""""""""""""""" */
15712 if (action
== 'm') /* released. */
15713 goto ignore_mouse_event
;
15718 if (sscanf((char *)buffer
+ 2,
15725 goto ignore_mouse_event
;
15727 state
= (iCb
- 32) & ~3;
15728 button
= (iCb
- 32) & 3;
15730 column_click
= iCx
;
15732 /* Only consider button click (not release) events. */
15733 /* """""""""""""""""""""""""""""""""""""""""""""""" */
15734 if (state
== 0 && button
== 3) /* released. */
15735 goto ignore_mouse_event
;
15740 if (sscanf((char *)buffer
+ 3, "%c%c%c", &cCb
, &cCx
, &cCy
) != 3)
15741 goto ignore_mouse_event
;
15743 state
= (cCb
- 32) & ~3;
15744 button
= (cCb
- 32) & 3;
15745 line_click
= cCy
- 32;
15746 column_click
= cCx
- 32;
15748 /* Only consider button click (not release) events. */
15749 /* """""""""""""""""""""""""""""""""""""""""""""""" */
15750 if (button
== 3) /* released. */
15751 goto ignore_mouse_event
;
15756 goto ignore_mouse_event
;
15759 /* Mouse Button remapping. */
15760 /* """"""""""""""""""""""" */
15761 old_button
= button
;
15762 button
= mouse
.button
[button
] - 1;
15766 /* Only buttons 0,1 and 2 are considered for clicks. */
15767 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
15768 if (button
< 0 || button
> 2)
15769 goto ignore_mouse_event
;
15771 /* Do not do anything if the user has above or below the window. */
15772 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
15773 if (line_click
< term
.curs_line
15775 >= term
.curs_line
+ win
.max_lines
+ (win
.has_hbar
? 1 : 0))
15778 /* Mouse wheel scroll down. */
15779 /* """""""""""""""""""""""" */
15780 if (state
== 64 && old_button
== 1)
15781 goto kd
; /* down arrow. */
15783 /* Mouse wheel scroll down + CTRL. */
15784 /* """"""""""""""""""""""""""""""" */
15785 if (state
== 80 && old_button
== 1)
15786 goto knp
; /* PgDn. */
15788 /* Mouse wheel scroll up. */
15789 /* """""""""""""""""""""" */
15790 if (state
== 64 && old_button
== 0)
15791 goto ku
; /* up arrow. */
15793 /* Mouse wheel scroll up + CTRL. */
15794 /* """"""""""""""""""""""""""""" */
15795 if (state
== 80 && old_button
== 0)
15796 goto kpp
; /* PgUp. */
15798 /* Manage the clicks at the ends of the vertical scroll bar. */
15799 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
15800 if (button
== 0 && column_click
== win
.sb_column
+ 1)
15802 if (line_click
== term
.curs_line
+ win
.max_lines
- 1)
15805 goto knp
; /* PgDn. */
15807 goto kd
; /* down arrow. */
15809 else if (line_click
== term
.curs_line
)
15812 goto kpp
; /* PgUp. */
15814 goto ku
; /* up arrow. */
15816 else if (line_click
> term
.curs_line
15817 && line_click
< term
.curs_line
+ win
.max_lines
- 1)
15819 float ratio
; /* cursor ratio in the between the extremities *
15820 | of the scroll bar. */
15822 if (win
.max_lines
> 3)
15824 float corr
; /* scaling correction. */
15825 long line
; /* new selected line. */
15827 corr
= (float)(win
.max_lines
- 2) / (win
.max_lines
- 3);
15828 ratio
= (float)(line_click
- term
.curs_line
- 1)
15829 / (win
.max_lines
- 2);
15831 /* Find the location of the new current word based on */
15832 /* the position of the cursor in the scroll bar. */
15833 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
15834 line
= ratio
* corr
* line_nb_of_word_a
[count
- 1];
15835 current
= first_word_in_line_a
[line
];
15837 /* Make sure the new current word is selectable. */
15838 /* """"""""""""""""""""""""""""""""""""""""""""" */
15839 if (current
< first_selectable
)
15840 current
= first_selectable
;
15842 if (current
> last_selectable
)
15843 current
= last_selectable
;
15845 while (!word_a
[current
].is_selectable
)
15848 /* Display the window. */
15849 /* """"""""""""""""""" */
15850 last_line
= build_metadata(&term
, count
, &win
);
15852 set_new_first_column(&win
, &term
);
15854 nl
= disp_lines(&win
,
15868 /* Manage the clicks in the horizontal scroll bar. */
15869 /* """"""""""""""""""""""""""""""""""""""""""""""" */
15870 if (win
.has_hbar
&& button
== 0
15871 && line_click
== term
.curs_line
+ win
.max_lines
)
15873 long wi
; /* Word index. */
15874 long line
= line_nb_of_word_a
[current
];
15877 int leftmost_start
;
15880 /* Find the first selectable word in the line */
15881 /* containing the cursor. */
15882 /* """""""""""""""""""""""""""""""""""""""""" */
15883 wi
= first_word_in_line_a
[line
];
15885 while (!word_a
[wi
].is_selectable
)
15889 leftmost_start
= word_a
[leftmost
].start
;
15891 if (column_click
== 1)
15893 /* First, manage the case where the user clicked at the */
15894 /* beginning of the scroll bar. */
15895 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
15896 if (current
> leftmost
&& word_a
[current
].start
> 0)
15900 /* Else we need to calculate the rightmost selectable word */
15901 /* in the line containing the cursor. */
15902 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
15903 else if (line
== last_line
)
15906 wi
= first_word_in_line_a
[line
+ 1] - 1;
15908 while (!word_a
[wi
].is_selectable
)
15913 /* Manage the case where the users clicked at the end */
15914 /* of the scroll bar. */
15915 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
15916 if (column_click
== term
.ncolumns
- 1)
15917 if (current
< rightmost
&& current
< count
- 1
15918 && word_a
[current
+ 1].start
> 0)
15921 /* Finally manage the core where the users clicked in */
15922 /* the crossbar. */
15923 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
15924 rightmost_end
= word_a
[rightmost
].end
;
15931 if (column_click
>= 2 && column_click
- 2 <= term
.ncolumns
- 4)
15933 ratio
= (1.0 * column_click
- 2) / (term
.ncolumns
- 4);
15934 target
= (int)((rightmost_end
- leftmost_start
+ 1) * ratio
)
15937 if (target
> 0) /* General case. */
15939 if (word_a
[current
].start
<= target
)
15944 while (index
<= rightmost
&& word_a
[index
].start
<= target
)
15947 else /* Trivial case. */
15948 current
= leftmost
;
15950 set_new_first_column(&win
, &term
);
15952 nl
= disp_lines(&win
,
15969 /* Manage clicks on the horizontal arrows in lines if any. */
15970 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
15971 if (win
.has_truncated_lines
15972 && shift_arrow_clicked(&win
,
15980 switch (clicked_arrow
)
15994 case 1: /* right */
16006 nl
= disp_lines(&win
,
16019 /* Get the new current word on click. */
16020 /* """""""""""""""""""""""""""""""""" */
16022 new_current
= get_clicked_index(&win
,
16028 /* Update the selection index and refresh if needed. */
16029 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
16030 if ((toggles
.taggable
|| toggles
.pinable
16031 || new_current
!= current
)
16034 current
= new_current
;
16036 /* Manage the marking of a word. */
16037 /* (button 0 + CTRL or button 2 pressed). */
16038 /* """""""""""""""""""""""""""""""""""""" */
16039 if ((button
== 0 && state
== 16)
16040 && (toggles
.taggable
|| toggles
.pinable
))
16048 /* Manage the tagging of a word. */
16049 /* (button 2 + CTRL or button 2 pressed). */
16050 /* """""""""""""""""""""""""""""""""""""" */
16051 if ((button
== 2 && state
== 0)
16052 && (toggles
.taggable
|| toggles
.pinable
))
16054 if (word_a
[current
].tag_id
> 0)
16060 if ((button
== 2 && state
== 16)
16061 && (toggles
.taggable
|| toggles
.pinable
))
16062 goto adaptative_tag_to_mark
; /* Like 'Z' keyboard command. */
16064 /* Redisplay the new window if the first button was pressed */
16065 /* otherwise reset the cursor position to its previous value. */
16066 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
16067 if (button
== 0 || button
== 2)
16068 nl
= disp_lines(&win
,
16079 current
= old_current
;
16082 /* Manage double clicks if the clicked word is selectable. */
16083 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
16084 if (!disable_double_click
&& button
== 0)
16086 /* More than one clicks on the same word? */
16087 /* """""""""""""""""""""""""""""""""""""" */
16088 if (click_nr
> 0 && old_current
== current
)
16092 /* Get the delay between the actual click and */
16093 /* the previous one. */
16094 /* """""""""""""""""""""""""""""""""""""""""" */
16095 clock_gettime(CLOCK_MONOTONIC
, &actual_click_ts
);
16096 delay
= (1000.0 * actual_click_ts
.tv_sec
16097 + 1e-6 * actual_click_ts
.tv_nsec
)
16098 - (1000.0 * last_click_ts
.tv_sec
16099 + 1e-6 * last_click_ts
.tv_nsec
);
16101 if (!error
&& delay
> 0
16102 && delay
<= mouse
.double_click_delay
)
16103 goto enter
; /* Press Enter. */
16105 clock_gettime(CLOCK_MONOTONIC
, &last_click_ts
);
16109 /* The first click on a selectable was not make yet. */
16110 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
16111 clock_gettime(CLOCK_MONOTONIC
, &last_click_ts
);
16120 ignore_mouse_event
:
16123 special_cmds_when_searching
:
16126 int c
; /* byte index in the scancode string .*/
16127 sub_tst_t
*sub_tst_data
;
16130 if (search_mode
!= NONE
)
16132 long old_len
= search_data
.len
;
16133 long old_utf8_len
= search_data
.utf8_len
;
16137 /* Copy all the bytes included in the key press to buffer. */
16138 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
16139 if (buffer
[0] != 0x08 && buffer
[0] != 0x7f) /* Backspace. */
16141 /* The only case where we have to manage backspace hits */
16142 /* here is if the user has entered them in fuzzy search */
16143 /* mode. As the search buffer has already been amended, */
16144 /* we do not have to update the search buffer again. */
16145 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
16147 && search_data
.utf8_len
16148 < word_real_max_size
- daccess
.flength
;
16150 search_data
.buf
[search_data
.len
++] = buffer
[c
];
16152 /* Update the glyph array with the content of the search */
16154 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
16155 if (search_data
.utf8_len
< word_real_max_size
- daccess
.flength
)
16157 search_data
.utf8_off_a
[search_data
.utf8_len
] = old_len
;
16158 search_data
.utf8_len_a
[search_data
.utf8_len
] = search_data
.len
16160 search_data
.utf8_len
++;
16164 /* Restart the search timer. */
16165 /* """"""""""""""""""""""""" */
16166 search_timer
= timers
.search
; /* default 10 s. */
16168 if (search_mode
== PREFIX
)
16170 ws
= utf8_strtowcs(search_data
.buf
);
16172 /* Purge the matching words list. */
16173 /* """""""""""""""""""""""""""""" */
16174 for (i
= 0; i
< matches_count
; i
++)
16176 long n
= matching_words_a
[i
];
16178 word_a
[n
].is_matching
= 0;
16180 memset(word_a
[n
].bitmap
,
16182 (word_a
[n
].mb
- daccess
.flength
) / CHAR_BIT
+ 1);
16187 tst_prefix_search(tst_word
, ws
, tst_cb
);
16189 /* matches_count is updated by tst_cb. */
16190 /* """"""""""""""""""""""""""""""""""" */
16191 if (matches_count
> 0)
16193 if (search_data
.len
== old_len
&& matches_count
== 1
16194 && buffer
[0] != 0x08 && buffer
[0] != 0x7f)
16198 /* Adjust the bitmap to the ending version. */
16199 /* """""""""""""""""""""""""""""""""""""""" */
16200 update_bitmaps(search_mode
, &search_data
, NO_AFFINITY
);
16202 current
= matching_words_a
[0];
16204 if (current
< win
.start
|| current
> win
.end
)
16205 last_line
= build_metadata(&term
, count
, &win
);
16207 /* Set new first column to display. */
16208 /* """""""""""""""""""""""""""""""" */
16209 set_new_first_column(&win
, &term
);
16211 nl
= disp_lines(&win
,
16227 search_data
.err
= 1;
16228 search_data
.len
= old_len
;
16229 search_data
.utf8_len
= old_utf8_len
;
16230 search_data
.buf
[search_data
.len
] = '\0';
16232 /* Set new first column to display. */
16233 /* """""""""""""""""""""""""""""""" */
16234 set_new_first_column(&win
, &term
);
16236 nl
= disp_lines(&win
,
16248 else if (search_mode
== FUZZY
)
16250 /* tst_search_list: [sub_tst_t *] -> [sub_tst_t *]... */
16253 /* level 1 level_2 */
16255 /* Each sub_tst_t * points to a data structure including */
16256 /* a sorted array to nodes in the words tst. */
16257 /* Each of these node starts a matching candidate. */
16258 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
16259 wchar_t *w
= utf8_strtowcs(search_data
.buf
+ old_len
);
16261 /* zero previous matching indicators. */
16262 /* """""""""""""""""""""""""""""""""" */
16263 for (i
= 0; i
< matches_count
; i
++)
16265 long n
= matching_words_a
[i
];
16267 word_a
[n
].is_matching
= 0;
16269 memset(word_a
[n
].bitmap
,
16271 (word_a
[n
].mb
- daccess
.flength
) / CHAR_BIT
+ 1);
16276 if (buffer
[0] == 0x08 || buffer
[0] == 0x7f) /* Backspace */
16278 node
= tst_search_list
->tail
;
16279 sub_tst_data
= (sub_tst_t
*)(node
->data
);
16281 sub_tst_data
->count
= 0;
16283 if (tst_search_list
->len
> 0)
16285 free(sub_tst_data
->array
);
16286 free(sub_tst_data
);
16288 ll_delete(tst_search_list
, tst_search_list
->tail
);
16293 if (search_data
.utf8_len
== 1)
16295 /* Search all the sub-tst trees having the searched */
16296 /* character as children, the resulting sub-tst are put */
16297 /* in the sub tst array attached to the currently */
16298 /* searched symbol. */
16299 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
16300 tst_fuzzy_traverse(tst_word
, NULL
, 0, w
[0]);
16302 node
= tst_search_list
->tail
;
16303 sub_tst_data
= (sub_tst_t
*)(node
->data
);
16304 if (sub_tst_data
->count
== 0)
16308 search_data
.len
= 0;
16309 search_data
.utf8_len
= 0;
16310 search_data
.buf
[0] = '\0';
16317 /* Prevent the list to grow larger than the maximal */
16318 /* word's length. */
16319 /* """""""""""""""""""""""""""""""""""""""""""""""" */
16320 if (tst_search_list
->len
16321 < word_real_max_size
- daccess
.flength
)
16323 /* use the results in the level n-1 list to build the */
16324 /* level n list. */
16325 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
16328 sub_tst_t
*tst_fuzzy_level_data
;
16330 tst_fuzzy_level_data
= sub_tst_new();
16332 ll_append(tst_search_list
, tst_fuzzy_level_data
);
16334 node
= tst_search_list
->tail
->prev
;
16335 sub_tst_data
= (sub_tst_t
*)(node
->data
);
16338 for (index
= 0; index
< sub_tst_data
->count
; index
++)
16339 rc
+= tst_fuzzy_traverse(sub_tst_data
->array
[index
],
16346 free(tst_fuzzy_level_data
->array
);
16347 free(tst_fuzzy_level_data
);
16349 ll_delete(tst_search_list
, tst_search_list
->tail
);
16351 search_data
.err
= 1;
16352 if (search_data
.fuzzy_err_pos
== -1)
16353 search_data
.fuzzy_err_pos
= search_data
.utf8_len
;
16357 search_data
.len
= old_len
;
16358 search_data
.utf8_len
= old_utf8_len
;
16359 search_data
.buf
[search_data
.len
] = '\0';
16368 /* Process this level to mark the word found as a matching */
16370 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
16371 node
= tst_search_list
->tail
;
16372 sub_tst_data
= (sub_tst_t
*)(node
->data
);
16374 for (index
= 0; index
< sub_tst_data
->count
; index
++)
16375 tst_traverse(sub_tst_data
->array
[index
], set_matching_flag
, 0);
16377 /* Update the bitmap and re-display the window. */
16378 /* """""""""""""""""""""""""""""""""""""""""""" */
16379 if (matches_count
> 0)
16381 if (search_data
.only_starting
)
16382 select_starting_matches(&win
,
16386 else if (search_data
.only_ending
)
16387 select_ending_matches(&win
, &term
, &search_data
, &last_line
);
16389 /* Adjust the bitmap to the ending version. */
16390 /* """""""""""""""""""""""""""""""""""""""" */
16391 update_bitmaps(search_mode
, &search_data
, NO_AFFINITY
);
16393 current
= matching_words_a
[0];
16395 if (current
< win
.start
|| current
> win
.end
)
16396 last_line
= build_metadata(&term
, count
, &win
);
16398 /* Set new first column to display. */
16399 /* """""""""""""""""""""""""""""""" */
16400 set_new_first_column(&win
, &term
);
16402 nl
= disp_lines(&win
,
16416 else /* SUBSTRING. */
16418 wchar_t *w
= utf8_strtowcs(search_data
.buf
);
16420 /* Purge the matching words list. */
16421 /* """""""""""""""""""""""""""""" */
16422 for (i
= 0; i
< matches_count
; i
++)
16424 long n
= matching_words_a
[i
];
16426 word_a
[n
].is_matching
= 0;
16428 memset(word_a
[n
].bitmap
,
16430 (word_a
[n
].mb
- daccess
.flength
) / CHAR_BIT
+ 1);
16435 if (search_data
.utf8_len
== 1)
16437 /* Search all the sub-tst trees having the searched */
16438 /* character as children, the resulting sub-tst are put */
16439 /* in the level list corresponding to the letter order. */
16440 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
16441 tst_substring_traverse(tst_word
, NULL
, 0, w
[0]);
16443 node
= tst_search_list
->tail
;
16444 sub_tst_data
= (sub_tst_t
*)(node
->data
);
16446 for (index
= 0; index
< sub_tst_data
->count
; index
++)
16447 tst_traverse(sub_tst_data
->array
[index
],
16453 /* Search for the rest of the word in all the sub-tst */
16454 /* trees previously found. */
16455 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
16456 node
= tst_search_list
->tail
;
16457 sub_tst_data
= (sub_tst_t
*)(node
->data
);
16461 for (index
= 0; index
< sub_tst_data
->count
; index
++)
16462 tst_prefix_search(sub_tst_data
->array
[index
], w
+ 1, tst_cb
);
16465 if (matches_count
> 0)
16467 if (search_data
.len
== old_len
&& matches_count
== 1
16468 && buffer
[0] != 0x08 && buffer
[0] != 0x7f)
16472 if (search_data
.only_starting
)
16473 select_starting_matches(&win
,
16477 else if (search_data
.only_ending
)
16478 select_ending_matches(&win
,
16483 update_bitmaps(search_mode
, &search_data
, NO_AFFINITY
);
16485 current
= matching_words_a
[0];
16487 if (current
< win
.start
|| current
> win
.end
)
16488 last_line
= build_metadata(&term
, count
, &win
);
16490 /* Set new first column to display. */
16491 /* """""""""""""""""""""""""""""""" */
16492 set_new_first_column(&win
, &term
);
16494 nl
= disp_lines(&win
,
16510 search_data
.err
= 1;
16511 search_data
.len
= old_len
;
16512 search_data
.utf8_len
--;
16513 search_data
.buf
[search_data
.len
] = '\0';
16515 /* Set new first column to display. */
16516 /* """""""""""""""""""""""""""""""" */
16517 set_new_first_column(&win
, &term
);
16519 nl
= disp_lines(&win
,