@hsusulist/luc 0.2.5 → 0.3.0
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 +14 -9
- package/bin/luc.js +23 -2
- package/package.json +4 -5
- package/scripts/build-linux.js +34 -0
- package/scripts/postinstall.js +6 -0
- package/scripts/postpack.js +14 -0
- package/scripts/prepack.js +27 -0
- package/src/lcode-linux/lcode.c +290 -0
- package/src/luc.h +303 -0
- package/src/luc_core.c +5407 -0
- package/src/luc_libs.c +5262 -0
- package/src/luc_trans.c +1744 -0
package/README.md
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
# @hsusulist/luc
|
|
2
2
|
|
|
3
|
-
Install and launch the [LUC](https://github.com/hsusulist/luc) language on Windows.
|
|
3
|
+
Install and launch the [LUC](https://github.com/hsusulist/luc) language on Windows or Linux.
|
|
4
4
|
|
|
5
5
|
```powershell
|
|
6
6
|
npm install -g @hsusulist/luc
|
|
7
7
|
luc --version
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
On Linux, the package compiles the runtime during installation. Install a C
|
|
11
|
+
compiler such as GCC or Clang first; set `CC` if your compiler is not named
|
|
12
|
+
`cc`. `luc --install` rebuilds the native runtime.
|
|
13
|
+
|
|
14
|
+
On Windows, the package installs the runtime into
|
|
15
|
+
`%LOCALAPPDATA%\Programs\LUC` (no PATH changes, no admin rights). If the
|
|
16
|
+
automatic install is skipped, run:
|
|
12
17
|
|
|
13
18
|
```powershell
|
|
14
19
|
luc --install
|
|
@@ -27,10 +32,10 @@ LCode is optional and is not selected by default. Selecting it installs
|
|
|
27
32
|
`lcode.exe` alongside the interpreter. To install only the editor without the
|
|
28
33
|
LUC command, use `npm install -g @hsusulist/lcode`.
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
GitHub release and verified with SHA-256 and Ed25519 (minisign
|
|
32
|
-
is installed.
|
|
35
|
+
On Windows, the npm package is a small launcher; the runtime is downloaded from
|
|
36
|
+
the signed GitHub release and verified with SHA-256 and Ed25519 (minisign
|
|
37
|
+
format) before it is installed. On Linux, the package builds from its bundled
|
|
38
|
+
LUC and LCode C sources.
|
|
33
39
|
|
|
34
|
-
The editor-only
|
|
35
|
-
|
|
36
|
-
`.sha256`, and `.sha256.minisig` files to each GitHub release.
|
|
40
|
+
The editor-only package uses the separate, signed `lcode-portable.zip` release
|
|
41
|
+
asset on Windows. On Linux it compiles the included native terminal editor.
|
package/bin/luc.js
CHANGED
|
@@ -6,6 +6,28 @@ const os = require('os');
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { spawnSync } = require('child_process');
|
|
8
8
|
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
|
|
11
|
+
if (process.platform === 'linux') {
|
|
12
|
+
const packageDir = path.join(__dirname, '..');
|
|
13
|
+
const { buildLinux } = require('../scripts/build-linux');
|
|
14
|
+
if (args[0] === '--install') {
|
|
15
|
+
buildLinux(packageDir, 'luc');
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
const binary = path.join(packageDir, 'native', 'luc');
|
|
19
|
+
if (!fs.existsSync(binary)) {
|
|
20
|
+
console.error('luc: Linux runtime is missing; run "luc --install" to build it.');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const result = spawnSync(binary, args, { stdio: 'inherit' });
|
|
24
|
+
if (result.error) {
|
|
25
|
+
console.error('luc: ' + result.error.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
29
|
+
}
|
|
30
|
+
|
|
9
31
|
function stateBinary() {
|
|
10
32
|
const local = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
11
33
|
try {
|
|
@@ -95,11 +117,10 @@ function mapInstallerArgs(args) {
|
|
|
95
117
|
}
|
|
96
118
|
|
|
97
119
|
if (process.platform !== 'win32') {
|
|
98
|
-
console.error('luc: this package
|
|
120
|
+
console.error('luc: this package supports Windows and Linux only.');
|
|
99
121
|
process.exit(1);
|
|
100
122
|
}
|
|
101
123
|
|
|
102
|
-
const args = process.argv.slice(2);
|
|
103
124
|
let binary = findBinary();
|
|
104
125
|
|
|
105
126
|
if (!binary) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hsusulist/luc",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "LUC language installer and launcher
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "LUC language installer and launcher",
|
|
5
5
|
"bin": {
|
|
6
6
|
"luc": "bin/luc.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"bin",
|
|
10
10
|
"scripts",
|
|
11
|
+
"src",
|
|
11
12
|
"install.ps1",
|
|
12
13
|
"LICENCE",
|
|
13
14
|
"README.md"
|
|
@@ -17,9 +18,7 @@
|
|
|
17
18
|
"postpack": "node scripts/postpack.js",
|
|
18
19
|
"postinstall": "node scripts/postinstall.js"
|
|
19
20
|
},
|
|
20
|
-
"os": [
|
|
21
|
-
"win32"
|
|
22
|
-
],
|
|
21
|
+
"os": ["win32", "linux"],
|
|
23
22
|
"engines": {
|
|
24
23
|
"node": ">=16"
|
|
25
24
|
},
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
function buildLinux(packageDir, name) {
|
|
8
|
+
const sourceDir = path.join(packageDir, 'src');
|
|
9
|
+
const files = ['luc_core.c', 'luc_libs.c', 'luc_trans.c'].map((file) =>
|
|
10
|
+
path.join(sourceDir, file)
|
|
11
|
+
);
|
|
12
|
+
for (const file of files) {
|
|
13
|
+
if (!fs.existsSync(file)) {
|
|
14
|
+
throw new Error('Linux source is missing from this npm package: ' + file);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const outputDir = path.join(packageDir, 'native');
|
|
19
|
+
const output = path.join(outputDir, name);
|
|
20
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
21
|
+
|
|
22
|
+
const compiler = process.env.CC || 'cc';
|
|
23
|
+
const args = ['-O2', '-std=gnu99', '-o', output].concat(files, ['-lm', '-ldl']);
|
|
24
|
+
const result = spawnSync(compiler, args, { stdio: 'inherit' });
|
|
25
|
+
if (result.error) {
|
|
26
|
+
throw new Error('could not start ' + compiler + ' to build LUC: ' + result.error.message);
|
|
27
|
+
}
|
|
28
|
+
if (result.status !== 0) {
|
|
29
|
+
throw new Error(compiler + ' failed to build LUC (exit code ' + result.status + ').');
|
|
30
|
+
}
|
|
31
|
+
return output;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { buildLinux };
|
package/scripts/postinstall.js
CHANGED
|
@@ -5,6 +5,12 @@ const os = require('os');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
|
|
8
|
+
if (process.platform === 'linux') {
|
|
9
|
+
const { buildLinux } = require('./build-linux');
|
|
10
|
+
buildLinux(path.join(__dirname, '..'), 'luc');
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
function findBinary() {
|
|
9
15
|
const candidates = [];
|
|
10
16
|
const local = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
package/scripts/postpack.js
CHANGED
|
@@ -9,3 +9,17 @@ for (const name of ['install.ps1', 'LICENCE']) {
|
|
|
9
9
|
} catch (_) {
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
const source = path.join(process.cwd(), 'src');
|
|
14
|
+
const marker = path.join(process.cwd(), '.npm-source-bundle-generated');
|
|
15
|
+
if (fs.existsSync(marker)) {
|
|
16
|
+
fs.rmSync(source, { recursive: true, force: true });
|
|
17
|
+
fs.unlinkSync(marker);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const helper = path.join(process.cwd(), 'scripts', 'build-linux.js');
|
|
21
|
+
const helperMarker = path.join(process.cwd(), '.npm-build-linux-generated');
|
|
22
|
+
if (fs.existsSync(helperMarker)) {
|
|
23
|
+
fs.unlinkSync(helper);
|
|
24
|
+
fs.unlinkSync(helperMarker);
|
|
25
|
+
}
|
package/scripts/prepack.js
CHANGED
|
@@ -17,3 +17,30 @@ function copy(name) {
|
|
|
17
17
|
|
|
18
18
|
copy('install.ps1');
|
|
19
19
|
copy('LICENCE');
|
|
20
|
+
|
|
21
|
+
const source = path.join(root, 'src');
|
|
22
|
+
const target = path.join(here, 'src');
|
|
23
|
+
if (fs.existsSync(target)) {
|
|
24
|
+
throw new Error('refusing to overwrite existing npm/src directory');
|
|
25
|
+
}
|
|
26
|
+
fs.mkdirSync(target);
|
|
27
|
+
for (const name of ['luc_core.c', 'luc_libs.c', 'luc_trans.c', 'luc.h']) {
|
|
28
|
+
const from = path.join(source, name);
|
|
29
|
+
if (!fs.existsSync(from)) throw new Error('missing release source: ' + from);
|
|
30
|
+
fs.copyFileSync(from, path.join(target, name));
|
|
31
|
+
}
|
|
32
|
+
const linuxEditor = path.join(source, 'lcode-linux', 'lcode.c');
|
|
33
|
+
if (!fs.existsSync(linuxEditor)) throw new Error('missing Linux editor source: ' + linuxEditor);
|
|
34
|
+
fs.mkdirSync(path.join(target, 'lcode-linux'));
|
|
35
|
+
fs.copyFileSync(linuxEditor, path.join(target, 'lcode-linux', 'lcode.c'));
|
|
36
|
+
fs.writeFileSync(path.join(here, '.npm-source-bundle-generated'), 'generated by prepack\n');
|
|
37
|
+
|
|
38
|
+
const helper = path.join(__dirname, 'build-linux.js');
|
|
39
|
+
const packagedHelper = path.join(here, 'scripts', 'build-linux.js');
|
|
40
|
+
if (helper !== packagedHelper) {
|
|
41
|
+
if (fs.existsSync(packagedHelper)) {
|
|
42
|
+
throw new Error('refusing to overwrite existing scripts/build-linux.js');
|
|
43
|
+
}
|
|
44
|
+
fs.copyFileSync(helper, packagedHelper);
|
|
45
|
+
fs.writeFileSync(path.join(here, '.npm-build-linux-generated'), 'generated by prepack\n');
|
|
46
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
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_save(LcLinuxEditor *e) {
|
|
67
|
+
FILE *f;
|
|
68
|
+
if (!e->path[0]) {
|
|
69
|
+
int suffix = 0;
|
|
70
|
+
do {
|
|
71
|
+
if (suffix == 0) snprintf(e->path, sizeof e->path, "untitled.luc");
|
|
72
|
+
else snprintf(e->path, sizeof e->path, "untitled-%d.luc", suffix);
|
|
73
|
+
suffix++;
|
|
74
|
+
} while (access(e->path, F_OK) == 0 && suffix < 10000);
|
|
75
|
+
if (access(e->path, F_OK) == 0) {
|
|
76
|
+
errno = EEXIST;
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
f = fopen(e->path, "wb");
|
|
81
|
+
if (!f) return 0;
|
|
82
|
+
if (e->len && fwrite(e->data, 1, e->len, f) != e->len) {
|
|
83
|
+
fclose(f);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
if (fclose(f) != 0) return 0;
|
|
87
|
+
e->dirty = 0;
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static int lc_linux_load(LcLinuxEditor *e, const char *path) {
|
|
92
|
+
FILE *f = fopen(path, "rb");
|
|
93
|
+
long size;
|
|
94
|
+
if (!f) {
|
|
95
|
+
if (errno != ENOENT || !lc_linux_reserve(e, 0)) return 0;
|
|
96
|
+
e->data[0] = 0;
|
|
97
|
+
return 1;
|
|
98
|
+
}
|
|
99
|
+
if (fseek(f, 0, SEEK_END) != 0 || (size = ftell(f)) < 0 ||
|
|
100
|
+
fseek(f, 0, SEEK_SET) != 0 || !lc_linux_reserve(e, (size_t)size)) {
|
|
101
|
+
fclose(f);
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
e->len = fread(e->data, 1, (size_t)size, f);
|
|
105
|
+
if (ferror(f)) {
|
|
106
|
+
fclose(f);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
fclose(f);
|
|
110
|
+
e->data[e->len] = 0;
|
|
111
|
+
e->cursor = e->len;
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static void lc_linux_draw(const LcLinuxEditor *e, size_t top, int rows, int cols) {
|
|
116
|
+
size_t line = 0, i = 0, cursor_line, cursor_col = 0, scan = 0;
|
|
117
|
+
int row = 0;
|
|
118
|
+
cursor_line = lc_linux_line_number(e, e->cursor);
|
|
119
|
+
while (scan < e.cursor && e->data[scan] != '\n') { cursor_col++; scan++; }
|
|
120
|
+
|
|
121
|
+
printf("\x1b[H\x1b[2J");
|
|
122
|
+
while (row < rows - 1 && i < e->len + 1) {
|
|
123
|
+
size_t end = lc_linux_line_end(e, i), j;
|
|
124
|
+
if (line >= top) {
|
|
125
|
+
printf("\x1b[%d;1H", row + 1);
|
|
126
|
+
for (j = i; j < end && j - i < (size_t)cols; j++) {
|
|
127
|
+
unsigned char c = (unsigned char)e->data[j];
|
|
128
|
+
putchar(c >= 32 && c != 127 ? c : ' ');
|
|
129
|
+
}
|
|
130
|
+
row++;
|
|
131
|
+
}
|
|
132
|
+
if (end >= e->len) break;
|
|
133
|
+
i = end + 1;
|
|
134
|
+
line++;
|
|
135
|
+
}
|
|
136
|
+
while (row < rows - 1) {
|
|
137
|
+
printf("\x1b[%d;1H~", row + 1);
|
|
138
|
+
row++;
|
|
139
|
+
}
|
|
140
|
+
printf("\x1b[%d;1H%s%s %zu:%zu Ctrl-S save Ctrl-Q save & quit %s",
|
|
141
|
+
rows, e->path[0] ? e->path : "[No Name]",
|
|
142
|
+
e->dirty ? " [+]" : "", cursor_line + 1, cursor_col + 1,
|
|
143
|
+
e->status);
|
|
144
|
+
{
|
|
145
|
+
size_t screen_line = cursor_line >= top ? cursor_line - top : 0;
|
|
146
|
+
printf("\x1b[%zu;%zuH", screen_line + 1,
|
|
147
|
+
cursor_col + 1 > (size_t)cols ? (size_t)cols : cursor_col + 1);
|
|
148
|
+
}
|
|
149
|
+
fflush(stdout);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static int lc_linux_key(void) {
|
|
153
|
+
unsigned char c;
|
|
154
|
+
if (read(STDIN_FILENO, &c, 1) != 1) return -1;
|
|
155
|
+
if (c != 27) return c;
|
|
156
|
+
{
|
|
157
|
+
struct timeval tv = {0, 100000};
|
|
158
|
+
fd_set fds;
|
|
159
|
+
unsigned char seq[2];
|
|
160
|
+
FD_ZERO(&fds);
|
|
161
|
+
FD_SET(STDIN_FILENO, &fds);
|
|
162
|
+
if (select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv) <= 0) return 27;
|
|
163
|
+
if (read(STDIN_FILENO, &seq[0], 1) != 1) return 27;
|
|
164
|
+
if (seq[0] != '[' || read(STDIN_FILENO, &seq[1], 1) != 1) return 27;
|
|
165
|
+
switch (seq[1]) {
|
|
166
|
+
case 'A': return 1001;
|
|
167
|
+
case 'B': return 1002;
|
|
168
|
+
case 'C': return 1003;
|
|
169
|
+
case 'D': return 1004;
|
|
170
|
+
case 'H': return 1005;
|
|
171
|
+
case 'F': return 1006;
|
|
172
|
+
default: return 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static void lc_code(const char *start) {
|
|
178
|
+
LcLinuxEditor e;
|
|
179
|
+
struct termios old_term, raw;
|
|
180
|
+
struct winsize ws;
|
|
181
|
+
size_t top = 0;
|
|
182
|
+
int done = 0;
|
|
183
|
+
memset(&e, 0, sizeof e);
|
|
184
|
+
if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
|
|
185
|
+
printf("lcode needs an interactive terminal.\n");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (start && *start) {
|
|
189
|
+
struct stat st;
|
|
190
|
+
if (stat(start, &st) == 0 && S_ISDIR(st.st_mode)) {
|
|
191
|
+
fprintf(stderr, "lcode: pass a file path, not a directory.\n");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
snprintf(e.path, sizeof e.path, "%s", start);
|
|
195
|
+
if (!lc_linux_load(&e, start)) {
|
|
196
|
+
fprintf(stderr, "lcode: cannot read '%s'.\n", start);
|
|
197
|
+
free(e.data);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
} else if (!lc_linux_reserve(&e, 0)) {
|
|
201
|
+
fprintf(stderr, "lcode: out of memory.\n");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (tcgetattr(STDIN_FILENO, &old_term) != 0) {
|
|
205
|
+
fprintf(stderr, "lcode: cannot read terminal settings.\n");
|
|
206
|
+
free(e.data);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
raw = old_term;
|
|
210
|
+
raw.c_lflag &= (tcflag_t)~(ECHO | ICANON | IEXTEN | ISIG);
|
|
211
|
+
raw.c_iflag &= (tcflag_t)~(IXON | ICRNL);
|
|
212
|
+
raw.c_oflag &= (tcflag_t)~(OPOST);
|
|
213
|
+
raw.c_cc[VMIN] = 1;
|
|
214
|
+
raw.c_cc[VTIME] = 0;
|
|
215
|
+
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) != 0) {
|
|
216
|
+
fprintf(stderr, "lcode: cannot enable terminal input mode.\n");
|
|
217
|
+
free(e.data);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
printf("\x1b[?1049h\x1b[?25l");
|
|
221
|
+
while (!done) {
|
|
222
|
+
int rows = 24, cols = 80, key;
|
|
223
|
+
size_t cursor_line = lc_linux_line_number(&e, e.cursor);
|
|
224
|
+
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
|
|
225
|
+
if (ws.ws_row > 0) rows = ws.ws_row;
|
|
226
|
+
if (ws.ws_col > 0) cols = ws.ws_col;
|
|
227
|
+
}
|
|
228
|
+
if (rows < 3) rows = 3;
|
|
229
|
+
if (cursor_line < top) top = cursor_line;
|
|
230
|
+
if (cursor_line >= top + (size_t)(rows - 1))
|
|
231
|
+
top = cursor_line - (size_t)(rows - 2);
|
|
232
|
+
lc_linux_draw(&e, top, rows, cols);
|
|
233
|
+
key = lc_linux_key();
|
|
234
|
+
if (key < 0) break;
|
|
235
|
+
if (key == 17) {
|
|
236
|
+
if (!e.dirty) done = 1;
|
|
237
|
+
else if (lc_linux_save(&e)) done = 1;
|
|
238
|
+
else snprintf(e.status, sizeof e.status, "save failed: %s", strerror(errno));
|
|
239
|
+
} else if (key == 19) {
|
|
240
|
+
if (lc_linux_save(&e)) e.status[0] = 0;
|
|
241
|
+
else snprintf(e.status, sizeof e.status, "save failed: %s", strerror(errno));
|
|
242
|
+
} else if (key == 1001 || key == 1002) {
|
|
243
|
+
size_t start_line = lc_linux_line_start(&e, e.cursor);
|
|
244
|
+
size_t end_line = lc_linux_line_end(&e, e.cursor);
|
|
245
|
+
size_t col = e.cursor - start_line;
|
|
246
|
+
if (key == 1001 && start_line > 0) {
|
|
247
|
+
size_t prev_end = start_line - 1;
|
|
248
|
+
size_t prev_start = lc_linux_line_start(&e, prev_end);
|
|
249
|
+
size_t prev_len = prev_end - prev_start;
|
|
250
|
+
e.cursor = prev_start + (col < prev_len ? col : prev_len);
|
|
251
|
+
} else if (key == 1002 && end_line < e.len) {
|
|
252
|
+
size_t next_start = end_line + 1;
|
|
253
|
+
size_t next_end = lc_linux_line_end(&e, next_start);
|
|
254
|
+
size_t next_len = next_end - next_start;
|
|
255
|
+
e.cursor = next_start + (col < next_len ? col : next_len);
|
|
256
|
+
}
|
|
257
|
+
} else if (key == 1003) {
|
|
258
|
+
if (e.cursor < e.len) e.cursor++;
|
|
259
|
+
} else if (key == 1004) {
|
|
260
|
+
if (e.cursor > 0) e.cursor--;
|
|
261
|
+
} else if (key == 1005) {
|
|
262
|
+
e.cursor = lc_linux_line_start(&e, e.cursor);
|
|
263
|
+
} else if (key == 1006) {
|
|
264
|
+
e.cursor = lc_linux_line_end(&e, e.cursor);
|
|
265
|
+
} else if (key == 127 || key == 8) {
|
|
266
|
+
if (e.cursor > 0) {
|
|
267
|
+
memmove(e.data + e.cursor - 1, e.data + e.cursor,
|
|
268
|
+
e.len - e.cursor + 1);
|
|
269
|
+
e.cursor--;
|
|
270
|
+
e.len--;
|
|
271
|
+
e.dirty = 1;
|
|
272
|
+
}
|
|
273
|
+
} else if (key == 13) {
|
|
274
|
+
lc_linux_insert(&e, '\n');
|
|
275
|
+
} else if (key >= 32 && key <= 255) {
|
|
276
|
+
lc_linux_insert(&e, (char)key);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_term);
|
|
280
|
+
printf("\x1b[?25h\x1b[?1049l");
|
|
281
|
+
if (e.dirty)
|
|
282
|
+
fprintf(stderr, "lcode: unsaved changes were discarded.\n");
|
|
283
|
+
free(e.data);
|
|
284
|
+
}
|
|
285
|
+
#else
|
|
286
|
+
static void lc_code(const char *start) {
|
|
287
|
+
(void)start;
|
|
288
|
+
printf("lcode-linux is only for Linux builds.\n");
|
|
289
|
+
}
|
|
290
|
+
#endif
|