]> git.lizzy.rs Git - linenoise.git/blob - linenoise.c
081246756fc01b1c8c51211bd51c3ab18f912a83
[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         case 23:    /* ctrl-w */
370             /* eat any spaces on the left */
371             {
372                 int n = 0;
373                 while (pos > 0 && len > 0 && buf[pos - 1] == ' ') {
374                     pos--;
375                     len--;
376                     n++;
377                 }
378                 /* now eat any non-spaces on the left */
379                 while (pos > 0 && len > 0 && buf[pos - 1] != ' ') {
380                     pos--;
381                     len--;
382                     n++;
383                 }
384                 memmove(buf+pos,buf+pos+n,len-pos);
385                 refreshLine(fd,prompt,buf,len,pos,cols);
386             }
387             break;
388         case 18:    /* ctrl-r */
389             {
390                 /* Display the reverse-i-search prompt and process chars */
391                 char rbuf[50];
392                 char rprompt[80];
393                 int i = 0;
394                 rbuf[0] = 0;
395                 while (1) {
396                     snprintf(rprompt, sizeof(rprompt), "(reverse-i-search)'%s': ", rbuf);
397                     refreshLine(fd,rprompt,buf,len,pos,cols);
398                     nread = read(fd,&c,1);
399                     if (nread == 1) {
400                         if (c == 8 || c == 127) {
401                             if (i > 0) {
402                                 rbuf[--i] = 0;
403                             }
404                             continue;
405                         }
406                         if (c == 7) {
407                             /* ctrl-g terminates the search with no effect */
408                             buf[0] = 0;
409                             len = 0;
410                             pos = 0;
411                             break;
412                         }
413                         if (c >= ' ' && c <= '~') {
414                             if (i < (int)sizeof(rbuf)) {
415                                 int j;
416                                 const char *p = NULL;
417                                 rbuf[i++] = c;
418                                 rbuf[i] = 0;
419                                 /* Now search back through the history for a match */
420                                 for (j = history_len - 1; j > 0; j--) {
421                                     p = strstr(history[j], rbuf);
422                                     if (p) {
423                                         /* Found a match. Copy it */
424                                         strncpy(buf,history[j],buflen);
425                                         buf[buflen] = '\0';
426                                         len = strlen(buf);
427                                         pos = p - history[j];
428                                         break;
429                                     }
430                                 }
431                                 if (!p) {
432                                     /* No match, so don't add it */
433                                     rbuf[--i] = 0;
434                                 }
435                             }
436                             continue;
437                         }
438                     }
439                     break;
440                 }
441                 /* Go process the char normally */
442                 refreshLine(fd,prompt,buf,len,pos,cols);
443                 goto process_char;
444             }
445             break;
446         case 20:    /* ctrl-t */
447             if (pos > 0 && pos < len) {
448                 int aux = buf[pos-1];
449                 buf[pos-1] = buf[pos];
450                 buf[pos] = aux;
451                 if (pos != len-1) pos++;
452                 refreshLine(fd,prompt,buf,len,pos,cols);
453             }
454             break;
455         case 2:     /* ctrl-b */
456             goto left_arrow;
457         case 6:     /* ctrl-f */
458             goto right_arrow;
459         case 16:    /* ctrl-p */
460             seq[1] = 65;
461             goto up_down_arrow;
462         case 14:    /* ctrl-n */
463             seq[1] = 66;
464             goto up_down_arrow;
465             break;
466         case 27:    /* escape sequence */
467             if (read(fd,seq,2) == -1) break;
468             ext = (seq[0] == 91 || seq[0] == 79);
469             if (ext && seq[1] == 68) {
470 left_arrow:
471                 /* left arrow */
472                 if (pos > 0) {
473                     pos--;
474                     refreshLine(fd,prompt,buf,len,pos,cols);
475                 }
476             } else if (ext && seq[1] == 67) {
477 right_arrow:
478                 /* right arrow */
479                 if (pos != len) {
480                     pos++;
481                     refreshLine(fd,prompt,buf,len,pos,cols);
482                 }
483             } else if (ext && (seq[1] == 65 || seq[1] == 66)) {
484 up_down_arrow:
485                 /* up and down arrow: history */
486                 if (history_len > 1) {
487                     /* Update the current history entry before to
488                      * overwrite it with tne next one. */
489                     free(history[history_len-1-history_index]);
490                     history[history_len-1-history_index] = strdup(buf);
491                     /* Show the new entry */
492                     history_index += (seq[1] == 65) ? 1 : -1;
493                     if (history_index < 0) {
494                         history_index = 0;
495                         break;
496                     } else if (history_index >= history_len) {
497                         history_index = history_len-1;
498                         break;
499                     }
500                     strncpy(buf,history[history_len-1-history_index],buflen);
501                     buf[buflen] = '\0';
502                     len = pos = strlen(buf);
503                     refreshLine(fd,prompt,buf,len,pos,cols);
504                 }
505             } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) {
506                 /* extended escape */
507                 if (read(fd,seq2,2) == -1) break;
508                 if (seq[1] == 51 && seq2[0] == 126) {
509                     /* delete */
510                     if (len > 0 && pos < len) {
511                         memmove(buf+pos,buf+pos+1,len-pos-1);
512                         len--;
513                         buf[len] = '\0';
514                         refreshLine(fd,prompt,buf,len,pos,cols);
515                     }
516                 }
517             }
518             break;
519         default:
520             /* Note that the only control character currently permitted is tab */
521             if (len < buflen && (c == '\t' || c >= ' ')) {
522                 if (len == pos && c >= ' ') {
523                     buf[pos] = c;
524                     pos++;
525                     len++;
526                     buf[len] = '\0';
527                     if (plen+len < cols) {
528                         /* Avoid a full update of the line in the
529                          * trivial case. */
530                         if (write(fd,&c,1) == -1) return -1;
531                     } else {
532                         refreshLine(fd,prompt,buf,len,pos,cols);
533                     }
534                 } else {
535                     memmove(buf+pos+1,buf+pos,len-pos);
536                     buf[pos] = c;
537                     len++;
538                     pos++;
539                     buf[len] = '\0';
540                     refreshLine(fd,prompt,buf,len,pos,cols);
541                 }
542             }
543             break;
544         case 21: /* Ctrl+u, delete the whole line. */
545             buf[0] = '\0';
546             pos = len = 0;
547             refreshLine(fd,prompt,buf,len,pos,cols);
548             break;
549         case 11: /* Ctrl+k, delete from current to end of line. */
550             buf[pos] = '\0';
551             len = pos;
552             refreshLine(fd,prompt,buf,len,pos,cols);
553             break;
554         case 1: /* Ctrl+a, go to the start of the line */
555             pos = 0;
556             refreshLine(fd,prompt,buf,len,pos,cols);
557             break;
558         case 5: /* ctrl+e, go to the end of the line */
559             pos = len;
560             refreshLine(fd,prompt,buf,len,pos,cols);
561             break;
562         case 12: /* ctrl+l, clear screen */
563             linenoiseClearScreen();
564             refreshLine(fd,prompt,buf,len,pos,cols);
565         }
566     }
567     return len;
568 }
569
570 static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
571     int fd = STDIN_FILENO;
572     int count;
573
574     if (buflen == 0) {
575         errno = EINVAL;
576         return -1;
577     }
578     if (!isatty(STDIN_FILENO)) {
579         if (fgets(buf, buflen, stdin) == NULL) return -1;
580         count = strlen(buf);
581         if (count && buf[count-1] == '\n') {
582             count--;
583             buf[count] = '\0';
584         }
585     } else {
586         if (enableRawMode(fd) == -1) return -1;
587         count = linenoisePrompt(fd, buf, buflen, prompt);
588         disableRawMode(fd);
589         printf("\n");
590     }
591     return count;
592 }
593
594 char *linenoise(const char *prompt) {
595     char buf[LINENOISE_MAX_LINE];
596     int count;
597
598     if (isUnsupportedTerm()) {
599         size_t len;
600
601         printf("%s",prompt);
602         fflush(stdout);
603         if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
604         len = strlen(buf);
605         while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
606             len--;
607             buf[len] = '\0';
608         }
609         return strdup(buf);
610     } else {
611         count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
612         if (count == -1) return NULL;
613         return strdup(buf);
614     }
615 }
616
617 /* Register a callback function to be called for tab-completion. */
618 void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
619     completionCallback = fn;
620 }
621
622 void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
623     size_t len = strlen(str);
624     char *copy = malloc(len+1);
625     memcpy(copy,str,len+1);
626     lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
627     lc->cvec[lc->len++] = copy;
628 }
629
630 /* Using a circular buffer is smarter, but a bit more complex to handle. */
631 int linenoiseHistoryAdd(const char *line) {
632     char *linecopy;
633
634     if (history_max_len == 0) return 0;
635     if (history == NULL) {
636         history = malloc(sizeof(char*)*history_max_len);
637         if (history == NULL) return 0;
638         memset(history,0,(sizeof(char*)*history_max_len));
639     }
640     linecopy = strdup(line);
641     if (!linecopy) return 0;
642     if (history_len == history_max_len) {
643         free(history[0]);
644         memmove(history,history+1,sizeof(char*)*(history_max_len-1));
645         history_len--;
646     }
647     history[history_len] = linecopy;
648     history_len++;
649     return 1;
650 }
651
652 int linenoiseHistorySetMaxLen(int len) {
653     char **new;
654
655     if (len < 1) return 0;
656     if (history) {
657         int tocopy = history_len;
658
659         new = malloc(sizeof(char*)*len);
660         if (new == NULL) return 0;
661         if (len < tocopy) tocopy = len;
662         memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy);
663         free(history);
664         history = new;
665     }
666     history_max_len = len;
667     if (history_len > history_max_len)
668         history_len = history_max_len;
669     return 1;
670 }
671
672 /* Save the history in the specified file. On success 0 is returned
673  * otherwise -1 is returned. */
674 int linenoiseHistorySave(char *filename) {
675     FILE *fp = fopen(filename,"w");
676     int j;
677     
678     if (fp == NULL) return -1;
679     for (j = 0; j < history_len; j++)
680         fprintf(fp,"%s\n",history[j]);
681     fclose(fp);
682     return 0;
683 }
684
685 /* Load the history from the specified file. If the file does not exist
686  * zero is returned and no operation is performed.
687  *
688  * If the file exists and the operation succeeded 0 is returned, otherwise
689  * on error -1 is returned. */
690 int linenoiseHistoryLoad(char *filename) {
691     FILE *fp = fopen(filename,"r");
692     char buf[LINENOISE_MAX_LINE];
693     
694     if (fp == NULL) return -1;
695
696     while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
697         char *p;
698         
699         p = strchr(buf,'\r');
700         if (!p) p = strchr(buf,'\n');
701         if (p) *p = '\0';
702         linenoiseHistoryAdd(buf);
703     }
704     fclose(fp);
705     return 0;
706 }