tMerge source into single file - ve - a minimal text editor (work in progress)
 (HTM) git clone git://src.adamsgaard.dk/ve
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 7a8cfc0fc10085e622932f958235613b9ca8e149
 (DIR) parent 0c629a2638b242c02e9dfce571b6738d64727b34
 (HTM) Author: Anders Damsgaard <anders@adamsgaard.dk>
       Date:   Wed,  7 Aug 2019 21:34:42 +0200
       
       Merge source into single file
       
       Diffstat:
         M Makefile                            |      14 +++++++-------
         D src/edit.c                          |      60 -------------------------------
         D src/edit.h                          |       9 ---------
         D src/find.c                          |     113 -------------------------------
         D src/find.h                          |       9 ---------
         D src/input.c                         |     254 -------------------------------
         D src/input.h                         |       7 -------
         D src/io.c                            |     103 -------------------------------
         D src/io.h                            |       7 -------
         D src/main.c                          |      27 ---------------------------
         D src/output.c                        |     215 -------------------------------
         D src/output.h                        |      14 --------------
         D src/row.c                           |     143 -------------------------------
         D src/row.h                           |      16 ----------------
         D src/terminal.c                      |     107 -------------------------------
         D src/terminal.h                      |      11 -----------
         D src/ve.c                            |      39 -------------------------------
         D src/ve.h                            |      39 -------------------------------
         A ve.c                                |    1062 +++++++++++++++++++++++++++++++
       
       19 files changed, 1069 insertions(+), 1180 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       t@@ -1,9 +1,9 @@
       +.POSIX:
       +
        CFLAGS = -g -std=c99 -pedantic -Wall -Wextra
       -#LDFLAGS = -lm
       -SRCDIR = src
       -SRC = $(wildcard $(SRCDIR)/*.c)
       -OBJ = $(patsubst %.c,%.o,$(SRC))
       -HDR = $(wildcard *.h)
       +LDFLAGS = 
       +SRC = ve.c
       +OBJ = $(SRC:.c=.o)
        BIN = ve
        
        PREFIX ?= /usr/local
       t@@ -12,7 +12,7 @@ STRIP ?= strip
        
        default: $(BIN)
        
       -$(BIN): $(OBJ) $(HDR)
       +$(BIN): $(OBJ)
                $(CC) $(LDFLAGS) $(OBJ) -o $@
        
        install: $(BIN)
       t@@ -31,7 +31,7 @@ memtest: $(BIN)
                valgrind --error-exitcode=1 --leak-check=full $(BIN) -v
        
        clean:
       -        $(RM) $(SRCDIR)/*.o
       +        $(RM) *.o
                $(RM) $(BIN)
        
        .PHONY: default install uninstall test memtest clean
 (DIR) diff --git a/src/edit.c b/src/edit.c
       t@@ -1,60 +0,0 @@
       -#include "row.h"
       -#include "ve.h"
       -
       -void
       -editor_insert_char(int c)
       -{
       -        if (E.cursor_y == E.num_rows)
       -                editor_row_insert(E.num_rows, "", 0);
       -        editor_row_insert_char(&E.row[E.cursor_y], E.cursor_x, c);
       -        E.cursor_x++;
       -}
       -
       -void
       -editor_insert_new_line()
       -{
       -        eRow *row;
       -        if (E.cursor_x == 0) {
       -                editor_row_insert(E.cursor_y, "", 0);
       -        } else {
       -                row = &E.row[E.cursor_y];
       -                editor_row_insert(E.cursor_y + 1, &row->chars[E.cursor_x],
       -                                  row->size - E.cursor_x);
       -                row = &E.row[E.cursor_y];
       -                row->size = E.cursor_x;
       -                row->chars[row->size] = '\0';
       -                editor_row_update(row);
       -        }
       -        E.cursor_y++;
       -        E.cursor_x = 0;
       -}
       -
       -/* delete a character before the cursor, and allow for deletion across rows */
       -void
       -editor_delete_char_left()
       -{
       -        eRow *row;
       -        if (E.cursor_y == E.num_rows || (E.cursor_x == 0 && E.cursor_y == 0))
       -                return;
       -        
       -        row = &E.row[E.cursor_y];
       -        if (E.cursor_x > 0) {
       -                editor_row_delete_char(row, E.cursor_x - 1);
       -                E.cursor_x--;
       -        } else {
       -                E.cursor_x = E.row[E.cursor_y - 1].size;
       -                editor_row_append_string(&E.row[E.cursor_y - 1],
       -                                         row->chars, row->size);
       -                editor_row_delete(E.cursor_y);
       -                E.cursor_y--;
       -        }
       -}
       -
       -void
       -editor_delete_char_right()
       -{
       -        if (E.cursor_y == E.num_rows)
       -                return;
       -        eRow *row = &E.row[E.cursor_y];
       -        editor_row_delete_char(row, E.cursor_x);
       -}
 (DIR) diff --git a/src/edit.h b/src/edit.h
       t@@ -1,9 +0,0 @@
       -#ifndef EDIT_H_
       -#define EDIT_H_
       -
       -void editor_insert_char(int c);
       -void editor_insert_new_line();
       -void editor_delete_char_left();
       -void editor_delete_char_right();
       -
       -#endif
 (DIR) diff --git a/src/find.c b/src/find.c
       t@@ -1,113 +0,0 @@
       -/* add feature test macro for strdup compatibility */
       -#define _DEFAULT_SOURCE
       -#define _BSD_SOURCE
       -#define _GNU_SOURCE
       -
       -#include <stdlib.h>
       -#include <string.h>
       -#include "input.h"
       -#include "ve.h"
       -#include "row.h"
       -#include "output.h"
       -
       -/* reverse of strstr (3) */
       -char*
       -strrstr(char *haystack, char *needle,
       -        size_t haystack_length, size_t needle_length)
       -{
       -  char *cp;
       -  for (cp = haystack + haystack_length - needle_length;
       -       cp >= haystack;
       -       cp--) {
       -    if (strncmp(cp, needle, needle_length) == 0)
       -        return cp;
       -  }
       -  return NULL;
       -} 
       -
       -/* find E.find_query from current cursor position moving in E.direction 
       - * if opposite_direction = 0, and opposite E.direction if
       - * opposite_direction = 1 */
       -void
       -editor_find_next_occurence(int opposite_direction)
       -{
       -        int y, y_inc, x_offset, query_len;
       -        eRow *row;
       -        char *match;
       -
       -        if (!E.find_query)
       -                return;
       -
       -        if ((E.find_direction && !opposite_direction) ||
       -                (!E.find_direction && opposite_direction))
       -                y_inc = +1;
       -        else
       -                y_inc = -1;
       -
       -        x_offset = 0;
       -        query_len = strlen(E.find_query);
       -        /* from cursor until end of document */
       -        for (y = E.cursor_y;
       -             y != ((y_inc == +1) ? E.num_rows : -1);
       -             y += y_inc) {
       -                row = &E.row[y];
       -
       -                if (y == E.cursor_y)
       -                        x_offset = y_inc + E.cursor_x;
       -                else
       -                        x_offset = 0;
       -
       -                if (y_inc == +1) {
       -                        match = strstr(row->chars + x_offset, E.find_query);
       -                        if (match)
       -                                break;
       -                } else {
       -                        match = strrstr(row->chars, E.find_query,
       -                                        (y == E.cursor_y) ? E.cursor_x : row->size,
       -                                        query_len);
       -                        if (match)
       -                                break;
       -                }
       -        }
       -
       -        if (match) {
       -                E.cursor_y = y;
       -                E.cursor_x = match - row->chars;
       -                /* E.row_offset = E.num_rows; */ /* put line to top of screen */
       -
       -        } else {
       -                /* from other end of file until cursor */
       -                for (y = (y_inc == +1) ? 0 : E.num_rows - 1;
       -                     y != E.cursor_y + y_inc;
       -                     y += y_inc) {
       -                        row = &E.row[y];
       -                        match = strstr(row->chars, E.find_query);
       -                        if (match)
       -                                break;
       -                }
       -                if (match) {
       -                        E.cursor_y = y;
       -                        E.cursor_x = editor_row_cursor_rx_to_x(row, match - row->chars);
       -                }
       -        }
       -}
       -
       -/* direction = 1 is forward, 0 is backward */
       -void
       -editor_find(int direction)
       -{
       -        char *query;
       -
       -        if (direction)
       -                query = editor_prompt("/%s");
       -        else
       -                query = editor_prompt("?%s");
       -        if (query == NULL)
       -                return;
       -
       -        E.find_direction = direction;
       -        free(E.find_query);
       -        E.find_query = strdup(query);
       -        editor_find_next_occurence(0);
       -        free(query);
       -}
 (DIR) diff --git a/src/find.h b/src/find.h
       t@@ -1,9 +0,0 @@
       -#ifndef FIND_H_
       -#define FIND_H_
       -
       -char* strrstr(char *haystack, char *needle,
       -              size_t haystack_length, size_t needle_length);
       -void editor_find_next_occurence(int opposite_direction);
       -void editor_find(int direction);
       -
       -#endif
 (DIR) diff --git a/src/input.c b/src/input.c
       t@@ -1,254 +0,0 @@
       -#include <unistd.h>
       -#include <stdlib.h>
       -#include <ctype.h>
       -#include <string.h>
       -#include "ve.h"
       -#include "terminal.h"
       -#include "edit.h"
       -#include "output.h"
       -#include "io.h"
       -#include "row.h"
       -#include "find.h"
       -
       -#define CTRL_KEY(k) ((k) & 0x1f)
       -
       -/* prompt is expected to be a format string containing a %s */
       -char*
       -editor_prompt(char *prompt)
       -{
       -        size_t bufsize, buflen;
       -        char *buf;
       -        int c;
       -        
       -        bufsize = 128;
       -        buflen = 0;
       -        buf = malloc(bufsize);
       -        buf[0] = '\0';
       -
       -        while (1) {
       -                editor_set_status_message(prompt, buf);
       -                editor_refresh_screen();
       -                editor_place_cursor(strlen(prompt) - 1 + strlen(buf),
       -                                    E.screen_rows + 2);
       -
       -                c = editor_read_key();
       -                if (c == CTRL_KEY('h') || c == 127) { /* detect backspace */
       -                        if (buflen != 0)
       -                                buf[--buflen] = '\0';
       -                } else if (c == '\x1b' || (c == '\r' && !buflen)) { /* detect escape */
       -                        editor_set_status_message("");
       -                        free(buf);
       -                        return NULL;
       -                } else if (c == '\r') {
       -                        if (buflen != 0) {
       -                                editor_set_status_message("");
       -                                return buf;
       -                        }
       -                } else if (!iscntrl(c) && c < 128) {
       -                        if (buflen >= bufsize - 1) {
       -                                bufsize *= 2;
       -                                buf = realloc(buf, bufsize);
       -                        }
       -                        buf[buflen++] = c;
       -                        buf[buflen] = '\0';
       -                }
       -        }
       -}
       -
       -/* move cursor according to screen, file, and line limits */
       -void
       -editor_move_cursor(char key)
       -{
       -        int row_len;
       -        eRow *row;
       -        
       -        switch(key) {
       -                case 'h':
       -                        if (E.cursor_x != 0) {
       -                                E.cursor_x--;
       -                        } else if (E.cursor_y > 0) {
       -                                E.cursor_y--;
       -                                E.cursor_x = E.row[E.cursor_y].size;
       -                        }
       -                        break;
       -                case 'j':
       -                        if (E.cursor_y < E.num_rows - 1)
       -                                E.cursor_y++;
       -                        break;
       -                case 'k':
       -                        if (E.cursor_y != 0)
       -                                E.cursor_y--;
       -                        break;
       -                case 'l':
       -                        row = (E.cursor_y >= E.num_rows) ? NULL : &E.row[E.cursor_y];
       -                        if (row && E.cursor_x < row->size - 1) {
       -                                E.cursor_x++;
       -                        } else if (row && E.cursor_x == row->size &&
       -                                   E.cursor_y < E.num_rows - 1) {
       -                                E.cursor_y++;
       -                                E.cursor_x = 0;
       -                        }
       -                        break;
       -        }
       -
       -        /* do not allow navigation past EOL by vertical navigation */
       -        row = (E.cursor_y >= E.num_rows) ? NULL : &E.row[E.cursor_y];
       -        row_len = row ? row->size : 0;
       -        if (E.cursor_x > row_len)
       -                E.cursor_x = row_len;
       -}
       -
       -void
       -editor_process_keypress()
       -{
       -        char c;
       -        int i;
       -        
       -        c = editor_read_key();
       -
       -        if (E.mode == 0) {  /* normal mode */
       -                switch (c) {
       -                        case 'i':
       -                                E.mode = 1;
       -                                break;
       -                        case 'a':
       -                                editor_move_cursor('l');
       -                                E.mode = 1;
       -                                break;
       -
       -                        case CTRL_KEY('q'):
       -                                if (E.file_changed) {
       -                                        editor_set_status_message("error: "
       -                                                                      "file has unsaved changes. "
       -                                                                      "Press C-x to confirm quit");
       -                                        break;
       -                                } else {
       -                                        write(STDOUT_FILENO, "\x1b[2J", 4); /* clear screen */
       -                                        write(STDOUT_FILENO, "\x1b[H", 3);
       -                                        exit(0);
       -                                        break;
       -                                }
       -                        case CTRL_KEY('x'):
       -                                write(STDOUT_FILENO, "\x1b[2J", 4); /* clear screen */
       -                                write(STDOUT_FILENO, "\x1b[H", 3);
       -                                exit(0);
       -                                break;
       -                        case 'h':
       -                        case 'j':
       -                        case 'k':
       -                        case 'l':
       -                                editor_move_cursor(c);
       -                                break;
       -
       -                        case CTRL_KEY('w'):
       -                                file_save(E.filename);
       -                                break;
       -
       -                        case CTRL_KEY('f'):
       -                                i = E.screen_rows;
       -                                while (i--)
       -                                        editor_move_cursor('j');
       -                                break;
       -                        case CTRL_KEY('b'):
       -                                i = E.screen_rows;
       -                                while (i--)
       -                                        editor_move_cursor('k');
       -                                break;
       -
       -                        case CTRL_KEY('d'):
       -                                i = E.screen_rows/2;
       -                                while (i--)
       -                                        editor_move_cursor('j');
       -                                break;
       -                        case CTRL_KEY('u'):
       -                                i = E.screen_rows/2;
       -                                while (i--)
       -                                        editor_move_cursor('k');
       -                                break;
       -
       -                        case '0':
       -                                E.cursor_x = 0;
       -                                break;
       -                        case '$':
       -                                if (E.cursor_y < E.num_rows)
       -                                        E.cursor_x = E.row[E.cursor_y].size - 1;
       -                                break;
       -
       -                        case 'g':
       -                                E.cursor_x = 0;
       -                                E.cursor_y = 0;
       -                                break;
       -                        case 'G':
       -                                E.cursor_x = 0;
       -                                E.cursor_y = E.num_rows - 1;
       -                                break;
       -
       -                        case 'x':
       -                                editor_delete_char_right();
       -                                break;
       -                        case 'd':
       -                                editor_row_delete(E.cursor_y);
       -                                editor_move_cursor('h');
       -                                break;
       -
       -                        case 'o':
       -                                if (E.cursor_y < E.num_rows)
       -                                        E.cursor_x = E.row[E.cursor_y].size;
       -                                editor_insert_new_line();
       -                                E.mode = 1;
       -                                break;
       -                        case 'O':
       -                                E.cursor_x = 0;
       -                                editor_insert_new_line();
       -                                editor_move_cursor('k');
       -                                E.mode = 1;
       -                                break;
       -
       -                        case 'I':
       -                                E.cursor_x = 0;
       -                                E.mode = 1;
       -                                break;
       -                        case 'A':
       -                                if (E.cursor_y < E.num_rows)
       -                                        E.cursor_x = E.row[E.cursor_y].size;
       -                                E.mode = 1;
       -                                break;
       -
       -                        case '/':
       -                                editor_find(1);
       -                                break;
       -                        case '?':
       -                                editor_find(0);
       -                                break;
       -                        case 'n':
       -                                editor_find_next_occurence(0);
       -                                break;
       -                        case 'N':
       -                                editor_find_next_occurence(1);
       -                                break;
       -                }
       -        } else if (E.mode == 1) {  /* insert mode */
       -                switch (c) {
       -                        case CTRL_KEY('c'):
       -                        case '\x1b':  /* escape */
       -                                E.mode = 0;
       -                                break;
       -                        
       -                        case CTRL_KEY('\r'): /* enter */
       -                                editor_insert_new_line();
       -                                break;
       -
       -                        case 127:  /* backspace */
       -                        case CTRL_KEY('h'):
       -                                editor_delete_char_left();
       -                                break;
       -
       -                        case CTRL_KEY('l'):
       -                                break;
       -
       -                        default:
       -                                editor_insert_char(c);
       -                                break;
       -                }
       -        }
       -}
 (DIR) diff --git a/src/input.h b/src/input.h
       t@@ -1,7 +0,0 @@
       -#ifndef INPUT_H_
       -#define INPUT_H_
       -
       -char* editor_prompt(char *prompt);
       -void editor_process_keypress();
       -
       -#endif
 (DIR) diff --git a/src/io.c b/src/io.c
       t@@ -1,103 +0,0 @@
       -/* add feature test macro for getline compatibility */
       -#define _DEFAULT_SOURCE
       -#define _BSD_SOURCE
       -#define _GNU_SOURCE
       -
       -#include <stdlib.h>
       -#include <stdio.h>
       -#include <sys/types.h>
       -#include <string.h>
       -#include <fcntl.h>
       -#include <unistd.h>
       -#include <errno.h>
       -#include "ve.h"
       -#include "terminal.h"
       -#include "row.h"
       -#include "output.h"
       -#include "input.h"
       -
       -void
       -file_open(char *filename)
       -{
       -        free(E.filename);
       -        E.filename = strdup(filename);
       -
       -        FILE *fp;
       -        char *line;
       -        size_t linecap;
       -        ssize_t linelen;
       -        
       -        fp = fopen(filename, "r");
       -        if (!fp)
       -                die("fopen in file_open");
       -
       -        line = NULL;
       -        linecap = 0;
       -        while ((linelen = getline(&line, &linecap, fp)) != -1) {
       -                while (linelen > 0 && (line[linelen - 1] == '\n' ||
       -                                       line[linelen - 1] == '\r'))
       -                        linelen--;
       -                editor_row_insert(E.num_rows, line, linelen);
       -        }
       -        free(line);
       -        fclose(fp);
       -        E.file_changed = 0;
       -}
       -
       -/* convert rows to one long char array of length buflen */
       -char*
       -editor_concatenate_rows(int *buflen)
       -{
       -        int totlen, j;
       -        char *buf, *p;
       -
       -        totlen = 0;
       -        for (j=0; j<E.num_rows; ++j)
       -                totlen += E.row[j].size + 1;  /* add space for newline char */
       -        *buflen = totlen;
       -
       -        buf = malloc(totlen);
       -        p = buf;
       -        for (j=0; j<E.num_rows; ++j) {
       -                memcpy(p, E.row[j].chars, E.row[j].size);
       -                p += E.row[j].size;
       -                *p = '\n';
       -                p++;
       -        }
       -        return buf;
       -}
       -
       -void
       -file_save(char *filename)
       -{
       -        int len, fd;
       -        char *buf;
       -
       -        if (filename == NULL) {
       -                filename = editor_prompt("save as: %s");
       -                if (filename == NULL) {
       -                        editor_set_status_message("save aborted");
       -                        return;
       -                }
       -        }
       -
       -        buf = editor_concatenate_rows(&len);
       -
       -        fd = open(filename, O_RDWR | O_CREAT, 0644);
       -        if (fd != -1) {
       -                if (ftruncate(fd, len) != -1) {
       -                        if (write(fd, buf, len) == len) {
       -                                close(fd);
       -                                free(buf);
       -                                E.filename = filename;
       -                                E.file_changed = 0;
       -                                editor_set_status_message("%d bytes written to disk", len);
       -                                return;
       -                        }
       -                }
       -                close(fd);
       -        }
       -        free(buf);
       -        editor_set_status_message("error: can't save! I/O error %s",
       -                                  strerror(errno));
       -}
 (DIR) diff --git a/src/io.h b/src/io.h
       t@@ -1,7 +0,0 @@
       -#ifndef IO_H_
       -#define IO_H_
       -
       -void file_open(char *filename);
       -void file_save(char *filename);
       -
       -#endif
 (DIR) diff --git a/src/main.c b/src/main.c
       t@@ -1,27 +0,0 @@
       -#include <stdlib.h>
       -#include "terminal.h"
       -#include "output.h"
       -#include "input.h"
       -#include "io.h"
       -#include "output.h"
       -#include "ve.h"
       -
       -int
       -main(int argc, char* argv[])
       -{
       -        enable_raw_mode();
       -        init_editor();
       -
       -        /* TODO: proper argument handling */
       -        if (argc >= 2) {
       -                file_open(argv[1]);
       -        }
       -
       -        editor_set_status_message("%s v%s", PROGNAME, VERSION);
       -
       -        while (1) {
       -                editor_refresh_screen();
       -                editor_process_keypress();
       -        }
       -        return 0;
       -}
 (DIR) diff --git a/src/output.c b/src/output.c
       t@@ -1,215 +0,0 @@
       -#include <unistd.h>
       -#include <string.h>
       -#include <stdlib.h>
       -#include <stdio.h>
       -#include <stdarg.h>
       -#include <time.h>
       -#include "terminal.h"
       -#include "output.h"
       -#include "row.h"
       -#include "ve.h"
       -
       -/* reallocate append buffer to hold string s */
       -void
       -ab_append(struct abuf *ab, const char *s, int len)
       -{
       -        char *new;
       -        new = realloc(ab->b, ab->len + len);
       -
       -        if (new == NULL)
       -                return;
       -        memcpy(&new[ab->len], s, len);
       -        ab->b = new;
       -        ab->len += len;
       -}
       -
       -void
       -ab_free(struct abuf *ab) {
       -        free(ab->b);
       -}
       -
       -int
       -show_status_message()
       -{
       -        if (E.show_status &&
       -            strlen(E.status_msg) &&
       -            time(NULL) - E.status_msg_time < STATUS_MESSAGE_TIMEOUT) {
       -                return 1;
       -        } else {
       -                if (E.show_status) {
       -                        E.show_status = 0;
       -                        E.screen_rows++;
       -                }
       -                return 0;
       -        }
       -}
       -
       -/* draw status line consisting of left and right components.
       - * if the screen is narrower than both parts, just show the left status, 
       - * truncated, if necessary. */
       -void
       -editor_draw_status(struct abuf *ab)
       -{
       -        char left_status[512], right_status[512];
       -        int left_status_len, right_status_len, padding;
       -        int percentage;
       -
       -        left_status_len = 0;
       -        switch (E.mode) {
       -                case 1:
       -                        left_status_len = snprintf(left_status, sizeof(left_status),
       -                                                   "INSERT ");
       -                        break;
       -                case 2:
       -                        left_status_len = snprintf(left_status, sizeof(left_status),
       -                                                   "VISUAL ");
       -                        break;
       -        }
       -        left_status_len += snprintf(left_status + left_status_len,
       -                                    sizeof(left_status),
       -                                    "%.20s %s",
       -                                    E.filename ? E.filename : "[unnamed]",
       -                                    E.file_changed ? "[+] " : "");
       -
       -        percentage = (int)((float)(E.cursor_y)/(E.num_rows-1)*100);
       -        if (percentage < 0) percentage = 0;
       -        right_status_len = snprintf(right_status, sizeof(right_status),
       -                                    "%d%% < %d, %d",
       -                                    percentage,
       -                                    E.cursor_x+1, E.cursor_y+1);
       -
       -        if (left_status_len > E.screen_columns)
       -                left_status_len = E.screen_columns;
       -
       -        padding = E.screen_columns - left_status_len - right_status_len;
       -        if (padding < 0) {
       -                if (left_status_len < E.screen_columns)
       -                        ab_append(ab, left_status, left_status_len);
       -                else
       -                        ab_append(ab, left_status, E.screen_columns);
       -        } else {
       -                ab_append(ab, left_status, left_status_len);
       -                while (padding--)
       -                        ab_append(ab, " ", 1);
       -                ab_append(ab, right_status, right_status_len);
       -        }
       -
       -        if (show_status_message()) {
       -                ab_append(ab, "\x1b[m", 3);
       -                ab_append(ab, "\r\n", 2);
       -        }
       -}
       -
       -/* draw status message if as long as it is specified and it is not too old */
       -void
       -editor_draw_status_message(struct abuf *ab)
       -{
       -        int msglen;
       -        ab_append(ab, "\x1b[K", 3);
       -        msglen = strlen(E.status_msg);
       -        if (msglen > E.screen_columns)
       -                msglen = E.screen_columns;
       -        if (show_status_message())
       -                ab_append(ab, E.status_msg, msglen);
       -}
       -
       -/* set vertical offset between file and screen when hitting the boundaries */
       -void
       -editor_scroll()
       -{
       -        E.cursor_rx = 0;
       -        if (E.cursor_y < E.num_rows)
       -                E.cursor_rx = editor_row_cursor_x_to_rx(&E.row[E.cursor_y],
       -                                                        E.cursor_x);
       -
       -        if (E.cursor_y < E.row_offset)
       -                E.row_offset = E.cursor_y;
       -        else if (E.cursor_y >= E.row_offset + E.screen_rows)
       -                E.row_offset = E.cursor_y - E.screen_rows + 1;
       -
       -        if (E.cursor_rx < E.column_offset)
       -                E.column_offset = E.cursor_rx;
       -        else if (E.cursor_rx >= E.column_offset + E.screen_columns)
       -                E.column_offset = E.cursor_rx - E.screen_columns + 1;
       -}
       -
       -/* draw editor screen.
       - * show tilde characters after EOF, and show status on last line */
       -void
       -editor_draw_rows(struct abuf *ab)
       -{
       -        int y, len, file_row;
       -        for (y = 0; y < E.screen_rows; ++y) {
       -                file_row = y + E.row_offset;
       -                if (file_row < E.num_rows) {
       -                        len = E.row[file_row].rsize - E.column_offset;
       -                        if (len < 0)
       -                                len = 0;
       -                        if (len > E.screen_columns)
       -                                len = E.screen_columns;
       -                        ab_append(ab, &E.row[file_row].rchars[E.column_offset], len);
       -                } else {
       -                        ab_append(ab, "~", 1);
       -                }
       -
       -                ab_append(ab, "\x1b[K", 3); /* erase to end of line */
       -                ab_append(ab, "\r\n", 2);
       -        }
       -}
       -
       -/* fill output append buffer and write to terminal.
       - * move cursor to left before repaint, and to cursor position after.
       - * hide the cursor when repainting.
       - * VT100 escape sequences:
       - * - http://vt100.net/docs/vt100-ug/chapter3.html
       - * - http://vt100.net/docs/vt100-ug/chapter3.html#CUP */
       -void
       -editor_refresh_screen()
       -{
       -        char buf[32];
       -        struct abuf ab = ABUF_INIT;
       -
       -        show_status_message();
       -        editor_scroll();
       -
       -        ab_append(&ab, "\x1b[?25l", 6); /* hide cursor */
       -        ab_append(&ab, "\x1b[H", 3);    /* cursor to home */
       -        
       -        editor_draw_rows(&ab);
       -        editor_draw_status(&ab);
       -        editor_draw_status_message(&ab);
       -
       -        snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
       -                (E.cursor_y - E.row_offset)+1,
       -                (E.cursor_rx - E.column_offset)+1);
       -        ab_append(&ab, buf, strlen(buf));
       -
       -        ab_append(&ab, "\x1b[?25h", 6); /* show cursor */
       -
       -        write(STDOUT_FILENO, ab.b, ab.len);
       -        ab_free(&ab);
       -}
       -
       -void
       -editor_place_cursor(int cx, int cy)
       -{
       -        char buf[128];
       -        snprintf(buf, sizeof(buf), "\x1b[%d;%dH", cy, cx);
       -        write(STDOUT_FILENO, buf, strlen(buf));
       -}
       -
       -/* set status message text, uses same format as printf */
       -void
       -editor_set_status_message(const char *fmt, ...)
       -{
       -        va_list ap;
       -        va_start(ap, fmt);
       -        vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap);
       -        va_end(ap);
       -        E.status_msg_time = time(NULL);
       -
       -        if (!E.show_status) {
       -                E.screen_rows--;
       -                E.show_status = 1;
       -        }
       -}
 (DIR) diff --git a/src/output.h b/src/output.h
       t@@ -1,14 +0,0 @@
       -#ifndef OUTPUT_H_
       -#define OUTPUT_H_
       -
       -struct abuf {
       -        char *b;
       -        int len;
       -};
       -#define ABUF_INIT {NULL, 0}
       -
       -void editor_refresh_screen();
       -void editor_set_status_message(const char *fmt, ...);
       -void editor_place_cursor(int cx, int cy);
       -
       -#endif
 (DIR) diff --git a/src/row.c b/src/row.c
       t@@ -1,143 +0,0 @@
       -#include <stdlib.h>
       -#include <string.h>
       -#include "ve.h"
       -
       -/* navigate over tab-representative string as one character */
       -int
       -editor_row_cursor_x_to_rx(eRow *row, int cursor_x)
       -{
       -        int rx, j;
       -        rx = 0;
       -        for (j=0; j<cursor_x; ++j) {
       -                if (row->chars[j] == '\t')
       -                        rx += (TAB_WIDTH - 1) - (rx % TAB_WIDTH);
       -                rx++;
       -        }
       -        return rx;
       -}
       -
       -/* translate on-screen position to data position */
       -int
       -editor_row_cursor_rx_to_x(eRow *row, int cursor_rx)
       -{
       -        int cur_rx, cx;
       -        cur_rx = 0;
       -        for (cx=0; cx<row->size; ++cx) {
       -                if (row->chars[cx] == '\t')
       -                        cur_rx += (TAB_WIDTH - 1) - (cur_rx % TAB_WIDTH);
       -                cur_rx++;
       -
       -                if (cur_rx > cursor_rx)
       -                        return cx;
       -        }
       -        return cx;
       -}
       -
       -/* translate tabs before display */
       -void
       -editor_row_update(eRow* row)
       -{
       -        int j, idx, tabs;
       -
       -        free(row->rchars);
       -        row->rchars = malloc(row->size + 1);
       -
       -        tabs = 0;
       -        for (j=0; j<row->size; ++j)
       -                if (row->chars[j] == '\t')
       -                        tabs++;
       -
       -        free(row->rchars);
       -        row->rchars = malloc(row->size + tabs*(TAB_WIDTH - 1) + 1);
       -
       -        idx = 0;
       -        for (j=0; j<row->size; ++j) {
       -                if (row->chars[j] == '\t') {
       -                        row->rchars[idx++] = '>';
       -                        while (idx % TAB_WIDTH != 0)
       -                                row->rchars[idx++] = ' ';
       -                } else {
       -                        row->rchars[idx++] = row->chars[j];
       -                }
       -        }
       -        row->rchars[idx] = '\0';
       -        row->rsize = idx;
       -}
       -
       -/* add row to buffer */
       -void
       -editor_row_insert(int i, char *s, size_t len)
       -{
       -        if (i<0 || i>E.num_rows)
       -                return;
       -
       -        E.row = realloc(E.row, sizeof(eRow) * (E.num_rows + 1));
       -        memmove(&E.row[i+1], &E.row[i], sizeof(eRow) * (E.num_rows - i));
       -
       -        E.row[i].size = len;
       -        E.row[i].chars = malloc(len + 1);
       -        memcpy(E.row[i].chars, s, len);
       -        E.row[i].chars[len] = '\0';
       -
       -        E.row[i].rsize = 0;
       -        E.row[i].rchars = NULL;
       -        editor_row_update(&E.row[i]);
       -
       -        ++E.num_rows;
       -        E.file_changed = 1;
       -}
       -
       -/* insert character to row, making sure to allocate memory accordingly */
       -void
       -editor_row_insert_char(eRow *row, int i, int c)
       -{
       -        if (i<0 || i>row->size)
       -                i = row->size;
       -        row->chars = realloc(row->chars, row->size + 2);
       -        memmove(&row->chars[i+1], &row->chars[i], row->size - i+1);
       -        row->size++;
       -        row->chars[i] = c;
       -        editor_row_update(row);
       -        E.file_changed = 1;
       -}
       -
       -/* append a string to the end of a row */
       -void
       -editor_row_append_string(eRow *row, char *s, size_t len)
       -{
       -        row->chars = realloc(row->chars, row->size + len + 1);
       -        memcpy(&row->chars[row->size], s, len);
       -        row->size += len;
       -        row->chars[row->size] = '\0';
       -        editor_row_update(row);
       -        E.file_changed = 1;
       -}
       -
       -void
       -editor_row_delete_char(eRow *row, int i)
       -{
       -        if (i<0 || i>=row->size)
       -                return;
       -        memmove(&row->chars[i], &row->chars[i+1], row->size - i);
       -        row->size--;
       -        editor_row_update(row);
       -        E.file_changed = 1;
       -}
       -
       -void
       -editor_row_free(eRow *row)
       -{
       -        free(row->rchars);
       -        free(row->chars);
       -}
       -
       -void
       -editor_row_delete(int i)
       -{
       -        if (i<0 || i>=E.num_rows)
       -                return;
       -        editor_row_free(&E.row[i]);
       -        memmove(&E.row[i], &E.row[i+1], sizeof(eRow)*(E.num_rows - i - 1));
       -        E.num_rows--;
       -        E.file_changed = 1;
       -}
 (DIR) diff --git a/src/row.h b/src/row.h
       t@@ -1,16 +0,0 @@
       -#ifndef ROW_H_
       -#define ROW_H_
       -
       -#include "ve.h"
       -
       -int editor_row_cursor_x_to_rx(eRow *row, int cursor_x);
       -int editor_row_cursor_rx_to_x(eRow *row, int cursor_rx);
       -void editor_row_update(eRow* row);
       -void editor_row_append_string(eRow *row, char *s, size_t len);
       -void editor_row_insert(int i, char *s, size_t len);
       -void editor_row_insert_char(eRow *row, int i, int c);
       -void editor_row_delete_char(eRow *row, int i);
       -void editor_row_delete(int i);
       -void editor_row_free(eRow *row);
       -
       -#endif
 (DIR) diff --git a/src/terminal.c b/src/terminal.c
       t@@ -1,107 +0,0 @@
       -#include <ctype.h>
       -#include <errno.h>
       -#include <stdio.h>
       -#include <stdlib.h>
       -#include <termios.h>
       -#include <unistd.h>
       -#include <sys/ioctl.h>
       -#include "ve.h"
       -
       -struct editor_config E;
       -
       -void
       -die(const char *s)
       -{
       -        /* clear screen on exit */
       -        write(STDOUT_FILENO, "\x1b[2J", 4);
       -        write(STDOUT_FILENO, "\x1b[H", 3);
       -        perror(s);
       -        exit(1);
       -}
       -
       -void
       -disable_raw_mode()
       -{
       -        if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
       -                die("tcsetattr in disable_raw_mode()");
       -}
       -
       -void
       -enable_raw_mode()
       -{
       -        struct termios raw;
       -
       -        if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1)
       -                die("tcgetattr in enable_raw_mode()");
       -        atexit(disable_raw_mode);
       -
       -        /* fix modifier keys, set 8 bits per char, ignore interrupts */
       -        raw = E.orig_termios;
       -        raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
       -        raw.c_oflag &= ~(OPOST);
       -        raw.c_cflag |= (CS8);
       -        raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
       -
       -        /* set read() timeout in tenths of seconds */
       -        raw.c_cc[VMIN] = 0;
       -        raw.c_cc[VTIME] = 1;
       -
       -        /* apply terminal settings */
       -        if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
       -                die("tcsetattr in enable_raw_mode()");
       -}
       -
       -char
       -editor_read_key()
       -{
       -        int nread;
       -        char c;
       -        while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
       -                if (nread == -1 && errno != EAGAIN)
       -                        die("read in editor_read_key()");
       -        }
       -        return c;
       -}
       -
       -/* get screen size by moving cursor and get current position */
       -int
       -get_cursor_position(int *rows, int *cols) {
       -        char buf[32];
       -        unsigned int i;
       -
       -        if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4)
       -                return -1;
       -
       -        i = 0;
       -        while (i < sizeof(buf) - 1) {
       -                if (read(STDIN_FILENO, &buf[i], 1) != 1)
       -                        break;
       -                if (buf[i] == 'R')
       -                        break;
       -                ++i;
       -        }
       -        buf[i] = '\0';
       -
       -        if (buf[0] != '\x1b' || buf[1] != '[')
       -                return -1;
       -        if (scanf(&buf[2], "%d;%d", rows, cols) != 2)
       -                return -1;
       -        return 0;
       -}
       -
       -int
       -get_window_size(int *rows, int *cols)
       -{
       -        struct winsize ws;
       -
       -        if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
       -                /* fallback screen size detection */
       -                if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12)
       -                        return -1;
       -                return get_cursor_position(rows, cols);
       -        } else {
       -                *cols = ws.ws_col;
       -                *rows = ws.ws_row;
       -                return 0;
       -        }
       -}
 (DIR) diff --git a/src/terminal.h b/src/terminal.h
       t@@ -1,11 +0,0 @@
       -#ifndef TERMINAL_H_
       -#define TERMINAL_H_
       -
       -void die(const char *s);
       -void disable_raw_mode();
       -void enable_raw_mode();
       -char editor_read_key();
       -int get_window_size(int *rows, int *cols);
       -void init_editor();
       -
       -#endif
 (DIR) diff --git a/src/ve.c b/src/ve.c
       t@@ -1,39 +0,0 @@
       -#include <stdlib.h>
       -#include "terminal.h"
       -#include "row.h"
       -#include "ve.h"
       -
       -struct editor_config E;
       -
       -void
       -deinit_editor() {
       -        int i;
       -        free(E.filename);
       -        free(E.find_query);
       -        for (i=0; i<E.num_rows; ++i)
       -                editor_row_free(&E.row[i]);
       -}
       -
       -/* set editor state variables, make room for status */
       -void
       -init_editor() {
       -        E.cursor_x = 0;
       -        E.cursor_y = 0;
       -        E.cursor_rx = 0;
       -        E.mode = 0;
       -        E.num_rows = 0;
       -        atexit(deinit_editor);
       -        E.row = NULL;
       -        E.row_offset = 0;
       -        E.column_offset = 0;
       -        E.filename = NULL;
       -        E.status_msg[0] = '\0';
       -        E.status_msg_time = 0;
       -        E.show_status = 0;
       -        E.file_changed = 0;
       -        E.find_query = NULL;
       -
       -        if (get_window_size(&E.screen_rows, &E.screen_columns) == -1)
       -                die("get_window_size");
       -        E.screen_rows -= 1;
       -}
 (DIR) diff --git a/src/ve.h b/src/ve.h
       t@@ -1,39 +0,0 @@
       -#ifndef BYOTE_H_
       -#define BYOTE_H_
       -
       -#include <termios.h>
       -#include <time.h>
       -
       -#define PROGNAME "ve"
       -#define VERSION "0.0.1"
       -
       -#define TAB_WIDTH 4
       -#define STATUS_MESSAGE_TIMEOUT 3
       -
       -/* editor row: stores a row of text */
       -typedef struct eRow {
       -        char *chars;   /* text read from file */
       -        char *rchars;  /* text to render */
       -        int size;      /* length of chars */
       -        int rsize;     /* length of rchars */
       -} eRow;
       -
       -struct editor_config {
       -        int cursor_x, cursor_y, cursor_rx;
       -        int screen_rows, screen_columns;
       -        int num_rows;
       -        eRow *row;
       -        int row_offset, column_offset;
       -        char *filename;
       -        struct termios orig_termios;
       -        int mode; /* 0: normal, 1: insert, 2: visual */
       -        int show_status;
       -        char status_msg[80];
       -        time_t status_msg_time;
       -        int file_changed;
       -        char *find_query;
       -        int find_direction;
       -};
       -
       -extern struct editor_config E;
       -#endif
 (DIR) diff --git a/ve.c b/ve.c
       t@@ -0,0 +1,1062 @@
       +/* see LICENSE for license details */
       +
       +/* add feature test macro for getline and strdup compatibility */
       +#define _DEFAULT_SOURCE
       +#define _BSD_SOURCE
       +#define _GNU_SOURCE
       +
       +#include <ctype.h>
       +#include <errno.h>
       +#include <fcntl.h>
       +#include <stdarg.h>
       +#include <stdlib.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <sys/types.h>
       +#include <sys/ioctl.h>
       +#include <termios.h>
       +#include <time.h>
       +#include <unistd.h>
       +
       +
       +/* macros */
       +#define PROGNAME "ve"
       +#define VERSION "0.0.1"
       +#define TAB_WIDTH 4
       +#define STATUS_MESSAGE_TIMEOUT 3
       +#define ABUF_INIT {NULL, 0}
       +#define CTRL_KEY(k) ((k) & 0x1f)
       +
       +
       +
       +/* types */
       +
       +struct abuf {
       +        char *b;
       +        int len;
       +};
       +
       +/* editor row: stores a row of text */
       +typedef struct eRow {
       +        char *chars;   /* text read from file */
       +        char *rchars;  /* text to render */
       +        int size;      /* length of chars */
       +        int rsize;     /* length of rchars */
       +} eRow;
       +
       +struct editor_config {
       +        int cursor_x, cursor_y, cursor_rx;
       +        int screen_rows, screen_columns;
       +        int num_rows;
       +        eRow *row;
       +        int row_offset, column_offset;
       +        char *filename;
       +        struct termios orig_termios;
       +        int mode; /* 0: normal, 1: insert, 2: visual */
       +        int show_status;
       +        char status_msg[80];
       +        time_t status_msg_time;
       +        int file_changed;
       +        char *find_query;
       +        int find_direction;
       +};
       +
       +
       +/* function declarations */
       +char* editor_prompt(char *prompt);
       +void editor_set_status_message(const char *fmt, ...);
       +
       +
       +/* global variables */
       +struct editor_config E;
       +
       +
       +/* function definitions */
       +
       +void
       +die(const char *s)
       +{
       +        /* clear screen on exit */
       +        write(STDOUT_FILENO, "\x1b[2J", 4);
       +        write(STDOUT_FILENO, "\x1b[H", 3);
       +        perror(s);
       +        exit(1);
       +}
       +
       +void
       +disable_raw_mode()
       +{
       +        if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
       +                die("tcsetattr in disable_raw_mode()");
       +}
       +
       +void
       +enable_raw_mode()
       +{
       +        struct termios raw;
       +
       +        if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1)
       +                die("tcgetattr in enable_raw_mode()");
       +        atexit(disable_raw_mode);
       +
       +        /* fix modifier keys, set 8 bits per char, ignore interrupts */
       +        raw = E.orig_termios;
       +        raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
       +        raw.c_oflag &= ~(OPOST);
       +        raw.c_cflag |= (CS8);
       +        raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
       +
       +        /* set read() timeout in tenths of seconds */
       +        raw.c_cc[VMIN] = 0;
       +        raw.c_cc[VTIME] = 1;
       +
       +        /* apply terminal settings */
       +        if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
       +                die("tcsetattr in enable_raw_mode()");
       +}
       +
       +char
       +editor_read_key()
       +{
       +        int nread;
       +        char c;
       +        while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
       +                if (nread == -1 && errno != EAGAIN)
       +                        die("read in editor_read_key()");
       +        }
       +        return c;
       +}
       +
       +/* get screen size by moving cursor and get current position */
       +int
       +get_cursor_position(int *rows, int *cols) {
       +        char buf[32];
       +        unsigned int i;
       +
       +        if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4)
       +                return -1;
       +
       +        i = 0;
       +        while (i < sizeof(buf) - 1) {
       +                if (read(STDIN_FILENO, &buf[i], 1) != 1)
       +                        break;
       +                if (buf[i] == 'R')
       +                        break;
       +                ++i;
       +        }
       +        buf[i] = '\0';
       +
       +        if (buf[0] != '\x1b' || buf[1] != '[')
       +                return -1;
       +        if (scanf(&buf[2], "%d;%d", rows, cols) != 2)
       +                return -1;
       +        return 0;
       +}
       +
       +int
       +get_window_size(int *rows, int *cols)
       +{
       +        struct winsize ws;
       +
       +        if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
       +                /* fallback screen size detection */
       +                if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12)
       +                        return -1;
       +                return get_cursor_position(rows, cols);
       +        } else {
       +                *cols = ws.ws_col;
       +                *rows = ws.ws_row;
       +                return 0;
       +        }
       +}
       +
       +
       +/* navigate over tab-representative string as one character */
       +int
       +editor_row_cursor_x_to_rx(eRow *row, int cursor_x)
       +{
       +        int rx, j;
       +        rx = 0;
       +        for (j=0; j<cursor_x; ++j) {
       +                if (row->chars[j] == '\t')
       +                        rx += (TAB_WIDTH - 1) - (rx % TAB_WIDTH);
       +                rx++;
       +        }
       +        return rx;
       +}
       +
       +/* translate on-screen position to data position */
       +int
       +editor_row_cursor_rx_to_x(eRow *row, int cursor_rx)
       +{
       +        int cur_rx, cx;
       +        cur_rx = 0;
       +        for (cx=0; cx<row->size; ++cx) {
       +                if (row->chars[cx] == '\t')
       +                        cur_rx += (TAB_WIDTH - 1) - (cur_rx % TAB_WIDTH);
       +                cur_rx++;
       +
       +                if (cur_rx > cursor_rx)
       +                        return cx;
       +        }
       +        return cx;
       +}
       +
       +/* translate tabs before display */
       +void
       +editor_row_update(eRow* row)
       +{
       +        int j, idx, tabs;
       +
       +        free(row->rchars);
       +        row->rchars = malloc(row->size + 1);
       +
       +        tabs = 0;
       +        for (j=0; j<row->size; ++j)
       +                if (row->chars[j] == '\t')
       +                        tabs++;
       +
       +        free(row->rchars);
       +        row->rchars = malloc(row->size + tabs*(TAB_WIDTH - 1) + 1);
       +
       +        idx = 0;
       +        for (j=0; j<row->size; ++j) {
       +                if (row->chars[j] == '\t') {
       +                        row->rchars[idx++] = '>';
       +                        while (idx % TAB_WIDTH != 0)
       +                                row->rchars[idx++] = ' ';
       +                } else {
       +                        row->rchars[idx++] = row->chars[j];
       +                }
       +        }
       +        row->rchars[idx] = '\0';
       +        row->rsize = idx;
       +}
       +
       +/* add row to buffer */
       +void
       +editor_row_insert(int i, char *s, size_t len)
       +{
       +        if (i<0 || i>E.num_rows)
       +                return;
       +
       +        E.row = realloc(E.row, sizeof(eRow) * (E.num_rows + 1));
       +        memmove(&E.row[i+1], &E.row[i], sizeof(eRow) * (E.num_rows - i));
       +
       +        E.row[i].size = len;
       +        E.row[i].chars = malloc(len + 1);
       +        memcpy(E.row[i].chars, s, len);
       +        E.row[i].chars[len] = '\0';
       +
       +        E.row[i].rsize = 0;
       +        E.row[i].rchars = NULL;
       +        editor_row_update(&E.row[i]);
       +
       +        ++E.num_rows;
       +        E.file_changed = 1;
       +}
       +
       +/* insert character to row, making sure to allocate memory accordingly */
       +void
       +editor_row_insert_char(eRow *row, int i, int c)
       +{
       +        if (i<0 || i>row->size)
       +                i = row->size;
       +        row->chars = realloc(row->chars, row->size + 2);
       +        memmove(&row->chars[i+1], &row->chars[i], row->size - i+1);
       +        row->size++;
       +        row->chars[i] = c;
       +        editor_row_update(row);
       +        E.file_changed = 1;
       +}
       +
       +/* append a string to the end of a row */
       +void
       +editor_row_append_string(eRow *row, char *s, size_t len)
       +{
       +        row->chars = realloc(row->chars, row->size + len + 1);
       +        memcpy(&row->chars[row->size], s, len);
       +        row->size += len;
       +        row->chars[row->size] = '\0';
       +        editor_row_update(row);
       +        E.file_changed = 1;
       +}
       +
       +void
       +editor_row_delete_char(eRow *row, int i)
       +{
       +        if (i<0 || i>=row->size)
       +                return;
       +        memmove(&row->chars[i], &row->chars[i+1], row->size - i);
       +        row->size--;
       +        editor_row_update(row);
       +        E.file_changed = 1;
       +}
       +
       +void
       +editor_row_free(eRow *row)
       +{
       +        free(row->rchars);
       +        free(row->chars);
       +}
       +
       +void
       +editor_row_delete(int i)
       +{
       +        if (i<0 || i>=E.num_rows)
       +                return;
       +        editor_row_free(&E.row[i]);
       +        memmove(&E.row[i], &E.row[i+1], sizeof(eRow)*(E.num_rows - i - 1));
       +        E.num_rows--;
       +        E.file_changed = 1;
       +}
       +
       +void
       +editor_insert_char(int c)
       +{
       +        if (E.cursor_y == E.num_rows)
       +                editor_row_insert(E.num_rows, "", 0);
       +        editor_row_insert_char(&E.row[E.cursor_y], E.cursor_x, c);
       +        E.cursor_x++;
       +}
       +
       +void
       +editor_insert_new_line()
       +{
       +        eRow *row;
       +        if (E.cursor_x == 0) {
       +                editor_row_insert(E.cursor_y, "", 0);
       +        } else {
       +                row = &E.row[E.cursor_y];
       +                editor_row_insert(E.cursor_y + 1, &row->chars[E.cursor_x],
       +                                  row->size - E.cursor_x);
       +                row = &E.row[E.cursor_y];
       +                row->size = E.cursor_x;
       +                row->chars[row->size] = '\0';
       +                editor_row_update(row);
       +        }
       +        E.cursor_y++;
       +        E.cursor_x = 0;
       +}
       +
       +/* delete a character before the cursor, and allow for deletion across rows */
       +void
       +editor_delete_char_left()
       +{
       +        eRow *row;
       +        if (E.cursor_y == E.num_rows || (E.cursor_x == 0 && E.cursor_y == 0))
       +                return;
       +        
       +        row = &E.row[E.cursor_y];
       +        if (E.cursor_x > 0) {
       +                editor_row_delete_char(row, E.cursor_x - 1);
       +                E.cursor_x--;
       +        } else {
       +                E.cursor_x = E.row[E.cursor_y - 1].size;
       +                editor_row_append_string(&E.row[E.cursor_y - 1],
       +                                         row->chars, row->size);
       +                editor_row_delete(E.cursor_y);
       +                E.cursor_y--;
       +        }
       +}
       +
       +void
       +editor_delete_char_right()
       +{
       +        if (E.cursor_y == E.num_rows)
       +                return;
       +        eRow *row = &E.row[E.cursor_y];
       +        editor_row_delete_char(row, E.cursor_x);
       +}
       +
       +void
       +file_open(char *filename)
       +{
       +        free(E.filename);
       +        E.filename = strdup(filename);
       +
       +        FILE *fp;
       +        char *line;
       +        size_t linecap;
       +        ssize_t linelen;
       +        
       +        fp = fopen(filename, "r");
       +        if (!fp)
       +                die("fopen in file_open");
       +
       +        line = NULL;
       +        linecap = 0;
       +        while ((linelen = getline(&line, &linecap, fp)) != -1) {
       +                while (linelen > 0 && (line[linelen - 1] == '\n' ||
       +                                       line[linelen - 1] == '\r'))
       +                        linelen--;
       +                editor_row_insert(E.num_rows, line, linelen);
       +        }
       +        free(line);
       +        fclose(fp);
       +        E.file_changed = 0;
       +}
       +
       +/* convert rows to one long char array of length buflen */
       +char*
       +editor_concatenate_rows(int *buflen)
       +{
       +        int totlen, j;
       +        char *buf, *p;
       +
       +        totlen = 0;
       +        for (j=0; j<E.num_rows; ++j)
       +                totlen += E.row[j].size + 1;  /* add space for newline char */
       +        *buflen = totlen;
       +
       +        buf = malloc(totlen);
       +        p = buf;
       +        for (j=0; j<E.num_rows; ++j) {
       +                memcpy(p, E.row[j].chars, E.row[j].size);
       +                p += E.row[j].size;
       +                *p = '\n';
       +                p++;
       +        }
       +        return buf;
       +}
       +
       +void
       +file_save(char *filename)
       +{
       +        int len, fd;
       +        char *buf;
       +
       +        if (filename == NULL) {
       +                filename = editor_prompt("save as: %s");
       +                if (filename == NULL) {
       +                        editor_set_status_message("save aborted");
       +                        return;
       +                }
       +        }
       +
       +        buf = editor_concatenate_rows(&len);
       +
       +        fd = open(filename, O_RDWR | O_CREAT, 0644);
       +        if (fd != -1) {
       +                if (ftruncate(fd, len) != -1) {
       +                        if (write(fd, buf, len) == len) {
       +                                close(fd);
       +                                free(buf);
       +                                E.filename = filename;
       +                                E.file_changed = 0;
       +                                editor_set_status_message("%d bytes written to disk", len);
       +                                return;
       +                        }
       +                }
       +                close(fd);
       +        }
       +        free(buf);
       +        editor_set_status_message("error: can't save! I/O error %s",
       +                                  strerror(errno));
       +}
       +
       +/* reverse of strstr (3) */
       +char*
       +strrstr(char *haystack, char *needle,
       +        size_t haystack_length, size_t needle_length)
       +{
       +  char *cp;
       +  for (cp = haystack + haystack_length - needle_length;
       +       cp >= haystack;
       +       cp--) {
       +    if (strncmp(cp, needle, needle_length) == 0)
       +        return cp;
       +  }
       +  return NULL;
       +} 
       +
       +/* find E.find_query from current cursor position moving in E.direction 
       + * if opposite_direction = 0, and opposite E.direction if
       + * opposite_direction = 1 */
       +void
       +editor_find_next_occurence(int opposite_direction)
       +{
       +        int y, y_inc, x_offset, query_len;
       +        eRow *row;
       +        char *match;
       +
       +        if (!E.find_query)
       +                return;
       +
       +        if ((E.find_direction && !opposite_direction) ||
       +                (!E.find_direction && opposite_direction))
       +                y_inc = +1;
       +        else
       +                y_inc = -1;
       +
       +        x_offset = 0;
       +        query_len = strlen(E.find_query);
       +        /* from cursor until end of document */
       +        for (y = E.cursor_y;
       +             y != ((y_inc == +1) ? E.num_rows : -1);
       +             y += y_inc) {
       +                row = &E.row[y];
       +
       +                if (y == E.cursor_y)
       +                        x_offset = y_inc + E.cursor_x;
       +                else
       +                        x_offset = 0;
       +
       +                if (y_inc == +1) {
       +                        match = strstr(row->chars + x_offset, E.find_query);
       +                        if (match)
       +                                break;
       +                } else {
       +                        match = strrstr(row->chars, E.find_query,
       +                                        (y == E.cursor_y) ? E.cursor_x : row->size,
       +                                        query_len);
       +                        if (match)
       +                                break;
       +                }
       +        }
       +
       +        if (match) {
       +                E.cursor_y = y;
       +                E.cursor_x = match - row->chars;
       +                /* E.row_offset = E.num_rows; */ /* put line to top of screen */
       +
       +        } else {
       +                /* from other end of file until cursor */
       +                for (y = (y_inc == +1) ? 0 : E.num_rows - 1;
       +                     y != E.cursor_y + y_inc;
       +                     y += y_inc) {
       +                        row = &E.row[y];
       +                        match = strstr(row->chars, E.find_query);
       +                        if (match)
       +                                break;
       +                }
       +                if (match) {
       +                        E.cursor_y = y;
       +                        E.cursor_x = editor_row_cursor_rx_to_x(row, match - row->chars);
       +                }
       +        }
       +}
       +
       +/* direction = 1 is forward, 0 is backward */
       +void
       +editor_find(int direction)
       +{
       +        char *query;
       +
       +        if (direction)
       +                query = editor_prompt("/%s");
       +        else
       +                query = editor_prompt("?%s");
       +        if (query == NULL)
       +                return;
       +
       +        E.find_direction = direction;
       +        free(E.find_query);
       +        E.find_query = strdup(query);
       +        editor_find_next_occurence(0);
       +        free(query);
       +}
       +
       +
       +/* reallocate append buffer to hold string s */
       +void
       +ab_append(struct abuf *ab, const char *s, int len)
       +{
       +        char *new;
       +        new = realloc(ab->b, ab->len + len);
       +
       +        if (new == NULL)
       +                return;
       +        memcpy(&new[ab->len], s, len);
       +        ab->b = new;
       +        ab->len += len;
       +}
       +
       +void
       +ab_free(struct abuf *ab) {
       +        free(ab->b);
       +}
       +
       +int
       +show_status_message()
       +{
       +        if (E.show_status &&
       +            strlen(E.status_msg) &&
       +            time(NULL) - E.status_msg_time < STATUS_MESSAGE_TIMEOUT) {
       +                return 1;
       +        } else {
       +                if (E.show_status) {
       +                        E.show_status = 0;
       +                        E.screen_rows++;
       +                }
       +                return 0;
       +        }
       +}
       +
       +/* draw status line consisting of left and right components.
       + * if the screen is narrower than both parts, just show the left status, 
       + * truncated, if necessary. */
       +void
       +editor_draw_status(struct abuf *ab)
       +{
       +        char left_status[512], right_status[512];
       +        int left_status_len, right_status_len, padding;
       +        int percentage;
       +
       +        left_status_len = 0;
       +        switch (E.mode) {
       +                case 1:
       +                        left_status_len = snprintf(left_status, sizeof(left_status),
       +                                                   "INSERT ");
       +                        break;
       +                case 2:
       +                        left_status_len = snprintf(left_status, sizeof(left_status),
       +                                                   "VISUAL ");
       +                        break;
       +        }
       +        left_status_len += snprintf(left_status + left_status_len,
       +                                    sizeof(left_status),
       +                                    "%.20s %s",
       +                                    E.filename ? E.filename : "[unnamed]",
       +                                    E.file_changed ? "[+] " : "");
       +
       +        percentage = (int)((float)(E.cursor_y)/(E.num_rows-1)*100);
       +        if (percentage < 0) percentage = 0;
       +        right_status_len = snprintf(right_status, sizeof(right_status),
       +                                    "%d%% < %d, %d",
       +                                    percentage,
       +                                    E.cursor_x+1, E.cursor_y+1);
       +
       +        if (left_status_len > E.screen_columns)
       +                left_status_len = E.screen_columns;
       +
       +        padding = E.screen_columns - left_status_len - right_status_len;
       +        if (padding < 0) {
       +                if (left_status_len < E.screen_columns)
       +                        ab_append(ab, left_status, left_status_len);
       +                else
       +                        ab_append(ab, left_status, E.screen_columns);
       +        } else {
       +                ab_append(ab, left_status, left_status_len);
       +                while (padding--)
       +                        ab_append(ab, " ", 1);
       +                ab_append(ab, right_status, right_status_len);
       +        }
       +
       +        if (show_status_message()) {
       +                ab_append(ab, "\x1b[m", 3);
       +                ab_append(ab, "\r\n", 2);
       +        }
       +}
       +
       +/* draw status message if as long as it is specified and it is not too old */
       +void
       +editor_draw_status_message(struct abuf *ab)
       +{
       +        int msglen;
       +        ab_append(ab, "\x1b[K", 3);
       +        msglen = strlen(E.status_msg);
       +        if (msglen > E.screen_columns)
       +                msglen = E.screen_columns;
       +        if (show_status_message())
       +                ab_append(ab, E.status_msg, msglen);
       +}
       +
       +/* set vertical offset between file and screen when hitting the boundaries */
       +void
       +editor_scroll()
       +{
       +        E.cursor_rx = 0;
       +        if (E.cursor_y < E.num_rows)
       +                E.cursor_rx = editor_row_cursor_x_to_rx(&E.row[E.cursor_y],
       +                                                        E.cursor_x);
       +
       +        if (E.cursor_y < E.row_offset)
       +                E.row_offset = E.cursor_y;
       +        else if (E.cursor_y >= E.row_offset + E.screen_rows)
       +                E.row_offset = E.cursor_y - E.screen_rows + 1;
       +
       +        if (E.cursor_rx < E.column_offset)
       +                E.column_offset = E.cursor_rx;
       +        else if (E.cursor_rx >= E.column_offset + E.screen_columns)
       +                E.column_offset = E.cursor_rx - E.screen_columns + 1;
       +}
       +
       +/* draw editor screen.
       + * show tilde characters after EOF, and show status on last line */
       +void
       +editor_draw_rows(struct abuf *ab)
       +{
       +        int y, len, file_row;
       +        for (y = 0; y < E.screen_rows; ++y) {
       +                file_row = y + E.row_offset;
       +                if (file_row < E.num_rows) {
       +                        len = E.row[file_row].rsize - E.column_offset;
       +                        if (len < 0)
       +                                len = 0;
       +                        if (len > E.screen_columns)
       +                                len = E.screen_columns;
       +                        ab_append(ab, &E.row[file_row].rchars[E.column_offset], len);
       +                } else {
       +                        ab_append(ab, "~", 1);
       +                }
       +
       +                ab_append(ab, "\x1b[K", 3); /* erase to end of line */
       +                ab_append(ab, "\r\n", 2);
       +        }
       +}
       +
       +/* fill output append buffer and write to terminal.
       + * move cursor to left before repaint, and to cursor position after.
       + * hide the cursor when repainting.
       + * VT100 escape sequences:
       + * - http://vt100.net/docs/vt100-ug/chapter3.html
       + * - http://vt100.net/docs/vt100-ug/chapter3.html#CUP */
       +void
       +editor_refresh_screen()
       +{
       +        char buf[32];
       +        struct abuf ab = ABUF_INIT;
       +
       +        show_status_message();
       +        editor_scroll();
       +
       +        ab_append(&ab, "\x1b[?25l", 6); /* hide cursor */
       +        ab_append(&ab, "\x1b[H", 3);    /* cursor to home */
       +        
       +        editor_draw_rows(&ab);
       +        editor_draw_status(&ab);
       +        editor_draw_status_message(&ab);
       +
       +        snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
       +                (E.cursor_y - E.row_offset)+1,
       +                (E.cursor_rx - E.column_offset)+1);
       +        ab_append(&ab, buf, strlen(buf));
       +
       +        ab_append(&ab, "\x1b[?25h", 6); /* show cursor */
       +
       +        write(STDOUT_FILENO, ab.b, ab.len);
       +        ab_free(&ab);
       +}
       +
       +void
       +editor_place_cursor(int cx, int cy)
       +{
       +        char buf[128];
       +        snprintf(buf, sizeof(buf), "\x1b[%d;%dH", cy, cx);
       +        write(STDOUT_FILENO, buf, strlen(buf));
       +}
       +
       +/* set status message text, uses same format as printf */
       +void
       +editor_set_status_message(const char *fmt, ...)
       +{
       +        va_list ap;
       +        va_start(ap, fmt);
       +        vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap);
       +        va_end(ap);
       +        E.status_msg_time = time(NULL);
       +
       +        if (!E.show_status) {
       +                E.screen_rows--;
       +                E.show_status = 1;
       +        }
       +}
       +
       +/* prompt is expected to be a format string containing a %s */
       +char*
       +editor_prompt(char *prompt)
       +{
       +        size_t bufsize, buflen;
       +        char *buf;
       +        int c;
       +        
       +        bufsize = 128;
       +        buflen = 0;
       +        buf = malloc(bufsize);
       +        buf[0] = '\0';
       +
       +        while (1) {
       +                editor_set_status_message(prompt, buf);
       +                editor_refresh_screen();
       +                editor_place_cursor(strlen(prompt) - 1 + strlen(buf),
       +                                    E.screen_rows + 2);
       +
       +                c = editor_read_key();
       +                if (c == CTRL_KEY('h') || c == 127) { /* detect backspace */
       +                        if (buflen != 0)
       +                                buf[--buflen] = '\0';
       +                } else if (c == '\x1b' || (c == '\r' && !buflen)) { /* detect escape */
       +                        editor_set_status_message("");
       +                        free(buf);
       +                        return NULL;
       +                } else if (c == '\r') {
       +                        if (buflen != 0) {
       +                                editor_set_status_message("");
       +                                return buf;
       +                        }
       +                } else if (!iscntrl(c) && c < 128) {
       +                        if (buflen >= bufsize - 1) {
       +                                bufsize *= 2;
       +                                buf = realloc(buf, bufsize);
       +                        }
       +                        buf[buflen++] = c;
       +                        buf[buflen] = '\0';
       +                }
       +        }
       +}
       +
       +/* move cursor according to screen, file, and line limits */
       +void
       +editor_move_cursor(char key)
       +{
       +        int row_len;
       +        eRow *row;
       +        
       +        switch(key) {
       +                case 'h':
       +                        if (E.cursor_x != 0) {
       +                                E.cursor_x--;
       +                        } else if (E.cursor_y > 0) {
       +                                E.cursor_y--;
       +                                E.cursor_x = E.row[E.cursor_y].size;
       +                        }
       +                        break;
       +                case 'j':
       +                        if (E.cursor_y < E.num_rows - 1)
       +                                E.cursor_y++;
       +                        break;
       +                case 'k':
       +                        if (E.cursor_y != 0)
       +                                E.cursor_y--;
       +                        break;
       +                case 'l':
       +                        row = (E.cursor_y >= E.num_rows) ? NULL : &E.row[E.cursor_y];
       +                        if (row && E.cursor_x < row->size - 1) {
       +                                E.cursor_x++;
       +                        } else if (row && E.cursor_x == row->size &&
       +                                   E.cursor_y < E.num_rows - 1) {
       +                                E.cursor_y++;
       +                                E.cursor_x = 0;
       +                        }
       +                        break;
       +        }
       +
       +        /* do not allow navigation past EOL by vertical navigation */
       +        row = (E.cursor_y >= E.num_rows) ? NULL : &E.row[E.cursor_y];
       +        row_len = row ? row->size : 0;
       +        if (E.cursor_x > row_len)
       +                E.cursor_x = row_len;
       +}
       +
       +void
       +editor_process_keypress()
       +{
       +        char c;
       +        int i;
       +        
       +        c = editor_read_key();
       +
       +        if (E.mode == 0) {  /* normal mode */
       +                switch (c) {
       +                        case 'i':
       +                                E.mode = 1;
       +                                break;
       +                        case 'a':
       +                                editor_move_cursor('l');
       +                                E.mode = 1;
       +                                break;
       +
       +                        case CTRL_KEY('q'):
       +                                if (E.file_changed) {
       +                                        editor_set_status_message("error: "
       +                                                                      "file has unsaved changes. "
       +                                                                      "Press C-x to confirm quit");
       +                                        break;
       +                                } else {
       +                                        write(STDOUT_FILENO, "\x1b[2J", 4); /* clear screen */
       +                                        write(STDOUT_FILENO, "\x1b[H", 3);
       +                                        exit(0);
       +                                        break;
       +                                }
       +                        case CTRL_KEY('x'):
       +                                write(STDOUT_FILENO, "\x1b[2J", 4); /* clear screen */
       +                                write(STDOUT_FILENO, "\x1b[H", 3);
       +                                exit(0);
       +                                break;
       +                        case 'h':
       +                        case 'j':
       +                        case 'k':
       +                        case 'l':
       +                                editor_move_cursor(c);
       +                                break;
       +
       +                        case CTRL_KEY('w'):
       +                                file_save(E.filename);
       +                                break;
       +
       +                        case CTRL_KEY('f'):
       +                                i = E.screen_rows;
       +                                while (i--)
       +                                        editor_move_cursor('j');
       +                                break;
       +                        case CTRL_KEY('b'):
       +                                i = E.screen_rows;
       +                                while (i--)
       +                                        editor_move_cursor('k');
       +                                break;
       +
       +                        case CTRL_KEY('d'):
       +                                i = E.screen_rows/2;
       +                                while (i--)
       +                                        editor_move_cursor('j');
       +                                break;
       +                        case CTRL_KEY('u'):
       +                                i = E.screen_rows/2;
       +                                while (i--)
       +                                        editor_move_cursor('k');
       +                                break;
       +
       +                        case '0':
       +                                E.cursor_x = 0;
       +                                break;
       +                        case '$':
       +                                if (E.cursor_y < E.num_rows)
       +                                        E.cursor_x = E.row[E.cursor_y].size - 1;
       +                                break;
       +
       +                        case 'g':
       +                                E.cursor_x = 0;
       +                                E.cursor_y = 0;
       +                                break;
       +                        case 'G':
       +                                E.cursor_x = 0;
       +                                E.cursor_y = E.num_rows - 1;
       +                                break;
       +
       +                        case 'x':
       +                                editor_delete_char_right();
       +                                break;
       +                        case 'd':
       +                                editor_row_delete(E.cursor_y);
       +                                editor_move_cursor('h');
       +                                break;
       +
       +                        case 'o':
       +                                if (E.cursor_y < E.num_rows)
       +                                        E.cursor_x = E.row[E.cursor_y].size;
       +                                editor_insert_new_line();
       +                                E.mode = 1;
       +                                break;
       +                        case 'O':
       +                                E.cursor_x = 0;
       +                                editor_insert_new_line();
       +                                editor_move_cursor('k');
       +                                E.mode = 1;
       +                                break;
       +
       +                        case 'I':
       +                                E.cursor_x = 0;
       +                                E.mode = 1;
       +                                break;
       +                        case 'A':
       +                                if (E.cursor_y < E.num_rows)
       +                                        E.cursor_x = E.row[E.cursor_y].size;
       +                                E.mode = 1;
       +                                break;
       +
       +                        case '/':
       +                                editor_find(1);
       +                                break;
       +                        case '?':
       +                                editor_find(0);
       +                                break;
       +                        case 'n':
       +                                editor_find_next_occurence(0);
       +                                break;
       +                        case 'N':
       +                                editor_find_next_occurence(1);
       +                                break;
       +                }
       +        } else if (E.mode == 1) {  /* insert mode */
       +                switch (c) {
       +                        case CTRL_KEY('c'):
       +                        case '\x1b':  /* escape */
       +                                E.mode = 0;
       +                                break;
       +                        
       +                        case CTRL_KEY('\r'): /* enter */
       +                                editor_insert_new_line();
       +                                break;
       +
       +                        case 127:  /* backspace */
       +                        case CTRL_KEY('h'):
       +                                editor_delete_char_left();
       +                                break;
       +
       +                        case CTRL_KEY('l'):
       +                                break;
       +
       +                        default:
       +                                editor_insert_char(c);
       +                                break;
       +                }
       +        }
       +}
       +
       +void
       +deinit_editor() {
       +        int i;
       +        free(E.filename);
       +        free(E.find_query);
       +        for (i=0; i<E.num_rows; ++i)
       +                editor_row_free(&E.row[i]);
       +}
       +
       +/* set editor state variables, make room for status */
       +void
       +init_editor() {
       +        E.cursor_x = 0;
       +        E.cursor_y = 0;
       +        E.cursor_rx = 0;
       +        E.mode = 0;
       +        E.num_rows = 0;
       +        atexit(deinit_editor);
       +        E.row = NULL;
       +        E.row_offset = 0;
       +        E.column_offset = 0;
       +        E.filename = NULL;
       +        E.status_msg[0] = '\0';
       +        E.status_msg_time = 0;
       +        E.show_status = 0;
       +        E.file_changed = 0;
       +        E.find_query = NULL;
       +
       +        if (get_window_size(&E.screen_rows, &E.screen_columns) == -1)
       +                die("get_window_size");
       +        E.screen_rows -= 1;
       +}
       +
       +
       +/* main */
       +int
       +main(int argc, char* argv[])
       +{
       +        enable_raw_mode();
       +        init_editor();
       +
       +        /* TODO: proper argument handling */
       +        if (argc >= 2) {
       +                file_open(argv[1]);
       +        }
       +
       +        editor_set_status_message("%s v%s", PROGNAME, VERSION);
       +
       +        while (1) {
       +                editor_refresh_screen();
       +                editor_process_keypress();
       +        }
       +        return 0;
       +}
       +