Commit message: SERVER-25335 avoid group and other permissions when creating .dbshell history file
CVE: CVE-2016-6494
CWE: CWE-200
Vulnerability description: The client in MongoDB uses world-readable permissions on .dbshell history files, which might allow local users to obtain sensitive information by reading these files.

Project: mongo
File: linenoise.cpp

    PromptBase() : promptPreviousInputLen(0) {}

Project: mongo
File: linenoise.cpp

    int length(void) const {
        return len;
    }

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket1Semicolon5Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    thisKeyMetaCtrl |= CTRL;
    return doDispatch(c, escLeftBracket1Semicolon3or5Dispatch);
}

Project: mongo
File: linenoise.cpp

static int getScreenColumns(void) {
    int cols;
#ifdef _WIN32
    CONSOLE_SCREEN_BUFFER_INFO inf;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &inf);
    cols = inf.dwSize.X;
#else
    struct winsize ws;
    cols = (ioctl(1, TIOCGWINSZ, &ws) == -1) ? 80 : ws.ws_col;
#endif
    // cols is 0 in certain circumstances like inside debugger, which creates further issues
    return (cols > 0) ? cols : 80;
}

Project: mongo
File: linenoise.cpp

static void recomputeCharacterWidths(const UChar32* text, char* widths, int charCount) {
    for (int i = 0; i < charCount; ++i) {
        widths[i] = mk_wcwidth(text[i]);
    }
}

Project: mongo
File: linenoise.cpp

static UChar32 pageDownKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | PAGE_DOWN_KEY;
}

Project: mongo
File: linenoise.cpp

static void dynamicRefresh(PromptBase& pi, UChar32* buf32, int len, int pos) {
    // calculate the position of the end of the prompt
    int xEndOfPrompt, yEndOfPrompt;
    calculateScreenPosition(
        0, 0, pi.promptScreenColumns, pi.promptChars, xEndOfPrompt, yEndOfPrompt);
    pi.promptIndentation = xEndOfPrompt;

    // calculate the position of the end of the input line
    int xEndOfInput, yEndOfInput;
    calculateScreenPosition(xEndOfPrompt,
                            yEndOfPrompt,
                            pi.promptScreenColumns,
                            calculateColumnPosition(buf32, len),
                            xEndOfInput,
                            yEndOfInput);

    // calculate the desired position of the cursor
    int xCursorPos, yCursorPos;
    calculateScreenPosition(xEndOfPrompt,
                            yEndOfPrompt,
                            pi.promptScreenColumns,
                            calculateColumnPosition(buf32, pos),
                            xCursorPos,
                            yCursorPos);

#ifdef _WIN32
    // position at the start of the prompt, clear to end of previous input
    CONSOLE_SCREEN_BUFFER_INFO inf;
    GetConsoleScreenBufferInfo(console_out, &inf);
    inf.dwCursorPosition.X = 0;
    inf.dwCursorPosition.Y -= pi.promptCursorRowOffset /*- pi.promptExtraLines*/;
    SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
    DWORD count;
    FillConsoleOutputCharacterA(console_out,
                                ' ',
                                pi.promptPreviousLen + pi.promptPreviousInputLen,
                                inf.dwCursorPosition,
                                &count);
    pi.promptPreviousLen = pi.promptIndentation;
    pi.promptPreviousInputLen = len;

    // display the prompt
    if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
        return;

    // display the input line
    if (write32(1, buf32, len) == -1)
        return;

    // position the cursor
    GetConsoleScreenBufferInfo(console_out, &inf);
    inf.dwCursorPosition.X = xCursorPos;  // 0-based on Win32
    inf.dwCursorPosition.Y -= yEndOfInput - yCursorPos;
    SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
#else  // _WIN32
    char seq[64];
    int cursorRowMovement = pi.promptCursorRowOffset - pi.promptExtraLines;
    if (cursorRowMovement > 0) {  // move the cursor up as required
        snprintf(seq, sizeof seq, "\x1b[%dA", cursorRowMovement);
        if (write(1, seq, strlen(seq)) == -1)
            return;
    }
    // position at the start of the prompt, clear to end of screen
    snprintf(seq, sizeof seq, "\x1b[1G\x1b[J");  // 1-based on VT100
    if (write(1, seq, strlen(seq)) == -1)
        return;

    // display the prompt
    if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
        return;

    // display the input line
    if (write32(1, buf32, len) == -1)
        return;

    // we have to generate our own newline on line wrap
    if (xEndOfInput == 0 && yEndOfInput > 0)
        if (write(1, "\n", 1) == -1)
            return;

    // position the cursor
    cursorRowMovement = yEndOfInput - yCursorPos;
    if (cursorRowMovement > 0) {  // move the cursor up as required
        snprintf(seq, sizeof seq, "\x1b[%dA", cursorRowMovement);
        if (write(1, seq, strlen(seq)) == -1)
            return;
    }
    // position the cursor within the line
    snprintf(seq, sizeof seq, "\x1b[%dG", xCursorPos + 1);  // 1-based on VT100
    if (write(1, seq, strlen(seq)) == -1)
        return;
#endif

    pi.promptCursorRowOffset = pi.promptExtraLines + yCursorPos;  // remember row for next pass
}

Project: mongo
File: linenoise.cpp

static UChar32 escFailureRoutine(UChar32 c) {
    beep();
    return -1;
}

Project: mongo
File: linenoise.cpp

    void updateSearchPrompt(void) {
        const Utf32String* basePrompt =
            (direction > 0) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt;
        size_t promptStartLength = basePrompt->length();
        promptChars = promptStartLength + searchTextLen + endSearchBasePrompt.length();
        Utf32String tempUnicode(promptChars + 1);
        memcpy(tempUnicode.get(), basePrompt->get(), sizeof(UChar32) * promptStartLength);
        memcpy(&tempUnicode[promptStartLength], searchText.get(), sizeof(UChar32) * searchTextLen);
        size_t endIndex = promptStartLength + searchTextLen;
        memcpy(&tempUnicode[endIndex],
               endSearchBasePrompt.get(),
               sizeof(UChar32) * (endSearchBasePrompt.length() + 1));
        tempUnicode.initFromBuffer();
        promptText = tempUnicode;
    }

Project: mongo
File: linenoise.cpp

    void updateSearchText(const UChar32* textPtr) {
        Utf32String tempUnicode(textPtr);
        searchTextLen = tempUnicode.chars();
        searchText = tempUnicode;
        updateSearchPrompt();
    }

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket9Routine(UChar32 c) {
    return escFailureRoutine(c);
}

Project: mongo
File: linenoise.cpp

static UChar32 doDispatch(UChar32 c, CharacterDispatch& dispatchTable) {
    for (unsigned int i = 0; i < dispatchTable.len; ++i) {
        if (static_cast<unsigned char>(dispatchTable.chars[i]) == c) {
            return dispatchTable.dispatch[i](c);
        }
    }
    return dispatchTable.dispatch[dispatchTable.len](c);
}

Project: mongo
File: linenoise.cpp

static UChar32 pageUpKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | PAGE_UP_KEY;
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket0Routine(UChar32 c) {
    return escFailureRoutine(c);
}

Project: mongo
File: linenoise.cpp

static int getScreenRows(void) {
    int rows;
#ifdef _WIN32
    CONSOLE_SCREEN_BUFFER_INFO inf;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &inf);
    rows = 1 + inf.srWindow.Bottom - inf.srWindow.Top;
#else
    struct winsize ws;
    rows = (ioctl(1, TIOCGWINSZ, &ws) == -1) ? 24 : ws.ws_row;
#endif
    return (rows > 0) ? rows : 24;
}

Project: mongo
File: linenoise.cpp

int linenoiseHistoryAdd(const char* line) {
    if (historyMaxLen == 0) {
        return 0;
    }
    if (history == NULL) {
        history = reinterpret_cast<UChar8**>(malloc(sizeof(UChar8*) * historyMaxLen));
        if (history == NULL) {
            return 0;
        }
        memset(history, 0, (sizeof(char*) * historyMaxLen));
    }
    UChar8* linecopy = reinterpret_cast<UChar8*>(strdup(line));
    if (!linecopy) {
        return 0;
    }
    if (historyLen == historyMaxLen) {
        free(history[0]);
        memmove(history, history + 1, sizeof(char*) * (historyMaxLen - 1));
        --historyLen;
        if (--historyPreviousIndex < -1) {
            historyPreviousIndex = -2;
        }
    }

    // convert newlines in multi-line code to spaces before storing
    UChar8* p = linecopy;
    while (*p) {
        if (*p == '\n') {
            *p = ' ';
        }
        ++p;
    }
    history[historyLen] = linecopy;
    ++historyLen;
    return 1;
}

Project: mongo
File: linenoise.cpp

int linenoiseHistorySetMaxLen(int len) {
    if (len < 1) {
        return 0;
    }
    if (history) {
        int tocopy = historyLen;
        UChar8** newHistory = reinterpret_cast<UChar8**>(malloc(sizeof(UChar8*) * len));
        if (newHistory == NULL) {
            return 0;
        }
        if (len < tocopy) {
            tocopy = len;
        }
        memcpy(newHistory, history + historyMaxLen - tocopy, sizeof(UChar8*) * tocopy);
        free(history);
        history = newHistory;
    }
    historyMaxLen = len;
    if (historyLen > historyMaxLen) {
        historyLen = historyMaxLen;
    }
    return 1;
}

Project: mongo
File: linenoise.cpp

static UChar32 deleteKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | DELETE_KEY;
}  // key labeled Delete

Project: mongo
File: linenoise.cpp

int linenoiseHistoryLoad(const char* filename) {
    FILE* fp = fopen(filename, "rt");
    if (fp == NULL) {
        return -1;
    }

    char buf[LINENOISE_MAX_LINE];
    while (fgets(buf, LINENOISE_MAX_LINE, fp) != NULL) {
        char* p = strchr(buf, '\r');
        if (!p) {
            p = strchr(buf, '\n');
        }
        if (p) {
            *p = '\0';
        }
        if (p != buf) {
            linenoiseHistoryAdd(buf);
        }
    }
    fclose(fp);
    return 0;
}

Project: mongo
File: linenoise.cpp

static void linenoiseAtExit(void) {
    disableRawMode();
}

Project: mongo
File: linenoise.cpp

static UChar32 setMetaRoutine(UChar32 c) {
    thisKeyMetaCtrl = META;
    if (c == 0x1B) {  // another ESC, stay in ESC processing mode
        c = readUnicodeCharacter();
        if (c == 0)
            return 0;
        return doDispatch(c, escDispatch);
    }
    return doDispatch(c, initialDispatch);
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket6Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracket6Dispatch);
}

Project: mongo
File: linenoise.cpp

static UChar32 leftArrowKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | LEFT_ARROW_KEY;
}

Project: mongo
File: linenoise.cpp

    Utf32String* yankPop() {
        if (size == 0) {
            return 0;
        }
        ++index;
        if (index == size) {
            index = 0;
        }
        return &theRing[indexToSlot[index]];
    }

Project: mongo
File: linenoise.cpp

    Utf32String* yank() {
        return (size > 0) ? &theRing[indexToSlot[index]] : 0;
    }

Project: mongo
File: linenoise.cpp

static UChar32 homeKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | HOME_KEY;
}

Project: mongo
File: linenoise.cpp

static void freeCompletions(linenoiseCompletions* lc) {
    lc->completionStrings.clear();
}

Project: mongo
File: linenoise.cpp

    PromptInfo(const UChar8* textPtr, int columns) {
        promptExtraLines = 0;
        promptLastLinePosition = 0;
        promptPreviousLen = 0;
        promptScreenColumns = columns;
        Utf32String tempUnicode(textPtr);

        // strip control characters from the prompt -- we do allow newline
        UChar32* pIn = tempUnicode.get();
        UChar32* pOut = pIn;
        while (*pIn) {
            UChar32 c = *pIn;
            if ('\n' == c || !isControlChar(c)) {
                *pOut = c;
                ++pOut;
            }
            ++pIn;
        }
        *pOut = 0;
        promptChars = pOut - tempUnicode.get();
        promptText = tempUnicode;

        int x = 0;
        for (int i = 0; i < promptChars; ++i) {
            UChar32 c = promptText[i];
            if ('\n' == c) {
                x = 0;
                ++promptExtraLines;
                promptLastLinePosition = i + 1;
            } else {
                ++x;
                if (x >= promptScreenColumns) {
                    x = 0;
                    ++promptExtraLines;
                    promptLastLinePosition = i + 1;
                }
            }
        }
        promptIndentation = promptChars - promptLastLinePosition;
        promptCursorRowOffset = promptExtraLines;
    }

Project: mongo
File: linenoise.cpp

    KillRing() : size(0), index(0), lastAction(actionOther) {
        theRing.reserve(capacity);
    }

Project: mongo
File: linenoise.cpp

static void beep() {
    fprintf(stderr, "\x7");  // ctrl-G == bell/beep
    fflush(stderr);
}

Project: mongo
File: linenoise.cpp

static UChar32 normalKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | c;
}

Project: mongo
File: linenoise.cpp

static int cleanupCtrl(int c) {
    if (c & CTRL) {
        int d = c & 0x1FF;
        if (d >= 'a' && d <= 'z') {
            c = (c + ('a' - ctrlChar('A'))) & ~CTRL;
        }
        if (d >= 'A' && d <= 'Z') {
            c = (c + ('A' - ctrlChar('A'))) & ~CTRL;
        }
        if (d >= ctrlChar('A') && d <= ctrlChar('Z')) {
            c = c & ~CTRL;
        }
    }
    return c;
}

Project: mongo
File: linenoise.cpp

static UChar32 readUnicodeCharacter(void) {
    static UChar8 utf8String[5];
    static size_t utf8Count = 0;
    while (true) {
        UChar8 c;
        if (read(0, &c, 1) <= 0)
            return 0;
        if (c <= 0x7F) {  // short circuit ASCII
            utf8Count = 0;
            return c;
        } else if (utf8Count < sizeof(utf8String) - 1) {
            utf8String[utf8Count++] = c;
            utf8String[utf8Count] = 0;
            UChar32 unicodeChar[2];
            size_t ucharCount;
            int errorCode;
            copyString8to32(unicodeChar, utf8String, 2, ucharCount, errorCode);
            if (ucharCount && errorCode == 0) {
                utf8Count = 0;
                return unicodeChar[0];
            }
        } else {
            utf8Count = 0;  // this shouldn't happen: got four bytes but no UTF-8 character
        }
    }
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket4Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracket4Dispatch);
}

Project: mongo
File: linenoise.cpp

void linenoiseHistoryFree(void) {
    if (history) {
        for (int j = 0; j < historyLen; ++j)
            free(history[j]);
        historyLen = 0;
        free(history);
        history = 0;
    }
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket1Semicolon3Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    thisKeyMetaCtrl |= META;
    return doDispatch(c, escLeftBracket1Semicolon3or5Dispatch);
}

Project: mongo
File: linenoise.cpp

    InputBuffer(UChar32* buffer, char* widthArray, int bufferLen)
        : buf32(buffer), charWidths(widthArray), buflen(bufferLen - 1), len(0), pos(0) {
        buf32[0] = 0;
    }

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket5Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracket5Dispatch);
}

Project: mongo
File: linenoise.cpp

int InputBuffer::getInputLine(PromptBase& pi) {
    // The latest history entry is always our current buffer
    if (len > 0) {
        size_t bufferSize = sizeof(UChar32) * len + 1;
        unique_ptr<char[]> tempBuffer(new char[bufferSize]);
        copyString32to8(reinterpret_cast<UChar8*>(tempBuffer.get()), buf32, bufferSize);
        linenoiseHistoryAdd(tempBuffer.get());
    } else {
        linenoiseHistoryAdd("");
    }
    historyIndex = historyLen - 1;
    historyRecallMostRecent = false;

    // display the prompt
    if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
        return -1;

#ifndef _WIN32
    // we have to generate our own newline on line wrap on Linux
    if (pi.promptIndentation == 0 && pi.promptExtraLines > 0)
        if (write(1, "\n", 1) == -1)
            return -1;
#endif

    // the cursor starts out at the end of the prompt
    pi.promptCursorRowOffset = pi.promptExtraLines;

    // kill and yank start in "other" mode
    killRing.lastAction = KillRing::actionOther;

    // when history search returns control to us, we execute its terminating keystroke
    int terminatingKeystroke = -1;

    // if there is already text in the buffer, display it first
    if (len > 0) {
        refreshLine(pi);
    }

    // loop collecting characters, respond to line editing characters
    while (true) {
        int c;
        if (terminatingKeystroke == -1) {
            c = linenoiseReadChar();  // get a new keystroke
        } else {
            c = terminatingKeystroke;   // use the terminating keystroke from search
            terminatingKeystroke = -1;  // clear it once we've used it
        }
        c = cleanupCtrl(c);  // convert CTRL + <char> into normal ctrl

        if (c == 0) {
            return len;
        }

        if (c == -1) {
            refreshLine(pi);
            continue;
        }

        if (c == -2) {
            if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
                return -1;
            refreshLine(pi);
            continue;
        }

        // ctrl-I/tab, command completion, needs to be before switch statement
        if (c == ctrlChar('I') && completionCallback) {
            if (pos == 0)  // SERVER-4967 -- in earlier versions, you could paste previous output
                continue;  //  back into the shell ... this output may have leading tabs.
                           // This hack (i.e. what the old code did) prevents command completion
                           //  on an empty line but lets users paste text with leading tabs.

            killRing.lastAction = KillRing::actionOther;
            historyRecallMostRecent = false;

            // completeLine does the actual completion and replacement
            c = completeLine(pi);

            if (c < 0)  // return on error
                return len;

            if (c == 0)  // read next character when 0
                continue;

            // deliberate fall-through here, so we use the terminating character
        }

        switch (c) {
            case ctrlChar('A'):  // ctrl-A, move cursor to start of line
            case HOME_KEY:
                killRing.lastAction = KillRing::actionOther;
                pos = 0;
                refreshLine(pi);
                break;

            case ctrlChar('B'):  // ctrl-B, move cursor left by one character
            case LEFT_ARROW_KEY:
                killRing.lastAction = KillRing::actionOther;
                if (pos > 0) {
                    --pos;
                    refreshLine(pi);
                }
                break;

            case META + 'b':  // meta-B, move cursor left by one word
            case META + 'B':
            case CTRL + LEFT_ARROW_KEY:
            case META + LEFT_ARROW_KEY:  // Emacs allows Meta, bash & readline don't
                killRing.lastAction = KillRing::actionOther;
                if (pos > 0) {
                    while (pos > 0 && !isCharacterAlphanumeric(buf32[pos - 1])) {
                        --pos;
                    }
                    while (pos > 0 && isCharacterAlphanumeric(buf32[pos - 1])) {
                        --pos;
                    }
                    refreshLine(pi);
                }
                break;

            case ctrlChar('C'):  // ctrl-C, abort this line
                killRing.lastAction = KillRing::actionOther;
                historyRecallMostRecent = false;
                errno = EAGAIN;
                --historyLen;
                free(history[historyLen]);
                // we need one last refresh with the cursor at the end of the line
                // so we don't display the next prompt over the previous input line
                pos = len;  // pass len as pos for EOL
                refreshLine(pi);
                if (write(1, "^C", 2) == -1)
                    return -1;  // Display the ^C we got
                return -1;

            case META + 'c':  // meta-C, give word initial Cap
            case META + 'C':
                killRing.lastAction = KillRing::actionOther;
                historyRecallMostRecent = false;
                if (pos < len) {
                    while (pos < len && !isCharacterAlphanumeric(buf32[pos])) {
                        ++pos;
                    }
                    if (pos < len && isCharacterAlphanumeric(buf32[pos])) {
                        if (buf32[pos] >= 'a' && buf32[pos] <= 'z') {
                            buf32[pos] += 'A' - 'a';
                        }
                        ++pos;
                    }
                    while (pos < len && isCharacterAlphanumeric(buf32[pos])) {
                        if (buf32[pos] >= 'A' && buf32[pos] <= 'Z') {
                            buf32[pos] += 'a' - 'A';
                        }
                        ++pos;
                    }
                    refreshLine(pi);
                }
                break;

            // ctrl-D, delete the character under the cursor
            // on an empty line, exit the shell
            case ctrlChar('D'):
                killRing.lastAction = KillRing::actionOther;
                if (len > 0 && pos < len) {
                    historyRecallMostRecent = false;
                    memmove(buf32 + pos, buf32 + pos + 1, sizeof(UChar32) * (len - pos));
                    --len;
                    refreshLine(pi);
                } else if (len == 0) {
                    --historyLen;
                    free(history[historyLen]);
                    return -1;
                }
                break;

            case META + 'd':  // meta-D, kill word to right of cursor
            case META + 'D':
                if (pos < len) {
                    historyRecallMostRecent = false;
                    int endingPos = pos;
                    while (endingPos < len && !isCharacterAlphanumeric(buf32[endingPos])) {
                        ++endingPos;
                    }
                    while (endingPos < len && isCharacterAlphanumeric(buf32[endingPos])) {
                        ++endingPos;
                    }
                    killRing.kill(&buf32[pos], endingPos - pos, true);
                    memmove(
                        buf32 + pos, buf32 + endingPos, sizeof(UChar32) * (len - endingPos + 1));
                    len -= endingPos - pos;
                    refreshLine(pi);
                }
                killRing.lastAction = KillRing::actionKill;
                break;

            case ctrlChar('E'):  // ctrl-E, move cursor to end of line
            case END_KEY:
                killRing.lastAction = KillRing::actionOther;
                pos = len;
                refreshLine(pi);
                break;

            case ctrlChar('F'):  // ctrl-F, move cursor right by one character
            case RIGHT_ARROW_KEY:
                killRing.lastAction = KillRing::actionOther;
                if (pos < len) {
                    ++pos;
                    refreshLine(pi);
                }
                break;

            case META + 'f':  // meta-F, move cursor right by one word
            case META + 'F':
            case CTRL + RIGHT_ARROW_KEY:
            case META + RIGHT_ARROW_KEY:  // Emacs allows Meta, bash & readline don't
                killRing.lastAction = KillRing::actionOther;
                if (pos < len) {
                    while (pos < len && !isCharacterAlphanumeric(buf32[pos])) {
                        ++pos;
                    }
                    while (pos < len && isCharacterAlphanumeric(buf32[pos])) {
                        ++pos;
                    }
                    refreshLine(pi);
                }
                break;

            case ctrlChar('H'):  // backspace/ctrl-H, delete char to left of cursor
                killRing.lastAction = KillRing::actionOther;
                if (pos > 0) {
                    historyRecallMostRecent = false;
                    memmove(buf32 + pos - 1, buf32 + pos, sizeof(UChar32) * (1 + len - pos));
                    --pos;
                    --len;
                    refreshLine(pi);
                }
                break;

            // meta-Backspace, kill word to left of cursor
            case META + ctrlChar('H'):
                if (pos > 0) {
                    historyRecallMostRecent = false;
                    int startingPos = pos;
                    while (pos > 0 && !isCharacterAlphanumeric(buf32[pos - 1])) {
                        --pos;
                    }
                    while (pos > 0 && isCharacterAlphanumeric(buf32[pos - 1])) {
                        --pos;
                    }
                    killRing.kill(&buf32[pos], startingPos - pos, false);
                    memmove(buf32 + pos,
                            buf32 + startingPos,
                            sizeof(UChar32) * (len - startingPos + 1));
                    len -= startingPos - pos;
                    refreshLine(pi);
                }
                killRing.lastAction = KillRing::actionKill;
                break;

            case ctrlChar('J'):  // ctrl-J/linefeed/newline, accept line
            case ctrlChar('M'):  // ctrl-M/return/enter
                killRing.lastAction = KillRing::actionOther;
                // we need one last refresh with the cursor at the end of the line
                // so we don't display the next prompt over the previous input line
                pos = len;  // pass len as pos for EOL
                refreshLine(pi);
                historyPreviousIndex = historyRecallMostRecent ? historyIndex : -2;
                --historyLen;
                free(history[historyLen]);
                return len;

            case ctrlChar('K'):  // ctrl-K, kill from cursor to end of line
                killRing.kill(&buf32[pos], len - pos, true);
                buf32[pos] = '\0';
                len = pos;
                refreshLine(pi);
                killRing.lastAction = KillRing::actionKill;
                historyRecallMostRecent = false;
                break;

            case ctrlChar('L'):  // ctrl-L, clear screen and redisplay line
                clearScreen(pi);
                break;

            case META + 'l':  // meta-L, lowercase word
            case META + 'L':
                killRing.lastAction = KillRing::actionOther;
                if (pos < len) {
                    historyRecallMostRecent = false;
                    while (pos < len && !isCharacterAlphanumeric(buf32[pos])) {
                        ++pos;
                    }
                    while (pos < len && isCharacterAlphanumeric(buf32[pos])) {
                        if (buf32[pos] >= 'A' && buf32[pos] <= 'Z') {
                            buf32[pos] += 'a' - 'A';
                        }
                        ++pos;
                    }
                    refreshLine(pi);
                }
                break;

            case ctrlChar('N'):  // ctrl-N, recall next line in history
            case ctrlChar('P'):  // ctrl-P, recall previous line in history
            case DOWN_ARROW_KEY:
            case UP_ARROW_KEY:
                killRing.lastAction = KillRing::actionOther;
                // if not already recalling, add the current line to the history list so we don't
                // have to special case it
                if (historyIndex == historyLen - 1) {
                    free(history[historyLen - 1]);
                    size_t tempBufferSize = sizeof(UChar32) * len + 1;
                    unique_ptr<UChar8[]> tempBuffer(new UChar8[tempBufferSize]);
                    copyString32to8(tempBuffer.get(), buf32, tempBufferSize);
                    history[historyLen - 1] = reinterpret_cast<UChar8*>(
                        strdup(reinterpret_cast<const char*>(tempBuffer.get())));
                }
                if (historyLen > 1) {
                    if (c == UP_ARROW_KEY) {
                        c = ctrlChar('P');
                    }
                    if (historyPreviousIndex != -2 && c != ctrlChar('P')) {
                        historyIndex = 1 + historyPreviousIndex;  // emulate Windows down-arrow
                    } else {
                        historyIndex += (c == ctrlChar('P')) ? -1 : 1;
                    }
                    historyPreviousIndex = -2;
                    if (historyIndex < 0) {
                        historyIndex = 0;
                        break;
                    } else if (historyIndex >= historyLen) {
                        historyIndex = historyLen - 1;
                        break;
                    }
                    historyRecallMostRecent = true;
                    size_t ucharCount;
                    int errorCode;
                    copyString8to32(buf32, history[historyIndex], buflen, ucharCount, errorCode);
                    len = pos = ucharCount;
                    refreshLine(pi);
                }
                break;

            case ctrlChar('R'):  // ctrl-R, reverse history search
            case ctrlChar('S'):  // ctrl-S, forward history search
                terminatingKeystroke = incrementalHistorySearch(pi, c);
                break;

            case ctrlChar('T'):  // ctrl-T, transpose characters
                killRing.lastAction = KillRing::actionOther;
                if (pos > 0 && len > 1) {
                    historyRecallMostRecent = false;
                    size_t leftCharPos = (pos == len) ? pos - 2 : pos - 1;
                    char aux = buf32[leftCharPos];
                    buf32[leftCharPos] = buf32[leftCharPos + 1];
                    buf32[leftCharPos + 1] = aux;
                    if (pos != len)
                        ++pos;
                    refreshLine(pi);
                }
                break;

            case ctrlChar('U'):  // ctrl-U, kill all characters to the left of the cursor
                if (pos > 0) {
                    historyRecallMostRecent = false;
                    killRing.kill(&buf32[0], pos, false);
                    len -= pos;
                    memmove(buf32, buf32 + pos, sizeof(UChar32) * (len + 1));
                    pos = 0;
                    refreshLine(pi);
                }
                killRing.lastAction = KillRing::actionKill;
                break;

            case META + 'u':  // meta-U, uppercase word
            case META + 'U':
                killRing.lastAction = KillRing::actionOther;
                if (pos < len) {
                    historyRecallMostRecent = false;
                    while (pos < len && !isCharacterAlphanumeric(buf32[pos])) {
                        ++pos;
                    }
                    while (pos < len && isCharacterAlphanumeric(buf32[pos])) {
                        if (buf32[pos] >= 'a' && buf32[pos] <= 'z') {
                            buf32[pos] += 'A' - 'a';
                        }
                        ++pos;
                    }
                    refreshLine(pi);
                }
                break;

            // ctrl-W, kill to whitespace (not word) to left of cursor
            case ctrlChar('W'):
                if (pos > 0) {
                    historyRecallMostRecent = false;
                    int startingPos = pos;
                    while (pos > 0 && buf32[pos - 1] == ' ') {
                        --pos;
                    }
                    while (pos > 0 && buf32[pos - 1] != ' ') {
                        --pos;
                    }
                    killRing.kill(&buf32[pos], startingPos - pos, false);
                    memmove(buf32 + pos,
                            buf32 + startingPos,
                            sizeof(UChar32) * (len - startingPos + 1));
                    len -= startingPos - pos;
                    refreshLine(pi);
                }
                killRing.lastAction = KillRing::actionKill;
                break;

            case ctrlChar('Y'):  // ctrl-Y, yank killed text
                historyRecallMostRecent = false;
                {
                    Utf32String* restoredText = killRing.yank();
                    if (restoredText) {
                        bool truncated = false;
                        size_t ucharCount = restoredText->length();
                        if (ucharCount > static_cast<size_t>(buflen - len)) {
                            ucharCount = buflen - len;
                            truncated = true;
                        }
                        memmove(buf32 + pos + ucharCount,
                                buf32 + pos,
                                sizeof(UChar32) * (len - pos + 1));
                        memmove(buf32 + pos, restoredText->get(), sizeof(UChar32) * ucharCount);
                        pos += ucharCount;
                        len += ucharCount;
                        refreshLine(pi);
                        killRing.lastAction = KillRing::actionYank;
                        killRing.lastYankSize = ucharCount;
                        if (truncated) {
                            beep();
                        }
                    } else {
                        beep();
                    }
                }
                break;

            case META + 'y':  // meta-Y, "yank-pop", rotate popped text
            case META + 'Y':
                if (killRing.lastAction == KillRing::actionYank) {
                    historyRecallMostRecent = false;
                    Utf32String* restoredText = killRing.yankPop();
                    if (restoredText) {
                        bool truncated = false;
                        size_t ucharCount = restoredText->length();
                        if (ucharCount >
                            static_cast<size_t>(killRing.lastYankSize + buflen - len)) {
                            ucharCount = killRing.lastYankSize + buflen - len;
                            truncated = true;
                        }
                        if (ucharCount > killRing.lastYankSize) {
                            memmove(buf32 + pos + ucharCount - killRing.lastYankSize,
                                    buf32 + pos,
                                    sizeof(UChar32) * (len - pos + 1));
                            memmove(buf32 + pos - killRing.lastYankSize,
                                    restoredText->get(),
                                    sizeof(UChar32) * ucharCount);
                        } else {
                            memmove(buf32 + pos - killRing.lastYankSize,
                                    restoredText->get(),
                                    sizeof(UChar32) * ucharCount);
                            memmove(buf32 + pos + ucharCount - killRing.lastYankSize,
                                    buf32 + pos,
                                    sizeof(UChar32) * (len - pos + 1));
                        }
                        pos += ucharCount - killRing.lastYankSize;
                        len += ucharCount - killRing.lastYankSize;
                        killRing.lastYankSize = ucharCount;
                        refreshLine(pi);
                        if (truncated) {
                            beep();
                        }
                        break;
                    }
                }
                beep();
                break;

#ifndef _WIN32
            case ctrlChar('Z'):    // ctrl-Z, job control
                disableRawMode();  // Returning to Linux (whatever) shell, leave raw mode
                raise(SIGSTOP);    // Break out in mid-line
                enableRawMode();   // Back from Linux shell, re-enter raw mode
                if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
                    break;        // Redraw prompt
                refreshLine(pi);  // Refresh the line
                break;
#endif

            // DEL, delete the character under the cursor
            case 127:
            case DELETE_KEY:
                killRing.lastAction = KillRing::actionOther;
                if (len > 0 && pos < len) {
                    historyRecallMostRecent = false;
                    memmove(buf32 + pos, buf32 + pos + 1, sizeof(UChar32) * (len - pos));
                    --len;
                    refreshLine(pi);
                }
                break;

            case META + '<':     // meta-<, beginning of history
            case PAGE_UP_KEY:    // Page Up, beginning of history
            case META + '>':     // meta->, end of history
            case PAGE_DOWN_KEY:  // Page Down, end of history
                killRing.lastAction = KillRing::actionOther;
                // if not already recalling, add the current line to the history list so we don't
                // have to special case it
                if (historyIndex == historyLen - 1) {
                    free(history[historyLen - 1]);
                    size_t tempBufferSize = sizeof(UChar32) * len + 1;
                    unique_ptr<UChar8[]> tempBuffer(new UChar8[tempBufferSize]);
                    copyString32to8(tempBuffer.get(), buf32, tempBufferSize);
                    history[historyLen - 1] = reinterpret_cast<UChar8*>(
                        strdup(reinterpret_cast<const char*>(tempBuffer.get())));
                }
                if (historyLen > 1) {
                    historyIndex = (c == META + '<' || c == PAGE_UP_KEY) ? 0 : historyLen - 1;
                    historyPreviousIndex = -2;
                    historyRecallMostRecent = true;
                    size_t ucharCount;
                    int errorCode;
                    copyString8to32(buf32, history[historyIndex], buflen, ucharCount, errorCode);
                    len = pos = ucharCount;
                    refreshLine(pi);
                }
                break;

            // not one of our special characters, maybe insert it in the buffer
            default:
                killRing.lastAction = KillRing::actionOther;
                historyRecallMostRecent = false;
                if (c & (META | CTRL)) {  // beep on unknown Ctrl and/or Meta keys
                    beep();
                    break;
                }
                if (len < buflen) {
                    if (isControlChar(c)) {  // don't insert control characters
                        beep();
                        break;
                    }
                    if (len == pos) {  // at end of buffer
                        buf32[pos] = c;
                        ++pos;
                        ++len;
                        buf32[len] = '\0';
                        int inputLen = calculateColumnPosition(buf32, len);
                        if (pi.promptIndentation + inputLen < pi.promptScreenColumns) {
                            if (inputLen > pi.promptPreviousInputLen)
                                pi.promptPreviousInputLen = inputLen;
                            /* Avoid a full update of the line in the
                             * trivial case. */
                            if (write32(1, reinterpret_cast<UChar32*>(&c), 1) == -1)
                                return -1;
                        } else {
                            refreshLine(pi);
                        }
                    } else {  // not at end of buffer, have to move characters to our right
                        memmove(buf32 + pos + 1, buf32 + pos, sizeof(UChar32) * (len - pos));
                        buf32[pos] = c;
                        ++len;
                        ++pos;
                        buf32[len] = '\0';
                        refreshLine(pi);
                    }
                } else {
                    beep();  // buffer is full, beep on new characters
                }
                break;
        }
    }
    return len;
}

Project: mongo
File: linenoise.cpp

static bool isCharacterAlphanumeric(UChar32 testChar) {
    return iswalnum(testChar);
}

Project: mongo
File: linenoise.cpp

static UChar32 deleteCharRoutine(UChar32 c) {
    return thisKeyMetaCtrl | ctrlChar('H');
}  // key labeled Backspace

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket7Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracket7Dispatch);
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket1Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracket1Dispatch);
}

Project: mongo
File: linenoise.cpp

int InputBuffer::incrementalHistorySearch(PromptBase& pi, int startChar) {
    size_t bufferSize;
    size_t ucharCount;
    int errorCode;

    // if not already recalling, add the current line to the history list so we don't have to
    // special case it
    if (historyIndex == historyLen - 1) {
        free(history[historyLen - 1]);
        bufferSize = sizeof(UChar32) * len + 1;
        unique_ptr<UChar8[]> tempBuffer(new UChar8[bufferSize]);
        copyString32to8(tempBuffer.get(), buf32, bufferSize);
        history[historyLen - 1] =
            reinterpret_cast<UChar8*>(strdup(reinterpret_cast<const char*>(tempBuffer.get())));
    }
    int historyLineLength = len;
    int historyLinePosition = pos;
    UChar32 emptyBuffer[1];
    char emptyWidths[1];
    InputBuffer empty(emptyBuffer, emptyWidths, 1);
    empty.refreshLine(pi);  // erase the old input first
    DynamicPrompt dp(pi, (startChar == ctrlChar('R')) ? -1 : 1);

    dp.promptPreviousLen = pi.promptPreviousLen;
    dp.promptPreviousInputLen = pi.promptPreviousInputLen;
    dynamicRefresh(
        dp, buf32, historyLineLength, historyLinePosition);  // draw user's text with our prompt

    // loop until we get an exit character
    int c;
    bool keepLooping = true;
    bool useSearchedLine = true;
    bool searchAgain = false;
    UChar32* activeHistoryLine = 0;
    while (keepLooping) {
        c = linenoiseReadChar();
        c = cleanupCtrl(c);  // convert CTRL + <char> into normal ctrl

        switch (c) {
            // these characters keep the selected text but do not execute it
            case ctrlChar('A'):  // ctrl-A, move cursor to start of line
            case HOME_KEY:
            case ctrlChar('B'):  // ctrl-B, move cursor left by one character
            case LEFT_ARROW_KEY:
            case META + 'b':  // meta-B, move cursor left by one word
            case META + 'B':
            case CTRL + LEFT_ARROW_KEY:
            case META + LEFT_ARROW_KEY:  // Emacs allows Meta, bash & readline don't
            case ctrlChar('D'):
            case META + 'd':  // meta-D, kill word to right of cursor
            case META + 'D':
            case ctrlChar('E'):  // ctrl-E, move cursor to end of line
            case END_KEY:
            case ctrlChar('F'):  // ctrl-F, move cursor right by one character
            case RIGHT_ARROW_KEY:
            case META + 'f':  // meta-F, move cursor right by one word
            case META + 'F':
            case CTRL + RIGHT_ARROW_KEY:
            case META + RIGHT_ARROW_KEY:  // Emacs allows Meta, bash & readline don't
            case META + ctrlChar('H'):
            case ctrlChar('J'):
            case ctrlChar('K'):  // ctrl-K, kill from cursor to end of line
            case ctrlChar('M'):
            case ctrlChar('N'):  // ctrl-N, recall next line in history
            case ctrlChar('P'):  // ctrl-P, recall previous line in history
            case DOWN_ARROW_KEY:
            case UP_ARROW_KEY:
            case ctrlChar('T'):  // ctrl-T, transpose characters
            case ctrlChar('U'):  // ctrl-U, kill all characters to the left of the cursor
            case ctrlChar('W'):
            case META + 'y':  // meta-Y, "yank-pop", rotate popped text
            case META + 'Y':
            case 127:
            case DELETE_KEY:
            case META + '<':  // start of history
            case PAGE_UP_KEY:
            case META + '>':  // end of history
            case PAGE_DOWN_KEY:
                keepLooping = false;
                break;

            // these characters revert the input line to its previous state
            case ctrlChar('C'):  // ctrl-C, abort this line
            case ctrlChar('G'):
            case ctrlChar('L'):  // ctrl-L, clear screen and redisplay line
                keepLooping = false;
                useSearchedLine = false;
                if (c != ctrlChar('L')) {
                    c = -1;  // ctrl-C and ctrl-G just abort the search and do nothing else
                }
                break;

            // these characters stay in search mode and update the display
            case ctrlChar('S'):
            case ctrlChar('R'):
                if (dp.searchTextLen == 0) {  // if no current search text, recall previous text
                    if (previousSearchText.length()) {
                        dp.updateSearchText(previousSearchText.get());
                    }
                }
                if ((dp.direction == 1 && c == ctrlChar('R')) ||
                    (dp.direction == -1 && c == ctrlChar('S'))) {
                    dp.direction = 0 - dp.direction;  // reverse direction
                    dp.updateSearchPrompt();          // change the prompt
                } else {
                    searchAgain = true;  // same direction, search again
                }
                break;

// job control is its own thing
#ifndef _WIN32
            case ctrlChar('Z'):    // ctrl-Z, job control
                disableRawMode();  // Returning to Linux (whatever) shell, leave raw mode
                raise(SIGSTOP);    // Break out in mid-line
                enableRawMode();   // Back from Linux shell, re-enter raw mode
                {
                    bufferSize = historyLineLength + 1;
                    unique_ptr<UChar32[]> tempUnicode(new UChar32[bufferSize]);
                    copyString8to32(tempUnicode.get(),
                                    history[historyIndex],
                                    bufferSize,
                                    ucharCount,
                                    errorCode);
                    dynamicRefresh(dp, tempUnicode.get(), historyLineLength, historyLinePosition);
                }
                continue;
                break;
#endif

            // these characters update the search string, and hence the selected input line
            case ctrlChar('H'):  // backspace/ctrl-H, delete char to left of cursor
                if (dp.searchTextLen > 0) {
                    unique_ptr<UChar32[]> tempUnicode(new UChar32[dp.searchTextLen]);
                    --dp.searchTextLen;
                    dp.searchText[dp.searchTextLen] = 0;
                    copyString32(tempUnicode.get(), dp.searchText.get(), dp.searchTextLen + 1);
                    dp.updateSearchText(tempUnicode.get());
                } else {
                    beep();
                }
                break;

            case ctrlChar('Y'):  // ctrl-Y, yank killed text
                break;

            default:
                if (!isControlChar(c) && c <= 0x0010FFFF) {  // not an action character
                    unique_ptr<UChar32[]> tempUnicode(new UChar32[dp.searchTextLen + 2]);
                    copyString32(tempUnicode.get(), dp.searchText.get(), dp.searchTextLen + 2);
                    tempUnicode[dp.searchTextLen] = c;
                    tempUnicode[dp.searchTextLen + 1] = 0;
                    dp.updateSearchText(tempUnicode.get());
                } else {
                    beep();
                }
        }  // switch

        // if we are staying in search mode, search now
        if (keepLooping) {
            bufferSize = historyLineLength + 1;
            activeHistoryLine = new UChar32[bufferSize];
            copyString8to32(
                activeHistoryLine, history[historyIndex], bufferSize, ucharCount, errorCode);
            if (dp.searchTextLen > 0) {
                bool found = false;
                int historySearchIndex = historyIndex;
                int lineLength = ucharCount;
                int lineSearchPos = historyLinePosition;
                if (searchAgain) {
                    lineSearchPos += dp.direction;
                }
                searchAgain = false;
                while (true) {
                    while ((dp.direction > 0) ? (lineSearchPos < lineLength)
                                              : (lineSearchPos >= 0)) {
                        if (strncmp32(dp.searchText.get(),
                                      &activeHistoryLine[lineSearchPos],
                                      dp.searchTextLen) == 0) {
                            found = true;
                            break;
                        }
                        lineSearchPos += dp.direction;
                    }
                    if (found) {
                        historyIndex = historySearchIndex;
                        historyLineLength = lineLength;
                        historyLinePosition = lineSearchPos;
                        break;
                    } else if ((dp.direction > 0) ? (historySearchIndex < historyLen - 1)
                                                  : (historySearchIndex > 0)) {
                        historySearchIndex += dp.direction;
                        bufferSize =
                            strlen(reinterpret_cast<char*>(history[historySearchIndex])) + 1;
                        delete[] activeHistoryLine;
                        activeHistoryLine = new UChar32[bufferSize];
                        copyString8to32(activeHistoryLine,
                                        history[historySearchIndex],
                                        bufferSize,
                                        ucharCount,
                                        errorCode);
                        lineLength = ucharCount;
                        lineSearchPos = (dp.direction > 0) ? 0 : (lineLength - dp.searchTextLen);
                    } else {
                        beep();
                        break;
                    }
                };  // while
            }
            if (activeHistoryLine) {
                delete[] activeHistoryLine;
            }
            bufferSize = historyLineLength + 1;
            activeHistoryLine = new UChar32[bufferSize];
            copyString8to32(
                activeHistoryLine, history[historyIndex], bufferSize, ucharCount, errorCode);
            dynamicRefresh(dp,
                           activeHistoryLine,
                           historyLineLength,
                           historyLinePosition);  // draw user's text with our prompt
        }
    }  // while

    // leaving history search, restore previous prompt, maybe make searched line current
    PromptBase pb;
    pb.promptChars = pi.promptIndentation;
    Utf32String tempUnicode(pb.promptChars + 1);
    copyString32(tempUnicode.get(), &pi.promptText[pi.promptLastLinePosition], pb.promptChars + 1);
    tempUnicode.initFromBuffer();
    pb.promptText = tempUnicode;
    pb.promptExtraLines = 0;
    pb.promptIndentation = pi.promptIndentation;
    pb.promptLastLinePosition = 0;
    pb.promptPreviousInputLen = historyLineLength;
    pb.promptCursorRowOffset = dp.promptCursorRowOffset;
    pb.promptScreenColumns = pi.promptScreenColumns;
    pb.promptPreviousLen = dp.promptChars;
    if (useSearchedLine && activeHistoryLine) {
        historyRecallMostRecent = true;
        copyString32(buf32, activeHistoryLine, buflen + 1);
        len = historyLineLength;
        pos = historyLinePosition;
    }
    if (activeHistoryLine) {
        delete[] activeHistoryLine;
    }
    dynamicRefresh(pb, buf32, len, pos);  // redraw the original prompt with current input
    pi.promptPreviousInputLen = len;
    pi.promptCursorRowOffset = pi.promptExtraLines + pb.promptCursorRowOffset;
    previousSearchText = dp.searchText;  // save search text for possible reuse on ctrl-R ctrl-R
    return c;                            // pass a character or -1 back to main loop
}

Project: mongo
File: linenoise.cpp

void InputBuffer::clearScreen(PromptBase& pi) {
    linenoiseClearScreen();
    if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
        return;
#ifndef _WIN32
    // we have to generate our own newline on line wrap on Linux
    if (pi.promptIndentation == 0 && pi.promptExtraLines > 0)
        if (write(1, "\n", 1) == -1)
            return;
#endif
    pi.promptCursorRowOffset = pi.promptExtraLines;
    refreshLine(pi);
}

Project: mongo
File: linenoise.cpp

static void setDisplayAttribute(bool enhancedDisplay) {
#ifdef _WIN32
    if (enhancedDisplay) {
        CONSOLE_SCREEN_BUFFER_INFO inf;
        GetConsoleScreenBufferInfo(console_out, &inf);
        oldDisplayAttribute = inf.wAttributes;
        BYTE oldLowByte = oldDisplayAttribute & 0xFF;
        BYTE newLowByte;
        switch (oldLowByte) {
            case 0x07:
                // newLowByte = FOREGROUND_BLUE | FOREGROUND_INTENSITY;  // too dim
                // newLowByte = FOREGROUND_BLUE;                         // even dimmer
                newLowByte =
                    FOREGROUND_BLUE | FOREGROUND_GREEN;  // most similar to xterm appearance
                break;
            case 0x70:
                newLowByte = BACKGROUND_BLUE | BACKGROUND_INTENSITY;
                break;
            default:
                newLowByte = oldLowByte ^ 0xFF;  // default to inverse video
                break;
        }
        inf.wAttributes = (inf.wAttributes & 0xFF00) | newLowByte;
        SetConsoleTextAttribute(console_out, inf.wAttributes);
    } else {
        SetConsoleTextAttribute(console_out, oldDisplayAttribute);
    }
#else
    if (enhancedDisplay) {
        if (write(1, "\x1b[1;34m", 7) == -1)
            return; /* bright blue (visible with both B&W bg) */
    } else {
        if (write(1, "\x1b[0m", 4) == -1)
            return; /* reset */
    }
#endif
}

Project: mongo
File: linenoise.cpp

static void disableRawMode(void) {
#ifdef _WIN32
    SetConsoleMode(console_in, oldMode);
    console_in = 0;
    console_out = 0;
#else
    if (rawmode && tcsetattr(0, TCSADRAIN, &orig_termios) != -1)
        rawmode = 0;
#endif
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket3Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracket3Dispatch);
}

Project: mongo
File: linenoise.cpp

static UChar32 upArrowKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | UP_ARROW_KEY;
}

Project: mongo
File: linenoise.cpp

void linenoiseSetCompletionCallback(linenoiseCompletionCallback* fn) {
    completionCallback = fn;
}

Project: mongo
File: linenoise.cpp

static bool isControlChar(UChar32 testChar) {
    return (testChar < ' ') ||                   // C0 controls
        (testChar >= 0x7F && testChar <= 0x9F);  // DEL and C1 controls
}

Project: mongo
File: linenoise.cpp

    void kill(const UChar32* text, int textLen, bool forward) {
        if (textLen == 0) {
            return;
        }
        Utf32String killedText(text, textLen);
        if (lastAction == actionKill && size > 0) {
            int slot = indexToSlot[0];
            int currentLen = theRing[slot].length();
            int resultLen = currentLen + textLen;
            Utf32String temp(resultLen + 1);
            if (forward) {
                memcpy(temp.get(), theRing[slot].get(), currentLen * sizeof(UChar32));
                memcpy(&temp[currentLen], killedText.get(), textLen * sizeof(UChar32));
            } else {
                memcpy(temp.get(), killedText.get(), textLen * sizeof(UChar32));
                memcpy(&temp[textLen], theRing[slot].get(), currentLen * sizeof(UChar32));
            }
            temp[resultLen] = 0;
            temp.initFromBuffer();
            theRing[slot] = temp;
        } else {
            if (size < capacity) {
                if (size > 0) {
                    memmove(&indexToSlot[1], &indexToSlot[0], size);
                }
                indexToSlot[0] = size;
                size++;
                theRing.push_back(killedText);
            } else {
                int slot = indexToSlot[capacity - 1];
                theRing[slot] = killedText;
                memmove(&indexToSlot[1], &indexToSlot[0], capacity - 1);
                indexToSlot[0] = slot;
            }
            index = 0;
        }
    }

Project: mongo
File: linenoise.cpp

void linenoiseAddCompletion(linenoiseCompletions* lc, const char* str) {
    lc->completionStrings.push_back(Utf32String(reinterpret_cast<const UChar8*>(str)));
}

Project: mongo
File: linenoise.cpp

static bool isUnsupportedTerm(void) {
    char* term = getenv("TERM");
    if (term == NULL)
        return false;
    for (int j = 0; unsupported_term[j]; ++j)
        if (!strcasecmp(term, unsupported_term[j])) {
            return true;
        }
    return false;
}

Project: mongo
File: linenoise.cpp

static UChar32 ctrlLeftArrowKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | CTRL | LEFT_ARROW_KEY;
}

Project: mongo
File: linenoise.cpp

static UChar32 escRoutine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escDispatch);
}

Project: mongo
File: linenoise.cpp

void linenoisePreloadBuffer(const char* preloadText) {
    if (!preloadText) {
        return;
    }
    int bufferSize = strlen(preloadText) + 1;
    unique_ptr<char[]> tempBuffer(new char[bufferSize]);
    strncpy(&tempBuffer[0], preloadText, bufferSize);

    // remove characters that won't display correctly
    char* pIn = &tempBuffer[0];
    char* pOut = pIn;
    bool controlsStripped = false;
    bool whitespaceSeen = false;
    while (*pIn) {
        unsigned char c = *pIn++;  // we need unsigned so chars 0x80 and above are allowed
        if ('\r' == c) {           // silently skip CR
            continue;
        }
        if ('\n' == c || '\t' == c) {  // note newline or tab
            whitespaceSeen = true;
            continue;
        }
        if (isControlChar(c)) {  // remove other control characters, flag for message
            controlsStripped = true;
            *pOut++ = ' ';
            continue;
        }
        if (whitespaceSeen) {  // convert whitespace to a single space
            *pOut++ = ' ';
            whitespaceSeen = false;
        }
        *pOut++ = c;
    }
    *pOut = 0;
    int processedLength = pOut - tempBuffer.get();
    bool lineTruncated = false;
    if (processedLength > (LINENOISE_MAX_LINE - 1)) {
        lineTruncated = true;
        tempBuffer[LINENOISE_MAX_LINE - 1] = 0;
    }
    preloadedBufferContents = tempBuffer.get();
    if (controlsStripped) {
        preloadErrorMessage += " [Edited line: control characters were converted to spaces]\n";
    }
    if (lineTruncated) {
        preloadErrorMessage += " [Edited line: the line length was reduced from ";
        char buf[128];
        snprintf(buf, sizeof(buf), "%d to %d]\n", processedLength, (LINENOISE_MAX_LINE - 1));
        preloadErrorMessage += buf;
    }
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket2Routine(UChar32 c) {
    return escFailureRoutine(c);  // Insert key, unused
}

Project: mongo
File: linenoise.cpp

static UChar32 escORoutine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escODispatch);
}

Project: mongo
File: linenoise.cpp

void InputBuffer::refreshLine(PromptBase& pi) {
    // check for a matching brace/bracket/paren, remember its position if found
    int highlight = -1;
    if (pos < len) {
        /* this scans for a brace matching buf32[pos] to highlight */
        int scanDirection = 0;
        if (strchr("}])", buf32[pos]))
            scanDirection = -1; /* backwards */
        else if (strchr("{[(", buf32[pos]))
            scanDirection = 1; /* forwards */

        if (scanDirection) {
            int unmatched = scanDirection;
            for (int i = pos + scanDirection; i >= 0 && i < len; i += scanDirection) {
                /* TODO: the right thing when inside a string */
                if (strchr("}])", buf32[i]))
                    --unmatched;
                else if (strchr("{[(", buf32[i]))
                    ++unmatched;

                if (unmatched == 0) {
                    highlight = i;
                    break;
                }
            }
        }
    }

    // calculate the position of the end of the input line
    int xEndOfInput, yEndOfInput;
    calculateScreenPosition(pi.promptIndentation,
                            0,
                            pi.promptScreenColumns,
                            calculateColumnPosition(buf32, len),
                            xEndOfInput,
                            yEndOfInput);

    // calculate the desired position of the cursor
    int xCursorPos, yCursorPos;
    calculateScreenPosition(pi.promptIndentation,
                            0,
                            pi.promptScreenColumns,
                            calculateColumnPosition(buf32, pos),
                            xCursorPos,
                            yCursorPos);

#ifdef _WIN32
    // position at the end of the prompt, clear to end of previous input
    CONSOLE_SCREEN_BUFFER_INFO inf;
    GetConsoleScreenBufferInfo(console_out, &inf);
    inf.dwCursorPosition.X = pi.promptIndentation;  // 0-based on Win32
    inf.dwCursorPosition.Y -= pi.promptCursorRowOffset - pi.promptExtraLines;
    SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
    DWORD count;
    if (len < pi.promptPreviousInputLen)
        FillConsoleOutputCharacterA(
            console_out, ' ', pi.promptPreviousInputLen, inf.dwCursorPosition, &count);
    pi.promptPreviousInputLen = len;

    // display the input line
    if (highlight == -1) {
        if (write32(1, buf32, len) == -1)
            return;
    } else {
        if (write32(1, buf32, highlight) == -1)
            return;
        setDisplayAttribute(true); /* bright blue (visible with both B&W bg) */
        if (write32(1, &buf32[highlight], 1) == -1)
            return;
        setDisplayAttribute(false);
        if (write32(1, buf32 + highlight + 1, len - highlight - 1) == -1)
            return;
    }

    // position the cursor
    GetConsoleScreenBufferInfo(console_out, &inf);
    inf.dwCursorPosition.X = xCursorPos;  // 0-based on Win32
    inf.dwCursorPosition.Y -= yEndOfInput - yCursorPos;
    SetConsoleCursorPosition(console_out, inf.dwCursorPosition);
#else  // _WIN32
    char seq[64];
    int cursorRowMovement = pi.promptCursorRowOffset - pi.promptExtraLines;
    if (cursorRowMovement > 0) {  // move the cursor up as required
        snprintf(seq, sizeof seq, "\x1b[%dA", cursorRowMovement);
        if (write(1, seq, strlen(seq)) == -1)
            return;
    }
    // position at the end of the prompt, clear to end of screen
    snprintf(seq, sizeof seq, "\x1b[%dG\x1b[J", pi.promptIndentation + 1);  // 1-based on VT100
    if (write(1, seq, strlen(seq)) == -1)
        return;

    if (highlight == -1) {  // write unhighlighted text
        if (write32(1, buf32, len) == -1)
            return;
    } else {  // highlight the matching brace/bracket/parenthesis
        if (write32(1, buf32, highlight) == -1)
            return;
        setDisplayAttribute(true);
        if (write32(1, &buf32[highlight], 1) == -1)
            return;
        setDisplayAttribute(false);
        if (write32(1, buf32 + highlight + 1, len - highlight - 1) == -1)
            return;
    }

    // we have to generate our own newline on line wrap
    if (xEndOfInput == 0 && yEndOfInput > 0)
        if (write(1, "\n", 1) == -1)
            return;

    // position the cursor
    cursorRowMovement = yEndOfInput - yCursorPos;
    if (cursorRowMovement > 0) {  // move the cursor up as required
        snprintf(seq, sizeof seq, "\x1b[%dA", cursorRowMovement);
        if (write(1, seq, strlen(seq)) == -1)
            return;
    }
    // position the cursor within the line
    snprintf(seq, sizeof seq, "\x1b[%dG", xCursorPos + 1);  // 1-based on VT100
    if (write(1, seq, strlen(seq)) == -1)
        return;
#endif

    pi.promptCursorRowOffset = pi.promptExtraLines + yCursorPos;  // remember row for next pass
}

Project: mongo
File: linenoise.cpp

static UChar32 ctrlRightArrowKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | CTRL | RIGHT_ARROW_KEY;
}

Project: mongo
File: linenoise.cpp

char* linenoise(const char* prompt) {
    if (isatty(STDIN_FILENO)) {  // input is from a terminal
        UChar32 buf32[LINENOISE_MAX_LINE];
        char charWidths[LINENOISE_MAX_LINE];
        if (!preloadErrorMessage.empty()) {
            printf("%s", preloadErrorMessage.c_str());
            fflush(stdout);
            preloadErrorMessage.clear();
        }
        PromptInfo pi(reinterpret_cast<const UChar8*>(prompt), getScreenColumns());
        if (isUnsupportedTerm()) {
            if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
                return 0;
            fflush(stdout);
            if (preloadedBufferContents.empty()) {
                unique_ptr<char[]> buf8(new char[LINENOISE_MAX_LINE]);
                if (fgets(buf8.get(), LINENOISE_MAX_LINE, stdin) == NULL) {
                    return NULL;
                }
                size_t len = strlen(buf8.get());
                while (len && (buf8[len - 1] == '\n' || buf8[len - 1] == '\r')) {
                    --len;
                    buf8[len] = '\0';
                }
                return strdup(buf8.get());  // caller must free buffer
            } else {
                char* buf8 = strdup(preloadedBufferContents.c_str());
                preloadedBufferContents.clear();
                return buf8;  // caller must free buffer
            }
        } else {
            if (enableRawMode() == -1) {
                return NULL;
            }
            InputBuffer ib(buf32, charWidths, LINENOISE_MAX_LINE);
            if (!preloadedBufferContents.empty()) {
                ib.preloadBuffer(reinterpret_cast<const UChar8*>(preloadedBufferContents.c_str()));
                preloadedBufferContents.clear();
            }
            int count = ib.getInputLine(pi);
            disableRawMode();
            printf("\n");
            if (count == -1) {
                return NULL;
            }
            size_t bufferSize = sizeof(UChar32) * ib.length() + 1;
            unique_ptr<UChar8[]> buf8(new UChar8[bufferSize]);
            copyString32to8(buf8.get(), buf32, bufferSize);
            return strdup(reinterpret_cast<char*>(buf8.get()));  // caller must free buffer
        }
    } else {  // input not from a terminal, we should work with piped input, i.e. redirected stdin
        unique_ptr<char[]> buf8(new char[LINENOISE_MAX_LINE]);
        if (fgets(buf8.get(), LINENOISE_MAX_LINE, stdin) == NULL) {
            return NULL;
        }

        // if fgets() gave us the newline, remove it
        int count = strlen(buf8.get());
        if (count > 0 && buf8[count - 1] == '\n') {
            --count;
            buf8[count] = '\0';
        }
        return strdup(buf8.get());  // caller must free buffer
    }
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracketRoutine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracketDispatch);
}

Project: mongo
File: linenoise.cpp

static UChar32 ctrlDownArrowKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | CTRL | DOWN_ARROW_KEY;
}

Project: mongo
File: linenoise.cpp

static void calculateScreenPosition(
    int x, int y, int screenColumns, int charCount, int& xOut, int& yOut) {
    xOut = x;
    yOut = y;
    int charsRemaining = charCount;
    while (charsRemaining > 0) {
        int charsThisRow =
            (x + charsRemaining < screenColumns) ? charsRemaining : screenColumns - x;
        xOut = x + charsThisRow;
        yOut = y;
        charsRemaining -= charsThisRow;
        x = 0;
        ++y;
    }
    if (xOut == screenColumns) {  // we have to special-case line wrap
        xOut = 0;
        ++yOut;
    }
}

Project: mongo
File: linenoise.cpp

    DynamicPrompt(PromptBase& pi, int initialDirection)
        : searchTextLen(0), direction(initialDirection) {
        promptScreenColumns = pi.promptScreenColumns;
        promptCursorRowOffset = 0;
        Utf32String emptyString(1);
        searchText = emptyString;
        const Utf32String* basePrompt =
            (direction > 0) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt;
        size_t promptStartLength = basePrompt->length();
        promptChars = promptStartLength + endSearchBasePrompt.length();
        promptLastLinePosition =
            promptChars;  // TODO fix this, we are asssuming that the history prompt won't wrap (!)
        promptPreviousLen = promptChars;
        Utf32String tempUnicode(promptChars + 1);
        memcpy(tempUnicode.get(), basePrompt->get(), sizeof(UChar32) * promptStartLength);
        memcpy(&tempUnicode[promptStartLength],
               endSearchBasePrompt.get(),
               sizeof(UChar32) * (endSearchBasePrompt.length() + 1));
        tempUnicode.initFromBuffer();
        promptText = tempUnicode;
        calculateScreenPosition(
            0, 0, pi.promptScreenColumns, promptChars, promptIndentation, promptExtraLines);
    }

Project: mongo
File: linenoise.cpp

static UChar32 downArrowKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | DOWN_ARROW_KEY;
}

Project: mongo
File: linenoise.cpp

static UChar32 endKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | END_KEY;
}

Project: mongo
File: linenoise.cpp

static UChar32 ctrlUpArrowKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | CTRL | UP_ARROW_KEY;
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket1SemicolonRoutine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracket1SemicolonDispatch);
}

Project: mongo
File: linenoise.cpp

void linenoiseClearScreen(void) {
#ifdef _WIN32
    COORD coord = {0, 0};
    CONSOLE_SCREEN_BUFFER_INFO inf;
    HANDLE screenHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(screenHandle, &inf);
    SetConsoleCursorPosition(screenHandle, coord);
    DWORD count;
    FillConsoleOutputCharacterA(screenHandle, ' ', inf.dwSize.X * inf.dwSize.Y, coord, &count);
#else
    if (write(1, "\x1b[H\x1b[2J", 7) <= 0)
        return;
#endif
}

Project: mongo
File: linenoise.cpp

static UChar32 escLeftBracket8Routine(UChar32 c) {
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;
    return doDispatch(c, escLeftBracket8Dispatch);
}

Project: mongo
File: linenoise.cpp

int InputBuffer::completeLine(PromptBase& pi) {
    linenoiseCompletions lc;
    char c = 0;

    // completionCallback() expects a parsable entity, so find the previous break character and
    // extract a copy to parse.  we also handle the case where tab is hit while not at end-of-line.
    int startIndex = pos;
    while (--startIndex >= 0) {
        if (strchr(breakChars, buf32[startIndex])) {
            break;
        }
    }
    ++startIndex;
    int itemLength = pos - startIndex;
    Utf32String unicodeCopy(&buf32[startIndex], itemLength);
    Utf8String parseItem(unicodeCopy);

    // get a list of completions
    completionCallback(reinterpret_cast<char*>(parseItem.get()), &lc);

    // if no completions, we are done
    if (lc.completionStrings.size() == 0) {
        beep();
        freeCompletions(&lc);
        return 0;
    }

    // at least one completion
    int longestCommonPrefix = 0;
    int displayLength = 0;
    if (lc.completionStrings.size() == 1) {
        longestCommonPrefix = lc.completionStrings[0].length();
    } else {
        bool keepGoing = true;
        while (keepGoing) {
            for (size_t j = 0; j < lc.completionStrings.size() - 1; ++j) {
                char c1 = lc.completionStrings[j][longestCommonPrefix];
                char c2 = lc.completionStrings[j + 1][longestCommonPrefix];
                if ((0 == c1) || (0 == c2) || (c1 != c2)) {
                    keepGoing = false;
                    break;
                }
            }
            if (keepGoing) {
                ++longestCommonPrefix;
            }
        }
    }
    if (lc.completionStrings.size() != 1) {  // beep if ambiguous
        beep();
    }

    // if we can extend the item, extend it and return to main loop
    if (longestCommonPrefix > itemLength) {
        displayLength = len + longestCommonPrefix - itemLength;
        if (displayLength > buflen) {
            longestCommonPrefix -= displayLength - buflen;  // don't overflow buffer
            displayLength = buflen;                         // truncate the insertion
            beep();                                         // and make a noise
        }
        Utf32String displayText(displayLength + 1);
        memcpy(displayText.get(), buf32, sizeof(UChar32) * startIndex);
        memcpy(&displayText[startIndex],
               &lc.completionStrings[0][0],
               sizeof(UChar32) * longestCommonPrefix);
        int tailIndex = startIndex + longestCommonPrefix;
        memcpy(&displayText[tailIndex],
               &buf32[pos],
               sizeof(UChar32) * (displayLength - tailIndex + 1));
        copyString32(buf32, displayText.get(), buflen + 1);
        pos = startIndex + longestCommonPrefix;
        len = displayLength;
        refreshLine(pi);
        return 0;
    }

    // we can't complete any further, wait for second tab
    do {
        c = linenoiseReadChar();
        c = cleanupCtrl(c);
    } while (c == static_cast<char>(-1));

    // if any character other than tab, pass it to the main loop
    if (c != ctrlChar('I')) {
        freeCompletions(&lc);
        return c;
    }

    // we got a second tab, maybe show list of possible completions
    bool showCompletions = true;
    bool onNewLine = false;
    if (lc.completionStrings.size() > completionCountCutoff) {
        int savePos = pos;  // move cursor to EOL to avoid overwriting the command line
        pos = len;
        refreshLine(pi);
        pos = savePos;
        printf("\nDisplay all %u possibilities? (y or n)",
               static_cast<unsigned int>(lc.completionStrings.size()));
        fflush(stdout);
        onNewLine = true;
        while (c != 'y' && c != 'Y' && c != 'n' && c != 'N' && c != ctrlChar('C')) {
            do {
                c = linenoiseReadChar();
                c = cleanupCtrl(c);
            } while (c == static_cast<char>(-1));
        }
        switch (c) {
            case 'n':
            case 'N':
                showCompletions = false;
                freeCompletions(&lc);
                break;
            case ctrlChar('C'):
                showCompletions = false;
                freeCompletions(&lc);
                if (write(1, "^C", 2) == -1)
                    return -1;  // Display the ^C we got
                c = 0;
                break;
        }
    }

    // if showing the list, do it the way readline does it
    bool stopList = false;
    if (showCompletions) {
        int longestCompletion = 0;
        for (size_t j = 0; j < lc.completionStrings.size(); ++j) {
            itemLength = lc.completionStrings[j].length();
            if (itemLength > longestCompletion) {
                longestCompletion = itemLength;
            }
        }
        longestCompletion += 2;
        int columnCount = pi.promptScreenColumns / longestCompletion;
        if (columnCount < 1) {
            columnCount = 1;
        }
        if (!onNewLine) {       // skip this if we showed "Display all %d possibilities?"
            int savePos = pos;  // move cursor to EOL to avoid overwriting the command line
            pos = len;
            refreshLine(pi);
            pos = savePos;
        }
        size_t pauseRow = getScreenRows() - 1;
        size_t rowCount = (lc.completionStrings.size() + columnCount - 1) / columnCount;
        for (size_t row = 0; row < rowCount; ++row) {
            if (row == pauseRow) {
                printf("\n--More--");
                fflush(stdout);
                c = 0;
                bool doBeep = false;
                while (c != ' ' && c != '\r' && c != '\n' && c != 'y' && c != 'Y' && c != 'n' &&
                       c != 'N' && c != 'q' && c != 'Q' && c != ctrlChar('C')) {
                    if (doBeep) {
                        beep();
                    }
                    doBeep = true;
                    do {
                        c = linenoiseReadChar();
                        c = cleanupCtrl(c);
                    } while (c == static_cast<char>(-1));
                }
                switch (c) {
                    case ' ':
                    case 'y':
                    case 'Y':
                        printf("\r        \r");
                        pauseRow += getScreenRows() - 1;
                        break;
                    case '\r':
                    case '\n':
                        printf("\r        \r");
                        ++pauseRow;
                        break;
                    case 'n':
                    case 'N':
                    case 'q':
                    case 'Q':
                        printf("\r        \r");
                        stopList = true;
                        break;
                    case ctrlChar('C'):
                        if (write(1, "^C", 2) == -1)
                            return -1;  // Display the ^C we got
                        stopList = true;
                        break;
                }
            } else {
                printf("\n");
            }
            if (stopList) {
                break;
            }
            for (int column = 0; column < columnCount; ++column) {
                size_t index = (column * rowCount) + row;
                if (index < lc.completionStrings.size()) {
                    itemLength = lc.completionStrings[index].length();
                    fflush(stdout);
                    if (write32(1, lc.completionStrings[index].get(), itemLength) == -1)
                        return -1;
                    if (((column + 1) * rowCount) + row < lc.completionStrings.size()) {
                        for (int k = itemLength; k < longestCompletion; ++k) {
                            printf(" ");
                        }
                    }
                }
            }
        }
        fflush(stdout);
        freeCompletions(&lc);
    }

    // display the prompt on a new line, then redisplay the input buffer
    if (!stopList || c == ctrlChar('C')) {
        if (write(1, "\n", 1) == -1)
            return 0;
    }
    if (write32(1, pi.promptText.get(), pi.promptChars) == -1)
        return 0;
#ifndef _WIN32
    // we have to generate our own newline on line wrap on Linux
    if (pi.promptIndentation == 0 && pi.promptExtraLines > 0)
        if (write(1, "\n", 1) == -1)
            return 0;
#endif
    pi.promptCursorRowOffset = pi.promptExtraLines;
    refreshLine(pi);
    return 0;
}

Project: mongo
File: linenoise.cpp

static int calculateColumnPosition(UChar32* buf32, int len) {
    int width = mk_wcswidth(reinterpret_cast<const int*>(buf32), len);
    if (width == -1)
        return len;
    else
        return width;
}

Project: mongo
File: linenoise.cpp

static int enableRawMode(void) {
#ifdef _WIN32
    if (!console_in) {
        console_in = GetStdHandle(STD_INPUT_HANDLE);
        console_out = GetStdHandle(STD_OUTPUT_HANDLE);

        GetConsoleMode(console_in, &oldMode);
        SetConsoleMode(console_in,
                       oldMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT));
    }
    return 0;
#else
    struct termios raw;

    if (!isatty(0))
        goto fatal;
    if (!atexit_registered) {
        atexit(linenoiseAtExit);
        atexit_registered = 1;
    }
    if (tcgetattr(0, &orig_termios) == -1)
        goto fatal;

    raw = orig_termios; /* modify the original mode */
                        /* input modes: no break, no CR to NL, no parity check, no strip char,
                         * no start/stop output control. */
    raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    /* output modes - disable post processing */
    // this is wrong, we don't want raw output, it turns newlines into straight linefeeds
    // raw.c_oflag &= ~(OPOST);
    /* control modes - set 8 bit chars */
    raw.c_cflag |= (CS8);
    /* local modes - echoing off, canonical off, no extended functions,
     * no signal chars (^Z,^C) */
    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    /* control chars - set return condition: min number of bytes and timer.
     * We want read to return every single byte, without timeout. */
    raw.c_cc[VMIN] = 1;
    raw.c_cc[VTIME] = 0; /* 1 byte, no timer */

    /* put terminal in raw mode after flushing */
    if (tcsetattr(0, TCSADRAIN, &raw) < 0)
        goto fatal;
    rawmode = 1;
    return 0;

fatal:
    errno = ENOTTY;
    return -1;
#endif
}

Project: mongo
File: linenoise.cpp

    void preloadBuffer(const UChar8* preloadText) {
        size_t ucharCount;
        int errorCode;
        copyString8to32(buf32, preloadText, buflen + 1, ucharCount, errorCode);
        recomputeCharacterWidths(buf32, charWidths, ucharCount);
        len = ucharCount;
        pos = ucharCount;
    }

Project: mongo
File: linenoise.cpp

static UChar32 rightArrowKeyRoutine(UChar32 c) {
    return thisKeyMetaCtrl | RIGHT_ARROW_KEY;
}

Project: mongo
File: linenoise.cpp

int linenoiseHistorySave(const char* filename) {
    FILE* fp;
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
    int fd = open(filename, O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        // report errno somehow?
        return -1;
    }
    fp = fdopen(fd, "wt");
#else
    fp = fopen(filename, "wt");
#endif  // _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
    if (fp == NULL) {
        return -1;
    }

    for (int j = 0; j < historyLen; ++j) {
        if (history[j][0] != '\0') {
            fprintf(fp, "%s\n", history[j]);
        }
    }
    fclose(fp);  // Also causes fd to be closed.
    return 0;
}

Project: mongo
File: linenoise.cpp

static UChar32 linenoiseReadChar(void) {
#ifdef _WIN32

    INPUT_RECORD rec;
    DWORD count;
    int modifierKeys = 0;
    bool escSeen = false;
    while (true) {
        ReadConsoleInputW(console_in, &rec, 1, &count);
#if 0  // helper for debugging keystrokes, display info in the debug "Output" window in the debugger
        {
            if ( rec.EventType == KEY_EVENT ) {
                //if ( rec.Event.KeyEvent.uChar.UnicodeChar ) {
                    char buf[1024];
                    sprintf(
                            buf,
                            "Unicode character 0x%04X, repeat count %d, virtual keycode 0x%04X, "
                            "virtual scancode 0x%04X, key %s%s%s%s%s\n",
                            rec.Event.KeyEvent.uChar.UnicodeChar,
                            rec.Event.KeyEvent.wRepeatCount,
                            rec.Event.KeyEvent.wVirtualKeyCode,
                            rec.Event.KeyEvent.wVirtualScanCode,
                            rec.Event.KeyEvent.bKeyDown ? "down" : "up",
                                (rec.Event.KeyEvent.dwControlKeyState & LEFT_CTRL_PRESSED)  ?
                                    " L-Ctrl" : "",
                                (rec.Event.KeyEvent.dwControlKeyState & RIGHT_CTRL_PRESSED) ?
                                    " R-Ctrl" : "",
                                (rec.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED)   ?
                                    " L-Alt"  : "",
                                (rec.Event.KeyEvent.dwControlKeyState & RIGHT_ALT_PRESSED)  ?
                                    " R-Alt"  : ""
                           );
                    OutputDebugStringA( buf );
                //}
            }
        }
#endif
        if (rec.EventType != KEY_EVENT) {
            continue;
        }
        // Windows provides for entry of characters that are not on your keyboard by sending the
        // Unicode characters as a "key up" with virtual keycode 0x12 (VK_MENU == Alt key) ...
        // accept these characters, otherwise only process characters on "key down"
        if (!rec.Event.KeyEvent.bKeyDown && rec.Event.KeyEvent.wVirtualKeyCode != VK_MENU) {
            continue;
        }
        modifierKeys = 0;
        // AltGr is encoded as ( LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED ), so don't treat this
        // combination as either CTRL or META we just turn off those two bits, so it is still
        // possible to combine CTRL and/or META with an AltGr key by using right-Ctrl and/or
        // left-Alt
        if ((rec.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) ==
            (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) {
            rec.Event.KeyEvent.dwControlKeyState &= ~(LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
        }
        if (rec.Event.KeyEvent.dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) {
            modifierKeys |= CTRL;
        }
        if (rec.Event.KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) {
            modifierKeys |= META;
        }
        if (escSeen) {
            modifierKeys |= META;
        }
        if (rec.Event.KeyEvent.uChar.UnicodeChar == 0) {
            switch (rec.Event.KeyEvent.wVirtualKeyCode) {
                case VK_LEFT:
                    return modifierKeys | LEFT_ARROW_KEY;
                case VK_RIGHT:
                    return modifierKeys | RIGHT_ARROW_KEY;
                case VK_UP:
                    return modifierKeys | UP_ARROW_KEY;
                case VK_DOWN:
                    return modifierKeys | DOWN_ARROW_KEY;
                case VK_DELETE:
                    return modifierKeys | DELETE_KEY;
                case VK_HOME:
                    return modifierKeys | HOME_KEY;
                case VK_END:
                    return modifierKeys | END_KEY;
                case VK_PRIOR:
                    return modifierKeys | PAGE_UP_KEY;
                case VK_NEXT:
                    return modifierKeys | PAGE_DOWN_KEY;
                default:
                    continue;  // in raw mode, ReadConsoleInput shows shift, ctrl ...
            }                  //  ... ignore them
        } else if (rec.Event.KeyEvent.uChar.UnicodeChar ==
                   ctrlChar('[')) {  // ESC, set flag for later
            escSeen = true;
            continue;
        } else {
            // we got a real character, return it
            return modifierKeys | rec.Event.KeyEvent.uChar.UnicodeChar;
        }
    }

#else
    UChar32 c;
    c = readUnicodeCharacter();
    if (c == 0)
        return 0;

// If _DEBUG_LINUX_KEYBOARD is set, then ctrl-^ puts us into a keyboard debugging mode
// where we print out decimal and decoded values for whatever the "terminal" program
// gives us on different keystrokes.  Hit ctrl-C to exit this mode.
//
#define _DEBUG_LINUX_KEYBOARD
#if defined(_DEBUG_LINUX_KEYBOARD)
    if (c == ctrlChar('^')) {  // ctrl-^, special debug mode, prints all keys hit, ctrl-C to get out
        printf("\nEntering keyboard debugging mode (on ctrl-^), press ctrl-C to exit this mode\n");
        while (true) {
            unsigned char keys[10];
            int ret = read(0, keys, 10);

            if (ret <= 0) {
                printf("\nret: %d\n", ret);
            }
            for (int i = 0; i < ret; ++i) {
                UChar32 key = static_cast<UChar32>(keys[i]);
                char* friendlyTextPtr;
                char friendlyTextBuf[10];
                const char* prefixText = (key < 0x80) ? "" : "0x80+";
                UChar32 keyCopy = (key < 0x80) ? key : key - 0x80;
                if (keyCopy >= '!' && keyCopy <= '~') {  // printable
                    friendlyTextBuf[0] = '\'';
                    friendlyTextBuf[1] = keyCopy;
                    friendlyTextBuf[2] = '\'';
                    friendlyTextBuf[3] = 0;
                    friendlyTextPtr = friendlyTextBuf;
                } else if (keyCopy == ' ') {
                    friendlyTextPtr = const_cast<char*>("space");
                } else if (keyCopy == 27) {
                    friendlyTextPtr = const_cast<char*>("ESC");
                } else if (keyCopy == 0) {
                    friendlyTextPtr = const_cast<char*>("NUL");
                } else if (keyCopy == 127) {
                    friendlyTextPtr = const_cast<char*>("DEL");
                } else {
                    friendlyTextBuf[0] = '^';
                    friendlyTextBuf[1] = keyCopy + 0x40;
                    friendlyTextBuf[2] = 0;
                    friendlyTextPtr = friendlyTextBuf;
                }
                printf("%d x%02X (%s%s)  ", key, key, prefixText, friendlyTextPtr);
            }
            printf("\x1b[1G\n");  // go to first column of new line

            // drop out of this loop on ctrl-C
            if (keys[0] == ctrlChar('C')) {
                printf("Leaving keyboard debugging mode (on ctrl-C)\n");
                fflush(stdout);
                return -2;
            }
        }
    }
#endif  // _DEBUG_LINUX_KEYBOARD

    EscapeSequenceProcessing::thisKeyMetaCtrl = 0;  // no modifiers yet at initialDispatch
    return EscapeSequenceProcessing::doDispatch(c, EscapeSequenceProcessing::initialDispatch);
#endif  // #_WIN32
}