/* linenoise.c -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*
+ * You can find the latest source code at:
+ *
+ * http://github.com/antirez/linenoise
+ *
* Does a number of crazy assumptions that happen to be true in 99.9999% of
* the 2010 UNIX computers around.
*
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
- * Reference: http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+ * References:
+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
*
- * Works with:
+ * Todo list:
+ * - Switch to gets() if $TERM is something we can't support.
+ * - Filter bogus Ctrl+<char> combinations.
+ * - Win32 support
*
- * Linux console (TERM = Linux)
- * Linux KDE terminal application (TERM = xterm)
- * Linux xterm (TERM = xterm)
- * Mac OS X iTerm (TERM = xterm)
+ * Bloat:
+ * - Completion?
+ * - History search like Ctrl+r in readline?
*
- * Broken with:
+ * List of escape sequences used by this program, we do everything just
+ * with three sequences. In order to be so cheap we may have some
+ * flickering effect with some slow terminal, but the lesser sequences
+ * the more compatible.
*
- * Mac OS X default Terminal application (TERM = xterm)
+ * CHA (Cursor Horizontal Absolute)
+ * Sequence: ESC [ n G
+ * Effect: moves cursor to column n
*
- * TODO
+ * EL (Erase Line)
+ * Sequence: ESC [ n K
+ * Effect: if n is 0 or missing, clear from cursor to end of line
+ * Effect: if n is 1, clear from beginning of line to cursor
+ * Effect: if n is 2, clear entire line
*
- * - Switch to gets() if $TERM is something we can't support.
- * - Completion?
+ * CUF (CUrsor Forward)
+ * Sequence: ESC [ n C
+ * Effect: moves cursor forward of n chars
+ *
*/
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
+#define LINENOISE_MAX_LINE 4096
+
static struct termios orig_termios; /* in order to restore at exit */
static int rawmode = 0; /* for atexit() function to check if restore is needed*/
static int atexit_registered = 0; /* register atexit just 1 time */
return ws.ws_col;
}
-static void refreshLine(int fd, char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
+static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
char seq[64];
size_t plen = strlen(prompt);
if (write(fd,seq,strlen(seq)) == -1) return;
}
-static int linenoisePrompt(int fd, char *buf, size_t buflen, char *prompt) {
+static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
size_t plen = strlen(prompt);
size_t pos = 0;
size_t len = 0;
if (nread <= 0) return len;
switch(c) {
case 13: /* enter */
- case 4: /* ctrl+d */
history_len--;
return len;
+ case 4: /* ctrl+d */
+ history_len--;
+ return (len == 0) ? -1 : (int)len;
case 3: /* ctrl+c */
errno = EAGAIN;
return -1;
case 127: /* backspace */
+ case 8: /* ctrl+h */
if (pos > 0 && len > 0) {
memmove(buf+pos-1,buf+pos,len-pos);
pos--;
return len;
}
-int linenoise(char *buf, size_t buflen, char *prompt) {
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
int fd = STDIN_FILENO;
int count;
return count;
}
+char *linenoise(const char *prompt) {
+ char buf[LINENOISE_MAX_LINE];
+ int count;
+
+ count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
+ if (count == -1) return NULL;
+ return strdup(buf);
+}
+
/* Using a circular buffer is smarter, but a bit more complex to handle. */
int linenoiseHistoryAdd(char *line) {
if (history_max_len == 0) return 0;