Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
7413 siemargl 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 .
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 
31
#include 
7414 siemargl 32
///#include 
33
///#include 
7413 siemargl 34
#include 
35
#include 
36
#include 
37
#include 
7414 siemargl 38
///#include 
39
///#include 
40
///#include 
7413 siemargl 41
#include 
7414 siemargl 42
///#include 
43
#include 
7413 siemargl 44
/*** Define section ***/
45
 
46
// This mimics the Ctrl + whatever behavior, setting the
47
// 3 upper bits of the character pressed to 0.
7414 siemargl 48
///#define CTRL_KEY(k) ((k) & 0x1f)
49
// Siemargl - set top 4 bits
50
#define CTRL_KEY(k) ((k) | 0xf000)
7413 siemargl 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
 
7414 siemargl 65
// Kolibri defaults
66
int con_def_wnd_width   =    80;
67
int	con_def_wnd_height  =    25;
68
 
69
 
7413 siemargl 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;
7414 siemargl 119
///    struct termios orig_termios;
7413 siemargl 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 {
7414 siemargl 131
///    BACKSPACE = 0x7f, // 127
132
    BACKSPACE = 0x3e7, // fixed russian letter
7413 siemargl 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() {
7414 siemargl 406
///    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &ec.orig_termios) == -1)
407
///        die("Failed to disable raw mode");
7413 siemargl 408
}
409
 
410
void enableRawMode() {
411
    // Save original terminal state into orig_termios.
7414 siemargl 412
///    if (tcgetattr(STDIN_FILENO, &ec.orig_termios) == -1)
413
///        die("Failed to get current terminal state");
7413 siemargl 414
    // At exit, restore the original state.
7414 siemargl 415
///    atexit(disableRawMode);
416
///
417
/*
7413 siemargl 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;
7414 siemargl 444
*/
7413 siemargl 445
    consoleBufferOpen();
446
 
7414 siemargl 447
///    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
448
///        die("Failed to set raw mode");
7413 siemargl 449
}
450
 
7414 siemargl 451
 
452
/*
7413 siemargl 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
}
7414 siemargl 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");
7413 siemargl 528
 
7414 siemargl 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
 
7413 siemargl 619
int getWindowSize(int* screen_rows, int* screen_cols) {
7414 siemargl 620
///    struct winsize ws;
7413 siemargl 621
 
622
    // Getting window size thanks to ioctl into the given
623
    // winsize struct.
7414 siemargl 624
    /*
7413 siemargl 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
    }
7414 siemargl 632
    */
633
    *screen_cols = con_def_wnd_width;
634
    *screen_rows = con_def_wnd_height;
7413 siemargl 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().
7414 siemargl 662
///    if (write(STDOUT_FILENO, "\x1b[?47h", 6) == -1)
663
///        die("Error changing terminal buffer");
7413 siemargl 664
}
665
 
666
void consoleBufferClose() {
667
    // Restore console to the state tte opened.
7414 siemargl 668
///    if (write(STDOUT_FILENO, "\x1b[?9l", 5) == -1 ||
669
///        write(STDOUT_FILENO, "\x1b[?47l", 6) == -1)
670
///        die("Error restoring buffer state");
7413 siemargl 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
 
7414 siemargl 1176
extern int getline(char **buf, size_t *bufsiz, FILE *fp);
1177
 
7413 siemargl 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
7414 siemargl 1192
    int line_len;  ///was ssize_t
7413 siemargl 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);
7414 siemargl 1217
///
1218
/*
7413 siemargl 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
    }
7414 siemargl 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
	}
7413 siemargl 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 ", 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
7414 siemargl 1564
///    write(STDOUT_FILENO, ab.buf, ab.len);
1565
	con_write_string(ab.buf, ab.len);
7413 siemargl 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.
7414 siemargl 1575
///    write(STDOUT_FILENO, "\x1b[2J", 4);
1576
	con_write_string("\x1b[2J", 4);
1577
 
7413 siemargl 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.
7414 siemargl 1582
///    write(STDOUT_FILENO, "\x1b[H", 3);
1583
	con_write_string("\x1b[H", 3);
7413 siemargl 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;
7414 siemargl 1710
///        case CTRL_KEY('p'):
1711
///            consoleBufferClose();
1712
///            kill(0, SIGTSTP);
7413 siemargl 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).
7414 siemargl 1780
///    signal(SIGWINCH, editorHandleSigwinch);
7413 siemargl 1781
    // The SIGCONT signal instructs the operating system to continue
1782
    // (restart) a process previously paused by the SIGSTOP or SIGTSTP
1783
    // signal.
7414 siemargl 1784
///    signal(SIGCONT, editorHandleSigcont);
7413 siemargl 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");
7414 siemargl 1799
 ///   printf("Ctrl-P    \t\tPause tte (type \"fg\" to resume)\n");
7413 siemargl 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[]) {
7414 siemargl 1828
	if (con_init_console_dll()) return 1; // init fail
1829
 
7413 siemargl 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
 
7414 siemargl 1845
	con_exit(0);
7413 siemargl 1846
    return 0;
1847
}