]> git.lizzy.rs Git - linenoise.git/blob - linenoise.c
Support reverse incremental search with ^R
[linenoise.git] / linenoise.c
1 /* linenoise.c -- guerrilla line editing library against the idea that a
2  * line editing lib needs to be 20,000 lines of C code.
3  *
4  * You can find the latest source code at:
5  * 
6  *   http://github.com/antirez/linenoise
7  *
8  * Does a number of crazy assumptions that happen to be true in 99.9999% of
9  * the 2010 UNIX computers around.
10  *
11  * ------------------------------------------------------------------------
12  *
13  * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
14  * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
15  *
16  * All rights reserved.
17  * 
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions are
20  * met:
21  * 
22  *  *  Redistributions of source code must retain the above copyright
23  *     notice, this list of conditions and the following disclaimer.
24  *
25  *  *  Redistributions in binary form must reproduce the above copyright
26  *     notice, this list of conditions and the following disclaimer in the
27  *     documentation and/or other materials provided with the distribution.
28  * 
29  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  * 
41  * ------------------------------------------------------------------------
42  *
43  * References:
44  * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
45  * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
46  *
47  * Todo list:
48  * - Switch to gets() if $TERM is something we can't support.
49  * - Win32 support
50  *
51  * Bloat:
52  * - Completion?
53  *
54  * List of escape sequences used by this program, we do everything just
55  * with three sequences. In order to be so cheap we may have some
56  * flickering effect with some slow terminal, but the lesser sequences
57  * the more compatible.
58  *
59  * CHA (Cursor Horizontal Absolute)
60  *    Sequence: ESC [ n G
61  *    Effect: moves cursor to column n
62  *
63  * EL (Erase Line)
64  *    Sequence: ESC [ n K
65  *    Effect: if n is 0 or missing, clear from cursor to end of line
66  *    Effect: if n is 1, clear from beginning of line to cursor
67  *    Effect: if n is 2, clear entire line
68  *
69  * CUF (CUrsor Forward)
70  *    Sequence: ESC [ n C
71  *    Effect: moves cursor forward of n chars
72  *
73  * The following are used to clear the screen: ESC [ H ESC [ 2 J
74  * This is actually composed of two sequences:
75  *
76  * cursorhome
77  *    Sequence: ESC [ H
78  *    Effect: moves the cursor to upper left corner
79  *
80  * ED2 (Clear entire screen)
81  *    Sequence: ESC [ 2 J
82  *    Effect: clear the whole screen
83  * 
84  */
85
86 #include <termios.h>
87 #include <unistd.h>
88 #include <stdlib.h>
89 #include <stdio.h>
90 #include <errno.h>
91 #include <string.h>
92 #include <stdlib.h>
93 #include <sys/types.h>
94 #include <sys/ioctl.h>
95 #include <unistd.h>
96 #include "linenoise.h"
97
98 #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
99 #define LINENOISE_MAX_LINE 4096
100 static char *unsupported_term[] = {"dumb","cons25",NULL};
101 static linenoiseCompletionCallback *completionCallback = NULL;
102
103 static struct termios orig_termios; /* in order to restore at exit */
104 static int rawmode = 0; /* for atexit() function to check if restore is needed*/
105 static int atexit_registered = 0; /* register atexit just 1 time */
106 static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
107 static int history_len = 0;
108 char **history = NULL;
109
110 static void linenoiseAtExit(void);
111 int linenoiseHistoryAdd(const char *line);
112
113 static int isUnsupportedTerm(void) {
114     char *term = getenv("TERM");
115     int j;
116
117     if (term == NULL) return 0;
118     for (j = 0; unsupported_term[j]; j++)
119         if (!strcasecmp(term,unsupported_term[j])) return 1;
120     return 0;
121 }
122
123 static void freeHistory(void) {
124     if (history) {
125         int j;
126
127         for (j = 0; j < history_len; j++)
128             free(history[j]);
129         free(history);
130     }
131 }
132
133 static int enableRawMode(int fd) {
134     struct termios raw;
135
136     if (!isatty(STDIN_FILENO)) goto fatal;
137     if (!atexit_registered) {
138         atexit(linenoiseAtExit);
139         atexit_registered = 1;
140     }
141     if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
142
143     raw = orig_termios;  /* modify the original mode */
144     /* input modes: no break, no CR to NL, no parity check, no strip char,
145      * no start/stop output control. */
146     raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
147     /* output modes - disable post processing */
148     raw.c_oflag &= ~(OPOST);
149     /* control modes - set 8 bit chars */
150     raw.c_cflag |= (CS8);
151     /* local modes - choing off, canonical off, no extended functions,
152      * no signal chars (^Z,^C) */
153     raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
154     /* control chars - set return condition: min number of bytes and timer.
155      * We want read to return every single byte, without timeout. */
156     raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
157
158     /* put terminal in raw mode after flushing */
159     if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
160     rawmode = 1;
161     return 0;
162
163 fatal:
164     errno = ENOTTY;
165     return -1;
166 }
167
168 static void disableRawMode(int fd) {
169     /* Don't even check the return value as it's too late. */
170     if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
171         rawmode = 0;
172 }
173
174 /* At exit we'll try to fix the terminal to the initial conditions. */
175 static void linenoiseAtExit(void) {
176     disableRawMode(STDIN_FILENO);
177     freeHistory();
178 }
179
180 static int getColumns(void) {
181     struct winsize ws;
182
183     if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80;
184     return ws.ws_col;
185 }
186
187 static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
188     char seq[64];
189     size_t plen = strlen(prompt);
190     int extra = 0;
191     size_t i, p;
192     
193     while((plen+pos) >= cols) {
194         buf++;
195         len--;
196         pos--;
197     }
198     while (plen+len > cols) {
199         len--;
200     }
201
202     /* Cursor to left edge */
203     snprintf(seq,64,"\x1b[0G");
204     if (write(fd,seq,strlen(seq)) == -1) return;
205     /* Write the prompt and the current buffer content */
206     if (write(fd,prompt,strlen(prompt)) == -1) return;
207     /* Need special handling for control characters */
208     p = 0;
209     for (i = 0; i < len; i++) {
210         if (buf[i] < ' ') {
211             write(fd, buf + p, i - p);
212             p = i + 1;
213             seq[0] = '^';
214             seq[1] = buf[i] + '@';
215             write(fd, seq, 2);
216             if (i < pos) {
217                 extra++;
218             }
219         }
220     }
221     write(fd, buf + p, i - p);
222     /* Erase to right */
223     snprintf(seq,64,"\x1b[0K");
224     if (write(fd,seq,strlen(seq)) == -1) return;
225     /* Move cursor to original position. */
226     snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen+extra));
227     if (write(fd,seq,strlen(seq)) == -1) return;
228 }
229
230 static void beep() {
231     fprintf(stderr, "\x7");
232     fflush(stderr);
233 }
234
235 static void freeCompletions(linenoiseCompletions *lc) {
236     size_t i;
237     for (i = 0; i < lc->len; i++)
238         free(lc->cvec[i]);
239     if (lc->cvec != NULL)
240         free(lc->cvec);
241 }
242
243 static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) {
244     linenoiseCompletions lc = { 0, NULL };
245     int nread, nwritten;
246     char c = 0;
247
248     completionCallback(buf,&lc);
249     if (lc.len == 0) {
250         beep();
251     } else {
252         size_t stop = 0, i = 0;
253         size_t clen;
254
255         while(!stop) {
256             /* Show completion or original buffer */
257             if (i < lc.len) {
258                 clen = strlen(lc.cvec[i]);
259                 refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols);
260             } else {
261                 refreshLine(fd,prompt,buf,*len,*pos,cols);
262             }
263
264             nread = read(fd,&c,1);
265             if (nread <= 0) {
266                 freeCompletions(&lc);
267                 return -1;
268             }
269
270             switch(c) {
271                 case 9: /* tab */
272                     i = (i+1) % (lc.len+1);
273                     if (i == lc.len) beep();
274                     break;
275                 case 27: /* escape */
276                     /* Re-show original buffer */
277                     if (i < lc.len) {
278                         refreshLine(fd,prompt,buf,*len,*pos,cols);
279                     }
280                     stop = 1;
281                     break;
282                 default:
283                     /* Update buffer and return */
284                     if (i < lc.len) {
285                         nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]);
286                         *len = *pos = nwritten;
287                     }
288                     stop = 1;
289                     break;
290             }
291         }
292     }
293
294     freeCompletions(&lc);
295     return c; /* Return last read character */
296 }
297
298 void linenoiseClearScreen(void) {
299     if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
300         /* nothing to do, just to avoid warning. */
301     }
302 }
303
304 static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
305     size_t plen = strlen(prompt);
306     size_t pos = 0;
307     size_t len = 0;
308     size_t cols = getColumns();
309     int history_index = 0;
310
311     buf[0] = '\0';
312     buflen--; /* Make sure there is always space for the nulterm */
313
314     /* The latest history entry is always our current buffer, that
315      * initially is just an empty string. */
316     linenoiseHistoryAdd("");
317     
318     if (write(fd,prompt,plen) == -1) return -1;
319     while(1) {
320         char c;
321         int nread;
322         int ext;
323         char seq[2], seq2[2];
324
325         nread = read(fd,&c,1);
326 process_char:
327         if (nread <= 0) return len;
328
329         /* Only autocomplete when the callback is set. It returns < 0 when
330          * there was an error reading from fd. Otherwise it will return the
331          * character that should be handled next. */
332         if (c == 9 && completionCallback != NULL) {
333             c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
334             /* Return on errors */
335             if (c < 0) return len;
336             /* Read next character when 0 */
337             if (c == 0) continue;
338         }
339
340         switch(c) {
341         case 13:    /* enter */
342             history_len--;
343             free(history[history_len]);
344             return (int)len;
345         case 3:     /* ctrl-c */
346             errno = EAGAIN;
347             return -1;
348         case 127:   /* backspace */
349         case 8:     /* ctrl-h */
350             if (pos > 0 && len > 0) {
351                 memmove(buf+pos-1,buf+pos,len-pos);
352                 pos--;
353                 len--;
354                 buf[len] = '\0';
355                 refreshLine(fd,prompt,buf,len,pos,cols);
356             }
357             break;
358         case 4:     /* ctrl-d, remove char at right of cursor */
359             if (len > 1 && pos < (len-1)) {
360                 memmove(buf+pos,buf+pos+1,len-pos);
361                 len--;
362                 buf[len] = '\0';
363                 refreshLine(fd,prompt,buf,len,pos,cols);
364             } else if (len == 0) {
365                 history_len--;
366                 free(history[history_len]);
367                 return -1;
368             }
369             break;
370         case 18:    /* ctrl-r */
371             {
372                 /* Display the reverse-i-search prompt and process chars */
373                 char rbuf[50];
374                 char rprompt[80];
375                 int i = 0;
376                 rbuf[0] = 0;
377                 while (1) {
378                     snprintf(rprompt, sizeof(rprompt), "(reverse-i-search)'%s': ", rbuf);
379                     refreshLine(fd,rprompt,buf,len,pos,cols);
380                     nread = read(fd,&c,1);
381                     if (nread == 1) {
382                         if (c == 8 || c == 127) {
383                             if (i > 0) {
384                                 rbuf[--i] = 0;
385                             }
386                             continue;
387                         }
388                         if (c == 7) {
389                             /* ctrl-g terminates the search with no effect */
390                             buf[0] = 0;
391                             len = 0;
392                             pos = 0;
393                             break;
394                         }
395                         if (c >= ' ' && c <= '~') {
396                             if (i < (int)sizeof(rbuf)) {
397                                 int j;
398                                 const char *p = NULL;
399                                 rbuf[i++] = c;
400                                 rbuf[i] = 0;
401                                 /* Now search back through the history for a match */
402                                 for (j = history_len - 1; j > 0; j--) {
403                                     p = strstr(history[j], rbuf);
404                                     if (p) {
405                                         /* Found a match. Copy it */
406                                         strncpy(buf,history[j],buflen);
407                                         buf[buflen] = '\0';
408                                         len = strlen(buf);
409                                         pos = p - history[j];
410                                         break;
411                                     }
412                                 }
413                                 if (!p) {
414                                     /* No match, so don't add it */
415                                     rbuf[--i] = 0;
416                                 }
417                             }
418                             continue;
419                         }
420                     }
421                     break;
422                 }
423                 /* Go process the char normally */
424                 refreshLine(fd,prompt,buf,len,pos,cols);
425                 goto process_char;
426             }
427             break;
428         case 20:    /* ctrl-t */
429             if (pos > 0 && pos < len) {
430                 int aux = buf[pos-1];
431                 buf[pos-1] = buf[pos];
432                 buf[pos] = aux;
433                 if (pos != len-1) pos++;
434                 refreshLine(fd,prompt,buf,len,pos,cols);
435             }
436             break;
437         case 2:     /* ctrl-b */
438             goto left_arrow;
439         case 6:     /* ctrl-f */
440             goto right_arrow;
441         case 16:    /* ctrl-p */
442             seq[1] = 65;
443             goto up_down_arrow;
444         case 14:    /* ctrl-n */
445             seq[1] = 66;
446             goto up_down_arrow;
447             break;
448         case 27:    /* escape sequence */
449             if (read(fd,seq,2) == -1) break;
450             ext = (seq[0] == 91 || seq[0] == 79);
451             if (ext && seq[1] == 68) {
452 left_arrow:
453                 /* left arrow */
454                 if (pos > 0) {
455                     pos--;
456                     refreshLine(fd,prompt,buf,len,pos,cols);
457                 }
458             } else if (ext && seq[1] == 67) {
459 right_arrow:
460                 /* right arrow */
461                 if (pos != len) {
462                     pos++;
463                     refreshLine(fd,prompt,buf,len,pos,cols);
464                 }
465             } else if (ext && (seq[1] == 65 || seq[1] == 66)) {
466 up_down_arrow:
467                 /* up and down arrow: history */
468                 if (history_len > 1) {
469                     /* Update the current history entry before to
470                      * overwrite it with tne next one. */
471                     free(history[history_len-1-history_index]);
472                     history[history_len-1-history_index] = strdup(buf);
473                     /* Show the new entry */
474                     history_index += (seq[1] == 65) ? 1 : -1;
475                     if (history_index < 0) {
476                         history_index = 0;
477                         break;
478                     } else if (history_index >= history_len) {
479                         history_index = history_len-1;
480                         break;
481                     }
482                     strncpy(buf,history[history_len-1-history_index],buflen);
483                     buf[buflen] = '\0';
484                     len = pos = strlen(buf);
485                     refreshLine(fd,prompt,buf,len,pos,cols);
486                 }
487             } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) {
488                 /* extended escape */
489                 if (read(fd,seq2,2) == -1) break;
490                 if (seq[1] == 51 && seq2[0] == 126) {
491                     /* delete */
492                     if (len > 0 && pos < len) {
493                         memmove(buf+pos,buf+pos+1,len-pos-1);
494                         len--;
495                         buf[len] = '\0';
496                         refreshLine(fd,prompt,buf,len,pos,cols);
497                     }
498                 }
499             }
500             break;
501         default:
502             /* Note that the only control character currently permitted is tab */
503             if (len < buflen && (c == '\t' || c >= ' ')) {
504                 if (len == pos && c >= ' ') {
505                     buf[pos] = c;
506                     pos++;
507                     len++;
508                     buf[len] = '\0';
509                     if (plen+len < cols) {
510                         /* Avoid a full update of the line in the
511                          * trivial case. */
512                         if (write(fd,&c,1) == -1) return -1;
513                     } else {
514                         refreshLine(fd,prompt,buf,len,pos,cols);
515                     }
516                 } else {
517                     memmove(buf+pos+1,buf+pos,len-pos);
518                     buf[pos] = c;
519                     len++;
520                     pos++;
521                     buf[len] = '\0';
522                     refreshLine(fd,prompt,buf,len,pos,cols);
523                 }
524             }
525             break;
526         case 21: /* Ctrl+u, delete the whole line. */
527             buf[0] = '\0';
528             pos = len = 0;
529             refreshLine(fd,prompt,buf,len,pos,cols);
530             break;
531         case 11: /* Ctrl+k, delete from current to end of line. */
532             buf[pos] = '\0';
533             len = pos;
534             refreshLine(fd,prompt,buf,len,pos,cols);
535             break;
536         case 1: /* Ctrl+a, go to the start of the line */
537             pos = 0;
538             refreshLine(fd,prompt,buf,len,pos,cols);
539             break;
540         case 5: /* ctrl+e, go to the end of the line */
541             pos = len;
542             refreshLine(fd,prompt,buf,len,pos,cols);
543             break;
544         case 12: /* ctrl+l, clear screen */
545             linenoiseClearScreen();
546             refreshLine(fd,prompt,buf,len,pos,cols);
547         }
548     }
549     return len;
550 }
551
552 static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
553     int fd = STDIN_FILENO;
554     int count;
555
556     if (buflen == 0) {
557         errno = EINVAL;
558         return -1;
559     }
560     if (!isatty(STDIN_FILENO)) {
561         if (fgets(buf, buflen, stdin) == NULL) return -1;
562         count = strlen(buf);
563         if (count && buf[count-1] == '\n') {
564             count--;
565             buf[count] = '\0';
566         }
567     } else {
568         if (enableRawMode(fd) == -1) return -1;
569         count = linenoisePrompt(fd, buf, buflen, prompt);
570         disableRawMode(fd);
571         printf("\n");
572     }
573     return count;
574 }
575
576 char *linenoise(const char *prompt) {
577     char buf[LINENOISE_MAX_LINE];
578     int count;
579
580     if (isUnsupportedTerm()) {
581         size_t len;
582
583         printf("%s",prompt);
584         fflush(stdout);
585         if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
586         len = strlen(buf);
587         while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
588             len--;
589             buf[len] = '\0';
590         }
591         return strdup(buf);
592     } else {
593         count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
594         if (count == -1) return NULL;
595         return strdup(buf);
596     }
597 }
598
599 /* Register a callback function to be called for tab-completion. */
600 void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
601     completionCallback = fn;
602 }
603
604 void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
605     size_t len = strlen(str);
606     char *copy = malloc(len+1);
607     memcpy(copy,str,len+1);
608     lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
609     lc->cvec[lc->len++] = copy;
610 }
611
612 /* Using a circular buffer is smarter, but a bit more complex to handle. */
613 int linenoiseHistoryAdd(const char *line) {
614     char *linecopy;
615
616     if (history_max_len == 0) return 0;
617     if (history == NULL) {
618         history = malloc(sizeof(char*)*history_max_len);
619         if (history == NULL) return 0;
620         memset(history,0,(sizeof(char*)*history_max_len));
621     }
622     linecopy = strdup(line);
623     if (!linecopy) return 0;
624     if (history_len == history_max_len) {
625         free(history[0]);
626         memmove(history,history+1,sizeof(char*)*(history_max_len-1));
627         history_len--;
628     }
629     history[history_len] = linecopy;
630     history_len++;
631     return 1;
632 }
633
634 int linenoiseHistorySetMaxLen(int len) {
635     char **new;
636
637     if (len < 1) return 0;
638     if (history) {
639         int tocopy = history_len;
640
641         new = malloc(sizeof(char*)*len);
642         if (new == NULL) return 0;
643         if (len < tocopy) tocopy = len;
644         memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy);
645         free(history);
646         history = new;
647     }
648     history_max_len = len;
649     if (history_len > history_max_len)
650         history_len = history_max_len;
651     return 1;
652 }
653
654 /* Save the history in the specified file. On success 0 is returned
655  * otherwise -1 is returned. */
656 int linenoiseHistorySave(char *filename) {
657     FILE *fp = fopen(filename,"w");
658     int j;
659     
660     if (fp == NULL) return -1;
661     for (j = 0; j < history_len; j++)
662         fprintf(fp,"%s\n",history[j]);
663     fclose(fp);
664     return 0;
665 }
666
667 /* Load the history from the specified file. If the file does not exist
668  * zero is returned and no operation is performed.
669  *
670  * If the file exists and the operation succeeded 0 is returned, otherwise
671  * on error -1 is returned. */
672 int linenoiseHistoryLoad(char *filename) {
673     FILE *fp = fopen(filename,"r");
674     char buf[LINENOISE_MAX_LINE];
675     
676     if (fp == NULL) return -1;
677
678     while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
679         char *p;
680         
681         p = strchr(buf,'\r');
682         if (!p) p = strchr(buf,'\n');
683         if (p) *p = '\0';
684         linenoiseHistoryAdd(buf);
685     }
686     fclose(fp);
687     return 0;
688 }