1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#define FOR_vi
20#include "toys.h"
21
22GLOBALS(
23 int cur_col;
24 int cur_row;
25 unsigned screen_height;
26 unsigned screen_width;
27 int vi_mode;
28 int count0;
29 int count1;
30 int vi_mov_flag;
31 int modified;
32 char vi_reg;
33)
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53struct linestack_show {
54 struct linestack_show *next;
55 long top, left;
56 int x, width, y, height;
57};
58
59static void draw_page();
60static int draw_str_until(int *drawn, char *str, int width, int bytes);
61static void draw_char(char c, int x, int y, int highlight);
62
63static int utf8_lnw(int* width, char* str, int bytes);
64static int utf8_dec(char key, char *utf8_scratch, int *sta_p);
65static int utf8_len(char *str);
66static int utf8_width(char *str, int bytes);
67static int draw_rune(char *c, int x, int y, int highlight);
68static char* utf8_last(char* str, int size);
69
70
71static int cur_left(int count0, int count1, char* unused);
72static int cur_right(int count0, int count1, char* unused);
73static int cur_up(int count0, int count1, char* unused);
74static int cur_down(int count0, int count1, char* unused);
75static void check_cursor_bounds();
76static void adjust_screen_buffer();
77
78
79struct str_line {
80 int alloc_len;
81 int str_len;
82 char *str_data;
83};
84
85
86
87struct linelist {
88 struct linelist *up;
89 struct linelist *down;
90 struct str_line *line;
91};
92
93struct str_line *il;
94struct linelist *text;
95struct linelist *scr_r;
96struct linelist *c_r;
97
98
99void dlist_insert_nomalloc(struct double_list **list, struct double_list *new)
100{
101 if (*list) {
102 new->next = *list;
103 new->prev = (*list)->prev;
104 if ((*list)->prev) (*list)->prev->next = new;
105 (*list)->prev = new;
106 } else *list = new->next = new->prev = new;
107}
108
109
110
111struct double_list *dlist_insert(struct double_list **list, char *data)
112{
113 struct double_list *new = xmalloc(sizeof(struct double_list));
114 new->data = data;
115 dlist_insert_nomalloc(list, new);
116
117 return new;
118}
119
120void linelist_unload()
121{
122
123}
124
125void write_file(char *filename)
126{
127 struct linelist *lst = text;
128 FILE *fp = 0;
129 if (!filename)
130 filename = (char*)*toys.optargs;
131 fp = fopen(filename, "w");
132 if (!fp) return;
133 while (lst) {
134 fprintf(fp, "%s\n", lst->line->str_data);
135 lst = lst->down;
136 }
137 fclose(fp);
138}
139
140int linelist_load(char *filename)
141{
142 struct linelist *lst = c_r;
143 FILE *fp = 0;
144 if (!filename)
145 filename = (char*)*toys.optargs;
146
147 fp = fopen(filename, "r");
148 if (!fp) {
149 char *line = xzalloc(80);
150 ssize_t alc = 80;
151 lst = (struct linelist*)dlist_add((struct double_list**)&lst,
152 xzalloc(sizeof(struct str_line)));
153 lst->line->alloc_len = alc;
154 lst->line->str_len = 0;
155 lst->line->str_data = line;
156 text = lst;
157 dlist_terminate(text->up);
158 return 1;
159 }
160
161 for (;;) {
162 char *line = xzalloc(80);
163 ssize_t alc = 80;
164 ssize_t len;
165 if ((len = getline(&line, (void *)&alc, fp)) == -1) {
166 if (errno == EINVAL || errno == ENOMEM) {
167 printf("error %d\n", errno);
168 }
169 free(line);
170 break;
171 }
172 lst = (struct linelist*)dlist_add((struct double_list**)&lst,
173 xzalloc(sizeof(struct str_line)));
174 lst->line->alloc_len = alc;
175 lst->line->str_len = len;
176 lst->line->str_data = line;
177
178 if (lst->line->str_data[len-1] == '\n') {
179 lst->line->str_data[len-1] = 0;
180 lst->line->str_len--;
181 }
182 if (text == 0) {
183 text = lst;
184 }
185
186 }
187 if (text) {
188 dlist_terminate(text->up);
189 }
190 fclose(fp);
191 return 1;
192
193}
194
195int vi_yy(char reg, int count0, int count1)
196{
197 return 1;
198}
199
200
201int vi_dd(char reg, int count0, int count1)
202{
203 int count = count0*count1;
204 struct linelist *lst = c_r;
205 if (c_r == text && text == scr_r) {
206 if (!text->down && !text->up && text->line) {
207 text->line->str_len = 1;
208 sprintf(text->line->str_data, " ");
209 goto success_exit;
210 }
211 if (text->down) {
212 text = text->down;
213 text->up = 0;
214 c_r = text;
215 scr_r = text;
216 free(lst->line->str_data);
217 free(lst->line);
218 free(lst);
219 }
220 goto recursion_exit;
221 }
222
223 if (lst)
224 {
225 if (lst->down) {
226 lst->down->up = lst->up;
227 }
228 if (lst->up) {
229 lst->up->down = lst->down;
230 }
231 if (scr_r == c_r) {
232 scr_r = c_r->down ? c_r->down : c_r->up;
233 }
234 if (c_r->down)
235 c_r = c_r->down;
236 else {
237 c_r = c_r->up;
238 count = 1;
239 }
240 free(lst->line->str_data);
241 free(lst->line);
242 free(lst);
243 }
244
245recursion_exit:
246 count--;
247
248 if (count>0)
249 return vi_dd(reg, count, 1);
250success_exit:
251 check_cursor_bounds();
252 adjust_screen_buffer();
253 return 1;
254}
255
256static int vi_x(char reg, int count0, int count1)
257{
258 int count = count0;
259 char *s;
260 char *last;
261 int *l;
262 int length = 0;
263 int width = 0;
264 int remaining = 0;
265 char *end;
266 char *start;
267 if (!c_r)
268 return 0;
269 s = c_r->line->str_data;
270 l = &c_r->line->str_len;
271
272 last = utf8_last(s,*l);
273 if (last == s+TT.cur_col) {
274 memset(last, 0, (*l)-TT.cur_col);
275 *l = TT.cur_col;
276 if (!TT.cur_col) return 1;
277 last = utf8_last(s, TT.cur_col);
278 TT.cur_col = last-s;
279 return 1;
280 }
281
282 start = s+TT.cur_col;
283 end = start;
284 remaining = (*l)-TT.cur_col;
285 for (;remaining;) {
286 int next = utf8_lnw(&width, end, remaining);
287 if (next && width) {
288 if (!count) break;
289 count--;
290 } if (!next) break;
291 length += next;
292 end += next;
293 remaining -= next;
294 }
295 if (remaining) {
296 memmove(start, end, remaining);
297 memset(start+remaining,0,end-start);
298 } else {
299 memset(start,0,(*l)-TT.cur_col);
300 }
301 *l -= end-start;
302 if (!TT.cur_col) return 1;
303 if (TT.cur_col == (*l)) {
304 last = utf8_last(s, TT.cur_col);
305 TT.cur_col = last-s;
306 }
307 return 1;
308}
309
310
311int vi_movw(int count0, int count1, char* unused)
312{
313 int count = count0*count1;
314 const char *empties = " \t\n\r";
315 const char *specials = ",.=-+*/(){}<>[]";
316
317 if (!c_r)
318 return 0;
319 if (TT.cur_col == c_r->line->str_len-1 || !c_r->line->str_len)
320 goto next_line;
321 if (strchr(empties, c_r->line->str_data[TT.cur_col]))
322 goto find_non_empty;
323 if (strchr(specials, c_r->line->str_data[TT.cur_col])) {
324 for (;strchr(specials, c_r->line->str_data[TT.cur_col]); ) {
325 TT.cur_col++;
326 if (TT.cur_col == c_r->line->str_len-1)
327 goto next_line;
328 }
329 } else for (;!strchr(specials, c_r->line->str_data[TT.cur_col]) &&
330 !strchr(empties, c_r->line->str_data[TT.cur_col]);) {
331 TT.cur_col++;
332 if (TT.cur_col == c_r->line->str_len-1)
333 goto next_line;
334 }
335
336 for (;strchr(empties, c_r->line->str_data[TT.cur_col]); ) {
337 TT.cur_col++;
338find_non_empty:
339 if (TT.cur_col == c_r->line->str_len-1) {
340next_line:
341
342 if (!c_r->down) return 0;
343 c_r = c_r->down;
344 TT.cur_col = 0;
345 if (!c_r->line->str_len) break;
346 }
347 }
348 count--;
349 if (count>0)
350 return vi_movw(count, 1, 0);
351
352 check_cursor_bounds();
353 adjust_screen_buffer();
354 return 1;
355}
356
357static int vi_movb(int count0, int count1, char* unused)
358{
359 int count = count0*count1;
360 if (!c_r)
361 return 0;
362 if (!TT.cur_col) {
363 if (!c_r->up) return 0;
364 c_r = c_r->up;
365 TT.cur_col = (c_r->line->str_len) ? c_r->line->str_len-1 : 0;
366 goto exit_function;
367 }
368 if (TT.cur_col)
369 TT.cur_col--;
370 while (c_r->line->str_data[TT.cur_col] <= ' ') {
371 if (TT.cur_col) TT.cur_col--;
372 else goto exit_function;
373 }
374 while (c_r->line->str_data[TT.cur_col] > ' ') {
375 if (TT.cur_col)TT.cur_col--;
376 else goto exit_function;
377 }
378 TT.cur_col++;
379exit_function:
380 count--;
381 if (count>1)
382 return vi_movb(count, 1, 0);
383 check_cursor_bounds();
384 adjust_screen_buffer();
385 return 1;
386}
387
388static int vi_move(int count0, int count1, char *unused)
389{
390 int count = count0*count1;
391 if (!c_r)
392 return 0;
393 if (TT.cur_col < c_r->line->str_len)
394 TT.cur_col++;
395 if (c_r->line->str_data[TT.cur_col] <= ' ' || count > 1)
396 vi_movw(count, 1, 0);
397 while (c_r->line->str_data[TT.cur_col] > ' ')
398 TT.cur_col++;
399 if (TT.cur_col) TT.cur_col--;
400
401 TT.vi_mov_flag |= 2;
402 check_cursor_bounds();
403 adjust_screen_buffer();
404 return 1;
405}
406
407void i_insert()
408{
409 char *t = xzalloc(c_r->line->alloc_len);
410 char *s = c_r->line->str_data;
411 int sel = c_r->line->str_len-TT.cur_col;
412 strncpy(t, &s[TT.cur_col], sel);
413 t[sel+1] = 0;
414 if (c_r->line->alloc_len < c_r->line->str_len+il->str_len+5) {
415 c_r->line->str_data = xrealloc(c_r->line->str_data,
416 c_r->line->alloc_len*2+il->alloc_len*2);
417
418 c_r->line->alloc_len = c_r->line->alloc_len*2+2*il->alloc_len;
419 memset(&c_r->line->str_data[c_r->line->str_len], 0,
420 c_r->line->alloc_len-c_r->line->str_len);
421
422 s = c_r->line->str_data;
423 }
424 strcpy(&s[TT.cur_col], il->str_data);
425 strcpy(&s[TT.cur_col+il->str_len], t);
426 TT.cur_col += il->str_len;
427 if (TT.cur_col) TT.cur_col--;
428 c_r->line->str_len += il->str_len;
429 free(t);
430
431}
432
433
434void i_split()
435{
436 struct str_line *l = xmalloc(sizeof(struct str_line));
437 int l_a = c_r->line->alloc_len;
438 int l_len = c_r->line->str_len-TT.cur_col;
439 l->str_data = xzalloc(l_a);
440 l->alloc_len = l_a;
441 l->str_len = l_len;
442 strncpy(l->str_data, &c_r->line->str_data[TT.cur_col], l_len);
443 l->str_data[l_len] = 0;
444 c_r->line->str_len -= l_len;
445 c_r->line->str_data[c_r->line->str_len] = 0;
446 c_r = (struct linelist*)dlist_insert((struct double_list**)&c_r, (char*)l);
447 c_r->line = l;
448 TT.cur_col = 0;
449 check_cursor_bounds();
450 adjust_screen_buffer();
451}
452
453static int vi_zero(int count0, int count1, char *unused)
454{
455 TT.cur_col = 0;
456 return 1;
457}
458
459static int vi_eol(int count0, int count1, char *unused)
460{
461 int count = count0*count1;
462 for (;count > 1 && c_r->down; count--)
463 c_r = c_r->down;
464
465 if (c_r && c_r->line->str_len)
466 TT.cur_col = c_r->line->str_len-1;
467 TT.vi_mov_flag |= 2;
468 check_cursor_bounds();
469 return 1;
470}
471
472static int vi_find_c(int count0, int count1, char *symbol)
473{
474 int count = count0*count1;
475 if (c_r && c_r->line->str_len) {
476 while (count--) {
477 char* pos = strstr(&c_r->line->str_data[TT.cur_col], symbol);
478 if (pos) {
479 TT.cur_col = pos-c_r->line->str_data;
480 return 1;
481 }
482 }
483 }
484 return 0;
485}
486
487static int vi_find_cb(int count0, int count1, char *symbol)
488{
489
490 return 1;
491}
492
493
494static int vi_go(int count0, int count1, char *symbol)
495{
496 c_r = text;
497 while(--count0) {
498 if (c_r && c_r->down) c_r = c_r->down;
499 }
500 TT.cur_col = 0;
501 check_cursor_bounds();
502 adjust_screen_buffer();
503 return 1;
504}
505
506
507static int vi_delete(char reg, struct linelist *row, int col, int flags)
508{
509 if (row == c_r) {
510 if (col < TT.cur_col) {
511 int distance = TT.cur_col - col;
512 TT.cur_col = col;
513 vi_x(reg, distance, 1);
514 } else {
515 int distance = col - TT.cur_col;
516 if (distance > 0) vi_x(reg, distance, 1);
517 }
518 if (TT.vi_mov_flag&2)
519 vi_x(reg, 1, 1);
520 }
521 return 1;
522}
523
524static int vi_D(char reg, int count0, int count1)
525{
526 int prev_col = TT.cur_col;
527 struct linelist *pos = c_r;
528 if (!count0) return 1;
529 vi_eol(1, 1, 0);
530 vi_delete(reg, pos, prev_col, 0);
531 count0--;
532 if (count0 && c_r->down) {
533 c_r = c_r->down;
534 vi_dd(reg, count0, 1);
535 }
536 return 1;
537}
538
539static int vi_join(char reg, int count0, int count1)
540{
541 while (count0--) {
542 if (c_r && c_r->down) {
543 int size = c_r->line->str_len+c_r->down->line->str_len;
544 if (size > c_r->line->alloc_len) {
545 if (size > c_r->down->line->alloc_len) {
546 c_r->line->str_data = xrealloc(c_r->line->str_data,
547 c_r->line->alloc_len*2+il->alloc_len*2);
548 memmove(&c_r->line->str_data[c_r->line->str_len],
549 c_r->down->line->str_data,c_r->down->line->str_len);
550 c_r->line->str_len = size;
551 c_r = c_r->down;
552 c_r->line->alloc_len = c_r->line->alloc_len*2+2*il->alloc_len;
553 vi_dd(0,1,1);
554 } else {
555 memmove(&c_r->down->line->str_data[c_r->line->str_len],
556 c_r->down->line->str_data,c_r->down->line->str_len);
557 memmove(c_r->down->line->str_data,c_r->line->str_data,
558 c_r->line->str_len);
559 c_r->down->line->str_len = size;
560 vi_dd(0,1,1);
561 }
562 } else {
563 memmove(&c_r->line->str_data[c_r->line->str_len],
564 c_r->down->line->str_data,c_r->down->line->str_len);
565 c_r->line->str_len = size;
566 c_r = c_r->down;
567 vi_dd(0,1,1);
568 }
569 c_r = c_r->up;
570
571 }
572 }
573 return 1;
574}
575
576static int vi_change(char reg, struct linelist *row, int col, int flags)
577{
578 vi_delete(reg, row, col, flags);
579 TT.vi_mode = 2;
580 return 1;
581}
582
583static int vi_yank(char reg, struct linelist *row, int col, int flags)
584{
585 return 1;
586}
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603struct vi_cmd_param {
604 const char* cmd;
605 unsigned flags;
606 int (*vi_cmd)(char, struct linelist*, int, int);
607};
608struct vi_mov_param {
609 const char* mov;
610 unsigned flags;
611 int (*vi_mov)(int, int, char*);
612};
613
614struct vi_spesial_param {
615 const char *cmd;
616 int (*vi_spesial)(char, int, int);
617};
618struct vi_spesial_param vi_spesial[5] =
619{
620 {"dd", &vi_dd},
621 {"yy", &vi_yy},
622 {"D", &vi_D},
623 {"J", &vi_join},
624 {"x", &vi_x},
625};
626
627
628
629struct vi_mov_param vi_movs[12] =
630{
631 {"0", 0, &vi_zero},
632 {"b", 0, &vi_movb},
633 {"e", 0, &vi_move},
634 {"G", 0, &vi_go},
635 {"h", 0, &cur_left},
636 {"j", 0, &cur_down},
637 {"k", 0, &cur_up},
638 {"l", 0, &cur_right},
639 {"w", 0, &vi_movw},
640 {"$", 0, &vi_eol},
641 {"f", 1, &vi_find_c},
642 {"F", 1, &vi_find_cb},
643};
644
645
646
647
648
649struct vi_cmd_param vi_cmds[3] =
650{
651 {"c", 1, &vi_change},
652 {"d", 1, &vi_delete},
653 {"y", 1, &vi_yank},
654};
655
656int run_vi_cmd(char *cmd)
657{
658 int i = 0;
659 int val = 0;
660 char *cmd_e;
661 int (*vi_cmd)(char, struct linelist*, int, int) = 0;
662 int (*vi_mov)(int, int, char*) = 0;
663 TT.count0 = 0;
664 TT.count1 = 0;
665 TT.vi_reg = '"';
666 TT.vi_mov_flag = 0;
667 if (*cmd == '"') {
668 cmd++;
669 TT.vi_reg = *cmd;
670 cmd++;
671 }
672 val = strtol(cmd, &cmd_e, 10);
673 if (errno || val == 0) val = 1;
674 else cmd = cmd_e;
675 TT.count0 = val;
676
677 for (i = 0; i < 5; i++) {
678 if (strstr(cmd, vi_spesial[i].cmd)) {
679 return vi_spesial[i].vi_spesial(TT.vi_reg, TT.count0, TT.count1);
680 }
681 }
682
683 for (i = 0; i < 3; i++) {
684 if (!strncmp(cmd, vi_cmds[i].cmd, strlen(vi_cmds[i].cmd))) {
685 vi_cmd = vi_cmds[i].vi_cmd;
686 cmd += strlen(vi_cmds[i].cmd);
687 break;
688 }
689 }
690 val = strtol(cmd, &cmd_e, 10);
691 if (errno || val == 0) val = 1;
692 else cmd = cmd_e;
693 TT.count1 = val;
694
695 for (i = 0; i < 12; i++) {
696 if (!strncmp(cmd, vi_movs[i].mov, strlen(vi_movs[i].mov))) {
697 vi_mov = vi_movs[i].vi_mov;
698 TT.vi_mov_flag = vi_movs[i].flags;
699 cmd++;
700 if (TT.vi_mov_flag&1 && !(*cmd)) return 0;
701 break;
702 }
703 }
704 if (vi_mov) {
705 int prev_col = TT.cur_col;
706 struct linelist *pos = c_r;
707 if (vi_mov(TT.count0, TT.count1, cmd)) {
708 if (vi_cmd) return (vi_cmd(TT.vi_reg, pos, prev_col, TT.vi_mov_flag));
709 else return 1;
710 } else return 0;
711 }
712 return 0;
713}
714
715int search_str(char *s)
716{
717 struct linelist *lst = c_r;
718 char *c = strstr(&c_r->line->str_data[TT.cur_col], s);
719 if (c) {
720 TT.cur_col = c_r->line->str_data-c;
721 TT.cur_col = c-c_r->line->str_data;
722 }
723 else for (; !c;) {
724 lst = lst->down;
725 if (!lst) return 1;
726 c = strstr(&lst->line->str_data[TT.cur_col], s);
727 }
728 c_r = lst;
729 TT.cur_col = c-c_r->line->str_data;
730 return 0;
731}
732
733int run_ex_cmd(char *cmd)
734{
735 if (cmd[0] == '/') {
736
737 if (!search_str(&cmd[1]) ) {
738 check_cursor_bounds();
739 adjust_screen_buffer();
740 }
741 } else if (cmd[0] == '?') {
742
743 } else if (cmd[0] == ':') {
744 if (strstr(&cmd[1], "q!")) {
745
746 return -1;
747 }
748 else if (strstr(&cmd[1], "wq")) {
749 write_file(0);
750 return -1;
751 }
752 else if (strstr(&cmd[1], "w")) {
753 write_file(0);
754 return 1;
755 }
756 }
757 return 0;
758
759}
760
761void vi_main(void)
762{
763 char keybuf[16];
764 char utf8_code[8];
765 int utf8_dec_p = 0;
766 int key = 0;
767 char vi_buf[16];
768 int vi_buf_pos = 0;
769 il = xzalloc(sizeof(struct str_line));
770 il->str_data = xzalloc(80);
771 il->alloc_len = 80;
772 keybuf[0] = 0;
773 memset(vi_buf, 0, 16);
774 memset(utf8_code, 0, 8);
775 linelist_load(0);
776 scr_r = text;
777 c_r = text;
778 TT.cur_row = 0;
779 TT.cur_col = 0;
780 TT.screen_width = 80;
781 TT.screen_height = 24;
782 TT.vi_mode = 1;
783 terminal_size(&TT.screen_width, &TT.screen_height);
784 TT.screen_height -= 2;
785 set_terminal(0, 1, 0, 0);
786
787
788 tty_esc("?1049h");
789 tty_esc("H");
790 xflush(1);
791 draw_page();
792 while(1) {
793 key = scan_key(keybuf, -1);
794 printf("key %d\n", key);
795 switch (key) {
796 case -1:
797 case 3:
798 case 4:
799 goto cleanup_vi;
800 }
801 if (TT.vi_mode == 1) {
802 switch (key) {
803 case '/':
804 case '?':
805 case ':':
806 TT.vi_mode = 0;
807 il->str_data[0]=key;
808 il->str_len++;
809 break;
810 case 'a':
811 if (c_r && c_r->line->str_len)
812 TT.cur_col++;
813 case 'i':
814 TT.vi_mode = 2;
815 break;
816 case 27:
817 vi_buf[0] = 0;
818 vi_buf_pos = 0;
819 break;
820 default:
821 if (key > 0x20 && key < 0x7B) {
822 vi_buf[vi_buf_pos] = key;
823 vi_buf_pos++;
824 if (run_vi_cmd(vi_buf)) {
825 memset(vi_buf, 0, 16);
826 vi_buf_pos = 0;
827 }
828 else if (vi_buf_pos == 16) {
829 vi_buf_pos = 0;
830 memset(vi_buf, 0, 16);
831 }
832
833 }
834
835 break;
836 }
837 } else if (TT.vi_mode == 0) {
838 switch (key) {
839 case 27:
840 TT.vi_mode = 1;
841 il->str_len = 0;
842 memset(il->str_data, 0, il->alloc_len);
843 break;
844 case 0x7F:
845 case 0x08:
846 if (il->str_len) {
847 il->str_data[il->str_len] = 0;
848 if (il->str_len > 1) il->str_len--;
849 }
850 break;
851 case 0x0D:
852 if (run_ex_cmd(il->str_data) == -1)
853 goto cleanup_vi;
854 TT.vi_mode = 1;
855 il->str_len = 0;
856 memset(il->str_data, 0, il->alloc_len);
857 break;
858 default:
859 if (key >= 0x20 && key < 0x7F) {
860 if (il->str_len == il->alloc_len) {
861 il->str_data = realloc(il->str_data, il->alloc_len*2);
862 il->alloc_len *= 2;
863 }
864 il->str_data[il->str_len] = key;
865 il->str_len++;
866 }
867 break;
868 }
869 } else if (TT.vi_mode == 2) {
870 switch (key) {
871 case 27:
872 i_insert();
873 TT.vi_mode = 1;
874 il->str_len = 0;
875 memset(il->str_data, 0, il->alloc_len);
876 break;
877 case 0x7F:
878 case 0x08:
879 if (il->str_len)
880 il->str_data[il->str_len--] = 0;
881 break;
882 case 0x09:
883
884 il->str_data[il->str_len++] = ' ';
885 il->str_data[il->str_len++] = ' ';
886 break;
887
888 case 0x0D:
889
890
891 i_insert();
892 il->str_len = 0;
893 memset(il->str_data, 0, il->alloc_len);
894 i_split();
895 break;
896 default:
897 if (key >= 0x20 && utf8_dec(key, utf8_code, &utf8_dec_p)) {
898 if (il->str_len+utf8_dec_p+1 >= il->alloc_len) {
899 il->str_data = realloc(il->str_data, il->alloc_len*2);
900 il->alloc_len *= 2;
901 }
902 strcpy(il->str_data+il->str_len, utf8_code);
903 il->str_len += utf8_dec_p;
904 utf8_dec_p = 0;
905 *utf8_code = 0;
906
907 }
908 break;
909 }
910 }
911
912 draw_page();
913
914 }
915cleanup_vi:
916 linelist_unload();
917 tty_reset();
918 tty_esc("?1049l");
919}
920
921static void draw_page()
922{
923 unsigned y = 0;
924 int cy_scr = 0;
925 int cx_scr = 0;
926 int utf_l = 0;
927
928 char* line = 0;
929 int bytes = 0;
930 int drawn = 0;
931 int x = 0;
932 struct linelist *scr_buf= scr_r;
933
934 tty_esc("2J");
935 tty_esc("H");
936
937 tty_jump(0, 0);
938
939
940 for (; y < TT.screen_height; ) {
941 if (line && bytes) {
942 draw_str_until(&drawn, line, TT.screen_width, bytes);
943 bytes = drawn ? (bytes-drawn) : 0;
944 line = bytes ? (line+drawn) : 0;
945 y++;
946 tty_jump(0, y);
947 } else if (scr_buf && scr_buf->line->str_data && scr_buf->line->str_len) {
948 if (scr_buf == c_r)
949 break;
950 line = scr_buf->line->str_data;
951 bytes = scr_buf->line->str_len;
952 scr_buf = scr_buf->down;
953 } else {
954 if (scr_buf == c_r)
955 break;
956 y++;
957 tty_jump(0, y);
958
959 if (scr_buf) scr_buf = scr_buf->down;
960 }
961
962 }
963
964
965 line = scr_buf->line->str_data;
966 bytes = TT.cur_col;
967 for (; y < TT.screen_height; ) {
968 if (bytes) {
969 x = draw_str_until(&drawn, line, TT.screen_width, bytes);
970 bytes = drawn ? (bytes-drawn) : 0;
971 line = bytes ? (line+drawn) : 0;
972 }
973 if (!bytes) break;
974 y++;
975 tty_jump(0, y);
976 }
977 if (TT.vi_mode == 2 && il->str_len) {
978 line = il->str_data;
979 bytes = il->str_len;
980 cx_scr = x;
981 cy_scr = y;
982 x = draw_str_until(&drawn, line, TT.screen_width-x, bytes);
983 bytes = drawn ? (bytes-drawn) : 0;
984 line = bytes ? (line+drawn) : 0;
985 cx_scr += x;
986 for (; y < TT.screen_height; ) {
987 if (bytes) {
988 x = draw_str_until(&drawn, line, TT.screen_width, bytes);
989 bytes = drawn ? (bytes-drawn) : 0;
990 line = bytes ? (line+drawn) : 0;
991 cx_scr = x;
992 }
993 if (!bytes) break;
994 y++;
995 cy_scr = y;
996 tty_jump(0, y);
997 }
998 } else {
999 cy_scr = y;
1000 cx_scr = x;
1001 }
1002 line = scr_buf->line->str_data+TT.cur_col;
1003 bytes = scr_buf->line->str_len-TT.cur_col;
1004 scr_buf = scr_buf->down;
1005 x = draw_str_until(&drawn,line, TT.screen_width-x, bytes);
1006 bytes = drawn ? (bytes-drawn) : 0;
1007 line = bytes ? (line+drawn) : 0;
1008 y++;
1009 tty_jump(0, y);
1010
1011
1012 for (; y < TT.screen_height; ) {
1013 if (line && bytes) {
1014 draw_str_until(&drawn, line, TT.screen_width, bytes);
1015 bytes = drawn ? (bytes-drawn) : 0;
1016 line = bytes ? (line+drawn) : 0;
1017 y++;
1018 tty_jump(0, y);
1019 } else if (scr_buf && scr_buf->line->str_data && scr_buf->line->str_len) {
1020 line = scr_buf->line->str_data;
1021 bytes = scr_buf->line->str_len;
1022 scr_buf = scr_buf->down;
1023 } else {
1024 y++;
1025 tty_jump(0, y);
1026 if (scr_buf) scr_buf = scr_buf->down;
1027 }
1028
1029 }
1030
1031 tty_jump(0, TT.screen_height);
1032 switch (TT.vi_mode) {
1033 case 0:
1034 tty_esc("30;44m");
1035 printf("COMMAND|");
1036 break;
1037 case 1:
1038 tty_esc("30;42m");
1039 printf("NORMAL|");
1040 break;
1041 case 2:
1042 tty_esc("30;41m");
1043 printf("INSERT|");
1044 break;
1045
1046 }
1047
1048 tty_esc("47m");
1049 tty_esc("30m");
1050 utf_l = utf8_len(&c_r->line->str_data[TT.cur_col]);
1051 if (utf_l) {
1052 char t[5] = {0, 0, 0, 0, 0};
1053 strncpy(t, &c_r->line->str_data[TT.cur_col], utf_l);
1054 printf("utf: %d %s", utf_l, t);
1055 }
1056 printf("| %d, %d\n", cx_scr, cy_scr);
1057
1058 tty_jump(TT.screen_width-12, TT.screen_height);
1059 printf("| %d, %d\n", TT.cur_row, TT.cur_col);
1060 tty_esc("37m");
1061 tty_esc("40m");
1062 if (!TT.vi_mode) {
1063 tty_esc("1m");
1064 tty_jump(0, TT.screen_height+1);
1065 printf("%s", il->str_data);
1066 tty_esc("0m");
1067 } else tty_jump(cx_scr, cy_scr);
1068
1069 xflush(1);
1070
1071}
1072
1073static void draw_char(char c, int x, int y, int highlight)
1074{
1075 tty_jump(x, y);
1076 if (highlight) {
1077 tty_esc("30m");
1078 tty_esc("47m");
1079 }
1080 printf("%c", c);
1081}
1082
1083
1084
1085static int draw_rune(char *c, int x, int y, int highlight)
1086{
1087 int l = utf8_len(c);
1088 char t[5] = {0, 0, 0, 0, 0};
1089 if (!l) return 0;
1090 tty_jump(x, y);
1091 tty_esc("0m");
1092 if (highlight) {
1093 tty_esc("30m");
1094 tty_esc("47m");
1095 }
1096 strncpy(t, c, 5);
1097 printf("%s", t);
1098 tty_esc("0m");
1099 return l;
1100}
1101
1102static void check_cursor_bounds()
1103{
1104 if (c_r->line->str_len == 0) TT.cur_col = 0;
1105 else if (c_r->line->str_len-1 < TT.cur_col) TT.cur_col = c_r->line->str_len-1;
1106 if (utf8_width(&c_r->line->str_data[TT.cur_col], c_r->line->str_len-TT.cur_col) <= 0)
1107 cur_left(1, 1, 0);
1108}
1109
1110static void adjust_screen_buffer()
1111{
1112
1113 struct linelist *t = text;
1114 int c = -1;
1115 int s = -1;
1116 int i = 0;
1117 for (;;) {
1118 i++;
1119 if (t == c_r)
1120 c = i;
1121 if (t == scr_r)
1122 s = i;
1123 t = t->down;
1124 if ( ((c != -1) && (s != -1)) || t == 0)
1125 break;
1126 }
1127 if (c <= s) {
1128 scr_r = c_r;
1129 }
1130 else if ( c > s ) {
1131
1132 int distance = c - s + 1;
1133
1134
1135 if (distance >= (int)TT.screen_height) {
1136 int adj = distance - TT.screen_height;
1137 while (adj--) {
1138 scr_r = scr_r->down;
1139 }
1140 }
1141 }
1142 TT.cur_row = c;
1143
1144}
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154static int utf8_len(char *str)
1155{
1156 int len = 0;
1157 int i = 0;
1158 uint8_t *c = (uint8_t*)str;
1159 if (!c || !(*c)) return 0;
1160 if (*c < 0x7F) return 1;
1161 if ((*c & 0xE0) == 0xc0) len = 2;
1162 else if ((*c & 0xF0) == 0xE0 ) len = 3;
1163 else if ((*c & 0xF8) == 0xF0 ) len = 4;
1164 else return 0;
1165 c++;
1166 for (i = len-1; i > 0; i--) {
1167 if ((*c++ & 0xc0) != 0x80) return 0;
1168 }
1169 return len;
1170}
1171
1172
1173static int utf8_lnw(int* width, char* str, int bytes)
1174{
1175 wchar_t wc;
1176 int length = 1;
1177 *width = 1;
1178
1179 length = mbtowc(&wc, str, bytes);
1180 switch (length) {
1181 case -1:
1182 mbtowc(0,0,4);
1183 case 0:
1184 *width = 0;
1185 length = 0;
1186 break;
1187 default:
1188 *width = wcwidth(wc);
1189 }
1190 return length;
1191}
1192
1193
1194
1195static int utf8_width(char *str, int bytes)
1196{
1197 wchar_t wc;
1198 switch (mbtowc(&wc, str, bytes)) {
1199 case -1:
1200 mbtowc(0,0,4);
1201 case 0:
1202 return -1;
1203 default:
1204 return wcwidth(wc);
1205 }
1206 return 0;
1207}
1208
1209static int utf8_dec(char key, char *utf8_scratch, int *sta_p)
1210{
1211 int len = 0;
1212 char *c = utf8_scratch;
1213 c[*sta_p] = key;
1214 if (!(*sta_p)) *c = key;
1215 if (*c < 0x7F) { *sta_p = 1; return 1; }
1216 if ((*c & 0xE0) == 0xc0) len = 2;
1217 else if ((*c & 0xF0) == 0xE0 ) len = 3;
1218 else if ((*c & 0xF8) == 0xF0 ) len = 4;
1219 else {*sta_p = 0; return 0; }
1220
1221 (*sta_p)++;
1222
1223 if (*sta_p == 1) return 0;
1224 if ((c[*sta_p-1] & 0xc0) != 0x80) {*sta_p = 0; return 0; }
1225
1226 if (*sta_p == len) { c[(*sta_p)] = 0; return 1; }
1227
1228 return 0;
1229}
1230
1231static char* utf8_last(char* str, int size)
1232{
1233 char* end = str+size;
1234 int pos = size;
1235 int len = 0;
1236 int width = 0;
1237 while (pos >= 0) {
1238 len = utf8_lnw(&width, end, size-pos);
1239 if (len && width) return end;
1240 end--; pos--;
1241 }
1242 return 0;
1243}
1244
1245static int draw_str_until(int *drawn, char *str, int width, int bytes)
1246{
1247 int rune_width = 0;
1248 int rune_bytes = 0;
1249 int max_bytes = bytes;
1250 int max_width = width;
1251 char* end = str;
1252 for (;width && bytes;) {
1253 rune_bytes = utf8_lnw(&rune_width, end, 4);
1254 if (!rune_bytes) break;
1255 if (width - rune_width < 0) goto write_bytes;
1256 width -= rune_width;
1257 bytes -= rune_bytes;
1258 end += rune_bytes;
1259 }
1260 for (;bytes;) {
1261 rune_bytes = utf8_lnw(&rune_width, end, 4);
1262 if (!rune_bytes) break;
1263 if (rune_width) break;
1264 bytes -= rune_bytes;
1265 end += rune_bytes;
1266 }
1267write_bytes:
1268 fwrite(str, max_bytes-bytes, 1, stdout);
1269 *drawn = max_bytes-bytes;
1270 return max_width-width;
1271}
1272
1273static int cur_left(int count0, int count1, char* unused)
1274{
1275 int count = count0*count1;
1276 for (;count--;) {
1277 if (!TT.cur_col) return 1;
1278
1279 TT.cur_col--;
1280 check_cursor_bounds();
1281 }
1282 return 1;
1283}
1284
1285static int cur_right(int count0, int count1, char* unused)
1286{
1287 int count = count0*count1;
1288 for (;count--;) {
1289 if (c_r->line->str_len <= 1) return 1;
1290 if (TT.cur_col >= c_r->line->str_len-1) {
1291 TT.cur_col = utf8_last(c_r->line->str_data, c_r->line->str_len)
1292 - c_r->line->str_data;
1293 return 1;
1294 }
1295 TT.cur_col++;
1296 if (utf8_width(&c_r->line->str_data[TT.cur_col],
1297 c_r->line->str_len-TT.cur_col) <= 0)
1298 cur_right(1, 1, 0);
1299 }
1300 return 1;
1301}
1302
1303static int cur_up(int count0, int count1, char* unused)
1304{
1305 int count = count0*count1;
1306 for (;count-- && c_r->up;)
1307 c_r = c_r->up;
1308
1309 check_cursor_bounds();
1310 adjust_screen_buffer();
1311 return 1;
1312}
1313
1314static int cur_down(int count0, int count1, char* unused)
1315{
1316 int count = count0*count1;
1317 for (;count-- && c_r->down;)
1318 c_r = c_r->down;
1319
1320 check_cursor_bounds();
1321 adjust_screen_buffer();
1322 return 1;
1323}
1324
1325