Subversion Repositories Kolibri OS

Rev

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