This source file includes following definitions.
- win32read
- strdup
- isUnsupportedTerm
- freeHistory
- enableRawMode
- disableRawMode
- linenoiseAtExit
- getColumns
- refreshLine
- beep
- freeCompletions
- completeLine
- linenoiseClearScreen
- linenoisePrompt
- linenoiseRaw
- linenoise
- linenoiseSetCompletionCallback
- linenoiseAddCompletion
- linenoiseHistoryAdd
- linenoiseHistorySetMaxLen
- linenoiseHistorySave
- linenoiseHistoryLoad
#ifndef _WIN32
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include "linenoise.h"
#define NOTUSED(V) ((void) V)
#ifdef _WIN32
#include "win32fixes.h"
#endif
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
#define LINENOISE_MAX_LINE 4096
#ifndef _WIN32
static char *unsupported_term[] = {"dumb","cons25",NULL};
#endif
static linenoiseCompletionCallback *completionCallback = NULL;
#ifndef _WIN32
static struct termios orig_termios;
#endif
static int rawmode = 0;
static int atexit_registered = 0;
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
static int history_len = 0;
char **history = NULL;
static void linenoiseAtExit(void);
int linenoiseHistoryAdd(const char *line);
#ifdef _WIN32
#ifndef STDIN_FILENO
#define STDIN_FILENO (_fileno(stdin))
#endif
HANDLE hOut;
HANDLE hIn;
DWORD consolemode;
static int win32read(char *c) {
DWORD foo;
INPUT_RECORD b;
KEY_EVENT_RECORD e;
while (1) {
if (!ReadConsoleInput(hIn, &b, 1, &foo)) return 0;
if (!foo) return 0;
if (b.EventType == KEY_EVENT && b.Event.KeyEvent.bKeyDown) {
e = b.Event.KeyEvent;
*c = b.Event.KeyEvent.uChar.AsciiChar;
if (e.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
switch (*c) {
case 'D':
*c = 4;
return 1;
case 'C':
*c = 3;
return 1;
case 'H':
*c = 8;
return 1;
case 'T':
*c = 20;
return 1;
case 'B':
*c = 2;
return 1;
case 'F':
*c = 6;
return 1;
case 'P':
*c = 16;
return 1;
case 'N':
*c = 14;
return 1;
case 'U':
*c = 21;
return 1;
case 'K':
*c = 11;
return 1;
case 'A':
*c = 1;
return 1;
case 'E':
*c = 5;
return 1;
}
} else {
switch (e.wVirtualKeyCode) {
case VK_ESCAPE:
*c = 3;
return 1;
case VK_RETURN:
*c = 13;
return 1;
case VK_LEFT:
*c = 2;
return 1;
case VK_RIGHT:
*c = 6;
return 1;
case VK_UP:
*c = 16;
return 1;
case VK_DOWN:
*c = 14;
return 1;
case VK_HOME:
*c = 1;
return 1;
case VK_END:
*c = 5;
return 1;
case VK_BACK:
*c = 8;
return 1;
case VK_DELETE:
*c = 127;
return 1;
default:
if (*c) return 1;
}
}
}
}
return -1;
}
#ifdef __STRICT_ANSI__
char *strdup(const char *s) {
size_t l = strlen(s)+1;
char *p = malloc(l);
memcpy(p,s,l);
return p;
}
#endif
#endif
static int isUnsupportedTerm(void) {
#ifndef _WIN32
char *term = getenv("TERM");
int j;
if (term == NULL) return 0;
for (j = 0; unsupported_term[j]; j++)
if (!strcasecmp(term,unsupported_term[j])) return 1;
#endif
return 0;
}
static void freeHistory(void) {
if (history) {
int j;
for (j = 0; j < history_len; j++)
free(history[j]);
free(history);
}
}
static int enableRawMode(int fd) {
#ifndef _WIN32
struct termios raw;
if (!isatty(STDIN_FILENO)) goto fatal;
if (!atexit_registered) {
atexit(linenoiseAtExit);
atexit_registered = 1;
}
if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
raw = orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0;
if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
rawmode = 1;
#else
NOTUSED(fd);
if (!atexit_registered) {
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut==INVALID_HANDLE_VALUE) goto fatal;
if (!GetConsoleMode(hOut, &consolemode)) {
CloseHandle(hOut);
errno = ENOTTY;
return -1;
};
hIn = GetStdHandle(STD_INPUT_HANDLE);
if (hIn == INVALID_HANDLE_VALUE) {
CloseHandle(hOut);
errno = ENOTTY;
return -1;
}
GetConsoleMode(hIn, &consolemode);
SetConsoleMode(hIn, ENABLE_PROCESSED_INPUT);
atexit(linenoiseAtExit);
atexit_registered = 1;
}
rawmode = 1;
#endif
return 0;
fatal:
errno = ENOTTY;
return -1;
}
static void disableRawMode(int fd) {
#ifdef _WIN32
NOTUSED(fd);
rawmode = 0;
#else
if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
rawmode = 0;
#endif
}
static void linenoiseAtExit(void) {
#ifdef _WIN32
SetConsoleMode(hIn, consolemode);
CloseHandle(hOut);
CloseHandle(hIn);
#else
disableRawMode(STDIN_FILENO);
#endif
freeHistory();
}
static int getColumns(void) {
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO b;
if (!GetConsoleScreenBufferInfo(hOut, &b)) return 80;
return b.srWindow.Right - b.srWindow.Left;
#else
struct winsize ws;
if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80;
return ws.ws_col;
#endif
}
static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
char seq[64];
#ifdef _WIN32
DWORD pl, bl, w;
CONSOLE_SCREEN_BUFFER_INFO b;
COORD coord;
#endif
size_t plen = strlen(prompt);
while((plen+pos) >= cols) {
buf++;
len--;
pos--;
}
while (plen+len > cols) {
len--;
}
#ifndef _WIN32
snprintf(seq,64,"\x1b[0G");
if (write(fd,seq,strlen(seq)) == -1) return;
if (write(fd,prompt,strlen(prompt)) == -1) return;
if (write(fd,buf,len) == -1) return;
snprintf(seq,64,"\x1b[0K");
if (write(fd,seq,strlen(seq)) == -1) return;
snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen));
if (write(fd,seq,strlen(seq)) == -1) return;
#else
NOTUSED(seq);
NOTUSED(fd);
if (!GetConsoleScreenBufferInfo(hOut, &b)) return;
coord.X = 0;
coord.Y = b.dwCursorPosition.Y;
FillConsoleOutputCharacterA(hOut, ' ', b.dwSize.X, coord, &w);
SetConsoleCursorPosition(hOut, coord);
WriteConsole(hOut, prompt, (DWORD)plen, &pl, NULL);
WriteConsole(hOut, buf, (DWORD)len, &bl, NULL);
coord.X = (int)(pos+plen);
coord.Y = b.dwCursorPosition.Y;
SetConsoleCursorPosition(hOut, coord);
#endif
}
static void beep() {
fprintf(stderr, "\x7");
fflush(stderr);
}
static void freeCompletions(linenoiseCompletions *lc) {
size_t i;
for (i = 0; i < lc->len; i++)
free(lc->cvec[i]);
if (lc->cvec != NULL)
free(lc->cvec);
}
static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) {
linenoiseCompletions lc = { 0, NULL };
int nread, nwritten;
char c = 0;
completionCallback(buf,&lc);
if (lc.len == 0) {
beep();
} else {
size_t stop = 0, i = 0;
size_t clen;
while(!stop) {
if (i < lc.len) {
clen = strlen(lc.cvec[i]);
refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols);
} else {
refreshLine(fd,prompt,buf,*len,*pos,cols);
}
nread = read(fd,&c,1);
if (nread <= 0) {
freeCompletions(&lc);
return -1;
}
switch(c) {
case 9:
i = (i+1) % (lc.len+1);
if (i == lc.len) beep();
break;
case 27:
if (i < lc.len) {
refreshLine(fd,prompt,buf,*len,*pos,cols);
}
stop = 1;
break;
default:
if (i < lc.len) {
nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]);
*len = *pos = nwritten;
}
stop = 1;
break;
}
}
}
freeCompletions(&lc);
return c;
}
void linenoiseClearScreen(void) {
if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
}
}
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;
size_t cols = getColumns();
int history_index = 0;
#ifdef _WIN32
DWORD foo;
#endif
buf[0] = '\0';
buflen--;
linenoiseHistoryAdd("");
#ifdef _WIN32
if (!WriteConsole(hOut, prompt, (DWORD)plen, &foo, NULL)) return -1;
#else
if (write(fd,prompt,plen) == -1) return -1;
#endif
while(1) {
char c;
int nread;
char seq[2], seq2[2];
#ifdef _WIN32
nread = win32read(&c);
#else
nread = read(fd,&c,1);
#endif
if (nread <= 0) return (int)len;
if (c == 9 && completionCallback != NULL) {
c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
if (c < 0) return (int)len;
if (c == 0) continue;
}
switch(c) {
case 13:
history_len--;
free(history[history_len]);
return (int)len;
case 3:
errno = EAGAIN;
return -1;
case 127:
#ifdef _WIN32
if (pos < len && len > 0) {
memmove(buf+pos,buf+pos+1,len-pos);
len--;
buf[len] = '\0';
refreshLine(fd,prompt,buf,len,pos,cols);
}
break;
#endif
case 8:
if (pos > 0 && len > 0) {
memmove(buf+pos-1,buf+pos,len-pos);
pos--;
len--;
buf[len] = '\0';
refreshLine(fd,prompt,buf,len,pos,cols);
}
break;
case 4:
if (len > 1 && pos < (len-1)) {
memmove(buf+pos,buf+pos+1,len-pos);
len--;
buf[len] = '\0';
refreshLine(fd,prompt,buf,len,pos,cols);
} else if (len == 0) {
history_len--;
free(history[history_len]);
return -1;
}
break;
case 20:
if (pos > 0 && pos < len) {
int aux = buf[pos-1];
buf[pos-1] = buf[pos];
buf[pos] = aux;
if (pos != len-1) pos++;
refreshLine(fd,prompt,buf,len,pos,cols);
}
break;
case 2:
goto left_arrow;
case 6:
goto right_arrow;
case 16:
seq[1] = 65;
goto up_down_arrow;
case 14:
seq[1] = 66;
goto up_down_arrow;
break;
case 27:
if (read(fd,seq,2) == -1) break;
if (seq[0] == 91 && seq[1] == 68) {
left_arrow:
if (pos > 0) {
pos--;
refreshLine(fd,prompt,buf,len,pos,cols);
}
} else if (seq[0] == 91 && seq[1] == 67) {
right_arrow:
if (pos != len) {
pos++;
refreshLine(fd,prompt,buf,len,pos,cols);
}
} else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) {
up_down_arrow:
if (history_len > 1) {
free(history[history_len-1-history_index]);
history[history_len-1-history_index] = strdup(buf);
history_index += (seq[1] == 65) ? 1 : -1;
if (history_index < 0) {
history_index = 0;
break;
} else if (history_index >= history_len) {
history_index = history_len-1;
break;
}
strncpy(buf,history[history_len-1-history_index],buflen);
buf[buflen] = '\0';
len = pos = strlen(buf);
refreshLine(fd,prompt,buf,len,pos,cols);
}
} else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) {
if (read(fd,seq2,2) == -1) break;
if (seq[1] == 51 && seq2[0] == 126) {
if (len > 0 && pos < len) {
memmove(buf+pos,buf+pos+1,len-pos-1);
len--;
buf[len] = '\0';
refreshLine(fd,prompt,buf,len,pos,cols);
}
}
}
break;
default:
if (len < buflen) {
if (len == pos) {
buf[pos] = c;
pos++;
len++;
buf[len] = '\0';
if (plen+len < cols) {
#ifdef _WIN32
if (!WriteConsole(hOut, &c, 1, &foo, NULL)) return -1;
#else
if (write(fd,&c,1) == -1) return -1;
#endif
} else {
refreshLine(fd,prompt,buf,len,pos,cols);
}
} else {
memmove(buf+pos+1,buf+pos,len-pos);
buf[pos] = c;
len++;
pos++;
buf[len] = '\0';
refreshLine(fd,prompt,buf,len,pos,cols);
}
}
break;
case 21:
buf[0] = '\0';
pos = len = 0;
refreshLine(fd,prompt,buf,len,pos,cols);
break;
case 11:
buf[pos] = '\0';
len = pos;
refreshLine(fd,prompt,buf,len,pos,cols);
break;
case 1:
pos = 0;
refreshLine(fd,prompt,buf,len,pos,cols);
break;
case 5:
pos = len;
refreshLine(fd,prompt,buf,len,pos,cols);
break;
case 12:
linenoiseClearScreen();
refreshLine(fd,prompt,buf,len,pos,cols);
break;
case 23:
{
size_t old_pos = pos;
size_t diff;
while (pos > 0 && buf[pos-1] == ' ')
pos--;
while (pos > 0 && buf[pos-1] != ' ')
pos--;
diff = old_pos - pos;
memmove(&buf[pos], &buf[old_pos], len-old_pos+1);
len -= diff;
refreshLine(fd,prompt,buf,len,pos,cols);
break;
}
}
}
return (int)len;
}
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
int fd = STDIN_FILENO;
int count;
if (buflen == 0) {
errno = EINVAL;
return -1;
}
if (!isatty(STDIN_FILENO)) {
if (fgets(buf, (int)buflen, stdin) == NULL) return -1;
count = (int)strlen(buf);
if (count && buf[count-1] == '\n') {
count--;
buf[count] = '\0';
}
} else {
if (enableRawMode(fd) == -1) return -1;
count = linenoisePrompt(fd, buf, buflen, prompt);
disableRawMode(fd);
printf("\n");
}
return count;
}
char *linenoise(const char *prompt) {
char buf[LINENOISE_MAX_LINE];
int count;
if (isUnsupportedTerm()) {
size_t len;
printf("%s",prompt);
fflush(stdout);
if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
len = strlen(buf);
while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
buf[len] = '\0';
}
return strdup(buf);
} else {
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
if (count == -1) return NULL;
return strdup(buf);
}
}
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
completionCallback = fn;
}
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
size_t len = strlen(str);
char *copy = malloc(len+1);
memcpy(copy,str,len+1);
lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
lc->cvec[lc->len++] = copy;
}
int linenoiseHistoryAdd(const char *line) {
char *linecopy;
if (history_max_len == 0) return 0;
if (history == NULL) {
history = malloc(sizeof(char*)*history_max_len);
if (history == NULL) return 0;
memset(history,0,(sizeof(char*)*history_max_len));
}
linecopy = strdup(line);
if (!linecopy) return 0;
if (history_len == history_max_len) {
free(history[0]);
memmove(history,history+1,sizeof(char*)*(history_max_len-1));
history_len--;
}
history[history_len] = linecopy;
history_len++;
return 1;
}
int linenoiseHistorySetMaxLen(int len) {
char **newC;
if (len < 1) return 0;
if (history) {
int tocopy = history_len;
newC = malloc(sizeof(char*)*len);
if (newC == NULL) return 0;
if (len < tocopy) tocopy = len;
memcpy(newC,history+(history_max_len-tocopy), sizeof(char*)*tocopy);
free(history);
history = newC;
}
history_max_len = len;
if (history_len > history_max_len)
history_len = history_max_len;
return 1;
}
int linenoiseHistorySave(char *filename) {
#ifdef _WIN32
FILE *fp = fopen(filename,"wb");
#else
FILE *fp = fopen(filename,"w");
#endif
int j;
if (fp == NULL) return -1;
for (j = 0; j < history_len; j++)
fprintf(fp,"%s\n",history[j]);
fclose(fp);
return 0;
}
int linenoiseHistoryLoad(char *filename) {
FILE *fp = fopen(filename,"r");
char buf[LINENOISE_MAX_LINE];
if (fp == NULL) return -1;
while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
char *p;
p = strchr(buf,'\r');
if (!p) p = strchr(buf,'\n');
if (p) *p = '\0';
linenoiseHistoryAdd(buf);
}
fclose(fp);
return 0;
}