ESH

Executive Shell
git clone http://git.omkov.net/ESH
Log | Tree | Refs | README | Download

ESH/src/lineread/lineread.c (386 lines, 12 KiB) -rw-r--r-- file download

a1eb486 Jakob Wakeling 2023-12-27 13:01:35
0
// Copyright (C) 2021, Jakob Wakeling
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
1
// All rights reserved.
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
2
4a1246f Jakob Wakeling 2023-12-28 14:53:40
3
#include "../util/log.h"
4a1246f Jakob Wakeling 2023-12-28 14:53:40
4
#include "../util/util.h"
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
5
#include "lineread.h"
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
6
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
7
#include <sys/ioctl.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
8
#include <termios.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
9
#include <unistd.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
10
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
11
#include <errno.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
12
#include <stdbool.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
13
#include <stddef.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
14
#include <stdio.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
15
#include <stdlib.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
16
#include <string.h>
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
17
4a1246f Jakob Wakeling 2023-12-28 14:53:40
18
typedef struct { char *s; uptr sp, sl, sc; } line;
4a1246f Jakob Wakeling 2023-12-28 14:53:40
19
typedef struct { line *a; uptr ap, ah, at, al, ac; } hist;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
20
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
21
static struct termios tco, tcn;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
22
static bool rawflag = false, ateflag = false;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
23
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
24
static hist h = { NULL, 0, 0, 0, 0 };
4a1246f Jakob Wakeling 2023-12-28 14:53:40
25
static const char *const prompt = "$ ";
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
26
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
27
static char *linentty(void);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
28
static char *lineedit(void);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
29
static void  line_esc(line *l, register int c);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
30
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
31
static inline void tcraw(void);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
32
static inline void tcrestore(void);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
33
static size_t getcols(void);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
34
static void clearscreen(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
35
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
36
static void line_refresh(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
37
static void line_reset(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
38
static void line_move_left(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
39
static void line_move_right(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
40
static void line_move_word_home(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
41
static void line_move_word_end(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
42
static void line_move_home(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
43
static void line_move_end(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
44
static void line_push(line *l, char c);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
45
static int  line_insert(line *l, char c);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
46
static void line_backspace(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
47
static void line_delete(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
48
static void line_delete_word_home(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
49
static void line_delete_word_end(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
50
static void line_delete_home(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
51
static void line_delete_end(line *l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
52
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
53
static int  hist_init(hist *h);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
54
static void hist_free(hist *h);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
55
static void hist_move_prior(hist *h);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
56
static void hist_move_next(hist *h);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
57
static void hist_move_home(hist *h);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
58
static void hist_move_end(hist *h);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
59
static void hist_push(hist *h, line l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
60
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
61
/* Read a line from stdin */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
62
char *lineread(void) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
63
	if (!isatty(STDIN_FILENO)) { errno = 0; return linentty(); }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
64
	else { return lineedit(); }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
65
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
66
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
67
/* Free memory allocated by lineread */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
68
void linefree(void) { hist_free(&h); }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
69
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
70
/* Read from a non-terminal stdin */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
71
static char *linentty(void) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
72
	line l; register char *r;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
73
	l.sp = 0; l.sl = 0; l.sc = 1024;
4a1246f Jakob Wakeling 2023-12-28 14:53:40
74
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
75
	if (!(l.s = xmalloc(l.sc * sizeof (*l.s)))) { return NULL; }
4a1246f Jakob Wakeling 2023-12-28 14:53:40
76
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
77
	for (register int c; (c = fgetc(stdin));) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
78
		if (c == EOF) { if (l.sl) { break; } r = NULL; goto ret; }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
79
		else { line_push(&l, c); } continue;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
80
	}
4a1246f Jakob Wakeling 2023-12-28 14:53:40
81
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
82
end:;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
83
	r = strndup(l.s, l.sl);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
84
ret:;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
85
	free(l.s); return r;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
86
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
87
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
88
#define l h.a[h.ap]
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
89
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
90
/* Dynamically read a line from stdin */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
91
static char *lineedit(void) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
92
	register char *r;
4a1246f Jakob Wakeling 2023-12-28 14:53:40
93
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
94
	{
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
95
		if (h.a == NULL) { if (hist_init(&h)) { return NULL; } }
4a1246f Jakob Wakeling 2023-12-28 14:53:40
96
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
97
		line m = { NULL, 0, 0, 1024 };
4a1246f Jakob Wakeling 2023-12-28 14:53:40
98
4a1246f Jakob Wakeling 2023-12-28 14:53:40
99
		// if (!(m.cols = getcols())) { return NULL; }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
100
		if (!(m.s = xmalloc(m.sc * sizeof (*m.s)))) { return NULL; } m.s[0] = 0;
4a1246f Jakob Wakeling 2023-12-28 14:53:40
101
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
102
		hist_push(&h, m);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
103
	}
4a1246f Jakob Wakeling 2023-12-28 14:53:40
104
4a1246f Jakob Wakeling 2023-12-28 14:53:40
105
	tcraw(); fputs(prompt, stdout);
4a1246f Jakob Wakeling 2023-12-28 14:53:40
106
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
107
	for (register int c; (c = fgetc(stdin));) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
108
		if (errno) { log_warn("FIXME: %s", strerror(errno)); errno = 0; }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
109
		// printf("%02X\n", c); continue;
4a1246f Jakob Wakeling 2023-12-28 14:53:40
110
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
111
		switch (c) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
112
		case '\x01': { line_move_home(&l);        } continue; // CTRL + A
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
113
		case '\x02': { line_move_left(&l);        } continue; // CTRL + B
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
114
		case '\x03': { line_reset(&l);            } continue; // CTRL + C
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
115
		case '\x04': { r = NULL;                  } goto ret; // CTRL + D
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
116
		case '\x05': { line_move_end(&l);         } continue; // CTRL + E
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
117
		case '\x06': { line_move_right(&l);       } continue; // CTRL + F
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
118
		case '\x07': {} continue;                      // IGNORE CTRL + G
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
119
		case '\x08': { line_backspace(&l);        } continue; // CTRL + H
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
120
		case '\x09': {} continue;                      // IGNORE CTRL + I
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
121
		case '\x0A': {} goto end;                             // CTRL + J
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
122
		case '\x0B': { line_delete_end(&l);       } continue; // CTRL + K
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
123
		case '\x0C': { clearscreen(&l);           } continue; // CTRL + L
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
124
		case '\x0D': {} goto end;                    // ENTER or CTRL + M
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
125
		case '\x0E': { /* Next history */         } continue; // CTRL + N
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
126
		case '\x0F': {} goto end;                             // CTRL + O
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
127
		case '\x10': { /* Prior history */        } continue; // CTRL + P
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
128
		case '\x11': { /* Start output */         } continue; // CTRL + Q
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
129
		case '\x12': {} continue;                      // IGNORE CTRL + R
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
130
		case '\x13': { /* Stop output */          } continue; // CTRL + S
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
131
		case '\x14': { /* Swap with prior */      } continue; // CTRL + T
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
132
		case '\x15': { line_delete_home(&l);      } continue; // CTRL + U
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
133
		case '\x16': { /* Insert char code */     } continue; // CTRL + V
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
134
		case '\x17': { line_delete_word_home(&l); } continue; // CTRL + W
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
135
		case '\x18': {} continue;                      // IGNORE CTRL + X
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
136
		case '\x19': { /* Paste deleted */        } continue; // CTRL + Y
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
137
		case '\x1B': switch ((c = fgetc(stdin))) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
138
			case 'b': { line_move_word_home(&l);  } continue; // ALT + B
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
139
			case 'd': { line_delete_word_end(&l); } continue; // ALT + D
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
140
			case 'f': { line_move_word_end(&l);   } continue; // ALT + F
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
141
			case '[': switch ((c = fgetc(stdin))) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
142
				case '1': case '2': case '3': case '4': case '5': case '6':
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
143
				case '7': case '8': case '9': { line_esc(&l, c); } continue;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
144
				case 'A': { hist_move_prior(&h); } continue; // UP
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
145
				case 'B': { hist_move_next(&h);  } continue; // DOWN
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
146
				case 'C': { line_move_right(&l); } continue; // RIGHT
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
147
				case 'D': { line_move_left(&l);  } continue; // LEFT
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
148
				case 'F': { line_move_end(&l);   } continue; // END
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
149
				case 'H': { line_move_home(&l);  } continue; // HOME
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
150
				default:  {} continue;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
151
			}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
152
			default:  {} continue;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
153
		}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
154
		case '\x7F': { line_backspace(&l);        } continue; // BACKSPACE
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
155
		default:     { line_insert(&l, c);        } continue;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
156
		}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
157
	}
4a1246f Jakob Wakeling 2023-12-28 14:53:40
158
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
159
end:;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
160
	r = strndup(l.s, l.sl);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
161
ret:;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
162
	tcrestore(); fputc('\n', stdout); return r;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
163
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
164
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
165
#undef l // h.a[h.ap]
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
166
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
167
/* Handle an extended ^[ sequence */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
168
static void line_esc(line *l, register int c) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
169
	switch (c) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
170
	case '2': switch ((c = fgetc(stdin))) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
171
		case '~': { /* Insert char code */ } return; // INSERT
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
172
	}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
173
	case '3': switch ((c = fgetc(stdin))) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
174
		case '~': { line_delete(l); } return; // DELETE
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
175
	}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
176
	case '5': switch ((c = fgetc(stdin))) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
177
		case '~': { hist_move_home(&h); } return; // PAGE UP
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
178
	}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
179
	case '6': switch ((c = fgetc(stdin))) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
180
		case '~': { hist_move_end(&h); } return; // PAGE DOWN
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
181
	}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
182
	}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
183
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
184
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
185
/* Put stdin into raw mode */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
186
static inline void tcraw(void) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
187
	if (rawflag) { return; }
4a1246f Jakob Wakeling 2023-12-28 14:53:40
188
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
189
	tcgetattr(STDIN_FILENO, &tco); tcn = tco;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
190
	if (!ateflag) { atexit(tcrestore); ateflag = true; }
4a1246f Jakob Wakeling 2023-12-28 14:53:40
191
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
192
	/* No break, no CR to NL, no parity check, no strip bit, no flow control */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
193
	tcn.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
194
	/* No post process, 8-bit chars */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
195
	tcn.c_oflag &= ~(OPOST); tcn.c_cflag |= (CS8);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
196
	/* Canonical off, echo off, no extensions, no signal characters */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
197
	tcn.c_lflag &= ~(ICANON | ECHO | IEXTEN | ISIG);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
198
	/* Read every byte with no delay */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
199
	tcn.c_cc[VMIN] = 1; tcn.c_cc[VTIME] = 0;
4a1246f Jakob Wakeling 2023-12-28 14:53:40
200
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
201
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tcn); rawflag = true; return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
202
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
203
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
204
/* Restore stdin to canonical mode */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
205
static inline void tcrestore(void) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
206
	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tco); rawflag = false; return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
207
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
208
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
209
/* Get the number of columns in the terminal */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
210
static size_t getcols(void) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
211
	struct winsize ws;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
212
	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) { return 0; }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
213
	return ws.ws_col;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
214
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
215
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
216
/* Clear the screen */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
217
static void clearscreen(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
218
	fputs("\x1B[H\x1B[2J", stdout); line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
219
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
220
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
221
/* Refresh line */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
222
static void line_refresh(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
223
	fputs("\r\x1B[0K", stdout);
4a1246f Jakob Wakeling 2023-12-28 14:53:40
224
	fputs(prompt, stdout); fputs(l->s, stdout);
4a1246f Jakob Wakeling 2023-12-28 14:53:40
225
	fprintf(stdout, "\r\x1B[%zuC", strlen(prompt) + l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
226
	return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
227
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
228
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
229
/* Reset line */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
230
static void line_reset(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
231
	l->s[0] = 0; l->sp = 0; l->sl = 0;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
232
	fputs("^C\n", stdout); line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
233
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
234
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
235
/* Move cursor left */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
236
static void line_move_left(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
237
	if (l->sp) { --l->sp; fputs("\x1B[D", stdout); } return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
238
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
239
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
240
/* Move cursor right */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
241
static void line_move_right(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
242
	if (l->sp != l->sl) { ++l->sp; fputs("\x1B[C", stdout); } return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
243
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
244
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
245
/* Move cursor to the word home */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
246
static void line_move_word_home(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
247
	for (; l->sp && l->s[l->sp - 1] == ' '; --l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
248
	for (; l->sp && l->s[l->sp - 1] != ' '; --l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
249
	line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
250
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
251
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
252
/* Move cursor to the word end */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
253
static void line_move_word_end(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
254
	for (; l->sp != l->sl && l->s[l->sp] == ' '; ++l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
255
	for (; l->sp != l->sl && l->s[l->sp] != ' '; ++l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
256
	line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
257
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
258
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
259
/* Move cursor to the line home */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
260
static void line_move_home(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
261
	l->sp = 0; line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
262
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
263
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
264
/* Move cursor to the line end */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
265
static void line_move_end(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
266
	l->sp = l->sl; line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
267
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
268
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
269
/* Push character onto end of line */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
270
static void line_push(line *l, char c) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
271
	if (l->sl + 1 > l->sc) { l->sc *= 2;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
272
		l->s = xrealloc(l->s, l->sc * sizeof (*l->s));
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
273
	} l->s[l->sl] = c; l->s[++l->sl] = 0; return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
274
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
275
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
276
/* Insert character at cursor */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
277
static int line_insert(line *l, char c) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
278
	if (l->sl + 1 == l->sc) { /* TODO expand string */ }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
279
	if (l->sp == l->sl) { // Cursor is at line end
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
280
		l->s[l->sp++] = c; l->s[++l->sl] = 0;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
281
		return fputc(c, stdout);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
282
	}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
283
	else {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
284
		memmove(l->s + l->sp + 1, l->s + l->sp, l->sl - l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
285
		l->s[l->sp++] = c; l->s[++l->sl] = 0; line_refresh(l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
286
	} return c;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
287
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
288
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
289
/* Delete character before cursor */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
290
static void line_backspace(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
291
	if (l->sp) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
292
		memmove(l->s + l->sp - 1, l->s + l->sp, l->sl - l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
293
		--l->sp; l->s[--l->sl] = 0; line_refresh(l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
294
	} return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
295
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
296
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
297
/* Delete character at cursor */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
298
static void line_delete(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
299
	if (l->sp != l->sl) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
300
		memmove(l->s + l->sp, l->s + l->sp + 1, l->sl - l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
301
		l->s[--l->sl] = 0; line_refresh(l);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
302
	} return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
303
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
304
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
305
/* Delete the word preceeding the cursor */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
306
static void line_delete_word_home(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
307
	size_t p = l->sp;
4a1246f Jakob Wakeling 2023-12-28 14:53:40
308
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
309
	for (; l->sp && l->s[l->sp - 1] == ' '; --l->sp);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
310
	for (; l->sp && l->s[l->sp - 1] != ' '; --l->sp);
4a1246f Jakob Wakeling 2023-12-28 14:53:40
311
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
312
	memmove(l->s + l->sp, l->s + p, l->sl - p + 1);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
313
	l->sl -= p - l->sp; line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
314
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
315
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
316
/* Delete the word following the cursor */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
317
static void line_delete_word_end(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
318
	size_t p = l->sp;
4a1246f Jakob Wakeling 2023-12-28 14:53:40
319
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
320
	for (; p != l->sl && l->s[p] == ' '; ++p);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
321
	for (; p != l->sl && l->s[p] != ' '; ++p);
4a1246f Jakob Wakeling 2023-12-28 14:53:40
322
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
323
	memmove(l->s + l->sp, l->s + p, l->sl - p + 1);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
324
	l->sl -= p - l->sp; line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
325
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
326
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
327
/* Delete characters from cursor to home */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
328
static void line_delete_home(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
329
	memmove(l->s, l->s + l->sp, l->sl - l->sp + 1);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
330
	l->sl -= l->sp; l->sp = 0; line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
331
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
332
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
333
/* Delete characters from cursor to end */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
334
static void line_delete_end(line *l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
335
	l->s[(l->sl = l->sp)] = 0; line_refresh(l); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
336
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
337
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
338
/* Initialise history */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
339
static int hist_init(hist *h) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
340
	/* FIXME do not hardcode history size */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
341
	h->ap = 0; h->ah = 0; h->at = 0; h->al = 0; h->ac = 1000 + 1;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
342
	if (!(h->a = xcalloc(h->ac, sizeof (*h->a)))) { return 1; }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
343
	/* TODO load history from file */ return 0;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
344
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
345
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
346
/* Free history */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
347
static void hist_free(hist *h) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
348
	for (size_t i = 0; i != h->ac; ++i) { free(h->a[i].s); } free(h->a);
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
349
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
350
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
351
/* Move backwards in history */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
352
static void hist_move_prior(hist *h) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
353
	if (h->ap != h->ah) { h->ap == 0 ? h->ap = h->ac - 1 : --h->ap; }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
354
	line_refresh(&h->a[h->ap]); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
355
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
356
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
357
/* Move forwards in history */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
358
static void hist_move_next(hist *h) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
359
	register size_t an = h->ap == h->ac - 1 ? 0 : h->ap + 1;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
360
	if (an != h->at) { h->ap = an; }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
361
	line_refresh(&h->a[h->ap]); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
362
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
363
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
364
/* Move to the start of history */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
365
static void hist_move_home(hist *h) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
366
	/* TODO */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
367
	h->ap = h->ah; line_refresh(&h->a[h->ap]); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
368
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
369
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
370
/* Move to the end of history */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
371
static void hist_move_end(hist *h) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
372
	/* TODO */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
373
	// h->ap = h->at; lineRefresh(&h->a[h->ap]); return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
374
}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
375
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
376
/* Push a line onto the end of history */
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
377
static void hist_push(hist *h, line l) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
378
	if (h->al != h->ac) {
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
379
		++h->al; h->ap = h->at; if (++h->at == h->ac) { h->at = 0; }
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
380
	}
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
381
	else { h->ap = h->at; if (++h->at == h->ac) { h->at = 0; } h->ah = h->at; }
4a1246f Jakob Wakeling 2023-12-28 14:53:40
382
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
383
	free(h->a[h->ap].s); h->a[h->ap] = l; return;
a1eb486 Jakob Wakeling 2023-12-27 13:01:35
384
}
385