Introduction
Introduction Statistics Contact Development Disclaimer Help
tneatvi: the skeleton - neatvi - [fork] simple vi-type editor with UTF-8 support
git clone git://src.adamsgaard.dk/neatvi
Log
Files
Refs
README
---
commit b1c10bc71e9c7d3c7db8cbd85adf17aa63f9736b
Author: Ali Gholami Rudi <[email protected]>
Date: Fri, 1 May 2015 17:56:55 +0430
neatvi: the skeleton
Diffstat:
A Makefile | 13 +++++++++++++
A ex.c | 438 +++++++++++++++++++++++++++++…
A kmap.h | 98 +++++++++++++++++++++++++++++…
A lbuf.c | 236 +++++++++++++++++++++++++++++…
A led.c | 144 +++++++++++++++++++++++++++++…
A ren.c | 270 +++++++++++++++++++++++++++++…
A sbuf.c | 94 +++++++++++++++++++++++++++++…
A term.c | 105 +++++++++++++++++++++++++++++…
A uc.c | 303 +++++++++++++++++++++++++++++…
A vi.c | 548 +++++++++++++++++++++++++++++…
A vi.h | 102 +++++++++++++++++++++++++++++…
11 files changed, 2351 insertions(+), 0 deletions(-)
---
diff --git a/Makefile b/Makefile
t@@ -0,0 +1,13 @@
+CC = cc
+CFLAGS = -Wall -O2
+LDFLAGS =
+
+OBJS = vi.o ex.o lbuf.o sbuf.o ren.o led.o uc.o term.o
+
+all: vi
+%.o: %.c
+ $(CC) -c $(CFLAGS) $<
+vi: $(OBJS)
+ $(CC) -o $@ $(OBJS) $(LDFLAGS)
+clean:
+ rm -f *.o vi
diff --git a/ex.c b/ex.c
t@@ -0,0 +1,438 @@
+#include <ctype.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "vi.h"
+
+#define EXLEN 512
+
+/* read an input line; ex's input function */
+static char *ex_read(char *msg)
+{
+ struct sbuf *sb;
+ char c;
+ if (xled) {
+ char *s = led_prompt(msg, "");
+ printf("\n");
+ return s;
+ }
+ sb = sbuf_make();
+ while ((c = getchar()) != EOF) {
+ if (c == '\n')
+ break;
+ sbuf_chr(sb, c);
+ }
+ if (c == EOF) {
+ sbuf_free(sb);
+ return NULL;
+ }
+ return sbuf_done(sb);
+}
+
+/* print an output line; ex's output function */
+static void ex_show(char *msg)
+{
+ if (xled)
+ led_print(msg, -1);
+ else
+ printf("%s", msg);
+}
+
+/* read ex command location */
+static char *ex_loc(char *s, char *loc)
+{
+ while (*s == ':' || isspace((unsigned char) *s))
+ s++;
+ while (*s && !isalpha((unsigned char) *s) && *s != '=') {
+ if (*s == '\'')
+ *loc++ = *s++;
+ if (*s == '/' || *s == '?') {
+ int d = *s;
+ *loc++ = *s++;
+ while (*s && *s != d) {
+ if (*s == '\\' && s[1])
+ *loc++ = *s++;
+ *loc++ = *s++;
+ }
+ }
+ *loc++ = *s++;
+ }
+ *loc = '\0';
+ return s;
+}
+
+/* read ex command name */
+static char *ex_cmd(char *s, char *cmd)
+{
+ char *cmd0 = cmd;
+ s = ex_loc(s, cmd);
+ while (isspace((unsigned char) *s))
+ s++;
+ while (isalpha((unsigned char) *s) || *s == '=' || *s == '!')
+ if ((*cmd++ = *s++) == 'k' && cmd == cmd0 + 1)
+ break;
+ *cmd = '\0';
+ return s;
+}
+
+/* read ex command argument */
+static char *ex_arg(char *s, char *arg)
+{
+ s = ex_cmd(s, arg);
+ while (isspace((unsigned char) *s))
+ s++;
+ while (*s && !isspace((unsigned char) *s))
+ *arg++ = *s++;
+ *arg = '\0';
+ return s;
+}
+
+static int ex_search(char *pat)
+{
+ struct sbuf *kwd;
+ int dir = *pat == '/' ? 1 : -1;
+ char *b = pat;
+ char *e = b;
+ int i = xrow;
+ regex_t re;
+ kwd = sbuf_make();
+ while (*++e) {
+ if (*e == *pat)
+ break;
+ sbuf_chr(kwd, (unsigned char) *e);
+ if (*e == '\\' && e[1])
+ e++;
+ }
+ regcomp(&re, sbuf_buf(kwd), 0);
+ while (i >= 0 && i < lbuf_len(xb)) {
+ if (!regexec(&re, lbuf_get(xb, i), 0, NULL, 0))
+ break;
+ i += dir;
+ }
+ regfree(&re);
+ sbuf_free(kwd);
+ return i;
+}
+
+static int ex_lineno(char *num)
+{
+ int n = xrow;
+ if (!num[0] || num[0] == '.')
+ n = xrow;
+ if (isdigit(num[0]))
+ n = atoi(num) - 1;
+ if (num[0] == '$')
+ n = lbuf_len(xb) - 1;
+ if (num[0] == '-')
+ n = xrow - (num[1] ? ex_lineno(num + 1) : 1);
+ if (num[0] == '+')
+ n = xrow + (num[1] ? ex_lineno(num + 1) : 1);
+ if (num[0] == '\'')
+ n = lbuf_markpos(xb, num[1]);
+ if (num[0] == '/' && num[1])
+ n = ex_search(num);
+ if (num[0] == '?' && num[1])
+ n = ex_search(num);
+ return n;
+}
+
+/* parse ex command location */
+static int ex_region(char *loc, int *beg, int *end)
+{
+ int naddr = 0;
+ if (!strcmp("%", loc)) {
+ *beg = 0;
+ *end = MAX(0, lbuf_len(xb));
+ return 0;
+ }
+ if (!*loc) {
+ *beg = xrow;
+ *end = xrow == lbuf_len(xb) ? xrow : xrow + 1;
+ return 0;
+ }
+ while (*loc) {
+ char *r = loc;
+ while (*loc && *loc != ';' && *loc != ',')
+ loc++;
+ *beg = *end;
+ *end = ex_lineno(r) + 1;
+ if (!naddr++)
+ *beg = *end - 1;
+ if (!*loc)
+ break;
+ if (*loc == ';')
+ xrow = *end - 1;
+ loc++;
+ }
+ if (*beg < 0 || *beg >= lbuf_len(xb))
+ return 1;
+ if (*end < *beg || *end > lbuf_len(xb))
+ return 1;
+ return 0;
+}
+
+static void ec_edit(char *ec)
+{
+ char arg[EXLEN];
+ int fd;
+ ex_arg(ec, arg);
+ fd = open(arg, O_RDONLY);
+ if (fd >= 0) {
+ lbuf_rm(xb, 0, lbuf_len(xb));
+ lbuf_rd(xb, fd, 0);
+ lbuf_undofree(xb);
+ close(fd);
+ xrow = MAX(0, lbuf_len(xb) - 1);
+ }
+ snprintf(xpath, PATHLEN, "%s", arg);
+}
+
+static void ec_read(char *ec)
+{
+ char arg[EXLEN], loc[EXLEN];
+ int fd;
+ int beg, end;
+ int n = lbuf_len(xb);
+ ex_arg(ec, arg);
+ ex_loc(ec, loc);
+ fd = open(arg[0] ? arg : xpath, O_RDONLY);
+ if (fd >= 0 && !ex_region(loc, &beg, &end)) {
+ lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
+ close(fd);
+ xrow = end + lbuf_len(xb) - n;
+ }
+}
+
+static void ec_write(char *ec)
+{
+ char arg[EXLEN], loc[EXLEN];
+ char *path;
+ int beg, end;
+ int fd;
+ ex_arg(ec, arg);
+ ex_loc(ec, loc);
+ path = arg[0] ? arg : xpath;
+ if (ex_region(loc, &beg, &end))
+ return;
+ if (!loc[0]) {
+ beg = 0;
+ end = lbuf_len(xb);
+ }
+ fd = open(path, O_WRONLY | O_CREAT, 0600);
+ if (fd >= 0) {
+ lbuf_wr(xb, fd, beg, end);
+ close(fd);
+ }
+}
+
+static void ec_insert(char *ec)
+{
+ char arg[EXLEN], cmd[EXLEN], loc[EXLEN];
+ struct sbuf *sb;
+ char *s;
+ int beg, end;
+ int n;
+ ex_arg(ec, arg);
+ ex_cmd(ec, cmd);
+ ex_loc(ec, loc);
+ if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
+ return;
+ if (cmd[0] == 'c') {
+ if (lbuf_len(xb))
+ lbuf_rm(xb, beg, end);
+ end = beg + 1;
+ }
+ sb = sbuf_make();
+ while ((s = ex_read(""))) {
+ if (!strcmp(".", s)) {
+ free(s);
+ break;
+ }
+ sbuf_str(sb, s);
+ sbuf_chr(sb, '\n');
+ free(s);
+ }
+ if (cmd[0] == 'a')
+ if (end > lbuf_len(xb))
+ end = lbuf_len(xb);
+ n = lbuf_len(xb);
+ lbuf_put(xb, end, sbuf_buf(sb));
+ xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
+ sbuf_free(sb);
+}
+
+static void ec_print(char *ec)
+{
+ char cmd[EXLEN], loc[EXLEN];
+ int beg, end;
+ int i;
+ ex_cmd(ec, cmd);
+ ex_loc(ec, loc);
+ if (!cmd[0] && !loc[0]) {
+ if (xrow >= lbuf_len(xb) - 1)
+ return;
+ xrow = xrow + 1;
+ }
+ if (!ex_region(loc, &beg, &end)) {
+ for (i = beg; i < end; i++)
+ ex_show(lbuf_get(xb, i));
+ xrow = end;
+ }
+}
+
+static void ec_delete(char *ec)
+{
+ char loc[EXLEN];
+ int beg, end;
+ ex_loc(ec, loc);
+ if (!ex_region(loc, &beg, &end) && lbuf_len(xb)) {
+ lbuf_rm(xb, beg, end);
+ xrow = beg;
+ }
+}
+
+static void ec_lnum(char *ec)
+{
+ char loc[EXLEN];
+ char msg[128];
+ int beg, end;
+ ex_loc(ec, loc);
+ if (ex_region(loc, &beg, &end))
+ return;
+ sprintf(msg, "%d\n", end);
+ ex_show(msg);
+}
+
+static void ec_undo(char *ec)
+{
+ lbuf_undo(xb);
+}
+
+static void ec_redo(char *ec)
+{
+ lbuf_redo(xb);
+}
+
+static void ec_mark(char *ec)
+{
+ char loc[EXLEN], arg[EXLEN];
+ int beg, end;
+ ex_arg(ec, arg);
+ ex_loc(ec, loc);
+ if (ex_region(loc, &beg, &end))
+ return;
+ lbuf_mark(xb, arg[0], end - 1);
+}
+
+static char *readuntil(char **src, int delim)
+{
+ struct sbuf *sbuf = sbuf_make();
+ char *s = *src;
+ /* reading the pattern */
+ while (*s && *s != delim) {
+ if (s[0] == '\\' && s[1])
+ sbuf_chr(sbuf, (unsigned char) *s++);
+ sbuf_chr(sbuf, (unsigned char) *s++);
+ }
+ if (*s) /* skipping the delimiter */
+ s++;
+ *src = s;
+ return sbuf_done(sbuf);
+}
+
+static void ec_substitute(char *ec)
+{
+ char loc[EXLEN], arg[EXLEN];
+ regmatch_t subs[16];
+ regex_t re;
+ int beg, end;
+ char *pat, *rep;
+ char *s = arg;
+ int delim;
+ int i;
+ ex_arg(ec, arg);
+ ex_loc(ec, loc);
+ if (ex_region(loc, &beg, &end))
+ return;
+ delim = (unsigned char) *s++;
+ pat = readuntil(&s, delim);
+ rep = readuntil(&s, delim);
+ regcomp(&re, pat, 0);
+ for (i = beg; i < end; i++) {
+ char *ln = lbuf_get(xb, i);
+ if (!regexec(&re, ln, LEN(subs), subs, 0)) {
+ struct sbuf *r = sbuf_make();
+ sbuf_mem(r, ln, subs[0].rm_so);
+ sbuf_str(r, rep);
+ sbuf_str(r, ln + subs[0].rm_eo);
+ lbuf_put(xb, i, sbuf_buf(r));
+ lbuf_rm(xb, i + 1, i + 2);
+ sbuf_free(r);
+ }
+ }
+ regfree(&re);
+ free(pat);
+ free(rep);
+}
+
+static void ec_quit(char *ec)
+{
+ xquit = 1;
+}
+
+static struct excmd {
+ char *abbr;
+ char *name;
+ void (*ec)(char *s);
+} excmds[] = {
+ {"p", "print", ec_print},
+ {"a", "append", ec_insert},
+ {"i", "insert", ec_insert},
+ {"d", "delete", ec_delete},
+ {"c", "change", ec_insert},
+ {"e", "edit", ec_edit},
+ {"=", "=", ec_lnum},
+ {"k", "mark", ec_mark},
+ {"q", "quit", ec_quit},
+ {"r", "read", ec_read},
+ {"w", "write", ec_write},
+ {"u", "undo", ec_undo},
+ {"r", "redo", ec_redo},
+ {"s", "substitute", ec_substitute},
+ {"", "", ec_print},
+};
+
+/* execute a single ex command */
+void ex_command(char *ln0)
+{
+ char cmd[EXLEN];
+ char *ln = ln0 ? ln0 : ex_read(":");
+ int i;
+ if (!ln)
+ return;
+ ex_cmd(ln, cmd);
+ for (i = 0; i < LEN(excmds); i++) {
+ if (!strcmp(excmds[i].abbr, cmd) || !strcmp(excmds[i].name, cm…
+ excmds[i].ec(ln);
+ break;
+ }
+ }
+ if (!ln0)
+ free(ln);
+ lbuf_undomark(xb);
+}
+
+/* ex main loop */
+void ex(void)
+{
+ if (xled)
+ term_init();
+ while (!xquit)
+ ex_command(NULL);
+ if (xled)
+ term_done();
+}
diff --git a/kmap.h b/kmap.h
t@@ -0,0 +1,98 @@
+static char *kmap_def[256];
+
+static char *kmap_farsi[256] = {
+ ['`'] = "‍",
+ ['1'] = "۱",
+ ['2'] = "۲",
+ ['3'] = "۳",
+ ['4'] = "۴",
+ ['5'] = "۵",
+ ['6'] = "۶",
+ ['7'] = "۷",
+ ['8'] = "۸",
+ ['9'] = "۹",
+ ['0'] = "۰",
+ ['-'] = "-",
+ ['='] = "=",
+ ['q'] = "ض",
+ ['w'] = "ص",
+ ['e'] = "ث",
+ ['r'] = "ق",
+ ['t'] = "ف",
+ ['y'] = "غ",
+ ['u'] = "ع",
+ ['i'] = "ه",
+ ['o'] = "خ",
+ ['p'] = "ح",
+ ['['] = "ج",
+ [']'] = "چ",
+ ['a'] = "ش",
+ ['s'] = "س",
+ ['d'] = "ی",
+ ['f'] = "ب",
+ ['g'] = "ل",
+ ['h'] = "ا",
+ ['j'] = "ت",
+ ['k'] = "ن",
+ ['l'] = "م",
+ [';'] = "ک",
+ ['\''] = "گ",
+ ['z'] = "ظ",
+ ['x'] = "ط",
+ ['c'] = "ز",
+ ['v'] = "ر",
+ ['b'] = "ذ",
+ ['n'] = "د",
+ ['m'] = "پ",
+ [','] = "و",
+ ['.'] = ".",
+ ['/'] = "/",
+ ['\\'] = "\\",
+ ['~'] = "÷",
+ ['!'] = "!",
+ ['@'] = "٬",
+ ['#'] = "٫",
+ ['$'] = "﷼",
+ ['%'] = "٪",
+ ['^'] = "×",
+ ['&'] = "،",
+ ['*'] = "*",
+ ['('] = ")",
+ [')'] = "(",
+ ['_'] = "ـ",
+ ['+'] = "+",
+ ['Q'] = "ْ",
+ ['W'] = "ٌ",
+ ['E'] = "ٍ",
+ ['R'] = "ً",
+ ['T'] = "ُ",
+ ['Y'] = "ِ",
+ ['U'] = "َ",
+ ['I'] = "ّ",
+ ['O'] = "]",
+ ['P'] = "[",
+ ['{'] = "}",
+ ['}'] = "{",
+ ['A'] = "ؤ",
+ ['S'] = "ئ",
+ ['D'] = "ي",
+ ['F'] = "إ",
+ ['G'] = "أ",
+ ['H'] = "آ",
+ ['J'] = "ة",
+ ['K'] = "»",
+ ['L'] = "«",
+ [':'] = ":",
+ ['"'] = "؛",
+ ['Z'] = "ك",
+ ['X'] = "ٓ",
+ ['C'] = "ژ",
+ ['V'] = "ٰ",
+ ['B'] = "‌",
+ ['N'] = "ٔ",
+ ['M'] = "ء",
+ ['<'] = ">",
+ ['>'] = "<",
+ ['?'] = "؟",
+ ['|'] = "|",
+};
diff --git a/lbuf.c b/lbuf.c
t@@ -0,0 +1,236 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "vi.h"
+
+#define MARK(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' : 30)
+
+/* line operations */
+struct lopt {
+ char *buf; /* text inserted or deleted */
+ int ins; /* insertion operation if non-zero */
+ int beg, end;
+ int seq; /* operation number */
+};
+
+/* line buffers */
+struct lbuf {
+ int mark[32]; /* buffer marks */
+ struct lopt hist[128]; /* buffer history */
+ int undo; /* current index into hist[] */
+ int useq; /* current operation sequence */
+ char **ln; /* lines */
+ int ln_n; /* number of lbuf in l[] */
+ int ln_sz; /* size of l[] */
+};
+
+struct lbuf *lbuf_make(void)
+{
+ struct lbuf *lb = malloc(sizeof(*lb));
+ int i;
+ memset(lb, 0, sizeof(*lb));
+ for (i = 0; i < LEN(lb->mark); i++)
+ lb->mark[i] = -1;
+ return lb;
+}
+
+void lbuf_free(struct lbuf *lb)
+{
+ int i;
+ for (i = 0; i < lb->ln_n; i++)
+ free(lb->ln[i]);
+ for (i = 0; i < LEN(lb->hist); i++)
+ free(lb->hist[i].buf);
+ free(lb->ln);
+ free(lb);
+}
+
+/* insert a line at pos */
+static void lbuf_insertline(struct lbuf *lb, int pos, char *s)
+{
+ if (lb->ln_n == lb->ln_sz) {
+ int nsz = lb->ln_sz + 512;
+ char **nln = malloc(nsz * sizeof(nln[0]));
+ memcpy(nln, lb->ln, lb->ln_n * sizeof(lb->ln[0]));
+ free(lb->ln);
+ lb->ln = nln;
+ lb->ln_sz = nsz;
+ }
+ lb->ln_n++;
+ memmove(lb->ln + pos + 1, lb->ln + pos,
+ (lb->ln_n - pos) * sizeof(lb->ln[0]));
+ lb->ln[pos] = s;
+}
+
+/* low-level insertion */
+static void lbuf_insert(struct lbuf *lb, int pos, char *s)
+{
+ int len = strlen(s);
+ struct sbuf *sb;
+ int lb_len = lbuf_len(lb);
+ int i;
+ sb = sbuf_make();
+ for (i = 0; i < len; i++) {
+ sbuf_chr(sb, (unsigned char) s[i]);
+ if (s[i] == '\n') {
+ lbuf_insertline(lb, pos++, sbuf_done(sb));
+ sb = sbuf_make();
+ }
+ }
+ sbuf_free(sb);
+ for (i = 0; i < LEN(lb->mark); i++) /* updating marks */
+ if (lb->mark[i] >= pos)
+ lb->mark[i] += lbuf_len(lb) - lb_len;
+}
+
+/* low-level deletion */
+static void lbuf_delete(struct lbuf *lb, int beg, int end)
+{
+ int i;
+ for (i = beg; i < end; i++)
+ free(lb->ln[i]);
+ memmove(lb->ln + beg, lb->ln + end, (lb->ln_n - end) * sizeof(lb->ln[0…
+ lb->ln_n -= end - beg;
+ for (i = 0; i < LEN(lb->mark); i++) /* updating marks */
+ if (lb->mark[i] > beg)
+ lb->mark[i] = MAX(beg, lb->mark[i] + beg - end);
+}
+
+/* append undo/redo history */
+static void lbuf_opt(struct lbuf *lb, int ins, int beg, int end)
+{
+ struct lopt *lo = &lb->hist[0];
+ int n = LEN(lb->hist);
+ int i;
+ if (lb->undo) {
+ for (i = 0; i < lb->undo; i++)
+ free(lb->hist[i].buf);
+ memmove(lb->hist + 1, lb->hist + lb->undo,
+ (n - lb->undo) * sizeof(lb->hist[0]));
+ for (i = n - lb->undo + 1; i < n; i++)
+ lb->hist[i].buf = NULL;
+ } else {
+ free(lb->hist[n - 1].buf);
+ memmove(lb->hist + 1, lb->hist, (n - 1) * sizeof(lb->hist[0]));
+ }
+ lo->ins = ins;
+ lo->beg = beg;
+ lo->end = end;
+ lo->buf = lbuf_cp(lb, beg, end);
+ lo->seq = lb->useq;
+ lb->undo = 0;
+}
+
+void lbuf_rd(struct lbuf *lbuf, int fd, int pos)
+{
+ char buf[1 << 8];
+ struct sbuf *sb;
+ int nr;
+ sb = sbuf_make();
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ sbuf_mem(sb, buf, nr);
+ lbuf_put(lbuf, pos, sbuf_buf(sb));
+ sbuf_free(sb);
+}
+
+void lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end)
+{
+ int i;
+ for (i = beg; i < end; i++)
+ write(fd, lbuf->ln[i], strlen(lbuf->ln[i]));
+}
+
+void lbuf_rm(struct lbuf *lb, int beg, int end)
+{
+ if (end > lb->ln_n)
+ end = lb->ln_n;
+ lbuf_opt(lb, 0, beg, end);
+ lbuf_delete(lb, beg, end);
+}
+
+void lbuf_put(struct lbuf *lb, int pos, char *s)
+{
+ int lb_len = lbuf_len(lb);
+ lbuf_insert(lb, pos, s);
+ lbuf_opt(lb, 1, pos, pos + lbuf_len(lb) - lb_len);
+}
+
+char *lbuf_cp(struct lbuf *lb, int beg, int end)
+{
+ struct sbuf *sb;
+ int i;
+ sb = sbuf_make();
+ for (i = beg; i < end; i++)
+ if (i < lb->ln_n)
+ sbuf_str(sb, lb->ln[i]);
+ return sbuf_done(sb);
+}
+
+char *lbuf_get(struct lbuf *lb, int pos)
+{
+ return pos >= 0 && pos < lb->ln_n ? lb->ln[pos] : NULL;
+}
+
+int lbuf_len(struct lbuf *lb)
+{
+ return lb->ln_n;
+}
+
+void lbuf_mark(struct lbuf *lbuf, int mark, int pos)
+{
+ lbuf->mark[MARK(mark)] = pos;
+}
+
+int lbuf_markpos(struct lbuf *lbuf, int mark)
+{
+ return lbuf->mark[MARK(mark)];
+}
+
+static struct lopt *lbuf_lopt(struct lbuf *lb, int i)
+{
+ struct lopt *lo = &lb->hist[i];
+ return i >= 0 && i < LEN(lb->hist) && lo->buf ? lo : NULL;
+}
+
+void lbuf_undo(struct lbuf *lb)
+{
+ struct lopt *lo = lbuf_lopt(lb, lb->undo);
+ int useq = lo ? lo->seq : 0;
+ while (lo && lo->seq == useq) {
+ lb->undo++;
+ if (lo->ins)
+ lbuf_delete(lb, lo->beg, lo->end);
+ else
+ lbuf_insert(lb, lo->beg, lo->buf);
+ lo = lbuf_lopt(lb, lb->undo);
+ }
+}
+
+void lbuf_redo(struct lbuf *lb)
+{
+ struct lopt *lo = lbuf_lopt(lb, lb->undo - 1);
+ int useq = lo ? lo->seq : 0;
+ while (lo && lo->seq == useq) {
+ lb->undo--;
+ if (lo->ins)
+ lbuf_insert(lb, lo->beg, lo->buf);
+ else
+ lbuf_delete(lb, lo->beg, lo->end);
+ lo = lbuf_lopt(lb, lb->undo - 1);
+ }
+}
+
+void lbuf_undofree(struct lbuf *lb)
+{
+ int i;
+ for (i = 0; i < LEN(lb->hist); i++)
+ free(lb->hist[i].buf);
+ memset(lb->hist, 0, sizeof(lb->hist));
+ lb->undo = 0;
+}
+
+void lbuf_undomark(struct lbuf *lbuf)
+{
+ lbuf->useq++;
+}
diff --git a/led.c b/led.c
t@@ -0,0 +1,144 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "vi.h"
+#include "kmap.h"
+
+static char **led_kmap = kmap_def;
+
+static char *keymap(char **kmap, int c)
+{
+ static char cs[4];
+ cs[0] = c;
+ return kmap[c] ? kmap[c] : cs;
+}
+
+char *led_keymap(int c)
+{
+ return c >= 0 ? keymap(led_kmap, c) : NULL;
+}
+
+void led_print(char *s, int row)
+{
+ char *r = ren_all(s, -1);
+ term_pos(row, 0);
+ term_kill();
+ term_str(r);
+ free(r);
+}
+
+static int led_lastchar(char *s)
+{
+ char *r = *s ? strchr(s, '\0') : s;
+ if (r != s)
+ r = uc_beg(s, r - 1);
+ return r - s;
+}
+
+static void led_printparts(char *pref, char *main, char *post)
+{
+ struct sbuf *ln;
+ int col;
+ int cur;
+ int dir;
+ ln = sbuf_make();
+ sbuf_str(ln, pref);
+ sbuf_str(ln, main);
+ cur = uc_slen(sbuf_buf(ln)) - 1;
+ dir = uc_dir(sbuf_buf(ln) + led_lastchar(sbuf_buf(ln)));
+ sbuf_str(ln, post);
+ led_print(sbuf_buf(ln), -1);
+ col = ren_cursor(sbuf_buf(ln), ren_pos(sbuf_buf(ln), MAX(cur, 0)));
+ if (cur >= 0) {
+ if (dir < 0 || (!dir && ren_dir(sbuf_buf(ln)) < 0))
+ col = MAX(col - 1, 0);
+ else
+ col += 1;
+ }
+ term_pos(-1, col);
+ sbuf_free(ln);
+}
+
+static char *led_line(char *pref, char *post, int *key, char ***kmap)
+{
+ struct sbuf *sb;
+ int c;
+ sb = sbuf_make();
+ if (!pref)
+ pref = "";
+ if (!post)
+ post = "";
+ while (1) {
+ led_printparts(pref, sbuf_buf(sb), post);
+ c = term_read(-1);
+ switch (c) {
+ case TERMCTRL('f'):
+ *kmap = *kmap == kmap_def ? kmap_farsi : kmap_def;
+ continue;
+ case TERMCTRL('h'):
+ case 127:
+ if (sbuf_len(sb))
+ sbuf_cut(sb, led_lastchar(sbuf_buf(sb)));
+ break;
+ case TERMCTRL('u'):
+ sbuf_cut(sb, 0);
+ break;
+ case TERMCTRL('v'):
+ sbuf_chr(sb, term_read(-1));
+ break;
+ default:
+ if (c == '\n' || c == TERMESC || c < 0)
+ break;
+ sbuf_str(sb, keymap(*kmap, c));
+ }
+ if (c == '\n' || c == TERMESC || c < 0)
+ break;
+ }
+ *key = c;
+ return sbuf_done(sb);
+}
+
+/* read an ex command */
+char *led_prompt(char *pref, char *post)
+{
+ char **kmap = kmap_def;
+ char *s;
+ int key;
+ s = led_line(pref, post, &key, &kmap);
+ if (key == '\n')
+ return s;
+ free(s);
+ return NULL;
+}
+
+/* read visual command input */
+char *led_input(char *pref, char *post, int *row, int *col)
+{
+ struct sbuf *sb = sbuf_make();
+ int key;
+ *row = 0;
+ while (1) {
+ char *ln = led_line(pref, post, &key, &led_kmap);
+ if (pref)
+ sbuf_str(sb, pref);
+ sbuf_str(sb, ln);
+ if (key == '\n')
+ sbuf_chr(sb, '\n');
+ *col = ren_last(pref ? sbuf_buf(sb) : ln);
+ led_printparts(pref ? pref : "", ln, key == '\n' ? "" : post);
+ if (key == '\n')
+ term_chr('\n');
+ pref = NULL;
+ term_kill();
+ free(ln);
+ if (key != '\n')
+ break;
+ (*row)++;
+ }
+ sbuf_str(sb, post);
+ if (key == TERMESC)
+ return sbuf_done(sb);
+ sbuf_free(sb);
+ return NULL;
+}
diff --git a/ren.c b/ren.c
t@@ -0,0 +1,270 @@
+/* rendering strings */
+/*
+ * Overview:
+ * + ren_translate() replaces the characters if necessary.
+ * + ren_position() specifies the position of characters on the screen.
+ * + ren_reorder() is called by ren_position() and changes the order of charac…
+ * + ren_highlight() performs syntax highlighting.
+ */
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "vi.h"
+
+static int bidi_maximalregion(char *s, int n, int dir, char **chrs, int idx, i…
+{
+ while (idx < n && uc_dir(chrs[idx]) * dir >= 0)
+ idx++;
+ *beg = idx;
+ *end = idx;
+ while (idx < n && uc_dir(chrs[idx]) * dir <= 0) {
+ if (uc_dir(chrs[idx]) * dir < 0)
+ *end = idx + 1;
+ idx++;
+ }
+ return *beg >= *end;
+}
+
+static void bidi_reverse(int *ord, int beg, int end)
+{
+ end--;
+ while (beg < end) {
+ int tmp = ord[beg];
+ ord[beg] = ord[end];
+ ord[end] = tmp;
+ beg++;
+ end--;
+ }
+}
+
+int ren_dir(char *s)
+{
+ return +1;
+}
+
+/* reorder the characters in s */
+static void ren_reorder(char *s, int *ord)
+{
+ int beg = 0, end = 0, n;
+ char **chrs = uc_chop(s, &n);
+ int dir = ren_dir(s);
+ while (!bidi_maximalregion(s, n, dir, chrs, end, &beg, &end))
+ bidi_reverse(ord, beg, end);
+ free(chrs);
+}
+
+/* specify the screen position of the characters in s */
+static int *ren_position(char *s, int *beg, int *end)
+{
+ int i, n;
+ char **chrs = uc_chop(s, &n);
+ int *off, *pos;
+ int diff = 0;
+ int dir = ren_dir(s);
+ pos = malloc(n * sizeof(pos[0]));
+ for (i = 0; i < n; i++)
+ pos[i] = i;
+ ren_reorder(s, pos);
+ off = malloc(n * sizeof(off[0]));
+ for (i = 0; i < n; i++)
+ off[pos[i]] = i;
+ for (i = 0; i < n; i++) {
+ pos[off[i]] += diff;
+ if (*chrs[i] == '\t')
+ diff += 8 - (pos[off[i]] & 7);
+ }
+ if (beg)
+ *beg = 0;
+ if (end)
+ *end = n + diff;
+ if (dir < 0) {
+ if (beg)
+ *beg = xcols - *end - 1;
+ if (end)
+ *end = xcols - 1;
+ for (i = 0; i < n; i++)
+ pos[i] = xcols - pos[i] - 1;
+ }
+ free(chrs);
+ free(off);
+ return pos;
+}
+
+static char *ren_translate(char *s)
+{
+ struct sbuf *sb = sbuf_make();
+ char *r = s;
+ while (*r) {
+ char *c = uc_shape(s, r);
+ if (!strcmp(c, "‌"))
+ c = "-";
+ if (!strcmp(c, "‍"))
+ c = "-";
+ sbuf_str(sb, c);
+ r = uc_next(r);
+ }
+ return sbuf_done(sb);
+}
+
+char *ren_all(char *s0, int wid)
+{
+ int n, w = 0;
+ int *pos; /* pos[i]: the screen position of the i-th character …
+ int *off; /* off[i]: the character at screen position i */
+ char **chrs; /* chrs[i]: the i-th character in s1 */
+ char *s1;
+ struct sbuf *out;
+ int i;
+ s1 = ren_translate(s0 ? s0 : "");
+ chrs = uc_chop(s1, &n);
+ pos = ren_position(s1, NULL, NULL);
+ for (i = 0; i < n; i++)
+ if (w <= pos[i])
+ w = pos[i] + 1;
+ off = malloc(w * sizeof(off[0]));
+ memset(off, 0xff, w * sizeof(off[0]));
+ for (i = 0; i < n; i++)
+ off[pos[i]] = i;
+ out = sbuf_make();
+ for (i = 0; i < w; i++) {
+ if (off[i] >= 0 && uc_isprint(chrs[off[i]]))
+ sbuf_mem(out, chrs[off[i]], uc_len(chrs[off[i]]));
+ else
+ sbuf_chr(out, ' ');
+ }
+ free(pos);
+ free(off);
+ free(chrs);
+ free(s1);
+ return sbuf_done(out);
+}
+
+int ren_last(char *s)
+{
+ int n = uc_slen(s);
+ int *pos = ren_position(s, NULL, NULL);
+ int ret = n ? pos[n - 1] : 0;
+ free(pos);
+ return ret;
+}
+
+/* find the next character after visual position p; if cur start from p itself…
+static int pos_next(int *pos, int n, int p, int cur)
+{
+ int i, ret = -1;
+ for (i = 0; i < n; i++)
+ if (pos[i] - !cur >= p && (ret < 0 || pos[i] < pos[ret]))
+ ret = i;
+ return ret >= 0 ? pos[ret] : -1;
+}
+
+/* find the previous character after visual position p; if cur start from p it…
+static int pos_prev(int *pos, int n, int p, int cur)
+{
+ int i, ret = -1;
+ for (i = 0; i < n; i++)
+ if (pos[i] + !cur <= p && (ret < 0 || pos[i] > pos[ret]))
+ ret = i;
+ return ret >= 0 ? pos[ret] : -1;
+}
+
+/* convert visual position to character offset */
+int ren_pos(char *s, int off)
+{
+ int n = uc_slen(s);
+ int *pos = ren_position(s, NULL, NULL);
+ int ret = off < n ? pos[off] : 0;
+ free(pos);
+ return ret;
+}
+
+/* convert visual position to character offset */
+int ren_off(char *s, int p)
+{
+ int off = -1;
+ int n = uc_slen(s);
+ int *pos = ren_position(s, NULL, NULL);
+ int i;
+ if (ren_dir(s) >= 0)
+ p = pos_prev(pos, n, p, 1);
+ else
+ p = pos_next(pos, n, p, 1);
+ for (i = 0; i < n; i++)
+ if (pos[i] == p)
+ off = i;
+ free(pos);
+ return off >= 0 ? off : 0;
+}
+
+/* adjust cursor position */
+int ren_cursor(char *s, int p)
+{
+ int dir = ren_dir(s ? s : "");
+ int n, next;
+ int beg, end;
+ int *pos;
+ if (!s)
+ return 0;
+ n = uc_slen(s);
+ pos = ren_position(s, &beg, &end);
+ if (dir >= 0)
+ p = pos_prev(pos, n, p, 1);
+ else
+ p = pos_next(pos, n, p, 1);
+ if (dir >= 0)
+ next = pos_next(pos, n, p, 0);
+ else
+ next = pos_prev(pos, n, p, 0);
+ p = (next >= 0 ? next : (dir >= 0 ? end : beg)) - dir;
+ free(pos);
+ return p >= 0 ? p : 0;
+}
+
+/* the position of the next character */
+int ren_next(char *s, int p, int dir)
+{
+ int n = uc_slen(s);
+ int *pos = ren_position(s, NULL, NULL);
+ if (ren_dir(s ? s : "") >= 0)
+ p = pos_prev(pos, n, p, 1);
+ else
+ p = pos_next(pos, n, p, 1);
+ if (dir * ren_dir(s ? s : "") >= 0)
+ p = pos_next(pos, n, p, 0);
+ else
+ p = pos_prev(pos, n, p, 0);
+ free(pos);
+ return p;
+}
+
+int ren_eol(char *s, int dir)
+{
+ int beg, end;
+ int *pos = ren_position(s, &beg, &end);
+ free(pos);
+ return dir * ren_dir(s) >= 0 ? end : beg;
+}
+
+/* compare two visual positions */
+int ren_cmp(char *s, int pos1, int pos2)
+{
+ return ren_dir(s ? s : "") >= 0 ? pos1 - pos2 : pos2 - pos1;
+}
+
+/*
+ * insertion offset before or after the given visual position
+ *
+ * When pre is nonzero, the return value indicates an offset of s,
+ * which, if a character is inserted at that position, it appears
+ * just before the character at pos. If pre is zero, the inserted
+ * character should appear just after the character at pos.
+ */
+int ren_insertionoffset(char *s, int pos, int pre)
+{
+ int l1;
+ if (!s)
+ return 0;
+ l1 = ren_off(s, pos);
+ return pre ? l1 : l1 + 1;
+}
diff --git a/sbuf.c b/sbuf.c
t@@ -0,0 +1,94 @@
+/* variable length string buffer */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "vi.h"
+
+#define SBUFSZ 128
+#define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1))
+
+struct sbuf {
+ char *s; /* allocated buffer */
+ int s_n; /* length of the string stored in s[] */
+ int s_sz; /* size of memory allocated for s[] */
+};
+
+static void sbuf_extend(struct sbuf *sbuf, int newsz)
+{
+ char *s = sbuf->s;
+ sbuf->s_sz = newsz;
+ sbuf->s = malloc(sbuf->s_sz);
+ if (sbuf->s_n)
+ memcpy(sbuf->s, s, sbuf->s_n);
+ free(s);
+}
+
+struct sbuf *sbuf_make(void)
+{
+ struct sbuf *sb = malloc(sizeof(*sb));
+ memset(sb, 0, sizeof(*sb));
+ return sb;
+}
+
+char *sbuf_buf(struct sbuf *sb)
+{
+ if (!sb->s)
+ sbuf_extend(sb, 1);
+ sb->s[sb->s_n] = '\0';
+ return sb->s;
+}
+
+char *sbuf_done(struct sbuf *sb)
+{
+ char *s = sbuf_buf(sb);
+ free(sb);
+ return s;
+}
+
+void sbuf_free(struct sbuf *sb)
+{
+ free(sb->s);
+ free(sb);
+}
+
+void sbuf_chr(struct sbuf *sbuf, int c)
+{
+ if (sbuf->s_n + 2 >= sbuf->s_sz)
+ sbuf_extend(sbuf, sbuf->s_sz + SBUFSZ);
+ sbuf->s[sbuf->s_n++] = c;
+}
+
+void sbuf_mem(struct sbuf *sbuf, char *s, int len)
+{
+ if (sbuf->s_n + len + 1 >= sbuf->s_sz)
+ sbuf_extend(sbuf, ALIGN(sbuf->s_n + len + 1, SBUFSZ));
+ memcpy(sbuf->s + sbuf->s_n, s, len);
+ sbuf->s_n += len;
+}
+
+void sbuf_str(struct sbuf *sbuf, char *s)
+{
+ sbuf_mem(sbuf, s, strlen(s));
+}
+
+int sbuf_len(struct sbuf *sbuf)
+{
+ return sbuf->s_n;
+}
+
+void sbuf_cut(struct sbuf *sb, int len)
+{
+ if (sb->s_n > len)
+ sb->s_n = len;
+}
+
+void sbuf_printf(struct sbuf *sbuf, char *s, ...)
+{
+ char buf[256];
+ va_list ap;
+ va_start(ap, s);
+ vsnprintf(buf, sizeof(buf), s, ap);
+ va_end(ap);
+ sbuf_str(sbuf, buf);
+}
diff --git a/term.c b/term.c
t@@ -0,0 +1,105 @@
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+#include "vi.h"
+
+static struct sbuf *term_sbuf;
+static int rows = 25, cols = 80;
+static struct termios termios;
+
+void term_init(void)
+{
+ struct winsize win;
+ struct termios newtermios;
+ tcgetattr(0, &termios);
+ newtermios = termios;
+ newtermios.c_lflag &= ~ICANON;
+ newtermios.c_lflag &= ~ECHO;
+ tcsetattr(0, TCSAFLUSH, &newtermios);
+ if (!ioctl(0, TIOCGWINSZ, &win)) {
+ cols = win.ws_col;
+ rows = win.ws_row;
+ }
+}
+
+void term_done(void)
+{
+ term_commit();
+ tcsetattr(0, 0, &termios);
+}
+
+void term_record(void)
+{
+ if (!term_sbuf)
+ term_sbuf = sbuf_make();
+}
+
+void term_commit(void)
+{
+ if (term_sbuf) {
+ write(1, sbuf_buf(term_sbuf), sbuf_len(term_sbuf));
+ sbuf_free(term_sbuf);
+ term_sbuf = NULL;
+ }
+}
+
+static void term_out(char *s)
+{
+ if (term_sbuf)
+ sbuf_str(term_sbuf, s);
+ else
+ write(1, s, strlen(s));
+}
+
+void term_str(char *s)
+{
+ term_out(s);
+}
+
+void term_chr(int ch)
+{
+ char s[4] = {ch};
+ term_out(s);
+}
+
+void term_kill(void)
+{
+ term_out("\33[K");
+}
+
+void term_pos(int r, int c)
+{
+ char buf[32] = "\r";
+ if (r < 0)
+ sprintf(buf, "\r\33[%d%c", abs(c), c > 0 ? 'C' : 'D');
+ else
+ sprintf(buf, "\33[%d;%dH", r + 1, c + 1);
+ term_out(buf);
+}
+
+int term_rows(void)
+{
+ return rows;
+}
+
+int term_cols(void)
+{
+ return cols;
+}
+
+int term_read(int ms)
+{
+ struct pollfd ufds[1];
+ char b;
+ ufds[0].fd = 0;
+ ufds[0].events = POLLIN;
+ if (poll(ufds, 1, ms * 1000) <= 0)
+ return -1;
+ if (read(0, &b, 1) <= 0)
+ return -1;
+ return (unsigned char) b;
+}
diff --git a/uc.c b/uc.c
t@@ -0,0 +1,303 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "vi.h"
+
+#define LEN(a) (sizeof(a) / sizeof((a)[0]))
+
+/* return the length of a utf-8 character */
+int uc_len(char *s)
+{
+ int c = (unsigned char) s[0];
+ if (c > 0 && c <= 0x7f)
+ return 1;
+ if (c >= 0xfc)
+ return 6;
+ if (c >= 0xf8)
+ return 5;
+ if (c >= 0xf0)
+ return 4;
+ if (c >= 0xe0)
+ return 3;
+ if (c >= 0xc0)
+ return 2;
+ return c != 0;
+}
+
+/* the number of utf-8 characters in s */
+int uc_slen(char *s)
+{
+ char *e = s + strlen(s);
+ int i;
+ for (i = 0; s < e; i++)
+ s += uc_len(s);
+ return i;
+}
+
+/* the unicode codepoint of the given utf-8 character */
+int uc_code(char *s)
+{
+ int result;
+ int l = uc_len(s);
+ if (l <= 1)
+ return (unsigned char) *s;
+ result = (0x3f >> --l) & (unsigned char) *s++;
+ while (l--)
+ result = (result << 6) | ((unsigned char) *s++ & 0x3f);
+ return result;
+}
+
+/* find the beginning of the character at s[i] */
+char *uc_beg(char *beg, char *s)
+{
+ while (s > beg && (((unsigned char) *s) & 0xc0) == 0x80)
+ s--;
+ return s;
+}
+
+/* find the end of the character at s[i] */
+char *uc_end(char *beg, char *s)
+{
+ if (!*s || !((unsigned char) *s & 0x80))
+ return s;
+ if (((unsigned char) *s & 0xc0) == 0xc0)
+ s++;
+ while (((unsigned char) *s & 0xc0) == 0x80)
+ s++;
+ return s - 1;
+}
+
+/* return a pointer to the character following s */
+char *uc_next(char *s)
+{
+ s = uc_end(s, s);
+ return *s ? s + 1 : s;
+}
+
+int uc_wid(char *s)
+{
+ return 1;
+}
+
+/* allocate and return an array for the characters in s */
+char **uc_chop(char *s, int *n)
+{
+ char **chrs;
+ int i;
+ *n = uc_slen(s);
+ chrs = malloc(*n * sizeof(chrs[0]));
+ for (i = 0; i < *n; i++) {
+ chrs[i] = s;
+ s = uc_next(s);
+ }
+ return chrs;
+}
+
+int uc_isspace(char *s)
+{
+ int c = s ? (unsigned char) *s : 0;
+ return c <= 0x7f && isspace(c);
+}
+
+int uc_isprint(char *s)
+{
+ int c = s ? (unsigned char) *s : 0;
+ return c > 0x7f || isprint(c);
+}
+
+#define UC_R2L(ch) (((ch) & 0xff00) == 0x0600 || \
+ ((ch) & 0xfffc) == 0x200c || \
+ ((ch) & 0xff00) == 0xfb00 || \
+ ((ch) & 0xff00) == 0xfc00 || \
+ ((ch) & 0xff00) == 0xfe00)
+
+/* sorted list of characters that can be shaped */
+static struct achar {
+ unsigned c; /* utf-8 code */
+ unsigned s; /* single form */
+ unsigned i; /* initial form */
+ unsigned m; /* medial form */
+ unsigned f; /* final form */
+} achars[] = {
+ {0x0621, 0xfe80}, /* hamza */
+ {0x0622, 0xfe81, 0, 0, 0xfe82}, /* alef madda */
+ {0x0623, 0xfe83, 0, 0, 0xfe84}, /* alef hamza a…
+ {0x0624, 0xfe85, 0, 0, 0xfe86}, /* waw hamza */
+ {0x0625, 0xfe87, 0, 0, 0xfe88}, /* alef hamza b…
+ {0x0626, 0xfe89, 0xfe8b, 0xfe8c, 0xfe8a}, /* yeh hamza */
+ {0x0627, 0xfe8d, 0, 0, 0xfe8e}, /* alef */
+ {0x0628, 0xfe8f, 0xfe91, 0xfe92, 0xfe90}, /* beh */
+ {0x0629, 0xfe93, 0, 0, 0xfe94}, /* teh marbuta …
+ {0x062a, 0xfe95, 0xfe97, 0xfe98, 0xfe96}, /* teh */
+ {0x062b, 0xfe99, 0xfe9b, 0xfe9c, 0xfe9a}, /* theh */
+ {0x062c, 0xfe9d, 0xfe9f, 0xfea0, 0xfe9e}, /* jeem */
+ {0x062d, 0xfea1, 0xfea3, 0xfea4, 0xfea2}, /* hah */
+ {0x062e, 0xfea5, 0xfea7, 0xfea8, 0xfea6}, /* khah */
+ {0x062f, 0xfea9, 0, 0, 0xfeaa}, /* dal */
+ {0x0630, 0xfeab, 0, 0, 0xfeac}, /* thal */
+ {0x0631, 0xfead, 0, 0, 0xfeae}, /* reh */
+ {0x0632, 0xfeaf, 0, 0, 0xfeb0}, /* zain */
+ {0x0633, 0xfeb1, 0xfeb3, 0xfeb4, 0xfeb2}, /* seen */
+ {0x0634, 0xfeb5, 0xfeb7, 0xfeb8, 0xfeb6}, /* sheen */
+ {0x0635, 0xfeb9, 0xfebb, 0xfebc, 0xfeba}, /* sad */
+ {0x0636, 0xfebd, 0xfebf, 0xfec0, 0xfebe}, /* dad */
+ {0x0637, 0xfec1, 0xfec3, 0xfec4, 0xfec2}, /* tah */
+ {0x0638, 0xfec5, 0xfec7, 0xfec8, 0xfec6}, /* zah */
+ {0x0639, 0xfec9, 0xfecb, 0xfecc, 0xfeca}, /* ain */
+ {0x063a, 0xfecd, 0xfecf, 0xfed0, 0xfece}, /* ghain */
+ {0x0640, 0x640, 0x640, 0x640}, /* tatweel */
+ {0x0641, 0xfed1, 0xfed3, 0xfed4, 0xfed2}, /* feh */
+ {0x0642, 0xfed5, 0xfed7, 0xfed8, 0xfed6}, /* qaf */
+ {0x0643, 0xfed9, 0xfedb, 0xfedc, 0xfeda}, /* kaf */
+ {0x0644, 0xfedd, 0xfedf, 0xfee0, 0xfede}, /* lam */
+ {0x0645, 0xfee1, 0xfee3, 0xfee4, 0xfee2}, /* meem */
+ {0x0646, 0xfee5, 0xfee7, 0xfee8, 0xfee6}, /* noon */
+ {0x0647, 0xfee9, 0xfeeb, 0xfeec, 0xfeea}, /* heh */
+ {0x0648, 0xfeed, 0, 0, 0xfeee}, /* waw */
+ {0x0649, 0xfeef, 0, 0, 0xfef0}, /* alef maksura…
+ {0x064a, 0xfef1, 0xfef3, 0xfef4, 0xfef2}, /* yeh */
+ {0x067e, 0xfb56, 0xfb58, 0xfb59, 0xfb57}, /* peh */
+ {0x0686, 0xfb7a, 0xfb7c, 0xfb7d, 0xfb7b}, /* tcheh */
+ {0x0698, 0xfb8a, 0, 0, 0xfb8b}, /* jeh */
+ {0x06a9, 0xfb8e, 0xfb90, 0xfb91, 0xfb8f}, /* fkaf */
+ {0x06af, 0xfb92, 0xfb94, 0xfb95, 0xfb93}, /* gaf */
+ {0x06cc, 0xfbfc, 0xfbfe, 0xfbff, 0xfbfd}, /* fyeh */
+ {0x200c}, /* ZWNJ */
+ {0x200d, 0, 0x200d, 0x200d}, /* ZWJ */
+};
+
+static struct achar *find_achar(int c)
+{
+ int h, m, l;
+ h = LEN(achars);
+ l = 0;
+ /* using binary search to find c */
+ while (l < h) {
+ m = (h + l) >> 1;
+ if (achars[m].c == c)
+ return &achars[m];
+ if (c < achars[m].c)
+ h = m;
+ else
+ l = m + 1;
+ }
+ return NULL;
+}
+
+static int can_join(int c1, int c2)
+{
+ struct achar *a1 = find_achar(c1);
+ struct achar *a2 = find_achar(c2);
+ return a1 && a2 && (a1->i || a1->m) && (a2->f || a2->m);
+}
+
+static int uc_cshape(int cur, int prev, int next)
+{
+ int c = cur;
+ int join_prev, join_next;
+ struct achar *ac = find_achar(c);
+ if (!ac) /* ignore non-Arabic characters */
+ return c;
+ join_prev = can_join(prev, c);
+ join_next = can_join(c, next);
+ if (join_prev && join_next)
+ c = ac->m;
+ if (join_prev && !join_next)
+ c = ac->f;
+ if (!join_prev && join_next)
+ c = ac->i;
+ if (!join_prev && !join_next)
+ c = ac->c; /* some fonts do not have a glyph for ac->s …
+ return c ? c : cur;
+}
+
+/*
+ * return nonzero for Arabic combining characters
+ *
+ * The standard Arabic diacritics:
+ * + 0x064b: fathatan
+ * + 0x064c: dammatan
+ * + 0x064d: kasratan
+ * + 0x064e: fatha
+ * + 0x064f: damma
+ * + 0x0650: kasra
+ * + 0x0651: shadda
+ * + 0x0652: sukun
+ * + 0x0653: madda above
+ * + 0x0654: hamza above
+ * + 0x0655: hamza below
+ * + 0x0670: superscript alef
+ */
+static int uc_comb(int c)
+{
+ return (c >= 0x064b && c <= 0x0655) || /* the standard …
+ (c >= 0xfc5e && c <= 0xfc63) || /* shadda ligat…
+ c == 0x0670; /* superscript ale…
+}
+
+/* the direction of the given utf-8 character */
+int uc_dir(char *s)
+{
+ int u, c = (unsigned char) s[0];
+ if (c < 128 && (ispunct(c) || isspace(c)))
+ return 0;
+ if (c < 128 && isalnum(c))
+ return 1;
+ u = uc_code(s);
+ if (UC_R2L(u))
+ return -1;
+ return 1;
+}
+
+static void uc_cput(char *d, int c)
+{
+ int l = 0;
+ if (c > 0xffff) {
+ *d++ = 0xf0 | (c >> 18);
+ l = 3;
+ } else if (c > 0x7ff) {
+ *d++ = 0xe0 | (c >> 12);
+ l = 2;
+ } else if (c > 0x7f) {
+ *d++ = 0xc0 | (c >> 6);
+ l = 1;
+ } else {
+ *d++ = c;
+ }
+ while (l--)
+ *d++ = 0x80 | ((c >> (l * 6)) & 0x3f);
+ *d = '\0';
+}
+
+/* shape the given arabic character; returns a static buffer */
+char *uc_shape(char *beg, char *s)
+{
+ static char out[16];
+ char *r;
+ int prev = 0;
+ int next = 0;
+ int curr = uc_code(s);
+ if (!curr || !UC_R2L(curr)) {
+ uc_cput(out, curr);
+ return out;
+ }
+ r = s;
+ while (r > beg) {
+ r = uc_beg(beg, r - 1);
+ if (!uc_comb(uc_code(r))) {
+ prev = uc_code(r);
+ break;
+ }
+ }
+ r = s;
+ while (*r) {
+ r = uc_next(r);
+ if (!uc_comb(uc_code(r))) {
+ next = uc_code(r);
+ break;
+ }
+ }
+ uc_cput(out, uc_cshape(curr, prev, next));
+ return out;
+}
diff --git a/vi.c b/vi.c
t@@ -0,0 +1,548 @@
+/*
+ * neatvi editor
+ *
+ * Copyright (C) 2015 Ali Gholami Rudi <ali at rudi dot ir>
+ *
+ * This program is released under the Modified BSD license.
+ */
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "vi.h"
+
+char xpath[PATHLEN]; /* current file */
+struct lbuf *xb; /* current buffer */
+int xrow, xcol, xtop; /* current row, column, and top row */
+int xled = 1; /* use the line editor */
+int xquit;
+
+static void vi_draw(void)
+{
+ int i;
+ term_record();
+ for (i = xtop; i < xtop + xrows; i++) {
+ char *s = lbuf_get(xb, i);
+ led_print(s ? s : "~", i - xtop);
+ }
+ term_pos(xrow, xcol);
+ term_commit();
+}
+
+static int vi_buf[128];
+static int vi_buflen;
+
+static int vi_read(void)
+{
+ return vi_buflen ? vi_buf[--vi_buflen] : term_read(1000);
+}
+
+static void vi_back(int c)
+{
+ if (vi_buflen < sizeof(vi_buf))
+ vi_buf[vi_buflen++] = c;
+}
+
+static char *vi_char(void)
+{
+ return led_keymap(vi_read());
+}
+
+static int vi_prefix(void)
+{
+ int pre = 0;
+ int c = vi_read();
+ if ((c >= '1' && c <= '9')) {
+ while (isdigit(c)) {
+ pre = pre * 10 + c - '0';
+ c = vi_read();
+ }
+ }
+ vi_back(c);
+ return pre;
+}
+
+static int lbuf_lnnext(struct lbuf *lb, int *r, int *c, int dir)
+{
+ char *ln = lbuf_get(lb, *r);
+ int col = ren_next(ln, *c, dir);
+ if (col < 0)
+ return -1;
+ *c = col;
+ return 0;
+}
+
+static void lbuf_eol(struct lbuf *lb, int *r, int *c, int dir)
+{
+ char *ln = lbuf_get(lb, *r);
+ *c = ren_eol(ln ? ln : "", dir);
+}
+
+static int lbuf_next(struct lbuf *lb, int *r, int *c, int dir)
+{
+ if (dir < 0 && *r >= lbuf_len(lb))
+ *r = MAX(0, lbuf_len(lb) - 1);
+ if (lbuf_lnnext(lb, r, c, dir)) {
+ if (!lbuf_get(lb, *r + dir))
+ return -1;
+ *r += dir;
+ lbuf_eol(lb, r, c, -dir);
+ return 0;
+ }
+ return 0;
+}
+
+/* return a static buffer to the character at visual position c of line r */
+static char *lbuf_chr(struct lbuf *lb, int r, int c)
+{
+ static char chr[8];
+ char *ln = lbuf_get(lb, r);
+ int n;
+ if (ln) {
+ char **chrs = uc_chop(ln, &n);
+ int off = ren_off(ln, c);
+ if (off >= 0 && off < n) {
+ memcpy(chr, chrs[off], uc_len(chrs[off]));
+ chr[uc_len(chr)] = '\0';
+ free(chrs);
+ return chr;
+ }
+ free(chrs);
+ }
+ return NULL;
+}
+
+static void lbuf_postindents(struct lbuf *lb, int *r, int *c)
+{
+ lbuf_eol(lb, r, c, -1);
+ while (uc_isspace(lbuf_chr(lb, *r, *c)))
+ if (lbuf_lnnext(lb, r, c, +1))
+ break;
+}
+
+static void lbuf_findchar(struct lbuf *lb, int *row, int *col, char *cs, int d…
+{
+ int c = *col;
+ if (!cs)
+ return;
+ while (n > 0 && !lbuf_lnnext(lb, row, &c, dir))
+ if (uc_code(lbuf_chr(lb, *row, c)) == uc_code(cs))
+ n--;
+ if (!n)
+ *col = c;
+}
+
+static void lbuf_tochar(struct lbuf *lb, int *row, int *col, char *cs, int dir…
+{
+ int c = *col;
+ if (!cs)
+ return;
+ while (n > 0 && !lbuf_lnnext(lb, row, &c, dir))
+ if (uc_code(lbuf_chr(lb, *row, c)) == uc_code(cs))
+ n--;
+ if (!n) {
+ *col = c;
+ lbuf_lnnext(lb, row, col, -dir);
+ }
+}
+
+static int vi_motionln(int *row, int cmd, int pre1, int pre2)
+{
+ int pre = (pre1 ? pre1 : 1) * (pre2 ? pre2 : 1);
+ int c = vi_read();
+ int mark;
+ switch (c) {
+ case '\n':
+ *row = MIN(*row + pre, lbuf_len(xb) - 1);
+ break;
+ case '-':
+ *row = MAX(*row - pre, 0);
+ break;
+ case '\'':
+ if ((mark = vi_read()) > 0 && (isalpha(mark) || mark == '\''))
+ if (lbuf_markpos(xb, mark) >= 0)
+ *row = lbuf_markpos(xb, mark);
+ break;
+ case 'j':
+ *row = MIN(*row + pre, lbuf_len(xb) - 1);
+ break;
+ case 'k':
+ *row = MAX(*row - pre, 0);
+ break;
+ case 'G':
+ *row = (pre1 || pre2) ? pre - 1 : lbuf_len(xb) - 1;
+ break;
+ case 'H':
+ if (lbuf_len(xb))
+ *row = MIN(xtop + pre - 1, lbuf_len(xb) - 1);
+ else
+ *row = 0;
+ break;
+ case 'L':
+ if (lbuf_len(xb))
+ *row = MIN(xtop + xrows - 1 - pre + 1, lbuf_len(xb) - …
+ else
+ *row = 0;
+ break;
+ case 'M':
+ if (lbuf_len(xb))
+ *row = MIN(xtop + xrows / 2, lbuf_len(xb) - 1);
+ else
+ *row = 0;
+ break;
+ default:
+ if (c == cmd) {
+ *row = MIN(*row + pre - 1, lbuf_len(xb) - 1);
+ break;
+ }
+ vi_back(c);
+ return 0;
+ }
+ return c;
+}
+
+static int vi_motion(int *row, int *col, int pre1, int pre2)
+{
+ int c = vi_read();
+ int pre = (pre1 ? pre1 : 1) * (pre2 ? pre2 : 1);
+ char *ln = lbuf_get(xb, *row);
+ int dir = ren_dir(ln ? ln : "");
+ int i;
+ switch (c) {
+ case ' ':
+ for (i = 0; i < pre; i++)
+ if (lbuf_next(xb, row, col, 1))
+ break;
+ break;
+ case 'f':
+ lbuf_findchar(xb, row, col, vi_char(), +1, pre);
+ break;
+ case 'F':
+ lbuf_findchar(xb, row, col, vi_char(), -1, pre);
+ break;
+ case 'h':
+ for (i = 0; i < pre; i++)
+ if (lbuf_lnnext(xb, row, col, -1 * dir))
+ break;
+ break;
+ case 'l':
+ for (i = 0; i < pre; i++)
+ if (lbuf_lnnext(xb, row, col, +1 * dir))
+ break;
+ break;
+ case 't':
+ lbuf_tochar(xb, row, col, vi_char(), 1, pre);
+ break;
+ case 'T':
+ lbuf_tochar(xb, row, col, vi_char(), 0, pre);
+ break;
+ case 'B':
+ if (!uc_isspace(lbuf_chr(xb, *row, *col)))
+ lbuf_next(xb, row, col, -1);
+ while (uc_isspace(lbuf_chr(xb, *row, *col)))
+ if (lbuf_next(xb, row, col, -1))
+ break;
+ while (!lbuf_next(xb, row, col, -1)) {
+ if (uc_isspace(lbuf_chr(xb, *row, *col))) {
+ lbuf_next(xb, row, col, 1);
+ break;
+ }
+ }
+ break;
+ case 'E':
+ if (!uc_isspace(lbuf_chr(xb, *row, *col)))
+ lbuf_next(xb, row, col, 1);
+ while (uc_isspace(lbuf_chr(xb, *row, *col)))
+ if (lbuf_next(xb, row, col, 1))
+ break;
+ while (!lbuf_next(xb, row, col, 1)) {
+ if (uc_isspace(lbuf_chr(xb, *row, *col))) {
+ lbuf_next(xb, row, col, -1);
+ break;
+ }
+ }
+ break;
+ case 'W':
+ while (!uc_isspace(lbuf_chr(xb, *row, *col)))
+ if (lbuf_next(xb, row, col, 1))
+ break;
+ while (uc_isspace(lbuf_chr(xb, *row, *col)))
+ if (lbuf_next(xb, row, col, 1))
+ break;
+ break;
+ case '0':
+ lbuf_eol(xb, row, col, -1);
+ break;
+ case '$':
+ lbuf_eol(xb, row, col, +1);
+ lbuf_lnnext(xb, row, col, -1);
+ break;
+ case 127:
+ case TERMCTRL('h'):
+ *col = ren_cursor(ln, *col);
+ for (i = 0; i < pre; i++)
+ if (lbuf_next(xb, row, col, -1))
+ break;
+ break;
+ default:
+ vi_back(c);
+ return 0;
+ }
+ return c;
+}
+
+static char *vi_strprefix(char *s, int off)
+{
+ struct sbuf *sb = sbuf_make();
+ int n;
+ char **chrs = uc_chop(s ? s : "", &n);
+ if (n > 0)
+ sbuf_mem(sb, s, chrs[MIN(n - 1, off)] - s);
+ free(chrs);
+ return sbuf_done(sb);
+}
+
+static char *vi_strpostfix(char *s, int off)
+{
+ struct sbuf *sb = sbuf_make();
+ int n;
+ char **chrs = uc_chop(s ? s : "", &n);
+ if (n >= 0 && off < n)
+ sbuf_str(sb, chrs[off]);
+ free(chrs);
+ if (!sbuf_len(sb))
+ sbuf_chr(sb, '\n');
+ return sbuf_done(sb);
+}
+
+static void swap(int *a, int *b)
+{
+ int t = *a;
+ *a = *b;
+ *b = t;
+}
+
+static char *sdup(char *s) /* strdup() */
+{
+ char *r = malloc(strlen(s) + 1);
+ return r ? strcpy(r, s) : NULL;
+}
+
+static void vc_motion(int c, int pre1)
+{
+ int r1 = xrow, r2 = xrow; /* region rows */
+ int c1 = xcol, c2 = xcol; /* visual region columns */
+ int l1, l2; /* logical region columns */
+ int ln = 0; /* line-based region */
+ int closed = 1; /* include the last character */
+ int mv, i;
+ char *pref = NULL;
+ char *post = NULL;
+ int pre2 = vi_prefix();
+ if (pre2 < 0)
+ return;
+ if (vi_motionln(&r2, c, pre1, pre2)) {
+ ln = 1;
+ lbuf_eol(xb, &r1, &c1, -1);
+ lbuf_eol(xb, &r2, &c2, +1);
+ } else if ((mv = vi_motion(&r2, &c2, pre1, pre2))) {
+ if (strchr("0bBhlwW ", mv))
+ closed = 0;
+ } else {
+ return;
+ }
+ /* make sure the first position is visually before the second */
+ if (r2 < r1 || (r2 == r1 && ren_cmp(lbuf_get(xb, r1), c1, c2) > 0)) {
+ swap(&r1, &r2);
+ swap(&c1, &c2);
+ }
+ for (i = 0; i < 2; i++) {
+ l1 = ren_insertionoffset(lbuf_get(xb, r1), c1, 1);
+ l2 = ren_insertionoffset(lbuf_get(xb, r2), c2, !closed);
+ if (r1 == r2 && l2 < l1) /* offsets out of order */
+ swap(&l1, &l2);
+ }
+ pref = ln ? sdup("") : vi_strprefix(lbuf_get(xb, r1), l1);
+ post = ln ? sdup("\n") : vi_strpostfix(lbuf_get(xb, r2), l2);
+ if (c == 'd') {
+ lbuf_rm(xb, r1, r2 + 1);
+ if (!ln) {
+ struct sbuf *sb = sbuf_make();
+ sbuf_str(sb, pref);
+ sbuf_str(sb, post);
+ lbuf_put(xb, r1, sbuf_buf(sb));
+ sbuf_free(sb);
+ }
+ xrow = r1;
+ xcol = c1;
+ if (ln)
+ lbuf_postindents(xb, &xrow, &xcol);
+ }
+ if (c == 'c') {
+ int row, col;
+ char *rep = led_input(pref, post, &row, &col);
+ if (rep) {
+ lbuf_rm(xb, r1, r2 + 1);
+ lbuf_put(xb, r1, rep);
+ xrow = r1 + row;
+ xcol = col;
+ free(rep);
+ }
+ }
+ free(pref);
+ free(post);
+}
+
+static void vc_insert(int cmd)
+{
+ char *pref, *post;
+ char *ln = lbuf_get(xb, xrow);
+ int row, col, off = 0;
+ char *rep;
+ if (cmd == 'I')
+ lbuf_postindents(xb, &xrow, &xcol);
+ if (cmd == 'A') {
+ lbuf_eol(xb, &xrow, &xcol, +1);
+ lbuf_lnnext(xb, &xrow, &xcol, -1);
+ }
+ if (cmd == 'o')
+ xrow += 1;
+ if (cmd == 'o' || cmd == 'O')
+ ln = NULL;
+ if (cmd == 'i' || cmd == 'I')
+ off = ln ? ren_insertionoffset(ln, xcol, 1) : 0;
+ if (cmd == 'a' || cmd == 'A')
+ off = ln ? ren_insertionoffset(ln, xcol, 0) : 0;
+ pref = ln ? vi_strprefix(ln, off) : sdup("");
+ post = ln ? vi_strpostfix(ln, off) : sdup("\n");
+ rep = led_input(pref, post, &row, &col);
+ if (rep) {
+ if (cmd != 'o' && cmd != 'O')
+ lbuf_rm(xb, xrow, xrow + 1);
+ lbuf_put(xb, xrow, rep);
+ xrow += row;
+ xcol = col;
+ free(rep);
+ }
+ free(pref);
+ free(post);
+}
+
+static void vi(void)
+{
+ int mark;
+ term_init();
+ xtop = 0;
+ xrow = 0;
+ lbuf_eol(xb, &xrow, &xcol, -1);
+ vi_draw();
+ term_pos(xrow, xcol);
+ while (!xquit) {
+ int redraw = 0;
+ int orow = xrow;
+ int pre1, mv;
+ if ((pre1 = vi_prefix()) < 0)
+ continue;
+ if ((mv = vi_motionln(&xrow, 0, pre1, 0))) {
+ if (strchr("\'GHML", mv))
+ lbuf_mark(xb, '\'', orow);
+ if (!strchr("jk", mv))
+ lbuf_postindents(xb, &xrow, &xcol);
+ } else if (!vi_motion(&xrow, &xcol, pre1, 0)) {
+ int c = vi_read();
+ if (c <= 0)
+ continue;
+ switch (c) {
+ case 'u':
+ lbuf_undo(xb);
+ redraw = 1;
+ break;
+ case TERMCTRL('b'):
+ xtop = MAX(0, xtop - xrows + 1);
+ xrow = xtop + xrows - 1;
+ redraw = 1;
+ break;
+ case TERMCTRL('f'):
+ if (lbuf_len(xb))
+ xtop = MIN(lbuf_len(xb) - 1, xtop + xr…
+ else
+ xtop = 0;
+ xrow = xtop;
+ redraw = 1;
+ break;
+ case TERMCTRL('r'):
+ lbuf_redo(xb);
+ redraw = 1;
+ break;
+ case ':':
+ term_pos(xrows, 0);
+ term_kill();
+ ex_command(NULL);
+ if (xquit)
+ continue;
+ redraw = 1;
+ break;
+ case 'c':
+ case 'd':
+ vc_motion(c, pre1);
+ redraw = 1;
+ break;
+ case 'i':
+ case 'I':
+ case 'a':
+ case 'A':
+ case 'o':
+ case 'O':
+ vc_insert(c);
+ redraw = 1;
+ break;
+ case 'm':
+ if ((mark = vi_read()) > 0 && isalpha(mark))
+ lbuf_mark(xb, mark, xrow);
+ break;
+ default:
+ continue;
+ }
+ }
+ if (xrow < 0 || xrow >= lbuf_len(xb))
+ xrow = lbuf_len(xb) ? lbuf_len(xb) - 1 : 0;
+ if (xrow < xtop || xrow >= xtop + xrows) {
+ xtop = xrow < xtop ? xrow : MAX(0, xrow - xrows + 1);
+ redraw = 1;
+ }
+ if (redraw)
+ vi_draw();
+ term_pos(xrow - xtop, ren_cursor(lbuf_get(xb, xrow), xcol));
+ lbuf_undomark(xb);
+ }
+ term_pos(xrows, 0);
+ term_kill();
+ term_done();
+}
+
+int main(int argc, char *argv[])
+{
+ int visual = 1;
+ char ecmd[PATHLEN];
+ int i;
+ xb = lbuf_make();
+ for (i = 1; i < argc && argv[i][0] == '-'; i++) {
+ if (argv[i][1] == 's')
+ xled = 0;
+ if (argv[i][1] == 'e')
+ visual = 0;
+ if (argv[i][1] == 'v')
+ visual = 1;
+ }
+ if (i < argc) {
+ snprintf(ecmd, PATHLEN, "e %s", argv[i]);
+ ex_command(ecmd);
+ }
+ if (visual)
+ vi();
+ else
+ ex();
+ lbuf_free(xb);
+ return 0;
+}
diff --git a/vi.h b/vi.h
t@@ -0,0 +1,102 @@
+/* neatvi main header */
+
+#define PATHLEN 512
+
+/* helper macros */
+#define LEN(a) (sizeof(a) / sizeof((a)[0]))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+
+/* line buffer, managing a number of lines */
+struct lbuf *lbuf_make(void);
+void lbuf_free(struct lbuf *lbuf);
+void lbuf_rd(struct lbuf *lbuf, int fd, int pos);
+void lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end);
+void lbuf_rm(struct lbuf *lbuf, int beg, int end);
+char *lbuf_cp(struct lbuf *lbuf, int beg, int end);
+void lbuf_put(struct lbuf *lbuf, int pos, char *s);
+char *lbuf_get(struct lbuf *lbuf, int pos);
+int lbuf_len(struct lbuf *lbuf);
+void lbuf_mark(struct lbuf *lbuf, int mark, int pos);
+int lbuf_markpos(struct lbuf *lbuf, int mark);
+void lbuf_undo(struct lbuf *lbuf);
+void lbuf_redo(struct lbuf *lbuf);
+void lbuf_undomark(struct lbuf *lbuf);
+void lbuf_undofree(struct lbuf *lbuf);
+
+/* string buffer, variable-sized string */
+struct sbuf *sbuf_make(void);
+void sbuf_free(struct sbuf *sb);
+char *sbuf_done(struct sbuf *sb);
+char *sbuf_buf(struct sbuf *sb);
+void sbuf_chr(struct sbuf *sb, int c);
+void sbuf_str(struct sbuf *sb, char *s);
+void sbuf_mem(struct sbuf *sb, char *s, int len);
+void sbuf_printf(struct sbuf *sbuf, char *s, ...);
+int sbuf_len(struct sbuf *sb);
+void sbuf_cut(struct sbuf *s, int len);
+
+/* rendering lines */
+char *ren_all(char *s, int wid);
+int ren_cursor(char *s, int pos);
+int ren_next(char *s, int p, int dir);
+int ren_eol(char *s, int dir);
+int ren_dir(char *s);
+int ren_pos(char *s, int off);
+int ren_off(char *s, int pos);
+int ren_last(char *s);
+int ren_cmp(char *s, int pos1, int pos2);
+int ren_insertionoffset(char *s, int pos, int pre);
+
+/* utf-8 helper functions */
+int uc_len(char *s);
+int uc_dir(char *s);
+int uc_wid(char *s);
+int uc_slen(char *s);
+int uc_code(char *s);
+int uc_isspace(char *s);
+int uc_isprint(char *s);
+char **uc_chop(char *s, int *n);
+char *uc_next(char *s);
+char *uc_beg(char *beg, char *s);
+char *uc_end(char *beg, char *s);
+char *uc_shape(char *beg, char *s);
+
+/* managing the terminal */
+#define xrows (term_rows() - 1)
+#define xcols (term_cols())
+
+void term_init(void);
+void term_done(void);
+void term_str(char *s);
+void term_chr(int ch);
+void term_pos(int r, int c);
+void term_clear(void);
+void term_kill(void);
+int term_rows(void);
+int term_cols(void);
+int term_read(int timeout);
+void term_record(void);
+void term_commit(void);
+
+#define TERMCTRL(x) ((x) - 96)
+#define TERMESC 27
+
+/* line-oriented input and output */
+char *led_prompt(char *pref, char *post);
+char *led_input(char *pref, char *post, int *row, int *col);
+void led_print(char *msg, int row);
+char *led_keymap(int c);
+
+/* ex commands */
+void ex(void);
+void ex_command(char *cmd);
+
+/* global variables */
+extern struct lbuf *xb;
+extern int xrow;
+extern int xcol;
+extern int xtop;
+extern int xled;
+extern char xpath[];
+extern int xquit;
You are viewing proxied material from mx1.adamsgaard.dk. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.