Subversion Repositories Kolibri OS

Rev

Rev 8121 | Rev 8225 | 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
 
7413 siemargl 463
 
7414 siemargl 464
/// by Siemargl rewritten, still Ctrl+ combination works only in english locale, so need analyze scancode
465
int editorReadKey() {
466
    int key = con_getch2();
467
    if (key == 0)
7419 siemargl 468
		die("Window closed by moused X-button");
7413 siemargl 469
 
7414 siemargl 470
	if (0 != (key & 0xff)) {
471
		key &= 0xff;
472
		switch (key) {
473
			case 27: // ESC
474
				return '\x1b';
475
 
476
			case 13: // ENTER
477
				return '\r';
478
 
479
			case 8: // BACKSPACE
480
				return BACKSPACE;
481
 
482
			case 9: // TAB
483
				return	key;
484
 
485
			case 22: // Ctrl+V
486
				return CTRL_KEY('v');
487
 
488
			case 3: // Ctrl+C
489
				return CTRL_KEY('c');
490
 
491
			case 12: // Ctrl+L
492
				return CTRL_KEY('l');
493
 
494
			case 17: // Ctrl+Q
7419 siemargl 495
			case 26: // Ctrl+Z
7414 siemargl 496
				return CTRL_KEY('q');
497
 
498
			case 19: // Ctrl+S
499
				return CTRL_KEY('s');
500
 
501
			case 5: // Ctrl+E
502
				return CTRL_KEY('e');
503
 
504
			case 4: // Ctrl+D
505
				return CTRL_KEY('d');
506
 
507
			case 6: // Ctrl+F
508
				return CTRL_KEY('f');
509
 
510
			case 8: // Ctrl+H
511
				return CTRL_KEY('h');
512
 
513
			case 24: // Ctrl+X
514
				return CTRL_KEY('x');
515
 
516
			default:
517
				return	key;
518
 
519
		}
520
	} else {
521
		key = (key >> 8) & 0xff;
522
		switch (key) {
523
			case 83: // Del
524
				return DEL_KEY;
525
 
526
			case 75: // Left
527
				return ARROW_LEFT;
528
 
529
			case 77: // Right
530
				return ARROW_RIGHT;
531
 
532
			case 72: // Up
533
				return ARROW_UP;
534
 
535
			case 80: // Down
536
				return ARROW_DOWN;
537
 
538
			case 81: // PgDn
539
				return PAGE_DOWN;
540
 
541
			case 73: // PgUp
542
				return PAGE_UP;
543
 
544
			case 71: // Home
545
				return HOME_KEY;
546
 
547
			case 79: // End
548
				return END_KEY;
549
 
550
			default:
551
				return	0;
552
		}
553
	}
554
	return	0;
555
}
556
 
557
 
558
 
559
 
560
 
7413 siemargl 561
int getWindowSize(int* screen_rows, int* screen_cols) {
7414 siemargl 562
///    struct winsize ws;
7413 siemargl 563
 
564
    // Getting window size thanks to ioctl into the given
565
    // winsize struct.
7414 siemargl 566
    /*
7413 siemargl 567
    if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
568
        return -1;
569
    } else {
570
        *screen_cols = ws.ws_col;
571
        *screen_rows = ws.ws_row;
572
        return 0;
573
    }
7414 siemargl 574
    */
575
    *screen_cols = con_def_wnd_width;
576
    *screen_rows = con_def_wnd_height;
7413 siemargl 577
}
578
 
579
void editorUpdateWindowSize() {
580
    if (getWindowSize(&ec.screen_rows, &ec.screen_cols) == -1)
581
        die("Failed to get window size");
582
    ec.screen_rows -= 2; // Room for the status bar.
583
}
584
 
585
void editorHandleSigwinch() {
586
    editorUpdateWindowSize();
587
    if (ec.cursor_y > ec.screen_rows)
588
        ec.cursor_y = ec.screen_rows - 1;
589
    if (ec.cursor_x > ec.screen_cols)
590
        ec.cursor_x = ec.screen_cols - 1;
591
    editorRefreshScreen();
592
}
593
 
594
void editorHandleSigcont() {
595
    disableRawMode();
596
    consoleBufferOpen();
597
    enableRawMode();
598
    editorRefreshScreen();
599
}
600
 
601
void consoleBufferOpen() {
602
    // Switch to another terminal buffer in order to be able to restore state at exit
603
    // by calling consoleBufferClose().
7414 siemargl 604
///    if (write(STDOUT_FILENO, "\x1b[?47h", 6) == -1)
605
///        die("Error changing terminal buffer");
7413 siemargl 606
}
607
 
608
void consoleBufferClose() {
609
    // Restore console to the state tte opened.
7414 siemargl 610
///    if (write(STDOUT_FILENO, "\x1b[?9l", 5) == -1 ||
611
///        write(STDOUT_FILENO, "\x1b[?47l", 6) == -1)
612
///        die("Error restoring buffer state");
7413 siemargl 613
 
614
    /*struct a_buf ab = {.buf = NULL, .len = 0};
615
    char* buf = NULL;
616
    if (asprintf(&buf, "\x1b[%d;%dH\r\n", ec.screen_rows + 1, 1) == -1)
617
        die("Error restoring buffer state");
618
    abufAppend(&ab, buf, strlen(buf));
619
    free(buf);
620
 
621
    if (write(STDOUT_FILENO, ab.buf, ab.len) == -1)
622
        die("Error restoring buffer state");
623
    abufFree(&ab);*/
624
 
625
    editorClearScreen();
626
}
627
 
628
/*** Syntax highlighting ***/
629
 
630
int isSeparator(int c) {
631
    // strchr() looks to see if any one of the characters in the first string
632
    // appear in the second string. If so, it returns a pointer to the
633
    // character in the second string that matched. Otherwise, it
634
    // returns NULL.
635
    return isspace(c) || c == '\0' || strchr(",.()+-/*=~%<>[]:;", c) != NULL;
636
}
637
 
638
int isAlsoNumber(int c) {
639
    return c == '.' || c == 'x' || c == 'a' || c == 'b' || c == 'c' || c == 'd' || c == 'e' || c == 'f';
640
}
641
 
642
void editorUpdateSyntax(editor_row* row) {
643
    row -> highlight = realloc(row -> highlight, row -> render_size);
644
    // void * memset ( void * ptr, int value, size_t num );
645
    // Sets the first num bytes of the block of memory pointed by ptr to
646
    // the specified value. With this we set all characters to HL_NORMAL.
647
    memset(row -> highlight, HL_NORMAL, row -> render_size);
648
 
649
    if (ec.syntax == NULL)
650
        return;
651
 
652
    char** keywords = ec.syntax -> keywords;
653
 
654
    char* scs = ec.syntax -> singleline_comment_start;
655
    char* mcs = ec.syntax -> multiline_comment_start;
656
    char* mce = ec.syntax -> multiline_comment_end;
657
 
658
    int scs_len = scs ? strlen(scs) : 0;
659
    int mcs_len = mcs ? strlen(mcs) : 0;
660
    int mce_len = mce ? strlen(mce) : 0;
661
 
662
    int prev_sep = 1; // True (1) if the previous char is a separator, false otherwise.
663
    int in_string = 0; // If != 0, inside a string. We also keep track if it's ' or "
664
    int in_comment = (row -> idx > 0 && ec.row[row -> idx - 1].hl_open_comment); // This is ONLY used on ML comments.
665
 
666
    int i = 0;
667
    while (i < row -> render_size) {
668
        char c = row -> render[i];
669
        // Highlight type of the previous character.
670
        unsigned char prev_highlight = (i > 0) ? row -> highlight[i - 1] : HL_NORMAL;
671
 
672
        if (scs_len && !in_string && !in_comment) {
673
            // int strncmp ( const char * str1, const char * str2, size_t num );
674
            // Compares up to num characters of the C string str1 to those of the C string str2.
675
            // This function starts comparing the first character of each string. If they are
676
            // equal to each other, it continues with the following pairs until the characters
677
            // differ, until a terminating null-character is reached, or until num characters
678
            // match in both strings, whichever happens first.
679
            if (!strncmp(&row -> render[i], scs, scs_len)) {
680
                memset(&row -> highlight[i], HL_SL_COMMENT, row -> render_size - i);
681
                break;
682
            }
683
        }
684
 
685
        if (mcs_len && mce_len && !in_string) {
686
            if (in_comment) {
687
                row -> highlight[i] = HL_ML_COMMENT;
688
                if (!strncmp(&row -> render[i], mce, mce_len)) {
689
                    memset(&row -> highlight[i], HL_ML_COMMENT, mce_len);
690
                    i += mce_len;
691
                    in_comment = 0;
692
                    prev_sep = 1;
693
                    continue;
694
                } else {
695
                    i++;
696
                    continue;
697
                }
698
            } else if (!strncmp(&row -> render[i], mcs, mcs_len)) {
699
                memset(&row -> highlight[i], HL_ML_COMMENT, mcs_len);
700
                i += mcs_len;
701
                in_comment = 1;
702
                continue;
703
            }
704
 
705
        }
706
 
707
        if (ec.syntax -> flags & HL_HIGHLIGHT_STRINGS) {
708
            if (in_string) {
709
                row -> highlight[i] = HL_STRING;
710
                // If we’re in a string and the current character is a backslash (\),
711
                // and there’s at least one more character in that line that comes
712
                // after the backslash, then we highlight the character that comes
713
                // after the backslash with HL_STRING and consume it. We increment
714
                // i by 2 to consume both characters at once.
715
                if (c == '\\' && i + 1 < row -> render_size) {
716
                    row -> highlight[i + 1] = HL_STRING;
717
                    i += 2;
718
                    continue;
719
                }
720
 
721
                if (c == in_string)
722
                    in_string = 0;
723
                i++;
724
                prev_sep = 1;
725
                continue;
726
            } else {
727
                if (c == '"' || c == '\'') {
728
                    in_string = c;
729
                    row -> highlight[i] = HL_STRING;
730
                    i++;
731
                    continue;
732
                }
733
            }
734
        }
735
 
736
        if (ec.syntax -> flags & HL_HIGHLIGHT_NUMBERS) {
737
            if ((isdigit(c) && (prev_sep || prev_highlight == HL_NUMBER)) ||
738
                (isAlsoNumber(c) && prev_highlight == HL_NUMBER)) {
739
                row -> highlight[i] = HL_NUMBER;
740
                i++;
741
                prev_sep = 0;
742
                continue;
743
            }
744
        }
745
 
746
        if (prev_sep) {
747
            int j;
748
            for (j = 0; keywords[j]; j++) {
749
                int kw_len = strlen(keywords[j]);
750
                int kw_2 = keywords[j][kw_len - 1] == '|';
751
                if (kw_2)
752
                    kw_len--;
753
 
754
                // Keywords require a separator both before and after the keyword.
755
                if (!strncmp(&row -> render[i], keywords[j], kw_len) &&
756
                    isSeparator(row -> render[i + kw_len])) {
757
                    memset(&row -> highlight[i], kw_2 ? HL_KEYWORD_2 : HL_KEYWORD_1, kw_len);
758
                    i += kw_len;
759
                    break;
760
                }
761
            }
762
            if (keywords[j] != NULL) {
763
                prev_sep = 0;
764
                continue;
765
            }
766
        }
767
 
768
        prev_sep = isSeparator(c);
769
        i++;
770
    }
771
 
772
    int changed = (row -> hl_open_comment != in_comment);
773
    // This tells us whether the row ended as an unclosed multi-line
774
    // comment or not.
775
    row -> hl_open_comment = in_comment;
776
    // A user could comment out an entire file just by changing one line.
777
    // So it seems like we need to update the syntax of all the lines
778
    // following the current line. However, we know the highlighting
779
    // of the next line will not change if the value of this line’s
780
    // // // hl_open_comment did not change. So we check if it changed, and
781
    // // only call editorUpdateSyntax() on the next line if
782
    // hl_open_comment changed (and if there is a next line in the file).
783
    // Because editorUpdateSyntax() keeps calling itself with the next
784
    // line, the change will continue to propagate to more and more lines
785
    // until one of them is unchanged, at which point we know that all
786
    // the lines after that one must be unchanged as well.
787
    if (changed && row -> idx + 1 < ec.num_rows)
788
        editorUpdateSyntax(&ec.row[row -> idx + 1]);
789
}
790
 
791
int editorSyntaxToColor(int highlight) {
792
    // We return ANSI codes for colors.
793
    // See https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
794
    // for a list of them.
795
    switch (highlight) {
796
        case HL_SL_COMMENT:
797
        case HL_ML_COMMENT: return 36;
798
        case HL_KEYWORD_1: return 31;
799
        case HL_KEYWORD_2: return 32;
800
        case HL_STRING: return 33;
801
        case HL_NUMBER: return 35;
802
        case HL_MATCH: return 34;
803
        default: return 37;
804
    }
805
}
806
 
807
void editorSelectSyntaxHighlight() {
808
    ec.syntax = NULL;
809
    if (ec.file_name == NULL)
810
        return;
811
 
812
    for (unsigned int j = 0; j < HL_DB_ENTRIES; j++) {
813
        struct editor_syntax* es = &HL_DB[j];
814
        unsigned int i = 0;
815
 
816
        while (es -> file_match[i]) {
817
            char* p = strstr(ec.file_name, es -> file_match[i]);
818
            if (p != NULL) {
819
                // Returns a pointer to the first occurrence of str2 in str1,
820
                // or a null pointer if str2 is not part of str1.
821
                int pat_len = strlen(es -> file_match[i]);
822
                if (es -> file_match[i][0] != '.' || p[pat_len] == '\0') {
823
                    ec.syntax = es;
824
 
825
                    int file_row;
826
                    for (file_row = 0; file_row < ec.num_rows; file_row++) {
827
                        editorUpdateSyntax(&ec.row[file_row]);
828
                    }
829
 
830
                    return;
831
                }
832
            }
833
            i++;
834
        }
835
    }
836
}
837
 
838
/*** Row operations ***/
839
 
840
int editorRowCursorXToRenderX(editor_row* row, int cursor_x) {
841
    int render_x = 0;
842
    int j;
843
    // For each character, if its a tab we use rx % TTE_TAB_STOP
844
    // to find out how many columns we are to the right of the last
845
    // tab stop, and then subtract that from TTE_TAB_STOP - 1 to
846
    // find out how many columns we are to the left of the next tab
847
    // stop. We add that amount to rx to get just to the left of the
848
    // next tab stop, and then the unconditional rx++ statement gets
849
    // us right on the next tab stop. Notice how this works even if
850
    // we are currently on a tab stop.
851
    for (j = 0; j < cursor_x; j++) {
852
        if (row -> chars[j] == '\t')
853
            render_x += (TTE_TAB_STOP - 1) - (render_x % TTE_TAB_STOP);
854
        render_x++;
855
    }
856
    return render_x;
857
}
858
 
859
int editorRowRenderXToCursorX(editor_row* row, int render_x) {
860
    int cur_render_x = 0;
861
    int cursor_x;
862
    for (cursor_x = 0; cursor_x < row -> size; cursor_x++) {
863
        if (row -> chars[cursor_x] == '\t')
864
            cur_render_x += (TTE_TAB_STOP - 1) - (cur_render_x % TTE_TAB_STOP);
865
        cur_render_x++;
866
 
867
        if (cur_render_x > render_x)
868
            return cursor_x;
869
    }
870
    return cursor_x;
871
}
872
 
873
void editorUpdateRow(editor_row* row) {
874
    // First, we have to loop through the chars of the row
875
    // and count the tabs in order to know how much memory
876
    // to allocate for render. The maximum number of characters
877
    // needed for each tab is 8. row->size already counts 1 for
878
    // each tab, so we multiply the number of tabs by 7 and add
879
    // that to row->size to get the maximum amount of memory we'll
880
    // need for the rendered row.
881
    int tabs = 0;
882
    int j;
883
    for (j = 0; j < row -> size; j++) {
884
        if (row -> chars[j] == '\t')
885
            tabs++;
886
    }
887
    free(row -> render);
888
    row -> render = malloc(row -> size + tabs * (TTE_TAB_STOP - 1) + 1);
889
 
890
    // After allocating the memory, we check whether the current character
891
    // is a tab. If it is, we append one space (because each tab must
892
    // advance the cursor forward at least one column), and then append
893
    // spaces until we get to a tab stop, which is a column that is
894
    // divisible by 8
895
    int idx = 0;
896
    for (j = 0; j < row -> size; j++) {
897
        if (row -> chars[j] == '\t') {
898
            row -> render[idx++] = ' ';
899
            while (idx % TTE_TAB_STOP != 0)
900
                row -> render[idx++] = ' ';
901
        } else
902
            row -> render[idx++] = row -> chars[j];
903
    }
904
    row -> render[idx] = '\0';
905
    row -> render_size = idx;
906
 
907
    editorUpdateSyntax(row);
908
}
909
 
910
void editorInsertRow(int at, char* s, size_t line_len) {
911
    if (at < 0 || at > ec.num_rows)
912
        return;
913
 
914
    ec.row = realloc(ec.row, sizeof(editor_row) * (ec.num_rows + 1));
915
    memmove(&ec.row[at + 1], &ec.row[at], sizeof(editor_row) * (ec.num_rows - at));
916
 
917
    for (int j = at + 1; j <= ec.num_rows; j++) {
918
        ec.row[j].idx++;
919
    }
920
 
921
    ec.row[at].idx = at;
922
 
923
    ec.row[at].size = line_len;
924
    ec.row[at].chars = malloc(line_len + 1); // We want to add terminator char '\0' at the end
925
    memcpy(ec.row[at].chars, s, line_len);
926
    ec.row[at].chars[line_len] = '\0';
927
 
928
    ec.row[at].render_size = 0;
929
    ec.row[at].render = NULL;
930
    ec.row[at].highlight = NULL;
931
    ec.row[at].hl_open_comment = 0;
932
    editorUpdateRow(&ec.row[at]);
933
 
934
    ec.num_rows++;
935
    ec.dirty++;
936
}
937
 
938
void editorFreeRow(editor_row* row) {
939
    free(row -> render);
940
    free(row -> chars);
941
    free(row -> highlight);
942
}
943
 
944
void editorDelRow(int at) {
945
    if (at < 0 || at >= ec.num_rows)
946
        return;
947
    editorFreeRow(&ec.row[at]);
948
    memmove(&ec.row[at], &ec.row[at + 1], sizeof(editor_row) * (ec.num_rows - at - 1));
949
 
950
    for (int j = at; j < ec.num_rows - 1; j++) {
951
        ec.row[j].idx--;
952
    }
953
 
954
    ec.num_rows--;
955
    ec.dirty++;
956
}
957
 
958
// -1 down, 1 up
959
void editorFlipRow(int dir) {
960
    editor_row c_row = ec.row[ec.cursor_y];
961
    ec.row[ec.cursor_y] = ec.row[ec.cursor_y - dir];
962
    ec.row[ec.cursor_y - dir] = c_row;
963
 
964
    ec.row[ec.cursor_y].idx += dir;
965
    ec.row[ec.cursor_y - dir].idx -= dir;
966
 
967
    int first = (dir == 1) ? ec.cursor_y - 1 : ec.cursor_y;
968
    editorUpdateSyntax(&ec.row[first]);
969
    editorUpdateSyntax(&ec.row[first] + 1);
970
    if (ec.num_rows - ec.cursor_y > 2)
971
      editorUpdateSyntax(&ec.row[first] + 2);
972
 
973
    ec.cursor_y -= dir;
974
    ec.dirty++;
975
}
976
 
977
void editorCopy(int cut) {
978
    ec.copied_char_buffer = realloc(ec.copied_char_buffer, strlen(ec.row[ec.cursor_y].chars) + 1);
979
    strcpy(ec.copied_char_buffer, ec.row[ec.cursor_y].chars);
980
    editorSetStatusMessage(cut ? "Content cut" : "Content copied");
981
}
982
 
983
void editorCut() {
984
    editorCopy(-1);
985
    editorDelRow(ec.cursor_y);
986
    if (ec.num_rows - ec.cursor_y > 0)
987
      editorUpdateSyntax(&ec.row[ec.cursor_y]);
988
    if (ec.num_rows - ec.cursor_y > 1)
989
      editorUpdateSyntax(&ec.row[ec.cursor_y + 1]);
990
    ec.cursor_x = ec.cursor_y == ec.num_rows ? 0 : ec.row[ec.cursor_y].size;
991
}
992
 
993
void editorPaste() {
994
    if (ec.copied_char_buffer == NULL)
995
      return;
996
 
997
    if (ec.cursor_y == ec.num_rows)
998
      editorInsertRow(ec.cursor_y, ec.copied_char_buffer, strlen(ec.copied_char_buffer));
999
    else
1000
      editorRowAppendString(&ec.row[ec.cursor_y], ec.copied_char_buffer, strlen(ec.copied_char_buffer));
1001
    ec.cursor_x += strlen(ec.copied_char_buffer);
1002
}
1003
 
1004
void editorRowInsertChar(editor_row* row, int at, int c) {
1005
    if (at < 0 || at > row -> size)
1006
        at = row -> size;
1007
    // We need to allocate 2 bytes because we also have to make room for
1008
    // the null byte.
1009
    row -> chars = realloc(row -> chars, row -> size + 2);
1010
    // memmove it's like memcpy(), but is safe to use when the source and
1011
    // destination arrays overlap
1012
    memmove(&row -> chars[at + 1], &row -> chars[at], row -> size - at + 1);
1013
    row -> size++;
1014
    row -> chars[at] = c;
1015
    editorUpdateRow(row);
1016
    ec.dirty++; // This way we can see "how dirty" a file is.
1017
}
1018
 
1019
void editorInsertNewline() {
1020
    // If we're at the beginning of a line, all we have to do is insert
1021
    // a new blank row before the line we're on.
1022
    if (ec.cursor_x == 0) {
1023
        editorInsertRow(ec.cursor_y, "", 0);
1024
    // Otherwise, we have to split the line we're on into two rows.
1025
    } else {
1026
        editor_row* row = &ec.row[ec.cursor_y];
1027
        editorInsertRow(ec.cursor_y + 1, &row -> chars[ec.cursor_x], row -> size - ec.cursor_x);
1028
        row = &ec.row[ec.cursor_y];
1029
        row -> size = ec.cursor_x;
1030
        row -> chars[row -> size] = '\0';
1031
        editorUpdateRow(row);
1032
    }
1033
    ec.cursor_y++;
1034
    ec.cursor_x = 0;
1035
}
1036
 
1037
void editorRowAppendString(editor_row* row, char* s, size_t len) {
1038
    row -> chars = realloc(row -> chars, row -> size + len + 1);
1039
    memcpy(&row -> chars[row -> size], s, len);
1040
    row -> size += len;
1041
    row -> chars[row -> size] = '\0';
1042
    editorUpdateRow(row);
1043
    ec.dirty++;
1044
}
1045
 
1046
void editorRowDelChar(editor_row* row, int at) {
1047
    if (at < 0 || at >= row -> size)
1048
        return;
1049
    // Overwriting the deleted character with the characters that come
1050
    // after it.
1051
    memmove(&row -> chars[at], &row -> chars[at + 1], row -> size - at);
1052
    row -> size--;
1053
    editorUpdateRow(row);
1054
    ec.dirty++;
1055
}
1056
 
1057
/*** Editor operations ***/
1058
 
1059
void editorInsertChar(int c) {
1060
    // If this is true, the cursor is on the tilde line after the end of
1061
    // the file, so we need to append a new row to the file before inserting
1062
    // a character there.
1063
    if (ec.cursor_y == ec.num_rows)
1064
        editorInsertRow(ec.num_rows, "", 0);
1065
    editorRowInsertChar(&ec.row[ec.cursor_y], ec.cursor_x, c);
1066
    ec.cursor_x++; // This way we can see "how dirty" a file is.
1067
}
1068
 
1069
void editorDelChar() {
1070
    // If the cursor is past the end of the file, there's nothing to delete.
1071
    if (ec.cursor_y == ec.num_rows)
1072
        return;
1073
    // Cursor is at the beginning of a file, there's nothing to delete.
1074
    if (ec.cursor_x == 0 && ec.cursor_y == 0)
1075
        return;
1076
 
1077
    editor_row* row = &ec.row[ec.cursor_y];
1078
    if (ec.cursor_x > 0) {
1079
        editorRowDelChar(row, ec.cursor_x - 1);
1080
        ec.cursor_x--;
1081
    // Deleting a line and moving up all the content.
1082
    } else {
1083
        ec.cursor_x = ec.row[ec.cursor_y - 1].size;
1084
        editorRowAppendString(&ec.row[ec.cursor_y -1], row -> chars, row -> size);
1085
        editorDelRow(ec.cursor_y);
1086
        ec.cursor_y--;
1087
    }
1088
}
1089
 
1090
/*** File I/O ***/
1091
 
1092
char* editorRowsToString(int* buf_len) {
1093
    int total_len = 0;
1094
    int j;
1095
    // Adding up the lengths of each row of text, adding 1
1096
    // to each one for the newline character we'll add to
1097
    // the end of each line.
1098
    for (j = 0; j < ec.num_rows; j++) {
7419 siemargl 1099
        total_len += ec.row[j].size + 1
1100
							+ (fileIsOd0a ? 1:0); /// winFile suppor
7413 siemargl 1101
    }
1102
    *buf_len = total_len;
1103
 
1104
    char* buf = malloc(total_len);
1105
    char* p = buf;
1106
    // Copying the contents of each row to the end of the
1107
    // buffer, appending a newline character after each
1108
    // row.
1109
    for (j = 0; j < ec.num_rows; j++) {
1110
        memcpy(p, ec.row[j].chars, ec.row[j].size);
1111
        p += ec.row[j].size;
7419 siemargl 1112
        /// winFile support
1113
        if (fileIsOd0a) *p++ = '\r';
7413 siemargl 1114
        *p = '\n';
1115
        p++;
1116
    }
1117
 
1118
    return buf;
1119
}
1120
 
7414 siemargl 1121
extern int getline(char **buf, size_t *bufsiz, FILE *fp);
1122
 
7413 siemargl 1123
void editorOpen(char* file_name) {
1124
    free(ec.file_name);
1125
    ec.file_name = strdup(file_name);
1126
 
1127
    editorSelectSyntaxHighlight();
1128
 
1129
    FILE* file = fopen(file_name, "r+");
1130
    if (!file)
1131
        die("Failed to open the file");
1132
 
1133
    char* line = NULL;
1134
    // Unsigned int of at least 16 bit.
1135
    size_t line_cap = 0;
1136
    // Bigger than int
7414 siemargl 1137
    int line_len;  ///was ssize_t
7419 siemargl 1138
    fileIsOd0a = 0; /// winFile support
1139
 
7413 siemargl 1140
    while ((line_len = getline(&line, &line_cap, file)) != -1) {
1141
        // We already know each row represents one line of text, there's no need
1142
        // to keep carriage return and newline characters.
1143
        if (line_len > 0 && (line[line_len - 1] == '\n' || line[line_len - 1] == '\r'))
1144
            line_len--;
7419 siemargl 1145
/// Siemargl fix 0d0a windows file, save format flag
1146
         if (line_len > 0 && line[line_len - 1] == '\r')
1147
         {
1148
            line_len--;
1149
            fileIsOd0a = 1;
1150
		 }
1151
       editorInsertRow(ec.num_rows, line, line_len);
7413 siemargl 1152
    }
1153
    free(line);
1154
    fclose(file);
1155
    ec.dirty = 0;
1156
}
1157
 
1158
void editorSave() {
1159
    if (ec.file_name == NULL) {
1160
        ec.file_name = editorPrompt("Save as: %s (ESC to cancel)", NULL);
1161
        if (ec.file_name == NULL) {
1162
            editorSetStatusMessage("Save aborted");
1163
            return;
1164
        }
1165
        editorSelectSyntaxHighlight();
1166
    }
1167
 
1168
    int len;
1169
    char* buf = editorRowsToString(&len);
7419 siemargl 1170
 
1171
/// siemargl rewrite using standard FILE stream
7414 siemargl 1172
	FILE *fd = fopen(ec.file_name,"w+b");
7419 siemargl 1173
	int rc = -1;
7414 siemargl 1174
	if (fd) {
7419 siemargl 1175
		if ((rc = fwrite(buf, 1, len, fd)) == len) {
7414 siemargl 1176
			fclose(fd);
1177
			free(buf);
1178
			ec.dirty = 0;
1179
			editorSetStatusMessage("%d bytes written to disk", len);
1180
			return;
1181
		}
1182
        fclose(fd);
1183
	}
7413 siemargl 1184
 
1185
    free(buf);
1186
    editorSetStatusMessage("Cant's save file. Error occurred: %s", strerror(errno));
1187
}
1188
 
1189
/*** Search section ***/
1190
 
1191
void editorSearchCallback(char* query, int key) {
1192
    // Index of the row that the last match was on, -1 if there was
1193
    // no last match.
1194
    static int last_match = -1;
1195
    // 1 for searching forward and -1 for searching backwards.
1196
    static int direction = 1;
1197
 
1198
    static int saved_highlight_line;
1199
    static char* saved_hightlight = NULL;
1200
 
1201
    if (saved_hightlight) {
1202
        memcpy(ec.row[saved_highlight_line].highlight, saved_hightlight, ec.row[saved_highlight_line].render_size);
1203
        free(saved_hightlight);
1204
        saved_hightlight = NULL;
1205
    }
1206
 
1207
    // Checking if the user pressed Enter or Escape, in which case
1208
    // they are leaving search mode so we return immediately.
1209
    if (key == '\r' || key == '\x1b') {
1210
        last_match = -1;
1211
        direction = 1;
1212
        return;
1213
    } else if (key == ARROW_RIGHT || key == ARROW_DOWN) {
1214
        direction = 1;
1215
    } else if (key == ARROW_LEFT || key == ARROW_UP) {
1216
        if (last_match == -1) {
1217
            // If nothing matched and the left or up arrow key was pressed
1218
            return;
1219
        }
1220
        direction = -1;
1221
    } else {
1222
        last_match = -1;
1223
        direction = 1;
1224
    }
1225
 
1226
    int current = last_match;
1227
    int i;
1228
    for (i = 0; i < ec.num_rows; i++) {
1229
        current += direction;
1230
        if (current == -1)
1231
            current = ec.num_rows - 1;
1232
        else if (current == ec.num_rows)
1233
            current = 0;
1234
 
1235
        editor_row* row = &ec.row[current];
1236
        // We use strstr to check if query is a substring of the
1237
        // current row. It returns NULL if there is no match,
1238
        // oterwhise it returns a pointer to the matching substring.
1239
        char* match = strstr(row -> render, query);
1240
        if (match) {
1241
            last_match = current;
1242
            ec.cursor_y = current;
1243
            ec.cursor_x = editorRowRenderXToCursorX(row, match - row -> render);
1244
            // We set this like so to scroll to the bottom of the file so
1245
            // that the next screen refresh will cause the matching line to
1246
            // be at the very top of the screen.
1247
            ec.row_offset = ec.num_rows;
1248
 
1249
            saved_highlight_line = current;
1250
            saved_hightlight = malloc(row -> render_size);
1251
            memcpy(saved_hightlight, row -> highlight, row -> render_size);
1252
            memset(&row -> highlight[match - row -> render], HL_MATCH, strlen(query));
1253
            break;
1254
        }
1255
    }
1256
}
1257
 
1258
void editorSearch() {
1259
    int saved_cursor_x = ec.cursor_x;
1260
    int saved_cursor_y = ec.cursor_y;
1261
    int saved_col_offset = ec.col_offset;
1262
    int saved_row_offset = ec.row_offset;
1263
 
1264
    char* query = editorPrompt("Search: %s (Use ESC / Enter / Arrows)", editorSearchCallback);
1265
 
1266
    if (query) {
1267
        free(query);
1268
    // If query is NULL, that means they pressed Escape, so in that case we
1269
    // restore the cursor previous position.
1270
    } else {
1271
        ec.cursor_x = saved_cursor_x;
1272
        ec.cursor_y = saved_cursor_y;
1273
        ec.col_offset = saved_col_offset;
1274
        ec.row_offset = saved_row_offset;
1275
    }
1276
}
1277
 
1278
/*** Append buffer section **/
1279
 
1280
void abufAppend(struct a_buf* ab, const char* s, int len) {
1281
    // Using realloc to get a block of free memory that is
1282
    // the size of the current string + the size of the string
1283
    // to be appended.
1284
    char* new = realloc(ab -> buf, ab -> len + len);
1285
 
1286
    if (new == NULL)
1287
        return;
1288
 
1289
    // Copying the string s at the end of the current data in
1290
    // the buffer.
1291
    memcpy(&new[ab -> len], s, len);
1292
    ab -> buf = new;
1293
    ab -> len += len;
1294
}
1295
 
1296
void abufFree(struct a_buf* ab) {
1297
    // Deallocating buffer.
1298
    free(ab -> buf);
1299
}
1300
 
1301
/*** Output section ***/
1302
 
1303
void editorScroll() {
1304
    ec.render_x = 0;
1305
    if (ec.cursor_y < ec.num_rows)
1306
        ec.render_x = editorRowCursorXToRenderX(&ec.row[ec.cursor_y], ec.cursor_x);
1307
    // The first if statement checks if the cursor is above the visible window,
1308
    // and if so, scrolls up to where the cursor is. The second if statement checks
1309
    // if the cursor is past the bottom of the visible window, and contains slightly
1310
    // more complicated arithmetic because ec.row_offset refers to what's at the top
1311
    // of the screen, and we have to get ec.screen_rows involved to talk about what's
1312
    // at the bottom of the screen.
1313
    if (ec.cursor_y < ec.row_offset)
1314
        ec.row_offset = ec.cursor_y;
1315
    if (ec.cursor_y >= ec.row_offset + ec.screen_rows)
1316
        ec.row_offset = ec.cursor_y - ec.screen_rows + 1;
1317
 
1318
    if (ec.render_x < ec.col_offset)
1319
        ec.col_offset = ec.render_x;
1320
    if (ec.render_x >= ec.col_offset + ec.screen_cols)
1321
        ec.col_offset = ec.render_x - ec.screen_cols + 1;
1322
}
1323
 
1324
void editorDrawStatusBar(struct a_buf* ab) {
1325
    // This switches to inverted colors.
1326
    // NOTE:
1327
    // The m command (Select Graphic Rendition) causes the text printed
1328
    // after it to be printed with various possible attributes including
1329
    // bold (1), underscore (4), blink (5), and inverted colors (7). An
1330
    // argument of 0 clears all attributes (the default one). See
1331
    // http://vt100.net/docs/vt100-ug/chapter3.html#SGR for more info.
1332
    abufAppend(ab, "\x1b[7m", 4);
1333
 
1334
    char status[80], r_status[80];
1335
    // Showing up to 20 characters of the filename, followed by the number of lines.
1336
    int len = snprintf(status, sizeof(status), " Editing: %.20s %s", ec.file_name ? ec.file_name : "New file", ec.dirty ? "(modified)" : "");
1337
    int col_size = ec.row && ec.cursor_y <= ec.num_rows - 1 ? col_size = ec.row[ec.cursor_y].size : 0;
1338
    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,
1339
        ec.cursor_x + 1 > col_size ? col_size : ec.cursor_x + 1, col_size);
1340
    if (len > ec.screen_cols)
1341
        len = ec.screen_cols;
1342
    abufAppend(ab, status, len);
1343
    while (len < ec.screen_cols) {
1344
        if (ec.screen_cols - len == r_len) {
1345
            abufAppend(ab, r_status, r_len);
1346
            break;
1347
        } else {
1348
            abufAppend(ab, " ", 1);
1349
            len++;
1350
        }
1351
    }
1352
    // This switches back to normal colors.
1353
    abufAppend(ab, "\x1b[m", 3);
1354
 
1355
    abufAppend(ab, "\r\n", 2);
1356
}
1357
 
1358
void editorDrawMessageBar(struct a_buf *ab) {
1359
    // Clearing the message bar.
7419 siemargl 1360
///    abufAppend(ab, "\x1b[K", 3);	/// not work in Kolibri
7413 siemargl 1361
    int msg_len = strlen(ec.status_msg);
1362
    if (msg_len > ec.screen_cols)
1363
        msg_len = ec.screen_cols;
1364
    // We only show the message if its less than 5 secons old, but
1365
    // remember the screen is only being refreshed after each keypress.
1366
    if (msg_len && time(NULL) - ec.status_msg_time < 5)
1367
        abufAppend(ab, ec.status_msg, msg_len);
1368
}
1369
 
1370
void editorDrawWelcomeMessage(struct a_buf* ab) {
1371
    char welcome[80];
1372
    // Using snprintf to truncate message in case the terminal
1373
    // is too tiny to handle the entire string.
1374
    int welcome_len = snprintf(welcome, sizeof(welcome),
8116 maxcodehac 1375
        "TinyTextEditor %s", TTE_VERSION);
7413 siemargl 1376
    if (welcome_len > ec.screen_cols)
1377
        welcome_len = ec.screen_cols;
1378
    // Centering the message.
1379
    int padding = (ec.screen_cols - welcome_len) / 2;
1380
    // Remember that everything != 0 is true.
1381
    if (padding) {
1382
        abufAppend(ab, "~", 1);
1383
        padding--;
1384
    }
1385
    while (padding--)
1386
        abufAppend(ab, " ", 1);
1387
    abufAppend(ab, welcome, welcome_len);
1388
}
1389
 
1390
// The ... argument makes editorSetStatusMessage() a variadic function,
1391
// meaning it can take any number of arguments. C's way of dealing with
1392
// these arguments is by having you call va_start() and va_end() on a
1393
// // value of type va_list. The last argument before the ... (in this
1394
// case, msg) must be passed to va_start(), so that the address of
1395
// the next arguments is known. Then, between the va_start() and
1396
// va_end() calls, you would call va_arg() and pass it the type of
1397
// the next argument (which you usually get from the given format
1398
// string) and it would return the value of that argument. In
1399
// this case, we pass msg and args to vsnprintf() and it takes care
1400
// of reading the format string and calling va_arg() to get each
1401
// argument.
1402
void editorSetStatusMessage(const char* msg, ...) {
1403
    va_list args;
1404
    va_start(args, msg);
1405
    vsnprintf(ec.status_msg, sizeof(ec.status_msg), msg, args);
1406
    va_end(args);
1407
    ec.status_msg_time = time(NULL);
1408
}
1409
 
1410
void editorDrawRows(struct a_buf* ab) {
1411
    int y;
1412
    for (y = 0; y < ec.screen_rows; y++) {
1413
        int file_row = y + ec.row_offset;
1414
        if(file_row >= ec.num_rows) {
1415
            if (ec.num_rows == 0 && y == ec.screen_rows / 3)
1416
                editorDrawWelcomeMessage(ab);
1417
            else
1418
                abufAppend(ab, "~", 1);
1419
        } else {
1420
            int len = ec.row[file_row].render_size - ec.col_offset;
1421
            // len can be a negative number, meaning the user scrolled
1422
            // horizontally past the end of the line. In that case, we set
1423
            // len to 0 so that nothing is displayed on that line.
1424
            if (len < 0)
1425
                len = 0;
1426
            if (len > ec.screen_cols)
1427
                len = ec.screen_cols;
1428
 
1429
            char* c = &ec.row[file_row].render[ec.col_offset];
1430
            unsigned char* highlight = &ec.row[file_row].highlight[ec.col_offset];
1431
            int current_color = -1;
1432
            int j;
1433
            for (j = 0; j < len; j++) {
1434
                // Displaying nonprintable characters as (A-Z, @, and ?).
1435
                if (iscntrl(c[j])) {
1436
                    char sym = (c[j] <= 26) ? '@' + c[j] : '?';
1437
                    abufAppend(ab, "\x1b[7m", 4);
1438
                    abufAppend(ab, &sym, 1);
1439
                    abufAppend(ab, "\x1b[m", 3);
1440
                    if (current_color != -1) {
1441
                        char buf[16];
1442
                        int c_len = snprintf(buf, sizeof(buf), "\x1b[%dm", current_color);
1443
                        abufAppend(ab, buf, c_len);
1444
                    }
1445
                } else if (highlight[j] == HL_NORMAL) {
1446
                    if (current_color != -1) {
7419 siemargl 1447
                        abufAppend(ab, "\x1b[m", 3);  /// was [39, 5
7413 siemargl 1448
                        current_color = -1;
1449
                    }
1450
                    abufAppend(ab, &c[j], 1);
1451
                } else {
1452
                    int color = editorSyntaxToColor(highlight[j]);
1453
                    // We only use escape sequence if the new color is different
1454
                    // from the last character's color.
1455
                    if (color != current_color) {
1456
                        current_color = color;
1457
                        char buf[16];
1458
                        int c_len = snprintf(buf, sizeof(buf), "\x1b[%dm", color);
1459
                        abufAppend(ab, buf, c_len);
1460
                    }
1461
 
1462
                    abufAppend(ab, &c[j], 1);
1463
                }
1464
            }
7419 siemargl 1465
            abufAppend(ab, "\x1b[m", 3);  /// was [39, 5
7413 siemargl 1466
        }
1467
 
1468
        // Redrawing each line instead of the whole screen.
7419 siemargl 1469
///        abufAppend(ab, "\x1b[K", 3);  /// not work in Kolibri
7413 siemargl 1470
        // Addind a new line
1471
        abufAppend(ab, "\r\n", 2);
1472
    }
1473
}
1474
 
1475
void editorRefreshScreen() {
1476
    editorScroll();
1477
 
1478
    struct a_buf ab = ABUF_INIT;
1479
 
1480
    // Hiding the cursor while the screen is refreshing.
1481
    // See http://vt100.net/docs/vt100-ug/chapter3.html#S3.3.4
1482
    // for more info.
7419 siemargl 1483
///    abufAppend(&ab, "\x1b[?25l", 6);
7413 siemargl 1484
    abufAppend(&ab, "\x1b[H", 3);
1485
 
7419 siemargl 1486
/// full clear because "\x1b[K" not work in Kolibri
1487
    abufAppend(&ab, "\x1b[2J", 4);
1488
 
7413 siemargl 1489
    editorDrawRows(&ab);
1490
    editorDrawStatusBar(&ab);
1491
    editorDrawMessageBar(&ab);
1492
 
1493
    // Moving the cursor where it should be.
1494
    char buf[32];
7419 siemargl 1495
///    snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (ec.cursor_y - ec.row_offset) + 1, (ec.render_x - ec.col_offset) + 1);
1496
/// a bit different in Kolibri
1497
    snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (ec.render_x - ec.col_offset), (ec.cursor_y - ec.row_offset));
1498
       abufAppend(&ab, buf, strlen(buf));
7413 siemargl 1499
 
1500
    // Showing again the cursor.
7419 siemargl 1501
///    abufAppend(&ab, "\x1b[?25h", 6);
7413 siemargl 1502
 
1503
    // Writing all content at once
7414 siemargl 1504
///    write(STDOUT_FILENO, ab.buf, ab.len);
1505
	con_write_string(ab.buf, ab.len);
7413 siemargl 1506
    abufFree(&ab);
1507
}
1508
 
1509
void editorClearScreen() {
1510
    // Writing 4 bytes out to the terminal:
1511
    // - (1 byte) \x1b : escape character
1512
    // - (3 bytes) [2J : Clears the entire screen, see
1513
    // http://vt100.net/docs/vt100-ug/chapter3.html#ED
1514
    // for more info.
7414 siemargl 1515
///    write(STDOUT_FILENO, "\x1b[2J", 4);
1516
	con_write_string("\x1b[2J", 4);
1517
 
7413 siemargl 1518
    // Writing 3 bytes to reposition the cursor back at
1519
    // the top-left corner, see
1520
    // http://vt100.net/docs/vt100-ug/chapter3.html#CUP
1521
    // for more info.
7414 siemargl 1522
///    write(STDOUT_FILENO, "\x1b[H", 3);
1523
	con_write_string("\x1b[H", 3);
7413 siemargl 1524
}
1525
 
1526
/*** Input section ***/
1527
 
1528
char* editorPrompt(char* prompt, void (*callback)(char*, int)) {
1529
    size_t buf_size = 128;
1530
    char* buf = malloc(buf_size);
1531
 
1532
    size_t buf_len = 0;
1533
    buf[0] = '\0';
1534
 
1535
    while (1) {
1536
        editorSetStatusMessage(prompt, buf);
1537
        editorRefreshScreen();
1538
 
1539
        int c = editorReadKey();
1540
        if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
1541
            if (buf_len != 0)
1542
                buf[--buf_len] = '\0';
1543
        } else if (c == '\x1b') {
1544
            editorSetStatusMessage("");
1545
            if (callback)
1546
                callback(buf, c);
1547
            free(buf);
1548
            return NULL;
1549
        } else if (c == '\r') {
1550
            if (buf_len != 0) {
1551
                editorSetStatusMessage("");
1552
                if (callback)
1553
                    callback(buf, c);
1554
                return buf;
1555
            }
1556
        } else if (!iscntrl(c) && isprint(c)) {
1557
            if (buf_len == buf_size - 1) {
1558
                buf_size *= 2;
1559
                buf = realloc(buf, buf_size);
1560
            }
1561
            buf[buf_len++] = c;
1562
            buf[buf_len] = '\0';
1563
        }
1564
 
1565
        if (callback)
1566
            callback(buf, c);
1567
    }
1568
}
1569
 
1570
void editorMoveCursor(int key) {
1571
    editor_row* row = (ec.cursor_y >= ec.num_rows) ? NULL : &ec.row[ec.cursor_y];
1572
 
1573
    switch (key) {
1574
        case ARROW_LEFT:
1575
            if (ec.cursor_x != 0)
1576
                ec.cursor_x--;
1577
            // If <- is pressed, move to the end of the previous line
1578
            else if (ec.cursor_y > 0) {
1579
                ec.cursor_y--;
1580
                ec.cursor_x = ec.row[ec.cursor_y].size;
1581
            }
1582
            break;
1583
        case ARROW_RIGHT:
1584
            if (row && ec.cursor_x < row -> size)
1585
                ec.cursor_x++;
1586
            // If -> is pressed, move to the start of the next line
1587
            else if (row && ec.cursor_x == row -> size) {
1588
                ec.cursor_y++;
1589
                ec.cursor_x = 0;
1590
            }
1591
            break;
1592
        case ARROW_UP:
1593
            if (ec.cursor_y != 0)
1594
                ec.cursor_y--;
1595
            break;
1596
        case ARROW_DOWN:
1597
            if (ec.cursor_y < ec.num_rows)
1598
                ec.cursor_y++;
1599
            break;
1600
    }
1601
 
1602
    // Move cursor_x if it ends up past the end of the line it's on
1603
    row = (ec.cursor_y >= ec.num_rows) ? NULL : &ec.row[ec.cursor_y];
1604
    int row_len = row ? row -> size : 0;
1605
    if (ec.cursor_x > row_len)
1606
        ec.cursor_x = row_len;
1607
}
1608
 
1609
void editorProcessKeypress() {
1610
    static int quit_times = TTE_QUIT_TIMES;
1611
 
1612
    int c = editorReadKey();
1613
 
1614
    switch (c) {
1615
        case '\r': // Enter key
1616
            editorInsertNewline();
1617
            break;
1618
        case CTRL_KEY('q'):
1619
            if (ec.dirty && quit_times > 0) {
7419 siemargl 1620
                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 1621
                quit_times--;
1622
                return;
1623
            }
1624
            editorClearScreen();
1625
            consoleBufferClose();
7419 siemargl 1626
            con_exit(1); /// KOS console
7413 siemargl 1627
            exit(0);
1628
            break;
1629
        case CTRL_KEY('s'):
1630
            editorSave();
1631
            break;
1632
        case CTRL_KEY('e'):
1633
            if (ec.cursor_y > 0 && ec.cursor_y <= ec.num_rows - 1)
1634
                editorFlipRow(1);
1635
            break;
1636
        case CTRL_KEY('d'):
1637
            if (ec.cursor_y < ec.num_rows - 1)
1638
            editorFlipRow(-1);
1639
            break;
1640
        case CTRL_KEY('x'):
1641
            if (ec.cursor_y < ec.num_rows)
1642
            editorCut();
1643
            break;
1644
        case CTRL_KEY('c'):
1645
            if (ec.cursor_y < ec.num_rows)
1646
            editorCopy(0);
1647
            break;
1648
        case CTRL_KEY('v'):
1649
            editorPaste();
1650
            break;
7414 siemargl 1651
///        case CTRL_KEY('p'):
1652
///            consoleBufferClose();
1653
///            kill(0, SIGTSTP);
7413 siemargl 1654
        case ARROW_UP:
1655
        case ARROW_DOWN:
1656
        case ARROW_LEFT:
1657
        case ARROW_RIGHT:
1658
            editorMoveCursor(c);
1659
            break;
1660
        case PAGE_UP:
1661
        case PAGE_DOWN:
1662
            { // You can't declare variables directly inside a switch statement.
1663
                if (c == PAGE_UP)
1664
                    ec.cursor_y = ec.row_offset;
1665
                else if (c == PAGE_DOWN)
1666
                    ec.cursor_y = ec.row_offset + ec.screen_rows - 1;
1667
 
1668
                int times = ec.screen_rows;
1669
                while (times--)
1670
                    editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
1671
            }
1672
            break;
1673
        case HOME_KEY:
1674
            ec.cursor_x = 0;
1675
            break;
1676
        case END_KEY:
1677
            if (ec.cursor_y < ec.num_rows)
1678
                ec.cursor_x = ec.row[ec.cursor_y].size;
1679
            break;
1680
        case CTRL_KEY('f'):
1681
            editorSearch();
1682
            break;
1683
        case BACKSPACE:
1684
        case CTRL_KEY('h'):
1685
        case DEL_KEY:
1686
            if (c == DEL_KEY)
1687
                editorMoveCursor(ARROW_RIGHT);
1688
            editorDelChar();
1689
            break;
1690
        case CTRL_KEY('l'):
1691
        case '\x1b': // Escape key
1692
            break;
1693
        default:
1694
            editorInsertChar(c);
1695
            break;
1696
    }
1697
 
1698
    quit_times = TTE_QUIT_TIMES;
1699
}
1700
 
1701
/*** Init section ***/
1702
 
1703
void initEditor() {
1704
    ec.cursor_x = 0;
1705
    ec.cursor_y = 0;
1706
    ec.render_x = 0;
1707
    ec.row_offset = 0;
1708
    ec.col_offset = 0;
1709
    ec.num_rows = 0;
1710
    ec.row = NULL;
1711
    ec.dirty = 0;
1712
    ec.file_name = NULL;
1713
    ec.status_msg[0] = '\0';
1714
    ec.status_msg_time = 0;
1715
    ec.copied_char_buffer = NULL;
1716
    ec.syntax = NULL;
1717
 
1718
    editorUpdateWindowSize();
1719
    // The SIGWINCH signal is sent to a process when its controlling
1720
    // terminal changes its size (a window change).
7414 siemargl 1721
///    signal(SIGWINCH, editorHandleSigwinch);
7413 siemargl 1722
    // The SIGCONT signal instructs the operating system to continue
1723
    // (restart) a process previously paused by the SIGSTOP or SIGTSTP
1724
    // signal.
7414 siemargl 1725
///    signal(SIGCONT, editorHandleSigcont);
7413 siemargl 1726
}
1727
 
1728
void printHelp() {
8127 maxcodehac 1729
/*
1730
	printf("Usage: tte [OPTIONS] [FILE]\n\n");
7413 siemargl 1731
    printf("\nKEYBINDINGS\n-----------\n\n");
1732
    printf("Keybinding\t\tAction\n\n");
7419 siemargl 1733
    printf("Ctrl-Q,^Z \t\tExit\n");
7413 siemargl 1734
    printf("Ctrl-S    \t\tSave\n");
1735
    printf("Ctrl-F    \t\tSearch. Esc, enter and arrows to interact once searching\n");
1736
    printf("Ctrl-E    \t\tFlip line upwards\n");
1737
    printf("Ctrl-D    \t\tFlip line downwards\n");
1738
    printf("Ctrl-C    \t\tCopy line\n");
1739
    printf("Ctrl-X    \t\tCut line\n");
1740
    printf("Ctrl-V    \t\tPaste line\n");
8127 maxcodehac 1741
///   printf("Ctrl-P    \t\tPause tte (type \"fg\" to resume)\n");
7413 siemargl 1742
 
1743
    printf("\n\nOPTIONS\n-------\n\n");
1744
    printf("Option        \t\tAction\n\n");
1745
    printf("-h | --help   \t\tPrints the help\n");
1746
    printf("-v | --version\t\tPrints the version of tte\n");
1747
 
1748
    printf("\n\nFor now, usage of ISO 8859-1 is recommended.\n");
8127 maxcodehac 1749
*/
1750
 
1751
    /// NOTIFY HOTKEYS
1752
    char* __help__ =
1753
    "'Hotkeys: \n\
1754
^Q, ^Z   Exit \n\
1755
Ctrl-S   Save \n\
1756
Ctrl-F   Search. Esc, \n\
1757
    enter and arrows to interact once searching \n\
1758
Ctrl-E   Flip line upwards \n\
1759
Ctrl-D   Flip line downwards \n\
1760
Ctrl-C   Copy line \n\
1761
Ctrl-X   Cut line \n\
1762
Ctrl-V   Paste line' -t -I";
1763
    notify(__help__);
1764
 
1765
    /// NOTIFY OPTIONS
1766
    __help__ =
1767
    "'Options:\n\
1768
-h, --help     Prints the help \n\
1769
-v, --version  Prints the version of tte \n\
1770
For now, usage of ISO 8859-1 is recommended.\n' -t -I";
1771
	notify(__help__);
7413 siemargl 1772
}
1773
 
1774
// 1 if editor should open, 0 otherwise, -1 if the program should exit
1775
int handleArgs(int argc, char* argv[]) {
1776
    if (argc == 1)
1777
        return 0;
1778
 
1779
    if (argc >= 2) {
1780
        if (strncmp("-h", argv[1], 2) == 0 || strncmp("--help", argv[1], 6) == 0) {
1781
            printHelp();
1782
            return -1;
1783
        } else if(strncmp("-v", argv[1], 2) == 0 || strncmp("--version", argv[1], 9) == 0) {
8127 maxcodehac 1784
            char* _notify_version = "'Version:\n";
1785
            strcat(_notify_version, TTE_VERSION);
1786
            strcat(_notify_version, "' -t -I");
1787
            /// printf("tte - version %s\n", TTE_VERSION);
1788
            notify(_notify_version);
7413 siemargl 1789
            return -1;
1790
        }
1791
    }
1792
 
1793
    return 1;
1794
}
1795
 
1796
int main(int argc, char* argv[]) {
8121 maxcodehac 1797
	con_init_console_dll_param(con_def_wnd_width, con_def_wnd_height, con_def_wnd_width, con_def_wnd_height, "TinyTextEditor");
7413 siemargl 1798
    initEditor();
1799
    int arg_response = handleArgs(argc, argv);
8116 maxcodehac 1800
    if (arg_response == 1) {
1801
		editorOpen(argv[1]);
1802
		char* title = argv[1];
1803
		strcat(title, " - TinyTextEditor");
1804
		con_set_title(title);
1805
	}
7413 siemargl 1806
    else if (arg_response == -1)
1807
        return 0;
1808
    enableRawMode();
7419 siemargl 1809
    editorSetStatusMessage(" Ctrl-Q, ^Z to quit | Ctrl-S to save | (tte -h | --help for more info)");
7413 siemargl 1810
 
1811
    while (1) {
1812
        editorRefreshScreen();
1813
        editorProcessKeypress();
1814
    }
1815
 
7419 siemargl 1816
	con_exit(1);
7413 siemargl 1817
    return 0;
1818
}