Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/linenoise/linenoise.c
2066 views
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/msteveb/linenoise
7
* (forked from http://github.com/antirez/linenoise)
8
*
9
* Does a number of crazy assumptions that happen to be true in 99.9999% of
10
* the 2010 UNIX computers around.
11
*
12
* ------------------------------------------------------------------------
13
*
14
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
15
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
16
* Copyright (c) 2011, Steve Bennett <steveb at workware dot net dot au>
17
*
18
* All rights reserved.
19
*
20
* Redistribution and use in source and binary forms, with or without
21
* modification, are permitted provided that the following conditions are
22
* met:
23
*
24
* * Redistributions of source code must retain the above copyright
25
* notice, this list of conditions and the following disclaimer.
26
*
27
* * Redistributions in binary form must reproduce the above copyright
28
* notice, this list of conditions and the following disclaimer in the
29
* documentation and/or other materials provided with the distribution.
30
*
31
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42
*
43
* ------------------------------------------------------------------------
44
*
45
* References:
46
* - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
47
* - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
48
*
49
* Bloat:
50
* - Completion?
51
*
52
* Unix/termios
53
* ------------
54
* List of escape sequences used by this program, we do everything just
55
* a few 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 [ 0 K
61
* Effect: clear from cursor to end of line
62
*
63
* CUF (CUrsor Forward)
64
* Sequence: ESC [ n C
65
* Effect: moves cursor forward n chars
66
*
67
* CR (Carriage Return)
68
* Sequence: \r
69
* Effect: moves cursor to column 1
70
*
71
* The following are used to clear the screen: ESC [ H ESC [ 2 J
72
* This is actually composed of two sequences:
73
*
74
* cursorhome
75
* Sequence: ESC [ H
76
* Effect: moves the cursor to upper left corner
77
*
78
* ED2 (Clear entire screen)
79
* Sequence: ESC [ 2 J
80
* Effect: clear the whole screen
81
*
82
* == For highlighting control characters, we also use the following two ==
83
* SO (enter StandOut)
84
* Sequence: ESC [ 7 m
85
* Effect: Uses some standout mode such as reverse video
86
*
87
* SE (Standout End)
88
* Sequence: ESC [ 0 m
89
* Effect: Exit standout mode
90
*
91
* == Only used if TIOCGWINSZ fails ==
92
* DSR/CPR (Report cursor position)
93
* Sequence: ESC [ 6 n
94
* Effect: reports current cursor position as ESC [ NNN ; MMM R
95
*
96
* == Only used in multiline mode ==
97
* CUU (Cursor Up)
98
* Sequence: ESC [ n A
99
* Effect: moves cursor up n chars.
100
*
101
* CUD (Cursor Down)
102
* Sequence: ESC [ n B
103
* Effect: moves cursor down n chars.
104
*
105
* win32/console
106
* -------------
107
* If __MINGW32__ is defined, the win32 console API is used.
108
* This could probably be made to work for the msvc compiler too.
109
* This support based in part on work by Jon Griffiths.
110
*/
111
112
#ifdef _WIN32 /* Windows platform, either MinGW or Visual Studio (MSVC) */
113
#include <windows.h>
114
#include <fcntl.h>
115
#define USE_WINCONSOLE
116
#ifdef __MINGW32__
117
#define HAVE_UNISTD_H
118
#endif
119
#else
120
#include <termios.h>
121
#include <sys/ioctl.h>
122
#include <poll.h>
123
#define USE_TERMIOS
124
#define HAVE_UNISTD_H
125
#endif
126
127
#ifdef HAVE_UNISTD_H
128
#include <unistd.h>
129
#endif
130
#include <stdlib.h>
131
#include <stdarg.h>
132
#include <stdio.h>
133
#include <assert.h>
134
#include <errno.h>
135
#include <string.h>
136
#include <signal.h>
137
#include <stdlib.h>
138
#include <sys/types.h>
139
140
#if defined(_WIN32) && !defined(__MINGW32__)
141
/* Microsoft headers don't like old POSIX names */
142
#define strdup _strdup
143
#define snprintf _snprintf
144
#endif
145
146
#include "linenoise.h"
147
#ifndef STRINGBUF_H
148
#include "stringbuf.h"
149
#endif
150
#ifndef UTF8_UTIL_H
151
#include "utf8.h"
152
#endif
153
154
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
155
156
/* ctrl('A') -> 0x01 */
157
#define ctrl(C) ((C) - '@')
158
/* meta('a') -> 0xe1 */
159
#define meta(C) ((C) | 0x80)
160
161
/* Use -ve numbers here to co-exist with normal unicode chars */
162
enum {
163
SPECIAL_NONE,
164
/* don't use -1 here since that indicates error */
165
SPECIAL_UP = -20,
166
SPECIAL_DOWN = -21,
167
SPECIAL_LEFT = -22,
168
SPECIAL_RIGHT = -23,
169
SPECIAL_DELETE = -24,
170
SPECIAL_HOME = -25,
171
SPECIAL_END = -26,
172
SPECIAL_INSERT = -27,
173
SPECIAL_PAGE_UP = -28,
174
SPECIAL_PAGE_DOWN = -29,
175
176
/* Some handy names for other special keycodes */
177
CHAR_ESCAPE = 27,
178
CHAR_DELETE = 127,
179
};
180
181
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
182
static int history_len = 0;
183
static int history_index = 0;
184
static char **history = NULL;
185
186
/* Structure to contain the status of the current (being edited) line */
187
struct current {
188
stringbuf *buf; /* Current buffer. Always null terminated */
189
int pos; /* Cursor position, measured in chars */
190
int cols; /* Size of the window, in chars */
191
int nrows; /* How many rows are being used in multiline mode (>= 1) */
192
int rpos; /* The current row containing the cursor - multiline mode only */
193
int colsright; /* refreshLine() cached cols for insert_char() optimisation */
194
int colsleft; /* refreshLine() cached cols for remove_char() optimisation */
195
const char *prompt;
196
stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
197
stringbuf *output; /* used only during refreshLine() - output accumulator */
198
#if defined(USE_TERMIOS)
199
int fd; /* Terminal fd */
200
int pending; /* pending char fd_read_char() */
201
#elif defined(USE_WINCONSOLE)
202
HANDLE outh; /* Console output handle */
203
HANDLE inh; /* Console input handle */
204
int rows; /* Screen rows */
205
int x; /* Current column during output */
206
int y; /* Current row */
207
#ifdef USE_UTF8
208
#define UBUF_MAX_CHARS 132
209
WORD ubuf[UBUF_MAX_CHARS + 1]; /* Accumulates utf16 output - one extra for final surrogate pairs */
210
int ubuflen; /* length used in ubuf */
211
int ubufcols; /* how many columns are represented by the chars in ubuf? */
212
#endif
213
#endif
214
};
215
216
static int fd_read(struct current *current);
217
static int getWindowSize(struct current *current);
218
static void cursorDown(struct current *current, int n);
219
static void cursorUp(struct current *current, int n);
220
static void eraseEol(struct current *current);
221
static void refreshLine(struct current *current);
222
static void refreshLineAlt(struct current *current, const char *prompt, const char *buf, int cursor_pos);
223
static void setCursorPos(struct current *current, int x);
224
static void setOutputHighlight(struct current *current, const int *props, int nprops);
225
static void set_current(struct current *current, const char *str);
226
227
static int fd_isatty(struct current *current)
228
{
229
#ifdef USE_TERMIOS
230
return isatty(current->fd);
231
#else
232
(void)current;
233
return 0;
234
#endif
235
}
236
237
void linenoiseHistoryFree(void) {
238
if (history) {
239
int j;
240
241
for (j = 0; j < history_len; j++)
242
free(history[j]);
243
free(history);
244
history = NULL;
245
history_len = 0;
246
}
247
}
248
249
typedef enum {
250
EP_START, /* looking for ESC */
251
EP_ESC, /* looking for [ */
252
EP_DIGITS, /* parsing digits */
253
EP_PROPS, /* parsing digits or semicolons */
254
EP_END, /* ok */
255
EP_ERROR, /* error */
256
} ep_state_t;
257
258
struct esc_parser {
259
ep_state_t state;
260
int props[5]; /* properties are stored here */
261
int maxprops; /* size of the props[] array */
262
int numprops; /* number of properties found */
263
int termchar; /* terminator char, or 0 for any alpha */
264
int current; /* current (partial) property value */
265
};
266
267
/**
268
* Initialise the escape sequence parser at *parser.
269
*
270
* If termchar is 0 any alpha char terminates ok. Otherwise only the given
271
* char terminates successfully.
272
* Run the parser state machine with calls to parseEscapeSequence() for each char.
273
*/
274
static void initParseEscapeSeq(struct esc_parser *parser, int termchar)
275
{
276
parser->state = EP_START;
277
parser->maxprops = sizeof(parser->props) / sizeof(*parser->props);
278
parser->numprops = 0;
279
parser->current = 0;
280
parser->termchar = termchar;
281
}
282
283
/**
284
* Pass character 'ch' into the state machine to parse:
285
* 'ESC' '[' <digits> (';' <digits>)* <termchar>
286
*
287
* The first character must be ESC.
288
* Returns the current state. The state machine is done when it returns either EP_END
289
* or EP_ERROR.
290
*
291
* On EP_END, the "property/attribute" values can be read from parser->props[]
292
* of length parser->numprops.
293
*/
294
static int parseEscapeSequence(struct esc_parser *parser, int ch)
295
{
296
switch (parser->state) {
297
case EP_START:
298
parser->state = (ch == '\x1b') ? EP_ESC : EP_ERROR;
299
break;
300
case EP_ESC:
301
parser->state = (ch == '[') ? EP_DIGITS : EP_ERROR;
302
break;
303
case EP_PROPS:
304
if (ch == ';') {
305
parser->state = EP_DIGITS;
306
donedigits:
307
if (parser->numprops + 1 < parser->maxprops) {
308
parser->props[parser->numprops++] = parser->current;
309
parser->current = 0;
310
}
311
break;
312
}
313
/* fall through */
314
case EP_DIGITS:
315
if (ch >= '0' && ch <= '9') {
316
parser->current = parser->current * 10 + (ch - '0');
317
parser->state = EP_PROPS;
318
break;
319
}
320
/* must be terminator */
321
if (parser->termchar != ch) {
322
if (parser->termchar != 0 || !((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))) {
323
parser->state = EP_ERROR;
324
break;
325
}
326
}
327
parser->state = EP_END;
328
goto donedigits;
329
case EP_END:
330
parser->state = EP_ERROR;
331
break;
332
case EP_ERROR:
333
break;
334
}
335
return parser->state;
336
}
337
338
/*#define DEBUG_REFRESHLINE*/
339
340
#ifdef DEBUG_REFRESHLINE
341
#define DRL(ARGS...) fprintf(dfh, ARGS)
342
static FILE *dfh;
343
344
static void DRL_CHAR(int ch)
345
{
346
if (ch < ' ') {
347
DRL("^%c", ch + '@');
348
}
349
else if (ch > 127) {
350
DRL("\\u%04x", ch);
351
}
352
else {
353
DRL("%c", ch);
354
}
355
}
356
static void DRL_STR(const char *str)
357
{
358
while (*str) {
359
int ch;
360
int n = utf8_tounicode(str, &ch);
361
str += n;
362
DRL_CHAR(ch);
363
}
364
}
365
#else
366
#define DRL(...)
367
#define DRL_CHAR(ch)
368
#define DRL_STR(str)
369
#endif
370
371
#if defined(USE_WINCONSOLE)
372
#include "linenoise-win32.c"
373
#endif
374
375
#if defined(USE_TERMIOS)
376
static void linenoiseAtExit(void);
377
static struct termios orig_termios; /* in order to restore at exit */
378
static int rawmode = 0; /* for atexit() function to check if restore is needed*/
379
static int atexit_registered = 0; /* register atexit just 1 time */
380
381
static const char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
382
383
static int isUnsupportedTerm(void) {
384
char *term = getenv("TERM");
385
386
if (term) {
387
int j;
388
for (j = 0; unsupported_term[j]; j++) {
389
if (strcmp(term, unsupported_term[j]) == 0) {
390
return 1;
391
}
392
}
393
}
394
return 0;
395
}
396
397
static int enableRawMode(struct current *current) {
398
struct termios raw;
399
400
current->fd = STDIN_FILENO;
401
current->cols = 0;
402
403
if (!isatty(current->fd) || isUnsupportedTerm() ||
404
tcgetattr(current->fd, &orig_termios) == -1) {
405
fatal:
406
errno = ENOTTY;
407
return -1;
408
}
409
410
if (!atexit_registered) {
411
atexit(linenoiseAtExit);
412
atexit_registered = 1;
413
}
414
415
raw = orig_termios; /* modify the original mode */
416
/* input modes: no break, no CR to NL, no parity check, no strip char,
417
* no start/stop output control. */
418
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
419
/* output modes - actually, no need to disable post processing */
420
/*raw.c_oflag &= ~(OPOST);*/
421
/* control modes - set 8 bit chars */
422
raw.c_cflag |= (CS8);
423
/* local modes - choing off, canonical off, no extended functions,
424
* no signal chars (^Z,^C) */
425
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
426
/* control chars - set return condition: min number of bytes and timer.
427
* We want read to return every single byte, without timeout. */
428
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
429
430
/* put terminal in raw mode. Because we aren't changing any output
431
* settings we don't need to use TCSADRAIN and I have seen that hang on
432
* OpenBSD when running under a pty
433
*/
434
if (tcsetattr(current->fd,TCSANOW,&raw) < 0) {
435
goto fatal;
436
}
437
rawmode = 1;
438
return 0;
439
}
440
441
static void disableRawMode(struct current *current) {
442
/* Don't even check the return value as it's too late. */
443
if (rawmode && tcsetattr(current->fd,TCSANOW,&orig_termios) != -1)
444
rawmode = 0;
445
}
446
447
/* At exit we'll try to fix the terminal to the initial conditions. */
448
static void linenoiseAtExit(void) {
449
if (rawmode) {
450
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
451
}
452
linenoiseHistoryFree();
453
}
454
455
/* gcc/glibc insists that we care about the return code of write!
456
* Clarification: This means that a void-cast like "(void) (EXPR)"
457
* does not work.
458
*/
459
#define IGNORE_RC(EXPR) if (EXPR) {}
460
461
/**
462
* Output bytes directly, or accumulate output (if current->output is set)
463
*/
464
static void outputChars(struct current *current, const char *buf, int len)
465
{
466
if (len < 0) {
467
len = strlen(buf);
468
}
469
if (current->output) {
470
sb_append_len(current->output, buf, len);
471
}
472
else {
473
IGNORE_RC(write(current->fd, buf, len));
474
}
475
}
476
477
/* Like outputChars, but using printf-style formatting
478
*/
479
static void outputFormatted(struct current *current, const char *format, ...)
480
{
481
va_list args;
482
char buf[64];
483
int n;
484
485
va_start(args, format);
486
n = vsnprintf(buf, sizeof(buf), format, args);
487
/* This will never happen because we are sure to use outputFormatted() only for short sequences */
488
assert(n < (int)sizeof(buf));
489
va_end(args);
490
outputChars(current, buf, n);
491
}
492
493
static void cursorToLeft(struct current *current)
494
{
495
outputChars(current, "\r", -1);
496
}
497
498
static void setOutputHighlight(struct current *current, const int *props, int nprops)
499
{
500
outputChars(current, "\x1b[", -1);
501
while (nprops--) {
502
outputFormatted(current, "%d%c", *props, (nprops == 0) ? 'm' : ';');
503
props++;
504
}
505
}
506
507
static void eraseEol(struct current *current)
508
{
509
outputChars(current, "\x1b[0K", -1);
510
}
511
512
static void setCursorPos(struct current *current, int x)
513
{
514
if (x == 0) {
515
cursorToLeft(current);
516
}
517
else {
518
outputFormatted(current, "\r\x1b[%dC", x);
519
}
520
}
521
522
static void cursorUp(struct current *current, int n)
523
{
524
if (n) {
525
outputFormatted(current, "\x1b[%dA", n);
526
}
527
}
528
529
static void cursorDown(struct current *current, int n)
530
{
531
if (n) {
532
outputFormatted(current, "\x1b[%dB", n);
533
}
534
}
535
536
void linenoiseClearScreen(void)
537
{
538
IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
539
}
540
541
/**
542
* Reads a char from 'current->fd', waiting at most 'timeout' milliseconds.
543
*
544
* A timeout of -1 means to wait forever.
545
*
546
* Returns -1 if no char is received within the time or an error occurs.
547
*/
548
static int fd_read_char(struct current *current, int timeout)
549
{
550
struct pollfd p;
551
unsigned char c;
552
553
if (current->pending) {
554
c = current->pending;
555
current->pending = 0;
556
return c;
557
}
558
559
p.fd = current->fd;
560
p.events = POLLIN;
561
562
if (poll(&p, 1, timeout) == 0) {
563
/* timeout */
564
return -1;
565
}
566
if (read(current->fd, &c, 1) != 1) {
567
return -1;
568
}
569
return c;
570
}
571
572
/**
573
* Reads a complete utf-8 character
574
* and returns the unicode value, or -1 on error.
575
*/
576
static int fd_read(struct current *current)
577
{
578
#ifdef USE_UTF8
579
char buf[MAX_UTF8_LEN];
580
int n;
581
int i;
582
int c;
583
584
if (current->pending) {
585
buf[0] = current->pending;
586
current->pending = 0;
587
}
588
else if (read(current->fd, &buf[0], 1) != 1) {
589
return -1;
590
}
591
n = utf8_charlen(buf[0]);
592
if (n < 1) {
593
return -1;
594
}
595
for (i = 1; i < n; i++) {
596
if (read(current->fd, &buf[i], 1) != 1) {
597
return -1;
598
}
599
}
600
/* decode and return the character */
601
utf8_tounicode(buf, &c);
602
return c;
603
#else
604
return fd_read_char(current, -1);
605
#endif
606
}
607
608
609
/**
610
* Stores the current cursor column in '*cols'.
611
* Returns 1 if OK, or 0 if failed to determine cursor pos.
612
*/
613
static int queryCursor(struct current *current, int* cols)
614
{
615
struct esc_parser parser;
616
int ch;
617
/* Unfortunately we don't have any persistent state, so assume
618
* a process will only ever interact with one terminal at a time.
619
*/
620
static int query_cursor_failed;
621
622
if (query_cursor_failed) {
623
/* If it ever fails, don't try again */
624
return 0;
625
}
626
627
/* Should not be buffering this output, it needs to go immediately */
628
assert(current->output == NULL);
629
630
/* control sequence - report cursor location */
631
outputChars(current, "\x1b[6n", -1);
632
633
/* Parse the response: ESC [ rows ; cols R */
634
initParseEscapeSeq(&parser, 'R');
635
while ((ch = fd_read_char(current, 100)) > 0) {
636
switch (parseEscapeSequence(&parser, ch)) {
637
default:
638
continue;
639
case EP_END:
640
if (parser.numprops == 2 && parser.props[1] < 1000) {
641
*cols = parser.props[1];
642
return 1;
643
}
644
break;
645
case EP_ERROR:
646
/* Push back the character that caused the error */
647
current->pending = ch;
648
break;
649
}
650
/* failed */
651
break;
652
}
653
query_cursor_failed = 1;
654
return 0;
655
}
656
657
/**
658
* Updates current->cols with the current window size (width)
659
*/
660
static int getWindowSize(struct current *current)
661
{
662
struct winsize ws;
663
664
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0) {
665
current->cols = ws.ws_col;
666
return 0;
667
}
668
669
/* Failed to query the window size. Perhaps we are on a serial terminal.
670
* Try to query the width by sending the cursor as far to the right
671
* and reading back the cursor position.
672
* Note that this is only done once per call to linenoise rather than
673
* every time the line is refreshed for efficiency reasons.
674
*
675
* In more detail, we:
676
* (a) request current cursor position,
677
* (b) move cursor far right,
678
* (c) request cursor position again,
679
* (d) at last move back to the old position.
680
* This gives us the width without messing with the externally
681
* visible cursor position.
682
*/
683
684
if (current->cols == 0) {
685
int here;
686
687
/* If anything fails => default 80 */
688
current->cols = 80;
689
690
/* (a) */
691
if (queryCursor (current, &here)) {
692
/* (b) */
693
setCursorPos(current, 999);
694
695
/* (c). Note: If (a) succeeded, then (c) should as well.
696
* For paranoia we still check and have a fallback action
697
* for (d) in case of failure..
698
*/
699
if (queryCursor (current, &current->cols)) {
700
/* (d) Reset the cursor back to the original location. */
701
if (current->cols > here) {
702
setCursorPos(current, here);
703
}
704
}
705
}
706
}
707
708
return 0;
709
}
710
711
/**
712
* If CHAR_ESCAPE was received, reads subsequent
713
* chars to determine if this is a known special key.
714
*
715
* Returns SPECIAL_NONE if unrecognised, or -1 if EOF.
716
*
717
* If no additional char is received within a short time,
718
* CHAR_ESCAPE is returned.
719
*/
720
static int check_special(struct current *current)
721
{
722
int c = fd_read_char(current, 50);
723
int c2;
724
725
if (c < 0) {
726
return CHAR_ESCAPE;
727
}
728
else if (c >= 'a' && c <= 'z') {
729
/* esc-a => meta-a */
730
return meta(c);
731
}
732
733
c2 = fd_read_char(current, 50);
734
if (c2 < 0) {
735
return c2;
736
}
737
if (c == '[' || c == 'O') {
738
/* Potential arrow key */
739
switch (c2) {
740
case 'A':
741
return SPECIAL_UP;
742
case 'B':
743
return SPECIAL_DOWN;
744
case 'C':
745
return SPECIAL_RIGHT;
746
case 'D':
747
return SPECIAL_LEFT;
748
case 'F':
749
return SPECIAL_END;
750
case 'H':
751
return SPECIAL_HOME;
752
}
753
}
754
if (c == '[' && c2 >= '1' && c2 <= '8') {
755
/* extended escape */
756
c = fd_read_char(current, 50);
757
if (c == '~') {
758
switch (c2) {
759
case '2':
760
return SPECIAL_INSERT;
761
case '3':
762
return SPECIAL_DELETE;
763
case '5':
764
return SPECIAL_PAGE_UP;
765
case '6':
766
return SPECIAL_PAGE_DOWN;
767
case '7':
768
return SPECIAL_HOME;
769
case '8':
770
return SPECIAL_END;
771
}
772
}
773
while (c != -1 && c != '~') {
774
/* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */
775
c = fd_read_char(current, 50);
776
}
777
}
778
779
return SPECIAL_NONE;
780
}
781
#endif
782
783
static void clearOutputHighlight(struct current *current)
784
{
785
int nohighlight = 0;
786
setOutputHighlight(current, &nohighlight, 1);
787
}
788
789
static void outputControlChar(struct current *current, char ch)
790
{
791
int reverse = 7;
792
setOutputHighlight(current, &reverse, 1);
793
outputChars(current, "^", 1);
794
outputChars(current, &ch, 1);
795
clearOutputHighlight(current);
796
}
797
798
#ifndef utf8_getchars
799
static int utf8_getchars(char *buf, int c)
800
{
801
#ifdef USE_UTF8
802
return utf8_fromunicode(buf, c);
803
#else
804
*buf = c;
805
return 1;
806
#endif
807
}
808
#endif
809
810
/**
811
* Returns the unicode character at the given offset,
812
* or -1 if none.
813
*/
814
static int get_char(struct current *current, int pos)
815
{
816
if (pos >= 0 && pos < sb_chars(current->buf)) {
817
int c;
818
int i = utf8_index(sb_str(current->buf), pos);
819
(void)utf8_tounicode(sb_str(current->buf) + i, &c);
820
return c;
821
}
822
return -1;
823
}
824
825
static int char_display_width(int ch)
826
{
827
if (ch < ' ') {
828
/* control chars take two positions */
829
return 2;
830
}
831
else {
832
return utf8_width(ch);
833
}
834
}
835
836
#ifndef NO_COMPLETION
837
static linenoiseCompletionCallback *completionCallback = NULL;
838
static void *completionUserdata = NULL;
839
static int showhints = 1;
840
static linenoiseHintsCallback *hintsCallback = NULL;
841
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
842
static void *hintsUserdata = NULL;
843
844
static void beep(void) {
845
#ifdef USE_TERMIOS
846
fprintf(stderr, "\x7");
847
fflush(stderr);
848
#endif
849
}
850
851
static void freeCompletions(linenoiseCompletions *lc) {
852
size_t i;
853
for (i = 0; i < lc->len; i++)
854
free(lc->cvec[i]);
855
free(lc->cvec);
856
}
857
858
static int completeLine(struct current *current) {
859
linenoiseCompletions lc = { 0, NULL };
860
int c = 0;
861
862
completionCallback(sb_str(current->buf),&lc,completionUserdata);
863
if (lc.len == 0) {
864
beep();
865
} else {
866
size_t stop = 0, i = 0;
867
868
while(!stop) {
869
/* Show completion or original buffer */
870
if (i < lc.len) {
871
int chars = utf8_strlen(lc.cvec[i], -1);
872
refreshLineAlt(current, current->prompt, lc.cvec[i], chars);
873
} else {
874
refreshLine(current);
875
}
876
877
c = fd_read(current);
878
if (c == -1) {
879
break;
880
}
881
882
switch(c) {
883
case '\t': /* tab */
884
i = (i+1) % (lc.len+1);
885
if (i == lc.len) beep();
886
break;
887
case CHAR_ESCAPE: /* escape */
888
/* Re-show original buffer */
889
if (i < lc.len) {
890
refreshLine(current);
891
}
892
stop = 1;
893
break;
894
default:
895
/* Update buffer and return */
896
if (i < lc.len) {
897
set_current(current,lc.cvec[i]);
898
}
899
stop = 1;
900
break;
901
}
902
}
903
}
904
905
freeCompletions(&lc);
906
return c; /* Return last read character */
907
}
908
909
/* Register a callback function to be called for tab-completion.
910
Returns the prior callback so that the caller may (if needed)
911
restore it when done. */
912
linenoiseCompletionCallback * linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn, void *userdata) {
913
linenoiseCompletionCallback * old = completionCallback;
914
completionCallback = fn;
915
completionUserdata = userdata;
916
return old;
917
}
918
919
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
920
lc->cvec = (char **)realloc(lc->cvec,sizeof(char*)*(lc->len+1));
921
lc->cvec[lc->len++] = strdup(str);
922
}
923
924
void linenoiseSetHintsCallback(linenoiseHintsCallback *callback, void *userdata)
925
{
926
hintsCallback = callback;
927
hintsUserdata = userdata;
928
}
929
930
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *callback)
931
{
932
freeHintsCallback = callback;
933
}
934
935
#endif
936
937
938
static const char *reduceSingleBuf(const char *buf, int availcols, int *cursor_pos)
939
{
940
/* We have availcols columns available.
941
* If necessary, strip chars off the front of buf until *cursor_pos
942
* fits within availcols
943
*/
944
int needcols = 0;
945
int pos = 0;
946
int new_cursor_pos = *cursor_pos;
947
const char *pt = buf;
948
949
DRL("reduceSingleBuf: availcols=%d, cursor_pos=%d\n", availcols, *cursor_pos);
950
951
while (*pt) {
952
int ch;
953
int n = utf8_tounicode(pt, &ch);
954
pt += n;
955
956
needcols += char_display_width(ch);
957
958
/* If we need too many cols, strip
959
* chars off the front of buf to make it fit.
960
* We keep 3 extra cols to the right of the cursor.
961
* 2 for possible wide chars, 1 for the last column that
962
* can't be used.
963
*/
964
while (needcols >= availcols - 3) {
965
n = utf8_tounicode(buf, &ch);
966
buf += n;
967
needcols -= char_display_width(ch);
968
DRL_CHAR(ch);
969
970
/* and adjust the apparent cursor position */
971
new_cursor_pos--;
972
973
if (buf == pt) {
974
/* can't remove more than this */
975
break;
976
}
977
}
978
979
if (pos++ == *cursor_pos) {
980
break;
981
}
982
983
}
984
DRL("<snip>");
985
DRL_STR(buf);
986
DRL("\nafter reduce, needcols=%d, new_cursor_pos=%d\n", needcols, new_cursor_pos);
987
988
/* Done, now new_cursor_pos contains the adjusted cursor position
989
* and buf points to he adjusted start
990
*/
991
*cursor_pos = new_cursor_pos;
992
return buf;
993
}
994
995
static int mlmode = 0;
996
997
void linenoiseSetMultiLine(int enableml)
998
{
999
mlmode = enableml;
1000
}
1001
1002
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
1003
* to the right of the prompt.
1004
* Returns 1 if a hint was shown, or 0 if not
1005
* If 'display' is 0, does no output. Just returns the appropriate return code.
1006
*/
1007
static int refreshShowHints(struct current *current, const char *buf, int availcols, int display)
1008
{
1009
int rc = 0;
1010
if (showhints && hintsCallback && availcols > 0) {
1011
int bold = 0;
1012
int color = -1;
1013
char *hint = hintsCallback(buf, &color, &bold, hintsUserdata);
1014
if (hint) {
1015
rc = 1;
1016
if (display) {
1017
const char *pt;
1018
if (bold == 1 && color == -1) color = 37;
1019
if (bold || color > 0) {
1020
int props[3] = { bold, color, 49 }; /* bold, color, fgnormal */
1021
setOutputHighlight(current, props, 3);
1022
}
1023
DRL("<hint bold=%d,color=%d>", bold, color);
1024
pt = hint;
1025
while (*pt) {
1026
int ch;
1027
int n = utf8_tounicode(pt, &ch);
1028
int width = char_display_width(ch);
1029
1030
if (width >= availcols) {
1031
DRL("<hinteol>");
1032
break;
1033
}
1034
DRL_CHAR(ch);
1035
1036
availcols -= width;
1037
outputChars(current, pt, n);
1038
pt += n;
1039
}
1040
if (bold || color > 0) {
1041
clearOutputHighlight(current);
1042
}
1043
/* Call the function to free the hint returned. */
1044
if (freeHintsCallback) freeHintsCallback(hint, hintsUserdata);
1045
}
1046
}
1047
}
1048
return rc;
1049
}
1050
1051
#ifdef USE_TERMIOS
1052
static void refreshStart(struct current *current)
1053
{
1054
/* We accumulate all output here */
1055
assert(current->output == NULL);
1056
current->output = sb_alloc();
1057
}
1058
1059
static void refreshEnd(struct current *current)
1060
{
1061
/* Output everything at once */
1062
IGNORE_RC(write(current->fd, sb_str(current->output), sb_len(current->output)));
1063
sb_free(current->output);
1064
current->output = NULL;
1065
}
1066
1067
static void refreshStartChars(struct current *current)
1068
{
1069
(void)current;
1070
}
1071
1072
static void refreshNewline(struct current *current)
1073
{
1074
DRL("<nl>");
1075
outputChars(current, "\n", 1);
1076
}
1077
1078
static void refreshEndChars(struct current *current)
1079
{
1080
(void)current;
1081
}
1082
#endif
1083
1084
static void refreshLineAlt(struct current *current, const char *prompt, const char *buf, int cursor_pos)
1085
{
1086
int i;
1087
const char *pt;
1088
int displaycol;
1089
int displayrow;
1090
int visible;
1091
int currentpos;
1092
int notecursor;
1093
int cursorcol = 0;
1094
int cursorrow = 0;
1095
int hint;
1096
struct esc_parser parser;
1097
1098
#ifdef DEBUG_REFRESHLINE
1099
dfh = fopen("linenoise.debuglog", "a");
1100
#endif
1101
1102
/* Should intercept SIGWINCH. For now, just get the size every time */
1103
getWindowSize(current);
1104
1105
refreshStart(current);
1106
1107
DRL("wincols=%d, cursor_pos=%d, nrows=%d, rpos=%d\n", current->cols, cursor_pos, current->nrows, current->rpos);
1108
1109
/* Here is the plan:
1110
* (a) move the the bottom row, going down the appropriate number of lines
1111
* (b) move to beginning of line and erase the current line
1112
* (c) go up one line and do the same, until we have erased up to the first row
1113
* (d) output the prompt, counting cols and rows, taking into account escape sequences
1114
* (e) output the buffer, counting cols and rows
1115
* (e') when we hit the current pos, save the cursor position
1116
* (f) move the cursor to the saved cursor position
1117
* (g) save the current cursor row and number of rows
1118
*/
1119
1120
/* (a) - The cursor is currently at row rpos */
1121
cursorDown(current, current->nrows - current->rpos - 1);
1122
DRL("<cud=%d>", current->nrows - current->rpos - 1);
1123
1124
/* (b), (c) - Erase lines upwards until we get to the first row */
1125
for (i = 0; i < current->nrows; i++) {
1126
if (i) {
1127
DRL("<cup>");
1128
cursorUp(current, 1);
1129
}
1130
DRL("<clearline>");
1131
cursorToLeft(current);
1132
eraseEol(current);
1133
}
1134
DRL("\n");
1135
1136
/* (d) First output the prompt. control sequences don't take up display space */
1137
pt = prompt;
1138
displaycol = 0; /* current display column */
1139
displayrow = 0; /* current display row */
1140
visible = 1;
1141
1142
refreshStartChars(current);
1143
1144
while (*pt) {
1145
int width;
1146
int ch;
1147
int n = utf8_tounicode(pt, &ch);
1148
1149
if (visible && ch == CHAR_ESCAPE) {
1150
/* The start of an escape sequence, so not visible */
1151
visible = 0;
1152
initParseEscapeSeq(&parser, 'm');
1153
DRL("<esc-seq-start>");
1154
}
1155
1156
if (ch == '\n' || ch == '\r') {
1157
/* treat both CR and NL the same and force wrap */
1158
refreshNewline(current);
1159
displaycol = 0;
1160
displayrow++;
1161
}
1162
else {
1163
width = visible * utf8_width(ch);
1164
1165
displaycol += width;
1166
if (displaycol >= current->cols) {
1167
/* need to wrap to the next line because of newline or if it doesn't fit
1168
* XXX this is a problem in single line mode
1169
*/
1170
refreshNewline(current);
1171
displaycol = width;
1172
displayrow++;
1173
}
1174
1175
DRL_CHAR(ch);
1176
#ifdef USE_WINCONSOLE
1177
if (visible) {
1178
outputChars(current, pt, n);
1179
}
1180
#else
1181
outputChars(current, pt, n);
1182
#endif
1183
}
1184
pt += n;
1185
1186
if (!visible) {
1187
switch (parseEscapeSequence(&parser, ch)) {
1188
case EP_END:
1189
visible = 1;
1190
setOutputHighlight(current, parser.props, parser.numprops);
1191
DRL("<esc-seq-end,numprops=%d>", parser.numprops);
1192
break;
1193
case EP_ERROR:
1194
DRL("<esc-seq-err>");
1195
visible = 1;
1196
break;
1197
}
1198
}
1199
}
1200
1201
/* Now we are at the first line with all lines erased */
1202
DRL("\nafter prompt: displaycol=%d, displayrow=%d\n", displaycol, displayrow);
1203
1204
1205
/* (e) output the buffer, counting cols and rows */
1206
if (mlmode == 0) {
1207
/* In this mode we may need to trim chars from the start of the buffer until the
1208
* cursor fits in the window.
1209
*/
1210
pt = reduceSingleBuf(buf, current->cols - displaycol, &cursor_pos);
1211
}
1212
else {
1213
pt = buf;
1214
}
1215
1216
currentpos = 0;
1217
notecursor = -1;
1218
1219
while (*pt) {
1220
int ch;
1221
int n = utf8_tounicode(pt, &ch);
1222
int width = char_display_width(ch);
1223
1224
if (currentpos == cursor_pos) {
1225
/* (e') wherever we output this character is where we want the cursor */
1226
notecursor = 1;
1227
}
1228
1229
if (displaycol + width >= current->cols) {
1230
if (mlmode == 0) {
1231
/* In single line mode stop once we print as much as we can on one line */
1232
DRL("<slmode>");
1233
break;
1234
}
1235
/* need to wrap to the next line since it doesn't fit */
1236
refreshNewline(current);
1237
displaycol = 0;
1238
displayrow++;
1239
}
1240
1241
if (notecursor == 1) {
1242
/* (e') Save this position as the current cursor position */
1243
cursorcol = displaycol;
1244
cursorrow = displayrow;
1245
notecursor = 0;
1246
DRL("<cursor>");
1247
}
1248
1249
displaycol += width;
1250
1251
if (ch < ' ') {
1252
outputControlChar(current, ch + '@');
1253
}
1254
else {
1255
outputChars(current, pt, n);
1256
}
1257
DRL_CHAR(ch);
1258
if (width != 1) {
1259
DRL("<w=%d>", width);
1260
}
1261
1262
pt += n;
1263
currentpos++;
1264
}
1265
1266
/* If we didn't see the cursor, it is at the current location */
1267
if (notecursor) {
1268
DRL("<cursor>");
1269
cursorcol = displaycol;
1270
cursorrow = displayrow;
1271
}
1272
1273
DRL("\nafter buf: displaycol=%d, displayrow=%d, cursorcol=%d, cursorrow=%d\n", displaycol, displayrow, cursorcol, cursorrow);
1274
1275
/* (f) show hints */
1276
hint = refreshShowHints(current, buf, current->cols - displaycol, 1);
1277
1278
/* Remember how many many cols are available for insert optimisation */
1279
if (prompt == current->prompt && hint == 0) {
1280
current->colsright = current->cols - displaycol;
1281
current->colsleft = displaycol;
1282
}
1283
else {
1284
/* Can't optimise */
1285
current->colsright = 0;
1286
current->colsleft = 0;
1287
}
1288
DRL("\nafter hints: colsleft=%d, colsright=%d\n\n", current->colsleft, current->colsright);
1289
1290
refreshEndChars(current);
1291
1292
/* (g) move the cursor to the correct place */
1293
cursorUp(current, displayrow - cursorrow);
1294
setCursorPos(current, cursorcol);
1295
1296
/* (h) Update the number of rows if larger, but never reduce this */
1297
if (displayrow >= current->nrows) {
1298
current->nrows = displayrow + 1;
1299
}
1300
/* And remember the row that the cursor is on */
1301
current->rpos = cursorrow;
1302
1303
refreshEnd(current);
1304
1305
#ifdef DEBUG_REFRESHLINE
1306
fclose(dfh);
1307
#endif
1308
}
1309
1310
static void refreshLine(struct current *current)
1311
{
1312
refreshLineAlt(current, current->prompt, sb_str(current->buf), current->pos);
1313
}
1314
1315
static void set_current(struct current *current, const char *str)
1316
{
1317
sb_clear(current->buf);
1318
sb_append(current->buf, str);
1319
current->pos = sb_chars(current->buf);
1320
}
1321
1322
/**
1323
* Removes the char at 'pos'.
1324
*
1325
* Returns 1 if the line needs to be refreshed, 2 if not
1326
* and 0 if nothing was removed
1327
*/
1328
static int remove_char(struct current *current, int pos)
1329
{
1330
if (pos >= 0 && pos < sb_chars(current->buf)) {
1331
int offset = utf8_index(sb_str(current->buf), pos);
1332
int nbytes = utf8_index(sb_str(current->buf) + offset, 1);
1333
int rc = 1;
1334
1335
/* Now we try to optimise in the simple but very common case that:
1336
* - outputChars() can be used directly (not win32)
1337
* - we are removing the char at EOL
1338
* - the buffer is not empty
1339
* - there are columns available to the left
1340
* - the char being deleted is not a wide or utf-8 character
1341
* - no hints are being shown
1342
*/
1343
if (current->output && current->pos == pos + 1 && current->pos == sb_chars(current->buf) && pos > 0) {
1344
#ifdef USE_UTF8
1345
/* Could implement utf8_prev_len() but simplest just to not optimise this case */
1346
char last = sb_str(current->buf)[offset];
1347
#else
1348
char last = 0;
1349
#endif
1350
if (current->colsleft > 0 && (last & 0x80) == 0) {
1351
/* Have cols on the left and not a UTF-8 char or continuation */
1352
/* Yes, can optimise */
1353
current->colsleft--;
1354
current->colsright++;
1355
rc = 2;
1356
}
1357
}
1358
1359
sb_delete(current->buf, offset, nbytes);
1360
1361
if (current->pos > pos) {
1362
current->pos--;
1363
}
1364
if (rc == 2) {
1365
if (refreshShowHints(current, sb_str(current->buf), current->colsright, 0)) {
1366
/* A hint needs to be shown, so can't optimise after all */
1367
rc = 1;
1368
}
1369
else {
1370
/* optimised output */
1371
outputChars(current, "\b \b", 3);
1372
}
1373
}
1374
return rc;
1375
return 1;
1376
}
1377
return 0;
1378
}
1379
1380
/**
1381
* Insert 'ch' at position 'pos'
1382
*
1383
* Returns 1 if the line needs to be refreshed, 2 if not
1384
* and 0 if nothing was inserted (no room)
1385
*/
1386
static int insert_char(struct current *current, int pos, int ch)
1387
{
1388
if (pos >= 0 && pos <= sb_chars(current->buf)) {
1389
char buf[MAX_UTF8_LEN + 1];
1390
int offset = utf8_index(sb_str(current->buf), pos);
1391
int n = utf8_getchars(buf, ch);
1392
int rc = 1;
1393
1394
/* null terminate since sb_insert() requires it */
1395
buf[n] = 0;
1396
1397
/* Now we try to optimise in the simple but very common case that:
1398
* - outputChars() can be used directly (not win32)
1399
* - we are inserting at EOL
1400
* - there are enough columns available
1401
* - no hints are being shown
1402
*/
1403
if (current->output && pos == current->pos && pos == sb_chars(current->buf)) {
1404
int width = char_display_width(ch);
1405
if (current->colsright > width) {
1406
/* Yes, can optimise */
1407
current->colsright -= width;
1408
current->colsleft -= width;
1409
rc = 2;
1410
}
1411
}
1412
sb_insert(current->buf, offset, buf);
1413
if (current->pos >= pos) {
1414
current->pos++;
1415
}
1416
if (rc == 2) {
1417
if (refreshShowHints(current, sb_str(current->buf), current->colsright, 0)) {
1418
/* A hint needs to be shown, so can't optimise after all */
1419
rc = 1;
1420
}
1421
else {
1422
/* optimised output */
1423
outputChars(current, buf, n);
1424
}
1425
}
1426
return rc;
1427
}
1428
return 0;
1429
}
1430
1431
/**
1432
* Captures up to 'n' characters starting at 'pos' for the cut buffer.
1433
*
1434
* This replaces any existing characters in the cut buffer.
1435
*/
1436
static void capture_chars(struct current *current, int pos, int nchars)
1437
{
1438
if (pos >= 0 && (pos + nchars - 1) < sb_chars(current->buf)) {
1439
int offset = utf8_index(sb_str(current->buf), pos);
1440
int nbytes = utf8_index(sb_str(current->buf) + offset, nchars);
1441
1442
if (nbytes > 0) {
1443
if (current->capture) {
1444
sb_clear(current->capture);
1445
}
1446
else {
1447
current->capture = sb_alloc();
1448
}
1449
sb_append_len(current->capture, sb_str(current->buf) + offset, nbytes);
1450
}
1451
}
1452
}
1453
1454
/**
1455
* Removes up to 'n' characters at cursor position 'pos'.
1456
*
1457
* Returns 0 if no chars were removed or non-zero otherwise.
1458
*/
1459
static int remove_chars(struct current *current, int pos, int n)
1460
{
1461
int removed = 0;
1462
1463
/* First save any chars which will be removed */
1464
capture_chars(current, pos, n);
1465
1466
while (n-- && remove_char(current, pos)) {
1467
removed++;
1468
}
1469
return removed;
1470
}
1471
/**
1472
* Inserts the characters (string) 'chars' at the cursor position 'pos'.
1473
*
1474
* Returns 0 if no chars were inserted or non-zero otherwise.
1475
*/
1476
static int insert_chars(struct current *current, int pos, const char *chars)
1477
{
1478
int inserted = 0;
1479
1480
while (*chars) {
1481
int ch;
1482
int n = utf8_tounicode(chars, &ch);
1483
if (insert_char(current, pos, ch) == 0) {
1484
break;
1485
}
1486
inserted++;
1487
pos++;
1488
chars += n;
1489
}
1490
return inserted;
1491
}
1492
1493
static int skip_space_nonspace(struct current *current, int dir, int check_is_space)
1494
{
1495
int moved = 0;
1496
int checkoffset = (dir < 0) ? -1 : 0;
1497
int limit = (dir < 0) ? 0 : sb_chars(current->buf);
1498
while (current->pos != limit && (get_char(current, current->pos + checkoffset) == ' ') == check_is_space) {
1499
current->pos += dir;
1500
moved++;
1501
}
1502
return moved;
1503
}
1504
1505
static int skip_space(struct current *current, int dir)
1506
{
1507
return skip_space_nonspace(current, dir, 1);
1508
}
1509
1510
static int skip_nonspace(struct current *current, int dir)
1511
{
1512
return skip_space_nonspace(current, dir, 0);
1513
}
1514
1515
static void set_history_index(struct current *current, int new_index)
1516
{
1517
if (history_len > 1) {
1518
/* Update the current history entry before to
1519
* overwrite it with the next one. */
1520
free(history[history_len - 1 - history_index]);
1521
history[history_len - 1 - history_index] = strdup(sb_str(current->buf));
1522
/* Show the new entry */
1523
history_index = new_index;
1524
if (history_index < 0) {
1525
history_index = 0;
1526
} else if (history_index >= history_len) {
1527
history_index = history_len - 1;
1528
} else {
1529
set_current(current, history[history_len - 1 - history_index]);
1530
refreshLine(current);
1531
}
1532
}
1533
}
1534
1535
/**
1536
* Returns the keycode to process, or 0 if none.
1537
*/
1538
static int reverseIncrementalSearch(struct current *current)
1539
{
1540
/* Display the reverse-i-search prompt and process chars */
1541
char rbuf[50];
1542
char rprompt[80];
1543
int rchars = 0;
1544
int rlen = 0;
1545
int searchpos = history_len - 1;
1546
int c;
1547
1548
rbuf[0] = 0;
1549
while (1) {
1550
int n = 0;
1551
const char *p = NULL;
1552
int skipsame = 0;
1553
int searchdir = -1;
1554
1555
snprintf(rprompt, sizeof(rprompt), "(reverse-i-search)'%s': ", rbuf);
1556
refreshLineAlt(current, rprompt, sb_str(current->buf), current->pos);
1557
c = fd_read(current);
1558
if (c == ctrl('H') || c == CHAR_DELETE) {
1559
if (rchars) {
1560
int p_ind = utf8_index(rbuf, --rchars);
1561
rbuf[p_ind] = 0;
1562
rlen = strlen(rbuf);
1563
}
1564
continue;
1565
}
1566
#ifdef USE_TERMIOS
1567
if (c == CHAR_ESCAPE) {
1568
c = check_special(current);
1569
}
1570
#endif
1571
if (c == ctrl('R')) {
1572
/* Search for the previous (earlier) match */
1573
if (searchpos > 0) {
1574
searchpos--;
1575
}
1576
skipsame = 1;
1577
}
1578
else if (c == ctrl('S')) {
1579
/* Search for the next (later) match */
1580
if (searchpos < history_len) {
1581
searchpos++;
1582
}
1583
searchdir = 1;
1584
skipsame = 1;
1585
}
1586
else if (c == ctrl('P') || c == SPECIAL_UP) {
1587
/* Exit Ctrl-R mode and go to the previous history line from the current search pos */
1588
set_history_index(current, history_len - searchpos);
1589
c = 0;
1590
break;
1591
}
1592
else if (c == ctrl('N') || c == SPECIAL_DOWN) {
1593
/* Exit Ctrl-R mode and go to the next history line from the current search pos */
1594
set_history_index(current, history_len - searchpos - 2);
1595
c = 0;
1596
break;
1597
}
1598
else if (c >= ' ' && c <= '~') {
1599
/* >= here to allow for null terminator */
1600
if (rlen >= (int)sizeof(rbuf) - MAX_UTF8_LEN) {
1601
continue;
1602
}
1603
1604
n = utf8_getchars(rbuf + rlen, c);
1605
rlen += n;
1606
rchars++;
1607
rbuf[rlen] = 0;
1608
1609
/* Adding a new char resets the search location */
1610
searchpos = history_len - 1;
1611
}
1612
else {
1613
/* Exit from incremental search mode */
1614
break;
1615
}
1616
1617
/* Now search through the history for a match */
1618
for (; searchpos >= 0 && searchpos < history_len; searchpos += searchdir) {
1619
p = strstr(history[searchpos], rbuf);
1620
if (p) {
1621
/* Found a match */
1622
if (skipsame && strcmp(history[searchpos], sb_str(current->buf)) == 0) {
1623
/* But it is identical, so skip it */
1624
continue;
1625
}
1626
/* Copy the matching line and set the cursor position */
1627
history_index = history_len - 1 - searchpos;
1628
set_current(current,history[searchpos]);
1629
current->pos = utf8_strlen(history[searchpos], p - history[searchpos]);
1630
break;
1631
}
1632
}
1633
if (!p && n) {
1634
/* No match, so don't add it */
1635
rchars--;
1636
rlen -= n;
1637
rbuf[rlen] = 0;
1638
}
1639
}
1640
if (c == ctrl('G') || c == ctrl('C')) {
1641
/* ctrl-g terminates the search with no effect */
1642
set_current(current, "");
1643
history_index = 0;
1644
c = 0;
1645
}
1646
else if (c == ctrl('J')) {
1647
/* ctrl-j terminates the search leaving the buffer in place */
1648
history_index = 0;
1649
c = 0;
1650
}
1651
/* Go process the char normally */
1652
refreshLine(current);
1653
return c;
1654
}
1655
1656
static int linenoiseEdit(struct current *current) {
1657
history_index = 0;
1658
1659
refreshLine(current);
1660
1661
while(1) {
1662
int c = fd_read(current);
1663
1664
#ifndef NO_COMPLETION
1665
/* Only autocomplete when the callback is set. It returns < 0 when
1666
* there was an error reading from fd. Otherwise it will return the
1667
* character that should be handled next. */
1668
if (c == '\t' && current->pos == sb_chars(current->buf) && completionCallback != NULL) {
1669
c = completeLine(current);
1670
}
1671
#endif
1672
if (c == ctrl('R')) {
1673
/* reverse incremental search will provide an alternative keycode or 0 for none */
1674
c = reverseIncrementalSearch(current);
1675
/* go on to process the returned char normally */
1676
}
1677
1678
#ifdef USE_TERMIOS
1679
if (c == CHAR_ESCAPE) { /* escape sequence */
1680
c = check_special(current);
1681
}
1682
#endif
1683
if (c == -1) {
1684
/* Return on errors */
1685
return sb_len(current->buf);
1686
}
1687
1688
switch(c) {
1689
case SPECIAL_NONE:
1690
break;
1691
case '\r': /* enter/CR */
1692
case '\n': /* LF */
1693
history_len--;
1694
free(history[history_len]);
1695
current->pos = sb_chars(current->buf);
1696
if (mlmode || hintsCallback) {
1697
showhints = 0;
1698
refreshLine(current);
1699
showhints = 1;
1700
}
1701
return sb_len(current->buf);
1702
case ctrl('C'): /* ctrl-c */
1703
errno = EAGAIN;
1704
return -1;
1705
case ctrl('Z'): /* ctrl-z */
1706
#ifdef SIGTSTP
1707
/* send ourselves SIGSUSP */
1708
disableRawMode(current);
1709
raise(SIGTSTP);
1710
/* and resume */
1711
enableRawMode(current);
1712
refreshLine(current);
1713
#endif
1714
continue;
1715
case CHAR_DELETE: /* backspace */
1716
case ctrl('H'):
1717
if (remove_char(current, current->pos - 1) == 1) {
1718
refreshLine(current);
1719
}
1720
break;
1721
case ctrl('D'): /* ctrl-d */
1722
if (sb_len(current->buf) == 0) {
1723
/* Empty line, so EOF */
1724
history_len--;
1725
free(history[history_len]);
1726
return -1;
1727
}
1728
/* Otherwise fall through to delete char to right of cursor */
1729
/* fall-thru */
1730
case SPECIAL_DELETE:
1731
if (remove_char(current, current->pos) == 1) {
1732
refreshLine(current);
1733
}
1734
break;
1735
case SPECIAL_INSERT:
1736
/* Ignore. Expansion Hook.
1737
* Future possibility: Toggle Insert/Overwrite Modes
1738
*/
1739
break;
1740
case meta('b'): /* meta-b, move word left */
1741
if (skip_nonspace(current, -1)) {
1742
refreshLine(current);
1743
}
1744
else if (skip_space(current, -1)) {
1745
skip_nonspace(current, -1);
1746
refreshLine(current);
1747
}
1748
break;
1749
case meta('f'): /* meta-f, move word right */
1750
if (skip_space(current, 1)) {
1751
refreshLine(current);
1752
}
1753
else if (skip_nonspace(current, 1)) {
1754
skip_space(current, 1);
1755
refreshLine(current);
1756
}
1757
break;
1758
case ctrl('W'): /* ctrl-w, delete word at left. save deleted chars */
1759
/* eat any spaces on the left */
1760
{
1761
int pos = current->pos;
1762
while (pos > 0 && get_char(current, pos - 1) == ' ') {
1763
pos--;
1764
}
1765
1766
/* now eat any non-spaces on the left */
1767
while (pos > 0 && get_char(current, pos - 1) != ' ') {
1768
pos--;
1769
}
1770
1771
if (remove_chars(current, pos, current->pos - pos)) {
1772
refreshLine(current);
1773
}
1774
}
1775
break;
1776
case ctrl('T'): /* ctrl-t */
1777
if (current->pos > 0 && current->pos <= sb_chars(current->buf)) {
1778
/* If cursor is at end, transpose the previous two chars */
1779
int fixer = (current->pos == sb_chars(current->buf));
1780
c = get_char(current, current->pos - fixer);
1781
remove_char(current, current->pos - fixer);
1782
insert_char(current, current->pos - 1, c);
1783
refreshLine(current);
1784
}
1785
break;
1786
case ctrl('V'): /* ctrl-v */
1787
/* Insert the ^V first */
1788
if (insert_char(current, current->pos, c)) {
1789
refreshLine(current);
1790
/* Now wait for the next char. Can insert anything except \0 */
1791
c = fd_read(current);
1792
1793
/* Remove the ^V first */
1794
remove_char(current, current->pos - 1);
1795
if (c > 0) {
1796
/* Insert the actual char, can't be error or null */
1797
insert_char(current, current->pos, c);
1798
}
1799
refreshLine(current);
1800
}
1801
break;
1802
case ctrl('B'):
1803
case SPECIAL_LEFT:
1804
if (current->pos > 0) {
1805
current->pos--;
1806
refreshLine(current);
1807
}
1808
break;
1809
case ctrl('F'):
1810
case SPECIAL_RIGHT:
1811
if (current->pos < sb_chars(current->buf)) {
1812
current->pos++;
1813
refreshLine(current);
1814
}
1815
break;
1816
case SPECIAL_PAGE_UP: /* move to start of history */
1817
set_history_index(current, history_len - 1);
1818
break;
1819
case SPECIAL_PAGE_DOWN: /* move to 0 == end of history, i.e. current */
1820
set_history_index(current, 0);
1821
break;
1822
case ctrl('P'):
1823
case SPECIAL_UP:
1824
set_history_index(current, history_index + 1);
1825
break;
1826
case ctrl('N'):
1827
case SPECIAL_DOWN:
1828
set_history_index(current, history_index - 1);
1829
break;
1830
case ctrl('A'): /* Ctrl+a, go to the start of the line */
1831
case SPECIAL_HOME:
1832
current->pos = 0;
1833
refreshLine(current);
1834
break;
1835
case ctrl('E'): /* ctrl+e, go to the end of the line */
1836
case SPECIAL_END:
1837
current->pos = sb_chars(current->buf);
1838
refreshLine(current);
1839
break;
1840
case ctrl('U'): /* Ctrl+u, delete to beginning of line, save deleted chars. */
1841
if (remove_chars(current, 0, current->pos)) {
1842
refreshLine(current);
1843
}
1844
break;
1845
case ctrl('K'): /* Ctrl+k, delete from current to end of line, save deleted chars. */
1846
if (remove_chars(current, current->pos, sb_chars(current->buf) - current->pos)) {
1847
refreshLine(current);
1848
}
1849
break;
1850
case ctrl('Y'): /* Ctrl+y, insert saved chars at current position */
1851
if (current->capture && insert_chars(current, current->pos, sb_str(current->capture))) {
1852
refreshLine(current);
1853
}
1854
break;
1855
case ctrl('L'): /* Ctrl+L, clear screen */
1856
linenoiseClearScreen();
1857
/* Force recalc of window size for serial terminals */
1858
current->cols = 0;
1859
current->rpos = 0;
1860
refreshLine(current);
1861
break;
1862
default:
1863
if (c >= meta('a') && c <= meta('z')) {
1864
/* Don't insert meta chars that are not bound */
1865
break;
1866
}
1867
/* Only tab is allowed without ^V */
1868
if (c == '\t' || c >= ' ') {
1869
if (insert_char(current, current->pos, c) == 1) {
1870
refreshLine(current);
1871
}
1872
}
1873
break;
1874
}
1875
}
1876
return sb_len(current->buf);
1877
}
1878
1879
int linenoiseColumns(void)
1880
{
1881
struct current current;
1882
current.output = NULL;
1883
enableRawMode (&current);
1884
getWindowSize (&current);
1885
disableRawMode (&current);
1886
return current.cols;
1887
}
1888
1889
/**
1890
* Reads a line from the file handle (without the trailing NL or CRNL)
1891
* and returns it in a stringbuf.
1892
* Returns NULL if no characters are read before EOF or error.
1893
*
1894
* Note that the character count will *not* be correct for lines containing
1895
* utf8 sequences. Do not rely on the character count.
1896
*/
1897
static stringbuf *sb_getline(FILE *fh)
1898
{
1899
stringbuf *sb = sb_alloc();
1900
int c;
1901
int n = 0;
1902
1903
while ((c = getc(fh)) != EOF) {
1904
char ch;
1905
n++;
1906
if (c == '\r') {
1907
/* CRLF -> LF */
1908
continue;
1909
}
1910
if (c == '\n' || c == '\r') {
1911
break;
1912
}
1913
ch = c;
1914
/* ignore the effect of character count for partial utf8 sequences */
1915
sb_append_len(sb, &ch, 1);
1916
}
1917
if (n == 0 || sb->data == NULL) {
1918
sb_free(sb);
1919
return NULL;
1920
}
1921
return sb;
1922
}
1923
1924
char *linenoiseWithInitial(const char *prompt, const char *initial)
1925
{
1926
int count;
1927
struct current current;
1928
stringbuf *sb;
1929
1930
memset(&current, 0, sizeof(current));
1931
1932
if (enableRawMode(&current) == -1) {
1933
printf("%s", prompt);
1934
fflush(stdout);
1935
sb = sb_getline(stdin);
1936
if (sb && !fd_isatty(&current)) {
1937
printf("%s\n", sb_str(sb));
1938
fflush(stdout);
1939
}
1940
}
1941
else {
1942
current.buf = sb_alloc();
1943
current.pos = 0;
1944
current.nrows = 1;
1945
current.prompt = prompt;
1946
1947
/* The latest history entry is always our current buffer */
1948
linenoiseHistoryAdd(initial);
1949
set_current(&current, initial);
1950
1951
count = linenoiseEdit(&current);
1952
1953
disableRawMode(&current);
1954
printf("\n");
1955
1956
sb_free(current.capture);
1957
if (count == -1) {
1958
sb_free(current.buf);
1959
return NULL;
1960
}
1961
sb = current.buf;
1962
}
1963
return sb ? sb_to_string(sb) : NULL;
1964
}
1965
1966
char *linenoise(const char *prompt)
1967
{
1968
return linenoiseWithInitial(prompt, "");
1969
}
1970
1971
/* Using a circular buffer is smarter, but a bit more complex to handle. */
1972
static int linenoiseHistoryAddAllocated(char *line) {
1973
1974
if (history_max_len == 0) {
1975
notinserted:
1976
free(line);
1977
return 0;
1978
}
1979
if (history == NULL) {
1980
history = (char **)calloc(sizeof(char*), history_max_len);
1981
}
1982
1983
/* do not insert duplicate lines into history */
1984
if (history_len > 0 && strcmp(line, history[history_len - 1]) == 0) {
1985
goto notinserted;
1986
}
1987
1988
if (history_len == history_max_len) {
1989
free(history[0]);
1990
memmove(history,history+1,sizeof(char*)*(history_max_len-1));
1991
history_len--;
1992
}
1993
history[history_len] = line;
1994
history_len++;
1995
return 1;
1996
}
1997
1998
int linenoiseHistoryAdd(const char *line) {
1999
return linenoiseHistoryAddAllocated(strdup(line));
2000
}
2001
2002
int linenoiseHistoryGetMaxLen(void) {
2003
return history_max_len;
2004
}
2005
2006
int linenoiseHistorySetMaxLen(int len) {
2007
char **newHistory;
2008
2009
if (len < 1) return 0;
2010
if (history) {
2011
int tocopy = history_len;
2012
2013
newHistory = (char **)calloc(sizeof(char*), len);
2014
2015
/* If we can't copy everything, free the elements we'll not use. */
2016
if (len < tocopy) {
2017
int j;
2018
2019
for (j = 0; j < tocopy-len; j++) free(history[j]);
2020
tocopy = len;
2021
}
2022
memcpy(newHistory,history+(history_len-tocopy), sizeof(char*)*tocopy);
2023
free(history);
2024
history = newHistory;
2025
}
2026
history_max_len = len;
2027
if (history_len > history_max_len)
2028
history_len = history_max_len;
2029
return 1;
2030
}
2031
2032
/* Save the history in the specified file. On success 0 is returned
2033
* otherwise -1 is returned. */
2034
int linenoiseHistorySave(const char *filename) {
2035
FILE *fp = fopen(filename,"w");
2036
int j;
2037
2038
if (fp == NULL) return -1;
2039
for (j = 0; j < history_len; j++) {
2040
const char *str = history[j];
2041
/* Need to encode backslash, nl and cr */
2042
while (*str) {
2043
if (*str == '\\') {
2044
fputs("\\\\", fp);
2045
}
2046
else if (*str == '\n') {
2047
fputs("\\n", fp);
2048
}
2049
else if (*str == '\r') {
2050
fputs("\\r", fp);
2051
}
2052
else {
2053
fputc(*str, fp);
2054
}
2055
str++;
2056
}
2057
fputc('\n', fp);
2058
}
2059
2060
fclose(fp);
2061
return 0;
2062
}
2063
2064
/* Load the history from the specified file.
2065
*
2066
* If the file does not exist or can't be opened, no operation is performed
2067
* and -1 is returned.
2068
* Otherwise 0 is returned.
2069
*/
2070
int linenoiseHistoryLoad(const char *filename) {
2071
FILE *fp = fopen(filename,"r");
2072
stringbuf *sb;
2073
2074
if (fp == NULL) return -1;
2075
2076
while ((sb = sb_getline(fp)) != NULL) {
2077
/* Take the stringbuf and decode backslash escaped values */
2078
char *buf = sb_to_string(sb);
2079
char *dest = buf;
2080
const char *src;
2081
2082
for (src = buf; *src; src++) {
2083
char ch = *src;
2084
2085
if (ch == '\\') {
2086
src++;
2087
if (*src == 'n') {
2088
ch = '\n';
2089
}
2090
else if (*src == 'r') {
2091
ch = '\r';
2092
} else {
2093
ch = *src;
2094
}
2095
}
2096
*dest++ = ch;
2097
}
2098
*dest = 0;
2099
2100
linenoiseHistoryAddAllocated(buf);
2101
}
2102
fclose(fp);
2103
return 0;
2104
}
2105
2106
/* Provide access to the history buffer.
2107
*
2108
* If 'len' is not NULL, the length is stored in *len.
2109
*/
2110
char **linenoiseHistory(int *len) {
2111
if (len) {
2112
*len = history_len;
2113
}
2114
return history;
2115
}
2116
2117