linenoise 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 21125585819cb1e4b7334c6451cedf89075e8371
4
+ data.tar.gz: 3e135e25334f937331e1ad76152ebe23e9c8035b
5
+ SHA512:
6
+ metadata.gz: 61dff245d9241f8f264060f8084a4cf0ab9e5a4d05f7eb0ceca5f82d741cbde7cbd6ee5b21d3cebea51e9c218de6542333c5bf312c0d985bbb7149802de64629
7
+ data.tar.gz: 5c29070bd3157b220d1763203884c7ca6f6dbc05735f8d12e5bf635fee98eee716a6dba751ce4fa78d7b4b235ace6d17b16735771447e3875a574e36ce9aa9fb
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ Linenoise Ruby Changelog
2
+ ========================
3
+
4
+ ### master
5
+
6
+ ### [v1.0.0][v1.0.0] (November 25, 2018)
7
+
8
+ * Initial release
9
+
10
+ [v1.0.0]: https://github.com/kyrylo/linenoise-rb/releases/tag/v1.0.0
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+ ===============
3
+
4
+ Copyright © 2018 Kyrylo Silin
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
7
+ this software and associated documentation files (the 'Software'), to deal in
8
+ the Software without restriction, including without limitation the rights to
9
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10
+ the Software, and to permit persons to whom the Software is furnished to do so,
11
+ subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ Linenoise
2
+ =========
3
+
4
+ Introduction
5
+ ------------
6
+
7
+ TBA
8
+
9
+ Installation
10
+ ------------
11
+
12
+ TBA
13
+
14
+ Examples
15
+ --------
16
+
17
+ TBA
18
+
19
+ API
20
+ ---
21
+
22
+ TBA
23
+
24
+
25
+ Contact
26
+ -------
27
+
28
+ In case you have a problem, question or a bug report, feel free to:
29
+
30
+ * [file an issue](https://github.com/kyrylo/linenoise/issues)
31
+ * send me an email (see https://github.com/kyrylo)
32
+ * [tweet at me](https://twitter.com/kyrylosilin)
33
+
34
+ License
35
+ -------
36
+
37
+ The project uses the MIT License. See LICENSE.md for details.
@@ -0,0 +1,2 @@
1
+ require 'mkmf'
2
+ create_makefile('linenoise/linenoise')
@@ -0,0 +1,1201 @@
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-2016, Salvatore Sanfilippo <antirez at gmail dot com>
14
+ * Copyright (c) 2010-2013, 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
+ * - Filter bogus Ctrl+<char> combinations.
49
+ * - Win32 support
50
+ *
51
+ * Bloat:
52
+ * - History search like Ctrl+r in readline?
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
+ * EL (Erase Line)
60
+ * Sequence: ESC [ n K
61
+ * Effect: if n is 0 or missing, clear from cursor to end of line
62
+ * Effect: if n is 1, clear from beginning of line to cursor
63
+ * Effect: if n is 2, clear entire line
64
+ *
65
+ * CUF (CUrsor Forward)
66
+ * Sequence: ESC [ n C
67
+ * Effect: moves cursor forward n chars
68
+ *
69
+ * CUB (CUrsor Backward)
70
+ * Sequence: ESC [ n D
71
+ * Effect: moves cursor backward n chars
72
+ *
73
+ * The following is used to get the terminal width if getting
74
+ * the width with the TIOCGWINSZ ioctl fails
75
+ *
76
+ * DSR (Device Status Report)
77
+ * Sequence: ESC [ 6 n
78
+ * Effect: reports the current cusor position as ESC [ n ; m R
79
+ * where n is the row and m is the column
80
+ *
81
+ * When multi line mode is enabled, we also use an additional escape
82
+ * sequence. However multi line editing is disabled by default.
83
+ *
84
+ * CUU (Cursor Up)
85
+ * Sequence: ESC [ n A
86
+ * Effect: moves cursor up of n chars.
87
+ *
88
+ * CUD (Cursor Down)
89
+ * Sequence: ESC [ n B
90
+ * Effect: moves cursor down of n chars.
91
+ *
92
+ * When linenoiseClearScreen() is called, two additional escape sequences
93
+ * are used in order to clear the screen and position the cursor at home
94
+ * position.
95
+ *
96
+ * CUP (Cursor position)
97
+ * Sequence: ESC [ H
98
+ * Effect: moves the cursor to upper left corner
99
+ *
100
+ * ED (Erase display)
101
+ * Sequence: ESC [ 2 J
102
+ * Effect: clear the whole screen
103
+ *
104
+ */
105
+
106
+ #include <termios.h>
107
+ #include <unistd.h>
108
+ #include <stdlib.h>
109
+ #include <stdio.h>
110
+ #include <errno.h>
111
+ #include <string.h>
112
+ #include <stdlib.h>
113
+ #include <ctype.h>
114
+ #include <sys/stat.h>
115
+ #include <sys/types.h>
116
+ #include <sys/ioctl.h>
117
+ #include <unistd.h>
118
+ #include "line_noise.h"
119
+
120
+ #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
121
+ #define LINENOISE_MAX_LINE 4096
122
+ static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
123
+ static linenoiseCompletionCallback *completionCallback = NULL;
124
+ static linenoiseHintsCallback *hintsCallback = NULL;
125
+ static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
126
+
127
+ static struct termios orig_termios; /* In order to restore at exit.*/
128
+ static int rawmode = 0; /* For atexit() function to check if restore is needed*/
129
+ static int mlmode = 0; /* Multi line mode. Default is single line. */
130
+ static int atexit_registered = 0; /* Register atexit just 1 time. */
131
+ static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
132
+ static int history_len = 0;
133
+ static char **history = NULL;
134
+
135
+ /* The linenoiseState structure represents the state during line editing.
136
+ * We pass this state to functions implementing specific editing
137
+ * functionalities. */
138
+ struct linenoiseState {
139
+ int ifd; /* Terminal stdin file descriptor. */
140
+ int ofd; /* Terminal stdout file descriptor. */
141
+ char *buf; /* Edited line buffer. */
142
+ size_t buflen; /* Edited line buffer size. */
143
+ const char *prompt; /* Prompt to display. */
144
+ size_t plen; /* Prompt length. */
145
+ size_t pos; /* Current cursor position. */
146
+ size_t oldpos; /* Previous refresh cursor position. */
147
+ size_t len; /* Current edited line length. */
148
+ size_t cols; /* Number of columns in terminal. */
149
+ size_t maxrows; /* Maximum num of rows used so far (multiline mode) */
150
+ int history_index; /* The history index we are currently editing. */
151
+ };
152
+
153
+ enum KEY_ACTION{
154
+ KEY_NULL = 0, /* NULL */
155
+ CTRL_A = 1, /* Ctrl+a */
156
+ CTRL_B = 2, /* Ctrl-b */
157
+ CTRL_C = 3, /* Ctrl-c */
158
+ CTRL_D = 4, /* Ctrl-d */
159
+ CTRL_E = 5, /* Ctrl-e */
160
+ CTRL_F = 6, /* Ctrl-f */
161
+ CTRL_H = 8, /* Ctrl-h */
162
+ TAB = 9, /* Tab */
163
+ CTRL_K = 11, /* Ctrl+k */
164
+ CTRL_L = 12, /* Ctrl+l */
165
+ ENTER = 13, /* Enter */
166
+ CTRL_N = 14, /* Ctrl-n */
167
+ CTRL_P = 16, /* Ctrl-p */
168
+ CTRL_T = 20, /* Ctrl-t */
169
+ CTRL_U = 21, /* Ctrl+u */
170
+ CTRL_W = 23, /* Ctrl+w */
171
+ ESC = 27, /* Escape */
172
+ BACKSPACE = 127 /* Backspace */
173
+ };
174
+
175
+ static void linenoiseAtExit(void);
176
+ int linenoiseHistoryAdd(const char *line);
177
+ static void refreshLine(struct linenoiseState *l);
178
+
179
+ /* Debugging macro. */
180
+ #if 0
181
+ FILE *lndebug_fp = NULL;
182
+ #define lndebug(...) \
183
+ do { \
184
+ if (lndebug_fp == NULL) { \
185
+ lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
186
+ fprintf(lndebug_fp, \
187
+ "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
188
+ (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
189
+ (int)l->maxrows,old_rows); \
190
+ } \
191
+ fprintf(lndebug_fp, ", " __VA_ARGS__); \
192
+ fflush(lndebug_fp); \
193
+ } while (0)
194
+ #else
195
+ #define lndebug(fmt, ...)
196
+ #endif
197
+
198
+ /* ======================= Low level terminal handling ====================== */
199
+
200
+ /* Set if to use or not the multi line mode. */
201
+ void linenoiseSetMultiLine(int ml) {
202
+ mlmode = ml;
203
+ }
204
+
205
+ /* Return true if the terminal name is in the list of terminals we know are
206
+ * not able to understand basic escape sequences. */
207
+ static int isUnsupportedTerm(void) {
208
+ char *term = getenv("TERM");
209
+ int j;
210
+
211
+ if (term == NULL) return 0;
212
+ for (j = 0; unsupported_term[j]; j++)
213
+ if (!strcasecmp(term,unsupported_term[j])) return 1;
214
+ return 0;
215
+ }
216
+
217
+ /* Raw mode: 1960 magic shit. */
218
+ static int enableRawMode(int fd) {
219
+ struct termios raw;
220
+
221
+ if (!isatty(STDIN_FILENO)) goto fatal;
222
+ if (!atexit_registered) {
223
+ atexit(linenoiseAtExit);
224
+ atexit_registered = 1;
225
+ }
226
+ if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
227
+
228
+ raw = orig_termios; /* modify the original mode */
229
+ /* input modes: no break, no CR to NL, no parity check, no strip char,
230
+ * no start/stop output control. */
231
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
232
+ /* output modes - disable post processing */
233
+ raw.c_oflag &= ~(OPOST);
234
+ /* control modes - set 8 bit chars */
235
+ raw.c_cflag |= (CS8);
236
+ /* local modes - choing off, canonical off, no extended functions,
237
+ * no signal chars (^Z,^C) */
238
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
239
+ /* control chars - set return condition: min number of bytes and timer.
240
+ * We want read to return every single byte, without timeout. */
241
+ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
242
+
243
+ /* put terminal in raw mode after flushing */
244
+ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
245
+ rawmode = 1;
246
+ return 0;
247
+
248
+ fatal:
249
+ errno = ENOTTY;
250
+ return -1;
251
+ }
252
+
253
+ static void disableRawMode(int fd) {
254
+ /* Don't even check the return value as it's too late. */
255
+ if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
256
+ rawmode = 0;
257
+ }
258
+
259
+ /* Use the ESC [6n escape sequence to query the horizontal cursor position
260
+ * and return it. On error -1 is returned, on success the position of the
261
+ * cursor. */
262
+ static int getCursorPosition(int ifd, int ofd) {
263
+ char buf[32];
264
+ int cols, rows;
265
+ unsigned int i = 0;
266
+
267
+ /* Report cursor location */
268
+ if (write(ofd, "\x1b[6n", 4) != 4) return -1;
269
+
270
+ /* Read the response: ESC [ rows ; cols R */
271
+ while (i < sizeof(buf)-1) {
272
+ if (read(ifd,buf+i,1) != 1) break;
273
+ if (buf[i] == 'R') break;
274
+ i++;
275
+ }
276
+ buf[i] = '\0';
277
+
278
+ /* Parse it. */
279
+ if (buf[0] != ESC || buf[1] != '[') return -1;
280
+ if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
281
+ return cols;
282
+ }
283
+
284
+ /* Try to get the number of columns in the current terminal, or assume 80
285
+ * if it fails. */
286
+ static int getColumns(int ifd, int ofd) {
287
+ struct winsize ws;
288
+
289
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
290
+ /* ioctl() failed. Try to query the terminal itself. */
291
+ int start, cols;
292
+
293
+ /* Get the initial position so we can restore it later. */
294
+ start = getCursorPosition(ifd,ofd);
295
+ if (start == -1) goto failed;
296
+
297
+ /* Go to right margin and get position. */
298
+ if (write(ofd,"\x1b[999C",6) != 6) goto failed;
299
+ cols = getCursorPosition(ifd,ofd);
300
+ if (cols == -1) goto failed;
301
+
302
+ /* Restore position. */
303
+ if (cols > start) {
304
+ char seq[32];
305
+ snprintf(seq,32,"\x1b[%dD",cols-start);
306
+ if (write(ofd,seq,strlen(seq)) == -1) {
307
+ /* Can't recover... */
308
+ }
309
+ }
310
+ return cols;
311
+ } else {
312
+ return ws.ws_col;
313
+ }
314
+
315
+ failed:
316
+ return 80;
317
+ }
318
+
319
+ /* Clear the screen. Used to handle ctrl+l */
320
+ void linenoiseClearScreen(void) {
321
+ if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
322
+ /* nothing to do, just to avoid warning. */
323
+ }
324
+ }
325
+
326
+ /* Beep, used for completion when there is nothing to complete or when all
327
+ * the choices were already shown. */
328
+ static void linenoiseBeep(void) {
329
+ fprintf(stderr, "\x7");
330
+ fflush(stderr);
331
+ }
332
+
333
+ /* ============================== Completion ================================ */
334
+
335
+ /* Free a list of completion option populated by linenoiseAddCompletion(). */
336
+ static void freeCompletions(linenoiseCompletions *lc) {
337
+ size_t i;
338
+ for (i = 0; i < lc->len; i++)
339
+ free(lc->cvec[i]);
340
+ if (lc->cvec != NULL)
341
+ free(lc->cvec);
342
+ }
343
+
344
+ /* This is an helper function for linenoiseEdit() and is called when the
345
+ * user types the <tab> key in order to complete the string currently in the
346
+ * input.
347
+ *
348
+ * The state of the editing is encapsulated into the pointed linenoiseState
349
+ * structure as described in the structure definition. */
350
+ static int completeLine(struct linenoiseState *ls) {
351
+ linenoiseCompletions lc = { 0, NULL };
352
+ int nread, nwritten;
353
+ char c = 0;
354
+
355
+ completionCallback(ls->buf,&lc);
356
+ if (lc.len == 0) {
357
+ linenoiseBeep();
358
+ } else {
359
+ size_t stop = 0, i = 0;
360
+
361
+ while(!stop) {
362
+ /* Show completion or original buffer */
363
+ if (i < lc.len) {
364
+ struct linenoiseState saved = *ls;
365
+
366
+ ls->len = ls->pos = strlen(lc.cvec[i]);
367
+ ls->buf = lc.cvec[i];
368
+ refreshLine(ls);
369
+ ls->len = saved.len;
370
+ ls->pos = saved.pos;
371
+ ls->buf = saved.buf;
372
+ } else {
373
+ refreshLine(ls);
374
+ }
375
+
376
+ nread = read(ls->ifd,&c,1);
377
+ if (nread <= 0) {
378
+ freeCompletions(&lc);
379
+ return -1;
380
+ }
381
+
382
+ switch(c) {
383
+ case 9: /* tab */
384
+ i = (i+1) % (lc.len+1);
385
+ if (i == lc.len) linenoiseBeep();
386
+ break;
387
+ case 27: /* escape */
388
+ /* Re-show original buffer */
389
+ if (i < lc.len) refreshLine(ls);
390
+ stop = 1;
391
+ break;
392
+ default:
393
+ /* Update buffer and return */
394
+ if (i < lc.len) {
395
+ nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]);
396
+ ls->len = ls->pos = nwritten;
397
+ }
398
+ stop = 1;
399
+ break;
400
+ }
401
+ }
402
+ }
403
+
404
+ freeCompletions(&lc);
405
+ return c; /* Return last read character */
406
+ }
407
+
408
+ /* Register a callback function to be called for tab-completion. */
409
+ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
410
+ completionCallback = fn;
411
+ }
412
+
413
+ /* Register a hits function to be called to show hits to the user at the
414
+ * right of the prompt. */
415
+ void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
416
+ hintsCallback = fn;
417
+ }
418
+
419
+ /* Register a function to free the hints returned by the hints callback
420
+ * registered with linenoiseSetHintsCallback(). */
421
+ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
422
+ freeHintsCallback = fn;
423
+ }
424
+
425
+ /* This function is used by the callback function registered by the user
426
+ * in order to add completion options given the input string when the
427
+ * user typed <tab>. See the example.c source code for a very easy to
428
+ * understand example. */
429
+ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
430
+ size_t len = strlen(str);
431
+ char *copy, **cvec;
432
+
433
+ copy = malloc(len+1);
434
+ if (copy == NULL) return;
435
+ memcpy(copy,str,len+1);
436
+ cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
437
+ if (cvec == NULL) {
438
+ free(copy);
439
+ return;
440
+ }
441
+ lc->cvec = cvec;
442
+ lc->cvec[lc->len++] = copy;
443
+ }
444
+
445
+ /* =========================== Line editing ================================= */
446
+
447
+ /* We define a very simple "append buffer" structure, that is an heap
448
+ * allocated string where we can append to. This is useful in order to
449
+ * write all the escape sequences in a buffer and flush them to the standard
450
+ * output in a single call, to avoid flickering effects. */
451
+ struct abuf {
452
+ char *b;
453
+ int len;
454
+ };
455
+
456
+ static void abInit(struct abuf *ab) {
457
+ ab->b = NULL;
458
+ ab->len = 0;
459
+ }
460
+
461
+ static void abAppend(struct abuf *ab, const char *s, int len) {
462
+ char *new = realloc(ab->b,ab->len+len);
463
+
464
+ if (new == NULL) return;
465
+ memcpy(new+ab->len,s,len);
466
+ ab->b = new;
467
+ ab->len += len;
468
+ }
469
+
470
+ static void abFree(struct abuf *ab) {
471
+ free(ab->b);
472
+ }
473
+
474
+ /* Helper of refreshSingleLine() and refreshMultiLine() to show hints
475
+ * to the right of the prompt. */
476
+ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
477
+ char seq[64];
478
+ if (hintsCallback && plen+l->len < l->cols) {
479
+ int color = -1, bold = 0;
480
+ char *hint = hintsCallback(l->buf,&color,&bold);
481
+ if (hint) {
482
+ int hintlen = strlen(hint);
483
+ int hintmaxlen = l->cols-(plen+l->len);
484
+ if (hintlen > hintmaxlen) hintlen = hintmaxlen;
485
+ if (bold == 1 && color == -1) color = 37;
486
+ if (color != -1 || bold != 0)
487
+ snprintf(seq,64,"\033[%d;%d;49m",bold,color);
488
+ else
489
+ seq[0] = '\0';
490
+ abAppend(ab,seq,strlen(seq));
491
+ abAppend(ab,hint,hintlen);
492
+ if (color != -1 || bold != 0)
493
+ abAppend(ab,"\033[0m",4);
494
+ /* Call the function to free the hint returned. */
495
+ if (freeHintsCallback) freeHintsCallback(hint);
496
+ }
497
+ }
498
+ }
499
+
500
+ /* Single line low level line refresh.
501
+ *
502
+ * Rewrite the currently edited line accordingly to the buffer content,
503
+ * cursor position, and number of columns of the terminal. */
504
+ static void refreshSingleLine(struct linenoiseState *l) {
505
+ char seq[64];
506
+ size_t plen = strlen(l->prompt);
507
+ int fd = l->ofd;
508
+ char *buf = l->buf;
509
+ size_t len = l->len;
510
+ size_t pos = l->pos;
511
+ struct abuf ab;
512
+
513
+ while((plen+pos) >= l->cols) {
514
+ buf++;
515
+ len--;
516
+ pos--;
517
+ }
518
+ while (plen+len > l->cols) {
519
+ len--;
520
+ }
521
+
522
+ abInit(&ab);
523
+ /* Cursor to left edge */
524
+ snprintf(seq,64,"\r");
525
+ abAppend(&ab,seq,strlen(seq));
526
+ /* Write the prompt and the current buffer content */
527
+ abAppend(&ab,l->prompt,strlen(l->prompt));
528
+ abAppend(&ab,buf,len);
529
+ /* Show hits if any. */
530
+ refreshShowHints(&ab,l,plen);
531
+ /* Erase to right */
532
+ snprintf(seq,64,"\x1b[0K");
533
+ abAppend(&ab,seq,strlen(seq));
534
+ /* Move cursor to original position. */
535
+ snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
536
+ abAppend(&ab,seq,strlen(seq));
537
+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
538
+ abFree(&ab);
539
+ }
540
+
541
+ /* Multi line low level line refresh.
542
+ *
543
+ * Rewrite the currently edited line accordingly to the buffer content,
544
+ * cursor position, and number of columns of the terminal. */
545
+ static void refreshMultiLine(struct linenoiseState *l) {
546
+ char seq[64];
547
+ int plen = strlen(l->prompt);
548
+ int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */
549
+ int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */
550
+ int rpos2; /* rpos after refresh. */
551
+ int col; /* colum position, zero-based. */
552
+ int old_rows = l->maxrows;
553
+ int fd = l->ofd, j;
554
+ struct abuf ab;
555
+
556
+ /* Update maxrows if needed. */
557
+ if (rows > (int)l->maxrows) l->maxrows = rows;
558
+
559
+ /* First step: clear all the lines used before. To do so start by
560
+ * going to the last row. */
561
+ abInit(&ab);
562
+ if (old_rows-rpos > 0) {
563
+ lndebug("go down %d", old_rows-rpos);
564
+ snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
565
+ abAppend(&ab,seq,strlen(seq));
566
+ }
567
+
568
+ /* Now for every row clear it, go up. */
569
+ for (j = 0; j < old_rows-1; j++) {
570
+ lndebug("clear+up");
571
+ snprintf(seq,64,"\r\x1b[0K\x1b[1A");
572
+ abAppend(&ab,seq,strlen(seq));
573
+ }
574
+
575
+ /* Clean the top line. */
576
+ lndebug("clear");
577
+ snprintf(seq,64,"\r\x1b[0K");
578
+ abAppend(&ab,seq,strlen(seq));
579
+
580
+ /* Write the prompt and the current buffer content */
581
+ abAppend(&ab,l->prompt,strlen(l->prompt));
582
+ abAppend(&ab,l->buf,l->len);
583
+
584
+ /* Show hits if any. */
585
+ refreshShowHints(&ab,l,plen);
586
+
587
+ /* If we are at the very end of the screen with our prompt, we need to
588
+ * emit a newline and move the prompt to the first column. */
589
+ if (l->pos &&
590
+ l->pos == l->len &&
591
+ (l->pos+plen) % l->cols == 0)
592
+ {
593
+ lndebug("<newline>");
594
+ abAppend(&ab,"\n",1);
595
+ snprintf(seq,64,"\r");
596
+ abAppend(&ab,seq,strlen(seq));
597
+ rows++;
598
+ if (rows > (int)l->maxrows) l->maxrows = rows;
599
+ }
600
+
601
+ /* Move cursor to right position. */
602
+ rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
603
+ lndebug("rpos2 %d", rpos2);
604
+
605
+ /* Go up till we reach the expected positon. */
606
+ if (rows-rpos2 > 0) {
607
+ lndebug("go-up %d", rows-rpos2);
608
+ snprintf(seq,64,"\x1b[%dA", rows-rpos2);
609
+ abAppend(&ab,seq,strlen(seq));
610
+ }
611
+
612
+ /* Set column. */
613
+ col = (plen+(int)l->pos) % (int)l->cols;
614
+ lndebug("set col %d", 1+col);
615
+ if (col)
616
+ snprintf(seq,64,"\r\x1b[%dC", col);
617
+ else
618
+ snprintf(seq,64,"\r");
619
+ abAppend(&ab,seq,strlen(seq));
620
+
621
+ lndebug("\n");
622
+ l->oldpos = l->pos;
623
+
624
+ if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
625
+ abFree(&ab);
626
+ }
627
+
628
+ /* Calls the two low level functions refreshSingleLine() or
629
+ * refreshMultiLine() according to the selected mode. */
630
+ static void refreshLine(struct linenoiseState *l) {
631
+ if (mlmode)
632
+ refreshMultiLine(l);
633
+ else
634
+ refreshSingleLine(l);
635
+ }
636
+
637
+ /* Insert the character 'c' at cursor current position.
638
+ *
639
+ * On error writing to the terminal -1 is returned, otherwise 0. */
640
+ int linenoiseEditInsert(struct linenoiseState *l, char c) {
641
+ if (l->len < l->buflen) {
642
+ if (l->len == l->pos) {
643
+ l->buf[l->pos] = c;
644
+ l->pos++;
645
+ l->len++;
646
+ l->buf[l->len] = '\0';
647
+ if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
648
+ /* Avoid a full update of the line in the
649
+ * trivial case. */
650
+ if (write(l->ofd,&c,1) == -1) return -1;
651
+ } else {
652
+ refreshLine(l);
653
+ }
654
+ } else {
655
+ memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);
656
+ l->buf[l->pos] = c;
657
+ l->len++;
658
+ l->pos++;
659
+ l->buf[l->len] = '\0';
660
+ refreshLine(l);
661
+ }
662
+ }
663
+ return 0;
664
+ }
665
+
666
+ /* Move cursor on the left. */
667
+ void linenoiseEditMoveLeft(struct linenoiseState *l) {
668
+ if (l->pos > 0) {
669
+ l->pos--;
670
+ refreshLine(l);
671
+ }
672
+ }
673
+
674
+ /* Move cursor on the right. */
675
+ void linenoiseEditMoveRight(struct linenoiseState *l) {
676
+ if (l->pos != l->len) {
677
+ l->pos++;
678
+ refreshLine(l);
679
+ }
680
+ }
681
+
682
+ /* Move cursor to the start of the line. */
683
+ void linenoiseEditMoveHome(struct linenoiseState *l) {
684
+ if (l->pos != 0) {
685
+ l->pos = 0;
686
+ refreshLine(l);
687
+ }
688
+ }
689
+
690
+ /* Move cursor to the end of the line. */
691
+ void linenoiseEditMoveEnd(struct linenoiseState *l) {
692
+ if (l->pos != l->len) {
693
+ l->pos = l->len;
694
+ refreshLine(l);
695
+ }
696
+ }
697
+
698
+ /* Substitute the currently edited line with the next or previous history
699
+ * entry as specified by 'dir'. */
700
+ #define LINENOISE_HISTORY_NEXT 0
701
+ #define LINENOISE_HISTORY_PREV 1
702
+ void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) {
703
+ if (history_len > 1) {
704
+ /* Update the current history entry before to
705
+ * overwrite it with the next one. */
706
+ free(history[history_len - 1 - l->history_index]);
707
+ history[history_len - 1 - l->history_index] = strdup(l->buf);
708
+ /* Show the new entry */
709
+ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
710
+ if (l->history_index < 0) {
711
+ l->history_index = 0;
712
+ return;
713
+ } else if (l->history_index >= history_len) {
714
+ l->history_index = history_len-1;
715
+ return;
716
+ }
717
+ strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen);
718
+ l->buf[l->buflen-1] = '\0';
719
+ l->len = l->pos = strlen(l->buf);
720
+ refreshLine(l);
721
+ }
722
+ }
723
+
724
+ /* Delete the character at the right of the cursor without altering the cursor
725
+ * position. Basically this is what happens with the "Delete" keyboard key. */
726
+ void linenoiseEditDelete(struct linenoiseState *l) {
727
+ if (l->len > 0 && l->pos < l->len) {
728
+ memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1);
729
+ l->len--;
730
+ l->buf[l->len] = '\0';
731
+ refreshLine(l);
732
+ }
733
+ }
734
+
735
+ /* Backspace implementation. */
736
+ void linenoiseEditBackspace(struct linenoiseState *l) {
737
+ if (l->pos > 0 && l->len > 0) {
738
+ memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos);
739
+ l->pos--;
740
+ l->len--;
741
+ l->buf[l->len] = '\0';
742
+ refreshLine(l);
743
+ }
744
+ }
745
+
746
+ /* Delete the previosu word, maintaining the cursor at the start of the
747
+ * current word. */
748
+ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
749
+ size_t old_pos = l->pos;
750
+ size_t diff;
751
+
752
+ while (l->pos > 0 && l->buf[l->pos-1] == ' ')
753
+ l->pos--;
754
+ while (l->pos > 0 && l->buf[l->pos-1] != ' ')
755
+ l->pos--;
756
+ diff = old_pos - l->pos;
757
+ memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1);
758
+ l->len -= diff;
759
+ refreshLine(l);
760
+ }
761
+
762
+ /* This function is the core of the line editing capability of linenoise.
763
+ * It expects 'fd' to be already in "raw mode" so that every key pressed
764
+ * will be returned ASAP to read().
765
+ *
766
+ * The resulting string is put into 'buf' when the user type enter, or
767
+ * when ctrl+d is typed.
768
+ *
769
+ * The function returns the length of the current buffer. */
770
+ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
771
+ {
772
+ struct linenoiseState l;
773
+
774
+ /* Populate the linenoise state that we pass to functions implementing
775
+ * specific editing functionalities. */
776
+ l.ifd = stdin_fd;
777
+ l.ofd = stdout_fd;
778
+ l.buf = buf;
779
+ l.buflen = buflen;
780
+ l.prompt = prompt;
781
+ l.plen = strlen(prompt);
782
+ l.oldpos = l.pos = 0;
783
+ l.len = 0;
784
+ l.cols = getColumns(stdin_fd, stdout_fd);
785
+ l.maxrows = 0;
786
+ l.history_index = 0;
787
+
788
+ /* Buffer starts empty. */
789
+ l.buf[0] = '\0';
790
+ l.buflen--; /* Make sure there is always space for the nulterm */
791
+
792
+ /* The latest history entry is always our current buffer, that
793
+ * initially is just an empty string. */
794
+ linenoiseHistoryAdd("");
795
+
796
+ if (write(l.ofd,prompt,l.plen) == -1) return -1;
797
+ while(1) {
798
+ char c;
799
+ int nread;
800
+ char seq[3];
801
+
802
+ nread = read(l.ifd,&c,1);
803
+ if (nread <= 0) return l.len;
804
+
805
+ /* Only autocomplete when the callback is set. It returns < 0 when
806
+ * there was an error reading from fd. Otherwise it will return the
807
+ * character that should be handled next. */
808
+ if (c == 9 && completionCallback != NULL) {
809
+ c = completeLine(&l);
810
+ /* Return on errors */
811
+ if (c < 0) return l.len;
812
+ /* Read next character when 0 */
813
+ if (c == 0) continue;
814
+ }
815
+
816
+ switch(c) {
817
+ case ENTER: /* enter */
818
+ history_len--;
819
+ free(history[history_len]);
820
+ if (mlmode) linenoiseEditMoveEnd(&l);
821
+ if (hintsCallback) {
822
+ /* Force a refresh without hints to leave the previous
823
+ * line as the user typed it after a newline. */
824
+ linenoiseHintsCallback *hc = hintsCallback;
825
+ hintsCallback = NULL;
826
+ refreshLine(&l);
827
+ hintsCallback = hc;
828
+ }
829
+ return (int)l.len;
830
+ case CTRL_C: /* ctrl-c */
831
+ errno = EAGAIN;
832
+ return -1;
833
+ case BACKSPACE: /* backspace */
834
+ case 8: /* ctrl-h */
835
+ linenoiseEditBackspace(&l);
836
+ break;
837
+ case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
838
+ line is empty, act as end-of-file. */
839
+ if (l.len > 0) {
840
+ linenoiseEditDelete(&l);
841
+ } else {
842
+ history_len--;
843
+ free(history[history_len]);
844
+ return -1;
845
+ }
846
+ break;
847
+ case CTRL_T: /* ctrl-t, swaps current character with previous. */
848
+ if (l.pos > 0 && l.pos < l.len) {
849
+ int aux = buf[l.pos-1];
850
+ buf[l.pos-1] = buf[l.pos];
851
+ buf[l.pos] = aux;
852
+ if (l.pos != l.len-1) l.pos++;
853
+ refreshLine(&l);
854
+ }
855
+ break;
856
+ case CTRL_B: /* ctrl-b */
857
+ linenoiseEditMoveLeft(&l);
858
+ break;
859
+ case CTRL_F: /* ctrl-f */
860
+ linenoiseEditMoveRight(&l);
861
+ break;
862
+ case CTRL_P: /* ctrl-p */
863
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
864
+ break;
865
+ case CTRL_N: /* ctrl-n */
866
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
867
+ break;
868
+ case ESC: /* escape sequence */
869
+ /* Read the next two bytes representing the escape sequence.
870
+ * Use two calls to handle slow terminals returning the two
871
+ * chars at different times. */
872
+ if (read(l.ifd,seq,1) == -1) break;
873
+ if (read(l.ifd,seq+1,1) == -1) break;
874
+
875
+ /* ESC [ sequences. */
876
+ if (seq[0] == '[') {
877
+ if (seq[1] >= '0' && seq[1] <= '9') {
878
+ /* Extended escape, read additional byte. */
879
+ if (read(l.ifd,seq+2,1) == -1) break;
880
+ if (seq[2] == '~') {
881
+ switch(seq[1]) {
882
+ case '3': /* Delete key. */
883
+ linenoiseEditDelete(&l);
884
+ break;
885
+ }
886
+ }
887
+ } else {
888
+ switch(seq[1]) {
889
+ case 'A': /* Up */
890
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
891
+ break;
892
+ case 'B': /* Down */
893
+ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
894
+ break;
895
+ case 'C': /* Right */
896
+ linenoiseEditMoveRight(&l);
897
+ break;
898
+ case 'D': /* Left */
899
+ linenoiseEditMoveLeft(&l);
900
+ break;
901
+ case 'H': /* Home */
902
+ linenoiseEditMoveHome(&l);
903
+ break;
904
+ case 'F': /* End*/
905
+ linenoiseEditMoveEnd(&l);
906
+ break;
907
+ }
908
+ }
909
+ }
910
+
911
+ /* ESC O sequences. */
912
+ else if (seq[0] == 'O') {
913
+ switch(seq[1]) {
914
+ case 'H': /* Home */
915
+ linenoiseEditMoveHome(&l);
916
+ break;
917
+ case 'F': /* End*/
918
+ linenoiseEditMoveEnd(&l);
919
+ break;
920
+ }
921
+ }
922
+ break;
923
+ default:
924
+ if (linenoiseEditInsert(&l,c)) return -1;
925
+ break;
926
+ case CTRL_U: /* Ctrl+u, delete the whole line. */
927
+ buf[0] = '\0';
928
+ l.pos = l.len = 0;
929
+ refreshLine(&l);
930
+ break;
931
+ case CTRL_K: /* Ctrl+k, delete from current to end of line. */
932
+ buf[l.pos] = '\0';
933
+ l.len = l.pos;
934
+ refreshLine(&l);
935
+ break;
936
+ case CTRL_A: /* Ctrl+a, go to the start of the line */
937
+ linenoiseEditMoveHome(&l);
938
+ break;
939
+ case CTRL_E: /* ctrl+e, go to the end of the line */
940
+ linenoiseEditMoveEnd(&l);
941
+ break;
942
+ case CTRL_L: /* ctrl+l, clear screen */
943
+ linenoiseClearScreen();
944
+ refreshLine(&l);
945
+ break;
946
+ case CTRL_W: /* ctrl+w, delete previous word */
947
+ linenoiseEditDeletePrevWord(&l);
948
+ break;
949
+ }
950
+ }
951
+ return l.len;
952
+ }
953
+
954
+ /* This special mode is used by linenoise in order to print scan codes
955
+ * on screen for debugging / development purposes. It is implemented
956
+ * by the linenoise_example program using the --keycodes option. */
957
+ void linenoisePrintKeyCodes(void) {
958
+ char quit[4];
959
+
960
+ printf("Linenoise key codes debugging mode.\n"
961
+ "Press keys to see scan codes. Type 'quit' at any time to exit.\n");
962
+ if (enableRawMode(STDIN_FILENO) == -1) return;
963
+ memset(quit,' ',4);
964
+ while(1) {
965
+ char c;
966
+ int nread;
967
+
968
+ nread = read(STDIN_FILENO,&c,1);
969
+ if (nread <= 0) continue;
970
+ memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */
971
+ quit[sizeof(quit)-1] = c; /* Insert current char on the right. */
972
+ if (memcmp(quit,"quit",sizeof(quit)) == 0) break;
973
+
974
+ printf("'%c' %02x (%d) (type quit to exit)\n",
975
+ isprint(c) ? c : '?', (int)c, (int)c);
976
+ printf("\r"); /* Go left edge manually, we are in raw mode. */
977
+ fflush(stdout);
978
+ }
979
+ disableRawMode(STDIN_FILENO);
980
+ }
981
+
982
+ /* This function calls the line editing function linenoiseEdit() using
983
+ * the STDIN file descriptor set in raw mode. */
984
+ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
985
+ int count;
986
+
987
+ if (buflen == 0) {
988
+ errno = EINVAL;
989
+ return -1;
990
+ }
991
+
992
+ if (enableRawMode(STDIN_FILENO) == -1) return -1;
993
+ count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
994
+ disableRawMode(STDIN_FILENO);
995
+ printf("\n");
996
+ return count;
997
+ }
998
+
999
+ /* This function is called when linenoise() is called with the standard
1000
+ * input file descriptor not attached to a TTY. So for example when the
1001
+ * program using linenoise is called in pipe or with a file redirected
1002
+ * to its standard input. In this case, we want to be able to return the
1003
+ * line regardless of its length (by default we are limited to 4k). */
1004
+ static char *linenoiseNoTTY(void) {
1005
+ char *line = NULL;
1006
+ size_t len = 0, maxlen = 0;
1007
+
1008
+ while(1) {
1009
+ if (len == maxlen) {
1010
+ if (maxlen == 0) maxlen = 16;
1011
+ maxlen *= 2;
1012
+ char *oldval = line;
1013
+ line = realloc(line,maxlen);
1014
+ if (line == NULL) {
1015
+ if (oldval) free(oldval);
1016
+ return NULL;
1017
+ }
1018
+ }
1019
+ int c = fgetc(stdin);
1020
+ if (c == EOF || c == '\n') {
1021
+ if (c == EOF && len == 0) {
1022
+ free(line);
1023
+ return NULL;
1024
+ } else {
1025
+ line[len] = '\0';
1026
+ return line;
1027
+ }
1028
+ } else {
1029
+ line[len] = c;
1030
+ len++;
1031
+ }
1032
+ }
1033
+ }
1034
+
1035
+ /* The high level function that is the main API of the linenoise library.
1036
+ * This function checks if the terminal has basic capabilities, just checking
1037
+ * for a blacklist of stupid terminals, and later either calls the line
1038
+ * editing function or uses dummy fgets() so that you will be able to type
1039
+ * something even in the most desperate of the conditions. */
1040
+ char *linenoise(const char *prompt) {
1041
+ char buf[LINENOISE_MAX_LINE];
1042
+ int count;
1043
+
1044
+ if (!isatty(STDIN_FILENO)) {
1045
+ /* Not a tty: read from file / pipe. In this mode we don't want any
1046
+ * limit to the line size, so we call a function to handle that. */
1047
+ return linenoiseNoTTY();
1048
+ } else if (isUnsupportedTerm()) {
1049
+ size_t len;
1050
+
1051
+ printf("%s",prompt);
1052
+ fflush(stdout);
1053
+ if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
1054
+ len = strlen(buf);
1055
+ while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
1056
+ len--;
1057
+ buf[len] = '\0';
1058
+ }
1059
+ return strdup(buf);
1060
+ } else {
1061
+ count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
1062
+ if (count == -1) return NULL;
1063
+ return strdup(buf);
1064
+ }
1065
+ }
1066
+
1067
+ /* This is just a wrapper the user may want to call in order to make sure
1068
+ * the linenoise returned buffer is freed with the same allocator it was
1069
+ * created with. Useful when the main program is using an alternative
1070
+ * allocator. */
1071
+ void linenoiseFree(void *ptr) {
1072
+ free(ptr);
1073
+ }
1074
+
1075
+ /* ================================ History ================================= */
1076
+
1077
+ /* Free the history, but does not reset it. Only used when we have to
1078
+ * exit() to avoid memory leaks are reported by valgrind & co. */
1079
+ static void freeHistory(void) {
1080
+ if (history) {
1081
+ int j;
1082
+
1083
+ for (j = 0; j < history_len; j++)
1084
+ free(history[j]);
1085
+ free(history);
1086
+ }
1087
+ }
1088
+
1089
+ /* At exit we'll try to fix the terminal to the initial conditions. */
1090
+ static void linenoiseAtExit(void) {
1091
+ disableRawMode(STDIN_FILENO);
1092
+ freeHistory();
1093
+ }
1094
+
1095
+ /* This is the API call to add a new entry in the linenoise history.
1096
+ * It uses a fixed array of char pointers that are shifted (memmoved)
1097
+ * when the history max length is reached in order to remove the older
1098
+ * entry and make room for the new one, so it is not exactly suitable for huge
1099
+ * histories, but will work well for a few hundred of entries.
1100
+ *
1101
+ * Using a circular buffer is smarter, but a bit more complex to handle. */
1102
+ int linenoiseHistoryAdd(const char *line) {
1103
+ char *linecopy;
1104
+
1105
+ if (history_max_len == 0) return 0;
1106
+
1107
+ /* Initialization on first call. */
1108
+ if (history == NULL) {
1109
+ history = malloc(sizeof(char*)*history_max_len);
1110
+ if (history == NULL) return 0;
1111
+ memset(history,0,(sizeof(char*)*history_max_len));
1112
+ }
1113
+
1114
+ /* Don't add duplicated lines. */
1115
+ if (history_len && !strcmp(history[history_len-1], line)) return 0;
1116
+
1117
+ /* Add an heap allocated copy of the line in the history.
1118
+ * If we reached the max length, remove the older line. */
1119
+ linecopy = strdup(line);
1120
+ if (!linecopy) return 0;
1121
+ if (history_len == history_max_len) {
1122
+ free(history[0]);
1123
+ memmove(history,history+1,sizeof(char*)*(history_max_len-1));
1124
+ history_len--;
1125
+ }
1126
+ history[history_len] = linecopy;
1127
+ history_len++;
1128
+ return 1;
1129
+ }
1130
+
1131
+ /* Set the maximum length for the history. This function can be called even
1132
+ * if there is already some history, the function will make sure to retain
1133
+ * just the latest 'len' elements if the new history length value is smaller
1134
+ * than the amount of items already inside the history. */
1135
+ int linenoiseHistorySetMaxLen(int len) {
1136
+ char **new;
1137
+
1138
+ if (len < 1) return 0;
1139
+ if (history) {
1140
+ int tocopy = history_len;
1141
+
1142
+ new = malloc(sizeof(char*)*len);
1143
+ if (new == NULL) return 0;
1144
+
1145
+ /* If we can't copy everything, free the elements we'll not use. */
1146
+ if (len < tocopy) {
1147
+ int j;
1148
+
1149
+ for (j = 0; j < tocopy-len; j++) free(history[j]);
1150
+ tocopy = len;
1151
+ }
1152
+ memset(new,0,sizeof(char*)*len);
1153
+ memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
1154
+ free(history);
1155
+ history = new;
1156
+ }
1157
+ history_max_len = len;
1158
+ if (history_len > history_max_len)
1159
+ history_len = history_max_len;
1160
+ return 1;
1161
+ }
1162
+
1163
+ /* Save the history in the specified file. On success 0 is returned
1164
+ * otherwise -1 is returned. */
1165
+ int linenoiseHistorySave(const char *filename) {
1166
+ mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
1167
+ FILE *fp;
1168
+ int j;
1169
+
1170
+ fp = fopen(filename,"w");
1171
+ umask(old_umask);
1172
+ if (fp == NULL) return -1;
1173
+ chmod(filename,S_IRUSR|S_IWUSR);
1174
+ for (j = 0; j < history_len; j++)
1175
+ fprintf(fp,"%s\n",history[j]);
1176
+ fclose(fp);
1177
+ return 0;
1178
+ }
1179
+
1180
+ /* Load the history from the specified file. If the file does not exist
1181
+ * zero is returned and no operation is performed.
1182
+ *
1183
+ * If the file exists and the operation succeeded 0 is returned, otherwise
1184
+ * on error -1 is returned. */
1185
+ int linenoiseHistoryLoad(const char *filename) {
1186
+ FILE *fp = fopen(filename,"r");
1187
+ char buf[LINENOISE_MAX_LINE];
1188
+
1189
+ if (fp == NULL) return -1;
1190
+
1191
+ while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
1192
+ char *p;
1193
+
1194
+ p = strchr(buf,'\r');
1195
+ if (!p) p = strchr(buf,'\n');
1196
+ if (p) *p = '\0';
1197
+ linenoiseHistoryAdd(buf);
1198
+ }
1199
+ fclose(fp);
1200
+ return 0;
1201
+ }