summaryrefslogtreecommitdiff
path: root/src/mongo/shell/linenoise_utf8.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/shell/linenoise_utf8.cpp')
-rw-r--r--src/mongo/shell/linenoise_utf8.cpp348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/mongo/shell/linenoise_utf8.cpp b/src/mongo/shell/linenoise_utf8.cpp
new file mode 100644
index 00000000000..d906c9676f2
--- /dev/null
+++ b/src/mongo/shell/linenoise_utf8.cpp
@@ -0,0 +1,348 @@
+// linenoise_utf8.cpp
+/*
+ * Copyright 2012 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mongo/shell/linenoise_utf8.h"
+
+#ifdef _WIN32
+#include <io.h>
+#include "mongo/platform/windows_basic.h"
+#include "mongo/util/text.h"
+#else
+#include <unistd.h>
+#endif
+
+namespace linenoise_utf8 {
+
+/**
+ * Convert a null terminated UTF-8 string from UTF-8 and store it in a UChar32 destination buffer
+ * Always null terminates the destination string if at least one character position is available
+ * Errors in the UTF-8 encoding will be handled in two ways: the erroneous characters will be
+ * converted to the Unicode error character U+FFFD and flag bits will be set in the conversionErrorCode
+ * int.
+ *
+ * @param uchar32output Destination UChar32 buffer
+ * @param utf8input Source UTF-8 string
+ * @param outputBufferSizeInCharacters Destination buffer size in characters
+ * @param outputUnicodeCharacterCount Number of UChar32 characters placed in output buffer
+ * @param conversionErrorCode Flag bits from enum BadUTF8, or zero if no error
+ */
+void copyString8to32(
+ UChar32* uchar32output,
+ const UChar8* utf8input,
+ size_t outputBufferSizeInCharacters,
+ size_t & outputUnicodeCharacterCount,
+ int & conversionErrorCode ) {
+ conversionErrorCode = BadUTF8_no_error;
+ if ( outputBufferSizeInCharacters == 0 ) {
+ outputUnicodeCharacterCount = 0;
+ return;
+ }
+ static const UChar32 errorCharacter = 0xFFFD;
+ const UChar8* pIn = utf8input;
+ UChar32* pOut = uchar32output;
+ UChar32 uchar32;
+ int reducedBufferSize = outputBufferSizeInCharacters - 1;
+ while ( *pIn && ( pOut - uchar32output ) < reducedBufferSize ) {
+
+ // default to error character so we don't set this in 18 places below
+ uchar32 = errorCharacter;
+
+ if ( pIn[0] <= 0x7F ) { // 0x00000000 to 0x0000007F
+ uchar32 = pIn[0];
+ pIn += 1;
+ }
+ else if ( pIn[0] <= 0xDF ) { // 0x00000080 to 0x000007FF
+ if ( ( pIn[0] >= 0xC2 ) && ( pIn[1] >= 0x80 ) && ( pIn[1] <= 0xBF ) ) {
+ uchar32 = ( ( pIn[0] & 0x1F ) << 6 ) | ( pIn[1] & 0x3F );
+ pIn += 2;
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 1;
+ }
+ }
+ else if ( pIn[0] == 0xE0 ) { // 0x00000800 to 0x00000FFF
+ if ( ( pIn[1] >= 0xA0 ) && ( pIn[1] <= 0xBF ) ) {
+ if ( ( pIn[2] >= 0x80 ) && ( pIn[2] <= 0xBF ) ) {
+ uchar32 = ( ( pIn[1] & 0x3F ) << 6 ) | ( pIn[2] & 0x3F );
+ pIn += 3;
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 2;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 1;
+ }
+ }
+ else if ( pIn[0] <= 0xEC ) { // 0x00001000 to 0x0000CFFF
+ if ( ( pIn[1] >= 0x80 ) && ( pIn[1] <= 0xBF ) ) {
+ if ( ( pIn[2] >= 0x80 ) && ( pIn[2] <= 0xBF ) ) {
+ uchar32 = ( ( pIn[0] & 0x0F ) << 12 ) | ( ( pIn[1] & 0x3F ) << 6 ) | ( pIn[2] & 0x3F );
+ pIn += 3;
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 2;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 1;
+ }
+ }
+ else if ( pIn[0] == 0xED ) { // 0x0000D000 to 0x0000D7FF
+ if ( ( pIn[1] >= 0x80 ) && ( pIn[1] <= 0x9F ) ) {
+ if ( ( pIn[2] >= 0x80 ) && ( pIn[2] <= 0xBF ) ) {
+ uchar32 = ( 0x0D << 12 ) | ( ( pIn[1] & 0x3F ) << 6 ) | ( pIn[2] & 0x3F );
+ pIn += 3;
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 2;
+ }
+ }
+ // // 0x0000D800 to 0x0000DFFF -- illegal surrogate value
+ else if ( ( pIn[1] >= 0x80 ) && ( pIn[1] <= 0xBF ) ) {
+ if ( ( pIn[2] >= 0x80 ) && ( pIn[2] <= 0xBF ) ) {
+ conversionErrorCode |= BadUTF8_surrogate;
+ pIn += 3;
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 2;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 1;
+ }
+ }
+ else if ( pIn[0] <= 0xEF ) { // 0x0000E000 to 0x0000FFFF
+ if ( ( pIn[1] >= 0x80 ) && ( pIn[1] <= 0xBF ) ) {
+ if ( ( pIn[2] >= 0x80 ) && ( pIn[2] <= 0xBF ) ) {
+ uchar32 = ( ( pIn[0] & 0x0F ) << 12 ) | ( ( pIn[1] & 0x3F ) << 6 ) | ( pIn[2] & 0x3F );
+ pIn += 3;
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 2;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 1;
+ }
+ }
+ else if ( pIn[0] == 0xF0 ) { // 0x00010000 to 0x0003FFFF
+ if ( ( pIn[1] >= 0x90 ) && ( pIn[1] <= 0xBF ) ) {
+ if ( ( pIn[2] >= 0x80 ) && ( pIn[2] <= 0xBF ) ) {
+ if ( ( pIn[3] >= 0x80 ) && ( pIn[3] <= 0xBF ) ) {
+ uchar32 = ( ( pIn[1] & 0x3F ) << 12 ) | ( ( pIn[2] & 0x3F ) << 6 ) | ( pIn[3] & 0x3F );
+ pIn += 4;
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 3;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 2;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 1;
+ }
+ }
+ else if ( pIn[0] <= 0xF4 ) { // 0x00040000 to 0x0010FFFF
+ if ( ( pIn[1] >= 0x80 ) && ( pIn[1] <= 0xBF ) ) {
+ if ( ( pIn[2] >= 0x80 ) && ( pIn[2] <= 0xBF ) ) {
+ if ( ( pIn[3] >= 0x80 ) && ( pIn[3] <= 0xBF ) ) {
+ uchar32 = ( ( pIn[0] & 0x07 ) << 18 ) | ( ( pIn[1] & 0x3F ) << 12 ) | ( ( pIn[2] & 0x3F ) << 6 ) | ( pIn[3] & 0x3F );
+ pIn += 4;
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 3;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 2;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 1;
+ }
+ }
+ else {
+ conversionErrorCode |= BadUTF8_invalid_byte;
+ pIn += 1;
+ }
+ if ( uchar32 != 0xFEFF ) { // do not store Byte Order Mark
+ *pOut++ = uchar32;
+ }
+ }
+ *pOut = 0;
+ outputUnicodeCharacterCount = pOut - uchar32output;
+}
+
+/**
+ * Copy a null terminated UChar32 string to a UChar32 destination buffer
+ * Always null terminates the destination string if at least one character position is available
+ *
+ * @param dest32 Destination UChar32 buffer
+ * @param source32 Source UChar32 string
+ * @param destLengthInCharacters Destination buffer length in characters
+ */
+void copyString32( UChar32* dest32, const UChar32* source32, size_t destLengthInCharacters ) {
+ if ( destLengthInCharacters ) {
+ while ( *source32 && --destLengthInCharacters > 0 ) {
+ *dest32++ = *source32++;
+ }
+ *dest32 = 0;
+ }
+}
+
+/**
+ * Convert a specified number of UChar32 characters from a possibly null terminated UChar32 string to UTF-8
+ * and store it in a UChar8 destination buffer
+ * Always null terminates the destination string if at least one character position is available
+ *
+ * @param dest8 Destination UChar8 buffer
+ * @param source32 Source UChar32 string
+ * @param outputBufferSizeInBytes Destination buffer size in bytes
+ * @param charCount Maximum number of UChar32 characters to process
+ * @return Count of bytes written to output buffer, not including null terminator
+ */
+size_t copyString32to8counted( UChar8* dest8, const UChar32* source32, size_t outputBufferSizeInBytes, size_t charCount ) {
+ size_t outputUTF8ByteCount = 0;
+ if ( outputBufferSizeInBytes ) {
+ size_t reducedBufferSize = outputBufferSizeInBytes - 4;
+ while ( *source32 && charCount-- && outputUTF8ByteCount < reducedBufferSize ) {
+ UChar32 c = *source32++;
+ if ( c <= 0x7F ) {
+ *dest8++ = c;
+ outputUTF8ByteCount += 1;
+ }
+ else if ( c <= 0x7FF ) {
+ *dest8++ = 0xC0 | ( c >> 6 );
+ *dest8++ = 0x80 | ( 0x3F & c );
+ outputUTF8ByteCount += 2;
+ }
+ else if ( c <= 0xFFFF ) {
+ *dest8++ = 0xE0 | ( c >> 12 );
+ *dest8++ = 0x80 | ( 0x3F & ( c >> 6) );
+ *dest8++ = 0x80 | ( 0x3F & c );
+ outputUTF8ByteCount += 3;
+ }
+ else if ( c <= 0x1FFFFF ) {
+ *dest8++ = 0xF0 | ( c >> 18 );
+ *dest8++ = 0x80 | ( 0x3F & ( c >> 12) );
+ *dest8++ = 0x80 | ( 0x3F & ( c >> 6) );
+ *dest8++ = 0x80 | ( 0x3F & c );
+ outputUTF8ByteCount += 4;
+ }
+ }
+ *dest8 = 0;
+ }
+ return outputUTF8ByteCount;
+}
+
+/**
+ * Convert a null terminated UChar32 string to UTF-8 and store it in a UChar8 destination buffer
+ * Always null terminates the destination string if at least one character position is available
+ *
+ * @param dest8 Destination UChar8 buffer
+ * @param source32 Source UChar32 string
+ * @param outputBufferSizeInBytes Destination buffer size in bytes
+ * @return Count of bytes written to output buffer, not including null terminator
+ */
+size_t copyString32to8( UChar8* dest8, const UChar32* source32, size_t outputBufferSizeInBytes ) {
+ return copyString32to8counted( dest8, source32, outputBufferSizeInBytes, 0x7FFFFFFF );
+}
+
+/**
+ * Count characters (i.e. Unicode code points, array elements) in a null terminated UChar32 string
+ *
+ * @param str32 Source UChar32 string
+ * @return String length in characters
+ */
+size_t strlen32( const UChar32* str32 ) {
+ size_t length = 0;
+ while ( *str32++ ) {
+ ++length;
+ }
+ return length;
+}
+
+/**
+ * Compare two UChar32 null-terminated strings with length parameter
+ *
+ * @param first32 First string to compare
+ * @param second32 Second string to compare
+ * @param length Maximum number of characters to compare
+ * @return Negative if first < second, positive if first > second, zero if equal
+ */
+int strncmp32( UChar32* first32, UChar32* second32, size_t length ) {
+ while ( length-- ) {
+ if ( *first32 == 0 || *first32 != *second32 ) {
+ return *first32 - *second32;
+ }
+ ++first32;
+ ++second32;
+ }
+ return 0;
+}
+
+/**
+ * Internally convert an array of UChar32 characters of specified length to UTF-8 and write it to fileHandle
+ *
+ * @param fileHandle File handle to write to
+ * @param string32 Source UChar32 characters, may not be null terminated
+ * @param sourceLengthInCharacters Number of source characters to convert and write
+ * @return Number of bytes written, -1 on error
+ */
+int write32( int fileHandle, const UChar32* string32, unsigned int sourceLengthInCharacters ) {
+ size_t tempBufferBytes = 4 * sourceLengthInCharacters + 1;
+ boost::scoped_array<char> tempCharString( new char[ tempBufferBytes ] );
+ size_t count = copyString32to8counted( reinterpret_cast<UChar8*>( tempCharString.get() ),
+ string32,
+ tempBufferBytes,
+ sourceLengthInCharacters );
+#if defined(_WIN32)
+ if ( _isatty( fileHandle ) ) {
+ bool success = mongo::writeUtf8ToWindowsConsole( tempCharString.get(), count );
+ if ( ! success ) {
+ return -1;
+ }
+ return count;
+ }
+ else {
+ return _write( fileHandle, tempCharString.get(), count );
+ }
+#else
+ return write( fileHandle, tempCharString.get(), count );
+#endif
+}
+
+} // namespace linenoise_utf8