Subversion Repositories HelenOS

Rev

Rev 2714 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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