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