aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/dumbsh.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dumbsh.c')
-rw-r--r--src/dumbsh.c283
1 files changed, 283 insertions, 0 deletions
diff --git a/src/dumbsh.c b/src/dumbsh.c
new file mode 100644
index 0000000..9ad0204
--- /dev/null
+++ b/src/dumbsh.c
@@ -0,0 +1,283 @@
1/* dumbsh.c - A really dumb shell, to demonstrate handle_keys usage.
2 *
3 * Copyright 2014 David Seikel <won_fang@yahoo.com.au>
4 *
5 * Not a real shell, so doesn't follow any standards,
6 * coz it wont implement them anyway.
7
8USE_DUMBSH(NEWTOY(dumbsh, "", TOYFLAG_USR|TOYFLAG_BIN))
9
10config DUMBSH
11 bool "dumbsh"
12 default n
13 help
14 usage: dumbsh
15
16 A really dumb shell.
17*/
18
19#include "toys.h"
20#include "lib/handlekeys.h"
21
22typedef void (*eventHandler) (void);
23
24struct keyCommand
25{
26 char *key;
27 eventHandler handler;
28};
29
30GLOBALS(
31 unsigned h, w;
32 int x, y;
33 struct double_list *history;
34)
35
36#define TT this.dumbsh
37
38// Sanity check cursor location and update the current line.
39static void updateLine()
40{
41 if (0 > TT.x) TT.x = 0;
42 if (0 > TT.y) TT.y = 0;
43 if (TT.w < TT.x) TT.x = TT.w;
44 if (TT.h < TT.y) TT.y = TT.h;
45 if (strlen(toybuf) < TT.x) TT.x = strlen(toybuf);
46 printf("\x1B[%d;0H%-*s\x1B[%d;%dH",
47 TT.y + 1, TT.w, toybuf, TT.y + 1, TT.x + 1);
48 fflush(stdout);
49}
50
51// The various commands.
52static void deleteChar()
53{
54 int j;
55
56 for (j = TT.x; toybuf[j]; j++)
57 toybuf[j] = toybuf[j + 1];
58 updateLine();
59}
60
61static void backSpaceChar()
62{
63 if (TT.x)
64 {
65 TT.x--;
66 deleteChar();
67 }
68}
69
70// This is where we would actually deal with
71// what ever command the user had typed in.
72// For now we just move on to the next line.
73// TODO - We would want to redirect I/O, capture some keys (^C),
74// but pass the rest on.
75// Dunno yet how to deal with that.
76// We still want handle_keys to be doing it's thing,
77// so maybe handing it another fd, and a callback.
78// A function to add and remove fd and callback pairs for
79// handle_keys to check?
80static void doCommand()
81{
82 toybuf[0] = 0;
83 TT.x = 0;
84 TT.y++;
85 printf("\n");
86 fflush(stdout);
87 updateLine();
88}
89
90static void endOfLine()
91{
92 TT.x = strlen(toybuf);
93 updateLine();
94}
95
96static void leftChar()
97{
98 TT.x--;
99 updateLine();
100}
101
102static void nextHistory()
103{
104 TT.history = TT.history->next;
105 strcpy(toybuf, TT.history->data);
106 TT.x = strlen(toybuf);
107 updateLine();
108}
109
110static void prevHistory()
111{
112 TT.history = TT.history->prev;
113 strcpy(toybuf, TT.history->data);
114 TT.x = strlen(toybuf);
115 updateLine();
116}
117
118static void quit()
119{
120 handle_keys_quit();
121}
122
123static void rightChar()
124{
125 TT.x++;
126 updateLine();
127}
128
129static void startOfLine()
130{
131 TT.x = 0;
132 updateLine();
133}
134
135// The key to command mappings, Emacs style.
136static const struct keyCommand simpleEmacsKeys[] =
137{
138 {"BS", backSpaceChar},
139 {"Del", deleteChar},
140 {"^D", deleteChar},
141 {"Return", doCommand},
142 {"Enter", doCommand},
143 {"Down", nextHistory},
144 {"^N", nextHistory},
145 {"End", endOfLine},
146 {"^E", endOfLine},
147 {"Left", leftChar},
148 {"^B", leftChar},
149 {"^X^C", quit},
150 {"^C", quit},
151 {"Right", rightChar},
152 {"^F", rightChar},
153 {"Home", startOfLine},
154 {"^A", startOfLine},
155 {"Up", prevHistory},
156 {"^P", prevHistory}
157};
158
159// Callback for incoming sequences from the terminal.
160static int handleEvent(long extra, struct keyevent *event)
161{
162 switch (event->type)
163 {
164 case HK_KEYS :
165 {
166 int j, l = strlen(event->sequence);
167
168 // Search for a key sequence bound to a command.
169 for (j = 0; j < ARRAY_LEN(simpleEmacsKeys); j++)
170 {
171 if (strncmp(simpleEmacsKeys[j].key, event->sequence, l) == 0)
172 {
173 // If it's a partial match, keep accumulating them.
174 if (strlen(simpleEmacsKeys[j].key) != l)
175 return 0;
176 else
177 {
178 if (simpleEmacsKeys[j].handler) simpleEmacsKeys[j].handler();
179 return 1;
180 }
181 }
182 }
183
184 // See if it's ordinary keys.
185 // NOTE - with vi style ordinary keys can be commands,
186 // but they would be found by the command check above first.
187 if (!event->isTranslated)
188 {
189 if (TT.x < sizeof(toybuf))
190 {
191 int j, l = strlen(event->sequence);
192
193 for (j = strlen(toybuf); j >= TT.x; j--)
194 toybuf[j + l] = toybuf[j];
195 for (j = 0; j < l; j++)
196 toybuf[TT.x + j] = event->sequence[j];
197 TT.x += l;
198 updateLine();
199 }
200 }
201 break;
202 }
203
204 case HK_CSI :
205 {
206 // Is it a cursor location report?
207 if (strcmp("R", event->sequence) == 0)
208 {
209 // Parameters are cursor line and column.
210 // NOTE - This may be sent at other times, not just during terminal resize.
211 // We are assuming here that it's a resize.
212 // The defaults are 1, which get ignored by the heuristic below.
213 int r = event->params[0], c = event->params[1];
214
215 // Check it's not an F3 key variation, coz some of them use
216 // the same CSI function command.
217 // This is a heuristic, we are checking against an unusable terminal size.
218 if ((2 == event->count) && (8 < r) && (8 < c))
219 {
220 TT.h = r;
221 TT.w = c;
222 updateLine();
223 }
224 break;
225 }
226 }
227
228 default : break;
229 }
230
231 // Tell handle_keys to drop it, coz we dealt with it, or it's not one of ours.
232 return 1;
233}
234
235void dumbsh_main(void)
236{
237 struct termios termIo, oldTermIo;
238 char *t = getenv("HOME");
239 int fd;
240
241 // Load bash history.
242 t = xmprintf("%s/%s", t ? t : "", ".bash_history");
243 if (-1 != (fd = open(t, O_RDONLY)))
244 {
245 while ((t = get_line(fd))) TT.history = dlist_add(&TT.history, t);
246 close(fd);
247 }
248 if (!TT.history)
249 TT.history = dlist_add(&TT.history, "");
250
251 // Grab the old terminal settings and save it.
252 tcgetattr(0, &oldTermIo);
253 tcflush(0, TCIFLUSH);
254 termIo = oldTermIo;
255
256 // Mould the terminal to our will.
257 // In this example we are turning off all the terminal smarts, but real code
258 // might not want that.
259 termIo.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL
260 | IUCLC | IXON | IXOFF | IXANY);
261 termIo.c_oflag &= ~OPOST;
262 termIo.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | TOSTOP | ICANON | ISIG
263 | IEXTEN);
264 termIo.c_cflag &= ~(CSIZE | PARENB);
265 termIo.c_cflag |= CS8;
266 termIo.c_cc[VTIME]=0; // deciseconds.
267 termIo.c_cc[VMIN]=1;
268 tcsetattr(0, TCSANOW, &termIo);
269
270 // Let the mouldy old terminal mold us.
271 TT.w = 80;
272 TT.h = 24;
273 terminal_size(&TT.w, &TT.h);
274
275 // Let's rock!
276 updateLine();
277 handle_keys(0, handleEvent);
278
279 // Clean up.
280 tcsetattr(0, TCSANOW, &oldTermIo);
281 puts("");
282 fflush(stdout);
283}