10f66f451Sopenharmony_ci/* vi.c - You can't spell "evil" without "vi".
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2015 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci * Copyright 2019 Jarno Mäkipää <jmakip87@gmail.com>
50f66f451Sopenharmony_ci *
60f66f451Sopenharmony_ci * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html
70f66f451Sopenharmony_ci
80f66f451Sopenharmony_ciUSE_VI(NEWTOY(vi, ">1s:", TOYFLAG_USR|TOYFLAG_BIN))
90f66f451Sopenharmony_ci
100f66f451Sopenharmony_ciconfig VI
110f66f451Sopenharmony_ci  bool "vi"
120f66f451Sopenharmony_ci  default n
130f66f451Sopenharmony_ci  help
140f66f451Sopenharmony_ci    usage: vi [-s script] FILE
150f66f451Sopenharmony_ci    -s script: run script file
160f66f451Sopenharmony_ci    Visual text editor. Predates the existence of standardized cursor keys,
170f66f451Sopenharmony_ci    so the controls are weird and historical.
180f66f451Sopenharmony_ci*/
190f66f451Sopenharmony_ci
200f66f451Sopenharmony_ci#define FOR_vi
210f66f451Sopenharmony_ci#include "toys.h"
220f66f451Sopenharmony_ci
230f66f451Sopenharmony_ciGLOBALS(
240f66f451Sopenharmony_ci  char *s;
250f66f451Sopenharmony_ci  int vi_mode, tabstop, list;
260f66f451Sopenharmony_ci  int cur_col, cur_row, scr_row;
270f66f451Sopenharmony_ci  int drawn_row, drawn_col;
280f66f451Sopenharmony_ci  int count0, count1, vi_mov_flag;
290f66f451Sopenharmony_ci  unsigned screen_height, screen_width;
300f66f451Sopenharmony_ci  char vi_reg, *last_search;
310f66f451Sopenharmony_ci  struct str_line {
320f66f451Sopenharmony_ci    int alloc;
330f66f451Sopenharmony_ci    int len;
340f66f451Sopenharmony_ci    char *data;
350f66f451Sopenharmony_ci  } *il;
360f66f451Sopenharmony_ci  size_t screen, cursor; //offsets
370f66f451Sopenharmony_ci  //yank buffer
380f66f451Sopenharmony_ci  struct yank_buf {
390f66f451Sopenharmony_ci    char reg;
400f66f451Sopenharmony_ci    int alloc;
410f66f451Sopenharmony_ci    char* data;
420f66f451Sopenharmony_ci  } yank;
430f66f451Sopenharmony_ci
440f66f451Sopenharmony_ci  int modified;
450f66f451Sopenharmony_ci  size_t filesize;
460f66f451Sopenharmony_ci// mem_block contains RO data that is either original file as mmap
470f66f451Sopenharmony_ci// or heap allocated inserted data
480f66f451Sopenharmony_ci//
490f66f451Sopenharmony_ci//
500f66f451Sopenharmony_ci//
510f66f451Sopenharmony_ci  struct block_list {
520f66f451Sopenharmony_ci    struct block_list *next, *prev;
530f66f451Sopenharmony_ci    struct mem_block {
540f66f451Sopenharmony_ci      size_t size;
550f66f451Sopenharmony_ci      size_t len;
560f66f451Sopenharmony_ci      enum alloc_flag {
570f66f451Sopenharmony_ci        MMAP,  //can be munmap() before exit()
580f66f451Sopenharmony_ci        HEAP,  //can be free() before exit()
590f66f451Sopenharmony_ci        STACK, //global or stack perhaps toybuf
600f66f451Sopenharmony_ci      } alloc;
610f66f451Sopenharmony_ci      const char *data;
620f66f451Sopenharmony_ci    } *node;
630f66f451Sopenharmony_ci  } *text;
640f66f451Sopenharmony_ci
650f66f451Sopenharmony_ci// slices do not contain actual allocated data but slices of data in mem_block
660f66f451Sopenharmony_ci// when file is first opened it has only one slice.
670f66f451Sopenharmony_ci// after inserting data into middle new mem_block is allocated for insert data
680f66f451Sopenharmony_ci// and 3 slices are created, where first and last slice are pointing to original
690f66f451Sopenharmony_ci// mem_block with offsets, and middle slice is pointing to newly allocated block
700f66f451Sopenharmony_ci// When deleting, data is not freed but mem_blocks are sliced more such way that
710f66f451Sopenharmony_ci// deleted data left between 2 slices
720f66f451Sopenharmony_ci  struct slice_list {
730f66f451Sopenharmony_ci    struct slice_list *next, *prev;
740f66f451Sopenharmony_ci    struct slice {
750f66f451Sopenharmony_ci      size_t len;
760f66f451Sopenharmony_ci      const char *data;
770f66f451Sopenharmony_ci    } *node;
780f66f451Sopenharmony_ci  } *slices;
790f66f451Sopenharmony_ci)
800f66f451Sopenharmony_ci
810f66f451Sopenharmony_cistatic const char *blank = " \n\r\t";
820f66f451Sopenharmony_cistatic const char *specials = ",.:;=-+*/(){}<>[]!@#$%^&|\\?\"\'";
830f66f451Sopenharmony_ci
840f66f451Sopenharmony_ci//get utf8 length and width at same time
850f66f451Sopenharmony_cistatic int utf8_lnw(int *width, char *s, int bytes)
860f66f451Sopenharmony_ci{
870f66f451Sopenharmony_ci  unsigned wc;
880f66f451Sopenharmony_ci  int length = 1;
890f66f451Sopenharmony_ci
900f66f451Sopenharmony_ci  if (*s == '\t') *width = TT.tabstop;
910f66f451Sopenharmony_ci  else {
920f66f451Sopenharmony_ci    length = utf8towc(&wc, s, bytes);
930f66f451Sopenharmony_ci    if (length < 1) length = 0, *width = 0;
940f66f451Sopenharmony_ci    else *width = wcwidth(wc);
950f66f451Sopenharmony_ci  }
960f66f451Sopenharmony_ci  return length;
970f66f451Sopenharmony_ci}
980f66f451Sopenharmony_ci
990f66f451Sopenharmony_cistatic int utf8_dec(char key, char *utf8_scratch, int *sta_p)
1000f66f451Sopenharmony_ci{
1010f66f451Sopenharmony_ci  int len = 0;
1020f66f451Sopenharmony_ci  char *c = utf8_scratch;
1030f66f451Sopenharmony_ci  c[*sta_p] = key;
1040f66f451Sopenharmony_ci  if (!(*sta_p))  *c = key;
1050f66f451Sopenharmony_ci  if (*c < 0x7F) { *sta_p = 1; return 1; }
1060f66f451Sopenharmony_ci  if ((*c & 0xE0) == 0xc0) len = 2;
1070f66f451Sopenharmony_ci  else if ((*c & 0xF0) == 0xE0 ) len = 3;
1080f66f451Sopenharmony_ci  else if ((*c & 0xF8) == 0xF0 ) len = 4;
1090f66f451Sopenharmony_ci  else {*sta_p = 0; return 0; }
1100f66f451Sopenharmony_ci
1110f66f451Sopenharmony_ci  (*sta_p)++;
1120f66f451Sopenharmony_ci
1130f66f451Sopenharmony_ci  if (*sta_p == 1) return 0;
1140f66f451Sopenharmony_ci  if ((c[*sta_p-1] & 0xc0) != 0x80) {*sta_p = 0; return 0; }
1150f66f451Sopenharmony_ci
1160f66f451Sopenharmony_ci  if (*sta_p == len) { c[(*sta_p)] = 0; return 1; }
1170f66f451Sopenharmony_ci
1180f66f451Sopenharmony_ci  return 0;
1190f66f451Sopenharmony_ci}
1200f66f451Sopenharmony_ci
1210f66f451Sopenharmony_cistatic char* utf8_last(char* str, int size)
1220f66f451Sopenharmony_ci{
1230f66f451Sopenharmony_ci  char* end = str+size;
1240f66f451Sopenharmony_ci  int pos = size, len, width = 0;
1250f66f451Sopenharmony_ci  for (;pos >= 0; end--, pos--) {
1260f66f451Sopenharmony_ci    len = utf8_lnw(&width, end, size-pos);
1270f66f451Sopenharmony_ci    if (len && width) return end;
1280f66f451Sopenharmony_ci  }
1290f66f451Sopenharmony_ci  return 0;
1300f66f451Sopenharmony_ci}
1310f66f451Sopenharmony_ci
1320f66f451Sopenharmony_cistruct double_list *dlist_add_before(struct double_list **head,
1330f66f451Sopenharmony_ci  struct double_list **list, char *data)
1340f66f451Sopenharmony_ci{
1350f66f451Sopenharmony_ci  struct double_list *new = xmalloc(sizeof(struct double_list));
1360f66f451Sopenharmony_ci  new->data = data;
1370f66f451Sopenharmony_ci  if (*list == *head) *head = new;
1380f66f451Sopenharmony_ci
1390f66f451Sopenharmony_ci  dlist_add_nomalloc(list, new);
1400f66f451Sopenharmony_ci  return new;
1410f66f451Sopenharmony_ci}
1420f66f451Sopenharmony_ci
1430f66f451Sopenharmony_cistruct double_list *dlist_add_after(struct double_list **head,
1440f66f451Sopenharmony_ci  struct double_list **list, char *data)
1450f66f451Sopenharmony_ci{
1460f66f451Sopenharmony_ci  struct double_list *new = xmalloc(sizeof(struct double_list));
1470f66f451Sopenharmony_ci  new->data = data;
1480f66f451Sopenharmony_ci
1490f66f451Sopenharmony_ci  if (*list) {
1500f66f451Sopenharmony_ci    new->prev = *list;
1510f66f451Sopenharmony_ci    new->next = (*list)->next;
1520f66f451Sopenharmony_ci    (*list)->next->prev = new;
1530f66f451Sopenharmony_ci    (*list)->next = new;
1540f66f451Sopenharmony_ci  } else *head = *list = new->next = new->prev = new;
1550f66f451Sopenharmony_ci  return new;
1560f66f451Sopenharmony_ci}
1570f66f451Sopenharmony_ci
1580f66f451Sopenharmony_ci// str must be already allocated
1590f66f451Sopenharmony_ci// ownership of allocated data is moved
1600f66f451Sopenharmony_ci// data, pre allocated data
1610f66f451Sopenharmony_ci// offset, offset in whole text
1620f66f451Sopenharmony_ci// size, data allocation size of given data
1630f66f451Sopenharmony_ci// len, length of the string
1640f66f451Sopenharmony_ci// type, define allocation type for cleanup purposes at app exit
1650f66f451Sopenharmony_cistatic int insert_str(const char *data, size_t offset, size_t size, size_t len,
1660f66f451Sopenharmony_ci  enum alloc_flag type)
1670f66f451Sopenharmony_ci{
1680f66f451Sopenharmony_ci  struct mem_block *b = xmalloc(sizeof(struct mem_block));
1690f66f451Sopenharmony_ci  struct slice *next = xmalloc(sizeof(struct slice));
1700f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
1710f66f451Sopenharmony_ci  b->size = size;
1720f66f451Sopenharmony_ci  b->len = len;
1730f66f451Sopenharmony_ci  b->alloc = type;
1740f66f451Sopenharmony_ci  b->data = data;
1750f66f451Sopenharmony_ci  next->len = len;
1760f66f451Sopenharmony_ci  next->data = data;
1770f66f451Sopenharmony_ci
1780f66f451Sopenharmony_ci  //mem blocks can be just added unordered
1790f66f451Sopenharmony_ci  TT.text = (struct block_list *)dlist_add((struct double_list **)&TT.text,
1800f66f451Sopenharmony_ci    (char *)b);
1810f66f451Sopenharmony_ci
1820f66f451Sopenharmony_ci  if (!s) {
1830f66f451Sopenharmony_ci    TT.slices = (struct slice_list *)dlist_add(
1840f66f451Sopenharmony_ci      (struct double_list **)&TT.slices,
1850f66f451Sopenharmony_ci      (char *)next);
1860f66f451Sopenharmony_ci  } else {
1870f66f451Sopenharmony_ci    size_t pos = 0;
1880f66f451Sopenharmony_ci    //search insertation point for slice
1890f66f451Sopenharmony_ci    do {
1900f66f451Sopenharmony_ci      if (pos<=offset && pos+s->node->len>offset) break;
1910f66f451Sopenharmony_ci      pos += s->node->len;
1920f66f451Sopenharmony_ci      s = s->next;
1930f66f451Sopenharmony_ci      if (s == TT.slices) return -1; //error out of bounds
1940f66f451Sopenharmony_ci    } while (1);
1950f66f451Sopenharmony_ci    //need to cut previous slice into 2 since insert is in middle
1960f66f451Sopenharmony_ci    if (pos+s->node->len>offset && pos!=offset) {
1970f66f451Sopenharmony_ci      struct slice *tail = xmalloc(sizeof(struct slice));
1980f66f451Sopenharmony_ci      tail->len = s->node->len-(offset-pos);
1990f66f451Sopenharmony_ci      tail->data = s->node->data+(offset-pos);
2000f66f451Sopenharmony_ci      s->node->len = offset-pos;
2010f66f451Sopenharmony_ci      //pos = offset;
2020f66f451Sopenharmony_ci      s = (struct slice_list *)dlist_add_after(
2030f66f451Sopenharmony_ci        (struct double_list **)&TT.slices,
2040f66f451Sopenharmony_ci        (struct double_list **)&s,
2050f66f451Sopenharmony_ci        (char *)tail);
2060f66f451Sopenharmony_ci
2070f66f451Sopenharmony_ci      s = (struct slice_list *)dlist_add_before(
2080f66f451Sopenharmony_ci        (struct double_list **)&TT.slices,
2090f66f451Sopenharmony_ci        (struct double_list **)&s,
2100f66f451Sopenharmony_ci        (char *)next);
2110f66f451Sopenharmony_ci    } else if (pos==offset) {
2120f66f451Sopenharmony_ci      // insert before
2130f66f451Sopenharmony_ci      s = (struct slice_list *)dlist_add_before(
2140f66f451Sopenharmony_ci        (struct double_list **)&TT.slices,
2150f66f451Sopenharmony_ci        (struct double_list **)&s,
2160f66f451Sopenharmony_ci        (char *)next);
2170f66f451Sopenharmony_ci    } else {
2180f66f451Sopenharmony_ci      // insert after
2190f66f451Sopenharmony_ci      s = (struct slice_list *)dlist_add_after((struct double_list **)&TT.slices,
2200f66f451Sopenharmony_ci      (struct double_list **)&s,
2210f66f451Sopenharmony_ci      (char *)next);
2220f66f451Sopenharmony_ci    }
2230f66f451Sopenharmony_ci  }
2240f66f451Sopenharmony_ci  return 0;
2250f66f451Sopenharmony_ci}
2260f66f451Sopenharmony_ci
2270f66f451Sopenharmony_ci// this will not free any memory
2280f66f451Sopenharmony_ci// will only create more slices depending on position
2290f66f451Sopenharmony_cistatic int cut_str(size_t offset, size_t len)
2300f66f451Sopenharmony_ci{
2310f66f451Sopenharmony_ci  struct slice_list *e, *s = TT.slices;
2320f66f451Sopenharmony_ci  size_t end = offset+len;
2330f66f451Sopenharmony_ci  size_t epos, spos = 0;
2340f66f451Sopenharmony_ci  if (!s) return -1;
2350f66f451Sopenharmony_ci
2360f66f451Sopenharmony_ci  //find start and end slices
2370f66f451Sopenharmony_ci  for (;;) {
2380f66f451Sopenharmony_ci    if (spos<=offset && spos+s->node->len>offset) break;
2390f66f451Sopenharmony_ci    spos += s->node->len;
2400f66f451Sopenharmony_ci    s = s->next;
2410f66f451Sopenharmony_ci
2420f66f451Sopenharmony_ci    if (s == TT.slices) return -1; //error out of bounds
2430f66f451Sopenharmony_ci  }
2440f66f451Sopenharmony_ci
2450f66f451Sopenharmony_ci  for (e = s, epos = spos; ; ) {
2460f66f451Sopenharmony_ci    if (epos<=end && epos+e->node->len>end) break;
2470f66f451Sopenharmony_ci    epos += e->node->len;
2480f66f451Sopenharmony_ci    e = e->next;
2490f66f451Sopenharmony_ci
2500f66f451Sopenharmony_ci    if (e == TT.slices) return -1; //error out of bounds
2510f66f451Sopenharmony_ci  }
2520f66f451Sopenharmony_ci
2530f66f451Sopenharmony_ci  for (;;) {
2540f66f451Sopenharmony_ci    if (spos == offset && ( end >= spos+s->node->len)) {
2550f66f451Sopenharmony_ci      //cut full
2560f66f451Sopenharmony_ci      spos += s->node->len;
2570f66f451Sopenharmony_ci      offset += s->node->len;
2580f66f451Sopenharmony_ci      s = dlist_pop(&s);
2590f66f451Sopenharmony_ci      if (s == TT.slices) TT.slices = s->next;
2600f66f451Sopenharmony_ci
2610f66f451Sopenharmony_ci    } else if (spos < offset && ( end >= spos+s->node->len)) {
2620f66f451Sopenharmony_ci      //cut end
2630f66f451Sopenharmony_ci      size_t clip = s->node->len - (offset - spos);
2640f66f451Sopenharmony_ci      offset = spos+s->node->len;
2650f66f451Sopenharmony_ci      spos += s->node->len;
2660f66f451Sopenharmony_ci      s->node->len -= clip;
2670f66f451Sopenharmony_ci    } else if (spos == offset && s == e) {
2680f66f451Sopenharmony_ci      //cut begin
2690f66f451Sopenharmony_ci      size_t clip = end - offset;
2700f66f451Sopenharmony_ci      s->node->len -= clip;
2710f66f451Sopenharmony_ci      s->node->data += clip;
2720f66f451Sopenharmony_ci      break;
2730f66f451Sopenharmony_ci    } else {
2740f66f451Sopenharmony_ci      //cut middle
2750f66f451Sopenharmony_ci      struct slice *tail = xmalloc(sizeof(struct slice));
2760f66f451Sopenharmony_ci      size_t clip = end-offset;
2770f66f451Sopenharmony_ci      tail->len = s->node->len-(offset-spos)-clip;
2780f66f451Sopenharmony_ci      tail->data = s->node->data+(offset-spos)+clip;
2790f66f451Sopenharmony_ci      s->node->len = offset-spos; //wrong?
2800f66f451Sopenharmony_ci      s = (struct slice_list *)dlist_add_after(
2810f66f451Sopenharmony_ci        (struct double_list **)&TT.slices,
2820f66f451Sopenharmony_ci        (struct double_list **)&s,
2830f66f451Sopenharmony_ci        (char *)tail);
2840f66f451Sopenharmony_ci      break;
2850f66f451Sopenharmony_ci    }
2860f66f451Sopenharmony_ci    if (s == e) break;
2870f66f451Sopenharmony_ci
2880f66f451Sopenharmony_ci    s = s->next;
2890f66f451Sopenharmony_ci  }
2900f66f451Sopenharmony_ci
2910f66f451Sopenharmony_ci  return 0;
2920f66f451Sopenharmony_ci}
2930f66f451Sopenharmony_ci
2940f66f451Sopenharmony_ci//find offset position in slices
2950f66f451Sopenharmony_cistatic struct slice_list *slice_offset(size_t *start, size_t offset)
2960f66f451Sopenharmony_ci{
2970f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
2980f66f451Sopenharmony_ci  size_t spos = 0;
2990f66f451Sopenharmony_ci
3000f66f451Sopenharmony_ci  //find start
3010f66f451Sopenharmony_ci  for ( ;s ; ) {
3020f66f451Sopenharmony_ci    if (spos<=offset && spos+s->node->len>offset) break;
3030f66f451Sopenharmony_ci
3040f66f451Sopenharmony_ci    spos += s->node->len;
3050f66f451Sopenharmony_ci    s = s->next;
3060f66f451Sopenharmony_ci
3070f66f451Sopenharmony_ci    if (s == TT.slices) s = 0; //error out of bounds
3080f66f451Sopenharmony_ci  }
3090f66f451Sopenharmony_ci  if (s) *start = spos;
3100f66f451Sopenharmony_ci  return s;
3110f66f451Sopenharmony_ci}
3120f66f451Sopenharmony_ci
3130f66f451Sopenharmony_cistatic size_t text_strchr(size_t offset, char c)
3140f66f451Sopenharmony_ci{
3150f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
3160f66f451Sopenharmony_ci  size_t epos, spos = 0;
3170f66f451Sopenharmony_ci  int i = 0;
3180f66f451Sopenharmony_ci
3190f66f451Sopenharmony_ci  //find start
3200f66f451Sopenharmony_ci  if (!(s = slice_offset(&spos, offset))) return SIZE_MAX;
3210f66f451Sopenharmony_ci
3220f66f451Sopenharmony_ci  i = offset-spos;
3230f66f451Sopenharmony_ci  epos = spos+i;
3240f66f451Sopenharmony_ci  do {
3250f66f451Sopenharmony_ci    for (; i < s->node->len; i++, epos++)
3260f66f451Sopenharmony_ci      if (s->node->data[i] == c) return epos;
3270f66f451Sopenharmony_ci    s = s->next;
3280f66f451Sopenharmony_ci    i = 0;
3290f66f451Sopenharmony_ci  } while (s != TT.slices);
3300f66f451Sopenharmony_ci
3310f66f451Sopenharmony_ci  return SIZE_MAX;
3320f66f451Sopenharmony_ci}
3330f66f451Sopenharmony_ci
3340f66f451Sopenharmony_cistatic size_t text_strrchr(size_t offset, char c)
3350f66f451Sopenharmony_ci{
3360f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
3370f66f451Sopenharmony_ci  size_t epos, spos = 0;
3380f66f451Sopenharmony_ci  int i = 0;
3390f66f451Sopenharmony_ci
3400f66f451Sopenharmony_ci  //find start
3410f66f451Sopenharmony_ci  if (!(s = slice_offset(&spos, offset))) return SIZE_MAX;
3420f66f451Sopenharmony_ci
3430f66f451Sopenharmony_ci  i = offset-spos;
3440f66f451Sopenharmony_ci  epos = spos+i;
3450f66f451Sopenharmony_ci  do {
3460f66f451Sopenharmony_ci    for (; i >= 0; i--, epos--)
3470f66f451Sopenharmony_ci      if (s->node->data[i] == c) return epos;
3480f66f451Sopenharmony_ci    s = s->prev;
3490f66f451Sopenharmony_ci    i = s->node->len-1;
3500f66f451Sopenharmony_ci  } while (s != TT.slices->prev); //tail
3510f66f451Sopenharmony_ci
3520f66f451Sopenharmony_ci  return SIZE_MAX;
3530f66f451Sopenharmony_ci}
3540f66f451Sopenharmony_ci
3550f66f451Sopenharmony_cistatic size_t text_filesize()
3560f66f451Sopenharmony_ci{
3570f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
3580f66f451Sopenharmony_ci  size_t pos = 0;
3590f66f451Sopenharmony_ci  if (s) do {
3600f66f451Sopenharmony_ci
3610f66f451Sopenharmony_ci    pos += s->node->len;
3620f66f451Sopenharmony_ci    s = s->next;
3630f66f451Sopenharmony_ci
3640f66f451Sopenharmony_ci  } while (s != TT.slices);
3650f66f451Sopenharmony_ci
3660f66f451Sopenharmony_ci  return pos;
3670f66f451Sopenharmony_ci}
3680f66f451Sopenharmony_ci
3690f66f451Sopenharmony_cistatic int text_count(size_t start, size_t end, char c)
3700f66f451Sopenharmony_ci{
3710f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
3720f66f451Sopenharmony_ci  size_t i, count = 0, spos = 0;
3730f66f451Sopenharmony_ci  if (!(s = slice_offset(&spos, start))) return 0;
3740f66f451Sopenharmony_ci  i = start-spos;
3750f66f451Sopenharmony_ci  if (s) do {
3760f66f451Sopenharmony_ci    for (; i < s->node->len && spos+i<end; i++)
3770f66f451Sopenharmony_ci      if (s->node->data[i] == c) count++;
3780f66f451Sopenharmony_ci    if (spos+i>=end) return count;
3790f66f451Sopenharmony_ci
3800f66f451Sopenharmony_ci    spos += s->node->len;
3810f66f451Sopenharmony_ci    i = 0;
3820f66f451Sopenharmony_ci    s = s->next;
3830f66f451Sopenharmony_ci
3840f66f451Sopenharmony_ci  } while (s != TT.slices);
3850f66f451Sopenharmony_ci
3860f66f451Sopenharmony_ci  return count;
3870f66f451Sopenharmony_ci}
3880f66f451Sopenharmony_ci
3890f66f451Sopenharmony_cistatic char text_byte(size_t offset)
3900f66f451Sopenharmony_ci{
3910f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
3920f66f451Sopenharmony_ci  size_t spos = 0;
3930f66f451Sopenharmony_ci  //find start
3940f66f451Sopenharmony_ci  if (!(s = slice_offset(&spos, offset))) return 0;
3950f66f451Sopenharmony_ci  return s->node->data[offset-spos];
3960f66f451Sopenharmony_ci}
3970f66f451Sopenharmony_ci
3980f66f451Sopenharmony_ci//utf-8 codepoint -1 if not valid, 0 if out_of_bounds, len if valid
3990f66f451Sopenharmony_ci//copies data to dest if dest is not 0
4000f66f451Sopenharmony_cistatic int text_codepoint(char *dest, size_t offset)
4010f66f451Sopenharmony_ci{
4020f66f451Sopenharmony_ci  char scratch[8] = {0};
4030f66f451Sopenharmony_ci  int state = 0, finished = 0;
4040f66f451Sopenharmony_ci
4050f66f451Sopenharmony_ci  for (;!(finished = utf8_dec(text_byte(offset), scratch, &state)); offset++)
4060f66f451Sopenharmony_ci    if (!state) return -1;
4070f66f451Sopenharmony_ci
4080f66f451Sopenharmony_ci  if (!finished && !state) return -1;
4090f66f451Sopenharmony_ci  if (dest) memcpy(dest, scratch, 8);
4100f66f451Sopenharmony_ci
4110f66f451Sopenharmony_ci  return strlen(scratch);
4120f66f451Sopenharmony_ci}
4130f66f451Sopenharmony_ci
4140f66f451Sopenharmony_cistatic size_t text_sol(size_t offset)
4150f66f451Sopenharmony_ci{
4160f66f451Sopenharmony_ci  size_t pos;
4170f66f451Sopenharmony_ci  if (!TT.filesize || !offset) return 0;
4180f66f451Sopenharmony_ci  else if (TT.filesize <= offset) return TT.filesize-1;
4190f66f451Sopenharmony_ci  else if ((pos = text_strrchr(offset-1, '\n')) == SIZE_MAX) return 0;
4200f66f451Sopenharmony_ci  else if (pos < offset) return pos+1;
4210f66f451Sopenharmony_ci  return offset;
4220f66f451Sopenharmony_ci}
4230f66f451Sopenharmony_ci
4240f66f451Sopenharmony_cistatic size_t text_eol(size_t offset)
4250f66f451Sopenharmony_ci{
4260f66f451Sopenharmony_ci  if (!TT.filesize) offset = 1;
4270f66f451Sopenharmony_ci  else if (TT.filesize <= offset) return TT.filesize-1;
4280f66f451Sopenharmony_ci  else if ((offset = text_strchr(offset, '\n')) == SIZE_MAX)
4290f66f451Sopenharmony_ci    return TT.filesize-1;
4300f66f451Sopenharmony_ci  return offset;
4310f66f451Sopenharmony_ci}
4320f66f451Sopenharmony_ci
4330f66f451Sopenharmony_cistatic size_t text_nsol(size_t offset)
4340f66f451Sopenharmony_ci{
4350f66f451Sopenharmony_ci  offset = text_eol(offset);
4360f66f451Sopenharmony_ci  if (text_byte(offset) == '\n') offset++;
4370f66f451Sopenharmony_ci  if (offset >= TT.filesize) offset--;
4380f66f451Sopenharmony_ci  return offset;
4390f66f451Sopenharmony_ci}
4400f66f451Sopenharmony_ci
4410f66f451Sopenharmony_cistatic size_t text_psol(size_t offset)
4420f66f451Sopenharmony_ci{
4430f66f451Sopenharmony_ci  offset = text_sol(offset);
4440f66f451Sopenharmony_ci  if (offset) offset--;
4450f66f451Sopenharmony_ci  if (offset && text_byte(offset-1) != '\n') offset = text_sol(offset-1);
4460f66f451Sopenharmony_ci  return offset;
4470f66f451Sopenharmony_ci}
4480f66f451Sopenharmony_ci
4490f66f451Sopenharmony_cistatic size_t text_getline(char *dest, size_t offset, size_t max_len)
4500f66f451Sopenharmony_ci{
4510f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
4520f66f451Sopenharmony_ci  size_t end, spos = 0;
4530f66f451Sopenharmony_ci  int i, j = 0;
4540f66f451Sopenharmony_ci
4550f66f451Sopenharmony_ci  if (dest) *dest = 0;
4560f66f451Sopenharmony_ci
4570f66f451Sopenharmony_ci  if (!s) return 0;
4580f66f451Sopenharmony_ci  if ((end = text_strchr(offset, '\n')) == SIZE_MAX)
4590f66f451Sopenharmony_ci    if ((end = TT.filesize)  > offset+max_len) return 0;
4600f66f451Sopenharmony_ci
4610f66f451Sopenharmony_ci  //find start
4620f66f451Sopenharmony_ci  if (!(s = slice_offset(&spos, offset))) return 0;
4630f66f451Sopenharmony_ci
4640f66f451Sopenharmony_ci  i = offset-spos;
4650f66f451Sopenharmony_ci  j = end-offset+1;
4660f66f451Sopenharmony_ci  if (dest) do {
4670f66f451Sopenharmony_ci    for (; i < s->node->len && j; i++, j--, dest++)
4680f66f451Sopenharmony_ci      *dest = s->node->data[i];
4690f66f451Sopenharmony_ci    s = s->next;
4700f66f451Sopenharmony_ci    i = 0;
4710f66f451Sopenharmony_ci  } while (s != TT.slices && j);
4720f66f451Sopenharmony_ci
4730f66f451Sopenharmony_ci  if (dest) *dest = 0;
4740f66f451Sopenharmony_ci
4750f66f451Sopenharmony_ci  return end-offset;
4760f66f451Sopenharmony_ci}
4770f66f451Sopenharmony_ci
4780f66f451Sopenharmony_ci//copying is needed when file has lot of inserts that are
4790f66f451Sopenharmony_ci//just few char long, but not always. Advanced search should
4800f66f451Sopenharmony_ci//check big slices directly and just copy edge cases.
4810f66f451Sopenharmony_ci//Also this is only line based search multiline
4820f66f451Sopenharmony_ci//and regexec should be done instead.
4830f66f451Sopenharmony_cistatic size_t text_strstr(size_t offset, char *str)
4840f66f451Sopenharmony_ci{
4850f66f451Sopenharmony_ci  size_t bytes, pos = offset;
4860f66f451Sopenharmony_ci  char *s = 0;
4870f66f451Sopenharmony_ci  do {
4880f66f451Sopenharmony_ci    bytes = text_getline(toybuf, pos, ARRAY_LEN(toybuf));
4890f66f451Sopenharmony_ci    if (!bytes) pos++; //empty line
4900f66f451Sopenharmony_ci    else if ((s = strstr(toybuf, str))) return pos+(s-toybuf);
4910f66f451Sopenharmony_ci    else pos += bytes;
4920f66f451Sopenharmony_ci  } while (pos < TT.filesize);
4930f66f451Sopenharmony_ci
4940f66f451Sopenharmony_ci  return SIZE_MAX;
4950f66f451Sopenharmony_ci}
4960f66f451Sopenharmony_ci
4970f66f451Sopenharmony_cistatic void block_list_free(void *node)
4980f66f451Sopenharmony_ci{
4990f66f451Sopenharmony_ci  struct block_list *d = node;
5000f66f451Sopenharmony_ci
5010f66f451Sopenharmony_ci  if (d->node->alloc == HEAP) free((void *)d->node->data);
5020f66f451Sopenharmony_ci  else if (d->node->alloc == MMAP) munmap((void *)d->node->data, d->node->size);
5030f66f451Sopenharmony_ci
5040f66f451Sopenharmony_ci  free(d->node);
5050f66f451Sopenharmony_ci  free(d);
5060f66f451Sopenharmony_ci}
5070f66f451Sopenharmony_ci
5080f66f451Sopenharmony_cistatic void linelist_unload()
5090f66f451Sopenharmony_ci{
5100f66f451Sopenharmony_ci  llist_traverse((void *)TT.slices, llist_free_double);
5110f66f451Sopenharmony_ci  llist_traverse((void *)TT.text, block_list_free);
5120f66f451Sopenharmony_ci  TT.slices = 0, TT.text = 0;
5130f66f451Sopenharmony_ci}
5140f66f451Sopenharmony_ci
5150f66f451Sopenharmony_cistatic int linelist_load(char *filename)
5160f66f451Sopenharmony_ci{
5170f66f451Sopenharmony_ci  if (!filename) filename = (char*)*toys.optargs;
5180f66f451Sopenharmony_ci
5190f66f451Sopenharmony_ci  if (filename) {
5200f66f451Sopenharmony_ci    int fd = open(filename, O_RDONLY);
5210f66f451Sopenharmony_ci    long long size;
5220f66f451Sopenharmony_ci
5230f66f451Sopenharmony_ci    if (fd == -1 || !(size = fdlength(fd))) {
5240f66f451Sopenharmony_ci      insert_str("", 0, 0, 0, STACK);
5250f66f451Sopenharmony_ci      TT.filesize = 0;
5260f66f451Sopenharmony_ci
5270f66f451Sopenharmony_ci      return 0;
5280f66f451Sopenharmony_ci    }
5290f66f451Sopenharmony_ci    insert_str(xmmap(0, size, PROT_READ, MAP_SHARED, fd, 0), 0, size,size,MMAP);
5300f66f451Sopenharmony_ci    xclose(fd);
5310f66f451Sopenharmony_ci    TT.filesize = text_filesize();
5320f66f451Sopenharmony_ci  }
5330f66f451Sopenharmony_ci
5340f66f451Sopenharmony_ci  return 1;
5350f66f451Sopenharmony_ci}
5360f66f451Sopenharmony_ci
5370f66f451Sopenharmony_cistatic void write_file(char *filename)
5380f66f451Sopenharmony_ci{
5390f66f451Sopenharmony_ci  struct slice_list *s = TT.slices;
5400f66f451Sopenharmony_ci  struct stat st;
5410f66f451Sopenharmony_ci  int fd = 0;
5420f66f451Sopenharmony_ci  if (!s) return;
5430f66f451Sopenharmony_ci
5440f66f451Sopenharmony_ci  if (!filename) filename = (char*)*toys.optargs;
5450f66f451Sopenharmony_ci
5460f66f451Sopenharmony_ci  sprintf(toybuf, "%s.swp", filename);
5470f66f451Sopenharmony_ci
5480f66f451Sopenharmony_ci  if ( (fd = xopen(toybuf, O_WRONLY | O_CREAT | O_TRUNC)) <0) return;
5490f66f451Sopenharmony_ci
5500f66f451Sopenharmony_ci  do {
5510f66f451Sopenharmony_ci    xwrite(fd, (void *)s->node->data, s->node->len );
5520f66f451Sopenharmony_ci    s = s->next;
5530f66f451Sopenharmony_ci  } while (s != TT.slices);
5540f66f451Sopenharmony_ci
5550f66f451Sopenharmony_ci  linelist_unload();
5560f66f451Sopenharmony_ci
5570f66f451Sopenharmony_ci  xclose(fd);
5580f66f451Sopenharmony_ci  if (!stat(filename, &st)) chmod(toybuf, st.st_mode);
5590f66f451Sopenharmony_ci  else chmod(toybuf, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
5600f66f451Sopenharmony_ci  xrename(toybuf, filename);
5610f66f451Sopenharmony_ci  linelist_load(filename);
5620f66f451Sopenharmony_ci}
5630f66f451Sopenharmony_ci
5640f66f451Sopenharmony_ci//jump into valid offset index
5650f66f451Sopenharmony_ci//and valid utf8 codepoint
5660f66f451Sopenharmony_cistatic void check_cursor_bounds()
5670f66f451Sopenharmony_ci{
5680f66f451Sopenharmony_ci  char buf[8] = {0};
5690f66f451Sopenharmony_ci  int len, width = 0;
5700f66f451Sopenharmony_ci  if (!TT.filesize) TT.cursor = 0;
5710f66f451Sopenharmony_ci
5720f66f451Sopenharmony_ci  for (;;) {
5730f66f451Sopenharmony_ci    if (TT.cursor < 1) {
5740f66f451Sopenharmony_ci      TT.cursor = 0;
5750f66f451Sopenharmony_ci      return;
5760f66f451Sopenharmony_ci    } else if (TT.cursor >= TT.filesize-1) {
5770f66f451Sopenharmony_ci      TT.cursor = TT.filesize-1;
5780f66f451Sopenharmony_ci      return;
5790f66f451Sopenharmony_ci    }
5800f66f451Sopenharmony_ci    if ((len = text_codepoint(buf, TT.cursor)) < 1) {
5810f66f451Sopenharmony_ci      TT.cursor--; //we are not in valid data try jump over
5820f66f451Sopenharmony_ci      continue;
5830f66f451Sopenharmony_ci    }
5840f66f451Sopenharmony_ci    if (utf8_lnw(&width, buf, len) && width) break;
5850f66f451Sopenharmony_ci    else TT.cursor--; //combine char jump over
5860f66f451Sopenharmony_ci  }
5870f66f451Sopenharmony_ci}
5880f66f451Sopenharmony_ci
5890f66f451Sopenharmony_ci// TT.vi_mov_flag is used for special cases when certain move
5900f66f451Sopenharmony_ci// acts differently depending is there DELETE/YANK or NOP
5910f66f451Sopenharmony_ci// Also commands such as G does not default to count0=1
5920f66f451Sopenharmony_ci// 0x1 = Command needs argument (f,F,r...)
5930f66f451Sopenharmony_ci// 0x2 = Move 1 right on yank/delete/insert (e, $...)
5940f66f451Sopenharmony_ci// 0x4 = yank/delete last line fully
5950f66f451Sopenharmony_ci// 0x10000000 = redraw after cursor needed
5960f66f451Sopenharmony_ci// 0x20000000 = full redraw needed
5970f66f451Sopenharmony_ci// 0x40000000 = count0 not given
5980f66f451Sopenharmony_ci// 0x80000000 = move was reverse
5990f66f451Sopenharmony_ci
6000f66f451Sopenharmony_ci//TODO rewrite the logic, difficulties counting lines
6010f66f451Sopenharmony_ci//and with big files scroll should not rely in knowing
6020f66f451Sopenharmony_ci//absoluteline numbers
6030f66f451Sopenharmony_cistatic void adjust_screen_buffer()
6040f66f451Sopenharmony_ci{
6050f66f451Sopenharmony_ci  size_t c, s;
6060f66f451Sopenharmony_ci  TT.cur_row = 0, TT.scr_row = 0;
6070f66f451Sopenharmony_ci  if (!TT.cursor) {
6080f66f451Sopenharmony_ci    TT.screen = 0;
6090f66f451Sopenharmony_ci    TT.vi_mov_flag = 0x20000000;
6100f66f451Sopenharmony_ci    return;
6110f66f451Sopenharmony_ci  } else if (TT.screen > (1<<18) || TT.cursor > (1<<18)) {
6120f66f451Sopenharmony_ci     //give up, file is big, do full redraw
6130f66f451Sopenharmony_ci
6140f66f451Sopenharmony_ci    TT.screen = text_strrchr(TT.cursor-1, '\n')+1;
6150f66f451Sopenharmony_ci    TT.vi_mov_flag = 0x20000000;
6160f66f451Sopenharmony_ci    return;
6170f66f451Sopenharmony_ci  }
6180f66f451Sopenharmony_ci
6190f66f451Sopenharmony_ci  s = text_count(0, TT.screen, '\n');
6200f66f451Sopenharmony_ci  c = text_count(0, TT.cursor, '\n');
6210f66f451Sopenharmony_ci  if (s >= c) {
6220f66f451Sopenharmony_ci    TT.screen = text_strrchr(TT.cursor-1, '\n')+1;
6230f66f451Sopenharmony_ci    s = c;
6240f66f451Sopenharmony_ci    TT.vi_mov_flag = 0x20000000; //TODO I disabled scroll
6250f66f451Sopenharmony_ci  } else {
6260f66f451Sopenharmony_ci    int distance = c-s+1;
6270f66f451Sopenharmony_ci    if (distance > (int)TT.screen_height) {
6280f66f451Sopenharmony_ci      int n, adj = distance-TT.screen_height;
6290f66f451Sopenharmony_ci      TT.vi_mov_flag = 0x20000000; //TODO I disabled scroll
6300f66f451Sopenharmony_ci      for (;adj; adj--, s++)
6310f66f451Sopenharmony_ci        if ((n = text_strchr(TT.screen, '\n'))+1 > TT.screen)
6320f66f451Sopenharmony_ci          TT.screen = n+1;
6330f66f451Sopenharmony_ci    }
6340f66f451Sopenharmony_ci  }
6350f66f451Sopenharmony_ci
6360f66f451Sopenharmony_ci  TT.scr_row = s;
6370f66f451Sopenharmony_ci  TT.cur_row = c;
6380f66f451Sopenharmony_ci
6390f66f451Sopenharmony_ci}
6400f66f451Sopenharmony_ci
6410f66f451Sopenharmony_ci//TODO search yank buffer by register
6420f66f451Sopenharmony_ci//TODO yanks could be separate slices so no need to copy data
6430f66f451Sopenharmony_ci//now only supports default register
6440f66f451Sopenharmony_cistatic int vi_yank(char reg, size_t from, int flags)
6450f66f451Sopenharmony_ci{
6460f66f451Sopenharmony_ci  size_t start = from, end = TT.cursor;
6470f66f451Sopenharmony_ci  char *str;
6480f66f451Sopenharmony_ci
6490f66f451Sopenharmony_ci  memset(TT.yank.data, 0, TT.yank.alloc);
6500f66f451Sopenharmony_ci  if (TT.vi_mov_flag&0x80000000) start = TT.cursor, end = from;
6510f66f451Sopenharmony_ci  else TT.cursor = start; //yank moves cursor to left pos always?
6520f66f451Sopenharmony_ci
6530f66f451Sopenharmony_ci  if (TT.yank.alloc < end-from) {
6540f66f451Sopenharmony_ci    size_t new_bounds = (1+end-from)/1024;
6550f66f451Sopenharmony_ci    new_bounds += ((1+end-from)%1024) ? 1 : 0;
6560f66f451Sopenharmony_ci    new_bounds *= 1024;
6570f66f451Sopenharmony_ci    TT.yank.data = xrealloc(TT.yank.data, new_bounds);
6580f66f451Sopenharmony_ci    TT.yank.alloc = new_bounds;
6590f66f451Sopenharmony_ci  }
6600f66f451Sopenharmony_ci
6610f66f451Sopenharmony_ci  //this is naive copy
6620f66f451Sopenharmony_ci  for (str = TT.yank.data ; start<end; start++, str++) *str = text_byte(start);
6630f66f451Sopenharmony_ci
6640f66f451Sopenharmony_ci  *str = 0;
6650f66f451Sopenharmony_ci
6660f66f451Sopenharmony_ci  return 1;
6670f66f451Sopenharmony_ci}
6680f66f451Sopenharmony_ci
6690f66f451Sopenharmony_cistatic int vi_delete(char reg, size_t from, int flags)
6700f66f451Sopenharmony_ci{
6710f66f451Sopenharmony_ci  size_t start = from, end = TT.cursor;
6720f66f451Sopenharmony_ci
6730f66f451Sopenharmony_ci  vi_yank(reg, from, flags);
6740f66f451Sopenharmony_ci
6750f66f451Sopenharmony_ci  if (TT.vi_mov_flag&0x80000000)
6760f66f451Sopenharmony_ci    start = TT.cursor, end = from;
6770f66f451Sopenharmony_ci
6780f66f451Sopenharmony_ci  //pre adjust cursor move one right until at next valid rune
6790f66f451Sopenharmony_ci  if (TT.vi_mov_flag&2) {
6800f66f451Sopenharmony_ci    //TODO
6810f66f451Sopenharmony_ci  }
6820f66f451Sopenharmony_ci  //do slice cut
6830f66f451Sopenharmony_ci  cut_str(start, end-start);
6840f66f451Sopenharmony_ci
6850f66f451Sopenharmony_ci  //cursor is at start at after delete
6860f66f451Sopenharmony_ci  TT.cursor = start;
6870f66f451Sopenharmony_ci  TT.filesize = text_filesize();
6880f66f451Sopenharmony_ci  //find line start by strrchr(/n) ++
6890f66f451Sopenharmony_ci  //set cur_col with crunch_n_str maybe?
6900f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x30000000;
6910f66f451Sopenharmony_ci
6920f66f451Sopenharmony_ci  return 1;
6930f66f451Sopenharmony_ci}
6940f66f451Sopenharmony_ci
6950f66f451Sopenharmony_cistatic int vi_change(char reg, size_t to, int flags)
6960f66f451Sopenharmony_ci{
6970f66f451Sopenharmony_ci  vi_delete(reg, to, flags);
6980f66f451Sopenharmony_ci  TT.vi_mode = 2;
6990f66f451Sopenharmony_ci  return 1;
7000f66f451Sopenharmony_ci}
7010f66f451Sopenharmony_ci
7020f66f451Sopenharmony_cistatic int cur_left(int count0, int count1, char *unused)
7030f66f451Sopenharmony_ci{
7040f66f451Sopenharmony_ci  int count = count0*count1;
7050f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x80000000;
7060f66f451Sopenharmony_ci  for (;count && TT.cursor; count--) {
7070f66f451Sopenharmony_ci    TT.cursor--;
7080f66f451Sopenharmony_ci    if (text_byte(TT.cursor) == '\n') TT.cursor++;
7090f66f451Sopenharmony_ci    check_cursor_bounds();
7100f66f451Sopenharmony_ci  }
7110f66f451Sopenharmony_ci  return 1;
7120f66f451Sopenharmony_ci}
7130f66f451Sopenharmony_ci
7140f66f451Sopenharmony_cistatic int cur_right(int count0, int count1, char *unused)
7150f66f451Sopenharmony_ci{
7160f66f451Sopenharmony_ci  int count = count0*count1, len, width = 0;
7170f66f451Sopenharmony_ci  char buf[8] = {0};
7180f66f451Sopenharmony_ci
7190f66f451Sopenharmony_ci  for (;count; count--) {
7200f66f451Sopenharmony_ci    len = text_codepoint(buf, TT.cursor);
7210f66f451Sopenharmony_ci
7220f66f451Sopenharmony_ci    if (*buf == '\n') break;
7230f66f451Sopenharmony_ci    else if (len > 0) TT.cursor += len;
7240f66f451Sopenharmony_ci    else TT.cursor++;
7250f66f451Sopenharmony_ci
7260f66f451Sopenharmony_ci    for (;TT.cursor < TT.filesize;) {
7270f66f451Sopenharmony_ci      if ((len = text_codepoint(buf, TT.cursor)) < 1) {
7280f66f451Sopenharmony_ci        TT.cursor++; //we are not in valid data try jump over
7290f66f451Sopenharmony_ci        continue;
7300f66f451Sopenharmony_ci      }
7310f66f451Sopenharmony_ci
7320f66f451Sopenharmony_ci      if (utf8_lnw(&width, buf, len) && width) break;
7330f66f451Sopenharmony_ci      else TT.cursor += len;
7340f66f451Sopenharmony_ci    }
7350f66f451Sopenharmony_ci  }
7360f66f451Sopenharmony_ci  check_cursor_bounds();
7370f66f451Sopenharmony_ci  return 1;
7380f66f451Sopenharmony_ci}
7390f66f451Sopenharmony_ci
7400f66f451Sopenharmony_ci//TODO column shift
7410f66f451Sopenharmony_cistatic int cur_up(int count0, int count1, char *unused)
7420f66f451Sopenharmony_ci{
7430f66f451Sopenharmony_ci  int count = count0*count1;
7440f66f451Sopenharmony_ci  for (;count--;) TT.cursor = text_psol(TT.cursor);
7450f66f451Sopenharmony_ci
7460f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x80000000;
7470f66f451Sopenharmony_ci  check_cursor_bounds();
7480f66f451Sopenharmony_ci  return 1;
7490f66f451Sopenharmony_ci}
7500f66f451Sopenharmony_ci
7510f66f451Sopenharmony_ci//TODO column shift
7520f66f451Sopenharmony_cistatic int cur_down(int count0, int count1, char *unused)
7530f66f451Sopenharmony_ci{
7540f66f451Sopenharmony_ci  int count = count0*count1;
7550f66f451Sopenharmony_ci  for (;count--;) TT.cursor = text_nsol(TT.cursor);
7560f66f451Sopenharmony_ci  check_cursor_bounds();
7570f66f451Sopenharmony_ci  return 1;
7580f66f451Sopenharmony_ci}
7590f66f451Sopenharmony_ci
7600f66f451Sopenharmony_cistatic int vi_H(int count0, int count1, char *unused)
7610f66f451Sopenharmony_ci{
7620f66f451Sopenharmony_ci  TT.cursor = text_sol(TT.screen);
7630f66f451Sopenharmony_ci  return 1;
7640f66f451Sopenharmony_ci}
7650f66f451Sopenharmony_ci
7660f66f451Sopenharmony_cistatic int vi_L(int count0, int count1, char *unused)
7670f66f451Sopenharmony_ci{
7680f66f451Sopenharmony_ci  TT.cursor = text_sol(TT.screen);
7690f66f451Sopenharmony_ci  cur_down(TT.screen_height-1, 1, 0);
7700f66f451Sopenharmony_ci  return 1;
7710f66f451Sopenharmony_ci}
7720f66f451Sopenharmony_ci
7730f66f451Sopenharmony_cistatic int vi_M(int count0, int count1, char *unused)
7740f66f451Sopenharmony_ci{
7750f66f451Sopenharmony_ci  TT.cursor = text_sol(TT.screen);
7760f66f451Sopenharmony_ci  cur_down(TT.screen_height/2, 1, 0);
7770f66f451Sopenharmony_ci  return 1;
7780f66f451Sopenharmony_ci}
7790f66f451Sopenharmony_ci
7800f66f451Sopenharmony_cistatic int search_str(char *s)
7810f66f451Sopenharmony_ci{
7820f66f451Sopenharmony_ci  size_t pos = text_strstr(TT.cursor+1, s);
7830f66f451Sopenharmony_ci
7840f66f451Sopenharmony_ci  if (TT.last_search != s) {
7850f66f451Sopenharmony_ci    free(TT.last_search);
7860f66f451Sopenharmony_ci    TT.last_search = xstrdup(s);
7870f66f451Sopenharmony_ci  }
7880f66f451Sopenharmony_ci
7890f66f451Sopenharmony_ci  if (pos != SIZE_MAX) TT.cursor = pos;
7900f66f451Sopenharmony_ci  check_cursor_bounds();
7910f66f451Sopenharmony_ci  return 0;
7920f66f451Sopenharmony_ci}
7930f66f451Sopenharmony_ci
7940f66f451Sopenharmony_cistatic int vi_yy(char reg, int count0, int count1)
7950f66f451Sopenharmony_ci{
7960f66f451Sopenharmony_ci  size_t history = TT.cursor;
7970f66f451Sopenharmony_ci  size_t pos = text_sol(TT.cursor); //go left to first char on line
7980f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x4;
7990f66f451Sopenharmony_ci
8000f66f451Sopenharmony_ci  for (;count0; count0--) TT.cursor = text_nsol(TT.cursor);
8010f66f451Sopenharmony_ci
8020f66f451Sopenharmony_ci  vi_yank(reg, pos, 0);
8030f66f451Sopenharmony_ci
8040f66f451Sopenharmony_ci  TT.cursor = history;
8050f66f451Sopenharmony_ci  return 1;
8060f66f451Sopenharmony_ci}
8070f66f451Sopenharmony_ci
8080f66f451Sopenharmony_cistatic int vi_dd(char reg, int count0, int count1)
8090f66f451Sopenharmony_ci{
8100f66f451Sopenharmony_ci  size_t pos = text_sol(TT.cursor); //go left to first char on line
8110f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x30000000;
8120f66f451Sopenharmony_ci
8130f66f451Sopenharmony_ci  for (;count0; count0--) TT.cursor = text_nsol(TT.cursor);
8140f66f451Sopenharmony_ci
8150f66f451Sopenharmony_ci  if (pos == TT.cursor && TT.filesize) pos--;
8160f66f451Sopenharmony_ci  vi_delete(reg, pos, 0);
8170f66f451Sopenharmony_ci  check_cursor_bounds();
8180f66f451Sopenharmony_ci  return 1;
8190f66f451Sopenharmony_ci}
8200f66f451Sopenharmony_ci
8210f66f451Sopenharmony_cistatic int vi_x(char reg, int count0, int count1)
8220f66f451Sopenharmony_ci{
8230f66f451Sopenharmony_ci  size_t from = TT.cursor;
8240f66f451Sopenharmony_ci
8250f66f451Sopenharmony_ci  if (text_byte(TT.cursor) == '\n') {
8260f66f451Sopenharmony_ci    cur_left(count0-1, 1, 0);
8270f66f451Sopenharmony_ci  }
8280f66f451Sopenharmony_ci  else {
8290f66f451Sopenharmony_ci    cur_right(count0-1, 1, 0);
8300f66f451Sopenharmony_ci    if (text_byte(TT.cursor) == '\n') TT.vi_mov_flag |= 2;
8310f66f451Sopenharmony_ci    else cur_right(1, 1, 0);
8320f66f451Sopenharmony_ci  }
8330f66f451Sopenharmony_ci
8340f66f451Sopenharmony_ci  vi_delete(reg, from, 0);
8350f66f451Sopenharmony_ci  check_cursor_bounds();
8360f66f451Sopenharmony_ci  return 1;
8370f66f451Sopenharmony_ci}
8380f66f451Sopenharmony_ci
8390f66f451Sopenharmony_cistatic int vi_movw(int count0, int count1, char *unused)
8400f66f451Sopenharmony_ci{
8410f66f451Sopenharmony_ci  int count = count0*count1;
8420f66f451Sopenharmony_ci  while (count--) {
8430f66f451Sopenharmony_ci    char c = text_byte(TT.cursor);
8440f66f451Sopenharmony_ci    do {
8450f66f451Sopenharmony_ci      if (TT.cursor > TT.filesize-1) break;
8460f66f451Sopenharmony_ci      //if at empty jump to non empty
8470f66f451Sopenharmony_ci      if (c == '\n') {
8480f66f451Sopenharmony_ci        if (++TT.cursor > TT.filesize-1) break;
8490f66f451Sopenharmony_ci        if ((c = text_byte(TT.cursor)) == '\n') break;
8500f66f451Sopenharmony_ci        continue;
8510f66f451Sopenharmony_ci      } else if (strchr(blank, c)) do {
8520f66f451Sopenharmony_ci        if (++TT.cursor > TT.filesize-1) break;
8530f66f451Sopenharmony_ci        c = text_byte(TT.cursor);
8540f66f451Sopenharmony_ci      } while (strchr(blank, c));
8550f66f451Sopenharmony_ci      //if at special jump to non special
8560f66f451Sopenharmony_ci      else if (strchr(specials, c)) do {
8570f66f451Sopenharmony_ci        if (++TT.cursor > TT.filesize-1) break;
8580f66f451Sopenharmony_ci        c = text_byte(TT.cursor);
8590f66f451Sopenharmony_ci      } while (strchr(specials, c));
8600f66f451Sopenharmony_ci      //else jump to empty or spesial
8610f66f451Sopenharmony_ci      else do {
8620f66f451Sopenharmony_ci        if (++TT.cursor > TT.filesize-1) break;
8630f66f451Sopenharmony_ci        c = text_byte(TT.cursor);
8640f66f451Sopenharmony_ci      } while (c && !strchr(blank, c) && !strchr(specials, c));
8650f66f451Sopenharmony_ci
8660f66f451Sopenharmony_ci    } while (strchr(blank, c) && c != '\n'); //never stop at empty
8670f66f451Sopenharmony_ci  }
8680f66f451Sopenharmony_ci  check_cursor_bounds();
8690f66f451Sopenharmony_ci  return 1;
8700f66f451Sopenharmony_ci}
8710f66f451Sopenharmony_ci
8720f66f451Sopenharmony_cistatic int vi_movb(int count0, int count1, char *unused)
8730f66f451Sopenharmony_ci{
8740f66f451Sopenharmony_ci  int count = count0*count1;
8750f66f451Sopenharmony_ci  int type = 0;
8760f66f451Sopenharmony_ci  char c;
8770f66f451Sopenharmony_ci  while (count--) {
8780f66f451Sopenharmony_ci    c = text_byte(TT.cursor);
8790f66f451Sopenharmony_ci    do {
8800f66f451Sopenharmony_ci      if (!TT.cursor) break;
8810f66f451Sopenharmony_ci      //if at empty jump to non empty
8820f66f451Sopenharmony_ci      if (strchr(blank, c)) do {
8830f66f451Sopenharmony_ci        if (!--TT.cursor) break;
8840f66f451Sopenharmony_ci        c = text_byte(TT.cursor);
8850f66f451Sopenharmony_ci      } while (strchr(blank, c));
8860f66f451Sopenharmony_ci      //if at special jump to non special
8870f66f451Sopenharmony_ci      else if (strchr(specials, c)) do {
8880f66f451Sopenharmony_ci        if (!--TT.cursor) break;
8890f66f451Sopenharmony_ci        type = 0;
8900f66f451Sopenharmony_ci        c = text_byte(TT.cursor);
8910f66f451Sopenharmony_ci      } while (strchr(specials, c));
8920f66f451Sopenharmony_ci      //else jump to empty or spesial
8930f66f451Sopenharmony_ci      else do {
8940f66f451Sopenharmony_ci        if (!--TT.cursor) break;
8950f66f451Sopenharmony_ci        type = 1;
8960f66f451Sopenharmony_ci        c = text_byte(TT.cursor);
8970f66f451Sopenharmony_ci      } while (!strchr(blank, c) && !strchr(specials, c));
8980f66f451Sopenharmony_ci
8990f66f451Sopenharmony_ci    } while (strchr(blank, c)); //never stop at empty
9000f66f451Sopenharmony_ci  }
9010f66f451Sopenharmony_ci  //find first
9020f66f451Sopenharmony_ci  for (;TT.cursor; TT.cursor--) {
9030f66f451Sopenharmony_ci    c = text_byte(TT.cursor-1);
9040f66f451Sopenharmony_ci    if (type && !strchr(blank, c) && !strchr(specials, c)) break;
9050f66f451Sopenharmony_ci    else if (!type && !strchr(specials, c)) break;
9060f66f451Sopenharmony_ci  }
9070f66f451Sopenharmony_ci
9080f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x80000000;
9090f66f451Sopenharmony_ci  check_cursor_bounds();
9100f66f451Sopenharmony_ci  return 1;
9110f66f451Sopenharmony_ci}
9120f66f451Sopenharmony_ci
9130f66f451Sopenharmony_cistatic int vi_move(int count0, int count1, char *unused)
9140f66f451Sopenharmony_ci{
9150f66f451Sopenharmony_ci  int count = count0*count1;
9160f66f451Sopenharmony_ci  int type = 0;
9170f66f451Sopenharmony_ci  char c;
9180f66f451Sopenharmony_ci
9190f66f451Sopenharmony_ci  if (count>1) vi_movw(count-1, 1, unused);
9200f66f451Sopenharmony_ci
9210f66f451Sopenharmony_ci  c = text_byte(TT.cursor);
9220f66f451Sopenharmony_ci  if (strchr(specials, c)) type = 1;
9230f66f451Sopenharmony_ci  TT.cursor++;
9240f66f451Sopenharmony_ci  for (;TT.cursor < TT.filesize-1; TT.cursor++) {
9250f66f451Sopenharmony_ci    c = text_byte(TT.cursor+1);
9260f66f451Sopenharmony_ci    if (!type && (strchr(blank, c) || strchr(specials, c))) break;
9270f66f451Sopenharmony_ci    else if (type && !strchr(specials, c)) break;
9280f66f451Sopenharmony_ci  }
9290f66f451Sopenharmony_ci
9300f66f451Sopenharmony_ci  TT.vi_mov_flag |= 2;
9310f66f451Sopenharmony_ci  check_cursor_bounds();
9320f66f451Sopenharmony_ci  return 1;
9330f66f451Sopenharmony_ci}
9340f66f451Sopenharmony_ci
9350f66f451Sopenharmony_ci
9360f66f451Sopenharmony_cistatic void i_insert(char *str, int len)
9370f66f451Sopenharmony_ci{
9380f66f451Sopenharmony_ci  if (!str || !len) return;
9390f66f451Sopenharmony_ci
9400f66f451Sopenharmony_ci  insert_str(xstrdup(str), TT.cursor, len, len, HEAP);
9410f66f451Sopenharmony_ci  TT.cursor += len;
9420f66f451Sopenharmony_ci  TT.filesize = text_filesize();
9430f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x30000000;
9440f66f451Sopenharmony_ci}
9450f66f451Sopenharmony_ci
9460f66f451Sopenharmony_cistatic int vi_zero(int count0, int count1, char *unused)
9470f66f451Sopenharmony_ci{
9480f66f451Sopenharmony_ci  TT.cursor = text_sol(TT.cursor);
9490f66f451Sopenharmony_ci  TT.cur_col = 0;
9500f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x80000000;
9510f66f451Sopenharmony_ci  return 1;
9520f66f451Sopenharmony_ci}
9530f66f451Sopenharmony_ci
9540f66f451Sopenharmony_cistatic int vi_dollar(int count0, int count1, char *unused)
9550f66f451Sopenharmony_ci{
9560f66f451Sopenharmony_ci  size_t new = text_strchr(TT.cursor, '\n');
9570f66f451Sopenharmony_ci
9580f66f451Sopenharmony_ci  if (new != TT.cursor) {
9590f66f451Sopenharmony_ci    TT.cursor = new - 1;
9600f66f451Sopenharmony_ci    TT.vi_mov_flag |= 2;
9610f66f451Sopenharmony_ci    check_cursor_bounds();
9620f66f451Sopenharmony_ci  }
9630f66f451Sopenharmony_ci  return 1;
9640f66f451Sopenharmony_ci}
9650f66f451Sopenharmony_ci
9660f66f451Sopenharmony_cistatic void vi_eol()
9670f66f451Sopenharmony_ci{
9680f66f451Sopenharmony_ci  TT.cursor = text_strchr(TT.cursor, '\n');
9690f66f451Sopenharmony_ci  check_cursor_bounds();
9700f66f451Sopenharmony_ci}
9710f66f451Sopenharmony_ci
9720f66f451Sopenharmony_cistatic void ctrl_b()
9730f66f451Sopenharmony_ci{
9740f66f451Sopenharmony_ci  int i;
9750f66f451Sopenharmony_ci
9760f66f451Sopenharmony_ci  for (i=0; i<TT.screen_height-2; ++i) {
9770f66f451Sopenharmony_ci    TT.screen = text_psol(TT.screen);
9780f66f451Sopenharmony_ci    // TODO: retain x offset.
9790f66f451Sopenharmony_ci    TT.cursor = text_psol(TT.screen);
9800f66f451Sopenharmony_ci  }
9810f66f451Sopenharmony_ci}
9820f66f451Sopenharmony_ci
9830f66f451Sopenharmony_cistatic void ctrl_f()
9840f66f451Sopenharmony_ci{
9850f66f451Sopenharmony_ci  int i;
9860f66f451Sopenharmony_ci
9870f66f451Sopenharmony_ci  for (i=0; i<TT.screen_height-2; ++i) TT.screen = text_nsol(TT.screen);
9880f66f451Sopenharmony_ci  // TODO: real vi keeps the x position.
9890f66f451Sopenharmony_ci  if (TT.screen > TT.cursor) TT.cursor = TT.screen;
9900f66f451Sopenharmony_ci}
9910f66f451Sopenharmony_ci
9920f66f451Sopenharmony_cistatic void ctrl_e()
9930f66f451Sopenharmony_ci{
9940f66f451Sopenharmony_ci  TT.screen = text_nsol(TT.screen);
9950f66f451Sopenharmony_ci  // TODO: real vi keeps the x position.
9960f66f451Sopenharmony_ci  if (TT.screen > TT.cursor) TT.cursor = TT.screen;
9970f66f451Sopenharmony_ci}
9980f66f451Sopenharmony_ci
9990f66f451Sopenharmony_cistatic void ctrl_y()
10000f66f451Sopenharmony_ci{
10010f66f451Sopenharmony_ci  TT.screen = text_psol(TT.screen);
10020f66f451Sopenharmony_ci  // TODO: only if we're on the bottom line
10030f66f451Sopenharmony_ci  TT.cursor = text_psol(TT.cursor);
10040f66f451Sopenharmony_ci  // TODO: real vi keeps the x position.
10050f66f451Sopenharmony_ci}
10060f66f451Sopenharmony_ci
10070f66f451Sopenharmony_ci//TODO check register where to push from
10080f66f451Sopenharmony_cistatic int vi_push(char reg, int count0, int count1)
10090f66f451Sopenharmony_ci{
10100f66f451Sopenharmony_ci  //if row changes during push original cursor position is kept
10110f66f451Sopenharmony_ci  //vi inconsistancy
10120f66f451Sopenharmony_ci  //if yank ends with \n push is linemode else push in place+1
10130f66f451Sopenharmony_ci  size_t history = TT.cursor;
10140f66f451Sopenharmony_ci  char *start = TT.yank.data;
10150f66f451Sopenharmony_ci  char *eol = strchr(start, '\n');
10160f66f451Sopenharmony_ci
10170f66f451Sopenharmony_ci  if (start[strlen(start)-1] == '\n') {
10180f66f451Sopenharmony_ci    if ((TT.cursor = text_strchr(TT.cursor, '\n')) == SIZE_MAX)
10190f66f451Sopenharmony_ci      TT.cursor = TT.filesize;
10200f66f451Sopenharmony_ci    else TT.cursor = text_nsol(TT.cursor);
10210f66f451Sopenharmony_ci  } else cur_right(1, 1, 0);
10220f66f451Sopenharmony_ci
10230f66f451Sopenharmony_ci  i_insert(start, strlen(start));
10240f66f451Sopenharmony_ci  if (eol) {
10250f66f451Sopenharmony_ci    TT.vi_mov_flag |= 0x10000000;
10260f66f451Sopenharmony_ci    TT.cursor = history;
10270f66f451Sopenharmony_ci  }
10280f66f451Sopenharmony_ci
10290f66f451Sopenharmony_ci  return 1;
10300f66f451Sopenharmony_ci}
10310f66f451Sopenharmony_ci
10320f66f451Sopenharmony_cistatic int vi_find_c(int count0, int count1, char *symbol)
10330f66f451Sopenharmony_ci{
10340f66f451Sopenharmony_ci////  int count = count0*count1;
10350f66f451Sopenharmony_ci  size_t pos = text_strchr(TT.cursor, *symbol);
10360f66f451Sopenharmony_ci  if (pos != SIZE_MAX) TT.cursor = pos;
10370f66f451Sopenharmony_ci  return 1;
10380f66f451Sopenharmony_ci}
10390f66f451Sopenharmony_ci
10400f66f451Sopenharmony_cistatic int vi_find_cb(int count0, int count1, char *symbol)
10410f66f451Sopenharmony_ci{
10420f66f451Sopenharmony_ci  //do backward search
10430f66f451Sopenharmony_ci  size_t pos = text_strrchr(TT.cursor, *symbol);
10440f66f451Sopenharmony_ci  if (pos != SIZE_MAX) TT.cursor = pos;
10450f66f451Sopenharmony_ci  return 1;
10460f66f451Sopenharmony_ci}
10470f66f451Sopenharmony_ci
10480f66f451Sopenharmony_ci//if count is not spesified should go to last line
10490f66f451Sopenharmony_cistatic int vi_go(int count0, int count1, char *symbol)
10500f66f451Sopenharmony_ci{
10510f66f451Sopenharmony_ci  size_t prev_cursor = TT.cursor;
10520f66f451Sopenharmony_ci  int count = count0*count1-1;
10530f66f451Sopenharmony_ci  TT.cursor = 0;
10540f66f451Sopenharmony_ci
10550f66f451Sopenharmony_ci  if (TT.vi_mov_flag&0x40000000 && (TT.cursor = TT.filesize) > 0)
10560f66f451Sopenharmony_ci    TT.cursor = text_sol(TT.cursor-1);
10570f66f451Sopenharmony_ci  else if (count) {
10580f66f451Sopenharmony_ci    size_t next = 0;
10590f66f451Sopenharmony_ci    for ( ;count && (next = text_strchr(next+1, '\n')) != SIZE_MAX; count--)
10600f66f451Sopenharmony_ci      TT.cursor = next;
10610f66f451Sopenharmony_ci    TT.cursor++;
10620f66f451Sopenharmony_ci  }
10630f66f451Sopenharmony_ci
10640f66f451Sopenharmony_ci  check_cursor_bounds();  //adjusts cursor column
10650f66f451Sopenharmony_ci  if (prev_cursor > TT.cursor) TT.vi_mov_flag |= 0x80000000;
10660f66f451Sopenharmony_ci
10670f66f451Sopenharmony_ci  return 1;
10680f66f451Sopenharmony_ci}
10690f66f451Sopenharmony_ci
10700f66f451Sopenharmony_cistatic int vi_o(char reg, int count0, int count1)
10710f66f451Sopenharmony_ci{
10720f66f451Sopenharmony_ci  TT.cursor = text_eol(TT.cursor);
10730f66f451Sopenharmony_ci  insert_str(xstrdup("\n"), TT.cursor++, 1, 1, HEAP);
10740f66f451Sopenharmony_ci  TT.vi_mov_flag |= 0x30000000;
10750f66f451Sopenharmony_ci  TT.vi_mode = 2;
10760f66f451Sopenharmony_ci  return 1;
10770f66f451Sopenharmony_ci}
10780f66f451Sopenharmony_ci
10790f66f451Sopenharmony_cistatic int vi_O(char reg, int count0, int count1)
10800f66f451Sopenharmony_ci{
10810f66f451Sopenharmony_ci  TT.cursor = text_psol(TT.cursor);
10820f66f451Sopenharmony_ci  return vi_o(reg, count0, count1);
10830f66f451Sopenharmony_ci}
10840f66f451Sopenharmony_ci
10850f66f451Sopenharmony_cistatic int vi_D(char reg, int count0, int count1)
10860f66f451Sopenharmony_ci{
10870f66f451Sopenharmony_ci  size_t pos = TT.cursor;
10880f66f451Sopenharmony_ci  if (!count0) return 1;
10890f66f451Sopenharmony_ci  vi_eol();
10900f66f451Sopenharmony_ci  vi_delete(reg, pos, 0);
10910f66f451Sopenharmony_ci  if (--count0) vi_dd(reg, count0, 1);
10920f66f451Sopenharmony_ci
10930f66f451Sopenharmony_ci  check_cursor_bounds();
10940f66f451Sopenharmony_ci  return 1;
10950f66f451Sopenharmony_ci}
10960f66f451Sopenharmony_ci
10970f66f451Sopenharmony_cistatic int vi_I(char reg, int count0, int count1)
10980f66f451Sopenharmony_ci{
10990f66f451Sopenharmony_ci  TT.cursor = text_sol(TT.cursor);
11000f66f451Sopenharmony_ci  TT.vi_mode = 2;
11010f66f451Sopenharmony_ci  return 1;
11020f66f451Sopenharmony_ci}
11030f66f451Sopenharmony_ci
11040f66f451Sopenharmony_cistatic int vi_join(char reg, int count0, int count1)
11050f66f451Sopenharmony_ci{
11060f66f451Sopenharmony_ci  size_t next;
11070f66f451Sopenharmony_ci  while (count0--) {
11080f66f451Sopenharmony_ci    //just strchr(/n) and cut_str(pos, 1);
11090f66f451Sopenharmony_ci    if ((next = text_strchr(TT.cursor, '\n')) == SIZE_MAX) break;
11100f66f451Sopenharmony_ci    TT.cursor = next+1;
11110f66f451Sopenharmony_ci    vi_delete(reg, TT.cursor-1, 0);
11120f66f451Sopenharmony_ci  }
11130f66f451Sopenharmony_ci  return 1;
11140f66f451Sopenharmony_ci}
11150f66f451Sopenharmony_ci
11160f66f451Sopenharmony_cistatic int vi_find_next(char reg, int count0, int count1)
11170f66f451Sopenharmony_ci{
11180f66f451Sopenharmony_ci  if (TT.last_search) search_str(TT.last_search);
11190f66f451Sopenharmony_ci  return 1;
11200f66f451Sopenharmony_ci}
11210f66f451Sopenharmony_ci
11220f66f451Sopenharmony_ci//NOTES
11230f66f451Sopenharmony_ci//vi-mode cmd syntax is
11240f66f451Sopenharmony_ci//("[REG])[COUNT0]CMD[COUNT1](MOV)
11250f66f451Sopenharmony_ci//where:
11260f66f451Sopenharmony_ci//-------------------------------------------------------------
11270f66f451Sopenharmony_ci//"[REG] is optional buffer where deleted/yanked text goes REG can be
11280f66f451Sopenharmony_ci//  atleast 0-9, a-z or default "
11290f66f451Sopenharmony_ci//[COUNT] is optional multiplier for cmd execution if there is 2 COUNT
11300f66f451Sopenharmony_ci//  operations they are multiplied together
11310f66f451Sopenharmony_ci//CMD is operation to be executed
11320f66f451Sopenharmony_ci//(MOV) is movement operation, some CMD does not require MOV and some
11330f66f451Sopenharmony_ci//  have special cases such as dd, yy, also movements can work without
11340f66f451Sopenharmony_ci//  CMD
11350f66f451Sopenharmony_ci//ex commands can be even more complicated than this....
11360f66f451Sopenharmony_ci//
11370f66f451Sopenharmony_cistruct vi_cmd_param {
11380f66f451Sopenharmony_ci  const char* cmd;
11390f66f451Sopenharmony_ci  unsigned flags;
11400f66f451Sopenharmony_ci  int (*vi_cmd)(char, size_t, int);//REG,from,FLAGS
11410f66f451Sopenharmony_ci};
11420f66f451Sopenharmony_cistruct vi_mov_param {
11430f66f451Sopenharmony_ci  const char* mov;
11440f66f451Sopenharmony_ci  unsigned flags;
11450f66f451Sopenharmony_ci  int (*vi_mov)(int, int, char*);//COUNT0,COUNT1,params
11460f66f451Sopenharmony_ci};
11470f66f451Sopenharmony_ci//special cases without MOV and such
11480f66f451Sopenharmony_cistruct vi_special_param {
11490f66f451Sopenharmony_ci  const char *cmd;
11500f66f451Sopenharmony_ci  int (*vi_special)(char, int, int);//REG,COUNT0,COUNT1
11510f66f451Sopenharmony_ci};
11520f66f451Sopenharmony_cistruct vi_special_param vi_special[] =
11530f66f451Sopenharmony_ci{
11540f66f451Sopenharmony_ci  {"D", &vi_D},
11550f66f451Sopenharmony_ci  {"I", &vi_I},
11560f66f451Sopenharmony_ci  {"J", &vi_join},
11570f66f451Sopenharmony_ci  {"O", &vi_O},
11580f66f451Sopenharmony_ci  {"n", &vi_find_next},
11590f66f451Sopenharmony_ci  {"o", &vi_o},
11600f66f451Sopenharmony_ci  {"p", &vi_push},
11610f66f451Sopenharmony_ci  {"x", &vi_x},
11620f66f451Sopenharmony_ci  {"dd", &vi_dd},
11630f66f451Sopenharmony_ci  {"yy", &vi_yy},
11640f66f451Sopenharmony_ci};
11650f66f451Sopenharmony_ci//there is around ~47 vi moves
11660f66f451Sopenharmony_ci//some of them need extra params
11670f66f451Sopenharmony_ci//such as f and '
11680f66f451Sopenharmony_cistruct vi_mov_param vi_movs[] =
11690f66f451Sopenharmony_ci{
11700f66f451Sopenharmony_ci  {"0", 0, &vi_zero},
11710f66f451Sopenharmony_ci  {"b", 0, &vi_movb},
11720f66f451Sopenharmony_ci  {"e", 0, &vi_move},
11730f66f451Sopenharmony_ci  {"G", 0, &vi_go},
11740f66f451Sopenharmony_ci  {"H", 0, &vi_H},
11750f66f451Sopenharmony_ci  {"h", 0, &cur_left},
11760f66f451Sopenharmony_ci  {"j", 0, &cur_down},
11770f66f451Sopenharmony_ci  {"k", 0, &cur_up},
11780f66f451Sopenharmony_ci  {"L", 0, &vi_L},
11790f66f451Sopenharmony_ci  {"l", 0, &cur_right},
11800f66f451Sopenharmony_ci  {"M", 0, &vi_M},
11810f66f451Sopenharmony_ci  {"w", 0, &vi_movw},
11820f66f451Sopenharmony_ci  {"$", 0, &vi_dollar},
11830f66f451Sopenharmony_ci  {"f", 1, &vi_find_c},
11840f66f451Sopenharmony_ci  {"F", 1, &vi_find_cb},
11850f66f451Sopenharmony_ci};
11860f66f451Sopenharmony_ci//change and delete unfortunately behave different depending on move command,
11870f66f451Sopenharmony_ci//such as ce cw are same, but dw and de are not...
11880f66f451Sopenharmony_ci//also dw stops at w position and cw seem to stop at e pos+1...
11890f66f451Sopenharmony_ci//so after movement we need to possibly set up some flags before executing
11900f66f451Sopenharmony_ci//command, and command needs to adjust...
11910f66f451Sopenharmony_cistruct vi_cmd_param vi_cmds[] =
11920f66f451Sopenharmony_ci{
11930f66f451Sopenharmony_ci  {"c", 1, &vi_change},
11940f66f451Sopenharmony_ci  {"d", 1, &vi_delete},
11950f66f451Sopenharmony_ci  {"y", 1, &vi_yank},
11960f66f451Sopenharmony_ci};
11970f66f451Sopenharmony_ci
11980f66f451Sopenharmony_cistatic int run_vi_cmd(char *cmd)
11990f66f451Sopenharmony_ci{
12000f66f451Sopenharmony_ci  int i = 0, val = 0;
12010f66f451Sopenharmony_ci  char *cmd_e;
12020f66f451Sopenharmony_ci  int (*vi_cmd)(char, size_t, int) = 0;
12030f66f451Sopenharmony_ci  int (*vi_mov)(int, int, char*) = 0;
12040f66f451Sopenharmony_ci
12050f66f451Sopenharmony_ci  TT.count0 = 0, TT.count1 = 0, TT.vi_mov_flag = 0;
12060f66f451Sopenharmony_ci  TT.vi_reg = '"';
12070f66f451Sopenharmony_ci
12080f66f451Sopenharmony_ci  if (*cmd == '"') {
12090f66f451Sopenharmony_ci    cmd++;
12100f66f451Sopenharmony_ci    TT.vi_reg = *cmd; //TODO check validity
12110f66f451Sopenharmony_ci    cmd++;
12120f66f451Sopenharmony_ci  }
12130f66f451Sopenharmony_ci  errno = 0;
12140f66f451Sopenharmony_ci  val = strtol(cmd, &cmd_e, 10);
12150f66f451Sopenharmony_ci  if (errno || val == 0) val = 1, TT.vi_mov_flag |= 0x40000000;
12160f66f451Sopenharmony_ci  else cmd = cmd_e;
12170f66f451Sopenharmony_ci  TT.count0 = val;
12180f66f451Sopenharmony_ci
12190f66f451Sopenharmony_ci  for (i = 0; i < ARRAY_LEN(vi_special); i++) {
12200f66f451Sopenharmony_ci    if (strstr(cmd, vi_special[i].cmd)) {
12210f66f451Sopenharmony_ci      return vi_special[i].vi_special(TT.vi_reg, TT.count0, TT.count1);
12220f66f451Sopenharmony_ci    }
12230f66f451Sopenharmony_ci  }
12240f66f451Sopenharmony_ci
12250f66f451Sopenharmony_ci  for (i = 0; i < ARRAY_LEN(vi_cmds); i++) {
12260f66f451Sopenharmony_ci    if (!strncmp(cmd, vi_cmds[i].cmd, strlen(vi_cmds[i].cmd))) {
12270f66f451Sopenharmony_ci      vi_cmd = vi_cmds[i].vi_cmd;
12280f66f451Sopenharmony_ci      cmd += strlen(vi_cmds[i].cmd);
12290f66f451Sopenharmony_ci      break;
12300f66f451Sopenharmony_ci    }
12310f66f451Sopenharmony_ci  }
12320f66f451Sopenharmony_ci  errno = 0;
12330f66f451Sopenharmony_ci  val = strtol(cmd, &cmd_e, 10);
12340f66f451Sopenharmony_ci  if (errno || val == 0) val = 1;
12350f66f451Sopenharmony_ci  else cmd = cmd_e;
12360f66f451Sopenharmony_ci  TT.count1 = val;
12370f66f451Sopenharmony_ci
12380f66f451Sopenharmony_ci  for (i = 0; i < ARRAY_LEN(vi_movs); i++) {
12390f66f451Sopenharmony_ci    if (!strncmp(cmd, vi_movs[i].mov, strlen(vi_movs[i].mov))) {
12400f66f451Sopenharmony_ci      vi_mov = vi_movs[i].vi_mov;
12410f66f451Sopenharmony_ci      TT.vi_mov_flag |= vi_movs[i].flags;
12420f66f451Sopenharmony_ci      cmd++;
12430f66f451Sopenharmony_ci      if (TT.vi_mov_flag&1 && !(*cmd)) return 0;
12440f66f451Sopenharmony_ci      break;
12450f66f451Sopenharmony_ci    }
12460f66f451Sopenharmony_ci  }
12470f66f451Sopenharmony_ci  if (vi_mov) {
12480f66f451Sopenharmony_ci    int prev_cursor = TT.cursor;
12490f66f451Sopenharmony_ci    if (vi_mov(TT.count0, TT.count1, cmd)) {
12500f66f451Sopenharmony_ci      if (vi_cmd) return (vi_cmd(TT.vi_reg, prev_cursor, TT.vi_mov_flag));
12510f66f451Sopenharmony_ci      else return 1;
12520f66f451Sopenharmony_ci    } else return 0; //return some error
12530f66f451Sopenharmony_ci  }
12540f66f451Sopenharmony_ci  return 0;
12550f66f451Sopenharmony_ci}
12560f66f451Sopenharmony_ci
12570f66f451Sopenharmony_ci
12580f66f451Sopenharmony_cistatic int run_ex_cmd(char *cmd)
12590f66f451Sopenharmony_ci{
12600f66f451Sopenharmony_ci  if (cmd[0] == '/') {
12610f66f451Sopenharmony_ci    search_str(&cmd[1]);
12620f66f451Sopenharmony_ci  } else if (cmd[0] == '?') {
12630f66f451Sopenharmony_ci    // TODO: backwards search.
12640f66f451Sopenharmony_ci  } else if (cmd[0] == ':') {
12650f66f451Sopenharmony_ci    if (!strcmp(&cmd[1], "q") || !strcmp(&cmd[1], "q!")) {
12660f66f451Sopenharmony_ci      // TODO: if no !, check whether file modified.
12670f66f451Sopenharmony_ci      //exit_application;
12680f66f451Sopenharmony_ci      return -1;
12690f66f451Sopenharmony_ci    }
12700f66f451Sopenharmony_ci    else if (strstr(&cmd[1], "wq")) {
12710f66f451Sopenharmony_ci      write_file(0);
12720f66f451Sopenharmony_ci      return -1;
12730f66f451Sopenharmony_ci    }
12740f66f451Sopenharmony_ci    else if (strstr(&cmd[1], "w")) {
12750f66f451Sopenharmony_ci      write_file(0);
12760f66f451Sopenharmony_ci      return 1;
12770f66f451Sopenharmony_ci    }
12780f66f451Sopenharmony_ci    else if (strstr(&cmd[1], "set list")) {
12790f66f451Sopenharmony_ci      TT.list = 1;
12800f66f451Sopenharmony_ci      TT.vi_mov_flag |= 0x30000000;
12810f66f451Sopenharmony_ci      return 1;
12820f66f451Sopenharmony_ci    }
12830f66f451Sopenharmony_ci    else if (strstr(&cmd[1], "set nolist")) {
12840f66f451Sopenharmony_ci      TT.list = 0;
12850f66f451Sopenharmony_ci      TT.vi_mov_flag |= 0x30000000;
12860f66f451Sopenharmony_ci      return 1;
12870f66f451Sopenharmony_ci    }
12880f66f451Sopenharmony_ci  }
12890f66f451Sopenharmony_ci  return 0;
12900f66f451Sopenharmony_ci
12910f66f451Sopenharmony_ci}
12920f66f451Sopenharmony_ci
12930f66f451Sopenharmony_cistatic int vi_crunch(FILE *out, int cols, int wc)
12940f66f451Sopenharmony_ci{
12950f66f451Sopenharmony_ci  int ret = 0;
12960f66f451Sopenharmony_ci  if (wc < 32 && TT.list) {
12970f66f451Sopenharmony_ci    tty_esc("1m");
12980f66f451Sopenharmony_ci    ret = crunch_escape(out,cols,wc);
12990f66f451Sopenharmony_ci    tty_esc("m");
13000f66f451Sopenharmony_ci  } else if (wc == 0x09) {
13010f66f451Sopenharmony_ci    if (out) {
13020f66f451Sopenharmony_ci      int i = TT.tabstop;
13030f66f451Sopenharmony_ci      for (;i--;) fputs(" ", out);
13040f66f451Sopenharmony_ci    }
13050f66f451Sopenharmony_ci    ret = TT.tabstop;
13060f66f451Sopenharmony_ci  } else if (wc == '\n') return 0;
13070f66f451Sopenharmony_ci  return ret;
13080f66f451Sopenharmony_ci}
13090f66f451Sopenharmony_ci
13100f66f451Sopenharmony_ci//crunch_str with n bytes restriction for printing substrings or
13110f66f451Sopenharmony_ci//non null terminated strings
13120f66f451Sopenharmony_cistatic int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore,
13130f66f451Sopenharmony_ci  int (*escout)(FILE *out, int cols, int wc))
13140f66f451Sopenharmony_ci{
13150f66f451Sopenharmony_ci  int columns = 0, col, bytes;
13160f66f451Sopenharmony_ci  char *start, *end;
13170f66f451Sopenharmony_ci  unsigned wc;
13180f66f451Sopenharmony_ci
13190f66f451Sopenharmony_ci  for (end = start = *str; *end && n>0; columns += col, end += bytes, n -= bytes) {
13200f66f451Sopenharmony_ci    if ((bytes = utf8towc(&wc, end, 4))>0 && (col = wcwidth(wc))>=0) {
13210f66f451Sopenharmony_ci      if (!escmore || wc>255 || !strchr(escmore, wc)) {
13220f66f451Sopenharmony_ci        if (width-columns<col) break;
13230f66f451Sopenharmony_ci        if (out) fwrite(end, bytes, 1, out);
13240f66f451Sopenharmony_ci
13250f66f451Sopenharmony_ci        continue;
13260f66f451Sopenharmony_ci      }
13270f66f451Sopenharmony_ci    }
13280f66f451Sopenharmony_ci
13290f66f451Sopenharmony_ci    if (bytes<1) {
13300f66f451Sopenharmony_ci      bytes = 1;
13310f66f451Sopenharmony_ci      wc = *end;
13320f66f451Sopenharmony_ci    }
13330f66f451Sopenharmony_ci    col = width-columns;
13340f66f451Sopenharmony_ci    if (col<1) break;
13350f66f451Sopenharmony_ci    if (escout) {
13360f66f451Sopenharmony_ci      if ((col = escout(out, col, wc))<0) break;
13370f66f451Sopenharmony_ci    } else if (out) fwrite(end, 1, bytes, out);
13380f66f451Sopenharmony_ci  }
13390f66f451Sopenharmony_ci  *str = end;
13400f66f451Sopenharmony_ci
13410f66f451Sopenharmony_ci  return columns;
13420f66f451Sopenharmony_ci}
13430f66f451Sopenharmony_ci
13440f66f451Sopenharmony_cistatic void draw_page()
13450f66f451Sopenharmony_ci{
13460f66f451Sopenharmony_ci  unsigned y = 0;
13470f66f451Sopenharmony_ci  int x = 0;
13480f66f451Sopenharmony_ci
13490f66f451Sopenharmony_ci  char *line = 0, *end = 0;
13500f66f451Sopenharmony_ci  int bytes = 0;
13510f66f451Sopenharmony_ci
13520f66f451Sopenharmony_ci  //screen coordinates for cursor
13530f66f451Sopenharmony_ci  int cy_scr = 0, cx_scr = 0;
13540f66f451Sopenharmony_ci
13550f66f451Sopenharmony_ci  //variables used only for cursor handling
13560f66f451Sopenharmony_ci  int aw = 0, iw = 0, clip = 0, margin = 8;
13570f66f451Sopenharmony_ci
13580f66f451Sopenharmony_ci  int scroll = 0, redraw = 0;
13590f66f451Sopenharmony_ci
13600f66f451Sopenharmony_ci  int SSOL, SOL;
13610f66f451Sopenharmony_ci
13620f66f451Sopenharmony_ci
13630f66f451Sopenharmony_ci  adjust_screen_buffer();
13640f66f451Sopenharmony_ci  //redraw = 3; //force full redraw
13650f66f451Sopenharmony_ci  redraw = (TT.vi_mov_flag & 0x30000000)>>28;
13660f66f451Sopenharmony_ci
13670f66f451Sopenharmony_ci  scroll = TT.drawn_row-TT.scr_row;
13680f66f451Sopenharmony_ci  if (TT.drawn_row<0 || TT.cur_row<0 || TT.scr_row<0) redraw = 3;
13690f66f451Sopenharmony_ci  else if (abs(scroll)>TT.screen_height/2) redraw = 3;
13700f66f451Sopenharmony_ci
13710f66f451Sopenharmony_ci  tty_jump(0, 0);
13720f66f451Sopenharmony_ci  if (redraw&2) tty_esc("2J"), tty_esc("H");   //clear screen
13730f66f451Sopenharmony_ci  else if (scroll>0) printf("\033[%dL", scroll);  //scroll up
13740f66f451Sopenharmony_ci  else if (scroll<0) printf("\033[%dM", -scroll); //scroll down
13750f66f451Sopenharmony_ci
13760f66f451Sopenharmony_ci  SOL = text_sol(TT.cursor);
13770f66f451Sopenharmony_ci  bytes = text_getline(toybuf, SOL, ARRAY_LEN(toybuf));
13780f66f451Sopenharmony_ci  line = toybuf;
13790f66f451Sopenharmony_ci
13800f66f451Sopenharmony_ci  for (SSOL = TT.screen, y = 0; SSOL < SOL; y++) SSOL = text_nsol(SSOL);
13810f66f451Sopenharmony_ci
13820f66f451Sopenharmony_ci  cy_scr = y;
13830f66f451Sopenharmony_ci
13840f66f451Sopenharmony_ci  //draw cursor row
13850f66f451Sopenharmony_ci  /////////////////////////////////////////////////////////////
13860f66f451Sopenharmony_ci  //for long lines line starts to scroll when cursor hits margin
13870f66f451Sopenharmony_ci  bytes = TT.cursor-SOL; // TT.cur_col;
13880f66f451Sopenharmony_ci  end = line;
13890f66f451Sopenharmony_ci
13900f66f451Sopenharmony_ci
13910f66f451Sopenharmony_ci  tty_jump(0, y);
13920f66f451Sopenharmony_ci  tty_esc("2K");
13930f66f451Sopenharmony_ci  //find cursor position
13940f66f451Sopenharmony_ci  aw = crunch_nstr(&end, INT_MAX, bytes, 0, "\t\n", vi_crunch);
13950f66f451Sopenharmony_ci
13960f66f451Sopenharmony_ci  //if we need to render text that is not inserted to buffer yet
13970f66f451Sopenharmony_ci  if (TT.vi_mode == 2 && TT.il->len) {
13980f66f451Sopenharmony_ci    char* iend = TT.il->data; //input end
13990f66f451Sopenharmony_ci    x = 0;
14000f66f451Sopenharmony_ci    //find insert end position
14010f66f451Sopenharmony_ci    iw = crunch_str(&iend, INT_MAX, 0, "\t\n", vi_crunch);
14020f66f451Sopenharmony_ci    clip = (aw+iw) - TT.screen_width+margin;
14030f66f451Sopenharmony_ci
14040f66f451Sopenharmony_ci    //if clipped area is bigger than text before insert
14050f66f451Sopenharmony_ci    if (clip > aw) {
14060f66f451Sopenharmony_ci      clip -= aw;
14070f66f451Sopenharmony_ci      iend = TT.il->data;
14080f66f451Sopenharmony_ci
14090f66f451Sopenharmony_ci      iw -= crunch_str(&iend, clip, 0, "\t\n", vi_crunch);
14100f66f451Sopenharmony_ci      x = crunch_str(&iend, iw, stdout, "\t\n", vi_crunch);
14110f66f451Sopenharmony_ci    } else {
14120f66f451Sopenharmony_ci      iend = TT.il->data;
14130f66f451Sopenharmony_ci      end = line;
14140f66f451Sopenharmony_ci
14150f66f451Sopenharmony_ci      //if clipped area is substring from cursor row start
14160f66f451Sopenharmony_ci      aw -= crunch_nstr(&end, clip, bytes, 0, "\t\n", vi_crunch);
14170f66f451Sopenharmony_ci      x = crunch_str(&end, aw,  stdout, "\t\n", vi_crunch);
14180f66f451Sopenharmony_ci      x += crunch_str(&iend, iw, stdout, "\t\n", vi_crunch);
14190f66f451Sopenharmony_ci    }
14200f66f451Sopenharmony_ci  }
14210f66f451Sopenharmony_ci  //when not inserting but still need to keep cursor inside screen
14220f66f451Sopenharmony_ci  //margin area
14230f66f451Sopenharmony_ci  else if ( aw+margin > TT.screen_width) {
14240f66f451Sopenharmony_ci    clip = aw-TT.screen_width+margin;
14250f66f451Sopenharmony_ci    end = line;
14260f66f451Sopenharmony_ci    aw -= crunch_nstr(&end, clip, bytes, 0, "\t\n", vi_crunch);
14270f66f451Sopenharmony_ci    x = crunch_str(&end, aw,  stdout, "\t\n", vi_crunch);
14280f66f451Sopenharmony_ci  }
14290f66f451Sopenharmony_ci  else {
14300f66f451Sopenharmony_ci    end = line;
14310f66f451Sopenharmony_ci    x = crunch_nstr(&end, aw, bytes, stdout, "\t\n", vi_crunch);
14320f66f451Sopenharmony_ci  }
14330f66f451Sopenharmony_ci  cx_scr = x;
14340f66f451Sopenharmony_ci  cy_scr = y;
14350f66f451Sopenharmony_ci  x += crunch_str(&end, TT.screen_width-x,  stdout, "\t\n", vi_crunch);
14360f66f451Sopenharmony_ci
14370f66f451Sopenharmony_ci  //start drawing all other rows that needs update
14380f66f451Sopenharmony_ci  ///////////////////////////////////////////////////////////////////
14390f66f451Sopenharmony_ci  y = 0, SSOL = TT.screen, line = toybuf;
14400f66f451Sopenharmony_ci  bytes = text_getline(toybuf, SSOL, ARRAY_LEN(toybuf));
14410f66f451Sopenharmony_ci
14420f66f451Sopenharmony_ci  //if we moved around in long line might need to redraw everything
14430f66f451Sopenharmony_ci  if (clip != TT.drawn_col) redraw = 3;
14440f66f451Sopenharmony_ci
14450f66f451Sopenharmony_ci  for (; y < TT.screen_height; y++ ) {
14460f66f451Sopenharmony_ci    int draw_line = 0;
14470f66f451Sopenharmony_ci    if (SSOL == SOL) {
14480f66f451Sopenharmony_ci      line = toybuf;
14490f66f451Sopenharmony_ci      SSOL += bytes+1;
14500f66f451Sopenharmony_ci      bytes = text_getline(line, SSOL, ARRAY_LEN(toybuf));
14510f66f451Sopenharmony_ci      continue;
14520f66f451Sopenharmony_ci    } else if (redraw) draw_line++;
14530f66f451Sopenharmony_ci    else if (scroll<0 && TT.screen_height-y-1<-scroll)
14540f66f451Sopenharmony_ci      scroll++, draw_line++;
14550f66f451Sopenharmony_ci    else if (scroll>0) scroll--, draw_line++;
14560f66f451Sopenharmony_ci
14570f66f451Sopenharmony_ci    tty_jump(0, y);
14580f66f451Sopenharmony_ci    if (draw_line) {
14590f66f451Sopenharmony_ci      tty_esc("2K");
14600f66f451Sopenharmony_ci      if (line && strlen(line)) {
14610f66f451Sopenharmony_ci        aw = crunch_nstr(&line, clip, bytes, 0, "\t\n", vi_crunch);
14620f66f451Sopenharmony_ci        crunch_str(&line, TT.screen_width-1, stdout, "\t\n", vi_crunch);
14630f66f451Sopenharmony_ci        if ( *line ) printf("@");
14640f66f451Sopenharmony_ci      } else printf("\033[2m~\033[m");
14650f66f451Sopenharmony_ci    }
14660f66f451Sopenharmony_ci    if (SSOL+bytes < TT.filesize)  {
14670f66f451Sopenharmony_ci      line = toybuf;
14680f66f451Sopenharmony_ci      SSOL += bytes+1;
14690f66f451Sopenharmony_ci      bytes = text_getline(line, SSOL, ARRAY_LEN(toybuf));
14700f66f451Sopenharmony_ci   } else line = 0;
14710f66f451Sopenharmony_ci  }
14720f66f451Sopenharmony_ci
14730f66f451Sopenharmony_ci  TT.drawn_row = TT.scr_row, TT.drawn_col = clip;
14740f66f451Sopenharmony_ci
14750f66f451Sopenharmony_ci  // Finished updating visual area, show status line.
14760f66f451Sopenharmony_ci  tty_jump(0, TT.screen_height);
14770f66f451Sopenharmony_ci  tty_esc("2K");
14780f66f451Sopenharmony_ci  if (TT.vi_mode == 2) printf("\033[1m-- INSERT --\033[m");
14790f66f451Sopenharmony_ci  if (!TT.vi_mode) {
14800f66f451Sopenharmony_ci    cx_scr = printf("%s", TT.il->data);
14810f66f451Sopenharmony_ci    cy_scr = TT.screen_height;
14820f66f451Sopenharmony_ci    *toybuf = 0;
14830f66f451Sopenharmony_ci  } else {
14840f66f451Sopenharmony_ci    // TODO: the row,col display doesn't show the cursor column
14850f66f451Sopenharmony_ci    // TODO: real vi shows the percentage by lines, not bytes
14860f66f451Sopenharmony_ci    sprintf(toybuf, "%zu/%zuC  %zu%%  %d,%d", TT.cursor, TT.filesize,
14870f66f451Sopenharmony_ci      (100*TT.cursor)/(TT.filesize ? : 1), TT.cur_row+1, TT.cur_col+1);
14880f66f451Sopenharmony_ci    if (TT.cur_col != cx_scr) sprintf(toybuf+strlen(toybuf),"-%d", cx_scr+1);
14890f66f451Sopenharmony_ci  }
14900f66f451Sopenharmony_ci  tty_jump(TT.screen_width-strlen(toybuf), TT.screen_height);
14910f66f451Sopenharmony_ci  printf("%s", toybuf);
14920f66f451Sopenharmony_ci
14930f66f451Sopenharmony_ci  tty_jump(cx_scr, cy_scr);
14940f66f451Sopenharmony_ci  xflush(1);
14950f66f451Sopenharmony_ci}
14960f66f451Sopenharmony_ci
14970f66f451Sopenharmony_civoid vi_main(void)
14980f66f451Sopenharmony_ci{
14990f66f451Sopenharmony_ci  char stdout_buf[8192];
15000f66f451Sopenharmony_ci  char keybuf[16] = {0};
15010f66f451Sopenharmony_ci  char vi_buf[16] = {0};
15020f66f451Sopenharmony_ci  char utf8_code[8] = {0};
15030f66f451Sopenharmony_ci  int utf8_dec_p = 0, vi_buf_pos = 0;
15040f66f451Sopenharmony_ci  FILE *script = FLAG(s) ? xfopen(TT.s, "r") : 0;
15050f66f451Sopenharmony_ci
15060f66f451Sopenharmony_ci  TT.il = xzalloc(sizeof(struct str_line));
15070f66f451Sopenharmony_ci  TT.il->data = xzalloc(80);
15080f66f451Sopenharmony_ci  TT.yank.data = xzalloc(128);
15090f66f451Sopenharmony_ci
15100f66f451Sopenharmony_ci  TT.il->alloc = 80, TT.yank.alloc = 128;
15110f66f451Sopenharmony_ci
15120f66f451Sopenharmony_ci  linelist_load(0);
15130f66f451Sopenharmony_ci
15140f66f451Sopenharmony_ci  TT.vi_mov_flag = 0x20000000;
15150f66f451Sopenharmony_ci  TT.vi_mode = 1, TT.tabstop = 8;
15160f66f451Sopenharmony_ci
15170f66f451Sopenharmony_ci  TT.screen_width = 80, TT.screen_height = 24;
15180f66f451Sopenharmony_ci  terminal_size(&TT.screen_width, &TT.screen_height);
15190f66f451Sopenharmony_ci  TT.screen_height -= 1;
15200f66f451Sopenharmony_ci
15210f66f451Sopenharmony_ci  // Avoid flicker.
15220f66f451Sopenharmony_ci  setbuffer(stdout, stdout_buf, sizeof(stdout_buf));
15230f66f451Sopenharmony_ci
15240f66f451Sopenharmony_ci  xsignal(SIGWINCH, generic_signal);
15250f66f451Sopenharmony_ci  set_terminal(0, 1, 0, 0);
15260f66f451Sopenharmony_ci  //writes stdout into different xterm buffer so when we exit
15270f66f451Sopenharmony_ci  //we dont get scroll log full of junk
15280f66f451Sopenharmony_ci  tty_esc("?1049h");
15290f66f451Sopenharmony_ci
15300f66f451Sopenharmony_ci  for (;;) {
15310f66f451Sopenharmony_ci    int key = 0;
15320f66f451Sopenharmony_ci
15330f66f451Sopenharmony_ci    draw_page();
15340f66f451Sopenharmony_ci    if (script) {
15350f66f451Sopenharmony_ci      key = fgetc(script);
15360f66f451Sopenharmony_ci      if (key == EOF) {
15370f66f451Sopenharmony_ci        fclose(script);
15380f66f451Sopenharmony_ci        script = 0;
15390f66f451Sopenharmony_ci        key = scan_key(keybuf, -1);
15400f66f451Sopenharmony_ci      }
15410f66f451Sopenharmony_ci    } else key = scan_key(keybuf, -1);
15420f66f451Sopenharmony_ci
15430f66f451Sopenharmony_ci    if (key == -1) goto cleanup_vi;
15440f66f451Sopenharmony_ci    else if (key == -3) {
15450f66f451Sopenharmony_ci      toys.signal = 0;
15460f66f451Sopenharmony_ci      terminal_size(&TT.screen_width, &TT.screen_height);
15470f66f451Sopenharmony_ci      TT.screen_height -= 1; //TODO this is hack fix visual alignment
15480f66f451Sopenharmony_ci      continue;
15490f66f451Sopenharmony_ci    }
15500f66f451Sopenharmony_ci
15510f66f451Sopenharmony_ci    // TODO: support cursor keys in ex mode too.
15520f66f451Sopenharmony_ci    if (TT.vi_mode && key>=256) {
15530f66f451Sopenharmony_ci      key -= 256;
15540f66f451Sopenharmony_ci      if (key==KEY_UP) cur_up(1, 1, 0);
15550f66f451Sopenharmony_ci      else if (key==KEY_DOWN) cur_down(1, 1, 0);
15560f66f451Sopenharmony_ci      else if (key==KEY_LEFT) cur_left(1, 1, 0);
15570f66f451Sopenharmony_ci      else if (key==KEY_RIGHT) cur_right(1, 1, 0);
15580f66f451Sopenharmony_ci      else if (key==KEY_HOME) vi_zero(1, 1, 0);
15590f66f451Sopenharmony_ci      else if (key==KEY_END) vi_dollar(1, 1, 0);
15600f66f451Sopenharmony_ci      else if (key==KEY_PGDN) ctrl_f();
15610f66f451Sopenharmony_ci      else if (key==KEY_PGUP) ctrl_b();
15620f66f451Sopenharmony_ci      continue;
15630f66f451Sopenharmony_ci    }
15640f66f451Sopenharmony_ci
15650f66f451Sopenharmony_ci    if (TT.vi_mode == 1) { //NORMAL
15660f66f451Sopenharmony_ci      switch (key) {
15670f66f451Sopenharmony_ci        case '/':
15680f66f451Sopenharmony_ci        case '?':
15690f66f451Sopenharmony_ci        case ':':
15700f66f451Sopenharmony_ci          TT.vi_mode = 0;
15710f66f451Sopenharmony_ci          TT.il->data[0]=key;
15720f66f451Sopenharmony_ci          TT.il->len++;
15730f66f451Sopenharmony_ci          break;
15740f66f451Sopenharmony_ci        case 'A':
15750f66f451Sopenharmony_ci          vi_eol();
15760f66f451Sopenharmony_ci          TT.vi_mode = 2;
15770f66f451Sopenharmony_ci          break;
15780f66f451Sopenharmony_ci        case 'a':
15790f66f451Sopenharmony_ci          cur_right(1, 1, 0);
15800f66f451Sopenharmony_ci          // FALLTHROUGH
15810f66f451Sopenharmony_ci        case 'i':
15820f66f451Sopenharmony_ci          TT.vi_mode = 2;
15830f66f451Sopenharmony_ci          break;
15840f66f451Sopenharmony_ci        case 'B'-'@':
15850f66f451Sopenharmony_ci          ctrl_b();
15860f66f451Sopenharmony_ci          break;
15870f66f451Sopenharmony_ci        case 'E'-'@':
15880f66f451Sopenharmony_ci          ctrl_e();
15890f66f451Sopenharmony_ci          break;
15900f66f451Sopenharmony_ci        case 'F'-'@':
15910f66f451Sopenharmony_ci          ctrl_f();
15920f66f451Sopenharmony_ci          break;
15930f66f451Sopenharmony_ci        case 'Y'-'@':
15940f66f451Sopenharmony_ci          ctrl_y();
15950f66f451Sopenharmony_ci          break;
15960f66f451Sopenharmony_ci        case 27:
15970f66f451Sopenharmony_ci          vi_buf[0] = 0;
15980f66f451Sopenharmony_ci          vi_buf_pos = 0;
15990f66f451Sopenharmony_ci          break;
16000f66f451Sopenharmony_ci        default:
16010f66f451Sopenharmony_ci          if (key > 0x20 && key < 0x7B) {
16020f66f451Sopenharmony_ci            vi_buf[vi_buf_pos] = key;//TODO handle input better
16030f66f451Sopenharmony_ci            vi_buf_pos++;
16040f66f451Sopenharmony_ci            if (run_vi_cmd(vi_buf)) {
16050f66f451Sopenharmony_ci              memset(vi_buf, 0, 16);
16060f66f451Sopenharmony_ci              vi_buf_pos = 0;
16070f66f451Sopenharmony_ci            }
16080f66f451Sopenharmony_ci            else if (vi_buf_pos == 16) {
16090f66f451Sopenharmony_ci              vi_buf_pos = 0;
16100f66f451Sopenharmony_ci              memset(vi_buf, 0, 16);
16110f66f451Sopenharmony_ci            }
16120f66f451Sopenharmony_ci
16130f66f451Sopenharmony_ci          }
16140f66f451Sopenharmony_ci
16150f66f451Sopenharmony_ci          break;
16160f66f451Sopenharmony_ci      }
16170f66f451Sopenharmony_ci    } else if (TT.vi_mode == 0) { //EX MODE
16180f66f451Sopenharmony_ci      switch (key) {
16190f66f451Sopenharmony_ci        case 0x7F:
16200f66f451Sopenharmony_ci        case 0x08:
16210f66f451Sopenharmony_ci          if (TT.il->len > 1) {
16220f66f451Sopenharmony_ci            TT.il->data[--TT.il->len] = 0;
16230f66f451Sopenharmony_ci            break;
16240f66f451Sopenharmony_ci          }
16250f66f451Sopenharmony_ci          // FALLTHROUGH
16260f66f451Sopenharmony_ci        case 27:
16270f66f451Sopenharmony_ci          TT.vi_mode = 1;
16280f66f451Sopenharmony_ci          TT.il->len = 0;
16290f66f451Sopenharmony_ci          memset(TT.il->data, 0, TT.il->alloc);
16300f66f451Sopenharmony_ci          break;
16310f66f451Sopenharmony_ci        case 0x0A:
16320f66f451Sopenharmony_ci        case 0x0D:
16330f66f451Sopenharmony_ci          if (run_ex_cmd(TT.il->data) == -1)
16340f66f451Sopenharmony_ci            goto cleanup_vi;
16350f66f451Sopenharmony_ci          TT.vi_mode = 1;
16360f66f451Sopenharmony_ci          TT.il->len = 0;
16370f66f451Sopenharmony_ci          memset(TT.il->data, 0, TT.il->alloc);
16380f66f451Sopenharmony_ci          break;
16390f66f451Sopenharmony_ci        default: //add chars to ex command until ENTER
16400f66f451Sopenharmony_ci          if (key >= 0x20 && key < 0x7F) { //might be utf?
16410f66f451Sopenharmony_ci            if (TT.il->len == TT.il->alloc) {
16420f66f451Sopenharmony_ci              TT.il->data = realloc(TT.il->data, TT.il->alloc*2);
16430f66f451Sopenharmony_ci              TT.il->alloc *= 2;
16440f66f451Sopenharmony_ci            }
16450f66f451Sopenharmony_ci            TT.il->data[TT.il->len] = key;
16460f66f451Sopenharmony_ci            TT.il->len++;
16470f66f451Sopenharmony_ci          }
16480f66f451Sopenharmony_ci          break;
16490f66f451Sopenharmony_ci      }
16500f66f451Sopenharmony_ci    } else if (TT.vi_mode == 2) {//INSERT MODE
16510f66f451Sopenharmony_ci      switch (key) {
16520f66f451Sopenharmony_ci        case 27:
16530f66f451Sopenharmony_ci          i_insert(TT.il->data, TT.il->len);
16540f66f451Sopenharmony_ci          cur_left(1, 1, 0);
16550f66f451Sopenharmony_ci          TT.vi_mode = 1;
16560f66f451Sopenharmony_ci          TT.il->len = 0;
16570f66f451Sopenharmony_ci          memset(TT.il->data, 0, TT.il->alloc);
16580f66f451Sopenharmony_ci          break;
16590f66f451Sopenharmony_ci        case 0x7F:
16600f66f451Sopenharmony_ci        case 0x08:
16610f66f451Sopenharmony_ci          if (TT.il->len) {
16620f66f451Sopenharmony_ci            char *last = utf8_last(TT.il->data, TT.il->len);
16630f66f451Sopenharmony_ci            int shrink = strlen(last);
16640f66f451Sopenharmony_ci            memset(last, 0, shrink);
16650f66f451Sopenharmony_ci            TT.il->len -= shrink;
16660f66f451Sopenharmony_ci          }
16670f66f451Sopenharmony_ci          break;
16680f66f451Sopenharmony_ci        case 0x0A:
16690f66f451Sopenharmony_ci        case 0x0D:
16700f66f451Sopenharmony_ci          //insert newline
16710f66f451Sopenharmony_ci          //
16720f66f451Sopenharmony_ci          TT.il->data[TT.il->len++] = '\n';
16730f66f451Sopenharmony_ci          i_insert(TT.il->data, TT.il->len);
16740f66f451Sopenharmony_ci          TT.il->len = 0;
16750f66f451Sopenharmony_ci          memset(TT.il->data, 0, TT.il->alloc);
16760f66f451Sopenharmony_ci          break;
16770f66f451Sopenharmony_ci        default:
16780f66f451Sopenharmony_ci          if ((key >= 0x20 || key == 0x09) &&
16790f66f451Sopenharmony_ci              utf8_dec(key, utf8_code, &utf8_dec_p)) {
16800f66f451Sopenharmony_ci
16810f66f451Sopenharmony_ci            if (TT.il->len+utf8_dec_p+1 >= TT.il->alloc) {
16820f66f451Sopenharmony_ci              TT.il->data = realloc(TT.il->data, TT.il->alloc*2);
16830f66f451Sopenharmony_ci              TT.il->alloc *= 2;
16840f66f451Sopenharmony_ci            }
16850f66f451Sopenharmony_ci            strcpy(TT.il->data+TT.il->len, utf8_code);
16860f66f451Sopenharmony_ci            TT.il->len += utf8_dec_p;
16870f66f451Sopenharmony_ci            utf8_dec_p = 0;
16880f66f451Sopenharmony_ci            *utf8_code = 0;
16890f66f451Sopenharmony_ci
16900f66f451Sopenharmony_ci          }
16910f66f451Sopenharmony_ci          break;
16920f66f451Sopenharmony_ci      }
16930f66f451Sopenharmony_ci    }
16940f66f451Sopenharmony_ci  }
16950f66f451Sopenharmony_cicleanup_vi:
16960f66f451Sopenharmony_ci  linelist_unload();
16970f66f451Sopenharmony_ci  free(TT.il->data), free(TT.il), free(TT.yank.data);
16980f66f451Sopenharmony_ci  tty_reset();
16990f66f451Sopenharmony_ci  tty_esc("?1049l");
17000f66f451Sopenharmony_ci}
1701