text: //INCLUDES
  
  #define _DEFAULT_SOURCE
  #define _BSD_SOURCE
  #define _GNU_SOURCE
  
  #include <ctype.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <stdio.h>
  #include <stdarg.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/ioctl.h>
  #include <sys/types.h>
  #include <termios.h>
  #include <time.h>
  #include <unistd.h>
  
  //DEFINES
  
  #define VERSION "0.0.1"
  #define CTRL_KEY(k) ((k) & 0x1f)
  #define TABSTOP 8
  #define QUIT_TIMES 3
  
  enum editorKey {
    BACKSPACE = 127,
    ARROW_LEFT = 1000,
    ARROW_RIGHT,
    ARROW_UP,
    ARROW_DOWN,
    DEL_KEY,
    HOME_KEY,
    END_KEY,
    PAGE_UP,
    PAGE_DOWN
  };
  
  enum editorHighlight {
    HL_NORMAL = 0,
    HL_COMMENT,
    HL_KEYWORD1,
    HL_KEYWORD2,
    HL_STRING,
    HL_NUMBER,
    HL_MATCH
  };
  
  #define HL_HIGHLIGHT_NUMBERS (1<<0)
  #define HL_HIGHLIGHT_STRINGS (1<<1)
  //DATA
  
  struct editorSyntax {
    char *filetype;
    char **filematch;
    char **keywords;
    char *singleline_comment_start;
    int flags;
  };
  
  typedef struct erow {
    int size;
    int rsize; // Tab rendering
    char *chars;
    char *render;
    unsigned char *hl;
  } erow;
  
  struct editorConfig {
    int cx, cy;
    int rx;
    int rowoff, coloff;
    int screenrows, screencols;
    int numrows;
    erow *row;
    int dirty;
    char *filename;
    char statusmsg[80];
    time_t statusmsg_time;
    struct editorSyntax *syntax;
    struct termios orig_termios; //The original mode of my terminal
  };
  
  struct editorConfig E;
  
  // FILETYPES
  /* Moved to syntax.c due for making pluggable filetype support.
     To add a new filetype support include the file in syntax.c and recompile.
     */
  #include "./syntax/syntax.c"
  
  #define HLDB_ENTRIES (sizeof(HLDB)/ sizeof(HLDB[0]))
  
  // FORWARD DECLARATIONS
  
  void editorSetStatusMessage(const char *fmt, ...);
  void editorRefreshScreen();
  char *editorPrompt(char *prompt, void(*callback)(char *, int));
  
  // TERMINAL
  void die(const char *s){
    write(STDOUT_FILENO,"\x1b[2j",4);
    write(STDOUT_FILENO,"\x1b[H",3);
  
    perror(s);
    exit(1);
  }
  
  void disableRawMode(){
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
      die("tcsetattr");
  }
  
  void enableRawMode(){
    //New terminal config
  
    if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) die("tcgetattr"); //Get the state of the STDIN_FILENO attr
    atexit(disableRawMode);
  
    struct termios raw = E.orig_termios;
  
    raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    raw.c_iflag &= ~(ICRNL | IXON);
    raw.c_oflag &= ~(OPOST);
    raw.c_cflag |= ~(CS8);
    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN |ISIG);
    raw.c_cc[VMIN] = 0;
    raw.c_cc[VTIME] = 1;
  
    if (tcsetattr(STDIN_FILENO,TCSAFLUSH,&raw) == -1) die("tcsetattr"); //Set the flags to the active terminal
  }
  
  int editorReadKey(){
    int nread;
    char c;
    while((nread = read(STDIN_FILENO, &c, 1)) != 1){
      if (nread == -1 && errno != EAGAIN) die("read");
    }
  
    if (c == '\x1b'){
      char seq[3];
  
      if(read(STDIN_FILENO, &seq[0], 1) != 1) return '\x1b';
      if(read(STDIN_FILENO, &seq[1], 1) != 1) return '\x1b';
  
      if (seq[0] == '['){
        if (seq[1] >= '0' && seq[1] <= '9'){
          if (read(STDIN_FILENO, &seq[2], 1) != 1) return '\x1b';
          if (seq[2] == '~'){
            switch (seq[1]){
              case '1': return HOME_KEY;
              case '3': return DEL_KEY;
              case '4': return END_KEY;
              case '5': return PAGE_UP;
              case '6': return PAGE_DOWN;
              case '7': return HOME_KEY;
              case '8': return END_KEY;
            }
          }
        } else {
          switch (seq[1]){
            case 'A': return ARROW_UP;
            case 'B': return ARROW_DOWN;
            case 'C': return ARROW_RIGHT;
            case 'D': return ARROW_LEFT;
            case 'H': return HOME_KEY;
            case 'F': return END_KEY;
          }
        }
      } else if (seq[0] == 'O'){
        switch(seq[1]){
          case 'H': return HOME_KEY;
          case 'F': return END_KEY;
        }
      }
      return '\x1b';
    } else {
      return c;
    }
  
  }
  
  int getCursorPosition(int *rows, int *cols){
    char buf[32];
    unsigned int i = 0;
  
    if (write(STDOUT_FILENO, "\x1b[6n",4) != 4) return -1;
  
    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 (sscanf(&buf[2], "%d;%d", rows, cols) != 2) return -1;
  
    return 0;
  }
  
  int getWindowSize(int *rows, int *cols){
    struct winsize ws;
  
    if (ioctl(STDOUT_FILENO,TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0){
      if (write(STDIN_FILENO, "\x1b[999C\x1b[999B", 12) != 12) return -1;
      return getCursorPosition(rows,cols);
    } else {
      *cols = ws.ws_col;
      *rows = ws.ws_row;
      return 0;
    }
  }
  // SYNTAX HIGHLIGHTING
  
  int is_separator(int c){
    return isspace(c) || c == '\0' || strchr(",.()+-/*=~%<>[];",c) != NULL;
  }
  
  void editorUpdateSyntax(erow *row){
    row->hl = realloc(row->hl, row->rsize);
    memset(row->hl, HL_NORMAL, row->rsize);
  
    if (E.syntax == NULL) return;
  
    char **keywords = E.syntax->keywords;
  
    char *scs = E.syntax->singleline_comment_start;
    int scs_len = scs ? strlen(scs) : 0;
  
    int prev_sep = 1;
    int in_string = 0;
    int i = 0;
    while (i < row->rsize){
      char c = row->render[i];
      unsigned char prev_hl = (i > 0) ? row->hl[i -1] : HL_NORMAL;
  
      if (scs_len && !in_string){
        if (!strncmp(&row->render[i],scs, scs_len)){
          memset(&row->hl[i], HL_COMMENT,row->rsize - i);
          break;
        }
      }
  
      if (E.syntax->flags & HL_HIGHLIGHT_STRINGS){
        if (in_string){
          row->hl[i] = HL_STRING;
          if (c == '\\' && i+1 < row->rsize) {
            row->hl[i+1] = HL_STRING;
            i+=2;
            continue;
          }
          if (c == in_string) in_string = 0;
          i++;
          prev_sep = 1;
          continue;
        } else {
          if ( c == '"' || c == '\'' ){
            in_string = c;
            row->hl[i] = HL_STRING;
            i++;
            continue;
          }
        }
      }
  
      if (E.syntax->flags & HL_HIGHLIGHT_NUMBERS){
        if ((isdigit(c) && (prev_sep || prev_hl == HL_NUMBER)) ||
            (c == '.' && prev_hl == HL_NUMBER)){
          row->hl[i] = HL_NUMBER;
          i++;
          prev_sep = 0;
          continue;
        }
      }
  
      if (prev_sep) {
        int j;
        for (j = 0; keywords[j]; j++){
          int klen = strlen(keywords[j]);
          int kw2 = keywords[j][klen -1] == '|';
          if (kw2) klen--;
  
          if (!strncmp(&row->render[i], keywords[j], klen) &&
              is_separator(row->render[i + klen])) {
            memset(&row->hl[i], kw2 ? HL_KEYWORD2 : HL_KEYWORD1, klen);
            i+= klen;
            break;
          }
        }
        if (keywords[j] != NULL){
          prev_sep = 0;
          continue;
        }
      }
  
      prev_sep = is_separator(c);
      i++;
    }
  }
  
  int editorSyntaxToColor(int hl){
    switch(hl){
      case HL_COMMENT: return 36;
      case HL_KEYWORD1: return 33;
      case HL_KEYWORD2: return 32;
      case HL_STRING: return 35;
      case HL_NUMBER: return 31;
      case HL_MATCH: return 34;
      default: return 37;
    }
  }
  
  void editorSelectSyntaxHighlight(){
    E.syntax = NULL;
    if (E.filename == NULL) return;
  
    char *ext = strchr(E.filename, '.');
  
    for (unsigned int j = 0; j < HLDB_ENTRIES; j++){
      struct editorSyntax *s =&HLDB[j];
      unsigned int i = 0;
      while (s->filematch[i]){
        int is_ext = (s->filematch[i][0] == '.');
        if ((is_ext && ext && !strcmp(ext, s->filematch[i])) ||
            (!is_ext && strstr(E.filename, s->filematch[i]))){
          E.syntax = s;
  
          for(int filerow = 0; filerow < E.numrows; filerow++){
            editorUpdateSyntax(&E.row[filerow]);
          }
  
          return;
        }
        i++;
      }
    }
  }
  
  // ROW OPERATIONS
  
  int editorRowCxToRx(erow *row, int cx){
    int rx = 0;
    for (int j = 0; j < cx; j++){
      if (row->chars[j] == '\t')
        rx += (TABSTOP -1) - (rx % TABSTOP);
      rx++;
    }
    return rx;
  }
  
  int editorRowRxToCx(erow *row, int rx){
    int cur_rx = 0;
    int cx;
    for (cx = 0; cx < row->size; cx++){
      if (row->chars[cx] == '\t')
        cur_rx += (TABSTOP - 1) - (cur_rx % TABSTOP);
      cur_rx++;
  
      if (cur_rx > rx) return cx;
    }
    return cx;
  }
  
  void editorUpdateRow(erow *row){
    int tabs = 0;
    for (int j = 0; j < row->size; j++)
      if (row->chars[j] == '\t') tabs++;
  
    free(row->render);
    row->render = malloc(row->size + tabs * (TABSTOP -1 ) + 1);
  
    int idx = 0;
    for (int j = 0; j < row->size; j++){
      if (row->chars[j] == '\t'){
        row->render[idx++] = ' ';
        while (idx % TABSTOP != 0) row->render[idx++] = ' ';
      } else {
        row->render[idx++] = row->chars[j];
      }
    }
    row->render[idx] = '\0';
    row->rsize = idx;
    editorUpdateSyntax(row);
  }
  
  void editorInsertRow(int at, char *s, size_t len){
    if ( at < 0 || at > E.numrows) return;
  
    E.row = realloc(E.row, sizeof(erow)*(E.numrows + 1));
    memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
  
    E.row[at].size = len;
    E.row[at].chars = malloc(len + 1);
    memcpy(E.row[at].chars, s , len);
    E.row[at].chars[len] = '\0';
  
    E.row[at].rsize = 0;
    E.row[at].render = NULL;
    E.row[at].hl = NULL;
    editorUpdateRow(&E.row[at]);
    E.numrows++;
    E.dirty++;
  }
  
  void editorFreeRow(erow *row){
    free(row->render);
    free(row->chars);
    free(row->hl);
  }
  
  void editorDelRow(int at){
    if (at < 0 || at >=E.numrows) return;
    editorFreeRow(&E.row[at]);
    memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at - 1));
    E.numrows--;
    E.dirty++;
  }
  
  void editorRowInsertChar(erow *row, int at, int c){
    if (at < 0 || at > row->size) at = row->size;
    row->chars = realloc(row->chars, row->size + 2);
    memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
    row->size++;
    row->chars[at] = c;
    editorUpdateRow(row);
    E.dirty++;
  }
  
  void editorRowAppendString(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';
    editorUpdateRow(row);
    E.dirty++;
  }
  
  void editorRowDelChar(erow *row, int at){
    if (at < 0 || at >= row->size) return;
    memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
    row->size--;
    editorUpdateRow(row);
    E.dirty++;
  }
  
  // EDITOR OPERATIONS
  
  void editorInsertChar(int c){
    if (E.cy == E.numrows){
      editorInsertRow(E.numrows,"",0);
    }
    editorRowInsertChar(&E.row[E.cy], E.cx, c);
    E.cx++;
  }
  
  void editorInsertNewline(){
    if (E.cx == 0){
      editorInsertRow(E.cy,"",0);
    } else {
      erow *row = &E.row[E.cy];
      editorInsertRow(E.cy + 1, &row->chars[E.cx], row->size - E.cx);
      row = &E.row[E.cy];
      row->size = E.cx;
      row->chars[row->size] = '\0';
      editorUpdateRow(row);
    }
    E.cy++;
    E.cx = 0;
  }
  
  void editorDelChar(){
    if (E.cy == E.numrows) return;
    if (E.cx == 0 && E.cy == 0) return;
  
    erow *row = &E.row[E.cy];
    if (E.cx > 0){
      editorRowDelChar(row,E.cx - 1);
      E.cx--;
    } else {
      E.cx = E.row[E.cy - 1].size;
      editorRowAppendString(&E.row[E.cy - 1], row->chars, row->size);
      editorDelRow(E.cy);
      E.cy--;
    }
  }
  
  // FILE I/O
  
  char *editorRowsToString(int *buflen){
    int totlen = 0;
    for (int j = 0; j < E.numrows; j++)
      totlen += E.row[j].size +1;
    *buflen = totlen;
  
    char *buf = malloc(totlen);
    char *p = buf;
    for (int j = 0; j < E.numrows; j++){
      memcpy(p, E.row[j].chars, E.row[j].size);
      p+= E.row[j].size;
      *p = '\n';
      p++;
    }
  
    return buf;
  }
  
  void editorOpen(char *filename){
    free(E.filename);
    E.filename = strdup(filename);
    editorSelectSyntaxHighlight();
    FILE *fp = fopen(filename, "r");
    if (!fp) die("fopen");
  
    char *line = NULL;
    size_t linecap = 0;
    ssize_t linelen;
    while((linelen = getline(&line, &linecap, fp)) != -1){
      while( linelen > 0 && (line[linelen - 1 ] == '\n' ||
            line[linelen - 1 ] == '\r'))
        linelen--;
      editorInsertRow(E.numrows,line,linelen);
    }
    free(line);
    fclose(fp);
    E.dirty = 0;
  }
  
  void editorSave(){
    if (E.filename == NULL){
      E.filename = editorPrompt("Save as: %s (ESC to cancel)",NULL);
      if (E.filename == NULL) {
        editorSetStatusMessage("Save aborted");
        return;
      }
      editorSelectSyntaxHighlight();
    }
    int len;
    char *buf = editorRowsToString(&len);
  
    int fd = open(E.filename, O_RDWR | O_CREAT, 0644);
    if (fd != -1){
      if(ftruncate(fd,len) != -1){
        if(write(fd, buf, len) == len){
          close(fd);
          free(buf); // i MUST free the memory of buf since i ask for it
          editorSetStatusMessage("%d bytes written to disk", len);
          E.dirty = 0;
          return;
        }
      }
      close(fd);
    }
    free(buf);
    editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
  }
  
  
  // FIND
  void editorReplace(){
    char *toFind = editorPrompt("Search: %s (ESC to cancel)",NULL);
    char *toRep = editorPrompt("Replace With: %s (ESC to cancel)",NULL);
    if (toFind == NULL || toRep == NULL ) return;
    int found = 0;
    for (int i = 0; i < E.numrows; i++){
      erow *row = &E.row[i];
      char *match = strstr(row->render, toFind);
      if (match){
        E.cy = i;
        E.cx = editorRowRxToCx(row, match - row->render);
        E.rowoff = E.numrows;
        found++;
        break;
      }
    }
    //Now that im positioned i have to delete every char in the match
    if (found){
      for(unsigned int i = 0; i < strlen(toFind); i++){
        editorRowDelChar(&E.row[E.cy],E.cx);
      }
  
      for (unsigned int i = 0; i < strlen(toRep); i++)
        editorInsertChar(toRep[i]);
    } else editorSetStatusMessage("No matches found.");
    free(toFind);
    free(toRep);
  }
  
  void editorFindCallback(char *query, int key){
    static int last_match = -1;
    static int direction = 1;
  
    static int saved_hl_line;
    static char *saved_hl = NULL;
  
    if (saved_hl){
      memcpy(E.row[saved_hl_line].hl, saved_hl, E.row[saved_hl_line].rsize);
      free(saved_hl);
      saved_hl = NULL;
    }
  
    if (key == '\r' || key == '\x1b'){
      last_match = -1;
      direction = 1;
      return;
    } else if (key == ARROW_RIGHT || key == ARROW_DOWN){
      direction = 1;
    } else if (key == ARROW_LEFT || key == ARROW_UP){
      direction = -1;
    } else {
      last_match = -1;
      direction =1;
    }
    if (last_match == -1) direction = 1;
    int current = last_match;
    for (int i =0; i < E.numrows; i++){
      current += direction;
      if (current == -1) current = E.numrows - 1;
      else if (current == E.numrows) current = 0;
      erow *row =&E.row[current];
      char *match = strstr(row->render, query);
      if (match){
        last_match = current;
        E.cy = current;
        E.cx = editorRowRxToCx(row,match - row->render);
        E.rowoff = E.numrows;
  
        saved_hl_line = current;
        saved_hl = malloc(row->rsize);
        memcpy(saved_hl, row->hl, row->rsize);
        memset(&row->hl[match - row->render], HL_MATCH, strlen(query));
        break;
      }
    }
  }
  
  void editorFind(){
    int saved_cx = E.cx;
    int saved_cy = E.cy;
    int saved_coloff = E.coloff;
    int saved_rowoff = E.rowoff;
  
    char *query = editorPrompt("Search: %s (Use ESC/Arrows/Enter)", editorFindCallback);
  
    if(query){
      free(query);
    } else{
      E.cx = saved_cx;
      E.cy = saved_cy;
      E.coloff = saved_coloff;
      E.rowoff = saved_rowoff;
    }
  
  }
  // APPEND BUFFER
  
  struct abuf {
    char *b;
    int len;
  };
  
  #define ABUF_INIT {NULL, 0}
  
  void abAppend(struct abuf *ab, const char *s, int len){
    char *ne = realloc(ab->b, ab->len + len);
  
    if (ne == NULL) return;
    memcpy(&ne[ab->len], s, len);
    ab->b = ne;
    ab->len += len;
  }
  
  void abFree(struct abuf *ab){
    free(ab->b);
  }
  
  // OUTPUT
  
  void editorScroll(){
    E.rx = 0;
    if (E.cy < E.numrows){
      E.rx = editorRowCxToRx(&E.row[E.cy], E.cx);
    }
  
    if (E.cy < E.rowoff){
      E.rowoff = E.cy;
    }
    if (E.cy >= E.rowoff + E.screenrows){
      E.rowoff = E.cy - E.screenrows +1;
    }
    if (E.cx < E.coloff){
      E.coloff = E.rx;
    }
    if (E.rx >= E.coloff + E.screencols){
      E.coloff = E.rx - E.screencols +1;
    }
  }
  
  void editorDrawRows(struct abuf *ab){
    for (int i = 0; i < E.screenrows; i++){
      int filerow = i + E.rowoff;
      if (filerow >= E.numrows){
        if (E.numrows == 0 && i == E.screenrows/3){
          char welcome[84];
          int welcomelen = snprintf(welcome, sizeof(welcome),
              "--  Ken (KILO enhanced) editor --"
              " Kalil de Lima: kaozdl@gmail.com --  version %s", VERSION);
          if (welcomelen > E.screencols) welcomelen = E.screencols;
          int padding = (E.screencols - welcomelen) / 2;
          if (padding) {
            abAppend(ab,"|",1);
            padding--;
          }
          while (padding--) abAppend(ab," ",1);
          abAppend(ab,welcome,welcomelen);
        } else {
          abAppend(ab, "|",1);
        }
      } else {
        int len = E.row[filerow].rsize - E.coloff;
        if(len < 0) len = 0;
        if (len > E.screencols) len = E.screencols;
        char *c = &E.row[filerow].render[E.coloff];
        unsigned char *hl = &E.row[filerow].hl[E.coloff];
        int current_color = -1;
        for (int j = 0; j < len; j++){
          if (hl[j] == HL_NORMAL){
            if (current_color != -1){
              abAppend(ab, "\x1b[39m", 5);
              current_color = -1;
            }
            abAppend(ab, &c[j], 1);
          } else{
            int color = editorSyntaxToColor(hl[j]);
            if (color != current_color){
              current_color = color;
              char buf[16];
              int clen = snprintf(buf,sizeof(buf), "\x1b[%dm",color);
              abAppend(ab, buf, clen);
            }
            abAppend(ab, &c[j], 1);
          }
        }
        abAppend(ab,"\x1b[39m",5);
      }
      abAppend(ab, "\x1b[K",3);
      abAppend(ab, "\r\n", 2);
    }
  }
  
  void editorDrawStatusBar(struct abuf *ab){
    abAppend(ab,"\x1b[7m",4);
    char status[80], rstatus[80];
    int len = snprintf(status, sizeof(status), "%.20s - %d lines %s",
        E.filename ? E.filename : "[No Name]", E.numrows,
        E.dirty ? "(modified)" : "");
    int rlen = snprintf(rstatus, sizeof(rstatus), "%s | %d/%d",
        E.syntax ? E.syntax->filetype : "no ft" ,E.cy +1, E.numrows);
    if (len > E.screencols) len = E.screencols;
    abAppend(ab,status,len);
    while (len < E.screencols){
      if (E.screencols - len == rlen){
        abAppend(ab, rstatus, rlen);
        break;
      } else{
        abAppend(ab, " ", 1);
        len++;
      }
    }
    abAppend(ab, "\x1b[m",3);
    abAppend(ab, "\r\n",2);
  }
  
  void editorDrawMessageBar(struct abuf *ab){
    abAppend(ab, "\x1b[K",3);
    int msglen = strlen(E.statusmsg);
    if (msglen > E.screencols) msglen = E.screencols;
    if (msglen && time(NULL) - E.statusmsg_time < 5)
      abAppend(ab, E.statusmsg, msglen);
  }
  
  void editorRefreshScreen(){
    editorScroll();
  
    struct abuf ab = ABUF_INIT;
  
    abAppend(&ab, "\x1b[?25l",6);
    abAppend(&ab,"\x1b[H",3);
  
    editorDrawRows(&ab);
    editorDrawStatusBar(&ab);
    editorDrawMessageBar(&ab);
  
    char buf[32];
    snprintf(buf,sizeof(buf),"\x1b[%d;%dH", (E.cy - E.rowoff ) +1, (E.rx - E.coloff) + 1);
    abAppend(&ab, buf, strlen(buf));
  
    abAppend(&ab, "\x1b[?25h",6);
  
    write(STDOUT_FILENO, ab.b, ab.len);
    abFree(&ab);
  }
  
  void editorSetStatusMessage(const char *fmt, ...){
    va_list ap;
    va_start(ap,fmt);
    vsnprintf(E.statusmsg, sizeof(E.statusmsg), fmt,ap);
    va_end(ap);
    E.statusmsg_time = time(NULL);
  }
  
  // INPUT
  char *editorPrompt(char *prompt, void (*callback)(char *, int)){
    size_t bufsize = 128;
    char *buf = malloc(bufsize);
  
    size_t buflen = 0;
    buf[0] = '\0';
  
    while (1) {
      editorSetStatusMessage(prompt,buf);
      editorRefreshScreen();
  
      int c = editorReadKey();
      if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE){
        if (buflen != 0) buf[--buflen] = '\0';
      } else if (c == '\x1b') {
        editorSetStatusMessage("");
        if (callback) callback(buf, c);
        free(buf);
        return NULL;
      } else if (c =='\r') {
        if (buflen != 0) {
          editorSetStatusMessage("");
          if (callback) callback(buf, c);
          return buf;
        }
      } else if (!iscntrl(c) && c < 128) {
        if (buflen == bufsize -1) {
          bufsize *= 2;
          buf = realloc(buf,bufsize);
        }
        buf[buflen++] = c;
        buf[buflen] = '\0';
      }
      if (callback) callback(buf,c);
    }
  }
  
  void editorMoveCursor(int key){
    erow *row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
  
    switch(key){
      case ARROW_LEFT:
        if (E.cx != 0){
          E.cx--;
        } else if (E.cy > 0){
          E.cy--;
          E.cx = E.row[E.cy].size;
        }
        break;
      case ARROW_RIGHT:
        if (row && E.cx < row->size){
          E.cx++;
        } else if (row && E.cx == row->size) {
          E.cy++;
          E.cx = 0;
        }
        break;
      case ARROW_UP:
        if (E.cy != 0){
          E.cy--;
        }
        break;
      case ARROW_DOWN:
        if (E.cy != E.numrows){
          E.cy++;
        }
        break;
    }
  
    row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
    int rowlen = row ? row->size : 0;
    if (E.cx > rowlen){
      E.cx = rowlen;
    }
  }
  
  void editorProcessKeyPress(){
    int c = editorReadKey();
    static int quit_times = QUIT_TIMES;
  
    switch(c){
      case '\r':
        editorInsertNewline();
        break;
  
      case CTRL_KEY('q'):
        if (E.dirty && quit_times > 0){
          editorSetStatusMessage("WARNING!!! File has unsaved changes. "
              "Press Ctrl-Q %d more times to quit.",quit_times);
          quit_times--;
          return;
        }
        write(STDOUT_FILENO,"\x1b[2J",4);
        write(STDOUT_FILENO,"\x1b[H",3);
        exit(0);
        break;
  
      case CTRL_KEY('s'):
        editorSave();
        break;
  
      case HOME_KEY:
        E.cx = 0;
        break;
  
      case END_KEY:
        if (E.cy < E.numrows)
          E.cx = E.row[E.cy].size;
        break;
      case CTRL_KEY('r'):
        editorReplace();
        break;
      case CTRL_KEY('f'):
        editorFind();
        break;
  
      case BACKSPACE:
      case CTRL_KEY('h'):
      case DEL_KEY:
        if (c == DEL_KEY) editorMoveCursor(ARROW_RIGHT);
        editorDelChar();
        break;
  
      case PAGE_UP:
      case PAGE_DOWN:
        {
          if(c == PAGE_UP){
            E.cy = E.rowoff;
          } else if (c == PAGE_DOWN){
            E.cy = E.rowoff + E.screenrows - 1;
            if (E.cy > E.numrows) E.cy = E.numrows;
          }
  
          int times = E.screenrows;
          while(times--)
            editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
        }
        break;
      case ARROW_UP:
      case ARROW_DOWN:
      case ARROW_LEFT:
      case ARROW_RIGHT:
        editorMoveCursor(c);
        break;
  
      case CTRL_KEY('l'):
      case '\x1b':
        break;
  
      default:
        editorInsertChar(c);
        break;
    }
    quit_times = QUIT_TIMES;
  }
  
  // INIT
  
  void initEditor(){
  
    E.cx = E.rx = E.cy = E.numrows = E.rowoff = E.coloff = E.dirty = 0;
    E.row = NULL;
    E.filename = NULL;
    E.statusmsg[0] = '\0';
    E.statusmsg_time = 0;
    E.syntax = NULL;
    if (getWindowSize(&E.screenrows, &E.screencols) == -1)die("getWindowSize");
    E.screenrows -=2;
  }
  
  int main(int argc, char *argv[]){
    enableRawMode();
    initEditor();
    if (argc >=2){
      editorOpen(argv[1]);
    }
  
    editorSetStatusMessage("HELP: Ctrl-Q = quit | Ctrl-S = save | Ctrl-F = find"
        " | Ctrl-R = find & replace");
  
    while (1){
      editorRefreshScreen();
      editorProcessKeyPress();
    }
    return 0;
  }
  