]> git.lizzy.rs Git - linenoise.git/blob - linenoise.c
Allow ^V to enter arbitrary control characters
[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  * For highlighting control characters, we also use:
85  * SO (enter StandOut)
86  *    Sequence: ESC [ 7 m
87  *    Effect: Uses some standout mode such as reverse video
88  *
89  * SE (Standout End)
90  *    Sequence: ESC [ 0 m
91  *    Effect: Exit standout mode
92  */
93
94 #include <termios.h>
95 #include <unistd.h>
96 #include <stdlib.h>
97 #include <stdio.h>
98 #include <errno.h>
99 #include <string.h>
100 #include <stdlib.h>
101 #include <sys/types.h>
102 #include <sys/ioctl.h>
103 #include <unistd.h>
104 #include "linenoise.h"
105
106 #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
107 #define LINENOISE_MAX_LINE 4096
108 static char *unsupported_term[] = {"dumb","cons25",NULL};
109 static linenoiseCompletionCallback *completionCallback = NULL;
110
111 static struct termios orig_termios; /* in order to restore at exit */
112 static int rawmode = 0; /* for atexit() function to check if restore is needed*/
113 static int atexit_registered = 0; /* register atexit just 1 time */
114 static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
115 static int history_len = 0;
116 char **history = NULL;
117
118 static void linenoiseAtExit(void);
119 int linenoiseHistoryAdd(const char *line);
120
121 static int isUnsupportedTerm(void) {
122     char *term = getenv("TERM");
123     int j;
124
125     if (term == NULL) return 0;
126     for (j = 0; unsupported_term[j]; j++)
127         if (!strcasecmp(term,unsupported_term[j])) return 1;
128     return 0;
129 }
130
131 static void freeHistory(void) {
132     if (history) {
133         int j;
134
135         for (j = 0; j < history_len; j++)
136             free(history[j]);
137         free(history);
138     }
139 }
140
141 static int enableRawMode(int fd) {
142     struct termios raw;
143
144     if (!isatty(STDIN_FILENO)) goto fatal;
145     if (!atexit_registered) {
146         atexit(linenoiseAtExit);
147         atexit_registered = 1;
148     }
149     if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
150
151     raw = orig_termios;  /* modify the original mode */
152     /* input modes: no break, no CR to NL, no parity check, no strip char,
153      * no start/stop output control. */
154     raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
155     /* output modes - disable post processing */
156     raw.c_oflag &= ~(OPOST);
157     /* control modes - set 8 bit chars */
158     raw.c_cflag |= (CS8);
159     /* local modes - choing off, canonical off, no extended functions,
160      * no signal chars (^Z,^C) */
161     raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
162     /* control chars - set return condition: min number of bytes and timer.
163      * We want read to return every single byte, without timeout. */
164     raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
165
166     /* put terminal in raw mode after flushing */
167     if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
168     rawmode = 1;
169     return 0;
170
171 fatal:
172     errno = ENOTTY;
173     return -1;
174 }
175
176 static void disableRawMode(int fd) {
177     /* Don't even check the return value as it's too late. */
178     if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
179         rawmode = 0;
180 }
181
182 /* At exit we'll try to fix the terminal to the initial conditions. */
183 static void linenoiseAtExit(void) {
184     disableRawMode(STDIN_FILENO);
185     freeHistory();
186 }
187
188 static int getColumns(void) {
189     struct winsize ws;
190
191     if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80;
192     return ws.ws_col;
193 }
194
195 static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
196     char seq[64];
197     size_t plen = strlen(prompt);
198     int extra = 0;
199     size_t i, p;
200     
201     while((plen+pos) >= cols) {
202         buf++;
203         len--;
204         pos--;
205     }
206     while (plen+len > cols) {
207         len--;
208     }
209
210     /* Cursor to left edge */
211     snprintf(seq,64,"\x1b[0G");
212     if (write(fd,seq,strlen(seq)) == -1) return;
213     /* Write the prompt and the current buffer content */
214     if (write(fd,prompt,strlen(prompt)) == -1) return;
215     /* Need special handling for control characters */
216     p = 0;
217     for (i = 0; i < len; i++) {
218         if (buf[i] < ' ') {
219             write(fd, buf + p, i - p);
220             p = i + 1;
221             snprintf(seq,64,"\033[7m^%c\033[0m", buf[i] + '@');
222             if (write(fd,seq,strlen(seq)) == -1) return;
223             if (i < pos) {
224                 extra++;
225             }
226         }
227     }
228     write(fd, buf + p, i - p);
229     /* Erase to right */
230     snprintf(seq,64,"\x1b[0K");
231     if (write(fd,seq,strlen(seq)) == -1) return;
232     /* Move cursor to original position. */
233     snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen+extra));
234     if (write(fd,seq,strlen(seq)) == -1) return;
235 }
236
237 static void beep() {
238     fprintf(stderr, "\x7");
239     fflush(stderr);
240 }
241
242 static void freeCompletions(linenoiseCompletions *lc) {
243     size_t i;
244     for (i = 0; i < lc->len; i++)
245         free(lc->cvec[i]);
246     if (lc->cvec != NULL)
247         free(lc->cvec);
248 }
249
250 static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) {
251     linenoiseCompletions lc = { 0, NULL };
252     int nread, nwritten;
253     char c = 0;
254
255     completionCallback(buf,&lc);
256     if (lc.len == 0) {
257         beep();
258     } else {
259         size_t stop = 0, i = 0;
260         size_t clen;
261
262         while(!stop) {
263             /* Show completion or original buffer */
264             if (i < lc.len) {
265                 clen = strlen(lc.cvec[i]);
266                 refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols);
267             } else {
268                 refreshLine(fd,prompt,buf,*len,*pos,cols);
269             }
270
271             nread = read(fd,&c,1);
272             if (nread <= 0) {
273                 freeCompletions(&lc);
274                 return -1;
275             }
276
277             switch(c) {
278                 case 9: /* tab */
279                     i = (i+1) % (lc.len+1);
280                     if (i == lc.len) beep();
281                     break;
282                 case 27: /* escape */
283                     /* Re-show original buffer */
284                     if (i < lc.len) {
285                         refreshLine(fd,prompt,buf,*len,*pos,cols);
286                     }
287                     stop = 1;
288                     break;
289                 default:
290                     /* Update buffer and return */
291                     if (i < lc.len) {
292                         nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]);
293                         *len = *pos = nwritten;
294                     }
295                     stop = 1;
296                     break;
297             }
298         }
299     }
300
301     freeCompletions(&lc);
302     return c; /* Return last read character */
303 }
304
305 void linenoiseClearScreen(void) {
306     if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
307         /* nothing to do, just to avoid warning. */
308     }
309 }
310
311 static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
312     size_t plen = strlen(prompt);
313     size_t pos = 0;
314     size_t len = 0;
315     size_t cols = getColumns();
316     int history_index = 0;
317
318     buf[0] = '\0';
319     buflen--; /* Make sure there is always space for the nulterm */
320
321     /* The latest history entry is always our current buffer, that
322      * initially is just an empty string. */
323     linenoiseHistoryAdd("");
324     
325     if (write(fd,prompt,plen) == -1) return -1;
326     while(1) {
327         char c;
328         int nread;
329         int ext;
330         char seq[2], seq2[2];
331
332         nread = read(fd,&c,1);
333 process_char:
334         if (nread <= 0) return len;
335
336         /* Only autocomplete when the callback is set. It returns < 0 when
337          * there was an error reading from fd. Otherwise it will return the
338          * character that should be handled next. */
339         if (c == 9 && completionCallback != NULL) {
340             c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
341             /* Return on errors */
342             if (c < 0) return len;
343             /* Read next character when 0 */
344             if (c == 0) continue;
345         }
346
347         switch(c) {
348         case 13:    /* enter */
349             history_len--;
350             free(history[history_len]);
351             return (int)len;
352         case 3:     /* ctrl-c */
353             errno = EAGAIN;
354             return -1;
355         case 127:   /* backspace */
356         case 8:     /* ctrl-h */
357             if (pos > 0 && len > 0) {
358                 memmove(buf+pos-1,buf+pos,len-pos);
359                 pos--;
360                 len--;
361                 buf[len] = '\0';
362                 refreshLine(fd,prompt,buf,len,pos,cols);
363             }
364             break;
365         case 4:     /* ctrl-d, remove char at right of cursor */
366             if (len > 1 && pos < (len-1)) {
367                 memmove(buf+pos,buf+pos+1,len-pos);
368                 len--;
369                 buf[len] = '\0';
370                 refreshLine(fd,prompt,buf,len,pos,cols);
371             } else if (len == 0) {
372                 history_len--;
373                 free(history[history_len]);
374                 return -1;
375             }
376         case 23:    /* ctrl-w */
377             /* eat any spaces on the left */
378             {
379                 int n = 0;
380                 while (pos > 0 && len > 0 && buf[pos - 1] == ' ') {
381                     pos--;
382                     len--;
383                     n++;
384                 }
385                 /* now eat any non-spaces on the left */
386                 while (pos > 0 && len > 0 && buf[pos - 1] != ' ') {
387                     pos--;
388                     len--;
389                     n++;
390                 }
391                 memmove(buf+pos,buf+pos+n,len-pos);
392                 refreshLine(fd,prompt,buf,len,pos,cols);
393             }
394             break;
395         case 18:    /* ctrl-r */
396             {
397                 /* Display the reverse-i-search prompt and process chars */
398                 char rbuf[50];
399                 char rprompt[80];
400                 int i = 0;
401                 rbuf[0] = 0;
402                 while (1) {
403                     snprintf(rprompt, sizeof(rprompt), "(reverse-i-search)'%s': ", rbuf);
404                     refreshLine(fd,rprompt,buf,len,pos,cols);
405                     nread = read(fd,&c,1);
406                     if (nread == 1) {
407                         if (c == 8 || c == 127) {
408                             if (i > 0) {
409                                 rbuf[--i] = 0;
410                             }
411                             continue;
412                         }
413                         if (c == 7) {
414                             /* ctrl-g terminates the search with no effect */
415                             buf[0] = 0;
416                             len = 0;
417                             pos = 0;
418                             break;
419                         }
420                         if (c >= ' ' && c <= '~') {
421                             if (i < (int)sizeof(rbuf)) {
422                                 int j;
423                                 const char *p = NULL;
424                                 rbuf[i++] = c;
425                                 rbuf[i] = 0;
426                                 /* Now search back through the history for a match */
427                                 for (j = history_len - 1; j > 0; j--) {
428                                     p = strstr(history[j], rbuf);
429                                     if (p) {
430                                         /* Found a match. Copy it */
431                                         strncpy(buf,history[j],buflen);
432                                         buf[buflen] = '\0';
433                                         len = strlen(buf);
434                                         pos = p - history[j];
435                                         break;
436                                     }
437                                 }
438                                 if (!p) {
439                                     /* No match, so don't add it */
440                                     rbuf[--i] = 0;
441                                 }
442                             }
443                             continue;
444                         }
445                     }
446                     break;
447                 }
448                 /* Go process the char normally */
449                 refreshLine(fd,prompt,buf,len,pos,cols);
450                 goto process_char;
451             }
452             break;
453         case 20:    /* ctrl-t */
454             if (pos > 0 && pos < len) {
455                 int aux = buf[pos-1];
456                 buf[pos-1] = buf[pos];
457                 buf[pos] = aux;
458                 if (pos != len-1) pos++;
459                 refreshLine(fd,prompt,buf,len,pos,cols);
460             }
461             break;
462         case 22:    /* ctrl-v */
463             if (len < buflen) {
464                 /* Insert the ^V first */
465                 memmove(buf+pos+1,buf+pos,len-pos);
466                 buf[pos] = c;
467                 len++;
468                 pos++;
469                 buf[len] = '\0';
470                 refreshLine(fd,prompt,buf,len,pos,cols);
471                 /* Now wait for the next char. Can insert anything except \0 */
472                 nread = read(fd,&c,1);
473                 if (nread == 1 && c) {
474                     /* Replace the ^V with the actual char */
475                     buf[pos - 1] = c;
476                 }
477                 else {
478                     /* Remove the ^V */
479                     pos--;
480                     memmove(buf+pos,buf+pos+1,len-pos);
481                 }
482                 refreshLine(fd,prompt,buf,len,pos,cols);
483             }
484             break;
485         case 2:     /* ctrl-b */
486             goto left_arrow;
487         case 6:     /* ctrl-f */
488             goto right_arrow;
489         case 16:    /* ctrl-p */
490             seq[1] = 65;
491             goto up_down_arrow;
492         case 14:    /* ctrl-n */
493             seq[1] = 66;
494             goto up_down_arrow;
495             break;
496         case 27:    /* escape sequence */
497             if (read(fd,seq,2) == -1) break;
498             ext = (seq[0] == 91 || seq[0] == 79);
499             if (ext && seq[1] == 68) {
500 left_arrow:
501                 /* left arrow */
502                 if (pos > 0) {
503                     pos--;
504                     refreshLine(fd,prompt,buf,len,pos,cols);
505                 }
506             } else if (ext && seq[1] == 67) {
507 right_arrow:
508                 /* right arrow */
509                 if (pos != len) {
510                     pos++;
511                     refreshLine(fd,prompt,buf,len,pos,cols);
512                 }
513             } else if (ext && (seq[1] == 65 || seq[1] == 66)) {
514 up_down_arrow:
515                 /* up and down arrow: history */
516                 if (history_len > 1) {
517                     /* Update the current history entry before to
518                      * overwrite it with tne next one. */
519                     free(history[history_len-1-history_index]);
520                     history[history_len-1-history_index] = strdup(buf);
521                     /* Show the new entry */
522                     history_index += (seq[1] == 65) ? 1 : -1;
523                     if (history_index < 0) {
524                         history_index = 0;
525                         break;
526                     } else if (history_index >= history_len) {
527                         history_index = history_len-1;
528                         break;
529                     }
530                     strncpy(buf,history[history_len-1-history_index],buflen);
531                     buf[buflen] = '\0';
532                     len = pos = strlen(buf);
533                     refreshLine(fd,prompt,buf,len,pos,cols);
534                 }
535             } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) {
536                 /* extended escape */
537                 if (read(fd,seq2,2) == -1) break;
538                 if (seq[1] == 51 && seq2[0] == 126) {
539                     /* delete */
540                     if (len > 0 && pos < len) {
541                         memmove(buf+pos,buf+pos+1,len-pos-1);
542                         len--;
543                         buf[len] = '\0';
544                         refreshLine(fd,prompt,buf,len,pos,cols);
545                     }
546                 }
547             }
548             break;
549         default:
550             /* Note that the only control character currently permitted is tab */
551             if (len < buflen && (c == '\t' || c >= ' ')) {
552                 if (len == pos && c >= ' ') {
553                     buf[pos] = c;
554                     pos++;
555                     len++;
556                     buf[len] = '\0';
557                     if (plen+len < cols) {
558                         /* Avoid a full update of the line in the
559                          * trivial case. */
560                         if (write(fd,&c,1) == -1) return -1;
561                     } else {
562                         refreshLine(fd,prompt,buf,len,pos,cols);
563                     }
564                 } else {
565                     memmove(buf+pos+1,buf+pos,len-pos);
566                     buf[pos] = c;
567                     len++;
568                     pos++;
569                     buf[len] = '\0';
570                     refreshLine(fd,prompt,buf,len,pos,cols);
571                 }
572             }
573             break;
574         case 21: /* Ctrl+u, delete the whole line. */
575             buf[0] = '\0';
576             pos = len = 0;
577             refreshLine(fd,prompt,buf,len,pos,cols);
578             break;
579         case 11: /* Ctrl+k, delete from current to end of line. */
580             buf[pos] = '\0';
581             len = pos;
582             refreshLine(fd,prompt,buf,len,pos,cols);
583             break;
584         case 1: /* Ctrl+a, go to the start of the line */
585             pos = 0;
586             refreshLine(fd,prompt,buf,len,pos,cols);
587             break;
588         case 5: /* ctrl+e, go to the end of the line */
589             pos = len;
590             refreshLine(fd,prompt,buf,len,pos,cols);
591             break;
592         case 12: /* ctrl+l, clear screen */
593             linenoiseClearScreen();
594             refreshLine(fd,prompt,buf,len,pos,cols);
595         }
596     }
597     return len;
598 }
599
600 static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
601     int fd = STDIN_FILENO;
602     int count;
603
604     if (buflen == 0) {
605         errno = EINVAL;
606         return -1;
607     }
608     if (!isatty(STDIN_FILENO)) {
609         if (fgets(buf, buflen, stdin) == NULL) return -1;
610         count = strlen(buf);
611         if (count && buf[count-1] == '\n') {
612             count--;
613             buf[count] = '\0';
614         }
615     } else {
616         if (enableRawMode(fd) == -1) return -1;
617         count = linenoisePrompt(fd, buf, buflen, prompt);
618         disableRawMode(fd);
619         printf("\n");
620     }
621     return count;
622 }
623
624 char *linenoise(const char *prompt) {
625     char buf[LINENOISE_MAX_LINE];
626     int count;
627
628     if (isUnsupportedTerm()) {
629         size_t len;
630
631         printf("%s",prompt);
632         fflush(stdout);
633         if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
634         len = strlen(buf);
635         while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
636             len--;
637             buf[len] = '\0';
638         }
639         return strdup(buf);
640     } else {
641         count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
642         if (count == -1) return NULL;
643         return strdup(buf);
644     }
645 }
646
647 /* Register a callback function to be called for tab-completion. */
648 void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
649     completionCallback = fn;
650 }
651
652 void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
653     size_t len = strlen(str);
654     char *copy = malloc(len+1);
655     memcpy(copy,str,len+1);
656     lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
657     lc->cvec[lc->len++] = copy;
658 }
659
660 /* Using a circular buffer is smarter, but a bit more complex to handle. */
661 int linenoiseHistoryAdd(const char *line) {
662     char *linecopy;
663
664     if (history_max_len == 0) return 0;
665     if (history == NULL) {
666         history = malloc(sizeof(char*)*history_max_len);
667         if (history == NULL) return 0;
668         memset(history,0,(sizeof(char*)*history_max_len));
669     }
670     linecopy = strdup(line);
671     if (!linecopy) return 0;
672     if (history_len == history_max_len) {
673         free(history[0]);
674         memmove(history,history+1,sizeof(char*)*(history_max_len-1));
675         history_len--;
676     }
677     history[history_len] = linecopy;
678     history_len++;
679     return 1;
680 }
681
682 int linenoiseHistorySetMaxLen(int len) {
683     char **new;
684
685     if (len < 1) return 0;
686     if (history) {
687         int tocopy = history_len;
688
689         new = malloc(sizeof(char*)*len);
690         if (new == NULL) return 0;
691         if (len < tocopy) tocopy = len;
692         memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy);
693         free(history);
694         history = new;
695     }
696     history_max_len = len;
697     if (history_len > history_max_len)
698         history_len = history_max_len;
699     return 1;
700 }
701
702 /* Save the history in the specified file. On success 0 is returned
703  * otherwise -1 is returned. */
704 int linenoiseHistorySave(char *filename) {
705     FILE *fp = fopen(filename,"w");
706     int j;
707     
708     if (fp == NULL) return -1;
709     for (j = 0; j < history_len; j++)
710         fprintf(fp,"%s\n",history[j]);
711     fclose(fp);
712     return 0;
713 }
714
715 /* Load the history from the specified file. If the file does not exist
716  * zero is returned and no operation is performed.
717  *
718  * If the file exists and the operation succeeded 0 is returned, otherwise
719  * on error -1 is returned. */
720 int linenoiseHistoryLoad(char *filename) {
721     FILE *fp = fopen(filename,"r");
722     char buf[LINENOISE_MAX_LINE];
723     
724     if (fp == NULL) return -1;
725
726     while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
727         char *p;
728         
729         p = strchr(buf,'\r');
730         if (!p) p = strchr(buf,'\n');
731         if (p) *p = '\0';
732         linenoiseHistoryAdd(buf);
733     }
734     fclose(fp);
735     return 0;
736 }