Semi-decennial update. 50% code inflation.
[cbaos.git] / cbashell / cbashell.c
blobea4d1ebdc78e1dc73944890c8e6cda18a7ca8138
1 /* Author: Domen Puncer <domen@cba.si>. License: WTFPL, see file LICENSE */
2 #include <stdio.h>
3 #include <string.h>
4 #include <compiler.h>
6 #include "command.h"
8 #define HIST_SIZE 5
9 #define LINE_LEN 64
10 static char history[HIST_SIZE][LINE_LEN+1]; /* circ buf of nul terminated strings should be more effective */
11 static int hist_len = 0;
12 static int hist_pos = -1;
14 static char line[LINE_LEN+1];
15 static int pos;
16 static int len;
18 enum vt100_key {
19 K_CTRL_C = 3,
20 K_CTRL_H = 8,
21 K_BACKSPACE = 8,
22 K_CTRL_U = '\x15',
23 //K_ENTER = '\r',
24 K_ENTER = '\n',
25 K_ESCAPE = '\x1b',
26 K_BACKSPACE2 = '\x7f',
28 /* below are our made up values */
29 /* vt100 start */
30 K_VT100_START = 0x100,
31 K_UP,
32 K_DOWN,
33 K_RIGHT,
34 K_LEFT,
35 K_END,
36 K_HOME,
37 K_INSERT,
38 K_DELETE,
39 K_PGUP,
40 K_PGDN,
42 /* internal marks */
43 K_PARTIAL = 0xfff0,
44 K_INVALID,
47 struct vt100_seq {
48 enum vt100_key key;
49 const char *seq;
52 static const struct vt100_seq vt100_seqs[] = {
53 { .key = K_UP, .seq = "[A" },
54 { .key = K_DOWN, .seq = "[B" },
55 { .key = K_RIGHT, .seq = "[C" },
56 { .key = K_LEFT, .seq = "[D" },
57 { .key = K_END, .seq = "[F" },
58 { .key = K_HOME, .seq = "[H" },
59 { .key = K_INSERT, .seq = "[2~" },
60 { .key = K_DELETE, .seq = "[3~" },
61 { .key = K_PGUP, .seq = "[5~" },
62 { .key = K_PGDN, .seq = "[6~" },
66 static int vt100_cmp(const char *seq, int len, const char *ntseq)
68 int i;
69 for (i=0; i<len; i++)
70 if (seq[i] != ntseq[i])
71 return -1;
73 /* full match */
74 if (ntseq[i] == '\0')
75 return 0;
77 /* matched, but the sequence wasn't complete */
78 return 1;
81 #if 0
82 static const char *vt100_seq(enum vt100_key key)
84 int i;
85 for (i=0; i<ALEN(vt100_seqs); i++) {
86 if (vt100_seqs[i].key == key)
87 return vt100_seqs[i].seq;
89 return NULL;
91 #endif
93 static enum vt100_key vt100_match(const char *seq, int len)
95 int i;
97 for (i=0; i<ALEN(vt100_seqs); i++) {
98 int m;
99 m = vt100_cmp(seq, len, vt100_seqs[i].seq);
100 if (m == 0)
101 return vt100_seqs[i].key;
102 else if (m == 1)
103 return K_PARTIAL;
105 return K_INVALID;
109 static void cbashell_command(const char *cmd)
111 int i;
113 for (i=0; command_list[i]; i++)
114 if (command_list[i](cmd) != 0)
115 break;
120 * readline like prompt handling
121 * TODO history should be improved with a circ buf for string storage or sth,
122 * to be more effective.
124 static int cbashell_char(int c)
126 if (hist_pos >= 0 && !(c == K_UP || c == K_DOWN)) {
127 strcpy(line, history[hist_pos]);
128 hist_pos = -1;
131 if (c >= ' ' && c < '\x7f') {
132 /* guard against buffer overflow */
133 if (len >= LINE_LEN)
134 return 0;
136 memmove(&line[pos+1], &line[pos], len-pos);
137 line[pos++] = c;
138 len++;
140 line[len] = '\0';
141 if (pos < len) {
142 /* string (overwrites), move back x left */
143 printf("%s\x1b[%iD", &line[pos-1], len-pos);
144 } else {
145 printf("%c", c);
147 } else
148 if (c == K_ENTER) {
149 if (len == 0) {
150 strcpy(line, history[0]);
151 goto repeat_last;
154 int i;
155 if (hist_len >= HIST_SIZE)
156 hist_len = HIST_SIZE-1;
158 for (i=hist_len-1; i>=0; i--)
159 strcpy(history[i+1], history[i]);
160 strcpy(history[0], line);
161 hist_len++;
163 repeat_last:
164 printf("\n");
165 cbashell_command(line);
167 printf("> ");
169 len = pos = 0;
170 line[len] = '\0';
171 } else
172 if (c == K_BACKSPACE || c == K_BACKSPACE2) {
173 if (pos > 0) {
174 memmove(&line[pos-1], &line[pos], len-pos);
175 pos--;
176 len--;
178 if (pos < len) {
179 line[len] = '\0';
180 /* move left, clear line right, string, move x left */
181 printf("\x1b[D\x1b[K%s\x1b[%iD", &line[pos], len-pos);
182 } else {
183 printf("\x1b[D\x1b[K");
186 } else
187 if (c == K_DELETE) {
188 if (pos < len) {
189 memmove(&line[pos], &line[pos+1], len-pos-1);
190 len--;
192 line[len] = '\0';
193 if (pos < len) {
194 /* clear line right, string, move x left */
195 printf("\x1b[K%s\x1b[%iD", &line[pos], len-pos);
196 } else
197 printf("\x1b[K");
199 } else
200 if (c == K_CTRL_C) {
201 return -1;
202 } else
203 if (c == K_LEFT) {
204 if (pos > 0) {
205 pos--;
206 printf("\x1b[D"); /* left */
208 } else
209 if (c == K_RIGHT) {
210 if (pos < len) {
211 pos++;
212 printf("\x1b[C"); /* right */
214 } else
215 if (c == K_HOME) {
216 if (pos > 0) {
217 printf("\x1b[%iD", pos); /* left x */
218 pos = 0;
220 } else
221 if (c == K_END) {
222 if (pos < len) {
223 printf("\x1b[%iC", len-pos); /* right x */
224 pos = len;
226 } else
227 if (c == K_UP) {
228 if (hist_pos < hist_len-1) {
229 hist_pos++;
230 if (pos > 0)
231 printf("\x1b[%iD", pos); /* left x */
232 printf("\x1b[K%s", history[hist_pos]); /* clear line right, print history */
233 len = pos = strlen(history[hist_pos]);
235 } else
236 if (c == K_DOWN) {
237 if (hist_pos >= 0) {
238 const char *newline = line;
239 hist_pos--;
240 if (hist_pos >= 0)
241 newline = history[hist_pos];
242 if (pos > 0)
243 printf("\x1b[%iD", pos); /* left x */
244 printf("\x1b[K%s", newline); /* clear line right, print history */
245 len = pos = strlen(newline);
247 } else
248 if (c == K_CTRL_U) {
249 if (pos > 0)
250 printf("\x1b[%iD", pos); /* left x */
251 printf("\x1b[K"); /* clear line right */
252 len = pos = 0;
253 line[len] = '\0';
254 } else
255 if (c >= K_VT100_START) {
256 /* debugging print */
257 printf("vt100:%i ", c);
259 else {
260 printf("%02x ", c);
262 return 0;
265 /* partial VT100 parsing, see
266 * http://ascii-table.com/ansi-escape-sequences-vt-100.php
268 int cbashell_charraw(int c)
270 static int inside_esc;
271 static char esc[8];
272 static int esc_pos;
274 /* VT100 stuff */
275 if (inside_esc) {
276 /* just a silly guard */
277 if (esc_pos == sizeof(esc))
278 esc_pos = 0;
280 esc[esc_pos++] = c;
281 c = vt100_match(esc, esc_pos);
282 if (c == K_INVALID) {
283 int i;
284 int r = 0;
285 r |= cbashell_char('^');
286 r |= cbashell_char('[');
287 for (i=0; i<esc_pos; i++)
288 r |= cbashell_char(esc[i]);
290 inside_esc = 0;
291 return r;
292 } else
293 if (c == K_PARTIAL)
294 return 0;
296 inside_esc = 0;
297 return cbashell_char(c);
300 if (c == K_ESCAPE) {
301 inside_esc = 1;
302 esc_pos = 0;
303 return 0;
306 return cbashell_char(c);
309 void cbashell_init()
311 printf("> ");
313 len = pos = 0;
314 line[len] = '\0';
316 hist_len = 0;
317 hist_pos = -1;