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;
}