Subversion Repositories Kolibri OS

Rev

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