1 /* linenoise.m -- guerrilla line editing library against the idea that a
2 * line editing lib needs to be 20,000 lines of C code.
4 * You can find the latest source code at:
6 * http://github.com/antirez/linenoise
8 * Does a number of crazy assumptions that happen to be true in 99.9999% of
9 * the 2010 UNIX computers around.
11 * ------------------------------------------------------------------------
13 * Copyright (c) 2010-2013, Salvatore Sanfilippo <antirez at gmail dot com>
14 * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
16 * All rights reserved.
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
22 * * Redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer.
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.
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.
41 * ------------------------------------------------------------------------
44 * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
45 * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
48 * - Filter bogus Ctrl+<char> combinations.
52 * - History search like Ctrl+r in readline?
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.
59 * CHA (Cursor Horizontal Absolute)
61 * Effect: moves cursor to column n
65 * Effect: if n is 0 or missing, clear from cursor to end of line
66 * Effect: if n is 1, clear from beginning of line to cursor
67 * Effect: if n is 2, clear entire line
69 * CUF (CUrsor Forward)
71 * Effect: moves cursor forward of n chars
73 * When multi line mode is enabled, we also use an additional escape
74 * sequence. However multi line editing is disabled by default.
78 * Effect: moves cursor up of n chars.
82 * Effect: moves cursor down of n chars.
84 * The following are used to clear the screen: ESC [ H ESC [ 2 J
85 * This is actually composed of two sequences:
89 * Effect: moves the cursor to upper left corner
91 * ED2 (Clear entire screen)
93 * Effect: clear the whole screen
102 #include <sys/types.h>
103 #include <sys/ioctl.h>
105 #import "linenoise.h"
107 #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
108 #define LINENOISE_MAX_LINE 4096
110 static Linenoise *instance = nil;
112 // At exit we'll try to fix the terminal to the initial conditions.
113 static void linenoiseAtExit(void)
115 [instance LN_disableRawModeForFD: STDIN_FILENO];
119 @implementation Linenoise
120 @synthesize multiline = _multiline;
121 @synthesize completionCallback = _completionCallback;
123 + (Linenoise*)sharedLinenoise
130 static bool initialized = false;
133 instance = [[self alloc] init];
134 atexit(linenoiseAtExit);
142 _maximalHistoryLength = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
144 [[OFMutableArray alloc] initWithCapacity: _maximalHistoryLength];
156 /* =========================== Line editing ================================= */
159 /* Insert the character 'c' at cursor current position.
161 * On error writing to the terminal -1 is returned, otherwise 0. */
162 - (int)LN_editInsertCharacter: (int)c
178 [_buf insertString: ins
185 /* Move cursor on the left. */
186 - (void)LN_editMoveLeft
194 /* Move cursor on the right. */
195 - (void)LN_editMoveRight
197 if (_pos != [_buf length]) {
203 /* Substitute the currently edited line with the next or previous history
204 * entry as specified by 'dir'. */
205 - (void)LN_editHistoryNextInDirection: (enum linenoiseDirection)dir
207 size_t count = [_history count];
209 /* Update the current history entry before to
210 * overwrite it with the next one. */
211 _history[count - 1 - _historyIndex] = _buf;
212 // Show the new entry
213 _historyIndex += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1;
214 if (_historyIndex < 0) {
217 } else if (_historyIndex >= count) {
218 _historyIndex = count - 1;
222 _buf = [_history[count - 1 - _historyIndex] mutableCopy];
223 _pos = [_buf length];
228 /* Delete the character at the right of the cursor without altering the cursor
229 * position. Basically this is what happens with the "Delete" keyboard key. */
230 - (void)LN_editDelete
232 size_t len = [_buf length];
233 if (len > 0 && _pos < len) {
234 [_buf deleteCharactersInRange: of_range(_pos, 1)];
239 /* Backspace implementation. */
240 - (void)LN_editBackspace
242 size_t len = [_buf length];
243 if (_pos > 0 && len > 0) {
245 [_buf deleteCharactersInRange: of_range(_pos, 1)];
250 /* Delete the previosu word, maintaining the cursor at the start of the
252 - (void)LN_editDeletePreviousWord
254 size_t old_pos = _pos;
256 while (_pos > 0 && [_buf characterAtIndex: _pos - 1] == ' ')
258 while (_pos > 0 && [_buf characterAtIndex: _pos - 1] != ' ')
261 [_buf deleteCharactersInRange: of_range(_pos, old_pos - _pos)];
265 /* This function is the core of the line editing capability of linenoise.
266 * It expects 'fd' to be already in "raw mode" so that every key pressed
267 * will be returned ASAP to read().
269 * The resulting string is put into 'buf' when the user type enter, or
270 * when ctrl+d is typed.
272 * The function returns the length of the current buffer. */
273 - (OFString*)LN_editWithFD: (int)fd
274 prompt: (OFString*)prompt
276 /* Populate the linenoise state that we pass to functions implementing
277 * specific editing functionalities. */
278 _term = [OFFile fileWithFileDescriptor: fd];
279 _buf = [@"" mutableCopy];
282 _cols = [self LN_getColumns];
286 /* The latest history entry is always our current buffer, that
287 * initially is just an empty string. */
288 [self addHistoryItem: @""];
290 [_term writeString: prompt];
295 char seq[2], seq2[2];
297 nread = [_term readIntoBuffer: &c
300 return [_buf autorelease];
303 /* Only autocomplete when the callback is set.
304 * It returns < 0 when there was an error reading from fd.
305 * Otherwise it will return the character that should be
307 if (c == 9 && _completionCallback != NULL) {
308 c = [self LN_completeLine];
311 return [_buf autorelease];
313 // Read next character when 0
320 [_history removeLastObject];
321 return [_buf autorelease];
326 case 127: // backspace
327 [self LN_editBackspace];
330 /* remove char at right of cursor, or if the
331 * line is empty, act as end-of-file. */
332 if ([_buf length] > 0) {
333 [self LN_editDelete];
335 [_history removeLastObject];
339 case 20: { // ctrl-t, swaps current character with previous.
341 if (pos <= 0 || pos >= [_buf length])
344 OFMutableString *reverse =
345 [[_buf substringWithRange: of_range(pos - 1, 2)]
349 [_buf replaceCharactersInRange: of_range(pos - 1, 2)
350 withString: reverse];
352 if (pos != [_buf length])
358 [self LN_editMoveLeft];
361 [self LN_editMoveRight];
364 [self LN_editHistoryNextInDirection:
365 LINENOISE_HISTORY_PREV];
368 [self LN_editHistoryNextInDirection:
369 LINENOISE_HISTORY_NEXT];
371 case 27: // escape sequence
372 /* Read the next two bytes representing the
373 * escape sequence. */
374 if ([_term readIntoBuffer: seq
378 if (seq[0] == 91 && seq[1] == 68) {
380 [self LN_editMoveLeft];
381 } else if (seq[0] == 91 && seq[1] == 67) {
383 [self LN_editMoveRight];
384 } else if (seq[0] == 91 &&
385 (seq[1] == 65 || seq[1] == 66)) {
386 /* Up and Down arrows */
387 [self LN_editHistoryNextInDirection:
388 (seq[1] == 65) ? LINENOISE_HISTORY_PREV
389 : LINENOISE_HISTORY_NEXT];
390 } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) {
391 // extended escape, read additional two bytes.
392 if ([_term readIntoBuffer: seq2
395 if (seq[1] == 51 && seq2[0] == 126) {
397 [self LN_editDelete];
402 if ([self LN_editInsertCharacter: c])
405 case 21: // Ctrl+u, delete the whole line.
406 _buf = [@"" mutableCopy];
410 case 11: { // Ctrl+k, delete from current to end of line.
412 size_t diff = [_buf length] - pos;
413 [_buf deleteCharactersInRange:
414 of_range(pos, diff)];
418 case 1: // Ctrl+a, go to the start of the line
422 case 5: // ctrl+e, go to the end of the line
423 _pos = [_buf length];
426 case 12: // ctrl+l, clear screen
430 case 23: // ctrl+w, delete previous word
431 [self LN_editDeletePreviousWord];
435 return [_buf autorelease];
438 /* This function calls the line editing function linenoiseEdit() using
439 * the STDIN file descriptor set in raw mode. */
440 - (OFString*)LN_editRawWithPrompt: (OFString*)prompt
443 int fd = [of_stdin fileDescriptorForReading];
446 return [of_stdin readLine];
448 if ([self LN_enableRawModeForFD: fd] == -1)
450 ret = [self LN_editWithFD: fd
452 [self LN_disableRawModeForFD: fd];
453 [of_stdout writeString: @"\n"];
458 /* The high level function that is the main API of the linenoise library.
459 * This function checks if the terminal has basic capabilities, just checking
460 * for a blacklist of stupid terminals, and later either calls the line
461 * editing function or uses dummy fgets() so that you will be able to type
462 * something even in the most desperate of the conditions. */
463 - (OFString*)readInputWithPrompt: (OFString*)prompt
465 if ([self LN_isUnsupportedTerm]) {
468 _prompt = [prompt retain];
470 [of_stdout writeString: prompt];
471 [of_stdout flushWriteBuffer];
473 ret = [of_stdin readLine];
480 return [self LN_editRawWithPrompt: prompt];
484 /* ============================== Completion ================================ */
486 /* This is an helper function for linenoiseEdit() and is called when the
487 * user types the <tab> key in order to complete the string currently in the
490 * The state of the editing is encapsulated into the pointed linenoiseState
491 * structure as described in the structure definition. */
492 - (int)LN_completeLine
494 OFList *lc = [OFList new];
498 _completionCallback(_buf, lc);
499 if ([lc count] == 0) {
504 of_list_object_t *completion = [lc firstListObject];
505 size_t count = [lc count];
508 // Show completion or original buffer
510 size_t saved_pos = _pos;
511 OFMutableString *saved_buf = _buf;
513 _buf = [completion->object mutableCopy];
514 _pos = [_buf length];
522 nread = [_term readIntoBuffer: &c
531 i = (i + 1) % (count + 1);
537 completion = completion->next;
538 if (completion == NULL)
539 completion = [lc firstListObject];
542 // Re-show original buffer
548 // Update buffer and return
552 [completion->object mutableCopy];
563 return c; // Return last read character
566 /* ================================ History ================================= */
568 // Using a circular buffer is smarter, but a bit more complex to handle.
569 - (int)addHistoryItem: (OFString*)line
571 if (_maximalHistoryLength == 0)
574 size_t len = [_history count];
576 if (len == _maximalHistoryLength) {
577 for (size_t i = 0; i < len - 1; i++)
578 _history[i] = _history[i+1];
579 [_history removeLastObject];
582 [_history addObject: line];
586 /* Set the maximum length for the history. This function can be called even
587 * if there is already some history, the function will make sure to retain
588 * just the latest 'len' elements if the new history length value is smaller
589 * than the amount of items already inside the history. */
590 - (void)setMaximalHistoryLength: (size_t)len
593 @throw [OFInvalidArgumentException exception];
595 OFMutableArray *old = _history, *new;
596 int tocopy = len < [old count] ? len : [old count];
598 new = [[OFMutableArray alloc] initWithCapacity: len];
600 for (int i = 0; i < tocopy; i++)
601 [new addObject: old[i]];
606 _maximalHistoryLength = len;
609 - (size_t)maximalHistoryLength
611 return _maximalHistoryLength;
614 /* Save the history in the specified file. On success 0 is returned
615 * otherwise -1 is returned. */
616 - (void)saveHistoryToFile: (OFString*)filename
618 OFFile *file = [OFFile fileWithPath: filename
620 for (int j = 0; j < [_history count]; j++)
621 [file writeLine: _history[j]];
625 /* Load the history from the specified file. If the file does not exist
626 * zero is returned and no operation is performed.
628 * If the file exists and the operation succeeded 0 is returned, otherwise
629 * on error -1 is returned. */
630 - (void)loadHistoryFromFile: (OFString*)filename
632 OFFile *file = [OFFile fileWithPath: filename
635 while ((line = [file readLine]) != nil)
636 [self addHistoryItem: line];
641 /* =========================== Line editing ================================= */
643 // Clear the screen. Used to handle ctrl+l
646 [of_stdout writeString: @"\x1b[H\x1b[2J"];
649 /* Calls the two low level functions LN_refreshSingleLine or
650 * LN_refreshMultiLine according to the selected mode. */
657 [self LN_refreshMultiLine];
659 [self LN_refreshSingleLine];
662 /* Single line low level line refresh.
664 * Rewrite the currently edited line accordingly to the buffer content,
665 * cursor position, and number of columns of the terminal. */
666 - (void)LN_refreshSingleLine;
668 size_t plen = [_prompt length];
670 OFString *buf = _buf;
672 if ((plen + pos) >= _cols) {
673 size_t offset = (plen + pos) - _cols;
674 size_t buflen = [buf length] - offset;
675 buf = [buf substringWithRange: of_range(offset, buflen)];
679 // Cursor to left edge
680 [_term writeString: @"\x1b[0G"];
681 // Write the prompt and the current buffer content
682 [_term writeString: _prompt];
683 [_term writeString: buf];
685 [_term writeString: @"\x1b[0K"];
686 // Move cursor to original position
687 [_term writeFormat: @"\x1b[0G\x1b[%zdC", pos + plen];
690 /* Multi line low level line refresh.
692 * Rewrite the currently edited line accordingly to the buffer content,
693 * cursor position, and number of columns of the terminal. */
694 - (void)LN_refreshMultiLine
696 size_t plen = [_prompt length];
698 // rows used by current buf.
699 size_t rows = (plen + [_buf length] + _cols - 1) / _cols;
700 // cursor relative row.
701 size_t rpos = (plen + _oldpos + _cols) / _cols;
702 // rpos after refresh.
704 ssize_t old_rows = _maxrows, j;
706 // Update maxrows if needed.
711 OFFile *file = [OFFile fileWithPath: @"/tmp/debug.txt"
713 [file writeFormat: @"[%zd %zd %zd] p: %zd, rows: %zd, rpos: %zd, "
714 @"max: %zd, oldmax: %zd", [_buf length], _pos, _oldpos, plen, rows,
715 rpos, _maxrows, old_rows];
718 /* First step: clear all the lines used before. To do so start by
719 * going to the last row. */
720 if (old_rows > rpos) {
722 [file writeFormat: @", go down %zd", old_rows - rpos];
724 [_term writeFormat: @"\x1b[%zdB", old_rows - rpos];
727 // Now for every row clear it, go up.
728 for (j = 0; j < old_rows - 1; j++) {
730 [file writeString: @", clear+up"];
732 [_term writeString: @"\x1b[0G\x1b[0K\x1b[1A"];
735 // Clean the top line.
737 [file writeString: @", clear"];
739 [_term writeString: @"\x1b[0G\x1b[0K"];
741 // Write the prompt and the current buffer content
742 [_term writeString: _prompt];
743 [_term writeString: _buf];
745 /* If we are at the very end of the screen with our prompt, we need to
746 * emit a newline and move the prompt to the first column. */
747 if (_pos && _pos == [_buf length] && (_pos + plen) % _cols == 0) {
749 [file writeString: @", <newline>"];
751 [_term writeString: @"\n\x1b[0G"];
757 // Move cursor to right position.
758 rpos2 = (plen + _pos + _cols) / _cols;
759 // current cursor relative row.
761 [file writeFormat: @", rpos2 %zd", rpos2];
763 // Go up till we reach the expected positon.
764 if (rows - rpos2 > 0) {
766 [file writeFormat: @", go-up %zd", rows - rpos2];
768 [_term writeFormat: @"\x1b[%zdA", rows - rpos2];
772 [file writeFormat: @", set col %zd", 1 + ((plen + _pos) % _cols)];
774 [_term writeFormat: @"\x1b[%zdG", 1 + ((plen + _pos) % _cols)];
779 [file writeString: @"\n"];
784 /* ======================= Low level terminal handling ====================== */
786 /* Return true if the terminal name is in the list of terminals we know are
787 * not able to understand basic escape sequences. */
789 - (bool)LN_isUnsupportedTerm
791 static char *unsupported_term[] = { "dumb", "cons25", NULL };
792 char *term = getenv("TERM");
797 for (j = 0; unsupported_term[j]; j++)
798 if (!strcasecmp(term, unsupported_term[j]))
803 /* Raw mode: 1960 magic shit. */
804 - (int)LN_enableRawModeForFD: (int)fd
808 if (!isatty(STDIN_FILENO))
810 if (tcgetattr(fd, &_orig_termios) == -1)
813 // modify the original mode
816 /* input modes: no break, no CR to NL, no parity check, no strip char,
817 * no start/stop output control. */
818 raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
819 // output modes - disable post processing
820 raw.c_oflag &= ~(OPOST);
821 // control modes - set 8 bit chars
822 raw.c_cflag |= (CS8);
823 /* local modes - choing off, canonical off, no extended functions,
824 * no signal chars (^Z, ^C) */
825 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
826 /* control chars - set return condition: min number of bytes and timer.
827 * We want read to return every single byte, without timeout. */
832 // put terminal in raw mode after flushing
833 if (tcsetattr(fd, TCSAFLUSH, &raw) < 0)
843 - (void)LN_disableRawModeForFD: (int)fd
845 if (_rawmode && tcsetattr(fd, TCSAFLUSH, &_orig_termios) != -1)
849 /* Try to get the number of columns in the current terminal, or assume 80
855 if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0)
860 /* Beep, used for completion when there is nothing to complete or when all
861 * the choices were already shown. */
864 fprintf(stderr, "\x7");