Subversion Repositories Kolibri OS

Rev

Rev 7413 | Rev 7419 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2. *   Copyright (C) 2017 Daniel Morales
  3. *
  4. *   This program is free software: you can redistribute it and/or modify
  5. *   it under the terms of the GNU General Public License as published by
  6. *   the Free Software Foundation, either version 3 of the License, or
  7. *   any later version.
  8. *
  9. *   This program is distributed in the hope that it will be useful,
  10. *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. *   GNU General Public License for more details.
  13. *
  14. *   You should have received a copy of the GNU General Public License
  15. *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16. */
  17.  
  18. /*** Include section ***/
  19.  
  20. // We add them above our includes, because the header
  21. // files we are including use the macros to decide what
  22. // features to expose. These macros remove some compilation
  23. // warnings. See
  24. // https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
  25. // for more info.
  26. #define _DEFAULT_SOURCE
  27. #define _BSD_SOURCE
  28. #define _GNU_SOURCE
  29.  
  30. #include <ctype.h>
  31. #include <errno.h>
  32. ///#include <fcntl.h>
  33. ///#include <signal.h>
  34. #include <stdio.h>
  35. #include <stdarg.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. ///#include <sys/ioctl.h>
  39. ///#include <sys/types.h>
  40. ///#include <termios.h>
  41. #include <time.h>
  42. ///#include <unistd.h>
  43. #include <conio.h>
  44. /*** Define section ***/
  45.  
  46. // This mimics the Ctrl + whatever behavior, setting the
  47. // 3 upper bits of the character pressed to 0.
  48. ///#define CTRL_KEY(k) ((k) & 0x1f)
  49. // Siemargl - set top 4 bits
  50. #define CTRL_KEY(k) ((k) | 0xf000)
  51. // Empty buffer
  52. #define ABUF_INIT {NULL, 0}
  53. // Version code
  54. #define TTE_VERSION "0.0.5"
  55. // Length of a tab stop
  56. #define TTE_TAB_STOP 4
  57. // Times to press Ctrl-Q before exiting
  58. #define TTE_QUIT_TIMES 2
  59. // Highlight flags
  60. #define HL_HIGHLIGHT_NUMBERS (1 << 0)
  61. #define HL_HIGHLIGHT_STRINGS (1 << 1)
  62.  
  63. /*** Data section ***/
  64.  
  65. // Kolibri defaults
  66. int con_def_wnd_width   =    80;
  67. int     con_def_wnd_height  =    25;
  68.  
  69.  
  70. typedef struct editor_row {
  71.     int idx; // Row own index within the file.
  72.     int size; // Size of the content (excluding NULL term)
  73.     int render_size; // Size of the rendered content
  74.     char* chars; // Row content
  75.     char* render; // Row content "rendered" for screen (for TABs).
  76.     unsigned char* highlight; // This will tell you if a character is part of a string, comment, number...
  77.     int hl_open_comment; // True if the line is part of a ML comment.
  78. } editor_row;
  79.  
  80. struct editor_syntax {
  81.     // file_type field is the name of the filetype that will be displayed
  82.     // to the user in the status bar.
  83.     char* file_type;
  84.     // file_match is an array of strings, where each string contains a
  85.     // pattern to match a filename against. If the filename matches,
  86.     // then the file will be recognized as having that filetype.
  87.     char** file_match;
  88.     // This will be a NULL-terminated array of strings, each string containing
  89.     // a keyword. To differentiate between the two types of keywords,
  90.     // we’ll terminate the second type of keywords with a pipe (|)
  91.     // character (also known as a vertical bar).
  92.     char** keywords;
  93.     // We let each language specify its own single-line comment pattern.
  94.     char* singleline_comment_start;
  95.     // flags is a bit field that will contain flags for whether to
  96.     // highlight numbers and whether to highlight strings for that
  97.     // filetype.
  98.     char* multiline_comment_start;
  99.     char* multiline_comment_end;
  100.     int flags;
  101. };
  102.  
  103. struct editor_config {
  104.     int cursor_x;
  105.     int cursor_y;
  106.     int render_x;
  107.     int row_offset; // Offset of row displayed.
  108.     int col_offset; // Offset of col displayed.
  109.     int screen_rows; // Number of rows that we can show
  110.     int screen_cols; // Number of cols that we can show
  111.     int num_rows; // Number of rows
  112.     editor_row* row;
  113.     int dirty; // To know if a file has been modified since opening.
  114.     char* file_name;
  115.     char status_msg[80];
  116.     time_t status_msg_time;
  117.     char* copied_char_buffer;
  118.     struct editor_syntax* syntax;
  119. ///    struct termios orig_termios;
  120. } ec;
  121.  
  122. // Having a dynamic buffer will allow us to write only one
  123. // time once the screen is refreshing, instead of doing
  124. // a lot of write's.
  125. struct a_buf {
  126.     char* buf;
  127.     int len;
  128. };
  129.  
  130. enum editor_key {
  131. ///    BACKSPACE = 0x7f, // 127
  132.     BACKSPACE = 0x3e7, // fixed russian letter
  133.     ARROW_LEFT = 0x3e8, // 1000, large value out of the range of a char.
  134.     ARROW_RIGHT,
  135.     ARROW_UP,
  136.     ARROW_DOWN,
  137.     PAGE_UP,
  138.     PAGE_DOWN,
  139.     HOME_KEY,
  140.     END_KEY,
  141.     DEL_KEY
  142. };
  143.  
  144. enum editor_highlight {
  145.     HL_NORMAL = 0,
  146.     HL_SL_COMMENT,
  147.     HL_ML_COMMENT,
  148.     HL_KEYWORD_1,
  149.     HL_KEYWORD_2,
  150.     HL_STRING,
  151.     HL_NUMBER,
  152.     HL_MATCH
  153. };
  154.  
  155. /*** Filetypes ***/
  156.  
  157. char* C_HL_extensions[] = {".c", ".h", ".cpp", ".hpp", ".cc", NULL}; // Array must be terminated with NULL.
  158. char* JAVA_HL_extensions[] = {".java", NULL};
  159. char* PYTHON_HL_extensions[] = {".py", NULL};
  160. char* BASH_HL_extensions[] = {".sh", NULL};
  161. char* JS_HL_extensions[] = {".js", ".jsx", NULL};
  162. char* PHP_HL_extensions[] = {".php", NULL};
  163. char* JSON_HL_extensions[] = {".json", ".jsonp", NULL};
  164. char* XML_HL_extensions[] = {".xml", NULL};
  165. char* SQL_HL_extensions[] = {".sql", NULL};
  166. char* RUBY_HL_extensions[] = {".rb", NULL};
  167.  
  168. char* C_HL_keywords[] = {
  169.     "switch", "if", "while", "for", "break", "continue", "return", "else",
  170.     "struct", "union", "typedef", "static", "enum", "class", "case", "#include",
  171.     "volatile", "register", "sizeof", "typedef", "union", "goto", "const", "auto",
  172.     "#define", "#if", "#endif", "#error", "#ifdef", "#ifndef", "#undef",
  173.  
  174.     "int|", "long|", "double|", "float|", "char|", "unsigned|", "signed|",
  175.     "void|", "bool|", NULL
  176. };
  177.  
  178. char* JAVA_HL_keywords[] = {
  179.     "switch", "if", "while", "for", "break", "continue", "return", "else",
  180.     "in", "public", "private", "protected", "static", "final", "abstract",
  181.     "enum", "class", "case", "try", "catch", "do", "extends", "implements",
  182.     "finally", "import", "instanceof", "interface", "new", "package", "super",
  183.     "native", "strictfp",
  184.     "synchronized", "this", "throw", "throws", "transient", "volatile",
  185.  
  186.     "byte|", "char|", "double|", "float|", "int|", "long|", "short|",
  187.     "boolean|", NULL
  188. };
  189.  
  190. char* PYTHON_HL_keywords[] = {
  191.     "and", "as", "assert", "break", "class", "continue", "def", "del", "elif",
  192.     "else", "except", "exec", "finally", "for", "from", "global", "if", "import",
  193.     "in", "is", "lambda", "not", "or", "pass", "print", "raise", "return", "try",
  194.     "while", "with", "yield",
  195.  
  196.     "buffer|", "bytearray|", "complex|", "False|", "float|", "frozenset|", "int|",
  197.     "list|", "long|", "None|", "set|", "str|", "tuple|", "True|", "type|",
  198.     "unicode|", "xrange|", NULL
  199. };
  200.  
  201. char* BASH_HL_keywords[] = {
  202.     "case", "do", "done", "elif", "else", "esac", "fi", "for", "function", "if",
  203.     "in", "select", "then", "time", "until", "while", "alias", "bg", "bind", "break",
  204.     "builtin", "cd", "command", "continue", "declare", "dirs", "disown", "echo",
  205.     "enable", "eval", "exec", "exit", "export", "fc", "fg", "getopts", "hash", "help",
  206.     "history", "jobs", "kill", "let", "local", "logout", "popd", "pushd", "pwd", "read",
  207.     "readonly", "return", "set", "shift", "suspend", "test", "times", "trap", "type",
  208.     "typeset", "ulimit", "umask", "unalias", "unset", "wait", "printf", NULL
  209. };
  210.  
  211. char* JS_HL_keywords[] = {
  212.     "break", "case", "catch", "class", "const", "continue", "debugger", "default",
  213.     "delete", "do", "else", "enum", "export", "extends", "finally", "for", "function",
  214.     "if", "implements", "import", "in", "instanceof", "interface", "let", "new",
  215.     "package", "private", "protected", "public", "return", "static", "super", "switch",
  216.     "this", "throw", "try", "typeof", "var", "void", "while", "with", "yield", "true",
  217.     "false", "null", "NaN", "global", "window", "prototype", "constructor", "document",
  218.     "isNaN", "arguments", "undefined",
  219.  
  220.     "Infinity|", "Array|", "Object|", "Number|", "String|", "Boolean|", "Function|",
  221.     "ArrayBuffer|", "DataView|", "Float32Array|", "Float64Array|", "Int8Array|",
  222.     "Int16Array|", "Int32Array|", "Uint8Array|", "Uint8ClampedArray|", "Uint32Array|",
  223.     "Date|", "Error|", "Map|", "RegExp|", "Symbol|", "WeakMap|", "WeakSet|", "Set|", NULL
  224. };
  225.  
  226. char* PHP_HL_keywords[] = {
  227.     "__halt_compiler", "break", "clone", "die", "empty", "endswitch", "final", "global",
  228.     "include_once", "list", "private", "return", "try", "xor", "abstract", "callable",
  229.     "const", "do", "enddeclare", "endwhile", "finally", "goto", "instanceof", "namespace",
  230.     "protected", "static", "unset", "yield", "and", "case", "continue", "echo", "endfor",
  231.     "eval", "for", "if", "insteadof", "new", "public", "switch", "use", "array", "catch",
  232.     "declare", "else", "endforeach", "exit", "foreach", "implements", "interface", "or",
  233.     "require", "throw", "var", "as", "class", "default", "elseif", "endif", "extends",
  234.     "function", "include", "isset", "print", "require_once", "trait", "while", NULL
  235. };
  236.  
  237. char* JSON_HL_keywords[] = {
  238.     NULL
  239. };
  240.  
  241. char* XML_HL_keywords[] = {
  242.     NULL
  243. };
  244.  
  245. char* SQL_HL_keywords[] = {
  246.     "SELECT", "FROM", "DROP", "CREATE", "TABLE", "DEFAULT", "FOREIGN", "UPDATE", "LOCK",
  247.     "INSERT", "INTO", "VALUES", "LOCK", "UNLOCK", "WHERE", "DINSTINCT", "BETWEEN", "NOT",
  248.     "NULL", "TO", "ON", "ORDER", "GROUP", "IF", "BY", "HAVING", "USING", "UNION", "UNIQUE",
  249.     "AUTO_INCREMENT", "LIKE", "WITH", "INNER", "OUTER", "JOIN", "COLUMN", "DATABASE", "EXISTS",
  250.     "NATURAL", "LIMIT", "UNSIGNED", "MAX", "MIN", "PRECISION", "ALTER", "DELETE", "CASCADE",
  251.     "PRIMARY", "KEY", "CONSTRAINT", "ENGINE", "CHARSET", "REFERENCES", "WRITE",
  252.  
  253.     "BIT|", "TINYINT|", "BOOL|", "BOOLEAN|", "SMALLINT|", "MEDIUMINT|", "INT|", "INTEGER|",
  254.     "BIGINT|", "DOUBLE|", "DECIMAL|", "DEC|" "FLOAT|", "DATE|", "DATETIME|", "TIMESTAMP|",
  255.     "TIME|", "YEAR|", "CHAR|", "VARCHAR|", "TEXT|", "ENUM|", "SET|", "BLOB|", "VARBINARY|",
  256.     "TINYBLOB|", "TINYTEXT|", "MEDIUMBLOB|", "MEDIUMTEXT|", "LONGTEXT|",
  257.  
  258.     "select", "from", "drop", "create", "table", "default", "foreign", "update", "lock",
  259.     "insert", "into", "values", "lock", "unlock", "where", "dinstinct", "between", "not",
  260.     "null", "to", "on", "order", "group", "if", "by", "having", "using", "union", "unique",
  261.     "auto_increment", "like", "with", "inner", "outer", "join", "column", "database", "exists",
  262.     "natural", "limit", "unsigned", "max", "min", "precision", "alter", "delete", "cascade",
  263.     "primary", "key", "constraint", "engine", "charset", "references", "write",
  264.  
  265.     "bit|", "tinyint|", "bool|", "boolean|", "smallint|", "mediumint|", "int|", "integer|",
  266.     "bigint|", "double|", "decimal|", "dec|" "float|", "date|", "datetime|", "timestamp|",
  267.     "time|", "year|", "char|", "varchar|", "text|", "enum|", "set|", "blob|", "varbinary|",
  268.     "tinyblob|", "tinytext|", "mediumblob|", "mediumtext|", "longtext|", NULL
  269. };
  270.  
  271. char* RUBY_HL_keywords[] = {
  272.     "__ENCODING__", "__LINE__", "__FILE__", "BEGIN", "END", "alias", "and", "begin", "break",
  273.     "case", "class", "def", "defined?", "do", "else", "elsif", "end", "ensure", "for", "if",
  274.     "in", "module", "next", "not", "or", "redo", "rescue", "retry", "return", "self", "super",
  275.     "then", "undef", "unless", "until", "when", "while", "yield", NULL
  276. };
  277.  
  278. struct editor_syntax HL_DB[] = {
  279.     {
  280.         "c",
  281.         C_HL_extensions,
  282.         C_HL_keywords,
  283.         "//",
  284.         "/*",
  285.         "*/",
  286.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  287.     },
  288.     {
  289.         "java",
  290.         JAVA_HL_extensions,
  291.         JAVA_HL_keywords,
  292.         "//",
  293.         "/*",
  294.         "*/",
  295.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  296.     },
  297.     {
  298.         "python",
  299.         PYTHON_HL_extensions,
  300.         PYTHON_HL_keywords,
  301.         "#",
  302.         "'''",
  303.         "'''",
  304.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  305.     },
  306.     {
  307.         "bash",
  308.         BASH_HL_extensions,
  309.         BASH_HL_keywords,
  310.         "#",
  311.         NULL,
  312.         NULL,
  313.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  314.     },
  315.     {
  316.         "js",
  317.         JS_HL_extensions,
  318.         JS_HL_keywords,
  319.         "//",
  320.         "/*",
  321.         "*/",
  322.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  323.     },
  324.     {
  325.         "php",
  326.         PHP_HL_extensions,
  327.         PHP_HL_keywords,
  328.         "//",
  329.         "/*",
  330.         "*/",
  331.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  332.     },
  333.     {
  334.         "json",
  335.         JSON_HL_extensions,
  336.         JSON_HL_keywords,
  337.         NULL,
  338.         NULL,
  339.         NULL,
  340.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  341.     },
  342.     {
  343.         "xml",
  344.         XML_HL_extensions,
  345.         XML_HL_keywords,
  346.         NULL,
  347.         NULL,
  348.         NULL,
  349.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  350.     },
  351.     {
  352.         "sql",
  353.         SQL_HL_extensions,
  354.         SQL_HL_keywords,
  355.         "--",
  356.         "/*",
  357.         "*/",
  358.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  359.     },
  360.     {
  361.         "ruby",
  362.         RUBY_HL_extensions,
  363.         RUBY_HL_keywords,
  364.         "#",
  365.         "=begin",
  366.         "=end",
  367.         HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
  368.     }
  369. };
  370.  
  371. // Size of the "Hightlight Database" (HL_DB).
  372. #define HL_DB_ENTRIES (sizeof(HL_DB) / sizeof(HL_DB[0]))
  373.  
  374. /*** Declarations section ***/
  375.  
  376. void editorClearScreen();
  377.  
  378. void editorRefreshScreen();
  379.  
  380. void editorSetStatusMessage(const char* msg, ...);
  381.  
  382. void consoleBufferOpen();
  383.  
  384. void abufFree();
  385.  
  386. void abufAppend();
  387.  
  388. char *editorPrompt(char* prompt, void (*callback)(char*, int));
  389.  
  390. void editorRowAppendString(editor_row* row, char* s, size_t len);
  391.  
  392. void editorInsertNewline();
  393.  
  394. /*** Terminal section ***/
  395.  
  396. void die(const char* s) {
  397.     editorClearScreen();
  398.     // perror looks for global errno variable and then prints
  399.     // a descriptive error mesage for it.
  400.     perror(s);
  401.     printf("\r\n");
  402.     exit(1);
  403. }
  404.  
  405. void disableRawMode() {
  406. ///    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &ec.orig_termios) == -1)
  407. ///        die("Failed to disable raw mode");
  408. }
  409.  
  410. void enableRawMode() {
  411.     // Save original terminal state into orig_termios.
  412. ///    if (tcgetattr(STDIN_FILENO, &ec.orig_termios) == -1)
  413. ///        die("Failed to get current terminal state");
  414.     // At exit, restore the original state.
  415. ///    atexit(disableRawMode);
  416. ///
  417. /*
  418.     // Modify the original state to enter in raw mode.
  419.     struct termios raw = ec.orig_termios;
  420.     // This disables Ctrl-M, Ctrl-S and Ctrl-Q commands.
  421.     // (BRKINT, INPCK and ISTRIP are not estrictly mandatory,
  422.     // but it is recommended to turn them off in case any
  423.     // system needs it).
  424.     raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  425.     // Turning off all output processing (\r\n).
  426.     raw.c_oflag &= ~(OPOST);
  427.     // Setting character size to 8 bits per byte (it should be
  428.     // like that on most systems, but whatever).
  429.     raw.c_cflag |= (CS8);
  430.     // Using NOT operator on ECHO | ICANON | IEXTEN | ISIG and
  431.     // then bitwise-AND them with flags field in order to
  432.     // force c_lflag 4th bit to become 0. This disables
  433.     // chars being printed (ECHO) and let us turn off
  434.     // canonical mode in order to read input byte-by-byte
  435.     // instead of line-by-line (ICANON), ISIG disables
  436.     // Ctrl-C command and IEXTEN the Ctrl-V one.
  437.     raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
  438.     // read() function now returns as soon as there is any
  439.     // input to be read.
  440.     raw.c_cc[VMIN] = 0;
  441.     // Forcing read() function to return every 1/10 of a
  442.     // second if there is nothing to read.
  443.     raw.c_cc[VTIME] = 1;
  444. */
  445.     consoleBufferOpen();
  446.  
  447. ///    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
  448. ///        die("Failed to set raw mode");
  449. }
  450.  
  451.  
  452. /*
  453. int editorReadKey() {
  454.     int nread;
  455.     char c;
  456.     while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
  457.         // Ignoring EAGAIN to make it work on Cygwin.
  458.         if (nread == -1 && errno != EAGAIN)
  459.             die("Error reading input");
  460.     }
  461.  
  462.     // Check escape sequences, if first byte
  463.     // is an escape character then...
  464.     if (c == '\x1b') {
  465.         char seq[3];
  466.  
  467.         if (read(STDIN_FILENO, &seq[0], 1) != 1 ||
  468.             read(STDIN_FILENO, &seq[1], 1) != 1)
  469.             return '\x1b';
  470.  
  471.         if (seq[0] == '[') {
  472.             if (seq[1] >= '0' && seq[1] <= '9') {
  473.                 if (read(STDIN_FILENO, &seq[2], 1) != 1)
  474.                     return '\x1b';
  475.                 if (seq[2] == '~') {
  476.                     switch (seq[1]) {
  477.                         // Home and End keys may be sent in many ways depending on the OS
  478.                         // \x1b[1~, \x1b[7~, \x1b[4~, \x1b[8~
  479.                         case '1':
  480.                         case '7':
  481.                             return HOME_KEY;
  482.                         case '4':
  483.                         case '8':
  484.                             return END_KEY;
  485.                         // Del key is sent as \x1b[3~
  486.                         case '3':
  487.                             return DEL_KEY;
  488.                         // Page Up and Page Down send '\x1b', '[', '5' or '6' and '~'.
  489.                         case '5': return PAGE_UP;
  490.                         case '6': return PAGE_DOWN;
  491.                     }
  492.                 }
  493.             } else {
  494.                 switch (seq[1]) {
  495.                     // Arrow keys send multiple bytes starting with '\x1b', '[''
  496.                     // and followed by an 'A', 'B', 'C' or 'D' depending on which
  497.                     // arrow is pressed.
  498.                     case 'A': return ARROW_UP;
  499.                     case 'B': return ARROW_DOWN;
  500.                     case 'C': return ARROW_RIGHT;
  501.                     case 'D': return ARROW_LEFT;
  502.                     // Home key can also be sent as \x1b[H
  503.                     case 'H': return HOME_KEY;
  504.                     // End key can also be sent as \x1b[F
  505.                     case 'F': return END_KEY;
  506.                 }
  507.             }
  508.         } else if (seq[0] == 'O') {
  509.             switch (seq[1]) {
  510.                 // Yes, Home key can ALSO be sent as \x1bOH
  511.                 case 'H': return HOME_KEY;
  512.                 // And... End key as \x1bOF
  513.                 case 'F': return END_KEY;
  514.             }
  515.         }
  516.         return '\x1b';
  517.     } else {
  518.         return c;
  519.     }
  520. }
  521. */
  522. /// by Siemargl rewritten, still Ctrl+ combination works only in english locale, so need analyze scancode
  523. int editorReadKey() {
  524.     int nread;
  525.     int key = con_getch2();
  526.     if (key == 0)
  527.                 die("Window closed by X-button");
  528.  
  529.         if (0 != (key & 0xff)) {
  530.                 key &= 0xff;
  531.                 switch (key) {
  532.                         case 27: // ESC
  533.                                 return '\x1b';
  534.  
  535.                         case 13: // ENTER
  536.                                 return '\r';
  537.  
  538.                         case 8: // BACKSPACE
  539.                                 return BACKSPACE;
  540.  
  541.                         case 9: // TAB
  542.                                 return  key;
  543.                                
  544.                         case 22: // Ctrl+V
  545.                                 return CTRL_KEY('v');
  546.                                
  547.                         case 3: // Ctrl+C
  548.                                 return CTRL_KEY('c');
  549.                                
  550.                         case 12: // Ctrl+L
  551.                                 return CTRL_KEY('l');
  552.  
  553.                         case 17: // Ctrl+Q
  554.                                 return CTRL_KEY('q');
  555.  
  556.                         case 19: // Ctrl+S
  557.                                 return CTRL_KEY('s');
  558.  
  559.                         case 5: // Ctrl+E
  560.                                 return CTRL_KEY('e');
  561.  
  562.                         case 4: // Ctrl+D
  563.                                 return CTRL_KEY('d');
  564.  
  565.                         case 6: // Ctrl+F
  566.                                 return CTRL_KEY('f');
  567.  
  568.                         case 8: // Ctrl+H
  569.                                 return CTRL_KEY('h');
  570.  
  571.                         case 24: // Ctrl+X
  572.                                 return CTRL_KEY('x');
  573.                                
  574.                         default:
  575.                                 return  key;
  576.  
  577.                 }
  578.         } else {
  579.                 key = (key >> 8) & 0xff;
  580.                 switch (key) {
  581.                         case 83: // Del
  582.                                 return DEL_KEY;
  583.  
  584.                         case 75: // Left
  585.                                 return ARROW_LEFT;
  586.                                
  587.                         case 77: // Right
  588.                                 return ARROW_RIGHT;
  589.  
  590.                         case 72: // Up
  591.                                 return ARROW_UP;
  592.  
  593.                         case 80: // Down
  594.                                 return ARROW_DOWN;
  595.  
  596.                         case 81: // PgDn
  597.                                 return PAGE_DOWN;
  598.                                
  599.                         case 73: // PgUp
  600.                                 return PAGE_UP;
  601.                                
  602.                         case 71: // Home
  603.                                 return HOME_KEY;
  604.                                
  605.                         case 79: // End
  606.                                 return END_KEY;
  607.                                
  608.                         default:
  609.                                 return  0;
  610.                 }
  611.         }
  612.         return  0;
  613. }
  614.  
  615.  
  616.  
  617.  
  618.  
  619. int getWindowSize(int* screen_rows, int* screen_cols) {
  620. ///    struct winsize ws;
  621.  
  622.     // Getting window size thanks to ioctl into the given
  623.     // winsize struct.
  624.     /*
  625.     if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
  626.         return -1;
  627.     } else {
  628.         *screen_cols = ws.ws_col;
  629.         *screen_rows = ws.ws_row;
  630.         return 0;
  631.     }
  632.     */
  633.     *screen_cols = con_def_wnd_width;
  634.     *screen_rows = con_def_wnd_height;
  635. }
  636.  
  637. void editorUpdateWindowSize() {
  638.     if (getWindowSize(&ec.screen_rows, &ec.screen_cols) == -1)
  639.         die("Failed to get window size");
  640.     ec.screen_rows -= 2; // Room for the status bar.
  641. }
  642.  
  643. void editorHandleSigwinch() {
  644.     editorUpdateWindowSize();
  645.     if (ec.cursor_y > ec.screen_rows)
  646.         ec.cursor_y = ec.screen_rows - 1;
  647.     if (ec.cursor_x > ec.screen_cols)
  648.         ec.cursor_x = ec.screen_cols - 1;
  649.     editorRefreshScreen();
  650. }
  651.  
  652. void editorHandleSigcont() {
  653.     disableRawMode();
  654.     consoleBufferOpen();
  655.     enableRawMode();
  656.     editorRefreshScreen();
  657. }
  658.  
  659. void consoleBufferOpen() {
  660.     // Switch to another terminal buffer in order to be able to restore state at exit
  661.     // by calling consoleBufferClose().
  662. ///    if (write(STDOUT_FILENO, "\x1b[?47h", 6) == -1)
  663. ///        die("Error changing terminal buffer");
  664. }
  665.  
  666. void consoleBufferClose() {
  667.     // Restore console to the state tte opened.
  668. ///    if (write(STDOUT_FILENO, "\x1b[?9l", 5) == -1 ||
  669. ///        write(STDOUT_FILENO, "\x1b[?47l", 6) == -1)
  670. ///        die("Error restoring buffer state");
  671.  
  672.     /*struct a_buf ab = {.buf = NULL, .len = 0};
  673.     char* buf = NULL;
  674.     if (asprintf(&buf, "\x1b[%d;%dH\r\n", ec.screen_rows + 1, 1) == -1)
  675.         die("Error restoring buffer state");
  676.     abufAppend(&ab, buf, strlen(buf));
  677.     free(buf);
  678.  
  679.     if (write(STDOUT_FILENO, ab.buf, ab.len) == -1)
  680.         die("Error restoring buffer state");
  681.     abufFree(&ab);*/
  682.  
  683.     editorClearScreen();
  684. }
  685.  
  686. /*** Syntax highlighting ***/
  687.  
  688. int isSeparator(int c) {
  689.     // strchr() looks to see if any one of the characters in the first string
  690.     // appear in the second string. If so, it returns a pointer to the
  691.     // character in the second string that matched. Otherwise, it
  692.     // returns NULL.
  693.     return isspace(c) || c == '\0' || strchr(",.()+-/*=~%<>[]:;", c) != NULL;
  694. }
  695.  
  696. int isAlsoNumber(int c) {
  697.     return c == '.' || c == 'x' || c == 'a' || c == 'b' || c == 'c' || c == 'd' || c == 'e' || c == 'f';
  698. }
  699.  
  700. void editorUpdateSyntax(editor_row* row) {
  701.     row -> highlight = realloc(row -> highlight, row -> render_size);
  702.     // void * memset ( void * ptr, int value, size_t num );
  703.     // Sets the first num bytes of the block of memory pointed by ptr to
  704.     // the specified value. With this we set all characters to HL_NORMAL.
  705.     memset(row -> highlight, HL_NORMAL, row -> render_size);
  706.  
  707.     if (ec.syntax == NULL)
  708.         return;
  709.  
  710.     char** keywords = ec.syntax -> keywords;
  711.  
  712.     char* scs = ec.syntax -> singleline_comment_start;
  713.     char* mcs = ec.syntax -> multiline_comment_start;
  714.     char* mce = ec.syntax -> multiline_comment_end;
  715.  
  716.     int scs_len = scs ? strlen(scs) : 0;
  717.     int mcs_len = mcs ? strlen(mcs) : 0;
  718.     int mce_len = mce ? strlen(mce) : 0;
  719.  
  720.     int prev_sep = 1; // True (1) if the previous char is a separator, false otherwise.
  721.     int in_string = 0; // If != 0, inside a string. We also keep track if it's ' or "
  722.     int in_comment = (row -> idx > 0 && ec.row[row -> idx - 1].hl_open_comment); // This is ONLY used on ML comments.
  723.  
  724.     int i = 0;
  725.     while (i < row -> render_size) {
  726.         char c = row -> render[i];
  727.         // Highlight type of the previous character.
  728.         unsigned char prev_highlight = (i > 0) ? row -> highlight[i - 1] : HL_NORMAL;
  729.  
  730.         if (scs_len && !in_string && !in_comment) {
  731.             // int strncmp ( const char * str1, const char * str2, size_t num );
  732.             // Compares up to num characters of the C string str1 to those of the C string str2.
  733.             // This function starts comparing the first character of each string. If they are
  734.             // equal to each other, it continues with the following pairs until the characters
  735.             // differ, until a terminating null-character is reached, or until num characters
  736.             // match in both strings, whichever happens first.
  737.             if (!strncmp(&row -> render[i], scs, scs_len)) {
  738.                 memset(&row -> highlight[i], HL_SL_COMMENT, row -> render_size - i);
  739.                 break;
  740.             }
  741.         }
  742.  
  743.         if (mcs_len && mce_len && !in_string) {
  744.             if (in_comment) {
  745.                 row -> highlight[i] = HL_ML_COMMENT;
  746.                 if (!strncmp(&row -> render[i], mce, mce_len)) {
  747.                     memset(&row -> highlight[i], HL_ML_COMMENT, mce_len);
  748.                     i += mce_len;
  749.                     in_comment = 0;
  750.                     prev_sep = 1;
  751.                     continue;
  752.                 } else {
  753.                     i++;
  754.                     continue;
  755.                 }
  756.             } else if (!strncmp(&row -> render[i], mcs, mcs_len)) {
  757.                 memset(&row -> highlight[i], HL_ML_COMMENT, mcs_len);
  758.                 i += mcs_len;
  759.                 in_comment = 1;
  760.                 continue;
  761.             }
  762.  
  763.         }
  764.  
  765.         if (ec.syntax -> flags & HL_HIGHLIGHT_STRINGS) {
  766.             if (in_string) {
  767.                 row -> highlight[i] = HL_STRING;
  768.                 // If we’re in a string and the current character is a backslash (\),
  769.                 // and there’s at least one more character in that line that comes
  770.                 // after the backslash, then we highlight the character that comes
  771.                 // after the backslash with HL_STRING and consume it. We increment
  772.                 // i by 2 to consume both characters at once.
  773.                 if (c == '\\' && i + 1 < row -> render_size) {
  774.                     row -> highlight[i + 1] = HL_STRING;
  775.                     i += 2;
  776.                     continue;
  777.                 }
  778.  
  779.                 if (c == in_string)
  780.                     in_string = 0;
  781.                 i++;
  782.                 prev_sep = 1;
  783.                 continue;
  784.             } else {
  785.                 if (c == '"' || c == '\'') {
  786.                     in_string = c;
  787.                     row -> highlight[i] = HL_STRING;
  788.                     i++;
  789.                     continue;
  790.                 }
  791.             }
  792.         }
  793.  
  794.         if (ec.syntax -> flags & HL_HIGHLIGHT_NUMBERS) {
  795.             if ((isdigit(c) && (prev_sep || prev_highlight == HL_NUMBER)) ||
  796.                 (isAlsoNumber(c) && prev_highlight == HL_NUMBER)) {
  797.                 row -> highlight[i] = HL_NUMBER;
  798.                 i++;
  799.                 prev_sep = 0;
  800.                 continue;
  801.             }
  802.         }
  803.  
  804.         if (prev_sep) {
  805.             int j;
  806.             for (j = 0; keywords[j]; j++) {
  807.                 int kw_len = strlen(keywords[j]);
  808.                 int kw_2 = keywords[j][kw_len - 1] == '|';
  809.                 if (kw_2)
  810.                     kw_len--;
  811.  
  812.                 // Keywords require a separator both before and after the keyword.
  813.                 if (!strncmp(&row -> render[i], keywords[j], kw_len) &&
  814.                     isSeparator(row -> render[i + kw_len])) {
  815.                     memset(&row -> highlight[i], kw_2 ? HL_KEYWORD_2 : HL_KEYWORD_1, kw_len);
  816.                     i += kw_len;
  817.                     break;
  818.                 }
  819.             }
  820.             if (keywords[j] != NULL) {
  821.                 prev_sep = 0;
  822.                 continue;
  823.             }
  824.         }
  825.  
  826.         prev_sep = isSeparator(c);
  827.         i++;
  828.     }
  829.  
  830.     int changed = (row -> hl_open_comment != in_comment);
  831.     // This tells us whether the row ended as an unclosed multi-line
  832.     // comment or not.
  833.     row -> hl_open_comment = in_comment;
  834.     // A user could comment out an entire file just by changing one line.
  835.     // So it seems like we need to update the syntax of all the lines
  836.     // following the current line. However, we know the highlighting
  837.     // of the next line will not change if the value of this line’s
  838.     // // // hl_open_comment did not change. So we check if it changed, and
  839.     // // only call editorUpdateSyntax() on the next line if
  840.     // hl_open_comment changed (and if there is a next line in the file).
  841.     // Because editorUpdateSyntax() keeps calling itself with the next
  842.     // line, the change will continue to propagate to more and more lines
  843.     // until one of them is unchanged, at which point we know that all
  844.     // the lines after that one must be unchanged as well.
  845.     if (changed && row -> idx + 1 < ec.num_rows)
  846.         editorUpdateSyntax(&ec.row[row -> idx + 1]);
  847. }
  848.  
  849. int editorSyntaxToColor(int highlight) {
  850.     // We return ANSI codes for colors.
  851.     // See https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
  852.     // for a list of them.
  853.     switch (highlight) {
  854.         case HL_SL_COMMENT:
  855.         case HL_ML_COMMENT: return 36;
  856.         case HL_KEYWORD_1: return 31;
  857.         case HL_KEYWORD_2: return 32;
  858.         case HL_STRING: return 33;
  859.         case HL_NUMBER: return 35;
  860.         case HL_MATCH: return 34;
  861.         default: return 37;
  862.     }
  863. }
  864.  
  865. void editorSelectSyntaxHighlight() {
  866.     ec.syntax = NULL;
  867.     if (ec.file_name == NULL)
  868.         return;
  869.  
  870.     for (unsigned int j = 0; j < HL_DB_ENTRIES; j++) {
  871.         struct editor_syntax* es = &HL_DB[j];
  872.         unsigned int i = 0;
  873.  
  874.         while (es -> file_match[i]) {
  875.             char* p = strstr(ec.file_name, es -> file_match[i]);
  876.             if (p != NULL) {
  877.                 // Returns a pointer to the first occurrence of str2 in str1,
  878.                 // or a null pointer if str2 is not part of str1.
  879.                 int pat_len = strlen(es -> file_match[i]);
  880.                 if (es -> file_match[i][0] != '.' || p[pat_len] == '\0') {
  881.                     ec.syntax = es;
  882.  
  883.                     int file_row;
  884.                     for (file_row = 0; file_row < ec.num_rows; file_row++) {
  885.                         editorUpdateSyntax(&ec.row[file_row]);
  886.                     }
  887.  
  888.                     return;
  889.                 }
  890.             }
  891.             i++;
  892.         }
  893.     }
  894. }
  895.  
  896. /*** Row operations ***/
  897.  
  898. int editorRowCursorXToRenderX(editor_row* row, int cursor_x) {
  899.     int render_x = 0;
  900.     int j;
  901.     // For each character, if its a tab we use rx % TTE_TAB_STOP
  902.     // to find out how many columns we are to the right of the last
  903.     // tab stop, and then subtract that from TTE_TAB_STOP - 1 to
  904.     // find out how many columns we are to the left of the next tab
  905.     // stop. We add that amount to rx to get just to the left of the
  906.     // next tab stop, and then the unconditional rx++ statement gets
  907.     // us right on the next tab stop. Notice how this works even if
  908.     // we are currently on a tab stop.
  909.     for (j = 0; j < cursor_x; j++) {
  910.         if (row -> chars[j] == '\t')
  911.             render_x += (TTE_TAB_STOP - 1) - (render_x % TTE_TAB_STOP);
  912.         render_x++;
  913.     }
  914.     return render_x;
  915. }
  916.  
  917. int editorRowRenderXToCursorX(editor_row* row, int render_x) {
  918.     int cur_render_x = 0;
  919.     int cursor_x;
  920.     for (cursor_x = 0; cursor_x < row -> size; cursor_x++) {
  921.         if (row -> chars[cursor_x] == '\t')
  922.             cur_render_x += (TTE_TAB_STOP - 1) - (cur_render_x % TTE_TAB_STOP);
  923.         cur_render_x++;
  924.  
  925.         if (cur_render_x > render_x)
  926.             return cursor_x;
  927.     }
  928.     return cursor_x;
  929. }
  930.  
  931. void editorUpdateRow(editor_row* row) {
  932.     // First, we have to loop through the chars of the row
  933.     // and count the tabs in order to know how much memory
  934.     // to allocate for render. The maximum number of characters
  935.     // needed for each tab is 8. row->size already counts 1 for
  936.     // each tab, so we multiply the number of tabs by 7 and add
  937.     // that to row->size to get the maximum amount of memory we'll
  938.     // need for the rendered row.
  939.     int tabs = 0;
  940.     int j;
  941.     for (j = 0; j < row -> size; j++) {
  942.         if (row -> chars[j] == '\t')
  943.             tabs++;
  944.     }
  945.     free(row -> render);
  946.     row -> render = malloc(row -> size + tabs * (TTE_TAB_STOP - 1) + 1);
  947.  
  948.     // After allocating the memory, we check whether the current character
  949.     // is a tab. If it is, we append one space (because each tab must
  950.     // advance the cursor forward at least one column), and then append
  951.     // spaces until we get to a tab stop, which is a column that is
  952.     // divisible by 8
  953.     int idx = 0;
  954.     for (j = 0; j < row -> size; j++) {
  955.         if (row -> chars[j] == '\t') {
  956.             row -> render[idx++] = ' ';
  957.             while (idx % TTE_TAB_STOP != 0)
  958.                 row -> render[idx++] = ' ';
  959.         } else
  960.             row -> render[idx++] = row -> chars[j];
  961.     }
  962.     row -> render[idx] = '\0';
  963.     row -> render_size = idx;
  964.  
  965.     editorUpdateSyntax(row);
  966. }
  967.  
  968. void editorInsertRow(int at, char* s, size_t line_len) {
  969.     if (at < 0 || at > ec.num_rows)
  970.         return;
  971.  
  972.     ec.row = realloc(ec.row, sizeof(editor_row) * (ec.num_rows + 1));
  973.     memmove(&ec.row[at + 1], &ec.row[at], sizeof(editor_row) * (ec.num_rows - at));
  974.  
  975.     for (int j = at + 1; j <= ec.num_rows; j++) {
  976.         ec.row[j].idx++;
  977.     }
  978.  
  979.     ec.row[at].idx = at;
  980.  
  981.     ec.row[at].size = line_len;
  982.     ec.row[at].chars = malloc(line_len + 1); // We want to add terminator char '\0' at the end
  983.     memcpy(ec.row[at].chars, s, line_len);
  984.     ec.row[at].chars[line_len] = '\0';
  985.  
  986.     ec.row[at].render_size = 0;
  987.     ec.row[at].render = NULL;
  988.     ec.row[at].highlight = NULL;
  989.     ec.row[at].hl_open_comment = 0;
  990.     editorUpdateRow(&ec.row[at]);
  991.  
  992.     ec.num_rows++;
  993.     ec.dirty++;
  994. }
  995.  
  996. void editorFreeRow(editor_row* row) {
  997.     free(row -> render);
  998.     free(row -> chars);
  999.     free(row -> highlight);
  1000. }
  1001.  
  1002. void editorDelRow(int at) {
  1003.     if (at < 0 || at >= ec.num_rows)
  1004.         return;
  1005.     editorFreeRow(&ec.row[at]);
  1006.     memmove(&ec.row[at], &ec.row[at + 1], sizeof(editor_row) * (ec.num_rows - at - 1));
  1007.  
  1008.     for (int j = at; j < ec.num_rows - 1; j++) {
  1009.         ec.row[j].idx--;
  1010.     }
  1011.  
  1012.     ec.num_rows--;
  1013.     ec.dirty++;
  1014. }
  1015.  
  1016. // -1 down, 1 up
  1017. void editorFlipRow(int dir) {
  1018.     editor_row c_row = ec.row[ec.cursor_y];
  1019.     ec.row[ec.cursor_y] = ec.row[ec.cursor_y - dir];
  1020.     ec.row[ec.cursor_y - dir] = c_row;
  1021.  
  1022.     ec.row[ec.cursor_y].idx += dir;
  1023.     ec.row[ec.cursor_y - dir].idx -= dir;
  1024.  
  1025.     int first = (dir == 1) ? ec.cursor_y - 1 : ec.cursor_y;
  1026.     editorUpdateSyntax(&ec.row[first]);
  1027.     editorUpdateSyntax(&ec.row[first] + 1);
  1028.     if (ec.num_rows - ec.cursor_y > 2)
  1029.       editorUpdateSyntax(&ec.row[first] + 2);
  1030.  
  1031.     ec.cursor_y -= dir;
  1032.     ec.dirty++;
  1033. }
  1034.  
  1035. void editorCopy(int cut) {
  1036.     ec.copied_char_buffer = realloc(ec.copied_char_buffer, strlen(ec.row[ec.cursor_y].chars) + 1);
  1037.     strcpy(ec.copied_char_buffer, ec.row[ec.cursor_y].chars);
  1038.     editorSetStatusMessage(cut ? "Content cut" : "Content copied");
  1039. }
  1040.  
  1041. void editorCut() {
  1042.     editorCopy(-1);
  1043.     editorDelRow(ec.cursor_y);
  1044.     if (ec.num_rows - ec.cursor_y > 0)
  1045.       editorUpdateSyntax(&ec.row[ec.cursor_y]);
  1046.     if (ec.num_rows - ec.cursor_y > 1)
  1047.       editorUpdateSyntax(&ec.row[ec.cursor_y + 1]);
  1048.     ec.cursor_x = ec.cursor_y == ec.num_rows ? 0 : ec.row[ec.cursor_y].size;
  1049. }
  1050.  
  1051. void editorPaste() {
  1052.     if (ec.copied_char_buffer == NULL)
  1053.       return;
  1054.  
  1055.     if (ec.cursor_y == ec.num_rows)
  1056.       editorInsertRow(ec.cursor_y, ec.copied_char_buffer, strlen(ec.copied_char_buffer));
  1057.     else
  1058.       editorRowAppendString(&ec.row[ec.cursor_y], ec.copied_char_buffer, strlen(ec.copied_char_buffer));
  1059.     ec.cursor_x += strlen(ec.copied_char_buffer);
  1060. }
  1061.  
  1062. void editorRowInsertChar(editor_row* row, int at, int c) {
  1063.     if (at < 0 || at > row -> size)
  1064.         at = row -> size;
  1065.     // We need to allocate 2 bytes because we also have to make room for
  1066.     // the null byte.
  1067.     row -> chars = realloc(row -> chars, row -> size + 2);
  1068.     // memmove it's like memcpy(), but is safe to use when the source and
  1069.     // destination arrays overlap
  1070.     memmove(&row -> chars[at + 1], &row -> chars[at], row -> size - at + 1);
  1071.     row -> size++;
  1072.     row -> chars[at] = c;
  1073.     editorUpdateRow(row);
  1074.     ec.dirty++; // This way we can see "how dirty" a file is.
  1075. }
  1076.  
  1077. void editorInsertNewline() {
  1078.     // If we're at the beginning of a line, all we have to do is insert
  1079.     // a new blank row before the line we're on.
  1080.     if (ec.cursor_x == 0) {
  1081.         editorInsertRow(ec.cursor_y, "", 0);
  1082.     // Otherwise, we have to split the line we're on into two rows.
  1083.     } else {
  1084.         editor_row* row = &ec.row[ec.cursor_y];
  1085.         editorInsertRow(ec.cursor_y + 1, &row -> chars[ec.cursor_x], row -> size - ec.cursor_x);
  1086.         row = &ec.row[ec.cursor_y];
  1087.         row -> size = ec.cursor_x;
  1088.         row -> chars[row -> size] = '\0';
  1089.         editorUpdateRow(row);
  1090.     }
  1091.     ec.cursor_y++;
  1092.     ec.cursor_x = 0;
  1093. }
  1094.  
  1095. void editorRowAppendString(editor_row* row, char* s, size_t len) {
  1096.     row -> chars = realloc(row -> chars, row -> size + len + 1);
  1097.     memcpy(&row -> chars[row -> size], s, len);
  1098.     row -> size += len;
  1099.     row -> chars[row -> size] = '\0';
  1100.     editorUpdateRow(row);
  1101.     ec.dirty++;
  1102. }
  1103.  
  1104. void editorRowDelChar(editor_row* row, int at) {
  1105.     if (at < 0 || at >= row -> size)
  1106.         return;
  1107.     // Overwriting the deleted character with the characters that come
  1108.     // after it.
  1109.     memmove(&row -> chars[at], &row -> chars[at + 1], row -> size - at);
  1110.     row -> size--;
  1111.     editorUpdateRow(row);
  1112.     ec.dirty++;
  1113. }
  1114.  
  1115. /*** Editor operations ***/
  1116.  
  1117. void editorInsertChar(int c) {
  1118.     // If this is true, the cursor is on the tilde line after the end of
  1119.     // the file, so we need to append a new row to the file before inserting
  1120.     // a character there.
  1121.     if (ec.cursor_y == ec.num_rows)
  1122.         editorInsertRow(ec.num_rows, "", 0);
  1123.     editorRowInsertChar(&ec.row[ec.cursor_y], ec.cursor_x, c);
  1124.     ec.cursor_x++; // This way we can see "how dirty" a file is.
  1125. }
  1126.  
  1127. void editorDelChar() {
  1128.     // If the cursor is past the end of the file, there's nothing to delete.
  1129.     if (ec.cursor_y == ec.num_rows)
  1130.         return;
  1131.     // Cursor is at the beginning of a file, there's nothing to delete.
  1132.     if (ec.cursor_x == 0 && ec.cursor_y == 0)
  1133.         return;
  1134.  
  1135.     editor_row* row = &ec.row[ec.cursor_y];
  1136.     if (ec.cursor_x > 0) {
  1137.         editorRowDelChar(row, ec.cursor_x - 1);
  1138.         ec.cursor_x--;
  1139.     // Deleting a line and moving up all the content.
  1140.     } else {
  1141.         ec.cursor_x = ec.row[ec.cursor_y - 1].size;
  1142.         editorRowAppendString(&ec.row[ec.cursor_y -1], row -> chars, row -> size);
  1143.         editorDelRow(ec.cursor_y);
  1144.         ec.cursor_y--;
  1145.     }
  1146. }
  1147.  
  1148. /*** File I/O ***/
  1149.  
  1150. char* editorRowsToString(int* buf_len) {
  1151.     int total_len = 0;
  1152.     int j;
  1153.     // Adding up the lengths of each row of text, adding 1
  1154.     // to each one for the newline character we'll add to
  1155.     // the end of each line.
  1156.     for (j = 0; j < ec.num_rows; j++) {
  1157.         total_len += ec.row[j].size + 1;
  1158.     }
  1159.     *buf_len = total_len;
  1160.  
  1161.     char* buf = malloc(total_len);
  1162.     char* p = buf;
  1163.     // Copying the contents of each row to the end of the
  1164.     // buffer, appending a newline character after each
  1165.     // row.
  1166.     for (j = 0; j < ec.num_rows; j++) {
  1167.         memcpy(p, ec.row[j].chars, ec.row[j].size);
  1168.         p += ec.row[j].size;
  1169.         *p = '\n';
  1170.         p++;
  1171.     }
  1172.  
  1173.     return buf;
  1174. }
  1175.  
  1176. extern int getline(char **buf, size_t *bufsiz, FILE *fp);
  1177.  
  1178. void editorOpen(char* file_name) {
  1179.     free(ec.file_name);
  1180.     ec.file_name = strdup(file_name);
  1181.  
  1182.     editorSelectSyntaxHighlight();
  1183.  
  1184.     FILE* file = fopen(file_name, "r+");
  1185.     if (!file)
  1186.         die("Failed to open the file");
  1187.  
  1188.     char* line = NULL;
  1189.     // Unsigned int of at least 16 bit.
  1190.     size_t line_cap = 0;
  1191.     // Bigger than int
  1192.     int line_len;  ///was ssize_t
  1193.     while ((line_len = getline(&line, &line_cap, file)) != -1) {
  1194.         // We already know each row represents one line of text, there's no need
  1195.         // to keep carriage return and newline characters.
  1196.         if (line_len > 0 && (line[line_len - 1] == '\n' || line[line_len - 1] == '\r'))
  1197.             line_len--;
  1198.         editorInsertRow(ec.num_rows, line, line_len);
  1199.     }
  1200.     free(line);
  1201.     fclose(file);
  1202.     ec.dirty = 0;
  1203. }
  1204.  
  1205. void editorSave() {
  1206.     if (ec.file_name == NULL) {
  1207.         ec.file_name = editorPrompt("Save as: %s (ESC to cancel)", NULL);
  1208.         if (ec.file_name == NULL) {
  1209.             editorSetStatusMessage("Save aborted");
  1210.             return;
  1211.         }
  1212.         editorSelectSyntaxHighlight();
  1213.     }
  1214.  
  1215.     int len;
  1216.     char* buf = editorRowsToString(&len);
  1217. ///
  1218. /*
  1219.     // We want to create if it doesn't already exist (O_CREAT flag), giving
  1220.     // 0644 permissions (the standard ones). O_RDWR stands for reading and
  1221.     // writing.
  1222.     int fd = open(ec.file_name, O_RDWR | O_CREAT, 0644);
  1223.     if (fd != -1) {
  1224.         // ftruncate sets the file's size to the specified length.
  1225.         if (ftruncate(fd, len) != -1) {
  1226.             // Writing the file.
  1227.             if (write(fd, buf, len) == len) {
  1228.                 close(fd);
  1229.                 free(buf);
  1230.                 ec.dirty = 0;
  1231.                 editorSetStatusMessage("%d bytes written to disk", len);
  1232.                 return;
  1233.             }
  1234.         }
  1235.         close(fd);
  1236.     }
  1237. */
  1238.         FILE *fd = fopen(ec.file_name,"w+b");
  1239.         if (fd) {
  1240.                 if (fwrite(buf, 1, len, fd) == len) {
  1241.                         fclose(fd);
  1242.                         free(buf);
  1243.                         ec.dirty = 0;
  1244.                         editorSetStatusMessage("%d bytes written to disk", len);
  1245.                         return;
  1246.                 }
  1247.         fclose(fd);
  1248.         }
  1249.  
  1250.     free(buf);
  1251.     editorSetStatusMessage("Cant's save file. Error occurred: %s", strerror(errno));
  1252. }
  1253.  
  1254. /*** Search section ***/
  1255.  
  1256. void editorSearchCallback(char* query, int key) {
  1257.     // Index of the row that the last match was on, -1 if there was
  1258.     // no last match.
  1259.     static int last_match = -1;
  1260.     // 1 for searching forward and -1 for searching backwards.
  1261.     static int direction = 1;
  1262.  
  1263.     static int saved_highlight_line;
  1264.     static char* saved_hightlight = NULL;
  1265.  
  1266.     if (saved_hightlight) {
  1267.         memcpy(ec.row[saved_highlight_line].highlight, saved_hightlight, ec.row[saved_highlight_line].render_size);
  1268.         free(saved_hightlight);
  1269.         saved_hightlight = NULL;
  1270.     }
  1271.  
  1272.     // Checking if the user pressed Enter or Escape, in which case
  1273.     // they are leaving search mode so we return immediately.
  1274.     if (key == '\r' || key == '\x1b') {
  1275.         last_match = -1;
  1276.         direction = 1;
  1277.         return;
  1278.     } else if (key == ARROW_RIGHT || key == ARROW_DOWN) {
  1279.         direction = 1;
  1280.     } else if (key == ARROW_LEFT || key == ARROW_UP) {
  1281.         if (last_match == -1) {
  1282.             // If nothing matched and the left or up arrow key was pressed
  1283.             return;
  1284.         }
  1285.         direction = -1;
  1286.     } else {
  1287.         last_match = -1;
  1288.         direction = 1;
  1289.     }
  1290.  
  1291.     int current = last_match;
  1292.     int i;
  1293.     for (i = 0; i < ec.num_rows; i++) {
  1294.         current += direction;
  1295.         if (current == -1)
  1296.             current = ec.num_rows - 1;
  1297.         else if (current == ec.num_rows)
  1298.             current = 0;
  1299.  
  1300.         editor_row* row = &ec.row[current];
  1301.         // We use strstr to check if query is a substring of the
  1302.         // current row. It returns NULL if there is no match,
  1303.         // oterwhise it returns a pointer to the matching substring.
  1304.         char* match = strstr(row -> render, query);
  1305.         if (match) {
  1306.             last_match = current;
  1307.             ec.cursor_y = current;
  1308.             ec.cursor_x = editorRowRenderXToCursorX(row, match - row -> render);
  1309.             // We set this like so to scroll to the bottom of the file so
  1310.             // that the next screen refresh will cause the matching line to
  1311.             // be at the very top of the screen.
  1312.             ec.row_offset = ec.num_rows;
  1313.  
  1314.             saved_highlight_line = current;
  1315.             saved_hightlight = malloc(row -> render_size);
  1316.             memcpy(saved_hightlight, row -> highlight, row -> render_size);
  1317.             memset(&row -> highlight[match - row -> render], HL_MATCH, strlen(query));
  1318.             break;
  1319.         }
  1320.     }
  1321. }
  1322.  
  1323. void editorSearch() {
  1324.     int saved_cursor_x = ec.cursor_x;
  1325.     int saved_cursor_y = ec.cursor_y;
  1326.     int saved_col_offset = ec.col_offset;
  1327.     int saved_row_offset = ec.row_offset;
  1328.  
  1329.     char* query = editorPrompt("Search: %s (Use ESC / Enter / Arrows)", editorSearchCallback);
  1330.  
  1331.     if (query) {
  1332.         free(query);
  1333.     // If query is NULL, that means they pressed Escape, so in that case we
  1334.     // restore the cursor previous position.
  1335.     } else {
  1336.         ec.cursor_x = saved_cursor_x;
  1337.         ec.cursor_y = saved_cursor_y;
  1338.         ec.col_offset = saved_col_offset;
  1339.         ec.row_offset = saved_row_offset;
  1340.     }
  1341. }
  1342.  
  1343. /*** Append buffer section **/
  1344.  
  1345. void abufAppend(struct a_buf* ab, const char* s, int len) {
  1346.     // Using realloc to get a block of free memory that is
  1347.     // the size of the current string + the size of the string
  1348.     // to be appended.
  1349.     char* new = realloc(ab -> buf, ab -> len + len);
  1350.  
  1351.     if (new == NULL)
  1352.         return;
  1353.  
  1354.     // Copying the string s at the end of the current data in
  1355.     // the buffer.
  1356.     memcpy(&new[ab -> len], s, len);
  1357.     ab -> buf = new;
  1358.     ab -> len += len;
  1359. }
  1360.  
  1361. void abufFree(struct a_buf* ab) {
  1362.     // Deallocating buffer.
  1363.     free(ab -> buf);
  1364. }
  1365.  
  1366. /*** Output section ***/
  1367.  
  1368. void editorScroll() {
  1369.     ec.render_x = 0;
  1370.     if (ec.cursor_y < ec.num_rows)
  1371.         ec.render_x = editorRowCursorXToRenderX(&ec.row[ec.cursor_y], ec.cursor_x);
  1372.     // The first if statement checks if the cursor is above the visible window,
  1373.     // and if so, scrolls up to where the cursor is. The second if statement checks
  1374.     // if the cursor is past the bottom of the visible window, and contains slightly
  1375.     // more complicated arithmetic because ec.row_offset refers to what's at the top
  1376.     // of the screen, and we have to get ec.screen_rows involved to talk about what's
  1377.     // at the bottom of the screen.
  1378.     if (ec.cursor_y < ec.row_offset)
  1379.         ec.row_offset = ec.cursor_y;
  1380.     if (ec.cursor_y >= ec.row_offset + ec.screen_rows)
  1381.         ec.row_offset = ec.cursor_y - ec.screen_rows + 1;
  1382.  
  1383.     if (ec.render_x < ec.col_offset)
  1384.         ec.col_offset = ec.render_x;
  1385.     if (ec.render_x >= ec.col_offset + ec.screen_cols)
  1386.         ec.col_offset = ec.render_x - ec.screen_cols + 1;
  1387. }
  1388.  
  1389. void editorDrawStatusBar(struct a_buf* ab) {
  1390.     // This switches to inverted colors.
  1391.     // NOTE:
  1392.     // The m command (Select Graphic Rendition) causes the text printed
  1393.     // after it to be printed with various possible attributes including
  1394.     // bold (1), underscore (4), blink (5), and inverted colors (7). An
  1395.     // argument of 0 clears all attributes (the default one). See
  1396.     // http://vt100.net/docs/vt100-ug/chapter3.html#SGR for more info.
  1397.     abufAppend(ab, "\x1b[7m", 4);
  1398.  
  1399.     char status[80], r_status[80];
  1400.     // Showing up to 20 characters of the filename, followed by the number of lines.
  1401.     int len = snprintf(status, sizeof(status), " Editing: %.20s %s", ec.file_name ? ec.file_name : "New file", ec.dirty ? "(modified)" : "");
  1402.     int col_size = ec.row && ec.cursor_y <= ec.num_rows - 1 ? col_size = ec.row[ec.cursor_y].size : 0;
  1403.     int r_len = snprintf(r_status, sizeof(r_status), "%d/%d lines  %d/%d cols ", ec.cursor_y + 1 > ec.num_rows ? ec.num_rows : ec.cursor_y + 1, ec.num_rows,
  1404.         ec.cursor_x + 1 > col_size ? col_size : ec.cursor_x + 1, col_size);
  1405.     if (len > ec.screen_cols)
  1406.         len = ec.screen_cols;
  1407.     abufAppend(ab, status, len);
  1408.     while (len < ec.screen_cols) {
  1409.         if (ec.screen_cols - len == r_len) {
  1410.             abufAppend(ab, r_status, r_len);
  1411.             break;
  1412.         } else {
  1413.             abufAppend(ab, " ", 1);
  1414.             len++;
  1415.         }
  1416.     }
  1417.     // This switches back to normal colors.
  1418.     abufAppend(ab, "\x1b[m", 3);
  1419.  
  1420.     abufAppend(ab, "\r\n", 2);
  1421. }
  1422.  
  1423. void editorDrawMessageBar(struct a_buf *ab) {
  1424.     // Clearing the message bar.
  1425.     abufAppend(ab, "\x1b[K", 3);
  1426.     int msg_len = strlen(ec.status_msg);
  1427.     if (msg_len > ec.screen_cols)
  1428.         msg_len = ec.screen_cols;
  1429.     // We only show the message if its less than 5 secons old, but
  1430.     // remember the screen is only being refreshed after each keypress.
  1431.     if (msg_len && time(NULL) - ec.status_msg_time < 5)
  1432.         abufAppend(ab, ec.status_msg, msg_len);
  1433. }
  1434.  
  1435. void editorDrawWelcomeMessage(struct a_buf* ab) {
  1436.     char welcome[80];
  1437.     // Using snprintf to truncate message in case the terminal
  1438.     // is too tiny to handle the entire string.
  1439.     int welcome_len = snprintf(welcome, sizeof(welcome),
  1440.         "tte %s <http://dmoral.es/>", TTE_VERSION);
  1441.     if (welcome_len > ec.screen_cols)
  1442.         welcome_len = ec.screen_cols;
  1443.     // Centering the message.
  1444.     int padding = (ec.screen_cols - welcome_len) / 2;
  1445.     // Remember that everything != 0 is true.
  1446.     if (padding) {
  1447.         abufAppend(ab, "~", 1);
  1448.         padding--;
  1449.     }
  1450.     while (padding--)
  1451.         abufAppend(ab, " ", 1);
  1452.     abufAppend(ab, welcome, welcome_len);
  1453. }
  1454.  
  1455. // The ... argument makes editorSetStatusMessage() a variadic function,
  1456. // meaning it can take any number of arguments. C's way of dealing with
  1457. // these arguments is by having you call va_start() and va_end() on a
  1458. // // value of type va_list. The last argument before the ... (in this
  1459. // case, msg) must be passed to va_start(), so that the address of
  1460. // the next arguments is known. Then, between the va_start() and
  1461. // va_end() calls, you would call va_arg() and pass it the type of
  1462. // the next argument (which you usually get from the given format
  1463. // string) and it would return the value of that argument. In
  1464. // this case, we pass msg and args to vsnprintf() and it takes care
  1465. // of reading the format string and calling va_arg() to get each
  1466. // argument.
  1467. void editorSetStatusMessage(const char* msg, ...) {
  1468.     va_list args;
  1469.     va_start(args, msg);
  1470.     vsnprintf(ec.status_msg, sizeof(ec.status_msg), msg, args);
  1471.     va_end(args);
  1472.     ec.status_msg_time = time(NULL);
  1473. }
  1474.  
  1475. void editorDrawRows(struct a_buf* ab) {
  1476.     int y;
  1477.     for (y = 0; y < ec.screen_rows; y++) {
  1478.         int file_row = y + ec.row_offset;
  1479.         if(file_row >= ec.num_rows) {
  1480.             if (ec.num_rows == 0 && y == ec.screen_rows / 3)
  1481.                 editorDrawWelcomeMessage(ab);
  1482.             else
  1483.                 abufAppend(ab, "~", 1);
  1484.         } else {
  1485.             int len = ec.row[file_row].render_size - ec.col_offset;
  1486.             // len can be a negative number, meaning the user scrolled
  1487.             // horizontally past the end of the line. In that case, we set
  1488.             // len to 0 so that nothing is displayed on that line.
  1489.             if (len < 0)
  1490.                 len = 0;
  1491.             if (len > ec.screen_cols)
  1492.                 len = ec.screen_cols;
  1493.  
  1494.             char* c = &ec.row[file_row].render[ec.col_offset];
  1495.             unsigned char* highlight = &ec.row[file_row].highlight[ec.col_offset];
  1496.             int current_color = -1;
  1497.             int j;
  1498.             for (j = 0; j < len; j++) {
  1499.                 // Displaying nonprintable characters as (A-Z, @, and ?).
  1500.                 if (iscntrl(c[j])) {
  1501.                     char sym = (c[j] <= 26) ? '@' + c[j] : '?';
  1502.                     abufAppend(ab, "\x1b[7m", 4);
  1503.                     abufAppend(ab, &sym, 1);
  1504.                     abufAppend(ab, "\x1b[m", 3);
  1505.                     if (current_color != -1) {
  1506.                         char buf[16];
  1507.                         int c_len = snprintf(buf, sizeof(buf), "\x1b[%dm", current_color);
  1508.                         abufAppend(ab, buf, c_len);
  1509.                     }
  1510.                 } else if (highlight[j] == HL_NORMAL) {
  1511.                     if (current_color != -1) {
  1512.                         abufAppend(ab, "\x1b[39m", 5);
  1513.                         current_color = -1;
  1514.                     }
  1515.                     abufAppend(ab, &c[j], 1);
  1516.                 } else {
  1517.                     int color = editorSyntaxToColor(highlight[j]);
  1518.                     // We only use escape sequence if the new color is different
  1519.                     // from the last character's color.
  1520.                     if (color != current_color) {
  1521.                         current_color = color;
  1522.                         char buf[16];
  1523.                         int c_len = snprintf(buf, sizeof(buf), "\x1b[%dm", color);
  1524.                         abufAppend(ab, buf, c_len);
  1525.                     }
  1526.  
  1527.                     abufAppend(ab, &c[j], 1);
  1528.                 }
  1529.             }
  1530.             abufAppend(ab, "\x1b[39m", 5);
  1531.         }
  1532.  
  1533.         // Redrawing each line instead of the whole screen.
  1534.         abufAppend(ab, "\x1b[K", 3);
  1535.         // Addind a new line
  1536.         abufAppend(ab, "\r\n", 2);
  1537.     }
  1538. }
  1539.  
  1540. void editorRefreshScreen() {
  1541.     editorScroll();
  1542.  
  1543.     struct a_buf ab = ABUF_INIT;
  1544.  
  1545.     // Hiding the cursor while the screen is refreshing.
  1546.     // See http://vt100.net/docs/vt100-ug/chapter3.html#S3.3.4
  1547.     // for more info.
  1548.     abufAppend(&ab, "\x1b[?25l", 6);
  1549.     abufAppend(&ab, "\x1b[H", 3);
  1550.  
  1551.     editorDrawRows(&ab);
  1552.     editorDrawStatusBar(&ab);
  1553.     editorDrawMessageBar(&ab);
  1554.  
  1555.     // Moving the cursor where it should be.
  1556.     char buf[32];
  1557.     snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (ec.cursor_y - ec.row_offset) + 1, (ec.render_x - ec.col_offset) + 1);
  1558.     abufAppend(&ab, buf, strlen(buf));
  1559.  
  1560.     // Showing again the cursor.
  1561.     abufAppend(&ab, "\x1b[?25h", 6);
  1562.  
  1563.     // Writing all content at once
  1564. ///    write(STDOUT_FILENO, ab.buf, ab.len);
  1565.         con_write_string(ab.buf, ab.len);
  1566.     abufFree(&ab);
  1567. }
  1568.  
  1569. void editorClearScreen() {
  1570.     // Writing 4 bytes out to the terminal:
  1571.     // - (1 byte) \x1b : escape character
  1572.     // - (3 bytes) [2J : Clears the entire screen, see
  1573.     // http://vt100.net/docs/vt100-ug/chapter3.html#ED
  1574.     // for more info.
  1575. ///    write(STDOUT_FILENO, "\x1b[2J", 4);
  1576.         con_write_string("\x1b[2J", 4);
  1577.  
  1578.     // Writing 3 bytes to reposition the cursor back at
  1579.     // the top-left corner, see
  1580.     // http://vt100.net/docs/vt100-ug/chapter3.html#CUP
  1581.     // for more info.
  1582. ///    write(STDOUT_FILENO, "\x1b[H", 3);
  1583.         con_write_string("\x1b[H", 3);
  1584. }
  1585.  
  1586. /*** Input section ***/
  1587.  
  1588. char* editorPrompt(char* prompt, void (*callback)(char*, int)) {
  1589.     size_t buf_size = 128;
  1590.     char* buf = malloc(buf_size);
  1591.  
  1592.     size_t buf_len = 0;
  1593.     buf[0] = '\0';
  1594.  
  1595.     while (1) {
  1596.         editorSetStatusMessage(prompt, buf);
  1597.         editorRefreshScreen();
  1598.  
  1599.         int c = editorReadKey();
  1600.         if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
  1601.             if (buf_len != 0)
  1602.                 buf[--buf_len] = '\0';
  1603.         } else if (c == '\x1b') {
  1604.             editorSetStatusMessage("");
  1605.             if (callback)
  1606.                 callback(buf, c);
  1607.             free(buf);
  1608.             return NULL;
  1609.         } else if (c == '\r') {
  1610.             if (buf_len != 0) {
  1611.                 editorSetStatusMessage("");
  1612.                 if (callback)
  1613.                     callback(buf, c);
  1614.                 return buf;
  1615.             }
  1616.         } else if (!iscntrl(c) && isprint(c)) {
  1617.             if (buf_len == buf_size - 1) {
  1618.                 buf_size *= 2;
  1619.                 buf = realloc(buf, buf_size);
  1620.             }
  1621.             buf[buf_len++] = c;
  1622.             buf[buf_len] = '\0';
  1623.         }
  1624.  
  1625.         if (callback)
  1626.             callback(buf, c);
  1627.     }
  1628. }
  1629.  
  1630. void editorMoveCursor(int key) {
  1631.     editor_row* row = (ec.cursor_y >= ec.num_rows) ? NULL : &ec.row[ec.cursor_y];
  1632.  
  1633.     switch (key) {
  1634.         case ARROW_LEFT:
  1635.             if (ec.cursor_x != 0)
  1636.                 ec.cursor_x--;
  1637.             // If <- is pressed, move to the end of the previous line
  1638.             else if (ec.cursor_y > 0) {
  1639.                 ec.cursor_y--;
  1640.                 ec.cursor_x = ec.row[ec.cursor_y].size;
  1641.             }
  1642.             break;
  1643.         case ARROW_RIGHT:
  1644.             if (row && ec.cursor_x < row -> size)
  1645.                 ec.cursor_x++;
  1646.             // If -> is pressed, move to the start of the next line
  1647.             else if (row && ec.cursor_x == row -> size) {
  1648.                 ec.cursor_y++;
  1649.                 ec.cursor_x = 0;
  1650.             }
  1651.             break;
  1652.         case ARROW_UP:
  1653.             if (ec.cursor_y != 0)
  1654.                 ec.cursor_y--;
  1655.             break;
  1656.         case ARROW_DOWN:
  1657.             if (ec.cursor_y < ec.num_rows)
  1658.                 ec.cursor_y++;
  1659.             break;
  1660.     }
  1661.  
  1662.     // Move cursor_x if it ends up past the end of the line it's on
  1663.     row = (ec.cursor_y >= ec.num_rows) ? NULL : &ec.row[ec.cursor_y];
  1664.     int row_len = row ? row -> size : 0;
  1665.     if (ec.cursor_x > row_len)
  1666.         ec.cursor_x = row_len;
  1667. }
  1668.  
  1669. void editorProcessKeypress() {
  1670.     static int quit_times = TTE_QUIT_TIMES;
  1671.  
  1672.     int c = editorReadKey();
  1673.  
  1674.     switch (c) {
  1675.         case '\r': // Enter key
  1676.             editorInsertNewline();
  1677.             break;
  1678.         case CTRL_KEY('q'):
  1679.             if (ec.dirty && quit_times > 0) {
  1680.                 editorSetStatusMessage("Warning! File has unsaved changes. Press Ctrl-Q %d more time%s to quit", quit_times, quit_times > 1 ? "s" : "");
  1681.                 quit_times--;
  1682.                 return;
  1683.             }
  1684.             editorClearScreen();
  1685.             consoleBufferClose();
  1686.             exit(0);
  1687.             break;
  1688.         case CTRL_KEY('s'):
  1689.             editorSave();
  1690.             break;
  1691.         case CTRL_KEY('e'):
  1692.             if (ec.cursor_y > 0 && ec.cursor_y <= ec.num_rows - 1)
  1693.                 editorFlipRow(1);
  1694.             break;
  1695.         case CTRL_KEY('d'):
  1696.             if (ec.cursor_y < ec.num_rows - 1)
  1697.             editorFlipRow(-1);
  1698.             break;
  1699.         case CTRL_KEY('x'):
  1700.             if (ec.cursor_y < ec.num_rows)
  1701.             editorCut();
  1702.             break;
  1703.         case CTRL_KEY('c'):
  1704.             if (ec.cursor_y < ec.num_rows)
  1705.             editorCopy(0);
  1706.             break;
  1707.         case CTRL_KEY('v'):
  1708.             editorPaste();
  1709.             break;
  1710. ///        case CTRL_KEY('p'):
  1711. ///            consoleBufferClose();
  1712. ///            kill(0, SIGTSTP);
  1713.         case ARROW_UP:
  1714.         case ARROW_DOWN:
  1715.         case ARROW_LEFT:
  1716.         case ARROW_RIGHT:
  1717.             editorMoveCursor(c);
  1718.             break;
  1719.         case PAGE_UP:
  1720.         case PAGE_DOWN:
  1721.             { // You can't declare variables directly inside a switch statement.
  1722.                 if (c == PAGE_UP)
  1723.                     ec.cursor_y = ec.row_offset;
  1724.                 else if (c == PAGE_DOWN)
  1725.                     ec.cursor_y = ec.row_offset + ec.screen_rows - 1;
  1726.  
  1727.                 int times = ec.screen_rows;
  1728.                 while (times--)
  1729.                     editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
  1730.             }
  1731.             break;
  1732.         case HOME_KEY:
  1733.             ec.cursor_x = 0;
  1734.             break;
  1735.         case END_KEY:
  1736.             if (ec.cursor_y < ec.num_rows)
  1737.                 ec.cursor_x = ec.row[ec.cursor_y].size;
  1738.             break;
  1739.         case CTRL_KEY('f'):
  1740.             editorSearch();
  1741.             break;
  1742.         case BACKSPACE:
  1743.         case CTRL_KEY('h'):
  1744.         case DEL_KEY:
  1745.             if (c == DEL_KEY)
  1746.                 editorMoveCursor(ARROW_RIGHT);
  1747.             editorDelChar();
  1748.             break;
  1749.         case CTRL_KEY('l'):
  1750.         case '\x1b': // Escape key
  1751.             break;
  1752.         default:
  1753.             editorInsertChar(c);
  1754.             break;
  1755.     }
  1756.  
  1757.     quit_times = TTE_QUIT_TIMES;
  1758. }
  1759.  
  1760. /*** Init section ***/
  1761.  
  1762. void initEditor() {
  1763.     ec.cursor_x = 0;
  1764.     ec.cursor_y = 0;
  1765.     ec.render_x = 0;
  1766.     ec.row_offset = 0;
  1767.     ec.col_offset = 0;
  1768.     ec.num_rows = 0;
  1769.     ec.row = NULL;
  1770.     ec.dirty = 0;
  1771.     ec.file_name = NULL;
  1772.     ec.status_msg[0] = '\0';
  1773.     ec.status_msg_time = 0;
  1774.     ec.copied_char_buffer = NULL;
  1775.     ec.syntax = NULL;
  1776.  
  1777.     editorUpdateWindowSize();
  1778.     // The SIGWINCH signal is sent to a process when its controlling
  1779.     // terminal changes its size (a window change).
  1780. ///    signal(SIGWINCH, editorHandleSigwinch);
  1781.     // The SIGCONT signal instructs the operating system to continue
  1782.     // (restart) a process previously paused by the SIGSTOP or SIGTSTP
  1783.     // signal.
  1784. ///    signal(SIGCONT, editorHandleSigcont);
  1785. }
  1786.  
  1787. void printHelp() {
  1788.     printf("Usage: tte [OPTIONS] [FILE]\n\n");
  1789.     printf("\nKEYBINDINGS\n-----------\n\n");
  1790.     printf("Keybinding\t\tAction\n\n");
  1791.     printf("Ctrl-Q    \t\tExit\n");
  1792.     printf("Ctrl-S    \t\tSave\n");
  1793.     printf("Ctrl-F    \t\tSearch. Esc, enter and arrows to interact once searching\n");
  1794.     printf("Ctrl-E    \t\tFlip line upwards\n");
  1795.     printf("Ctrl-D    \t\tFlip line downwards\n");
  1796.     printf("Ctrl-C    \t\tCopy line\n");
  1797.     printf("Ctrl-X    \t\tCut line\n");
  1798.     printf("Ctrl-V    \t\tPaste line\n");
  1799.  ///   printf("Ctrl-P    \t\tPause tte (type \"fg\" to resume)\n");
  1800.  
  1801.     printf("\n\nOPTIONS\n-------\n\n");
  1802.     printf("Option        \t\tAction\n\n");
  1803.     printf("-h | --help   \t\tPrints the help\n");
  1804.     printf("-v | --version\t\tPrints the version of tte\n");
  1805.  
  1806.     printf("\n\nFor now, usage of ISO 8859-1 is recommended.\n");
  1807. }
  1808.  
  1809. // 1 if editor should open, 0 otherwise, -1 if the program should exit
  1810. int handleArgs(int argc, char* argv[]) {
  1811.     if (argc == 1)
  1812.         return 0;
  1813.  
  1814.     if (argc >= 2) {
  1815.         if (strncmp("-h", argv[1], 2) == 0 || strncmp("--help", argv[1], 6) == 0) {
  1816.             printHelp();
  1817.             return -1;
  1818.         } else if(strncmp("-v", argv[1], 2) == 0 || strncmp("--version", argv[1], 9) == 0) {
  1819.             printf("tte - version %s\n", TTE_VERSION);
  1820.             return -1;
  1821.         }
  1822.     }
  1823.  
  1824.     return 1;
  1825. }
  1826.  
  1827. int main(int argc, char* argv[]) {
  1828.         if (con_init_console_dll()) return 1; // init fail
  1829.  
  1830.     initEditor();
  1831.     int arg_response = handleArgs(argc, argv);
  1832.     if (arg_response == 1)
  1833.         editorOpen(argv[1]);
  1834.     else if (arg_response == -1)
  1835.         return 0;
  1836.     enableRawMode();
  1837.  
  1838.     editorSetStatusMessage(" Ctrl-Q to quit | Ctrl-S to save | (tte -h | --help for more info)");
  1839.  
  1840.     while (1) {
  1841.         editorRefreshScreen();
  1842.         editorProcessKeypress();
  1843.     }
  1844.  
  1845.         con_exit(0);
  1846.     return 0;
  1847. }
  1848.