Subversion Repositories HelenOS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2714 cejka 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. The binary size increase is <3K.
25
 
26
    Editting will not display correctly for lines greater then the
27
    terminal width. (more then one line.) However, history will.
28
*/
29
 
30
#include <stdio.h>
31
#include <unistd.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <termios.h>
35
#include <ctype.h>
36
#include <sys/ioctl.h>
37
 
38
#include "input.h"
39
#include "output.h"
40
 
41
#ifdef HETIO
42
 
43
#include "hetio.h"
44
 
45
 
46
#define  MAX_HISTORY   15           /* Maximum length of the linked list for the command line history */
47
 
48
#define ESC 27
49
#define DEL 127
50
 
51
static struct history *his_front = NULL;    /* First element in command line list */
52
static struct history *his_end = NULL;      /* Last element in command line list */
53
static struct termios old_term, new_term;   /* Current termio and the previous termio before starting ash */
54
 
55
static int history_counter = 0;         /* Number of commands in history list */
56
static int reset_term = 0;          /* Set to true if the terminal needs to be reset upon exit */
57
//static int hetio_inter = 0;
58
int hetio_inter = 0;
59
 
60
struct history
61
{
62
   char *s;
63
   struct history *p;
64
   struct history *n;
65
};
66
 
67
 
68
void input_delete    (int);
69
void input_home      (int *);
70
void input_end       (int *, int);
71
void input_backspace (int *, int *);
72
 
73
 
74
 
75
void hetio_init(void)
76
{
77
    hetio_inter = 1;
78
}
79
 
80
 
81
void hetio_reset_term(void)
82
{
83
    if (reset_term)
84
        tcsetattr(1, TCSANOW, &old_term);
85
}
86
 
87
 
88
void setIO(struct termios *new, struct termios *old)    /* Set terminal IO to canonical mode, and save old term settings. */
89
{
90
    tcgetattr(0, old);
91
    memcpy(new, old, sizeof(*new));
92
    new->c_cc[VMIN] = 1;
93
    new->c_cc[VTIME] = 0;
94
    new->c_lflag &= ~ICANON; /* unbuffered input */
95
    new->c_lflag &= ~ECHO;
96
    tcsetattr(0, TCSANOW, new);
97
}
98
 
99
void input_home(int *cursor)                /* Command line input routines */
100
{
101
    while (*cursor > 0) {
102
        out1c('\b');
103
        --*cursor;
104
    }
105
    flushout(&output);
106
}
107
 
108
 
109
void input_delete(int cursor)
110
{
111
    int j = 0;
112
 
113
    memmove(parsenextc + cursor, parsenextc + cursor + 1,
114
        BUFSIZ - cursor - 1);
115
    for (j = cursor; j < (BUFSIZ - 1); j++) {
116
        if (!*(parsenextc + j))
117
            break;
118
        else
119
            out1c(*(parsenextc + j));
120
    }
121
 
122
    out1str(" \b");
123
 
124
    while (j-- > cursor)
125
        out1c('\b');
126
    flushout(&output);
127
}
128
 
129
 
130
void input_end(int *cursor, int len)
131
{
132
    while (*cursor < len) {
133
        out1str("\033[C");
134
        ++*cursor;
135
    }
136
    flushout(&output);
137
}
138
 
139
 
140
void
141
input_backspace(int *cursor, int *len)
142
{
143
    int j = 0;
144
 
145
    if (*cursor > 0) {
146
        out1str("\b \b");
147
        --*cursor;
148
        memmove(parsenextc + *cursor, parsenextc + *cursor + 1,
149
            BUFSIZ - *cursor + 1);
150
 
151
        for (j = *cursor; j < (BUFSIZ - 1); j++) {
152
            if (!*(parsenextc + j))
153
                break;
154
            else
155
                out1c(*(parsenextc + j));
156
        }
157
 
158
        out1str(" \b");
159
 
160
        while (j-- > *cursor)
161
            out1c('\b');
162
 
163
        --*len;
164
        flushout(&output);
165
    }
166
}
167
 
168
int hetio_read_input(int fd)
169
{
170
    int nr = 0;
171
 
172
    if (!hetio_inter) {     /* Are we an interactive shell? */
173
        return -255;       
174
    } else {
175
        int len = 0;
176
        int j = 0;
177
        int cursor = 0;
178
        int break_out = 0;
179
        int ret = 0;
180
        char c = 0;
181
        struct history *hp = his_end;
182
 
183
        if (!reset_term) {
184
            setIO(&new_term, &old_term);
185
            reset_term = 1;
186
        } else {
187
            tcsetattr(0, TCSANOW, &new_term);
188
        }
189
 
190
        memset(parsenextc, 0, BUFSIZ);
191
 
192
        while (1) {
193
            if ((ret = read(fd, &c, 1)) < 1)
194
                return ret;
195
 
196
            switch (c) {
197
                case 1:     /* Control-A Beginning of line */
198
                    input_home(&cursor);
199
                    break;
200
                case 5:     /* Control-E EOL */
201
                    input_end(&cursor, len);
202
                    break;
203
                case 4:     /* Control-D */
204
#ifndef CTRL_D_DELETE
205
                    return 0;
206
#else
207
                    if (cursor != len) {
208
                        input_delete(cursor);
209
                        len--;
210
                    }
211
                    break;
212
#endif
213
                case '\b':  /* Backspace */
214
                case DEL:
215
                    input_backspace(&cursor, &len);
216
                    break;
217
                case '\n':  /* Enter */
218
                    *(parsenextc + len++ + 1) = c;
219
                    out1c(c);
220
                    flushout(&output);
221
                    break_out = 1;
222
                    break;
223
                case ESC:   /* escape sequence follows */
224
                    if ((ret = read(fd, &c, 1)) < 1)
225
                        return ret;
226
 
227
                    if (c == '[' || c == 'O' ) {    /* 91 */
228
                        if ((ret = read(fd, &c, 1)) < 1)
229
                            return ret;
230
 
231
                        switch (c) {
232
                            case 'A':
233
                                if (hp && hp->p) {      /* Up */
234
                                    hp = hp->p;
235
                                    goto hop;
236
                                }
237
                                break;
238
                            case 'B':
239
                                if (hp && hp->n && hp->n->s) {  /* Down */
240
                                    hp = hp->n;
241
                                    goto hop;
242
                                }
243
                                break;
244
 
245
hop:                        /* hop */                          
246
                                len = strlen(parsenextc);
247
 
248
                                for (; cursor > 0; cursor--)        /* return to begining of line */
249
                                    out1c('\b');
250
 
251
                                for (j = 0; j < len; j++)       /* erase old command */
252
                                    out1c(' ');
253
 
254
                                for (j = len; j > 0; j--)       /* return to begining of line */
255
                                    out1c('\b');
256
 
257
                                strcpy (parsenextc, hp->s);     /* write new command */
258
                                len = strlen (hp->s);
259
                                out1str(parsenextc);
260
                                flushout(&output);
261
                                cursor = len;
262
                                break;
263
                            case 'C':       /* Right */
264
                                    if (cursor < len) {
265
                                    out1str("\033[C");
266
                                    cursor++;
267
                                    flushout(&output);
268
                                }
269
                                break;
270
                            case 'D':       /* Left */
271
                                if (cursor > 0) {
272
                                    out1str("\033[D");
273
                                    cursor--;
274
                                    flushout(&output);
275
                                }
276
                                break;
277
                            case '3':       /* Delete */
278
                                if (cursor != len) {
279
                                    input_delete(cursor);
280
                                    len--;
281
                                }
282
                                break;                             
283
                            case 'H':       /* Home (xterm) */
284
                            case '1':       /* Home (Ctrl-A) */
285
                                    input_home(&cursor);
286
                                break;
287
                            case 'F':       /* End (xterm_ */
288
                            case '4':       /* End (Ctrl-E) */
289
                                input_end(&cursor, len);
290
                                break;
291
                        }
292
                        if (c == '1' || c == '3' || c == '4')
293
                            if ((ret = read(fd, &c, 1)) < 1)
294
                                return ret;  /* read 126 (~) */
295
                    }
296
 
297
                    c = 0;
298
                    break;
299
 
300
                default:                /* If it's regular input, do the normal thing */
301
 
302
                    if (!isprint(c))        /* Skip non-printable characters */
303
                        break;
304
 
305
                        if (len >= (BUFSIZ - 2))    /* Need to leave space for enter */
306
                        break;
307
 
308
                    len++;
309
 
310
                    if (cursor == (len - 1)) {  /* Append if at the end of the line */
311
                        *(parsenextc + cursor) = c;
312
                    } else {            /* Insert otherwise */
313
                        memmove(parsenextc + cursor + 1, parsenextc + cursor,
314
                            len - cursor - 1);
315
 
316
                        *(parsenextc + cursor) = c;
317
 
318
                        for (j = cursor; j < len; j++)
319
                            out1c(*(parsenextc + j));
320
                        for (; j > cursor; j--)
321
                            out1str("\033[D");
322
                    }
323
 
324
                    cursor++;
325
                    out1c(c);
326
                    flushout(&output);
327
                    break;
328
            }
329
 
330
            if (break_out)      /* Enter is the command terminator, no more input. */
331
                break;
332
        }
333
 
334
        nr = len + 1;
335
        tcsetattr(0, TCSANOW, &old_term);
336
 
337
 
338
        if (*(parsenextc)) {        /* Handle command history log */
339
            struct history *h = his_end;
340
 
341
            if (!h) {       /* No previous history */
342
                h = his_front = malloc(sizeof (struct history));
343
                h->n = malloc(sizeof (struct history));
344
                h->p = NULL;
345
                h->s = strdup(parsenextc);
346
 
347
                h->n->p = h;
348
                h->n->n = NULL;
349
                h->n->s = NULL;
350
                his_end = h->n;
351
                history_counter++;
352
            } else {    /* Add a new history command */
353
 
354
                h->n = malloc(sizeof (struct history));
355
 
356
                h->n->p = h;
357
                h->n->n = NULL;
358
                h->n->s = NULL;
359
                h->s = strdup(parsenextc);
360
                his_end = h->n;
361
 
362
                if (history_counter >= MAX_HISTORY) {   /* After max history, remove the last known command */
363
                    struct history *p = his_front->n;
364
 
365
                    p->p = NULL;
366
                    free(his_front->s);
367
                    free(his_front);
368
                    his_front = p;
369
                } else {
370
                    history_counter++;
371
                }
372
            }
373
        }
374
    }
375
 
376
    return nr;
377
}
378
#endif