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