Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/hetio.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 8685 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 8685 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* |
2 | * Termios command line History and Editting for NetBSD sh (ash) |
3 | * Copyright (c) 1999 |
4 | * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu> |
5 | * Etc: Dave Cinege <dcinege@psychosis.com> |
6 | * |
7 | * You may use this code as you wish, so long as the original author(s) |
8 | * are attributed in any redistributions of the source code. |
9 | * This code is 'as is' with no warranty. |
10 | * This code may safely be consumed by a BSD or GPL license. |
11 | * |
12 | * v 0.5 19990328 Initial release |
13 | * |
14 | * Future plans: Simple file and path name completion. (like BASH) |
15 | * |
16 | */ |
17 | |
18 | /* |
19 | Usage and Known bugs: |
20 | Terminal key codes are not extensive, and more will probably |
21 | need to be added. This version was created on Debian GNU/Linux 2.x. |
22 | Delete, Backspace, Home, End, and the arrow keys were tested |
23 | to work in an Xterm and console. Ctrl-A also works as Home. |
24 | Ctrl-E also works as End. Ctrl-D and Ctrl-U perform their respective |
25 | functions. The binary size increase is <3K. |
26 | |
27 | Editting will not display correctly for lines greater then the |
28 | terminal width. (more then one line.) However, history will. |
29 | */ |
30 | |
31 | #include <stdio.h> |
32 | #include <unistd.h> |
33 | #include <stdlib.h> |
34 | #include <string.h> |
35 | #include <termios.h> |
36 | #include <ctype.h> |
37 | #include <sys/ioctl.h> |
38 | |
39 | #include "input.h" |
40 | #include "output.h" |
41 | |
42 | #include "hetio.h" |
43 | |
44 | |
45 | #define MAX_HISTORY 15 /* Maximum length of the linked list for the command line history */ |
46 | |
47 | #define ESC 27 |
48 | #define DEL 127 |
49 | |
50 | static struct history *his_front = NULL; /* First element in command line list */ |
51 | static struct history *his_end = NULL; /* Last element in command line list */ |
52 | static struct termios old_term, new_term; /* Current termio and the previous termio before starting ash */ |
53 | |
54 | static int history_counter = 0; /* Number of commands in history list */ |
55 | static int reset_term = 0; /* Set to true if the terminal needs to be reset upon exit */ |
56 | static int hetio_inter = 0; |
57 | |
58 | struct history |
59 | { |
60 | char *s; |
61 | struct history *p; |
62 | struct history *n; |
63 | }; |
64 | |
65 | |
66 | void input_delete (int); |
67 | void input_home (int *); |
68 | void input_end (int *, int); |
69 | void input_backspace (int *, int *); |
70 | |
71 | |
72 | |
73 | void hetio_init(void) |
74 | { |
75 | hetio_inter = 1; |
76 | } |
77 | |
78 | |
79 | void hetio_reset_term(void) |
80 | { |
81 | if (reset_term) |
82 | tcsetattr(1, TCSANOW, &old_term); |
83 | } |
84 | |
85 | |
86 | void setIO(struct termios *new, struct termios *old) /* Set terminal IO to canonical mode, and save old term settings. */ |
87 | { |
88 | tcgetattr(0, old); |
89 | memcpy(new, old, sizeof(*new)); |
90 | new->c_cc[VMIN] = 1; |
91 | new->c_cc[VTIME] = 0; |
92 | new->c_lflag &= ~ICANON; /* unbuffered input */ |
93 | new->c_lflag &= ~ECHO; |
94 | tcsetattr(0, TCSANOW, new); |
95 | } |
96 | |
97 | void input_home(int *cursor) /* Command line input routines */ |
98 | { |
99 | while (*cursor > 0) { |
100 | out1c('\b'); |
101 | --*cursor; |
102 | } |
103 | flushout(out1); |
104 | } |
105 | |
106 | |
107 | void input_delete(int cursor) |
108 | { |
109 | int j = 0; |
110 | |
111 | memmove(parsenextc + cursor, parsenextc + cursor + 1, |
112 | BUFSIZ - cursor - 1); |
113 | for (j = cursor; j < (BUFSIZ - 1); j++) { |
114 | if (!*(parsenextc + j)) |
115 | break; |
116 | else |
117 | out1c(*(parsenextc + j)); |
118 | } |
119 | |
120 | out1str(" \b"); |
121 | |
122 | while (j-- > cursor) |
123 | out1c('\b'); |
124 | flushout(out1); |
125 | } |
126 | |
127 | |
128 | void input_end(int *cursor, int len) |
129 | { |
130 | while (*cursor < len) { |
131 | out1str("\033[C"); |
132 | ++*cursor; |
133 | } |
134 | flushout(out1); |
135 | } |
136 | |
137 | |
138 | void |
139 | input_backspace(int *cursor, int *len) |
140 | { |
141 | int j = 0; |
142 | |
143 | if (*cursor > 0) { |
144 | out1str("\b \b"); |
145 | --*cursor; |
146 | memmove(parsenextc + *cursor, parsenextc + *cursor + 1, |
147 | BUFSIZ - *cursor + 1); |
148 | |
149 | for (j = *cursor; j < (BUFSIZ - 1); j++) { |
150 | if (!*(parsenextc + j)) |
151 | break; |
152 | else |
153 | out1c(*(parsenextc + j)); |
154 | } |
155 | |
156 | out1str(" \b"); |
157 | |
158 | while (j-- > *cursor) |
159 | out1c('\b'); |
160 | |
161 | --*len; |
162 | flushout(out1); |
163 | } |
164 | } |
165 | |
166 | int hetio_read_input(int fd) |
167 | { |
168 | int nr = 0; |
169 | |
170 | /* Are we an interactive shell? */ |
171 | if (!hetio_inter || fd) { |
172 | return -255; |
173 | } else { |
174 | int len = 0; |
175 | int j = 0; |
176 | int cursor = 0; |
177 | int break_out = 0; |
178 | int ret = 0; |
179 | char c = 0; |
180 | struct history *hp = his_end; |
181 | |
182 | if (!reset_term) { |
183 | setIO(&new_term, &old_term); |
184 | reset_term = 1; |
185 | } else { |
186 | tcsetattr(0, TCSANOW, &new_term); |
187 | } |
188 | |
189 | memset(parsenextc, 0, BUFSIZ); |
190 | |
191 | while (1) { |
192 | if ((ret = read(fd, &c, 1)) < 1) |
193 | return ret; |
194 | |
195 | switch (c) { |
196 | case 1: /* Control-A Beginning of line */ |
197 | input_home(&cursor); |
198 | break; |
199 | case 5: /* Control-E EOL */ |
200 | input_end(&cursor, len); |
201 | break; |
202 | case 4: /* Control-D */ |
203 | if (!len) |
204 | exitshell(0); |
205 | break; |
206 | case 21: /* Control-U */ |
207 | /* Return to begining of line. */ |
208 | for (; cursor > 0; cursor--) |
209 | out1c('\b'); |
210 | /* Erase old command. */ |
211 | for (j = 0; j < len; j++) { |
212 | /* |
213 | * Clear buffer while we're at |
214 | * it. |
215 | */ |
216 | parsenextc[j] = 0; |
217 | out1c(' '); |
218 | } |
219 | /* return to begining of line */ |
220 | for (; len > 0; len--) |
221 | out1c('\b'); |
222 | flushout(out1); |
223 | break; |
224 | case '\b': /* Backspace */ |
225 | case DEL: |
226 | input_backspace(&cursor, &len); |
227 | break; |
228 | case '\n': /* Enter */ |
229 | *(parsenextc + len++ + 1) = c; |
230 | out1c(c); |
231 | flushout(out1); |
232 | break_out = 1; |
233 | break; |
234 | case ESC: /* escape sequence follows */ |
235 | if ((ret = read(fd, &c, 1)) < 1) |
236 | return ret; |
237 | |
238 | if (c == '[' ) { /* 91 */ |
239 | if ((ret = read(fd, &c, 1)) < 1) |
240 | return ret; |
241 | |
242 | switch (c) { |
243 | case 'A': |
244 | if (hp && hp->p) { /* Up */ |
245 | hp = hp->p; |
246 | goto hop; |
247 | } |
248 | break; |
249 | case 'B': |
250 | if (hp && hp->n && hp->n->s) { /* Down */ |
251 | hp = hp->n; |
252 | goto hop; |
253 | } |
254 | break; |
255 | |
256 | hop: /* hop */ |
257 | len = strlen(parsenextc); |
258 | |
259 | for (; cursor > 0; cursor--) /* return to begining of line */ |
260 | out1c('\b'); |
261 | |
262 | for (j = 0; j < len; j++) /* erase old command */ |
263 | out1c(' '); |
264 | |
265 | for (; j > 0; j--) /* return to begining of line */ |
266 | out1c('\b'); |
267 | |
268 | strcpy (parsenextc, hp->s); /* write new command */ |
269 | len = strlen (hp->s); |
270 | out1str(parsenextc); |
271 | flushout(out1); |
272 | cursor = len; |
273 | break; |
274 | case 'C': /* Right */ |
275 | if (cursor < len) { |
276 | out1str("\033[C"); |
277 | cursor++; |
278 | flushout(out1); |
279 | } |
280 | break; |
281 | case 'D': /* Left */ |
282 | if (cursor > 0) { |
283 | out1str("\033[D"); |
284 | cursor--; |
285 | flushout(out1); |
286 | } |
287 | break; |
288 | case '3': /* Delete */ |
289 | if (cursor != len) { |
290 | input_delete(cursor); |
291 | len--; |
292 | } |
293 | break; |
294 | case '1': /* Home (Ctrl-A) */ |
295 | input_home(&cursor); |
296 | break; |
297 | case '4': /* End (Ctrl-E) */ |
298 | input_end(&cursor, len); |
299 | break; |
300 | } |
301 | if (c == '1' || c == '3' || c == '4') |
302 | if ((ret = read(fd, &c, 1)) < 1) |
303 | return ret; /* read 126 (~) */ |
304 | } |
305 | |
306 | if (c == 'O') { /* 79 */ |
307 | if ((ret = read(fd, &c, 1)) < 1) |
308 | return ret; |
309 | switch (c) { |
310 | case 'H': /* Home (xterm) */ |
311 | input_home(&cursor); |
312 | break; |
313 | case 'F': /* End (xterm_ */ |
314 | input_end(&cursor, len); |
315 | break; |
316 | } |
317 | } |
318 | |
319 | c = 0; |
320 | break; |
321 | |
322 | default: /* If it's regular input, do the normal thing */ |
323 | if (!isprint(c)) /* Skip non-printable characters */ |
324 | break; |
325 | |
326 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ |
327 | break; |
328 | |
329 | len++; |
330 | |
331 | if (cursor == (len - 1)) { /* Append if at the end of the line */ |
332 | *(parsenextc + cursor) = c; |
333 | } else { /* Insert otherwise */ |
334 | memmove(parsenextc + cursor + 1, parsenextc + cursor, |
335 | len - cursor - 1); |
336 | |
337 | *(parsenextc + cursor) = c; |
338 | |
339 | for (j = cursor; j < len; j++) |
340 | out1c(*(parsenextc + j)); |
341 | for (; j > cursor; j--) |
342 | out1str("\033[D"); |
343 | } |
344 | |
345 | cursor++; |
346 | out1c(c); |
347 | flushout(out1); |
348 | break; |
349 | } |
350 | |
351 | if (break_out) /* Enter is the command terminator, no more input. */ |
352 | break; |
353 | } |
354 | |
355 | nr = len + 1; |
356 | tcsetattr(0, TCSANOW, &old_term); |
357 | |
358 | if (*(parsenextc)) { /* Handle command history log */ |
359 | struct history *h = his_end; |
360 | |
361 | if (!h) { /* No previous history */ |
362 | h = his_front = malloc(sizeof (struct history)); |
363 | h->n = malloc(sizeof (struct history)); |
364 | h->p = NULL; |
365 | h->s = strdup(parsenextc); |
366 | |
367 | h->n->p = h; |
368 | h->n->n = NULL; |
369 | h->n->s = NULL; |
370 | his_end = h->n; |
371 | history_counter++; |
372 | } else { /* Add a new history command */ |
373 | |
374 | h->n = malloc(sizeof (struct history)); |
375 | |
376 | h->n->p = h; |
377 | h->n->n = NULL; |
378 | h->n->s = NULL; |
379 | h->s = strdup(parsenextc); |
380 | his_end = h->n; |
381 | |
382 | if (history_counter >= MAX_HISTORY) { /* After max history, remove the last known command */ |
383 | struct history *p = his_front->n; |
384 | |
385 | p->p = NULL; |
386 | free(his_front->s); |
387 | free(his_front); |
388 | his_front = p; |
389 | } else { |
390 | history_counter++; |
391 | } |
392 | } |
393 | } |
394 | } |
395 | |
396 | return nr; |
397 | } |