1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "vt_edit.h"
4 
5 #include <string.h>      /* memmove/memset */
6 #include <pobl/bl_mem.h> /* alloca */
7 #include <pobl/bl_debug.h>
8 #include <pobl/bl_util.h>
9 #include <mef/ef_charset.h> /* ef_charset_t */
10 
11 #include "vt_edit_util.h"
12 #include "vt_edit_scroll.h"
13 
14 #if 0
15 #define __DEBUG
16 #endif
17 
18 #if 0
19 #define CURSOR_DEBUG
20 #endif
21 
22 #if 0
23 #define COMPAT_XTERM
24 #endif
25 
26 /*
27  * vt_edit_t::tab_stops
28  * e.g.)
29  * 1 line = 40 columns
30  * => tab_stops = u_int8_t * 5 (40bits)
31  * => Check tab_stops bits if you want to know whether a column is set tab stop
32  * or not.
33  */
34 #define TAB_STOPS_SIZE(edit) (((edit)->model.num_cols - 1) / 8 + 1)
35 
36 #define reset_wraparound_checker(edit) ((edit)->wraparound_ready_line = NULL)
37 
38 #define MARGIN_IS_ENABLED(edit) \
39   ((edit)->use_margin &&        \
40    (0 < (edit)->hmargin_beg || (edit)->hmargin_end + 1 < (edit)->model.num_cols))
41 
42 #define CURSOR_IS_INSIDE_HMARGIN(edit) \
43   ((edit)->hmargin_beg <= (edit)->cursor.col && (edit)->cursor.col <= (edit)->hmargin_end)
44 
45 #define CURSOR_IS_INSIDE_VMARGIN(edit) \
46   ((edit)->vmargin_beg <= (edit)->cursor.row && (edit)->cursor.row <= (edit)->vmargin_end)
47 
48 /* --- static variables --- */
49 
50 static char *resize_mode_name_table[] = {
51   "none", "scroll", "wrap",
52 };
53 static vt_resize_mode_t resize_mode = RZ_WRAP;
54 
55 /* --- static functions --- */
56 
57 /*
58  * Insert chars within a line.
59  * The cursor must be inside the left and right margins. (The caller of this
60  * function must check it in advance.)
61  */
insert_chars(vt_edit_t * edit,vt_char_t * ins_chars,u_int num_ins_chars,int do_move_cursor)62 static int insert_chars(vt_edit_t *edit, vt_char_t *ins_chars, u_int num_ins_chars,
63                         int do_move_cursor) {
64   vt_char_t *buffer;
65   u_int buf_len;
66   u_int num_cols;
67   u_int filled_len;
68   u_int filled_cols;
69   u_int last_index;
70   u_int cols_after;
71   u_int cols;
72   int cursor_col;
73   int count;
74   vt_line_t *cursor_line;
75 
76 #ifdef CURSOR_DEBUG
77   vt_cursor_dump(&edit->cursor);
78 #endif
79 
80   cursor_line = CURSOR_LINE(edit);
81 
82   buf_len = edit->model.num_cols;
83 
84 #ifndef COMPAT_XTERM
85   if (edit->cursor.col > edit->hmargin_end) {
86     num_cols = edit->model.num_cols;
87   } else
88 #endif
89   {
90     num_cols = edit->hmargin_end + 1;
91   }
92 
93   if ((buffer = vt_str_alloca(buf_len)) == NULL) {
94     return 0;
95   }
96   vt_str_init(buffer, buf_len);
97 
98   filled_len = 0;
99   filled_cols = 0;
100   cursor_col = edit->cursor.col;
101 
102   /*
103    * before cursor(excluding cursor)
104    */
105 
106   if (edit->cursor.col_in_char) {
107 #ifdef DEBUG
108     if (vt_char_cols(CURSOR_CHAR(edit)) <= edit->cursor.col_in_char) {
109       bl_warn_printf(BL_DEBUG_TAG " illegal col_in_char.\n");
110     }
111 #endif
112 
113     /*
114      * padding spaces for former half of cursor.
115      */
116     for (count = 0; count < edit->cursor.col_in_char; count++) {
117       vt_char_copy(&buffer[filled_len++], vt_sp_ch());
118     }
119 
120     filled_cols += count;
121     cols_after = vt_char_cols(CURSOR_CHAR(edit)) - count;
122     cursor_col -= count;
123   } else {
124     cols_after = 0;
125   }
126 
127   /*
128    * chars are appended one by one below since the line may be full.
129    */
130 
131   /*
132    * inserted chars
133    */
134 
135   for (count = 0; count < num_ins_chars; count++) {
136     cols = vt_char_cols(&ins_chars[count]);
137     if (cursor_col + filled_cols + cols > num_cols) {
138       /*
139        * ---+      ---+
140        *    |         |
141        * abcde  => abe|
142        */
143       if (filled_len > 0) {
144         vt_char_copy(&buffer[filled_len - 1], &ins_chars[num_ins_chars - 1]);
145       }
146 
147       break;
148     }
149 
150     vt_char_copy(&buffer[filled_len++], &ins_chars[count]);
151     filled_cols += cols;
152   }
153 
154   if (edit->cursor.char_index + filled_len == num_cols) {
155     /* cursor position doesn't proceed. */
156     last_index = filled_len - 1;
157   } else {
158     last_index = filled_len;
159   }
160 
161   /*
162    * cursor char
163    */
164 
165   if (cols_after) {
166     /*
167      * padding spaces for latter half of cursor.
168      */
169     for (count = 0; count < cols_after; count++) {
170       /* + 1 is for vt_sp_ch() */
171       if (cursor_col + filled_cols + 1 > num_cols) {
172         goto line_full;
173       }
174 
175       vt_char_copy(&buffer[filled_len++], vt_sp_ch());
176     }
177     filled_cols += count;
178   } else {
179     cols = vt_char_cols(CURSOR_CHAR(edit));
180     if (cursor_col + filled_cols + cols > num_cols) {
181       goto line_full;
182     }
183 
184     vt_char_copy(&buffer[filled_len++], CURSOR_CHAR(edit));
185     filled_cols += cols;
186   }
187 
188   /*
189    * after cursor(excluding cursor)
190    */
191 
192   for (count = edit->cursor.char_index + 1; count < cursor_line->num_filled_chars; count++) {
193     cols = vt_char_cols(vt_char_at(cursor_line, count));
194     if (cursor_col + filled_cols + cols > num_cols) {
195       break;
196     }
197 
198     vt_char_copy(&buffer[filled_len++], vt_char_at(cursor_line, count));
199     filled_cols += cols;
200   }
201 
202 line_full:
203   /*
204    * Updating current line and cursor.
205    */
206 
207   vt_line_overwrite(cursor_line, edit->cursor.char_index, buffer, filled_len, filled_cols);
208 
209   vt_str_final(buffer, buf_len);
210 
211   if (do_move_cursor) {
212     vt_cursor_moveh_by_char(&edit->cursor, edit->cursor.char_index + last_index);
213   } else if (edit->cursor.col_in_char) {
214     vt_cursor_moveh_by_char(&edit->cursor, edit->cursor.char_index + edit->cursor.col_in_char);
215   }
216 
217 #ifdef CURSOR_DEBUG
218   vt_cursor_dump(&edit->cursor);
219 #endif
220 
221   return 1;
222 }
223 
horizontal_tabs(vt_edit_t * edit,u_int num,int is_forward)224 static int horizontal_tabs(vt_edit_t *edit, u_int num, int is_forward) {
225   int col;
226   u_int count;
227 
228   if (edit->wraparound_ready_line) {
229     vt_edit_go_downward(edit, SCROLL);
230     vt_edit_goto_beg_of_line(edit);
231   }
232 
233   /*
234    * Compatible with rlogin 2.23.1.
235    *
236    * To be compatible with xterm-332, enclose by #if 0 ... #endif.
237    * (esctest: DECSETTests.test_DECSET_DECAWM_NoLineWrapOnTabWithLeftRightMargin)
238    */
239 #if 1
240   if (edit->cursor.col < edit->hmargin_beg) {
241     vt_cursor_goto_by_col(&edit->cursor, edit->hmargin_beg, edit->cursor.row);
242   } else if (edit->cursor.col > edit->hmargin_end) {
243     vt_cursor_goto_by_col(&edit->cursor, edit->hmargin_end, edit->cursor.row);
244   }
245 #endif
246 
247   col = edit->cursor.col;
248 
249   for (count = 0; count < num; count++) {
250     while (1) {
251       if (is_forward) {
252         if (col >= edit->hmargin_end) {
253           return 1;
254         }
255 
256         col++;
257         vt_edit_go_forward(edit, WRAPAROUND);
258       } else {
259         if (col <= edit->hmargin_beg) {
260           return 1;
261         }
262 
263         col--;
264         vt_edit_go_back(edit, WRAPAROUND);
265       }
266 
267       if (vt_edit_is_tab_stop(edit, col)) {
268         break;
269       }
270     }
271   }
272 
273   return 1;
274 }
275 
copy_area(vt_edit_t * src_edit,int src_col,int src_row,u_int num_copy_cols,u_int num_copy_rows,vt_edit_t * dst_edit,int dst_col,int dst_row)276 static void copy_area(vt_edit_t *src_edit, int src_col, int src_row, u_int num_copy_cols,
277                       u_int num_copy_rows, vt_edit_t *dst_edit, int dst_col, int dst_row) {
278   u_int count;
279   vt_line_t *src_line;
280   vt_line_t *dst_line;
281   int src_char_index;
282   int dst_char_index;
283   u_int src_cols_rest;
284   u_int src_cols_after;
285   u_int dst_cols_rest;
286   u_int num_src_chars;
287   u_int num_src_cols;
288 
289   for (count = 0; count < num_copy_rows; count++) {
290     int srow;
291     int drow;
292 
293     if (src_row < dst_row) {
294       srow = src_row + num_copy_rows - count - 1;
295       drow = dst_row + num_copy_rows - count - 1;
296     } else {
297       srow = src_row + count;
298       drow = dst_row + count;
299     }
300 
301     if (!(src_line = vt_edit_get_line(src_edit, srow)) ||
302         !(dst_line = vt_edit_get_line(dst_edit, drow))) {
303       continue;
304     }
305 
306     /* Beginning of src line */
307 
308     src_char_index =
309         vt_convert_col_to_char_index(src_line, &src_cols_rest, src_col, BREAK_BOUNDARY);
310     if (src_char_index >= src_line->num_filled_chars) {
311       src_cols_after = num_copy_cols;
312     } else if (src_cols_rest > 0) {
313       src_cols_after = vt_char_cols(src_line->chars + src_char_index) - src_cols_rest;
314       src_char_index++;
315     } else {
316       src_cols_after = 0;
317     }
318 
319     /* Beginning of dst line */
320 
321     dst_char_index = vt_convert_col_to_char_index(dst_line, &dst_cols_rest, dst_col, 0);
322 
323     /* Fill rest at the beginning */
324 
325     if (dst_cols_rest + src_cols_after > 0) {
326       vt_line_fill(dst_line, vt_sp_ch(), dst_char_index, dst_cols_rest + src_cols_after);
327       if (src_char_index >= src_line->num_filled_chars) {
328         continue;
329       }
330 
331       dst_char_index += (dst_cols_rest + src_cols_after);
332     }
333 
334     /* End of src line */
335 
336     num_src_chars =
337         vt_convert_col_to_char_index(src_line, &src_cols_rest, /* original value is replaced. */
338                                      src_col + num_copy_cols - 1, 0) +
339         1 - src_char_index;
340     if (src_cols_rest == 0) {
341       if ((src_cols_rest =
342                vt_char_cols(src_line->chars + src_char_index + num_src_chars - 1) - 1) > 0) {
343         num_src_chars--;
344       }
345     } else {
346       src_cols_rest = 0;
347     }
348     num_src_cols = num_copy_cols - src_cols_after - src_cols_rest;
349 
350     /* Copy src to dst */
351 
352     if (num_src_cols > 0) {
353       vt_line_overwrite(dst_line, dst_char_index, src_line->chars + src_char_index,
354                         num_src_chars, num_src_cols);
355     }
356 
357     /* Fill rest at the end */
358 
359     if (src_cols_rest > 0) {
360       vt_line_fill(dst_line, vt_sp_ch(), dst_char_index + num_src_chars, src_cols_rest);
361     }
362   }
363 }
364 
erase_area(vt_edit_t * edit,int col,int row,u_int num_cols,u_int num_rows)365 static void erase_area(vt_edit_t *edit, int col, int row, u_int num_cols, u_int num_rows) {
366   u_int count;
367   vt_line_t *line;
368   int char_index;
369   u_int cols_rest;
370 
371   for (count = 0; count < num_rows; count++) {
372     if (!(line = vt_edit_get_line(edit, row + count))) {
373       continue;
374     }
375 
376     char_index = vt_convert_col_to_char_index(line, &cols_rest, col, BREAK_BOUNDARY);
377 
378     if (char_index >= line->num_filled_chars && !edit->use_bce) {
379       continue;
380     }
381 
382     if (cols_rest > 0) {
383       vt_line_fill(line, edit->use_bce ? &edit->bce_ch : vt_sp_ch(), char_index, cols_rest);
384       char_index += cols_rest;
385     }
386 
387     vt_line_fill(line, edit->use_bce ? &edit->bce_ch : vt_sp_ch(), char_index, num_cols);
388   }
389 }
390 
scroll_downward_region(vt_edit_t * edit,u_int size,int is_cursor_beg,int ignore_cursor_pos)391 static int scroll_downward_region(vt_edit_t *edit, u_int size, int is_cursor_beg,
392                                   int ignore_cursor_pos) {
393   int vmargin_beg;
394 
395   if (is_cursor_beg) {
396     if (edit->cursor.row < edit->vmargin_beg) {
397       return 0;
398     }
399 
400     vmargin_beg = edit->cursor.row;
401   } else {
402     vmargin_beg = edit->vmargin_beg;
403   }
404 
405   /*
406    * XXX
407    * CURSOR_IS_INSIDE_HMARGIN(edit) disables vim to scroll the right side of
408    * vertically splitted window.
409    */
410   if (ignore_cursor_pos ||
411       (/* CURSOR_IS_INSIDE_HMARGIN(edit) && */
412        edit->cursor.row >= vmargin_beg && edit->cursor.row <= edit->vmargin_end)) {
413     if (size > edit->vmargin_end - vmargin_beg + 1) {
414       size = edit->vmargin_end - vmargin_beg + 1;
415     } else {
416       copy_area(edit, edit->hmargin_beg, vmargin_beg, edit->hmargin_end - edit->hmargin_beg + 1,
417                 edit->vmargin_end - vmargin_beg + 1 - size,
418                 edit, edit->hmargin_beg, vmargin_beg + size);
419     }
420 
421     erase_area(edit, edit->hmargin_beg, vmargin_beg, edit->hmargin_end - edit->hmargin_beg + 1,
422                size);
423 
424     return 1;
425   } else {
426     return 0;
427   }
428 }
429 
scroll_upward_region(vt_edit_t * edit,u_int size,int is_cursor_beg,int ignore_cursor_pos)430 static int scroll_upward_region(vt_edit_t *edit, u_int size, int is_cursor_beg,
431                                 int ignore_cursor_pos) {
432   int vmargin_beg;
433 
434   if (is_cursor_beg) {
435     if (edit->cursor.row < edit->vmargin_beg) {
436       return 0;
437     }
438 
439     vmargin_beg = edit->cursor.row;
440   } else {
441     vmargin_beg = edit->vmargin_beg;
442   }
443 
444   if (ignore_cursor_pos ||
445       (CURSOR_IS_INSIDE_HMARGIN(edit) &&
446        edit->cursor.row >= vmargin_beg && edit->cursor.row <= edit->vmargin_end)) {
447     if (size > edit->vmargin_end - vmargin_beg + 1) {
448       size = edit->vmargin_end - vmargin_beg + 1;
449     } else {
450       copy_area(edit, edit->hmargin_beg, vmargin_beg + size,
451                 edit->hmargin_end - edit->hmargin_beg + 1,
452                 edit->vmargin_end - vmargin_beg + 1 - size,
453                 edit, edit->hmargin_beg, vmargin_beg);
454     }
455 
456     erase_area(edit, edit->hmargin_beg, edit->vmargin_end + 1 - size,
457                edit->hmargin_end - edit->hmargin_beg + 1, size);
458 
459     return 1;
460   } else {
461     return 0;
462   }
463 }
464 
apply_relative_origin(vt_edit_t * edit,int * col,int * row,u_int * num_cols,u_int * num_rows)465 static int apply_relative_origin(vt_edit_t *edit, int *col, int *row, u_int *num_cols,
466                                  u_int *num_rows) {
467   if (edit->is_relative_origin) {
468     if (((*row) += edit->vmargin_beg) > edit->vmargin_end ||
469         ((*col) += edit->hmargin_beg) > edit->hmargin_end) {
470       return 0;
471     }
472 
473     if ((*row) + (*num_rows) > edit->vmargin_end + 1) {
474       (*num_rows) = edit->vmargin_end + 1 - (*row);
475     }
476 
477     if ((*col) + (*num_cols) > edit->hmargin_end + 1) {
478       (*num_cols) = edit->hmargin_end + 1 - (*col);
479     }
480   } else {
481     if ((*row) >= edit->model.num_rows || (*col) >= edit->model.num_cols) {
482       return 0;
483     }
484 
485     if ((*row) + (*num_rows) > edit->model.num_rows) {
486       (*num_rows) = edit->model.num_rows - (*row);
487     }
488 
489     if ((*col) + (*num_cols) > edit->model.num_cols) {
490       (*num_cols) = edit->model.num_cols - (*col);
491     }
492   }
493 
494   return 1;
495 }
496 
497 /*
498  * max_cols is always greater than 0, and it never becomes less than 0 at the last call
499  * of this function.
500  * vt_line_set_modified() should be executed by the caller of this function.
501  */
replace_chars_in_line(vt_line_t * line,vt_char_t ** chars,u_int max_cols)502 static u_int replace_chars_in_line(vt_line_t *line, vt_char_t **chars, u_int max_cols) {
503   u_int num_chars;
504 
505   vt_line_reset(line);
506   num_chars = vt_str_cols_to_len(*chars, &max_cols);
507   vt_line_overwrite(line, 0, *chars, num_chars, max_cols);
508   (*chars) += num_chars;
509 
510   return max_cols;
511 }
512 
resize_hadjustment(vt_edit_t * edit,u_int new_cols,u_int old_cols)513 static void resize_hadjustment(vt_edit_t *edit, u_int new_cols, u_int old_cols) {
514   int cursor_row = edit->cursor.row;
515   int cursor_col = edit->cursor.col;
516   u_int row;
517   vt_char_t *orig_buffer;
518   u_int max_buf_len = 0;
519 
520   for (row = 0; row < edit->model.num_rows;) {
521     vt_line_t *line = vt_model_get_line(&edit->model, row);
522     vt_line_t *line_tmp;
523     int expand_wrap_lines;
524     u_int count;
525     u_int buf_len = 0;
526     u_int line_nchars;
527 
528     line_nchars = vt_line_get_num_filled_chars_except_sp(line);
529 
530     if (vt_line_is_continued_to_next(line) && old_cols < new_cols) {
531       expand_wrap_lines = 1;
532     } else if (vt_str_cols(line->chars, line_nchars) > new_cols) {
533       expand_wrap_lines = 0;
534     } else {
535       row++;
536       continue;
537     }
538 
539     count = 0;
540     line_tmp = line;
541     while (1) {
542       buf_len += line_tmp->num_filled_chars;
543 
544       if (!vt_line_is_continued_to_next(line_tmp)) {
545         break;
546       } else if (row + count + 1 >= edit->model.num_rows) {
547         /* XXX The bottom line should not be wrapped, but it can happen. */
548         vt_line_set_continued_to_next(line_tmp, 0);
549         break;
550       }
551 
552       line_tmp = vt_model_get_line(&edit->model, row + (++count));
553     }
554 
555     if (buf_len > 0 &&
556         (buf_len <= max_buf_len || (orig_buffer = vt_str_alloca((max_buf_len = buf_len))))) {
557       u_int num_wrap_rows;
558       u_int old_num_wrap_rows = 0;
559       u_int num_filled_chars = 0;
560       u_int num_filled_cols = 0;
561       u_int line_ncols;
562       int cursor_adjusted = 0;
563       vt_char_t *buffer = orig_buffer;
564 
565       vt_str_init(buffer, buf_len);
566 
567       line_tmp = line;
568       count = 0;
569       while (1) {
570         line_nchars = vt_line_get_num_filled_chars_except_sp(line_tmp);
571         line_ncols = vt_str_cols(line_tmp->chars, line_nchars);
572 
573         vt_str_copy(buffer + num_filled_chars, line_tmp->chars, line_nchars);
574         num_filled_chars += line_nchars;
575         num_filled_cols += line_ncols;
576 
577         /*
578          * cursor_adjusted flag is necessary because 'cursor_row = cursor_row + n - count'
579          * below increments cursor_row and 'row + count == cursor_row' will get true again.
580          */
581         if (!cursor_adjusted && row + count == cursor_row) {
582           int n;
583 
584           /*
585            * XXX
586            * This calculation is incorrect if a character at the end of line
587            * is full width.
588            */
589           cursor_col = edit->cursor.col + count * old_cols;
590           n = cursor_col / new_cols;
591           cursor_row = cursor_row + n - count;
592           cursor_col -= (n * new_cols);
593 
594           if (edit->cursor.char_index >= line_nchars) {
595             /* Copy trailing spaces */
596             vt_str_copy(buffer + num_filled_chars, line_tmp->chars + line_nchars,
597                         edit->cursor.char_index + 1 - line_nchars);
598             num_filled_chars += (edit->cursor.char_index + 1 - line_ncols);
599             num_filled_cols += vt_str_cols(line_tmp->chars + line_nchars,
600                                       edit->cursor.char_index + 1 - line_nchars);
601           }
602 
603           cursor_adjusted = 1;
604         }
605 
606         if (!vt_line_is_continued_to_next(line_tmp)) {
607           break;
608         }
609 
610         /*
611          * If 'row + count == cursor_row && cursor_col >= line_ncols' above is true,
612          * vt_line_is_continued_to_next(line_tmp) is always false.
613          */
614         vt_str_copy(buffer + num_filled_chars, line_tmp->chars + line_nchars,
615                     line_tmp->num_filled_chars - line_nchars);
616         num_filled_chars += (line_tmp->num_filled_chars - line_nchars);
617         num_filled_cols += (vt_str_cols(line_tmp->chars, line_tmp->num_filled_chars) - line_ncols);
618 
619         line_tmp = vt_model_get_line(&edit->model, row + (++count));
620         old_num_wrap_rows ++;
621       }
622 
623       if (num_filled_cols == 0) {
624         num_wrap_rows = 0;
625       } else {
626         num_wrap_rows = (num_filled_cols - 1) / new_cols;
627       }
628 
629       if (expand_wrap_lines) {
630         u_int next_row = row + vt_edit_replace(edit, row, buffer, num_filled_cols, new_cols);
631 
632         if (old_num_wrap_rows > num_wrap_rows) {
633           for (count = row + old_num_wrap_rows + 1; count < edit->model.num_rows; count++) {
634             vt_line_copy(vt_model_get_line(&edit->model,
635                                            count - (old_num_wrap_rows - num_wrap_rows)),
636                          vt_model_get_line(&edit->model, count));
637           }
638 
639           for (count -= (old_num_wrap_rows - num_wrap_rows);
640                count < edit->model.num_rows; count++) {
641             vt_line_reset(vt_model_get_line(&edit->model, count));
642           }
643 
644           if (cursor_row > row + old_num_wrap_rows) {
645             cursor_row -= (old_num_wrap_rows - num_wrap_rows);
646           }
647         }
648 
649         row = next_row;
650       } else {
651         u_int empty_rows;
652 
653         /* old_num_wrap_rows is never greater than num_wrap_rows if expand_wrap_lines is false. */
654         num_wrap_rows -= old_num_wrap_rows;
655 
656         for (empty_rows = 0; empty_rows < num_wrap_rows; empty_rows++) {
657           int r = edit->model.num_rows - empty_rows - 1;
658 
659           if (r <= cursor_row) {
660             break;
661           }
662 
663           line = vt_model_get_line(&edit->model, r);
664           if (vt_line_get_num_filled_chars_except_sp(line) > 0) {
665             break;
666           }
667         }
668 
669         if (empty_rows > 0) {
670           if (cursor_row > row + old_num_wrap_rows) {
671             cursor_row += empty_rows;
672           }
673 
674           num_wrap_rows -= empty_rows;
675 
676           for (count = edit->model.num_rows - 1 - empty_rows; count > row; count--) {
677             vt_line_copy(vt_model_get_line(&edit->model, count + empty_rows),
678                          vt_model_get_line(&edit->model, count));
679           }
680         }
681 
682         if (num_wrap_rows > 0) {
683           count = 0;
684           do {
685             if (count >= row) {
686               line = vt_model_get_line(&edit->model, row);
687               /*
688                * new_cols is always less than num_filled_cols here because this line is
689                * scrolled out below but at least one line should remain in the screen.
690                */
691               num_filled_cols -= replace_chars_in_line(line, &buffer, new_cols);
692               vt_line_set_continued_to_next(line, 1);
693             } else {
694               line = vt_model_get_line(&edit->model, count);
695               if (cursor_row < row) {
696                 cursor_row--;
697               }
698             }
699 
700             if (edit->is_logging) {
701               (*edit->scroll_listener->receive_scrolled_out_line)(edit->scroll_listener->self,
702                                                                   line);
703             }
704           } while (++count < num_wrap_rows);
705 
706           if (num_wrap_rows <= row) {
707             for (count = 0; count < row - num_wrap_rows; count++) {
708               vt_line_copy(vt_model_get_line(&edit->model, count),
709                            vt_model_get_line(&edit->model, num_wrap_rows + count));
710             }
711             row -= num_wrap_rows;
712           } else {
713             row = 0;
714           }
715         }
716 
717         row += vt_edit_replace(edit, row, buffer, num_filled_cols, new_cols);
718       }
719 
720       vt_str_final(orig_buffer, buf_len);
721     }
722   }
723 
724   if (cursor_row < 0) {
725     cursor_row = cursor_col = 0;
726   } else if (cursor_row >= edit->model.num_rows) {
727     vt_line_t *line;
728 
729     cursor_row = edit->model.num_rows - 1;
730     line = vt_model_get_line(&edit->model, cursor_row);
731     /* cursor_col points space character just after the end character except space. */
732     cursor_col = vt_str_cols(line->chars, vt_line_get_num_filled_chars_except_sp(line));
733   }
734 
735 #if 0
736   if (edit->cursor.col != cursor_col || edit->cursor.row != cursor_row)
737 #endif
738   {
739     vt_cursor_goto_by_col(&edit->cursor, cursor_col, cursor_row);
740   }
741 }
742 
743 /* --- global functions --- */
744 
vt_set_resize_mode(vt_resize_mode_t mode)745 void vt_set_resize_mode(vt_resize_mode_t mode) {
746   resize_mode = mode;
747 }
748 
vt_get_resize_mode_by_name(const char * name)749 vt_resize_mode_t vt_get_resize_mode_by_name(const char *name) {
750   vt_resize_mode_t mode;
751 
752   for (mode = 0; mode < RZ_MODE_MAX; mode++) {
753     if (strcmp(resize_mode_name_table[mode], name) == 0) {
754       return mode;
755     }
756   }
757 
758   /* default value */
759   return RZ_WRAP;
760 }
761 
vt_edit_init(vt_edit_t * edit,vt_edit_scroll_event_listener_t * scroll_listener,u_int num_cols,u_int num_rows,u_int tab_size,int is_logging,int use_bce)762 int vt_edit_init(vt_edit_t *edit, vt_edit_scroll_event_listener_t *scroll_listener,
763                  u_int num_cols, u_int num_rows, u_int tab_size, int is_logging,
764                  int use_bce) {
765   if (!vt_model_init(&edit->model, num_cols, num_rows)) {
766     return 0;
767   }
768 
769   vt_cursor_init(&edit->cursor, &edit->model);
770 
771   vt_line_assure_boundary(CURSOR_LINE(edit), 0);
772 
773   vt_char_init(&edit->bce_ch);
774   vt_char_copy(&edit->bce_ch, vt_sp_ch());
775 
776   edit->use_bce = use_bce;
777 
778   edit->is_logging = is_logging;
779 
780   reset_wraparound_checker(edit);
781 
782   edit->vmargin_beg = 0;
783   edit->vmargin_end = vt_model_end_row(&edit->model);
784   edit->scroll_listener = scroll_listener;
785 
786   edit->use_margin = 0;
787   edit->hmargin_beg = 0;
788   edit->hmargin_end = num_cols - 1;
789 
790   if ((edit->tab_stops = malloc(TAB_STOPS_SIZE(edit))) == NULL) {
791 #ifdef DEBUG
792     bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
793 #endif
794 
795     return 0;
796   }
797 
798   vt_edit_set_tab_size(edit, tab_size);
799 
800   edit->is_relative_origin = 0;
801   edit->is_auto_wrap = 1;
802 
803   return 1;
804 }
805 
vt_edit_final(vt_edit_t * edit)806 void vt_edit_final(vt_edit_t *edit) {
807   vt_model_final(&edit->model);
808 
809   free(edit->tab_stops);
810 
811   vt_char_final(&edit->bce_ch);
812 }
813 
vt_edit_clone(vt_edit_t * dst_edit,vt_edit_t * src_edit)814 int vt_edit_clone(vt_edit_t *dst_edit, vt_edit_t *src_edit) {
815   u_int row;
816   u_int num_rows;
817   vt_line_t *src_line;
818   vt_line_t *dst_line;
819 
820   memcpy(((char*)dst_edit) + sizeof(vt_model_t), ((char*)src_edit) + sizeof(vt_model_t),
821          sizeof(vt_edit_t) - sizeof(vt_model_t));
822 
823   if (!(dst_edit->tab_stops = malloc(TAB_STOPS_SIZE(src_edit)))) {
824     return 0;
825   }
826   memcpy(dst_edit->tab_stops, src_edit->tab_stops, TAB_STOPS_SIZE(src_edit));
827 
828   dst_edit->cursor.model = &dst_edit->model;
829 
830   num_rows = vt_edit_get_rows(src_edit);
831 
832   if (!vt_model_init(&dst_edit->model, vt_edit_get_cols(src_edit), num_rows)) {
833     free(dst_edit->tab_stops);
834 
835     return 0;
836   }
837 
838   for (row = 0; row < num_rows; row++) {
839     dst_line = vt_edit_get_line(dst_edit, row);
840 
841     if ((src_line = vt_edit_get_line(src_edit, row)) == src_edit->wraparound_ready_line) {
842       dst_edit->wraparound_ready_line = dst_line;
843     }
844 
845     vt_line_copy(dst_line, src_line);
846   }
847 
848   return 1;
849 }
850 
vt_edit_resize(vt_edit_t * edit,u_int new_cols,u_int new_rows)851 int vt_edit_resize(vt_edit_t *edit, u_int new_cols, u_int new_rows) {
852   u_int old_cols;
853   u_int row;
854   u_int old_filled_rows;
855   u_int slide;
856   int ret = 1;
857 
858 #ifdef CURSOR_DEBUG
859   vt_cursor_dump(&edit->cursor);
860 #endif
861 
862   old_cols = edit->model.num_cols;
863 
864   if (resize_mode == RZ_WRAP && old_cols > new_cols) {
865     resize_hadjustment(edit, new_cols, old_cols);
866     ret = 2;
867   }
868 
869   if ((old_filled_rows = vt_model_get_num_filled_rows(&edit->model)) <= edit->cursor.row) {
870     old_filled_rows = edit->cursor.row + 1;
871   }
872 
873   if (old_filled_rows > new_rows) {
874     slide = old_filled_rows - new_rows;
875   } else {
876     slide = 0;
877   }
878 
879   if (edit->is_logging) {
880     int scroll_all = 0;
881 
882     if (resize_mode == RZ_SCROLL) {
883       for (row = 0; row < old_filled_rows; row++) {
884         vt_line_t *line = vt_model_get_line(&edit->model, row);
885         if (vt_str_cols(line->chars, vt_line_get_num_filled_chars_except_sp(line)) > new_cols) {
886           scroll_all = 1;
887           break;
888         }
889       }
890     }
891 
892     if (scroll_all) {
893       for (row = 0; row < old_filled_rows; row++) {
894         (*edit->scroll_listener->receive_scrolled_out_line)(edit->scroll_listener->self,
895                                                             vt_model_get_line(&edit->model, row));
896       }
897       vt_edit_goto_home(edit);
898       vt_edit_clear_below(edit);
899       ret = 3;
900     } else {
901       for (row = 0; row < slide; row++) {
902         (*edit->scroll_listener->receive_scrolled_out_line)(edit->scroll_listener->self,
903                                                             vt_model_get_line(&edit->model, row));
904       }
905     }
906   }
907 
908   if (!vt_model_resize(&edit->model, new_cols, new_rows, slide)) {
909 #ifdef DEBUG
910     bl_warn_printf(BL_DEBUG_TAG " vt_model_resize() failed.\n");
911 #endif
912 
913     return 0;
914   }
915 
916   if (slide > edit->cursor.row) {
917     vt_cursor_goto_by_char(&edit->cursor, 0, 0);
918   } else {
919     if ((edit->cursor.row -= slide) >= new_rows) {
920       edit->cursor.col = new_cols;
921       edit->cursor.row = new_rows - 1;
922     }
923 
924     if (edit->cursor.col >= new_cols) {
925       /* col points space character just after the end character except space. */
926       int col = vt_line_get_num_filled_chars_except_sp(
927                   vt_model_get_line(&edit->model,edit->cursor.row));
928 
929       vt_cursor_goto_by_col(&edit->cursor, col, edit->cursor.row);
930     }
931   }
932 
933   if (resize_mode == RZ_WRAP && old_cols < new_cols) {
934     if (edit->is_logging &&
935         (*edit->scroll_listener->top_line_is_wrapped)(edit->scroll_listener->self)) {
936       /*
937        * XXX
938        * +-------+    +---------+    +---------+
939        * |abcdefg|    |abcdefg  |    |abcdefg  | backlog
940        * +-------+ -> +---------+ -> |hij      |
941        * |hij    |    |hij      |    +---------+
942        * +-------+    +---------+    |         | main screen
943        *                             +---------+
944        */
945       u_int count;
946       int continued = 1;
947 
948       for (count = 0; continued; count++) {
949         vt_line_t *line = vt_model_get_line(&edit->model, count);
950 
951         (*edit->scroll_listener->receive_scrolled_out_line)(edit->scroll_listener->self, line);
952         continued = vt_line_is_continued_to_next(line);
953         vt_line_reset(line);
954       }
955 
956       vt_model_scroll_upward(&edit->model, count);
957       if (edit->cursor.row < count) {
958         vt_cursor_goto_by_char(&edit->cursor, 0, 0);
959       } else {
960         vt_cursor_goto_by_col(&edit->cursor, edit->cursor.col, edit->cursor.row - count);
961       }
962     }
963 
964     resize_hadjustment(edit, new_cols, old_cols);
965     ret = 2;
966   }
967 
968   reset_wraparound_checker(edit);
969 
970   edit->vmargin_beg = 0;
971   edit->vmargin_end = vt_model_end_row(&edit->model);
972 
973   edit->use_margin = 0;
974   edit->hmargin_beg = 0;
975   edit->hmargin_end = new_cols - 1;
976 
977   free(edit->tab_stops);
978 
979   if ((edit->tab_stops = malloc(TAB_STOPS_SIZE(edit))) == NULL) {
980     return 0;
981   }
982 
983   vt_edit_set_tab_size(edit, edit->tab_size);
984 
985 #ifdef CURSOR_DEBUG
986   vt_cursor_dump(&edit->cursor);
987 #endif
988 
989   return ret;
990 }
991 
vt_edit_insert_chars(vt_edit_t * edit,vt_char_t * ins_chars,u_int num_ins_chars)992 int vt_edit_insert_chars(vt_edit_t *edit, vt_char_t *ins_chars, u_int num_ins_chars) {
993   /*
994    * edit->wraparound_ready_line is ignored.
995    * esctest: SMTests.test_SM_IRM_DoesNotWrapUnlessCursorAtMargin fails by this.
996    *
997    * XXX
998    * xterm-332, TeraTerm-4.95: Wraparound works if IRM is set.
999    * rlogin-2.23.1: Wraparound is disabled if IRM is set.
1000    */
1001   reset_wraparound_checker(edit);
1002 
1003 #ifdef COMPAT_XTERM
1004   if (!CURSOR_IS_INSIDE_HMARGIN(edit)) {
1005     return vt_edit_overwrite_chars(edit, ins_chars, num_ins_chars);
1006   } else
1007 #endif
1008   {
1009     return insert_chars(edit, ins_chars, num_ins_chars, 1);
1010   }
1011 }
1012 
vt_edit_insert_blank_chars(vt_edit_t * edit,u_int num_blank_chars)1013 int vt_edit_insert_blank_chars(vt_edit_t *edit, u_int num_blank_chars) {
1014   int count;
1015   vt_char_t *blank_chars;
1016   vt_char_t *sp_ch;
1017 
1018   if (!CURSOR_IS_INSIDE_HMARGIN(edit)) {
1019     return 0;
1020   }
1021 
1022   reset_wraparound_checker(edit);
1023 
1024   if ((blank_chars = vt_str_alloca(num_blank_chars)) == NULL) {
1025     return 0;
1026   }
1027   vt_str_init(blank_chars, num_blank_chars);
1028 
1029   if (edit->use_bce) {
1030     /*
1031      * If bce_ch is not used here, vttest 11.4.5 "If your terminal
1032      * has the ANSI 'Insert Character' function..." will fail.
1033      */
1034     sp_ch = &edit->bce_ch;
1035   } else {
1036     sp_ch = vt_sp_ch();
1037   }
1038 
1039   for (count = 0; count < num_blank_chars; count++) {
1040     vt_char_copy(&blank_chars[count], sp_ch);
1041   }
1042 
1043   vt_str_final(blank_chars, num_blank_chars);
1044 
1045   /* cursor will not moved. */
1046   return insert_chars(edit, blank_chars, num_blank_chars, 0);
1047 }
1048 
vt_edit_overwrite_chars(vt_edit_t * edit,vt_char_t * ow_chars,u_int num_ow_chars)1049 int vt_edit_overwrite_chars(vt_edit_t *edit, vt_char_t *ow_chars, u_int num_ow_chars) {
1050   int count;
1051   vt_char_t *buffer;
1052   u_int buf_len;
1053   u_int num_cols;
1054   u_int filled_len;
1055   vt_line_t *line;
1056   int beg;
1057   u_int cols;
1058   int new_char_index;
1059 
1060 #ifdef CURSOR_DEBUG
1061   vt_cursor_dump(&edit->cursor);
1062 #endif
1063 
1064   buf_len = num_ow_chars + edit->model.num_cols;
1065 
1066   if (edit->cursor.col > edit->hmargin_end) {
1067     num_cols = edit->model.num_cols;
1068   } else {
1069     num_cols = edit->hmargin_end + 1;
1070   }
1071 
1072   if ((buffer = vt_str_alloca(buf_len)) == NULL) {
1073     return 0;
1074   }
1075   vt_str_init(buffer, buf_len);
1076 
1077   line = CURSOR_LINE(edit);
1078   filled_len = 0;
1079 
1080   /* before cursor(excluding cursor) */
1081 
1082   if (edit->cursor.col_in_char) {
1083     int count;
1084 
1085     /*
1086      * padding spaces before cursor.
1087      */
1088     for (count = 0; count < edit->cursor.col_in_char; count++) {
1089       vt_char_copy(&buffer[filled_len++], vt_sp_ch());
1090     }
1091   }
1092 
1093   /* appending overwriting chars */
1094   vt_str_copy(&buffer[filled_len], ow_chars, num_ow_chars);
1095   filled_len += num_ow_chars;
1096 
1097   /*
1098    * overwriting
1099    */
1100 
1101   beg = 0;
1102   count = 0;
1103   cols = 0;
1104 
1105   while (1) {
1106     u_int _cols;
1107 
1108     _cols = vt_char_cols(&buffer[count]);
1109 
1110     if (edit->cursor.col + cols + _cols > num_cols ||
1111         (edit->wraparound_ready_line && edit->cursor.col + cols + _cols == num_cols)) {
1112       vt_line_overwrite(line, edit->cursor.char_index, &buffer[beg], count - beg, cols);
1113 
1114       if (!edit->is_auto_wrap) {
1115         /*
1116          * ---+      ---+
1117          *    |         |
1118          * abcde  => abe|
1119          * (esctest: DECSET_DECAWM_OffRespectsLeftRightMargin)
1120          */
1121         if (count > 0) {
1122           vt_char_copy(&buffer[count - 1], &buffer[filled_len - 1]);
1123         }
1124 
1125         break;
1126       }
1127 
1128       vt_line_set_continued_to_next(line, 1);
1129 
1130       if (edit->cursor.row + 1 > edit->vmargin_end) {
1131         if (MARGIN_IS_ENABLED(edit) ? !scroll_upward_region(edit, 1, 0, 0)
1132                                     : !vt_edsl_scroll_upward(edit, 1)) {
1133           return 0;
1134         }
1135 
1136         /*
1137          * If edit->cursor.row == edit->vmargin_end in this situation,
1138          * vmargin_beg == vmargin_end.
1139          */
1140         if (edit->cursor.row + 1 <= edit->vmargin_end) {
1141           edit->cursor.row++;
1142         }
1143       } else {
1144         edit->cursor.row++;
1145       }
1146 
1147       if (edit->hmargin_beg > 0) {
1148         vt_cursor_goto_by_col(&edit->cursor, edit->hmargin_beg, edit->cursor.row);
1149       } else {
1150         edit->cursor.char_index = edit->cursor.col = 0;
1151       }
1152 
1153       /* Reset edit->wraparound_ready_line because it is not cursor line now. */
1154       reset_wraparound_checker(edit);
1155 
1156       beg = count;
1157       cols = _cols;
1158       line = CURSOR_LINE(edit);
1159     } else {
1160       cols += _cols;
1161     }
1162 
1163     if (++count >= filled_len) {
1164       break;
1165     }
1166   }
1167 
1168   new_char_index = edit->cursor.char_index + count - beg;
1169 
1170   if (edit->cursor.col + cols >= num_cols && edit->wraparound_ready_line != line) {
1171     /*
1172      * Don't use vt_line_end_char_index() instead of
1173      * new_char_index --, because num_cols is not
1174      * vt_model::num_cols but is vt_edit_t::hmargin_end + 1.
1175      */
1176     new_char_index--;
1177     edit->wraparound_ready_line = line;
1178   } else {
1179     reset_wraparound_checker(edit);
1180   }
1181 
1182   vt_line_overwrite(line, edit->cursor.char_index, &buffer[beg], count - beg, cols);
1183   vt_line_set_continued_to_next(line, 0);
1184 
1185   vt_cursor_moveh_by_char(&edit->cursor, new_char_index);
1186 
1187   vt_str_final(buffer, buf_len);
1188 
1189 #ifdef CURSOR_DEBUG
1190   vt_cursor_dump(&edit->cursor);
1191 #endif
1192 
1193   return 1;
1194 }
1195 
vt_edit_replace(vt_edit_t * edit,int beg_row,vt_char_t * chars,u_int cols,u_int max_cols_per_line)1196 u_int vt_edit_replace(vt_edit_t *edit, int beg_row /* starting row to be replaced */,
1197                       vt_char_t *chars, u_int cols /* > 0 */, u_int max_cols_per_line /* > 0 */) {
1198   int row = beg_row;
1199   vt_line_t *line;
1200 
1201   while (1) {
1202     if ((line = vt_model_get_line(&edit->model, row)) == NULL) {
1203       if (edit->is_logging) {
1204         (*edit->scroll_listener->receive_scrolled_out_line)(edit->scroll_listener->self,
1205                                                             vt_model_get_line(&edit->model, 0));
1206       }
1207       vt_model_scroll_upward(&edit->model, 1);
1208 
1209       if ((line = vt_model_get_line(&edit->model, row - 1)) == NULL) {
1210         break;
1211       }
1212     } else {
1213       row++;
1214     }
1215 
1216     cols -= replace_chars_in_line(line, &chars,
1217                                   cols < max_cols_per_line ? cols : max_cols_per_line);
1218     if (cols == 0) {
1219       break;
1220     }
1221 
1222     vt_line_set_continued_to_next(line, 1);
1223   }
1224 
1225   return row - beg_row;
1226 }
1227 
1228 /*
1229  * deleting cols within a line.
1230  */
vt_edit_delete_cols(vt_edit_t * edit,u_int del_cols)1231 int vt_edit_delete_cols(vt_edit_t *edit, u_int del_cols) {
1232   int char_index;
1233   vt_char_t *buffer;
1234   u_int buf_len;
1235   u_int filled_len;
1236   vt_line_t *cursor_line;
1237   u_int num_filled_cols;
1238 
1239 #ifdef CURSOR_DEBUG
1240   vt_cursor_dump(&edit->cursor);
1241 #endif
1242 
1243   reset_wraparound_checker(edit);
1244 
1245   cursor_line = CURSOR_LINE(edit);
1246 
1247   num_filled_cols = vt_line_get_num_filled_cols(cursor_line);
1248 
1249   if (!MARGIN_IS_ENABLED(edit) && edit->cursor.col + del_cols >= num_filled_cols) {
1250     /* no need to overwrite */
1251     vt_edit_clear_line_to_right(edit); /* Considering BCE */
1252 
1253     return 1;
1254   }
1255 
1256   /*
1257    * collecting chars after cursor line.
1258    */
1259 
1260   buf_len = cursor_line->num_chars - edit->cursor.col;
1261 
1262   if ((buffer = vt_str_alloca(buf_len)) == NULL) {
1263     return 0;
1264   }
1265   vt_str_init(buffer, buf_len);
1266 
1267   filled_len = 0;
1268 
1269   /* before cursor(including cursor) */
1270 
1271   if (edit->cursor.col_in_char) {
1272     int cols_after;
1273     int count;
1274 
1275 #ifdef DEBUG
1276     if (vt_char_cols(CURSOR_CHAR(edit)) <= edit->cursor.col_in_char) {
1277       bl_warn_printf(BL_DEBUG_TAG " illegal col_in_char.\n");
1278     }
1279 #endif
1280 
1281     cols_after = vt_char_cols(CURSOR_CHAR(edit)) - edit->cursor.col_in_char;
1282 
1283     /*
1284      * padding spaces before cursor.
1285      */
1286     for (count = 0; count < edit->cursor.col_in_char; count++) {
1287       vt_char_copy(&buffer[filled_len++], vt_sp_ch());
1288     }
1289 
1290     if (del_cols >= cols_after) {
1291       del_cols -= cols_after;
1292     } else {
1293       del_cols = 0;
1294 
1295       /*
1296        * padding spaces after cursor.
1297        */
1298       for (count = 0; count < cols_after - del_cols; count++) {
1299         vt_char_copy(&buffer[filled_len++], vt_sp_ch());
1300       }
1301     }
1302 
1303     char_index = edit->cursor.char_index + 1;
1304   } else {
1305     char_index = edit->cursor.char_index;
1306   }
1307 
1308   /* after cursor(excluding cursor) + del_cols */
1309 
1310   if (del_cols) {
1311     u_int cols;
1312 
1313     cols = vt_char_cols(vt_char_at(cursor_line, char_index++));
1314 
1315     if (MARGIN_IS_ENABLED(edit)) {
1316       if (!CURSOR_IS_INSIDE_HMARGIN(edit)) {
1317         return 0;
1318       }
1319 
1320       if (num_filled_cols > edit->hmargin_end + 1) {
1321         u_int count;
1322         u_int copy_len;
1323 
1324         while (cols < del_cols && edit->cursor.col + cols <= edit->hmargin_end) {
1325           cols += vt_char_cols(vt_char_at(cursor_line, char_index++));
1326         }
1327         del_cols = cols;
1328 
1329         while (edit->cursor.col + (cols++) <= edit->hmargin_end) {
1330           vt_char_copy(buffer + filled_len++, vt_char_at(cursor_line, char_index++));
1331         }
1332 
1333         for (count = 0; count < del_cols; count++) {
1334           vt_char_copy(buffer + (filled_len++), edit->use_bce ? &edit->bce_ch : vt_sp_ch());
1335         }
1336 
1337         copy_len = 0;
1338         while (char_index + copy_len < cursor_line->num_filled_chars) {
1339           vt_char_cols(vt_char_at(cursor_line, char_index + (copy_len++)));
1340         }
1341 
1342         vt_str_copy(buffer + filled_len, vt_char_at(cursor_line, char_index), copy_len);
1343         filled_len += copy_len;
1344       }
1345     } else {
1346       while (cols < del_cols && char_index < cursor_line->num_filled_chars) {
1347         cols += vt_char_cols(vt_char_at(cursor_line, char_index++));
1348       }
1349 
1350       vt_str_copy(buffer + filled_len, vt_char_at(cursor_line, char_index),
1351                   cursor_line->num_filled_chars - char_index);
1352       filled_len += (cursor_line->num_filled_chars - char_index);
1353     }
1354   }
1355 
1356   if (filled_len > 0) {
1357     /*
1358      * overwriting.
1359      */
1360 
1361     vt_edit_clear_line_to_right(edit); /* Considering BCE */
1362     vt_line_overwrite(cursor_line, edit->cursor.char_index, buffer, filled_len,
1363                       vt_str_cols(buffer, filled_len));
1364   } else {
1365     vt_line_reset(cursor_line);
1366   }
1367 
1368   vt_str_final(buffer, buf_len);
1369 
1370   if (edit->cursor.col_in_char) {
1371     vt_cursor_moveh_by_char(&edit->cursor, edit->cursor.char_index + edit->cursor.col_in_char);
1372   }
1373 
1374 #ifdef CURSOR_DEBUG
1375   vt_cursor_dump(&edit->cursor);
1376 #endif
1377 
1378   return 1;
1379 }
1380 
vt_edit_clear_cols(vt_edit_t * edit,u_int cols)1381 int vt_edit_clear_cols(vt_edit_t *edit, u_int cols) {
1382   vt_line_t *cursor_line;
1383 
1384   reset_wraparound_checker(edit);
1385 
1386   if (edit->cursor.col + cols >= edit->model.num_cols) {
1387     return vt_edit_clear_line_to_right(edit);
1388   }
1389 
1390   cursor_line = CURSOR_LINE(edit);
1391 
1392   if (edit->cursor.col_in_char) {
1393     vt_line_fill(cursor_line, edit->use_bce ? &edit->bce_ch : vt_sp_ch(),
1394                  edit->cursor.char_index, edit->cursor.col_in_char);
1395 
1396     vt_cursor_char_is_cleared(&edit->cursor);
1397   }
1398 
1399   vt_line_fill(cursor_line, edit->use_bce ? &edit->bce_ch : vt_sp_ch(), edit->cursor.char_index,
1400                cols);
1401 
1402   return 1;
1403 }
1404 
vt_edit_insert_new_line(vt_edit_t * edit)1405 int vt_edit_insert_new_line(vt_edit_t *edit) {
1406   reset_wraparound_checker(edit);
1407 
1408   if (MARGIN_IS_ENABLED(edit)) {
1409     return scroll_downward_region(edit, 1, 1, 0);
1410   } else {
1411     return vt_edsl_insert_new_line(edit);
1412   }
1413 }
1414 
vt_edit_delete_line(vt_edit_t * edit)1415 int vt_edit_delete_line(vt_edit_t *edit) {
1416   reset_wraparound_checker(edit);
1417 
1418   if (MARGIN_IS_ENABLED(edit)) {
1419     return scroll_upward_region(edit, 1, 1, 0);
1420   } else {
1421     return vt_edsl_delete_line(edit);
1422   }
1423 }
1424 
vt_edit_clear_line_to_right(vt_edit_t * edit)1425 int vt_edit_clear_line_to_right(vt_edit_t *edit) {
1426   vt_line_t *cursor_line;
1427 
1428   reset_wraparound_checker(edit);
1429 
1430   cursor_line = CURSOR_LINE(edit);
1431 
1432   if (edit->cursor.col_in_char) {
1433     vt_line_fill(cursor_line, edit->use_bce ? &edit->bce_ch : vt_sp_ch(), edit->cursor.char_index,
1434                  edit->cursor.col_in_char);
1435     vt_cursor_char_is_cleared(&edit->cursor);
1436   }
1437 
1438   if (edit->use_bce) {
1439     vt_line_clear_with(cursor_line, edit->cursor.char_index, &edit->bce_ch);
1440   } else {
1441     vt_line_clear(CURSOR_LINE(edit), edit->cursor.char_index);
1442   }
1443 
1444   return 1;
1445 }
1446 
vt_edit_clear_line_to_left(vt_edit_t * edit)1447 int vt_edit_clear_line_to_left(vt_edit_t *edit) {
1448   vt_line_t *cursor_line;
1449 
1450   reset_wraparound_checker(edit);
1451 
1452   cursor_line = CURSOR_LINE(edit);
1453 
1454   vt_line_fill(cursor_line, edit->use_bce ? &edit->bce_ch : vt_sp_ch(), 0, edit->cursor.col + 1);
1455 
1456   vt_cursor_left_chars_in_line_are_cleared(&edit->cursor);
1457 
1458   return 1;
1459 }
1460 
vt_edit_clear_below(vt_edit_t * edit)1461 int vt_edit_clear_below(vt_edit_t *edit) {
1462   reset_wraparound_checker(edit);
1463 
1464   if (!vt_edit_clear_line_to_right(edit)) {
1465     return 0;
1466   }
1467 
1468   if (edit->use_bce) {
1469     int row;
1470 
1471     for (row = edit->cursor.row + 1; row < edit->model.num_rows; row++) {
1472       vt_line_clear_with(vt_model_get_line(&edit->model, row), 0, &edit->bce_ch);
1473     }
1474 
1475     return 1;
1476   } else {
1477     return vt_edit_clear_lines(edit, edit->cursor.row + 1,
1478                                edit->model.num_rows - edit->cursor.row - 1);
1479   }
1480 }
1481 
vt_edit_clear_above(vt_edit_t * edit)1482 int vt_edit_clear_above(vt_edit_t *edit) {
1483   reset_wraparound_checker(edit);
1484 
1485   if (!vt_edit_clear_line_to_left(edit)) {
1486     return 0;
1487   }
1488 
1489   if (edit->use_bce) {
1490     int row;
1491 
1492     for (row = 0; row < edit->cursor.row; row++) {
1493       vt_line_clear_with(vt_model_get_line(&edit->model, row), 0, &edit->bce_ch);
1494     }
1495 
1496     return 1;
1497   } else {
1498     return vt_edit_clear_lines(edit, 0, edit->cursor.row);
1499   }
1500 }
1501 
vt_edit_save_protected_chars(vt_edit_t * edit,int beg_row,int end_row,int relative)1502 vt_protect_store_t *vt_edit_save_protected_chars(vt_edit_t *edit,
1503                                                  int beg_row, /* -1: cursor row (unless relative) */
1504                                                  int end_row, /* -1: cursor row (unless relative) */
1505                                                  int relative) {
1506   vt_protect_store_t *save = NULL;
1507   vt_char_t *dst;
1508   vt_char_t *src;
1509   int row;
1510   vt_line_t *line;
1511 
1512   if (relative) {
1513     if (edit->is_relative_origin) {
1514       if ((beg_row += edit->vmargin_beg) > edit->vmargin_end) {
1515         return NULL;
1516       }
1517       if ((end_row += edit->vmargin_beg) > edit->vmargin_end) {
1518         end_row = edit->vmargin_end;
1519       }
1520     }
1521   } else {
1522     if (beg_row == -1) {
1523       beg_row = vt_cursor_row(edit);
1524     }
1525     if (end_row == -1) {
1526       end_row = vt_cursor_row(edit);
1527     }
1528   }
1529 
1530   for (row = beg_row; row <= end_row; row++) {
1531     if ((line = vt_edit_get_line(edit, row))) {
1532       u_int num = vt_line_get_num_filled_chars_except_sp(line);
1533       u_int count;
1534 
1535       src = line->chars;
1536       for (count = 0; count < num; count++, src++) {
1537         if (vt_char_is_protected(src)) {
1538           if (!save) {
1539             if (!(save = malloc(sizeof(vt_protect_store_t) +
1540                                 sizeof(vt_char_t) * (vt_edit_get_cols(edit) + 1) *
1541                                                     (end_row - row + 1)))) {
1542               return NULL;
1543             }
1544 
1545             dst = save->chars = save + 1;
1546             vt_str_init(dst, (vt_edit_get_cols(edit) + 1) * (end_row - row + 1));
1547             dst += count;
1548             save->beg_row = row;
1549             save->end_row = end_row;
1550           }
1551 
1552           vt_char_copy(dst++, src);
1553         } else if (save) {
1554           dst += vt_char_cols(src);
1555         }
1556       }
1557     }
1558 
1559     if (save) {
1560       vt_char_copy(dst++, vt_nl_ch());
1561     }
1562   }
1563 
1564   return save;
1565 }
1566 
vt_edit_restore_protected_chars(vt_edit_t * edit,vt_protect_store_t * save)1567 void vt_edit_restore_protected_chars(vt_edit_t *edit, vt_protect_store_t *save) {
1568   int row;
1569   int col;
1570   u_int cols;
1571   vt_line_t *line;
1572   vt_char_t *src_p;
1573 
1574   if (save == NULL) {
1575     return;
1576   }
1577 
1578   src_p = save->chars;
1579 
1580   for (row = save->beg_row; row <= save->end_row; row++) {
1581     if ((line = vt_edit_get_line(edit, row))) {
1582       for (col = 0; !vt_char_equal(src_p, vt_nl_ch()); src_p++) {
1583         if (vt_char_is_protected(src_p)) {
1584           cols = vt_char_cols(src_p);
1585           vt_line_overwrite(line,
1586                             /* cols_rest must be always 0, so pass NULL. */
1587                             vt_convert_col_to_char_index(line, NULL, col, BREAK_BOUNDARY),
1588                             src_p, 1, cols);
1589         } else {
1590           cols = 1;
1591         }
1592         col += cols;
1593       }
1594       src_p++;
1595     }
1596   }
1597 
1598   vt_str_final(save->chars, (vt_edit_get_cols(edit) + 1) * (save->end_row - save->beg_row + 1));
1599   free(save);
1600 }
1601 
vt_edit_set_vmargin(vt_edit_t * edit,int beg,int end)1602 int vt_edit_set_vmargin(vt_edit_t *edit, int beg, int end) {
1603   /*
1604    * If beg and end is -1, use default(full size of window).
1605    * (see vt_parser.c)
1606    *
1607    * For compatibility with xterm:
1608    * 1. if beg and end are smaller than 0, ignore the sequence.
1609    * 2. if end is not larger than beg, ignore the sequence.
1610    * 3. if beg and end are out of window, ignore the sequence.
1611    */
1612 
1613   if (beg < 0) {
1614     if (beg == -1) {
1615       beg = 0;
1616     } else {
1617       return 0;
1618     }
1619   }
1620 
1621   if (end < 0) {
1622     if (end == -1) {
1623       end = vt_model_end_row(&edit->model);
1624     } else {
1625       return 0;
1626     }
1627   }
1628 
1629   if (beg >= end) {
1630     return 0;
1631   }
1632 
1633   if (beg >= edit->model.num_rows) {
1634     return 0;
1635   }
1636 
1637   if (end >= edit->model.num_rows) {
1638     end = vt_model_end_row(&edit->model);
1639   }
1640 
1641   edit->vmargin_beg = beg;
1642   edit->vmargin_end = end;
1643 
1644   return 1;
1645 }
1646 
vt_edit_scroll_leftward(vt_edit_t * edit,u_int size)1647 void vt_edit_scroll_leftward(vt_edit_t *edit, u_int size) {
1648   copy_area(edit, edit->hmargin_beg + size, edit->vmargin_beg,
1649             edit->hmargin_end - edit->hmargin_beg + 1 - size,
1650             edit->vmargin_end - edit->vmargin_beg + 1,
1651             edit, edit->hmargin_beg, edit->vmargin_beg);
1652   erase_area(edit, edit->hmargin_end + 1 - size, edit->vmargin_beg,
1653              size, edit->vmargin_end - edit->vmargin_beg + 1);
1654 }
1655 
vt_edit_scroll_rightward(vt_edit_t * edit,u_int size)1656 void vt_edit_scroll_rightward(vt_edit_t *edit, u_int size) {
1657   copy_area(edit, edit->hmargin_beg, edit->vmargin_beg,
1658             edit->hmargin_end - edit->hmargin_beg + 1 - size,
1659             edit->vmargin_end - edit->vmargin_beg + 1,
1660             edit, edit->hmargin_beg + size, edit->vmargin_beg);
1661   erase_area(edit, edit->hmargin_beg, edit->vmargin_beg,
1662              size, edit->vmargin_end - edit->vmargin_beg + 1);
1663 }
1664 
vt_edit_scroll_leftward_from_cursor(vt_edit_t * edit,u_int width)1665 int vt_edit_scroll_leftward_from_cursor(vt_edit_t *edit, u_int width) {
1666   int src;
1667   u_int height;
1668 
1669   if (!CURSOR_IS_INSIDE_HMARGIN(edit) || !CURSOR_IS_INSIDE_VMARGIN(edit)) {
1670     return 0;
1671   }
1672 
1673   height = edit->vmargin_end - edit->vmargin_beg + 1;
1674 
1675   if ((src = edit->cursor.col + width) <= edit->hmargin_end) {
1676     copy_area(edit, src, edit->vmargin_beg, edit->hmargin_end - src + 1, height,
1677               edit, edit->cursor.col, edit->vmargin_beg);
1678   } else {
1679     width = edit->hmargin_end - edit->cursor.col + 1;
1680   }
1681 
1682   erase_area(edit, edit->hmargin_end - width + 1, edit->vmargin_beg, width, height);
1683 
1684   return 1;
1685 }
1686 
vt_edit_scroll_rightward_from_cursor(vt_edit_t * edit,u_int width)1687 int vt_edit_scroll_rightward_from_cursor(vt_edit_t *edit, u_int width) {
1688   int dst;
1689   u_int height;
1690 
1691   if (!CURSOR_IS_INSIDE_HMARGIN(edit) || !CURSOR_IS_INSIDE_VMARGIN(edit)) {
1692     return 0;
1693   }
1694 
1695   height = edit->vmargin_end - edit->vmargin_beg + 1;
1696 
1697   if ((dst = edit->cursor.col + width) <= edit->hmargin_end) {
1698     copy_area(edit, edit->cursor.col, edit->vmargin_beg, edit->hmargin_end - dst + 1, height,
1699               edit, dst, edit->vmargin_beg);
1700   } else {
1701     width = edit->hmargin_end - edit->cursor.col + 1;
1702   }
1703 
1704   erase_area(edit, edit->cursor.col, edit->vmargin_beg, width, height);
1705 
1706   return 1;
1707 }
1708 
vt_edit_scroll_upward(vt_edit_t * edit,u_int size)1709 int vt_edit_scroll_upward(vt_edit_t *edit, u_int size) {
1710   int cursor_row;
1711   int cursor_col;
1712 
1713   cursor_row = edit->cursor.row;
1714   cursor_col = edit->cursor.col;
1715 
1716   if (MARGIN_IS_ENABLED(edit)) {
1717     scroll_upward_region(edit, size, 0, 1);
1718   } else {
1719     vt_edsl_scroll_upward(edit, size);
1720   }
1721 
1722   vt_cursor_goto_by_col(&edit->cursor, cursor_col, cursor_row);
1723 
1724   return 1;
1725 }
1726 
vt_edit_scroll_downward(vt_edit_t * edit,u_int size)1727 int vt_edit_scroll_downward(vt_edit_t *edit, u_int size) {
1728   int cursor_row;
1729   int cursor_col;
1730 
1731   cursor_row = edit->cursor.row;
1732   cursor_col = edit->cursor.col;
1733 
1734   if (MARGIN_IS_ENABLED(edit)) {
1735     scroll_downward_region(edit, size, 0, 1);
1736   } else {
1737     vt_edsl_scroll_downward(edit, size);
1738   }
1739 
1740   vt_cursor_goto_by_col(&edit->cursor, cursor_col, cursor_row);
1741 
1742   return 1;
1743 }
1744 
vt_edit_set_use_hmargin(vt_edit_t * edit,int use)1745 void vt_edit_set_use_hmargin(vt_edit_t *edit, int use) {
1746   if (!use) {
1747     edit->use_margin = 0;
1748     edit->hmargin_beg = 0;
1749     edit->hmargin_end = edit->model.num_cols - 1;
1750   } else {
1751     edit->use_margin = 1;
1752   }
1753 }
1754 
vt_edit_set_hmargin(vt_edit_t * edit,int beg,int end)1755 int vt_edit_set_hmargin(vt_edit_t *edit, int beg, int end) {
1756   if (!edit->use_margin) {
1757     /*
1758      * The terminal only recognizes DECSLRM if vertical split screen mode (DECLRMM) is set.
1759      * (see https://www.vt100.net/docs/vt510-rm/DECSLRM.html)
1760      */
1761     return 0;
1762   }
1763 
1764   /*
1765    * If beg and end is -1, use default(full size of window).
1766    * (see vt_parser.c)
1767    *
1768    * For compatibility with xterm:
1769    * 1. if beg and end are smaller than 0, ignore the sequence.
1770    * 2. if end is not larger than beg, ignore the sequence.
1771    * 3. if beg and end are out of window, ignore the sequence.
1772    */
1773 
1774   if (beg < 0) {
1775     if (beg == -1) {
1776       beg = 0;
1777     } else {
1778       return 0;
1779     }
1780   }
1781 
1782   if (end < 0) {
1783     if (end == -1) {
1784       end = edit->model.num_cols - 1;
1785     } else {
1786       return 0;
1787     }
1788   }
1789 
1790   if (beg >= end) {
1791     return 0;
1792   }
1793 
1794   if (beg >= edit->model.num_cols) {
1795     return 0;
1796   }
1797 
1798   if (end >= edit->model.num_cols) {
1799     end = edit->model.num_cols - 1;
1800   }
1801 
1802   edit->hmargin_beg = beg;
1803   edit->hmargin_end = end;
1804 
1805   return 1;
1806 }
1807 
vt_edit_forward_tabs(vt_edit_t * edit,u_int num)1808 int vt_edit_forward_tabs(vt_edit_t *edit, u_int num) { return horizontal_tabs(edit, num, 1); }
1809 
vt_edit_backward_tabs(vt_edit_t * edit,u_int num)1810 int vt_edit_backward_tabs(vt_edit_t *edit, u_int num) {
1811 #if 0
1812   /* compat with xterm 332 CBT behavior (esctest: CHATests.test_CHA_IgnoresScrollRegion) */
1813   int orig_hmargin_beg = edit->hmargin_beg;
1814   int ret;
1815 
1816   edit->hmargin_beg = 0;
1817   ret = horizontal_tabs(edit, num, 0);
1818   edit->hmargin_beg = orig_hmargin_beg;
1819 
1820   return ret;
1821 #else
1822   /* compat with RLogin 2.23.1 / Teraterm 4.95 */
1823   return horizontal_tabs(edit, num, 0);
1824 #endif
1825 }
1826 
vt_edit_set_tab_size(vt_edit_t * edit,u_int tab_size)1827 void vt_edit_set_tab_size(vt_edit_t *edit, u_int tab_size) {
1828   int col;
1829   u_int8_t *tab_stops;
1830 
1831   if (tab_size == 0) {
1832 #ifdef DEBUG
1833     bl_warn_printf(BL_DEBUG_TAG " tab size 0 is not acceptable.\n");
1834 #endif
1835 
1836     return;
1837   }
1838 
1839   vt_edit_clear_all_tab_stops(edit);
1840 
1841   col = 0;
1842   tab_stops = edit->tab_stops;
1843 
1844   while (1) {
1845     if (col % tab_size == 0) {
1846       (*tab_stops) |= (1 << (col % 8));
1847     }
1848 
1849     col++;
1850 
1851     if (col >= edit->model.num_cols) {
1852       tab_stops++;
1853 
1854       break;
1855     } else if (col % 8 == 7) {
1856       tab_stops++;
1857     }
1858   }
1859 
1860 #ifdef __DEBUG
1861   {
1862     int i;
1863 
1864     bl_debug_printf(BL_DEBUG_TAG " tab stops =>\n");
1865 
1866     for (i = 0; i < edit->model.num_cols; i++) {
1867       if (vt_edit_is_tab_stop(edit, i)) {
1868         bl_msg_printf("*");
1869       } else {
1870         bl_msg_printf(" ");
1871       }
1872     }
1873     bl_msg_printf("\n");
1874   }
1875 #endif
1876 
1877   edit->tab_size = tab_size;
1878 }
1879 
vt_edit_set_tab_stop(vt_edit_t * edit)1880 void vt_edit_set_tab_stop(vt_edit_t *edit) {
1881   edit->tab_stops[edit->cursor.col / 8] |= (1 << (edit->cursor.col % 8));
1882 }
1883 
vt_edit_clear_tab_stop(vt_edit_t * edit)1884 void vt_edit_clear_tab_stop(vt_edit_t *edit) {
1885   edit->tab_stops[edit->cursor.col / 8] &= ~(1 << (edit->cursor.col % 8));
1886 }
1887 
vt_edit_clear_all_tab_stops(vt_edit_t * edit)1888 void vt_edit_clear_all_tab_stops(vt_edit_t *edit) {
1889   memset(edit->tab_stops, 0, TAB_STOPS_SIZE(edit));
1890 }
1891 
vt_edit_set_modified_all(vt_edit_t * edit)1892 void vt_edit_set_modified_all(vt_edit_t *edit) {
1893   u_int count;
1894 
1895   for (count = 0; count < edit->model.num_rows; count++) {
1896     vt_line_set_modified_all(vt_model_get_line(&edit->model, count));
1897   }
1898 }
1899 
vt_edit_goto_beg_of_line(vt_edit_t * edit)1900 void vt_edit_goto_beg_of_line(vt_edit_t *edit) {
1901   reset_wraparound_checker(edit);
1902 
1903   if (edit->hmargin_beg > 0 && edit->cursor.col >= edit->hmargin_beg) {
1904     vt_cursor_goto_by_col(&edit->cursor, edit->hmargin_beg, edit->cursor.row);
1905   } else {
1906     vt_cursor_goto_beg_of_line(&edit->cursor);
1907   }
1908 }
1909 
1910 /*
1911  * Note that this function ignores edit->is_relative_origin.
1912  */
vt_edit_goto_home(vt_edit_t * edit)1913 void vt_edit_goto_home(vt_edit_t *edit) {
1914   reset_wraparound_checker(edit);
1915 
1916   vt_cursor_goto_home(&edit->cursor);
1917 }
1918 
vt_edit_go_forward(vt_edit_t * edit,int flag)1919 int vt_edit_go_forward(vt_edit_t *edit, int flag /* WARPAROUND | SCROLL */
1920                        ) {
1921   u_int num_cols;
1922 
1923 #ifdef CURSOR_DEBUG
1924   vt_cursor_dump(&edit->cursor);
1925 #endif
1926 
1927   if (CURSOR_IS_INSIDE_HMARGIN(edit)) {
1928     num_cols = edit->hmargin_end + 1;
1929   } else {
1930     num_cols = edit->model.num_cols;
1931   }
1932 
1933   reset_wraparound_checker(edit);
1934 
1935   if (edit->cursor.col + 1 >= num_cols) {
1936     if (!(flag & WRAPAROUND)) {
1937       return 0;
1938     }
1939 
1940     if (vt_is_scroll_lowerlimit(edit, edit->cursor.row)) {
1941       if (!(flag & SCROLL) || (MARGIN_IS_ENABLED(edit) ? !scroll_upward_region(edit, 1, 0, 0)
1942                                                        : !vt_edsl_scroll_upward(edit, 1))) {
1943         return 0;
1944       }
1945     }
1946 
1947     vt_cursor_cr_lf(&edit->cursor);
1948   } else if (!vt_cursor_go_forward(&edit->cursor)) {
1949     vt_line_break_boundary(CURSOR_LINE(edit), 1);
1950     vt_cursor_go_forward(&edit->cursor);
1951   }
1952 
1953 #ifdef CURSOR_DEBUG
1954   vt_cursor_dump(&edit->cursor);
1955 #endif
1956 
1957   return 1;
1958 }
1959 
vt_edit_go_back(vt_edit_t * edit,int flag)1960 int vt_edit_go_back(vt_edit_t *edit, int flag /* WRAPAROUND | SCROLL */
1961                     ) {
1962 #ifdef CURSOR_DEBUG
1963   vt_cursor_dump(&edit->cursor);
1964 #endif
1965 
1966   reset_wraparound_checker(edit);
1967 
1968   /*
1969    * full width char check.
1970    */
1971 
1972   if (edit->cursor.col_in_char) {
1973 #ifdef __DEBUG
1974     bl_debug_printf(BL_DEBUG_TAG " cursor is at 2nd byte of multi byte char.\n");
1975 #endif
1976 
1977     edit->cursor.col--;
1978     edit->cursor.col_in_char--;
1979 
1980     return 1;
1981   }
1982 
1983   /*
1984    * moving backward.
1985    */
1986 
1987   if (edit->cursor.char_index == 0 || edit->cursor.char_index == edit->hmargin_beg) {
1988     if (!(flag & WRAPAROUND)) {
1989       return 0;
1990     }
1991 
1992     if (vt_is_scroll_upperlimit(edit, edit->cursor.row)) {
1993       if (!(flag & SCROLL) || (MARGIN_IS_ENABLED(edit) ? !scroll_downward_region(edit, 1, 0, 0)
1994                                                        : !vt_edsl_scroll_downward(edit, 1))) {
1995         return 0;
1996       }
1997     }
1998 
1999     if (edit->cursor.row == 0) {
2000       return 0;
2001     }
2002 
2003     edit->cursor.row--;
2004     edit->cursor.char_index = vt_line_end_char_index(CURSOR_LINE(edit));
2005   } else {
2006     edit->cursor.char_index--;
2007   }
2008 
2009   edit->cursor.col_in_char = vt_char_cols(CURSOR_CHAR(edit)) - 1;
2010   edit->cursor.col = vt_convert_char_index_to_col(CURSOR_LINE(edit), edit->cursor.char_index, 0) +
2011                      edit->cursor.col_in_char;
2012 
2013 #ifdef CURSOR_DEBUG
2014   vt_cursor_dump(&edit->cursor);
2015 #endif
2016 
2017   return 1;
2018 }
2019 
vt_edit_go_upward(vt_edit_t * edit,int flag)2020 int vt_edit_go_upward(vt_edit_t *edit, int flag /* SCROLL */
2021                       ) {
2022 #ifdef CURSOR_DEBUG
2023   vt_cursor_dump(&edit->cursor);
2024 #endif
2025 
2026   reset_wraparound_checker(edit);
2027 
2028   if (vt_is_scroll_upperlimit(edit, edit->cursor.row)) {
2029     if (!(flag & SCROLL) || (MARGIN_IS_ENABLED(edit) ? !scroll_downward_region(edit, 1, 0, 0)
2030                                                      : !vt_edit_scroll_downward(edit, 1))) {
2031 #ifdef DEBUG
2032       bl_warn_printf(BL_DEBUG_TAG " cursor cannot go upward(reaches scroll lower limit).\n");
2033 #endif
2034 
2035       return 0;
2036     }
2037   } else {
2038     if (!vt_cursor_goto_by_col(&edit->cursor, edit->cursor.col, edit->cursor.row - 1)) {
2039       return 0;
2040     }
2041   }
2042 
2043 #ifdef CURSOR_DEBUG
2044   vt_cursor_dump(&edit->cursor);
2045 #endif
2046 
2047   return 1;
2048 }
2049 
vt_edit_go_downward(vt_edit_t * edit,int flag)2050 int vt_edit_go_downward(vt_edit_t *edit, int flag /* SCROLL */
2051                         ) {
2052 #ifdef CURSOR_DEBUG
2053   vt_cursor_dump(&edit->cursor);
2054 #endif
2055 
2056   reset_wraparound_checker(edit);
2057 
2058   if (vt_is_scroll_lowerlimit(edit, edit->cursor.row)) {
2059     if (!(flag & SCROLL) || (MARGIN_IS_ENABLED(edit) ? !scroll_upward_region(edit, 1, 0, 0)
2060                                                      : !vt_edit_scroll_upward(edit, 1))) {
2061 #ifdef DEBUG
2062       bl_warn_printf(BL_DEBUG_TAG " cursor cannot go downward(reaches scroll lower limit).\n");
2063 #endif
2064 
2065       return 0;
2066     }
2067   } else {
2068     if (!vt_cursor_goto_by_col(&edit->cursor, edit->cursor.col, edit->cursor.row + 1)) {
2069       return 0;
2070     }
2071   }
2072 
2073 #ifdef CURSOR_DEBUG
2074   vt_cursor_dump(&edit->cursor);
2075 #endif
2076 
2077   return 1;
2078 }
2079 
vt_edit_goto(vt_edit_t * edit,int col,int row)2080 int vt_edit_goto(vt_edit_t *edit, int col, int row) {
2081   reset_wraparound_checker(edit);
2082 
2083   if (edit->is_relative_origin) {
2084     if ((row += edit->vmargin_beg) > edit->vmargin_end) {
2085       row = edit->vmargin_end;
2086     }
2087 
2088     if ((col += edit->hmargin_beg) > edit->hmargin_end) {
2089       col = edit->hmargin_end;
2090     }
2091   }
2092 
2093   return vt_cursor_goto_by_col(&edit->cursor, col, row);
2094 }
2095 
vt_edit_set_last_column_flag(vt_edit_t * edit,int flag)2096 void vt_edit_set_last_column_flag(vt_edit_t *edit, int flag) {
2097   if (flag) {
2098     edit->wraparound_ready_line = CURSOR_LINE(edit);
2099   } else {
2100     reset_wraparound_checker(edit);
2101   }
2102 }
2103 
vt_edit_restore_cursor(vt_edit_t * edit)2104 int vt_edit_restore_cursor(vt_edit_t *edit) {
2105   if (vt_cursor_restore(&edit->cursor)) {
2106     reset_wraparound_checker(edit);
2107 
2108     return 1;
2109   } else {
2110     return 0;
2111   }
2112 }
2113 
vt_edit_fill_area(vt_edit_t * edit,int code,int is_protected,int col,int row,u_int num_cols,u_int num_rows)2114 void vt_edit_fill_area(vt_edit_t *edit, int code /* Unicode */, int is_protected,
2115                        int col, int row, u_int num_cols, u_int num_rows) {
2116   int char_index;
2117   u_int cols_rest;
2118   vt_line_t *line;
2119   vt_char_t ch;
2120 
2121   if (!apply_relative_origin(edit, &col, &row, &num_cols, &num_rows)) {
2122     return;
2123   }
2124 
2125   vt_char_init(&ch);
2126   vt_char_set(&ch, code,
2127               code <= 0x7f ? US_ASCII : ISO10646_UCS4_1, /* XXX biwidth is not supported. */
2128               0, 0, 0,
2129               /*
2130                * xterm-332 and Tera Term 4.95 don't use BCE for DECALN(ESC#8), but use for DECFRA.
2131                * rlogin-2.23.1 use BCE for both of them.
2132                *
2133                * (Test)
2134                * echo -e "\x1b[0;33;44m\x1b[60;1;1;10;10\$x"
2135                * echo -e "\x1b[0;33;44m\x1b#8"
2136                */
2137               edit->use_bce ? vt_char_fg_color(&edit->bce_ch) : VT_FG_COLOR,
2138               edit->use_bce ? vt_char_bg_color(&edit->bce_ch) : VT_BG_COLOR,
2139               0, 0, 0, 0, is_protected);
2140 
2141   for (; num_rows > 0; num_rows--) {
2142     line = vt_model_get_line(&edit->model, row++);
2143 
2144     char_index = vt_convert_col_to_char_index(line, &cols_rest, col, BREAK_BOUNDARY);
2145     if (cols_rest > 0) {
2146       vt_line_fill(line, edit->use_bce ? &edit->bce_ch : vt_sp_ch(), char_index, cols_rest);
2147       char_index += cols_rest;
2148     }
2149 
2150     vt_line_fill(line, &ch, char_index, num_cols);
2151   }
2152 
2153   vt_char_final(&ch);
2154 }
2155 
vt_edit_copy_area(vt_edit_t * src_edit,int src_col,int src_row,u_int num_copy_cols,u_int num_copy_rows,vt_edit_t * dst_edit,int dst_col,int dst_row)2156 void vt_edit_copy_area(vt_edit_t *src_edit, int src_col, int src_row, u_int num_copy_cols,
2157                       u_int num_copy_rows, vt_edit_t *dst_edit, int dst_col, int dst_row) {
2158   if (src_edit->is_relative_origin) {
2159     if ((src_row += src_edit->vmargin_beg) > src_edit->vmargin_end ||
2160         (dst_row += dst_edit->vmargin_beg) > dst_edit->vmargin_end ||
2161         (src_col += src_edit->hmargin_beg) > src_edit->hmargin_end ||
2162         (dst_col += dst_edit->hmargin_beg) > dst_edit->hmargin_end) {
2163       return;
2164     }
2165 
2166     if (src_row + num_copy_rows > src_edit->vmargin_end + 1) {
2167       num_copy_rows = src_edit->vmargin_end + 1 - src_row;
2168     }
2169 
2170     if (dst_row + num_copy_rows > dst_edit->vmargin_end + 1) {
2171       num_copy_rows = dst_edit->vmargin_end + 1 - dst_row;
2172     }
2173 
2174     if (src_col + num_copy_cols > src_edit->hmargin_end + 1) {
2175       num_copy_cols = src_edit->hmargin_end + 1 - src_col;
2176     }
2177 
2178     if (dst_col + num_copy_cols > dst_edit->hmargin_end + 1) {
2179       num_copy_cols = dst_edit->hmargin_end + 1 - dst_col;
2180     }
2181   } else {
2182     if (src_row >= src_edit->model.num_rows ||
2183         dst_row >= dst_edit->model.num_rows ||
2184         src_col >= src_edit->model.num_cols ||
2185         dst_col >= dst_edit->model.num_cols) {
2186       return;
2187     }
2188 
2189     if (src_row + num_copy_rows > src_edit->model.num_rows) {
2190       num_copy_rows = src_edit->model.num_rows - src_row;
2191     }
2192 
2193     if (dst_row + num_copy_rows > dst_edit->model.num_rows) {
2194       num_copy_rows = dst_edit->model.num_rows - dst_row;
2195     }
2196 
2197     if (src_col + num_copy_cols > src_edit->model.num_cols) {
2198       num_copy_cols = src_edit->model.num_cols - src_col;
2199     }
2200 
2201     if (dst_col + num_copy_cols > dst_edit->model.num_cols) {
2202       num_copy_cols = dst_edit->model.num_cols - dst_col;
2203     }
2204   }
2205 
2206   copy_area(src_edit, src_col, src_row, num_copy_cols, num_copy_rows,
2207             dst_edit, dst_col, dst_row);
2208 }
2209 
vt_edit_erase_area(vt_edit_t * edit,int col,int row,u_int num_cols,u_int num_rows)2210 void vt_edit_erase_area(vt_edit_t *edit, int col, int row, u_int num_cols, u_int num_rows) {
2211   if (!apply_relative_origin(edit, &col, &row, &num_cols, &num_rows)) {
2212     return;
2213   }
2214 
2215   erase_area(edit, col, row, num_cols, num_rows);
2216 }
2217 
vt_edit_change_attr_area(vt_edit_t * edit,int col,int row,u_int num_cols,u_int num_rows,void (* func)(vt_char_t *,int,int,int,int,int,int,int),int attr)2218 void vt_edit_change_attr_area(vt_edit_t *edit, int col, int row, u_int num_cols, u_int num_rows,
2219                               void (*func)(vt_char_t*, int, int, int, int, int, int, int),
2220                               int attr) {
2221   u_int count;
2222   vt_line_t *line;
2223   int char_index;
2224   int end_char_index;
2225   u_int cols_rest;
2226   int bold;
2227   int italic;
2228   int underline_style;
2229   int blinking;
2230   int reversed;
2231   int crossed_out;
2232   int overlined;
2233 
2234   if (attr == 0) {
2235     bold = italic = underline_style = blinking = reversed = crossed_out = overlined = -1;
2236   } else {
2237     bold = italic = underline_style = blinking = reversed = crossed_out = overlined = 0;
2238 
2239     if (attr == 1) {
2240       bold = 1;
2241     } else if (attr == 3) {
2242       italic = 1;
2243     } else if (attr == 4) {
2244       underline_style = LS_UNDERLINE_SINGLE;
2245     } else if (attr == 5 || attr == 6) {
2246       blinking = 1;
2247     } else if (attr == 7) {
2248       reversed = 1;
2249     } else if (attr == 9) {
2250       crossed_out = 1;
2251     } else if (attr == 21) {
2252       underline_style = LS_UNDERLINE_DOUBLE;
2253     } else if (attr == 22) {
2254       bold = -1;
2255     } else if (attr == 23) {
2256       italic = -1;
2257     } else if (attr == 24) {
2258       underline_style = -1;
2259     } else if (attr == 25) {
2260       blinking = -1;
2261     } else if (attr == 27) {
2262       reversed = -1;
2263     } else if (attr == 29) {
2264       crossed_out = -1;
2265     } else if (attr == 53) {
2266       overlined = 1;
2267     } else if (attr == 55) {
2268       overlined = -1;
2269     } else {
2270       return;
2271     }
2272   }
2273 
2274   /*
2275    * XXX
2276    * apply_relative_origin() adjusts arguments regarding edit->use_rect_attr_select as true
2277    * all the time.
2278    */
2279   if (!apply_relative_origin(edit, &col, &row, &num_cols, &num_rows)) {
2280     return;
2281   }
2282 
2283   for (count = 0; count < num_rows; count++) {
2284     if (count == 1 && !edit->use_rect_attr_select) {
2285       int old_col;
2286 
2287       old_col = col;
2288       col = edit->is_relative_origin ? edit->hmargin_beg : 0;
2289       num_cols += (old_col - col);
2290     }
2291 
2292     if (!(line = vt_edit_get_line(edit, row + count))) {
2293       continue;
2294     }
2295 
2296     char_index = vt_convert_col_to_char_index(line, &cols_rest, col, BREAK_BOUNDARY);
2297     if (char_index >= line->num_filled_chars && attr > 7) {
2298       continue;
2299     }
2300 
2301     if (cols_rest > 0) {
2302       char_index++;
2303     }
2304 
2305     if (edit->use_rect_attr_select || count + 1 == num_rows) {
2306       end_char_index =
2307           vt_convert_col_to_char_index(line, NULL, col + num_cols - 1, BREAK_BOUNDARY);
2308     } else {
2309       end_char_index = vt_convert_col_to_char_index(
2310           line, NULL, edit->is_relative_origin ? edit->hmargin_end : vt_edit_get_cols(edit) - 1,
2311           BREAK_BOUNDARY);
2312     }
2313 
2314     vt_line_assure_boundary(line, end_char_index);
2315 
2316     vt_line_set_modified(line, char_index, end_char_index);
2317 
2318     for (; char_index <= end_char_index; char_index++) {
2319       (*func)(vt_char_at(line, char_index), bold, italic, underline_style, blinking, reversed,
2320               crossed_out, overlined);
2321     }
2322   }
2323 }
2324 
vt_edit_get_checksum(vt_edit_t * edit,int col,int row,u_int num_cols,u_int num_rows)2325 u_int16_t vt_edit_get_checksum(vt_edit_t *edit, int col, int row, u_int num_cols, u_int num_rows) {
2326   int count;
2327   u_int16_t checksum;
2328   vt_line_t *line;
2329   int char_index;
2330   u_int cols_rest;
2331 
2332   if (!apply_relative_origin(edit, &col, &row, &num_cols, &num_rows)) {
2333     return 0;
2334   }
2335 
2336   checksum = 0;
2337   for (count = 0; count < num_rows; count++) {
2338     if ((line = vt_edit_get_line(edit, row + count))) {
2339       int end_char_index;
2340       vt_char_t *ch;
2341 
2342       if ((char_index = vt_convert_col_to_char_index(line, &cols_rest, col, BREAK_BOUNDARY)) >=
2343           line->num_filled_chars) {
2344         continue;
2345       }
2346 
2347       if (cols_rest > 0) {
2348         char_index ++;
2349         checksum --; /* 2nd column of full width character is 0xFFFF (compat with xterm) */
2350       }
2351 
2352       if ((end_char_index = vt_convert_col_to_char_index(line, &cols_rest,
2353                                                          col + num_cols, BREAK_BOUNDARY)) >
2354 #if 0
2355           line->num_filled_chars
2356 #else
2357           vt_line_get_num_filled_chars_except_sp(line) /* compat with xterm */
2358 #endif
2359           ) {
2360 #if 0
2361         end_char_index = line->num_filled_chars;
2362 #else
2363         end_char_index = vt_line_get_num_filled_chars_except_sp(line); /* compat with xterm */
2364 #endif
2365       }
2366 
2367       if (cols_rest > 0) {
2368         end_char_index++;
2369         checksum ++; /* col + num_cols - 1 is 1st column of full width character */
2370       }
2371 
2372       for(; char_index < end_char_index; char_index++) {
2373         ch = vt_char_at(line, char_index);
2374         checksum += vt_char_code(ch);
2375         if (vt_char_cols(ch) == 2) {
2376           checksum --; /* 2nd column of full width character is 0xFFFF (compat with xterm) */
2377         }
2378       }
2379     }
2380   }
2381 
2382   return checksum;
2383 }
2384 
vt_edit_clear_size_attr(vt_edit_t * edit)2385 void vt_edit_clear_size_attr(vt_edit_t *edit) {
2386   u_int count;
2387 
2388   for (count = 0; count < edit->model.num_rows; count++) {
2389     vt_line_set_size_attr(vt_edit_get_line(edit, count), 0);
2390   }
2391 }
2392 
vt_edit_cursor_logical_col(vt_edit_t * edit)2393 int vt_edit_cursor_logical_col(vt_edit_t *edit) {
2394   if (edit->is_relative_origin) {
2395     if (edit->cursor.col > edit->hmargin_beg) {
2396       return edit->cursor.col - edit->hmargin_beg;
2397     } else {
2398       return 0;
2399     }
2400   }
2401 
2402   return edit->cursor.col;
2403 }
2404 
vt_edit_cursor_logical_row(vt_edit_t * edit)2405 int vt_edit_cursor_logical_row(vt_edit_t *edit) {
2406   if (edit->is_relative_origin) {
2407     if (edit->cursor.row > edit->vmargin_beg) {
2408       return edit->cursor.row - edit->vmargin_beg;
2409     } else {
2410       return 0;
2411     }
2412   }
2413 
2414   return edit->cursor.row;
2415 }
2416 
2417 /*
2418  * for debugging.
2419  */
2420 
2421 #ifdef DEBUG
2422 
vt_edit_dump(vt_edit_t * edit)2423 void vt_edit_dump(vt_edit_t *edit) {
2424   int row;
2425   vt_line_t *line;
2426 
2427   bl_debug_printf(BL_DEBUG_TAG " ===> dumping edit...[cursor(index)%d (col)%d (row)%d]\n",
2428                   edit->cursor.char_index, edit->cursor.col, edit->cursor.row);
2429 
2430   for (row = 0; row < edit->model.num_rows; row++) {
2431     int char_index;
2432 
2433     line = vt_model_get_line(&edit->model, row);
2434 
2435     if (vt_line_is_modified(line)) {
2436       if (vt_line_is_cleared_to_end(line)) {
2437         bl_msg_printf("!%.2d-END", vt_line_get_beg_of_modified(line));
2438       } else {
2439         bl_msg_printf("!%.2d-%.2d", vt_line_get_beg_of_modified(line),
2440                       vt_line_get_end_of_modified(line));
2441       }
2442     } else {
2443       bl_msg_printf("      ");
2444     }
2445 
2446     bl_msg_printf("[%.2d %.2d]", line->num_filled_chars, vt_line_get_num_filled_cols(line));
2447 
2448     if (line->num_filled_chars > 0) {
2449       for (char_index = 0; char_index < line->num_filled_chars; char_index++) {
2450         if (edit->cursor.row == row && edit->cursor.char_index == char_index) {
2451           bl_msg_printf("**");
2452         }
2453 
2454         vt_char_dump(vt_char_at(line, char_index));
2455 
2456         if (edit->cursor.row == row && edit->cursor.char_index == char_index) {
2457           bl_msg_printf("**");
2458         }
2459       }
2460     }
2461 
2462     bl_msg_printf("\n");
2463   }
2464 
2465   bl_debug_printf(BL_DEBUG_TAG " <=== end of edit.\n\n");
2466 }
2467 
vt_edit_dump_updated(vt_edit_t * edit)2468 void vt_edit_dump_updated(vt_edit_t *edit) {
2469   int row;
2470 
2471   for (row = 0; row < edit->model.num_rows; row++) {
2472     bl_msg_printf("(%.2d)%d ", row, vt_line_is_modified(vt_model_get_line(&edit->model, row)));
2473   }
2474 
2475   bl_msg_printf("\n");
2476 }
2477 
2478 #endif
2479