1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "vt_screen.h"
4 
5 #include <stdlib.h> /* abs */
6 #include <unistd.h> /* write */
7 #include <sys/time.h>
8 #include <pobl/bl_debug.h>
9 #include <pobl/bl_mem.h>  /* malloc/free */
10 #include <pobl/bl_str.h>  /* strdup */
11 #include <pobl/bl_util.h> /* BL_MIN */
12 
13 #include "vt_char_encoding.h"
14 #include "vt_str_parser.h"
15 
16 #define ROW_IN_LOGS(screen, row) (vt_get_num_logged_lines(&(screen)->logs) + row)
17 
18 #if 1
19 #define EXIT_BS_AT_BOTTOM
20 #endif
21 
22 /* --- static variables --- */
23 
24 static char *word_separators = " .,:;/|@()[]{}";
25 static int regard_uri_as_word = 0;
26 
27 /* --- static functions --- */
28 
get_n_prev_char_pos(vt_screen_t * screen,int * char_index,int * row,int n)29 static int get_n_prev_char_pos(vt_screen_t *screen, int *char_index, int *row, int n) {
30   int count;
31 
32   *char_index = vt_cursor_char_index(screen->edit);
33   *row = vt_cursor_row(screen->edit);
34 
35   for (count = 0; count < n; count++) {
36     if (*char_index == 0) {
37       return 0;
38     }
39 
40     (*char_index)--;
41   }
42 
43   return 1;
44 }
45 
is_word_separator(vt_char_t * ch)46 static int is_word_separator(vt_char_t *ch) {
47   char *p;
48   char c;
49 
50   if (vt_char_cs(ch) != US_ASCII) {
51     return 0;
52   }
53 
54   p = word_separators;
55   c = vt_char_code(ch);
56 
57   while (*p) {
58     if (c == *p) {
59       return 1;
60     }
61 
62     p++;
63   }
64 
65   return 0;
66 }
67 
68 /*
69  * callbacks of vt_edit_scroll_event_listener_t.
70  */
71 
72 /*
73  * Don't operate vt_model_t in this function because vt_model_t is not scrolled
74  * yet.
75  * Operate vt_model_t in scrolled_out_lines_finished().
76  */
receive_scrolled_out_line(void * p,vt_line_t * line)77 static void receive_scrolled_out_line(void *p, vt_line_t *line) {
78   vt_screen_t *screen;
79 
80   screen = p;
81 
82   if (vt_status_line_is_focused(screen)) {
83     vt_line_set_size_attr(line, 0);
84     line->mark = 0;
85 
86     return;
87   }
88 
89   if (screen->screen_listener && screen->screen_listener->line_scrolled_out) {
90     (*screen->screen_listener->line_scrolled_out)(screen->screen_listener->self);
91   }
92 
93   if (screen->logvis) {
94     (*screen->logvis->visual_line)(screen->logvis, line);
95   } else {
96     line->num_filled_chars =
97         vt_line_get_num_filled_chars_except_sp_with_func(line, vt_char_equal);
98   }
99 
100   vt_log_add(&screen->logs, line);
101 
102 /* XXX see vt_line_iscii_visual() */
103 #if 1
104   if (line->num_chars > vt_screen_get_logical_cols(screen)) {
105     /*
106      * line->num_filled_chars can be more than line->num_chars
107      * without vt_line_reset() here because line is visualized.
108      */
109     vt_line_reset(line);
110     vt_line_set_updated(line);
111 
112     vt_str_final(line->chars + vt_screen_get_logical_cols(screen),
113                  line->num_chars - vt_screen_get_logical_cols(screen));
114     line->num_chars = vt_screen_get_logical_cols(screen);
115   } else
116 #endif
117   {
118     vt_line_set_size_attr(line, 0);
119     line->mark = 0;
120   }
121 
122   if (vt_screen_is_backscrolling(screen) == BSM_STATIC) {
123     screen->backscroll_rows++;
124   }
125 
126   if (screen->search) {
127     screen->search->row--;
128   }
129 }
130 
scrolled_out_lines_finished(void * p)131 static void scrolled_out_lines_finished(void *p) {
132   vt_screen_t *screen;
133 
134   screen = p;
135 
136   if (vt_screen_is_backscrolling(screen) == BSM_DEFAULT) {
137     vt_screen_set_modified_all(screen);
138   }
139 }
140 
window_scroll_upward_region(void * p,int beg_row,int end_row,u_int size)141 static int window_scroll_upward_region(void *p, int beg_row, int end_row, u_int size) {
142   vt_screen_t *screen;
143 
144   screen = p;
145 
146   if (screen->is_backscrolling) {
147     /*
148      * Not necessary to scrolling window. If backscroll_mode is BSM_DEFAULT,
149      * vt_screen_set_modified_all() in scrolled_out_lines_finished() later.
150      */
151     return 1;
152   }
153 
154   if (!screen->screen_listener || !screen->screen_listener->window_scroll_upward_region) {
155     return 0;
156   }
157 
158   return (*screen->screen_listener->window_scroll_upward_region)(screen->screen_listener->self,
159                                                                  beg_row, end_row, size);
160 }
161 
window_scroll_downward_region(void * p,int beg_row,int end_row,u_int size)162 static int window_scroll_downward_region(void *p, int beg_row, int end_row, u_int size) {
163   vt_screen_t *screen;
164 
165   screen = p;
166 
167   if (screen->is_backscrolling) {
168     /*
169      * Not necessary to scrolling window. If backscroll_mode is BSM_DEFAULT,
170      * vt_screen_set_modified_all() in scrolled_out_lines_finished() later.
171      */
172     return 1;
173   }
174 
175   if (!screen->screen_listener || !screen->screen_listener->window_scroll_downward_region) {
176     return 0;
177   }
178 
179   return (*screen->screen_listener->window_scroll_downward_region)(screen->screen_listener->self,
180                                                                    beg_row, end_row, size);
181 }
182 
top_line_is_wrapped(void * p)183 static int top_line_is_wrapped(void *p) {
184   vt_screen_t *screen;
185   u_int num;
186 
187   screen = p;
188 
189   if ((num = vt_get_num_logged_lines(&screen->logs)) > 0) {
190     if (vt_line_is_continued_to_next(vt_log_get(&screen->logs, num - 1))) {
191       return 1;
192     }
193   }
194 
195   return 0;
196 }
197 
modify_region(vt_screen_t * screen,int * end_char_index,int * end_row)198 static int modify_region(vt_screen_t *screen, /* visual */
199                          int *end_char_index, int *end_row) {
200   int row;
201   vt_line_t *line;
202 
203   /* Removing empty lines of the end. */
204 
205   row = *end_row;
206 
207   while ((line = vt_screen_get_line(screen, row)) == NULL || vt_line_is_empty(line)) {
208     if (--row < 0 && abs(row) > vt_get_num_logged_lines(&screen->logs)) {
209       return 0;
210     }
211   }
212 
213   if (row < *end_row) {
214     if (vt_line_is_rtl(line)) {
215       *end_char_index = vt_line_beg_char_index_regarding_rtl(line);
216     } else {
217       if ((*end_char_index = vt_line_get_num_filled_chars_except_sp(line)) > 0) {
218         (*end_char_index)--;
219       }
220     }
221 
222     *end_row = row;
223   }
224 
225   return 1;
226 }
227 
convert_col_to_char_index(vt_screen_t * screen,vt_line_t * line,int * beg_char_index,int * end_char_index,int beg_col,int end_col)228 static void convert_col_to_char_index(vt_screen_t *screen, /* visual */
229                                       vt_line_t *line, int *beg_char_index,
230                                       int *end_char_index, /* end + 1 */
231                                       int beg_col, int end_col) {
232   int padding;
233   int beg;
234   int end;
235   u_int rest;
236 
237   if (vt_line_is_rtl(line) &&
238       (padding = vt_screen_get_cols(screen) - vt_line_get_num_filled_cols(line)) > 0) {
239     beg_col -= padding;
240     end_col -= padding;
241   }
242 
243   *beg_char_index = vt_convert_col_to_char_index(line, &rest, beg_col, 0);
244 
245   if ((end = vt_line_get_num_filled_chars_except_sp(line)) <= *beg_char_index ||
246       (end == *beg_char_index + 1 && rest > 0)) {
247     *beg_char_index = end;
248   } else if ((beg = vt_line_beg_char_index_regarding_rtl(line)) > *beg_char_index) {
249     *beg_char_index = beg;
250   }
251 
252   *end_char_index = vt_convert_col_to_char_index(line, NULL, end_col, 0) + 1;
253   if (end < *end_char_index) {
254     *end_char_index = end;
255   }
256 }
257 
reverse_or_restore_color_rect(vt_screen_t * screen,int beg_col,int beg_row,int end_col,int end_row,int (* func)(vt_line_t *,int))258 static int reverse_or_restore_color_rect(vt_screen_t *screen, /* visual */
259                                          int beg_col, int beg_row, int end_col, int end_row,
260                                          int (*func)(vt_line_t *, int)) {
261   int row;
262   int beg_char_index;
263   int end_char_index;
264   vt_line_t *line;
265 
266   if (beg_col > end_col) {
267     int col;
268 
269     col = beg_col;
270     beg_col = end_col;
271     end_col = col;
272   }
273 
274   for (row = beg_row; row <= end_row; row++) {
275     if ((line = vt_screen_get_line(screen, row))) {
276       int char_index;
277 
278       convert_col_to_char_index(screen, line, &beg_char_index, &end_char_index, beg_col, end_col);
279 
280       for (char_index = beg_char_index; char_index < end_char_index; char_index++) {
281         (*func)(line, char_index);
282       }
283     }
284   }
285 
286   return 1;
287 }
288 
reverse_or_restore_color(vt_screen_t * screen,int beg_char_index,int beg_row,int end_char_index,int end_row,int (* func)(vt_line_t *,int))289 static int reverse_or_restore_color(vt_screen_t *screen, /* visual */
290                                     int beg_char_index, int beg_row, int end_char_index,
291                                     int end_row, int (*func)(vt_line_t *, int)) {
292   /*
293    * LTR line:    a<aa bbb ccc>
294    *                ^beg     ^end
295    * RTL line:    c<cc bbb aaa>
296    *                ^end     ^beg
297    *              ^beg_regarding_rtl
298    * <>: selected region
299    */
300 
301   int char_index;
302   int row;
303   vt_line_t *line;
304   u_int size_except_sp;
305   int beg_regarding_rtl;
306 
307 #ifdef __DEBUG
308   bl_debug_printf(BL_DEBUG_TAG " reverse/restore region: %d %d %d %d\n", beg_char_index, beg_row,
309                   end_char_index, end_row);
310 #endif
311 
312   if (!modify_region(screen, &end_char_index, &end_row)) {
313     return 0;
314   }
315 
316   /* Removing empty lines of the beginning. */
317 
318   row = beg_row;
319 
320   while (1) {
321     if (!(line = vt_screen_get_line(screen, row)) || vt_line_is_empty(line)) {
322       goto next_line;
323     }
324 
325     size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
326     beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
327 
328     if (vt_line_is_rtl(line)) {
329       if (row > beg_row || beg_char_index >= size_except_sp) {
330         beg_char_index = BL_MAX(size_except_sp, 1) - 1;
331       } else if (beg_char_index < beg_regarding_rtl) {
332         goto next_line;
333       }
334     } else {
335       if (row > beg_row || beg_char_index < beg_regarding_rtl) {
336         beg_char_index = beg_regarding_rtl;
337       } else if (beg_char_index >= size_except_sp) {
338         goto next_line;
339       }
340     }
341 
342     break;
343 
344   next_line:
345     if (++row > end_row) {
346       return 0;
347     }
348   }
349 
350   if (row < end_row) {
351     if (vt_line_is_rtl(line)) {
352       for (char_index = beg_regarding_rtl; char_index <= beg_char_index; char_index++) {
353         (*func)(line, char_index);
354       }
355     } else {
356       for (char_index = beg_char_index; char_index < size_except_sp; char_index++) {
357         (*func)(line, char_index);
358       }
359     }
360 
361     for (row++; row < end_row; row++) {
362       if (vt_line_is_empty((line = vt_screen_get_line(screen, row)))) {
363         continue;
364       }
365 
366       size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
367 
368       for (char_index = vt_line_beg_char_index_regarding_rtl(line); char_index < size_except_sp;
369            char_index++) {
370         (*func)(line, char_index);
371       }
372     }
373 
374     if (vt_line_is_empty((line = vt_screen_get_line(screen, row)))) {
375       return 1;
376     }
377 
378     size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
379     beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
380 
381     if (vt_line_is_rtl(line)) {
382       beg_char_index = BL_MAX(size_except_sp, 1) - 1;
383     } else {
384       beg_char_index = beg_regarding_rtl;
385     }
386   }
387 
388   /* row == end_row */
389 
390   if (vt_line_is_rtl(line)) {
391     if (end_char_index < size_except_sp) {
392       for (char_index = BL_MAX(end_char_index, beg_regarding_rtl); char_index <= beg_char_index;
393            char_index++) {
394         (*func)(line, char_index);
395       }
396     }
397   } else {
398     if (end_char_index >= beg_regarding_rtl) {
399       for (char_index = beg_char_index; char_index < BL_MIN(end_char_index + 1, size_except_sp);
400            char_index++) {
401         (*func)(line, char_index);
402       }
403     }
404   }
405 
406   return 1;
407 }
408 
check_or_copy_region_rect(vt_screen_t * screen,vt_char_t * chars,u_int num_chars,int beg_col,int beg_row,int end_col,int end_row)409 static u_int check_or_copy_region_rect(
410     vt_screen_t *screen, /* visual */
411     vt_char_t *chars,    /* Behavior is undefined if chars is insufficient. */
412     u_int num_chars, int beg_col, int beg_row, int end_col, int end_row) {
413   int row;
414   vt_line_t *line;
415   u_int region_size;
416   int beg_char_index;
417   int end_char_index;
418 
419   if (beg_col > end_col) {
420     int col;
421 
422     col = beg_col;
423     beg_col = end_col;
424     end_col = col;
425   }
426 
427   region_size = 0;
428 
429   for (row = beg_row; row <= end_row; row++) {
430     if ((line = vt_screen_get_line(screen, row))) {
431       u_int size;
432 
433       convert_col_to_char_index(screen, line, &beg_char_index, &end_char_index, beg_col, end_col);
434 
435       size = end_char_index - beg_char_index;
436 
437       if (chars && num_chars >= region_size + size) {
438         vt_line_copy_logical_str(line, chars + region_size, beg_char_index, size);
439       }
440 
441       region_size += size;
442 
443       if (row < end_row) {
444         if (chars && num_chars >= region_size + 1) {
445           vt_char_copy(chars + region_size, vt_nl_ch());
446         }
447 
448         region_size++;
449       }
450     }
451   }
452 
453   return region_size;
454 }
455 
check_or_copy_region(vt_screen_t * screen,vt_char_t * chars,u_int num_chars,int beg_char_index,int beg_row,int end_char_index,int end_row)456 static u_int check_or_copy_region(
457     vt_screen_t *screen,                    /* visual */
458     vt_char_t *chars,                       /* Behavior is undefined if chars is insufficient. */
459     u_int num_chars, int beg_char_index, /* can be over size_except_sp */
460     int beg_row, int end_char_index,        /* can be over size_except_sp */
461     int end_row) {
462   /*
463    * LTR line:    a<aa bbb ccc>
464    *                ^beg     ^end
465    * RTL line:    c<cc bbb aaa>
466    *                ^end     ^beg
467    *              ^beg_regarding_rtl
468    * <>: selected region
469    */
470 
471   vt_line_t *line;
472   u_int size;
473   u_int region_size;
474   u_int size_except_sp;
475   int beg_regarding_rtl;
476   int row;
477 
478 #ifdef __DEBUG
479   bl_debug_printf(BL_DEBUG_TAG " check/copy region: %d %d %d %d\n", beg_char_index, beg_row,
480                   end_char_index, end_row);
481 #endif
482 
483   if (!modify_region(screen, &end_char_index, &end_row)) {
484     return 0;
485   }
486 
487   row = beg_row;
488 
489   /* Removing empty lines of the beginning. */
490   while (1) {
491     if (!(line = vt_screen_get_line(screen, row)) || vt_line_is_empty(line)) {
492       goto next_line;
493     }
494 
495     size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
496     beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
497 
498     if (vt_line_is_rtl(line)) {
499       if (row > beg_row || beg_char_index >= size_except_sp) {
500         beg_char_index = BL_MAX(size_except_sp, 1) - 1;
501       } else if (beg_char_index < beg_regarding_rtl) {
502         goto next_line;
503       }
504     } else {
505       if (row > beg_row || beg_char_index < beg_regarding_rtl) {
506         beg_char_index = beg_regarding_rtl;
507       } else if (beg_char_index >= size_except_sp) {
508         goto next_line;
509       }
510     }
511 
512     break;
513 
514   next_line:
515     if (++row > end_row) {
516       return 0;
517     }
518   }
519 
520   region_size = 0;
521 
522   if (row < end_row) {
523     if (vt_line_is_rtl(line)) {
524       size = beg_char_index - beg_regarding_rtl + 1;
525 
526       if (chars && num_chars >= region_size + size) {
527         vt_line_copy_logical_str(line, chars + region_size, beg_regarding_rtl, size);
528       }
529     } else {
530       size = size_except_sp - beg_char_index;
531 
532       if (chars && num_chars >= region_size + size) {
533         vt_line_copy_logical_str(line, chars + region_size, beg_char_index, size);
534       }
535     }
536 
537     region_size += size;
538 
539     if (!vt_line_is_continued_to_next(line)) {
540       if (chars && num_chars > region_size) {
541         vt_char_copy(chars + region_size, vt_nl_ch());
542       }
543       region_size++;
544     }
545 
546     for (row++; row < end_row; row++) {
547       line = vt_screen_get_line(screen, row);
548 
549       beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
550       size = vt_line_get_num_filled_chars_except_sp(line) - beg_regarding_rtl;
551 
552       if (chars && num_chars >= region_size + size) {
553         vt_line_copy_logical_str(line, chars + region_size, beg_regarding_rtl, size);
554       }
555 
556       region_size += size;
557 
558       if (!vt_line_is_continued_to_next(line)) {
559         if (chars && num_chars > region_size) {
560           vt_char_copy(chars + region_size, vt_nl_ch());
561         }
562         region_size++;
563       }
564     }
565 
566     if (vt_line_is_empty((line = vt_screen_get_line(screen, row)))) {
567       return region_size;
568     }
569 
570     size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
571     beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
572 
573     if (vt_line_is_rtl(line)) {
574       beg_char_index = BL_MAX(size_except_sp, 1) - 1;
575     } else {
576       beg_char_index = beg_regarding_rtl;
577     }
578   }
579 
580   /* row == end_row */
581 
582   if (size_except_sp == 0) {
583     /* do nothing */
584   } else if (vt_line_is_rtl(line)) {
585     if (end_char_index < size_except_sp) {
586       if (end_char_index < beg_regarding_rtl) {
587         end_char_index = beg_regarding_rtl;
588       }
589 
590       if (beg_row == end_row && beg_char_index < end_char_index) {
591         int tmp = end_char_index;
592         end_char_index = beg_char_index;
593         beg_char_index = tmp;
594       }
595 
596       size = beg_char_index - end_char_index + 1;
597 
598       if (chars && num_chars >= region_size + size) {
599         vt_line_copy_logical_str(line, chars + region_size, end_char_index, size);
600       }
601 
602       region_size += size;
603     }
604   } else {
605     if (end_char_index >= beg_regarding_rtl) {
606       if (end_char_index >= size_except_sp) {
607         end_char_index = size_except_sp - 1;
608       }
609 
610       size = end_char_index - beg_char_index + 1;
611 
612       if (chars && num_chars >= region_size + size) {
613         vt_line_copy_logical_str(line, chars + region_size, beg_char_index, size);
614       }
615 
616       region_size += size;
617     }
618   }
619 
620   return region_size;
621 }
622 
get_msec_time(void)623 static u_int32_t get_msec_time(void) {
624 #ifdef HAVE_GETTIMEOFDAY
625   struct timeval tv;
626 
627   gettimeofday(&tv, NULL);
628 
629   /* XXX '/ 1000' => '>> 10' and '* 1000' => '<< 10' */
630   return (tv.tv_sec << 10) + (tv.tv_usec >> 10);
631 #else
632   return time(NULL) << 10;
633 #endif
634 }
635 
change_edit(vt_screen_t * screen,vt_edit_t * edit)636 static void change_edit(vt_screen_t *screen, vt_edit_t *edit) {
637   vt_screen_disable_local_echo(screen);
638 
639   if (edit != screen->status_edit) {
640     if (screen->logvis) {
641       (*screen->logvis->init)(screen->logvis, &edit->model, &edit->cursor);
642     }
643 
644     if (screen->main_edit) {
645       screen->main_edit = edit;
646     }
647 
648     if (screen->edit != screen->status_edit) {
649       vt_edit_set_modified_all(edit);
650     }
651   } else {
652     screen->main_edit = screen->edit;
653   }
654 
655   edit->bce_ch = screen->edit->bce_ch;
656 
657   screen->edit = edit;
658 }
659 
get_edit(vt_screen_t * screen,u_int page_id)660 static vt_edit_t *get_edit(vt_screen_t *screen, u_int page_id) {
661   if (page_id == 0) {
662     if (vt_screen_is_alternative_edit(screen)) {
663       return &screen->alt_edit;
664     } else {
665       return &screen->normal_edit;
666     }
667   } else if (page_id <= MAX_PAGE_ID) {
668     if (screen->page_edits == NULL) {
669       int count;
670 
671       if (!(screen->page_edits = malloc(sizeof(vt_edit_t) * MAX_PAGE_ID))) {
672         return NULL;
673       }
674 
675       for (count = 0; count < MAX_PAGE_ID; count++) {
676         vt_edit_init(screen->page_edits + count, &screen->edit_scroll_listener,
677                      vt_edit_get_cols(&screen->normal_edit),
678                      vt_edit_get_rows(&screen->normal_edit),
679                      vt_edit_get_tab_size(&screen->normal_edit), 1,
680                      screen->normal_edit.use_bce);
681       }
682     }
683 
684     return screen->page_edits + (page_id - 1);
685   }
686 
687   return NULL;
688 }
689 
status_edit_new(vt_edit_t * main_edit)690 static vt_edit_t *status_edit_new(vt_edit_t *main_edit) {
691   vt_edit_t *status_edit;
692 
693   if ((status_edit = malloc(sizeof(vt_edit_t)))) {
694     vt_edit_init(status_edit, main_edit->scroll_listener,
695                  vt_edit_get_cols(main_edit), 1, vt_edit_get_tab_size(main_edit), 1,
696                  main_edit->use_bce);
697   }
698 
699   return status_edit;
700 }
701 
get_wrap_lines_in_logs(vt_logs_t * logs,u_int * cols,u_int * rows,int beg)702 static u_int get_wrap_lines_in_logs(vt_logs_t *logs, u_int *cols, u_int *rows, int beg) {
703   vt_line_t *line;
704   u_int num_chars = 0;
705   int row;
706   u_int num;
707 
708   for (row = beg; row > 0 && vt_line_is_continued_to_next(vt_log_get(logs, row - 1)); row--);
709 
710   *rows = beg - row + 1;
711   *cols = 0;
712 
713   for(; row < beg; row++) {
714     line = vt_log_get(logs, row);
715     vt_line_ctl_logical(line); /* logged lines are always visualized. */
716     *cols += vt_str_cols(line->chars, line->num_filled_chars);
717     num_chars += line->num_filled_chars;
718     vt_line_ctl_visual(line);
719   }
720 
721   line = vt_log_get(logs, row);
722   vt_line_ctl_logical(line); /* logged lines are always visualized. */
723   if (!vt_line_is_continued_to_next(line)) {
724     num = vt_line_get_num_filled_chars_except_sp(line);
725   } else {
726     num = line->num_filled_chars;
727   }
728   *cols += vt_str_cols(line->chars, num);
729   num_chars += num;
730   vt_line_ctl_visual(line);
731 
732   return num_chars;
733 }
734 
get_wrap_lines_in_edit(vt_edit_t * edit,vt_char_t * chars,u_int * cols,u_int * rows,int beg)735 static u_int get_wrap_lines_in_edit(vt_edit_t *edit, vt_char_t *chars,
736                                     u_int *cols, u_int *rows, int beg) {
737   u_int num_chars = 0;
738   vt_line_t *line;
739   int row = beg;
740 
741   if (cols) {
742     *cols = 0;
743   }
744 
745   while ((line = vt_edit_get_line(edit, row++))) {
746     if (!vt_line_is_continued_to_next(line)) {
747       u_int num = vt_line_get_num_filled_chars_except_sp(line);
748 
749       if (chars) {
750         vt_str_copy(chars + num_chars, line->chars, num);
751       } else {
752         *cols += vt_str_cols(line->chars, num);
753       }
754       num_chars += num;
755 
756       break;
757     } else {
758       if (chars) {
759         vt_str_copy(chars + num_chars, line->chars, line->num_filled_chars);
760       } else {
761         *cols += vt_str_cols(line->chars, line->num_filled_chars);
762       }
763       num_chars += line->num_filled_chars;
764     }
765   }
766 
767   if (rows) {
768     *rows = row - beg;
769   }
770 
771   return num_chars;
772 }
773 
774 /*
775  * Don't call this if vt_edit_is_logging(screen->edit) is false.
776  * comb_logical() is not called for lines reverted to the main screen, so
777  * dynamic combining is corrupt.
778  */
rollback(vt_screen_t * screen,u_int dst_cols,u_int dst_rows)779 static void rollback(vt_screen_t *screen, u_int dst_cols, u_int dst_rows) {
780   int src_row;
781   int src_end_row; /* end row in backlog */
782   u_int dst_row;
783   u_int n_scrollback;
784   vt_line_t *line;
785   vt_char_t *chars;
786   vt_char_t *chars_in_edit;
787   u_int src_cols_in_edit;
788   u_int src_nchars_in_edit;
789   u_int src_rows_in_edit;
790   u_int src_nchars;
791   u_int max_nchars;
792   u_int src_cols;
793   u_int count;
794   int cursor_col = vt_cursor_col(screen->edit);
795   int cursor_row = vt_cursor_row(screen->edit);
796 
797   if ((src_end_row = vt_get_num_logged_lines(&screen->logs)) == 0) {
798     return;
799   }
800   src_row = (--src_end_row);
801 
802   if (vt_line_is_continued_to_next(vt_log_get(&screen->logs, src_row))) {
803     src_nchars_in_edit = get_wrap_lines_in_edit(screen->edit, NULL,
804                                                 &src_cols_in_edit, &src_rows_in_edit, 0);
805     src_cols = src_cols_in_edit;
806     dst_rows += src_rows_in_edit;
807 
808     if ((chars_in_edit = vt_str_alloca(src_nchars_in_edit)) == NULL) {
809       return;
810     }
811     vt_str_init(chars_in_edit, src_nchars_in_edit);
812 
813     get_wrap_lines_in_edit(screen->edit, chars_in_edit, NULL, NULL, 0);
814   } else {
815     src_nchars_in_edit = src_cols = src_cols_in_edit = src_rows_in_edit = 0;
816   }
817   max_nchars = src_nchars = 0;
818 
819   count = 0;
820   while (1) {
821     u_int n;
822     u_int r;
823     u_int c;
824     u_int tmp;
825 
826     n = get_wrap_lines_in_logs(&screen->logs, &c, &r, src_row);
827 
828     if (src_cols + c == 0) {
829       tmp = 1;
830     } else {
831       tmp = (src_cols + c - 1) / dst_cols + 1;
832     }
833     if (tmp > dst_rows - count) {
834       break;
835     }
836 
837     if (count == 0) {
838       n += src_nchars_in_edit;
839 
840       if (cursor_row < src_rows_in_edit) {
841         /*
842          * XXX
843          * This calculation is incorrect if a character at the end of line
844          * is full width.
845          */
846         cursor_col += (c + cursor_row * dst_cols);
847         cursor_row = cursor_col / dst_cols;
848         cursor_col -= (cursor_row * dst_cols);
849         /*
850          * The number of rows reverted to the screen is added to cursor_row by
851          * 'cursor_row += (dst_rows - src_rows_in_edit)' below.
852          */
853         cursor_row -= (cursor_row + 1 - src_rows_in_edit);
854       }
855     }
856 
857     if (n > max_nchars) {
858       max_nchars = n;
859     }
860 
861     count += tmp;
862     src_cols = 0;
863     src_nchars += n;
864     if ((src_row -= r) < 0) {
865       break;
866     }
867   }
868 
869   if ((dst_rows = count) == 0) {
870     return;
871   }
872 
873   n_scrollback = src_end_row - src_row;
874   src_row++;
875 
876   if ((chars = vt_str_alloca(max_nchars)) == NULL) {
877     return;
878   }
879   vt_str_init(chars, max_nchars);
880 
881   vt_edit_scroll_downward(screen->edit, dst_rows - src_rows_in_edit);
882   cursor_row += (dst_rows - src_rows_in_edit);
883 
884   dst_row = 0;
885   while (src_row <= src_end_row) {
886     u_int n;
887 
888     line = vt_log_get(&screen->logs, src_row++);
889     src_cols = 0;
890     src_nchars = 0;
891 
892     /* vt_line_ctl_visual() is not called because vt_log_rollback_index() clears it. */
893     vt_line_ctl_logical(line);
894     while (1) {
895       if (!vt_line_is_continued_to_next(line)) {
896         n = vt_line_get_num_filled_chars_except_sp(line);
897         vt_str_copy(chars + src_nchars, line->chars, n);
898         src_nchars += n;
899         src_cols += vt_str_cols(line->chars, n);
900 
901         break;
902       } else {
903         vt_str_copy(chars + src_nchars, line->chars, line->num_filled_chars);
904         src_nchars += line->num_filled_chars;
905         src_cols += vt_str_cols(line->chars, line->num_filled_chars);
906 
907         if (src_row > src_end_row) {
908           vt_str_copy(chars + src_nchars, chars_in_edit, src_nchars_in_edit);
909           src_nchars += src_nchars_in_edit;
910           src_cols += src_cols_in_edit;
911 
912           break;
913         } else {
914           line = vt_log_get(&screen->logs, src_row++);
915           /* vt_line_ctl_visual() is not called because vt_log_rollback_index() clears it. */
916           vt_line_ctl_logical(line);
917         }
918       }
919     }
920 
921     if (src_nchars > 0) {
922       dst_row += vt_edit_replace(screen->edit, dst_row, chars, src_cols, dst_cols);
923     } else {
924       dst_row++;
925     }
926   }
927 
928   vt_edit_goto(screen->edit, cursor_col, cursor_row);
929 
930   vt_str_final(chars, max_nchars);
931   if (src_nchars_in_edit > 0) {
932     vt_str_final(chars_in_edit, src_nchars_in_edit);
933   }
934 
935   vt_log_rollback_index(&screen->logs, n_scrollback);
936 }
937 
resize(vt_screen_t * screen,u_int cols,u_int rows,int pack)938 static int resize(vt_screen_t *screen, u_int cols /* > 0 */, u_int rows /* > 0 */, int pack) {
939   /* This considers status line ('rows' contains status line) */
940   u_int old_empty_rows;
941   u_int empty_rows;
942 
943   old_empty_rows = vt_edit_get_rows(screen->edit) - vt_edit_get_num_filled_rows(screen->edit);
944 
945   if (screen->status_edit) {
946     vt_edit_resize(screen->status_edit, cols, 1);
947 
948     if (vt_screen_has_status_line(screen) && rows >= 2) {
949       rows--;
950     }
951   }
952 
953   switch(vt_edit_resize(&screen->normal_edit, cols, rows)) {
954   case 2: /* RZ_WRAP */
955     screen->need_rewrap_logs = 1;
956     break;
957 
958   case 3: /* RZ_SCROLL */
959     pack = 0;
960     break;
961   }
962 
963 #ifdef DEBUG
964   {
965     vt_line_t *line = vt_edit_get_line(&screen->normal_edit,
966                                        vt_cursor_row(&screen->normal_edit));
967 
968     if (line->num_filled_chars <= screen->normal_edit.cursor.char_index) {
969       bl_debug_printf("Cursor posion (%d %d) is over the end of line (%d)\n",
970                       screen->normal_edit.cursor.char_index,
971                       screen->normal_edit.cursor.row,
972                       line->num_filled_chars);
973       abort();
974     }
975   }
976 #endif
977 
978   vt_edit_resize(&screen->alt_edit, cols, rows);
979 
980   if (screen->stored_edit) {
981     vt_edit_resize(&screen->stored_edit->edit, cols, rows);
982   }
983 
984   if (screen->page_edits) {
985     u_int count;
986 
987     for (count = 0; count < MAX_PAGE_ID; count++) {
988       vt_edit_resize(screen->page_edits + count, cols, rows);
989     }
990   }
991 
992   empty_rows = vt_edit_get_rows(screen->edit) - vt_edit_get_num_filled_rows(screen->edit);
993 
994   if (pack && vt_edit_is_logging(screen->edit) && empty_rows > old_empty_rows) {
995     u_int n_scroll = vt_get_num_logged_lines(&screen->logs);
996 
997     if (n_scroll > 0) {
998       if (n_scroll > empty_rows - old_empty_rows) {
999         n_scroll = empty_rows - old_empty_rows;
1000       }
1001 
1002 #if 0
1003       vt_edit_scroll_downward(screen->edit, n_scroll);
1004 
1005       for (count = 0; count < n_scroll; count++) {
1006         vt_line_t *src = vt_log_get(&screen->logs,
1007                                     vt_get_num_logged_lines(&screen->logs) - n_scroll + count);
1008         vt_line_t *dst = vt_edit_get_line(screen->edit, count);
1009         /* vt_screen_resize() is always called in logical context. */
1010         vt_line_ctl_logical(src);
1011         vt_line_copy(dst, src);
1012         vt_line_set_modified_all(dst);
1013 
1014         vt_edit_go_downward(screen->edit, 0);
1015       }
1016 
1017       vt_log_rollback_index(&screen->logs, n_scroll);
1018 #else
1019       rollback(screen, cols, n_scroll);
1020 #endif
1021 
1022       return 2;
1023     }
1024   }
1025 
1026   return 1;
1027 }
1028 
rewrap_logs(vt_screen_t * screen)1029 static int rewrap_logs(vt_screen_t *screen) {
1030   vt_logs_t new_logs;
1031   u_int src_row;
1032   vt_char_t *buf;
1033   u_int max_nchars = 0;
1034   u_int num_cols;
1035   u_int num_log_lines;
1036   vt_line_t new_line;
1037   int last_line_is_wrapped;
1038 
1039   if ((num_log_lines = vt_get_num_logged_lines(&screen->logs)) == 0) {
1040     return 1;
1041   }
1042 
1043   vt_log_init(&new_logs, vt_get_log_size(&screen->logs));
1044   if (vt_log_size_is_unlimited(&screen->logs)) {
1045     vt_unlimit_log_size(&new_logs);
1046   }
1047 
1048   num_cols = vt_edit_get_cols(screen->edit);
1049   last_line_is_wrapped = vt_line_is_continued_to_next(vt_log_get(&screen->logs, num_log_lines - 1));
1050 
1051   for (src_row = 0; src_row < num_log_lines; src_row++) {
1052     vt_line_t *line = vt_log_get(&screen->logs, src_row);
1053     vt_char_t *wrap_chars;
1054     u_int wrap_ncols;
1055     u_int len;
1056     u_int cols;
1057 
1058     vt_line_ctl_logical(line);
1059 
1060     if (!vt_line_is_continued_to_next(line)) {
1061       wrap_ncols = vt_str_cols(line->chars, vt_line_get_num_filled_chars_except_sp(line));
1062       if (wrap_ncols <= num_cols) {
1063         vt_line_ctl_visual(line);
1064         vt_log_add(&new_logs, line);
1065 
1066         continue;
1067       }
1068 
1069       wrap_chars = line->chars;
1070     } else {
1071       u_int count;
1072       u_int count2;
1073       u_int nchars = line->num_filled_chars;
1074 
1075       wrap_ncols = vt_str_cols(line->chars, nchars);
1076 
1077       for (count = 1; src_row + count < num_log_lines;) {
1078         line = vt_log_get(&screen->logs, src_row + (count++));
1079         vt_line_ctl_logical(line);
1080         if (!vt_line_is_continued_to_next(line)) {
1081           u_int n = vt_line_get_num_filled_chars_except_sp(line);
1082           nchars += n;
1083           wrap_ncols += vt_str_cols(line->chars, n);
1084           break;
1085         } else {
1086           nchars += line->num_filled_chars;
1087           wrap_ncols += vt_str_cols(line->chars, line->num_filled_chars);
1088         }
1089       }
1090 
1091       if (nchars > max_nchars) {
1092         if (max_nchars > 0) {
1093           vt_str_final(buf, max_nchars);
1094         }
1095 
1096         max_nchars = nchars;
1097 
1098         if ((buf = vt_str_alloca(max_nchars)) == NULL) {
1099           return 0;
1100         }
1101         vt_str_init(buf, max_nchars);
1102       }
1103 
1104       for (count2 = 0, len = 0; ; count2++) {
1105         /* vt_line_ctl_logical() was applied above. */
1106         line = vt_log_get(&screen->logs, src_row + count2);
1107 
1108         if (count2 == count - 1) {
1109           u_int n = vt_line_get_num_filled_chars_except_sp(line);
1110           vt_str_copy(buf + len, line->chars, n);
1111           len += n;
1112           break;
1113         } else {
1114           vt_str_copy(buf + len, line->chars, line->num_filled_chars);
1115           len += line->num_filled_chars;
1116         }
1117       }
1118 
1119       wrap_chars = buf;
1120 
1121       src_row += (count - 1);
1122     }
1123 
1124     vt_line_init(&new_line, num_cols);
1125     cols = 0;
1126     len = 0;
1127 
1128     while (1) {
1129       u_int c;
1130       u_int n;
1131 
1132       if (cols + num_cols > wrap_ncols) {
1133         c = wrap_ncols - cols;
1134       } else {
1135         c = num_cols;
1136       }
1137       n = vt_str_cols_to_len(wrap_chars + len, &c);
1138 
1139       vt_line_overwrite(&new_line, 0, wrap_chars + len, n, c);
1140       if (screen->logvis) {
1141         (*screen->logvis->visual_line)(screen->logvis, &new_line);
1142       }
1143 
1144       len += n;
1145       cols += c;
1146 
1147       if (cols < wrap_ncols) {
1148         vt_line_set_continued_to_next(&new_line, 1);
1149         vt_log_add(&new_logs, &new_line);
1150         vt_line_reset(&new_line);
1151       } else {
1152         vt_log_add(&new_logs, &new_line);
1153         vt_line_final(&new_line);
1154         break;
1155       }
1156     }
1157   }
1158 
1159   vt_str_final(buf, max_nchars);
1160 
1161   if (last_line_is_wrapped) {
1162     vt_line_set_continued_to_next(vt_log_get(&new_logs, vt_get_num_logged_lines(&new_logs) - 1), 1);
1163   }
1164 
1165   vt_log_final(&screen->logs);
1166   screen->logs = new_logs;
1167 
1168   if (num_log_lines != vt_get_num_logged_lines(&screen->logs)) {
1169     return 2;
1170   }
1171 
1172   return 1;
1173 }
1174 
1175 /* --- global functions --- */
1176 
vt_set_word_separators(const char * seps)1177 void vt_set_word_separators(const char *seps) {
1178   static char *default_word_separators;
1179 
1180   if (default_word_separators) {
1181     if (word_separators != default_word_separators) {
1182       free(word_separators);
1183     }
1184 
1185     if (seps == NULL || *seps == '\0') {
1186       /* Fall back to default. */
1187       word_separators = default_word_separators;
1188 
1189       return;
1190     }
1191   } else if (seps == NULL || *seps == '\0') {
1192     /* Not changed */
1193     return;
1194   } else {
1195     /* Store the default value. */
1196     default_word_separators = word_separators;
1197   }
1198 
1199   word_separators = bl_str_unescape(seps);
1200 }
1201 
vt_get_word_separators(void)1202 char *vt_get_word_separators(void) { return word_separators; }
1203 
vt_set_regard_uri_as_word(int flag)1204 void vt_set_regard_uri_as_word(int flag) { regard_uri_as_word = flag; }
1205 
vt_get_regard_uri_as_word(void)1206 int vt_get_regard_uri_as_word(void) { return regard_uri_as_word; }
1207 
vt_screen_new(u_int cols,u_int rows,u_int tab_size,u_int num_log_lines,int use_bce,vt_bs_mode_t bs_mode)1208 vt_screen_t *vt_screen_new(u_int cols, u_int rows, u_int tab_size, u_int num_log_lines,
1209                            int use_bce, vt_bs_mode_t bs_mode) {
1210   vt_screen_t *screen;
1211 
1212   if ((screen = calloc(1, sizeof(vt_screen_t))) == NULL) {
1213 #ifdef DEBUG
1214     bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
1215 #endif
1216 
1217     return NULL;
1218   }
1219 
1220   screen->edit_scroll_listener.self = screen;
1221   screen->edit_scroll_listener.receive_scrolled_out_line = receive_scrolled_out_line;
1222   screen->edit_scroll_listener.scrolled_out_lines_finished = scrolled_out_lines_finished;
1223   screen->edit_scroll_listener.window_scroll_upward_region = window_scroll_upward_region;
1224   screen->edit_scroll_listener.window_scroll_downward_region = window_scroll_downward_region;
1225   screen->edit_scroll_listener.top_line_is_wrapped = top_line_is_wrapped;
1226 
1227   if (!vt_edit_init(&screen->normal_edit, &screen->edit_scroll_listener, cols, rows, tab_size, 1,
1228                     use_bce)) {
1229 #ifdef DEBUG
1230     bl_warn_printf(BL_DEBUG_TAG " vt_edit_init(normal_edit) failed.\n");
1231 #endif
1232 
1233     goto error1;
1234   }
1235 
1236   if (!vt_edit_init(&screen->alt_edit, &screen->edit_scroll_listener, cols, rows, tab_size, 0,
1237                     use_bce)) {
1238 #ifdef DEBUG
1239     bl_warn_printf(BL_DEBUG_TAG " vt_edit_init(alt_edit) failed.\n");
1240 #endif
1241 
1242     goto error2;
1243   }
1244 
1245   screen->edit = &screen->normal_edit;
1246 
1247   if (!vt_log_init(&screen->logs, num_log_lines)) {
1248 #ifdef DEBUG
1249     bl_warn_printf(BL_DEBUG_TAG " vt_log_init failed.\n");
1250 #endif
1251 
1252     goto error3;
1253   }
1254 
1255   screen->backscroll_mode = bs_mode;
1256 
1257   return screen;
1258 
1259 error3:
1260   vt_edit_final(&screen->normal_edit);
1261 error2:
1262   vt_edit_final(&screen->alt_edit);
1263 error1:
1264   free(screen);
1265 
1266   return NULL;
1267 }
1268 
vt_screen_destroy(vt_screen_t * screen)1269 int vt_screen_destroy(vt_screen_t *screen) {
1270   /*
1271    * this should be done before vt_edit_final() since termscr->logvis refers
1272    * to vt_edit_t and may have some data structure for it.
1273    */
1274   if (screen->logvis) {
1275     (*screen->logvis->logical)(screen->logvis);
1276     (*screen->logvis->destroy)(screen->logvis);
1277   }
1278 
1279   vt_edit_final(&screen->normal_edit);
1280   vt_edit_final(&screen->alt_edit);
1281 
1282   if (screen->page_edits) {
1283     int count;
1284 
1285     for (count = 0; count < MAX_PAGE_ID; count++) {
1286       vt_edit_final(screen->page_edits + count);
1287     }
1288 
1289     free(screen->page_edits);
1290   }
1291 
1292   if (screen->status_edit) {
1293     vt_edit_final(screen->status_edit);
1294     free(screen->status_edit);
1295   }
1296 
1297   vt_log_final(&screen->logs);
1298 
1299   vt_screen_search_final(screen);
1300 
1301   free(screen);
1302 
1303   return 1;
1304 }
1305 
vt_screen_set_listener(vt_screen_t * screen,vt_screen_event_listener_t * screen_listener)1306 void vt_screen_set_listener(vt_screen_t *screen, vt_screen_event_listener_t *screen_listener) {
1307   screen->screen_listener = screen_listener;
1308 }
1309 
vt_screen_resize(vt_screen_t * screen,u_int cols,u_int rows)1310 int vt_screen_resize(vt_screen_t *screen, u_int cols, u_int rows) {
1311   if (cols == 0) {
1312     cols = 1;
1313   }
1314   if (rows == 0) {
1315     rows = 1;
1316   }
1317 
1318   return resize(screen, cols, rows, 1);
1319 }
1320 
1321 /* This considers status line */
vt_screen_cursor_row(vt_screen_t * screen)1322 int vt_screen_cursor_row(vt_screen_t *screen) {
1323   int row = vt_cursor_row(screen->edit);
1324 
1325   if (vt_status_line_is_focused(screen)) {
1326     row += vt_edit_get_rows(screen->main_edit);
1327   }
1328 
1329   return row;
1330 }
1331 
1332 /* This considers status line */
vt_screen_cursor_row_in_screen(vt_screen_t * screen)1333 int vt_screen_cursor_row_in_screen(vt_screen_t *screen) {
1334   int row = vt_screen_cursor_row(screen);
1335 
1336   if (screen->backscroll_rows > 0) {
1337     if ((row += screen->backscroll_rows) >= vt_screen_get_rows(screen)) {
1338       return -1;
1339     }
1340   }
1341 
1342   return row;
1343 }
1344 
vt_screen_get_logical_cols(vt_screen_t * screen)1345 u_int vt_screen_get_logical_cols(vt_screen_t *screen) {
1346   if (screen->logvis && screen->logvis->is_visual) {
1347     return (*screen->logvis->logical_cols)(screen->logvis);
1348   } else {
1349     return vt_edit_get_cols(screen->edit);
1350   }
1351 }
1352 
1353 /* This considers status line */
vt_screen_get_rows(vt_screen_t * screen)1354 u_int vt_screen_get_rows(vt_screen_t *screen) {
1355   u_int rows;
1356 
1357   rows = vt_edit_get_rows(screen->edit);
1358 
1359   if (vt_screen_has_status_line(screen)) {
1360     if (vt_status_line_is_focused(screen)) {
1361       rows += vt_edit_get_rows(screen->main_edit);
1362     } else {
1363       rows ++;
1364     }
1365   }
1366 
1367   return rows;
1368 }
1369 
1370 /* This ignores status line */
vt_screen_get_logical_rows(vt_screen_t * screen)1371 u_int vt_screen_get_logical_rows(vt_screen_t *screen) {
1372   if (screen->logvis && screen->logvis->is_visual) {
1373     return (*screen->logvis->logical_rows)(screen->logvis);
1374   } else {
1375     return vt_edit_get_rows(screen->edit);
1376   }
1377 }
1378 
vt_screen_convert_scr_row_to_abs(vt_screen_t * screen,int row)1379 int vt_screen_convert_scr_row_to_abs(vt_screen_t *screen, int row) {
1380   return row - screen->backscroll_rows;
1381 }
1382 
1383 /*
1384  * If 0 <= row && row <= vt_screen_get_rows(), this function always returns
1385  * non-NULL value.
1386  * This considers status line.
1387  */
vt_screen_get_line(vt_screen_t * screen,int row)1388 vt_line_t *vt_screen_get_line(vt_screen_t *screen, int row) {
1389   if (row < 0) {
1390     return vt_log_get(&screen->logs, ROW_IN_LOGS(screen, row));
1391   } else if (vt_screen_has_status_line(screen)) {
1392     if (row == vt_edit_get_rows(screen->main_edit)) {
1393       return vt_edit_get_line(screen->status_edit, 0);
1394     } else {
1395       return vt_edit_get_line(screen->main_edit, row);
1396     }
1397   } else {
1398     return vt_edit_get_line(screen->edit, row);
1399   }
1400 }
1401 
1402 /*
1403  * If 0 <= row && row <= vt_screen_get_rows(), this function always returns
1404  * non-NULL value.
1405  * This considers status line.
1406  */
vt_screen_get_line_in_screen(vt_screen_t * screen,int row)1407 vt_line_t *vt_screen_get_line_in_screen(vt_screen_t *screen, int row) {
1408   if (screen->is_backscrolling && screen->backscroll_rows > 0) {
1409     row -= screen->backscroll_rows;
1410 
1411     if (row < 0) {
1412       return vt_log_get(&screen->logs, ROW_IN_LOGS(screen, row));
1413     }
1414   }
1415 
1416   if (vt_screen_has_status_line(screen)) {
1417     if (row == vt_edit_get_rows(screen->main_edit)) {
1418       return vt_edit_get_line(screen->status_edit, 0);
1419     } else {
1420       return vt_edit_get_line(screen->main_edit, row);
1421     }
1422   } else {
1423     return vt_edit_get_line(screen->edit, row);
1424   }
1425 }
1426 
vt_screen_set_modified_all(vt_screen_t * screen)1427 void vt_screen_set_modified_all(vt_screen_t *screen) {
1428   int row;
1429   vt_line_t *line;
1430   u_int num_rows = vt_screen_get_rows(screen);
1431 
1432   for (row = 0; row < num_rows; row++) {
1433     line = vt_screen_get_line_in_screen(screen, row); /* Always non-NULL */
1434     vt_line_set_modified_all(line);
1435   }
1436 }
1437 
vt_screen_add_logical_visual(vt_screen_t * screen,vt_logical_visual_t * logvis)1438 int vt_screen_add_logical_visual(vt_screen_t *screen, vt_logical_visual_t *logvis) {
1439   (*logvis->init)(logvis, &screen->edit->model, &screen->edit->cursor);
1440 
1441   if (screen->logvis) {
1442     if (screen->container_logvis == NULL &&
1443         (screen->container_logvis = vt_logvis_container_new()) == NULL) {
1444       return 0;
1445     }
1446 
1447     vt_logvis_container_add(screen->container_logvis, screen->logvis);
1448     vt_logvis_container_add(screen->container_logvis, logvis);
1449 
1450     screen->logvis = screen->container_logvis;
1451   } else {
1452     screen->logvis = logvis;
1453   }
1454 
1455   return 1;
1456 }
1457 
vt_screen_destroy_logical_visual(vt_screen_t * screen)1458 int vt_screen_destroy_logical_visual(vt_screen_t *screen) {
1459   if (screen->logvis) {
1460     (*screen->logvis->logical)(screen->logvis);
1461     (*screen->logvis->destroy)(screen->logvis);
1462     screen->logvis = NULL;
1463     screen->container_logvis = NULL;
1464 
1465     return 1;
1466   } else {
1467     return 0;
1468   }
1469 }
1470 
vt_screen_render(vt_screen_t * screen)1471 void vt_screen_render(vt_screen_t *screen) {
1472   if (screen->logvis) {
1473     (*screen->logvis->render)(screen->logvis);
1474   }
1475 }
1476 
vt_screen_visual(vt_screen_t * screen)1477 int vt_screen_visual(vt_screen_t *screen) {
1478   if (screen->logvis) {
1479     return (*screen->logvis->visual)(screen->logvis);
1480   } else {
1481     return 1;
1482   }
1483 }
1484 
vt_screen_logical(vt_screen_t * screen)1485 int vt_screen_logical(vt_screen_t *screen) {
1486   if (screen->logvis) {
1487     return (*screen->logvis->logical)(screen->logvis);
1488   } else {
1489     return 1;
1490   }
1491 }
1492 
vt_screen_is_backscrolling(vt_screen_t * screen)1493 vt_bs_mode_t vt_screen_is_backscrolling(vt_screen_t *screen) {
1494   if (screen->is_backscrolling == BSM_STATIC) {
1495     if (screen->backscroll_rows < vt_get_log_size(&screen->logs)) {
1496       return BSM_STATIC;
1497     } else {
1498       return BSM_DEFAULT;
1499     }
1500   } else {
1501     return screen->is_backscrolling;
1502   }
1503 }
1504 
vt_set_backscroll_mode(vt_screen_t * screen,vt_bs_mode_t mode)1505 void vt_set_backscroll_mode(vt_screen_t *screen, vt_bs_mode_t mode) {
1506   screen->backscroll_mode = mode;
1507 
1508   if (screen->is_backscrolling) {
1509     screen->is_backscrolling = mode;
1510   }
1511 }
1512 
vt_enter_backscroll_mode(vt_screen_t * screen)1513 int vt_enter_backscroll_mode(vt_screen_t *screen) {
1514   int ret;
1515 
1516   screen->is_backscrolling = screen->backscroll_mode;
1517 
1518   if (screen->need_rewrap_logs) {
1519     ret = rewrap_logs(screen);
1520     screen->need_rewrap_logs = 0;
1521   } else {
1522     ret = 1;
1523   }
1524 
1525   return ret;
1526 }
1527 
vt_exit_backscroll_mode(vt_screen_t * screen)1528 void vt_exit_backscroll_mode(vt_screen_t *screen) {
1529   screen->is_backscrolling = 0;
1530   screen->backscroll_rows = 0;
1531 
1532   vt_screen_set_modified_all(screen);
1533 }
1534 
vt_screen_backscroll_to(vt_screen_t * screen,int row)1535 int vt_screen_backscroll_to(vt_screen_t *screen, int row) {
1536   if (!screen->is_backscrolling) {
1537     return 0;
1538   }
1539 
1540   if (row > 0) {
1541     screen->backscroll_rows = 0;
1542   } else {
1543     screen->backscroll_rows = abs(row);
1544   }
1545 
1546   vt_screen_set_modified_all(screen);
1547 
1548 #ifdef EXIT_BS_AT_BOTTOM
1549   if (screen->backscroll_rows == 0) {
1550     vt_exit_backscroll_mode(screen);
1551   }
1552 #endif
1553 
1554   return 1;
1555 }
1556 
vt_screen_backscroll_upward(vt_screen_t * screen,u_int size)1557 int vt_screen_backscroll_upward(vt_screen_t *screen, u_int size) {
1558   vt_line_t *line;
1559   u_int count;
1560   u_int num_rows;
1561 
1562   if (!screen->is_backscrolling) {
1563     return 0;
1564   }
1565 
1566   if (screen->backscroll_rows < size) {
1567     size = screen->backscroll_rows;
1568   }
1569 
1570   if (size == 0) {
1571     return 0;
1572   }
1573 
1574   screen->backscroll_rows -= size;
1575 
1576   num_rows = vt_screen_get_rows(screen);
1577 
1578   if (!screen->screen_listener || !screen->screen_listener->window_scroll_upward_region ||
1579       !(*screen->screen_listener->window_scroll_upward_region)(
1580           screen->screen_listener->self, 0, num_rows - 1, size)) {
1581     for (count = 0; count < num_rows - size; count++) {
1582       if ((line = vt_screen_get_line_in_screen(screen, count)) == NULL) {
1583         break;
1584       }
1585 
1586       vt_line_set_modified_all(line);
1587     }
1588   }
1589 
1590   for (count = num_rows - size; count < num_rows; count++) {
1591     if ((line = vt_screen_get_line_in_screen(screen, count)) == NULL) {
1592       break;
1593     }
1594 
1595     vt_line_set_modified_all(line);
1596   }
1597 
1598 #ifdef EXIT_BS_AT_BOTTOM
1599   if (screen->backscroll_rows == 0) {
1600     vt_exit_backscroll_mode(screen);
1601   }
1602 #endif
1603 
1604   return 1;
1605 }
1606 
vt_screen_backscroll_downward(vt_screen_t * screen,u_int size)1607 int vt_screen_backscroll_downward(vt_screen_t *screen, u_int size) {
1608   vt_line_t *line;
1609   u_int count;
1610   int num_rows;
1611 
1612   if (!screen->is_backscrolling) {
1613     return 0;
1614   }
1615 
1616   if (vt_get_num_logged_lines(&screen->logs) < screen->backscroll_rows + size) {
1617     size = vt_get_num_logged_lines(&screen->logs) - screen->backscroll_rows;
1618   }
1619 
1620   if (size == 0) {
1621     return 0;
1622   }
1623 
1624   screen->backscroll_rows += size;
1625 
1626   num_rows = vt_screen_get_rows(screen);
1627 
1628   if (!screen->screen_listener || !screen->screen_listener->window_scroll_downward_region ||
1629       !(*screen->screen_listener->window_scroll_downward_region)(
1630           screen->screen_listener->self, 0, num_rows - 1, size)) {
1631     for (count = size; count < num_rows; count++) {
1632       if ((line = vt_screen_get_line_in_screen(screen, count)) == NULL) {
1633         break;
1634       }
1635 
1636       vt_line_set_modified_all(line);
1637     }
1638   }
1639 
1640   for (count = 0; count < size; count++) {
1641     if ((line = vt_screen_get_line_in_screen(screen, count)) == NULL) {
1642       break;
1643     }
1644 
1645     vt_line_set_modified_all(line);
1646   }
1647 
1648   return 1;
1649 }
1650 
vt_screen_backscroll_upward_to_mark(vt_screen_t * screen,int * row)1651 u_int vt_screen_backscroll_upward_to_mark(vt_screen_t *screen, int *row) {
1652   vt_line_t *line;
1653   int count = 0;
1654 
1655   do {
1656     if ((line = vt_screen_get_line(screen, *row + (++count))) == NULL) {
1657       return 0;
1658     }
1659   } while (!line->mark);
1660 
1661   *row += count;
1662   if (screen->backscroll_rows == 0 && *row >= 0) {
1663     return 0;
1664   }
1665 
1666   count = *row + screen->backscroll_rows;
1667 
1668   if (vt_screen_backscroll_upward(screen, count)) {
1669     return count;
1670   } else {
1671     return 0;
1672   }
1673 }
1674 
vt_screen_backscroll_downward_to_mark(vt_screen_t * screen,int * row)1675 u_int vt_screen_backscroll_downward_to_mark(vt_screen_t *screen, int *row) {
1676   vt_line_t *line;
1677   int count = 0;
1678 
1679   do {
1680     if ((line = vt_screen_get_line(screen, *row - (++count))) == NULL) {
1681       return 0;
1682     }
1683   } while (!line->mark);
1684 
1685   *row -= count;
1686   if (screen->backscroll_rows == 0 && *row >= 0) {
1687     return 0;
1688   }
1689 
1690   count = -screen->backscroll_rows - *row;
1691 
1692   if (vt_screen_backscroll_downward(screen, count)) {
1693     return count;
1694   } else {
1695     return 0;
1696   }
1697 }
1698 
vt_screen_reverse_color(vt_screen_t * screen,int beg_char_index,int beg_row,int end_char_index,int end_row,int is_rect)1699 int vt_screen_reverse_color(vt_screen_t *screen, int beg_char_index, int beg_row,
1700                             int end_char_index, int end_row, int is_rect) {
1701   if (is_rect) {
1702     return reverse_or_restore_color_rect(screen, beg_char_index, beg_row, end_char_index, end_row,
1703                                          vt_line_reverse_color);
1704   } else {
1705     return reverse_or_restore_color(screen, beg_char_index, beg_row, end_char_index, end_row,
1706                                     vt_line_reverse_color);
1707   }
1708 }
1709 
vt_screen_restore_color(vt_screen_t * screen,int beg_char_index,int beg_row,int end_char_index,int end_row,int is_rect)1710 int vt_screen_restore_color(vt_screen_t *screen, int beg_char_index, int beg_row,
1711                             int end_char_index, int end_row, int is_rect) {
1712   if (is_rect) {
1713     return reverse_or_restore_color_rect(screen, beg_char_index, beg_row, end_char_index, end_row,
1714                                          vt_line_restore_color);
1715   } else {
1716     return reverse_or_restore_color(screen, beg_char_index, beg_row, end_char_index, end_row,
1717                                     vt_line_restore_color);
1718   }
1719 }
1720 
vt_screen_copy_region(vt_screen_t * screen,vt_char_t * chars,u_int num_chars,int beg_char_index,int beg_row,int end_char_index,int end_row,int is_rect)1721 u_int vt_screen_copy_region(vt_screen_t *screen, vt_char_t *chars, u_int num_chars,
1722                             int beg_char_index, int beg_row, int end_char_index, int end_row,
1723                             int is_rect) {
1724   if (is_rect) {
1725     return check_or_copy_region_rect(screen, chars, num_chars, beg_char_index, beg_row,
1726                                      end_char_index, end_row);
1727   } else {
1728     return check_or_copy_region(screen, chars, num_chars, beg_char_index, beg_row,
1729                                 end_char_index, end_row);
1730   }
1731 }
1732 
vt_screen_get_region_size(vt_screen_t * screen,int beg_char_index,int beg_row,int end_char_index,int end_row,int is_rect)1733 u_int vt_screen_get_region_size(vt_screen_t *screen, int beg_char_index, int beg_row,
1734                                 int end_char_index, int end_row, int is_rect) {
1735   if (is_rect) {
1736     return check_or_copy_region_rect(screen, NULL, 0, beg_char_index, beg_row, end_char_index,
1737                                      end_row);
1738   } else {
1739     return check_or_copy_region(screen, NULL, 0, beg_char_index, beg_row, end_char_index, end_row);
1740   }
1741 }
1742 
vt_screen_get_line_region(vt_screen_t * screen,int * beg_row,int * end_char_index,int * end_row,int base_row)1743 int vt_screen_get_line_region(vt_screen_t *screen, int *beg_row, int *end_char_index, int *end_row,
1744                               int base_row) {
1745   int row;
1746   vt_line_t *line;
1747   vt_line_t *next_line;
1748 
1749   /*
1750    * finding the end of line.
1751    */
1752   row = base_row;
1753 
1754   if ((line = vt_screen_get_line(screen, row)) == NULL || vt_line_is_empty(line)) {
1755     return 0;
1756   }
1757 
1758   while (1) {
1759     if (!vt_line_is_continued_to_next(line)) {
1760       break;
1761     }
1762 
1763     if ((next_line = vt_screen_get_line(screen, row + 1)) == NULL || vt_line_is_empty(next_line)) {
1764       break;
1765     }
1766 
1767     line = next_line;
1768     row++;
1769   }
1770 
1771   *end_char_index = line->num_filled_chars - 1;
1772   *end_row = row;
1773 
1774   /*
1775    * finding the beginning of line.
1776    */
1777   row = base_row;
1778 
1779   while (1) {
1780     if ((line = vt_screen_get_line(screen, row - 1)) == NULL || vt_line_is_empty(line) ||
1781         !vt_line_is_continued_to_next(line)) {
1782       break;
1783     }
1784 
1785     row--;
1786   }
1787 
1788   *beg_row = row;
1789 
1790   return 1;
1791 }
1792 
vt_screen_get_word_region(vt_screen_t * screen,int * beg_char_index,int * beg_row,int * end_char_index,int * end_row,int base_char_index,int base_row)1793 int vt_screen_get_word_region(vt_screen_t *screen, int *beg_char_index, int *beg_row,
1794                               int *end_char_index, int *end_row, int base_char_index,
1795                               int base_row) {
1796   int row;
1797   int char_index;
1798   vt_line_t *line;
1799   vt_line_t *base_line;
1800   vt_char_t *ch;
1801   int flag;
1802 
1803   if ((base_line = vt_screen_get_line(screen, base_row)) == NULL || vt_line_is_empty(base_line)) {
1804     return 0;
1805   }
1806 
1807   if (regard_uri_as_word) {
1808     char *orig;
1809     u_int len;
1810     vt_char_t *str;
1811 
1812     orig = word_separators;
1813     word_separators = "\" '`<>|";
1814     regard_uri_as_word = 0;
1815 
1816     /* Not return 0 */
1817     vt_screen_get_word_region(screen, beg_char_index, beg_row, end_char_index, end_row,
1818                               base_char_index, base_row);
1819 
1820     word_separators = orig;
1821     regard_uri_as_word = 1;
1822 
1823     if ((len = check_or_copy_region(screen, NULL, 0, *beg_char_index, *beg_row, *end_char_index,
1824                                     *end_row)) > 0 &&
1825         (str = vt_str_alloca(len))) {
1826       u_int count;
1827       int slash_num;
1828 
1829       vt_str_init(str, len);
1830       check_or_copy_region(screen, str, len, *beg_char_index, *beg_row, *end_char_index, *end_row);
1831 
1832       for (slash_num = 0, count = 0; count < len; count++) {
1833         if (vt_char_cs(str + count) == US_ASCII) {
1834           switch (vt_char_code(str + count)) {
1835             case '/':
1836               if (++slash_num == 1) {
1837                 continue;
1838               }
1839 
1840             /* Fall through */
1841 
1842             case '@':
1843               vt_str_final(str, len);
1844 
1845               return 1;
1846           }
1847         }
1848       }
1849 
1850       vt_str_final(str, len);
1851     }
1852   }
1853 
1854   if (is_word_separator(vt_char_at(base_line, base_char_index))) {
1855     *beg_char_index = base_char_index;
1856     *end_char_index = base_char_index;
1857     *beg_row = base_row;
1858     *end_row = base_row;
1859 
1860     return 1;
1861   }
1862 
1863   flag = vt_char_is_fullwidth(vt_char_at(base_line, base_char_index));
1864 
1865   /*
1866    * search the beg of word
1867    */
1868   row = base_row;
1869   char_index = base_char_index;
1870   line = base_line;
1871 
1872   while (1) {
1873     if (char_index == 0) {
1874       if ((line = vt_screen_get_line(screen, row - 1)) == NULL || vt_line_is_empty(line) ||
1875           !vt_line_is_continued_to_next(line)) {
1876         *beg_char_index = char_index;
1877 
1878         break;
1879       }
1880 
1881       row--;
1882       char_index = line->num_filled_chars - 1;
1883     } else {
1884       char_index--;
1885     }
1886 
1887     ch = vt_char_at(line, char_index);
1888 
1889     if (is_word_separator(ch) || flag != vt_char_is_fullwidth(ch)) {
1890       *beg_char_index = char_index + 1;
1891 
1892       break;
1893     }
1894   }
1895 
1896   *beg_row = row;
1897 
1898   /*
1899    * search the end of word.
1900    */
1901   row = base_row;
1902   char_index = base_char_index;
1903   line = base_line;
1904 
1905   while (1) {
1906     if (char_index == line->num_filled_chars - 1) {
1907       if (!vt_line_is_continued_to_next(line) ||
1908           (line = vt_screen_get_line(screen, row + 1)) == NULL || vt_line_is_empty(line)) {
1909         *end_char_index = char_index;
1910 
1911         break;
1912       }
1913 
1914       row++;
1915       char_index = 0;
1916     } else {
1917       char_index++;
1918     }
1919 
1920     ch = vt_char_at(line, char_index);
1921 
1922     if (is_word_separator(ch) || flag != vt_char_is_fullwidth(ch)) {
1923       *end_char_index = char_index - 1;
1924 
1925       break;
1926     }
1927   }
1928 
1929   *end_row = row;
1930 
1931 #ifdef __DEBUG
1932   bl_debug_printf(BL_DEBUG_TAG " selected word region: %d %d => %d %d %d %d\n", base_char_index,
1933                   base_row, *beg_char_index, *beg_row, *end_char_index, *end_row);
1934 #endif
1935 
1936   return 1;
1937 }
1938 
vt_screen_search_init(vt_screen_t * screen,int char_index,int row,int (* match)(size_t *,size_t *,void *,u_char *,int))1939 int vt_screen_search_init(vt_screen_t *screen,
1940                           int char_index, int row, /* -1: cursor position */
1941                           int (*match)(size_t *, size_t *, void *, u_char *, int)) {
1942   if (screen->search) {
1943     return 0;
1944   }
1945 
1946   if (!(screen->search = malloc(sizeof(*screen->search)))) {
1947     return 0;
1948   }
1949 
1950   screen->search->match = match;
1951 
1952   screen->search->char_index = char_index;
1953   screen->search->row = row;
1954 
1955   return 1;
1956 }
1957 
vt_screen_search_final(vt_screen_t * screen)1958 void vt_screen_search_final(vt_screen_t *screen) {
1959   free(screen->search);
1960   screen->search = NULL;
1961 }
1962 
vt_screen_search_reset_position(vt_screen_t * screen)1963 int vt_screen_search_reset_position(vt_screen_t *screen) {
1964   if (!screen->search) {
1965     return 0;
1966   }
1967 
1968   /* char_index == -1 has special meaning. */
1969   screen->search->char_index = -1;
1970   screen->search->row = -1;
1971 
1972   return 1;
1973 }
1974 
1975 /*
1976  * It is assumed that this function is called in *visual* context.
1977  *
1978  * XXX
1979  * It is not supported to match text in multiple lines.
1980  */
vt_screen_search_find(vt_screen_t * screen,int * beg_char_index,int * beg_row,int * end_char_index,int * end_row,void * regex,int backward)1981 int vt_screen_search_find(vt_screen_t *screen,
1982                           int *beg_char_index, /* visual position is returned */
1983                           int *beg_row,        /* visual position is returned */
1984                           int *end_char_index, /* visual position is returned */
1985                           int *end_row,        /* visual position is returned */
1986                           void *regex, int backward) {
1987   vt_char_t *line_str;
1988   u_int cols;
1989   ef_parser_t *parser;
1990   ef_conv_t *conv;
1991   u_char *buf;
1992   size_t buf_len;
1993   vt_line_t *line;
1994   int step;
1995   int res;
1996 
1997   if (!screen->search) {
1998     return 0;
1999   }
2000 
2001   cols = vt_screen_get_logical_cols(screen);
2002   if (!(line_str = vt_str_alloca(cols))) {
2003     return 0;
2004   }
2005   vt_str_init(line_str, cols);
2006 
2007   buf_len = vt_screen_get_logical_cols(screen) * VTCHAR_UTF_MAX_SIZE + 1;
2008   if (!(buf = alloca(buf_len))) {
2009     return 0;
2010   }
2011 
2012   if (!(parser = vt_str_parser_new())) {
2013     return 0;
2014   }
2015 
2016   if (!(conv = vt_char_encoding_conv_new(VT_UTF8))) {
2017     (*parser->destroy)(parser);
2018 
2019     return 0;
2020   }
2021 
2022   /* char_index == -1 has special meaning. */
2023   if (screen->search->char_index == -1) {
2024     screen->search->char_index = vt_screen_cursor_char_index(screen);
2025     screen->search->row = vt_screen_cursor_row(screen);
2026   }
2027 
2028   *beg_char_index = screen->search->char_index;
2029   *beg_row = screen->search->row;
2030   step = (backward ? -1 : 1);
2031 
2032 #ifdef DEBUG
2033   bl_debug_printf(BL_DEBUG_TAG " Search start from %d %d\n", *beg_char_index, *beg_row);
2034 #endif
2035 
2036   res = 0;
2037 
2038   for (; (line = vt_screen_get_line(screen, *beg_row)); (*beg_row) += step) {
2039     size_t line_len;
2040     size_t match_beg;
2041     size_t match_len;
2042 
2043     if ((line_len = vt_line_get_num_filled_chars_except_sp(line)) == 0) {
2044       continue;
2045     }
2046 
2047     /*
2048      * Visual => Logical
2049      */
2050     vt_line_copy_logical_str(line, line_str, 0, line_len);
2051 
2052     (*parser->init)(parser);
2053     if (backward) {
2054       vt_str_parser_set_str(parser, line_str, (*beg_row != screen->search->row)
2055                                                   ? line_len
2056                                                   : BL_MIN(*beg_char_index + 1, line_len));
2057       *beg_char_index = 0;
2058     } else {
2059       if (*beg_row != screen->search->row) {
2060         *beg_char_index = 0;
2061       } else if (line_len <= *beg_char_index) {
2062         continue;
2063       }
2064 
2065       vt_str_parser_set_str(parser, line_str + (*beg_char_index), line_len - *beg_char_index);
2066     }
2067 
2068     (*conv->init)(conv);
2069     *(buf + (*conv->convert)(conv, buf, buf_len - 1, parser)) = '\0';
2070 
2071     if ((*screen->search->match)(&match_beg, &match_len, regex, buf, backward)) {
2072       size_t count;
2073       u_int comb_size;
2074       int beg;
2075       int end;
2076       vt_char_t *comb;
2077 
2078       comb = vt_get_combining_chars(line_str + (*beg_char_index), &comb_size);
2079 
2080       for (count = 0; count < match_beg; count++) {
2081         /* Ignore 2nd and following bytes. */
2082         if ((buf[count] & 0xc0) != 0x80) {
2083           while (comb_size > 0 && vt_char_cs(comb) == PICTURE_CHARSET) {
2084             comb_size--;
2085             comb++;
2086           }
2087 
2088           if (comb_size > 0) {
2089             comb_size--;
2090             comb++;
2091           } else if ((++(*beg_char_index)) >= line_len - 1) {
2092             break;
2093           } else {
2094             comb = vt_get_combining_chars(line_str + (*beg_char_index), &comb_size);
2095           }
2096         }
2097       }
2098 
2099       *end_char_index = (*beg_char_index) - 1;
2100       for (; count < match_beg + match_len; count++) {
2101         /* Ignore 2nd and following bytes. */
2102         if ((buf[count] & 0xc0) != 0x80) {
2103           while (comb_size > 0 && vt_char_cs(comb) == PICTURE_CHARSET) {
2104             comb_size--;
2105             comb++;
2106           }
2107 
2108           if (comb_size > 0) {
2109             comb_size--;
2110             comb++;
2111           } else if ((++(*end_char_index)) >= line_len - 1) {
2112             break;
2113           } else {
2114             comb = vt_get_combining_chars(line_str + (*end_char_index), &comb_size);
2115           }
2116         }
2117       }
2118 
2119       if (*end_char_index < *beg_char_index) {
2120         continue;
2121       }
2122 
2123       *end_row = *beg_row;
2124 
2125       if (backward) {
2126         if (*beg_char_index > 0) {
2127           screen->search->char_index = *beg_char_index - 1;
2128           screen->search->row = *beg_row;
2129         } else {
2130           screen->search->char_index = vt_screen_get_logical_cols(screen) - 1;
2131           screen->search->row = *beg_row - 1;
2132         }
2133       } else {
2134         screen->search->char_index = *beg_char_index + 1;
2135         screen->search->row = *beg_row;
2136       }
2137 
2138 #ifdef DEBUG
2139       bl_debug_printf(BL_DEBUG_TAG " Search position x %d y %d\n", screen->search->char_index,
2140                       screen->search->row);
2141 #endif
2142 
2143       /*
2144        * Logical => Visual
2145        *
2146        * XXX Incomplete
2147        * Current implementation have problems like this.
2148        *  (logical)RRRLLLNNN => (visual)NNNLLLRRR
2149        *  Searching LLLNNN =>           ^^^^^^ hits but only NNNL is reversed.
2150        */
2151       beg = vt_line_convert_logical_char_index_to_visual(line, *beg_char_index, NULL);
2152       end = vt_line_convert_logical_char_index_to_visual(line, *end_char_index, NULL);
2153 
2154       if (beg > end) {
2155         *beg_char_index = end;
2156         *end_char_index = beg;
2157       } else {
2158         *beg_char_index = beg;
2159         *end_char_index = end;
2160       }
2161 
2162       if (vt_line_is_rtl(line)) {
2163         int char_index;
2164 
2165         /* XXX for x_selection */
2166         char_index = -(*beg_char_index);
2167         *beg_char_index = -(*end_char_index);
2168         *end_char_index = char_index;
2169       }
2170 
2171       res = 1;
2172 
2173       break;
2174     }
2175   }
2176 
2177   (*parser->destroy)(parser);
2178   (*conv->destroy)(conv);
2179   vt_str_final(line_str, vt_screen_get_logical_cols(screen));
2180 
2181   return res;
2182 }
2183 
vt_screen_blink(vt_screen_t * screen)2184 int vt_screen_blink(vt_screen_t *screen) {
2185   int has_blinking_char;
2186 
2187   has_blinking_char = 0;
2188 
2189   if (screen->has_blinking_char) {
2190     int char_index;
2191     int row;
2192     vt_line_t *line;
2193     u_int num_rows = vt_screen_get_rows(screen);
2194 
2195     /* '+ screen->backscroll_rows' prohibit backlog lines to blink. */
2196     for (row = 0; row + screen->backscroll_rows < num_rows; row++) {
2197       line = vt_screen_get_line(screen, row); /* Always non-NULL */
2198 
2199       for (char_index = 0; char_index < line->num_filled_chars; char_index++) {
2200         if (vt_char_is_blinking(line->chars + char_index)) {
2201           vt_line_set_modified(line, char_index, char_index);
2202           has_blinking_char = 1;
2203         }
2204       }
2205     }
2206 
2207     if (screen->backscroll_rows == 0) {
2208       screen->has_blinking_char = has_blinking_char;
2209     }
2210   }
2211 
2212   return has_blinking_char;
2213 }
2214 
2215 /*
2216  * VT100 commands
2217  *
2218  * !! Notice !!
2219  * These functions are called under logical order mode.
2220  */
2221 
vt_screen_get_n_prev_char(vt_screen_t * screen,int n)2222 vt_char_t *vt_screen_get_n_prev_char(vt_screen_t *screen, int n) {
2223   int char_index;
2224   int row;
2225   vt_line_t *line;
2226 
2227   if (!get_n_prev_char_pos(screen, &char_index, &row, 1)) {
2228     return NULL;
2229   }
2230 
2231   if ((line = vt_edit_get_line(screen->edit, row)) == NULL) {
2232     return NULL;
2233   }
2234 
2235   return vt_char_at(line, char_index);
2236 }
2237 
vt_screen_combine_with_prev_char(vt_screen_t * screen,u_int32_t code,ef_charset_t cs,int is_fullwidth,int is_awidth,int is_comb,vt_color_t fg_color,vt_color_t bg_color,int is_bold,int is_italic,int line_style,int is_blinking,int is_protected)2238 int vt_screen_combine_with_prev_char(vt_screen_t *screen, u_int32_t code, ef_charset_t cs,
2239                                      int is_fullwidth, int is_awidth, int is_comb,
2240                                      vt_color_t fg_color, vt_color_t bg_color, int is_bold,
2241                                      int is_italic, int line_style, int is_blinking,
2242                                      int is_protected) {
2243   int char_index;
2244   int row;
2245   vt_char_t *ch;
2246   vt_line_t *line;
2247 
2248   if (!get_n_prev_char_pos(screen, &char_index, &row, 1)) {
2249     return 0;
2250   }
2251 
2252   if ((line = vt_edit_get_line(screen->edit, row)) == NULL) {
2253     return 0;
2254   }
2255 
2256   if ((ch = vt_char_at(line, char_index)) == NULL) {
2257     return 0;
2258   }
2259 
2260   if (!vt_char_combine(ch, code, cs, is_fullwidth, is_awidth, is_comb, fg_color, bg_color, is_bold,
2261                        is_italic, line_style, is_blinking, is_protected)) {
2262     return 0;
2263   }
2264 
2265   vt_line_set_modified(line, char_index, char_index);
2266 
2267   return 1;
2268 }
2269 
vt_screen_insert_chars(vt_screen_t * screen,vt_char_t * chars,u_int len)2270 int vt_screen_insert_chars(vt_screen_t *screen, vt_char_t *chars, u_int len) {
2271   return vt_edit_insert_chars(screen->edit, chars, len);
2272 }
2273 
vt_screen_insert_new_lines(vt_screen_t * screen,u_int size)2274 int vt_screen_insert_new_lines(vt_screen_t *screen, u_int size) {
2275   u_int count;
2276 
2277   if (size > vt_edit_get_rows(screen->edit)) {
2278     size = vt_edit_get_rows(screen->edit);
2279   }
2280 
2281   for (count = 0; count < size; count++) {
2282     vt_edit_insert_new_line(screen->edit);
2283   }
2284 
2285   return 1;
2286 }
2287 
vt_screen_overwrite_chars(vt_screen_t * screen,vt_char_t * chars,u_int len)2288 int vt_screen_overwrite_chars(vt_screen_t *screen, vt_char_t *chars, u_int len) {
2289   return vt_edit_overwrite_chars(screen->edit, chars, len);
2290 }
2291 
vt_screen_delete_lines(vt_screen_t * screen,u_int size)2292 int vt_screen_delete_lines(vt_screen_t *screen, u_int size) {
2293   u_int count;
2294 
2295   if (size > vt_edit_get_rows(screen->edit)) {
2296     size = vt_edit_get_rows(screen->edit);
2297   }
2298 
2299   for (count = 0; count < size; count++) {
2300     if (!vt_edit_delete_line(screen->edit)) {
2301 #ifdef DEBUG
2302       bl_warn_printf(BL_DEBUG_TAG " deleting nonexisting lines.\n");
2303 #endif
2304 
2305       return 0;
2306     }
2307   }
2308 
2309   return 1;
2310 }
2311 
vt_screen_go_forward(vt_screen_t * screen,u_int size,int scroll)2312 int vt_screen_go_forward(vt_screen_t *screen, u_int size, int scroll) {
2313   u_int count;
2314 
2315   for (count = 0; count < size; count++) {
2316     if (!vt_edit_go_forward(screen->edit, 0)) {
2317       if (scroll) {
2318         if (size > vt_edit_get_cols(screen->edit)) {
2319           size = vt_edit_get_cols(screen->edit);
2320           if (size <= count) {
2321             break;
2322           }
2323         }
2324 
2325         vt_edit_scroll_leftward(screen->edit, size - count);
2326 
2327         break;
2328       }
2329 #ifdef DEBUG
2330       else {
2331         bl_warn_printf(BL_DEBUG_TAG " cursor cannot go forward any more.\n");
2332       }
2333 #endif
2334 
2335       return 0;
2336     }
2337   }
2338 
2339   return 1;
2340 }
2341 
vt_screen_go_back(vt_screen_t * screen,u_int size,int scroll)2342 int vt_screen_go_back(vt_screen_t *screen, u_int size, int scroll) {
2343   u_int count;
2344 
2345   for (count = 0; count < size; count++) {
2346     if (!vt_edit_go_back(screen->edit, 0)) {
2347       if (scroll) {
2348         if (size > vt_edit_get_cols(screen->edit)) {
2349           size = vt_edit_get_cols(screen->edit);
2350           if (size <= count) {
2351             break;
2352           }
2353         }
2354 
2355         vt_edit_scroll_rightward(screen->edit, size - count);
2356 
2357         break;
2358       }
2359 #ifdef DEBUG
2360       else {
2361         bl_warn_printf(BL_DEBUG_TAG " cursor cannot go back any more.\n");
2362       }
2363 #endif
2364 
2365       return 0;
2366     }
2367   }
2368 
2369   return 1;
2370 }
2371 
vt_screen_go_upward(vt_screen_t * screen,u_int size)2372 int vt_screen_go_upward(vt_screen_t *screen, u_int size) {
2373   u_int count;
2374 
2375   for (count = 0; count < size; count++) {
2376     if (!vt_edit_go_upward(screen->edit, 0)) {
2377 #ifdef DEBUG
2378       bl_warn_printf(BL_DEBUG_TAG " cursor cannot go upward any more.\n");
2379 #endif
2380 
2381       return 0;
2382     }
2383   }
2384 
2385   return 1;
2386 }
2387 
vt_screen_go_downward(vt_screen_t * screen,u_int size)2388 int vt_screen_go_downward(vt_screen_t *screen, u_int size) {
2389   u_int count;
2390 
2391   for (count = 0; count < size; count++) {
2392     if (!vt_edit_go_downward(screen->edit, 0)) {
2393 #ifdef DEBUG
2394       bl_warn_printf(BL_DEBUG_TAG " cursor cannot go downward any more.\n");
2395 #endif
2396 
2397       return 0;
2398     }
2399   }
2400 
2401   return 1;
2402 }
2403 
vt_screen_use_normal_edit(vt_screen_t * screen)2404 int vt_screen_use_normal_edit(vt_screen_t *screen) {
2405   if (screen->edit != &screen->normal_edit) {
2406     int col = screen->edit->cursor.col;
2407     int row = screen->edit->cursor.row;
2408 
2409     change_edit(screen, &screen->normal_edit);
2410     vt_edit_goto(screen->edit, col, row);
2411 
2412     return 1;
2413   } else {
2414     return 0;
2415   }
2416 }
2417 
vt_screen_use_alternative_edit(vt_screen_t * screen)2418 int vt_screen_use_alternative_edit(vt_screen_t *screen) {
2419   if (screen->edit != &screen->alt_edit) {
2420     int col = screen->edit->cursor.col;
2421     int row = screen->edit->cursor.row;
2422 
2423     change_edit(screen, &screen->alt_edit);
2424     vt_edit_goto(screen->edit, col, row);
2425 
2426     return 1;
2427   } else {
2428     return 0;
2429   }
2430 }
2431 
2432 /*
2433  * This function must be called in logical context in order to swap
2434  * stored_edit->edit and screen->edit in the same conditions.
2435  */
vt_screen_enable_local_echo(vt_screen_t * screen)2436 int vt_screen_enable_local_echo(vt_screen_t *screen) {
2437   if (!screen->stored_edit) {
2438     if (!(screen->stored_edit = malloc(sizeof(vt_stored_edit_t)))) {
2439       return 0;
2440     }
2441 
2442     screen->stored_edit->edit = *screen->edit;
2443 
2444     /*
2445      * New data is allocated in screen->edit, not in stored_edit->edit
2446      * because screen->edit allocated here will be deleted and
2447      * stored_edit->edit will be revived in vt_screen_disable_local_echo().
2448      */
2449     if (!vt_edit_clone(screen->edit, &screen->stored_edit->edit)) {
2450       free(screen->stored_edit);
2451       screen->stored_edit = NULL;
2452 
2453       return 0;
2454     }
2455 
2456     screen->edit->is_logging = 0;
2457   }
2458 
2459   screen->stored_edit->time = get_msec_time();
2460 
2461   return 1;
2462 }
2463 
vt_screen_local_echo_wait(vt_screen_t * screen,int msec)2464 int vt_screen_local_echo_wait(vt_screen_t *screen, int msec /* 0: stored_edit->time = 0 (>=
2465                                                                get_msec_time() is always false.) */
2466                               ) {
2467   if (screen->stored_edit) {
2468     if (msec == 0) {
2469       screen->stored_edit->time = 0;
2470     } else if (screen->stored_edit->time + msec >= get_msec_time()) {
2471       return 1;
2472     }
2473   }
2474 
2475   return 0;
2476 }
2477 
2478 /*
2479  * This function must be called in logical context in order to swap
2480  * stored_edit->edit and screen->edit in the same conditions.
2481  */
vt_screen_disable_local_echo(vt_screen_t * screen)2482 int vt_screen_disable_local_echo(vt_screen_t *screen) {
2483   u_int row;
2484   u_int num_rows;
2485   vt_line_t *old_line;
2486   vt_line_t *new_line;
2487 
2488   if (!screen->stored_edit) {
2489     return 0;
2490   }
2491 
2492   num_rows = vt_edit_get_rows(screen->edit);
2493 
2494   /*
2495    * Set modified flag to the difference between the current edit and the stored
2496    * one
2497    * to restore the screen before starting local echo.
2498    */
2499   for (row = 0; row < num_rows; row++) {
2500     if ((new_line = vt_edit_get_line(&screen->stored_edit->edit, row)) &&
2501         /* old_line is always non-NULL */
2502         (vt_line_is_modified((old_line = vt_edit_get_line(screen->edit, row))) ||
2503          old_line->num_filled_chars != new_line->num_filled_chars
2504 #if 1
2505          || !vt_str_bytes_equal(old_line->chars, new_line->chars, new_line->num_filled_chars)
2506 #endif
2507          )) {
2508       vt_line_set_modified_all(new_line);
2509     }
2510   }
2511 
2512   vt_edit_final(screen->edit);
2513   *screen->edit = screen->stored_edit->edit;
2514 
2515   free(screen->stored_edit);
2516   screen->stored_edit = NULL;
2517 
2518   return 1;
2519 }
2520 
vt_screen_copy_area(vt_screen_t * screen,int src_col,int src_row,u_int num_copy_cols,u_int num_copy_rows,u_int src_page,int dst_col,int dst_row,u_int dst_page)2521 void vt_screen_copy_area(vt_screen_t *screen, int src_col, int src_row, u_int num_copy_cols,
2522                          u_int num_copy_rows, u_int src_page,
2523                          int dst_col, int dst_row, u_int dst_page) {
2524   vt_edit_t *src_edit;
2525   vt_edit_t *dst_edit;
2526 
2527   if (src_page > MAX_PAGE_ID) {
2528     src_page = MAX_PAGE_ID;
2529   }
2530   if (dst_page > MAX_PAGE_ID) {
2531     dst_page = MAX_PAGE_ID;
2532   }
2533 
2534   if ((src_edit = get_edit(screen, src_page)) && (dst_edit = get_edit(screen, dst_page))) {
2535     vt_edit_copy_area(src_edit, src_col, src_row, num_copy_cols, num_copy_rows,
2536                       dst_edit, dst_col, dst_row);
2537   }
2538 }
2539 
vt_screen_get_checksum(vt_screen_t * screen,int col,int row,u_int num_cols,u_int num_rows,int page)2540 u_int16_t vt_screen_get_checksum(vt_screen_t *screen, int col, int row,
2541                              u_int num_cols, u_int num_rows, int page) {
2542   vt_edit_t *edit;
2543 
2544   if (page > MAX_PAGE_ID) {
2545     page = MAX_PAGE_ID;
2546   }
2547 
2548   if ((edit = get_edit(screen, page))) {
2549     return vt_edit_get_checksum(edit, col, row, num_cols, num_rows);
2550   }
2551 
2552   return 0;
2553 }
2554 
vt_screen_enable_blinking(vt_screen_t * screen)2555 void vt_screen_enable_blinking(vt_screen_t *screen) { screen->has_blinking_char = 1; }
2556 
vt_screen_write_content(vt_screen_t * screen,int fd,ef_conv_t * conv,int clear_at_end,vt_write_content_area_t area)2557 int vt_screen_write_content(vt_screen_t *screen, int fd, ef_conv_t *conv, int clear_at_end,
2558                             vt_write_content_area_t area) {
2559   int beg;
2560   int end;
2561   vt_char_t *buf;
2562   u_int buf_len;
2563   u_int num;
2564   u_char conv_buf[512];
2565   ef_parser_t *vt_str_parser;
2566 
2567 #if 1
2568   /* for sig_error in vt_term_manager.c */
2569   vt_screen_logical(screen);
2570 #endif
2571 
2572   if (area == WCA_CURSOR_LINE) {
2573     beg = vt_screen_cursor_row(screen);
2574     end = beg + 1;
2575   } else {
2576     end = vt_screen_get_rows(screen);
2577     if (area == WCA_ALL) {
2578       beg = -vt_screen_get_num_logged_lines(screen);
2579     } else /* if (area == WCA_SCREEN) */ {
2580       beg = 0;
2581     }
2582   }
2583 
2584   /* WCA_CURSOR_LINE may cause num == 0. */
2585   if ((buf_len = vt_screen_get_region_size(screen, 0, beg, 0, end, 0)) == 0) {
2586     return 0;
2587   }
2588 
2589   /* buf_len could become huge size which gets alloca() to fail. */
2590   if ((buf = vt_str_new(buf_len)) == NULL) {
2591     return 0;
2592   }
2593 
2594   vt_screen_copy_region(screen, buf, buf_len, 0, beg, 0, end, 0);
2595 
2596   if (!(vt_str_parser = vt_str_parser_new())) {
2597     vt_str_destroy(buf, buf_len);
2598 
2599     return 0;
2600   }
2601 
2602   (*vt_str_parser->init)(vt_str_parser);
2603   vt_str_parser_set_str(vt_str_parser, buf, buf_len);
2604   (*conv->init)(conv);
2605 
2606   while (!vt_str_parser->is_eos &&
2607          (num = (*conv->convert)(conv, conv_buf, sizeof(conv_buf), vt_str_parser)) > 0) {
2608     write(fd, conv_buf, num);
2609   }
2610 
2611   if (clear_at_end) {
2612     write(fd, "\n\x1b[1000S\x1b[H", 11);
2613   }
2614 
2615   (*vt_str_parser->destroy)(vt_str_parser);
2616   vt_str_destroy(buf, buf_len);
2617 
2618   return 1;
2619 }
2620 
vt_screen_get_page_id(vt_screen_t * screen)2621 int vt_screen_get_page_id(vt_screen_t *screen) {
2622   if (screen->edit == &screen->normal_edit ||
2623       screen->edit == &screen->alt_edit /* alt edit is regarded as 0 */) {
2624     return 0;
2625   } else if (screen->page_edits) {
2626     return screen->edit - screen->page_edits + 1;
2627   }
2628 
2629   return -1;
2630 }
2631 
vt_screen_goto_page(vt_screen_t * screen,u_int page_id)2632 int vt_screen_goto_page(vt_screen_t *screen, u_int page_id) {
2633   vt_edit_t *edit;
2634 
2635   if (page_id > MAX_PAGE_ID) {
2636     page_id = MAX_PAGE_ID;
2637   }
2638 
2639   if ((edit = get_edit(screen, page_id))) {
2640     vt_edit_goto(edit, screen->edit->cursor.col, screen->edit->cursor.row);
2641     if (vt_edit_get_tab_size(edit) != vt_edit_get_tab_size(screen->edit)) {
2642       vt_edit_set_tab_size(edit, vt_edit_get_tab_size(screen->edit));
2643     }
2644     edit->vmargin_beg = screen->edit->vmargin_beg;
2645     edit->vmargin_end = screen->edit->vmargin_end;
2646     edit->hmargin_beg = screen->edit->hmargin_beg;
2647     edit->hmargin_end = screen->edit->hmargin_end;
2648     edit->use_margin = screen->edit->use_margin;
2649     edit->is_logging = screen->edit->is_logging;
2650     edit->is_relative_origin = screen->edit->is_relative_origin;
2651     edit->is_auto_wrap = screen->edit->is_auto_wrap;
2652     edit->use_bce = screen->edit->use_bce;
2653     edit->use_rect_attr_select = screen->edit->use_rect_attr_select;
2654 
2655     change_edit(screen, edit);
2656 
2657     return 1;
2658   }
2659 
2660   return 0;
2661 }
2662 
vt_screen_goto_next_page(vt_screen_t * screen,u_int offset)2663 int vt_screen_goto_next_page(vt_screen_t *screen, u_int offset) {
2664   int page_id = vt_screen_get_page_id(screen);
2665 
2666   if (page_id != -1) {
2667     return vt_screen_goto_page(screen, page_id + offset);
2668   }
2669 
2670   return 0;
2671 }
2672 
vt_screen_goto_prev_page(vt_screen_t * screen,u_int offset)2673 int vt_screen_goto_prev_page(vt_screen_t *screen, u_int offset) {
2674   int page_id = vt_screen_get_page_id(screen);
2675 
2676   if (page_id != -1) {
2677     if (page_id < offset) {
2678       page_id = offset;
2679     }
2680 
2681     return vt_screen_goto_page(screen, page_id - offset);
2682   }
2683 
2684   return 0;
2685 }
2686 
vt_screen_set_use_status_line(vt_screen_t * screen,int use)2687 void vt_screen_set_use_status_line(vt_screen_t *screen, int use) {
2688   if (use) {
2689     if (!vt_screen_has_status_line(screen)) {
2690       if ((!screen->status_edit && !(screen->status_edit = status_edit_new(screen->edit))) ||
2691           /* XXX status line is *not* supported on vertical mode */
2692           (screen->logvis && !screen->logvis->is_reversible)) {
2693         return;
2694       }
2695 
2696       screen->main_edit = screen->edit;
2697       screen->has_status_line = 1;
2698       vt_edit_set_modified_all(screen->status_edit);
2699       /*
2700        * XXX
2701        * I don't know whether the cursor goes to home position or not,
2702        * but vttest 11.2.6.2.1 seems to assume it.
2703        * (https://vt100.net/docs/vt510-rm/DECSSDT.html doesn't describe it.)
2704        */
2705 #if 1
2706       vt_edit_goto(screen->status_edit, 0, 0);
2707 #endif
2708 
2709       resize(screen, vt_edit_get_cols(screen->main_edit),
2710              vt_edit_get_rows(screen->main_edit), 0);
2711     }
2712   } else {
2713     if (vt_screen_has_status_line(screen)) {
2714       screen->has_status_line = 0;
2715 
2716       resize(screen, vt_edit_get_cols(screen->main_edit),
2717              vt_edit_get_rows(screen->main_edit) + 1, 0);
2718       screen->main_edit = NULL;
2719     }
2720   }
2721 }
2722 
vt_focus_status_line(vt_screen_t * screen)2723 void vt_focus_status_line(vt_screen_t *screen) {
2724   if (!screen->status_edit && !(screen->status_edit = status_edit_new(screen->edit))) {
2725     return;
2726   }
2727 
2728   if (!vt_status_line_is_focused(screen)) {
2729     change_edit(screen, screen->status_edit);
2730   }
2731 }
2732 
vt_focus_main_screen(vt_screen_t * screen)2733 void vt_focus_main_screen(vt_screen_t *screen) {
2734   if (screen->main_edit && vt_status_line_is_focused(screen)) {
2735     change_edit(screen, screen->main_edit);
2736   }
2737 }
2738