@hsusulist/luc 0.2.5 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -14
- package/bin/luc.js +295 -5
- package/install.ps1 +222 -9
- package/package.json +4 -5
- package/scripts/build-linux.js +34 -0
- package/scripts/postinstall.js +8 -2
- package/scripts/postpack.js +14 -0
- package/scripts/prepack.js +27 -0
- package/src/lcode-linux/lcode.c +582 -0
- package/src/luc.h +303 -0
- package/src/luc_core.c +5527 -0
- package/src/luc_libs.c +5760 -0
- package/src/luc_nums.c +1747 -0
- package/src/luc_trans.c +1735 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
/* Initial POSIX terminal editor backend for LUC's Linux build. */
|
|
2
|
+
#if !defined(_WIN32)
|
|
3
|
+
#include <errno.h>
|
|
4
|
+
#include <stdio.h>
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
#include <sys/select.h>
|
|
8
|
+
#include <sys/stat.h>
|
|
9
|
+
#include <sys/time.h>
|
|
10
|
+
#include <sys/ioctl.h>
|
|
11
|
+
#include <termios.h>
|
|
12
|
+
#include <unistd.h>
|
|
13
|
+
|
|
14
|
+
typedef struct {
|
|
15
|
+
char *data;
|
|
16
|
+
size_t len, cap, cursor, preferred_col;
|
|
17
|
+
int dirty;
|
|
18
|
+
char path[1024];
|
|
19
|
+
char status[128];
|
|
20
|
+
} LcLinuxEditor;
|
|
21
|
+
|
|
22
|
+
static int lc_linux_reserve(LcLinuxEditor *e, size_t extra) {
|
|
23
|
+
size_t need = e->len + extra + 1;
|
|
24
|
+
size_t cap = e->cap ? e->cap : 256;
|
|
25
|
+
char *p;
|
|
26
|
+
if (need <= e->cap) return 1;
|
|
27
|
+
while (cap < need) {
|
|
28
|
+
if (cap > (size_t)-1 / 2) return 0;
|
|
29
|
+
cap *= 2;
|
|
30
|
+
}
|
|
31
|
+
p = (char *)realloc(e->data, cap);
|
|
32
|
+
if (!p) return 0;
|
|
33
|
+
e->data = p;
|
|
34
|
+
e->cap = cap;
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static size_t lc_linux_line_start(const LcLinuxEditor *e, size_t pos) {
|
|
39
|
+
while (pos > 0 && e->data[pos - 1] != '\n') pos--;
|
|
40
|
+
return pos;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static size_t lc_linux_line_end(const LcLinuxEditor *e, size_t pos) {
|
|
44
|
+
while (pos < e->len && e->data[pos] != '\n') pos++;
|
|
45
|
+
return pos;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static size_t lc_linux_line_number(const LcLinuxEditor *e, size_t pos) {
|
|
49
|
+
size_t line = 0, i;
|
|
50
|
+
for (i = 0; i < pos; i++) if (e->data[i] == '\n') line++;
|
|
51
|
+
return line;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static void lc_linux_insert(LcLinuxEditor *e, char c) {
|
|
55
|
+
if (!lc_linux_reserve(e, 1)) {
|
|
56
|
+
snprintf(e->status, sizeof e->status, "out of memory");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
memmove(e->data + e->cursor + 1, e->data + e->cursor,
|
|
60
|
+
e->len - e->cursor + 1);
|
|
61
|
+
e->data[e->cursor++] = c;
|
|
62
|
+
e->len++;
|
|
63
|
+
e->dirty = 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static int lc_linux_word_char(unsigned char c) {
|
|
67
|
+
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
|
68
|
+
(c >= '0' && c <= '9') || c == '_';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static unsigned char lc_linux_lower(unsigned char c) {
|
|
72
|
+
return c >= 'A' && c <= 'Z' ? (unsigned char)(c + ('a' - 'A')) : c;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static int lc_linux_starts_word(const char *s, const char *word) {
|
|
76
|
+
size_t i = 0;
|
|
77
|
+
while (*s == ' ' || *s == '\t') s++;
|
|
78
|
+
while (word[i] && lc_linux_lower((unsigned char)s[i]) == lc_linux_lower((unsigned char)word[i])) i++;
|
|
79
|
+
return word[i] == 0 && !lc_linux_word_char((unsigned char)s[i]);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static int lc_linux_ends_word(const char *s, const char *word) {
|
|
83
|
+
size_t n = strlen(s), m = strlen(word), i;
|
|
84
|
+
while (n && (s[n - 1] == ' ' || s[n - 1] == '\t')) n--;
|
|
85
|
+
if (n < m) return 0;
|
|
86
|
+
for (i = 0; i < m; i++) {
|
|
87
|
+
if (lc_linux_lower((unsigned char)s[n - m + i]) != lc_linux_lower((unsigned char)word[i])) return 0;
|
|
88
|
+
}
|
|
89
|
+
return n == m || !lc_linux_word_char((unsigned char)s[n - m - 1]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static int lc_linux_has_word(const char *s, const char *word) {
|
|
93
|
+
size_t i, n = strlen(s), m = strlen(word), j;
|
|
94
|
+
if (!m || m > n) return 0;
|
|
95
|
+
for (i = 0; i + m <= n; i++) {
|
|
96
|
+
if (i && lc_linux_word_char((unsigned char)s[i - 1])) continue;
|
|
97
|
+
if (i + m < n && lc_linux_word_char((unsigned char)s[i + m])) continue;
|
|
98
|
+
for (j = 0; j < m; j++) {
|
|
99
|
+
if (lc_linux_lower((unsigned char)s[i + j]) != lc_linux_lower((unsigned char)word[j])) break;
|
|
100
|
+
}
|
|
101
|
+
if (j == m) return 1;
|
|
102
|
+
}
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static int lc_linux_ext(const char *path, const char *ext) {
|
|
107
|
+
const char *dot = strrchr(path, '.');
|
|
108
|
+
size_t i, n = strlen(ext);
|
|
109
|
+
if (!dot || strlen(dot + 1) != n) return 0;
|
|
110
|
+
for (i = 0; i < n; i++) {
|
|
111
|
+
if (lc_linux_lower((unsigned char)dot[i + 1]) != lc_linux_lower((unsigned char)ext[i])) return 0;
|
|
112
|
+
}
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static void lc_linux_strip_comment(char *s, const char *path) {
|
|
117
|
+
int quote = 0, escape = 0;
|
|
118
|
+
int hash = lc_linux_ext(path, "py") || lc_linux_ext(path, "sh") ||
|
|
119
|
+
lc_linux_ext(path, "bash") || lc_linux_ext(path, "zsh") ||
|
|
120
|
+
lc_linux_ext(path, "rb") || lc_linux_ext(path, "pl") ||
|
|
121
|
+
lc_linux_ext(path, "r") || lc_linux_ext(path, "yaml") ||
|
|
122
|
+
lc_linux_ext(path, "yml");
|
|
123
|
+
int dash = !path[0] || lc_linux_ext(path, "luc") || lc_linux_ext(path, "lua") ||
|
|
124
|
+
lc_linux_ext(path, "sql");
|
|
125
|
+
int slash = lc_linux_ext(path, "c") || lc_linux_ext(path, "h") ||
|
|
126
|
+
lc_linux_ext(path, "cpp") || lc_linux_ext(path, "hpp") ||
|
|
127
|
+
lc_linux_ext(path, "cc") || lc_linux_ext(path, "cxx") ||
|
|
128
|
+
lc_linux_ext(path, "js") || lc_linux_ext(path, "ts") ||
|
|
129
|
+
lc_linux_ext(path, "java") || lc_linux_ext(path, "go") ||
|
|
130
|
+
lc_linux_ext(path, "rs") || lc_linux_ext(path, "cs") ||
|
|
131
|
+
lc_linux_ext(path, "kt") || lc_linux_ext(path, "swift") ||
|
|
132
|
+
lc_linux_ext(path, "dart") || lc_linux_ext(path, "scala") ||
|
|
133
|
+
lc_linux_ext(path, "php") || lc_linux_ext(path, "css") ||
|
|
134
|
+
lc_linux_ext(path, "scss") || lc_linux_ext(path, "less") ||
|
|
135
|
+
lc_linux_ext(path, "tf") || lc_linux_ext(path, "hcl");
|
|
136
|
+
size_t i, n = strlen(s);
|
|
137
|
+
for (i = 0; i < n; i++) {
|
|
138
|
+
unsigned char c = (unsigned char)s[i];
|
|
139
|
+
if (quote) {
|
|
140
|
+
if (escape) escape = 0;
|
|
141
|
+
else if (c == '\\') escape = 1;
|
|
142
|
+
else if (c == (unsigned char)quote) quote = 0;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (c == '\'' || c == '"' || c == '`') { quote = c; continue; }
|
|
146
|
+
if (hash && c == '#') { s[i] = 0; return; }
|
|
147
|
+
if (dash && c == '-' && s[i + 1] == '-') { s[i] = 0; return; }
|
|
148
|
+
if (slash && c == '/' && s[i + 1] == '/') { s[i] = 0; return; }
|
|
149
|
+
if (slash && c == '/' && s[i + 1] == '*') { s[i] = 0; return; }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static int lc_linux_html_block(const char *s, char *closer, size_t cap) {
|
|
154
|
+
static const char *voids[] = {
|
|
155
|
+
"area", "base", "br", "col", "embed", "hr", "img", "input",
|
|
156
|
+
"link", "meta", "param", "source", "track", "wbr", NULL
|
|
157
|
+
};
|
|
158
|
+
char tag[128];
|
|
159
|
+
size_t n = strlen(s), i = 1, j = 0, k;
|
|
160
|
+
if (n < 3 || s[0] != '<' || s[1] == '/' || s[1] == '!' ||
|
|
161
|
+
s[1] == '?' || s[n - 1] != '>') return 0;
|
|
162
|
+
while (i < n - 1 && ((s[i] >= 'a' && s[i] <= 'z') ||
|
|
163
|
+
(s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= '0' && s[i] <= '9') ||
|
|
164
|
+
s[i] == ':' || s[i] == '-')) {
|
|
165
|
+
if (j + 1 >= sizeof tag) return 0;
|
|
166
|
+
tag[j++] = s[i++];
|
|
167
|
+
}
|
|
168
|
+
if (!j || s[n - 2] == '/') return 0;
|
|
169
|
+
tag[j] = 0;
|
|
170
|
+
for (k = 0; voids[k]; k++) {
|
|
171
|
+
size_t a = strlen(tag), b = strlen(voids[k]), x;
|
|
172
|
+
if (a != b) continue;
|
|
173
|
+
for (x = 0; x < a; x++) {
|
|
174
|
+
if (lc_linux_lower((unsigned char)tag[x]) != lc_linux_lower((unsigned char)voids[k][x])) break;
|
|
175
|
+
}
|
|
176
|
+
if (x == a) return 0;
|
|
177
|
+
}
|
|
178
|
+
return snprintf(closer, cap, "</%s>", tag) < (int)cap;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static int lc_linux_block(const char *path, char *code, char *closer, size_t cap) {
|
|
182
|
+
static const char *py_blocks[] = {
|
|
183
|
+
"if", "elif", "else", "for", "while", "def", "class", "try",
|
|
184
|
+
"except", "finally", "with", "match", "case", NULL
|
|
185
|
+
};
|
|
186
|
+
static const char *end_blocks[] = {
|
|
187
|
+
"def", "class", "module", "if", "unless", "while", "until",
|
|
188
|
+
"for", "begin", "case", NULL
|
|
189
|
+
};
|
|
190
|
+
size_t n = strlen(code), i;
|
|
191
|
+
const char *p = code;
|
|
192
|
+
closer[0] = 0;
|
|
193
|
+
while (n && (code[n - 1] == ' ' || code[n - 1] == '\t' || code[n - 1] == '\r')) code[--n] = 0;
|
|
194
|
+
if (!n) return 0;
|
|
195
|
+
if (code[n - 1] == '{') return snprintf(closer, cap, "}") < (int)cap;
|
|
196
|
+
if ((lc_linux_ext(path, "json") || lc_linux_ext(path, "js") ||
|
|
197
|
+
lc_linux_ext(path, "py") || lc_linux_ext(path, "c") ||
|
|
198
|
+
lc_linux_ext(path, "h") || lc_linux_ext(path, "go") ||
|
|
199
|
+
lc_linux_ext(path, "rs") || lc_linux_ext(path, "java") ||
|
|
200
|
+
lc_linux_ext(path, "cs") || lc_linux_ext(path, "ts") ||
|
|
201
|
+
lc_linux_ext(path, "php")) && code[n - 1] == '[') {
|
|
202
|
+
return snprintf(closer, cap, "]") < (int)cap;
|
|
203
|
+
}
|
|
204
|
+
if (lc_linux_ext(path, "html") || lc_linux_ext(path, "htm") ||
|
|
205
|
+
lc_linux_ext(path, "xml") || lc_linux_ext(path, "svg")) {
|
|
206
|
+
return lc_linux_html_block(code, closer, cap);
|
|
207
|
+
}
|
|
208
|
+
if (lc_linux_ext(path, "py")) {
|
|
209
|
+
if (code[n - 1] != ':') return 0;
|
|
210
|
+
if (lc_linux_starts_word(p, "async")) {
|
|
211
|
+
p += 5;
|
|
212
|
+
while (*p == ' ' || *p == '\t') p++;
|
|
213
|
+
}
|
|
214
|
+
for (i = 0; py_blocks[i]; i++) if (lc_linux_starts_word(p, py_blocks[i])) return 1;
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
if (lc_linux_ext(path, "yaml") || lc_linux_ext(path, "yml")) return code[n - 1] == ':';
|
|
218
|
+
if (!path[0] || lc_linux_ext(path, "luc") || lc_linux_ext(path, "lua")) {
|
|
219
|
+
if (lc_linux_starts_word(code, "else") || lc_linux_starts_word(code, "elseif")) return 1;
|
|
220
|
+
if (lc_linux_ext(path, "lua") && lc_linux_ends_word(code, "repeat")) {
|
|
221
|
+
return snprintf(closer, cap, "until") < (int)cap;
|
|
222
|
+
}
|
|
223
|
+
if (lc_linux_ends_word(code, "then") || lc_linux_ends_word(code, "do") ||
|
|
224
|
+
(lc_linux_has_word(code, "function") && code[n - 1] == ')') ||
|
|
225
|
+
(lc_linux_starts_word(code, "command") && code[n - 1] == ')')) {
|
|
226
|
+
return snprintf(closer, cap, "end") < (int)cap;
|
|
227
|
+
}
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
if (lc_linux_ext(path, "rb") || lc_linux_ext(path, "pl")) {
|
|
231
|
+
if (lc_linux_starts_word(code, "else") || lc_linux_starts_word(code, "elsif") ||
|
|
232
|
+
lc_linux_starts_word(code, "when") || lc_linux_starts_word(code, "rescue") ||
|
|
233
|
+
lc_linux_starts_word(code, "ensure") || lc_linux_starts_word(code, "continue")) return 1;
|
|
234
|
+
for (i = 0; end_blocks[i]; i++) {
|
|
235
|
+
if (lc_linux_starts_word(code, end_blocks[i])) return snprintf(closer, cap, "end") < (int)cap;
|
|
236
|
+
}
|
|
237
|
+
if (lc_linux_ends_word(code, "do") || lc_linux_has_word(code, "do")) {
|
|
238
|
+
return snprintf(closer, cap, "end") < (int)cap;
|
|
239
|
+
}
|
|
240
|
+
if ((lc_linux_ext(path, "pl") && lc_linux_starts_word(code, "sub")) ||
|
|
241
|
+
(lc_linux_ext(path, "rb") && lc_linux_starts_word(code, "def"))) {
|
|
242
|
+
return snprintf(closer, cap, "end") < (int)cap;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (lc_linux_ext(path, "sh") || lc_linux_ext(path, "bash") || lc_linux_ext(path, "zsh")) {
|
|
246
|
+
if (lc_linux_starts_word(code, "else") || lc_linux_starts_word(code, "elif")) return 1;
|
|
247
|
+
if (lc_linux_starts_word(code, "case") && lc_linux_ends_word(code, "in")) {
|
|
248
|
+
return snprintf(closer, cap, "esac") < (int)cap;
|
|
249
|
+
}
|
|
250
|
+
if (lc_linux_ends_word(code, "then")) return snprintf(closer, cap, "fi") < (int)cap;
|
|
251
|
+
if (lc_linux_ends_word(code, "do")) return snprintf(closer, cap, "done") < (int)cap;
|
|
252
|
+
}
|
|
253
|
+
if (lc_linux_ext(path, "sql")) {
|
|
254
|
+
if (lc_linux_starts_word(code, "else") || lc_linux_starts_word(code, "when")) return 1;
|
|
255
|
+
if (lc_linux_ends_word(code, "case") || lc_linux_ends_word(code, "begin")) {
|
|
256
|
+
return snprintf(closer, cap, "END;") < (int)cap;
|
|
257
|
+
}
|
|
258
|
+
if (lc_linux_ends_word(code, "then")) {
|
|
259
|
+
return snprintf(closer, cap, lc_linux_has_word(code, "if") ? "END IF;" : "END;") < (int)cap;
|
|
260
|
+
}
|
|
261
|
+
if (lc_linux_ends_word(code, "loop")) return snprintf(closer, cap, "END LOOP;") < (int)cap;
|
|
262
|
+
}
|
|
263
|
+
return 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
static int lc_linux_tail_closer(const char *tail, const char *closer) {
|
|
267
|
+
size_t i = 0, j, n = strlen(tail), m = strlen(closer);
|
|
268
|
+
while (i < n && (tail[i] == ' ' || tail[i] == '\t' || tail[i] == '\r')) i++;
|
|
269
|
+
if (n - i < m) return 0;
|
|
270
|
+
for (j = 0; j < m; j++) {
|
|
271
|
+
if (lc_linux_lower((unsigned char)tail[i + j]) != lc_linux_lower((unsigned char)closer[j])) return 0;
|
|
272
|
+
}
|
|
273
|
+
i += m;
|
|
274
|
+
while (i < n && (tail[i] == ' ' || tail[i] == '\t' || tail[i] == '\r')) i++;
|
|
275
|
+
if (i < n && tail[i] == ';') i++;
|
|
276
|
+
while (i < n && (tail[i] == ' ' || tail[i] == '\t' || tail[i] == '\r')) i++;
|
|
277
|
+
return i == n;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static void lc_linux_newline(LcLinuxEditor *e) {
|
|
281
|
+
size_t start = lc_linux_line_start(e, e->cursor);
|
|
282
|
+
size_t end = lc_linux_line_end(e, e->cursor);
|
|
283
|
+
size_t prefix_len = e->cursor - start, tail_len = end - e->cursor;
|
|
284
|
+
size_t base_len = 0, inner_len, close_len = 0, i, insert_len;
|
|
285
|
+
char closer[256], *prefix, *code_buf, *code, *tail, *base, *insert;
|
|
286
|
+
int block, has_closer, tail_blank = 1, make_closer;
|
|
287
|
+
const char *path = e->path;
|
|
288
|
+
prefix = (char *)malloc(prefix_len + 1);
|
|
289
|
+
code_buf = (char *)malloc(prefix_len + 1);
|
|
290
|
+
tail = (char *)malloc(tail_len + 1);
|
|
291
|
+
if (!prefix || !code_buf || !tail) {
|
|
292
|
+
free(prefix); free(code_buf); free(tail);
|
|
293
|
+
snprintf(e->status, sizeof e->status, "out of memory");
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
memcpy(prefix, e->data + start, prefix_len); prefix[prefix_len] = 0;
|
|
297
|
+
memcpy(code_buf, prefix, prefix_len + 1); code = code_buf;
|
|
298
|
+
memcpy(tail, e->data + e->cursor, tail_len); tail[tail_len] = 0;
|
|
299
|
+
lc_linux_strip_comment(code, path);
|
|
300
|
+
while (*code == ' ' || *code == '\t') code++;
|
|
301
|
+
block = lc_linux_block(path, code, closer, sizeof closer);
|
|
302
|
+
while (e->data[start + base_len] == ' ' || e->data[start + base_len] == '\t') base_len++;
|
|
303
|
+
base = (char *)malloc(base_len + 1);
|
|
304
|
+
if (!base) {
|
|
305
|
+
free(prefix); free(code_buf); free(tail);
|
|
306
|
+
snprintf(e->status, sizeof e->status, "out of memory");
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
memcpy(base, e->data + start, base_len); base[base_len] = 0;
|
|
310
|
+
for (i = 0; i < tail_len; i++) {
|
|
311
|
+
if (tail[i] != ' ' && tail[i] != '\t' && tail[i] != '\r') { tail_blank = 0; break; }
|
|
312
|
+
}
|
|
313
|
+
has_closer = block && closer[0] && lc_linux_tail_closer(tail, closer);
|
|
314
|
+
if (block && !tail_blank && !has_closer) block = 0;
|
|
315
|
+
make_closer = block && closer[0];
|
|
316
|
+
inner_len = base_len + (block ? 4 : 0);
|
|
317
|
+
insert_len = 1 + inner_len;
|
|
318
|
+
if (make_closer) {
|
|
319
|
+
if (has_closer) {
|
|
320
|
+
size_t first = 0, last = tail_len;
|
|
321
|
+
while (first < last && (tail[first] == ' ' || tail[first] == '\t' || tail[first] == '\r')) first++;
|
|
322
|
+
while (last > first && (tail[last - 1] == ' ' || tail[last - 1] == '\t' || tail[last - 1] == '\r')) last--;
|
|
323
|
+
close_len = last - first;
|
|
324
|
+
memmove(tail, tail + first, close_len);
|
|
325
|
+
} else {
|
|
326
|
+
close_len = strlen(closer);
|
|
327
|
+
}
|
|
328
|
+
insert_len += 1 + base_len + close_len;
|
|
329
|
+
}
|
|
330
|
+
insert = (char *)malloc(insert_len);
|
|
331
|
+
if (!insert || !lc_linux_reserve(e, insert_len)) {
|
|
332
|
+
free(prefix); free(code_buf); free(tail); free(base); free(insert);
|
|
333
|
+
snprintf(e->status, sizeof e->status, "out of memory");
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
i = 0;
|
|
337
|
+
insert[i++] = '\n';
|
|
338
|
+
memcpy(insert + i, base, base_len); i += base_len;
|
|
339
|
+
if (block) { memcpy(insert + i, " ", 4); i += 4; }
|
|
340
|
+
if (make_closer) {
|
|
341
|
+
insert[i++] = '\n';
|
|
342
|
+
memcpy(insert + i, base, base_len); i += base_len;
|
|
343
|
+
memcpy(insert + i, has_closer ? tail : closer, close_len); i += close_len;
|
|
344
|
+
}
|
|
345
|
+
if (block && tail_len) {
|
|
346
|
+
memmove(e->data + e->cursor, e->data + end, e->len - end + 1);
|
|
347
|
+
e->len -= tail_len;
|
|
348
|
+
}
|
|
349
|
+
memmove(e->data + e->cursor + insert_len, e->data + e->cursor,
|
|
350
|
+
e->len - e->cursor + 1);
|
|
351
|
+
memcpy(e->data + e->cursor, insert, insert_len);
|
|
352
|
+
e->cursor += 1 + inner_len;
|
|
353
|
+
e->len += insert_len;
|
|
354
|
+
e->dirty = 1;
|
|
355
|
+
free(prefix); free(code_buf); free(tail); free(base); free(insert);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
static int lc_linux_save(LcLinuxEditor *e) {
|
|
359
|
+
FILE *f;
|
|
360
|
+
if (!e->path[0]) {
|
|
361
|
+
int suffix = 0;
|
|
362
|
+
do {
|
|
363
|
+
if (suffix == 0) snprintf(e->path, sizeof e->path, "untitled.luc");
|
|
364
|
+
else snprintf(e->path, sizeof e->path, "untitled-%d.luc", suffix);
|
|
365
|
+
suffix++;
|
|
366
|
+
} while (access(e->path, F_OK) == 0 && suffix < 10000);
|
|
367
|
+
if (access(e->path, F_OK) == 0) {
|
|
368
|
+
errno = EEXIST;
|
|
369
|
+
return 0;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
f = fopen(e->path, "wb");
|
|
373
|
+
if (!f) return 0;
|
|
374
|
+
if (e->len && fwrite(e->data, 1, e->len, f) != e->len) {
|
|
375
|
+
fclose(f);
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
if (fclose(f) != 0) return 0;
|
|
379
|
+
e->dirty = 0;
|
|
380
|
+
return 1;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
static int lc_linux_load(LcLinuxEditor *e, const char *path) {
|
|
384
|
+
FILE *f = fopen(path, "rb");
|
|
385
|
+
long size;
|
|
386
|
+
if (!f) {
|
|
387
|
+
if (errno != ENOENT || !lc_linux_reserve(e, 0)) return 0;
|
|
388
|
+
e->data[0] = 0;
|
|
389
|
+
return 1;
|
|
390
|
+
}
|
|
391
|
+
if (fseek(f, 0, SEEK_END) != 0 || (size = ftell(f)) < 0 ||
|
|
392
|
+
fseek(f, 0, SEEK_SET) != 0 || !lc_linux_reserve(e, (size_t)size)) {
|
|
393
|
+
fclose(f);
|
|
394
|
+
return 0;
|
|
395
|
+
}
|
|
396
|
+
e->len = fread(e->data, 1, (size_t)size, f);
|
|
397
|
+
if (ferror(f)) {
|
|
398
|
+
fclose(f);
|
|
399
|
+
return 0;
|
|
400
|
+
}
|
|
401
|
+
fclose(f);
|
|
402
|
+
e->data[e->len] = 0;
|
|
403
|
+
e->cursor = e->len;
|
|
404
|
+
return 1;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
static void lc_linux_draw(const LcLinuxEditor *e, size_t top, int rows, int cols) {
|
|
408
|
+
size_t line = 0, i = 0, cursor_line, cursor_col = 0, scan = 0;
|
|
409
|
+
int row = 0;
|
|
410
|
+
cursor_line = lc_linux_line_number(e, e->cursor);
|
|
411
|
+
while (scan < e->cursor && e->data[scan] != '\n') { cursor_col++; scan++; }
|
|
412
|
+
|
|
413
|
+
printf("\x1b[H\x1b[2J");
|
|
414
|
+
while (row < rows - 1 && i < e->len + 1) {
|
|
415
|
+
size_t end = lc_linux_line_end(e, i), j;
|
|
416
|
+
if (line >= top) {
|
|
417
|
+
printf("\x1b[%d;1H", row + 1);
|
|
418
|
+
for (j = i; j < end && j - i < (size_t)cols; j++) {
|
|
419
|
+
unsigned char c = (unsigned char)e->data[j];
|
|
420
|
+
putchar(c >= 32 && c != 127 ? c : ' ');
|
|
421
|
+
}
|
|
422
|
+
row++;
|
|
423
|
+
}
|
|
424
|
+
if (end >= e->len) break;
|
|
425
|
+
i = end + 1;
|
|
426
|
+
line++;
|
|
427
|
+
}
|
|
428
|
+
while (row < rows - 1) {
|
|
429
|
+
printf("\x1b[%d;1H~", row + 1);
|
|
430
|
+
row++;
|
|
431
|
+
}
|
|
432
|
+
printf("\x1b[%d;1H%s%s %zu:%zu Ctrl-S save Ctrl-Q save & quit %s",
|
|
433
|
+
rows, e->path[0] ? e->path : "[No Name]",
|
|
434
|
+
e->dirty ? " [+]" : "", cursor_line + 1, cursor_col + 1,
|
|
435
|
+
e->status);
|
|
436
|
+
{
|
|
437
|
+
size_t screen_line = cursor_line >= top ? cursor_line - top : 0;
|
|
438
|
+
printf("\x1b[%zu;%zuH", screen_line + 1,
|
|
439
|
+
cursor_col + 1 > (size_t)cols ? (size_t)cols : cursor_col + 1);
|
|
440
|
+
}
|
|
441
|
+
fflush(stdout);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
static int lc_linux_key(void) {
|
|
445
|
+
unsigned char c;
|
|
446
|
+
if (read(STDIN_FILENO, &c, 1) != 1) return -1;
|
|
447
|
+
if (c != 27) return c;
|
|
448
|
+
{
|
|
449
|
+
struct timeval tv = {0, 100000};
|
|
450
|
+
fd_set fds;
|
|
451
|
+
unsigned char seq[2];
|
|
452
|
+
FD_ZERO(&fds);
|
|
453
|
+
FD_SET(STDIN_FILENO, &fds);
|
|
454
|
+
if (select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv) <= 0) return 27;
|
|
455
|
+
if (read(STDIN_FILENO, &seq[0], 1) != 1) return 27;
|
|
456
|
+
if (seq[0] != '[' || read(STDIN_FILENO, &seq[1], 1) != 1) return 27;
|
|
457
|
+
switch (seq[1]) {
|
|
458
|
+
case 'A': return 1001;
|
|
459
|
+
case 'B': return 1002;
|
|
460
|
+
case 'C': return 1003;
|
|
461
|
+
case 'D': return 1004;
|
|
462
|
+
case 'H': return 1005;
|
|
463
|
+
case 'F': return 1006;
|
|
464
|
+
default: return 0;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
static void lc_code(const char *start) {
|
|
470
|
+
LcLinuxEditor e;
|
|
471
|
+
struct termios old_term, raw;
|
|
472
|
+
struct winsize ws;
|
|
473
|
+
size_t top = 0;
|
|
474
|
+
int done = 0;
|
|
475
|
+
memset(&e, 0, sizeof e);
|
|
476
|
+
if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
|
|
477
|
+
printf("lcode needs an interactive terminal.\n");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (start && *start) {
|
|
481
|
+
struct stat st;
|
|
482
|
+
if (stat(start, &st) == 0 && S_ISDIR(st.st_mode)) {
|
|
483
|
+
fprintf(stderr, "lcode: pass a file path, not a directory.\n");
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
snprintf(e.path, sizeof e.path, "%s", start);
|
|
487
|
+
if (!lc_linux_load(&e, start)) {
|
|
488
|
+
fprintf(stderr, "lcode: cannot read '%s'.\n", start);
|
|
489
|
+
free(e.data);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
} else if (!lc_linux_reserve(&e, 0)) {
|
|
493
|
+
fprintf(stderr, "lcode: out of memory.\n");
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (tcgetattr(STDIN_FILENO, &old_term) != 0) {
|
|
497
|
+
fprintf(stderr, "lcode: cannot read terminal settings.\n");
|
|
498
|
+
free(e.data);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
raw = old_term;
|
|
502
|
+
raw.c_lflag &= (tcflag_t)~(ECHO | ICANON | IEXTEN | ISIG);
|
|
503
|
+
raw.c_iflag &= (tcflag_t)~(IXON | ICRNL);
|
|
504
|
+
raw.c_oflag &= (tcflag_t)~(OPOST);
|
|
505
|
+
raw.c_cc[VMIN] = 1;
|
|
506
|
+
raw.c_cc[VTIME] = 0;
|
|
507
|
+
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) != 0) {
|
|
508
|
+
fprintf(stderr, "lcode: cannot enable terminal input mode.\n");
|
|
509
|
+
free(e.data);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
printf("\x1b[?1049h\x1b[?25l");
|
|
513
|
+
while (!done) {
|
|
514
|
+
int rows = 24, cols = 80, key;
|
|
515
|
+
size_t cursor_line = lc_linux_line_number(&e, e.cursor);
|
|
516
|
+
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
|
|
517
|
+
if (ws.ws_row > 0) rows = ws.ws_row;
|
|
518
|
+
if (ws.ws_col > 0) cols = ws.ws_col;
|
|
519
|
+
}
|
|
520
|
+
if (rows < 3) rows = 3;
|
|
521
|
+
if (cursor_line < top) top = cursor_line;
|
|
522
|
+
if (cursor_line >= top + (size_t)(rows - 1))
|
|
523
|
+
top = cursor_line - (size_t)(rows - 2);
|
|
524
|
+
lc_linux_draw(&e, top, rows, cols);
|
|
525
|
+
key = lc_linux_key();
|
|
526
|
+
if (key < 0) break;
|
|
527
|
+
if (key == 17) {
|
|
528
|
+
if (!e.dirty) done = 1;
|
|
529
|
+
else if (lc_linux_save(&e)) done = 1;
|
|
530
|
+
else snprintf(e.status, sizeof e.status, "save failed: %s", strerror(errno));
|
|
531
|
+
} else if (key == 19) {
|
|
532
|
+
if (lc_linux_save(&e)) e.status[0] = 0;
|
|
533
|
+
else snprintf(e.status, sizeof e.status, "save failed: %s", strerror(errno));
|
|
534
|
+
} else if (key == 1001 || key == 1002) {
|
|
535
|
+
size_t start_line = lc_linux_line_start(&e, e.cursor);
|
|
536
|
+
size_t end_line = lc_linux_line_end(&e, e.cursor);
|
|
537
|
+
size_t col = e.cursor - start_line;
|
|
538
|
+
if (key == 1001 && start_line > 0) {
|
|
539
|
+
size_t prev_end = start_line - 1;
|
|
540
|
+
size_t prev_start = lc_linux_line_start(&e, prev_end);
|
|
541
|
+
size_t prev_len = prev_end - prev_start;
|
|
542
|
+
e.cursor = prev_start + (col < prev_len ? col : prev_len);
|
|
543
|
+
} else if (key == 1002 && end_line < e.len) {
|
|
544
|
+
size_t next_start = end_line + 1;
|
|
545
|
+
size_t next_end = lc_linux_line_end(&e, next_start);
|
|
546
|
+
size_t next_len = next_end - next_start;
|
|
547
|
+
e.cursor = next_start + (col < next_len ? col : next_len);
|
|
548
|
+
}
|
|
549
|
+
} else if (key == 1003) {
|
|
550
|
+
if (e.cursor < e.len) e.cursor++;
|
|
551
|
+
} else if (key == 1004) {
|
|
552
|
+
if (e.cursor > 0) e.cursor--;
|
|
553
|
+
} else if (key == 1005) {
|
|
554
|
+
e.cursor = lc_linux_line_start(&e, e.cursor);
|
|
555
|
+
} else if (key == 1006) {
|
|
556
|
+
e.cursor = lc_linux_line_end(&e, e.cursor);
|
|
557
|
+
} else if (key == 127 || key == 8) {
|
|
558
|
+
if (e.cursor > 0) {
|
|
559
|
+
memmove(e.data + e.cursor - 1, e.data + e.cursor,
|
|
560
|
+
e.len - e.cursor + 1);
|
|
561
|
+
e.cursor--;
|
|
562
|
+
e.len--;
|
|
563
|
+
e.dirty = 1;
|
|
564
|
+
}
|
|
565
|
+
} else if (key == 13) {
|
|
566
|
+
lc_linux_newline(&e);
|
|
567
|
+
} else if (key >= 32 && key <= 255) {
|
|
568
|
+
lc_linux_insert(&e, (char)key);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_term);
|
|
572
|
+
printf("\x1b[?25h\x1b[?1049l");
|
|
573
|
+
if (e.dirty)
|
|
574
|
+
fprintf(stderr, "lcode: unsaved changes were discarded.\n");
|
|
575
|
+
free(e.data);
|
|
576
|
+
}
|
|
577
|
+
#else
|
|
578
|
+
static void lc_code(const char *start) {
|
|
579
|
+
(void)start;
|
|
580
|
+
printf("lcode-linux is only for Linux builds.\n");
|
|
581
|
+
}
|
|
582
|
+
#endif
|