1 /* ################################################################### */
2 /* Copyright 2015-present, Pierre Gentile (p.gen.progs@gmail.com) */
3 /* */
4 /* This Software is licensed under the GPL licensed Version 2, */
5 /* please read http://www.gnu.org/copyleft/gpl.html */
6 /* */
7 /* you can redistribute it and/or modify it under the terms of the GNU */
8 /* General Public License as published by the Free Software */
9 /* Foundation; either version 2 of the License. */
10 /* */
11 /* This software is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
14 /* General Public License for more details. */
15 /* ################################################################### */
16
17 #include "config.h"
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <signal.h>
23 #include <ctype.h>
24 #include <time.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <locale.h>
28 #include <langinfo.h>
29 #if (defined(__sun) && defined(__SVR4)) || defined(_AIX)
30 #include <curses.h>
31 #endif
32 #include <term.h>
33 #include <termios.h>
34 #include <regex.h>
35 #include <errno.h>
36 #include <sys/ioctl.h>
37 #include <sys/time.h>
38 #include <sys/param.h>
39 #include <wchar.h>
40
41 #include "xmalloc.h"
42 #include "list.h"
43 #include "index.h"
44 #include "utf8.h"
45 #include "fgetc.h"
46 #include "utils.h"
47 #include "ctxopt.h"
48 #include "usage.h"
49 #include "smenu.h"
50
51 /* ***************** */
52 /* Extern variables. */
53 /* ***************** */
54
55 extern ll_t * tst_search_list;
56
57 /* ***************** */
58 /* Global variables. */
59 /* ***************** */
60
61 word_t * word_a; /* array containing words data (size: count). */
62 long count = 0; /* number of words read from stdin. */
63 long current; /* index the current selection under the cursor). */
64 long new_current; /* final cur. position, (used in search function). */
65 long prev_current; /* prev. position stored when using direct access. */
66
67 long * line_nb_of_word_a; /* array containing the line number (from 0) *
68 | of each word read. */
69 long * first_word_in_line_a; /* array containing the index of the first *
70 | word of each lines. */
71
72 search_mode_t search_mode = NONE;
73 search_mode_t old_search_mode = NONE;
74
75 int help_mode = 0; /* 1 if help is displayed else 0. */
76
77 char * word_buffer;
78
79 int (*my_isprint)(int);
80
81 /* UTF-8 useful symbols. */
82 /* """"""""""""""""""""" */
83 char * left_arrow = "\xe2\x86\x90";
84 char * up_arrow = "\xe2\x86\x91";
85 char * right_arrow = "\xe2\x86\x92";
86 char * down_arrow = "\xe2\x86\x93";
87 char * vertical_bar = "\xe2\x94\x82"; /* box drawings light vertical. */
88 char * shift_left_sym = "\xe2\x97\x80"; /* leftwards_arrow. */
89 char * shift_right_sym = "\xe2\x96\xb6"; /* rightwards_arrow. */
90 char * sbar_line = "\xe2\x94\x82"; /* box_drawings_light_vertical. */
91 char * sbar_top = "\xe2\x94\x90"; /* box_drawings_light_down_and_left. */
92 char * sbar_down = "\xe2\x94\x98"; /* box_drawings_light_up_and_left. */
93 char * sbar_curs = "\xe2\x95\x91"; /* box_drawings_double_vertical. */
94 char * sbar_arr_up = "\xe2\x96\xb2"; /* black_up_pointing_triangle. */
95 char * sbar_arr_down = "\xe2\x96\xbc"; /* black_down_pointing_triangle. */
96 char * msg_arr_down = "\xe2\x96\xbc"; /* black_down_pointing_triangle. */
97
98 /* Variables used to manage the direct access entries. */
99 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
100 daccess_t daccess;
101 char * daccess_stack;
102 int daccess_stack_head;
103
104 /* Variables used for fuzzy and substring searching. */
105 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
106 long * matching_words_a;
107 long matching_words_a_size;
108 long matches_count;
109 long * best_matching_words_a;
110 long best_matching_words_a_size;
111 long best_matches_count;
112 long * alt_matching_words_a = NULL;
113 long alt_matches_count;
114
115 /* Variables used in signal handlers. */
116 /* """""""""""""""""""""""""""""""""" */
117 volatile sig_atomic_t got_winch = 0;
118 volatile sig_atomic_t got_winch_alrm = 0;
119 volatile sig_atomic_t got_help_alrm = 0;
120 volatile sig_atomic_t got_daccess_alrm = 0;
121 volatile sig_atomic_t got_search_alrm = 0;
122 volatile sig_atomic_t got_timeout_tick = 0;
123 volatile sig_atomic_t got_sigsegv = 0;
124 volatile sig_atomic_t got_sigterm = 0;
125 volatile sig_atomic_t got_sighup = 0;
126
127 /* Variables used when a timeout is set (option -x). */
128 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
129 timeout_t timeout;
130 char * timeout_word; /* printed word when the timeout type is WORD. */
131 char * timeout_seconds; /* string containing the number of remaining *
132 | seconds. */
133 int quiet_timeout = 0; /* 1 when we want no message to be displayed. */
134
135 /* *************** */
136 /* Help functions. */
137 /* *************** */
138
139 /* ===================== */
140 /* Help message display. */
141 /* ===================== */
142 void
help(win_t * win,term_t * term,long last_line,toggle_t * toggles)143 help(win_t * win, term_t * term, long last_line, toggle_t * toggles)
144 {
145 int index; /* used to identify the objects long the help line. */
146 int line = 0; /* number of windows lines used by the help line. */
147 int len = 0; /* length of the help line. */
148 int offset = 0; /* offset from the first column of the terminal to *
149 | the start of the help line. */
150 int entries_nb; /* number of help entries to display. */
151 int help_len; /* total length of the help line. */
152
153 struct entry_s
154 {
155 char attr; /* r=reverse, n=normal, b=bold. */
156 char * str; /* string to be displayed for an object in the help line. */
157 int len; /* length of one of these objects. */
158 };
159
160 char * arrows = concat(left_arrow, up_arrow, right_arrow, down_arrow,
161 (char *)0);
162
163 struct entry_s entries[] = {
164 { 'n', "Move:", 5 }, { 'b', arrows, 4 }, { 'n', "|", 1 },
165 { 'b', "h", 1 }, { 'b', "j", 1 }, { 'b', "k", 1 },
166 { 'b', "l", 1 }, { 'n', ",", 1 }, { 'b', "PgUp", 4 },
167 { 'n', "/", 1 }, { 'b', "PgDn", 4 }, { 'n', "/", 1 },
168 { 'b', "Home", 4 }, { 'n', "/", 1 }, { 'b', "End", 3 },
169 { 'n', " ", 1 }, { 'n', "Abort:", 6 }, { 'b', "q", 1 },
170 { 'n', " ", 1 }, { 'n', "Find:", 5 }, { 'b', "/", 1 },
171 { 'n', "|", 1 }, { 'b', "\"\'", 2 }, { 'n', "|", 1 },
172 { 'b', "~*", 2 }, { 'n', "|", 1 }, { 'b', "=^", 2 },
173 { 'n', ",", 1 }, { 'b', "SP", 2 }, { 'n', "|", 1 },
174 { 'b', "nN", 2 }, { 'n', " ", 1 }, { 'n', "Select:", 7 },
175 { 'b', "CR", 2 }, { 'n', "|", 1 }, { 'b', "tTU", 3 }
176 };
177
178 entries_nb = sizeof(entries) / sizeof(struct entry_s);
179
180 /* Remove the last two entries if tagging is not enabled. */
181 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
182 if (!toggles->taggable)
183 entries_nb -= 2;
184
185 /* Get the total length of the help line. */
186 /* """""""""""""""""""""""""""""""""""""" */
187 help_len = 0;
188 for (index = 0; index < entries_nb; index++)
189 help_len += entries[index].len;
190
191 /* Save the position of the terminal cursor so that it can be */
192 /* put back there after printing of the help line. */
193 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
194 tputs(TPARM1(save_cursor), 1, outch);
195
196 /* Center the help line if the -M (Middle) option is set. */
197 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
198 if (win->offset > 0)
199 if ((offset = win->offset + win->max_width / 2 - help_len / 2) > 0)
200 {
201 int i;
202
203 for (i = 0; i < offset; i++)
204 fputc(' ', stdout);
205 }
206
207 /* Print the different objects forming the help line. */
208 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
209 for (index = 0; index < entries_nb; index++)
210 {
211 if ((len += entries[index].len) >= term->ncolumns - 1)
212 {
213 line++;
214
215 if (line > last_line || line == win->max_lines)
216 break;
217
218 len = entries[index].len;
219 fputc('\n', stdout);
220 }
221
222 switch (entries[index].attr)
223 {
224 case 'b':
225 if (term->has_bold)
226 tputs(TPARM1(enter_bold_mode), 1, outch);
227 break;
228 case 'r':
229 if (term->has_reverse)
230 tputs(TPARM1(enter_reverse_mode), 1, outch);
231 else if (term->has_standout)
232 tputs(TPARM1(enter_standout_mode), 1, outch);
233 break;
234 case 'n':
235 tputs(TPARM1(exit_attribute_mode), 1, outch);
236 break;
237 }
238 fputs(entries[index].str, stdout);
239 }
240
241 tputs(TPARM1(exit_attribute_mode), 1, outch);
242 tputs(TPARM1(clr_eol), 1, outch);
243
244 /* Put back the cursor to its saved position. */
245 /* """""""""""""""""""""""""""""""""""""""""" */
246 tputs(TPARM1(restore_cursor), 1, outch);
247 }
248
249 /* *********************************** */
250 /* Attributes string parsing function. */
251 /* *********************************** */
252
253 /* ================================= */
254 /* Decode attributes toggles if any. */
255 /* b -> bold */
256 /* d -> dim */
257 /* r -> reverse */
258 /* s -> standout */
259 /* u -> underline */
260 /* i -> italic */
261 /* l -> blink */
262 /* */
263 /* Returns 0 if some unexpected. */
264 /* toggle is found else 0. */
265 /* ================================= */
266 int
decode_attr_toggles(char * s,attrib_t * attr)267 decode_attr_toggles(char * s, attrib_t * attr)
268 {
269 int rc = 1;
270
271 attr->bold = attr->dim = attr->reverse = 0;
272 attr->standout = attr->underline = attr->italic = attr->blink = 0;
273
274 while (*s != '\0')
275 {
276 switch (*s)
277 {
278 case 'b':
279 attr->bold = 1;
280 attr->is_set = SET;
281 break;
282 case 'd':
283 attr->dim = 1;
284 attr->is_set = SET;
285 break;
286 case 'r':
287 attr->reverse = 1;
288 attr->is_set = SET;
289 break;
290 case 's':
291 attr->standout = 1;
292 attr->is_set = SET;
293 break;
294 case 'u':
295 attr->underline = 1;
296 attr->is_set = SET;
297 break;
298 case 'i':
299 attr->italic = 1;
300 attr->is_set = SET;
301 break;
302 case 'l':
303 attr->blink = 1;
304 attr->is_set = SET;
305 break;
306 default:
307 rc = 0;
308 break;
309 }
310 s++;
311 }
312 return rc;
313 }
314
315 /* =========================================================*/
316 /* Parse attributes in str in the form [fg][/bg][,toggles] */
317 /* where: */
318 /* fg and bg are short representing a color value */
319 /* toggles is an array of toggles (see decode_attr_toggles) */
320 /* Returns 1 on success else 0. */
321 /* attr will be filled by the function. */
322 /* =========================================================*/
323 int
parse_attr(char * str,attrib_t * attr,short max_color)324 parse_attr(char * str, attrib_t * attr, short max_color)
325 {
326 int n;
327 char * pos;
328 char s1[12] = { 0 };
329 char s2[7] = { 0 };
330 short d1 = -1, d2 = -1;
331 int rc = 1;
332
333 n = sscanf(str, "%11[^,],%6s", s1, s2);
334
335 if (n == 0)
336 return 0;
337
338 if ((pos = strchr(s1, '/')))
339 {
340 if (pos == s1) /* s1 starts with a / */
341 {
342 d1 = -1;
343 if (sscanf(s1 + 1, "%hd", &d2) == 0)
344 {
345 d2 = -1;
346 if (n == 1)
347 return 0;
348 }
349 }
350 else if (sscanf(s1, "%hd/%hd", &d1, &d2) < 2)
351 {
352 d1 = d2 = -1;
353 if (n == 1)
354 return 0;
355 }
356 }
357 else /* no / in the first string. */
358 {
359 d2 = -1;
360 if (sscanf(s1, "%hd", &d1) == 0)
361 {
362 d1 = -1;
363 if (n == 2 || decode_attr_toggles(s1, attr) == 0)
364 return 0;
365 }
366 }
367
368 if (d1 < -1 || d2 < -1)
369 return 0;
370
371 if (max_color == 0)
372 {
373 attr->fg = -1;
374 attr->bg = -1;
375 }
376 else
377 {
378 attr->fg = d1;
379 attr->bg = d2;
380 }
381
382 if (n == 2)
383 rc = decode_attr_toggles(s2, attr);
384
385 return rc;
386 }
387
388 /* ============================================== */
389 /* Set the terminal attributes according to attr. */
390 /* ============================================== */
391 void
apply_attr(term_t * term,attrib_t attr)392 apply_attr(term_t * term, attrib_t attr)
393 {
394 if (attr.fg >= 0)
395 set_foreground_color(term, attr.fg);
396
397 if (attr.bg >= 0)
398 set_background_color(term, attr.bg);
399
400 if (attr.bold > 0)
401 tputs(TPARM1(enter_bold_mode), 1, outch);
402
403 if (attr.dim > 0)
404 tputs(TPARM1(enter_dim_mode), 1, outch);
405
406 if (attr.reverse > 0)
407 tputs(TPARM1(enter_reverse_mode), 1, outch);
408
409 if (attr.standout > 0)
410 tputs(TPARM1(enter_standout_mode), 1, outch);
411
412 if (attr.underline > 0)
413 tputs(TPARM1(enter_underline_mode), 1, outch);
414
415 if (attr.italic > 0)
416 tputs(TPARM1(enter_italics_mode), 1, outch);
417
418 if (attr.blink > 0)
419 tputs(TPARM1(enter_blink_mode), 1, outch);
420 }
421
422 /* ********************* */
423 /* ini parsing function. */
424 /* ********************* */
425
426 /* ===================================================== */
427 /* Callback function called when parsing each non-header */
428 /* line of the ini file. */
429 /* Returns 0 if OK, 1 if not. */
430 /* ===================================================== */
431 int
ini_cb(win_t * win,term_t * term,limit_t * limits,ticker_t * timers,misc_t * misc,langinfo_t * langinfo,const char * section,const char * name,char * value)432 ini_cb(win_t * win, term_t * term, limit_t * limits, ticker_t * timers,
433 misc_t * misc, langinfo_t * langinfo, const char * section,
434 const char * name, char * value)
435 {
436 int error = 0;
437 int has_colors = (term->colors > 7);
438
439 if (strcmp(section, "colors") == 0)
440 {
441 attrib_t v = { UNSET, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
442
443 #define CHECK_ATTR(x) \
444 else if (strcmp(name, #x) == 0) \
445 { \
446 error = !parse_attr(value, &v, term->colors); \
447 if (error) \
448 goto out; \
449 else \
450 { \
451 if (win->x##_attr.is_set != FORCED) \
452 { \
453 win->x##_attr.is_set = SET; \
454 if (v.fg >= 0) \
455 win->x##_attr.fg = v.fg; \
456 if (v.bg >= 0) \
457 win->x##_attr.bg = v.bg; \
458 if (v.bold >= 0) \
459 win->x##_attr.bold = v.bold; \
460 if (v.dim >= 0) \
461 win->x##_attr.dim = v.dim; \
462 if (v.reverse >= 0) \
463 win->x##_attr.reverse = v.reverse; \
464 if (v.standout >= 0) \
465 win->x##_attr.standout = v.standout; \
466 if (v.underline >= 0) \
467 win->x##_attr.underline = v.underline; \
468 if (v.italic >= 0) \
469 win->x##_attr.italic = v.italic; \
470 if (v.blink >= 0) \
471 win->x##_attr.blink = v.blink; \
472 } \
473 } \
474 }
475
476 #define CHECK_ATT_ATTR(x, y) \
477 else if (strcmp(name, #x #y) == 0) \
478 { \
479 error = !parse_attr(value, &v, term->colors); \
480 if (error) \
481 goto out; \
482 else \
483 { \
484 if (win->x##_attr[y - 1].is_set != FORCED) \
485 { \
486 win->x##_attr[y - 1].is_set = SET; \
487 if (v.fg >= 0) \
488 win->x##_attr[y - 1].fg = v.fg; \
489 if (v.bg >= 0) \
490 win->x##_attr[y - 1].bg = v.bg; \
491 if (v.bold >= 0) \
492 win->x##_attr[y - 1].bold = v.bold; \
493 if (v.dim >= 0) \
494 win->x##_attr[y - 1].dim = v.dim; \
495 if (v.reverse >= 0) \
496 win->x##_attr[y - 1].reverse = v.reverse; \
497 if (v.standout >= 0) \
498 win->x##_attr[y - 1].standout = v.standout; \
499 if (v.underline >= 0) \
500 win->x##_attr[y - 1].underline = v.underline; \
501 if (v.italic >= 0) \
502 win->x##_attr[y - 1].italic = v.italic; \
503 if (v.blink >= 0) \
504 win->x##_attr[y - 1].blink = v.blink; \
505 } \
506 } \
507 }
508
509 /* [colors] section. */
510 /* """"""""""""""""" */
511 if (has_colors)
512 {
513 if (strcmp(name, "method") == 0)
514 {
515 if (strcmp(value, "classic") == 0)
516 term->color_method = 0;
517 else if (strcmp(value, "ansi") == 0)
518 term->color_method = 1;
519 else
520 {
521 error = 1;
522 goto out;
523 }
524 }
525
526 /* clang-format off */
527 CHECK_ATTR(cursor)
528 CHECK_ATTR(bar)
529 CHECK_ATTR(shift)
530 CHECK_ATTR(message)
531 CHECK_ATTR(search_field)
532 CHECK_ATTR(search_text)
533 CHECK_ATTR(match_field)
534 CHECK_ATTR(match_text)
535 CHECK_ATTR(match_err_field)
536 CHECK_ATTR(match_err_text)
537 CHECK_ATTR(include)
538 CHECK_ATTR(exclude)
539 CHECK_ATTR(tag)
540 CHECK_ATTR(cursor_on_tag)
541 CHECK_ATTR(daccess)
542 CHECK_ATT_ATTR(special, 1)
543 CHECK_ATT_ATTR(special, 2)
544 CHECK_ATT_ATTR(special, 3)
545 CHECK_ATT_ATTR(special, 4)
546 CHECK_ATT_ATTR(special, 5)
547 CHECK_ATT_ATTR(special, 6)
548 CHECK_ATT_ATTR(special, 7)
549 CHECK_ATT_ATTR(special, 8)
550 CHECK_ATT_ATTR(special, 9)
551 /* clang-format on */
552 }
553 }
554 else if (strcmp(section, "window") == 0)
555 {
556 int v;
557
558 /* [window] section. */
559 /* """"""""""""""""" */
560 if (strcmp(name, "lines") == 0)
561 {
562 if ((error = !(sscanf(value, "%d", &v) == 1 && v >= 0)))
563 goto out;
564 else
565 win->asked_max_lines = v;
566 }
567 }
568 else if (strcmp(section, "limits") == 0)
569 {
570 long v;
571
572 /* [limits] section. */
573 /* """"""""""""""""" */
574 if (strcmp(name, "word_length") == 0)
575 {
576 if ((error = !(sscanf(value, "%ld", &v) == 1 && v > 0)))
577 goto out;
578 else
579 limits->word_length = v;
580 }
581 else if (strcmp(name, "words") == 0)
582 {
583 if ((error = !(sscanf(value, "%ld", &v) == 1 && v > 0)))
584 goto out;
585 else
586 limits->words = v;
587 }
588 else if (strcmp(name, "columns") == 0)
589 {
590 if ((error = !(sscanf(value, "%ld", &v) == 1 && v > 0)))
591 goto out;
592 else
593 limits->cols = v;
594 }
595 }
596 else if (strcmp(section, "timers") == 0)
597 {
598 int v;
599
600 /* [timers] section. */
601 /* """"""""""""""""" */
602 if (strcmp(name, "help") == 0)
603 {
604 if ((error = !(sscanf(value, "%d", &v) == 1 && v > 0)))
605 goto out;
606 else
607 timers->help = v;
608 }
609 else if (strcmp(name, "window") == 0)
610 {
611 if ((error = !(sscanf(value, "%d", &v) == 1 && v > 0)))
612 goto out;
613 else
614 timers->winch = v;
615 }
616 else if (strcmp(name, "direct_access") == 0)
617 {
618 if ((error = !(sscanf(value, "%d", &v) == 1 && v > 0)))
619 goto out;
620 else
621 timers->direct_access = v;
622 }
623 else if (strcmp(name, "search") == 0)
624 {
625 if ((error = !(sscanf(value, "%d", &v) == 1 && v > 0)))
626 goto out;
627 else
628 timers->search = v;
629 }
630 }
631 else if (strcmp(section, "misc") == 0)
632 {
633 /* [misc] section. */
634 /* """"""""""""""" */
635 if (strcmp(name, "default_search_method") == 0)
636 {
637 if (misc->default_search_method == NONE)
638 {
639 if (strcmp(value, "prefix") == 0)
640 misc->default_search_method = PREFIX;
641 else if (strcmp(value, "fuzzy") == 0)
642 misc->default_search_method = FUZZY;
643 else if (strcmp(value, "substring") == 0)
644 misc->default_search_method = SUBSTRING;
645 }
646 }
647 }
648
649 out:
650
651 return error;
652 }
653
654 /* ======================================================================== */
655 /* Load an .ini format file. */
656 /* filename - path to a file. */
657 /* report - callback can return non-zero to stop, the callback error code */
658 /* returned from this function. */
659 /* return - return 0 on success. */
660 /* */
661 /* This function is public domain. No copyright is claimed. */
662 /* Jon Mayo April 2011. */
663 /* ======================================================================== */
664 int
ini_load(const char * filename,win_t * win,term_t * term,limit_t * limits,ticker_t * timers,misc_t * misc,langinfo_t * langinfo,int (* report)(win_t * win,term_t * term,limit_t * limits,ticker_t * timers,misc_t * misc,langinfo_t * langinfo,const char * section,const char * name,char * value))665 ini_load(const char * filename, win_t * win, term_t * term, limit_t * limits,
666 ticker_t * timers, misc_t * misc, langinfo_t * langinfo,
667 int (*report)(win_t * win, term_t * term, limit_t * limits,
668 ticker_t * timers, misc_t * misc, langinfo_t * langinfo,
669 const char * section, const char * name, char * value))
670 {
671 char name[64] = "";
672 char value[256] = "";
673 char section[128] = "";
674 char * s;
675 FILE * f;
676 int cnt;
677 int error;
678
679 /* If the filename is empty we skip this phase and use the */
680 /* default values. */
681 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
682 if (filename == NULL)
683 return 1;
684
685 /* We do that if the file is not readable as well. */
686 /* """"""""""""""""""""""""""""""""""""""""""""""" */
687 f = fopen(filename, "r");
688 if (!f)
689 return 0; /* Returns success as the presence of this file *
690 | is optional. */
691
692 error = 0;
693
694 /* Skip blank lines. */
695 /* """"""""""""""""" */
696 while (fscanf(f, "%*[\n]") == 1)
697 {
698 }
699
700 while (!feof(f))
701 {
702 if (fscanf(f, " [%127[^];\n]]", section) == 1)
703 {
704 /* Do nothing. */
705 /* """"""""""" */
706 }
707 if ((cnt = fscanf(f, " %63[^=;\n] = %255[^;\n]", name, value)))
708 {
709 if (cnt == 1)
710 *value = 0;
711
712 for (s = name + strlen(name) - 1; s > name && isspace(*s); s--)
713 *s = 0;
714
715 for (s = value + strlen(value) - 1; s > value && isspace(*s); s--)
716 *s = 0;
717
718 /* Callback function calling. */
719 /* """""""""""""""""""""""""" */
720 error = report(win, term, limits, timers, misc, langinfo, section, name,
721 value);
722
723 if (error)
724 goto out;
725 }
726 if (fscanf(f, " ;%*[^\n]"))
727 {
728 /* To silence the compiler about unused results. */
729 }
730
731 /* Skip blank lines. */
732 /* """"""""""""""""" */
733 while (fscanf(f, "%*[\n]") == 1)
734 {
735 /* Do nothing. */
736 /* """"""""""" */
737 }
738 }
739
740 out:
741 fclose(f);
742
743 if (error)
744 fprintf(stderr, "Invalid entry found: %s=%s in %s.\n", name, value,
745 filename);
746
747 return error;
748 }
749
750 /* ======================================================= */
751 /* Return the full path on the configuration file supposed */
752 /* to be in the home directory of the user. */
753 /* NULL is returned if the built path is too large. */
754 /* ======================================================= */
755 char *
make_ini_path(char * name,char * base)756 make_ini_path(char * name, char * base)
757 {
758 char * path;
759 char * home;
760 long path_max;
761 long len;
762 char * conf;
763
764 /* Set the prefix of the path from the environment */
765 /* base can be "HOME" or "PWD". */
766 /* """"""""""""""""""""""""""""""""""""""""""""""" */
767 home = getenv(base);
768
769 if (home == NULL)
770 home = "";
771
772 path_max = pathconf(".", _PC_PATH_MAX);
773 len = strlen(home) + strlen(name) + 3;
774
775 if (path_max < 0)
776 path_max = 4096; /* POSIX minimal value. */
777
778 if (len <= path_max)
779 {
780 path = xmalloc(len);
781 conf = strrchr(name, '/');
782 if (conf != NULL)
783 conf++;
784 else
785 conf = name;
786
787 snprintf(path, 4096, "%s/.%s", home, conf);
788 }
789 else
790 path = NULL;
791
792 return path;
793 }
794
795 /* ********************************* */
796 /* Functions used when sorting tags. */
797 /* ********************************* */
798
799 /* ============================================================ */
800 /* Compare the pin order of two pinned word in the output list. */
801 /* ============================================================ */
802 int
tag_comp(void * a,void * b)803 tag_comp(void * a, void * b)
804 {
805 output_t * oa = (output_t *)a;
806 output_t * ob = (output_t *)b;
807
808 if (oa->order == ob->order)
809 return 0;
810
811 return (oa->order < ob->order) ? -1 : 1;
812 }
813
814 /* ========================================================= */
815 /* Swap the values of two selected words in the output list. */
816 /* ========================================================= */
817 void
tag_swap(void * a,void * b)818 tag_swap(void * a, void * b)
819 {
820 output_t * oa = (output_t *)a;
821 output_t * ob = (output_t *)b;
822 char * tmp_str;
823 long tmp_order;
824
825 tmp_str = oa->output_str;
826 oa->output_str = ob->output_str;
827 ob->output_str = tmp_str;
828
829 tmp_order = oa->order;
830 oa->order = ob->order;
831 ob->order = tmp_order;
832 }
833
834 /* ****************** */
835 /* Utility functions. */
836 /* ****************** */
837
838 /* =================================================================== */
839 /* Create a new element to be added to the tst_search_list used by the */
840 /* search mechanism. */
841 /* =================================================================== */
842 sub_tst_t *
sub_tst_new(void)843 sub_tst_new(void)
844 {
845 sub_tst_t * elem = xmalloc(sizeof(sub_tst_t));
846
847 elem->size = 64;
848 elem->count = 0;
849 elem->array = xmalloc(elem->size * sizeof(tst_node_t));
850
851 return elem;
852 }
853
854 /* ========================================= */
855 /* Emit a small (visual) beep warn the user. */
856 /* ========================================= */
857 void
my_beep(toggle_t * toggles)858 my_beep(toggle_t * toggles)
859 {
860 struct timespec ts, rem;
861 int rc;
862
863 if (!toggles->visual_bell)
864 fputc('\a', stdout);
865 else
866 {
867 tputs(TPARM1(cursor_visible), 1, outch);
868
869 ts.tv_sec = 0;
870 ts.tv_nsec = 200000000; /* 0.2s */
871
872 errno = 0;
873 rc = nanosleep(&ts, &rem);
874
875 while (rc < 0 && errno == EINTR)
876 {
877 errno = 0;
878 rc = nanosleep(&rem, &rem);
879 }
880
881 tputs(TPARM1(cursor_invisible), 1, outch);
882 }
883 }
884
885 /* =========================================== */
886 /* Integer verification constraint for ctxopt. */
887 /* =========================================== */
888 int
check_integer_constraint(int nb_args,char ** args,char * value,char * par)889 check_integer_constraint(int nb_args, char ** args, char * value, char * par)
890 {
891 if (!is_integer(value))
892 {
893 fprintf(stderr, "The argument of %s is not an integer: %s", par, value);
894 return 0;
895 }
896 return 1;
897 }
898
899 /* ======================================================================== */
900 /* Update the bitmap associated with a word. This bitmap indicates the */
901 /* positions of the UFT-8 glyphs of the search buffer in each word. */
902 /* The disp_word function will use it to display these special characters. */
903 /* ======================================================================== */
904 void
update_bitmaps(search_mode_t mode,search_data_t * data,bitmap_affinity_t affinity)905 update_bitmaps(search_mode_t mode, search_data_t * data,
906 bitmap_affinity_t affinity)
907 {
908 long i, j, n; /* work variables. */
909
910 long lmg; /* position of the last matching glyph of the search buffer *
911 | in a word. */
912
913 long sg; /* index going from lmg backward to 0 of the tested glyphs *
914 | of the search buffer (searched glyph). */
915
916 long bm_len; /* number of chars taken by the bitmask. */
917
918 char * start; /* pointer on the position of the matching position *
919 | of the last search buffer glyph in the word. */
920
921 char * bm; /* the word's current bitmap. */
922
923 char * str; /* copy of the current word put in lower case. */
924 char * str_orig; /* original version of the word. */
925
926 char * first_glyph;
927
928 char * sb_orig = data->buf; /* sb: search buffer. */
929 char * sb;
930 long * o = data->utf8_off_a; /* array of the offsets of the search *
931 | buffer glyphs. */
932 long * l = data->utf8_len_a; /* array of the lengths in bytes of *
933 | the search buffer glyphs. */
934 long last = data->utf8_len - 1; /* offset of the last glyph in the *
935 | search buffer. */
936
937 long badness = 0; /* number of 0s between two 1s. */
938
939 best_matches_count = 0;
940
941 if (mode == FUZZY || mode == SUBSTRING)
942 {
943 first_glyph = xmalloc(5);
944
945 if (mode == FUZZY)
946 {
947 sb = xstrdup(sb_orig);
948 utf8_strtolower(sb, sb_orig);
949 }
950 else
951 sb = sb_orig;
952
953 for (i = 0; i < matches_count; i++)
954 {
955 n = matching_words_a[i];
956
957 str_orig = xstrdup(word_a[n].str + daccess.flength);
958
959 /* We need to remove the trailing spaces to use the */
960 /* following algorithm. */
961 /* .len holds the original length in bytes of the word. */
962 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
963 rtrim(str_orig, " \t", 0);
964
965 bm_len = (word_a[n].mb - daccess.flength) / CHAR_BIT + 1;
966 bm = word_a[n].bitmap;
967
968 /* In fuzzy search mode str are converted in lower case letters */
969 /* for comparison reason. */
970 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
971 if (mode == FUZZY)
972 {
973 str = xstrdup(str_orig);
974 utf8_strtolower(str, str_orig);
975 }
976 else
977 str = str_orig;
978
979 start = str;
980 lmg = 0;
981
982 /* start points to the first UTF-8 glyph of the word. */
983 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
984 while ((size_t)(start - str) < word_a[n].len - daccess.flength)
985 {
986 /* Reset the bitmap. */
987 /* """"""""""""""""" */
988 memset(bm, '\0', bm_len);
989
990 /* Compare the glyph pointed to by start to the last glyph of */
991 /* the search buffer, the aim is to point to the first */
992 /* occurrence of the last glyph of it. */
993 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
994 if (memcmp(start, sb + o[last], l[last]) == 0)
995 {
996 char * p; /* Pointer to the beginning of an UTF-8 glyph in *
997 | the potential lowercase version of the word. */
998
999 if (last == 0)
1000 {
1001 /* There is only one glyph in the search buffer, we can */
1002 /* stop here. */
1003 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
1004 BIT_ON(bm, lmg);
1005 if (affinity != END_AFFINITY)
1006 break;
1007 }
1008
1009 /* If the search buffer contains more than one glyph, we need */
1010 /* to search the first combination which match the buffer in */
1011 /* the word. */
1012 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1013 p = start;
1014 j = last; /* j counts the number of glyphs in the search buffer *
1015 | not found in the word. */
1016
1017 /* Proceed backwards from the position of last glyph of the */
1018 /* search to check if all the previous glyphs can be fond before */
1019 /* in the word. If not try to find the next position of this */
1020 /* last glyph in the word. */
1021 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1022 sg = lmg;
1023 while (j > 0 && (p = utf8_prev(str, p)) != NULL)
1024 {
1025 if (memcmp(p, sb + o[j - 1], l[j - 1]) == 0)
1026 {
1027 BIT_ON(bm, sg - 1);
1028 j--;
1029 }
1030 else if (mode == SUBSTRING)
1031 break;
1032
1033 sg--;
1034 }
1035
1036 /* All the glyphs have been found. */
1037 /* """"""""""""""""""""""""""""""" */
1038 if (j == 0)
1039 {
1040 BIT_ON(bm, lmg);
1041 if (affinity != END_AFFINITY)
1042 break;
1043 }
1044 }
1045
1046 lmg++;
1047 start = utf8_next(start);
1048 }
1049
1050 if (mode == FUZZY)
1051 {
1052 size_t utf8_index;
1053
1054 free(str);
1055
1056 /* We know that the first non blank glyph is part of the pattern, */
1057 /* so highlight it if it is not and suppresses the highlighting */
1058 /* of the next occurrence that must be here because this word has */
1059 /* already been filtered by select_starting_pattern(). */
1060 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1061 if (affinity == START_AFFINITY)
1062 {
1063 char *ptr1, *ptr2;
1064 long i;
1065 long utf8_len;
1066
1067 /* Skip leading spaces and tabs. */
1068 /* """"""""""""""""""""""""""""" */
1069 for (i = 0; i < word_a[n].mb; i++)
1070 if (!isblank(*(word_a[n].str + daccess.flength + i)))
1071 break;
1072
1073 first_glyph = utf8_strprefix(first_glyph, word_a[n].str + i, 1,
1074 &utf8_len);
1075
1076 if (!BIT_ISSET(word_a[n].bitmap, i))
1077 {
1078 BIT_ON(word_a[n].bitmap, i);
1079
1080 ptr1 = word_a[n].str + i;
1081 i++;
1082 while ((ptr2 = utf8_next(ptr1)) != NULL)
1083 {
1084 if (memcmp(ptr2, first_glyph, utf8_len) == 0)
1085 {
1086 if (BIT_ISSET(word_a[n].bitmap, i))
1087 {
1088 BIT_OFF(word_a[n].bitmap, i);
1089 break;
1090 }
1091 else
1092 ptr1 = ptr2;
1093 }
1094 else
1095 ptr1 = ptr2;
1096
1097 i++;
1098 }
1099 }
1100 }
1101
1102 /* Compute the number of 'holes' in the bitmap to determine the */
1103 /* badness of a match. Th goal is to put the cursor on the word */
1104 /* with the smallest badness. */
1105 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1106 utf8_index = 0;
1107 j = 0;
1108 badness = 0;
1109
1110 while (utf8_index < word_a[n].mb
1111 && !BIT_ISSET(word_a[n].bitmap, utf8_index))
1112 utf8_index++;
1113
1114 while (utf8_index < word_a[n].mb)
1115 {
1116 if (!BIT_ISSET(word_a[n].bitmap, utf8_index))
1117 badness++;
1118 else
1119 j++;
1120
1121 /* Stop here if all the possible bits has been checked as they */
1122 /* cannot be more numerous than the number of UTF-8 glyphs in */
1123 /* the search buffer. */
1124 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1125 if (j == data->utf8_len)
1126 break;
1127
1128 utf8_index++;
1129 }
1130 }
1131 free(str_orig);
1132
1133 if (search_mode == FUZZY)
1134 {
1135 /* When the badness is zero (best match), add the word position. */
1136 /* at the end of a special array which will be used to move the. */
1137 /* cursor among this category of words. */
1138 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1139 if (badness == 0)
1140 {
1141 if (best_matches_count == best_matching_words_a_size)
1142 {
1143 best_matching_words_a = xrealloc(best_matching_words_a,
1144 (best_matching_words_a_size + 16)
1145 * sizeof(long));
1146 best_matching_words_a_size += 16;
1147 }
1148
1149 best_matching_words_a[best_matches_count] = n;
1150
1151 best_matches_count++;
1152 }
1153 }
1154 }
1155 if (mode == FUZZY)
1156 free(sb);
1157
1158 free(first_glyph);
1159 }
1160 else if (mode == PREFIX)
1161 {
1162 for (i = 0; i < matches_count; i++)
1163 {
1164 n = matching_words_a[i];
1165 bm = word_a[n].bitmap;
1166 bm_len = (word_a[n].mb - daccess.flength) / CHAR_BIT + 1;
1167
1168 memset(bm, '\0', bm_len);
1169
1170 for (j = 0; j <= last; j++)
1171 BIT_ON(bm, j);
1172 }
1173 }
1174 }
1175
1176 /* ====================================================== */
1177 /* Find the next word index in the list of matching words */
1178 /* returns -1 if not found. */
1179 /* ====================================================== */
1180 long
find_next_matching_word(long * array,long nb,long value,long * index)1181 find_next_matching_word(long * array, long nb, long value, long * index)
1182 {
1183 long left = 0, right = nb, middle;
1184
1185 /* Use the cached value when possible. */
1186 /* """"""""""""""""""""""""""""""""""" */
1187 if (*index >= 0 && *index < nb - 1 && array[*index] == value)
1188 return (array[++(*index)]);
1189
1190 if (nb > 0)
1191 {
1192 /* Bisection search. */
1193 /* ''''''''''''''''' */
1194 while (left < right)
1195 {
1196 middle = (left + right) / 2;
1197
1198 if (value < array[middle])
1199 right = middle;
1200 else
1201 left = middle + 1;
1202 }
1203 if (left < nb - 1)
1204 {
1205 *index = left;
1206 return array[*index];
1207 }
1208 else
1209 {
1210 if (value > array[nb - 1])
1211 {
1212 *index = -1;
1213 return *index;
1214 }
1215 else
1216 {
1217 *index = nb - 1;
1218 return array[*index];
1219 }
1220 }
1221 }
1222 else
1223 {
1224 *index = -1;
1225 return *index;
1226 }
1227 }
1228
1229 /* ========================================================== */
1230 /* Find the previous word index in the list of matching words */
1231 /* returns -1 if not found. */
1232 /* ========================================================== */
1233 long
find_prev_matching_word(long * array,long nb,long value,long * index)1234 find_prev_matching_word(long * array, long nb, long value, long * index)
1235 {
1236 long left = 0, right = nb, middle;
1237
1238 /* Use the cached value when possible. */
1239 /* """"""""""""""""""""""""""""""""""" */
1240 if (*index > 0 && array[*index] == value)
1241 return (array[--(*index)]);
1242
1243 if (nb > 0)
1244 {
1245 /* Bisection search. */
1246 /* ''''''''''''''''' */
1247 while (left < right)
1248 {
1249 middle = (left + right) / 2;
1250
1251 if (array[middle] == value)
1252 {
1253 if (middle > 0)
1254 {
1255 *index = middle - 1;
1256 return array[*index];
1257 }
1258 else
1259 {
1260 *index = -1;
1261 return *index;
1262 }
1263 }
1264
1265 if (value < array[middle])
1266 right = middle;
1267 else
1268 left = middle + 1;
1269 }
1270 if (left > 0)
1271 {
1272 *index = left - 1;
1273 return array[*index];
1274 }
1275 else
1276 {
1277 *index = -1;
1278 return *index;
1279 }
1280 }
1281 else
1282 {
1283 *index = -1;
1284 return *index;
1285 }
1286 }
1287
1288 /* ============================================================= */
1289 /* Remove all traces of matched words and redisplay the windows. */
1290 /* ============================================================= */
1291 void
clean_matches(search_data_t * search_data,long size)1292 clean_matches(search_data_t * search_data, long size)
1293 {
1294 sub_tst_t * sub_tst_data;
1295 ll_node_t * fuzzy_node;
1296 long i;
1297
1298 /* Clean the list of lists data-structure containing the search levels */
1299 /* Note that the first element of the list is never deleted. */
1300 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1301 if (tst_search_list)
1302 {
1303 fuzzy_node = tst_search_list->tail;
1304 while (tst_search_list->len > 1)
1305 {
1306 sub_tst_data = (sub_tst_t *)(fuzzy_node->data);
1307
1308 free(sub_tst_data->array);
1309 free(sub_tst_data);
1310
1311 ll_delete(tst_search_list, tst_search_list->tail);
1312 fuzzy_node = tst_search_list->tail;
1313 }
1314 sub_tst_data = (sub_tst_t *)(fuzzy_node->data);
1315 sub_tst_data->count = 0;
1316 }
1317
1318 search_data->fuzzy_err = 0;
1319
1320 /* Clean the search buffer. */
1321 /* """""""""""""""""""""""" */
1322 memset(search_data->buf, '\0', size - daccess.flength);
1323
1324 search_data->len = 0;
1325 search_data->utf8_len = 0;
1326 search_data->only_ending = 0;
1327 search_data->only_starting = 0;
1328
1329 /* Clean the match flags and bitmaps. */
1330 /* """""""""""""""""""""""""""""""""" */
1331 for (i = 0; i < matches_count; i++)
1332 {
1333 long n = matching_words_a[i];
1334
1335 word_a[n].is_matching = 0;
1336
1337 memset(word_a[n].bitmap, '\0',
1338 (word_a[n].mb - daccess.flength) / CHAR_BIT + 1);
1339 }
1340
1341 matches_count = 0;
1342 }
1343
1344 /* *************************** */
1345 /* Terminal utility functions. */
1346 /* *************************** */
1347
1348 /* ===================================================================== */
1349 /* outch is a function version of putchar that can be passed to tputs as */
1350 /* a routine to call. */
1351 /* ===================================================================== */
1352 int
1353 #ifdef __sun
outch(char c)1354 outch(char c)
1355 #else
1356 outch(int c)
1357 #endif
1358 {
1359 putchar(c);
1360 return 1;
1361 }
1362
1363 /* =============================================== */
1364 /* Set the terminal in non echo/non canonical mode */
1365 /* wait for at least one byte, no timeout. */
1366 /* =============================================== */
1367 void
setup_term(int const fd)1368 setup_term(int const fd)
1369 {
1370 /* Save the terminal parameters and configure it in row mode. */
1371 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1372 tcgetattr(fd, &old_in_attrs);
1373
1374 new_in_attrs.c_iflag = 0;
1375 new_in_attrs.c_oflag = old_in_attrs.c_oflag;
1376 new_in_attrs.c_cflag = old_in_attrs.c_cflag;
1377 new_in_attrs.c_lflag = old_in_attrs.c_lflag & ~(ICANON | ECHO | ISIG);
1378
1379 new_in_attrs.c_cc[VMIN] = 1; /* wait for at least 1 byte. */
1380 new_in_attrs.c_cc[VTIME] = 0; /* no timeout. */
1381
1382 tcsetattr(fd, TCSANOW, &new_in_attrs);
1383 }
1384
1385 /* ====================================== */
1386 /* Set the terminal in its previous mode. */
1387 /* ====================================== */
1388 void
restore_term(int const fd)1389 restore_term(int const fd)
1390 {
1391 tcsetattr(fd, TCSANOW, &old_in_attrs);
1392 }
1393
1394 /* ============================================== */
1395 /* Get the terminal numbers of lines and columns */
1396 /* Assume that the TIOCGWINSZ, ioctl is available */
1397 /* Defaults to 80x24. */
1398 /* ============================================== */
1399 void
get_terminal_size(int * const r,int * const c,term_t * term)1400 get_terminal_size(int * const r, int * const c, term_t * term)
1401 {
1402 struct winsize ws;
1403
1404 *r = *c = -1;
1405
1406 if (ioctl(0, TIOCGWINSZ, &ws) == 0)
1407 {
1408 *r = ws.ws_row;
1409 *c = ws.ws_col;
1410
1411 if (*r > 0 && *c > 0)
1412 return;
1413 }
1414
1415 *r = tigetnum("lines");
1416 *c = tigetnum("cols");
1417
1418 if (*r <= 0 || *c <= 0)
1419 {
1420 *r = 80;
1421 *c = 24;
1422 }
1423 }
1424
1425 /* ======================================================= */
1426 /* Get cursor position the terminal. */
1427 /* Assume that the Escape sequence ESC [ 6 n is available. */
1428 /* Returns 1 on success and 0 on error. */
1429 /* ======================================================= */
1430 int
get_cursor_position(int * const r,int * const c)1431 get_cursor_position(int * const r, int * const c)
1432 {
1433 char buf[32] = { 0 };
1434 char * s;
1435
1436 int count = 64;
1437 int v;
1438 int rc = 1;
1439
1440 int ask; /* Number of asked characters. */
1441 int got; /* Number of characters obtained. */
1442
1443 int buf_size = sizeof(buf);
1444
1445 /* Report cursor location. */
1446 /* """"""""""""""""""""""" */
1447 while ((v = write(STDOUT_FILENO, "\x1b[6n", 4)) == -1 && count)
1448 {
1449 if (errno == EINTR)
1450 count--;
1451 else
1452 {
1453 rc = 0;
1454 goto read;
1455 }
1456
1457 errno = 0;
1458 }
1459
1460 if (v != 4)
1461 rc = 0;
1462
1463 read:
1464
1465 /* Read the response: ESC [ rows ; cols R. */
1466 /* """"""""""""""""""""""""""""""""""""""" */
1467 *(s = buf) = 0;
1468
1469 do
1470 {
1471 ask = buf_size - 1 - (s - buf);
1472 got = read(STDIN_FILENO, s, ask);
1473
1474 if (got < 0 && errno == EINTR)
1475 got = 0;
1476 else if (got == 0)
1477 break;
1478
1479 s += got;
1480 } while (strchr(buf, 'R') == NULL);
1481
1482 /* Parse it. */
1483 /* """"""""" */
1484 if (buf[0] != 0x1b || buf[1] != '[')
1485 return 0;
1486
1487 if (sscanf(buf + 2, "%d;%d", r, c) != 2)
1488 rc = 0;
1489
1490 return rc;
1491 }
1492
1493 /* ====================================================== */
1494 /* Returns 1 if a string is empty or only made of spaces. */
1495 /* ====================================================== */
1496 int
isempty(const char * s)1497 isempty(const char * s)
1498 {
1499 while (*s != '\0')
1500 {
1501 if (my_isprint(*s) && *s != ' ' && *s != '\t')
1502 return 0;
1503 s++;
1504 }
1505 return 1;
1506 }
1507
1508 /* ======================================================================== */
1509 /* Parse a regular expression based selector. */
1510 /* The string to parse is bounded by a delimiter so we must parse something */
1511 /* like: <delim><regex string><delim> as in /a.*b/ by example. */
1512 /* */
1513 /* str (in) delimited string to parse */
1514 /* filter (in) INCLUDE_FILTER or EXCLUDE_FILTER */
1515 /* inc_regex_list (out) INCLUDE_FILTER or EXCLUDE_FILTER */
1516 /* regular expression if the filter is INCLUDE_FILTER */
1517 /* exc_regex_list (out) INCLUDE_FILTER or EXCLUDE_FILTER */
1518 /* regular expression if the filter is EXCLUDE_FILTER */
1519 /* ======================================================================== */
1520 void
parse_regex_selector_part(char * str,filters_t filter,ll_t ** inc_regex_list,ll_t ** exc_regex_list)1521 parse_regex_selector_part(char * str, filters_t filter, ll_t ** inc_regex_list,
1522 ll_t ** exc_regex_list)
1523 {
1524 regex_t * regex;
1525
1526 str[strlen(str) - 1] = '\0';
1527
1528 regex = xmalloc(sizeof(regex_t));
1529 if (regcomp(regex, str + 1, REG_EXTENDED | REG_NOSUB) == 0)
1530 {
1531 if (filter == INCLUDE_FILTER)
1532 {
1533 if (*inc_regex_list == NULL)
1534 *inc_regex_list = ll_new();
1535
1536 ll_append(*inc_regex_list, regex);
1537 }
1538 else
1539 {
1540 if (*exc_regex_list == NULL)
1541 *exc_regex_list = ll_new();
1542
1543 ll_append(*exc_regex_list, regex);
1544 }
1545 }
1546 }
1547
1548 /* ===================================================================== */
1549 /* Parse a description string. */
1550 /* <letter><range1>,<range2>,... */
1551 /* <range> is n1-n2 | n1 where n1 starts with 1. */
1552 /* */
1553 /* <letter> is a|A|s|S|r|R|u|U where: */
1554 /* a|A is for Add. */
1555 /* s|S is for Select (same as Add). */
1556 /* r|R is for Remove. */
1557 /* d|D is for Deselect (same as Remove). */
1558 /* */
1559 /* str (in) string to parse. */
1560 /* filter (out) is INCLUDE_FILTER or EXCLUDE_FILTER according */
1561 /* to <letter>. */
1562 /* unparsed (out) is empty on success and contains the unparsed */
1563 /* part on failure. */
1564 /* inc_interval_list (out) is a list of the interval of elements to */
1565 /* be included. */
1566 /* inc_regex_list (out) is a list of extended regular expressions of */
1567 /* elements to be included. */
1568 /* exc_interval_list (out) is a list of the interval of elements to be */
1569 /* excluded. */
1570 /* exc_regex_list (out) is a list of extended interval of elements to */
1571 /* be excluded. */
1572 /* ===================================================================== */
1573 void
parse_selectors(char * str,filters_t * filter,char * unparsed,ll_t ** inc_interval_list,ll_t ** inc_regex_list,ll_t ** exc_interval_list,ll_t ** exc_regex_list,langinfo_t * langinfo,misc_t * misc)1574 parse_selectors(char * str, filters_t * filter, char * unparsed,
1575 ll_t ** inc_interval_list, ll_t ** inc_regex_list,
1576 ll_t ** exc_interval_list, ll_t ** exc_regex_list,
1577 langinfo_t * langinfo, misc_t * misc)
1578 {
1579 char mark; /* Value to set */
1580 char c;
1581 size_t start = 1; /* column string offset in the parsed string. */
1582 size_t first, second; /* range starting and ending values. */
1583 char * ptr; /* pointer to the remaining string to parse. */
1584 interval_t * interval;
1585
1586 /* Replace the UTF-8 ascii representation in the selector by */
1587 /* their binary values. */
1588 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1589 utf8_interpret(str, langinfo, misc->invalid_char_substitute);
1590
1591 /* Get the first character to see if this is */
1592 /* an additive or restrictive operation. */
1593 /* """"""""""""""""""""""""""""""""""""""""" */
1594 if (sscanf(str, "%c", &c) == 0)
1595 return;
1596
1597 switch (c)
1598 {
1599 case 'i':
1600 case 'I':
1601 *filter = INCLUDE_FILTER;
1602 mark = INCLUDE_MARK;
1603 break;
1604
1605 case 'e':
1606 case 'E':
1607 *filter = EXCLUDE_FILTER;
1608 mark = EXCLUDE_MARK;
1609 break;
1610
1611 case '1':
1612 case '2':
1613 case '3':
1614 case '4':
1615 case '5':
1616 case '6':
1617 case '7':
1618 case '8':
1619 case '9':
1620 *filter = INCLUDE_FILTER;
1621 mark = INCLUDE_MARK;
1622 start = 0;
1623 break;
1624
1625 default:
1626 if (!isgraph(c))
1627 return;
1628
1629 *filter = INCLUDE_FILTER;
1630 mark = INCLUDE_MARK;
1631 start = 0;
1632 break;
1633 }
1634
1635 /* Set ptr to the start of the interval list to parse. */
1636 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
1637 ptr = str + start;
1638
1639 first = second = -1;
1640
1641 /* Scan the comma separated ranges. */
1642 /* """""""""""""""""""""""""""""""" */
1643 while (*ptr)
1644 {
1645 size_t swap;
1646 int is_range = 0;
1647 char delim1, delim2 = '\0';
1648 char * oldptr;
1649
1650 oldptr = ptr;
1651 while (*ptr && *ptr != ',')
1652 {
1653 if (*ptr == '-')
1654 is_range = 1;
1655 ptr++;
1656 }
1657
1658 /* Forbid the trailing comma (ex: xxx,). */
1659 /* """"""""""""""""""""""""""""""""""""" */
1660 if (*ptr == ',' && (*(ptr + 1) == '\0' || *(ptr + 1) == '-'))
1661 {
1662 my_strcpy(unparsed, ptr);
1663 return;
1664 }
1665
1666 /* Forbid the empty patterns (ex: xxx,,yyy). */
1667 /* """"""""""""""""""""""""""""""""""""""""" */
1668 if (oldptr == ptr)
1669 {
1670 my_strcpy(unparsed, ptr);
1671 return;
1672 }
1673
1674 /* Mark the end of the interval found. */
1675 /* """"""""""""""""""""""""""""""""""" */
1676 if (*ptr)
1677 *ptr++ = '\0';
1678
1679 /* If the regex contains at least three characters then delim1 */
1680 /* and delim2 point to the first and last delimiter of the */
1681 /* regular expression. Ex /abc/ */
1682 /* ^ ^ */
1683 /* | | */
1684 /* delim1 delim2 */
1685 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1686 delim1 = *(str + start);
1687 if (*ptr == '\0')
1688 delim2 = *(ptr - 1);
1689 else if (ptr > str + start + 2)
1690 delim2 = *(ptr - 2);
1691
1692 /* Check is we have found a well describes regular expression. */
1693 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1694 if (ptr > str + start + 2 && delim1 == delim2 && isgraph(delim1)
1695 && isgraph(delim2) && !isdigit(delim1) && !isdigit(delim2))
1696 {
1697 /* Process the regex. */
1698 /* """""""""""""""""" */
1699 parse_regex_selector_part(str + start, *filter, inc_regex_list,
1700 exc_regex_list);
1701
1702 /* Adjust the start of the new interval to read in the */
1703 /* initial string. */
1704 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
1705 start = ptr - str;
1706
1707 continue;
1708 }
1709 else if (delim2 != '\0' && (!isdigit(delim1) || !isdigit(delim2)))
1710 {
1711 /* Both delimiter must be numeric if delim2 exist. */
1712 /* """"""""""""""""""""""""""""""""""""""""""""""" */
1713 my_strcpy(unparsed, str + start);
1714 return;
1715 }
1716
1717 if (is_range)
1718 {
1719 int rc;
1720 int pos;
1721
1722 rc = sscanf(str + start, "%zu-%zu%n", &first, &second, &pos);
1723 if (rc != 2 || *(str + start + pos) != '\0')
1724 {
1725 my_strcpy(unparsed, str + start);
1726 return;
1727 }
1728
1729 if (first < 1 || second < 1)
1730 {
1731 /* Both interval boundaries must be strictly positive. */
1732 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
1733 my_strcpy(unparsed, str + start);
1734 return;
1735 }
1736
1737 /* Ensure that the low bound of the interval is lower or equal */
1738 /* to the high one. Swap them if needed. */
1739 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1740 interval = interval_new();
1741
1742 if (first > second)
1743 {
1744 swap = first;
1745 first = second;
1746 second = swap;
1747 }
1748
1749 interval->low = first - 1;
1750 interval->high = second - 1;
1751 }
1752 else
1753 {
1754 /* Read the number given. */
1755 /* """""""""""""""""""""" */
1756 if (sscanf(str + start, "%zu", &first) != 1)
1757 {
1758 my_strcpy(unparsed, str + start);
1759 return;
1760 }
1761
1762 interval = interval_new();
1763 interval->low = interval->high = first - 1;
1764 }
1765
1766 /* Adjust the start of the new interval to read in the */
1767 /* initial string. */
1768 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
1769 start = ptr - str;
1770
1771 /* Add the new interval to the correct list. */
1772 /* """"""""""""""""""""""""""""""""""""""""" */
1773 if (mark == EXCLUDE_MARK)
1774 {
1775 if (*exc_interval_list == NULL)
1776 *exc_interval_list = ll_new();
1777
1778 ll_append(*exc_interval_list, interval);
1779 }
1780 else
1781 {
1782 if (*inc_interval_list == NULL)
1783 *inc_interval_list = ll_new();
1784
1785 ll_append(*inc_interval_list, interval);
1786 }
1787 }
1788
1789 /* If we are here, then all the intervals have be successfully parsed */
1790 /* Ensure that the unparsed string is empty. */
1791 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1792 *unparsed = '\0';
1793 }
1794
1795 /* ========================================================= */
1796 /* Parse the sed like string passed as argument to -S/-I/-E. */
1797 /* Update the sed parameter. */
1798 /* ========================================================= */
1799 int
parse_sed_like_string(sed_t * sed)1800 parse_sed_like_string(sed_t * sed)
1801 {
1802 char sep;
1803 char * first_sep_pos;
1804 char * last_sep_pos;
1805 char * buf;
1806 long index;
1807 int icase;
1808 char c;
1809
1810 if (strlen(sed->pattern) < 4)
1811 return 0;
1812
1813 /* Get the separator (the 1st character). */
1814 /* """""""""""""""""""""""""""""""""""""" */
1815 buf = xstrdup(sed->pattern);
1816 sep = buf[0];
1817
1818 /* Space like separators are not permitted. */
1819 /* """""""""""""""""""""""""""""""""""""""" */
1820 if (isspace(sep))
1821 goto err;
1822
1823 /* Get the extended regular expression. */
1824 /* """""""""""""""""""""""""""""""""""" */
1825 if ((first_sep_pos = strchr(buf + 1, sep)) == NULL)
1826 goto err;
1827
1828 *first_sep_pos = '\0';
1829
1830 /* Get the substitution string. */
1831 /* """""""""""""""""""""""""""" */
1832 if ((last_sep_pos = strchr(first_sep_pos + 1, sep)) == NULL)
1833 goto err;
1834
1835 *last_sep_pos = '\0';
1836
1837 sed->substitution = xstrdup(first_sep_pos + 1);
1838
1839 /* Get the global indicator (trailing g) */
1840 /* and the visual indicator (trailing v) */
1841 /* and the stop indicator (trailing s). */
1842 /* """"""""""""""""""""""""""""""""""""" */
1843 sed->global = sed->visual = icase = 0;
1844
1845 index = 1;
1846 while ((c = *(last_sep_pos + index)) != '\0')
1847 {
1848 if (c == 'g')
1849 sed->global = 1;
1850 else if (c == 'v')
1851 sed->visual = 1;
1852 else if (c == 's')
1853 sed->stop = 1;
1854 else if (c == 'i')
1855 icase = 1;
1856 else
1857 goto err;
1858
1859 index++;
1860 }
1861
1862 /* Empty regular expression ? */
1863 /* """""""""""""""""""""""""" */
1864 if (*(buf + 1) == '\0')
1865 goto err;
1866
1867 /* Compile the regular expression and abort on failure. */
1868 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
1869 if (regcomp(&(sed->re), buf + 1,
1870 !icase ? REG_EXTENDED : (REG_EXTENDED | REG_ICASE))
1871 != 0)
1872 goto err;
1873
1874 free(buf);
1875
1876 return 1;
1877
1878 err:
1879 free(buf);
1880
1881 return 0;
1882 }
1883
1884 /* ===================================================================== */
1885 /* Utility function used by replace to expand the replacement string. */
1886 /* IN: */
1887 /* orig: matching part of the original string to be replaced. */
1888 /* repl: string containing the replacement directives */
1889 /* subs_a: array of ranges containing the start and end offset */
1890 /* of the remembered parts of the strings referenced */
1891 /* by the sequence \n where n is in [1,10]. */
1892 /* match_start/end: offset in orig for the current matched string */
1893 /* subs_nb: number of elements containing significant values in */
1894 /* the array described above. */
1895 /* match: current match number in the original string. */
1896 /* */
1897 /* OUT: */
1898 /* The modified string according to the content of repl. */
1899 /* ===================================================================== */
1900 char *
build_repl_string(char * orig,char * repl,long match_start,long match_end,range_t * subs_a,long subs_nb,long match)1901 build_repl_string(char * orig, char * repl, long match_start, long match_end,
1902 range_t * subs_a, long subs_nb, long match)
1903 {
1904 size_t rsize = 0;
1905 size_t allocated = 16;
1906 char * str = xmalloc(allocated);
1907 int special = 0;
1908 long offset = match * subs_nb; /* offset of the 1st sub *
1909 | corresponding to the match. */
1910
1911 if (*repl == '\0')
1912 str = xstrdup("");
1913 else
1914 while (*repl)
1915 {
1916 switch (*repl)
1917 {
1918 case '\\':
1919 if (special)
1920 {
1921 if (allocated == rsize)
1922 str = xrealloc(str, allocated += 16);
1923 str[rsize] = '\\';
1924 rsize++;
1925 str[rsize] = '\0';
1926 special = 0;
1927 }
1928 else
1929 special = 1;
1930 break;
1931
1932 case '1':
1933 case '2':
1934 case '3':
1935 case '4':
1936 case '5':
1937 case '6':
1938 case '7':
1939 case '8':
1940 case '9':
1941 if (special)
1942 {
1943 if ((*repl) - '0' <= subs_nb)
1944 {
1945 long index = (*repl) - '0' - 1 + offset;
1946 long delta = subs_a[index].end - subs_a[index].start;
1947
1948 if (allocated <= rsize + delta)
1949 str = xrealloc(str, allocated += (delta + 16));
1950
1951 memcpy(str + rsize, orig + subs_a[index].start, delta);
1952
1953 rsize += delta;
1954 str[rsize] = '\0';
1955 }
1956
1957 special = 0;
1958 }
1959 else
1960 {
1961 if (allocated == rsize)
1962 str = xrealloc(str, allocated += 16);
1963 str[rsize] = *repl;
1964 rsize++;
1965 str[rsize] = '\0';
1966 }
1967 break;
1968
1969 case '&':
1970 if (!special)
1971 {
1972 long delta = match_end - match_start;
1973
1974 if (allocated <= rsize + delta)
1975 str = xrealloc(str, allocated += (delta + 16));
1976
1977 memcpy(str + rsize, orig + match_start, delta);
1978
1979 rsize += delta;
1980 str[rsize] = '\0';
1981
1982 break;
1983 }
1984
1985 /* No break here, '&' must be treated as a normal */
1986 /* character when protected. */
1987 /* '''''''''''''''''''''''''''''''''''''''''''''' */
1988
1989 default:
1990 special = 0;
1991 if (allocated == rsize)
1992 str = xrealloc(str, allocated += 16);
1993 str[rsize] = *repl;
1994 rsize++;
1995 str[rsize] = '\0';
1996 }
1997 repl++;
1998 }
1999 return str;
2000 }
2001
2002 /* ====================================================================== */
2003 /* Replace the part of a string matched by an extender regular expression */
2004 /* by the substitution string. */
2005 /* The regex used must have been previously compiled. */
2006 /* */
2007 /* orig: original string */
2008 /* sed: composite variable containing the regular expression, a */
2009 /* substitution string and various other informations. */
2010 /* output: destination buffer. */
2011 /* */
2012 /* RC: */
2013 /* return 1 if the replacement has been successful else 0. */
2014 /* */
2015 /* NOTE: */
2016 /* uses the global variable word_buffer. */
2017 /* ====================================================================== */
2018 int
replace(char * orig,sed_t * sed)2019 replace(char * orig, sed_t * sed)
2020 {
2021 size_t match_nb = 0; /* number of matches in the original string. */
2022 int sub_nb = 0; /* number of remembered matches in the *
2023 | original sting. */
2024 size_t target_len = 0; /* length of the resulting string. */
2025 size_t subs_max = 0;
2026
2027 if (*orig == '\0')
2028 return 1;
2029
2030 range_t * matches_a = xmalloc(strlen(orig) * sizeof(range_t));
2031 range_t * subs_a = xmalloc(10 * sizeof(range_t));
2032
2033 const char * p = orig; /* points to the end of the previous match. */
2034 regmatch_t m[10]; /* array containing the start/end offsets *
2035 | of the matches found. */
2036
2037 while (1)
2038 {
2039 size_t i = 0;
2040 size_t match; /* current match index. */
2041 size_t index = 0; /* current char index in the original string. */
2042 int nomatch = 0; /* equals to 1 when there is no more matching. */
2043 char * exp_repl; /* expanded replacement string. */
2044
2045 if (*p == '\0')
2046 nomatch = 1;
2047 else
2048 nomatch = regexec(&sed->re, p, 10, m, 0);
2049
2050 if (nomatch)
2051 {
2052 if (match_nb > 0)
2053 {
2054 for (index = 0; index < matches_a[0].start; index++)
2055 word_buffer[target_len++] = orig[index];
2056
2057 for (match = 0; match < match_nb; match++)
2058 {
2059 size_t len;
2060 size_t end;
2061
2062 exp_repl = build_repl_string(orig, sed->substitution,
2063 matches_a[match].start,
2064 matches_a[match].end, subs_a, subs_max,
2065 match);
2066
2067 len = strlen(exp_repl);
2068
2069 my_strcpy(word_buffer + target_len, exp_repl);
2070 target_len += len;
2071 free(exp_repl);
2072
2073 index += matches_a[match].end - matches_a[match].start;
2074
2075 if (match < match_nb - 1 && sed->global)
2076 end = matches_a[match + 1].start;
2077 else
2078 end = strlen(orig);
2079
2080 while (index < end)
2081 word_buffer[target_len++] = orig[index++];
2082
2083 word_buffer[target_len] = '\0';
2084
2085 if (!sed->global)
2086 break;
2087 }
2088 }
2089 else
2090 {
2091 my_strcpy(word_buffer, orig);
2092 return 0;
2093 }
2094
2095 return nomatch;
2096 }
2097
2098 subs_max = 0;
2099 for (i = 0; i < 10; i++)
2100 {
2101 size_t start;
2102 size_t finish;
2103
2104 if (m[i].rm_so == -1)
2105 break;
2106
2107 start = m[i].rm_so + (p - orig);
2108 finish = m[i].rm_eo + (p - orig);
2109
2110 if (i == 0)
2111 {
2112 matches_a[match_nb].start = start;
2113 matches_a[match_nb].end = finish;
2114 match_nb++;
2115 if (match_nb > utf8_strlen(orig))
2116 goto fail;
2117 }
2118 else
2119 {
2120 subs_a[sub_nb].start = start;
2121 subs_a[sub_nb].end = finish;
2122 sub_nb++;
2123 subs_max++;
2124 }
2125 }
2126 if (m[0].rm_eo > 0)
2127 p += m[0].rm_eo;
2128 else
2129 p++; /* Empty match. */
2130 }
2131
2132 fail:
2133 return 0;
2134 }
2135
2136 /* ============================================================ */
2137 /* Remove all ANSI color codes from s and puts the result in d. */
2138 /* Memory space for d must have been allocated before. */
2139 /* ============================================================ */
2140 void
strip_ansi_color(char * s,toggle_t * toggles,misc_t * misc)2141 strip_ansi_color(char * s, toggle_t * toggles, misc_t * misc)
2142 {
2143 char * p = s;
2144 long len = strlen(s);
2145
2146 while (*s != '\0')
2147 {
2148 /* Remove a sequence of \x1b[...m from s. */
2149 /* """""""""""""""""""""""""""""""""""""" */
2150 if ((*s == 0x1b) && (*(s + 1) == '['))
2151 {
2152 while ((*s != '\0') && (*s++ != 'm'))
2153 ;
2154 }
2155 /* Convert a single \x1b in the invalid substitute character. */
2156 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2157 else if (*s == 0x1b)
2158 {
2159 if (toggles->blank_nonprintable && len > 1)
2160 *s++ = ' ';
2161 else
2162 *s++ = misc->invalid_char_substitute;
2163 p++;
2164 }
2165 /* No ESC char, we can move on. */
2166 /* """""""""""""""""""""""""""" */
2167 else
2168 *p++ = *s++;
2169 }
2170
2171 *p = '\0';
2172 }
2173
2174 /* ================================================================= */
2175 /* Callback function to insert the index of a matching word index in */
2176 /* the sorted list of the already matched words. */
2177 /* ================================================================= */
2178 int
set_matching_flag(void * elem)2179 set_matching_flag(void * elem)
2180 {
2181 ll_t * list = (ll_t *)elem;
2182
2183 ll_node_t * node = list->head;
2184
2185 while (node)
2186 {
2187 size_t pos;
2188
2189 pos = *(size_t *)(node->data);
2190 if (word_a[pos].is_selectable)
2191 word_a[pos].is_matching = 1;
2192
2193 insert_sorted_index(&matching_words_a, &matching_words_a_size,
2194 &matches_count, pos);
2195
2196 node = node->next;
2197 }
2198 return 1;
2199 }
2200
2201 /* ======================================================================= */
2202 /* Callback function used by tst_traverse. */
2203 /* Iterate the linked list attached to the string containing the index of */
2204 /* the words in the input flow. Each page number is then added in a sorted */
2205 /* array avoiding duplications keeping the array sorted. */
2206 /* Mark the identified words as a matching word. */
2207 /* ======================================================================= */
2208 int
tst_cb(void * elem)2209 tst_cb(void * elem)
2210 {
2211 /* The data attached to the string in the tst is a linked list of */
2212 /* position of the string in the input flow, This list is naturally */
2213 /* sorted. */
2214 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2215 ll_t * list = (ll_t *)elem;
2216
2217 ll_node_t * node = list->head;
2218
2219 while (node)
2220 {
2221 size_t pos;
2222
2223 pos = *(long *)(node->data);
2224
2225 word_a[pos].is_matching = 1;
2226 insert_sorted_index(&matching_words_a, &matching_words_a_size,
2227 &matches_count, pos);
2228
2229 node = node->next;
2230 }
2231 return 1;
2232 }
2233
2234 /* ======================================================================= */
2235 /* Callback function used by tst_traverse. */
2236 /* Iterate the linked list attached to the string containing the index of */
2237 /* the words in the input flow. Each page number is then used to determine */
2238 /* the lower page greater than the cursor position */
2239 /* ----------------------------------------------------------------------- */
2240 /* This is a special version of tst_cb which permit to find the first */
2241 /* word. */
2242 /* ----------------------------------------------------------------------- */
2243 /* Require new_current to be set to count - 1 at start. */
2244 /* Update new_current to the smallest greater position than current. */
2245 /* ======================================================================= */
2246 int
tst_cb_cli(void * elem)2247 tst_cb_cli(void * elem)
2248 {
2249 long n = 0;
2250 int rc = 0;
2251
2252 /* The data attached to the string in the tst is a linked list of */
2253 /* position of the string in the input flow, This list is naturally */
2254 /* sorted. */
2255 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2256 ll_t * list = (ll_t *)elem;
2257
2258 ll_node_t * node = list->head;
2259
2260 while (n++ < list->len)
2261 {
2262 long pos;
2263
2264 pos = *(long *)(node->data);
2265
2266 word_a[pos].is_matching = 1;
2267 insert_sorted_index(&matching_words_a, &matching_words_a_size,
2268 &matches_count, pos);
2269
2270 /* We already are at the last word, report the finding. */
2271 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2272 if (pos == count - 1)
2273 return 1;
2274
2275 /* Only consider the indexes above the current cursor position. */
2276 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2277 if (pos >= current) /* Enable the search of the current word. */
2278 {
2279 /* As the future new current index has been set to the highest */
2280 /* possible value, each new possible position can only improve */
2281 /* the estimation we set rc to 1 to mark that. */
2282 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2283 if (pos < new_current)
2284 {
2285 new_current = pos;
2286 rc = 1;
2287 }
2288 }
2289
2290 node = node->next;
2291 }
2292 return rc;
2293 }
2294
2295 /* **************** */
2296 /* Input functions. */
2297 /* **************** */
2298
2299 /* =============================================== */
2300 /* Non delay reading of a scancode. */
2301 /* Update a scancodes buffer and return its length */
2302 /* in bytes. */
2303 /* =============================================== */
2304 int
get_scancode(unsigned char * s,size_t max)2305 get_scancode(unsigned char * s, size_t max)
2306 {
2307 int c;
2308 size_t i = 1;
2309 struct termios original_ts, nowait_ts;
2310
2311 if ((c = my_fgetc(stdin)) == EOF)
2312 return 0;
2313
2314 /* Initialize the string with the first byte. */
2315 /* """""""""""""""""""""""""""""""""""""""""" */
2316 memset(s, '\0', max);
2317 s[0] = c;
2318
2319 /* 0x1b (ESC) has been found, proceed to check if additional codes */
2320 /* are available. */
2321 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2322 if (c == 0x1b || c > 0x80)
2323 {
2324 /* Save the terminal parameters and configure getchar() */
2325 /* to return immediately. */
2326 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2327 tcgetattr(0, &original_ts);
2328 nowait_ts = original_ts;
2329 nowait_ts.c_lflag &= ~ISIG;
2330 nowait_ts.c_cc[VMIN] = 0;
2331 nowait_ts.c_cc[VTIME] = 0;
2332 tcsetattr(0, TCSADRAIN, &nowait_ts);
2333
2334 /* Check if additional code is available after 0x1b. */
2335 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2336 if ((c = my_fgetc(stdin)) != EOF)
2337 {
2338 s[1] = c;
2339
2340 i = 2;
2341 while (i < max && (c = my_fgetc(stdin)) != EOF)
2342 s[i++] = c;
2343 }
2344 else
2345 {
2346 /* There isn't a new code, this mean 0x1b came from ESC key. */
2347 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2348 }
2349
2350 /* Restore the save terminal parameters. */
2351 /* """"""""""""""""""""""""""""""""""""" */
2352 tcsetattr(0, TCSADRAIN, &original_ts);
2353
2354 /* Ignore EOF when a scancode contains an escape sequence. */
2355 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2356 clearerr(stdin);
2357 }
2358
2359 return i;
2360 }
2361
2362 /* ============================================================ */
2363 /* Helper function to compare to delimiters for use by ll_find. */
2364 /* ============================================================ */
2365 int
buffer_cmp(const void * a,const void * b)2366 buffer_cmp(const void * a, const void * b)
2367 {
2368 return strcmp((const char *)a, (const char *)b);
2369 }
2370
2371 /* ===================================================================== */
2372 /* Get bytes from stdin. If the first byte is the leading character of a */
2373 /* UTF-8 glyph, the following ones are also read. */
2374 /* The utf8_get_length function is used to get the number of bytes of */
2375 /* the character. */
2376 /* ===================================================================== */
2377 int
get_bytes(FILE * input,char * utf8_buffer,ll_t * zapped_glyphs_list,langinfo_t * langinfo,misc_t * misc)2378 get_bytes(FILE * input, char * utf8_buffer, ll_t * zapped_glyphs_list,
2379 langinfo_t * langinfo, misc_t * misc)
2380 {
2381 int byte;
2382 int last;
2383 int n;
2384
2385 do
2386 {
2387 last = 0;
2388
2389 /* Read the first byte. */
2390 /* """""""""""""""""""" */
2391 byte = my_fgetc(input);
2392
2393 if (byte == EOF)
2394 return EOF;
2395
2396 utf8_buffer[last++] = byte;
2397
2398 /* Check if we need to read more bytes to form a sequence */
2399 /* and put the number of bytes of the sequence in last. */
2400 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
2401 if (langinfo->utf8 && ((n = utf8_get_length(byte)) > 1))
2402 {
2403 while (last < n && (byte = my_fgetc(input)) != EOF
2404 && (byte & 0xc0) == 0x80)
2405 utf8_buffer[last++] = byte;
2406
2407 if (byte == EOF)
2408 return EOF;
2409 }
2410
2411 utf8_buffer[last] = '\0';
2412
2413 /* Replace an invalid UTF-8 byte sequence by a single dot. */
2414 /* In this case the original sequence is lost (unsupported */
2415 /* encoding). */
2416 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2417 if (langinfo->utf8 && utf8_validate(utf8_buffer) != NULL)
2418 {
2419 byte = utf8_buffer[0] = misc->invalid_char_substitute;
2420 utf8_buffer[1] = '\0';
2421 }
2422 } while (ll_find(zapped_glyphs_list, utf8_buffer, buffer_cmp) != NULL);
2423
2424 return byte;
2425 }
2426
2427 /* =======================================================================*/
2428 /* Expand the string str by replacing all its embedded special characters */
2429 /* by their corresponding escape sequence. */
2430 /* dest must be long enough to contain the expanded string. */
2431 /* ====================================================================== */
2432 size_t
expand(char * src,char * dest,langinfo_t * langinfo,toggle_t * toggles,misc_t * misc)2433 expand(char * src, char * dest, langinfo_t * langinfo, toggle_t * toggles,
2434 misc_t * misc)
2435 {
2436 char c;
2437 int n;
2438 int all_spaces = 1;
2439 char * ptr = dest;
2440 size_t len = 0;
2441
2442 while ((c = *(src++)))
2443 {
2444 /* UTF-8 codepoints take more than on character. */
2445 /* """"""""""""""""""""""""""""""""""""""""""""" */
2446 if ((n = utf8_get_length(c)) > 1)
2447 {
2448 all_spaces = 0;
2449
2450 if (langinfo->utf8)
2451 /* If the locale is UTF-8 aware, copy src into ptr. */
2452 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2453 do
2454 {
2455 *(ptr++) = c;
2456 len++;
2457 } while (--n && (c = *(src++)));
2458 else
2459 {
2460 /* If not, ignore the bytes composing the UTF-8 */
2461 /* glyph and replace them with a single '.'. */
2462 /* """""""""""""""""""""""""""""""""""""""""""" */
2463 do
2464 {
2465 /* Skip this byte. */
2466 /* ''''''''''''''' */
2467 } while (--n && ('\0' != *(src++)));
2468
2469 *(ptr++) = misc->invalid_char_substitute;
2470 len++;
2471 }
2472 }
2473 else
2474 /* This is not an UTF-8 glyph. */
2475 /* """"""""""""""""""""""""""" */
2476 switch (c)
2477 {
2478 case '\a':
2479 *(ptr++) = '\\';
2480 *(ptr++) = 'a';
2481 len += 2;
2482 all_spaces = 0;
2483 break;
2484 case '\b':
2485 *(ptr++) = '\\';
2486 *(ptr++) = 'b';
2487 len += 2;
2488 all_spaces = 0;
2489 break;
2490 case '\t':
2491 *(ptr++) = '\\';
2492 *(ptr++) = 't';
2493 len += 2;
2494 all_spaces = 0;
2495 break;
2496 case '\n':
2497 *(ptr++) = '\\';
2498 *(ptr++) = 'n';
2499 len += 2;
2500 all_spaces = 0;
2501 break;
2502 case '\v':
2503 *(ptr++) = '\\';
2504 *(ptr++) = 'v';
2505 len += 2;
2506 all_spaces = 0;
2507 break;
2508 case '\f':
2509 *(ptr++) = '\\';
2510 *(ptr++) = 'f';
2511 len += 2;
2512 all_spaces = 0;
2513 break;
2514 case '\r':
2515 *(ptr++) = '\\';
2516 *(ptr++) = 'r';
2517 len += 2;
2518 all_spaces = 0;
2519 break;
2520 case '\\':
2521 *(ptr++) = '\\';
2522 *(ptr++) = '\\';
2523 len += 2;
2524 all_spaces = 0;
2525 break;
2526 default:
2527 if (my_isprint(c))
2528 {
2529 *(ptr++) = c;
2530 all_spaces = 0;
2531 }
2532 else
2533 {
2534 if (toggles->blank_nonprintable)
2535 *(ptr++) = ' ';
2536 else
2537 {
2538 *(ptr++) = misc->invalid_char_substitute;
2539 all_spaces = 0;
2540 }
2541 }
2542 len++;
2543 }
2544 }
2545
2546 /* If the word contains only spaces, replace them */
2547 /* by underscores so that it can be seen. */
2548 /* """""""""""""""""""""""""""""""""""""""""""""" */
2549 if (all_spaces)
2550 memset(dest, ' ', len);
2551
2552 *ptr = '\0'; /* Ensure that dest has a nul terminator. */
2553
2554 return len;
2555 }
2556
2557 /* ===================================================================== */
2558 /* get_word(input): return a char pointer to the next word (as a string) */
2559 /* Accept: a FILE * for the input stream. */
2560 /* Return: a char * */
2561 /* On Success: the return value will point to a nul-terminated */
2562 /* string. */
2563 /* On Failure: the return value will be set to NULL. */
2564 /* ===================================================================== */
2565 char *
get_word(FILE * input,ll_t * word_delims_list,ll_t * record_delims_list,ll_t * zapped_glyphs_list,char * utf8_buffer,unsigned char * is_last,toggle_t * toggles,langinfo_t * langinfo,win_t * win,limit_t * limits,misc_t * misc)2566 get_word(FILE * input, ll_t * word_delims_list, ll_t * record_delims_list,
2567 ll_t * zapped_glyphs_list, char * utf8_buffer, unsigned char * is_last,
2568 toggle_t * toggles, langinfo_t * langinfo, win_t * win,
2569 limit_t * limits, misc_t * misc)
2570 {
2571 char * temp = NULL;
2572 int byte;
2573 long utf8_count = 0; /* count chars used in current allocation. */
2574 long wordsize; /* size of current allocation in chars. */
2575 int is_dquote; /* double quote presence indicator. */
2576 int is_squote; /* single quote presence indicator. */
2577 int is_special; /* a character is special after a \ */
2578
2579 /* Skip leading delimiters. */
2580 /* """""""""""""""""""""""" */
2581 byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo, misc);
2582
2583 while (byte == EOF
2584 || ll_find(word_delims_list, utf8_buffer, buffer_cmp) != NULL)
2585 {
2586 if (byte == EOF)
2587 return NULL;
2588
2589 byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo, misc);
2590 }
2591
2592 /* Allocate initial word storage space. */
2593 /* """""""""""""""""""""""""""""""""""" */
2594 temp = xmalloc(wordsize = CHARSCHUNK);
2595
2596 /* Start stashing bytes. Stop when we meet a non delimiter or EOF. */
2597 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2598 utf8_count = 0;
2599 is_dquote = 0;
2600 is_squote = 0;
2601 is_special = 0;
2602
2603 while (byte != EOF)
2604 {
2605 size_t i = 0;
2606
2607 if (utf8_count >= limits->word_length)
2608 {
2609 fprintf(stderr,
2610 "The length of a word has reached the limit of "
2611 "%ld characters.\n",
2612 limits->word_length);
2613
2614 exit(EXIT_FAILURE);
2615 }
2616
2617 if (byte == '\\' && !is_special)
2618 {
2619 is_special = 1;
2620 goto next;
2621 }
2622
2623 /* Parse special characters. */
2624 /* """"""""""""""""""""""""" */
2625 if (is_special)
2626 switch (byte)
2627 {
2628 case 'a':
2629 utf8_buffer[0] = byte = '\a';
2630 utf8_buffer[1] = '\0';
2631 break;
2632
2633 case 'b':
2634 utf8_buffer[0] = byte = '\b';
2635 utf8_buffer[1] = '\0';
2636 break;
2637
2638 case 't':
2639 utf8_buffer[0] = byte = '\t';
2640 utf8_buffer[1] = '\0';
2641 break;
2642
2643 case 'n':
2644 utf8_buffer[0] = byte = '\n';
2645 utf8_buffer[1] = '\0';
2646 break;
2647
2648 case 'v':
2649 utf8_buffer[0] = byte = '\v';
2650 utf8_buffer[1] = '\0';
2651 break;
2652
2653 case 'f':
2654 utf8_buffer[0] = byte = '\f';
2655 utf8_buffer[1] = '\0';
2656 break;
2657
2658 case 'r':
2659 utf8_buffer[0] = byte = '\r';
2660 utf8_buffer[1] = '\0';
2661 break;
2662
2663 case 'u':
2664 utf8_buffer[0] = '\\';
2665 utf8_buffer[1] = 'u';
2666 utf8_buffer[2] = '\0';
2667 break;
2668
2669 case 'U':
2670 utf8_buffer[0] = '\\';
2671 utf8_buffer[1] = 'U';
2672 utf8_buffer[2] = '\0';
2673 break;
2674
2675 case '\\':
2676 utf8_buffer[0] = byte = '\\';
2677 utf8_buffer[1] = '\0';
2678 break;
2679 }
2680 else
2681 {
2682 if (!misc->ignore_quotes)
2683 {
2684 /* Manage double quotes. */
2685 /* """"""""""""""""""""" */
2686 if (byte == '"' && !is_squote)
2687 is_dquote = !is_dquote;
2688
2689 /* Manage single quotes. */
2690 /* """"""""""""""""""""" */
2691 if (byte == '\'' && !is_dquote)
2692 is_squote = !is_squote;
2693 }
2694 }
2695
2696 /* Only consider delimiters when outside quotations. */
2697 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2698 if ((!is_dquote && !is_squote)
2699 && ll_find(word_delims_list, utf8_buffer, buffer_cmp) != NULL)
2700 break;
2701
2702 if (!misc->ignore_quotes)
2703 {
2704 /* We no dot count the significant quotes. */
2705 /* """"""""""""""""""""""""""""""""""""""" */
2706 if (!is_special
2707 && ((byte == '"' && !is_squote) || (byte == '\'' && !is_dquote)))
2708 {
2709 is_special = 0;
2710 goto next;
2711 }
2712 }
2713
2714 /* Feed temp with the content of utf8_buffer. */
2715 /* """""""""""""""""""""""""""""""""""""""""" */
2716 while (utf8_buffer[i] != '\0')
2717 {
2718 if (utf8_count >= wordsize - 1)
2719 temp = xrealloc(temp,
2720 wordsize += (utf8_count / CHARSCHUNK + 1) * CHARSCHUNK);
2721
2722 *(temp + utf8_count++) = utf8_buffer[i];
2723 i++;
2724 }
2725
2726 is_special = 0;
2727
2728 next:
2729 byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo, misc);
2730 }
2731
2732 /* Nul-terminate the word to make it a string. */
2733 /* """"""""""""""""""""""""""""""""""""""""""" */
2734 *(temp + utf8_count) = '\0';
2735
2736 /* Replace the UTF-8 ASCII representations in the word just */
2737 /* read by their binary values. */
2738 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2739 utf8_interpret(temp, langinfo, misc->invalid_char_substitute);
2740
2741 /* Skip all field delimiters before a record delimiter. */
2742 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2743 if (ll_find(record_delims_list, utf8_buffer, buffer_cmp) == NULL)
2744 {
2745 byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo, misc);
2746
2747 while (byte != EOF
2748 && ll_find(word_delims_list, utf8_buffer, buffer_cmp) != NULL
2749 && ll_find(record_delims_list, utf8_buffer, buffer_cmp) == NULL)
2750 byte = get_bytes(input, utf8_buffer, zapped_glyphs_list, langinfo, misc);
2751
2752 if (langinfo->utf8 && utf8_get_length(utf8_buffer[0]) > 1)
2753 {
2754 size_t pos;
2755
2756 pos = strlen(utf8_buffer);
2757 while (pos > 0)
2758 my_ungetc(utf8_buffer[--pos]);
2759 }
2760 else
2761 my_ungetc(byte);
2762 }
2763
2764 /* Mark it as the last word of a record if its sequence matches a */
2765 /* record delimiter. */
2766 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2767 if (byte == EOF
2768 || ((win->col_mode || win->line_mode || win->tab_mode)
2769 && ll_find(record_delims_list, utf8_buffer, buffer_cmp) != NULL))
2770 *is_last = 1;
2771 else
2772 *is_last = 0;
2773
2774 /* Remove the ANSI color escape sequences from the word. */
2775 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2776 strip_ansi_color(temp, toggles, misc);
2777
2778 return temp;
2779 }
2780
2781 /* ================================================================ */
2782 /* Convert the 8 first colors from setf/setaf coding to setaf/setf. */
2783 /* ================================================================ */
2784 short
color_transcode(short color)2785 color_transcode(short color)
2786 {
2787 switch (color)
2788 {
2789 case 1:
2790 return 4;
2791 case 3:
2792 return 6;
2793 case 4:
2794 return 1;
2795 case 6:
2796 return 3;
2797 default:
2798 return color;
2799 }
2800 }
2801
2802 /* ========================================================== */
2803 /* Set a foreground color according to terminal capabilities. */
2804 /* ========================================================== */
2805 void
set_foreground_color(term_t * term,short color)2806 set_foreground_color(term_t * term, short color)
2807 {
2808 if (term->color_method == 0)
2809 {
2810 if (term->has_setf)
2811 tputs(TPARM2(set_foreground, color), 1, outch);
2812 if (term->has_setaf)
2813 tputs(TPARM2(set_a_foreground, color_transcode(color)), 1, outch);
2814 }
2815
2816 else if (term->color_method == 1)
2817 {
2818 if (term->has_setaf)
2819 tputs(TPARM2(set_a_foreground, color), 1, outch);
2820 if (term->has_setf)
2821 tputs(TPARM2(set_foreground, color_transcode(color)), 1, outch);
2822 }
2823 }
2824
2825 /* ========================================================== */
2826 /* Set a background color according to terminal capabilities. */
2827 /* ========================================================== */
2828 void
set_background_color(term_t * term,short color)2829 set_background_color(term_t * term, short color)
2830 {
2831 if (term->color_method == 0)
2832 {
2833 if (term->has_setb)
2834 tputs(TPARM2(set_background, color), 1, outch);
2835 if (term->has_setab)
2836 tputs(TPARM2(set_a_background, color_transcode(color)), 1, outch);
2837 }
2838
2839 else if (term->color_method == 1)
2840 {
2841 if (term->has_setab)
2842 tputs(TPARM2(set_a_background, color), 1, outch);
2843 if (term->has_setb)
2844 tputs(TPARM2(set_background, color_transcode(color)), 1, outch);
2845 }
2846 }
2847
2848 /* ======================================================= */
2849 /* Put a scrolling symbol at the first column of the line. */
2850 /* ======================================================= */
2851 void
left_margin_putp(char * s,term_t * term,win_t * win)2852 left_margin_putp(char * s, term_t * term, win_t * win)
2853 {
2854 apply_attr(term, win->shift_attr);
2855
2856 /* We won't print this symbol when not in column mode. */
2857 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2858 if (*s != '\0')
2859 fputs(s, stdout);
2860
2861 tputs(TPARM1(exit_attribute_mode), 1, outch);
2862 }
2863
2864 /* ====================================================== */
2865 /* Put a scrolling symbol at the last column of the line. */
2866 /* ====================================================== */
2867 void
right_margin_putp(char * s1,char * s2,langinfo_t * langinfo,term_t * term,win_t * win,long line,long offset)2868 right_margin_putp(char * s1, char * s2, langinfo_t * langinfo, term_t * term,
2869 win_t * win, long line, long offset)
2870 {
2871 apply_attr(term, win->bar_attr);
2872
2873 if (term->has_hpa)
2874 tputs(TPARM2(column_address, offset + win->max_width + 1), 1, outch);
2875 else if (term->has_cursor_address)
2876 tputs(TPARM3(cursor_address, term->curs_line + line - 2,
2877 offset + win->max_width + 1),
2878 1, outch);
2879 else if (term->has_parm_right_cursor)
2880 {
2881 fputc('\r', stdout);
2882 tputs(TPARM2(parm_right_cursor, offset + win->max_width + 1), 1, outch);
2883 }
2884 else
2885 {
2886 long i;
2887
2888 fputc('\r', stdout);
2889 for (i = 0; i < offset + win->max_width + 1; i++)
2890 tputs(TPARM1(cursor_right), 1, outch);
2891 }
2892
2893 if (langinfo->utf8)
2894 fputs(s1, stdout);
2895 else
2896 fputs(s2, stdout);
2897
2898 tputs(TPARM1(exit_attribute_mode), 1, outch);
2899 }
2900
2901 /* *************** */
2902 /* Core functions. */
2903 /* *************** */
2904
2905 /* ============================================================== */
2906 /* Split the lines of the message given to -m to a linked list of */
2907 /* lines. */
2908 /* Also fill the maximum screen width and the maximum number */
2909 /* of bytes of the longest line. */
2910 /* ============================================================== */
2911 void
get_message_lines(char * message,ll_t * message_lines_list,long * message_max_width,long * message_max_len)2912 get_message_lines(char * message, ll_t * message_lines_list,
2913 long * message_max_width, long * message_max_len)
2914 {
2915 char * str;
2916 char * ptr;
2917 char * cr_ptr;
2918 long n;
2919 wchar_t * w = NULL;
2920
2921 *message_max_width = 0;
2922 *message_max_len = 0;
2923 ptr = message;
2924
2925 /* For each line terminated with a EOL character. */
2926 /* """""""""""""""""""""""""""""""""""""""""""""" */
2927 while (*ptr != '\0' && (cr_ptr = strchr(ptr, '\n')) != NULL)
2928 {
2929 if (cr_ptr > ptr)
2930 {
2931 str = xmalloc(cr_ptr - ptr + 1);
2932 str[cr_ptr - ptr] = '\0';
2933 memcpy(str, ptr, cr_ptr - ptr);
2934 }
2935 else
2936 str = xstrdup("");
2937
2938 ll_append(message_lines_list, str);
2939
2940 /* If needed, update the message maximum width. */
2941 /* """""""""""""""""""""""""""""""""""""""""""" */
2942 n = wcswidth((w = utf8_strtowcs(str)), utf8_strlen(str));
2943 free(w);
2944
2945 if (n > *message_max_width)
2946 *message_max_width = n;
2947
2948 /* If needed, update the message maximum number */
2949 /* of bytes used by the longest line. */
2950 /* """""""""""""""""""""""""""""""""""""""""""" */
2951 if ((n = (long)strlen(str)) > *message_max_len)
2952 *message_max_len = n;
2953
2954 ptr = cr_ptr + 1;
2955 }
2956
2957 /* For the last line. */
2958 /* """""""""""""""""" */
2959 if (*ptr != '\0')
2960 {
2961 ll_append(message_lines_list, xstrdup(ptr));
2962
2963 n = wcswidth((w = utf8_strtowcs(ptr)), utf8_strlen(ptr));
2964 free(w);
2965
2966 if (n > *message_max_width)
2967 *message_max_width = n;
2968
2969 /* If needed, update the message maximum number */
2970 /* of bytes used by the longest line. */
2971 /* """""""""""""""""""""""""""""""""""""""""""" */
2972 if ((n = (long)strlen(ptr)) > *message_max_len)
2973 *message_max_len = n;
2974 }
2975 else
2976 ll_append(message_lines_list, xstrdup(""));
2977 }
2978
2979 /* =================================================================== */
2980 /* Set the new start and the new end of the window structure according */
2981 /* to the current cursor position. */
2982 /* =================================================================== */
2983 void
set_win_start_end(win_t * win,long current,long last)2984 set_win_start_end(win_t * win, long current, long last)
2985 {
2986 long cur_line, end_line;
2987
2988 cur_line = line_nb_of_word_a[current];
2989 if (cur_line == last)
2990 win->end = count - 1;
2991 else
2992 {
2993 /* In help mode we must not modify the windows start/end position as */
2994 /* It must be redrawn exactly as it was before. */
2995 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2996 if (!help_mode)
2997 {
2998 if (cur_line + win->max_lines / 2 + 1 <= last)
2999 win->end = first_word_in_line_a[cur_line + win->max_lines / 2 + 1] - 1;
3000 else
3001 win->end = first_word_in_line_a[last];
3002 }
3003 }
3004 end_line = line_nb_of_word_a[win->end];
3005
3006 if (end_line < win->max_lines)
3007 win->start = 0;
3008 else
3009 win->start = first_word_in_line_a[end_line - win->max_lines + 1];
3010 }
3011
3012 /* ======================================================================== */
3013 /* Set the metadata associated with a word, its starting and ending */
3014 /* position, the line in which it is put and so on. */
3015 /* Set win.start win.end and the starting and ending position of each word. */
3016 /* This function is only called initially, when resizing the terminal and */
3017 /* potentially when the search function is used. */
3018 /* ======================================================================== */
3019 long
build_metadata(term_t * term,long count,win_t * win)3020 build_metadata(term_t * term, long count, win_t * win)
3021 {
3022 long i = 0;
3023 long word_len;
3024 long len = 0;
3025 long last = 0;
3026 long word_width;
3027 long tab_count; /* Current number of words in the line, *
3028 | used in tab_mode. */
3029 wchar_t * w;
3030
3031 line_nb_of_word_a[0] = 0;
3032 first_word_in_line_a[0] = 0;
3033
3034 /* In column mode we need to calculate win->max_width, first initialize */
3035 /* it to 0 and increment it later in the loop. */
3036 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3037 if (!win->col_mode)
3038 win->max_width = 0;
3039
3040 tab_count = 0;
3041 while (i < count)
3042 {
3043 /* Determine the number of screen positions used by the word. */
3044 /* Note: mbstowcs will always succeed here as word_a[i].str */
3045 /* has already been utf8_validated/repaired. */
3046 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3047 word_len = mbstowcs(NULL, word_a[i].str, 0);
3048 word_width = wcswidth((w = utf8_strtowcs(word_a[i].str)), word_len);
3049
3050 /* Manage the case where the word is larger than the terminal width. */
3051 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3052 if (word_width >= term->ncolumns - 2)
3053 {
3054 /* Shorten the word until it fits. */
3055 /* """"""""""""""""""""""""""""""" */
3056 do
3057 {
3058 word_width = wcswidth(w, word_len--);
3059 } while (word_len > 0 && word_width >= term->ncolumns - 2);
3060 }
3061 free(w);
3062
3063 /* Look if there is enough remaining place on the line when not in */
3064 /* column mode. Force a break if the 'is_last' flag is set in all */
3065 /* modes or if we hit the max number of allowed columns in tab mode. */
3066 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3067 if ((!win->col_mode && !win->line_mode
3068 && (len + word_width + 1) >= term->ncolumns - 1)
3069 || ((win->col_mode || win->line_mode || win->tab_mode) && i > 0
3070 && word_a[i - 1].is_last)
3071 || (win->tab_mode && win->max_cols > 0 && tab_count >= win->max_cols))
3072 {
3073
3074 /* We must build another line. */
3075 /* """"""""""""""""""""""""""" */
3076 line_nb_of_word_a[i] = ++last;
3077 first_word_in_line_a[last] = i;
3078
3079 word_a[i].start = 0;
3080
3081 len = word_width + 1; /* Resets the current line length. */
3082 tab_count = 1; /* Resets the current number of words *
3083 | in the line. */
3084 word_a[i].end = word_width - 1;
3085 word_a[i].mb = word_len;
3086 }
3087 else
3088 {
3089 word_a[i].start = len;
3090 word_a[i].end = word_a[i].start + word_width - 1;
3091 word_a[i].mb = word_len;
3092 line_nb_of_word_a[i] = last;
3093
3094 len += word_width + 1; /* Increase line length. */
3095 tab_count++; /* We've seen another word in the line/ */
3096 }
3097
3098 /* If not in column mode, we need to calculate win->(real_)max_width */
3099 /* as it hasn't been already done. */
3100 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3101 if (len > win->max_width)
3102 {
3103 if (len > term->ncolumns)
3104 win->max_width = term->ncolumns - 2;
3105 else
3106 win->max_width = len;
3107 }
3108
3109 if (len > win->real_max_width)
3110 win->real_max_width = len;
3111
3112 i++;
3113 }
3114
3115 if (!win->center || win->max_width > term->ncolumns - 2)
3116 win->offset = 0;
3117 else
3118 win->offset = (term->ncolumns - 2 - win->max_width) / 2;
3119
3120 /* We need to recalculate win->start and win->end here */
3121 /* because of a possible terminal resizing. */
3122 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
3123 set_win_start_end(win, current, last);
3124
3125 return last;
3126 }
3127
3128 /* ======================================================================= */
3129 /* Helper function used by disp_word to print the cursor with the matching */
3130 /* characters of the word highlighted. */
3131 /* ======================================================================= */
3132 void
disp_cursor_word(long pos,win_t * win,term_t * term,int err)3133 disp_cursor_word(long pos, win_t * win, term_t * term, int err)
3134 {
3135 size_t i;
3136 int att_set = 0;
3137 char * p = word_a[pos].str + daccess.flength;
3138 char * np;
3139
3140 /* Set the cursor attribute. */
3141 /* """"""""""""""""""""""""" */
3142 tputs(TPARM1(exit_attribute_mode), 1, outch);
3143
3144 tputs(TPARM1(exit_attribute_mode), 1, outch);
3145 if (word_a[pos].is_tagged)
3146 apply_attr(term, win->cursor_on_tag_attr);
3147 else
3148 apply_attr(term, win->cursor_attr);
3149
3150 for (i = 0; i < word_a[pos].mb - daccess.flength; i++)
3151 {
3152 if (BIT_ISSET(word_a[pos].bitmap, i))
3153 {
3154 if (!att_set)
3155 {
3156 att_set = 1;
3157
3158 /* Set the buffer display attribute. */
3159 /* """"""""""""""""""""""""""""""""" */
3160 tputs(TPARM1(exit_attribute_mode), 1, outch);
3161 if (err)
3162 apply_attr(term, win->match_err_text_attr);
3163 else
3164 apply_attr(term, win->match_text_attr);
3165
3166 if (word_a[pos].is_tagged)
3167 apply_attr(term, win->cursor_on_tag_attr);
3168 else
3169 apply_attr(term, win->cursor_attr);
3170 }
3171 }
3172 else
3173 {
3174 if (att_set)
3175 {
3176 att_set = 0;
3177
3178 /* Set the search cursor attribute. */
3179 /* """""""""""""""""""""""""""""""" */
3180 tputs(TPARM1(exit_attribute_mode), 1, outch);
3181 if (word_a[pos].is_tagged)
3182 apply_attr(term, win->cursor_on_tag_attr);
3183 else
3184 apply_attr(term, win->cursor_attr);
3185 }
3186 }
3187 np = utf8_next(p);
3188 if (np == NULL)
3189 fputs(p, stdout);
3190 else
3191 printf("%.*s", (int)(np - p), p);
3192 p = np;
3193 }
3194 }
3195
3196 /* ========================================================== */
3197 /* Helper function used by disp_word to print a matching word */
3198 /* with the matching characters of the word highlighted. */
3199 /* ========================================================== */
3200 void
disp_matching_word(long pos,win_t * win,term_t * term,int is_current,int err)3201 disp_matching_word(long pos, win_t * win, term_t * term, int is_current,
3202 int err)
3203 {
3204 size_t i;
3205 int att_set = 0;
3206 char * p = word_a[pos].str + daccess.flength;
3207 char * np;
3208 long level = 0;
3209
3210 level = word_a[pos].special_level;
3211
3212 /* Set the search cursor attribute. */
3213 /* """""""""""""""""""""""""""""""" */
3214 tputs(TPARM1(exit_attribute_mode), 1, outch);
3215
3216 if (!is_current)
3217 {
3218 if (err)
3219 apply_attr(term, win->match_err_field_attr);
3220 else
3221 {
3222 if (level > 0)
3223 apply_attr(term, win->special_attr[level - 1]);
3224 else
3225 apply_attr(term, win->match_field_attr);
3226 }
3227 }
3228 else
3229 {
3230 if (err)
3231 apply_attr(term, win->search_err_field_attr);
3232 else
3233 apply_attr(term, win->search_field_attr);
3234 }
3235
3236 if (word_a[pos].is_tagged)
3237 apply_attr(term, win->tag_attr);
3238
3239 for (i = 0; i < word_a[pos].mb - daccess.flength; i++)
3240 {
3241 if (BIT_ISSET(word_a[pos].bitmap, i))
3242 {
3243 if (!att_set)
3244 {
3245 att_set = 1;
3246
3247 /* Set the buffer display attribute. */
3248 /* """"""""""""""""""""""""""""""""" */
3249 tputs(TPARM1(exit_attribute_mode), 1, outch);
3250 if (!is_current)
3251 {
3252 if (err)
3253 apply_attr(term, win->match_err_text_attr);
3254 else
3255 apply_attr(term, win->match_text_attr);
3256 }
3257 else
3258 apply_attr(term, win->search_text_attr);
3259
3260 if (word_a[pos].is_tagged)
3261 apply_attr(term, win->tag_attr);
3262 }
3263 }
3264 else
3265 {
3266 if (att_set)
3267 {
3268 att_set = 0;
3269
3270 /* Set the search cursor attribute. */
3271 /* """""""""""""""""""""""""""""""" */
3272 tputs(TPARM1(exit_attribute_mode), 1, outch);
3273 if (!is_current)
3274 {
3275 if (err)
3276 apply_attr(term, win->match_err_field_attr);
3277 else
3278 {
3279 if (level > 0)
3280 apply_attr(term, win->special_attr[level - 1]);
3281 else
3282 apply_attr(term, win->match_field_attr);
3283 }
3284 }
3285 else
3286 {
3287 if (err)
3288 apply_attr(term, win->search_err_field_attr);
3289 else
3290 apply_attr(term, win->search_field_attr);
3291 }
3292
3293 if (word_a[pos].is_tagged)
3294 apply_attr(term, win->tag_attr);
3295 }
3296 }
3297
3298 np = utf8_next(p);
3299 if (np == NULL)
3300 fputs(p, stdout);
3301 else
3302 printf("%.*s", (int)(np - p), p);
3303 p = np;
3304 }
3305 }
3306
3307 /* ====================================================================== */
3308 /* Display a word in, the windows. Manages the following different cases: */
3309 /* - Search mode display */
3310 /* - Cursor display */
3311 /* - Normal display */
3312 /* - Color or mono display */
3313 /* ====================================================================== */
3314 void
disp_word(long pos,search_mode_t search_mode,search_data_t * search_data,term_t * term,win_t * win,char * tmp_word)3315 disp_word(long pos, search_mode_t search_mode, search_data_t * search_data,
3316 term_t * term, win_t * win, char * tmp_word)
3317 {
3318 long s = word_a[pos].start;
3319 long e = word_a[pos].end;
3320 long p;
3321
3322 char * buffer = search_data->buf;
3323
3324 if (pos == current)
3325 {
3326 if (search_mode != NONE)
3327 {
3328 utf8_strprefix(tmp_word, word_a[pos].str, (long)word_a[pos].mb, &p);
3329 if (word_a[pos].is_numbered)
3330 {
3331 /* Set the direct access number attribute. */
3332 /* """"""""""""""""""""""""""""""""""""""" */
3333 apply_attr(term, win->daccess_attr);
3334
3335 /* And print it. */
3336 /* """"""""""""" */
3337 fputs(daccess.left, stdout);
3338 printf("%.*s", daccess.length, tmp_word + 1);
3339 fputs(daccess.right, stdout);
3340 tputs(TPARM1(exit_attribute_mode), 1, outch);
3341 fputc(' ', stdout);
3342 }
3343 else if (daccess.length > 0)
3344 {
3345 /* Prints the leading spaces. */
3346 /* """""""""""""""""""""""""" */
3347 tputs(TPARM1(exit_attribute_mode), 1, outch);
3348 printf("%.*s", daccess.flength, tmp_word);
3349 }
3350
3351 /* Set the search cursor attribute. */
3352 /* """""""""""""""""""""""""""""""" */
3353 if (search_data->fuzzy_err)
3354 apply_attr(term, win->search_err_field_attr);
3355 else
3356 apply_attr(term, win->search_field_attr);
3357
3358 /* The tab attribute must complete the attributes already set. */
3359 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3360 if (word_a[pos].is_tagged)
3361 apply_attr(term, win->tag_attr);
3362
3363 /* Print the word part. */
3364 /* """""""""""""""""""" */
3365 fputs(tmp_word + daccess.flength, stdout);
3366
3367 if (buffer[0] != '\0')
3368 {
3369 long i = 0;
3370
3371 /* Put the cursor at the beginning of the word. */
3372 /* """""""""""""""""""""""""""""""""""""""""""" */
3373 for (i = 0; i < e - s + 1 - daccess.flength; i++)
3374 tputs(TPARM1(cursor_left), 1, outch);
3375
3376 tputs(TPARM1(exit_attribute_mode), 1, outch);
3377
3378 /* Set the search cursor attribute. */
3379 /* """""""""""""""""""""""""""""""" */
3380 if (search_data->fuzzy_err)
3381 apply_attr(term, win->search_err_field_attr);
3382 else
3383 apply_attr(term, win->search_field_attr);
3384
3385 disp_matching_word(pos, win, term, 1, search_data->fuzzy_err);
3386 }
3387 }
3388 else
3389 {
3390 if (daccess.length > 0)
3391 {
3392 /* If this word is not numbered, reset the display */
3393 /* attributes before printing the leading spaces. */
3394 /* """"""""""""""""""""""""""""""""""""""""""""""" */
3395 if (!word_a[pos].is_numbered)
3396 {
3397 /* Print the non significant part of the word. */
3398 /* """"""""""""""""""""""""""""""""""""""""""" */
3399 tputs(TPARM1(exit_attribute_mode), 1, outch);
3400 printf("%.*s", daccess.flength - 1, word_a[pos].str);
3401 tputs(TPARM1(exit_attribute_mode), 1, outch);
3402 fputc(' ', stdout);
3403 }
3404 else
3405 {
3406 apply_attr(term, win->daccess_attr);
3407
3408 /* Print the non significant part of the word. */
3409 /* """"""""""""""""""""""""""""""""""""""""""" */
3410 fputs(daccess.left, stdout);
3411 printf("%.*s", daccess.length, word_a[pos].str + 1);
3412 fputs(daccess.right, stdout);
3413 tputs(TPARM1(exit_attribute_mode), 1, outch);
3414 fputc(' ', stdout);
3415 }
3416 }
3417
3418 /* If we are not in search mode, display a normal cursor. */
3419 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3420 utf8_strprefix(tmp_word, word_a[pos].str, (long)word_a[pos].mb, &p);
3421 if (word_a[pos].is_matching)
3422 disp_cursor_word(pos, win, term, search_data->fuzzy_err);
3423 else
3424 {
3425 if (word_a[pos].is_tagged)
3426 apply_attr(term, win->cursor_on_tag_attr);
3427 else
3428 apply_attr(term, win->cursor_attr);
3429
3430 fputs(tmp_word + daccess.flength, stdout);
3431 }
3432 }
3433 tputs(TPARM1(exit_attribute_mode), 1, outch);
3434 }
3435 else
3436 {
3437 /* Display a normal word without any attribute. */
3438 /* """""""""""""""""""""""""""""""""""""""""""" */
3439 utf8_strprefix(tmp_word, word_a[pos].str, (long)word_a[pos].mb, &p);
3440
3441 /* If words are numbered, emphasis their numbers. */
3442 /* """""""""""""""""""""""""""""""""""""""""""""" */
3443 if (word_a[pos].is_numbered)
3444 {
3445 apply_attr(term, win->daccess_attr);
3446
3447 fputs(daccess.left, stdout);
3448 printf("%.*s", daccess.length, tmp_word + 1);
3449 fputs(daccess.right, stdout);
3450
3451 tputs(TPARM1(exit_attribute_mode), 1, outch);
3452 fputc(' ', stdout);
3453 }
3454 else if (daccess.length > 0)
3455 {
3456 long i;
3457
3458 /* Insert leading spaces if the word is non numbered and */
3459 /* padding for all words is set. */
3460 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3461 tputs(TPARM1(exit_attribute_mode), 1, outch);
3462 if (daccess.padding == 'a')
3463 for (i = 0; i < daccess.flength; i++)
3464 fputc(' ', stdout);
3465 }
3466
3467 if (!word_a[pos].is_selectable)
3468 apply_attr(term, win->exclude_attr);
3469 else if (word_a[pos].special_level > 0)
3470 {
3471 long level = word_a[pos].special_level - 1;
3472
3473 apply_attr(term, win->special_attr[level]);
3474 }
3475 else
3476 apply_attr(term, win->include_attr);
3477
3478 if (word_a[pos].is_matching)
3479 disp_matching_word(pos, win, term, 0, search_data->fuzzy_err);
3480 else
3481 {
3482 if (word_a[pos].is_tagged)
3483 apply_attr(term, win->tag_attr);
3484
3485 if ((daccess.length > 0 && daccess.padding == 'a')
3486 || word_a[pos].is_numbered)
3487 fputs(tmp_word + daccess.flength, stdout);
3488 else
3489 fputs(tmp_word, stdout);
3490 }
3491
3492 tputs(TPARM1(exit_attribute_mode), 1, outch);
3493 }
3494 }
3495
3496 /* ======================================== */
3497 /* Display a message line above the window. */
3498 /* ======================================== */
3499 void
disp_message(ll_t * message_lines_list,long message_max_width,long message_max_len,term_t * term,win_t * win,langinfo_t * langinfo)3500 disp_message(ll_t * message_lines_list, long message_max_width,
3501 long message_max_len, term_t * term, win_t * win,
3502 langinfo_t * langinfo)
3503 {
3504 ll_node_t * node;
3505 char * line;
3506 char * buf;
3507 size_t len;
3508 long size;
3509 long offset;
3510 wchar_t * w;
3511 int n = 0; /* Counter used to display message lines. */
3512 int cut = 0; /* Will be 1 if the message is shortened. */
3513
3514 sigset_t mask;
3515
3516 win->message_lines = 0;
3517
3518 /* Do nothing if there is no message to display. */
3519 /* """"""""""""""""""""""""""""""""""""""""""""" */
3520 if (message_lines_list == NULL)
3521 return;
3522
3523 /* Recalculate the number of to-be-displayed lines in the messages */
3524 /* if space is missing. */
3525 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3526 if (term->nlines < 3)
3527 return;
3528
3529 win->message_lines = message_lines_list->len;
3530 if (win->message_lines > term->nlines - 2)
3531 {
3532 win->message_lines = term->nlines - 2;
3533 win->max_lines = term->nlines - win->message_lines - 1;
3534 cut = 1;
3535 }
3536 win->message_lines++;
3537
3538 /* Deactivate the periodic timer to prevent the interruptions to corrupt */
3539 /* screen by altering the timing of the decoding of the terminfo */
3540 /* capabilities. */
3541 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3542 sigemptyset(&mask);
3543 sigaddset(&mask, SIGALRM);
3544 sigprocmask(SIG_BLOCK, &mask, NULL);
3545
3546 node = message_lines_list->head;
3547 buf = xmalloc(message_max_len + 1);
3548
3549 /* Follow the message lines list and display each line. */
3550 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
3551 for (n = 1; n < win->message_lines; n++)
3552 {
3553 long i;
3554
3555 line = node->data;
3556 len = utf8_strlen(line);
3557 w = utf8_strtowcs(line);
3558
3559 /* Adjust size and len if the terminal is not large enough. */
3560 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3561 size = wcswidth(w, len);
3562 while (len > 0 && size > term->ncolumns)
3563 size = wcswidth(w, --len);
3564
3565 free(w);
3566
3567 /* Compute the offset from the left screen border if -M option is set. */
3568 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3569 offset = (term->ncolumns - message_max_width - 3) / 2;
3570
3571 if (win->center && offset > 0)
3572 for (i = 0; i < offset; i++)
3573 fputc(' ', stdout);
3574
3575 apply_attr(term, win->message_attr);
3576
3577 /* Only print the start of a line if the screen width if too small. */
3578 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3579 utf8_strprefix(buf, line, len, &size);
3580
3581 /* Print the line without the ending \n. */
3582 /* ''''''''''''''''''''''''''''''''''''' */
3583 if (n > 1 && cut && n == win->message_lines - 1)
3584 {
3585 if (langinfo->utf8)
3586 fputs(msg_arr_down, stdout);
3587 else
3588 fputc('v', stdout);
3589 }
3590 else
3591 printf("%s", buf);
3592
3593 /* Complete the short line with spaces until it reach the */
3594 /* message max size. */
3595 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3596 for (i = size; i < message_max_width; i++)
3597 {
3598 if (i + (offset < 0 ? 0 : offset) >= term->ncolumns)
3599 break;
3600 fputc(' ', stdout);
3601 }
3602
3603 /* Drop the attributes and print a \n. */
3604 /* ''''''''''''''''''''''''''''''''''' */
3605 if (term->nlines > 2)
3606 {
3607 tputs(TPARM1(exit_attribute_mode), 1, outch);
3608 puts("");
3609 }
3610
3611 node = node->next;
3612 }
3613
3614 /* Add an empty line without attribute to separate the menu title */
3615 /* and the menu content. */
3616 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3617 puts("");
3618
3619 free(buf);
3620
3621 /* Re-enable the periodic timer. */
3622 /* """"""""""""""""""""""""""""" */
3623 sigprocmask(SIG_UNBLOCK, &mask, NULL);
3624 }
3625
3626 /* ============================= */
3627 /* Display the selection window. */
3628 /* ============================= */
3629 long
disp_lines(win_t * win,toggle_t * toggles,long current,long count,search_mode_t search_mode,search_data_t * search_data,term_t * term,long last_line,char * tmp_word,langinfo_t * langinfo)3630 disp_lines(win_t * win, toggle_t * toggles, long current, long count,
3631 search_mode_t search_mode, search_data_t * search_data,
3632 term_t * term, long last_line, char * tmp_word,
3633 langinfo_t * langinfo)
3634 {
3635 long lines_disp;
3636 long i;
3637 char scroll_symbol[5];
3638 long len;
3639 long display_bar;
3640
3641 sigset_t mask;
3642
3643 /* Disable the periodic timer to prevent the interruptions to corrupt */
3644 /* screen by altering the timing of the decoding of the terminfo */
3645 /* capabilities. */
3646 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3647 sigemptyset(&mask);
3648 sigaddset(&mask, SIGALRM);
3649 sigprocmask(SIG_BLOCK, &mask, NULL);
3650
3651 scroll_symbol[0] = ' ';
3652 scroll_symbol[1] = '\0';
3653
3654 lines_disp = 1;
3655
3656 tputs(TPARM1(save_cursor), 1, outch);
3657
3658 i = win->start;
3659
3660 /* Modify the max number of displayed lines if we do not have */
3661 /* enough place. */
3662 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3663 if (win->max_lines > term->nlines - win->message_lines)
3664 win->max_lines = term->nlines - win->message_lines;
3665
3666 if (last_line >= win->max_lines)
3667 display_bar = 1;
3668 else
3669 display_bar = 0;
3670
3671 if (win->col_mode || win->line_mode)
3672 len = term->ncolumns - 3;
3673 else
3674 len = term->ncolumns - 2;
3675
3676 /* If in column mode and the sum of the columns sizes + gutters is */
3677 /* greater than the terminal width, then prepend a space to be able to */
3678 /* display the left arrow indicating that the first displayed column */
3679 /* is not the first one. */
3680 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3681 if (len > 1
3682 && ((win->col_mode || win->line_mode)
3683 && win->real_max_width > term->ncolumns - 2))
3684 {
3685 if (win->first_column > 0)
3686 {
3687 if (langinfo->utf8)
3688 strcpy(scroll_symbol, shift_left_sym);
3689 else
3690 strcpy(scroll_symbol, "<");
3691 }
3692 }
3693 else
3694 scroll_symbol[0] = '\0';
3695
3696 /* Center the display ? */
3697 /* """""""""""""""""""" */
3698 if (win->offset > 0)
3699 {
3700 long i;
3701 for (i = 0; i < win->offset; i++)
3702 fputc(' ', stdout);
3703 }
3704
3705 left_margin_putp(scroll_symbol, term, win);
3706 while (len > 1 && i <= count - 1)
3707 {
3708 /* Display one word and the space or symbol following it. */
3709 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3710 if (word_a[i].start >= win->first_column
3711 && word_a[i].end < len + win->first_column)
3712 {
3713 disp_word(i, search_mode, search_data, term, win, tmp_word);
3714
3715 /* If there are more element to be displayed after the right margin. */
3716 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3717 if ((win->col_mode || win->line_mode) && i < count - 1
3718 && word_a[i + 1].end >= len + win->first_column)
3719 {
3720 apply_attr(term, win->shift_attr);
3721
3722 if (langinfo->utf8)
3723 fputs(shift_right_sym, stdout);
3724 else
3725 fputc('>', stdout);
3726
3727 tputs(TPARM1(exit_attribute_mode), 1, outch);
3728 }
3729
3730 /* If we want to display the gutter. */
3731 /* """"""""""""""""""""""""""""""""" */
3732 else if (!word_a[i].is_last && win->col_sep
3733 && (win->tab_mode || win->col_mode))
3734 {
3735 long pos;
3736
3737 /* Make sure that we are using the right gutter character even */
3738 /* if the first displayed word is * not the first of its line. */
3739 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3740 pos = i - first_word_in_line_a[line_nb_of_word_a[i]];
3741
3742 if (pos >= win->gutter_nb) /* Use the last gutter character. */
3743 fputs(win->gutter_a[win->gutter_nb - 1], stdout);
3744 else
3745 fputs(win->gutter_a[pos], stdout);
3746 }
3747 else
3748 /* Else just display a space. */
3749 /* """""""""""""""""""""""""" */
3750 fputc(' ', stdout);
3751 }
3752
3753 /* Mark the line as the current line, the line containing the cursor. */
3754 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3755 if (i == current)
3756 win->cur_line = lines_disp;
3757
3758 /* Check if we must start a new line. */
3759 /* """""""""""""""""""""""""""""""""" */
3760 if (i == count - 1 || word_a[i + 1].start == 0)
3761 {
3762 tputs(TPARM1(clr_eol), 1, outch);
3763 if (lines_disp < win->max_lines)
3764 {
3765 /* If we have more than one line to display. */
3766 /* """"""""""""""""""""""""""""""""""""""""" */
3767 if (display_bar && !toggles->no_scrollbar
3768 && (lines_disp > 1 || i < count - 1))
3769 {
3770 /* Display the next element of the scrollbar. */
3771 /* """""""""""""""""""""""""""""""""""""""""" */
3772 if (line_nb_of_word_a[i] == 0)
3773 {
3774 if (win->max_lines > 1)
3775 right_margin_putp(sbar_top, "\\", langinfo, term, win, lines_disp,
3776 win->offset);
3777 else
3778 right_margin_putp(sbar_arr_down, "^", langinfo, term, win,
3779 lines_disp, win->offset);
3780 }
3781 else if (lines_disp == 1)
3782 right_margin_putp(sbar_arr_up, "^", langinfo, term, win, lines_disp,
3783 win->offset);
3784 else if (line_nb_of_word_a[i] == last_line)
3785 {
3786 if (win->max_lines > 1)
3787 right_margin_putp(sbar_down, "/", langinfo, term, win, lines_disp,
3788 win->offset);
3789 else
3790 right_margin_putp(sbar_arr_up, "^", langinfo, term, win,
3791 lines_disp, win->offset);
3792 }
3793 else if (last_line + 1 > win->max_lines
3794 && (long)((float)(line_nb_of_word_a[current])
3795 / (last_line + 1) * (win->max_lines - 2)
3796 + 2)
3797 == lines_disp)
3798 right_margin_putp(sbar_curs, "+", langinfo, term, win, lines_disp,
3799 win->offset);
3800 else
3801 right_margin_putp(sbar_line, "|", langinfo, term, win, lines_disp,
3802 win->offset);
3803 }
3804
3805 /* Print a newline character if we are not at the end of */
3806 /* the input nor at the end of the window. */
3807 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3808 if (i < count - 1 && lines_disp < win->max_lines)
3809 {
3810 fputc('\n', stdout);
3811
3812 if (win->offset > 0)
3813 {
3814 long i;
3815 for (i = 0; i < win->offset; i++)
3816 fputc(' ', stdout);
3817 }
3818
3819 left_margin_putp(scroll_symbol, term, win);
3820 }
3821
3822 /* We do not increment the number of lines seen after */
3823 /* a premature end of input. */
3824 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
3825 if (i < count - 1)
3826 lines_disp++;
3827
3828 if (win->max_lines == 1)
3829 break;
3830 }
3831 else if (i <= count - 1 && lines_disp == win->max_lines)
3832 {
3833 /* The last line of the window has been displayed. */
3834 /* """"""""""""""""""""""""""""""""""""""""""""""" */
3835 if (display_bar && line_nb_of_word_a[i] == last_line)
3836 {
3837 if (!toggles->no_scrollbar)
3838 {
3839 if (win->max_lines > 1)
3840 right_margin_putp(sbar_down, "/", langinfo, term, win, lines_disp,
3841 win->offset);
3842 else
3843 right_margin_putp(sbar_arr_up, "^", langinfo, term, win,
3844 lines_disp, win->offset);
3845 }
3846 }
3847 else
3848 {
3849 if (display_bar && !toggles->no_scrollbar)
3850 right_margin_putp(sbar_arr_down, "v", langinfo, term, win,
3851 lines_disp, win->offset);
3852 break;
3853 }
3854 }
3855 else
3856 /* These lines were not in the widows and so we have nothing to do. */
3857 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3858 break;
3859 }
3860
3861 /* Next word. */
3862 /* """""""""" */
3863 i++;
3864 }
3865
3866 /* Update win->end, this is necessary because we only */
3867 /* call build_metadata on start and on terminal resize. */
3868 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
3869 if (i == count)
3870 win->end = i - 1;
3871 else
3872 win->end = i;
3873 /* We restore the cursor position saved before the display of the window. */
3874 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3875 tputs(TPARM1(restore_cursor), 1, outch);
3876
3877 /* Re-enable the periodic timer. */
3878 /* """"""""""""""""""""""""""""" */
3879 sigprocmask(SIG_UNBLOCK, &mask, NULL);
3880
3881 return lines_disp;
3882 }
3883
3884 /* ============================================= */
3885 /* Signal handler. Manages SIGWINCH and SIGALRM. */
3886 /* ============================================= */
3887 void
sig_handler(int s)3888 sig_handler(int s)
3889 {
3890 switch (s)
3891 {
3892 /* Standard termination signals. */
3893 /* """"""""""""""""""""""""""""" */
3894 case SIGSEGV:
3895 got_sigsegv = 1;
3896 break;
3897
3898 case SIGTERM:
3899 got_sigterm = 1;
3900 break;
3901
3902 case SIGHUP:
3903 got_sighup = 1;
3904 break;
3905
3906 /* Terminal resize. */
3907 /* """""""""""""""" */
3908 case SIGWINCH:
3909 got_winch = 1;
3910 break;
3911
3912 /* Alarm triggered, This signal is used by the search mechanism to */
3913 /* forces a window refresh. */
3914 /* The help mechanism uses it to clear the message */
3915 /* It is also used to redisplay the window after the end of a terminal */
3916 /* resizing. */
3917 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3918 case SIGALRM:
3919 if (timeout.initial_value > 0)
3920 got_timeout_tick = 1;
3921
3922 if (help_timer > 0)
3923 help_timer--;
3924
3925 if (help_timer == 0 && help_mode)
3926 got_help_alrm = 1;
3927
3928 if (daccess_timer > 0)
3929 daccess_timer--;
3930
3931 if (daccess_timer == 0)
3932 got_daccess_alrm = 1;
3933
3934 if (winch_timer > 0)
3935 winch_timer--;
3936
3937 if (winch_timer == 0)
3938 {
3939 got_winch = 0;
3940 got_help_alrm = 0;
3941 got_winch_alrm = 1;
3942 }
3943
3944 if (search_timer > 0)
3945 search_timer--;
3946
3947 if (search_timer == 0 && search_mode != NONE)
3948 got_search_alrm = 1;
3949
3950 break;
3951 }
3952 }
3953
3954 /* ========================================================= */
3955 /* Set new first column to display when horizontal scrolling */
3956 /* Alter win->first_column. */
3957 /* ========================================================= */
3958 void
set_new_first_column(win_t * win,term_t * term)3959 set_new_first_column(win_t * win, term_t * term)
3960 {
3961 long pos;
3962
3963 if (word_a[current].start < win->first_column)
3964 {
3965 pos = current;
3966
3967 while (win->first_column > 0 && word_a[current].start < win->first_column)
3968 {
3969 win->first_column = word_a[pos].start;
3970 pos--;
3971 }
3972 }
3973 else if (word_a[current].end - win->first_column >= term->ncolumns - 3)
3974 {
3975 pos = first_word_in_line_a[line_nb_of_word_a[current]];
3976
3977 while (!word_a[pos].is_last
3978 && word_a[current].end - win->first_column >= term->ncolumns - 3)
3979 {
3980 pos++;
3981 win->first_column = word_a[pos].start;
3982 }
3983 }
3984 }
3985
3986 /* ===================================================== */
3987 /* Restrict the matches to word ending with the pattern. */
3988 /* ===================================================== */
3989 void
select_ending_matches(win_t * win,term_t * term,search_data_t * search_data,long * last_line)3990 select_ending_matches(win_t * win, term_t * term, search_data_t * search_data,
3991 long * last_line)
3992 {
3993 if (matches_count > 0)
3994 {
3995 long i;
3996 long j = 0;
3997 long index;
3998 long nb;
3999 long * tmp;
4000 char * ptr;
4001 char * last_glyph;
4002 int utf8_len;
4003
4004 /* Creation of an alternate array which will */
4005 /* contain only the candidates having potentially */
4006 /* an ending pattern, if this array becomes non */
4007 /* empty then it will replace the original array. */
4008 /* """""""""""""""""""""""""""""""""""""""""""""" */
4009 alt_matching_words_a = xrealloc(alt_matching_words_a,
4010 matches_count * (sizeof(long)));
4011
4012 for (i = 0; i < matches_count; i++)
4013 {
4014 index = matching_words_a[i];
4015 char * str = word_a[index].str;
4016
4017 /* count the trailing blanks non counted in the bitmap. */
4018 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
4019 ptr = str + strlen(str);
4020
4021 nb = 0;
4022 while ((ptr = utf8_prev(str, ptr)) != NULL && isblank(*ptr))
4023 if (ptr - str > 0)
4024 nb++;
4025 else
4026 break;
4027
4028 /* Check the bit corresponding to the last non blank glyph */
4029 /* If set we add the index to an alternate array, if not we */
4030 /* clear the bitmap of the corresponding word. */
4031 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4032 if (BIT_ISSET(word_a[index].bitmap,
4033 word_a[index].mb - nb - daccess.flength - 1))
4034 alt_matching_words_a[j++] = index;
4035 else
4036 {
4037 /* Look if the end of the word potentially contain an */
4038 /* ending pattern. */
4039 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
4040 if (search_mode == FUZZY)
4041 {
4042 utf8_len = mblen(ptr, 4);
4043 last_glyph = search_data->buf + search_data->len - utf8_len;
4044
4045 /* in fuzzy search mode we only look the last glyph. */
4046 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4047 if (memcmp(ptr, last_glyph, utf8_len) == 0)
4048 alt_matching_words_a[j++] = index;
4049 else
4050 memset(word_a[index].bitmap, '\0',
4051 (word_a[index].mb - daccess.flength) / CHAR_BIT + 1);
4052 }
4053 else
4054 {
4055 /* in not fuzzy search mode use all the pattern. */
4056 /* """"""""""""""""""""""""""""""""""""""""""""" */
4057 for (nb = 0; nb < search_data->utf8_len - 1; nb++)
4058 ptr = utf8_prev(str, ptr);
4059 if (memcmp(ptr, search_data->buf, search_data->len) == 0)
4060 alt_matching_words_a[j++] = index;
4061 else
4062 memset(word_a[index].bitmap, '\0',
4063 (word_a[index].mb - daccess.flength) / CHAR_BIT + 1);
4064 }
4065 }
4066 }
4067
4068 /* Swap the normal and alt array. */
4069 /* """""""""""""""""""""""""""""" */
4070 matches_count = j;
4071 matching_words_a_size = j;
4072
4073 tmp = matching_words_a;
4074 matching_words_a = alt_matching_words_a;
4075 alt_matching_words_a = tmp;
4076
4077 if (j > 0)
4078 {
4079 /* Adjust the bitmap to the ending version. */
4080 /* """""""""""""""""""""""""""""""""""""""" */
4081 update_bitmaps(search_mode, search_data, END_AFFINITY);
4082
4083 current = matching_words_a[0];
4084
4085 if (current < win->start || current > win->end)
4086 *last_line = build_metadata(term, count, win);
4087
4088 /* Set new first column to display. */
4089 /* """""""""""""""""""""""""""""""" */
4090 set_new_first_column(win, term);
4091 }
4092 }
4093 }
4094
4095 /* ======================================================= */
4096 /* Restrict the matches to word starting with the pattern. */
4097 /* ======================================================= */
4098 void
select_starting_matches(win_t * win,term_t * term,search_data_t * search_data,long * last_line)4099 select_starting_matches(win_t * win, term_t * term, search_data_t * search_data,
4100 long * last_line)
4101 {
4102 if (matches_count > 0)
4103 {
4104 long i;
4105 long j = 0;
4106 long index;
4107 long nb;
4108 long * tmp;
4109 long pos;
4110 char * first_glyph;
4111 int utf8_len;
4112
4113 alt_matching_words_a = xrealloc(alt_matching_words_a,
4114 matches_count * (sizeof(long)));
4115
4116 first_glyph = xmalloc(5);
4117
4118 for (i = 0; i < matches_count; i++)
4119 {
4120 index = matching_words_a[i];
4121
4122 for (nb = 0; nb < word_a[index].mb; nb++)
4123 if (!isblank(*(word_a[index].str + daccess.flength + nb)))
4124 break;
4125
4126 if (BIT_ISSET(word_a[index].bitmap, nb))
4127 alt_matching_words_a[j++] = index;
4128 else
4129 {
4130
4131 if (search_mode == FUZZY)
4132 {
4133 first_glyph = utf8_strprefix(first_glyph,
4134 word_a[index].str + nb + daccess.flength,
4135 1, &pos);
4136 utf8_len = pos;
4137
4138 /* in fuzzy search mode we only look the first glyph. */
4139 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
4140 if (memcmp(search_data->buf, first_glyph, utf8_len) == 0)
4141 alt_matching_words_a[j++] = index;
4142 else
4143 memset(word_a[index].bitmap, '\0',
4144 (word_a[index].mb + nb - daccess.flength) / CHAR_BIT + 1);
4145 }
4146 else
4147 {
4148 /* in not fuzzy search mode use all the pattern. */
4149 /* """"""""""""""""""""""""""""""""""""""""""""" */
4150 if (memcmp(search_data->buf, word_a[index].str + nb,
4151 search_data->len - nb)
4152 == 0)
4153 alt_matching_words_a[j++] = index;
4154 else
4155 memset(word_a[index].bitmap, '\0',
4156 (word_a[index].mb + nb - daccess.flength) / CHAR_BIT + 1);
4157 }
4158 }
4159 }
4160
4161 free(first_glyph);
4162
4163 matches_count = j;
4164 matching_words_a_size = j;
4165
4166 tmp = matching_words_a;
4167 matching_words_a = alt_matching_words_a;
4168 alt_matching_words_a = tmp;
4169
4170 if (j > 0)
4171 {
4172 /* Adjust the bitmap to the ending version. */
4173 /* """""""""""""""""""""""""""""""""""""""" */
4174 update_bitmaps(search_mode, search_data, START_AFFINITY);
4175
4176 current = matching_words_a[0];
4177
4178 if (current < win->start || current > win->end)
4179 *last_line = build_metadata(term, count, win);
4180
4181 /* Set new first column to display. */
4182 /* """""""""""""""""""""""""""""""" */
4183 set_new_first_column(win, term);
4184 }
4185 }
4186 }
4187
4188 /* ====================== */
4189 /* Moves the cursor left. */
4190 /* ====================== */
4191 void
move_left(win_t * win,term_t * term,toggle_t * toggles,search_data_t * search_data,langinfo_t * langinfo,long * nl,long last_line,char * tmp_word)4192 move_left(win_t * win, term_t * term, toggle_t * toggles,
4193 search_data_t * search_data, langinfo_t * langinfo, long * nl,
4194 long last_line, char * tmp_word)
4195 {
4196 long old_current = current;
4197 long old_start = win->start;
4198 long old_first_column = win->first_column;
4199 long wi; /* Word index. */
4200
4201 do
4202 {
4203 if (current > 0)
4204 {
4205 /* Sets the new win->start and win->end if the cursor */
4206 /* is at the beginning of the windows. */
4207 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
4208 if (current == win->start)
4209 if (win->start > 0)
4210 {
4211 for (wi = win->start - 1; wi >= 0 && word_a[wi].start != 0; wi--)
4212 {
4213 }
4214 win->start = wi;
4215
4216 if (word_a[wi].str != NULL)
4217 win->start = wi;
4218
4219 if (win->end < count - 1)
4220 {
4221 for (wi = win->end + 2; wi < count - 1 && word_a[wi].start != 0;
4222 wi++)
4223 {
4224 }
4225 if (word_a[wi].str != NULL)
4226 win->end = wi;
4227 }
4228 }
4229
4230 /* In column mode we need to take care of the */
4231 /* horizontal scrolling. */
4232 /* """""""""""""""""""""""""""""""""""""""""" */
4233 if (win->col_mode || win->line_mode)
4234 {
4235 long pos;
4236
4237 if (word_a[current].start == 0)
4238 {
4239 long len;
4240
4241 len = term->ncolumns - 3;
4242 pos = first_word_in_line_a[line_nb_of_word_a[current - 1]];
4243
4244 while (word_a[current - 1].end - win->first_column >= len)
4245 {
4246 win->first_column += word_a[pos].end - word_a[pos].start + 2;
4247
4248 pos++;
4249 }
4250 }
4251 else if (word_a[current - 1].start < win->first_column)
4252 win->first_column = word_a[current - 1].start;
4253 }
4254 current--;
4255 }
4256 else
4257 break;
4258 } while (current != old_current && !word_a[current].is_selectable);
4259
4260 /* The old settings need to be restored if the */
4261 /* new current word is not selectable. */
4262 /* """"""""""""""""""""""""""""""""""""""""""" */
4263 if (!word_a[current].is_selectable)
4264 {
4265 current = old_current;
4266 win->start = old_start;
4267 if (win->col_mode || win->line_mode)
4268 win->first_column = old_first_column;
4269 }
4270
4271 if (current != old_current)
4272 *nl = disp_lines(win, toggles, current, count, search_mode, search_data,
4273 term, last_line, tmp_word, langinfo);
4274 }
4275
4276 /* ======================= */
4277 /* Moves the cursor right. */
4278 /* ======================= */
4279 void
move_right(win_t * win,term_t * term,toggle_t * toggles,search_data_t * search_data,langinfo_t * langinfo,long * nl,long last_line,char * tmp_word)4280 move_right(win_t * win, term_t * term, toggle_t * toggles,
4281 search_data_t * search_data, langinfo_t * langinfo, long * nl,
4282 long last_line, char * tmp_word)
4283 {
4284 long old_current = current;
4285 long old_start = win->start;
4286 long old_first_column = win->first_column;
4287 long wi; /* word index */
4288
4289 do
4290 {
4291 if (current < count - 1)
4292 {
4293 /* Sets the new win->start and win->end if the cursor */
4294 /* is at the end of the windows. */
4295 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
4296 if (current == win->end)
4297 if (win->start < count - 1 && win->end != count - 1)
4298 {
4299 for (wi = win->start + 1; wi < count - 1 && word_a[wi].start != 0;
4300 wi++)
4301 {
4302 }
4303
4304 if (word_a[wi].str != NULL)
4305 win->start = wi;
4306
4307 if (win->end < count - 1)
4308 {
4309 for (wi = win->end + 2; wi < count - 1 && word_a[wi].start != 0;
4310 wi++)
4311 {
4312 }
4313 if (word_a[wi].str != NULL)
4314 win->end = wi;
4315 }
4316 }
4317
4318 /* In column mode we need to take care of the */
4319 /* horizontal scrolling. */
4320 /* """""""""""""""""""""""""""""""""""""""""" */
4321 if (win->col_mode || win->line_mode)
4322 {
4323 if (word_a[current].is_last)
4324 win->first_column = 0;
4325 else
4326 {
4327 long pos;
4328 long len;
4329
4330 len = term->ncolumns - 3;
4331
4332 if (word_a[current + 1].end >= len + win->first_column)
4333 {
4334 /* Find the first word to be displayed in this line. */
4335 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4336 pos = first_word_in_line_a[line_nb_of_word_a[current]];
4337
4338 while (word_a[pos].start <= win->first_column)
4339 pos++;
4340
4341 pos--;
4342
4343 /* If the new current word cannot be displayed, search */
4344 /* the first word in the line that can be displayed by */
4345 /* iterating on pos. */
4346 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
4347 while (word_a[current + 1].end - word_a[pos].start >= len)
4348 pos++;
4349
4350 if (word_a[pos].start > 0)
4351 win->first_column = word_a[pos].start;
4352 }
4353 }
4354 }
4355 current++;
4356 }
4357 else
4358 break;
4359 } while (current != old_current && !word_a[current].is_selectable);
4360
4361 /* The old settings need to be restored if the */
4362 /* new current word is not selectable. */
4363 /* """"""""""""""""""""""""""""""""""""""""""" */
4364 if (!word_a[current].is_selectable)
4365 {
4366 current = old_current;
4367 win->start = old_start;
4368 if (win->col_mode || win->line_mode)
4369 win->first_column = old_first_column;
4370 }
4371
4372 if (current != old_current)
4373 *nl = disp_lines(win, toggles, current, count, search_mode, search_data,
4374 term, last_line, tmp_word, langinfo);
4375 }
4376
4377 /* ================================================================== */
4378 /* Get the last word of a line after it has been formed to fit in the */
4379 /* terminal. */
4380 /* ================================================================== */
4381 long
get_line_last_word(long line,long last_line)4382 get_line_last_word(long line, long last_line)
4383 {
4384 if (line == last_line)
4385 return count - 1;
4386 else
4387 return first_word_in_line_a[line + 1] - 1;
4388 }
4389
4390 /* ==================================================================== */
4391 /* Try to locate the best word in the target line when trying to move */
4392 /* the cursor upward. */
4393 /* returns 1 if a word has been found else 0. */
4394 /* This function has the side effect to potentially change the value of */
4395 /* the variable 'current' if an adequate word is found. */
4396 /* ==================================================================== */
4397 int
find_best_word_upward(long last_word,long s,long e)4398 find_best_word_upward(long last_word, long s, long e)
4399 {
4400 int found = 0;
4401 long index;
4402 long cursor;
4403
4404 /* Look for the first word whose start position in the line is */
4405 /* less or equal to the source word starting position. */
4406 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4407 cursor = last_word;
4408 while (word_a[cursor].start > s)
4409 cursor--;
4410
4411 /* In case no word is eligible, keep the cursor on */
4412 /* the last word. */
4413 /* """"""""""""""""""""""""""""""""""""""""""""""" */
4414 if (cursor == last_word && word_a[cursor].start > 0)
4415 cursor--;
4416
4417 /* Try to guess the best choice if we have multiple choices. */
4418 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4419 if (word_a[cursor].end >= s
4420 && word_a[cursor].end - s >= e - word_a[cursor + 1].start)
4421 current = cursor;
4422 else
4423 {
4424 if (cursor < last_word)
4425 current = cursor + 1;
4426 else
4427 current = cursor;
4428 }
4429
4430 /* If the word is not selectable, try to find a selectable word */
4431 /* in the line. */
4432 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4433 if (!word_a[current].is_selectable)
4434 {
4435 index = 0;
4436 while (word_a[current - index].start > 0
4437 && !word_a[current - index].is_selectable)
4438 index++;
4439
4440 if (word_a[current - index].is_selectable)
4441 {
4442 current -= index;
4443 found = 1;
4444 }
4445 else
4446 {
4447 index = 0;
4448 while (current + index < last_word
4449 && !word_a[current + index].is_selectable)
4450 index++;
4451
4452 if (word_a[current + index].is_selectable)
4453 {
4454 current += index;
4455 found = 1;
4456 }
4457 }
4458 }
4459 else
4460 found = 1;
4461
4462 return found;
4463 }
4464
4465 /* ==================================================================== */
4466 /* Try to locate the best word in the target line when trying to move */
4467 /* the cursor downward. */
4468 /* returns 1 if a word has been found else 0. */
4469 /* This function has the side effect to potentially change the value of */
4470 /* the variable 'current' if an adequate word is found. */
4471 /* ==================================================================== */
4472 int
find_best_word_downward(long last_word,long s,long e)4473 find_best_word_downward(long last_word, long s, long e)
4474 {
4475 int found = 0;
4476 long index;
4477 long cursor;
4478
4479 /* Look for the first word whose start position in the line is */
4480 /* less or equal than the source word starting position. */
4481 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4482 cursor = last_word;
4483 while (word_a[cursor].start > s)
4484 cursor--;
4485
4486 /* In case no word is eligible, keep the cursor on */
4487 /* the last word. */
4488 /* """"""""""""""""""""""""""""""""""""""""""""""" */
4489 if (cursor == last_word && word_a[cursor].start > 0)
4490 cursor--;
4491
4492 /* Try to guess the best choice if we have multiple choices. */
4493 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4494 if (cursor < count - 1
4495 && word_a[cursor].end - s >= e - word_a[cursor + 1].start)
4496 current = cursor;
4497 else
4498 {
4499 if (cursor < count - 1)
4500 {
4501 if (cursor < last_word)
4502 current = cursor + 1;
4503 else
4504 current = cursor;
4505 }
4506 else
4507 current = count - 1;
4508 }
4509
4510 /* If the word is not selectable, try to find a selectable word */
4511 /* in ts line. */
4512 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4513 if (!word_a[current].is_selectable)
4514 {
4515 index = 0;
4516 while (word_a[current - index].start > 0
4517 && !word_a[current - index].is_selectable)
4518 index++;
4519
4520 if (word_a[current - index].is_selectable)
4521 {
4522 current -= index;
4523 found = 1;
4524 }
4525 else
4526 {
4527 index = 0;
4528 while (current + index < last_word
4529 && !word_a[current + index].is_selectable)
4530 index++;
4531
4532 if (word_a[current + index].is_selectable)
4533 {
4534 current += index;
4535 found = 1;
4536 }
4537 }
4538 }
4539 else
4540 found = 1;
4541
4542 return found;
4543 }
4544
4545 /* ==================== */
4546 /* Moves the cursor up. */
4547 /* ==================== */
4548 void
move_up(win_t * win,term_t * term,toggle_t * toggles,search_data_t * search_data,langinfo_t * langinfo,long * nl,long page,long first_selectable,long last_line,char * tmp_word)4549 move_up(win_t * win, term_t * term, toggle_t * toggles,
4550 search_data_t * search_data, langinfo_t * langinfo, long * nl,
4551 long page, long first_selectable, long last_line, char * tmp_word)
4552 {
4553 long line; /* The line being processed (target line). */
4554 long start_line; /* The first line of the window. */
4555 long cur_line; /* The line of the cursor. */
4556 long nlines; /* Number of line in the window. */
4557 long first_selectable_line; /* the line containing the first *
4558 | selectable word. */
4559 long lines_skipped; /* The number of line between the target line and the *
4560 | first line containing a selectable word in case of *
4561 | exclusions. */
4562 long last_word; /* The last word on the target line. */
4563 long s, e; /* Starting and ending terminal position of a word. */
4564 int found; /* 1 if a line could be fond else 0. */
4565
4566 /* Store the initial starting and ending positions of */
4567 /* the word under the cursor. */
4568 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
4569 s = word_a[current].start;
4570 e = word_a[current].end;
4571
4572 /* Identify the line number of the first window's line */
4573 /* and the line number of the current line. */
4574 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
4575 start_line = line_nb_of_word_a[win->start];
4576 cur_line = line_nb_of_word_a[current];
4577 first_selectable_line = line_nb_of_word_a[first_selectable];
4578 lines_skipped = 0;
4579 found = 0;
4580 nlines = win->max_lines < last_line + 1 ? win->max_lines : last_line + 1;
4581
4582 /* initialise the target line. */
4583 /* """"""""""""""""""""""""""" */
4584 line = cur_line;
4585
4586 /* Special case if the cursor is already in the line containing the */
4587 /* first selectable word. */
4588 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4589 if (line == first_selectable_line)
4590 {
4591 /* we can't move the cursor up but we still can try to show the */
4592 /* more non selectable words as we can. */
4593 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4594 if (line <= start_line + nlines - 1 - page)
4595 {
4596 /* We are scrolling one line at a time and the cursor is not in */
4597 /* the last line of the window. */
4598 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4599 if (start_line - page > 0)
4600 /* There is enough remaining line to fill a window. */
4601 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
4602 start_line -= page;
4603 else
4604 /* We cannot scroll further. */
4605 /* ''''''''''''''''''''''''' */
4606 start_line = 0;
4607 }
4608 else
4609 {
4610 /* The cursor is already in the last line of the windows. */
4611 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4612 if (line >= nlines)
4613 /* There is enough remaining line to fill a window. */
4614 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
4615 start_line = line - nlines + 1;
4616 else
4617 /* We cannot scroll further. */
4618 /* ''''''''''''''''''''''''' */
4619 start_line = 0;
4620 }
4621 }
4622 else
4623 {
4624 if (line - page < 0)
4625 {
4626 /* Trivial case, we are already on the first page */
4627 /* just jump to the first selectable line. */
4628 /* """""""""""""""""""""""""""""""""""""""""""""" */
4629 line = first_selectable_line;
4630 last_word = get_line_last_word(line, last_line);
4631 find_best_word_upward(last_word, s, e);
4632 }
4633 else
4634 {
4635 /* Temporarily move up one page. */
4636 /* """"""""""""""""""""""""""""" */
4637 line -= page;
4638
4639 /* The target line cannot be before the line containing the first */
4640 /* selectable word. */
4641 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4642 if (line < first_selectable_line)
4643 {
4644 line = first_selectable_line;
4645 last_word = get_line_last_word(line, last_line);
4646 find_best_word_upward(last_word, s, e);
4647 }
4648 else
4649 {
4650 /* If this is not the case, search upward for the line with a */
4651 /* selectable word. This line is guaranteed to exist. */
4652 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4653 while (line >= first_selectable_line)
4654 {
4655 last_word = get_line_last_word(line, last_line);
4656
4657 if (find_best_word_upward(last_word, s, e))
4658 {
4659 found = 1;
4660 break;
4661 }
4662
4663 line--;
4664 lines_skipped++;
4665 }
4666 }
4667 }
4668 }
4669
4670 /* Look if we need to adjust the window to follow the cursor. */
4671 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4672 if (!found && start_line - page >= 0)
4673 {
4674 /* We are on the first line containing a selectable word and */
4675 /* There is enough place to scroll up a page but only scrolls */
4676 /* up if the cursor remains in the window. */
4677 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4678 if (start_line + nlines - line > page)
4679 start_line -= page;
4680 }
4681 else if (line < start_line)
4682 {
4683 /* The target line is above the windows. */
4684 /* """"""""""""""""""""""""""""""""""""" */
4685 if (start_line - page - lines_skipped < 0)
4686 /* There isn't enough remaining lines to scroll up */
4687 /* a page size. */
4688 /* """"""""""""""""""""""""""""""""""""""""""""""" */
4689 start_line = 0;
4690 else
4691 start_line -= page + lines_skipped;
4692 }
4693
4694 /* And set the new value of the starting word of the window. */
4695 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4696 win->start = first_word_in_line_a[start_line];
4697
4698 /* Set the new first column to display */
4699 /* """"""""""""""""""""""""""""""""""" */
4700 set_new_first_column(win, term);
4701
4702 /* Redisplay the window. */
4703 /* """"""""""""""""""""" */
4704 *nl = disp_lines(win, toggles, current, count, search_mode, search_data, term,
4705 last_line, tmp_word, langinfo);
4706 }
4707
4708 /* ====================== */
4709 /* Moves the cursor down. */
4710 /* ====================== */
4711 void
move_down(win_t * win,term_t * term,toggle_t * toggles,search_data_t * search_data,langinfo_t * langinfo,long * nl,long page,long last_selectable,long last_line,char * tmp_word)4712 move_down(win_t * win, term_t * term, toggle_t * toggles,
4713 search_data_t * search_data, langinfo_t * langinfo, long * nl,
4714 long page, long last_selectable, long last_line, char * tmp_word)
4715 {
4716 long line; /* The line being processed (target line). */
4717 long start_line; /* The first line of the window. */
4718 long cur_line; /* The line of the cursor. */
4719 long nlines; /* Number of line in the window. */
4720 long last_selectable_line; /* the line containing the last *
4721 | selectable word. */
4722 long lines_skipped; /* The number of line between the target line and the *
4723 | first line containing a selectable word in case of *
4724 | exclusions. */
4725 long last_word; /* The last word on the target line. */
4726 long s, e; /* Starting and ending terminal position of a word. */
4727 int found; /* 1 if a line could be fond in the next page else 0. */
4728
4729 /* Store the initial starting and ending positions of */
4730 /* the word under the cursor. */
4731 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
4732 s = word_a[current].start;
4733 e = word_a[current].end;
4734
4735 /* Identify the line number of the first window's line */
4736 /* and the line number of the current line. */
4737 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
4738 start_line = line_nb_of_word_a[win->start];
4739 cur_line = line_nb_of_word_a[current];
4740 last_selectable_line = line_nb_of_word_a[last_selectable];
4741 lines_skipped = 0;
4742 found = 0;
4743 nlines = win->max_lines < last_line + 1 ? win->max_lines : last_line + 1;
4744
4745 /* initialise the target line. */
4746 /* """"""""""""""""""""""""""" */
4747 line = cur_line;
4748
4749 /* Special case if the cursor is already in the line containing the */
4750 /* last selectable word. */
4751 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4752 if (line == last_selectable_line)
4753 {
4754 /* we can't move the cursor down but we still can try to show the */
4755 /* more non selectable words as we can. */
4756 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4757 if (line >= start_line + page)
4758 {
4759 /* We are scrolling one line at a time and the cursor is not in */
4760 /* the first line of the window. */
4761 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4762 if (start_line + page + nlines - 1 <= last_line)
4763 /* There is enough remaining line to fill a window. */
4764 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
4765 start_line += page;
4766 else
4767 /* We cannot scroll further. */
4768 /* ''''''''''''''''''''''''' */
4769 start_line = last_line - nlines + 1;
4770 }
4771 else
4772 {
4773 /* The cursor is already in the first line of the windows. */
4774 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4775 if (last_line - line + 1 > nlines)
4776 /* There is enough remaining line to fill a window. */
4777 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
4778 start_line = line;
4779 else
4780 /* We cannot scroll further. */
4781 /* ''''''''''''''''''''''''' */
4782 start_line = last_line - nlines + 1;
4783 }
4784 }
4785 else
4786 {
4787 /* The cursor is above the line containing the last selectable word. */
4788 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4789 if (last_line - line - page < 0)
4790 {
4791 /* Trivial case, we are already on the last page */
4792 /* just jump to the last selectable line. */
4793 /* """"""""""""""""""""""""""""""""""""""""""""" */
4794 line = last_selectable_line;
4795 last_word = get_line_last_word(line, last_line);
4796 find_best_word_downward(last_word, s, e);
4797 }
4798 else
4799 {
4800 /* Temporarily move down one page. */
4801 /* """"""""""""""""""""""""""""""" */
4802 line += page;
4803
4804 /* The target line cannot be before the line containing the first */
4805 /* selectable word. */
4806 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4807 if (line > last_selectable_line)
4808 {
4809 line = last_selectable_line;
4810 last_word = get_line_last_word(line, last_line);
4811 find_best_word_downward(last_word, s, e);
4812 }
4813 else
4814 {
4815 /* If this is not the case, search upward for the line with a */
4816 /* selectable word. This line is guaranteed to exist. */
4817 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4818 while (line <= last_selectable_line)
4819 {
4820 last_word = get_line_last_word(line, last_line);
4821
4822 if (find_best_word_downward(last_word, s, e))
4823 {
4824 found = 1;
4825 break;
4826 }
4827
4828 line++;
4829 lines_skipped++;
4830 }
4831 }
4832 }
4833
4834 /* Look if we need to adjust the window to follow the cursor. */
4835 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4836 if (!found && start_line + nlines - 1 + page <= last_line)
4837 {
4838 /* We are on the last line containing a selectable word and */
4839 /* There is enough place to scroll down a page but only scrolls */
4840 /* down if the cursor remains in the window. */
4841 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4842 if (line - start_line >= page)
4843 start_line += page;
4844 }
4845 else if (line > start_line + nlines - 1)
4846 {
4847 /* The target line is below the windows. */
4848 /* """"""""""""""""""""""""""""""""""""" */
4849 if (start_line + nlines + page + lines_skipped - 1 > last_line)
4850 /* There isn't enough remaining lines to scroll down */
4851 /* a page size. */
4852 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4853 start_line = last_line - nlines + 1;
4854 else
4855 start_line += page + lines_skipped;
4856 }
4857 }
4858
4859 /* And set the new value of the starting word of the window. */
4860 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4861 win->start = first_word_in_line_a[start_line];
4862
4863 /* Set the new first column to display. */
4864 /* """""""""""""""""""""""""""""""""""" */
4865 set_new_first_column(win, term);
4866
4867 /* Redisplay the window. */
4868 /* """"""""""""""""""""" */
4869 *nl = disp_lines(win, toggles, current, count, search_mode, search_data, term,
4870 last_line, tmp_word, langinfo);
4871 }
4872
4873 /* ========================================= */
4874 /* Initialize some internal data structures. */
4875 /* ========================================= */
4876 void
init_main_ds(attrib_t * init_attr,win_t * win,limit_t * limits,ticker_t * timers,toggle_t * toggles,misc_t * misc,timeout_t * timeout,daccess_t * daccess)4877 init_main_ds(attrib_t * init_attr, win_t * win, limit_t * limits,
4878 ticker_t * timers, toggle_t * toggles, misc_t * misc,
4879 timeout_t * timeout, daccess_t * daccess)
4880 {
4881 int i;
4882
4883 /* Initial attribute settings. */
4884 /* """"""""""""""""""""""""""" */
4885 init_attr->is_set = UNSET;
4886 init_attr->fg = -1;
4887 init_attr->bg = -1;
4888 init_attr->bold = -1;
4889 init_attr->dim = -1;
4890 init_attr->reverse = -1;
4891 init_attr->standout = -1;
4892 init_attr->underline = -1;
4893 init_attr->italic = -1;
4894 init_attr->blink = -1;
4895
4896 /* Win fields initialization. */
4897 /* """""""""""""""""""""""""" */
4898 win->max_lines = 5;
4899 win->message_lines = 0;
4900 win->asked_max_lines = -1;
4901 win->center = 0;
4902 win->max_cols = 0;
4903 win->col_sep = 0;
4904 win->wide = 0;
4905 win->tab_mode = 0;
4906 win->col_mode = 0;
4907 win->line_mode = 0;
4908 win->first_column = 0;
4909 win->real_max_width = 0;
4910
4911 win->cursor_attr = *init_attr;
4912 win->cursor_on_tag_attr = *init_attr;
4913 win->bar_attr = *init_attr;
4914 win->shift_attr = *init_attr;
4915 win->message_attr = *init_attr;
4916 win->search_field_attr = *init_attr;
4917 win->search_text_attr = *init_attr;
4918 win->search_err_field_attr = *init_attr;
4919 win->search_err_text_attr = *init_attr;
4920 win->match_field_attr = *init_attr;
4921 win->match_text_attr = *init_attr;
4922 win->match_err_field_attr = *init_attr;
4923 win->match_err_text_attr = *init_attr;
4924 win->include_attr = *init_attr;
4925 win->exclude_attr = *init_attr;
4926 win->tag_attr = *init_attr;
4927 win->daccess_attr = *init_attr;
4928
4929 win->sel_sep = NULL;
4930
4931 for (i = 0; i < 9; i++)
4932 win->special_attr[i] = *init_attr;
4933
4934 /* Default limits initialization. */
4935 /* """""""""""""""""""""""""""""" */
4936 limits->words = 32767;
4937 limits->cols = 256;
4938 limits->word_length = 512;
4939
4940 /* Default timers in 1/10 s. */
4941 /* """"""""""""""""""""""""" */
4942 timers->search = 100 * FREQ / 10;
4943 timers->help = 150 * FREQ / 10;
4944 timers->winch = 20 * FREQ / 10;
4945 timers->direct_access = 6 * FREQ / 10;
4946
4947 /* Toggles initialization. */
4948 /* """"""""""""""""""""""" */
4949 toggles->del_line = 0;
4950 toggles->enter_val_in_search = 0;
4951 toggles->no_scrollbar = 0;
4952 toggles->blank_nonprintable = 0;
4953 toggles->keep_spaces = 0;
4954 toggles->taggable = 0;
4955 toggles->autotag = 0;
4956 toggles->noautotag = 0;
4957 toggles->pinable = 0;
4958 toggles->visual_bell = 0;
4959 toggles->incremental_search = 0;
4960
4961 /* Misc default values. */
4962 /* """""""""""""""""""" */
4963 misc->default_search_method = NONE;
4964 misc->ignore_quotes = 0;
4965
4966 /* Set the default timeout to 0 (no expiration). */
4967 /* """"""""""""""""""""""""""""""""""""""""""""" */
4968 timeout->initial_value = 0;
4969 timeout->remain = 0;
4970 timeout->reached = 0;
4971
4972 /* Initialize Direct Access settings. */
4973 /* """""""""""""""""""""""""""""""""" */
4974 daccess->mode = DA_TYPE_NONE;
4975 daccess->left = xstrdup(" ");
4976 daccess->right = xstrdup(")");
4977 daccess->alignment = 'r';
4978 daccess->padding = 'a';
4979 daccess->head = 'k'; /* Keep by default. */
4980 daccess->length = -2;
4981 daccess->flength = 0;
4982 daccess->offset = 0;
4983 daccess->plus = 0;
4984 daccess->size = 0;
4985 daccess->ignore = 0;
4986 daccess->follow = 'y';
4987 daccess->missing = 'y';
4988 daccess->num_sep = NULL;
4989 daccess->def_number = -1;
4990 }
4991
4992 /* *********************************** */
4993 /* ctxopt contexts callback functions. */
4994 /* *********************************** */
4995
4996 /* ******************************** */
4997 /* ctxopt option callback function. */
4998 /* ******************************** */
4999
5000 void
help_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5001 help_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5002 char ** values, int nb_opt_data, void ** opt_data, int nb_ctx_data,
5003 void ** ctx_data)
5004 {
5005 if (strcmp(ctx_name, "Columns") == 0)
5006 columns_help();
5007 else if (strcmp(ctx_name, "Lines") == 0)
5008 lines_help();
5009 else if (strcmp(ctx_name, "Tabulations") == 0)
5010 tabulations_help();
5011 else if (strcmp(ctx_name, "Tagging") == 0)
5012 tagging_help();
5013 else
5014 main_help();
5015
5016 exit(EXIT_FAILURE);
5017 }
5018
5019 void
long_help_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5020 long_help_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5021 char ** values, int nb_opt_data, void ** opt_data,
5022 int nb_ctx_data, void ** ctx_data)
5023 {
5024 ctxopt_disp_usage(continue_after);
5025
5026 printf("\nRead the manual for more information.\n");
5027
5028 exit(EXIT_FAILURE);
5029 }
5030
5031 void
usage_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5032 usage_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5033 char ** values, int nb_opt_data, void ** opt_data, int nb_ctx_data,
5034 void ** ctx_data)
5035 {
5036 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5037 }
5038
5039 void
lines_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5040 lines_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5041 char ** values, int nb_opt_data, void ** opt_data, int nb_ctx_data,
5042 void ** ctx_data)
5043 {
5044 win_t * win = opt_data[0];
5045
5046 if (nb_values == 1)
5047 sscanf(values[0], "%ld", &(win->asked_max_lines));
5048 else
5049 win->asked_max_lines = 0;
5050 }
5051
5052 void
tab_mode_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5053 tab_mode_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5054 char ** values, int nb_opt_data, void ** opt_data,
5055 int nb_ctx_data, void ** ctx_data)
5056 {
5057 win_t * win = opt_data[0];
5058
5059 long max_cols;
5060
5061 if (nb_values == 1)
5062 {
5063 sscanf(values[0], "%ld", &max_cols); /* Numericity and range were *
5064 | already checked by ctxopt. */
5065 win->max_cols = max_cols;
5066 }
5067
5068 win->tab_mode = 1;
5069 win->col_mode = 0;
5070 win->line_mode = 0;
5071 }
5072
5073 void
set_pattern_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5074 set_pattern_action(char * ctx_name, char * opt_name, char * param,
5075 int nb_values, char ** values, int nb_opt_data,
5076 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5077 {
5078 char ** pattern = opt_data[0];
5079 langinfo_t * langinfo = opt_data[1];
5080 misc_t * misc = opt_data[2];
5081
5082 *pattern = xstrdup(values[0]);
5083 utf8_interpret(*pattern, langinfo, misc->invalid_char_substitute);
5084 }
5085
5086 void
int_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5087 int_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5088 char ** values, int nb_opt_data, void ** opt_data, int nb_ctx_data,
5089 void ** ctx_data)
5090 {
5091 char ** string = opt_data[0];
5092 int * shell_like = opt_data[1];
5093 langinfo_t * langinfo = opt_data[2];
5094 misc_t * misc = opt_data[3];
5095
5096 if (nb_values == 1)
5097 {
5098 *string = xstrdup(values[0]);
5099 if (!langinfo->utf8)
5100 utf8_sanitize(*string, misc->invalid_char_substitute);
5101 utf8_interpret(*string, langinfo, misc->invalid_char_substitute);
5102 }
5103
5104 *shell_like = 0;
5105 }
5106
5107 void
set_string_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5108 set_string_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5109 char ** values, int nb_opt_data, void ** opt_data,
5110 int nb_ctx_data, void ** ctx_data)
5111 {
5112 char ** string = opt_data[0];
5113 langinfo_t * langinfo = opt_data[1];
5114 misc_t * misc = opt_data[2];
5115
5116 *string = xstrdup(values[0]);
5117 if (!langinfo->utf8)
5118 utf8_sanitize(*string, misc->invalid_char_substitute);
5119 utf8_interpret(*string, langinfo, misc->invalid_char_substitute);
5120 }
5121
5122 void
wide_mode_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5123 wide_mode_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5124 char ** values, int nb_opt_data, void ** opt_data,
5125 int nb_ctx_data, void ** ctx_data)
5126 {
5127 win_t * win = opt_data[0];
5128
5129 win->wide = 1;
5130 }
5131
5132 void
center_mode_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5133 center_mode_action(char * ctx_name, char * opt_name, char * param,
5134 int nb_values, char ** values, int nb_opt_data,
5135 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5136 {
5137 win_t * win = opt_data[0];
5138
5139 win->center = 1;
5140 }
5141
5142 void
columns_select_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5143 columns_select_action(char * ctx_name, char * opt_name, char * param,
5144 int nb_values, char ** values, int nb_opt_data,
5145 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5146 {
5147 int v;
5148 ll_t ** cols_selector_list = opt_data[0];
5149
5150 if (*cols_selector_list == NULL)
5151 *cols_selector_list = ll_new();
5152
5153 for (v = 0; v < nb_values; v++)
5154 ll_append(*cols_selector_list, xstrdup(values[v]));
5155 }
5156
5157 void
rows_select_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5158 rows_select_action(char * ctx_name, char * opt_name, char * param,
5159 int nb_values, char ** values, int nb_opt_data,
5160 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5161 {
5162 int v;
5163 ll_t ** rows_selector_list = opt_data[0];
5164 win_t * win = opt_data[1];
5165
5166 if (*rows_selector_list == NULL)
5167 *rows_selector_list = ll_new();
5168
5169 for (v = 0; v < nb_values; v++)
5170 ll_append(*rows_selector_list, xstrdup(values[v]));
5171
5172 win->max_cols = 0; /* Disable the window column restriction. */
5173 }
5174
5175 void
toggle_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5176 toggle_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5177 char ** values, int nb_opt_data, void ** opt_data,
5178 int nb_ctx_data, void ** ctx_data)
5179 {
5180 toggle_t * toggles = opt_data[0];
5181
5182 if (strcmp(opt_name, "clean") == 0)
5183 toggles->del_line = 1;
5184 else if (strcmp(opt_name, "keep_spaces") == 0)
5185 toggles->keep_spaces = 1;
5186 else if (strcmp(opt_name, "visual_bell") == 0)
5187 toggles->visual_bell = 1;
5188 else if (strcmp(opt_name, "validate_in_search_mode") == 0)
5189 toggles->enter_val_in_search = 1;
5190 else if (strcmp(opt_name, "blank_nonprintable") == 0)
5191 toggles->blank_nonprintable = 1;
5192 else if (strcmp(opt_name, "no_scroll_bar") == 0)
5193 toggles->no_scrollbar = 1;
5194 else if (strcmp(opt_name, "auto_tag") == 0)
5195 toggles->autotag = 1;
5196 else if (strcmp(opt_name, "no_auto_tag") == 0)
5197 toggles->noautotag = 1;
5198 else if (strcmp(opt_name, "incremental_search") == 0)
5199 toggles->incremental_search = 1;
5200 }
5201
5202 void
invalid_char_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5203 invalid_char_action(char * ctx_name, char * opt_name, char * param,
5204 int nb_values, char ** values, int nb_opt_data,
5205 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5206 {
5207 misc_t * misc = opt_data[0];
5208
5209 char ic = *values[0];
5210
5211 if (isprint(ic))
5212 misc->invalid_char_substitute = ic;
5213 else
5214 misc->invalid_char_substitute = '.';
5215 }
5216
5217 void
gutter_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5218 gutter_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5219 char ** values, int nb_opt_data, void ** opt_data,
5220 int nb_ctx_data, void ** ctx_data)
5221 {
5222 win_t * win = opt_data[0];
5223 langinfo_t * langinfo = opt_data[1];
5224 misc_t * misc = opt_data[2];
5225
5226 if (nb_values == 0)
5227 {
5228 /* As there is no argument, the gutter array will only contain */
5229 /* a vertical bar. */
5230 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5231 win->gutter_a = xmalloc(1 * sizeof(char *));
5232
5233 if (langinfo->utf8)
5234 win->gutter_a[0] = xstrdup(vertical_bar);
5235 else
5236 win->gutter_a[0] = xstrdup("|");
5237
5238 win->gutter_nb = 1;
5239 }
5240 else
5241 {
5242 /* The argument is used to feed the gutter array, each of its character */
5243 /* Will serve as gutter in a round-robin way. */
5244 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5245 long n;
5246 wchar_t * w;
5247 long i, offset;
5248 int mblen;
5249 char * gutter;
5250
5251 gutter = xstrdup(values[0]);
5252
5253 utf8_interpret(gutter, langinfo,
5254 misc->invalid_char_substitute); /* Guarantees a well *
5255 | formed UTF-8 string/ */
5256
5257 win->gutter_nb = utf8_strlen(gutter);
5258 win->gutter_a = xmalloc(win->gutter_nb * sizeof(char *));
5259
5260 offset = 0;
5261
5262 for (i = 0; i < win->gutter_nb; i++)
5263 {
5264 mblen = utf8_get_length(*(gutter + offset));
5265 win->gutter_a[i] = xcalloc(1, mblen + 1);
5266 memcpy(win->gutter_a[i], gutter + offset, mblen);
5267
5268 n = wcswidth((w = utf8_strtowcs(win->gutter_a[i])), 1);
5269 free(w);
5270
5271 if (n > 1)
5272 {
5273 fprintf(stderr, "%s: A multi columns gutter is not allowed.\n", param);
5274 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5275 }
5276 offset += mblen;
5277 }
5278 free(gutter);
5279 }
5280 win->col_sep = 1; /* Activate the gutter. */
5281 }
5282
5283 void
column_mode_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5284 column_mode_action(char * ctx_name, char * opt_name, char * param,
5285 int nb_values, char ** values, int nb_opt_data,
5286 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5287 {
5288 win_t * win = opt_data[0];
5289
5290 win->tab_mode = 0;
5291 win->col_mode = 1;
5292 win->line_mode = 0;
5293 win->max_cols = 0;
5294 }
5295
5296 void
line_mode_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5297 line_mode_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5298 char ** values, int nb_opt_data, void ** opt_data,
5299 int nb_ctx_data, void ** ctx_data)
5300 {
5301 win_t * win = opt_data[0];
5302
5303 win->line_mode = 1;
5304 win->tab_mode = 0;
5305 win->col_mode = 0;
5306 win->max_cols = 0;
5307 }
5308
5309 void
include_re_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5310 include_re_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5311 char ** values, int nb_opt_data, void ** opt_data,
5312 int nb_ctx_data, void ** ctx_data)
5313 {
5314 int * pattern_def_include = opt_data[0];
5315 char ** include_pattern = opt_data[1];
5316 langinfo_t * langinfo = opt_data[2];
5317 misc_t * misc = opt_data[3];
5318
5319 /* Set the default behaviour if not already set. */
5320 /* """"""""""""""""""""""""""""""""""""""""""""" */
5321 if (*pattern_def_include == -1)
5322 *pattern_def_include = 0;
5323
5324 if (*include_pattern == NULL)
5325 *include_pattern = concat("(", values[0], ")", (char *)0);
5326 else
5327 *include_pattern = concat(*include_pattern, "|(", values[0], ")",
5328 (char *)0);
5329
5330 /* Replace the UTF-8 ASCII representations by their binary values in */
5331 /* inclusion patterns. */
5332 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5333 utf8_interpret(*include_pattern, langinfo, misc->invalid_char_substitute);
5334 }
5335
5336 void
exclude_re_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5337 exclude_re_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5338 char ** values, int nb_opt_data, void ** opt_data,
5339 int nb_ctx_data, void ** ctx_data)
5340 {
5341 int * pattern_def_exclude = opt_data[0];
5342 char ** exclude_pattern = opt_data[1];
5343 langinfo_t * langinfo = opt_data[2];
5344 misc_t * misc = opt_data[3];
5345
5346 /* Set the default behaviour if not already set. */
5347 /* """"""""""""""""""""""""""""""""""""""""""""" */
5348 if (*pattern_def_exclude == -1)
5349 *pattern_def_exclude = 0;
5350
5351 if (*exclude_pattern == NULL)
5352 *exclude_pattern = concat("(", values[0], ")", (char *)0);
5353 else
5354 *exclude_pattern = concat(*exclude_pattern, "|(", values[0], ")",
5355 (char *)0);
5356
5357 /* Replace the UTF-8 ASCII representations by their binary values in */
5358 /* exclusion patterns. */
5359 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5360 utf8_interpret(*exclude_pattern, langinfo, misc->invalid_char_substitute);
5361 }
5362
5363 void
post_subst_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5364 post_subst_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5365 char ** values, int nb_opt_data, void ** opt_data,
5366 int nb_ctx_data, void ** ctx_data)
5367 {
5368 ll_t ** list = opt_data[0];
5369 langinfo_t * langinfo = opt_data[1];
5370 misc_t * misc = opt_data[2];
5371
5372 sed_t * sed_node;
5373 int i;
5374
5375 if (*list == NULL)
5376 *list = ll_new();
5377
5378 for (i = 0; i < nb_values; i++)
5379 {
5380 sed_node = xmalloc(sizeof(sed_t));
5381 sed_node->pattern = xstrdup(values[i]);
5382 utf8_interpret(sed_node->pattern, langinfo, misc->invalid_char_substitute);
5383 sed_node->stop = 0;
5384 ll_append(*list, sed_node);
5385 }
5386 }
5387
5388 void
special_level_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5389 special_level_action(char * ctx_name, char * opt_name, char * param,
5390 int nb_values, char ** values, int nb_opt_data,
5391 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5392 {
5393 char ** special_pattern = opt_data[0];
5394 win_t * win = opt_data[1];
5395 term_t * term = opt_data[2];
5396 langinfo_t * langinfo = opt_data[3];
5397 attrib_t * init_attr = opt_data[4];
5398 misc_t * misc = opt_data[5];
5399
5400 attrib_t attr = *init_attr;
5401 char opt = param[strlen(param) - 1]; /* last character of param. */
5402 int i;
5403
5404 special_pattern[opt - '1'] = xstrdup(values[0]);
5405 utf8_interpret(special_pattern[opt - '1'], langinfo,
5406 misc->invalid_char_substitute);
5407
5408 /* Parse optional additional arguments. */
5409 /* """""""""""""""""""""""""""""""""""" */
5410 for (i = 1; i < nb_values; i++)
5411 {
5412 /* Colors must respect the format: <fg color>/<bg color>. */
5413 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
5414 if (parse_attr(values[i], &attr, term->colors))
5415 {
5416 win->special_attr[opt - '1'].is_set = FORCED;
5417 win->special_attr[opt - '1'].fg = attr.fg;
5418 win->special_attr[opt - '1'].bg = attr.bg;
5419 win->special_attr[opt - '1'].bold = attr.bold;
5420 win->special_attr[opt - '1'].dim = attr.dim;
5421 win->special_attr[opt - '1'].reverse = attr.reverse;
5422 win->special_attr[opt - '1'].standout = attr.standout;
5423 win->special_attr[opt - '1'].underline = attr.underline;
5424 win->special_attr[opt - '1'].italic = attr.italic;
5425 win->special_attr[opt - '1'].blink = attr.blink;
5426 }
5427 }
5428 }
5429
5430 void
attributes_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5431 attributes_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5432 char ** values, int nb_opt_data, void ** opt_data,
5433 int nb_ctx_data, void ** ctx_data)
5434 {
5435 win_t * win = opt_data[0];
5436 term_t * term = opt_data[1];
5437 attrib_t * init_attr = opt_data[2];
5438
5439 long i, a; /* loop index. */
5440 long offset = 0; /* nb of chars to ship to find the attribute *
5441 | representation (prefix size). */
5442
5443 attrib_t attr;
5444 attrib_t * attr_to_set = NULL;
5445
5446 /* Flags to check if an attribute is already set */
5447 /* """"""""""""""""""""""""""""""""""""""""""""" */
5448 int inc_attr_set = 0; /* included words. */
5449 int exc_attr_set = 0; /* excluded words. */
5450 int cur_attr_set = 0; /* highlighted word (cursor). */
5451 int bar_attr_set = 0; /* scroll bar. */
5452 int shift_attr_set = 0; /* horizontal scrolling arrows. */
5453 int message_attr_set = 0; /* message (title). */
5454 int tag_attr_set = 0; /* selected (tagged) words. */
5455 int cursor_on_tag_attr_set = 0; /* selected words under the cursor. */
5456 int sf_attr_set = 0; /* currently searched field color. */
5457 int st_attr_set = 0; /* currently searched text color. */
5458 int mf_attr_set = 0; /* matching word field color. */
5459 int mt_attr_set = 0; /* matching word text color. */
5460 int daccess_attr_set = 0; /* Direct access text color. */
5461
5462 /* Information relatives to the attributes to be searched and set. */
5463 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
5464 struct
5465 {
5466 attrib_t * attr;
5467 char * msg;
5468 int * flag;
5469 char * prefix;
5470 int prefix_len;
5471 } attr_infos[] = {
5472 { &win->exclude_attr, "The exclude attribute is already set.",
5473 &exc_attr_set, "e:", 2 },
5474 { &win->include_attr, "The include attribute is already set.",
5475 &inc_attr_set, "i:", 2 },
5476 { &win->cursor_attr, "The cursor attribute is already set.", &cur_attr_set,
5477 "c:", 2 },
5478 { &win->bar_attr, "The scroll bar attribute is already set.", &bar_attr_set,
5479 "b:", 2 },
5480 { &win->shift_attr, "The shift attribute is already set.", &shift_attr_set,
5481 "s:", 2 },
5482 { &win->message_attr, "The message attribute is already set.",
5483 &message_attr_set, "m:", 2 },
5484 { &win->tag_attr, "The tag attribute is already set.", &tag_attr_set,
5485 "t:", 2 },
5486 { &win->cursor_on_tag_attr,
5487 "The cursor on tagged word attribute is already set.",
5488 &cursor_on_tag_attr_set, "ct:", 3 },
5489 { &win->search_field_attr, "The search field attribute is already set.",
5490 &sf_attr_set, "sf:", 3 },
5491 { &win->search_text_attr, "The search text attribute is already set.",
5492 &st_attr_set, "st:", 3 },
5493 { &win->search_err_field_attr,
5494 "The search with error field attribute is already set.", &sf_attr_set,
5495 "sfe:", 4 },
5496 { &win->search_err_text_attr,
5497 "The search text with error attribute is already set.", &st_attr_set,
5498 "ste:", 4 },
5499 { &win->match_field_attr,
5500 "The matching word field attribute is already set.", &mf_attr_set,
5501 "mf:", 3 },
5502 { &win->match_text_attr, "The matching word text attribute is already set.",
5503 &mt_attr_set, "mt:", 3 },
5504 { &win->match_err_field_attr,
5505 "The matching word with error field attribute is already set.",
5506 &mf_attr_set, "mfe:", 4 },
5507 { &win->match_err_text_attr,
5508 "The matching word with error text attribute is already set.",
5509 &mt_attr_set, "mte:", 4 },
5510 { &win->daccess_attr, "The direct access tag attribute is already set.",
5511 &daccess_attr_set, "da:", 3 },
5512 { NULL, NULL, NULL, NULL, 0 }
5513 };
5514
5515 /* Parse the arguments. */
5516 /* """""""""""""""""""" */
5517 for (a = 0; a < nb_values; a++)
5518 {
5519 attr = *init_attr;
5520
5521 i = 0;
5522 while (attr_infos[i].flag != NULL)
5523 {
5524 if (strncmp(values[a], attr_infos[i].prefix, attr_infos[i].prefix_len)
5525 == 0)
5526 {
5527 if (*attr_infos[i].flag)
5528 {
5529 fprintf(stderr, "%s: ", param);
5530 fputs(attr_infos[i].msg, stderr);
5531 fputs("\n", stderr);
5532 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5533 }
5534
5535 attr_to_set = attr_infos[i].attr;
5536 *attr_infos[i].flag = 1;
5537 offset = attr_infos[i].prefix_len;
5538 break; /* We have found a matching prefix, *
5539 | no need to continue. */
5540 }
5541 i++;
5542 }
5543 if (attr_infos[i].flag == NULL)
5544 {
5545 fprintf(stderr, "%s: Bad attribute prefix in %s\n", param, values[a]);
5546 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5547 }
5548
5549 /* Attributes must respect the format: */
5550 /* <fg color>/<bg color>,<styles>. */
5551 /* """"""""""""""""""""""""""""""""""" */
5552 if (parse_attr(values[a] + offset, &attr, term->colors))
5553 {
5554 attr_to_set->is_set = FORCED;
5555 attr_to_set->fg = attr.fg;
5556 attr_to_set->bg = attr.bg;
5557 attr_to_set->bold = attr.bold;
5558 attr_to_set->dim = attr.dim;
5559 attr_to_set->reverse = attr.reverse;
5560 attr_to_set->standout = attr.standout;
5561 attr_to_set->underline = attr.underline;
5562 attr_to_set->italic = attr.italic;
5563 attr_to_set->blink = attr.blink;
5564 }
5565 else
5566 {
5567 fprintf(stderr, "%s: Bad attribute settings %s\n", param, values[a]);
5568 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5569 }
5570 }
5571 }
5572
5573 void
limits_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5574 limits_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5575 char ** values, int nb_opt_data, void ** opt_data,
5576 int nb_ctx_data, void ** ctx_data)
5577 {
5578 limit_t * limits = opt_data[0];
5579
5580 long l;
5581 long val;
5582
5583 char * lim;
5584 char * endptr;
5585 char * p;
5586
5587 /* Parse the arguments. */
5588 /* """""""""""""""""""" */
5589 for (l = 0; l < nb_values; l++)
5590 {
5591 errno = 0;
5592 lim = values[l];
5593 p = lim + 2;
5594
5595 switch (*lim)
5596 {
5597 case 'l': /* word length. */
5598 val = strtol(p, &endptr, 0);
5599 if (errno == ERANGE || (val == 0 && errno != 0) || endptr == p
5600 || *endptr != '\0')
5601 {
5602 fprintf(stderr, "%s: Invalid word length limit. ", p);
5603 fprintf(stderr, "Using the default value: %ld\n",
5604 limits->word_length);
5605 }
5606 else
5607 limits->word_length = val;
5608 break;
5609
5610 case 'w': /* max number of words. */
5611 val = strtol(p, &endptr, 0);
5612 if (errno == ERANGE || (val == 0 && errno != 0) || endptr == p
5613 || *endptr != '\0')
5614 {
5615 fprintf(stderr, "%s: Invalid words number limit. ", p);
5616 fprintf(stderr, "Using the default value: %ld\n", limits->words);
5617 }
5618 else
5619 limits->words = val;
5620 break;
5621
5622 case 'c': /* max number of words. */
5623 val = strtol(p, &endptr, 0);
5624 if (errno == ERANGE || (val == 0 && errno != 0) || endptr == p
5625 || *endptr != '\0')
5626 {
5627 fprintf(stderr, "%s: Invalid columns number limit. ", p);
5628 fprintf(stderr, "Using the default value: %ld\n", limits->cols);
5629 }
5630 else
5631 limits->cols = val;
5632 break;
5633
5634 default:
5635 fprintf(stderr, "%s: Invalid limit keyword, should be l, w or c.\n", p);
5636 break;
5637 }
5638 }
5639 }
5640
5641 void
version_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5642 version_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5643 char ** values, int nb_opt_data, void ** opt_data,
5644 int nb_ctx_data, void ** ctx_data)
5645 {
5646 fputs("Version: " VERSION "\n", stdout);
5647 exit(EXIT_SUCCESS);
5648 }
5649
5650 void
timeout_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5651 timeout_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5652 char ** values, int nb_opt_data, void ** opt_data,
5653 int nb_ctx_data, void ** ctx_data)
5654 {
5655 langinfo_t * langinfo = opt_data[0];
5656 misc_t * misc = opt_data[1];
5657
5658 if (strcmp(opt_name, "hidden_timeout") == 0)
5659 quiet_timeout = 1;
5660
5661 if (strprefix("current", values[0]))
5662 timeout.mode = CURRENT;
5663 else if (strprefix("quit", values[0]))
5664 timeout.mode = QUIT;
5665 else if (strprefix("word", values[0]))
5666 {
5667 if (nb_values == 3)
5668 {
5669 timeout.mode = WORD;
5670 timeout_word = xstrdup(values[1]);
5671 utf8_interpret(timeout_word, langinfo, misc->invalid_char_substitute);
5672 }
5673 else
5674 {
5675 fprintf(stderr, "%s: Missing timeout selected word or delay.\n", param);
5676 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5677 }
5678 }
5679
5680 if (sscanf(values[nb_values - 1], "%5u", &timeout.initial_value) == 1)
5681 {
5682 timeout.initial_value *= FREQ;
5683 timeout.remain = timeout.initial_value;
5684 }
5685 else
5686 {
5687 fprintf(stderr, "%s: Invalid timeout delay.\n", param);
5688 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5689 }
5690 }
5691
5692 void
tag_mode_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5693 tag_mode_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5694 char ** values, int nb_opt_data, void ** opt_data,
5695 int nb_ctx_data, void ** ctx_data)
5696 {
5697 toggle_t * toggles = opt_data[0];
5698 win_t * win = opt_data[1];
5699 langinfo_t * langinfo = opt_data[2];
5700 misc_t * misc = opt_data[3];
5701
5702 toggles->taggable = 1;
5703
5704 if (nb_values == 1)
5705 {
5706 win->sel_sep = xstrdup(values[0]);
5707 utf8_interpret(win->sel_sep, langinfo, misc->invalid_char_substitute);
5708 }
5709 }
5710
5711 void
pin_mode_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5712 pin_mode_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5713 char ** values, int nb_opt_data, void ** opt_data,
5714 int nb_ctx_data, void ** ctx_data)
5715 {
5716 toggle_t * toggles = opt_data[0];
5717 win_t * win = opt_data[1];
5718 langinfo_t * langinfo = opt_data[2];
5719 misc_t * misc = opt_data[3];
5720
5721 toggles->taggable = 1;
5722 toggles->pinable = 1;
5723
5724 if (nb_values == 1)
5725 {
5726 win->sel_sep = xstrdup(values[0]);
5727 utf8_interpret(win->sel_sep, langinfo, misc->invalid_char_substitute);
5728 }
5729 }
5730
5731 void
search_method_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5732 search_method_action(char * ctx_name, char * opt_name, char * param,
5733 int nb_values, char ** values, int nb_opt_data,
5734 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5735 {
5736 misc_t * misc = opt_data[0];
5737
5738 if (strprefix("prefix", values[0]))
5739 misc->default_search_method = PREFIX;
5740 else if (strprefix("fuzzy", values[0]))
5741 misc->default_search_method = FUZZY;
5742 else if (strprefix("substring", values[0]))
5743 misc->default_search_method = SUBSTRING;
5744 else
5745 {
5746 fprintf(stderr, "%s: Bad search method: %s\n", param, values[0]);
5747 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5748 }
5749 }
5750
5751 void
auto_da_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5752 auto_da_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5753 char ** values, int nb_opt_data, void ** opt_data,
5754 int nb_ctx_data, void ** ctx_data)
5755 {
5756 char ** daccess_pattern = opt_data[0];
5757 char * value;
5758 int i;
5759
5760 if (nb_values == 0)
5761 {
5762 if (daccess.missing == 'y' || ((daccess.mode & DA_TYPE_POS) == 0))
5763 {
5764 if (*daccess_pattern == NULL)
5765 {
5766 *daccess_pattern = xstrdup("(.)");
5767 daccess.mode |= DA_TYPE_AUTO; /* Auto. */
5768 }
5769 else
5770 *daccess_pattern = concat(*daccess_pattern, "|(.)", (char *)0);
5771 }
5772 }
5773 else
5774 for (i = 0; i < nb_values; i++)
5775 {
5776 if (*values[i] == '\0'
5777 && (daccess.missing == 'y' || ((daccess.mode & DA_TYPE_POS) == 0)))
5778 value = ".";
5779 else
5780 value = values[i];
5781
5782 if (*daccess_pattern == NULL)
5783 {
5784 *daccess_pattern = concat("(", value, ")", (char *)0);
5785 daccess.mode |= DA_TYPE_AUTO; /* Auto. */
5786 }
5787 else
5788 *daccess_pattern = concat(*daccess_pattern, "|(", value, ")",
5789 (char *)0);
5790 }
5791
5792 if (daccess.def_number < 0)
5793 {
5794 if (strcmp(param, "-N") == 0)
5795 daccess.def_number = 0; /* Words are unnumbered by default. */
5796 else
5797 daccess.def_number = 1; /* Words are numbered by default. */
5798 }
5799 }
5800
5801 void
field_da_number_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5802 field_da_number_action(char * ctx_name, char * opt_name, char * param,
5803 int nb_values, char ** values, int nb_opt_data,
5804 void ** opt_data, int nb_ctx_data, void ** ctx_data)
5805 {
5806 daccess.mode |= DA_TYPE_POS;
5807 }
5808
5809 void
da_options_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)5810 da_options_action(char * ctx_name, char * opt_name, char * param, int nb_values,
5811 char ** values, int nb_opt_data, void ** opt_data,
5812 int nb_ctx_data, void ** ctx_data)
5813 {
5814 langinfo_t * langinfo = opt_data[0];
5815 long * daccess_index = opt_data[1];
5816 misc_t * misc = opt_data[2];
5817
5818 int pos;
5819 wchar_t * w;
5820 int n;
5821 int i;
5822
5823 /* Parse optional additional arguments. */
5824 /* """""""""""""""""""""""""""""""""""" */
5825 for (i = 0; i < nb_values; i++)
5826 {
5827 char * value = values[i];
5828
5829 switch (*value)
5830 {
5831 case 'l': /* Left char .*/
5832 free(daccess.left);
5833
5834 daccess.left = xstrdup(value + 2);
5835 utf8_interpret(daccess.left, langinfo, misc->invalid_char_substitute);
5836
5837 if (utf8_strlen(daccess.left) != 1)
5838 {
5839 fprintf(stderr, "%s: Too many characters after l:\n", param);
5840 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5841 }
5842
5843 n = wcswidth((w = utf8_strtowcs(daccess.left)), 1);
5844 free(w);
5845
5846 if (n > 1)
5847 {
5848 fprintf(stderr,
5849 "%s: A multi columns character is not allowed "
5850 "after l:\n",
5851 param);
5852 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5853 }
5854 break;
5855
5856 case 'r': /* Right char. */
5857 free(daccess.right);
5858
5859 daccess.right = xstrdup(value + 2);
5860 utf8_interpret(daccess.right, langinfo, misc->invalid_char_substitute);
5861
5862 if (utf8_strlen(daccess.right) != 1)
5863 {
5864 fprintf(stderr, "%s: Too many characters after r:\n", param);
5865 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5866 }
5867
5868 n = wcswidth((w = utf8_strtowcs(daccess.right)), 1);
5869 free(w);
5870
5871 if (n > 1)
5872 {
5873 fprintf(stderr,
5874 "%s: A multi columns character is not allowed "
5875 "after r:\n",
5876 param);
5877 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5878 }
5879 break;
5880
5881 case 'a': /* Alignment. */
5882 if (strprefix("left", value + 2))
5883 daccess.alignment = 'l';
5884 else if (strprefix("right", value + 2))
5885 daccess.alignment = 'r';
5886 else
5887 {
5888 fprintf(stderr,
5889 "%s: The value after a: must be "
5890 "l(eft) or r(ight)\n",
5891 param);
5892 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5893 }
5894 break;
5895
5896 case 'p': /* Padding. */
5897 if (strprefix("all", value + 2))
5898 daccess.padding = 'a';
5899 else if (strprefix("included", value + 2))
5900 daccess.padding = 'i';
5901 else
5902 {
5903 fprintf(stderr, "%s: Bad value after p:\n", param);
5904 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5905 }
5906 break;
5907
5908 case 'w': /* Width. */
5909 if (sscanf(value + 2, "%d%n", &daccess.length, &pos) != 1)
5910 {
5911 fprintf(stderr, "%s: Bad value after w:\n", param);
5912 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5913 }
5914 if (value[pos + 2] != '\0')
5915 {
5916 fprintf(stderr, "%s: Bad value after w:\n", param);
5917 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5918 }
5919 if (daccess.length <= 0 || daccess.length > 5)
5920 {
5921 fprintf(stderr, "%s: w sub-option must be between 1 and 5\n", param);
5922 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5923 }
5924 break;
5925
5926 case 'o': /* Start offset. */
5927 if (sscanf(value + 2, "%zu%n+", &daccess.offset, &pos) == 1)
5928 {
5929 if (value[pos + 2] == '+')
5930 {
5931 daccess.plus = 1;
5932
5933 if (value[pos + 3] != '\0')
5934 {
5935 fprintf(stderr, "%s: Bad value after o:\n", param);
5936 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5937 }
5938 }
5939 else if (value[pos + 2] != '\0')
5940 {
5941 fprintf(stderr, "%s: Bad value after o:\n", param);
5942 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5943 }
5944 }
5945 else
5946 {
5947 fprintf(stderr, "%s: Bad value after o:\n", param);
5948 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5949 }
5950
5951 break;
5952
5953 case 'n': /* Number of digits to extract. */
5954 if (sscanf(value + 2, "%d%n", &daccess.size, &pos) != 1)
5955 {
5956 fprintf(stderr, "%s: Bad value after n:\n", param);
5957 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5958 }
5959 if (value[pos + 2] != '\0')
5960 {
5961 fprintf(stderr, "%s: Bad value after n:\n", param);
5962 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5963 }
5964 if (daccess.size <= 0 || daccess.size > 5)
5965 {
5966 fprintf(stderr, "n sub-option must have a value between 1 and 5.\n");
5967 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5968 }
5969 break;
5970
5971 case 'i': /* Number of UTF-8 glyphs to ignore after the *
5972 | selector to extract. */
5973 if (sscanf(value + 2, "%zu%n", &daccess.ignore, &pos) != 1)
5974 {
5975 fprintf(stderr, "%s: Bad value after i:\n", param);
5976 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5977 }
5978 if (value[pos + 2] != '\0')
5979 {
5980 fprintf(stderr, "%s: Bad value after i:\n", param);
5981 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5982 }
5983 break;
5984
5985 case 'f': /* Follow. */
5986 if (strprefix("yes", value + 2))
5987 daccess.follow = 'y';
5988 else if (strprefix("no", value + 2))
5989 daccess.follow = 'n';
5990 else
5991 {
5992 fprintf(stderr, "%s: Bad value after f:\n", param);
5993 ctxopt_ctx_disp_usage(ctx_name, exit_after);
5994 }
5995 break;
5996
5997 case 'm': /* Possibly number missing embedded numbers. */
5998 if (strprefix("yes", value + 2))
5999 daccess.missing = 'y';
6000 else if (strprefix("no", value + 2))
6001 daccess.missing = 'n';
6002 else
6003 {
6004 fprintf(stderr, "%s: Bad value after m:\n", param);
6005 ctxopt_ctx_disp_usage(ctx_name, exit_after);
6006 }
6007 break;
6008
6009 case 'd': /* Decorate. */
6010 free(daccess.num_sep);
6011
6012 daccess.num_sep = xstrdup(value + 2);
6013 utf8_interpret(daccess.num_sep, langinfo,
6014 misc->invalid_char_substitute);
6015
6016 if (utf8_strlen(daccess.num_sep) != 1)
6017 {
6018 fprintf(stderr, "%s: Too many characters after d:\n", param);
6019 ctxopt_ctx_disp_usage(ctx_name, exit_after);
6020 }
6021
6022 n = wcswidth((w = utf8_strtowcs(daccess.num_sep)), 1);
6023 free(w);
6024
6025 if (n > 1)
6026 {
6027 fprintf(stderr,
6028 "%s: A multi columns separator is not allowed "
6029 "after d:\n",
6030 param);
6031 ctxopt_ctx_disp_usage(ctx_name, exit_after);
6032 }
6033 break;
6034
6035 case 's': /* Start index. */
6036 {
6037 long pos;
6038
6039 if (sscanf(value + 2, "%ld%ln", daccess_index, &pos) == 1)
6040 {
6041 if (*daccess_index < 0 || *(value + 2 + pos) != '\0')
6042 *daccess_index = 1;
6043 }
6044 else
6045 {
6046 fprintf(stderr, "%s: Invalid first index after s:\n", param);
6047 ctxopt_ctx_disp_usage(ctx_name, exit_after);
6048 }
6049 }
6050 break;
6051
6052 case 'h': /* Head. */
6053 if (strprefix("trim", value + 2))
6054 daccess.head = 't';
6055 else if (strprefix("cut", value + 2))
6056 daccess.head = 'c';
6057 else if (strprefix("keep", value + 2))
6058 daccess.head = 'k';
6059 else
6060 {
6061 fprintf(stderr, "%s: Bad value after :h\n", param);
6062 ctxopt_ctx_disp_usage(ctx_name, exit_after);
6063 }
6064 break;
6065
6066 default:
6067 {
6068 fprintf(stderr, "%s: Bad sub-command: %s\n", param, value);
6069 ctxopt_ctx_disp_usage(ctx_name, exit_after);
6070 }
6071 }
6072
6073 if (daccess.length <= 0 || daccess.length > 5)
6074 daccess.length = -2; /* special value -> auto. */
6075 }
6076 }
6077
6078 void
ignore_quotes_action(char * ctx_name,char * opt_name,char * param,int nb_values,char ** values,int nb_opt_data,void ** opt_data,int nb_ctx_data,void ** ctx_data)6079 ignore_quotes_action(char * ctx_name, char * opt_name, char * param,
6080 int nb_values, char ** values, int nb_opt_data,
6081 void ** opt_data, int nb_ctx_data, void ** ctx_data)
6082 {
6083 misc_t * misc = opt_data[0];
6084
6085 misc->ignore_quotes = 1;
6086 }
6087
6088 /* =================================================================== */
6089 /* Cancels a search. Called when ESC is hit or a new search session is */
6090 /* initiated and the incremental_search option is not used. */
6091 /* =================================================================== */
6092 void
reset_search_buffer(win_t * win,search_data_t * search_data,ticker_t * timers,toggle_t * toggles,term_t * term,daccess_t * daccess,langinfo_t * langinfo,long last_line,char * tmp_word,long word_real_max_size)6093 reset_search_buffer(win_t * win, search_data_t * search_data, ticker_t * timers,
6094 toggle_t * toggles, term_t * term, daccess_t * daccess,
6095 langinfo_t * langinfo, long last_line, char * tmp_word,
6096 long word_real_max_size)
6097 {
6098 /* ESC key has been pressed. */
6099 /* """"""""""""""""""""""""" */
6100 search_mode_t old_search_mode = search_mode;
6101
6102 /* Cancel the search timer. */
6103 /* """""""""""""""""""""""" */
6104 search_timer = 0;
6105
6106 search_data->fuzzy_err = 0;
6107 search_data->only_starting = 0;
6108 search_data->only_ending = 0;
6109
6110 if (help_mode)
6111 disp_lines(win, toggles, current, count, search_mode, search_data, term,
6112 last_line, tmp_word, langinfo);
6113
6114 /* Reset the direct access selector stack. */
6115 /* """"""""""""""""""""""""""""""""""""""" */
6116 memset(daccess_stack, '\0', 6);
6117 daccess_stack_head = 0;
6118 daccess_timer = timers->direct_access;
6119
6120 /* Clean the potential matching words non empty list. */
6121 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
6122 search_mode = NONE;
6123
6124 if (matches_count > 0 || old_search_mode != search_mode)
6125 {
6126 clean_matches(search_data, word_real_max_size);
6127
6128 disp_lines(win, toggles, current, count, search_mode, search_data, term,
6129 last_line, tmp_word, langinfo);
6130 }
6131 }
6132
6133 /* ================= */
6134 /* Main entry point. */
6135 /* ================= */
6136 int
main(int argc,char * argv[])6137 main(int argc, char * argv[])
6138 {
6139 /* Mapping of supported charsets and the number of bits used in them. */
6140 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6141 charsetinfo_t all_supported_charsets[] = {
6142 { "UTF-8", 8 },
6143
6144 { "ANSI_X3.4-1968", 7 },
6145 { "ANSI_X3.4-1986", 7 },
6146 { "646", 7 },
6147 { "ASCII", 7 },
6148 { "CP367", 7 },
6149 { "IBM367", 7 },
6150 { "ISO_646.BASIC", 7 },
6151 { "ISO_646.IRV:1991", 7 },
6152 { "ISO_646.IRV", 7 },
6153 { "ISO646-US", 7 },
6154 { "ISO-IR-6", 7 },
6155 { "US", 7 },
6156 { "US-ASCII", 7 },
6157
6158 { "hp-roman8", 8 },
6159 { "roman8", 8 },
6160 { "r8", 8 },
6161
6162 { "ISO8859-1", 8 },
6163 { "ISO-8859-1", 8 },
6164 { "ISO-IR-100", 8 },
6165 { "ISO_8859-1:1987", 8 },
6166 { "ISO_8859-1", 8 },
6167 { "LATIN1", 8 },
6168 { "L1", 8 },
6169 { "IBM819", 8 },
6170 { "CP819", 8 },
6171
6172 { "ISO8859-2", 8 },
6173 { "ISO-8859-2", 8 },
6174 { "ISO-IR-101", 8 },
6175 { "ISO_8859-2:1987", 8 },
6176 { "ISO_8859-2", 8 },
6177 { "LATIN2", 8 },
6178 { "L2", 8 },
6179 { "CP28592", 8 },
6180
6181 { "ISO8859-3", 8 },
6182 { "ISO-8859-3", 8 },
6183 { "ISO-IR-109", 8 },
6184 { "ISO_8859-3:1988", 8 },
6185 { "ISO_8859-3", 8 },
6186 { "LATIN3", 8 },
6187 { "L3", 8 },
6188 { "CP28593", 8 },
6189
6190 { "ISO8859-4", 8 },
6191 { "ISO-8859-4", 8 },
6192 { "ISO-IR-110", 8 },
6193 { "ISO_8859-4:1988", 8 },
6194 { "LATIN4", 8 },
6195 { "L4", 8 },
6196 { "CP28594", 8 },
6197
6198 { "ISO8859-5", 8 },
6199 { "ISO-8859-5", 8 },
6200 { "ISO-IR-144", 8 },
6201 { "ISO_8859-5:1988", 8 },
6202 { "CYRILLIC", 8 },
6203 { "CP28595", 8 },
6204
6205 { "KOI8-R", 8 },
6206 { "KOI8-RU", 8 },
6207 { "KOI8-U", 8 },
6208
6209 { "ISO8859-6", 8 },
6210 { "ISO-8859-6", 8 },
6211 { "ISO-IR-127", 8 },
6212 { "ISO_8859-6:1987", 8 },
6213 { "ECMA-114", 8 },
6214 { "ASMO-708", 8 },
6215 { "ARABIC", 8 },
6216 { "CP28596", 8 },
6217
6218 { "ISO8859-7", 8 },
6219 { "ISO-8859-7", 8 },
6220 { "ISO-IR-126", 8 },
6221 { "ISO_8859-7:2003", 8 },
6222 { "ISO_8859-7:1987", 8 },
6223 { "ELOT_928", 8 },
6224 { "ECMA-118", 8 },
6225 { "GREEK", 8 },
6226 { "GREEK8", 8 },
6227 { "CP28597", 8 },
6228
6229 { "ISO8859-8", 8 },
6230 { "ISO-8859-8", 8 },
6231 { "ISO-IR-138", 8 },
6232 { "ISO_8859-8:1988", 8 },
6233 { "HEBREW", 8 },
6234 { "CP28598", 8 },
6235
6236 { "ISO8859-9", 8 },
6237 { "ISO-8859-9", 8 },
6238 { "ISO-IR-148", 8 },
6239 { "ISO_8859-9:1989", 8 },
6240 { "LATIN5", 8 },
6241 { "L5", 8 },
6242 { "CP28599", 8 },
6243
6244 { "ISO8859-10", 8 },
6245 { "ISO-8859-10", 8 },
6246 { "ISO-IR-157", 8 },
6247 { "ISO_8859-10:1992", 8 },
6248 { "LATIN6", 8 },
6249 { "L6", 8 },
6250 { "CP28600", 8 },
6251
6252 { "ISO8859-11", 8 },
6253 { "ISO-8859-11", 8 },
6254 { "ISO-8859-11:2001", 8 },
6255 { "ISO-IR-166", 8 },
6256 { "CP474", 8 },
6257
6258 { "TIS-620", 8 },
6259 { "TIS620", 8 },
6260 { "TIS620-0", 8 },
6261 { "TIS620.2529-1", 8 },
6262 { "TIS620.2533-0", 8 },
6263
6264 /* ISO-8859-12 was abandoned in 1997. */
6265 /* """""""""""""""""""""""""""""""""" */
6266
6267 { "ISO8859-13", 8 },
6268 { "ISO-8859-13", 8 },
6269 { "ISO-IR-179", 8 },
6270 { "LATIN7", 8 },
6271 { "L7", 8 },
6272 { "CP28603", 8 },
6273
6274 { "ISO8859-14", 8 },
6275 { "ISO-8859-14", 8 },
6276 { "LATIN8", 8 },
6277 { "L8", 8 },
6278
6279 { "ISO8859-15", 8 },
6280 { "ISO-8859-15", 8 },
6281 { "LATIN-9", 8 },
6282 { "CP28605", 8 },
6283
6284 { "ISO8859-16", 8 },
6285 { "ISO-8859-16", 8 },
6286 { "ISO-IR-226", 8 },
6287 { "ISO_8859-16:2001", 8 },
6288 { "LATIN10", 8 },
6289 { "L10", 8 },
6290
6291 { "CP1250", 8 },
6292 { "CP1251", 8 },
6293
6294 { "CP1252", 8 },
6295 { "MS-ANSI", 8 },
6296 { NULL, 0 }
6297 };
6298
6299 int nb_rem_args = 0;
6300 char ** rem_args = NULL;
6301
6302 char * message = NULL; /* message to be displayed above the selection *
6303 | window. */
6304 ll_t * message_lines_list = NULL; /* list of the lines in the message to *
6305 | be displayed. */
6306 long message_max_width = 0; /* total width of the message (longest line). */
6307 long message_max_len = 0; /* max number of bytes taken by a message *
6308 | line. */
6309
6310 char * int_string = NULL; /* String to be output when typing ^C. */
6311 int int_as_in_shell = 1; /* CTRL-C mimics the shell behaviour. */
6312
6313 FILE * input_file; /* The name of the file passed as argument if any. */
6314
6315 long index; /* generic counter. */
6316
6317 long daccess_index = 1; /* First index of the numbered words. */
6318
6319 char * daccess_np = NULL; /* direct access numbered pattern. */
6320 regex_t daccess_np_re; /* variable to store the compiled direct access *
6321 | pattern (-N) RE. */
6322
6323 char * daccess_up = NULL; /* direct access not numbered pattern. */
6324 regex_t daccess_up_re; /* variable to store the compiled direct access *
6325 | pattern (-U) RE. */
6326
6327 char * include_pattern = NULL;
6328 char * exclude_pattern = NULL;
6329 int pattern_def_include = -1; /* Set to -1 until an -i or -e option *
6330 | is specified, This variable remembers *
6331 | if the words not matched will be *
6332 | included (value 1) or excluded *
6333 | (value 0) by default. */
6334 regex_t include_re; /* variable to store the compiled include (-i) REs. */
6335 regex_t exclude_re; /* variable to store the compiled exclude (-e) REs. */
6336
6337 ll_t * early_sed_list = NULL; /* List of sed like string representation *
6338 | of regex given after (-ES). */
6339 ll_t * sed_list = NULL; /* List of sed like string representation *
6340 | of regex given after (-S). */
6341 ll_t * include_sed_list = NULL; /* idem for -I. */
6342 ll_t * exclude_sed_list = NULL; /* idem for -E. */
6343
6344 ll_t * inc_col_interval_list = NULL; /* list of included or */
6345 ll_t * exc_col_interval_list = NULL; /* excluded numerical intervals */
6346 ll_t * inc_row_interval_list = NULL; /* for lines and columns. */
6347 ll_t * exc_row_interval_list = NULL;
6348
6349 ll_t * inc_col_regex_list = NULL; /* same for lines and columns specified. */
6350 ll_t * exc_col_regex_list = NULL; /* by regular expressions. */
6351 ll_t * inc_row_regex_list = NULL;
6352 ll_t * exc_row_regex_list = NULL;
6353
6354 filters_t rows_filter_type = UNKNOWN_FILTER;
6355
6356 char * first_word_pattern = NULL; /* used by -A/-Z. */
6357 char * last_word_pattern = NULL;
6358 regex_t first_word_re;
6359 regex_t last_word_re;
6360
6361 char * special_pattern[9] = { NULL, NULL, NULL, NULL, NULL,
6362 NULL, NULL, NULL, NULL }; /* -1 .. -9 */
6363 regex_t special_re[9];
6364
6365 int include_visual_only = 0; /* If set to 1, the original word which is *
6366 | read from stdin will be output even if its */
6367 int exclude_visual_only = 0; /* visual representation was modified via *
6368 | -S/-I/-E. */
6369
6370 ll_t * cols_selector_list = NULL;
6371 char * cols_selector = NULL;
6372
6373 ll_t * rows_selector_list = NULL;
6374 char * rows_selector = NULL;
6375
6376 long wi; /* word index. */
6377
6378 term_t term; /* Terminal structure. */
6379
6380 tst_node_t * tst_word = NULL; /* TST used by the search function. */
6381 tst_node_t * tst_daccess = NULL; /* TST used by the direct access system. */
6382
6383 long page; /* Step for the vertical cursor moves. */
6384 char * word; /* Temporary variable to work on words. */
6385 char * tmp_word; /* Temporary variable able to contain the beginning of *
6386 | the word to be displayed. */
6387
6388 long last_line = 0; /* last logical line number (from 0). */
6389 win_t win;
6390 limit_t limits; /* set of various limitations. */
6391 ticker_t timers; /* timers contents. */
6392 misc_t misc; /* misc contents. */
6393 toggle_t toggles; /* set of binary indicators. */
6394
6395 int old_fd0; /* backups of the old stdin file descriptor. */
6396 int old_fd1; /* backups of the old stdout file descriptor. */
6397 FILE * old_stdin;
6398 FILE * old_stdout; /* The selected word will go there. */
6399
6400 long nl; /* Number of lines displayed in the window. */
6401 long offset; /* Used to correctly put the cursor at the start of the *
6402 | selection window, even after a terminal vertical scroll. */
6403
6404 long first_selectable; /* Index of the first selectable word in the input *
6405 | stream. */
6406 long last_selectable; /* Index of the last selectable word in the input *
6407 | stream. */
6408
6409 long min_size; /* Minimum screen width of a column in tabular mode. */
6410
6411 long tab_max_size; /* Maximum screen width of a column in tabular *
6412 | mode. */
6413 long tab_real_max_size; /* Maximum size in bytes of a column in tabular *
6414 | mode. */
6415
6416 long * col_real_max_size = NULL; /* Array of maximum sizes (bytes) of each */
6417 /* column in column mode. */
6418 long * col_max_size = NULL; /* Array of maximum sizes of each column */
6419 /* in column mode. */
6420
6421 long word_real_max_size = 0; /* size of the longer word after expansion. */
6422 long cols_real_max_size = 0; /* Max real width of all columns used when *
6423 | -w and -c are both set. */
6424 long cols_max_size = 0; /* Same as above for the columns widths */
6425
6426 long col_index = 0; /* Index of the current column when reading words, *
6427 | used in column mode. */
6428 long cols_number = 0; /* Number of columns in column mode. */
6429
6430 char * pre_selection_index = NULL; /* pattern used to set the initial *
6431 | cursor position. */
6432 unsigned char buffer[16]; /* Input buffer. */
6433
6434 search_data_t search_data;
6435 search_data.buf = NULL; /* Search buffer */
6436 search_data.len = 0; /* Current position in the search buffer */
6437 search_data.utf8_len = 0; /* Current position in the search buffer in *
6438 | UTF-8 units. */
6439 search_data.fuzzy_err = 0; /* reset the error indicator. */
6440 search_data.fuzzy_err_pos = -1; /* no last error position in search *
6441 buffer. */
6442
6443 long matching_word_cur_index = -1; /* cache for the next/previous moves *
6444 | in the matching words array. */
6445
6446 struct sigaction sa; /* Signal structure. */
6447
6448 char * iws = NULL, *ils = NULL, *zg = NULL;
6449 ll_t * word_delims_list = NULL;
6450 ll_t * zapped_glyphs_list = NULL;
6451 ll_t * record_delims_list = NULL;
6452
6453 char utf8_buffer[5]; /* buffer to store the bytes of a UTF-8 glyph *
6454 | (4 chars max). */
6455 unsigned char is_last;
6456 char * charset;
6457
6458 char * home_ini_file; /* init file full path. */
6459 char * local_ini_file; /* init file full path. */
6460
6461 charsetinfo_t * charset_ptr;
6462 langinfo_t langinfo;
6463 int is_supported_charset;
6464
6465 long line_count = 0; /* Only used when -R is selected. */
6466
6467 attrib_t init_attr;
6468
6469 ll_node_t * inc_interval_node = NULL; /* one node of this list. */
6470 ll_node_t * exc_interval_node = NULL; /* one node of this list. */
6471
6472 interval_t * inc_interval; /* the data in each node. */
6473 interval_t * exc_interval; /* the data in each node. */
6474 int row_def_selectable; /* default selectable value. */
6475
6476 int line_selected_by_regex = 0;
6477 int line_excluded = 0;
6478
6479 char * timeout_message;
6480
6481 char * common_options;
6482 char * main_options, *main_spec_options;
6483 char * col_options, *col_spec_options;
6484 char * line_options, *line_spec_options;
6485 char * tab_options, *tab_spec_options;
6486 char * tag_options, *tag_spec_options;
6487
6488 /* Used to check the usablility of the DSR terminal feature. */
6489 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6490 int row; /* absolute line position in terminal (1...) */
6491 int col; /* absolute column position in terminal (1...) */
6492
6493 /* Initialize some internal data structures. */
6494 /* """"""""""""""""""""""""""""""""""""""""" */
6495 init_main_ds(&init_attr, &win, &limits, &timers, &toggles, &misc, &timeout,
6496 &daccess);
6497
6498 /* direct access variable initialization. */
6499 /* """""""""""""""""""""""""""""""""""""" */
6500 daccess_stack = xcalloc(6, 1);
6501 daccess_stack_head = 0;
6502
6503 /* fuzzy variables initialization. */
6504 /* """"""""""""""""""""""""""""""" */
6505 tst_search_list = ll_new();
6506 ll_append(tst_search_list, sub_tst_new());
6507
6508 matching_words_a_size = 64;
6509 matching_words_a = xmalloc(matching_words_a_size * sizeof(long));
6510 matches_count = 0;
6511
6512 best_matching_words_a_size = 16;
6513 best_matching_words_a = xmalloc(best_matching_words_a_size * sizeof(long));
6514 best_matches_count = 0;
6515
6516 /* Initialize the tag hit number which will permit to sort the */
6517 /* pinned words when displayed. */
6518 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6519 long next_tag_nb = 0;
6520
6521 /* Columns selection variables. */
6522 /* """""""""""""""""""""""""""" */
6523 char * cols_filter = NULL;
6524
6525 /* Initialize the count of tagged words. */
6526 /* """"""""""""""""""""""""""""""""""""" */
6527 long tagged_words = 0;
6528
6529 /* Get the current locale. */
6530 /* """"""""""""""""""""""" */
6531 setlocale(LC_ALL, "");
6532 charset = nl_langinfo(CODESET);
6533
6534 /* Check if the local charset is supported. */
6535 /* """""""""""""""""""""""""""""""""""""""" */
6536 is_supported_charset = 0;
6537 charset_ptr = all_supported_charsets;
6538
6539 while (charset_ptr->name != NULL)
6540 {
6541 if (my_strcasecmp(charset, charset_ptr->name) == 0)
6542 {
6543 is_supported_charset = 1;
6544 langinfo.bits = charset_ptr->bits;
6545 break;
6546 }
6547 charset_ptr++;
6548 }
6549
6550 if (!is_supported_charset)
6551 {
6552 fprintf(stderr, "%s is not a supported charset.", charset);
6553
6554 exit(EXIT_FAILURE);
6555 }
6556
6557 /* Remember the fact that the charset is UTF-8. */
6558 /* """""""""""""""""""""""""""""""""""""""""""" */
6559 if (strcmp(charset, "UTF-8") == 0)
6560 langinfo.utf8 = 1;
6561 else
6562 langinfo.utf8 = 0;
6563
6564 /* my_isprint is a function pointer that points to the 7 or 8-bit */
6565 /* version of isprint according to the content of UTF-8. */
6566 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6567 if (langinfo.utf8 || langinfo.bits == 8)
6568 my_isprint = isprint8;
6569 else
6570 my_isprint = isprint7;
6571
6572 /* Set terminal in noncanonical, noecho mode and */
6573 /* if TERM is unset or unknown, vt100 is assumed. */
6574 /* """""""""""""""""""""""""""""""""""""""""""""" */
6575 if (getenv("TERM") == NULL)
6576 setupterm("vt100", 1, (int *)0);
6577 else
6578 setupterm((char *)0, 1, (int *)0);
6579
6580 /* Get the number of colors if the use of colors is available */
6581 /* and authorized. */
6582 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6583 if (getenv("NO_COLOR") != NULL)
6584 term.colors = 0;
6585 else
6586 {
6587 term.colors = tigetnum("colors");
6588 if (term.colors < 0)
6589 term.colors = 0;
6590 }
6591
6592 /* Ignore SIGTTIN. */
6593 /* """"""""""""""" */
6594 sigset_t sigs, oldsigs;
6595
6596 sigemptyset(&sigs);
6597 sigaddset(&sigs, SIGTTIN);
6598 sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
6599
6600 /* Temporarily set /dev/tty as stdin/stdout to get its size */
6601 /* even in a pipe. */
6602 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6603 old_fd0 = dup(0);
6604 old_stdin = freopen("/dev/tty", "r", stdin);
6605 old_fd1 = dup(1);
6606 old_stdout = freopen("/dev/tty", "w", stdout);
6607
6608 if (old_stdin == NULL || old_stdout == NULL)
6609 {
6610 fprintf(stderr, "A terminal is required to use this program.\n");
6611 exit(EXIT_FAILURE);
6612 }
6613
6614 /* Get the number of lines/columns of the terminal. */
6615 /* """""""""""""""""""""""""""""""""""""""""""""""" */
6616 get_terminal_size(&term.nlines, &term.ncolumns, &term);
6617
6618 /* Restore the old stdin and stdout. */
6619 /* """"""""""""""""""""""""""""""""" */
6620 dup2(old_fd0, 0);
6621 dup2(old_fd1, 1);
6622 close(old_fd0);
6623 close(old_fd1);
6624
6625 /* Default substitution character on invalid input. */
6626 /* """""""""""""""""""""""""""""""""""""""""""""""" */
6627 misc.invalid_char_substitute = '.';
6628
6629 /* Build the full path of the .ini file. */
6630 /* """"""""""""""""""""""""""""""""""""" */
6631 home_ini_file = make_ini_path(argv[0], "HOME");
6632 local_ini_file = make_ini_path(argv[0], "PWD");
6633
6634 /* Set the attributes from the configuration file if possible. */
6635 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
6636 if (ini_load(home_ini_file, &win, &term, &limits, &timers, &misc, &langinfo,
6637 ini_cb))
6638 exit(EXIT_FAILURE);
6639
6640 if (ini_load(local_ini_file, &win, &term, &limits, &timers, &misc, &langinfo,
6641 ini_cb))
6642 exit(EXIT_FAILURE);
6643
6644 free(home_ini_file);
6645 free(local_ini_file);
6646
6647 /* Command line options setting using ctxopt. */
6648 /* """""""""""""""""""""""""""""""""""""""""" */
6649 ctxopt_init(argv[0], "stop_if_non_option=No "
6650 "allow_abbreviations=No "
6651 "display_usage_on_error=Yes ");
6652
6653 common_options = "[*help] "
6654 "[*usage] "
6655 "[include_re... #regex] "
6656 "[exclude_re... #regex] "
6657 "[title #message] "
6658 "[int [#string]] "
6659 "[attributes #prefix:attr...] "
6660 "[special_level_1 #...<3] "
6661 "[special_level_2 #...<3] "
6662 "[special_level_3 #...<3] "
6663 "[special_level_4 #...<3] "
6664 "[special_level_5 #...<3] "
6665 "[special_level_6 #...<3] "
6666 "[special_level_7 #...<3] "
6667 "[special_level_8 #...<3] "
6668 "[special_level_9 #...<3] "
6669 "[zapped_glyphs #bytes] "
6670 "[lines [#height]] "
6671 "[blank_nonprintable] "
6672 "[*invalid_character #invalid_char_subst] "
6673 "[center_mode] "
6674 "[clean] "
6675 "[keep_spaces] "
6676 "[word_separators #bytes] "
6677 "[no_scroll_bar] "
6678 "[early_subst_all... #/regex/repl/opts] "
6679 "[post_subst_all... #/regex/repl/opts] "
6680 "[post_subst_included... #/regex/repl/opts] "
6681 "[post_subst_excluded... #/regex/repl/opts] "
6682 "[search_method #prefix|substring|fuzzy] "
6683 "[start_pattern #pattern] "
6684 "[timeout #...] "
6685 "[hidden_timeout #...] "
6686 "[validate_in_search_mode] "
6687 "[visual_bell] "
6688 "[ignore_quotes] "
6689 "[incremental_search] "
6690 "[limits #limit:value...] "; /* keep the last space! */
6691
6692 main_spec_options = "[*version] "
6693 "[*long_help] "
6694 "[da_options #prefix:attr...] "
6695 "[auto_da_number... [#regex...]] "
6696 "[auto_da_unnumber... [#regex...]] "
6697 "[field_da_number] "
6698 "[column_mode>Columns] "
6699 "[line_mode>Lines] "
6700 "[tab_mode>Tabulations [#cols]] "
6701 "[tag_mode>Tagging [#delim]] "
6702 "[pin_mode>Tagging [#delim]]";
6703
6704 col_spec_options = "[wide_mode] "
6705 "[columns_select... #selector...] "
6706 "[rows_select... #selector...] "
6707 "[gutter [#string]] "
6708 "[line_separators #bytes] "
6709 "[da_options #prefix:attr...] "
6710 "[auto_da_number... [#regex...]] "
6711 "[auto_da_unnumber... [#regex...]] "
6712 "[field_da_number] "
6713 "[tag_mode>Tagging [#delim]] "
6714 "[pin_mode>Tagging [#delim]] "
6715 "[force_first_column #regex] "
6716 "[force_last_column #regex]";
6717
6718 line_spec_options = "[rows_select... #selector...] "
6719 "[line_separators #bytes] "
6720 "[da_options #prefix:attr...] "
6721 "[auto_da_number... [#regex...]] "
6722 "[auto_da_unnumber... [#regex...]] "
6723 "[field_da_number] "
6724 "[tag_mode>Tagging [#delim]] "
6725 "[pin_mode>Tagging [#delim]] "
6726 "[force_first_column #regex] "
6727 "[force_last_column #regex]";
6728
6729 tab_spec_options = "[wide_mode] "
6730 "[gutter [#string]] "
6731 "[line_separators #bytes] "
6732 "[da_options #prefix:attr...] "
6733 "[auto_da_number... [#regex...]] "
6734 "[auto_da_unnumber... [#regex...]] "
6735 "[field_da_number] "
6736 "[tag_mode>Tagging [#delim]] "
6737 "[pin_mode>Tagging [#delim]] "
6738 "[force_first_column #regex] "
6739 "[force_last_column #regex]";
6740
6741 tag_spec_options = "[auto_tag] "
6742 "[no_auto_tag] "
6743 "[column_mode>Columns] "
6744 "[line_mode>Lines] "
6745 "[tab_mode>Tabulations [#cols]]";
6746
6747 main_options = concat(common_options, main_spec_options, (char *)0);
6748 col_options = concat(common_options, col_spec_options, (char *)0);
6749 line_options = concat(common_options, line_spec_options, (char *)0);
6750 tab_options = concat(common_options, tab_spec_options, (char *)0);
6751 tag_options = concat(common_options, tag_spec_options, (char *)0);
6752
6753 ctxopt_new_ctx("Main", main_options);
6754 ctxopt_new_ctx("Columns", col_options);
6755 ctxopt_new_ctx("Lines", line_options);
6756 ctxopt_new_ctx("Tabulations", tab_options);
6757 ctxopt_new_ctx("Tagging", tag_options);
6758
6759 free(main_options);
6760 free(col_options);
6761 free(line_options);
6762 free(tab_options);
6763 free(tag_options);
6764
6765 /* ctxopt parameters. */
6766 /* """""""""""""""""" */
6767
6768 ctxopt_add_opt_settings(parameters, "help", "-h -help");
6769 ctxopt_add_opt_settings(parameters, "long_help", "-H -long-help");
6770 ctxopt_add_opt_settings(parameters, "usage", "-? -u -usage");
6771 ctxopt_add_opt_settings(parameters, "version", "-V -version");
6772 ctxopt_add_opt_settings(parameters, "include_re",
6773 "-i -in -inc -incl -include");
6774 ctxopt_add_opt_settings(parameters, "exclude_re",
6775 "-e -ex -exc -excl -exclude");
6776 ctxopt_add_opt_settings(parameters, "lines", "-n -lines -height");
6777 ctxopt_add_opt_settings(parameters, "title", "-m -msg -message -title");
6778 ctxopt_add_opt_settings(parameters, "int", "-! -int -int_string");
6779 ctxopt_add_opt_settings(parameters, "attributes", "-a -attr -attributes");
6780 ctxopt_add_opt_settings(parameters, "special_level_1", "-1 -l1 -level1");
6781 ctxopt_add_opt_settings(parameters, "special_level_2", "-2 -l2 -level2");
6782 ctxopt_add_opt_settings(parameters, "special_level_3", "-3 -l3 -level3");
6783 ctxopt_add_opt_settings(parameters, "special_level_4", "-4 -l4 -level4");
6784 ctxopt_add_opt_settings(parameters, "special_level_5", "-5 -l5 -level5");
6785 ctxopt_add_opt_settings(parameters, "special_level_6", "-6 -l6 -level6");
6786 ctxopt_add_opt_settings(parameters, "special_level_7", "-7 -l7 -level7");
6787 ctxopt_add_opt_settings(parameters, "special_level_8", "-8 -l8 -level8");
6788 ctxopt_add_opt_settings(parameters, "special_level_9", "-9 -l9 -level9");
6789 ctxopt_add_opt_settings(parameters, "tag_mode", "-T -tm -tag -tag_mode");
6790 ctxopt_add_opt_settings(parameters, "pin_mode", "-P -pm -pin -pin_mode");
6791 ctxopt_add_opt_settings(parameters, "auto_tag", "-p -at -auto_tag");
6792 ctxopt_add_opt_settings(parameters, "no_auto_tag", "-0 -noat -no_auto_tag");
6793 ctxopt_add_opt_settings(parameters, "auto_da_number", "-N -number");
6794 ctxopt_add_opt_settings(parameters, "auto_da_unnumber", "-U -unnumber");
6795 ctxopt_add_opt_settings(parameters, "field_da_number",
6796 "-F -en -embedded_number");
6797 ctxopt_add_opt_settings(parameters, "da_options", "-D -data -options");
6798 ctxopt_add_opt_settings(parameters, "invalid_character", "-. -dot -invalid");
6799 ctxopt_add_opt_settings(parameters, "blank_nonprintable", "-b -blank");
6800 ctxopt_add_opt_settings(parameters, "center_mode", "-M -middle -center");
6801 ctxopt_add_opt_settings(parameters, "clean",
6802 "-d -restore -delete -clean "
6803 "-delete_window -clean_window");
6804 ctxopt_add_opt_settings(parameters, "column_mode",
6805 "-c -col -col_mode -column");
6806 ctxopt_add_opt_settings(parameters, "line_mode", "-l -line -line_mode");
6807 ctxopt_add_opt_settings(parameters, "tab_mode",
6808 "-t -tab -tab_mode -tabulate_mode");
6809 ctxopt_add_opt_settings(parameters, "wide_mode", "-w -wide -wide_mode");
6810 ctxopt_add_opt_settings(parameters, "columns_select",
6811 "-C -cs -cols -cols_select");
6812 ctxopt_add_opt_settings(parameters, "rows_select",
6813 "-R -rs -rows -rows_select");
6814 ctxopt_add_opt_settings(parameters, "force_first_column",
6815 "-A -fc -first_column");
6816 ctxopt_add_opt_settings(parameters, "force_last_column",
6817 "-Z -lc -last_column");
6818 ctxopt_add_opt_settings(parameters, "gutter", "-g -gutter");
6819 ctxopt_add_opt_settings(parameters, "keep_spaces", "-k -ks -keep_spaces");
6820 ctxopt_add_opt_settings(parameters, "word_separators",
6821 "-W -ws -wd -word_delimiters -word_separators");
6822 ctxopt_add_opt_settings(parameters, "line_separators",
6823 "-L -ls -ld -line-delimiters -line_separators");
6824 ctxopt_add_opt_settings(parameters, "zapped_glyphs", "-z -zap -zap-glyphs");
6825 ctxopt_add_opt_settings(parameters, "no_scroll_bar",
6826 "-q -no_bar -no-scroll_bar");
6827 ctxopt_add_opt_settings(parameters, "early_subst_all", "-ES -early_subst");
6828 ctxopt_add_opt_settings(parameters, "post_subst_all", "-S -subst");
6829 ctxopt_add_opt_settings(parameters, "post_subst_included",
6830 "-I -si -subst_included");
6831 ctxopt_add_opt_settings(parameters, "post_subst_excluded",
6832 "-E -se -subst_excluded");
6833 ctxopt_add_opt_settings(parameters, "search_method", "-/ -search_method");
6834 ctxopt_add_opt_settings(parameters, "start_pattern",
6835 "-s -sp -start -start_pattern");
6836 ctxopt_add_opt_settings(parameters, "timeout", "-x -tmout -timeout");
6837 ctxopt_add_opt_settings(parameters, "hidden_timeout",
6838 "-X -htmout -hidden_timeout");
6839 ctxopt_add_opt_settings(parameters, "validate_in_search_mode",
6840 "-r -auto_validate");
6841 ctxopt_add_opt_settings(parameters, "visual_bell", "-v -vb -visual_bell");
6842 ctxopt_add_opt_settings(parameters, "ignore_quotes", "-Q -ignore_quotes");
6843 ctxopt_add_opt_settings(parameters, "incremental_search",
6844 "-is -incremental_search");
6845 ctxopt_add_opt_settings(parameters, "limits", "-lim -limits");
6846
6847 /* ctxopt options incompatibilities. */
6848 /* """"""""""""""""""""""""""""""""" */
6849
6850 ctxopt_add_ctx_settings(incompatibilities, "Main",
6851 "column_mode line_mode tab_mode");
6852 ctxopt_add_ctx_settings(incompatibilities, "Main", "tag_mode pin_mode");
6853 ctxopt_add_ctx_settings(incompatibilities, "Main", "help usage");
6854 ctxopt_add_ctx_settings(incompatibilities, "Main", "timeout hidden_timeout");
6855
6856 /* ctxopt options requirements. */
6857 /* """""""""""""""""""""""""""" */
6858
6859 ctxopt_add_ctx_settings(requirements, "Main",
6860 "da_options "
6861 "field_da_number auto_da_number auto_da_unnumber");
6862 ctxopt_add_ctx_settings(requirements, "Columns",
6863 "da_options "
6864 "field_da_number auto_da_number auto_da_unnumber");
6865 ctxopt_add_ctx_settings(requirements, "Lines",
6866 "da_options "
6867 "field_da_number auto_da_number auto_da_unnumber");
6868 ctxopt_add_ctx_settings(requirements, "Tabulations",
6869 "da_options "
6870 "field_da_number auto_da_number auto_da_unnumber");
6871
6872 /* ctxopt actions. */
6873 /* """"""""""""""" */
6874
6875 ctxopt_add_opt_settings(actions, "auto_tag", toggle_action, &toggles,
6876 (char *)0);
6877 ctxopt_add_opt_settings(actions, "no_auto_tag", toggle_action, &toggles,
6878 (char *)0);
6879 ctxopt_add_opt_settings(actions, "invalid_character", invalid_char_action,
6880 &misc, (char *)0);
6881 ctxopt_add_opt_settings(actions, "blank_nonprintable", toggle_action,
6882 &toggles, (char *)0);
6883 ctxopt_add_opt_settings(actions, "center_mode", center_mode_action, &win,
6884 (char *)0);
6885 ctxopt_add_opt_settings(actions, "clean", toggle_action, &toggles, (char *)0);
6886 ctxopt_add_opt_settings(actions, "column_mode", column_mode_action, &win,
6887 (char *)0);
6888 ctxopt_add_opt_settings(actions, "line_mode", line_mode_action, &win,
6889 (char *)0);
6890 ctxopt_add_opt_settings(actions, "tab_mode", tab_mode_action, &win,
6891 (char *)0);
6892 ctxopt_add_opt_settings(actions, "columns_select", columns_select_action,
6893 &cols_selector_list, (char *)0);
6894 ctxopt_add_opt_settings(actions, "rows_select", rows_select_action,
6895 &rows_selector_list, &win, (char *)0);
6896 ctxopt_add_opt_settings(actions, "include_re", include_re_action,
6897 &pattern_def_include, &include_pattern, &langinfo,
6898 &misc, (char *)0);
6899 ctxopt_add_opt_settings(actions, "exclude_re", exclude_re_action,
6900 &pattern_def_include, &exclude_pattern, &langinfo,
6901 &misc, (char *)0);
6902 ctxopt_add_opt_settings(actions, "gutter", gutter_action, &win, &langinfo,
6903 &misc, (char *)0);
6904 ctxopt_add_opt_settings(actions, "help", help_action, (char *)0);
6905 ctxopt_add_opt_settings(actions, "long_help", long_help_action, (char *)0);
6906 ctxopt_add_opt_settings(actions, "usage", usage_action, (char *)0);
6907 ctxopt_add_opt_settings(actions, "keep_spaces", toggle_action, &toggles,
6908 (char *)0);
6909 ctxopt_add_opt_settings(actions, "lines", lines_action, &win, (char *)0);
6910 ctxopt_add_opt_settings(actions, "no_scroll_bar", toggle_action, &toggles,
6911 (char *)0);
6912 ctxopt_add_opt_settings(actions, "start_pattern", set_pattern_action,
6913 &pre_selection_index, &langinfo, &misc, (char *)0);
6914 ctxopt_add_opt_settings(actions, "title", set_string_action, &message,
6915 &langinfo, &misc, (char *)0);
6916 ctxopt_add_opt_settings(actions, "int", int_action, &int_string,
6917 &int_as_in_shell, &langinfo, &misc, (char *)0);
6918 ctxopt_add_opt_settings(actions, "validate_in_search_mode", toggle_action,
6919 &toggles, (char *)0);
6920 ctxopt_add_opt_settings(actions, "version", version_action, (char *)0);
6921 ctxopt_add_opt_settings(actions, "visual_bell", toggle_action, &toggles,
6922 (char *)0);
6923 ctxopt_add_opt_settings(actions, "incremental_search", toggle_action,
6924 &toggles, (char *)0);
6925 ctxopt_add_opt_settings(actions, "wide_mode", wide_mode_action, &win,
6926 (char *)0);
6927 ctxopt_add_opt_settings(actions, "early_subst_all", post_subst_action,
6928 &early_sed_list, &langinfo, &misc, (char *)0);
6929 ctxopt_add_opt_settings(actions, "post_subst_all", post_subst_action,
6930 &sed_list, &langinfo, &misc, (char *)0);
6931 ctxopt_add_opt_settings(actions, "post_subst_included", post_subst_action,
6932 &include_sed_list, &langinfo, &misc, (char *)0);
6933 ctxopt_add_opt_settings(actions, "post_subst_excluded", post_subst_action,
6934 &exclude_sed_list, &langinfo, &misc, (char *)0);
6935 ctxopt_add_opt_settings(actions, "special_level_1", special_level_action,
6936 special_pattern, &win, &term, &langinfo, &init_attr,
6937 &misc, (char *)0);
6938 ctxopt_add_opt_settings(actions, "special_level_2", special_level_action,
6939 special_pattern, &win, &term, &langinfo, &init_attr,
6940 &misc, (char *)0);
6941 ctxopt_add_opt_settings(actions, "special_level_3", special_level_action,
6942 special_pattern, &win, &term, &langinfo, &init_attr,
6943 &misc, (char *)0);
6944 ctxopt_add_opt_settings(actions, "special_level_4", special_level_action,
6945 special_pattern, &win, &term, &langinfo, &init_attr,
6946 &misc, (char *)0);
6947 ctxopt_add_opt_settings(actions, "special_level_5", special_level_action,
6948 special_pattern, &win, &term, &langinfo, &init_attr,
6949 &misc, (char *)0);
6950 ctxopt_add_opt_settings(actions, "special_level_6", special_level_action,
6951 special_pattern, &win, &term, &langinfo, &init_attr,
6952 &misc, (char *)0);
6953 ctxopt_add_opt_settings(actions, "special_level_7", special_level_action,
6954 special_pattern, &win, &term, &langinfo, &init_attr,
6955 &misc, (char *)0);
6956 ctxopt_add_opt_settings(actions, "special_level_8", special_level_action,
6957 special_pattern, &win, &term, &langinfo, &init_attr,
6958 &misc, (char *)0);
6959 ctxopt_add_opt_settings(actions, "special_level_9", special_level_action,
6960 special_pattern, &win, &term, &langinfo, &init_attr,
6961 &misc, (char *)0);
6962 ctxopt_add_opt_settings(actions, "attributes", attributes_action, &win, &term,
6963 &init_attr, (char *)0);
6964 ctxopt_add_opt_settings(actions, "timeout", timeout_action, &langinfo, &misc,
6965 (char *)0);
6966 ctxopt_add_opt_settings(actions, "hidden_timeout", timeout_action, &langinfo,
6967 (char *)0);
6968 ctxopt_add_opt_settings(actions, "force_first_column", set_pattern_action,
6969 &first_word_pattern, &langinfo, &misc, (char *)0);
6970 ctxopt_add_opt_settings(actions, "force_last_column", set_pattern_action,
6971 &last_word_pattern, &langinfo, &misc, (char *)0);
6972 ctxopt_add_opt_settings(actions, "word_separators", set_pattern_action, &iws,
6973 &langinfo, &misc, (char *)0);
6974 ctxopt_add_opt_settings(actions, "line_separators", set_pattern_action, &ils,
6975 &langinfo, &misc, (char *)0);
6976 ctxopt_add_opt_settings(actions, "zapped_glyphs", set_pattern_action, &zg,
6977 &langinfo, &misc, (char *)0);
6978 ctxopt_add_opt_settings(actions, "tag_mode", tag_mode_action, &toggles, &win,
6979 &langinfo, &misc, (char *)0);
6980 ctxopt_add_opt_settings(actions, "pin_mode", pin_mode_action, &toggles, &win,
6981 &langinfo, &misc, (char *)0);
6982 ctxopt_add_opt_settings(actions, "search_method", search_method_action, &misc,
6983 (char *)0);
6984 ctxopt_add_opt_settings(actions, "auto_da_number", auto_da_action,
6985 &daccess_np, (char *)0);
6986 ctxopt_add_opt_settings(actions, "auto_da_unnumber", auto_da_action,
6987 &daccess_up, (char *)0);
6988 ctxopt_add_opt_settings(actions, "field_da_number", field_da_number_action,
6989 (char *)0);
6990 ctxopt_add_opt_settings(actions, "da_options", da_options_action, &langinfo,
6991 &daccess_index, &misc, (char *)0);
6992 ctxopt_add_opt_settings(actions, "ignore_quotes", ignore_quotes_action, &misc,
6993 (char *)0);
6994 ctxopt_add_opt_settings(actions, "limits", limits_action, &limits, (char *)0);
6995
6996 /* ctxopt constraints. */
6997 /* """"""""""""""""""" */
6998
6999 ctxopt_add_opt_settings(constraints, "attributes", ctxopt_re_constraint,
7000 "[^:]+:.+");
7001 ctxopt_add_opt_settings(constraints, "da_options", ctxopt_re_constraint,
7002 "[^:]+:.+");
7003 ctxopt_add_opt_settings(constraints, "lines", check_integer_constraint, "");
7004
7005 ctxopt_add_opt_settings(constraints, "tab_mode", check_integer_constraint,
7006 "");
7007 ctxopt_add_opt_settings(constraints, "tab_mode", ctxopt_range_constraint,
7008 "1 .");
7009
7010 /* Evaluation order. */
7011 /* """"""""""""""""" */
7012
7013 ctxopt_add_opt_settings(after, "field_da_number",
7014 "auto_da_number auto_da_unnumber");
7015
7016 ctxopt_add_opt_settings(after, "da_options",
7017 "field_da_number auto_da_number auto_da_unnumber");
7018
7019 /* Command line options analysis. */
7020 /* """""""""""""""""""""""""""""" */
7021 ctxopt_analyze(argc - 1, argv + 1, &nb_rem_args, &rem_args);
7022
7023 /* Command line options evaluation. */
7024 /* """""""""""""""""""""""""""""""" */
7025 ctxopt_evaluate();
7026
7027 /* Check remaining non analyzed command line arguments. */
7028 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
7029 if (nb_rem_args == 1)
7030 {
7031 input_file = fopen(rem_args[0], "r");
7032 if (input_file == NULL)
7033 {
7034 fprintf(stderr, "The file \"%s\" does not exist or cannot be read.\n",
7035 rem_args[0]);
7036 ctxopt_disp_usage(exit_after);
7037 exit(EXIT_FAILURE); /* Avoid a compiler warning. */
7038 }
7039 }
7040 else if (nb_rem_args == 0)
7041 input_file = stdin;
7042 else
7043 {
7044 fprintf(stderr, "Extra arguments detected:\n");
7045
7046 fprintf(stderr, "%s", rem_args[1]);
7047 for (int i = 2; i < nb_rem_args; i++)
7048 fprintf(stderr, ", %s", rem_args[i]);
7049 fprintf(stderr, ".\n");
7050
7051 ctxopt_disp_usage(exit_after);
7052 exit(EXIT_FAILURE); /* Avoid a compiler warning. */
7053 }
7054
7055 /* Free the memory used internally by ctxopt. */
7056 /* """""""""""""""""""""""""""""""""""""""""" */
7057 ctxopt_free_memory();
7058
7059 /* If we did not impose the number of columns, use the whole */
7060 /* terminal width. */
7061 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7062 if (win.tab_mode && !win.max_cols)
7063 win.wide = 1;
7064
7065 win.start = 0;
7066
7067 term.color_method = 1; /* We default to setaf/setbf to set colors. */
7068 term.curs_line = term.curs_column = 0;
7069
7070 {
7071 char * str;
7072
7073 str = tigetstr("cuu1");
7074 term.has_cursor_up = (str == (char *)-1 || str == NULL) ? 0 : 1;
7075 str = tigetstr("cud1");
7076 term.has_cursor_down = (str == (char *)-1 || str == NULL) ? 0 : 1;
7077 str = tigetstr("cub1");
7078 term.has_cursor_left = (str == (char *)-1 || str == NULL) ? 0 : 1;
7079 str = tigetstr("cuf1");
7080 term.has_cursor_right = (str == (char *)-1 || str == NULL) ? 0 : 1;
7081 str = tigetstr("cup");
7082 term.has_cursor_address = (str == (char *)-1 || str == NULL) ? 0 : 1;
7083 str = tigetstr("sc");
7084 term.has_save_cursor = (str == (char *)-1 || str == NULL) ? 0 : 1;
7085 str = tigetstr("rc");
7086 term.has_restore_cursor = (str == (char *)-1 || str == NULL) ? 0 : 1;
7087 str = tigetstr("setf");
7088 term.has_setf = (str == (char *)-1 || str == NULL) ? 0 : 1;
7089 str = tigetstr("setb");
7090 term.has_setb = (str == (char *)-1 || str == NULL) ? 0 : 1;
7091 str = tigetstr("setaf");
7092 term.has_setaf = (str == (char *)-1 || str == NULL) ? 0 : 1;
7093 str = tigetstr("setab");
7094 term.has_setab = (str == (char *)-1 || str == NULL) ? 0 : 1;
7095 str = tigetstr("hpa");
7096 term.has_hpa = (str == (char *)-1 || str == NULL) ? 0 : 1;
7097 str = tigetstr("cuf");
7098 term.has_parm_right_cursor = (str == (char *)-1 || str == NULL) ? 0 : 1;
7099 str = tigetstr("bold");
7100 term.has_bold = (str == (char *)-1 || str == NULL) ? 0 : 1;
7101 str = tigetstr("dim");
7102 term.has_dim = (str == (char *)-1 || str == NULL) ? 0 : 1;
7103 str = tigetstr("rev");
7104 term.has_reverse = (str == (char *)-1 || str == NULL) ? 0 : 1;
7105 str = tigetstr("smul");
7106 term.has_underline = (str == (char *)-1 || str == NULL) ? 0 : 1;
7107 str = tigetstr("smso");
7108 term.has_standout = (str == (char *)-1 || str == NULL) ? 0 : 1;
7109 str = tigetstr("sitm");
7110 term.has_italic = (str == (char *)-1 || str == NULL) ? 0 : 1;
7111 str = tigetstr("blink");
7112 term.has_blink = (str == (char *)-1 || str == NULL) ? 0 : 1;
7113 }
7114
7115 if (!term.has_cursor_up || !term.has_cursor_down || !term.has_cursor_left
7116 || !term.has_cursor_right || !term.has_save_cursor
7117 || !term.has_restore_cursor)
7118 {
7119 fprintf(stderr, "The terminal does not have the required cursor "
7120 "management capabilities.\n");
7121
7122 exit(EXIT_FAILURE);
7123 }
7124
7125 word_buffer = xcalloc(1, daccess.flength + limits.word_length + 1);
7126
7127 /* default_search_method is not set in the command line nor in a config */
7128 /* file, set it to fuzzy. */
7129 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7130 if (misc.default_search_method == NONE)
7131 misc.default_search_method = FUZZY;
7132
7133 /* If some attributes were not set, set their default values. */
7134 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7135 if (term.colors > 7)
7136 {
7137 int special_def_attr[9] = { 1, 2, 3, 5, 6, 7, 7, 7, 7 };
7138
7139 if (!win.cursor_attr.is_set)
7140 {
7141 if (term.has_reverse)
7142 win.cursor_attr.reverse = 1;
7143 else if (term.has_standout)
7144 win.cursor_attr.standout = 1;
7145 else
7146 {
7147 win.cursor_attr.fg = 0;
7148 win.cursor_attr.bg = 1;
7149 }
7150
7151 win.cursor_attr.is_set = SET;
7152 }
7153
7154 if (!win.cursor_on_tag_attr.is_set)
7155 {
7156 if (term.has_reverse)
7157 win.cursor_on_tag_attr.reverse = 1;
7158
7159 if (term.has_underline)
7160 win.cursor_on_tag_attr.underline = 1;
7161 else
7162 win.cursor_on_tag_attr.fg = 2;
7163
7164 win.cursor_on_tag_attr.is_set = SET;
7165 }
7166
7167 if (!win.bar_attr.is_set)
7168 {
7169 win.bar_attr.fg = 2;
7170 win.bar_attr.is_set = SET;
7171 }
7172
7173 if (!win.shift_attr.is_set)
7174 {
7175 win.shift_attr.fg = 2;
7176 win.shift_attr.is_set = SET;
7177 }
7178
7179 if (!win.message_attr.is_set)
7180 {
7181 if (term.has_bold)
7182 win.message_attr.bold = 1;
7183 else if (term.has_reverse)
7184 win.message_attr.reverse = 1;
7185 else
7186 {
7187 win.message_attr.fg = 0;
7188 win.message_attr.bg = 7;
7189 }
7190
7191 win.message_attr.is_set = SET;
7192 }
7193
7194 if (!win.search_field_attr.is_set)
7195 {
7196 win.search_field_attr.bg = 5;
7197 win.search_field_attr.is_set = SET;
7198 }
7199
7200 if (!win.search_text_attr.is_set)
7201 {
7202 win.search_text_attr.fg = 0;
7203 win.search_text_attr.bg = 6;
7204
7205 win.search_text_attr.is_set = SET;
7206 }
7207
7208 if (!win.search_err_field_attr.is_set)
7209 {
7210 win.search_err_field_attr.bg = 1;
7211 win.search_err_field_attr.is_set = SET;
7212 }
7213
7214 if (!win.search_err_text_attr.is_set)
7215 {
7216 if (term.has_reverse)
7217 win.search_err_text_attr.reverse = 1;
7218
7219 win.search_err_text_attr.fg = 1;
7220 win.search_err_text_attr.is_set = SET;
7221 }
7222
7223 if (!win.match_field_attr.is_set)
7224 {
7225 win.match_field_attr.is_set = SET;
7226 }
7227
7228 if (!win.match_text_attr.is_set)
7229 {
7230 win.match_text_attr.fg = 5;
7231 win.match_text_attr.is_set = SET;
7232 }
7233
7234 if (!win.match_err_field_attr.is_set)
7235 {
7236 win.match_err_field_attr.is_set = SET;
7237 }
7238
7239 if (!win.match_err_text_attr.is_set)
7240 {
7241 win.match_err_text_attr.fg = 1;
7242 win.match_err_text_attr.is_set = SET;
7243 }
7244
7245 if (!win.exclude_attr.is_set)
7246 {
7247 win.exclude_attr.fg = 6;
7248
7249 win.exclude_attr.is_set = SET;
7250 }
7251
7252 /* This attribute should complete the attributes already set. */
7253 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7254 if (!win.tag_attr.is_set)
7255 {
7256 if (term.has_underline)
7257 win.tag_attr.underline = 1;
7258 else if (term.has_bold)
7259 win.tag_attr.bold = 1;
7260 else
7261 win.tag_attr.fg = 2;
7262
7263 win.tag_attr.is_set = SET;
7264 }
7265
7266 if (!win.daccess_attr.is_set)
7267 {
7268 if (term.has_bold)
7269 win.daccess_attr.bold = 1;
7270
7271 win.daccess_attr.is_set = SET;
7272 }
7273
7274 for (index = 0; index < 9; index++)
7275 {
7276 if (!win.special_attr[index].is_set)
7277 {
7278 win.special_attr[index].fg = special_def_attr[index];
7279 win.special_attr[index].is_set = SET;
7280 }
7281 }
7282 }
7283 else
7284 {
7285 if (!win.cursor_attr.is_set)
7286 {
7287 if (term.has_reverse)
7288 win.cursor_attr.reverse = 1;
7289
7290 win.cursor_attr.is_set = SET;
7291 }
7292
7293 if (!win.cursor_on_tag_attr.is_set)
7294 {
7295 if (term.has_reverse)
7296 win.cursor_on_tag_attr.reverse = 1;
7297
7298 if (term.has_underline)
7299 win.cursor_on_tag_attr.underline = 1;
7300 else if (term.has_bold)
7301 win.cursor_on_tag_attr.bold = 1;
7302
7303 win.cursor_on_tag_attr.is_set = SET;
7304 }
7305
7306 if (!win.bar_attr.is_set)
7307 {
7308 if (term.has_bold)
7309 win.bar_attr.bold = 1;
7310
7311 win.bar_attr.is_set = SET;
7312 }
7313
7314 if (!win.shift_attr.is_set)
7315 {
7316 if (term.has_reverse)
7317 win.shift_attr.reverse = 1;
7318
7319 win.shift_attr.is_set = SET;
7320 }
7321
7322 if (!win.message_attr.is_set)
7323 {
7324 if (term.has_bold)
7325 win.message_attr.bold = 1;
7326 else if (term.has_reverse)
7327 win.message_attr.reverse = 1;
7328
7329 win.message_attr.is_set = SET;
7330 }
7331
7332 if (!win.search_field_attr.is_set)
7333 {
7334 if (term.has_reverse)
7335 win.search_field_attr.reverse = 1;
7336
7337 win.search_field_attr.is_set = SET;
7338 }
7339
7340 if (!win.search_text_attr.is_set)
7341 {
7342 if (term.has_bold)
7343 win.search_text_attr.bold = 1;
7344
7345 win.search_text_attr.is_set = SET;
7346 }
7347
7348 if (!win.search_err_field_attr.is_set)
7349 {
7350 if (term.has_bold)
7351 win.search_err_field_attr.bold = 1;
7352
7353 win.search_err_field_attr.is_set = SET;
7354 }
7355
7356 if (!win.search_err_text_attr.is_set)
7357 {
7358 if (term.has_reverse)
7359 win.search_err_text_attr.reverse = 1;
7360
7361 win.search_err_text_attr.is_set = SET;
7362 }
7363
7364 if (!win.match_field_attr.is_set)
7365 {
7366 if (term.has_bold)
7367 win.match_field_attr.bold = 1;
7368 else if (term.has_reverse)
7369 win.match_field_attr.reverse = 1;
7370
7371 win.match_field_attr.is_set = SET;
7372 }
7373
7374 if (!win.match_text_attr.is_set)
7375 {
7376 if (term.has_reverse)
7377 win.match_text_attr.reverse = 1;
7378 else if (term.has_bold)
7379 win.match_text_attr.bold = 1;
7380
7381 win.match_text_attr.is_set = SET;
7382 }
7383
7384 if (!win.exclude_attr.is_set)
7385 {
7386 if (term.has_dim)
7387 win.exclude_attr.dim = 1;
7388 else if (term.has_italic)
7389 win.exclude_attr.italic = 1;
7390 else if (term.has_bold)
7391 win.exclude_attr.bold = 1;
7392
7393 win.exclude_attr.is_set = SET;
7394 }
7395
7396 if (!win.tag_attr.is_set)
7397 {
7398 if (term.has_underline)
7399 win.tag_attr.underline = 1;
7400 else if (term.has_standout)
7401 win.tag_attr.standout = 1;
7402 else if (term.has_reverse)
7403 win.tag_attr.reverse = 1;
7404
7405 win.tag_attr.is_set = SET;
7406 }
7407
7408 if (!win.daccess_attr.is_set)
7409 {
7410 if (term.has_bold)
7411 win.daccess_attr.bold = 1;
7412
7413 win.daccess_attr.is_set = SET;
7414 }
7415
7416 for (index = 0; index < 9; index++)
7417 {
7418 if (!win.special_attr[index].is_set)
7419 {
7420 if (term.has_bold)
7421 win.special_attr[index].bold = 1;
7422 else if (term.has_standout)
7423 win.special_attr[index].standout = 1;
7424
7425 win.special_attr[index].is_set = SET;
7426 }
7427 }
7428 }
7429
7430 /* Initialize the timeout message when the x/X option is set. */
7431 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7432 if (!quiet_timeout && timeout.initial_value > 0)
7433 {
7434 switch (timeout.mode)
7435 {
7436 case QUIT:
7437 timeout_message = xstrdup(
7438 "[ s before quitting without selecting anything]");
7439 break;
7440 case CURRENT:
7441 timeout_message = xstrdup(
7442 "[ s before selecting the current highlighted word]");
7443 break;
7444 case WORD:
7445 {
7446 char * s = "[ s before selecting the word \"";
7447
7448 timeout_message = xcalloc(1, 4 + strlen(s) + strlen(timeout_word));
7449
7450 strcpy(timeout_message, s);
7451 strcat(timeout_message, timeout_word);
7452 strcat(timeout_message, "\"]");
7453
7454 break;
7455 }
7456
7457 default:
7458 /* The other cases are impossible due to options analysis. */
7459 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
7460 timeout_message = xstrdup(" "); /* Just in case. */
7461 }
7462
7463 timeout_seconds = xcalloc(1, 6);
7464 sprintf(timeout_seconds, "%5u", timeout.initial_value / FREQ);
7465 memcpy(timeout_message + 1, timeout_seconds, 5);
7466
7467 message_lines_list = ll_new();
7468
7469 if (message)
7470 {
7471 long len;
7472
7473 get_message_lines(message, message_lines_list, &message_max_width,
7474 &message_max_len);
7475 ll_append(message_lines_list, timeout_message);
7476
7477 if ((len = strlen(timeout_message)) > message_max_len)
7478 message_max_len = message_max_width = len;
7479 }
7480 else
7481 {
7482 ll_append(message_lines_list, timeout_message);
7483 message_max_len = message_max_width = strlen(timeout_message);
7484 }
7485 }
7486 else if (message)
7487 {
7488 message_lines_list = ll_new();
7489 get_message_lines(message, message_lines_list, &message_max_width,
7490 &message_max_len);
7491 }
7492
7493 /* Force the maximum number of window's line if -n is used. */
7494 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7495 if (term.nlines <= win.message_lines)
7496 {
7497 win.message_lines = term.nlines - 1;
7498 win.max_lines = 1;
7499 }
7500 else if (win.asked_max_lines >= 0)
7501 {
7502 if (win.asked_max_lines == 0)
7503 win.max_lines = term.nlines - win.message_lines;
7504 else
7505 {
7506 if (win.asked_max_lines > term.nlines - win.message_lines)
7507 win.max_lines = term.nlines - win.message_lines;
7508 else
7509 win.max_lines = win.asked_max_lines;
7510 }
7511 }
7512 else /* -n was not used. Set win.asked_max_lines to its default value. */
7513 win.asked_max_lines = win.max_lines;
7514
7515 /* Allocate the memory for our words structures. */
7516 /* """"""""""""""""""""""""""""""""""""""""""""" */
7517 word_a = xmalloc(WORDSCHUNK * sizeof(word_t));
7518
7519 /* Fill an array of word_t elements obtained from stdin. */
7520 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
7521 tab_real_max_size = 0;
7522 tab_max_size = 0;
7523 min_size = 0;
7524
7525 /* Parse the list of glyphs to be zapped (option -z). */
7526 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
7527 zapped_glyphs_list = ll_new();
7528 if (zg != NULL)
7529 {
7530 int utf8_len;
7531 char * zg_ptr = zg;
7532 char * tmp;
7533
7534 utf8_len = mblen(zg_ptr, 4);
7535
7536 while (utf8_len != 0)
7537 {
7538 tmp = xmalloc(utf8_len + 1);
7539 memcpy(tmp, zg_ptr, utf8_len);
7540 tmp[utf8_len] = '\0';
7541 ll_append(zapped_glyphs_list, tmp);
7542
7543 zg_ptr += utf8_len;
7544 utf8_len = mblen(zg_ptr, 4);
7545 }
7546 }
7547
7548 /* Parse the word separators string (option -W). If it is empty then */
7549 /* the standard delimiters (space, tab and EOL) are used. Each of its */
7550 /* UTF-8 sequences are stored in a linked list. */
7551 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7552 word_delims_list = ll_new();
7553
7554 if (iws == NULL)
7555 {
7556 ll_append(word_delims_list, " ");
7557 ll_append(word_delims_list, "\t");
7558 ll_append(word_delims_list, "\n");
7559 }
7560 else
7561 {
7562 int utf8_len;
7563 char * iws_ptr = iws;
7564 char * tmp;
7565
7566 utf8_len = mblen(iws_ptr, 4);
7567
7568 while (utf8_len != 0)
7569 {
7570 tmp = xmalloc(utf8_len + 1);
7571 memcpy(tmp, iws_ptr, utf8_len);
7572 tmp[utf8_len] = '\0';
7573 ll_append(word_delims_list, tmp);
7574
7575 iws_ptr += utf8_len;
7576 utf8_len = mblen(iws_ptr, 4);
7577 }
7578 }
7579
7580 /* Parse the line separators string (option -L). If it is empty then */
7581 /* the standard delimiter (newline) is used. Each of its UTF-8 */
7582 /* sequences are stored in a linked list. */
7583 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7584 record_delims_list = ll_new();
7585
7586 /* A default line separator is set to '\n' except in tab_mode */
7587 /* where it should be explicitly set. */
7588 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7589 if (ils == NULL && !win.tab_mode)
7590 ll_append(record_delims_list, "\n");
7591 else
7592 {
7593 int utf8_len;
7594 char * ils_ptr = ils;
7595 char * tmp;
7596
7597 utf8_len = mblen(ils_ptr, 4);
7598
7599 while (utf8_len != 0)
7600 {
7601 tmp = xmalloc(utf8_len + 1);
7602 memcpy(tmp, ils_ptr, utf8_len);
7603 tmp[utf8_len] = '\0';
7604 ll_append(record_delims_list, tmp);
7605
7606 /* Add this record delimiter as a word delimiter. */
7607 /* """""""""""""""""""""""""""""""""""""""""""""" */
7608 if (ll_find(word_delims_list, tmp, buffer_cmp) == NULL)
7609 ll_append(word_delims_list, tmp);
7610
7611 ils_ptr += utf8_len;
7612 utf8_len = mblen(ils_ptr, 4);
7613 }
7614 }
7615
7616 /* Initialize the first chunks of the arrays which will contain the */
7617 /* maximum length of each column in column mode. */
7618 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7619 if (win.col_mode)
7620 {
7621 long ci; /* Column index. */
7622
7623 col_real_max_size = xmalloc(COLSCHUNK * sizeof(long));
7624 col_max_size = xmalloc(COLSCHUNK * sizeof(long));
7625
7626 for (ci = 0; ci < COLSCHUNK; ci++)
7627 col_real_max_size[ci] = col_max_size[ci] = 0;
7628
7629 col_index = cols_number = 0;
7630 }
7631
7632 /* Compile the regular expression patterns. */
7633 /* """""""""""""""""""""""""""""""""""""""" */
7634 if (daccess_np
7635 && regcomp(&daccess_np_re, daccess_np, REG_EXTENDED | REG_NOSUB) != 0)
7636 {
7637 fprintf(stderr, "%s: Bad regular expression.\n", daccess_np);
7638
7639 exit(EXIT_FAILURE);
7640 }
7641
7642 if (daccess_up
7643 && regcomp(&daccess_up_re, daccess_up, REG_EXTENDED | REG_NOSUB) != 0)
7644 {
7645 fprintf(stderr, "%s: Bad regular expression.\n", daccess_up);
7646
7647 exit(EXIT_FAILURE);
7648 }
7649
7650 if (include_pattern
7651 && regcomp(&include_re, include_pattern, REG_EXTENDED | REG_NOSUB) != 0)
7652 {
7653 fprintf(stderr, "%s: Bad regular expression.\n", include_pattern);
7654
7655 exit(EXIT_FAILURE);
7656 }
7657
7658 if (exclude_pattern
7659 && regcomp(&exclude_re, exclude_pattern, REG_EXTENDED | REG_NOSUB) != 0)
7660 {
7661 fprintf(stderr, "%s: Bad regular expression.\n", exclude_pattern);
7662
7663 exit(EXIT_FAILURE);
7664 }
7665
7666 if (first_word_pattern
7667 && regcomp(&first_word_re, first_word_pattern, REG_EXTENDED | REG_NOSUB)
7668 != 0)
7669 {
7670 fprintf(stderr, "%s: Bad regular expression.\n", first_word_pattern);
7671
7672 exit(EXIT_FAILURE);
7673 }
7674
7675 if (last_word_pattern
7676 && regcomp(&last_word_re, last_word_pattern, REG_EXTENDED | REG_NOSUB)
7677 != 0)
7678 {
7679 fprintf(stderr, "%s: Bad regular expression.\n", last_word_pattern);
7680
7681 exit(EXIT_FAILURE);
7682 }
7683
7684 for (index = 0; index < 9; index++)
7685 {
7686 if (special_pattern[index]
7687 && regcomp(&special_re[index], special_pattern[index],
7688 REG_EXTENDED | REG_NOSUB)
7689 != 0)
7690 {
7691 fprintf(stderr, "%s: Bad regular expression.\n", special_pattern[index]);
7692
7693 exit(EXIT_FAILURE);
7694 }
7695 }
7696
7697 /* Parse the post-processing patterns and extract its values. */
7698 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7699 if (early_sed_list != NULL)
7700 {
7701 ll_node_t * node = early_sed_list->head;
7702
7703 while (node != NULL)
7704 {
7705 if (!parse_sed_like_string((sed_t *)(node->data)))
7706 {
7707 fprintf(stderr, "Bad -ES argument. Must be something like: "
7708 "/regex/repl_string/[g][v][s][i].\n");
7709
7710 exit(EXIT_FAILURE);
7711 }
7712
7713 node = node->next;
7714 }
7715 }
7716
7717 if (sed_list != NULL)
7718 {
7719 ll_node_t * node = sed_list->head;
7720
7721 while (node != NULL)
7722 {
7723 if (!parse_sed_like_string((sed_t *)(node->data)))
7724 {
7725 fprintf(stderr, "Bad -S argument. Must be something like: "
7726 "/regex/repl_string/[g][v][s][i].\n");
7727
7728 exit(EXIT_FAILURE);
7729 }
7730 if ((!include_visual_only || !exclude_visual_only)
7731 && ((sed_t *)(node->data))->visual)
7732 {
7733 include_visual_only = 1;
7734 exclude_visual_only = 1;
7735 }
7736
7737 node = node->next;
7738 }
7739 }
7740
7741 if (include_sed_list != NULL)
7742 {
7743 ll_node_t * node = include_sed_list->head;
7744
7745 while (node != NULL)
7746 {
7747 if (!parse_sed_like_string((sed_t *)(node->data)))
7748 {
7749 fprintf(stderr, "Bad -I argument. Must be something like: "
7750 "/regex/repl_string/[g][v][s][i].\n");
7751
7752 exit(EXIT_FAILURE);
7753 }
7754 if (!include_visual_only && ((sed_t *)(node->data))->visual)
7755 include_visual_only = 1;
7756
7757 node = node->next;
7758 }
7759 }
7760
7761 if (exclude_sed_list != NULL)
7762 {
7763 ll_node_t * node = exclude_sed_list->head;
7764
7765 while (node != NULL)
7766 {
7767 if (!parse_sed_like_string((sed_t *)(node->data)))
7768 {
7769 fprintf(stderr, "Bad -E argument. Must be something like: "
7770 "/regex/repl_string/[g][v][s][i].\n");
7771
7772 exit(EXIT_FAILURE);
7773 }
7774 if (!exclude_visual_only && ((sed_t *)(node->data))->visual)
7775 exclude_visual_only = 1;
7776
7777 node = node->next;
7778 }
7779 }
7780
7781 /* Parse the row selection string if any. */
7782 /* """""""""""""""""""""""""""""""""""""" */
7783 if (rows_selector_list != NULL)
7784 {
7785 ll_node_t * node_selector = rows_selector_list->head;
7786 filters_t filter_type;
7787
7788 rows_filter_type = UNKNOWN_FILTER;
7789 while (node_selector != NULL)
7790 {
7791 rows_selector = node_selector->data;
7792 char * unparsed = xstrdup((char *)rows_selector);
7793
7794 parse_selectors(rows_selector, &filter_type, unparsed,
7795 &inc_row_interval_list, &inc_row_regex_list,
7796 &exc_row_interval_list, &exc_row_regex_list, &langinfo,
7797 &misc);
7798
7799 if (*unparsed != '\0')
7800 {
7801 fprintf(stderr, "%s: Bad -R argument. Unparsed part.\n", unparsed);
7802
7803 exit(EXIT_FAILURE);
7804 }
7805
7806 if (rows_filter_type == UNKNOWN_FILTER)
7807 rows_filter_type = filter_type;
7808
7809 node_selector = node_selector->next;
7810
7811 free(unparsed);
7812 }
7813 merge_intervals(inc_row_interval_list);
7814 merge_intervals(exc_row_interval_list);
7815 }
7816
7817 /* Parse the column selection string if any. */
7818 /* """"""""""""""""""""""""""""""""""""""""" */
7819 if (cols_selector_list != NULL)
7820 {
7821 filters_t filter_type, cols_filter_type;
7822 interval_t * data;
7823 ll_node_t * node;
7824 ll_node_t * node_selector = cols_selector_list->head;
7825
7826 cols_filter = xmalloc(limits.cols);
7827
7828 cols_filter_type = UNKNOWN_FILTER;
7829 while (node_selector != NULL)
7830 {
7831 cols_selector = node_selector->data;
7832 char * unparsed = xstrdup((char *)cols_selector);
7833
7834 parse_selectors(cols_selector, &filter_type, unparsed,
7835 &inc_col_interval_list, &inc_col_regex_list,
7836 &exc_col_interval_list, &exc_col_regex_list, &langinfo,
7837 &misc);
7838
7839 if (*unparsed != '\0')
7840 {
7841 fprintf(stderr, "%s: Bad -C argument. Unparsed part.\n", unparsed);
7842
7843 exit(EXIT_FAILURE);
7844 }
7845
7846 merge_intervals(inc_col_interval_list);
7847 merge_intervals(exc_col_interval_list);
7848
7849 free(unparsed);
7850
7851 if (cols_filter_type == UNKNOWN_FILTER)
7852 cols_filter_type = filter_type;
7853
7854 /* Only initialize the whole set when -C is encountered for the */
7855 /* first time. */
7856 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7857 if (cols_filter_type == INCLUDE_FILTER)
7858 memset(cols_filter, SOFT_EXCLUDE_MARK, limits.cols);
7859 else
7860 memset(cols_filter, SOFT_INCLUDE_MARK, limits.cols);
7861
7862 /* Process the explicitly included columns intervals. */
7863 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
7864 if (inc_col_interval_list != NULL)
7865 for (node = inc_col_interval_list->head; node; node = node->next)
7866 {
7867 data = node->data;
7868
7869 if (data->low >= limits.cols)
7870 break;
7871
7872 if (data->high >= limits.cols)
7873 data->high = limits.cols - 1;
7874
7875 memset(cols_filter + data->low, INCLUDE_MARK,
7876 data->high - data->low + 1);
7877 }
7878
7879 /* Process the explicitly excluded column intervals. */
7880 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
7881 if (exc_col_interval_list != NULL)
7882 for (node = exc_col_interval_list->head; node; node = node->next)
7883 {
7884 data = node->data;
7885
7886 if (data->low >= limits.cols)
7887 break;
7888
7889 if (data->high >= limits.cols)
7890 data->high = limits.cols - 1;
7891
7892 memset(cols_filter + data->low, EXCLUDE_MARK,
7893 data->high - data->low + 1);
7894 }
7895
7896 node_selector = node_selector->next;
7897 }
7898 }
7899
7900 /* Initialize the useful values needed to walk through */
7901 /* the rows intervals. */
7902 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
7903 if (rows_filter_type == INCLUDE_FILTER)
7904 row_def_selectable = SOFT_EXCLUDE_MARK;
7905 else if (rows_filter_type == EXCLUDE_FILTER)
7906 row_def_selectable = SOFT_INCLUDE_MARK;
7907 else
7908 {
7909 if (pattern_def_include == 0)
7910 row_def_selectable = SOFT_EXCLUDE_MARK;
7911 else
7912 row_def_selectable = SOFT_INCLUDE_MARK;
7913 }
7914
7915 /* Set the head of the interval list. */
7916 /* """""""""""""""""""""""""""""""""" */
7917 if (inc_row_interval_list)
7918 inc_interval_node = inc_row_interval_list->head;
7919 else
7920 inc_interval_node = NULL;
7921
7922 if (exc_row_interval_list)
7923 exc_interval_node = exc_row_interval_list->head;
7924 else
7925 exc_interval_node = NULL;
7926
7927 /* And get the first interval.*/
7928 /* """"""""""""""""""""""""""" */
7929 if (inc_interval_node)
7930 inc_interval = (interval_t *)inc_interval_node->data;
7931 else
7932 inc_interval = NULL;
7933
7934 if (exc_interval_node)
7935 exc_interval = (interval_t *)exc_interval_node->data;
7936 else
7937 exc_interval = NULL;
7938
7939 /* First pass: */
7940 /* Get and process the input stream words. */
7941 /* In this pass, the different actions will occur: */
7942 /* - A new word is read from stdin */
7943 /* - A new SOL and or EOL is possibly set */
7944 /* - A special level is possibly affected to the word just read */
7945 /* - The -R is taken into account */
7946 /* - The first part of the -C option is done */
7947 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7948 while ((word = get_word(input_file, word_delims_list, record_delims_list,
7949 zapped_glyphs_list, utf8_buffer, &is_last, &toggles,
7950 &langinfo, &win, &limits, &misc))
7951 != NULL)
7952 {
7953 int selectable;
7954 int is_first = 0;
7955 int special_level;
7956 int row_inc_matched = 0;
7957 ll_node_t * node;
7958
7959 if (*word == '\0')
7960 continue;
7961
7962 /* Early substitution. */
7963 /* """"""""""""""""""" */
7964 if (early_sed_list != NULL)
7965 {
7966 char * tmp;
7967
7968 node = early_sed_list->head;
7969
7970 while (node != NULL)
7971 {
7972 tmp = xstrdup(word);
7973 if (replace(word, (sed_t *)(node->data)))
7974 {
7975
7976 free(word);
7977 word = xstrdup(word_buffer);
7978
7979 if (((sed_t *)(node->data))->stop)
7980 break;
7981 }
7982
7983 *word_buffer = '\0';
7984 node = node->next;
7985 free(tmp);
7986 }
7987 }
7988
7989 if (*word == '\0')
7990 continue;
7991
7992 /* Manipulates the is_last flag word indicator to make this word */
7993 /* the first or last one of the current line in column/line/tab mode. */
7994 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
7995 if (win.col_mode || win.line_mode || win.tab_mode)
7996 {
7997 if (first_word_pattern
7998 && regexec(&first_word_re, word, (int)0, NULL, 0) == 0)
7999 is_first = 1;
8000
8001 if (last_word_pattern && !is_last
8002 && regexec(&last_word_re, word, (int)0, NULL, 0) == 0)
8003 is_last = 1;
8004 }
8005
8006 /* Check if the word is special. */
8007 /* """"""""""""""""""""""""""""" */
8008 special_level = 0;
8009 for (index = 0; index < 9; index++)
8010 {
8011 if (special_pattern[index] != NULL
8012 && regexec(&special_re[index], word, (int)0, NULL, 0) == 0)
8013 {
8014 special_level = (int)index + 1;
8015 break;
8016 }
8017 }
8018
8019 /* Default selectable state. */
8020 /* """"""""""""""""""""""""" */
8021 selectable = SOFT_INCLUDE_MARK;
8022
8023 /* For each new line check if the line is in the current */
8024 /* interval or if we need to get the next interval if any .*/
8025 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
8026 if (rows_selector)
8027 {
8028 if (count > 0 && word_a[count - 1].is_last)
8029 {
8030 /* We are in a new line, reset the flag indicating that we are on */
8031 /* a line selected by a regular expression and the flag saying */
8032 /* that the whole line has been excluded. */
8033 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8034 line_selected_by_regex = 0;
8035 line_excluded = 0;
8036
8037 /* And also reset the flag telling that the row has been explicitly */
8038 /* removed from the selectable list of words. */
8039 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8040 row_inc_matched = 0;
8041
8042 /* Increment the line counter used to see if we are an include or */
8043 /* exclude set of lines. */
8044 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8045 line_count++;
8046
8047 /* Look if we need to use the next interval of the list. */
8048 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8049 if (inc_interval_node && line_count > inc_interval->high)
8050 {
8051 inc_interval_node = inc_interval_node->next;
8052 if (inc_interval_node)
8053 inc_interval = (interval_t *)inc_interval_node->data;
8054 }
8055
8056 if (exc_interval_node && line_count > exc_interval->high)
8057 {
8058 exc_interval_node = exc_interval_node->next;
8059 if (exc_interval_node)
8060 exc_interval = (interval_t *)exc_interval_node->data;
8061 }
8062 }
8063
8064 /* Look if the line is in an excluded or included line. */
8065 /* The included line intervals are only checked if the word didn't */
8066 /* belong to an excluded line interval before. */
8067 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8068 if (exc_interval)
8069 {
8070 if (line_count >= exc_interval->low && line_count <= exc_interval->high)
8071 selectable = EXCLUDE_MARK;
8072 }
8073 if (selectable != EXCLUDE_MARK && inc_interval)
8074 {
8075 if (line_count >= inc_interval->low && line_count <= inc_interval->high)
8076 {
8077 selectable = INCLUDE_MARK;
8078
8079 /* As the raw has been explicitly selected, record that so than */
8080 /* we can distinguish that from the implicit selection. */
8081 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8082 row_inc_matched = 1;
8083 }
8084 }
8085 }
8086
8087 /* Check if the all the words in the current row must be included or */
8088 /* excluded from the selectable set of words. */
8089 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8090 if (selectable != EXCLUDE_MARK)
8091 {
8092 /* Look in the excluded list of regular expressions. */
8093 /* ''''''''''''''''''''''''''''''''''''''''''''''''' */
8094 if (exc_row_regex_list != NULL)
8095 {
8096 regex_t * row_re;
8097
8098 ll_node_t * row_regex_node = exc_row_regex_list->head;
8099
8100 while (row_regex_node != NULL)
8101 {
8102 row_re = row_regex_node->data;
8103 if (regexec(row_re, word, (int)0, NULL, 0) == 0)
8104 {
8105 long c = count - 1;
8106
8107 /* Mark all the next words of the line as excluded. */
8108 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
8109 line_selected_by_regex = 1;
8110 line_excluded = 1;
8111
8112 /* Mark all the previous words of the line as excluded. */
8113 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
8114 while (c >= 0 && !word_a[c].is_last)
8115 {
8116 word_a[c].is_selectable = EXCLUDE_MARK;
8117 c--;
8118 }
8119
8120 /* Mark the current word as not excluded. */
8121 /* '''''''''''''''''''''''''''''''''''''' */
8122 selectable = EXCLUDE_MARK;
8123
8124 /* No need to continue as the line is already marked as */
8125 /* excluded. */
8126 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
8127 break;
8128 }
8129
8130 row_regex_node = row_regex_node->next;
8131 }
8132 }
8133
8134 /* If the line has not yet been excluded and the list of explicitly */
8135 /* include regular expressions is not empty then give them a chance. */
8136 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8137 if (selectable != EXCLUDE_MARK && inc_row_regex_list != NULL)
8138 {
8139 regex_t * row_re;
8140
8141 ll_node_t * row_regex_node = inc_row_regex_list->head;
8142
8143 while (row_regex_node != NULL)
8144 {
8145 row_re = row_regex_node->data;
8146 if (regexec(row_re, word, (int)0, NULL, 0) == 0)
8147 {
8148 long c = count - 1;
8149
8150 while (c >= 0 && !word_a[c].is_last)
8151 {
8152 /* Do not include an already excluded word. */
8153 /* """""""""""""""""""""""""""""""""""""""" */
8154 if (word_a[c].is_selectable)
8155 word_a[c].is_selectable = INCLUDE_MARK;
8156
8157 c--;
8158 }
8159
8160 /* Mark all the next words of the line as included. */
8161 /* '''''''''''''''''''''''''''''''''''''''''''''''' */
8162 line_selected_by_regex = 1;
8163
8164 /* Mark all the previous words of the line as included. */
8165 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
8166 selectable = INCLUDE_MARK;
8167 }
8168
8169 row_regex_node = row_regex_node->next;
8170 }
8171 }
8172 }
8173
8174 /* If the line contains a word that matched a regex which determines */
8175 /* the inclusion of exclusion of this line, then use the regex */
8176 /* selection flag to determine the inclusion/exclusion of the future */
8177 /* words in the line. */
8178 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8179 if (line_excluded)
8180 selectable = EXCLUDE_MARK;
8181 else
8182 {
8183 if (line_selected_by_regex)
8184 selectable = (row_def_selectable == ROW_REGEX_EXCLUDE)
8185 ? SOFT_EXCLUDE_MARK
8186 : INCLUDE_MARK;
8187
8188 /* Check if the current word is matching an include or exclude */
8189 /* pattern */
8190 /* Only do it if if hasn't be explicitly deselected before. */
8191 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8192 if (selectable != EXCLUDE_MARK)
8193 {
8194 /* Check if the word will be excluded in the list of selectable */
8195 /* words or not. */
8196 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8197 if (exclude_pattern)
8198 {
8199 if (regexec(&exclude_re, word, (int)0, NULL, 0) == 0)
8200 selectable = EXCLUDE_MARK;
8201 }
8202
8203 if (selectable != 0 && !line_selected_by_regex)
8204 {
8205 /* Check if the word will be included in the list of selectable */
8206 /* words or not. */
8207 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8208 if (include_pattern)
8209 {
8210 if (regexec(&include_re, word, (int)0, NULL, 0) == 0)
8211 selectable = INCLUDE_MARK;
8212 else if (!row_inc_matched)
8213 selectable = row_def_selectable;
8214 }
8215 else if (rows_selector && !row_inc_matched)
8216 selectable = row_def_selectable;
8217 }
8218 }
8219 }
8220
8221 if (win.col_mode)
8222 {
8223 /* In column mode we must manage the allocation space for some */
8224 /* column's related data structures and check if some limits ave not */
8225 /* been reached. */
8226 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8227
8228 if (is_first)
8229 col_index = 1;
8230 else
8231 {
8232 col_index++;
8233
8234 if (col_index > cols_number)
8235 {
8236 /* Check the limits. */
8237 /* ''''''''''''''''' */
8238 if (col_index == limits.cols)
8239 {
8240 fprintf(stderr,
8241 "The number of columns has reached the limit of %ld.\n",
8242 limits.cols);
8243
8244 exit(EXIT_FAILURE);
8245 }
8246
8247 cols_number++;
8248
8249 /* Look if we need to enlarge the arrays indexed by the */
8250 /* number of columns. */
8251 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
8252 if (cols_number % COLSCHUNK == 0)
8253 {
8254 long ci; /* column index */
8255
8256 col_real_max_size = xrealloc(col_real_max_size,
8257 (cols_number + COLSCHUNK)
8258 * sizeof(long));
8259
8260 col_max_size = xrealloc(col_max_size,
8261 (cols_number + COLSCHUNK) * sizeof(long));
8262
8263 /* Initialize the max size for the new columns. */
8264 /* '''''''''''''''''''''''''''''''''''''''''''' */
8265 for (ci = 0; ci < COLSCHUNK; ci++)
8266 {
8267 col_real_max_size[cols_number + ci] = 0;
8268 col_max_size[cols_number + ci] = 0;
8269 }
8270 }
8271 }
8272 }
8273
8274 /* We must now check if the word matches a RE that */
8275 /* exclude the whole column. */
8276 /* """"""""""""""""""""""""""""""""""""""""""""""" */
8277 if (cols_selector != NULL)
8278 {
8279 long ci; /* column index. */
8280
8281 regex_t * col_re;
8282
8283 if (cols_filter[col_index - 1] == EXCLUDE_MARK)
8284 selectable = EXCLUDE_MARK;
8285 else
8286 {
8287 if (exc_col_regex_list != NULL)
8288 {
8289 /* Some columns must be excluded by regex. */
8290 /* ''''''''''''''''''''''''''''''''''''''' */
8291 ll_node_t * col_regex_node = exc_col_regex_list->head;
8292
8293 while (col_regex_node != NULL)
8294 {
8295 col_re = col_regex_node->data;
8296
8297 if (regexec(col_re, word, (int)0, NULL, 0) == 0)
8298 {
8299 cols_filter[col_index - 1] = EXCLUDE_MARK;
8300 selectable = EXCLUDE_MARK;
8301
8302 /* Mark non selectable the items above in the column. */
8303 /* '''''''''''''''''''''''''''''''''''''''''''''''''' */
8304 ci = 0;
8305 for (wi = 0; wi < count; wi++)
8306 {
8307 if (ci == col_index - 1)
8308 word_a[wi].is_selectable = EXCLUDE_MARK;
8309
8310 if (word_a[wi].is_last)
8311 ci = 0;
8312 else
8313 ci++;
8314 }
8315 break;
8316 }
8317
8318 col_regex_node = col_regex_node->next;
8319 }
8320 }
8321
8322 if (inc_col_regex_list != NULL)
8323 {
8324 /* Some columns must be included by regex. */
8325 /* ''''''''''''''''''''''''''''''''''''''' */
8326 ll_node_t * col_regex_node = inc_col_regex_list->head;
8327
8328 while (col_regex_node != NULL)
8329 {
8330 col_re = col_regex_node->data;
8331
8332 if (regexec(col_re, word, (int)0, NULL, 0) == 0)
8333 {
8334 cols_filter[col_index - 1] = INCLUDE_MARK;
8335 break;
8336 }
8337
8338 col_regex_node = col_regex_node->next;
8339 }
8340 }
8341 }
8342 }
8343 }
8344
8345 /* Store some known values in the current word's structure. */
8346 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8347 word_a[count].start = word_a[count].end = 0;
8348
8349 word_a[count].str = word;
8350 word_a[count].is_selectable = selectable;
8351
8352 word_a[count].special_level = special_level;
8353 word_a[count].is_matching = 0;
8354 word_a[count].is_tagged = 0;
8355 word_a[count].is_numbered = 0;
8356 word_a[count].tag_order = 0;
8357
8358 if (win.col_mode || win.line_mode || win.tab_mode)
8359 {
8360 /* Set the last word in line indicator when in */
8361 /* column/line/tab mode. */
8362 /* ''''''''''''''''''''''''''''''''''''''''''' */
8363 if (is_first && count > 0)
8364 word_a[count - 1].is_last = 1;
8365 word_a[count].is_last = is_last;
8366 if (is_last)
8367 col_index = 0;
8368 }
8369 else
8370 word_a[count].is_last = 0;
8371
8372 /* One more word... */
8373 /* """""""""""""""" */
8374 if (count == limits.words)
8375 {
8376 fprintf(stderr,
8377 "The number of read words has reached the limit of %ld.\n",
8378 limits.words);
8379
8380 exit(EXIT_FAILURE);
8381 }
8382
8383 count++;
8384
8385 if (count % WORDSCHUNK == 0)
8386 word_a = xrealloc(word_a, (count + WORDSCHUNK) * sizeof(word_t));
8387 }
8388
8389 /* Early exit if there is no input or if no word is selected. */
8390 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8391 if (count == 0)
8392 exit(EXIT_FAILURE);
8393
8394 /* Ignore SIGINT */
8395 /* """"""""""""" */
8396 sigaddset(&sigs, SIGINT);
8397 sigprocmask(SIG_BLOCK, &sigs, &oldsigs);
8398
8399 /* The last word is always the last of its line. */
8400 /* """"""""""""""""""""""""""""""""""""""""""""" */
8401 if (win.col_mode || win.line_mode || win.tab_mode)
8402 word_a[count - 1].is_last = 1;
8403
8404 /* Second pass to modify the word according to all/include/exclude */
8405 /* regular expressions and the columns settings set in the previous pass. */
8406 /* This must be done separately because in the first pass, some word */
8407 /* could have been marked as excluded before the currently processed word */
8408 /* (second part of the -C option) */
8409 /* In this pass the following actions will also be done: */
8410 /* - Finish the work on columns. */
8411 /* - Possibly modify the word according to -S/-I/-E arguments */
8412 /* - Replace unprintable characters in the word by mnemonics */
8413 /* - Remember the max size of the words/columns/tabs */
8414 /* - Insert the word in a TST (Ternary Search Tree) index to facilitate */
8415 /* word search (each node pf the TST will contain an UTF-8 glyph). */
8416 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8417 col_index = 0;
8418 for (wi = 0; wi < count; wi++)
8419 {
8420 char * unaltered_word;
8421 long size;
8422 long word_len;
8423 wchar_t * tmpw;
8424 word_t * word;
8425 long s;
8426 long len;
8427 char * expanded_word;
8428 long i;
8429
8430 /* If the column section argument is set, then adjust the final */
8431 /* selectable attribute according to the already set words and column */
8432 /* selectable flag contents. */
8433 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8434 if (cols_selector_list != NULL)
8435 {
8436 if (cols_filter[col_index] == EXCLUDE_MARK)
8437 word_a[wi].is_selectable = EXCLUDE_MARK;
8438 else if (word_a[wi].is_selectable != EXCLUDE_MARK)
8439 {
8440 switch (cols_filter[col_index])
8441 {
8442 case INCLUDE_MARK:
8443 word_a[wi].is_selectable = INCLUDE_MARK;
8444 break;
8445
8446 case SOFT_EXCLUDE_MARK:
8447 if (word_a[wi].is_selectable == SOFT_EXCLUDE_MARK
8448 || word_a[wi].is_selectable == SOFT_INCLUDE_MARK)
8449 word_a[wi].is_selectable = EXCLUDE_MARK;
8450 else
8451 word_a[wi].is_selectable = INCLUDE_MARK;
8452 break;
8453
8454 case SOFT_INCLUDE_MARK:
8455 if (word_a[wi].is_selectable == SOFT_EXCLUDE_MARK)
8456 word_a[wi].is_selectable = EXCLUDE_MARK;
8457 else
8458 word_a[wi].is_selectable = INCLUDE_MARK;
8459 break;
8460 }
8461 }
8462 }
8463
8464 word = &word_a[wi];
8465
8466 /* Make sure that daccess.length >= daccess.size */
8467 /* with DA_TYPE_POS. */
8468 /* """"""""""""""""""""""""""""""""""""""""""""" */
8469 if (daccess.mode != DA_TYPE_NONE)
8470 {
8471 if (daccess.mode & DA_TYPE_POS)
8472 {
8473 if (daccess.size > 0)
8474 if (daccess.size > daccess.length)
8475 daccess.length = daccess.size;
8476 }
8477
8478 /* Auto determination of the length of the selector */
8479 /* with DA_TYPE_AUTO. */
8480 /* """""""""""""""""""""""""""""""""""""""""""""""" */
8481 if ((daccess.mode & DA_TYPE_AUTO) && daccess.length == -2)
8482 {
8483 long n = count;
8484
8485 daccess.length = 0;
8486
8487 while (n)
8488 {
8489 n /= 10;
8490 daccess.length++;
8491 }
8492 }
8493
8494 /* Set the full length of the prefix in case of numbering. */
8495 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
8496 if (daccess.length > 0)
8497 daccess.flength = 3 + daccess.length;
8498
8499 if (word->is_selectable != EXCLUDE_MARK
8500 && word->is_selectable != SOFT_EXCLUDE_MARK)
8501 {
8502 char * selector;
8503 char * tmp = xmalloc(strlen(word->str) + 4 + daccess.length);
8504 long * word_pos = xmalloc(sizeof(long));
8505 int may_number;
8506
8507 if (!isempty(word->str))
8508 {
8509 *word_pos = wi;
8510
8511 tmp[0] = ' ';
8512
8513 /* Check if the word is eligible to the numbering process. */
8514 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
8515 if (daccess_up == NULL && daccess_np == NULL)
8516 {
8517 if (daccess.mode & DA_TYPE_POS)
8518 may_number = 1;
8519 else
8520 may_number = 0;
8521 }
8522 else
8523 {
8524 if (daccess_up != NULL
8525 && !!regexec(&daccess_up_re, word->str, (int)0, NULL, 0) == 0)
8526 may_number = 0;
8527 else
8528 {
8529 if (daccess_np != NULL
8530 && !!regexec(&daccess_np_re, word->str, (int)0, NULL, 0) == 0)
8531 may_number = 1;
8532 else
8533 may_number = daccess.def_number;
8534 }
8535 }
8536
8537 /* It is... */
8538 /* """""""" */
8539 if (may_number)
8540 {
8541 if (daccess.mode & DA_TYPE_POS)
8542 {
8543 if (!word->is_numbered)
8544 {
8545 if (daccess.size > 0
8546 && daccess.offset + daccess.size + daccess.ignore
8547 <= utf8_strlen(word->str))
8548 {
8549 unsigned selector_value; /* numerical value of the *
8550 | extracted selector. */
8551 long selector_offset; /* offset in byte to the selector *
8552 | to extract. */
8553 char * ptr; /* points just after the selector *
8554 | to extract. */
8555 long plus_offset; /* points to the first occurrence *
8556 | of a number in word->str after *
8557 | the offset given. */
8558
8559 selector_offset = utf8_offset(word->str, daccess.offset);
8560
8561 if (daccess.plus)
8562 {
8563 plus_offset = strcspn(word->str + selector_offset,
8564 "0123456789");
8565
8566 if (plus_offset + daccess.size + daccess.ignore
8567 <= strlen(word->str))
8568 selector_offset += plus_offset;
8569 }
8570
8571 ptr = word->str + selector_offset;
8572 selector = xstrndup(ptr, daccess.size);
8573
8574 /* read the embedded number and, if correct, format */
8575 /* it according to daccess.alignment. */
8576 /* """""""""""""""""""""""""""""""""""""""""""""""" */
8577 if (sscanf(selector, "%u", &selector_value) == 1)
8578 {
8579 sprintf(selector, "%u", selector_value);
8580
8581 sprintf(tmp + 1, "%*u",
8582 daccess.alignment == 'l' ? -daccess.length
8583 : daccess.length,
8584 selector_value);
8585
8586 /* Overwrite the end of the word to erase */
8587 /* the selector. */
8588 /* """""""""""""""""""""""""""""""""""""" */
8589 my_strcpy(ptr, ptr + daccess.size
8590 + utf8_offset(ptr + daccess.size,
8591 daccess.ignore));
8592
8593 /* Modify the word according to the 'h' directive */
8594 /* of -D. */
8595 /* """""""""""""""""""""""""""""""""""""""""""""" */
8596 if (daccess.head == 'c')
8597 /* h:c is present cut the leading characters */
8598 /* before the selector. */
8599 /* ''''''''''''''''''''''''''''''''''''''''' */
8600 memmove(word->str, ptr, strlen(ptr) + 1);
8601 else if (daccess.head == 't')
8602 {
8603 /* h:t is present trim the leading characters */
8604 /* before the selector if they are ' ' or '\t'. */
8605 /* '''''''''''''''''''''''''''''''''''''''''''' */
8606 char * p = word->str;
8607
8608 while (p != ptr && (*p == ' ' || *p == '\t'))
8609 p++;
8610
8611 if (p == ptr)
8612 memmove(word->str, ptr, strlen(ptr) + 1);
8613 }
8614
8615 ltrim(selector, " ");
8616 rtrim(selector, " ", 0);
8617
8618 tst_daccess = tst_insert(tst_daccess,
8619 utf8_strtowcs(selector), word_pos);
8620
8621 if (daccess.follow == 'y')
8622 daccess_index = selector_value + 1;
8623
8624 word->is_numbered = 1;
8625 }
8626 free(selector);
8627 }
8628 }
8629 }
8630
8631 /* Try to number this word if it is still non numbered and */
8632 /* the -N/-U option is given. */
8633 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
8634 if (!word->is_numbered && (daccess.mode & DA_TYPE_AUTO))
8635 {
8636 sprintf(tmp + 1, "%*ld",
8637 daccess.alignment == 'l' ? -daccess.length
8638 : daccess.length,
8639 daccess_index);
8640
8641 selector = xstrdup(tmp + 1);
8642 ltrim(selector, " ");
8643 rtrim(selector, " ", 0);
8644
8645 /* Insert it in the tst tree containing the selector's */
8646 /* digits. */
8647 /* ''''''''''''''''''''''''''''''''''''''''''''''''''' */
8648 tst_daccess = tst_insert(tst_daccess, utf8_strtowcs(selector),
8649 word_pos);
8650 daccess_index++;
8651
8652 free(selector);
8653
8654 word->is_numbered = 1;
8655 }
8656 }
8657
8658 /* Fill the space taken by the numbering by space if the word */
8659 /* is not numbered. */
8660 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8661 if (daccess.length > 0 && !word->is_numbered)
8662 {
8663 for (i = 0; i < daccess.flength; i++)
8664 tmp[i] = ' ';
8665 }
8666
8667 /* Make sure that the 2 character after this placeholder */
8668 /* are initialized. */
8669 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
8670 if (daccess.length > 0)
8671 {
8672 tmp[1 + daccess.length] = ' ';
8673 tmp[2 + daccess.length] = ' ';
8674 }
8675 }
8676 else if (daccess.length > 0)
8677 {
8678 /* make sure that the prefix of empty word is blank */
8679 /* as they may be display in column mode. */
8680 /* """""""""""""""""""""""""""""""""""""""""""""""" */
8681 for (i = 0; i < daccess.flength; i++)
8682 tmp[i] = ' ';
8683 }
8684
8685 if (daccess.length > 0)
8686 {
8687 my_strcpy(tmp + daccess.flength, word->str);
8688 free(word->str);
8689 word->str = tmp;
8690 }
8691 else
8692 free(tmp);
8693 }
8694 else
8695 {
8696 /* Should we also add space at the beginning of excluded words ? */
8697 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8698 if (daccess.padding == 'a')
8699 {
8700 char * tmp = xmalloc(strlen(word->str) + 4 + daccess.length);
8701 for (i = 0; i < daccess.flength; i++)
8702 tmp[i] = ' ';
8703 my_strcpy(tmp + daccess.flength, word->str);
8704 free(word->str);
8705 word->str = tmp;
8706 }
8707 }
8708 }
8709 else
8710 {
8711 daccess.size = 0;
8712 daccess.length = 0;
8713 }
8714
8715 /* Save the original word. */
8716 /* """"""""""""""""""""""" */
8717 unaltered_word = xstrdup(word->str);
8718
8719 /* Possibly modify the word according to -S/-I/-E arguments. */
8720 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8721 {
8722 ll_node_t * node = NULL;
8723 char * tmp;
8724
8725 /* Manage the -S case. */
8726 /* """"""""""""""""""" */
8727 if (sed_list != NULL)
8728 {
8729 node = sed_list->head;
8730
8731 while (node != NULL)
8732 {
8733 tmp = xstrndup(word->str, daccess.flength);
8734 if (replace(word->str + daccess.flength, (sed_t *)(node->data)))
8735 {
8736
8737 free(word->str);
8738 memmove(word_buffer + daccess.flength, word_buffer,
8739 strlen(word_buffer) + 1);
8740 memmove(word_buffer, tmp, daccess.flength);
8741
8742 word->str = xstrdup(word_buffer);
8743
8744 if (((sed_t *)(node->data))->stop)
8745 break;
8746 }
8747
8748 *word_buffer = '\0';
8749 node = node->next;
8750 free(tmp);
8751 }
8752 }
8753 else
8754 {
8755 /* Manage the -I/-E case. */
8756 /* """""""""""""""""""""" */
8757 if ((word->is_selectable == INCLUDE_MARK
8758 || word->is_selectable == SOFT_INCLUDE_MARK)
8759 && include_sed_list != NULL)
8760 node = include_sed_list->head;
8761 else if ((word->is_selectable == EXCLUDE_MARK
8762 || word->is_selectable == SOFT_EXCLUDE_MARK)
8763 && exclude_sed_list != NULL)
8764 node = exclude_sed_list->head;
8765 else
8766 node = NULL;
8767
8768 *word_buffer = '\0';
8769
8770 while (node != NULL)
8771 {
8772 tmp = xstrndup(word->str, daccess.flength);
8773 if (replace(word->str + daccess.flength, (sed_t *)(node->data)))
8774 {
8775
8776 free(word->str);
8777 memmove(word_buffer + daccess.flength, word_buffer,
8778 strlen(word_buffer) + 1);
8779 memmove(word_buffer, tmp, daccess.flength);
8780
8781 word->str = xstrdup(word_buffer);
8782
8783 if (((sed_t *)(node->data))->stop)
8784 break;
8785 }
8786 *word_buffer = '\0';
8787 node = node->next;
8788 free(tmp);
8789 }
8790 }
8791 }
8792
8793 /* A substitution leading to an empty word is invalid in column mode. */
8794 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8795 if (win.col_mode)
8796 {
8797 long len;
8798
8799 if (daccess.padding == 'a')
8800 len = daccess.flength;
8801 else
8802 len = 0;
8803
8804 if (*(word->str + len) == '\0')
8805 exit(EXIT_FAILURE);
8806 }
8807
8808 /* Alter the word just read be replacing special chars by their */
8809 /* escaped equivalents. */
8810 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8811 word_len = strlen(word->str);
8812
8813 expanded_word = xmalloc(5 * word_len + 1);
8814 len = expand(word->str, expanded_word, &langinfo, &toggles, &misc);
8815
8816 /* Update it if needed. */
8817 /* '''''''''''''''''''' */
8818 if (strcmp(expanded_word, word->str) != 0)
8819 {
8820 word_len = len;
8821 free(word->str);
8822 word->str = xstrdup(expanded_word);
8823 }
8824
8825 free(expanded_word);
8826
8827 if (win.col_mode)
8828 {
8829 /* Update the max values of col_real_max_size[col_index] */
8830 /* and col_max_size[col_index]. */
8831 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
8832 if ((s = (long)word_len) > col_real_max_size[col_index])
8833 {
8834 col_real_max_size[col_index] = s;
8835
8836 /* Also update the real max size of all columns seen. */
8837 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
8838 if (s > cols_real_max_size)
8839 cols_real_max_size = s;
8840 }
8841
8842 s = (long)mbstowcs(NULL, word->str, 0);
8843 s = wcswidth((tmpw = utf8_strtowcs(word->str)), s);
8844 free(tmpw);
8845
8846 if (s > col_max_size[col_index])
8847 {
8848 col_max_size[col_index] = s;
8849
8850 /* Also update the max size of all columns seen. */
8851 /* """"""""""""""""""""""""""""""""""""""""""""" */
8852 if (s > cols_max_size)
8853 cols_max_size = s;
8854 }
8855 /* Update the size of the longest expanded word. */
8856 /* """"""""""""""""""""""""""""""""""""""""""""" */
8857 word_real_max_size = cols_real_max_size;
8858 }
8859 else if (win.tab_mode)
8860 {
8861 /* Store the new max number of bytes in a word */
8862 /* and update the size of the longest expanded word. */
8863 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
8864 if ((long)word_len > tab_real_max_size)
8865 word_real_max_size = tab_real_max_size = (long)word_len;
8866
8867 /* Store the new max word width. */
8868 /* """"""""""""""""""""""""""""" */
8869 size = (long)mbstowcs(NULL, word->str, 0);
8870
8871 if ((size = wcswidth((tmpw = utf8_strtowcs(word->str)), size))
8872 > tab_max_size)
8873 tab_max_size = size;
8874
8875 free(tmpw);
8876 }
8877 else if (word_real_max_size < word_len)
8878 /* Update the size of the longest expanded word. */
8879 /* """"""""""""""""""""""""""""""""""""""""""""" */
8880 word_real_max_size = word_len;
8881
8882 /* When the visual only flag is set, we keep the unaltered word so */
8883 /* that it can be restored even if its visual and searchable */
8884 /* representation may have been altered by the previous code. */
8885 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8886
8887 /* Record the length of the word in bytes. This information will be */
8888 /* used if the -k option (keep spaces ) is not set. */
8889 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8890 word->len = strlen(word->str);
8891
8892 /* Save the non modified word in .orig if it has been altered. */
8893 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8894 if ((strcmp(word->str, unaltered_word) != 0)
8895 && ((word->is_selectable && include_visual_only)
8896 || (!word->is_selectable && exclude_visual_only)))
8897 {
8898 word->orig = unaltered_word;
8899 }
8900 else
8901 {
8902 word->orig = NULL;
8903 free(unaltered_word);
8904 }
8905
8906 if (win.col_mode)
8907 {
8908 if (word_a[wi].is_last)
8909 col_index = 0;
8910 else
8911 col_index++;
8912 }
8913 }
8914
8915 /* Set the minimum width of a column (-w and -t or -c option). */
8916 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8917 if (win.wide)
8918 {
8919 if (win.tab_mode)
8920 {
8921 if (win.max_cols > 0)
8922 min_size = (term.ncolumns - 2) / win.max_cols - 1;
8923
8924 if (min_size < tab_max_size)
8925 min_size = tab_max_size;
8926
8927 word_real_max_size = min_size + tab_real_max_size - tab_max_size;
8928 }
8929 else /* Column mode. */
8930 {
8931 min_size = (term.ncolumns - 2) / cols_number;
8932 if (min_size < cols_max_size)
8933 min_size = cols_max_size;
8934
8935 word_real_max_size = cols_real_max_size;
8936 }
8937 }
8938
8939 /* Third (compress) pass: remove all empty word and words containing */
8940 /* only spaces when not in column mode. */
8941 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8942 if (!win.col_mode)
8943 {
8944 long offset;
8945
8946 offset = 0;
8947 for (wi = 0; wi < count - offset; wi++)
8948 {
8949 long len;
8950
8951 while (wi + offset < count)
8952 {
8953 if (daccess.padding == 'a' || word_a[wi + offset].is_numbered)
8954 len = daccess.flength;
8955 else
8956 len = 0;
8957
8958 if (!isempty(word_a[wi + offset].str + len))
8959 break;
8960
8961 /* Keep non selectable empty words to allow special effects. */
8962 /* ''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
8963 if (word_a[wi + offset].is_selectable == SOFT_EXCLUDE_MARK
8964 || word_a[wi + offset].is_selectable == EXCLUDE_MARK)
8965 break;
8966
8967 offset++;
8968 }
8969
8970 if (offset > 0)
8971 word_a[wi] = word_a[wi + offset];
8972 }
8973 count -= offset;
8974 }
8975
8976 if (count == 0)
8977 exit(EXIT_FAILURE);
8978
8979 /* Allocate the space for the satellites arrays. */
8980 /* """"""""""""""""""""""""""""""""""""""""""""" */
8981 line_nb_of_word_a = xmalloc(count * sizeof(long));
8982 first_word_in_line_a = xmalloc(count * sizeof(long));
8983
8984 /* Fourth pass: */
8985 /* When in column or tabulating mode, we need to adjust the length of */
8986 /* all the words by adding the right number of spaces so that they will */
8987 /* be aligned correctly. In column mode the size of each column is */
8988 /* variable; in tabulate mode it is constant. */
8989 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8990 if (win.col_mode)
8991 {
8992 char * temp;
8993
8994 /* Sets all columns to the same size when -w and -c are both set. */
8995 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
8996 if (win.wide)
8997 for (col_index = 0; col_index < cols_number; col_index++)
8998 {
8999 col_max_size[col_index] = cols_max_size;
9000 col_real_max_size[col_index] = cols_real_max_size;
9001 }
9002
9003 /* Total space taken by all the columns plus the gutter. */
9004 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
9005 win.max_width = 0;
9006 win.real_max_width = 0;
9007 for (col_index = 0; col_index < cols_number; col_index++)
9008 {
9009 if (win.max_width + col_max_size[col_index] + 1 <= term.ncolumns - 2)
9010 win.max_width += col_max_size[col_index] + 1;
9011
9012 win.real_max_width += col_max_size[col_index] + 1;
9013 }
9014
9015 col_index = 0;
9016 for (wi = 0; wi < count; wi++)
9017 {
9018 long s1, s2;
9019 long word_width;
9020 wchar_t * w;
9021
9022 s1 = (long)strlen(word_a[wi].str);
9023 word_width = mbstowcs(NULL, word_a[wi].str, 0);
9024 s2 = wcswidth((w = utf8_strtowcs(word_a[wi].str)), word_width);
9025 free(w);
9026 temp = xcalloc(1, col_real_max_size[col_index] + s1 - s2 + 1);
9027 memset(temp, ' ', col_max_size[col_index] + s1 - s2);
9028 memcpy(temp, word_a[wi].str, s1);
9029 temp[col_real_max_size[col_index] + s1 - s2] = '\0';
9030 free(word_a[wi].str);
9031 word_a[wi].str = temp;
9032
9033 if (word_a[wi].is_last)
9034 col_index = 0;
9035 else
9036 col_index++;
9037 }
9038 }
9039 else if (win.tab_mode)
9040 {
9041 char * temp;
9042
9043 if (tab_max_size < min_size)
9044 {
9045 tab_max_size = min_size;
9046 if (tab_max_size > tab_real_max_size)
9047 tab_real_max_size = tab_max_size;
9048 }
9049
9050 for (wi = 0; wi < count; wi++)
9051 {
9052 long s1, s2;
9053 long word_width;
9054 wchar_t * w;
9055
9056 s1 = (long)strlen(word_a[wi].str);
9057 word_width = mbstowcs(NULL, word_a[wi].str, 0);
9058 s2 = wcswidth((w = utf8_strtowcs(word_a[wi].str)), word_width);
9059 free(w);
9060 temp = xcalloc(1, tab_real_max_size + s1 - s2 + 1);
9061 memset(temp, ' ', tab_max_size + s1 - s2);
9062 memcpy(temp, word_a[wi].str, s1);
9063 temp[tab_real_max_size + s1 - s2] = '\0';
9064 free(word_a[wi].str);
9065 word_a[wi].str = temp;
9066 }
9067 }
9068
9069 /* Fifth pass: transforms the remaining SOFT_EXCLUDE_MARKs with */
9070 /* EXCLUDE_MARKs. */
9071 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9072 for (wi = 0; wi < count; wi++)
9073 {
9074 long * data;
9075 wchar_t * w;
9076 ll_t * list;
9077
9078 if (word_a[wi].is_selectable == SOFT_EXCLUDE_MARK)
9079 word_a[wi].is_selectable = EXCLUDE_MARK;
9080
9081 /* If the word is selectable insert it in the TST tree */
9082 /* with its associated index in the input stream. */
9083 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
9084 if (word_a[wi].is_selectable)
9085 {
9086 data = xmalloc(sizeof(long));
9087 *data = wi;
9088
9089 /* Create a wide characters string from the word screen */
9090 /* representation to be able to store in in the TST. */
9091 /* Note that the direct access selector,if any, is not */
9092 /* stored. */
9093 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
9094 if (word_a[wi].is_numbered)
9095 w = utf8_strtowcs(word_a[wi].str + daccess.flength);
9096 else
9097 w = utf8_strtowcs(word_a[wi].str);
9098
9099 /* If we didn't already encounter this word, then create a new */
9100 /* entry in the TST for it and store its index in its list. */
9101 /* Otherwise, add its index in its index list. */
9102 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9103 if (tst_word && (list = tst_search(tst_word, w)) != NULL)
9104 ll_append(list, data);
9105 else
9106 {
9107 list = ll_new();
9108 ll_append(list, data);
9109 tst_word = tst_insert(tst_word, w, list);
9110 }
9111 free(w);
9112 }
9113 }
9114
9115 /* The word after the last one is set to NULL. */
9116 /* """"""""""""""""""""""""""""""""""""""""""" */
9117 word_a[count].str = NULL;
9118
9119 /* We can now allocate the space for our tmp_word work variable. */
9120 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9121 tmp_word = xcalloc(word_real_max_size + 1, 1);
9122
9123 search_data.utf8_off_a = xmalloc(word_real_max_size * sizeof(long));
9124 search_data.utf8_len_a = xmalloc(word_real_max_size * sizeof(long));
9125
9126 win.start = 0; /* index of the first element in the *
9127 | words array to be displayed. */
9128
9129 /* We can now build the first metadata. */
9130 /* """""""""""""""""""""""""""""""""""" */
9131 last_line = build_metadata(&term, count, &win);
9132
9133 /* Index of the selected element in the array words */
9134 /* The string can be: */
9135 /* "last" The string "last" put the cursor on the last word */
9136 /* n a number put the cursor on the word n */
9137 /* /pref /+a regexp put the cursor on the first */
9138 /* word matching the prefix "pref" */
9139 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9140
9141 for (wi = 0; wi < count; wi++)
9142 {
9143 long len;
9144
9145 if (daccess.padding == 'a' || word_a[wi].is_numbered)
9146 len = daccess.flength;
9147 else
9148 len = 0;
9149
9150 word_a[wi].bitmap = xcalloc(1, (word_a[wi].mb - len) / CHAR_BIT + 1);
9151 }
9152
9153 /* Find the first selectable word (if any) in the input stream. */
9154 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9155 for (first_selectable = 0;
9156 first_selectable < count && !word_a[first_selectable].is_selectable;
9157 first_selectable++)
9158 ;
9159
9160 /* If not found, abort. */
9161 /* """""""""""""""""""" */
9162 if (first_selectable == count)
9163 {
9164 fprintf(stderr, "No selectable word found.\n");
9165
9166 exit(EXIT_FAILURE);
9167 }
9168
9169 /* Else find the last selectable word in the input stream. */
9170 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
9171 last_selectable = count - 1;
9172 while (last_selectable > 0 && !word_a[last_selectable].is_selectable)
9173 last_selectable--;
9174
9175 if (pre_selection_index == NULL)
9176 /* Option -s was not used. */
9177 /* """"""""""""""""""""""" */
9178 current = first_selectable;
9179 else if (*pre_selection_index == '/')
9180 {
9181 /* A regular expression is expected. */
9182 /* """"""""""""""""""""""""""""""""" */
9183 regex_t re;
9184 long index;
9185
9186 if (regcomp(&re, pre_selection_index + 1, REG_EXTENDED | REG_NOSUB) != 0)
9187 {
9188 fprintf(stderr, "%s: Invalid regular expression.\n", pre_selection_index);
9189
9190 exit(EXIT_FAILURE);
9191 }
9192 else
9193 {
9194 int found = 0;
9195 char * word;
9196
9197 for (index = first_selectable; index <= last_selectable; index++)
9198 {
9199 if (!word_a[index].is_selectable)
9200 continue;
9201
9202 if (word_a[index].orig != NULL)
9203 word = word_a[index].orig;
9204 else
9205 word = word_a[index].str;
9206
9207 if (regexec(&re, word, (int)0, NULL, 0) == 0)
9208 {
9209 current = index;
9210 found = 1;
9211 break;
9212 }
9213 }
9214
9215 if (!found)
9216 current = first_selectable;
9217 }
9218 }
9219 else if (*pre_selection_index == '=') /* exact search. */
9220 {
9221 /* A prefix is expected. */
9222 /* """"""""""""""""""""" */
9223 wchar_t * w;
9224
9225 ll_t * list;
9226 ll_node_t * node;
9227
9228 list = tst_search(tst_word, w = utf8_strtowcs(pre_selection_index + 1));
9229 if (list != NULL)
9230 {
9231 node = list->head;
9232 current = *(long *)(node->data);
9233 }
9234 else
9235 current = first_selectable;
9236
9237 free(w);
9238 }
9239 else if (*pre_selection_index != '\0')
9240 {
9241 /* A prefix string or an index is expected. */
9242 /* """""""""""""""""""""""""""""""""""""""" */
9243 int len;
9244 char * ptr = pre_selection_index;
9245
9246 if (*ptr == '#')
9247 {
9248 /* An index is expected. */
9249 /* """"""""""""""""""""" */
9250 ptr++;
9251
9252 if (sscanf(ptr, "%ld%n", ¤t, &len) == 1 && len == (int)strlen(ptr))
9253 {
9254 /* We got an index (numeric value). */
9255 /* """""""""""""""""""""""""""""""" */
9256 if (current < 0)
9257 current = first_selectable;
9258
9259 if (current >= count)
9260 current = count - 1;
9261
9262 if (!word_a[current].is_selectable)
9263 {
9264 if (current > last_selectable)
9265 current = last_selectable;
9266 else if (current < first_selectable)
9267 current = first_selectable;
9268 else
9269 while (current > first_selectable && !word_a[current].is_selectable)
9270 current--;
9271 }
9272 }
9273 else if (*ptr == '\0' || strcmp(ptr, "last") == 0)
9274 /* We got a special index (empty or last). */
9275 /* """"""""""""""""""""""""""""""""""""""" */
9276 current = last_selectable;
9277 else
9278 {
9279 fprintf(stderr, "%s: Invalid index.\n", ptr);
9280
9281 exit(EXIT_FAILURE);
9282 }
9283 }
9284 else
9285 {
9286 /* A prefix is expected. */
9287 /* """"""""""""""""""""" */
9288 wchar_t * w;
9289
9290 new_current = last_selectable;
9291 if (NULL
9292 != tst_prefix_search(tst_word, w = utf8_strtowcs(ptr), tst_cb_cli))
9293 current = new_current;
9294 else
9295 current = first_selectable;
9296 free(w);
9297 }
9298 }
9299 else
9300 current = first_selectable;
9301
9302 /* We now need to adjust the 'start'/'end' fields of the */
9303 /* structure 'win'. */
9304 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
9305 set_win_start_end(&win, current, last_line);
9306
9307 /* Re-associates /dev/tty with stdin and stdout. */
9308 /* """"""""""""""""""""""""""""""""""""""""""""" */
9309 if (freopen("/dev/tty", "r", stdin) == NULL)
9310 {
9311 fprintf(stderr, "Unable to associate /dev/tty with stdin.\n");
9312 exit(EXIT_FAILURE);
9313 }
9314
9315 old_fd1 = dup(1);
9316 old_stdout = fdopen(old_fd1, "w");
9317
9318 setbuf(old_stdout, NULL);
9319
9320 if (freopen("/dev/tty", "w", stdout) == NULL)
9321 {
9322 fprintf(stderr, "Unable to associate /dev/tty with stdout.\n");
9323 exit(EXIT_FAILURE);
9324 }
9325
9326 setvbuf(stdout, NULL, _IONBF, 0);
9327
9328 /* Set the characteristics of the terminal. */
9329 /* """""""""""""""""""""""""""""""""""""""" */
9330 setup_term(fileno(stdin));
9331
9332 if (!get_cursor_position(&row, &col))
9333 {
9334 fprintf(stderr, "The terminal does not have the capability to report "
9335 "the cursor position.\n");
9336 restore_term(fileno(stdin));
9337
9338 exit(EXIT_FAILURE);
9339 }
9340
9341 /* Initialize the search buffer with tab_real_max_size+1 NULs */
9342 /* It will never be reallocated, only cleared. */
9343 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9344 search_data.buf = xcalloc(1, word_real_max_size + 1 - daccess.flength);
9345
9346 /* Hide the cursor. */
9347 /* """""""""""""""" */
9348 tputs(TPARM1(cursor_invisible), 1, outch);
9349
9350 /* Force the display to start at a beginning of line. */
9351 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
9352 get_cursor_position(&term.curs_line, &term.curs_column);
9353 if (term.curs_column > 1)
9354 puts("");
9355
9356 /* Display the words window and its title for the first time. */
9357 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9358 disp_message(message_lines_list, message_max_width, message_max_len, &term,
9359 &win, &langinfo);
9360
9361 /* Before displaying the word windows for the first time when ins */
9362 /* column or line mode, we need to ensure that the word under the */
9363 /* cursor will be visible by setting the number of the first column */
9364 /* to be displayed. */
9365 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9366 if (win.col_mode || win.line_mode)
9367 {
9368 long pos;
9369 long len;
9370
9371 len = term.ncolumns - 3;
9372
9373 /* Adjust win.first_column if the cursor is not visible. */
9374 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
9375 pos = first_word_in_line_a[line_nb_of_word_a[current]];
9376
9377 while (word_a[current].end - word_a[pos].start >= len)
9378 pos++;
9379
9380 win.first_column = word_a[pos].start;
9381 }
9382
9383 /* Save the initial cursor line and column, here only the line is */
9384 /* interesting us. This will tell us if we are in need to compensate */
9385 /* a terminal automatic scrolling. */
9386 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9387 get_cursor_position(&term.curs_line, &term.curs_column);
9388
9389 nl = disp_lines(&win, &toggles, current, count, search_mode, &search_data,
9390 &term, last_line, tmp_word, &langinfo);
9391
9392 /* Determine the number of lines to move the cursor up if the window */
9393 /* display needed a terminal scrolling. */
9394 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9395 if (nl + term.curs_line - 1 > term.nlines)
9396 offset = term.curs_line + nl - term.nlines;
9397 else
9398 offset = 0;
9399
9400 /* Set the cursor to the first line of the window. */
9401 /* """"""""""""""""""""""""""""""""""""""""""""""" */
9402 {
9403 long i; /* generic index in this block. */
9404
9405 for (i = 1; i < offset; i++)
9406 tputs(TPARM1(cursor_up), 1, outch);
9407 }
9408
9409 /* Save again the cursor current line and column positions so that we */
9410 /* will be able to put the terminal cursor back here. */
9411 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9412 get_cursor_position(&term.curs_line, &term.curs_column);
9413
9414 /* Arm the periodic timer. */
9415 /* """"""""""""""""""""""" */
9416 periodic_itv.it_value.tv_sec = 0;
9417 periodic_itv.it_value.tv_usec = TCK;
9418 periodic_itv.it_interval.tv_sec = 0;
9419 periodic_itv.it_interval.tv_usec = TCK;
9420 setitimer(ITIMER_REAL, &periodic_itv, NULL);
9421
9422 /* Signal management. */
9423 /* """""""""""""""""" */
9424 void sig_handler(int s);
9425
9426 sa.sa_handler = sig_handler;
9427 sa.sa_flags = 0;
9428 sigemptyset(&sa.sa_mask);
9429 sigaction(SIGWINCH, &sa, NULL);
9430 sigaction(SIGALRM, &sa, NULL);
9431 sigaction(SIGTERM, &sa, NULL);
9432 sigaction(SIGHUP, &sa, NULL);
9433 sigaction(SIGSEGV, &sa, NULL);
9434
9435 /* Main loop. */
9436 /* """""""""" */
9437 while (1)
9438 {
9439 int sc = 0; /* scancode */
9440
9441 /* Manage a segmentation fault by exiting with failure and restoring */
9442 /* the terminal and the cursor. */
9443 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9444 if (got_sigsegv)
9445 {
9446 fputs("SIGSEGV received!\n", stderr);
9447 tputs(TPARM1(carriage_return), 1, outch);
9448 tputs(TPARM1(cursor_normal), 1, outch);
9449 restore_term(fileno(stdin));
9450
9451 exit(128 + SIGSEGV);
9452 }
9453
9454 /* Manage the hangup and termination signal by exiting with failure */
9455 /* and restoring the terminal and the cursor. */
9456 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9457 if (got_sigterm || got_sighup)
9458 {
9459 fputs("Interrupted!\n", stderr);
9460 tputs(TPARM1(carriage_return), 1, outch);
9461 tputs(TPARM1(cursor_normal), 1, outch);
9462 restore_term(fileno(stdin));
9463
9464 if (got_sigterm)
9465 exit(128 + SIGTERM);
9466 else
9467 exit(128 + SIGHUP);
9468 }
9469
9470 /* If this alarm is triggered, then redisplay the window */
9471 /* to remove the help message and disable this timer. */
9472 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
9473 if (got_help_alrm)
9474 {
9475 got_help_alrm = 0;
9476
9477 /* Calculate the new metadata and draw the window again. */
9478 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
9479 last_line = build_metadata(&term, count, &win);
9480
9481 help_mode = 0;
9482 nl = disp_lines(&win, &toggles, current, count, search_mode, &search_data,
9483 &term, last_line, tmp_word, &langinfo);
9484 }
9485
9486 /* Reset the direct access selector if the direct access alarm rang. */
9487 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9488 if (got_daccess_alrm)
9489 {
9490 got_daccess_alrm = 0;
9491 memset(daccess_stack, '\0', 6);
9492 daccess_stack_head = 0;
9493
9494 daccess_timer = timers.direct_access;
9495 }
9496
9497 if (got_search_alrm)
9498 {
9499 got_search_alrm = 0;
9500
9501 /* Calculate the new metadata and draw the window again. */
9502 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
9503 last_line = build_metadata(&term, count, &win);
9504
9505 search_mode = NONE;
9506
9507 nl = disp_lines(&win, &toggles, current, count, search_mode, &search_data,
9508 &term, last_line, tmp_word, &langinfo);
9509 }
9510
9511 if (got_winch)
9512 {
9513 got_winch = 0;
9514 got_winch_alrm = 0;
9515 winch_timer = timers.winch; /* Rearm the refresh timer. */
9516 }
9517
9518 /* If the timeout is set then decrement its remaining value */
9519 /* Upon expiration of this alarm, we trigger a content update */
9520 /* of the window. */
9521 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9522 if (got_winch_alrm)
9523 {
9524 long i; /* generic index in this block */
9525 int nlines, ncolumns;
9526 int line, column;
9527 int original_message_lines;
9528
9529 got_winch_alrm = 0; /* Reset the flag signaling the need for a refresh. */
9530 winch_timer = -1; /* Disarm the timer used for this refresh. */
9531
9532 if (message_lines_list != NULL && message_lines_list->len > 0)
9533 original_message_lines = message_lines_list->len + 1;
9534 else
9535 original_message_lines = 0;
9536
9537 get_terminal_size(&nlines, &ncolumns, &term);
9538
9539 /* Update term with the new number of lines and columns */
9540 /* of the real terminal. */
9541 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
9542 term.nlines = nlines;
9543 term.ncolumns = ncolumns;
9544
9545 /* Reset the number of lines if the terminal has enough lines. */
9546 /* message_lines_list->len+1 is used here instead of */
9547 /* win.message_lines because win.message_lines may have been */
9548 /* altered by a previous scrolling and not yet recalculated. */
9549 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9550 if (term.nlines <= original_message_lines)
9551 {
9552 win.message_lines = term.nlines - 1;
9553 win.max_lines = 1;
9554 }
9555 else
9556 {
9557 win.message_lines = original_message_lines;
9558
9559 if (win.max_lines < term.nlines - win.message_lines)
9560 {
9561 if (win.asked_max_lines == 0)
9562 win.max_lines = term.nlines - win.message_lines;
9563 else
9564 {
9565 if (win.asked_max_lines > term.nlines - win.message_lines)
9566 win.max_lines = term.nlines - win.message_lines;
9567 else
9568 win.max_lines = win.asked_max_lines;
9569 }
9570 }
9571 else
9572 win.max_lines = term.nlines - win.message_lines;
9573 }
9574
9575 /* Erase the visible part of the displayed window. */
9576 /* """"""""""""""""""""""""""""""""""""""""""""""" */
9577 for (i = 0; i < win.message_lines; i++)
9578 {
9579 tputs(TPARM1(clr_bol), 1, outch);
9580 tputs(TPARM1(clr_eol), 1, outch);
9581 tputs(TPARM1(cursor_up), 1, outch);
9582 }
9583
9584 tputs(TPARM1(clr_bol), 1, outch);
9585 tputs(TPARM1(clr_eol), 1, outch);
9586 tputs(TPARM1(save_cursor), 1, outch);
9587
9588 get_cursor_position(&line, &column);
9589
9590 for (i = 1; i < nl + win.message_lines; i++)
9591 {
9592 if (line + i >= nlines)
9593 break; /* We have reached the last terminal line. */
9594
9595 tputs(TPARM1(cursor_down), 1, outch);
9596 tputs(TPARM1(clr_bol), 1, outch);
9597 tputs(TPARM1(clr_eol), 1, outch);
9598 }
9599 tputs(TPARM1(restore_cursor), 1, outch);
9600
9601 /* Get new cursor position. */
9602 /* """""""""""""""""""""""" */
9603 get_cursor_position(&term.curs_line, &term.curs_column);
9604
9605 /* Calculate the new metadata and draw the window again. */
9606 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
9607 last_line = build_metadata(&term, count, &win);
9608
9609 if (win.col_mode || win.line_mode)
9610 {
9611 long pos;
9612 long len;
9613
9614 len = term.ncolumns - 3;
9615
9616 /* Adjust win.first_column if the cursor is no more visible. */
9617 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9618 pos = first_word_in_line_a[line_nb_of_word_a[current]];
9619
9620 while (word_a[current].end - word_a[pos].start >= len)
9621 pos++;
9622
9623 win.first_column = word_a[pos].start;
9624 }
9625
9626 disp_message(message_lines_list, message_max_width, message_max_len,
9627 &term, &win, &langinfo);
9628
9629 nl = disp_lines(&win, &toggles, current, count, search_mode, &search_data,
9630 &term, last_line, tmp_word, &langinfo);
9631
9632 /* Determine the number of lines to move the cursor up if the window */
9633 /* display needed a terminal scrolling. */
9634 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9635 if (nl + win.message_lines + term.curs_line > term.nlines)
9636 offset = term.curs_line + nl + win.message_lines - term.nlines;
9637 else
9638 offset = 0;
9639
9640 /* Set the cursor to the first line of the window. */
9641 /* """"""""""""""""""""""""""""""""""""""""""""""" */
9642 for (i = 1; i < offset; i++)
9643 tputs(TPARM1(cursor_up), 1, outch);
9644
9645 /* Get new cursor position. */
9646 /* """""""""""""""""""""""" */
9647 get_cursor_position(&term.curs_line, &term.curs_column);
9648
9649 /* Short-circuit the loop. */
9650 /* """"""""""""""""""""""" */
9651 continue;
9652 }
9653
9654 /* and possibly set its reached value. */
9655 /* The counter is frozen in search and help mode. */
9656 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9657 if (timeout.initial_value && search_mode == NONE && !help_mode)
9658 {
9659 if (got_timeout_tick)
9660 {
9661 long i;
9662 char * timeout_string;
9663
9664 got_timeout_tick = 0;
9665
9666 timeout.remain--;
9667
9668 if (!quiet_timeout && timeout.remain % FREQ == 0)
9669 {
9670 sprintf(timeout_seconds, "%5u", timeout.remain / FREQ);
9671 timeout_string =
9672 (char *)(((ll_node_t *)(message_lines_list->tail))->data);
9673 memcpy(timeout_string + 1, timeout_seconds, 5);
9674
9675 /* Erase the current window. */
9676 /* """"""""""""""""""""""""" */
9677 for (i = 0; i < win.message_lines; i++)
9678 {
9679 tputs(TPARM1(cursor_up), 1, outch);
9680 tputs(TPARM1(clr_bol), 1, outch);
9681 tputs(TPARM1(clr_eol), 1, outch);
9682 }
9683
9684 tputs(TPARM1(clr_bol), 1, outch);
9685 tputs(TPARM1(clr_eol), 1, outch);
9686
9687 /* Display the words window and its title for the first time. */
9688 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9689 disp_message(message_lines_list, message_max_width, message_max_len,
9690 &term, &win, &langinfo);
9691 }
9692 /* The timeout has expired. */
9693 /* """""""""""""""""""""""" */
9694 if (timeout.remain == 0)
9695 timeout.reached = 1;
9696 }
9697 }
9698
9699 if (timeout.reached)
9700 {
9701 if (timeout.mode == QUIT)
9702 goto quit;
9703 else if (timeout.mode == CURRENT || timeout.mode == WORD)
9704 goto enter;
9705 }
9706
9707 /* Pressed keys scancodes processing. */
9708 /* """""""""""""""""""""""""""""""""" */
9709 page = 1; /* Default number of lines to do down/up *
9710 | with PgDn/PgUp. */
9711
9712 sc = get_scancode(buffer, 15);
9713
9714 if (sc && winch_timer < 0) /* Do not allow input when a window *
9715 | refresh is scheduled. */
9716 {
9717 if (timeout.initial_value && buffer[0] != 0x0d && buffer[0] != 'q'
9718 && buffer[0] != 'Q' && buffer[0] != 3)
9719 {
9720 long i;
9721
9722 char * timeout_string;
9723
9724 /* Reset the timeout to its initial value. */
9725 /* """"""""""""""""""""""""""""""""""""""" */
9726 timeout.remain = timeout.initial_value;
9727
9728 if (!quiet_timeout)
9729 {
9730 sprintf(timeout_seconds, "%5u", timeout.initial_value / FREQ);
9731 timeout_string =
9732 (char *)(((ll_node_t *)(message_lines_list->tail))->data);
9733 memcpy(timeout_string + 1, timeout_seconds, 5);
9734
9735 /* Clear the message. */
9736 /* """""""""""""""""" */
9737 for (i = 0; i < win.message_lines; i++)
9738 {
9739 tputs(TPARM1(cursor_up), 1, outch);
9740 tputs(TPARM1(clr_bol), 1, outch);
9741 tputs(TPARM1(clr_eol), 1, outch);
9742 }
9743
9744 tputs(TPARM1(clr_bol), 1, outch);
9745 tputs(TPARM1(clr_eol), 1, outch);
9746
9747 /* Display the words window and its title for the first time. */
9748 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9749 disp_message(message_lines_list, message_max_width, message_max_len,
9750 &term, &win, &langinfo);
9751 }
9752
9753 setitimer(ITIMER_REAL, &periodic_itv, NULL);
9754 }
9755
9756 if (search_mode == NONE)
9757 if (help_mode && buffer[0] != '?')
9758 {
9759 got_help_alrm = 1;
9760 continue;
9761 }
9762
9763 switch (buffer[0])
9764 {
9765 case 0x01: /* ^A */
9766 if (search_mode != NONE)
9767 goto khome;
9768
9769 break;
9770
9771 case 0x1a: /* ^Z */
9772 if (search_mode != NONE)
9773 goto kend;
9774
9775 break;
9776
9777 case 0x1b: /* ESC */
9778 /* An escape sequence or a UTF-8 sequence has been pressed. */
9779 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
9780 if (memcmp("\x1bOH", buffer, 3) == 0
9781 || memcmp("\x1bk", buffer, 2) == 0
9782 || memcmp("\x1b[H", buffer, 3) == 0
9783 || memcmp("\x1b[1~", buffer, 4) == 0
9784 || memcmp("\x1b[7~", buffer, 4) == 0)
9785 {
9786 /* HOME key has been pressed. */
9787 /* """""""""""""""""""""""""" */
9788 if (search_mode != NONE)
9789 {
9790 khome:
9791 search_data.only_starting = 1;
9792 search_data.only_ending = 0;
9793 select_starting_matches(&win, &term, &search_data, &last_line);
9794 }
9795 else
9796 {
9797 /* Find the first selectable word. */
9798 /* """"""""""""""""""""""""""""""" */
9799 current = win.start;
9800
9801 while (current < win.end && !word_a[current].is_selectable)
9802 current++;
9803
9804 /* In column mode we need to take care of the */
9805 /* horizontal scrolling. */
9806 /* """""""""""""""""""""""""""""""""""""""""" */
9807 if (win.col_mode || win.line_mode)
9808 if (word_a[current].end < win.first_column)
9809 win.first_column = word_a[current].start;
9810 }
9811
9812 nl = disp_lines(&win, &toggles, current, count, search_mode,
9813 &search_data, &term, last_line, tmp_word,
9814 &langinfo);
9815 break;
9816 }
9817
9818 if (memcmp("\x1b[1;5H", buffer, 6) == 0
9819 || memcmp("\x1b[1;2H", buffer, 6) == 0
9820 || memcmp("\x1b[7^", buffer, 4) == 0)
9821 /* SHIFT/CTRL HOME key has been pressed. */
9822 /* """"""""""""""""""""""""""""""""""""" */
9823 goto kschome;
9824
9825 if (memcmp("\x1bOF", buffer, 3) == 0
9826 || memcmp("\x1bj", buffer, 2) == 0
9827 || memcmp("\x1b[F", buffer, 3) == 0
9828 || memcmp("\x1b[4~", buffer, 4) == 0
9829 || memcmp("\x1b[8~", buffer, 4) == 0)
9830 {
9831 /* END key has been pressed. */
9832 /* """"""""""""""""""""""""" */
9833 if (search_mode != NONE)
9834 {
9835 kend:
9836
9837 if (matches_count > 0 && search_mode != PREFIX)
9838 {
9839 search_data.only_starting = 0;
9840 search_data.only_ending = 1;
9841 select_ending_matches(&win, &term, &search_data, &last_line);
9842 }
9843 }
9844 else
9845 {
9846 /* Find the last selectable word. */
9847 /* """""""""""""""""""""""""""""" */
9848 current = win.end;
9849
9850 while (current > win.start && !word_a[current].is_selectable)
9851 current--;
9852
9853 /* In column mode we need to take care of the */
9854 /* horizontal scrolling. */
9855 /* """""""""""""""""""""""""""""""""""""""""" */
9856 if (win.col_mode || win.line_mode)
9857 {
9858 long pos;
9859 long len;
9860
9861 len = term.ncolumns - 3;
9862
9863 if (word_a[current].end >= len + win.first_column)
9864 {
9865 /* Find the first word to be displayed in this line. */
9866 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
9867 pos = first_word_in_line_a[line_nb_of_word_a[current]];
9868
9869 while (word_a[pos].start <= win.first_column)
9870 pos++;
9871
9872 /* If the new current word cannot be displayed, search */
9873 /* the first word in the line that can be displayed by */
9874 /* iterating on pos. */
9875 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
9876 pos--;
9877
9878 while (word_a[current].end - word_a[pos].start >= len)
9879 pos++;
9880
9881 if (word_a[pos].start > 0)
9882 win.first_column = word_a[pos].start;
9883 }
9884 }
9885 }
9886
9887 nl = disp_lines(&win, &toggles, current, count, search_mode,
9888 &search_data, &term, last_line, tmp_word,
9889 &langinfo);
9890 break;
9891 }
9892
9893 if (memcmp("\x1b[1;5F", buffer, 6) == 0
9894 || memcmp("\x1b[1;2F", buffer, 6) == 0
9895 || memcmp("\x1b[8^", buffer, 4) == 0)
9896 /* SHIFT/CTRL END key has been pressed. */
9897 /* """""""""""""""""""""""""""""""""""" */
9898 goto kscend;
9899
9900 if (memcmp("\x1bOD", buffer, 3) == 0
9901 || memcmp("\x1b[D", buffer, 3) == 0)
9902 /* Left arrow key has been pressed. */
9903 /* """""""""""""""""""""""""""""""" */
9904 goto kl;
9905
9906 if (memcmp("\x1bOC", buffer, 3) == 0
9907 || memcmp("\x1b[C", buffer, 3) == 0)
9908 /* Right arrow key has been pressed. */
9909 /* """"""""""""""""""""""""""""""""" */
9910 goto kr;
9911
9912 if (memcmp("\x1bOA", buffer, 3) == 0
9913 || memcmp("\x1b[A", buffer, 3) == 0)
9914 /* Up arrow key has been pressed. */
9915 /* """""""""""""""""""""""""""""" */
9916 goto ku;
9917
9918 if (memcmp("\x1bOB", buffer, 3) == 0
9919 || memcmp("\x1b[B", buffer, 3) == 0)
9920 /* Down arrow key has been pressed. */
9921 /* """""""""""""""""""""""""""""""" */
9922 goto kd;
9923
9924 if (memcmp("\x1b[I", buffer, 3) == 0
9925 || memcmp("\x1b[5~", buffer, 4) == 0)
9926 /* PgUp key has been pressed. */
9927 /* """""""""""""""""""""""""" */
9928 goto kpp;
9929
9930 if (memcmp("\x1b[G", buffer, 3) == 0
9931 || memcmp("\x1b[6~", buffer, 4) == 0)
9932 /* PgDn key has been pressed. */
9933 /* """""""""""""""""""""""""" */
9934 goto knp;
9935
9936 if (memcmp("\x1b[L", buffer, 3) == 0
9937 || memcmp("\x1b[2~", buffer, 4) == 0)
9938 /* Ins key has been pressed. */
9939 /* """"""""""""""""""""""""" */
9940 goto kins;
9941
9942 if (memcmp("\x1b[3~", buffer, 4) == 0)
9943 /* Del key has been pressed. */
9944 /* """"""""""""""""""""""""" */
9945 goto kdel;
9946
9947 if (memcmp("\x1b[1;5C", buffer, 6) == 0
9948 || memcmp("\x1bOc", buffer, 3) == 0)
9949 /* CTRL -> has been pressed. */
9950 /* """""""""""""""""""""""""" */
9951 goto keol;
9952
9953 if (memcmp("\x1b[1;5D", buffer, 6) == 0
9954 || memcmp("\x1bOd", buffer, 3) == 0)
9955 /* CTRL <- key has been pressed. */
9956 /* """"""""""""""""""""""""""""" */
9957 goto ksol;
9958
9959 if (buffer[0] == 0x1b && buffer[1] == '\0')
9960 {
9961 /* ESC key has been pressed. */
9962 /* """"""""""""""""""""""""" */
9963 reset_search_buffer(&win, &search_data, &timers, &toggles, &term,
9964 &daccess, &langinfo, last_line, tmp_word,
9965 word_real_max_size);
9966
9967 break;
9968 }
9969
9970 /* Else ignore key. */
9971 break;
9972
9973 quit:
9974 case 'q':
9975 case 'Q':
9976 case 3: /* ^C */
9977 /* q or Q of ^C has been pressed. */
9978 /* """""""""""""""""""""""""""""" */
9979 if (search_mode != NONE && buffer[0] != 3)
9980 goto special_cmds_when_searching;
9981
9982 {
9983 long i; /* Generic index in this block. */
9984
9985 for (i = 0; i < win.message_lines; i++)
9986 tputs(TPARM1(cursor_up), 1, outch);
9987
9988 if (toggles.del_line)
9989 {
9990 tputs(TPARM1(clr_eol), 1, outch);
9991 tputs(TPARM1(clr_bol), 1, outch);
9992 tputs(TPARM1(save_cursor), 1, outch);
9993
9994 for (i = 1; i < nl + win.message_lines; i++)
9995 {
9996 tputs(TPARM1(cursor_down), 1, outch);
9997 tputs(TPARM1(clr_eol), 1, outch);
9998 tputs(TPARM1(clr_bol), 1, outch);
9999 }
10000 tputs(TPARM1(restore_cursor), 1, outch);
10001 }
10002 else
10003 {
10004 for (i = 1; i < nl + win.message_lines; i++)
10005 tputs(TPARM1(cursor_down), 1, outch);
10006 puts("");
10007 }
10008 }
10009
10010 /* Restore the visibility of the cursor. */
10011 /* """"""""""""""""""""""""""""""""""""" */
10012 tputs(TPARM1(cursor_normal), 1, outch);
10013
10014 if (buffer[0] == 3)
10015 {
10016 if (int_string != NULL)
10017 fprintf(old_stdout, "%s", int_string);
10018
10019 /* Set the cursor at the start on the line an restore the */
10020 /* original terminal state before exiting. */
10021 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
10022 tputs(TPARM1(carriage_return), 1, outch);
10023 restore_term(fileno(stdin));
10024
10025 if (int_as_in_shell)
10026 exit(128 + SIGINT);
10027 }
10028 else
10029 restore_term(fileno(stdin));
10030
10031 exit(EXIT_SUCCESS);
10032
10033 case 0x0c:
10034 /* Form feed (^L) is a traditional method to redraw a screen. */
10035 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10036 if (current < win.start || current > win.end)
10037 last_line = build_metadata(&term, count, &win);
10038
10039 nl = disp_lines(&win, &toggles, current, count, search_mode,
10040 &search_data, &term, last_line, tmp_word, &langinfo);
10041 break;
10042
10043 case 'n':
10044 case ' ':
10045 /* n or the space bar has been pressed. */
10046 /* """""""""""""""""""""""""""""""""""" */
10047 if (search_mode != NONE)
10048 goto special_cmds_when_searching;
10049
10050 if (matches_count > 0)
10051 {
10052 long pos = find_next_matching_word(matching_words_a, matches_count,
10053 current,
10054 &matching_word_cur_index);
10055 if (pos >= 0)
10056 current = pos;
10057
10058 if (current < win.start || current > win.end)
10059 last_line = build_metadata(&term, count, &win);
10060
10061 /* Set new first column to display. */
10062 /* """""""""""""""""""""""""""""""" */
10063 set_new_first_column(&win, &term);
10064
10065 nl = disp_lines(&win, &toggles, current, count, search_mode,
10066 &search_data, &term, last_line, tmp_word,
10067 &langinfo);
10068 }
10069 break;
10070
10071 case 'N':
10072 /* N has been pressed.*/
10073 /* """"""""""""""""""" */
10074 if (search_mode != NONE)
10075 goto special_cmds_when_searching;
10076
10077 if (matches_count > 0)
10078 {
10079 long pos = find_prev_matching_word(matching_words_a, matches_count,
10080 current,
10081 &matching_word_cur_index);
10082 if (pos >= 0)
10083 current = pos;
10084 }
10085
10086 if (current < win.start || current > win.end)
10087 last_line = build_metadata(&term, count, &win);
10088
10089 /* Set new first column to display. */
10090 /* """""""""""""""""""""""""""""""" */
10091 set_new_first_column(&win, &term);
10092
10093 nl = disp_lines(&win, &toggles, current, count, search_mode,
10094 &search_data, &term, last_line, tmp_word, &langinfo);
10095 break;
10096
10097 case 's':
10098 /* s has been pressed. */
10099 /* """"""""""""""""""" */
10100 if (search_mode != NONE)
10101 goto special_cmds_when_searching;
10102
10103 if (matches_count > 0)
10104 {
10105 long pos;
10106
10107 if (best_matches_count > 0)
10108 pos = find_next_matching_word(best_matching_words_a,
10109 best_matches_count, current,
10110 &matching_word_cur_index);
10111 else
10112 pos = find_next_matching_word(matching_words_a, matches_count,
10113 current, &matching_word_cur_index);
10114
10115 if (pos >= 0)
10116 current = pos;
10117
10118 if (current < win.start || current > win.end)
10119 last_line = build_metadata(&term, count, &win);
10120
10121 /* Set new first column to display. */
10122 /* """""""""""""""""""""""""""""""" */
10123 set_new_first_column(&win, &term);
10124
10125 nl = disp_lines(&win, &toggles, current, count, search_mode,
10126 &search_data, &term, last_line, tmp_word,
10127 &langinfo);
10128 }
10129 break;
10130
10131 case 'S':
10132 /* S has been pressed. */
10133 /* """"""""""""""""""" */
10134 if (search_mode != NONE)
10135 goto special_cmds_when_searching;
10136
10137 if (matches_count > 0)
10138 {
10139 long pos;
10140
10141 if (best_matches_count > 0)
10142 pos = find_prev_matching_word(best_matching_words_a,
10143 best_matches_count, current,
10144 &matching_word_cur_index);
10145 else
10146 pos = find_prev_matching_word(matching_words_a, matches_count,
10147 current, &matching_word_cur_index);
10148
10149 if (pos >= 0)
10150 current = pos;
10151 }
10152
10153 if (current < win.start || current > win.end)
10154 last_line = build_metadata(&term, count, &win);
10155
10156 /* Set new first column to display. */
10157 /* """""""""""""""""""""""""""""""" */
10158 set_new_first_column(&win, &term);
10159
10160 nl = disp_lines(&win, &toggles, current, count, search_mode,
10161 &search_data, &term, last_line, tmp_word, &langinfo);
10162 break;
10163
10164 enter:
10165 case 0x0d: /* CR */
10166 {
10167 /* <Enter> has been pressed. */
10168 /* """"""""""""""""""""""""" */
10169
10170 int extra_lines;
10171 char * output_str;
10172 output_t * output_node;
10173
10174 int width = 0;
10175
10176 wchar_t * w;
10177 long i; /* Generic index in this block. */
10178
10179 if (help_mode)
10180 nl = disp_lines(&win, &toggles, current, count, search_mode,
10181 &search_data, &term, last_line, tmp_word,
10182 &langinfo);
10183
10184 if (search_mode != NONE)
10185 {
10186 /* Cancel the search timer. */
10187 /* """""""""""""""""""""""" */
10188 search_timer = 0;
10189
10190 search_mode = NONE;
10191 search_data.only_starting = 0;
10192 search_data.only_ending = 0;
10193
10194 nl = disp_lines(&win, &toggles, current, count, search_mode,
10195 &search_data, &term, last_line, tmp_word,
10196 &langinfo);
10197
10198 if (!toggles.enter_val_in_search)
10199 break;
10200 }
10201
10202 /* Erase or jump after the window before printing the */
10203 /* selected string. */
10204 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
10205 if (toggles.del_line)
10206 {
10207 for (i = 0; i < win.message_lines; i++)
10208 tputs(TPARM1(cursor_up), 1, outch);
10209
10210 tputs(TPARM1(clr_eol), 1, outch);
10211 tputs(TPARM1(clr_bol), 1, outch);
10212 tputs(TPARM1(save_cursor), 1, outch);
10213
10214 for (i = 1; i < nl + win.message_lines; i++)
10215 {
10216 tputs(TPARM1(cursor_down), 1, outch);
10217 tputs(TPARM1(clr_eol), 1, outch);
10218 tputs(TPARM1(clr_bol), 1, outch);
10219 }
10220
10221 tputs(TPARM1(restore_cursor), 1, outch);
10222 }
10223 else
10224 {
10225 for (i = 1; i < nl; i++)
10226 tputs(TPARM1(cursor_down), 1, outch);
10227 puts("");
10228 }
10229
10230 /* When a timeout of type WORD is set, prints the specified word */
10231 /* else prints the current selected entries. */
10232 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10233 if (timeout.initial_value > 0 && timeout.remain == 0
10234 && timeout.mode == WORD)
10235 fprintf(old_stdout, "%s", timeout_word);
10236 else
10237 {
10238 char * num_str;
10239 char * str;
10240
10241 if (toggles.taggable)
10242 {
10243 ll_t * output_list = ll_new();
10244 ll_node_t * node;
10245
10246 /* When using -P, updates the tagging order of this word to */
10247 /* make sure that the output will be correctly sorted. */
10248 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10249 if (toggles.pinable)
10250 word_a[current].tag_order = next_tag_nb++;
10251
10252 for (wi = 0; wi < count; wi++)
10253 {
10254 if (word_a[wi].is_tagged || wi == current)
10255 {
10256 /* If the -p option is not used we do not take into */
10257 /* account an untagged word under the cursor if at least */
10258 /* on word is tagged. */
10259 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
10260 if (wi == current && tagged_words > 0 && !toggles.autotag
10261 && !word_a[wi].is_tagged)
10262 continue;
10263
10264 /* In tagged mode, do not autotag the word under the cursor */
10265 /* if toggles.noautotag is set and no word are tagged. */
10266 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10267 if (tagged_words == 0 && toggles.taggable
10268 && toggles.noautotag)
10269 continue;
10270
10271 /* Chose the original string to print if the current one */
10272 /* has been altered by a possible expansion. */
10273 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
10274 output_node = xmalloc(sizeof(output_t));
10275
10276 if (word_a[wi].orig != NULL)
10277 str = word_a[wi].orig;
10278 else
10279 str = word_a[wi].str;
10280
10281 if (word_a[wi].is_numbered && daccess.num_sep)
10282 {
10283 num_str = xstrndup(str + 1, daccess.length);
10284
10285 ltrim(num_str, " ");
10286 rtrim(num_str, " ", 0);
10287
10288 output_node->output_str = concat(num_str, daccess.num_sep,
10289 str + daccess.flength,
10290 (char *)0);
10291
10292 free(num_str);
10293 }
10294 else
10295 output_node->output_str = xstrdup(str + daccess.flength);
10296
10297 output_node->order = word_a[wi].tag_order;
10298
10299 /* Trim the spaces if -k is not given. */
10300 /* """"""""""""""""""""""""""""""""""" */
10301 if (!toggles.keep_spaces)
10302 {
10303 ltrim(output_node->output_str, " \t");
10304 rtrim(output_node->output_str, " \t", 0);
10305 }
10306
10307 ll_append(output_list, output_node);
10308 }
10309 }
10310
10311 /* If nothing is selected, exist without printing anything. */
10312 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10313 if (output_list->head == NULL)
10314 goto exit;
10315
10316 /* If -P is in use, then sort the output list. */
10317 /* """"""""""""""""""""""""""""""""""""""""""" */
10318 if (toggles.pinable)
10319 ll_sort(output_list, tag_comp, tag_swap);
10320
10321 /* And print them. */
10322 /* """"""""""""""" */
10323 node = output_list->head;
10324 while (node->next != NULL)
10325 {
10326 str = ((output_t *)(node->data))->output_str;
10327
10328 fprintf(old_stdout, "%s", str);
10329 width += wcswidth((w = utf8_strtowcs(str)), 65535);
10330 free(w);
10331 free(str);
10332 free(node->data);
10333
10334 if (win.sel_sep != NULL)
10335 {
10336 fprintf(old_stdout, "%s", win.sel_sep);
10337 width += wcswidth((w = utf8_strtowcs(win.sel_sep)), 65535);
10338 free(w);
10339 }
10340 else
10341 {
10342 fprintf(old_stdout, " ");
10343 width++;
10344 }
10345
10346 node = node->next;
10347 }
10348
10349 str = ((output_t *)(node->data))->output_str;
10350 fprintf(old_stdout, "%s", str);
10351 width += wcswidth((w = utf8_strtowcs(str)), 65535);
10352 free(w);
10353 free(str);
10354 free(node->data);
10355 }
10356 else
10357 {
10358 /* Chose the original string to print if the current one has */
10359 /* been altered by a possible expansion. */
10360 /* Once this made, print it. */
10361 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10362 if (word_a[current].orig != NULL)
10363 str = word_a[current].orig;
10364 else
10365 str = word_a[current].str;
10366
10367 if (word_a[current].is_numbered && daccess.num_sep)
10368 {
10369 num_str = xstrndup(str + 1, daccess.length);
10370
10371 ltrim(num_str, " ");
10372 rtrim(num_str, " ", 0);
10373
10374 output_str = concat(num_str, daccess.num_sep,
10375 str + daccess.flength, (char *)0);
10376
10377 free(num_str);
10378 }
10379 else
10380 output_str = str + daccess.flength;
10381
10382 /* Trim the spaces if -k is not given. */
10383 /* """"""""""""""""""""""""""""""""""" */
10384 if (!toggles.keep_spaces)
10385 {
10386 ltrim(output_str, " \t");
10387 rtrim(output_str, " \t", 0);
10388 }
10389
10390 width = wcswidth((w = utf8_strtowcs(output_str)), 65535);
10391 free(w);
10392
10393 /* And print it. */
10394 /* """"""""""""" */
10395 fprintf(old_stdout, "%s", output_str);
10396 }
10397
10398 /* If the output stream is a terminal. */
10399 /* """"""""""""""""""""""""""""""""""" */
10400 if (isatty(old_fd1))
10401 {
10402 /* width is (in term of terminal columns) the size of the */
10403 /* string to be displayed. */
10404 /* */
10405 /* With that information, count the number of terminal lines */
10406 /* printed. */
10407 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10408 extra_lines = width / term.ncolumns;
10409
10410 /* Clean the printed line and all the extra lines used. */
10411 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
10412 tputs(TPARM1(delete_line), 1, outch);
10413
10414 for (i = 0; i < extra_lines; i++)
10415 {
10416 tputs(TPARM1(cursor_up), 1, outch);
10417 tputs(TPARM1(clr_eol), 1, outch);
10418 tputs(TPARM1(clr_bol), 1, outch);
10419 }
10420 }
10421 }
10422
10423 exit:
10424
10425 /* Restore the visibility of the cursor. */
10426 /* """"""""""""""""""""""""""""""""""""" */
10427 tputs(TPARM1(cursor_normal), 1, outch);
10428
10429 /* Set the cursor at the start on the line an restore the */
10430 /* original terminal state before exiting. */
10431 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
10432 tputs(TPARM1(carriage_return), 1, outch);
10433 restore_term(fileno(stdin));
10434
10435 exit(EXIT_SUCCESS);
10436 }
10437
10438 ksol:
10439 /* Go to the start of the line. */
10440 /* """""""""""""""""""""""""""" */
10441 if (search_mode != NONE)
10442 search_mode = NONE;
10443
10444 /* Fall through. */
10445 /* """"""""""""" */
10446
10447 case 'H':
10448 if (search_mode == NONE)
10449 {
10450 current = first_word_in_line_a[line_nb_of_word_a[current]];
10451
10452 while (!word_a[current].is_selectable)
10453 current++;
10454
10455 win.first_column = 0;
10456 set_new_first_column(&win, &term);
10457
10458 nl = disp_lines(&win, &toggles, current, count, search_mode,
10459 &search_data, &term, last_line, tmp_word,
10460 &langinfo);
10461 }
10462 else
10463 goto special_cmds_when_searching;
10464
10465 break;
10466
10467 kl:
10468 /* Cursor Left key has been pressed. */
10469 /* """"""""""""""""""""""""""""""""" */
10470 if (search_mode != NONE)
10471 search_mode = NONE;
10472
10473 /* Fall through. */
10474 /* """"""""""""" */
10475
10476 case 'h':
10477 if (search_mode == NONE)
10478 move_left(&win, &term, &toggles, &search_data, &langinfo, &nl,
10479 last_line, tmp_word);
10480 else
10481 goto special_cmds_when_searching;
10482
10483 break;
10484
10485 keol:
10486 /* Go to the end of the line. */
10487 /* """""""""""""""""""""""""" */
10488 if (search_mode != NONE)
10489 search_mode = NONE;
10490
10491 /* Fall through. */
10492 /* """"""""""""" */
10493
10494 case 'L':
10495 if (search_mode == NONE)
10496 {
10497 long cur_line = line_nb_of_word_a[current];
10498
10499 current = get_line_last_word(cur_line, last_line);
10500 win.first_column = win.real_max_width - 1 - (term.ncolumns - 3);
10501
10502 while (!word_a[current].is_selectable)
10503 current--;
10504
10505 set_new_first_column(&win, &term);
10506
10507 nl = disp_lines(&win, &toggles, current, count, search_mode,
10508 &search_data, &term, last_line, tmp_word,
10509 &langinfo);
10510 }
10511 else
10512 goto special_cmds_when_searching;
10513
10514 break;
10515
10516 kr:
10517 /* Right key has been pressed. */
10518 /* """"""""""""""""""""""""""" */
10519 if (search_mode != NONE)
10520 search_mode = NONE;
10521
10522 /* Fall through. */
10523 /* """"""""""""" */
10524
10525 case 'l':
10526 if (search_mode == NONE)
10527 move_right(&win, &term, &toggles, &search_data, &langinfo, &nl,
10528 last_line, tmp_word);
10529 else
10530 goto special_cmds_when_searching;
10531
10532 break;
10533
10534 case 0x0b:
10535 /* ^K key has been pressed. */
10536 /* """""""""""""""""""""""" */
10537 goto kschome;
10538
10539 case 'K':
10540 if (search_mode != NONE)
10541 goto special_cmds_when_searching;
10542
10543 kpp:
10544 /* PgUp key has been pressed. */
10545 /* """""""""""""""""""""""""" */
10546 page = win.max_lines;
10547
10548 ku:
10549 /* Cursor Up key has been pressed. */
10550 /* """"""""""""""""""""""""""""""" */
10551 if (search_mode != NONE)
10552 search_mode = NONE;
10553
10554 /* Fall through. */
10555 /* """"""""""""" */
10556
10557 case 'k':
10558 if (search_mode == NONE)
10559 move_up(&win, &term, &toggles, &search_data, &langinfo, &nl, page,
10560 first_selectable, last_line, tmp_word);
10561 else
10562 goto special_cmds_when_searching;
10563
10564 break;
10565
10566 kschome:
10567 /* Go to the first selectable word. */
10568 /* """""""""""""""""""""""""""""""" */
10569 current = 0;
10570
10571 if (search_mode != NONE)
10572 search_mode = NONE;
10573
10574 /* Find the first selectable word. */
10575 /* """"""""""""""""""""""""""""""" */
10576 while (!word_a[current].is_selectable)
10577 current++;
10578
10579 if (current < win.start || current > win.end)
10580 last_line = build_metadata(&term, count, &win);
10581
10582 /* In column mode we need to take care of the */
10583 /* horizontal scrolling. */
10584 /* """""""""""""""""""""""""""""""""""""""""" */
10585 if (win.col_mode || win.line_mode)
10586 {
10587 long pos;
10588
10589 /* Adjust win.first_column if the cursor is */
10590 /* no more visible. */
10591 /* """""""""""""""""""""""""""""""""""""""" */
10592 pos = first_word_in_line_a[line_nb_of_word_a[current]];
10593
10594 while (word_a[current].end - word_a[pos].start >= term.ncolumns - 3)
10595 pos++;
10596
10597 win.first_column = word_a[pos].start;
10598 }
10599
10600 nl = disp_lines(&win, &toggles, current, count, search_mode,
10601 &search_data, &term, last_line, tmp_word, &langinfo);
10602 break;
10603
10604 case 0x0a:
10605 /* ^J key has been pressed. */
10606 /* """""""""""""""""""""""" */
10607 goto kscend;
10608
10609 case 'J':
10610 if (search_mode != NONE)
10611 goto special_cmds_when_searching;
10612
10613 knp:
10614 /* PgDn key has been pressed. */
10615 /* """""""""""""""""""""""""" */
10616 page = win.max_lines;
10617
10618 kd:
10619 /* Cursor Down key has been pressed. */
10620 /* """"""""""""""""""""""""""""""""" */
10621 if (search_mode != NONE)
10622 search_mode = NONE;
10623
10624 /* Fall through. */
10625 /* """"""""""""" */
10626
10627 case 'j':
10628 if (search_mode == NONE)
10629 move_down(&win, &term, &toggles, &search_data, &langinfo, &nl, page,
10630 last_selectable, last_line, tmp_word);
10631 else
10632 goto special_cmds_when_searching;
10633
10634 break;
10635
10636 kscend:
10637 /* Go to the last selectable word. */
10638 /* """"""""""""""""""""""""""""""" */
10639 current = count - 1;
10640
10641 if (search_mode != NONE)
10642 search_mode = NONE;
10643
10644 /* Find the first selectable word. */
10645 /* """"""""""""""""""""""""""""""" */
10646 while (!word_a[current].is_selectable)
10647 current--;
10648
10649 if (current < win.start || current > win.end)
10650 last_line = build_metadata(&term, count, &win);
10651
10652 /* In column mode we need to take care of the */
10653 /* horizontal scrolling. */
10654 /* """""""""""""""""""""""""""""""""""""""""" */
10655 if (win.col_mode || win.line_mode)
10656 {
10657 long pos;
10658
10659 /* Adjust win.first_column if the cursor is no more visible. */
10660 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10661 pos = first_word_in_line_a[line_nb_of_word_a[current]];
10662
10663 while (word_a[current].end - word_a[pos].start >= term.ncolumns - 3)
10664 pos++;
10665
10666 win.first_column = word_a[pos].start;
10667 }
10668
10669 nl = disp_lines(&win, &toggles, current, count, search_mode,
10670 &search_data, &term, last_line, tmp_word, &langinfo);
10671 break;
10672
10673 case '/':
10674 /* Generic search method according the misc settings. */
10675 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
10676 if (misc.default_search_method == PREFIX)
10677 goto prefix_method;
10678 else if (misc.default_search_method == SUBSTRING)
10679 goto substring_method;
10680 else if (misc.default_search_method == FUZZY)
10681 goto fuzzy_method;
10682
10683 break;
10684
10685 case '~':
10686 case '*':
10687 /* ~ or * key has been pressed */
10688 /* (start of a fuzzy search session). */
10689 /* """""""""""""""""""""""""""""""""" */
10690 fuzzy_method:
10691
10692 if (search_mode == NONE)
10693 {
10694 if (!toggles.incremental_search)
10695 reset_search_buffer(&win, &search_data, &timers, &toggles, &term,
10696 &daccess, &langinfo, last_line, tmp_word,
10697 word_real_max_size);
10698
10699 /* Set the search timer. */
10700 /* """"""""""""""""""""" */
10701 search_timer = timers.search; /* default 10 s. */
10702
10703 search_mode = FUZZY;
10704
10705 if (old_search_mode != FUZZY)
10706 {
10707 old_search_mode = FUZZY;
10708 clean_matches(&search_data, word_real_max_size);
10709 }
10710
10711 nl = disp_lines(&win, &toggles, current, count, search_mode,
10712 &search_data, &term, last_line, tmp_word,
10713 &langinfo);
10714 }
10715 else
10716 goto special_cmds_when_searching;
10717
10718 break;
10719
10720 case '\'':
10721 case '\"':
10722 /* ' or " key has been pressed */
10723 /* (start of a substring search session). */
10724 /* """""""""""""""""""""""""""""""""""""" */
10725 substring_method:
10726
10727 if (search_mode == NONE)
10728 {
10729 if (!toggles.incremental_search)
10730 reset_search_buffer(&win, &search_data, &timers, &toggles, &term,
10731 &daccess, &langinfo, last_line, tmp_word,
10732 word_real_max_size);
10733
10734 /* Set the search timer. */
10735 /* """"""""""""""""""""" */
10736 search_timer = timers.search; /* default 10 s. */
10737
10738 search_mode = SUBSTRING;
10739
10740 if (old_search_mode != SUBSTRING)
10741 {
10742 old_search_mode = SUBSTRING;
10743 clean_matches(&search_data, word_real_max_size);
10744 }
10745
10746 nl = disp_lines(&win, &toggles, current, count, search_mode,
10747 &search_data, &term, last_line, tmp_word,
10748 &langinfo);
10749 }
10750 else
10751 goto special_cmds_when_searching;
10752
10753 break;
10754
10755 case '=':
10756 case '^':
10757 /* ^ or = key has been pressed */
10758 /* (start of a prefix search session). */
10759 /* """"""""""""""""""""""""""""""""""" */
10760 prefix_method:
10761
10762 if (search_mode == NONE)
10763 {
10764 if (!toggles.incremental_search)
10765 reset_search_buffer(&win, &search_data, &timers, &toggles, &term,
10766 &daccess, &langinfo, last_line, tmp_word,
10767 word_real_max_size);
10768
10769 /* Set the search timer. */
10770 /* """"""""""""""""""""" */
10771 search_timer = timers.search; /* default 10 s. */
10772
10773 search_mode = PREFIX;
10774
10775 if (old_search_mode != PREFIX)
10776 {
10777 old_search_mode = PREFIX;
10778 clean_matches(&search_data, word_real_max_size);
10779 }
10780
10781 nl = disp_lines(&win, &toggles, current, count, search_mode,
10782 &search_data, &term, last_line, tmp_word,
10783 &langinfo);
10784 break;
10785 }
10786 else
10787 goto special_cmds_when_searching;
10788
10789 break;
10790
10791 kins:
10792 /* The INS key has been pressed to tag a word if */
10793 /* tagging is enabled. */
10794 /* """"""""""""""""""""""""""""""""""""""""""""" */
10795 if (toggles.taggable)
10796 {
10797 if (word_a[current].is_tagged == 0)
10798 {
10799 tagged_words++;
10800 word_a[current].is_tagged = 1;
10801
10802 if (toggles.pinable)
10803 word_a[current].tag_order = next_tag_nb++;
10804
10805 nl = disp_lines(&win, &toggles, current, count, search_mode,
10806 &search_data, &term, last_line, tmp_word,
10807 &langinfo);
10808 }
10809 }
10810 break;
10811
10812 kdel:
10813 /* The DEL key has been pressed to untag a word if */
10814 /* tagging is enabled. */
10815 /* """"""""""""""""""""""""""""""""""""""""""""""" */
10816 if (toggles.taggable)
10817 {
10818 if (word_a[current].is_tagged == 1)
10819 {
10820 word_a[current].is_tagged = 0;
10821 tagged_words--;
10822
10823 nl = disp_lines(&win, &toggles, current, count, search_mode,
10824 &search_data, &term, last_line, tmp_word,
10825 &langinfo);
10826 }
10827 }
10828 break;
10829
10830 case 't':
10831 /* t has been pressed to tag/untag a word if */
10832 /* tagging is enabled. */
10833 /* """"""""""""""""""""""""""""""""""""""""" */
10834 if (search_mode == NONE)
10835 {
10836 if (toggles.taggable)
10837 {
10838 if (word_a[current].is_tagged)
10839 {
10840 word_a[current].is_tagged = 0;
10841 tagged_words--;
10842 }
10843 else
10844 {
10845 word_a[current].is_tagged = 1;
10846 tagged_words++;
10847
10848 if (toggles.pinable)
10849 word_a[current].tag_order = next_tag_nb++;
10850 }
10851 nl = disp_lines(&win, &toggles, current, count, search_mode,
10852 &search_data, &term, last_line, tmp_word,
10853 &langinfo);
10854 }
10855 }
10856 else
10857 goto special_cmds_when_searching;
10858 break;
10859
10860 case 'T':
10861 /* T has been pressed to tag all matching words resulting */
10862 /* from a previous search if tagging is enabled. */
10863 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
10864 if (search_mode == NONE)
10865 {
10866 if (toggles.taggable)
10867 {
10868 long i, n;
10869
10870 for (i = 0; i < matches_count; i++)
10871 {
10872 n = matching_words_a[i];
10873
10874 if (!word_a[n].is_tagged)
10875 {
10876 word_a[n].is_tagged = 1;
10877 tagged_words++;
10878
10879 if (toggles.pinable)
10880 word_a[n].tag_order = next_tag_nb++;
10881 }
10882 }
10883 nl = disp_lines(&win, &toggles, current, count, search_mode,
10884 &search_data, &term, last_line, tmp_word,
10885 &langinfo);
10886 }
10887 }
10888 else
10889 goto special_cmds_when_searching;
10890 break;
10891
10892 case 'U':
10893 /* U has been pressed to untag all matching words resulting */
10894 /* from a previous search if tagging is enabled. */
10895 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10896 if (search_mode == NONE)
10897 {
10898 if (toggles.taggable)
10899 {
10900 long i, n;
10901
10902 for (i = 0; i < matches_count; i++)
10903 {
10904 n = matching_words_a[i];
10905
10906 if (word_a[n].is_tagged)
10907 {
10908 word_a[n].is_tagged = 0;
10909 tagged_words--;
10910 }
10911 }
10912 nl = disp_lines(&win, &toggles, current, count, search_mode,
10913 &search_data, &term, last_line, tmp_word,
10914 &langinfo);
10915 }
10916 }
10917 else
10918 goto special_cmds_when_searching;
10919 break;
10920
10921 case '0':
10922 case '1':
10923 case '2':
10924 case '3':
10925 case '4':
10926 case '5':
10927 case '6':
10928 case '7':
10929 case '8':
10930 case '9':
10931 /* A digit has been pressed to build a number to be used for */
10932 /* A direct acces to a words if direct access is enabled. */
10933 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
10934 {
10935 if (search_mode == NONE && daccess.mode != DA_TYPE_NONE)
10936 {
10937 wchar_t * w;
10938 long * pos;
10939
10940 /* Set prev_current to the initial current word to be */
10941 /* able to return here if the first direct access fails. */
10942 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
10943 if (daccess_stack_head == 0)
10944 prev_current = current;
10945
10946 if (daccess_stack_head == daccess.length)
10947 break;
10948
10949 daccess_stack[daccess_stack_head] = buffer[0];
10950 daccess_stack_head++;
10951 w = utf8_strtowcs(daccess_stack);
10952 pos = tst_search(tst_daccess, w);
10953 free(w);
10954
10955 if (pos != NULL)
10956 {
10957 current = *pos;
10958
10959 if (current < win.start || current > win.end)
10960 last_line = build_metadata(&term, count, &win);
10961
10962 /* Set new first column to display. */
10963 /* """""""""""""""""""""""""""""""" */
10964 set_new_first_column(&win, &term);
10965
10966 nl = disp_lines(&win, &toggles, current, count, search_mode,
10967 &search_data, &term, last_line, tmp_word,
10968 &langinfo);
10969 }
10970 else
10971 {
10972 if (current != prev_current)
10973 {
10974 current = prev_current;
10975
10976 if (current < win.start || current > win.end)
10977 last_line = build_metadata(&term, count, &win);
10978
10979 /* Set new first column to display. */
10980 /* """""""""""""""""""""""""""""""" */
10981 set_new_first_column(&win, &term);
10982
10983 nl = disp_lines(&win, &toggles, current, count, search_mode,
10984 &search_data, &term, last_line, tmp_word,
10985 &langinfo);
10986 }
10987 }
10988
10989 daccess_timer = timers.direct_access;
10990 }
10991 else
10992 goto special_cmds_when_searching;
10993 }
10994 break;
10995
10996 case 0x08: /* ^H */
10997 case 0x7f: /* BS */
10998 /* backspace/CTRL-H management. */
10999 /* """""""""""""""""""""""""""" */
11000 {
11001 long i;
11002
11003 if (daccess_stack_head > 0)
11004 daccess_stack[--daccess_stack_head] = '\0';
11005
11006 if (search_mode != NONE)
11007 {
11008 if (search_data.utf8_len > 0)
11009 {
11010 char * prev;
11011
11012 prev = utf8_prev(search_data.buf,
11013 search_data.buf + search_data.len - 1);
11014
11015 if (search_data.utf8_len == search_data.fuzzy_err_pos - 1)
11016 {
11017 search_data.fuzzy_err = 0;
11018 search_data.fuzzy_err_pos = -1;
11019 }
11020 search_data.utf8_len--;
11021
11022 if (prev)
11023 {
11024 *(utf8_next(prev)) = '\0';
11025 search_data.len = prev - search_data.buf + 1;
11026 }
11027 else
11028 {
11029 *search_data.buf = '\0';
11030 search_data.len = 0;
11031
11032 for (i = 0; i < matches_count; i++)
11033 {
11034 long n = matching_words_a[i];
11035
11036 word_a[n].is_matching = 0;
11037
11038 memset(word_a[n].bitmap, '\0',
11039 (word_a[n].mb - daccess.flength) / CHAR_BIT + 1);
11040 }
11041
11042 matches_count = 0;
11043
11044 nl = disp_lines(&win, &toggles, current, count, search_mode,
11045 &search_data, &term, last_line, tmp_word,
11046 &langinfo);
11047 }
11048 }
11049 else
11050 my_beep(&toggles);
11051
11052 if (search_data.utf8_len > 0)
11053 goto special_cmds_when_searching;
11054 else
11055 /* When there is only one glyph in the search list in */
11056 /* FUZZY and SUBSTRING mode then all is already done except */
11057 /* the cleanup of the first level of the tst_search_list. */
11058 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
11059 if (search_mode != PREFIX)
11060 {
11061 sub_tst_t * sub_tst_data;
11062 ll_node_t * node;
11063
11064 node = tst_search_list->tail;
11065 sub_tst_data = (sub_tst_t *)(node->data);
11066
11067 search_data.fuzzy_err = 0;
11068
11069 sub_tst_data->count = 0;
11070 }
11071 }
11072 }
11073
11074 break;
11075
11076 case '?':
11077 /* Help mode. */
11078 /* """""""""" */
11079 if (search_mode == NONE)
11080 {
11081 help(&win, &term, last_line, &toggles);
11082 help_mode = 1;
11083
11084 /* Arm the help timer. */
11085 /* """"""""""""""""""" */
11086 help_timer = timers.help; /* default 15 s. */
11087 }
11088 else
11089 goto special_cmds_when_searching;
11090 break;
11091
11092 special_cmds_when_searching:
11093 default:
11094 {
11095 int c; /* byte index in the scancode string .*/
11096 sub_tst_t * sub_tst_data;
11097 unsigned long long index;
11098 long i;
11099
11100 if (search_mode != NONE)
11101 {
11102 long old_len = search_data.len;
11103 long old_utf8_len = search_data.utf8_len;
11104 ll_node_t * node;
11105 wchar_t * ws;
11106
11107 /* Copy all the bytes included in the key press to buffer. */
11108 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
11109 if (buffer[0] != 0x08 && buffer[0] != 0x7f) /* Backspace. */
11110 {
11111 /* The only case where we have to manage backspace hits */
11112 /* here is if the user has entered them in fuzzy search */
11113 /* mode. As the search buffer has already been amended, */
11114 /* we do not have to update the search buffer again. */
11115 /* '''''''''''''''''''''''''''''''''''''''''''''''''''' */
11116 for (c = 0; c < sc
11117 && search_data.utf8_len
11118 < word_real_max_size - daccess.flength;
11119 c++)
11120 search_data.buf[search_data.len++] = buffer[c];
11121
11122 /* Update the glyph array with the content of the search */
11123 /* buffer. */
11124 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
11125 if (search_data.utf8_len < word_real_max_size - daccess.flength)
11126 {
11127 search_data.utf8_off_a[search_data.utf8_len] = old_len;
11128 search_data.utf8_len_a[search_data.utf8_len] = search_data.len
11129 - old_len;
11130 search_data.utf8_len++;
11131 }
11132 }
11133
11134 /* Restart the search timer. */
11135 /* """"""""""""""""""""""""" */
11136 search_timer = timers.search; /* default 10 s. */
11137
11138 if (search_mode == PREFIX)
11139 {
11140 ws = utf8_strtowcs(search_data.buf);
11141
11142 /* Purge the matching words list. */
11143 /* """""""""""""""""""""""""""""" */
11144 for (i = 0; i < matches_count; i++)
11145 {
11146 long n = matching_words_a[i];
11147
11148 word_a[n].is_matching = 0;
11149
11150 memset(word_a[n].bitmap, '\0',
11151 (word_a[n].mb - daccess.flength) / CHAR_BIT + 1);
11152 }
11153
11154 matches_count = 0;
11155
11156 tst_prefix_search(tst_word, ws, tst_cb);
11157
11158 /* Latches_count is updated by tst_cb. */
11159 /* """"""""""""""""""""""""""""""""""" */
11160 if (matches_count > 0)
11161 {
11162 if (search_data.len == old_len && matches_count == 1
11163 && buffer[0] != 0x08 && buffer[0] != 0x7f)
11164 my_beep(&toggles);
11165 else
11166 {
11167 /* Adjust the bitmap to the ending version. */
11168 /* """""""""""""""""""""""""""""""""""""""" */
11169 update_bitmaps(search_mode, &search_data, NO_AFFINITY);
11170
11171 current = matching_words_a[0];
11172
11173 if (current < win.start || current > win.end)
11174 last_line = build_metadata(&term, count, &win);
11175
11176 /* Set new first column to display. */
11177 /* """""""""""""""""""""""""""""""" */
11178 set_new_first_column(&win, &term);
11179
11180 nl = disp_lines(&win, &toggles, current, count, search_mode,
11181 &search_data, &term, last_line, tmp_word,
11182 &langinfo);
11183 }
11184 }
11185 else
11186 {
11187 my_beep(&toggles);
11188
11189 search_data.len = old_len;
11190 search_data.utf8_len = old_utf8_len;
11191 search_data.buf[search_data.len] = '\0';
11192 }
11193 }
11194 else if (search_mode == FUZZY)
11195 {
11196 /* tst_search_list: [sub_tst_t *] -> [sub_tst_t *]... */
11197 /* ^ ^ */
11198 /* | | */
11199 /* level 1 level_2 */
11200 /* */
11201 /* Each sub_tst_t * points to a data structure including */
11202 /* a sorted array to nodes in the words tst. */
11203 /* Each of these node starts a matching candidate. */
11204 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
11205 wchar_t * w = utf8_strtowcs(search_data.buf + old_len);
11206
11207 /* zero previous matching indicators. */
11208 /* """""""""""""""""""""""""""""""""" */
11209 for (i = 0; i < matches_count; i++)
11210 {
11211 long n = matching_words_a[i];
11212
11213 word_a[n].is_matching = 0;
11214
11215 memset(word_a[n].bitmap, '\0',
11216 (word_a[n].mb - daccess.flength) / CHAR_BIT + 1);
11217 }
11218
11219 matches_count = 0;
11220
11221 if (buffer[0] == 0x08 || buffer[0] == 0x7f) /* Backspace */
11222 {
11223 node = tst_search_list->tail;
11224 sub_tst_data = (sub_tst_t *)(node->data);
11225
11226 sub_tst_data->count = 0;
11227
11228 if (tst_search_list->len > 0)
11229 {
11230 free(sub_tst_data->array);
11231 free(sub_tst_data);
11232
11233 ll_delete(tst_search_list, tst_search_list->tail);
11234 }
11235
11236 if (search_data.utf8_len == search_data.fuzzy_err_pos - 1)
11237 {
11238 search_data.fuzzy_err = 0;
11239 search_data.fuzzy_err_pos = -1;
11240 }
11241 }
11242 else
11243 {
11244 if (search_data.utf8_len == 1)
11245 {
11246 /* Search all the sub-tst trees having the searched */
11247 /* character as children, the resulting sub-tst are put */
11248 /* in the sub tst array attached to the currently */
11249 /* searched symbol. */
11250 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
11251 tst_fuzzy_traverse(tst_word, NULL, 0, w[0]);
11252
11253 node = tst_search_list->tail;
11254 sub_tst_data = (sub_tst_t *)(node->data);
11255 if (sub_tst_data->count == 0)
11256 {
11257 my_beep(&toggles);
11258
11259 search_data.len = 0;
11260 search_data.utf8_len = 0;
11261 search_data.buf[0] = '\0';
11262
11263 break;
11264 }
11265 }
11266 else
11267 {
11268 /* Prevent the list to grow larger than the maximal */
11269 /* word's length. */
11270 /* """""""""""""""""""""""""""""""""""""""""""""""" */
11271 if (tst_search_list->len
11272 < word_real_max_size - daccess.flength)
11273 {
11274 /* use the results in the level n-1 list to build the */
11275 /* level n list. */
11276 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
11277 int rc;
11278
11279 sub_tst_t * tst_fuzzy_level_data;
11280
11281 tst_fuzzy_level_data = sub_tst_new();
11282
11283 ll_append(tst_search_list, tst_fuzzy_level_data);
11284
11285 node = tst_search_list->tail->prev;
11286 sub_tst_data = (sub_tst_t *)(node->data);
11287
11288 rc = 0;
11289 for (index = 0; index < sub_tst_data->count; index++)
11290 rc += tst_fuzzy_traverse(sub_tst_data->array[index], NULL,
11291 1, w[0]);
11292
11293 if (rc == 0)
11294 {
11295 free(tst_fuzzy_level_data->array);
11296 free(tst_fuzzy_level_data);
11297
11298 ll_delete(tst_search_list, tst_search_list->tail);
11299
11300 search_data.fuzzy_err = 1;
11301 if (search_data.fuzzy_err_pos == -1)
11302 search_data.fuzzy_err_pos = search_data.utf8_len;
11303
11304 my_beep(&toggles);
11305
11306 search_data.len = old_len;
11307 search_data.utf8_len = old_utf8_len;
11308 search_data.buf[search_data.len] = '\0';
11309 }
11310 }
11311 else
11312 my_beep(&toggles);
11313 }
11314 }
11315 free(w);
11316
11317 /* Process this level to mark the word found as a matching */
11318 /* word if any. */
11319 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
11320 node = tst_search_list->tail;
11321 sub_tst_data = (sub_tst_t *)(node->data);
11322
11323 for (index = 0; index < sub_tst_data->count; index++)
11324 tst_traverse(sub_tst_data->array[index], set_matching_flag, 0);
11325
11326 /* Update the bitmap and re-display the window. */
11327 /* """""""""""""""""""""""""""""""""""""""""""" */
11328 if (matches_count > 0)
11329 {
11330 if (search_data.only_starting)
11331 select_starting_matches(&win, &term, &search_data,
11332 &last_line);
11333 else if (search_data.only_ending)
11334 select_ending_matches(&win, &term, &search_data, &last_line);
11335 else
11336 /* Adjust the bitmap to the ending version. */
11337 /* """""""""""""""""""""""""""""""""""""""" */
11338 update_bitmaps(search_mode, &search_data, NO_AFFINITY);
11339
11340 current = matching_words_a[0];
11341
11342 if (current < win.start || current > win.end)
11343 last_line = build_metadata(&term, count, &win);
11344
11345 /* Set new first column to display. */
11346 /* """""""""""""""""""""""""""""""" */
11347 set_new_first_column(&win, &term);
11348
11349 nl = disp_lines(&win, &toggles, current, count, search_mode,
11350 &search_data, &term, last_line, tmp_word,
11351 &langinfo);
11352 }
11353 else
11354 my_beep(&toggles);
11355 }
11356 else /* SUBSTRING. */
11357 {
11358 wchar_t * w = utf8_strtowcs(search_data.buf);
11359
11360 /* Purge the matching words list. */
11361 /* """""""""""""""""""""""""""""" */
11362 for (i = 0; i < matches_count; i++)
11363 {
11364 long n = matching_words_a[i];
11365
11366 word_a[n].is_matching = 0;
11367
11368 memset(word_a[n].bitmap, '\0',
11369 (word_a[n].mb - daccess.flength) / CHAR_BIT + 1);
11370 }
11371
11372 matches_count = 0;
11373
11374 if (search_data.utf8_len == 1)
11375 {
11376 /* Search all the sub-tst trees having the searched */
11377 /* character as children, the resulting sub-tst are put */
11378 /* in the level list corresponding to the letter order. */
11379 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
11380 tst_substring_traverse(tst_word, NULL, 0, w[0]);
11381
11382 node = tst_search_list->tail;
11383 sub_tst_data = (sub_tst_t *)(node->data);
11384
11385 for (index = 0; index < sub_tst_data->count; index++)
11386 tst_traverse(sub_tst_data->array[index], set_matching_flag,
11387 0);
11388 }
11389 else
11390 {
11391 /* Search for the rest of the word in all the sub-tst */
11392 /* trees previously found. */
11393 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
11394 node = tst_search_list->tail;
11395 sub_tst_data = (sub_tst_t *)(node->data);
11396
11397 matches_count = 0;
11398
11399 for (index = 0; index < sub_tst_data->count; index++)
11400 tst_prefix_search(sub_tst_data->array[index], w + 1, tst_cb);
11401 }
11402
11403 if (matches_count > 0)
11404 {
11405 if (search_data.len == old_len && matches_count == 1
11406 && buffer[0] != 0x08 && buffer[0] != 0x7f)
11407 my_beep(&toggles);
11408 else
11409 {
11410 if (search_data.only_starting)
11411 select_starting_matches(&win, &term, &search_data,
11412 &last_line);
11413 else if (search_data.only_ending)
11414 select_ending_matches(&win, &term, &search_data,
11415 &last_line);
11416 else
11417 update_bitmaps(search_mode, &search_data, NO_AFFINITY);
11418
11419 current = matching_words_a[0];
11420
11421 if (current < win.start || current > win.end)
11422 last_line = build_metadata(&term, count, &win);
11423
11424 /* Set new first column to display. */
11425 /* """""""""""""""""""""""""""""""" */
11426 set_new_first_column(&win, &term);
11427
11428 nl = disp_lines(&win, &toggles, current, count, search_mode,
11429 &search_data, &term, last_line, tmp_word,
11430 &langinfo);
11431 }
11432 }
11433 else
11434 {
11435 my_beep(&toggles);
11436
11437 search_data.len = old_len;
11438 search_data.utf8_len--;
11439 search_data.buf[search_data.len] = '\0';
11440 }
11441 }
11442 }
11443 }
11444 }
11445 }
11446 }
11447 }
11448