1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#define FOR_vi
21#include "toys.h"
22
23GLOBALS(
24 char *s;
25 int vi_mode, tabstop, list;
26 int cur_col, cur_row, scr_row;
27 int drawn_row, drawn_col;
28 int count0, count1, vi_mov_flag;
29 unsigned screen_height, screen_width;
30 char vi_reg, *last_search;
31 struct str_line {
32 int alloc;
33 int len;
34 char *data;
35 } *il;
36 size_t screen, cursor;
37
38 struct yank_buf {
39 char reg;
40 int alloc;
41 char* data;
42 } yank;
43
44 int modified;
45 size_t filesize;
46
47
48
49
50
51 struct block_list {
52 struct block_list *next, *prev;
53 struct mem_block {
54 size_t size;
55 size_t len;
56 enum alloc_flag {
57 MMAP,
58 HEAP,
59 STACK,
60 } alloc;
61 const char *data;
62 } *node;
63 } *text;
64
65
66
67
68
69
70
71
72 struct slice_list {
73 struct slice_list *next, *prev;
74 struct slice {
75 size_t len;
76 const char *data;
77 } *node;
78 } *slices;
79)
80
81static const char *blank = " \n\r\t";
82static const char *specials = ",.:;=-+*/(){}<>[]!@#$%^&|\\?\"\'";
83
84
85static int utf8_lnw(int *width, char *s, int bytes)
86{
87 unsigned wc;
88 int length = 1;
89
90 if (*s == '\t') *width = TT.tabstop;
91 else {
92 length = utf8towc(&wc, s, bytes);
93 if (length < 1) length = 0, *width = 0;
94 else *width = wcwidth(wc);
95 }
96 return length;
97}
98
99static int utf8_dec(char key, char *utf8_scratch, int *sta_p)
100{
101 int len = 0;
102 char *c = utf8_scratch;
103 c[*sta_p] = key;
104 if (!(*sta_p)) *c = key;
105 if (*c < 0x7F) { *sta_p = 1; return 1; }
106 if ((*c & 0xE0) == 0xc0) len = 2;
107 else if ((*c & 0xF0) == 0xE0 ) len = 3;
108 else if ((*c & 0xF8) == 0xF0 ) len = 4;
109 else {*sta_p = 0; return 0; }
110
111 (*sta_p)++;
112
113 if (*sta_p == 1) return 0;
114 if ((c[*sta_p-1] & 0xc0) != 0x80) {*sta_p = 0; return 0; }
115
116 if (*sta_p == len) { c[(*sta_p)] = 0; return 1; }
117
118 return 0;
119}
120
121static char* utf8_last(char* str, int size)
122{
123 char* end = str+size;
124 int pos = size, len, width = 0;
125 for (;pos >= 0; end--, pos--) {
126 len = utf8_lnw(&width, end, size-pos);
127 if (len && width) return end;
128 }
129 return 0;
130}
131
132struct double_list *dlist_add_before(struct double_list **head,
133 struct double_list **list, char *data)
134{
135 struct double_list *new = xmalloc(sizeof(struct double_list));
136 new->data = data;
137 if (*list == *head) *head = new;
138
139 dlist_add_nomalloc(list, new);
140 return new;
141}
142
143struct double_list *dlist_add_after(struct double_list **head,
144 struct double_list **list, char *data)
145{
146 struct double_list *new = xmalloc(sizeof(struct double_list));
147 new->data = data;
148
149 if (*list) {
150 new->prev = *list;
151 new->next = (*list)->next;
152 (*list)->next->prev = new;
153 (*list)->next = new;
154 } else *head = *list = new->next = new->prev = new;
155 return new;
156}
157
158
159
160
161
162
163
164
165static int insert_str(const char *data, size_t offset, size_t size, size_t len,
166 enum alloc_flag type)
167{
168 struct mem_block *b = xmalloc(sizeof(struct mem_block));
169 struct slice *next = xmalloc(sizeof(struct slice));
170 struct slice_list *s = TT.slices;
171 b->size = size;
172 b->len = len;
173 b->alloc = type;
174 b->data = data;
175 next->len = len;
176 next->data = data;
177
178
179 TT.text = (struct block_list *)dlist_add((struct double_list **)&TT.text,
180 (char *)b);
181
182 if (!s) {
183 TT.slices = (struct slice_list *)dlist_add(
184 (struct double_list **)&TT.slices,
185 (char *)next);
186 } else {
187 size_t pos = 0;
188
189 do {
190 if (pos<=offset && pos+s->node->len>offset) break;
191 pos += s->node->len;
192 s = s->next;
193 if (s == TT.slices) return -1;
194 } while (1);
195
196 if (pos+s->node->len>offset && pos!=offset) {
197 struct slice *tail = xmalloc(sizeof(struct slice));
198 tail->len = s->node->len-(offset-pos);
199 tail->data = s->node->data+(offset-pos);
200 s->node->len = offset-pos;
201
202 s = (struct slice_list *)dlist_add_after(
203 (struct double_list **)&TT.slices,
204 (struct double_list **)&s,
205 (char *)tail);
206
207 s = (struct slice_list *)dlist_add_before(
208 (struct double_list **)&TT.slices,
209 (struct double_list **)&s,
210 (char *)next);
211 } else if (pos==offset) {
212
213 s = (struct slice_list *)dlist_add_before(
214 (struct double_list **)&TT.slices,
215 (struct double_list **)&s,
216 (char *)next);
217 } else {
218
219 s = (struct slice_list *)dlist_add_after((struct double_list **)&TT.slices,
220 (struct double_list **)&s,
221 (char *)next);
222 }
223 }
224 return 0;
225}
226
227
228
229static int cut_str(size_t offset, size_t len)
230{
231 struct slice_list *e, *s = TT.slices;
232 size_t end = offset+len;
233 size_t epos, spos = 0;
234 if (!s) return -1;
235
236
237 for (;;) {
238 if (spos<=offset && spos+s->node->len>offset) break;
239 spos += s->node->len;
240 s = s->next;
241
242 if (s == TT.slices) return -1;
243 }
244
245 for (e = s, epos = spos; ; ) {
246 if (epos<=end && epos+e->node->len>end) break;
247 epos += e->node->len;
248 e = e->next;
249
250 if (e == TT.slices) return -1;
251 }
252
253 for (;;) {
254 if (spos == offset && ( end >= spos+s->node->len)) {
255
256 spos += s->node->len;
257 offset += s->node->len;
258 s = dlist_pop(&s);
259 if (s == TT.slices) TT.slices = s->next;
260
261 } else if (spos < offset && ( end >= spos+s->node->len)) {
262
263 size_t clip = s->node->len - (offset - spos);
264 offset = spos+s->node->len;
265 spos += s->node->len;
266 s->node->len -= clip;
267 } else if (spos == offset && s == e) {
268
269 size_t clip = end - offset;
270 s->node->len -= clip;
271 s->node->data += clip;
272 break;
273 } else {
274
275 struct slice *tail = xmalloc(sizeof(struct slice));
276 size_t clip = end-offset;
277 tail->len = s->node->len-(offset-spos)-clip;
278 tail->data = s->node->data+(offset-spos)+clip;
279 s->node->len = offset-spos;
280 s = (struct slice_list *)dlist_add_after(
281 (struct double_list **)&TT.slices,
282 (struct double_list **)&s,
283 (char *)tail);
284 break;
285 }
286 if (s == e) break;
287
288 s = s->next;
289 }
290
291 return 0;
292}
293
294
295static struct slice_list *slice_offset(size_t *start, size_t offset)
296{
297 struct slice_list *s = TT.slices;
298 size_t spos = 0;
299
300
301 for ( ;s ; ) {
302 if (spos<=offset && spos+s->node->len>offset) break;
303
304 spos += s->node->len;
305 s = s->next;
306
307 if (s == TT.slices) s = 0;
308 }
309 if (s) *start = spos;
310 return s;
311}
312
313static size_t text_strchr(size_t offset, char c)
314{
315 struct slice_list *s = TT.slices;
316 size_t epos, spos = 0;
317 int i = 0;
318
319
320 if (!(s = slice_offset(&spos, offset))) return SIZE_MAX;
321
322 i = offset-spos;
323 epos = spos+i;
324 do {
325 for (; i < s->node->len; i++, epos++)
326 if (s->node->data[i] == c) return epos;
327 s = s->next;
328 i = 0;
329 } while (s != TT.slices);
330
331 return SIZE_MAX;
332}
333
334static size_t text_strrchr(size_t offset, char c)
335{
336 struct slice_list *s = TT.slices;
337 size_t epos, spos = 0;
338 int i = 0;
339
340
341 if (!(s = slice_offset(&spos, offset))) return SIZE_MAX;
342
343 i = offset-spos;
344 epos = spos+i;
345 do {
346 for (; i >= 0; i--, epos--)
347 if (s->node->data[i] == c) return epos;
348 s = s->prev;
349 i = s->node->len-1;
350 } while (s != TT.slices->prev);
351
352 return SIZE_MAX;
353}
354
355static size_t text_filesize()
356{
357 struct slice_list *s = TT.slices;
358 size_t pos = 0;
359 if (s) do {
360
361 pos += s->node->len;
362 s = s->next;
363
364 } while (s != TT.slices);
365
366 return pos;
367}
368
369static int text_count(size_t start, size_t end, char c)
370{
371 struct slice_list *s = TT.slices;
372 size_t i, count = 0, spos = 0;
373 if (!(s = slice_offset(&spos, start))) return 0;
374 i = start-spos;
375 if (s) do {
376 for (; i < s->node->len && spos+i<end; i++)
377 if (s->node->data[i] == c) count++;
378 if (spos+i>=end) return count;
379
380 spos += s->node->len;
381 i = 0;
382 s = s->next;
383
384 } while (s != TT.slices);
385
386 return count;
387}
388
389static char text_byte(size_t offset)
390{
391 struct slice_list *s = TT.slices;
392 size_t spos = 0;
393
394 if (!(s = slice_offset(&spos, offset))) return 0;
395 return s->node->data[offset-spos];
396}
397
398
399
400static int text_codepoint(char *dest, size_t offset)
401{
402 char scratch[8] = {0};
403 int state = 0, finished = 0;
404
405 for (;!(finished = utf8_dec(text_byte(offset), scratch, &state)); offset++)
406 if (!state) return -1;
407
408 if (!finished && !state) return -1;
409 if (dest) memcpy(dest, scratch, 8);
410
411 return strlen(scratch);
412}
413
414static size_t text_sol(size_t offset)
415{
416 size_t pos;
417 if (!TT.filesize || !offset) return 0;
418 else if (TT.filesize <= offset) return TT.filesize-1;
419 else if ((pos = text_strrchr(offset-1, '\n')) == SIZE_MAX) return 0;
420 else if (pos < offset) return pos+1;
421 return offset;
422}
423
424static size_t text_eol(size_t offset)
425{
426 if (!TT.filesize) offset = 1;
427 else if (TT.filesize <= offset) return TT.filesize-1;
428 else if ((offset = text_strchr(offset, '\n')) == SIZE_MAX)
429 return TT.filesize-1;
430 return offset;
431}
432
433static size_t text_nsol(size_t offset)
434{
435 offset = text_eol(offset);
436 if (text_byte(offset) == '\n') offset++;
437 if (offset >= TT.filesize) offset--;
438 return offset;
439}
440
441static size_t text_psol(size_t offset)
442{
443 offset = text_sol(offset);
444 if (offset) offset--;
445 if (offset && text_byte(offset-1) != '\n') offset = text_sol(offset-1);
446 return offset;
447}
448
449static size_t text_getline(char *dest, size_t offset, size_t max_len)
450{
451 struct slice_list *s = TT.slices;
452 size_t end, spos = 0;
453 int i, j = 0;
454
455 if (dest) *dest = 0;
456
457 if (!s) return 0;
458 if ((end = text_strchr(offset, '\n')) == SIZE_MAX)
459 if ((end = TT.filesize) > offset+max_len) return 0;
460
461
462 if (!(s = slice_offset(&spos, offset))) return 0;
463
464 i = offset-spos;
465 j = end-offset+1;
466 if (dest) do {
467 for (; i < s->node->len && j; i++, j--, dest++)
468 *dest = s->node->data[i];
469 s = s->next;
470 i = 0;
471 } while (s != TT.slices && j);
472
473 if (dest) *dest = 0;
474
475 return end-offset;
476}
477
478
479
480
481
482
483static size_t text_strstr(size_t offset, char *str)
484{
485 size_t bytes, pos = offset;
486 char *s = 0;
487 do {
488 bytes = text_getline(toybuf, pos, ARRAY_LEN(toybuf));
489 if (!bytes) pos++;
490 else if ((s = strstr(toybuf, str))) return pos+(s-toybuf);
491 else pos += bytes;
492 } while (pos < TT.filesize);
493
494 return SIZE_MAX;
495}
496
497static void block_list_free(void *node)
498{
499 struct block_list *d = node;
500
501 if (d->node->alloc == HEAP) free((void *)d->node->data);
502 else if (d->node->alloc == MMAP) munmap((void *)d->node->data, d->node->size);
503
504 free(d->node);
505 free(d);
506}
507
508static void linelist_unload()
509{
510 llist_traverse((void *)TT.slices, llist_free_double);
511 llist_traverse((void *)TT.text, block_list_free);
512 TT.slices = 0, TT.text = 0;
513}
514
515static int linelist_load(char *filename)
516{
517 if (!filename) filename = (char*)*toys.optargs;
518
519 if (filename) {
520 int fd = open(filename, O_RDONLY);
521 long long size;
522
523 if (fd == -1 || !(size = fdlength(fd))) {
524 insert_str("", 0, 0, 0, STACK);
525 TT.filesize = 0;
526
527 return 0;
528 }
529 insert_str(xmmap(0, size, PROT_READ, MAP_SHARED, fd, 0), 0, size,size,MMAP);
530 xclose(fd);
531 TT.filesize = text_filesize();
532 }
533
534 return 1;
535}
536
537static void write_file(char *filename)
538{
539 struct slice_list *s = TT.slices;
540 struct stat st;
541 int fd = 0;
542 if (!s) return;
543
544 if (!filename) filename = (char*)*toys.optargs;
545
546 sprintf(toybuf, "%s.swp", filename);
547
548 if ( (fd = xopen(toybuf, O_WRONLY | O_CREAT | O_TRUNC)) <0) return;
549
550 do {
551 xwrite(fd, (void *)s->node->data, s->node->len );
552 s = s->next;
553 } while (s != TT.slices);
554
555 linelist_unload();
556
557 xclose(fd);
558 if (!stat(filename, &st)) chmod(toybuf, st.st_mode);
559 else chmod(toybuf, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
560 xrename(toybuf, filename);
561 linelist_load(filename);
562}
563
564
565
566static void check_cursor_bounds()
567{
568 char buf[8] = {0};
569 int len, width = 0;
570 if (!TT.filesize) TT.cursor = 0;
571
572 for (;;) {
573 if (TT.cursor < 1) {
574 TT.cursor = 0;
575 return;
576 } else if (TT.cursor >= TT.filesize-1) {
577 TT.cursor = TT.filesize-1;
578 return;
579 }
580 if ((len = text_codepoint(buf, TT.cursor)) < 1) {
581 TT.cursor--;
582 continue;
583 }
584 if (utf8_lnw(&width, buf, len) && width) break;
585 else TT.cursor--;
586 }
587}
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603static void adjust_screen_buffer()
604{
605 size_t c, s;
606 TT.cur_row = 0, TT.scr_row = 0;
607 if (!TT.cursor) {
608 TT.screen = 0;
609 TT.vi_mov_flag = 0x20000000;
610 return;
611 } else if (TT.screen > (1<<18) || TT.cursor > (1<<18)) {
612
613
614 TT.screen = text_strrchr(TT.cursor-1, '\n')+1;
615 TT.vi_mov_flag = 0x20000000;
616 return;
617 }
618
619 s = text_count(0, TT.screen, '\n');
620 c = text_count(0, TT.cursor, '\n');
621 if (s >= c) {
622 TT.screen = text_strrchr(TT.cursor-1, '\n')+1;
623 s = c;
624 TT.vi_mov_flag = 0x20000000;
625 } else {
626 int distance = c-s+1;
627 if (distance > (int)TT.screen_height) {
628 int n, adj = distance-TT.screen_height;
629 TT.vi_mov_flag = 0x20000000;
630 for (;adj; adj--, s++)
631 if ((n = text_strchr(TT.screen, '\n'))+1 > TT.screen)
632 TT.screen = n+1;
633 }
634 }
635
636 TT.scr_row = s;
637 TT.cur_row = c;
638
639}
640
641
642
643
644static int vi_yank(char reg, size_t from, int flags)
645{
646 size_t start = from, end = TT.cursor;
647 char *str;
648
649 memset(TT.yank.data, 0, TT.yank.alloc);
650 if (TT.vi_mov_flag&0x80000000) start = TT.cursor, end = from;
651 else TT.cursor = start;
652
653 if (TT.yank.alloc < end-from) {
654 size_t new_bounds = (1+end-from)/1024;
655 new_bounds += ((1+end-from)%1024) ? 1 : 0;
656 new_bounds *= 1024;
657 TT.yank.data = xrealloc(TT.yank.data, new_bounds);
658 TT.yank.alloc = new_bounds;
659 }
660
661
662 for (str = TT.yank.data ; start<end; start++, str++) *str = text_byte(start);
663
664 *str = 0;
665
666 return 1;
667}
668
669static int vi_delete(char reg, size_t from, int flags)
670{
671 size_t start = from, end = TT.cursor;
672
673 vi_yank(reg, from, flags);
674
675 if (TT.vi_mov_flag&0x80000000)
676 start = TT.cursor, end = from;
677
678
679 if (TT.vi_mov_flag&2) {
680
681 }
682
683 cut_str(start, end-start);
684
685
686 TT.cursor = start;
687 TT.filesize = text_filesize();
688
689
690 TT.vi_mov_flag |= 0x30000000;
691
692 return 1;
693}
694
695static int vi_change(char reg, size_t to, int flags)
696{
697 vi_delete(reg, to, flags);
698 TT.vi_mode = 2;
699 return 1;
700}
701
702static int cur_left(int count0, int count1, char *unused)
703{
704 int count = count0*count1;
705 TT.vi_mov_flag |= 0x80000000;
706 for (;count && TT.cursor; count--) {
707 TT.cursor--;
708 if (text_byte(TT.cursor) == '\n') TT.cursor++;
709 check_cursor_bounds();
710 }
711 return 1;
712}
713
714static int cur_right(int count0, int count1, char *unused)
715{
716 int count = count0*count1, len, width = 0;
717 char buf[8] = {0};
718
719 for (;count; count--) {
720 len = text_codepoint(buf, TT.cursor);
721
722 if (*buf == '\n') break;
723 else if (len > 0) TT.cursor += len;
724 else TT.cursor++;
725
726 for (;TT.cursor < TT.filesize;) {
727 if ((len = text_codepoint(buf, TT.cursor)) < 1) {
728 TT.cursor++;
729 continue;
730 }
731
732 if (utf8_lnw(&width, buf, len) && width) break;
733 else TT.cursor += len;
734 }
735 }
736 check_cursor_bounds();
737 return 1;
738}
739
740
741static int cur_up(int count0, int count1, char *unused)
742{
743 int count = count0*count1;
744 for (;count--;) TT.cursor = text_psol(TT.cursor);
745
746 TT.vi_mov_flag |= 0x80000000;
747 check_cursor_bounds();
748 return 1;
749}
750
751
752static int cur_down(int count0, int count1, char *unused)
753{
754 int count = count0*count1;
755 for (;count--;) TT.cursor = text_nsol(TT.cursor);
756 check_cursor_bounds();
757 return 1;
758}
759
760static int vi_H(int count0, int count1, char *unused)
761{
762 TT.cursor = text_sol(TT.screen);
763 return 1;
764}
765
766static int vi_L(int count0, int count1, char *unused)
767{
768 TT.cursor = text_sol(TT.screen);
769 cur_down(TT.screen_height-1, 1, 0);
770 return 1;
771}
772
773static int vi_M(int count0, int count1, char *unused)
774{
775 TT.cursor = text_sol(TT.screen);
776 cur_down(TT.screen_height/2, 1, 0);
777 return 1;
778}
779
780static int search_str(char *s)
781{
782 size_t pos = text_strstr(TT.cursor+1, s);
783
784 if (TT.last_search != s) {
785 free(TT.last_search);
786 TT.last_search = xstrdup(s);
787 }
788
789 if (pos != SIZE_MAX) TT.cursor = pos;
790 check_cursor_bounds();
791 return 0;
792}
793
794static int vi_yy(char reg, int count0, int count1)
795{
796 size_t history = TT.cursor;
797 size_t pos = text_sol(TT.cursor);
798 TT.vi_mov_flag |= 0x4;
799
800 for (;count0; count0--) TT.cursor = text_nsol(TT.cursor);
801
802 vi_yank(reg, pos, 0);
803
804 TT.cursor = history;
805 return 1;
806}
807
808static int vi_dd(char reg, int count0, int count1)
809{
810 size_t pos = text_sol(TT.cursor);
811 TT.vi_mov_flag |= 0x30000000;
812
813 for (;count0; count0--) TT.cursor = text_nsol(TT.cursor);
814
815 if (pos == TT.cursor && TT.filesize) pos--;
816 vi_delete(reg, pos, 0);
817 check_cursor_bounds();
818 return 1;
819}
820
821static int vi_x(char reg, int count0, int count1)
822{
823 size_t from = TT.cursor;
824
825 if (text_byte(TT.cursor) == '\n') {
826 cur_left(count0-1, 1, 0);
827 }
828 else {
829 cur_right(count0-1, 1, 0);
830 if (text_byte(TT.cursor) == '\n') TT.vi_mov_flag |= 2;
831 else cur_right(1, 1, 0);
832 }
833
834 vi_delete(reg, from, 0);
835 check_cursor_bounds();
836 return 1;
837}
838
839static int vi_movw(int count0, int count1, char *unused)
840{
841 int count = count0*count1;
842 while (count--) {
843 char c = text_byte(TT.cursor);
844 do {
845 if (TT.cursor > TT.filesize-1) break;
846
847 if (c == '\n') {
848 if (++TT.cursor > TT.filesize-1) break;
849 if ((c = text_byte(TT.cursor)) == '\n') break;
850 continue;
851 } else if (strchr(blank, c)) do {
852 if (++TT.cursor > TT.filesize-1) break;
853 c = text_byte(TT.cursor);
854 } while (strchr(blank, c));
855
856 else if (strchr(specials, c)) do {
857 if (++TT.cursor > TT.filesize-1) break;
858 c = text_byte(TT.cursor);
859 } while (strchr(specials, c));
860
861 else do {
862 if (++TT.cursor > TT.filesize-1) break;
863 c = text_byte(TT.cursor);
864 } while (c && !strchr(blank, c) && !strchr(specials, c));
865
866 } while (strchr(blank, c) && c != '\n');
867 }
868 check_cursor_bounds();
869 return 1;
870}
871
872static int vi_movb(int count0, int count1, char *unused)
873{
874 int count = count0*count1;
875 int type = 0;
876 char c;
877 while (count--) {
878 c = text_byte(TT.cursor);
879 do {
880 if (!TT.cursor) break;
881
882 if (strchr(blank, c)) do {
883 if (!--TT.cursor) break;
884 c = text_byte(TT.cursor);
885 } while (strchr(blank, c));
886
887 else if (strchr(specials, c)) do {
888 if (!--TT.cursor) break;
889 type = 0;
890 c = text_byte(TT.cursor);
891 } while (strchr(specials, c));
892
893 else do {
894 if (!--TT.cursor) break;
895 type = 1;
896 c = text_byte(TT.cursor);
897 } while (!strchr(blank, c) && !strchr(specials, c));
898
899 } while (strchr(blank, c));
900 }
901
902 for (;TT.cursor; TT.cursor--) {
903 c = text_byte(TT.cursor-1);
904 if (type && !strchr(blank, c) && !strchr(specials, c)) break;
905 else if (!type && !strchr(specials, c)) break;
906 }
907
908 TT.vi_mov_flag |= 0x80000000;
909 check_cursor_bounds();
910 return 1;
911}
912
913static int vi_move(int count0, int count1, char *unused)
914{
915 int count = count0*count1;
916 int type = 0;
917 char c;
918
919 if (count>1) vi_movw(count-1, 1, unused);
920
921 c = text_byte(TT.cursor);
922 if (strchr(specials, c)) type = 1;
923 TT.cursor++;
924 for (;TT.cursor < TT.filesize-1; TT.cursor++) {
925 c = text_byte(TT.cursor+1);
926 if (!type && (strchr(blank, c) || strchr(specials, c))) break;
927 else if (type && !strchr(specials, c)) break;
928 }
929
930 TT.vi_mov_flag |= 2;
931 check_cursor_bounds();
932 return 1;
933}
934
935
936static void i_insert(char *str, int len)
937{
938 if (!str || !len) return;
939
940 insert_str(xstrdup(str), TT.cursor, len, len, HEAP);
941 TT.cursor += len;
942 TT.filesize = text_filesize();
943 TT.vi_mov_flag |= 0x30000000;
944}
945
946static int vi_zero(int count0, int count1, char *unused)
947{
948 TT.cursor = text_sol(TT.cursor);
949 TT.cur_col = 0;
950 TT.vi_mov_flag |= 0x80000000;
951 return 1;
952}
953
954static int vi_dollar(int count0, int count1, char *unused)
955{
956 size_t new = text_strchr(TT.cursor, '\n');
957
958 if (new != TT.cursor) {
959 TT.cursor = new - 1;
960 TT.vi_mov_flag |= 2;
961 check_cursor_bounds();
962 }
963 return 1;
964}
965
966static void vi_eol()
967{
968 TT.cursor = text_strchr(TT.cursor, '\n');
969 check_cursor_bounds();
970}
971
972static void ctrl_b()
973{
974 int i;
975
976 for (i=0; i<TT.screen_height-2; ++i) {
977 TT.screen = text_psol(TT.screen);
978
979 TT.cursor = text_psol(TT.screen);
980 }
981}
982
983static void ctrl_f()
984{
985 int i;
986
987 for (i=0; i<TT.screen_height-2; ++i) TT.screen = text_nsol(TT.screen);
988
989 if (TT.screen > TT.cursor) TT.cursor = TT.screen;
990}
991
992static void ctrl_e()
993{
994 TT.screen = text_nsol(TT.screen);
995
996 if (TT.screen > TT.cursor) TT.cursor = TT.screen;
997}
998
999static void ctrl_y()
1000{
1001 TT.screen = text_psol(TT.screen);
1002
1003 TT.cursor = text_psol(TT.cursor);
1004
1005}
1006
1007
1008static int vi_push(char reg, int count0, int count1)
1009{
1010
1011
1012
1013 size_t history = TT.cursor;
1014 char *start = TT.yank.data;
1015 char *eol = strchr(start, '\n');
1016
1017 if (start[strlen(start)-1] == '\n') {
1018 if ((TT.cursor = text_strchr(TT.cursor, '\n')) == SIZE_MAX)
1019 TT.cursor = TT.filesize;
1020 else TT.cursor = text_nsol(TT.cursor);
1021 } else cur_right(1, 1, 0);
1022
1023 i_insert(start, strlen(start));
1024 if (eol) {
1025 TT.vi_mov_flag |= 0x10000000;
1026 TT.cursor = history;
1027 }
1028
1029 return 1;
1030}
1031
1032static int vi_find_c(int count0, int count1, char *symbol)
1033{
1034
1035 size_t pos = text_strchr(TT.cursor, *symbol);
1036 if (pos != SIZE_MAX) TT.cursor = pos;
1037 return 1;
1038}
1039
1040static int vi_find_cb(int count0, int count1, char *symbol)
1041{
1042
1043 size_t pos = text_strrchr(TT.cursor, *symbol);
1044 if (pos != SIZE_MAX) TT.cursor = pos;
1045 return 1;
1046}
1047
1048
1049static int vi_go(int count0, int count1, char *symbol)
1050{
1051 size_t prev_cursor = TT.cursor;
1052 int count = count0*count1-1;
1053 TT.cursor = 0;
1054
1055 if (TT.vi_mov_flag&0x40000000 && (TT.cursor = TT.filesize) > 0)
1056 TT.cursor = text_sol(TT.cursor-1);
1057 else if (count) {
1058 size_t next = 0;
1059 for ( ;count && (next = text_strchr(next+1, '\n')) != SIZE_MAX; count--)
1060 TT.cursor = next;
1061 TT.cursor++;
1062 }
1063
1064 check_cursor_bounds();
1065 if (prev_cursor > TT.cursor) TT.vi_mov_flag |= 0x80000000;
1066
1067 return 1;
1068}
1069
1070static int vi_o(char reg, int count0, int count1)
1071{
1072 TT.cursor = text_eol(TT.cursor);
1073 insert_str(xstrdup("\n"), TT.cursor++, 1, 1, HEAP);
1074 TT.vi_mov_flag |= 0x30000000;
1075 TT.vi_mode = 2;
1076 return 1;
1077}
1078
1079static int vi_O(char reg, int count0, int count1)
1080{
1081 TT.cursor = text_psol(TT.cursor);
1082 return vi_o(reg, count0, count1);
1083}
1084
1085static int vi_D(char reg, int count0, int count1)
1086{
1087 size_t pos = TT.cursor;
1088 if (!count0) return 1;
1089 vi_eol();
1090 vi_delete(reg, pos, 0);
1091 if (--count0) vi_dd(reg, count0, 1);
1092
1093 check_cursor_bounds();
1094 return 1;
1095}
1096
1097static int vi_I(char reg, int count0, int count1)
1098{
1099 TT.cursor = text_sol(TT.cursor);
1100 TT.vi_mode = 2;
1101 return 1;
1102}
1103
1104static int vi_join(char reg, int count0, int count1)
1105{
1106 size_t next;
1107 while (count0--) {
1108
1109 if ((next = text_strchr(TT.cursor, '\n')) == SIZE_MAX) break;
1110 TT.cursor = next+1;
1111 vi_delete(reg, TT.cursor-1, 0);
1112 }
1113 return 1;
1114}
1115
1116static int vi_find_next(char reg, int count0, int count1)
1117{
1118 if (TT.last_search) search_str(TT.last_search);
1119 return 1;
1120}
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137struct vi_cmd_param {
1138 const char* cmd;
1139 unsigned flags;
1140 int (*vi_cmd)(char, size_t, int);
1141};
1142struct vi_mov_param {
1143 const char* mov;
1144 unsigned flags;
1145 int (*vi_mov)(int, int, char*);
1146};
1147
1148struct vi_special_param {
1149 const char *cmd;
1150 int (*vi_special)(char, int, int);
1151};
1152struct vi_special_param vi_special[] =
1153{
1154 {"D", &vi_D},
1155 {"I", &vi_I},
1156 {"J", &vi_join},
1157 {"O", &vi_O},
1158 {"n", &vi_find_next},
1159 {"o", &vi_o},
1160 {"p", &vi_push},
1161 {"x", &vi_x},
1162 {"dd", &vi_dd},
1163 {"yy", &vi_yy},
1164};
1165
1166
1167
1168struct vi_mov_param vi_movs[] =
1169{
1170 {"0", 0, &vi_zero},
1171 {"b", 0, &vi_movb},
1172 {"e", 0, &vi_move},
1173 {"G", 0, &vi_go},
1174 {"H", 0, &vi_H},
1175 {"h", 0, &cur_left},
1176 {"j", 0, &cur_down},
1177 {"k", 0, &cur_up},
1178 {"L", 0, &vi_L},
1179 {"l", 0, &cur_right},
1180 {"M", 0, &vi_M},
1181 {"w", 0, &vi_movw},
1182 {"$", 0, &vi_dollar},
1183 {"f", 1, &vi_find_c},
1184 {"F", 1, &vi_find_cb},
1185};
1186
1187
1188
1189
1190
1191struct vi_cmd_param vi_cmds[] =
1192{
1193 {"c", 1, &vi_change},
1194 {"d", 1, &vi_delete},
1195 {"y", 1, &vi_yank},
1196};
1197
1198static int run_vi_cmd(char *cmd)
1199{
1200 int i = 0, val = 0;
1201 char *cmd_e;
1202 int (*vi_cmd)(char, size_t, int) = 0;
1203 int (*vi_mov)(int, int, char*) = 0;
1204
1205 TT.count0 = 0, TT.count1 = 0, TT.vi_mov_flag = 0;
1206 TT.vi_reg = '"';
1207
1208 if (*cmd == '"') {
1209 cmd++;
1210 TT.vi_reg = *cmd;
1211 cmd++;
1212 }
1213 errno = 0;
1214 val = strtol(cmd, &cmd_e, 10);
1215 if (errno || val == 0) val = 1, TT.vi_mov_flag |= 0x40000000;
1216 else cmd = cmd_e;
1217 TT.count0 = val;
1218
1219 for (i = 0; i < ARRAY_LEN(vi_special); i++) {
1220 if (strstr(cmd, vi_special[i].cmd)) {
1221 return vi_special[i].vi_special(TT.vi_reg, TT.count0, TT.count1);
1222 }
1223 }
1224
1225 for (i = 0; i < ARRAY_LEN(vi_cmds); i++) {
1226 if (!strncmp(cmd, vi_cmds[i].cmd, strlen(vi_cmds[i].cmd))) {
1227 vi_cmd = vi_cmds[i].vi_cmd;
1228 cmd += strlen(vi_cmds[i].cmd);
1229 break;
1230 }
1231 }
1232 errno = 0;
1233 val = strtol(cmd, &cmd_e, 10);
1234 if (errno || val == 0) val = 1;
1235 else cmd = cmd_e;
1236 TT.count1 = val;
1237
1238 for (i = 0; i < ARRAY_LEN(vi_movs); i++) {
1239 if (!strncmp(cmd, vi_movs[i].mov, strlen(vi_movs[i].mov))) {
1240 vi_mov = vi_movs[i].vi_mov;
1241 TT.vi_mov_flag |= vi_movs[i].flags;
1242 cmd++;
1243 if (TT.vi_mov_flag&1 && !(*cmd)) return 0;
1244 break;
1245 }
1246 }
1247 if (vi_mov) {
1248 int prev_cursor = TT.cursor;
1249 if (vi_mov(TT.count0, TT.count1, cmd)) {
1250 if (vi_cmd) return (vi_cmd(TT.vi_reg, prev_cursor, TT.vi_mov_flag));
1251 else return 1;
1252 } else return 0;
1253 }
1254 return 0;
1255}
1256
1257
1258static int run_ex_cmd(char *cmd)
1259{
1260 if (cmd[0] == '/') {
1261 search_str(&cmd[1]);
1262 } else if (cmd[0] == '?') {
1263
1264 } else if (cmd[0] == ':') {
1265 if (!strcmp(&cmd[1], "q") || !strcmp(&cmd[1], "q!")) {
1266
1267
1268 return -1;
1269 }
1270 else if (strstr(&cmd[1], "wq")) {
1271 write_file(0);
1272 return -1;
1273 }
1274 else if (strstr(&cmd[1], "w")) {
1275 write_file(0);
1276 return 1;
1277 }
1278 else if (strstr(&cmd[1], "set list")) {
1279 TT.list = 1;
1280 TT.vi_mov_flag |= 0x30000000;
1281 return 1;
1282 }
1283 else if (strstr(&cmd[1], "set nolist")) {
1284 TT.list = 0;
1285 TT.vi_mov_flag |= 0x30000000;
1286 return 1;
1287 }
1288 }
1289 return 0;
1290
1291}
1292
1293static int vi_crunch(FILE *out, int cols, int wc)
1294{
1295 int ret = 0;
1296 if (wc < 32 && TT.list) {
1297 xputsn("\e[1m");
1298 ret = crunch_escape(out,cols,wc);
1299 xputsn("\e[m");
1300 } else if (wc == 0x09) {
1301 if (out) {
1302 int i = TT.tabstop;
1303 for (;i--;) fputs(" ", out);
1304 }
1305 ret = TT.tabstop;
1306 } else if (wc == '\n') return 0;
1307 return ret;
1308}
1309
1310
1311
1312static int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore,
1313 int (*escout)(FILE *out, int cols, int wc))
1314{
1315 int columns = 0, col, bytes;
1316 char *start, *end;
1317 unsigned wc;
1318
1319 for (end = start = *str; *end && n>0; columns += col, end += bytes, n -= bytes) {
1320 if ((bytes = utf8towc(&wc, end, 4))>0 && (col = wcwidth(wc))>=0) {
1321 if (!escmore || wc>255 || !strchr(escmore, wc)) {
1322 if (width-columns<col) break;
1323 if (out) fwrite(end, bytes, 1, out);
1324
1325 continue;
1326 }
1327 }
1328
1329 if (bytes<1) {
1330 bytes = 1;
1331 wc = *end;
1332 }
1333 col = width-columns;
1334 if (col<1) break;
1335 if (escout) {
1336 if ((col = escout(out, col, wc))<0) break;
1337 } else if (out) fwrite(end, 1, bytes, out);
1338 }
1339 *str = end;
1340
1341 return columns;
1342}
1343
1344static void draw_page()
1345{
1346 unsigned y = 0;
1347 int x = 0;
1348
1349 char *line = 0, *end = 0;
1350 int bytes = 0;
1351
1352
1353 int cy_scr = 0, cx_scr = 0;
1354
1355
1356 int aw = 0, iw = 0, clip = 0, margin = 8;
1357
1358 int scroll = 0, redraw = 0;
1359
1360 int SSOL, SOL;
1361
1362
1363 adjust_screen_buffer();
1364
1365 redraw = (TT.vi_mov_flag & 0x30000000)>>28;
1366
1367 scroll = TT.drawn_row-TT.scr_row;
1368 if (TT.drawn_row<0 || TT.cur_row<0 || TT.scr_row<0) redraw = 3;
1369 else if (abs(scroll)>TT.screen_height/2) redraw = 3;
1370
1371 xputsn("\e[H");
1372 if (redraw&2) xputsn("\e[2J\e[H");
1373 else if (scroll>0) printf("\e[%dL", scroll);
1374 else if (scroll<0) printf("\e[%dM", -scroll);
1375
1376 SOL = text_sol(TT.cursor);
1377 bytes = text_getline(toybuf, SOL, ARRAY_LEN(toybuf));
1378 line = toybuf;
1379
1380 for (SSOL = TT.screen, y = 0; SSOL < SOL; y++) SSOL = text_nsol(SSOL);
1381
1382 cy_scr = y;
1383
1384
1385
1386
1387 bytes = TT.cursor-SOL;
1388 end = line;
1389
1390
1391 printf("\e[%u;0H\e[2K", y+1);
1392
1393 aw = crunch_nstr(&end, INT_MAX, bytes, 0, "\t\n", vi_crunch);
1394
1395
1396 if (TT.vi_mode == 2 && TT.il->len) {
1397 char* iend = TT.il->data;
1398 x = 0;
1399
1400 iw = crunch_str(&iend, INT_MAX, 0, "\t\n", vi_crunch);
1401 clip = (aw+iw) - TT.screen_width+margin;
1402
1403
1404 if (clip > aw) {
1405 clip -= aw;
1406 iend = TT.il->data;
1407
1408 iw -= crunch_str(&iend, clip, 0, "\t\n", vi_crunch);
1409 x = crunch_str(&iend, iw, stdout, "\t\n", vi_crunch);
1410 } else {
1411 iend = TT.il->data;
1412 end = line;
1413
1414
1415 aw -= crunch_nstr(&end, clip, bytes, 0, "\t\n", vi_crunch);
1416 x = crunch_str(&end, aw, stdout, "\t\n", vi_crunch);
1417 x += crunch_str(&iend, iw, stdout, "\t\n", vi_crunch);
1418 }
1419 }
1420
1421
1422 else if ( aw+margin > TT.screen_width) {
1423 clip = aw-TT.screen_width+margin;
1424 end = line;
1425 aw -= crunch_nstr(&end, clip, bytes, 0, "\t\n", vi_crunch);
1426 x = crunch_str(&end, aw, stdout, "\t\n", vi_crunch);
1427 }
1428 else {
1429 end = line;
1430 x = crunch_nstr(&end, aw, bytes, stdout, "\t\n", vi_crunch);
1431 }
1432 cx_scr = x;
1433 cy_scr = y;
1434 x += crunch_str(&end, TT.screen_width-x, stdout, "\t\n", vi_crunch);
1435
1436
1437
1438 y = 0, SSOL = TT.screen, line = toybuf;
1439 bytes = text_getline(toybuf, SSOL, ARRAY_LEN(toybuf));
1440
1441
1442 if (clip != TT.drawn_col) redraw = 3;
1443
1444 for (; y < TT.screen_height; y++ ) {
1445 int draw_line = 0;
1446 if (SSOL == SOL) {
1447 line = toybuf;
1448 SSOL += bytes+1;
1449 bytes = text_getline(line, SSOL, ARRAY_LEN(toybuf));
1450 continue;
1451 } else if (redraw) draw_line++;
1452 else if (scroll<0 && TT.screen_height-y-1<-scroll)
1453 scroll++, draw_line++;
1454 else if (scroll>0) scroll--, draw_line++;
1455
1456 printf("\e[%u;0H", y+1);
1457 if (draw_line) {
1458 printf("\e[2K");
1459 if (line && strlen(line)) {
1460 aw = crunch_nstr(&line, clip, bytes, 0, "\t\n", vi_crunch);
1461 crunch_str(&line, TT.screen_width-1, stdout, "\t\n", vi_crunch);
1462 if ( *line ) printf("@");
1463 } else printf("\e[2m~\e[m");
1464 }
1465 if (SSOL+bytes < TT.filesize) {
1466 line = toybuf;
1467 SSOL += bytes+1;
1468 bytes = text_getline(line, SSOL, ARRAY_LEN(toybuf));
1469 } else line = 0;
1470 }
1471
1472 TT.drawn_row = TT.scr_row, TT.drawn_col = clip;
1473
1474
1475 printf("\e[%u;0H\e[2K", TT.screen_height+1);
1476 if (TT.vi_mode == 2) printf("\e[1m-- INSERT --\e[m");
1477 if (!TT.vi_mode) {
1478 cx_scr = printf("%s", TT.il->data);
1479 cy_scr = TT.screen_height;
1480 *toybuf = 0;
1481 } else {
1482
1483
1484 sprintf(toybuf, "%zu/%zuC %zu%% %d,%d", TT.cursor, TT.filesize,
1485 (100*TT.cursor)/(TT.filesize ? : 1), TT.cur_row+1, TT.cur_col+1);
1486 if (TT.cur_col != cx_scr) sprintf(toybuf+strlen(toybuf),"-%d", cx_scr+1);
1487 }
1488 printf("\e[%u;%uH%s\e[%u;%uH", TT.screen_height+1,
1489 (int) (1+TT.screen_width-strlen(toybuf)),
1490 toybuf, cy_scr+1, cx_scr+1);
1491 xflush(1);
1492}
1493
1494void vi_main(void)
1495{
1496 char stdout_buf[BUFSIZ];
1497 char keybuf[16] = {0};
1498 char vi_buf[16] = {0};
1499 char utf8_code[8] = {0};
1500 int utf8_dec_p = 0, vi_buf_pos = 0;
1501 FILE *script = FLAG(s) ? xfopen(TT.s, "r") : 0;
1502
1503 TT.il = xzalloc(sizeof(struct str_line));
1504 TT.il->data = xzalloc(80);
1505 TT.yank.data = xzalloc(128);
1506
1507 TT.il->alloc = 80, TT.yank.alloc = 128;
1508
1509 linelist_load(0);
1510
1511 TT.vi_mov_flag = 0x20000000;
1512 TT.vi_mode = 1, TT.tabstop = 8;
1513
1514 TT.screen_width = 80, TT.screen_height = 24;
1515 terminal_size(&TT.screen_width, &TT.screen_height);
1516 TT.screen_height -= 1;
1517
1518
1519 setbuf(stdout, stdout_buf);
1520
1521 xsignal(SIGWINCH, generic_signal);
1522 set_terminal(0, 1, 0, 0);
1523
1524
1525 xputsn("\e[?1049h");
1526
1527 for (;;) {
1528 int key = 0;
1529
1530 draw_page();
1531 if (script) {
1532 key = fgetc(script);
1533 if (key == EOF) {
1534 fclose(script);
1535 script = 0;
1536 key = scan_key(keybuf, -1);
1537 }
1538 } else key = scan_key(keybuf, -1);
1539
1540 if (key == -1) goto cleanup_vi;
1541 else if (key == -3) {
1542 toys.signal = 0;
1543 terminal_size(&TT.screen_width, &TT.screen_height);
1544 TT.screen_height -= 1;
1545 continue;
1546 }
1547
1548
1549 if (TT.vi_mode && key>=256) {
1550 key -= 256;
1551 if (key==KEY_UP) cur_up(1, 1, 0);
1552 else if (key==KEY_DOWN) cur_down(1, 1, 0);
1553 else if (key==KEY_LEFT) cur_left(1, 1, 0);
1554 else if (key==KEY_RIGHT) cur_right(1, 1, 0);
1555 else if (key==KEY_HOME) vi_zero(1, 1, 0);
1556 else if (key==KEY_END) vi_dollar(1, 1, 0);
1557 else if (key==KEY_PGDN) ctrl_f();
1558 else if (key==KEY_PGUP) ctrl_b();
1559 continue;
1560 }
1561
1562 if (TT.vi_mode == 1) {
1563 switch (key) {
1564 case '/':
1565 case '?':
1566 case ':':
1567 TT.vi_mode = 0;
1568 TT.il->data[0]=key;
1569 TT.il->len++;
1570 break;
1571 case 'A':
1572 vi_eol();
1573 TT.vi_mode = 2;
1574 break;
1575 case 'a':
1576 cur_right(1, 1, 0);
1577
1578 case 'i':
1579 TT.vi_mode = 2;
1580 break;
1581 case 'B'-'@':
1582 ctrl_b();
1583 break;
1584 case 'E'-'@':
1585 ctrl_e();
1586 break;
1587 case 'F'-'@':
1588 ctrl_f();
1589 break;
1590 case 'Y'-'@':
1591 ctrl_y();
1592 break;
1593 case 27:
1594 vi_buf[0] = 0;
1595 vi_buf_pos = 0;
1596 break;
1597 default:
1598 if (key > 0x20 && key < 0x7B) {
1599 vi_buf[vi_buf_pos] = key;
1600 vi_buf_pos++;
1601 if (run_vi_cmd(vi_buf)) {
1602 memset(vi_buf, 0, 16);
1603 vi_buf_pos = 0;
1604 }
1605 else if (vi_buf_pos == 16) {
1606 vi_buf_pos = 0;
1607 memset(vi_buf, 0, 16);
1608 }
1609
1610 }
1611
1612 break;
1613 }
1614 } else if (TT.vi_mode == 0) {
1615 switch (key) {
1616 case 0x7F:
1617 case 0x08:
1618 if (TT.il->len > 1) {
1619 TT.il->data[--TT.il->len] = 0;
1620 break;
1621 }
1622
1623 case 27:
1624 TT.vi_mode = 1;
1625 TT.il->len = 0;
1626 memset(TT.il->data, 0, TT.il->alloc);
1627 break;
1628 case 0x0A:
1629 case 0x0D:
1630 if (run_ex_cmd(TT.il->data) == -1)
1631 goto cleanup_vi;
1632 TT.vi_mode = 1;
1633 TT.il->len = 0;
1634 memset(TT.il->data, 0, TT.il->alloc);
1635 break;
1636 default:
1637 if (key >= 0x20 && key < 0x7F) {
1638 if (TT.il->len == TT.il->alloc) {
1639 TT.il->data = realloc(TT.il->data, TT.il->alloc*2);
1640 TT.il->alloc *= 2;
1641 }
1642 TT.il->data[TT.il->len] = key;
1643 TT.il->len++;
1644 }
1645 break;
1646 }
1647 } else if (TT.vi_mode == 2) {
1648 switch (key) {
1649 case 27:
1650 i_insert(TT.il->data, TT.il->len);
1651 cur_left(1, 1, 0);
1652 TT.vi_mode = 1;
1653 TT.il->len = 0;
1654 memset(TT.il->data, 0, TT.il->alloc);
1655 break;
1656 case 0x7F:
1657 case 0x08:
1658 if (TT.il->len) {
1659 char *last = utf8_last(TT.il->data, TT.il->len);
1660 int shrink = strlen(last);
1661 memset(last, 0, shrink);
1662 TT.il->len -= shrink;
1663 }
1664 break;
1665 case 0x0A:
1666 case 0x0D:
1667
1668
1669 TT.il->data[TT.il->len++] = '\n';
1670 i_insert(TT.il->data, TT.il->len);
1671 TT.il->len = 0;
1672 memset(TT.il->data, 0, TT.il->alloc);
1673 break;
1674 default:
1675 if ((key >= 0x20 || key == 0x09) &&
1676 utf8_dec(key, utf8_code, &utf8_dec_p)) {
1677
1678 if (TT.il->len+utf8_dec_p+1 >= TT.il->alloc) {
1679 TT.il->data = realloc(TT.il->data, TT.il->alloc*2);
1680 TT.il->alloc *= 2;
1681 }
1682 strcpy(TT.il->data+TT.il->len, utf8_code);
1683 TT.il->len += utf8_dec_p;
1684 utf8_dec_p = 0;
1685 *utf8_code = 0;
1686
1687 }
1688 break;
1689 }
1690 }
1691 }
1692cleanup_vi:
1693 linelist_unload();
1694 free(TT.il->data), free(TT.il), free(TT.yank.data);
1695 tty_reset();
1696 xputsn("\e[?1049l");
1697}
1698