diff options
Diffstat (limited to 'src/mongo/shell')
-rw-r--r-- | src/mongo/shell/bench.cpp | 1486 | ||||
-rw-r--r-- | src/mongo/shell/bench.h | 793 | ||||
-rw-r--r-- | src/mongo/shell/clientAndShell.cpp | 57 | ||||
-rw-r--r-- | src/mongo/shell/dbshell.cpp | 701 | ||||
-rw-r--r-- | src/mongo/shell/linenoise.cpp | 2839 | ||||
-rw-r--r-- | src/mongo/shell/linenoise.h | 22 | ||||
-rw-r--r-- | src/mongo/shell/linenoise_utf8.cpp | 237 | ||||
-rw-r--r-- | src/mongo/shell/linenoise_utf8.h | 170 | ||||
-rw-r--r-- | src/mongo/shell/mk_wcwidth.cpp | 538 | ||||
-rw-r--r-- | src/mongo/shell/mk_wcwidth.h | 2 | ||||
-rw-r--r-- | src/mongo/shell/shell_options.cpp | 482 | ||||
-rw-r--r-- | src/mongo/shell/shell_options.h | 91 | ||||
-rw-r--r-- | src/mongo/shell/shell_options_init.cpp | 51 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils.cpp | 506 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils.h | 106 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils_extended.cpp | 367 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils_extended.h | 10 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils_launcher.cpp | 1345 | ||||
-rw-r--r-- | src/mongo/shell/shell_utils_launcher.h | 164 |
19 files changed, 5115 insertions, 4852 deletions
diff --git a/src/mongo/shell/bench.cpp b/src/mongo/shell/bench.cpp index 99311f23f62..bd446e49018 100644 --- a/src/mongo/shell/bench.cpp +++ b/src/mongo/shell/bench.cpp @@ -54,459 +54,505 @@ // TODO: Maybe extract as library to avoid code duplication? namespace { - inline pcrecpp::RE_Options flags2options(const char* flags) { - pcrecpp::RE_Options options; - options.set_utf8(true); - while ( flags && *flags ) { - if ( *flags == 'i' ) - options.set_caseless(true); - else if ( *flags == 'm' ) - options.set_multiline(true); - else if ( *flags == 'x' ) - options.set_extended(true); - flags++; - } - return options; - } +inline pcrecpp::RE_Options flags2options(const char* flags) { + pcrecpp::RE_Options options; + options.set_utf8(true); + while (flags && *flags) { + if (*flags == 'i') + options.set_caseless(true); + else if (*flags == 'm') + options.set_multiline(true); + else if (*flags == 'x') + options.set_extended(true); + flags++; + } + return options; +} } namespace mongo { - using std::unique_ptr; - using std::cout; - using std::endl; - using std::map; - - BenchRunEventCounter::BenchRunEventCounter() { - reset(); - } - - BenchRunEventCounter::~BenchRunEventCounter() {} +using std::unique_ptr; +using std::cout; +using std::endl; +using std::map; - void BenchRunEventCounter::reset() { - _numEvents = 0; - _totalTimeMicros = 0; - } - - void BenchRunEventCounter::updateFrom(const BenchRunEventCounter &other) { - _numEvents += other._numEvents; - _totalTimeMicros += other._totalTimeMicros; - } - - BenchRunStats::BenchRunStats() { - reset(); - } +BenchRunEventCounter::BenchRunEventCounter() { + reset(); +} - BenchRunStats::~BenchRunStats() {} +BenchRunEventCounter::~BenchRunEventCounter() {} - void BenchRunStats::reset() { - error = false; - errCount = 0; - opCount = 0; +void BenchRunEventCounter::reset() { + _numEvents = 0; + _totalTimeMicros = 0; +} - findOneCounter.reset(); - updateCounter.reset(); - insertCounter.reset(); - deleteCounter.reset(); - queryCounter.reset(); - commandCounter.reset(); - trappedErrors.clear(); - } +void BenchRunEventCounter::updateFrom(const BenchRunEventCounter& other) { + _numEvents += other._numEvents; + _totalTimeMicros += other._totalTimeMicros; +} - void BenchRunStats::updateFrom(const BenchRunStats &other) { - if (other.error) - error = true; - errCount += other.errCount; - opCount += other.opCount; - - findOneCounter.updateFrom(other.findOneCounter); - updateCounter.updateFrom(other.updateCounter); - insertCounter.updateFrom(other.insertCounter); - deleteCounter.updateFrom(other.deleteCounter); - queryCounter.updateFrom(other.queryCounter); - commandCounter.updateFrom(other.commandCounter); - - for (size_t i = 0; i < other.trappedErrors.size(); ++i) - trappedErrors.push_back(other.trappedErrors[i]); - } +BenchRunStats::BenchRunStats() { + reset(); +} - BenchRunConfig::BenchRunConfig() { - initializeToDefaults(); - } +BenchRunStats::~BenchRunStats() {} - void BenchRunConfig::initializeToDefaults() { - host = "localhost"; - db = "test"; - username = ""; - password = ""; +void BenchRunStats::reset() { + error = false; + errCount = 0; + opCount = 0; - parallel = 1; - seconds = 1.0; - hideResults = true; - handleErrors = false; - hideErrors = false; + findOneCounter.reset(); + updateCounter.reset(); + insertCounter.reset(); + deleteCounter.reset(); + queryCounter.reset(); + commandCounter.reset(); + trappedErrors.clear(); +} - trapPattern.reset(); - noTrapPattern.reset(); - watchPattern.reset(); - noWatchPattern.reset(); +void BenchRunStats::updateFrom(const BenchRunStats& other) { + if (other.error) + error = true; + errCount += other.errCount; + opCount += other.opCount; + + findOneCounter.updateFrom(other.findOneCounter); + updateCounter.updateFrom(other.updateCounter); + insertCounter.updateFrom(other.insertCounter); + deleteCounter.updateFrom(other.deleteCounter); + queryCounter.updateFrom(other.queryCounter); + commandCounter.updateFrom(other.commandCounter); + + for (size_t i = 0; i < other.trappedErrors.size(); ++i) + trappedErrors.push_back(other.trappedErrors[i]); +} - ops = BSONObj(); +BenchRunConfig::BenchRunConfig() { + initializeToDefaults(); +} - throwGLE = false; - breakOnTrap = true; - randomSeed = 1314159265358979323; - } +void BenchRunConfig::initializeToDefaults() { + host = "localhost"; + db = "test"; + username = ""; + password = ""; - BenchRunConfig *BenchRunConfig::createFromBson( const BSONObj &args ) { - BenchRunConfig *config = new BenchRunConfig(); - config->initializeFromBson( args ); - return config; - } + parallel = 1; + seconds = 1.0; + hideResults = true; + handleErrors = false; + hideErrors = false; - void BenchRunConfig::initializeFromBson( const BSONObj &args ) { - initializeToDefaults(); - - if ( args["host"].type() == String ) - this->host = args["host"].String(); - if ( args["db"].type() == String ) - this->db = args["db"].String(); - if ( args["username"].type() == String ) - this->username = args["username"].String(); - if ( args["password"].type() == String ) - this->password = args["password"].String(); - - if ( args["parallel"].isNumber() ) - this->parallel = args["parallel"].numberInt(); - if ( args["randomSeed"].isNumber() ) - this->randomSeed = args["randomSeed"].numberInt(); - if ( args["seconds"].isNumber() ) - this->seconds = args["seconds"].number(); - if ( ! args["hideResults"].eoo() ) - this->hideResults = args["hideResults"].trueValue(); - if ( ! args["handleErrors"].eoo() ) - this->handleErrors = args["handleErrors"].trueValue(); - if ( ! args["hideErrors"].eoo() ) - this->hideErrors = args["hideErrors"].trueValue(); - if ( ! args["throwGLE"].eoo() ) - this->throwGLE = args["throwGLE"].trueValue(); - if ( ! args["breakOnTrap"].eoo() ) - this->breakOnTrap = args["breakOnTrap"].trueValue(); - - uassert(16164, "loopCommands config not supported", args["loopCommands"].eoo()); - - if ( ! args["trapPattern"].eoo() ){ - const char* regex = args["trapPattern"].regex(); - const char* flags = args["trapPattern"].regexFlags(); - this->trapPattern = std::shared_ptr< pcrecpp::RE >( new pcrecpp::RE( regex, flags2options( flags ) ) ); - } + trapPattern.reset(); + noTrapPattern.reset(); + watchPattern.reset(); + noWatchPattern.reset(); - if ( ! args["noTrapPattern"].eoo() ){ - const char* regex = args["noTrapPattern"].regex(); - const char* flags = args["noTrapPattern"].regexFlags(); - this->noTrapPattern = std::shared_ptr< pcrecpp::RE >( new pcrecpp::RE( regex, flags2options( flags ) ) ); - } + ops = BSONObj(); - if ( ! args["watchPattern"].eoo() ){ - const char* regex = args["watchPattern"].regex(); - const char* flags = args["watchPattern"].regexFlags(); - this->watchPattern = std::shared_ptr< pcrecpp::RE >( new pcrecpp::RE( regex, flags2options( flags ) ) ); - } + throwGLE = false; + breakOnTrap = true; + randomSeed = 1314159265358979323; +} - if ( ! args["noWatchPattern"].eoo() ){ - const char* regex = args["noWatchPattern"].regex(); - const char* flags = args["noWatchPattern"].regexFlags(); - this->noWatchPattern = std::shared_ptr< pcrecpp::RE >( new pcrecpp::RE( regex, flags2options( flags ) ) ); - } +BenchRunConfig* BenchRunConfig::createFromBson(const BSONObj& args) { + BenchRunConfig* config = new BenchRunConfig(); + config->initializeFromBson(args); + return config; +} - this->ops = args["ops"].Obj().getOwned(); - } +void BenchRunConfig::initializeFromBson(const BSONObj& args) { + initializeToDefaults(); + + if (args["host"].type() == String) + this->host = args["host"].String(); + if (args["db"].type() == String) + this->db = args["db"].String(); + if (args["username"].type() == String) + this->username = args["username"].String(); + if (args["password"].type() == String) + this->password = args["password"].String(); + + if (args["parallel"].isNumber()) + this->parallel = args["parallel"].numberInt(); + if (args["randomSeed"].isNumber()) + this->randomSeed = args["randomSeed"].numberInt(); + if (args["seconds"].isNumber()) + this->seconds = args["seconds"].number(); + if (!args["hideResults"].eoo()) + this->hideResults = args["hideResults"].trueValue(); + if (!args["handleErrors"].eoo()) + this->handleErrors = args["handleErrors"].trueValue(); + if (!args["hideErrors"].eoo()) + this->hideErrors = args["hideErrors"].trueValue(); + if (!args["throwGLE"].eoo()) + this->throwGLE = args["throwGLE"].trueValue(); + if (!args["breakOnTrap"].eoo()) + this->breakOnTrap = args["breakOnTrap"].trueValue(); + + uassert(16164, "loopCommands config not supported", args["loopCommands"].eoo()); + + if (!args["trapPattern"].eoo()) { + const char* regex = args["trapPattern"].regex(); + const char* flags = args["trapPattern"].regexFlags(); + this->trapPattern = + std::shared_ptr<pcrecpp::RE>(new pcrecpp::RE(regex, flags2options(flags))); + } + + if (!args["noTrapPattern"].eoo()) { + const char* regex = args["noTrapPattern"].regex(); + const char* flags = args["noTrapPattern"].regexFlags(); + this->noTrapPattern = + std::shared_ptr<pcrecpp::RE>(new pcrecpp::RE(regex, flags2options(flags))); + } + + if (!args["watchPattern"].eoo()) { + const char* regex = args["watchPattern"].regex(); + const char* flags = args["watchPattern"].regexFlags(); + this->watchPattern = + std::shared_ptr<pcrecpp::RE>(new pcrecpp::RE(regex, flags2options(flags))); + } + + if (!args["noWatchPattern"].eoo()) { + const char* regex = args["noWatchPattern"].regex(); + const char* flags = args["noWatchPattern"].regexFlags(); + this->noWatchPattern = + std::shared_ptr<pcrecpp::RE>(new pcrecpp::RE(regex, flags2options(flags))); + } + + this->ops = args["ops"].Obj().getOwned(); +} - DBClientBase *BenchRunConfig::createConnection() const { - const ConnectionString connectionString = uassertStatusOK(ConnectionString::parse(host)); +DBClientBase* BenchRunConfig::createConnection() const { + const ConnectionString connectionString = uassertStatusOK(ConnectionString::parse(host)); - std::string errorMessage; - DBClientBase *connection = connectionString.connect(errorMessage); - uassert( 16158, errorMessage, connection != NULL ); + std::string errorMessage; + DBClientBase* connection = connectionString.connect(errorMessage); + uassert(16158, errorMessage, connection != NULL); - return connection; - } + return connection; +} - BenchRunState::BenchRunState( unsigned numWorkers ) - : _mutex(), - _numUnstartedWorkers( numWorkers ), - _numActiveWorkers( 0 ), - _isShuttingDown( 0 ), - _isCollectingStats( 0 ) { - } +BenchRunState::BenchRunState(unsigned numWorkers) + : _mutex(), + _numUnstartedWorkers(numWorkers), + _numActiveWorkers(0), + _isShuttingDown(0), + _isCollectingStats(0) {} - BenchRunState::~BenchRunState() { - wassert(_numActiveWorkers == 0 && _numUnstartedWorkers == 0); - } +BenchRunState::~BenchRunState() { + wassert(_numActiveWorkers == 0 && _numUnstartedWorkers == 0); +} - void BenchRunState::waitForState(State awaitedState) { - stdx::unique_lock<stdx::mutex> lk(_mutex); +void BenchRunState::waitForState(State awaitedState) { + stdx::unique_lock<stdx::mutex> lk(_mutex); - switch ( awaitedState ) { + switch (awaitedState) { case BRS_RUNNING: - while ( _numUnstartedWorkers > 0 ) { - massert( 16147, "Already finished.", _numUnstartedWorkers + _numActiveWorkers > 0 ); - _stateChangeCondition.wait( lk ); + while (_numUnstartedWorkers > 0) { + massert(16147, "Already finished.", _numUnstartedWorkers + _numActiveWorkers > 0); + _stateChangeCondition.wait(lk); } break; case BRS_FINISHED: - while ( _numUnstartedWorkers + _numActiveWorkers > 0 ) { - _stateChangeCondition.wait( lk ); + while (_numUnstartedWorkers + _numActiveWorkers > 0) { + _stateChangeCondition.wait(lk); } break; default: - msgasserted(16152, mongoutils::str::stream() << "Cannot wait for state " << awaitedState); - } + msgasserted(16152, + mongoutils::str::stream() << "Cannot wait for state " << awaitedState); } +} - void BenchRunState::tellWorkersToFinish() { - _isShuttingDown.store( 1 ); - } +void BenchRunState::tellWorkersToFinish() { + _isShuttingDown.store(1); +} - void BenchRunState::tellWorkersToCollectStats() { - _isCollectingStats.store( 1 ); - } +void BenchRunState::tellWorkersToCollectStats() { + _isCollectingStats.store(1); +} - void BenchRunState::assertFinished() { - stdx::lock_guard<stdx::mutex> lk(_mutex); - verify(0 == _numUnstartedWorkers + _numActiveWorkers); - } +void BenchRunState::assertFinished() { + stdx::lock_guard<stdx::mutex> lk(_mutex); + verify(0 == _numUnstartedWorkers + _numActiveWorkers); +} - bool BenchRunState::shouldWorkerFinish() { - return (_isShuttingDown.loadRelaxed() == 1); - } +bool BenchRunState::shouldWorkerFinish() { + return (_isShuttingDown.loadRelaxed() == 1); +} - bool BenchRunState::shouldWorkerCollectStats() { - return (_isCollectingStats.loadRelaxed() == 1); - } +bool BenchRunState::shouldWorkerCollectStats() { + return (_isCollectingStats.loadRelaxed() == 1); +} - void BenchRunState::onWorkerStarted() { - stdx::lock_guard<stdx::mutex> lk(_mutex); - verify( _numUnstartedWorkers > 0 ); - --_numUnstartedWorkers; - ++_numActiveWorkers; - if (_numUnstartedWorkers == 0) { - _stateChangeCondition.notify_all(); - } +void BenchRunState::onWorkerStarted() { + stdx::lock_guard<stdx::mutex> lk(_mutex); + verify(_numUnstartedWorkers > 0); + --_numUnstartedWorkers; + ++_numActiveWorkers; + if (_numUnstartedWorkers == 0) { + _stateChangeCondition.notify_all(); } +} - void BenchRunState::onWorkerFinished() { - stdx::lock_guard<stdx::mutex> lk(_mutex); - verify( _numActiveWorkers > 0 ); - --_numActiveWorkers; - if (_numActiveWorkers + _numUnstartedWorkers == 0) { - _stateChangeCondition.notify_all(); - } +void BenchRunState::onWorkerFinished() { + stdx::lock_guard<stdx::mutex> lk(_mutex); + verify(_numActiveWorkers > 0); + --_numActiveWorkers; + if (_numActiveWorkers + _numUnstartedWorkers == 0) { + _stateChangeCondition.notify_all(); } +} - BSONObj benchStart( const BSONObj& , void* ); - BSONObj benchFinish( const BSONObj& , void* ); +BSONObj benchStart(const BSONObj&, void*); +BSONObj benchFinish(const BSONObj&, void*); - static bool _hasSpecial( const BSONObj& obj ) { - BSONObjIterator i( obj ); - while ( i.more() ) { - BSONElement e = i.next(); - if ( e.fieldName()[0] == '#' ) - return true; +static bool _hasSpecial(const BSONObj& obj) { + BSONObjIterator i(obj); + while (i.more()) { + BSONElement e = i.next(); + if (e.fieldName()[0] == '#') + return true; - if ( ! e.isABSONObj() ) - continue; + if (!e.isABSONObj()) + continue; - if ( _hasSpecial( e.Obj() ) ) - return true; - } - return false; + if (_hasSpecial(e.Obj())) + return true; } + return false; +} - static BSONObj fixQuery( const BSONObj& obj, BsonTemplateEvaluator& btl ) { - if ( ! _hasSpecial( obj ) ) - return obj; - BSONObjBuilder b( obj.objsize() + 128 ); +static BSONObj fixQuery(const BSONObj& obj, BsonTemplateEvaluator& btl) { + if (!_hasSpecial(obj)) + return obj; + BSONObjBuilder b(obj.objsize() + 128); - verify(BsonTemplateEvaluator::StatusSuccess == btl.evaluate(obj, b)); - return b.obj(); - } + verify(BsonTemplateEvaluator::StatusSuccess == btl.evaluate(obj, b)); + return b.obj(); +} - BenchRunWorker::BenchRunWorker(size_t id, const BenchRunConfig *config, BenchRunState *brState, - int64_t randomSeed) - : _id(id), _config(config), _brState(brState), _randomSeed(randomSeed) { - } +BenchRunWorker::BenchRunWorker(size_t id, + const BenchRunConfig* config, + BenchRunState* brState, + int64_t randomSeed) + : _id(id), _config(config), _brState(brState), _randomSeed(randomSeed) {} - BenchRunWorker::~BenchRunWorker() {} +BenchRunWorker::~BenchRunWorker() {} - void BenchRunWorker::start() { - stdx::thread(stdx::bind(&BenchRunWorker::run, this)); - } +void BenchRunWorker::start() { + stdx::thread(stdx::bind(&BenchRunWorker::run, this)); +} - bool BenchRunWorker::shouldStop() const { - return _brState->shouldWorkerFinish(); - } +bool BenchRunWorker::shouldStop() const { + return _brState->shouldWorkerFinish(); +} - bool BenchRunWorker::shouldCollectStats() const { - return _brState->shouldWorkerCollectStats(); - } +bool BenchRunWorker::shouldCollectStats() const { + return _brState->shouldWorkerCollectStats(); +} - void doNothing(const BSONObj&) { } +void doNothing(const BSONObj&) {} - void BenchRunWorker::generateLoadOnConnection( DBClientBase* conn ) { - verify( conn ); - long long count = 0; - mongo::Timer timer; +void BenchRunWorker::generateLoadOnConnection(DBClientBase* conn) { + verify(conn); + long long count = 0; + mongo::Timer timer; - BsonTemplateEvaluator bsonTemplateEvaluator(_randomSeed); - invariant(bsonTemplateEvaluator.setId(_id) == BsonTemplateEvaluator::StatusSuccess); + BsonTemplateEvaluator bsonTemplateEvaluator(_randomSeed); + invariant(bsonTemplateEvaluator.setId(_id) == BsonTemplateEvaluator::StatusSuccess); - if (_config->username != "") { - string errmsg; - if (!conn->auth("admin", _config->username, _config->password, errmsg)) { - uasserted(15931, "Authenticating to connection for _benchThread failed: " + errmsg); - } + if (_config->username != "") { + string errmsg; + if (!conn->auth("admin", _config->username, _config->password, errmsg)) { + uasserted(15931, "Authenticating to connection for _benchThread failed: " + errmsg); } + } - while ( !shouldStop() ) { - BSONObjIterator i( _config->ops ); - while ( i.more() ) { - - if ( shouldStop() ) break; - auto& stats = shouldCollectStats() ? _stats : _statsBlackHole; - BSONElement e = i.next(); - - string ns = e["ns"].String(); - string op = e["op"].String(); + while (!shouldStop()) { + BSONObjIterator i(_config->ops); + while (i.more()) { + if (shouldStop()) + break; + auto& stats = shouldCollectStats() ? _stats : _statsBlackHole; + BSONElement e = i.next(); - int delay = e["delay"].eoo() ? 0 : e["delay"].Int(); + string ns = e["ns"].String(); + string op = e["op"].String(); - // Let's default to writeCmd == false. - bool useWriteCmd = e["writeCmd"].eoo() ? false : - e["writeCmd"].Bool(); + int delay = e["delay"].eoo() ? 0 : e["delay"].Int(); - BSONObj context = e["context"].eoo() ? BSONObj() : e["context"].Obj(); + // Let's default to writeCmd == false. + bool useWriteCmd = e["writeCmd"].eoo() ? false : e["writeCmd"].Bool(); - unique_ptr<Scope> scope; - ScriptingFunction scopeFunc = 0; - BSONObj scopeObj; + BSONObj context = e["context"].eoo() ? BSONObj() : e["context"].Obj(); - bool check = ! e["check"].eoo(); - if( check ){ - if ( e["check"].type() == CodeWScope || e["check"].type() == Code || e["check"].type() == String ) { - scope = globalScriptEngine->getPooledScope(NULL, ns, "benchrun"); - verify( scope.get() ); + unique_ptr<Scope> scope; + ScriptingFunction scopeFunc = 0; + BSONObj scopeObj; - if ( e.type() == CodeWScope ) { - scopeFunc = scope->createFunction( e["check"].codeWScopeCode() ); - scopeObj = BSONObj( e.codeWScopeScopeDataUnsafe() ); - } - else { - scopeFunc = scope->createFunction( e["check"].valuestr() ); - } + bool check = !e["check"].eoo(); + if (check) { + if (e["check"].type() == CodeWScope || e["check"].type() == Code || + e["check"].type() == String) { + scope = globalScriptEngine->getPooledScope(NULL, ns, "benchrun"); + verify(scope.get()); - scope->init( &scopeObj ); - invariant(scopeFunc); - } - else { - warning() << "Invalid check type detected in benchRun op : " << e << endl; - check = false; + if (e.type() == CodeWScope) { + scopeFunc = scope->createFunction(e["check"].codeWScopeCode()); + scopeObj = BSONObj(e.codeWScopeScopeDataUnsafe()); + } else { + scopeFunc = scope->createFunction(e["check"].valuestr()); } + + scope->init(&scopeObj); + invariant(scopeFunc); + } else { + warning() << "Invalid check type detected in benchRun op : " << e << endl; + check = false; } + } - try { - if ( op == "nop") { - // do nothing + try { + if (op == "nop") { + // do nothing + } else if (op == "findOne") { + BSONObj result; + { + BenchRunEventTrace _bret(&stats.findOneCounter); + result = + conn->findOne(ns, fixQuery(e["query"].Obj(), bsonTemplateEvaluator)); } - else if ( op == "findOne" ) { - - BSONObj result; - { - BenchRunEventTrace _bret(&stats.findOneCounter); - result = conn->findOne( ns , fixQuery( e["query"].Obj(), - bsonTemplateEvaluator ) ); - } - if( check ){ - int err = scope->invoke( scopeFunc , 0 , &result, 1000 * 60 , false ); - if( err ){ - log() << "Error checking in benchRun thread [findOne]" << causedBy( scope->getError() ) << endl; + if (check) { + int err = scope->invoke(scopeFunc, 0, &result, 1000 * 60, false); + if (err) { + log() << "Error checking in benchRun thread [findOne]" + << causedBy(scope->getError()) << endl; - stats.errCount++; + stats.errCount++; - return; - } + return; } + } - if( ! _config->hideResults || e["showResult"].trueValue() ) log() << "Result from benchRun thread [findOne] : " << result << endl; - + if (!_config->hideResults || e["showResult"].trueValue()) + log() << "Result from benchRun thread [findOne] : " << result << endl; + + } else if (op == "command") { + bool ok; + BSONObj result; + { + BenchRunEventTrace _bret(&stats.commandCounter); + ok = conn->runCommand(ns, + fixQuery(e["command"].Obj(), bsonTemplateEvaluator), + result, + e["options"].numberInt()); } - else if ( op == "command" ) { - - bool ok; - BSONObj result; - { - BenchRunEventTrace _bret(&stats.commandCounter); - ok = conn->runCommand( ns, - fixQuery( e["command"].Obj(), - bsonTemplateEvaluator ), - result, - e["options"].numberInt()); - } - if (!ok) { - stats.errCount++; - } - else if( check ){ - int err = scope->invoke( scopeFunc , 0 , &result, 1000 * 60 , false ); - if( err ){ - log() << "Error checking in benchRun thread [command]" << causedBy( scope->getError() ) << endl; + if (!ok) { + stats.errCount++; + } else if (check) { + int err = scope->invoke(scopeFunc, 0, &result, 1000 * 60, false); + if (err) { + log() << "Error checking in benchRun thread [command]" + << causedBy(scope->getError()) << endl; - stats.errCount++; + stats.errCount++; - return; - } + return; } + } - if( ! _config->hideResults || e["showResult"].trueValue() ) log() << "Result from benchRun thread [command] : " << result << endl; - + if (!_config->hideResults || e["showResult"].trueValue()) + log() << "Result from benchRun thread [command] : " << result << endl; + + } else if (op == "find" || op == "query") { + int limit = e["limit"].eoo() ? 0 : e["limit"].numberInt(); + int skip = e["skip"].eoo() ? 0 : e["skip"].Int(); + int options = e["options"].eoo() ? 0 : e["options"].Int(); + int batchSize = e["batchSize"].eoo() ? 0 : e["batchSize"].Int(); + BSONObj filter = e["filter"].eoo() ? BSONObj() : e["filter"].Obj(); + int expected = e["expected"].eoo() ? -1 : e["expected"].Int(); + + unique_ptr<DBClientCursor> cursor; + int count; + + BSONObj fixedQuery = fixQuery(e["query"].Obj(), bsonTemplateEvaluator); + + // use special query function for exhaust query option + if (options & QueryOption_Exhaust) { + BenchRunEventTrace _bret(&stats.queryCounter); + stdx::function<void(const BSONObj&)> castedDoNothing(doNothing); + count = conn->query(castedDoNothing, ns, fixedQuery, &filter, options); + } else { + BenchRunEventTrace _bret(&stats.queryCounter); + cursor = + conn->query(ns, fixedQuery, limit, skip, &filter, options, batchSize); + count = cursor->itcount(); } - else if( op == "find" || op == "query" ) { - int limit = e["limit"].eoo() ? 0 : e["limit"].numberInt(); - int skip = e["skip"].eoo() ? 0 : e["skip"].Int(); - int options = e["options"].eoo() ? 0 : e["options"].Int(); - int batchSize = e["batchSize"].eoo() ? 0 : e["batchSize"].Int(); - BSONObj filter = e["filter"].eoo() ? BSONObj() : e["filter"].Obj(); - int expected = e["expected"].eoo() ? -1 : e["expected"].Int(); + if (expected >= 0 && count != expected) { + cout << "bench query on: " << ns << " expected: " << expected + << " got: " << count << endl; + verify(false); + } - unique_ptr<DBClientCursor> cursor; - int count; + if (check) { + BSONObj thisValue = BSON("count" << count << "context" << context); + int err = scope->invoke(scopeFunc, 0, &thisValue, 1000 * 60, false); + if (err) { + log() << "Error checking in benchRun thread [find]" + << causedBy(scope->getError()) << endl; - BSONObj fixedQuery = fixQuery(e["query"].Obj(), bsonTemplateEvaluator); + stats.errCount++; - // use special query function for exhaust query option - if (options & QueryOption_Exhaust) { - BenchRunEventTrace _bret(&stats.queryCounter); - stdx::function<void (const BSONObj&)> castedDoNothing(doNothing); - count = conn->query(castedDoNothing, ns, fixedQuery, &filter, options); - } - else { - BenchRunEventTrace _bret(&stats.queryCounter); - cursor = conn->query(ns, fixedQuery, limit, skip, &filter, options, - batchSize); - count = cursor->itcount(); + return; } + } - if ( expected >= 0 && count != expected ) { - cout << "bench query on: " << ns << " expected: " << expected << " got: " << count << endl; - verify(false); + if (!_config->hideResults || e["showResult"].trueValue()) + log() << "Result from benchRun thread [query] : " << count << endl; + + } else if (op == "update") { + bool multi = e["multi"].trueValue(); + bool upsert = e["upsert"].trueValue(); + BSONObj queryOrginal = e["query"].eoo() ? BSONObj() : e["query"].Obj(); + BSONObj updateOriginal = e["update"].Obj(); + BSONObj result; + bool safe = e["safe"].trueValue(); + + { + BenchRunEventTrace _bret(&stats.updateCounter); + BSONObj query = fixQuery(queryOrginal, bsonTemplateEvaluator); + BSONObj update = fixQuery(updateOriginal, bsonTemplateEvaluator); + + if (useWriteCmd) { + // TODO: Replace after SERVER-11774. + BSONObjBuilder builder; + builder.append("update", nsToCollectionSubstring(ns)); + BSONArrayBuilder docBuilder(builder.subarrayStart("updates")); + docBuilder.append(BSON("q" << query << "u" << update << "multi" << multi + << "upsert" << upsert)); + docBuilder.done(); + conn->runCommand( + nsToDatabaseSubstring(ns).toString(), builder.done(), result); + } else { + conn->update(ns, query, update, upsert, multi); + if (safe) + result = conn->getLastErrorDetailed(); } + } - if( check ){ - BSONObj thisValue = BSON( "count" << count << "context" << context ); - int err = scope->invoke( scopeFunc , 0 , &thisValue, 1000 * 60 , false ); - if( err ){ - log() << "Error checking in benchRun thread [find]" << causedBy( scope->getError() ) << endl; + if (safe) { + if (check) { + int err = scope->invoke(scopeFunc, 0, &result, 1000 * 60, false); + if (err) { + log() << "Error checking in benchRun thread [update]" + << causedBy(scope->getError()) << endl; stats.errCount++; @@ -514,464 +560,406 @@ namespace mongo { } } - if( ! _config->hideResults || e["showResult"].trueValue() ) log() << "Result from benchRun thread [query] : " << count << endl; + if (!_config->hideResults || e["showResult"].trueValue()) + log() << "Result from benchRun thread [safe update] : " << result + << endl; + if (!result["err"].eoo() && result["err"].type() == String && + (_config->throwGLE || e["throwGLE"].trueValue())) + throw DBException((string) "From benchRun GLE" + + causedBy(result["err"].String()), + result["code"].eoo() ? 0 : result["code"].Int()); } - else if( op == "update" ) { - - bool multi = e["multi"].trueValue(); - bool upsert = e["upsert"].trueValue(); - BSONObj queryOrginal = e["query"].eoo() ? BSONObj() : e["query"].Obj(); - BSONObj updateOriginal = e["update"].Obj(); - BSONObj result; - bool safe = e["safe"].trueValue(); - - { - BenchRunEventTrace _bret(&stats.updateCounter); - BSONObj query = fixQuery(queryOrginal, bsonTemplateEvaluator); - BSONObj update = fixQuery(updateOriginal, bsonTemplateEvaluator); - - if (useWriteCmd) { - // TODO: Replace after SERVER-11774. - BSONObjBuilder builder; - builder.append("update", - nsToCollectionSubstring(ns)); - BSONArrayBuilder docBuilder( - builder.subarrayStart("updates")); - docBuilder.append(BSON("q" << query << - "u" << update << - "multi" << multi << - "upsert" << upsert)); - docBuilder.done(); - conn->runCommand( - nsToDatabaseSubstring(ns).toString(), - builder.done(), result); - } - else { - conn->update(ns, query, update, - upsert , multi); - if (safe) - result = conn->getLastErrorDetailed(); - } + } else if (op == "insert") { + bool safe = e["safe"].trueValue(); + BSONObj result; + + { + BenchRunEventTrace _bret(&stats.insertCounter); + + BSONObj insertDoc = fixQuery(e["doc"].Obj(), bsonTemplateEvaluator); + + if (useWriteCmd) { + BSONObjBuilder builder; + builder.append("insert", nsToCollectionSubstring(ns)); + BSONArrayBuilder docBuilder(builder.subarrayStart("documents")); + docBuilder.append(insertDoc); + docBuilder.done(); + // TODO: Replace after SERVER-11774. + conn->runCommand( + nsToDatabaseSubstring(ns).toString(), builder.done(), result); + } else { + conn->insert(ns, insertDoc); + if (safe) + result = conn->getLastErrorDetailed(); } + } - if( safe ){ - if( check ){ - int err = scope->invoke( scopeFunc , 0 , &result, 1000 * 60 , false ); - if( err ){ - log() << "Error checking in benchRun thread [update]" << causedBy( scope->getError() ) << endl; + if (safe) { + if (check) { + int err = scope->invoke(scopeFunc, 0, &result, 1000 * 60, false); + if (err) { + log() << "Error checking in benchRun thread [insert]" + << causedBy(scope->getError()) << endl; - stats.errCount++; + stats.errCount++; - return; - } + return; } + } - if( ! _config->hideResults || e["showResult"].trueValue() ) log() << "Result from benchRun thread [safe update] : " << result << endl; + if (!_config->hideResults || e["showResult"].trueValue()) + log() << "Result from benchRun thread [safe insert] : " << result + << endl; - if( ! result["err"].eoo() && result["err"].type() == String && ( _config->throwGLE || e["throwGLE"].trueValue() ) ) - throw DBException( (string)"From benchRun GLE" + causedBy( result["err"].String() ), - result["code"].eoo() ? 0 : result["code"].Int() ); - } + if (!result["err"].eoo() && result["err"].type() == String && + (_config->throwGLE || e["throwGLE"].trueValue())) + throw DBException((string) "From benchRun GLE" + + causedBy(result["err"].String()), + result["code"].eoo() ? 0 : result["code"].Int()); } - else if( op == "insert" ) { - bool safe = e["safe"].trueValue(); - BSONObj result; - - { - BenchRunEventTrace _bret(&stats.insertCounter); - - BSONObj insertDoc = fixQuery(e["doc"].Obj(), bsonTemplateEvaluator); - - if (useWriteCmd) { - BSONObjBuilder builder; - builder.append("insert", nsToCollectionSubstring(ns)); - BSONArrayBuilder docBuilder( - builder.subarrayStart("documents")); - docBuilder.append(insertDoc); - docBuilder.done(); - // TODO: Replace after SERVER-11774. - conn->runCommand( - nsToDatabaseSubstring(ns).toString(), - builder.done(), result); - } - else { - conn->insert(ns, insertDoc); - if (safe) - result = conn->getLastErrorDetailed(); - } + } else if (op == "delete" || op == "remove") { + bool multi = e["multi"].eoo() ? true : e["multi"].trueValue(); + BSONObj query = e["query"].eoo() ? BSONObj() : e["query"].Obj(); + bool safe = e["safe"].trueValue(); + BSONObj result; + { + BenchRunEventTrace _bret(&stats.deleteCounter); + BSONObj predicate = fixQuery(query, bsonTemplateEvaluator); + if (useWriteCmd) { + // TODO: Replace after SERVER-11774. + BSONObjBuilder builder; + builder.append("delete", nsToCollectionSubstring(ns)); + BSONArrayBuilder docBuilder(builder.subarrayStart("deletes")); + int limit = (multi == true) ? 0 : 1; + docBuilder.append(BSON("q" << predicate << "limit" << limit)); + docBuilder.done(); + conn->runCommand( + nsToDatabaseSubstring(ns).toString(), builder.done(), result); + } else { + conn->remove(ns, predicate, !multi); + if (safe) + result = conn->getLastErrorDetailed(); } + } - if( safe ){ - if( check ){ - int err = scope->invoke( scopeFunc , 0 , &result, 1000 * 60 , false ); - if( err ){ - log() << "Error checking in benchRun thread [insert]" << causedBy( scope->getError() ) << endl; - - stats.errCount++; - - return; - } - } + if (safe) { + if (check) { + int err = scope->invoke(scopeFunc, 0, &result, 1000 * 60, false); + if (err) { + log() << "Error checking in benchRun thread [delete]" + << causedBy(scope->getError()) << endl; - if( ! _config->hideResults || e["showResult"].trueValue() ) log() << "Result from benchRun thread [safe insert] : " << result << endl; + stats.errCount++; - if( ! result["err"].eoo() && result["err"].type() == String && ( _config->throwGLE || e["throwGLE"].trueValue() ) ) - throw DBException( (string)"From benchRun GLE" + causedBy( result["err"].String() ), - result["code"].eoo() ? 0 : result["code"].Int() ); - } - } - else if( op == "delete" || op == "remove" ) { - - bool multi = e["multi"].eoo() ? true : e["multi"].trueValue(); - BSONObj query = e["query"].eoo() ? BSONObj() : e["query"].Obj(); - bool safe = e["safe"].trueValue(); - BSONObj result; - { - BenchRunEventTrace _bret(&stats.deleteCounter); - BSONObj predicate = fixQuery(query, bsonTemplateEvaluator); - if (useWriteCmd) { - - // TODO: Replace after SERVER-11774. - BSONObjBuilder builder; - builder.append("delete", - nsToCollectionSubstring(ns)); - BSONArrayBuilder docBuilder( - builder.subarrayStart("deletes")); - int limit = (multi == true) ? 0 : 1; - docBuilder.append( - BSON("q" << predicate << - "limit" << limit)); - docBuilder.done(); - conn->runCommand( - nsToDatabaseSubstring(ns).toString(), - builder.done(), result); - } - else { - conn->remove(ns, predicate, !multi); - if (safe) - result = conn->getLastErrorDetailed(); + return; } } - if( safe ){ - if( check ){ - int err = scope->invoke( scopeFunc , 0 , &result, 1000 * 60 , false ); - if( err ){ - log() << "Error checking in benchRun thread [delete]" << causedBy( scope->getError() ) << endl; - - stats.errCount++; + if (!_config->hideResults || e["showResult"].trueValue()) + log() << "Result from benchRun thread [safe remove] : " << result + << endl; - return; - } - } + if (!result["err"].eoo() && result["err"].type() == String && + (_config->throwGLE || e["throwGLE"].trueValue())) + throw DBException((string) "From benchRun GLE " + + causedBy(result["err"].String()), + result["code"].eoo() ? 0 : result["code"].Int()); + } + } else if (op == "createIndex") { + conn->ensureIndex(ns, e["key"].Obj(), false, "", false); + } else if (op == "dropIndex") { + conn->dropIndex(ns, e["key"].Obj()); + } else if (op == "let") { + string target = e["target"].eoo() ? string() : e["target"].String(); + BSONElement value = e["value"].eoo() ? BSONElement() : e["value"]; + BSONObjBuilder valBuilder; + BSONObjBuilder templateBuilder; + valBuilder.append(value); + bsonTemplateEvaluator.evaluate(valBuilder.done(), templateBuilder); + bsonTemplateEvaluator.setVariable(target, + templateBuilder.done().firstElement()); + } else { + log() << "don't understand op: " << op << endl; + stats.error = true; + return; + } + // Count 1 for total ops. Successfully got through the try phrase + stats.opCount++; + } catch (DBException& ex) { + if (!_config->hideErrors || e["showError"].trueValue()) { + bool yesWatch = + (_config->watchPattern && _config->watchPattern->FullMatch(ex.what())); + bool noWatch = + (_config->noWatchPattern && _config->noWatchPattern->FullMatch(ex.what())); + + if ((!_config->watchPattern && _config->noWatchPattern && + !noWatch) || // If we're just ignoring things + (!_config->noWatchPattern && _config->watchPattern && + yesWatch) || // If we're just watching things + (_config->watchPattern && _config->noWatchPattern && yesWatch && !noWatch)) + log() << "Error in benchRun thread for op " << e << causedBy(ex) << endl; + } - if( ! _config->hideResults || e["showResult"].trueValue() ) log() << "Result from benchRun thread [safe remove] : " << result << endl; + bool yesTrap = (_config->trapPattern && _config->trapPattern->FullMatch(ex.what())); + bool noTrap = + (_config->noTrapPattern && _config->noTrapPattern->FullMatch(ex.what())); - if( ! result["err"].eoo() && result["err"].type() == String && ( _config->throwGLE || e["throwGLE"].trueValue() ) ) - throw DBException( (string)"From benchRun GLE " + causedBy( result["err"].String() ), - result["code"].eoo() ? 0 : result["code"].Int() ); - } - } - else if ( op == "createIndex" ) { - conn->ensureIndex(ns, e["key"].Obj(), false, "", false); + if ((!_config->trapPattern && _config->noTrapPattern && !noTrap) || + (!_config->noTrapPattern && _config->trapPattern && yesTrap) || + (_config->trapPattern && _config->noTrapPattern && yesTrap && !noTrap)) { + { + stats.trappedErrors.push_back( + BSON("error" << ex.what() << "op" << e << "count" << count)); } - else if ( op == "dropIndex" ) { - conn->dropIndex( ns , e["key"].Obj() ); - } - else if( op == "let" ) { - string target = e["target"].eoo() ? string() : e["target"].String(); - BSONElement value = e["value"].eoo() ? BSONElement() : e["value"]; - BSONObjBuilder valBuilder; - BSONObjBuilder templateBuilder; - valBuilder.append(value); - bsonTemplateEvaluator.evaluate(valBuilder.done(), templateBuilder); - bsonTemplateEvaluator.setVariable(target, templateBuilder.done().firstElement()); - } - else { - log() << "don't understand op: " << op << endl; - stats.error = true; + if (_config->breakOnTrap) return; - } - // Count 1 for total ops. Successfully got through the try phrase - stats.opCount++; } - catch( DBException& ex ){ - if( ! _config->hideErrors || e["showError"].trueValue() ){ - - bool yesWatch = ( _config->watchPattern && _config->watchPattern->FullMatch( ex.what() ) ); - bool noWatch = ( _config->noWatchPattern && _config->noWatchPattern->FullMatch( ex.what() ) ); + if (!_config->handleErrors && !e["handleError"].trueValue()) + return; + + stats.errCount++; + } catch (...) { + if (!_config->hideErrors || e["showError"].trueValue()) + log() << "Error in benchRun thread caused by unknown error for op " << e + << endl; + if (!_config->handleErrors && !e["handleError"].trueValue()) + return; + + stats.errCount++; + } - if( ( ! _config->watchPattern && _config->noWatchPattern && ! noWatch ) || // If we're just ignoring things - ( ! _config->noWatchPattern && _config->watchPattern && yesWatch ) || // If we're just watching things - ( _config->watchPattern && _config->noWatchPattern && yesWatch && ! noWatch ) ) - log() << "Error in benchRun thread for op " << e << causedBy( ex ) << endl; - } + if (++count % 100 == 0 && !useWriteCmd) { + conn->getLastError(); + } - bool yesTrap = ( _config->trapPattern && _config->trapPattern->FullMatch( ex.what() ) ); - bool noTrap = ( _config->noTrapPattern && _config->noTrapPattern->FullMatch( ex.what() ) ); + if (delay > 0) + sleepmillis(delay); + } + } - if( ( ! _config->trapPattern && _config->noTrapPattern && ! noTrap ) || - ( ! _config->noTrapPattern && _config->trapPattern && yesTrap ) || - ( _config->trapPattern && _config->noTrapPattern && yesTrap && ! noTrap ) ){ - { - stats.trappedErrors.push_back( BSON( "error" << ex.what() << "op" << e << "count" << count ) ); - } - if( _config->breakOnTrap ) return; - } - if( ! _config->handleErrors && ! e["handleError"].trueValue() ) return; + conn->getLastError(); +} - stats.errCount++; - } - catch( ... ){ - if( ! _config->hideErrors || e["showError"].trueValue() ) log() << "Error in benchRun thread caused by unknown error for op " << e << endl; - if( ! _config->handleErrors && ! e["handleError"].trueValue() ) return; +namespace { +class BenchRunWorkerStateGuard { + MONGO_DISALLOW_COPYING(BenchRunWorkerStateGuard); - stats.errCount++; - } +public: + explicit BenchRunWorkerStateGuard(BenchRunState* brState) : _brState(brState) { + _brState->onWorkerStarted(); + } - if (++count % 100 == 0 && !useWriteCmd) { - conn->getLastError(); - } + ~BenchRunWorkerStateGuard() { + _brState->onWorkerFinished(); + } - if (delay > 0) - sleepmillis( delay ); +private: + BenchRunState* _brState; +}; +} // namespace +void BenchRunWorker::run() { + try { + std::unique_ptr<DBClientBase> conn(_config->createConnection()); + if (!_config->username.empty()) { + string errmsg; + if (!conn->auth("admin", _config->username, _config->password, errmsg)) { + uasserted(15932, "Authenticating to connection for benchThread failed: " + errmsg); } } - - conn->getLastError(); + BenchRunWorkerStateGuard _workerStateGuard(_brState); + generateLoadOnConnection(conn.get()); + } catch (DBException& e) { + error() << "DBException not handled in benchRun thread" << causedBy(e) << endl; + } catch (std::exception& e) { + error() << "std::exception not handled in benchRun thread" << causedBy(e) << endl; + } catch (...) { + error() << "Unknown exception not handled in benchRun thread." << endl; } +} - namespace { - class BenchRunWorkerStateGuard { - MONGO_DISALLOW_COPYING(BenchRunWorkerStateGuard); - public: - explicit BenchRunWorkerStateGuard( BenchRunState *brState ) : _brState( brState ) { - _brState->onWorkerStarted(); - } +BenchRunner::BenchRunner(BenchRunConfig* config) : _brState(config->parallel), _config(config) { + _oid.init(); + stdx::lock_guard<stdx::mutex> lk(_staticMutex); + _activeRuns[_oid] = this; +} - ~BenchRunWorkerStateGuard() { - _brState->onWorkerFinished(); - } +BenchRunner::~BenchRunner() { + for (size_t i = 0; i < _workers.size(); ++i) + delete _workers[i]; +} - private: - BenchRunState *_brState; - }; - } // namespace - - void BenchRunWorker::run() { - try { - std::unique_ptr<DBClientBase> conn( _config->createConnection() ); - if ( !_config->username.empty() ) { - string errmsg; - if (!conn->auth("admin", _config->username, _config->password, errmsg)) { - uasserted(15932, - "Authenticating to connection for benchThread failed: " + errmsg); - } +void BenchRunner::start() { + { + std::unique_ptr<DBClientBase> conn(_config->createConnection()); + // Must authenticate to admin db in order to run serverStatus command + if (_config->username != "") { + string errmsg; + if (!conn->auth("admin", _config->username, _config->password, errmsg)) { + uasserted(16704, + str::stream() + << "User " << _config->username + << " could not authenticate to admin db; admin db access is " + "required to use benchRun with auth enabled"); } - BenchRunWorkerStateGuard _workerStateGuard( _brState ); - generateLoadOnConnection( conn.get() ); - } - catch( DBException& e ){ - error() << "DBException not handled in benchRun thread" << causedBy( e ) << endl; - } - catch( std::exception& e ){ - error() << "std::exception not handled in benchRun thread" << causedBy( e ) << endl; } - catch( ... ){ - error() << "Unknown exception not handled in benchRun thread." << endl; + + // Start threads + for (int64_t i = 0; i < _config->parallel; i++) { + // Make a unique random seed for the worker. + int64_t seed = _config->randomSeed + i; + BenchRunWorker* worker = new BenchRunWorker(i, _config.get(), &_brState, seed); + worker->start(); + _workers.push_back(worker); } - } - BenchRunner::BenchRunner( BenchRunConfig *config ) - : _brState(config->parallel), - _config(config) { + _brState.waitForState(BenchRunState::BRS_RUNNING); - _oid.init(); - stdx::lock_guard<stdx::mutex> lk(_staticMutex); - _activeRuns[_oid] = this; - } - - BenchRunner::~BenchRunner() { - for (size_t i = 0; i < _workers.size(); ++i) - delete _workers[i]; - } - - void BenchRunner::start( ) { - - { - std::unique_ptr<DBClientBase> conn( _config->createConnection() ); - // Must authenticate to admin db in order to run serverStatus command - if (_config->username != "") { - string errmsg; - if (!conn->auth("admin", _config->username, _config->password, errmsg)) { - uasserted(16704, - str::stream() << "User " << _config->username - << " could not authenticate to admin db; admin db access is " - "required to use benchRun with auth enabled"); - } - } - - // Start threads - for ( int64_t i = 0; i < _config->parallel; i++ ) { - // Make a unique random seed for the worker. - int64_t seed = _config->randomSeed + i; - BenchRunWorker *worker = new BenchRunWorker(i, _config.get(), - &_brState, seed); - worker->start(); - _workers.push_back(worker); - } - - _brState.waitForState(BenchRunState::BRS_RUNNING); - - // initial stats - _brState.tellWorkersToCollectStats(); - _brTimer = new mongo::Timer(); - } - } - - void BenchRunner::stop() { - _brState.tellWorkersToFinish(); - _brState.waitForState(BenchRunState::BRS_FINISHED); - _microsElapsed = _brTimer->micros(); - delete _brTimer; - - { - std::unique_ptr<DBClientBase> conn( _config->createConnection() ); - if (_config->username != "") { - string errmsg; - // this can only fail if admin access was revoked since start of run - if (!conn->auth("admin", _config->username, _config->password, errmsg)) { - uasserted(16705, - str::stream() << "User " << _config->username - << " could not authenticate to admin db; admin db access is " - "still required to use benchRun with auth enabled"); - } - } - } - - { - stdx::lock_guard<stdx::mutex> lk(_staticMutex); - _activeRuns.erase( _oid ); - } - } - - BenchRunner* BenchRunner::createWithConfig( const BSONObj &configArgs ) { - BenchRunConfig *config = BenchRunConfig::createFromBson( configArgs ); - return new BenchRunner(config); - } - - BenchRunner* BenchRunner::get( OID oid ) { - stdx::lock_guard<stdx::mutex> lk(_staticMutex); - return _activeRuns[ oid ]; - } - - void BenchRunner::populateStats( BenchRunStats *stats ) { - _brState.assertFinished(); - stats->reset(); - for ( size_t i = 0; i < _workers.size(); ++i ) - stats->updateFrom( _workers[i]->stats() ); + // initial stats + _brState.tellWorkersToCollectStats(); + _brTimer = new mongo::Timer(); } +} - static void appendAverageMicrosIfAvailable( - BSONObjBuilder &buf, const std::string &name, const BenchRunEventCounter &counter) { +void BenchRunner::stop() { + _brState.tellWorkersToFinish(); + _brState.waitForState(BenchRunState::BRS_FINISHED); + _microsElapsed = _brTimer->micros(); + delete _brTimer; - if (counter.getNumEvents() > 0) - buf.append(name, - static_cast<double>(counter.getTotalTimeMicros()) / counter.getNumEvents()); - } + { + std::unique_ptr<DBClientBase> conn(_config->createConnection()); + if (_config->username != "") { + string errmsg; + // this can only fail if admin access was revoked since start of run + if (!conn->auth("admin", _config->username, _config->password, errmsg)) { + uasserted(16705, + str::stream() + << "User " << _config->username + << " could not authenticate to admin db; admin db access is " + "still required to use benchRun with auth enabled"); + } + } + } - BSONObj BenchRunner::finish( BenchRunner* runner ) { + { + stdx::lock_guard<stdx::mutex> lk(_staticMutex); + _activeRuns.erase(_oid); + } +} - runner->stop(); +BenchRunner* BenchRunner::createWithConfig(const BSONObj& configArgs) { + BenchRunConfig* config = BenchRunConfig::createFromBson(configArgs); + return new BenchRunner(config); +} + +BenchRunner* BenchRunner::get(OID oid) { + stdx::lock_guard<stdx::mutex> lk(_staticMutex); + return _activeRuns[oid]; +} - BenchRunStats stats; - runner->populateStats(&stats); +void BenchRunner::populateStats(BenchRunStats* stats) { + _brState.assertFinished(); + stats->reset(); + for (size_t i = 0; i < _workers.size(); ++i) + stats->updateFrom(_workers[i]->stats()); +} - // vector<BSONOBj> errors = runner->config.errors; - bool error = stats.error; +static void appendAverageMicrosIfAvailable(BSONObjBuilder& buf, + const std::string& name, + const BenchRunEventCounter& counter) { + if (counter.getNumEvents() > 0) + buf.append(name, + static_cast<double>(counter.getTotalTimeMicros()) / counter.getNumEvents()); +} - if ( error ) - return BSON( "err" << 1 ); +BSONObj BenchRunner::finish(BenchRunner* runner) { + runner->stop(); - BSONObjBuilder buf; - buf.append( "note" , "values per second" ); - buf.append( "errCount", static_cast<long long>(stats.errCount) ); - buf.append( "trapped", "error: not implemented" ); - appendAverageMicrosIfAvailable(buf, "findOneLatencyAverageMicros", stats.findOneCounter); - appendAverageMicrosIfAvailable(buf, "insertLatencyAverageMicros", stats.insertCounter); - appendAverageMicrosIfAvailable(buf, "deleteLatencyAverageMicros", stats.deleteCounter); - appendAverageMicrosIfAvailable(buf, "updateLatencyAverageMicros", stats.updateCounter); - appendAverageMicrosIfAvailable(buf, "queryLatencyAverageMicros", stats.queryCounter); - appendAverageMicrosIfAvailable(buf, "commandsLatencyAverageMicros", stats.commandCounter); + BenchRunStats stats; + runner->populateStats(&stats); - buf.append("totalOps", static_cast<long long>(stats.opCount)); + // vector<BSONOBj> errors = runner->config.errors; + bool error = stats.error; - auto appendPerSec = [&buf, runner](StringData name, double total) { - buf.append(name, total / (runner->_microsElapsed / 1000000.0)); - }; + if (error) + return BSON("err" << 1); - appendPerSec("totalOps/s", stats.opCount); - appendPerSec("findOne", stats.findOneCounter.getNumEvents()); - appendPerSec("insert", stats.insertCounter.getNumEvents()); - appendPerSec("delete", stats.deleteCounter.getNumEvents()); - appendPerSec("update", stats.updateCounter.getNumEvents()); - appendPerSec("query", stats.queryCounter.getNumEvents()); - appendPerSec("command", stats.commandCounter.getNumEvents()); + BSONObjBuilder buf; + buf.append("note", "values per second"); + buf.append("errCount", static_cast<long long>(stats.errCount)); + buf.append("trapped", "error: not implemented"); + appendAverageMicrosIfAvailable(buf, "findOneLatencyAverageMicros", stats.findOneCounter); + appendAverageMicrosIfAvailable(buf, "insertLatencyAverageMicros", stats.insertCounter); + appendAverageMicrosIfAvailable(buf, "deleteLatencyAverageMicros", stats.deleteCounter); + appendAverageMicrosIfAvailable(buf, "updateLatencyAverageMicros", stats.updateCounter); + appendAverageMicrosIfAvailable(buf, "queryLatencyAverageMicros", stats.queryCounter); + appendAverageMicrosIfAvailable(buf, "commandsLatencyAverageMicros", stats.commandCounter); - BSONObj zoo = buf.obj(); + buf.append("totalOps", static_cast<long long>(stats.opCount)); - delete runner; - return zoo; - } + auto appendPerSec = [&buf, runner](StringData name, double total) { + buf.append(name, total / (runner->_microsElapsed / 1000000.0)); + }; - stdx::mutex BenchRunner::_staticMutex; - map< OID, BenchRunner* > BenchRunner::_activeRuns; + appendPerSec("totalOps/s", stats.opCount); + appendPerSec("findOne", stats.findOneCounter.getNumEvents()); + appendPerSec("insert", stats.insertCounter.getNumEvents()); + appendPerSec("delete", stats.deleteCounter.getNumEvents()); + appendPerSec("update", stats.updateCounter.getNumEvents()); + appendPerSec("query", stats.queryCounter.getNumEvents()); + appendPerSec("command", stats.commandCounter.getNumEvents()); - /** - * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } - */ - BSONObj BenchRunner::benchRunSync( const BSONObj& argsFake, void* data ) { + BSONObj zoo = buf.obj(); - BSONObj start = benchStart( argsFake, data ); + delete runner; + return zoo; +} - OID oid = OID( start.firstElement().String() ); - BenchRunner* runner = BenchRunner::get( oid ); +stdx::mutex BenchRunner::_staticMutex; +map<OID, BenchRunner*> BenchRunner::_activeRuns; - sleepmillis( (int)(1000.0 * runner->config().seconds) ); +/** + * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } + */ +BSONObj BenchRunner::benchRunSync(const BSONObj& argsFake, void* data) { + BSONObj start = benchStart(argsFake, data); - return benchFinish( start, data ); - } + OID oid = OID(start.firstElement().String()); + BenchRunner* runner = BenchRunner::get(oid); - /** - * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } - */ - BSONObj BenchRunner::benchStart( const BSONObj& argsFake, void* data ) { + sleepmillis((int)(1000.0 * runner->config().seconds)); - verify( argsFake.firstElement().isABSONObj() ); - BSONObj args = argsFake.firstElement().Obj(); + return benchFinish(start, data); +} - // Get new BenchRunner object - BenchRunner* runner = BenchRunner::createWithConfig( args ); +/** + * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } + */ +BSONObj BenchRunner::benchStart(const BSONObj& argsFake, void* data) { + verify(argsFake.firstElement().isABSONObj()); + BSONObj args = argsFake.firstElement().Obj(); - runner->start(); - return BSON( "" << runner->oid().toString() ); - } + // Get new BenchRunner object + BenchRunner* runner = BenchRunner::createWithConfig(args); - /** - * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } - */ - BSONObj BenchRunner::benchFinish( const BSONObj& argsFake, void* data ) { + runner->start(); + return BSON("" << runner->oid().toString()); +} - OID oid = OID( argsFake.firstElement().String() ); +/** + * benchRun( { ops : [] , host : XXX , db : XXXX , parallel : 5 , seconds : 5 } + */ +BSONObj BenchRunner::benchFinish(const BSONObj& argsFake, void* data) { + OID oid = OID(argsFake.firstElement().String()); - // Get old BenchRunner object - BenchRunner* runner = BenchRunner::get( oid ); + // Get old BenchRunner object + BenchRunner* runner = BenchRunner::get(oid); - BSONObj finalObj = BenchRunner::finish( runner ); + BSONObj finalObj = BenchRunner::finish(runner); - return BSON( "" << finalObj ); - } + return BSON("" << finalObj); +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/shell/bench.h b/src/mongo/shell/bench.h index a3ab7dc16ab..3fcb9e1db03 100644 --- a/src/mongo/shell/bench.h +++ b/src/mongo/shell/bench.h @@ -38,425 +38,446 @@ #include "mongo/util/timer.h" namespace pcrecpp { - class RE; -} // namespace pcrecpp; +class RE; +} // namespace pcrecpp; namespace mongo { +/** + * Configuration object describing a bench run activity. + */ +class BenchRunConfig { + MONGO_DISALLOW_COPYING(BenchRunConfig); + +public: + /** + * Create a new BenchRunConfig object, and initialize it from the BSON + * document, "args". + * + * Caller owns the returned object, and is responsible for its deletion. + */ + static BenchRunConfig* createFromBson(const BSONObj& args); + + BenchRunConfig(); + + void initializeFromBson(const BSONObj& args); + + // Create a new connection to the mongo instance specified by this configuration. + DBClientBase* createConnection() const; + + /** + * Connection std::string describing the host to which to connect. + */ + std::string host; + + /** + * Name of the database on which to operate. + */ + std::string db; + + /** + * Optional username for authenticating to the database. + */ + std::string username; + + /** + * Optional password for authenticating to the database. + * + * Only useful if username is non-empty. + */ + std::string password; + /** - * Configuration object describing a bench run activity. + * Number of parallel threads to perform the bench run activity. */ - class BenchRunConfig { - MONGO_DISALLOW_COPYING(BenchRunConfig); - public: - - /** - * Create a new BenchRunConfig object, and initialize it from the BSON - * document, "args". - * - * Caller owns the returned object, and is responsible for its deletion. - */ - static BenchRunConfig *createFromBson( const BSONObj &args ); - - BenchRunConfig(); - - void initializeFromBson( const BSONObj &args ); - - // Create a new connection to the mongo instance specified by this configuration. - DBClientBase *createConnection() const; - - /** - * Connection std::string describing the host to which to connect. - */ - std::string host; - - /** - * Name of the database on which to operate. - */ - std::string db; - - /** - * Optional username for authenticating to the database. - */ - std::string username; - - /** - * Optional password for authenticating to the database. - * - * Only useful if username is non-empty. - */ - std::string password; - - /** - * Number of parallel threads to perform the bench run activity. - */ - unsigned parallel; - - /** - * Desired duration of the bench run activity, in seconds. - * - * NOTE: Only used by the javascript benchRun() and benchRunSync() functions. - */ - double seconds; - - /// Base random seed for threads - int64_t randomSeed; - - bool hideResults; - bool handleErrors; - bool hideErrors; - - std::shared_ptr< pcrecpp::RE > trapPattern; - std::shared_ptr< pcrecpp::RE > noTrapPattern; - std::shared_ptr< pcrecpp::RE > watchPattern; - std::shared_ptr< pcrecpp::RE > noWatchPattern; - - /** - * Operation description. A BSON array of objects, each describing a single - * operation. - * - * Every thread in a benchRun job will perform these operations in sequence, restarting at - * the beginning when the end is reached, until the job is stopped. - * - * TODO: Document the operation objects. - * - * TODO: Introduce support for performing each operation exactly N times. - */ - BSONObj ops; - - bool throwGLE; - bool breakOnTrap; - - private: - /// Initialize a config object to its default values. - void initializeToDefaults(); - }; + unsigned parallel; /** - * An event counter for events that have an associated duration. + * Desired duration of the bench run activity, in seconds. * - * Not thread safe. Expected use is one instance per thread during parallel execution. + * NOTE: Only used by the javascript benchRun() and benchRunSync() functions. */ - class BenchRunEventCounter { - MONGO_DISALLOW_COPYING(BenchRunEventCounter); - public: - /// Constructs a zeroed out counter. - BenchRunEventCounter(); - ~BenchRunEventCounter(); - - /** - * Zero out the counter. - */ - void reset(); - - /** - * Conceptually the equivalent of "+=". Adds "other" into this. - */ - void updateFrom( const BenchRunEventCounter &other ); - - /** - * Count one instance of the event, which took "timeMicros" microseconds. - */ - void countOne(long long timeMicros) { - ++_numEvents; - _totalTimeMicros += timeMicros; - } - - /** - * Get the total number of microseconds ellapsed during all observed events. - */ - unsigned long long getTotalTimeMicros() const { return _totalTimeMicros; } - - /** - * Get the number of observed events. - */ - unsigned long long getNumEvents() const { return _numEvents; } - - private: - unsigned long long _numEvents; - long long _totalTimeMicros; - }; + double seconds; + + /// Base random seed for threads + int64_t randomSeed; + + bool hideResults; + bool handleErrors; + bool hideErrors; + + std::shared_ptr<pcrecpp::RE> trapPattern; + std::shared_ptr<pcrecpp::RE> noTrapPattern; + std::shared_ptr<pcrecpp::RE> watchPattern; + std::shared_ptr<pcrecpp::RE> noWatchPattern; /** - * RAII object for tracing an event. + * Operation description. A BSON array of objects, each describing a single + * operation. * - * Construct an instance of this at the beginning of an event, and have it go out of scope at - * the end, to facilitate tracking events. + * Every thread in a benchRun job will perform these operations in sequence, restarting at + * the beginning when the end is reached, until the job is stopped. * - * This type can be used to separately count failures and successes by passing two event - * counters to the BenchRunEventCounter constructor, and calling "succeed()" on the object at - * the end of a successful event. If an exception is thrown, the fail counter will receive the - * event, and otherwise, the succes counter will. + * TODO: Document the operation objects. * - * In all cases, the counter objects must outlive the trace object. + * TODO: Introduce support for performing each operation exactly N times. */ - class BenchRunEventTrace { - MONGO_DISALLOW_COPYING(BenchRunEventTrace); - public: - explicit BenchRunEventTrace(BenchRunEventCounter *eventCounter) { - initialize(eventCounter, eventCounter, false); - } - - BenchRunEventTrace(BenchRunEventCounter *successCounter, - BenchRunEventCounter *failCounter, - bool defaultToFailure=true) { - initialize(successCounter, failCounter, defaultToFailure); - } - - ~BenchRunEventTrace() { - (_succeeded ? _successCounter : _failCounter)->countOne(_timer.micros()); - } - - void succeed() { _succeeded = true; } - void fail() { _succeeded = false; } - - private: - void initialize(BenchRunEventCounter *successCounter, - BenchRunEventCounter *failCounter, - bool defaultToFailure) { - _successCounter = successCounter; - _failCounter = failCounter; - _succeeded = !defaultToFailure; - } - - Timer _timer; - BenchRunEventCounter *_successCounter; - BenchRunEventCounter *_failCounter; - bool _succeeded; - }; + BSONObj ops; + + bool throwGLE; + bool breakOnTrap; + +private: + /// Initialize a config object to its default values. + void initializeToDefaults(); +}; + +/** + * An event counter for events that have an associated duration. + * + * Not thread safe. Expected use is one instance per thread during parallel execution. + */ +class BenchRunEventCounter { + MONGO_DISALLOW_COPYING(BenchRunEventCounter); + +public: + /// Constructs a zeroed out counter. + BenchRunEventCounter(); + ~BenchRunEventCounter(); + + /** + * Zero out the counter. + */ + void reset(); + + /** + * Conceptually the equivalent of "+=". Adds "other" into this. + */ + void updateFrom(const BenchRunEventCounter& other); /** - * Statistics object representing the result of a bench run activity. + * Count one instance of the event, which took "timeMicros" microseconds. */ - class BenchRunStats { - MONGO_DISALLOW_COPYING(BenchRunStats); - public: - BenchRunStats(); - ~BenchRunStats(); + void countOne(long long timeMicros) { + ++_numEvents; + _totalTimeMicros += timeMicros; + } - void reset(); + /** + * Get the total number of microseconds ellapsed during all observed events. + */ + unsigned long long getTotalTimeMicros() const { + return _totalTimeMicros; + } - void updateFrom( const BenchRunStats &other ); + /** + * Get the number of observed events. + */ + unsigned long long getNumEvents() const { + return _numEvents; + } - bool error; - unsigned long long errCount; - unsigned long long opCount; +private: + unsigned long long _numEvents; + long long _totalTimeMicros; +}; - BenchRunEventCounter findOneCounter; - BenchRunEventCounter updateCounter; - BenchRunEventCounter insertCounter; - BenchRunEventCounter deleteCounter; - BenchRunEventCounter queryCounter; - BenchRunEventCounter commandCounter; +/** + * RAII object for tracing an event. + * + * Construct an instance of this at the beginning of an event, and have it go out of scope at + * the end, to facilitate tracking events. + * + * This type can be used to separately count failures and successes by passing two event + * counters to the BenchRunEventCounter constructor, and calling "succeed()" on the object at + * the end of a successful event. If an exception is thrown, the fail counter will receive the + * event, and otherwise, the succes counter will. + * + * In all cases, the counter objects must outlive the trace object. + */ +class BenchRunEventTrace { + MONGO_DISALLOW_COPYING(BenchRunEventTrace); + +public: + explicit BenchRunEventTrace(BenchRunEventCounter* eventCounter) { + initialize(eventCounter, eventCounter, false); + } + + BenchRunEventTrace(BenchRunEventCounter* successCounter, + BenchRunEventCounter* failCounter, + bool defaultToFailure = true) { + initialize(successCounter, failCounter, defaultToFailure); + } + + ~BenchRunEventTrace() { + (_succeeded ? _successCounter : _failCounter)->countOne(_timer.micros()); + } + + void succeed() { + _succeeded = true; + } + void fail() { + _succeeded = false; + } + +private: + void initialize(BenchRunEventCounter* successCounter, + BenchRunEventCounter* failCounter, + bool defaultToFailure) { + _successCounter = successCounter; + _failCounter = failCounter; + _succeeded = !defaultToFailure; + } + + Timer _timer; + BenchRunEventCounter* _successCounter; + BenchRunEventCounter* _failCounter; + bool _succeeded; +}; + +/** + * Statistics object representing the result of a bench run activity. + */ +class BenchRunStats { + MONGO_DISALLOW_COPYING(BenchRunStats); + +public: + BenchRunStats(); + ~BenchRunStats(); + + void reset(); + + void updateFrom(const BenchRunStats& other); + + bool error; + unsigned long long errCount; + unsigned long long opCount; + + BenchRunEventCounter findOneCounter; + BenchRunEventCounter updateCounter; + BenchRunEventCounter insertCounter; + BenchRunEventCounter deleteCounter; + BenchRunEventCounter queryCounter; + BenchRunEventCounter commandCounter; + + std::map<std::string, long long> opcounters; + std::vector<BSONObj> trappedErrors; +}; + +/** + * State of a BenchRun activity. + * + * Logically, the states are "starting up", "running" and "finished." + */ +class BenchRunState { + MONGO_DISALLOW_COPYING(BenchRunState); - std::map<std::string, long long> opcounters; - std::vector<BSONObj> trappedErrors; - }; +public: + enum State { BRS_STARTING_UP, BRS_RUNNING, BRS_FINISHED }; + explicit BenchRunState(unsigned numWorkers); + ~BenchRunState(); + + // + // Functions called by the job-controlling thread, through an instance of BenchRunner. + // + + /** + * Block until the current state is "awaitedState." + * + * massert() (uassert()?) if "awaitedState" is unreachable from + * the current state. + */ + void waitForState(State awaitedState); + + /** + * Notify the worker threads to wrap up. Does not block. + */ + void tellWorkersToFinish(); + + /** + * Notify the worker threads to collect statistics. Does not block. + */ + void tellWorkersToCollectStats(); + + /// Check that the current state is BRS_FINISHED. + void assertFinished(); + + // + // Functions called by the worker threads, through instances of BenchRunWorker. + // + + /** + * Predicate that workers call to see if they should finish (as a result of a call + * to tellWorkersToFinish()). + */ + bool shouldWorkerFinish(); + + /** + * Predicate that workers call to see if they should start collecting stats (as a result + * of a call to tellWorkersToCollectStats()). + */ + bool shouldWorkerCollectStats(); + + /** + * Called by each BenchRunWorker from within its thread context, immediately before it + * starts sending requests to the configured mongo instance. + */ + void onWorkerStarted(); + + /** + * Called by each BenchRunWorker from within its thread context, shortly after it finishes + * sending requests to the configured mongo instance. + */ + void onWorkerFinished(); + +private: + stdx::mutex _mutex; + stdx::condition_variable _stateChangeCondition; + unsigned _numUnstartedWorkers; + unsigned _numActiveWorkers; + AtomicUInt32 _isShuttingDown; + AtomicUInt32 _isCollectingStats; +}; + +/** + * A single worker in the bench run activity. + * + * Represents the behavior of one thread working in a bench run activity. + */ +class BenchRunWorker { + MONGO_DISALLOW_COPYING(BenchRunWorker); + +public: /** - * State of a BenchRun activity. + * Create a new worker, performing one thread's worth of the activity described in + * "config", and part of the larger activity with state "brState". Both "config" + * and "brState" must exist for the life of this object. * - * Logically, the states are "starting up", "running" and "finished." + * "id" is a positive integer which should uniquely identify the worker. */ - class BenchRunState { - MONGO_DISALLOW_COPYING(BenchRunState); - public: - enum State { BRS_STARTING_UP, BRS_RUNNING, BRS_FINISHED }; - - explicit BenchRunState(unsigned numWorkers); - ~BenchRunState(); - - // - // Functions called by the job-controlling thread, through an instance of BenchRunner. - // - - /** - * Block until the current state is "awaitedState." - * - * massert() (uassert()?) if "awaitedState" is unreachable from - * the current state. - */ - void waitForState(State awaitedState); - - /** - * Notify the worker threads to wrap up. Does not block. - */ - void tellWorkersToFinish(); - - /** - * Notify the worker threads to collect statistics. Does not block. - */ - void tellWorkersToCollectStats(); - - /// Check that the current state is BRS_FINISHED. - void assertFinished(); - - // - // Functions called by the worker threads, through instances of BenchRunWorker. - // - - /** - * Predicate that workers call to see if they should finish (as a result of a call - * to tellWorkersToFinish()). - */ - bool shouldWorkerFinish(); - - /** - * Predicate that workers call to see if they should start collecting stats (as a result - * of a call to tellWorkersToCollectStats()). - */ - bool shouldWorkerCollectStats(); - - /** - * Called by each BenchRunWorker from within its thread context, immediately before it - * starts sending requests to the configured mongo instance. - */ - void onWorkerStarted(); - - /** - * Called by each BenchRunWorker from within its thread context, shortly after it finishes - * sending requests to the configured mongo instance. - */ - void onWorkerFinished(); - - private: - stdx::mutex _mutex; - stdx::condition_variable _stateChangeCondition; - unsigned _numUnstartedWorkers; - unsigned _numActiveWorkers; - AtomicUInt32 _isShuttingDown; - AtomicUInt32 _isCollectingStats; - }; + BenchRunWorker(size_t id, + const BenchRunConfig* config, + BenchRunState* brState, + int64_t randomSeed); + ~BenchRunWorker(); /** - * A single worker in the bench run activity. + * Start performing the "work" behavior in a new thread. + */ + void start(); + + /** + * Get the run statistics for a worker. + * + * Should only be observed _after_ the worker has signaled its completion by calling + * onWorkerFinished() on the BenchRunState passed into its constructor. + */ + const BenchRunStats& stats() const { + return _stats; + } + +private: + /// The main method of the worker, executed inside the thread launched by start(). + void run(); + + /// The function that actually sets about generating the load described in "_config". + void generateLoadOnConnection(DBClientBase* conn); + + /// Predicate, used to decide whether or not it's time to terminate the worker. + bool shouldStop() const; + /// Predicate, used to decide whether or not it's time to collect statistics + bool shouldCollectStats() const; + + size_t _id; + const BenchRunConfig* _config; + BenchRunState* _brState; + BenchRunStats _stats; + /// Dummy stats to use before observation period. + BenchRunStats _statsBlackHole; + int64_t _randomSeed; +}; + +/** + * Object representing a "bench run" activity. + */ +class BenchRunner { + MONGO_DISALLOW_COPYING(BenchRunner); + +public: + /** + * Utility method to create a new bench runner from a BSONObj representation + * of a configuration. * - * Represents the behavior of one thread working in a bench run activity. + * TODO: This is only really for the use of the javascript benchRun() methods, + * and should probably move out of the BenchRunner class. */ - class BenchRunWorker { - MONGO_DISALLOW_COPYING(BenchRunWorker); - public: - - /** - * Create a new worker, performing one thread's worth of the activity described in - * "config", and part of the larger activity with state "brState". Both "config" - * and "brState" must exist for the life of this object. - * - * "id" is a positive integer which should uniquely identify the worker. - */ - BenchRunWorker(size_t id, const BenchRunConfig *config, - BenchRunState *brState, int64_t randomSeed); - ~BenchRunWorker(); - - /** - * Start performing the "work" behavior in a new thread. - */ - void start(); - - /** - * Get the run statistics for a worker. - * - * Should only be observed _after_ the worker has signaled its completion by calling - * onWorkerFinished() on the BenchRunState passed into its constructor. - */ - const BenchRunStats &stats() const { return _stats; } - - private: - /// The main method of the worker, executed inside the thread launched by start(). - void run(); - - /// The function that actually sets about generating the load described in "_config". - void generateLoadOnConnection( DBClientBase *conn ); - - /// Predicate, used to decide whether or not it's time to terminate the worker. - bool shouldStop() const; - /// Predicate, used to decide whether or not it's time to collect statistics - bool shouldCollectStats() const; - - size_t _id; - const BenchRunConfig *_config; - BenchRunState *_brState; - BenchRunStats _stats; - /// Dummy stats to use before observation period. - BenchRunStats _statsBlackHole; - int64_t _randomSeed; - }; + static BenchRunner* createWithConfig(const BSONObj& configArgs); /** - * Object representing a "bench run" activity. + * Look up a bench runner object by OID. + * + * TODO: Same todo as for "createWithConfig". + */ + static BenchRunner* get(OID oid); + + /** + * Stop a running "runner", and return a BSON representation of its resultant + * BenchRunStats. + * + * TODO: Same as for "createWithConfig". + */ + static BSONObj finish(BenchRunner* runner); + + /** + * Create a new bench runner, to perform the activity described by "*config." + * + * Takes ownership of "config", and will delete it. + */ + explicit BenchRunner(BenchRunConfig* config); + ~BenchRunner(); + + /** + * Start the activity. Only call once per instance of BenchRunner. + */ + void start(); + + /** + * Stop the activity. Block until the activitiy has stopped. + */ + void stop(); + + /** + * Store the collected event data from a completed bench run activity into "stats." + * + * Illegal to call until after stop() returns. */ - class BenchRunner { - MONGO_DISALLOW_COPYING(BenchRunner); - public: - /** - * Utility method to create a new bench runner from a BSONObj representation - * of a configuration. - * - * TODO: This is only really for the use of the javascript benchRun() methods, - * and should probably move out of the BenchRunner class. - */ - static BenchRunner* createWithConfig( const BSONObj &configArgs ); - - /** - * Look up a bench runner object by OID. - * - * TODO: Same todo as for "createWithConfig". - */ - static BenchRunner* get( OID oid ); - - /** - * Stop a running "runner", and return a BSON representation of its resultant - * BenchRunStats. - * - * TODO: Same as for "createWithConfig". - */ - static BSONObj finish( BenchRunner* runner ); - - /** - * Create a new bench runner, to perform the activity described by "*config." - * - * Takes ownership of "config", and will delete it. - */ - explicit BenchRunner( BenchRunConfig *config ); - ~BenchRunner(); - - /** - * Start the activity. Only call once per instance of BenchRunner. - */ - void start(); - - /** - * Stop the activity. Block until the activitiy has stopped. - */ - void stop(); - - /** - * Store the collected event data from a completed bench run activity into "stats." - * - * Illegal to call until after stop() returns. - */ - void populateStats(BenchRunStats *stats); - - OID oid() const { return _oid; } - - const BenchRunConfig &config() const { return *_config; } // TODO: Remove this function. - - // JS bindings - static BSONObj benchFinish(const BSONObj& argsFake, void* data); - static BSONObj benchStart(const BSONObj& argsFake, void* data); - static BSONObj benchRunSync(const BSONObj& argsFake, void* data); - - private: - // TODO: Same as for createWithConfig. - static stdx::mutex _staticMutex; - static std::map< OID, BenchRunner* > _activeRuns; - - OID _oid; - BenchRunState _brState; - Timer *_brTimer; - unsigned long long _microsElapsed; - std::unique_ptr<BenchRunConfig> _config; - std::vector<BenchRunWorker *> _workers; - }; + void populateStats(BenchRunStats* stats); + + OID oid() const { + return _oid; + } + + const BenchRunConfig& config() const { + return *_config; + } // TODO: Remove this function. + + // JS bindings + static BSONObj benchFinish(const BSONObj& argsFake, void* data); + static BSONObj benchStart(const BSONObj& argsFake, void* data); + static BSONObj benchRunSync(const BSONObj& argsFake, void* data); + +private: + // TODO: Same as for createWithConfig. + static stdx::mutex _staticMutex; + static std::map<OID, BenchRunner*> _activeRuns; + + OID _oid; + BenchRunState _brState; + Timer* _brTimer; + unsigned long long _microsElapsed; + std::unique_ptr<BenchRunConfig> _config; + std::vector<BenchRunWorker*> _workers; +}; } // namespace mongo diff --git a/src/mongo/shell/clientAndShell.cpp b/src/mongo/shell/clientAndShell.cpp index 07f9b5cd83a..2f55cef17aa 100644 --- a/src/mongo/shell/clientAndShell.cpp +++ b/src/mongo/shell/clientAndShell.cpp @@ -39,44 +39,43 @@ namespace mongo { - using std::endl; - using std::string; - using std::vector; +using std::endl; +using std::string; +using std::vector; - class Client; - class DBClientBase; - class OperationContext; +class Client; +class DBClientBase; +class OperationContext; - bool dbexitCalled = false; +bool dbexitCalled = false; - void dbexit( ExitCode returnCode, const char *whyMsg ) { - { - stdx::lock_guard<stdx::mutex> lk( shell_utils::mongoProgramOutputMutex ); - dbexitCalled = true; - } - - log() << "dbexit called" << endl; +void dbexit(ExitCode returnCode, const char* whyMsg) { + { + stdx::lock_guard<stdx::mutex> lk(shell_utils::mongoProgramOutputMutex); + dbexitCalled = true; + } - if (whyMsg) { - log() << " b/c " << whyMsg << endl; - } + log() << "dbexit called" << endl; - log() << "exiting" << endl; - quickExit( returnCode ); + if (whyMsg) { + log() << " b/c " << whyMsg << endl; } - bool inShutdown() { - return dbexitCalled; - } + log() << "exiting" << endl; + quickExit(returnCode); +} - bool haveLocalShardingInfo( Client* client, const string& ns ) { - return false; - } +bool inShutdown() { + return dbexitCalled; +} - DBClientBase* createDirectClient(OperationContext* txn) { - uassert( 10256 , "no createDirectClient in clientOnly" , 0 ); - return 0; - } +bool haveLocalShardingInfo(Client* client, const string& ns) { + return false; +} +DBClientBase* createDirectClient(OperationContext* txn) { + uassert(10256, "no createDirectClient in clientOnly", 0); + return 0; +} } diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp index aa161945e0d..17abb93f6ba 100644 --- a/src/mongo/shell/dbshell.cpp +++ b/src/mongo/shell/dbshell.cpp @@ -82,70 +82,68 @@ using namespace mongo; string historyFile; bool gotInterrupted = false; bool inMultiLine = false; -static volatile bool atPrompt = false; // can eval before getting to prompt +static volatile bool atPrompt = false; // can eval before getting to prompt namespace mongo { - Scope * shellMainScope; +Scope* shellMainScope; - extern bool dbexitCalled; +extern bool dbexitCalled; } -void generateCompletions( const string& prefix , vector<string>& all ) { - if ( prefix.find( '"' ) != string::npos ) +void generateCompletions(const string& prefix, vector<string>& all) { + if (prefix.find('"') != string::npos) return; try { - BSONObj args = BSON( "0" << prefix ); - shellMainScope->invokeSafe("function callShellAutocomplete(x) {shellAutocomplete(x)}", - &args, - NULL); + BSONObj args = BSON("0" << prefix); + shellMainScope->invokeSafe( + "function callShellAutocomplete(x) {shellAutocomplete(x)}", &args, NULL); BSONObjBuilder b; - shellMainScope->append( b , "" , "__autocomplete__" ); + shellMainScope->append(b, "", "__autocomplete__"); BSONObj res = b.obj(); BSONObj arr = res.firstElement().Obj(); - BSONObjIterator i( arr ); - while ( i.more() ) { + BSONObjIterator i(arr); + while (i.more()) { BSONElement e = i.next(); - all.push_back( e.String() ); + all.push_back(e.String()); } - } - catch ( ... ) { + } catch (...) { } } -void completionHook( const char* text , linenoiseCompletions* lc ) { +void completionHook(const char* text, linenoiseCompletions* lc) { vector<string> all; - generateCompletions( text , all ); + generateCompletions(text, all); - for ( unsigned i = 0; i < all.size(); ++i ) - linenoiseAddCompletion( lc , (char*)all[i].c_str() ); + for (unsigned i = 0; i < all.size(); ++i) + linenoiseAddCompletion(lc, (char*)all[i].c_str()); } void shellHistoryInit() { stringstream ss; - const char * h = shell_utils::getUserDir(); - if ( h ) + const char* h = shell_utils::getUserDir(); + if (h) ss << h << "/"; ss << ".dbshell"; historyFile = ss.str(); - linenoiseHistoryLoad( historyFile.c_str() ); - linenoiseSetCompletionCallback( completionHook ); + linenoiseHistoryLoad(historyFile.c_str()); + linenoiseSetCompletionCallback(completionHook); } void shellHistoryDone() { - linenoiseHistorySave( historyFile.c_str() ); + linenoiseHistorySave(historyFile.c_str()); linenoiseHistoryFree(); } -void shellHistoryAdd( const char * line ) { - if ( line[0] == '\0' ) +void shellHistoryAdd(const char* line) { + if (line[0] == '\0') return; // dont record duplicate lines static string lastLine; - if ( lastLine == line ) + if (lastLine == line) return; lastLine = line; @@ -154,56 +152,55 @@ void shellHistoryAdd( const char * line ) { // detected by using regular expresions. This is so we can avoid storing passwords // in the history file in plaintext. static pcrecpp::RE hiddenHelpers( - "\\.\\s*(auth|createUser|updateUser|changeUserPassword)\\s*\\("); + "\\.\\s*(auth|createUser|updateUser|changeUserPassword)\\s*\\("); // Also don't want the raw user management commands to show in the shell when run directly // via runCommand. static pcrecpp::RE hiddenCommands( - "(run|admin)Command\\s*\\(\\s*{\\s*(createUser|updateUser)\\s*:"); - if (!hiddenHelpers.PartialMatch(line) && !hiddenCommands.PartialMatch(line)) - { - linenoiseHistoryAdd( line ); + "(run|admin)Command\\s*\\(\\s*{\\s*(createUser|updateUser)\\s*:"); + if (!hiddenHelpers.PartialMatch(line) && !hiddenCommands.PartialMatch(line)) { + linenoiseHistoryAdd(line); } } void killOps() { - if ( mongo::shell_utils::_nokillop ) + if (mongo::shell_utils::_nokillop) return; - if ( atPrompt ) + if (atPrompt) return; - sleepmillis(10); // give current op a chance to finish + sleepmillis(10); // give current op a chance to finish - mongo::shell_utils::connectionRegistry. - killOperationsOnAllConnections(!shellGlobalParams.autoKillOp); + mongo::shell_utils::connectionRegistry.killOperationsOnAllConnections( + !shellGlobalParams.autoKillOp); } // Stubs for signal_handlers.cpp namespace mongo { - void logProcessDetailsForLogRotate() {} - - void exitCleanly(ExitCode code) { - { - stdx::lock_guard<stdx::mutex> lk(mongo::shell_utils::mongoProgramOutputMutex); - mongo::dbexitCalled = true; - } +void logProcessDetailsForLogRotate() {} - ::killOps(); - ::shellHistoryDone(); - quickExit(0); +void exitCleanly(ExitCode code) { + { + stdx::lock_guard<stdx::mutex> lk(mongo::shell_utils::mongoProgramOutputMutex); + mongo::dbexitCalled = true; } + + ::killOps(); + ::shellHistoryDone(); + quickExit(0); +} } -void quitNicely( int sig ) { +void quitNicely(int sig) { exitCleanly(EXIT_CLEAN); } // the returned string is allocated with strdup() or malloc() and must be freed by calling free() -char * shellReadline( const char * prompt , int handlesigint = 0 ) { +char* shellReadline(const char* prompt, int handlesigint = 0) { atPrompt = true; - char * ret = linenoise( prompt ); - if ( ! ret ) { + char* ret = linenoise(prompt); + if (!ret) { gotInterrupted = true; // got ^C, break out of multiline } @@ -212,32 +209,31 @@ char * shellReadline( const char * prompt , int handlesigint = 0 ) { } void setupSignals() { - signal( SIGINT , quitNicely ); + signal(SIGINT, quitNicely); } -string fixHost( const std::string& url, const std::string& host, const std::string& port ) { - if ( host.size() == 0 && port.size() == 0 ) { - if ( url.find( "/" ) == string::npos ) { +string fixHost(const std::string& url, const std::string& host, const std::string& port) { + if (host.size() == 0 && port.size() == 0) { + if (url.find("/") == string::npos) { // check for ips - if ( url.find( "." ) != string::npos ) + if (url.find(".") != string::npos) return url + "/test"; - if ( url.rfind( ":" ) != string::npos && - isdigit( url[url.rfind(":")+1] ) ) + if (url.rfind(":") != string::npos && isdigit(url[url.rfind(":") + 1])) return url + "/test"; } return url; } - if ( url.find( "/" ) != string::npos ) { + if (url.find("/") != string::npos) { cerr << "url can't have host or port if you specify them individually" << endl; quickExit(-1); } - string newurl( ( host.size() == 0 ) ? "127.0.0.1" : host ); - if ( port.size() > 0 ) + string newurl((host.size() == 0) ? "127.0.0.1" : host); + if (port.size() > 0) newurl += ":" + port; - else if ( host.find(':') == string::npos ) { + else if (host.find(':') == string::npos) { // need to add port with IPv6 addresses newurl += ":27017"; } @@ -249,22 +245,23 @@ string fixHost( const std::string& url, const std::string& host, const std::stri static string OpSymbols = "~!%^&*-+=|:,<>/?."; -bool isOpSymbol( char c ) { - for ( size_t i = 0; i < OpSymbols.size(); i++ ) - if ( OpSymbols[i] == c ) return true; +bool isOpSymbol(char c) { + for (size_t i = 0; i < OpSymbols.size(); i++) + if (OpSymbols[i] == c) + return true; return false; } -bool isUseCmd( const std::string& code ) { +bool isUseCmd(const std::string& code) { string cmd = code; - if ( cmd.find( " " ) > 0 ) - cmd = cmd.substr( 0 , cmd.find( " " ) ); + if (cmd.find(" ") > 0) + cmd = cmd.substr(0, cmd.find(" ")); return cmd == "use"; } /** * Skip over a quoted string, including quotes escaped with backslash - * + * * @param code String * @param start Starting position within string, always > 0 * @param quote Quote character (single or double quote) @@ -281,80 +278,84 @@ size_t skipOverString(const std::string& code, size_t start, char quote) { // that the escaping backslash is not itself escaped. Comparisons of start and pos // are to keep us from reading beyond the beginning of the quoted string. // - if (start == pos || code[pos - 1] != '\\' || // previous char was backslash - start == pos - 1 || code[pos - 2] == '\\' // char before backslash was not another - ) { + if (start == pos || code[pos - 1] != '\\' || // previous char was backslash + start == pos - 1 || + code[pos - 2] == '\\' // char before backslash was not another + ) { break; // The quote we found was not preceded by an unescaped backslash; it is real } - ++pos; // The quote we found was escaped with backslash, so it doesn't count + ++pos; // The quote we found was escaped with backslash, so it doesn't count } return pos; } -bool isBalanced( const std::string& code ) { - if (isUseCmd( code )) +bool isBalanced(const std::string& code) { + if (isUseCmd(code)) return true; // don't balance "use <dbname>" in case dbname contains special chars int curlyBrackets = 0; int squareBrackets = 0; int parens = 0; bool danglingOp = false; - for ( size_t i=0; i<code.size(); i++ ) { - switch( code[i] ) { - case '/': - if ( i + 1 < code.size() && code[i+1] == '/' ) { - while ( i <code.size() && code[i] != '\n' ) + for (size_t i = 0; i < code.size(); i++) { + switch (code[i]) { + case '/': + if (i + 1 < code.size() && code[i + 1] == '/') { + while (i < code.size() && code[i] != '\n') + i++; + } + continue; + case '{': + curlyBrackets++; + break; + case '}': + if (curlyBrackets <= 0) + return true; + curlyBrackets--; + break; + case '[': + squareBrackets++; + break; + case ']': + if (squareBrackets <= 0) + return true; + squareBrackets--; + break; + case '(': + parens++; + break; + case ')': + if (parens <= 0) + return true; + parens--; + break; + case '"': + case '\'': + i = skipOverString(code, i + 1, code[i]); + if (i >= code.size()) { + return true; // Do not let unterminated strings enter multi-line mode + } + break; + case '\\': + if (i + 1 < code.size() && code[i + 1] == '/') i++; - } - continue; - case '{': - curlyBrackets++; - break; - case '}': - if ( curlyBrackets <= 0 ) - return true; - curlyBrackets--; - break; - case '[': - squareBrackets++; - break; - case ']': - if ( squareBrackets <= 0 ) - return true; - squareBrackets--; - break; - case '(': - parens++; - break; - case ')': - if ( parens <= 0 ) - return true; - parens--; - break; - case '"': - case '\'': - i = skipOverString(code, i + 1, code[i]); - if (i >= code.size()) { - return true; // Do not let unterminated strings enter multi-line mode - } - break; - case '\\': - if ( i + 1 < code.size() && code[i+1] == '/' ) i++; - break; - case '+': - case '-': - if ( i + 1 < code.size() && code[i+1] == code[i] ) { - i++; - continue; // postfix op (++/--) can't be a dangling op - } - break; + break; + case '+': + case '-': + if (i + 1 < code.size() && code[i + 1] == code[i]) { + i++; + continue; // postfix op (++/--) can't be a dangling op + } + break; } - if ( i >= code.size() ) { + if (i >= code.size()) { danglingOp = false; break; } - if ( isOpSymbol( code[i] ) ) danglingOp = true; - else if ( !std::isspace( static_cast<unsigned char>( code[i] ) ) ) danglingOp = false; + if (isOpSymbol(code[i])) + danglingOp = true; + else if (!std::isspace(static_cast<unsigned char>(code[i]))) + danglingOp = false; } return curlyBrackets == 0 && squareBrackets == 0 && parens == 0 && !danglingOp; @@ -363,74 +364,74 @@ bool isBalanced( const std::string& code ) { struct BalancedTest : public mongo::StartupTest { public: void run() { - verify( isBalanced( "x = 5" ) ); - verify( isBalanced( "function(){}" ) ); - verify( isBalanced( "function(){\n}" ) ); - verify( ! isBalanced( "function(){" ) ); - verify( isBalanced( "x = \"{\";" ) ); - verify( isBalanced( "// {" ) ); - verify( ! isBalanced( "// \n {" ) ); - verify( ! isBalanced( "\"//\" {" ) ); - verify( isBalanced( "{x:/x\\//}" ) ); - verify( ! isBalanced( "{ \\/// }" ) ); - verify( isBalanced( "x = 5 + y " ) ); - verify( ! isBalanced( "x = " ) ); - verify( ! isBalanced( "x = // hello" ) ); - verify( ! isBalanced( "x = 5 +" ) ); - verify( isBalanced( " x ++" ) ); - verify( isBalanced( "-- x" ) ); - verify( !isBalanced( "a." ) ); - verify( !isBalanced( "a. " ) ); - verify( isBalanced( "a.b" ) ); - - // SERVER-5809 and related cases -- - verify( isBalanced( "a = {s:\"\\\"\"}" ) ); // a = {s:"\""} - verify( isBalanced( "db.test.save({s:\"\\\"\"})" ) ); // db.test.save({s:"\""}) - verify( isBalanced( "printjson(\" \\\" \")" ) ); // printjson(" \" ") -- SERVER-8554 - verify( isBalanced( "var a = \"\\\\\";" ) ); // var a = "\\"; - verify( isBalanced( "var a = (\"\\\\\") //\"" ) ); // var a = ("\\") //" - verify( isBalanced( "var a = (\"\\\\\") //\\\"" ) ); // var a = ("\\") //\" - verify( isBalanced( "var a = (\"\\\\\") //" ) ); // var a = ("\\") // - verify( isBalanced( "var a = (\"\\\\\")" ) ); // var a = ("\\") - verify( isBalanced( "var a = (\"\\\\\\\"\")" ) ); // var a = ("\\\"") - verify( ! isBalanced( "var a = (\"\\\\\" //\"" ) ); // var a = ("\\" //" - verify( ! isBalanced( "var a = (\"\\\\\" //" ) ); // var a = ("\\" // - verify( ! isBalanced( "var a = (\"\\\\\"" ) ); // var a = ("\\" + verify(isBalanced("x = 5")); + verify(isBalanced("function(){}")); + verify(isBalanced("function(){\n}")); + verify(!isBalanced("function(){")); + verify(isBalanced("x = \"{\";")); + verify(isBalanced("// {")); + verify(!isBalanced("// \n {")); + verify(!isBalanced("\"//\" {")); + verify(isBalanced("{x:/x\\//}")); + verify(!isBalanced("{ \\/// }")); + verify(isBalanced("x = 5 + y ")); + verify(!isBalanced("x = ")); + verify(!isBalanced("x = // hello")); + verify(!isBalanced("x = 5 +")); + verify(isBalanced(" x ++")); + verify(isBalanced("-- x")); + verify(!isBalanced("a.")); + verify(!isBalanced("a. ")); + verify(isBalanced("a.b")); + + // SERVER-5809 and related cases -- + verify(isBalanced("a = {s:\"\\\"\"}")); // a = {s:"\""} + verify(isBalanced("db.test.save({s:\"\\\"\"})")); // db.test.save({s:"\""}) + verify(isBalanced("printjson(\" \\\" \")")); // printjson(" \" ") -- SERVER-8554 + verify(isBalanced("var a = \"\\\\\";")); // var a = "\\"; + verify(isBalanced("var a = (\"\\\\\") //\"")); // var a = ("\\") //" + verify(isBalanced("var a = (\"\\\\\") //\\\"")); // var a = ("\\") //\" + verify(isBalanced("var a = (\"\\\\\") //")); // var a = ("\\") // + verify(isBalanced("var a = (\"\\\\\")")); // var a = ("\\") + verify(isBalanced("var a = (\"\\\\\\\"\")")); // var a = ("\\\"") + verify(!isBalanced("var a = (\"\\\\\" //\"")); // var a = ("\\" //" + verify(!isBalanced("var a = (\"\\\\\" //")); // var a = ("\\" // + verify(!isBalanced("var a = (\"\\\\\"")); // var a = ("\\" } } balanced_test; -string finishCode( string code ) { - while ( ! isBalanced( code ) ) { +string finishCode(string code) { + while (!isBalanced(code)) { inMultiLine = true; code += "\n"; // cancel multiline if two blank lines are entered - if ( code.find( "\n\n\n" ) != string::npos ) + if (code.find("\n\n\n") != string::npos) return ";"; - char * line = shellReadline( "... " , 1 ); - if ( gotInterrupted ) { - if ( line ) - free( line ); + char* line = shellReadline("... ", 1); + if (gotInterrupted) { + if (line) + free(line); return ""; } - if ( ! line ) + if (!line) return ""; - char * linePtr = line; - while ( str::startsWith( linePtr, "... " ) ) + char* linePtr = line; + while (str::startsWith(linePtr, "... ")) linePtr += 4; code += linePtr; - free( line ); + free(line); } return code; } -bool execPrompt( mongo::Scope &scope, const char *promptFunction, string &prompt ) { - string execStatement = string( "__prompt__ = " ) + promptFunction + "();"; - scope.exec( "delete __prompt__;", "", false, false, false, 0 ); - scope.exec( execStatement, "", false, false, false, 0 ); - if ( scope.type( "__prompt__" ) == String ) { - prompt = scope.getString( "__prompt__" ); +bool execPrompt(mongo::Scope& scope, const char* promptFunction, string& prompt) { + string execStatement = string("__prompt__ = ") + promptFunction + "();"; + scope.exec("delete __prompt__;", "", false, false, false, 0); + scope.exec(execStatement, "", false, false, false, 0); + if (scope.type("__prompt__") == String) { + prompt = scope.getString("__prompt__"); return true; } return false; @@ -441,53 +442,51 @@ bool execPrompt( mongo::Scope &scope, const char *promptFunction, string &prompt * * @param whatToEdit Name of JavaScript variable to be edited, or any text string */ -static void edit( const string& whatToEdit ) { - +static void edit(const string& whatToEdit) { // EDITOR may be defined in the JavaScript scope or in the environment string editor; - if ( shellMainScope->type( "EDITOR" ) == String ) { - editor = shellMainScope->getString( "EDITOR" ); - } - else { - static const char * editorFromEnv = getenv( "EDITOR" ); - if ( editorFromEnv ) { + if (shellMainScope->type("EDITOR") == String) { + editor = shellMainScope->getString("EDITOR"); + } else { + static const char* editorFromEnv = getenv("EDITOR"); + if (editorFromEnv) { editor = editorFromEnv; } } - if ( editor.empty() ) { + if (editor.empty()) { cout << "please define EDITOR as a JavaScript string or as an environment variable" << endl; return; } // "whatToEdit" might look like a variable/property name bool editingVariable = true; - for ( const char* p = whatToEdit.c_str(); *p; ++p ) { - if ( ! ( isalnum( *p ) || *p == '_' || *p == '.' ) ) { + for (const char* p = whatToEdit.c_str(); *p; ++p) { + if (!(isalnum(*p) || *p == '_' || *p == '.')) { editingVariable = false; break; } } string js; - if ( editingVariable ) { - // If "whatToEdit" is undeclared or uninitialized, declare - int varType = shellMainScope->type( whatToEdit.c_str() ); - if ( varType == Undefined ) { - shellMainScope->exec( "var " + whatToEdit , "(shell)", false, true, false ); + if (editingVariable) { + // If "whatToEdit" is undeclared or uninitialized, declare + int varType = shellMainScope->type(whatToEdit.c_str()); + if (varType == Undefined) { + shellMainScope->exec("var " + whatToEdit, "(shell)", false, true, false); } // Convert "whatToEdit" to JavaScript (JSON) text - if ( !shellMainScope->exec( "__jsout__ = tojson(" + whatToEdit + ")", "tojs", false, false, false ) ) - return; // Error already printed + if (!shellMainScope->exec( + "__jsout__ = tojson(" + whatToEdit + ")", "tojs", false, false, false)) + return; // Error already printed - js = shellMainScope->getString( "__jsout__" ); + js = shellMainScope->getString("__jsout__"); - if ( strstr( js.c_str(), "[native code]" ) ) { + if (strstr(js.c_str(), "[native code]")) { cout << "can't edit native functions" << endl; return; } - } - else { + } else { js = whatToEdit; } @@ -495,110 +494,110 @@ static void edit( const string& whatToEdit ) { string filename; const int maxAttempts = 10; int i; - for ( i = 0; i < maxAttempts; ++i ) { + for (i = 0; i < maxAttempts; ++i) { StringBuilder sb; #ifdef _WIN32 char tempFolder[MAX_PATH]; - GetTempPathA( sizeof tempFolder, tempFolder ); - sb << tempFolder << "mongo_edit" << time( 0 ) + i << ".js"; + GetTempPathA(sizeof tempFolder, tempFolder); + sb << tempFolder << "mongo_edit" << time(0) + i << ".js"; #else - sb << "/tmp/mongo_edit" << time( 0 ) + i << ".js"; + sb << "/tmp/mongo_edit" << time(0) + i << ".js"; #endif filename = sb.str(); if (!::mongo::shell_utils::fileExists(filename)) break; } - if ( i == maxAttempts ) { + if (i == maxAttempts) { cout << "couldn't create unique temp file after " << maxAttempts << " attempts" << endl; return; } // Create the temp file - FILE * tempFileStream; - tempFileStream = fopen( filename.c_str(), "wt" ); - if ( ! tempFileStream ) { - cout << "couldn't create temp file (" << filename << "): " << errnoWithDescription() << endl; + FILE* tempFileStream; + tempFileStream = fopen(filename.c_str(), "wt"); + if (!tempFileStream) { + cout << "couldn't create temp file (" << filename << "): " << errnoWithDescription() + << endl; return; } // Write JSON into the temp file size_t fileSize = js.size(); - if ( fwrite( js.data(), sizeof( char ), fileSize, tempFileStream ) != fileSize ) { + if (fwrite(js.data(), sizeof(char), fileSize, tempFileStream) != fileSize) { int systemErrno = errno; - cout << "failed to write to temp file: " << errnoWithDescription( systemErrno ) << endl; - fclose( tempFileStream ); - remove( filename.c_str() ); + cout << "failed to write to temp file: " << errnoWithDescription(systemErrno) << endl; + fclose(tempFileStream); + remove(filename.c_str()); return; } - fclose( tempFileStream ); + fclose(tempFileStream); // Pass file to editor StringBuilder sb; sb << editor << " " << filename; - int ret = ::system( sb.str().c_str() ); - if ( ret ) { - if ( ret == -1 ) { + int ret = ::system(sb.str().c_str()); + if (ret) { + if (ret == -1) { int systemErrno = errno; - cout << "failed to launch $EDITOR (" << editor << "): " << errnoWithDescription( systemErrno ) << endl; - } - else + cout << "failed to launch $EDITOR (" << editor + << "): " << errnoWithDescription(systemErrno) << endl; + } else cout << "editor exited with error (" << ret << "), not applying changes" << endl; - remove( filename.c_str() ); + remove(filename.c_str()); return; } // The editor gave return code zero, so read the file back in - tempFileStream = fopen( filename.c_str(), "rt" ); - if ( ! tempFileStream ) { + tempFileStream = fopen(filename.c_str(), "rt"); + if (!tempFileStream) { cout << "couldn't open temp file on return from editor: " << errnoWithDescription() << endl; - remove( filename.c_str() ); + remove(filename.c_str()); return; } sb.reset(); int bytes; do { char buf[1024]; - bytes = fread( buf, sizeof( char ), sizeof buf, tempFileStream ); - if ( ferror( tempFileStream ) ) { + bytes = fread(buf, sizeof(char), sizeof buf, tempFileStream); + if (ferror(tempFileStream)) { cout << "failed to read temp file: " << errnoWithDescription() << endl; - fclose( tempFileStream ); - remove( filename.c_str() ); + fclose(tempFileStream); + remove(filename.c_str()); return; } - sb.append( StringData( buf, bytes ) ); - } while ( bytes ); + sb.append(StringData(buf, bytes)); + } while (bytes); // Done with temp file, close and delete it - fclose( tempFileStream ); - remove( filename.c_str() ); + fclose(tempFileStream); + remove(filename.c_str()); - if ( editingVariable ) { + if (editingVariable) { // Try to execute assignment to copy edited value back into the variable - const string code = whatToEdit + string( " = " ) + sb.str(); - if ( !shellMainScope->exec( code, "tojs", false, true, false ) ) { + const string code = whatToEdit + string(" = ") + sb.str(); + if (!shellMainScope->exec(code, "tojs", false, true, false)) { cout << "error executing assignment: " << code << endl; } - } - else { - linenoisePreloadBuffer( sb.str().c_str() ); + } else { + linenoisePreloadBuffer(sb.str().c_str()); } } -int _main( int argc, char* argv[], char **envp ) { +int _main(int argc, char* argv[], char** envp) { setupSignalHandlers(true); setupSignals(); - mongo::shell_utils::RecordMyLocation( argv[ 0 ] ); + mongo::shell_utils::RecordMyLocation(argv[0]); shellGlobalParams.url = "test"; mongo::runGlobalInitializersOrDie(argc, argv, envp); // hide password from ps output - for ( int i = 0; i < (argc-1); ++i ) { - if ( !strcmp(argv[i], "-p") || !strcmp( argv[i], "--password" ) ) { + for (int i = 0; i < (argc - 1); ++i) { + if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--password")) { char* arg = argv[i + 1]; - while ( *arg ) { + while (*arg) { *arg++ = 'x'; } } @@ -609,12 +608,13 @@ int _main( int argc, char* argv[], char **envp ) { mongo::StartupTest::runTests(); - logger::globalLogManager()->getNamedDomain("javascriptOutput")->attachAppender( - logger::MessageLogDomain::AppenderAutoPtr( - new logger::ConsoleAppender<logger::MessageEventEphemeral>( - new logger::MessageEventUnadornedEncoder))); + logger::globalLogManager() + ->getNamedDomain("javascriptOutput") + ->attachAppender(logger::MessageLogDomain::AppenderAutoPtr( + new logger::ConsoleAppender<logger::MessageEventEphemeral>( + new logger::MessageEventUnadornedEncoder))); - if (!shellGlobalParams.nodb) { // connect to db + if (!shellGlobalParams.nodb) { // connect to db stringstream ss; if (mongo::serverGlobalParams.quiet) ss << "__quiet = true;"; @@ -642,32 +642,30 @@ int _main( int argc, char* argv[], char **envp ) { stringstream authStringStream; authStringStream << "(function() { " << endl; if (!shellGlobalParams.authenticationMechanism.empty()) { - authStringStream << "DB.prototype._defaultAuthenticationMechanism = \"" << - escape(shellGlobalParams.authenticationMechanism) << "\";" << endl; + authStringStream << "DB.prototype._defaultAuthenticationMechanism = \"" + << escape(shellGlobalParams.authenticationMechanism) << "\";" << endl; } if (!shellGlobalParams.gssapiServiceName.empty()) { - authStringStream << "DB.prototype._defaultGssapiServiceName = \"" << - escape(shellGlobalParams.gssapiServiceName) << "\";" << endl; + authStringStream << "DB.prototype._defaultGssapiServiceName = \"" + << escape(shellGlobalParams.gssapiServiceName) << "\";" << endl; } if (!shellGlobalParams.nodb && shellGlobalParams.username.size()) { - authStringStream << "var username = \"" << escape(shellGlobalParams.username) << "\";" << - endl; + authStringStream << "var username = \"" << escape(shellGlobalParams.username) << "\";" + << endl; if (shellGlobalParams.usingPassword) { authStringStream << "var password = \"" << escape(shellGlobalParams.password) << "\";" << endl; } if (shellGlobalParams.authenticationDatabase.empty()) { authStringStream << "var authDb = db;" << endl; - } - else { + } else { authStringStream << "var authDb = db.getSiblingDB(\"" << escape(shellGlobalParams.authenticationDatabase) << "\");" << endl; } - authStringStream << "authDb._authOrThrow({ " << - saslCommandUserFieldName << ": username "; - if (shellGlobalParams.usingPassword) { + authStringStream << "authDb._authOrThrow({ " << saslCommandUserFieldName << ": username "; + if (shellGlobalParams.usingPassword) { authStringStream << ", " << saslCommandPasswordFieldName << ": password "; } @@ -680,93 +678,92 @@ int _main( int argc, char* argv[], char **envp ) { authStringStream << "}())"; mongo::shell_utils::_dbAuth = authStringStream.str(); - mongo::ScriptEngine::setConnectCallback( mongo::shell_utils::onConnect ); + mongo::ScriptEngine::setConnectCallback(mongo::shell_utils::onConnect); mongo::ScriptEngine::setup(); - mongo::globalScriptEngine->setScopeInitCallback( mongo::shell_utils::initScope ); - unique_ptr< mongo::Scope > scope( mongo::globalScriptEngine->newScope() ); + mongo::globalScriptEngine->setScopeInitCallback(mongo::shell_utils::initScope); + unique_ptr<mongo::Scope> scope(mongo::globalScriptEngine->newScope()); shellMainScope = scope.get(); - if( shellGlobalParams.runShell ) + if (shellGlobalParams.runShell) cout << "type \"help\" for help" << endl; - + // Load and execute /etc/mongorc.js before starting shell std::string rcGlobalLocation; #ifndef _WIN32 - rcGlobalLocation = "/etc/mongorc.js" ; + rcGlobalLocation = "/etc/mongorc.js"; #else wchar_t programDataPath[MAX_PATH]; - if ( S_OK == SHGetFolderPathW(NULL, - CSIDL_COMMON_APPDATA, - NULL, - 0, - programDataPath) ) { + if (S_OK == SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, programDataPath)) { rcGlobalLocation = str::stream() << toUtf8String(programDataPath) << "\\MongoDB\\mongorc.js"; } -#endif - if ( !rcGlobalLocation.empty() && ::mongo::shell_utils::fileExists(rcGlobalLocation) ) { - if ( ! scope->execFile( rcGlobalLocation , false , true ) ) { +#endif + if (!rcGlobalLocation.empty() && ::mongo::shell_utils::fileExists(rcGlobalLocation)) { + if (!scope->execFile(rcGlobalLocation, false, true)) { cout << "The \"" << rcGlobalLocation << "\" file could not be executed" << endl; } } - if ( !shellGlobalParams.script.empty() ) { + if (!shellGlobalParams.script.empty()) { mongo::shell_utils::MongoProgramScope s; - if ( ! scope->exec( shellGlobalParams.script , "(shell eval)" , true , true , false ) ) + if (!scope->exec(shellGlobalParams.script, "(shell eval)", true, true, false)) return -4; } for (size_t i = 0; i < shellGlobalParams.files.size(); ++i) { mongo::shell_utils::MongoProgramScope s; - if ( shellGlobalParams.files.size() > 1 ) + if (shellGlobalParams.files.size() > 1) cout << "loading file: " << shellGlobalParams.files[i] << endl; - if ( ! scope->execFile( shellGlobalParams.files[i] , false , true ) ) { + if (!scope->execFile(shellGlobalParams.files[i], false, true)) { cout << "failed to load: " << shellGlobalParams.files[i] << endl; return -3; } } - if ( shellGlobalParams.files.size() == 0 && shellGlobalParams.script.empty() ) + if (shellGlobalParams.files.size() == 0 && shellGlobalParams.script.empty()) shellGlobalParams.runShell = true; - if ( shellGlobalParams.runShell ) { - + if (shellGlobalParams.runShell) { mongo::shell_utils::MongoProgramScope s; // If they specify norc, assume it's not their first time bool hasMongoRC = shellGlobalParams.norc; string rcLocation; - if ( !shellGlobalParams.norc ) { + if (!shellGlobalParams.norc) { #ifndef _WIN32 - if ( getenv( "HOME" ) != NULL ) - rcLocation = str::stream() << getenv( "HOME" ) << "/.mongorc.js" ; + if (getenv("HOME") != NULL) + rcLocation = str::stream() << getenv("HOME") << "/.mongorc.js"; #else - if ( getenv( "HOMEDRIVE" ) != NULL && getenv( "HOMEPATH" ) != NULL ) + if (getenv("HOMEDRIVE") != NULL && getenv("HOMEPATH") != NULL) rcLocation = str::stream() << toUtf8String(_wgetenv(L"HOMEDRIVE")) << toUtf8String(_wgetenv(L"HOMEPATH")) << "\\.mongorc.js"; #endif - if ( !rcLocation.empty() && ::mongo::shell_utils::fileExists(rcLocation) ) { + if (!rcLocation.empty() && ::mongo::shell_utils::fileExists(rcLocation)) { hasMongoRC = true; - if ( ! scope->execFile( rcLocation , false , true ) ) { - cout << "The \".mongorc.js\" file located in your home folder could not be executed" << endl; + if (!scope->execFile(rcLocation, false, true)) { + cout << "The \".mongorc.js\" file located in your home folder could not be " + "executed" << endl; return -5; } } } - if ( !hasMongoRC && isatty(fileno(stdin)) ) { - cout << "Welcome to the MongoDB shell.\n" - "For interactive help, type \"help\".\n" - "For more comprehensive documentation, see\n\thttp://docs.mongodb.org/\n" - "Questions? Try the support group\n\thttp://groups.google.com/group/mongodb-user" << endl; + if (!hasMongoRC && isatty(fileno(stdin))) { + cout + << "Welcome to the MongoDB shell.\n" + "For interactive help, type \"help\".\n" + "For more comprehensive documentation, see\n\thttp://docs.mongodb.org/\n" + "Questions? Try the support group\n\thttp://groups.google.com/group/mongodb-user" + << endl; File f; f.open(rcLocation.c_str(), false); // Create empty .mongorc.js file } if (!shellGlobalParams.nodb && !mongo::serverGlobalParams.quiet && isatty(fileno(stdin))) { - scope->exec( "shellHelper( 'show', 'startupWarnings' )", "(shellwarnings", false, true, false ); + scope->exec( + "shellHelper( 'show', 'startupWarnings' )", "(shellwarnings", false, true, false); } shellHistoryInit(); @@ -774,116 +771,120 @@ int _main( int argc, char* argv[], char **envp ) { string prompt; int promptType; - while ( 1 ) { + while (1) { inMultiLine = false; gotInterrupted = false; - promptType = scope->type( "prompt" ); - if ( promptType == String ) { - prompt = scope->getString( "prompt" ); - } - else if ( ( promptType == Code ) && - execPrompt( *scope, "prompt", prompt ) ) { - } - else if ( execPrompt( *scope, "defaultPrompt", prompt ) ) { - } - else { + promptType = scope->type("prompt"); + if (promptType == String) { + prompt = scope->getString("prompt"); + } else if ((promptType == Code) && execPrompt(*scope, "prompt", prompt)) { + } else if (execPrompt(*scope, "defaultPrompt", prompt)) { + } else { prompt = "> "; } - char * line = shellReadline( prompt.c_str() ); + char* line = shellReadline(prompt.c_str()); - char * linePtr = line; // can't clobber 'line', we need to free() it later - if ( linePtr ) { - while ( linePtr[0] == ' ' ) + char* linePtr = line; // can't clobber 'line', we need to free() it later + if (linePtr) { + while (linePtr[0] == ' ') ++linePtr; - int lineLen = strlen( linePtr ); - while ( lineLen > 0 && linePtr[lineLen - 1] == ' ' ) + int lineLen = strlen(linePtr); + while (lineLen > 0 && linePtr[lineLen - 1] == ' ') linePtr[--lineLen] = 0; } - if ( ! linePtr || ( strlen( linePtr ) == 4 && strstr( linePtr , "exit" ) ) ) { + if (!linePtr || (strlen(linePtr) == 4 && strstr(linePtr, "exit"))) { if (!mongo::serverGlobalParams.quiet) cout << "bye" << endl; - if ( line ) - free( line ); + if (line) + free(line); break; } string code = linePtr; - if ( code == "exit" || code == "exit;" ) { - free( line ); + if (code == "exit" || code == "exit;") { + free(line); break; } - if ( code == "cls" ) { - free( line ); + if (code == "cls") { + free(line); linenoiseClearScreen(); continue; } - if ( code.size() == 0 ) { - free( line ); + if (code.size() == 0) { + free(line); continue; } - if ( str::startsWith( linePtr, "edit " ) ) { - shellHistoryAdd( linePtr ); + if (str::startsWith(linePtr, "edit ")) { + shellHistoryAdd(linePtr); - const char* s = linePtr + 5; // skip "edit " - while( *s && isspace( *s ) ) + const char* s = linePtr + 5; // skip "edit " + while (*s && isspace(*s)) s++; - edit( s ); - free( line ); + edit(s); + free(line); continue; } gotInterrupted = false; - code = finishCode( code ); - if ( gotInterrupted ) { + code = finishCode(code); + if (gotInterrupted) { cout << endl; - free( line ); + free(line); continue; } - if ( code.size() == 0 ) { - free( line ); + if (code.size() == 0) { + free(line); break; } bool wascmd = false; { string cmd = linePtr; - if ( cmd.find( " " ) > 0 ) - cmd = cmd.substr( 0 , cmd.find( " " ) ); + if (cmd.find(" ") > 0) + cmd = cmd.substr(0, cmd.find(" ")); - if ( cmd.find( "\"" ) == string::npos ) { + if (cmd.find("\"") == string::npos) { try { - scope->exec( (string)"__iscmd__ = shellHelper[\"" + cmd + "\"];" , "(shellhelp1)" , false , true , true ); - if ( scope->getBoolean( "__iscmd__" ) ) { - scope->exec( (string)"shellHelper( \"" + cmd + "\" , \"" + code.substr( cmd.size() ) + "\");" , "(shellhelp2)" , false , true , false ); + scope->exec((string) "__iscmd__ = shellHelper[\"" + cmd + "\"];", + "(shellhelp1)", + false, + true, + true); + if (scope->getBoolean("__iscmd__")) { + scope->exec((string) "shellHelper( \"" + cmd + "\" , \"" + + code.substr(cmd.size()) + "\");", + "(shellhelp2)", + false, + true, + false); wascmd = true; } - } - catch ( std::exception& e ) { + } catch (std::exception& e) { cout << "error2:" << e.what() << endl; wascmd = true; } } } - if ( ! wascmd ) { + if (!wascmd) { try { - if ( scope->exec( code.c_str() , "(shell)" , false , true , false ) ) - scope->exec( "shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false ); - } - catch ( std::exception& e ) { + if (scope->exec(code.c_str(), "(shell)", false, true, false)) + scope->exec( + "shellPrintHelper( __lastres__ );", "(shell2)", true, true, false); + } catch (std::exception& e) { cout << "error:" << e.what() << endl; } } - shellHistoryAdd( code.c_str() ); - free( line ); + shellHistoryAdd(code.c_str()); + free(line); } shellHistoryDone(); @@ -903,24 +904,22 @@ int wmain(int argc, wchar_t* argvW[], wchar_t* envpW[]) { try { WindowsCommandLine wcl(argc, argvW, envpW); returnCode = _main(argc, wcl.argv(), wcl.envp()); - } - catch ( mongo::DBException& e ) { + } catch (mongo::DBException& e) { cerr << "exception: " << e.what() << endl; returnCode = 1; } quickExit(returnCode); } -#else // #ifdef _WIN32 -int main( int argc, char* argv[], char **envp ) { +#else // #ifdef _WIN32 +int main(int argc, char* argv[], char** envp) { static mongo::StaticObserver staticObserver; int returnCode; try { - returnCode = _main( argc , argv, envp ); - } - catch ( mongo::DBException& e ) { + returnCode = _main(argc, argv, envp); + } catch (mongo::DBException& e) { cerr << "exception: " << e.what() << endl; returnCode = 1; } quickExit(returnCode); } -#endif // #ifdef _WIN32 +#endif // #ifdef _WIN32 diff --git a/src/mongo/shell/linenoise.cpp b/src/mongo/shell/linenoise.cpp index 43b42b9f028..5fd3371f374 100644 --- a/src/mongo/shell/linenoise.cpp +++ b/src/mongo/shell/linenoise.cpp @@ -32,7 +32,7 @@ * line editing lib needs to be 20,000 lines of C code. * * You can find the latest source code at: - * + * * http://github.com/antirez/linenoise * * Does a number of crazy assumptions that happen to be true in 99.9999% of @@ -80,7 +80,7 @@ * ED2 (Clear entire screen) * Sequence: ESC [ 2 J * Effect: clear the whole screen - * + * */ #ifdef _WIN32 @@ -144,7 +144,7 @@ struct linenoiseCompletions { #define LINENOISE_MAX_LINE 4096 // make control-characters more readable -#define ctrlChar( upperCaseASCII ) ( upperCaseASCII - 0x40 ) +#define ctrlChar(upperCaseASCII) (upperCaseASCII - 0x40) /** * Recompute widths of all characters in a UChar32 buffer @@ -152,9 +152,9 @@ struct linenoiseCompletions { * @param widths output buffer of character widths * @param charCount number of characters in buffer */ -static void recomputeCharacterWidths( const UChar32* text, char* widths, int charCount ) { - for ( int i = 0; i < charCount; ++i ) { - widths[ i ] = mk_wcwidth( text[ i ] ); +static void recomputeCharacterWidths(const UChar32* text, char* widths, int charCount) { + for (int i = 0; i < charCount; ++i) { + widths[i] = mk_wcwidth(text[i]); } } @@ -167,19 +167,21 @@ static void recomputeCharacterWidths( const UChar32* text, char* widths, int cha * @param xOut returned x position (zero-based) * @param yOut returned y position (zero-based) */ -static void calculateScreenPosition( int x, int y, int screenColumns, int charCount, int& xOut, int& yOut ) { +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; + 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 + if (xOut == screenColumns) { // we have to special-case line wrap xOut = 0; ++yOut; } @@ -190,50 +192,49 @@ static void calculateScreenPosition( int x, int y, int screenColumns, int charCo * @param buf32 text to calculate * @param len length of text to calculate */ -static int calculateColumnPosition( UChar32* buf32, int len) { - int width = mk_wcswidth( reinterpret_cast<const int*>( buf32 ), len ); - if ( width == -1 ) +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; } -static bool isControlChar( UChar32 testChar ) { - return ( testChar < ' ' ) || // C0 controls - ( testChar >= 0x7F && testChar <= 0x9F ); // DEL and C1 controls +static bool isControlChar(UChar32 testChar) { + return (testChar < ' ') || // C0 controls + (testChar >= 0x7F && testChar <= 0x9F); // DEL and C1 controls } -struct PromptBase { // a convenience struct for grouping prompt info - Utf32String promptText; // our copy of the prompt text, edited - char* promptCharWidths; // character widths from mk_wcwidth() - int promptChars; // chars in promptText - int promptExtraLines; // extra lines (beyond 1) occupied by prompt - int promptIndentation; // column offset to end of prompt - int promptLastLinePosition; // index into promptText where last line begins - int promptPreviousInputLen; // promptChars of previous input line, for clearing - int promptCursorRowOffset; // where the cursor is relative to the start of the prompt - int promptScreenColumns; // width of screen in columns - int promptPreviousLen; // help erasing - int promptErrorCode; // error code (invalid UTF-8) or zero - - PromptBase() : promptPreviousInputLen( 0 ) { } +struct PromptBase { // a convenience struct for grouping prompt info + Utf32String promptText; // our copy of the prompt text, edited + char* promptCharWidths; // character widths from mk_wcwidth() + int promptChars; // chars in promptText + int promptExtraLines; // extra lines (beyond 1) occupied by prompt + int promptIndentation; // column offset to end of prompt + int promptLastLinePosition; // index into promptText where last line begins + int promptPreviousInputLen; // promptChars of previous input line, for clearing + int promptCursorRowOffset; // where the cursor is relative to the start of the prompt + int promptScreenColumns; // width of screen in columns + int promptPreviousLen; // help erasing + int promptErrorCode; // error code (invalid UTF-8) or zero + + PromptBase() : promptPreviousInputLen(0) {} }; struct PromptInfo : public PromptBase { - - PromptInfo( const UChar8* textPtr, int columns ) { + PromptInfo(const UChar8* textPtr, int columns) { promptExtraLines = 0; promptLastLinePosition = 0; promptPreviousLen = 0; promptScreenColumns = columns; - Utf32String tempUnicode( textPtr ); + Utf32String tempUnicode(textPtr); // strip control characters from the prompt -- we do allow newline UChar32* pIn = tempUnicode.get(); UChar32* pOut = pIn; - while ( *pIn ) { + while (*pIn) { UChar32 c = *pIn; - if ( '\n' == c || !isControlChar( c ) ) { + if ('\n' == c || !isControlChar(c)) { *pOut = c; ++pOut; } @@ -244,16 +245,15 @@ struct PromptInfo : public PromptBase { promptText = tempUnicode; int x = 0; - for ( int i = 0; i < promptChars; ++i ) { + for (int i = 0; i < promptChars; ++i) { UChar32 c = promptText[i]; - if ( '\n' == c ) { + if ('\n' == c) { x = 0; ++promptExtraLines; promptLastLinePosition = i + 1; - } - else { + } else { ++x; - if ( x >= promptScreenColumns ) { + if (x >= promptScreenColumns) { x = 0; ++promptExtraLines; promptLastLinePosition = i + 1; @@ -267,52 +267,62 @@ struct PromptInfo : public PromptBase { // Used with DynamicPrompt (history search) // -static const Utf32String forwardSearchBasePrompt( reinterpret_cast<const UChar8*>( "(i-search)`" ) ); -static const Utf32String reverseSearchBasePrompt( reinterpret_cast<const UChar8*>( "(reverse-i-search)`" ) ); -static const Utf32String endSearchBasePrompt( reinterpret_cast<const UChar8*>( "': " ) ); -static Utf32String previousSearchText; // remembered across invocations of linenoise() +static const Utf32String forwardSearchBasePrompt(reinterpret_cast<const UChar8*>("(i-search)`")); +static const Utf32String reverseSearchBasePrompt( + reinterpret_cast<const UChar8*>("(reverse-i-search)`")); +static const Utf32String endSearchBasePrompt(reinterpret_cast<const UChar8*>("': ")); +static Utf32String previousSearchText; // remembered across invocations of linenoise() // changing prompt for "(reverse-i-search)`text':" etc. // struct DynamicPrompt : public PromptBase { - Utf32String searchText; // text we are searching for - char* searchCharWidths; // character widths from mk_wcwidth() - int searchTextLen; // chars in searchText - int direction; // current search direction, 1=forward, -1=reverse + Utf32String searchText; // text we are searching for + char* searchCharWidths; // character widths from mk_wcwidth() + int searchTextLen; // chars in searchText + int direction; // current search direction, 1=forward, -1=reverse - DynamicPrompt( PromptBase& pi, int initialDirection ) : searchTextLen( 0 ), direction( initialDirection ) { + DynamicPrompt(PromptBase& pi, int initialDirection) + : searchTextLen(0), direction(initialDirection) { promptScreenColumns = pi.promptScreenColumns; promptCursorRowOffset = 0; - Utf32String emptyString( 1 ); + Utf32String emptyString(1); searchText = emptyString; - const Utf32String* basePrompt = ( direction > 0 ) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt; + 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 (!) + 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 ) ); + 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 ); + calculateScreenPosition( + 0, 0, pi.promptScreenColumns, promptChars, promptIndentation, promptExtraLines); } - void updateSearchPrompt( void ) { - const Utf32String* basePrompt = ( direction > 0 ) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt; + 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 ); + 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 ) ); + memcpy(&tempUnicode[endIndex], + endSearchBasePrompt.get(), + sizeof(UChar32) * (endSearchBasePrompt.length() + 1)); tempUnicode.initFromBuffer(); promptText = tempUnicode; } - void updateSearchText( const UChar32* textPtr ) { - Utf32String tempUnicode( textPtr ); + void updateSearchText(const UChar32* textPtr) { + Utf32String tempUnicode(textPtr); searchTextLen = tempUnicode.chars(); searchText = tempUnicode; updateSearchPrompt(); @@ -320,56 +330,53 @@ struct DynamicPrompt : public PromptBase { }; class KillRing { - static const int capacity = 10; - int size; - int index; - char indexToSlot[10]; + static const int capacity = 10; + int size; + int index; + char indexToSlot[10]; vector<Utf32String> theRing; public: - enum action { actionOther, actionKill, actionYank }; - action lastAction; - size_t lastYankSize; + enum action { actionOther, actionKill, actionYank }; + action lastAction; + size_t lastYankSize; - KillRing() : size( 0 ), index( 0 ), lastAction( actionOther ) { - theRing.reserve( capacity ); + KillRing() : size(0), index(0), lastAction(actionOther) { + theRing.reserve(capacity); } - void kill( const UChar32* text, int textLen, bool forward ) { - if ( textLen == 0 ) { + void kill(const UChar32* text, int textLen, bool forward) { + if (textLen == 0) { return; } - Utf32String killedText( text, textLen ); - if ( lastAction == actionKill && size > 0 ) { + 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 ) ); + 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 ); + } else { + if (size < capacity) { + if (size > 0) { + memmove(&indexToSlot[1], &indexToSlot[0], size); } indexToSlot[0] = size; size++; - theRing.push_back( killedText ); - } - else { + theRing.push_back(killedText); + } else { int slot = indexToSlot[capacity - 1]; theRing[slot] = killedText; - memmove( &indexToSlot[1], &indexToSlot[0], capacity - 1 ); + memmove(&indexToSlot[1], &indexToSlot[0], capacity - 1); indexToSlot[0] = slot; } index = 0; @@ -377,48 +384,50 @@ public: } Utf32String* yank() { - return ( size > 0 ) ? &theRing[indexToSlot[index]] : 0; + return (size > 0) ? &theRing[indexToSlot[index]] : 0; } Utf32String* yankPop() { - if ( size == 0 ) { + if (size == 0) { return 0; } ++index; - if ( index == size ) { + if (index == size) { index = 0; } return &theRing[indexToSlot[index]]; } - }; class InputBuffer { - UChar32* buf32; // input buffer - char* charWidths; // character widths from mk_wcwidth() - int buflen; // buffer size in characters - int len; // length of text in input buffer - int pos; // character position in buffer ( 0 <= pos <= len ) + UChar32* buf32; // input buffer + char* charWidths; // character widths from mk_wcwidth() + int buflen; // buffer size in characters + int len; // length of text in input buffer + int pos; // character position in buffer ( 0 <= pos <= len ) - void clearScreen( PromptBase& pi ); - int incrementalHistorySearch( PromptBase& pi, int startChar ); - int completeLine( PromptBase& pi ); - void refreshLine( PromptBase& pi ); + void clearScreen(PromptBase& pi); + int incrementalHistorySearch(PromptBase& pi, int startChar); + int completeLine(PromptBase& pi); + void refreshLine(PromptBase& pi); public: - InputBuffer( UChar32* buffer, char* widthArray, int bufferLen ) : buf32( buffer ), charWidths( widthArray ), buflen( bufferLen - 1 ), len( 0 ), pos( 0 ) { + InputBuffer(UChar32* buffer, char* widthArray, int bufferLen) + : buf32(buffer), charWidths(widthArray), buflen(bufferLen - 1), len(0), pos(0) { buf32[0] = 0; } - void preloadBuffer( const UChar8* preloadText ) { + void preloadBuffer(const UChar8* preloadText) { size_t ucharCount; int errorCode; - copyString8to32( buf32, preloadText, buflen + 1, ucharCount, errorCode ); - recomputeCharacterWidths( buf32, charWidths, ucharCount ); + copyString8to32(buf32, preloadText, buflen + 1, ucharCount, errorCode); + recomputeCharacterWidths(buf32, charWidths, ucharCount); len = ucharCount; pos = ucharCount; } - int getInputLine( PromptBase& pi ); - int length( void ) const { return len; } + int getInputLine(PromptBase& pi); + int length(void) const { + return len; + } }; // Special codes for keyboard input: @@ -444,20 +453,20 @@ public: // // Maximum unsigned 32-bit value = 0xFFFFFFFF; // For reference, max 32-bit value // Highest allocated Unicode char = 0x001FFFFF; // For reference, max Unicode value -static const int META = 0x40000000; // Meta key combination -static const int CTRL = 0x20000000; // Ctrl key combination -static const int SPECIAL_KEY = 0x10000000; // Common bit for all special keys -static const int UP_ARROW_KEY = 0x10200000; // Special keys -static const int DOWN_ARROW_KEY = 0x10400000; -static const int RIGHT_ARROW_KEY = 0x10600000; -static const int LEFT_ARROW_KEY = 0x10800000; -static const int HOME_KEY = 0x10A00000; -static const int END_KEY = 0x10C00000; -static const int DELETE_KEY = 0x10E00000; -static const int PAGE_UP_KEY = 0x11000000; -static const int PAGE_DOWN_KEY = 0x11200000; - -static const char* unsupported_term[] = { "dumb", "cons25", "emacs", NULL }; +static const int META = 0x40000000; // Meta key combination +static const int CTRL = 0x20000000; // Ctrl key combination +static const int SPECIAL_KEY = 0x10000000; // Common bit for all special keys +static const int UP_ARROW_KEY = 0x10200000; // Special keys +static const int DOWN_ARROW_KEY = 0x10400000; +static const int RIGHT_ARROW_KEY = 0x10600000; +static const int LEFT_ARROW_KEY = 0x10800000; +static const int HOME_KEY = 0x10A00000; +static const int END_KEY = 0x10C00000; +static const int DELETE_KEY = 0x10E00000; +static const int PAGE_UP_KEY = 0x11000000; +static const int PAGE_DOWN_KEY = 0x11200000; + +static const char* unsupported_term[] = {"dumb", "cons25", "emacs", NULL}; static linenoiseCompletionCallback* completionCallback = NULL; #ifdef _WIN32 @@ -470,7 +479,7 @@ static struct termios orig_termios; /* in order to restore at exit */ static KillRing killRing; -static int rawmode = 0; /* for atexit() function to check if restore is needed*/ +static int rawmode = 0; /* for atexit() function to check if restore is needed*/ static int atexit_registered = 0; /* register atexit just 1 time */ static int historyMaxLen = LINENOISE_DEFAULT_HISTORY_MAX_LEN; static int historyLen = 0; @@ -483,72 +492,77 @@ static UChar8** history = NULL; static int historyPreviousIndex = -2; static bool historyRecallMostRecent = false; -static void linenoiseAtExit( void ); +static void linenoiseAtExit(void); -static bool isUnsupportedTerm( void ) { - char* term = getenv( "TERM" ); - if ( term == NULL ) +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] ) ) { + for (int j = 0; unsupported_term[j]; ++j) + if (!strcasecmp(term, unsupported_term[j])) { return true; } return false; } static void beep() { - fprintf( stderr, "\x7" ); // ctrl-G == bell/beep - fflush( stderr ); + fprintf(stderr, "\x7"); // ctrl-G == bell/beep + fflush(stderr); } -void linenoiseHistoryFree( void ) { - if ( history ) { - for ( int j = 0; j < historyLen; ++j ) - free( history[j] ); +void linenoiseHistoryFree(void) { + if (history) { + for (int j = 0; j < historyLen; ++j) + free(history[j]); historyLen = 0; - free( history ); + free(history); history = 0; } } -static int enableRawMode( void ) { +static int enableRawMode(void) { #ifdef _WIN32 - if ( ! console_in ) { - console_in = GetStdHandle( STD_INPUT_HANDLE ); - console_out = GetStdHandle( STD_OUTPUT_HANDLE ); + 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 ) ); + 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 ); + if (!isatty(0)) + goto fatal; + if (!atexit_registered) { + atexit(linenoiseAtExit); atexit_registered = 1; } - if ( tcgetattr( 0, &orig_termios ) == -1 ) goto fatal; + 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 ); + 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); + // raw.c_oflag &= ~(OPOST); /* control modes - set 8 bit chars */ - raw.c_cflag |= ( CS8 ); + 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 ); + 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 */ + 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; + if (tcsetattr(0, TCSADRAIN, &raw) < 0) + goto fatal; rawmode = 1; return 0; @@ -558,82 +572,83 @@ fatal: #endif } -static void disableRawMode( void ) { +static void disableRawMode(void) { #ifdef _WIN32 - SetConsoleMode( console_in, oldMode ); + SetConsoleMode(console_in, oldMode); console_in = 0; console_out = 0; #else - if ( rawmode && tcsetattr ( 0, TCSADRAIN, &orig_termios ) != -1 ) + if (rawmode && tcsetattr(0, TCSADRAIN, &orig_termios) != -1) rawmode = 0; #endif } // At exit we'll try to fix the terminal to the initial conditions -static void linenoiseAtExit( void ) { +static void linenoiseAtExit(void) { disableRawMode(); } -static int getScreenColumns( void ) { +static int getScreenColumns(void) { int cols; #ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO inf; - GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &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; + 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; } -static int getScreenRows( void ) { +static int getScreenRows(void) { int rows; #ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO inf; - GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &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; + rows = (ioctl(1, TIOCGWINSZ, &ws) == -1) ? 24 : ws.ws_row; #endif return (rows > 0) ? rows : 24; } -static void setDisplayAttribute( bool enhancedDisplay ) { +static void setDisplayAttribute(bool enhancedDisplay) { #ifdef _WIN32 - if ( enhancedDisplay ) { + if (enhancedDisplay) { CONSOLE_SCREEN_BUFFER_INFO inf; - GetConsoleScreenBufferInfo( console_out, &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; + 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 ); + 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 */ + 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 } @@ -645,84 +660,97 @@ static void setDisplayAttribute( bool enhancedDisplay ) { * @param len count of characters in the buffer * @param pos current cursor position within the buffer (0 <= pos <= len) */ -static void dynamicRefresh( PromptBase& pi, UChar32* buf32, int len, int pos ) { - +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 ); + 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 ); + 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 ); + 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 ); + GetConsoleScreenBufferInfo(console_out, &inf); inf.dwCursorPosition.X = 0; inf.dwCursorPosition.Y -= pi.promptCursorRowOffset /*- pi.promptExtraLines*/; - SetConsoleCursorPosition( console_out, inf.dwCursorPosition ); + SetConsoleCursorPosition(console_out, inf.dwCursorPosition); DWORD count; - FillConsoleOutputCharacterA( console_out, ' ', pi.promptPreviousLen + pi.promptPreviousInputLen, inf.dwCursorPosition, &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; + if (write32(1, pi.promptText.get(), pi.promptChars) == -1) + return; // display the input line - if ( write32( 1, buf32, len ) == -1 ) return; + if (write32(1, buf32, len) == -1) + return; // position the cursor - GetConsoleScreenBufferInfo( console_out, &inf ); + GetConsoleScreenBufferInfo(console_out, &inf); inf.dwCursorPosition.X = xCursorPos; // 0-based on Win32 inf.dwCursorPosition.Y -= yEndOfInput - yCursorPos; - SetConsoleCursorPosition( console_out, inf.dwCursorPosition ); -#else // _WIN32 + 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; + 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; + 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; + if (write32(1, pi.promptText.get(), pi.promptChars) == -1) + return; // display the input line - if ( write32( 1, buf32, len ) == -1 ) return; + 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; + 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; + 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; + 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 @@ -732,28 +760,27 @@ static void dynamicRefresh( PromptBase& pi, UChar32* buf32, int len, int pos ) { * Refresh the user's input line: the prompt is already onscreen and is not redrawn here * @param pi PromptBase struct holding information about the prompt and our screen position */ -void InputBuffer::refreshLine( PromptBase& pi ) { - +void InputBuffer::refreshLine(PromptBase& pi) { // check for a matching brace/bracket/paren, remember its position if found int highlight = -1; - if ( pos < len ) { + if (pos < len) { /* this scans for a brace matching buf32[pos] to highlight */ int scanDirection = 0; - if ( strchr( "}])", buf32[pos] ) ) + if (strchr("}])", buf32[pos])) scanDirection = -1; /* backwards */ - else if ( strchr( "{[(", buf32[pos] ) ) + else if (strchr("{[(", buf32[pos])) scanDirection = 1; /* forwards */ - if ( scanDirection ) { + if (scanDirection) { int unmatched = scanDirection; - for ( int i = pos + scanDirection; i >= 0 && i < len; i += scanDirection ) { + for (int i = pos + scanDirection; i >= 0 && i < len; i += scanDirection) { /* TODO: the right thing when inside a string */ - if ( strchr( "}])", buf32[i] ) ) + if (strchr("}])", buf32[i])) --unmatched; - else if ( strchr( "{[(", buf32[i] ) ) + else if (strchr("{[(", buf32[i])) ++unmatched; - if ( unmatched == 0 ) { + if (unmatched == 0) { highlight = i; break; } @@ -763,86 +790,98 @@ void InputBuffer::refreshLine( PromptBase& pi ) { // calculate the position of the end of the input line int xEndOfInput, yEndOfInput; - calculateScreenPosition( pi.promptIndentation, - 0, - pi.promptScreenColumns, - calculateColumnPosition( buf32, len ), - 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 ); + 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 ); + 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 ); + SetConsoleCursorPosition(console_out, inf.dwCursorPosition); DWORD count; - if ( len < pi.promptPreviousInputLen ) - FillConsoleOutputCharacterA( console_out, ' ', pi.promptPreviousInputLen, inf.dwCursorPosition, &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; + 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 ); + GetConsoleScreenBufferInfo(console_out, &inf); inf.dwCursorPosition.X = xCursorPos; // 0-based on Win32 inf.dwCursorPosition.Y -= yEndOfInput - yCursorPos; - SetConsoleCursorPosition( console_out, inf.dwCursorPosition ); -#else // _WIN32 + 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; + 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; + 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; + 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; + 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; + 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; + 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 @@ -855,35 +894,34 @@ void InputBuffer::refreshLine( PromptBase& pi ) { * * @return UChar32 Unicode character */ -static UChar32 readUnicodeCharacter( void ) { +static UChar32 readUnicodeCharacter(void) { static UChar8 utf8String[5]; static size_t utf8Count = 0; - while ( true ) { + while (true) { UChar8 c; - if ( read( 0, &c, 1 ) <= 0 ) return 0; - if ( c <= 0x7F ) { // short circuit ASCII + 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; + } 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 ) { + copyString8to32(unicodeChar, utf8String, 2, ucharCount, errorCode); + if (ucharCount && errorCode == 0) { utf8Count = 0; return unicodeChar[0]; } - } - else { + } else { utf8Count = 0; // this shouldn't happen: got four bytes but no UTF-8 character } } } -namespace EscapeSequenceProcessing { // move these out of global namespace +namespace EscapeSequenceProcessing { // move these out of global namespace // This chunk of code does parsing of the escape sequences sent by various Linux terminals. // @@ -907,16 +945,16 @@ namespace EscapeSequenceProcessing { // move these out of global namespace // as input, does any required processing including reading more characters and calling other // dispatch routines, then eventually returns the final (possibly extended or special) character. // -typedef UChar32 ( *CharacterDispatchRoutine )( UChar32 ); +typedef UChar32 (*CharacterDispatchRoutine)(UChar32); // This structure is used by doDispatch() to hold a list of characters to test for and // a list of routines to call if the character matches. The dispatch routine list is one // longer than the character list; the final entry is used if no character matches. // struct CharacterDispatch { - unsigned int len; // length of the chars list - const char* chars; // chars to test - CharacterDispatchRoutine* dispatch; // array of routines to call + unsigned int len; // length of the chars list + const char* chars; // chars to test + CharacterDispatchRoutine* dispatch; // array of routines to call }; // This dispatch routine is given a dispatch table and then farms work out to routines @@ -924,266 +962,279 @@ struct CharacterDispatch { // read more input characters to decide what should eventually be returned. Eventually, // a called routine returns either a character or -1 to indicate parsing failure. // -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 ); +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 ); + return dispatchTable.dispatch[dispatchTable.len](c); } -static UChar32 thisKeyMetaCtrl = 0; // holds pre-set Meta and/or Ctrl modifiers +static UChar32 thisKeyMetaCtrl = 0; // holds pre-set Meta and/or Ctrl modifiers // Final dispatch routines -- return something // -static UChar32 normalKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | c; } -static UChar32 upArrowKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | UP_ARROW_KEY; } -static UChar32 downArrowKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | DOWN_ARROW_KEY; } -static UChar32 rightArrowKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | RIGHT_ARROW_KEY; } -static UChar32 leftArrowKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | LEFT_ARROW_KEY; } -static UChar32 homeKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | HOME_KEY; } -static UChar32 endKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | END_KEY; } -static UChar32 pageUpKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | PAGE_UP_KEY; } -static UChar32 pageDownKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | PAGE_DOWN_KEY; } -static UChar32 deleteCharRoutine( UChar32 c ) { return thisKeyMetaCtrl | ctrlChar( 'H' ); } // key labeled Backspace -static UChar32 deleteKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | DELETE_KEY; } // key labeled Delete -static UChar32 ctrlUpArrowKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | CTRL | UP_ARROW_KEY; } -static UChar32 ctrlDownArrowKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | CTRL | DOWN_ARROW_KEY; } -static UChar32 ctrlRightArrowKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | CTRL | RIGHT_ARROW_KEY; } -static UChar32 ctrlLeftArrowKeyRoutine( UChar32 c ) { return thisKeyMetaCtrl | CTRL | LEFT_ARROW_KEY; } -static UChar32 escFailureRoutine( UChar32 c ) { beep(); return -1; } +static UChar32 normalKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | c; +} +static UChar32 upArrowKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | UP_ARROW_KEY; +} +static UChar32 downArrowKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | DOWN_ARROW_KEY; +} +static UChar32 rightArrowKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | RIGHT_ARROW_KEY; +} +static UChar32 leftArrowKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | LEFT_ARROW_KEY; +} +static UChar32 homeKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | HOME_KEY; +} +static UChar32 endKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | END_KEY; +} +static UChar32 pageUpKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | PAGE_UP_KEY; +} +static UChar32 pageDownKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | PAGE_DOWN_KEY; +} +static UChar32 deleteCharRoutine(UChar32 c) { + return thisKeyMetaCtrl | ctrlChar('H'); +} // key labeled Backspace +static UChar32 deleteKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | DELETE_KEY; +} // key labeled Delete +static UChar32 ctrlUpArrowKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | CTRL | UP_ARROW_KEY; +} +static UChar32 ctrlDownArrowKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | CTRL | DOWN_ARROW_KEY; +} +static UChar32 ctrlRightArrowKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | CTRL | RIGHT_ARROW_KEY; +} +static UChar32 ctrlLeftArrowKeyRoutine(UChar32 c) { + return thisKeyMetaCtrl | CTRL | LEFT_ARROW_KEY; +} +static UChar32 escFailureRoutine(UChar32 c) { + beep(); + return -1; +} // Handle ESC [ 1 ; 3 (or 5) <more stuff> escape sequences // -static CharacterDispatchRoutine escLeftBracket1Semicolon3or5Routines[] = { - upArrowKeyRoutine, - downArrowKeyRoutine, - rightArrowKeyRoutine, - leftArrowKeyRoutine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket1Semicolon3or5Dispatch = { 4, "ABCD", escLeftBracket1Semicolon3or5Routines }; +static CharacterDispatchRoutine escLeftBracket1Semicolon3or5Routines[] = {upArrowKeyRoutine, + downArrowKeyRoutine, + rightArrowKeyRoutine, + leftArrowKeyRoutine, + escFailureRoutine}; +static CharacterDispatch escLeftBracket1Semicolon3or5Dispatch = { + 4, "ABCD", escLeftBracket1Semicolon3or5Routines}; // Handle ESC [ 1 ; <more stuff> escape sequences // -static UChar32 escLeftBracket1Semicolon3Routine( UChar32 c ) { +static UChar32 escLeftBracket1Semicolon3Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; + if (c == 0) + return 0; thisKeyMetaCtrl |= META; - return doDispatch( c, escLeftBracket1Semicolon3or5Dispatch ); + return doDispatch(c, escLeftBracket1Semicolon3or5Dispatch); } -static UChar32 escLeftBracket1Semicolon5Routine( UChar32 c ) { +static UChar32 escLeftBracket1Semicolon5Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; + if (c == 0) + return 0; thisKeyMetaCtrl |= CTRL; - return doDispatch( c, escLeftBracket1Semicolon3or5Dispatch ); + return doDispatch(c, escLeftBracket1Semicolon3or5Dispatch); } static CharacterDispatchRoutine escLeftBracket1SemicolonRoutines[] = { - escLeftBracket1Semicolon3Routine, - escLeftBracket1Semicolon5Routine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket1SemicolonDispatch = { 2, "35", escLeftBracket1SemicolonRoutines }; + escLeftBracket1Semicolon3Routine, escLeftBracket1Semicolon5Routine, escFailureRoutine}; +static CharacterDispatch escLeftBracket1SemicolonDispatch = { + 2, "35", escLeftBracket1SemicolonRoutines}; // Handle ESC [ 1 <more stuff> escape sequences // -static UChar32 escLeftBracket1SemicolonRoutine( UChar32 c ) { +static UChar32 escLeftBracket1SemicolonRoutine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracket1SemicolonDispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracket1SemicolonDispatch); } static CharacterDispatchRoutine escLeftBracket1Routines[] = { - homeKeyRoutine, - escLeftBracket1SemicolonRoutine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket1Dispatch = { 2, "~;", escLeftBracket1Routines }; + homeKeyRoutine, escLeftBracket1SemicolonRoutine, escFailureRoutine}; +static CharacterDispatch escLeftBracket1Dispatch = {2, "~;", escLeftBracket1Routines}; // Handle ESC [ 3 <more stuff> escape sequences // -static CharacterDispatchRoutine escLeftBracket3Routines[] = { - deleteKeyRoutine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket3Dispatch = { 1, "~", escLeftBracket3Routines }; +static CharacterDispatchRoutine escLeftBracket3Routines[] = {deleteKeyRoutine, escFailureRoutine}; +static CharacterDispatch escLeftBracket3Dispatch = {1, "~", escLeftBracket3Routines}; // Handle ESC [ 4 <more stuff> escape sequences // -static CharacterDispatchRoutine escLeftBracket4Routines[] = { - endKeyRoutine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket4Dispatch = { 1, "~", escLeftBracket4Routines }; +static CharacterDispatchRoutine escLeftBracket4Routines[] = {endKeyRoutine, escFailureRoutine}; +static CharacterDispatch escLeftBracket4Dispatch = {1, "~", escLeftBracket4Routines}; // Handle ESC [ 5 <more stuff> escape sequences // -static CharacterDispatchRoutine escLeftBracket5Routines[] = { - pageUpKeyRoutine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket5Dispatch = { 1, "~", escLeftBracket5Routines }; +static CharacterDispatchRoutine escLeftBracket5Routines[] = {pageUpKeyRoutine, escFailureRoutine}; +static CharacterDispatch escLeftBracket5Dispatch = {1, "~", escLeftBracket5Routines}; // Handle ESC [ 6 <more stuff> escape sequences // -static CharacterDispatchRoutine escLeftBracket6Routines[] = { - pageDownKeyRoutine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket6Dispatch = { 1, "~", escLeftBracket6Routines }; +static CharacterDispatchRoutine escLeftBracket6Routines[] = {pageDownKeyRoutine, escFailureRoutine}; +static CharacterDispatch escLeftBracket6Dispatch = {1, "~", escLeftBracket6Routines}; // Handle ESC [ 7 <more stuff> escape sequences // -static CharacterDispatchRoutine escLeftBracket7Routines[] = { - homeKeyRoutine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket7Dispatch = { 1, "~", escLeftBracket7Routines }; +static CharacterDispatchRoutine escLeftBracket7Routines[] = {homeKeyRoutine, escFailureRoutine}; +static CharacterDispatch escLeftBracket7Dispatch = {1, "~", escLeftBracket7Routines}; // Handle ESC [ 8 <more stuff> escape sequences // -static CharacterDispatchRoutine escLeftBracket8Routines[] = { - endKeyRoutine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracket8Dispatch = { 1, "~", escLeftBracket8Routines }; +static CharacterDispatchRoutine escLeftBracket8Routines[] = {endKeyRoutine, escFailureRoutine}; +static CharacterDispatch escLeftBracket8Dispatch = {1, "~", escLeftBracket8Routines}; // Handle ESC [ <digit> escape sequences // -static UChar32 escLeftBracket0Routine( UChar32 c ) { - return escFailureRoutine( c ); +static UChar32 escLeftBracket0Routine(UChar32 c) { + return escFailureRoutine(c); } -static UChar32 escLeftBracket1Routine( UChar32 c ) { +static UChar32 escLeftBracket1Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracket1Dispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracket1Dispatch); } -static UChar32 escLeftBracket2Routine( UChar32 c ) { - return escFailureRoutine( c ); // Insert key, unused +static UChar32 escLeftBracket2Routine(UChar32 c) { + return escFailureRoutine(c); // Insert key, unused } -static UChar32 escLeftBracket3Routine( UChar32 c ) { +static UChar32 escLeftBracket3Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracket3Dispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracket3Dispatch); } -static UChar32 escLeftBracket4Routine( UChar32 c ) { +static UChar32 escLeftBracket4Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracket4Dispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracket4Dispatch); } -static UChar32 escLeftBracket5Routine( UChar32 c ) { +static UChar32 escLeftBracket5Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracket5Dispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracket5Dispatch); } -static UChar32 escLeftBracket6Routine( UChar32 c ) { +static UChar32 escLeftBracket6Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracket6Dispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracket6Dispatch); } -static UChar32 escLeftBracket7Routine( UChar32 c ) { +static UChar32 escLeftBracket7Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracket7Dispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracket7Dispatch); } -static UChar32 escLeftBracket8Routine( UChar32 c ) { +static UChar32 escLeftBracket8Routine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracket8Dispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracket8Dispatch); } -static UChar32 escLeftBracket9Routine( UChar32 c ) { - return escFailureRoutine( c ); +static UChar32 escLeftBracket9Routine(UChar32 c) { + return escFailureRoutine(c); } // Handle ESC [ <more stuff> escape sequences // -static CharacterDispatchRoutine escLeftBracketRoutines[] = { - upArrowKeyRoutine, - downArrowKeyRoutine, - rightArrowKeyRoutine, - leftArrowKeyRoutine, - homeKeyRoutine, - endKeyRoutine, - escLeftBracket0Routine, - escLeftBracket1Routine, - escLeftBracket2Routine, - escLeftBracket3Routine, - escLeftBracket4Routine, - escLeftBracket5Routine, - escLeftBracket6Routine, - escLeftBracket7Routine, - escLeftBracket8Routine, - escLeftBracket9Routine, - escFailureRoutine -}; -static CharacterDispatch escLeftBracketDispatch = { 16, "ABCDHF0123456789", escLeftBracketRoutines }; +static CharacterDispatchRoutine escLeftBracketRoutines[] = {upArrowKeyRoutine, + downArrowKeyRoutine, + rightArrowKeyRoutine, + leftArrowKeyRoutine, + homeKeyRoutine, + endKeyRoutine, + escLeftBracket0Routine, + escLeftBracket1Routine, + escLeftBracket2Routine, + escLeftBracket3Routine, + escLeftBracket4Routine, + escLeftBracket5Routine, + escLeftBracket6Routine, + escLeftBracket7Routine, + escLeftBracket8Routine, + escLeftBracket9Routine, + escFailureRoutine}; +static CharacterDispatch escLeftBracketDispatch = {16, "ABCDHF0123456789", escLeftBracketRoutines}; // Handle ESC O <char> escape sequences // -static CharacterDispatchRoutine escORoutines[] = { - upArrowKeyRoutine, - downArrowKeyRoutine, - rightArrowKeyRoutine, - leftArrowKeyRoutine, - homeKeyRoutine, - endKeyRoutine, - ctrlUpArrowKeyRoutine, - ctrlDownArrowKeyRoutine, - ctrlRightArrowKeyRoutine, - ctrlLeftArrowKeyRoutine, - escFailureRoutine -}; -static CharacterDispatch escODispatch = { 10, "ABCDHFabcd", escORoutines }; +static CharacterDispatchRoutine escORoutines[] = {upArrowKeyRoutine, + downArrowKeyRoutine, + rightArrowKeyRoutine, + leftArrowKeyRoutine, + homeKeyRoutine, + endKeyRoutine, + ctrlUpArrowKeyRoutine, + ctrlDownArrowKeyRoutine, + ctrlRightArrowKeyRoutine, + ctrlLeftArrowKeyRoutine, + escFailureRoutine}; +static CharacterDispatch escODispatch = {10, "ABCDHFabcd", escORoutines}; // Initial ESC dispatch -- could be a Meta prefix or the start of an escape sequence // -static UChar32 escLeftBracketRoutine( UChar32 c ) { +static UChar32 escLeftBracketRoutine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escLeftBracketDispatch ); + if (c == 0) + return 0; + return doDispatch(c, escLeftBracketDispatch); } -static UChar32 escORoutine( UChar32 c ) { +static UChar32 escORoutine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escODispatch ); + if (c == 0) + return 0; + return doDispatch(c, escODispatch); } -static UChar32 setMetaRoutine( UChar32 c ); // need forward reference +static UChar32 setMetaRoutine(UChar32 c); // need forward reference static CharacterDispatchRoutine escRoutines[] = { - escLeftBracketRoutine, - escORoutine, - setMetaRoutine -}; -static CharacterDispatch escDispatch = { 2, "[O", escRoutines }; + escLeftBracketRoutine, escORoutine, setMetaRoutine}; +static CharacterDispatch escDispatch = {2, "[O", escRoutines}; // Initial dispatch -- we are not in the middle of anything yet // -static UChar32 escRoutine( UChar32 c ) { +static UChar32 escRoutine(UChar32 c) { c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escDispatch ); + if (c == 0) + return 0; + return doDispatch(c, escDispatch); } static CharacterDispatchRoutine initialRoutines[] = { - escRoutine, - deleteCharRoutine, - normalKeyRoutine -}; -static CharacterDispatch initialDispatch = { 2, "\x1B\x7F", initialRoutines }; + escRoutine, deleteCharRoutine, normalKeyRoutine}; +static CharacterDispatch initialDispatch = {2, "\x1B\x7F", initialRoutines}; // Special handling for the ESC key because it does double duty // -static UChar32 setMetaRoutine( UChar32 c ) { +static UChar32 setMetaRoutine(UChar32 c) { thisKeyMetaCtrl = META; - if ( c == 0x1B ) { // another ESC, stay in ESC processing mode + if (c == 0x1B) { // another ESC, stay in ESC processing mode c = readUnicodeCharacter(); - if ( c == 0 ) return 0; - return doDispatch( c, escDispatch ); + if (c == 0) + return 0; + return doDispatch(c, escDispatch); } - return doDispatch( c, initialDispatch ); + return doDispatch(c, initialDispatch); } -} // namespace EscapeSequenceProcessing // move these out of global namespace +} // namespace EscapeSequenceProcessing // move these out of global namespace -#endif // #ifndef _WIN32 +#endif // #ifndef _WIN32 // linenoiseReadChar -- read a keystroke or keychord from the keyboard, and translate it // into an encoded "keystroke". When convenient, extended keys are translated into their @@ -1191,16 +1242,16 @@ static UChar32 setMetaRoutine( UChar32 c ) { // // A return value of zero means "no input available", and a return value of -1 means "invalid key". // -static UChar32 linenoiseReadChar( void ) { +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 + 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 ) { @@ -1223,49 +1274,59 @@ static UChar32 linenoiseReadChar( void ) { } } #endif - if ( rec.EventType != KEY_EVENT ) { + 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 ) { + 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 & (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 ) ) { + if (rec.Event.KeyEvent.dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) { modifierKeys |= CTRL; } - if ( rec.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED ) ) { + if (rec.Event.KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) { modifierKeys |= META; } - if ( escSeen ) { + 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 + 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 { + } else { // we got a real character, return it return modifierKeys | rec.Event.KeyEvent.uChar.UnicodeChar; } @@ -1274,71 +1335,67 @@ static UChar32 linenoiseReadChar( void ) { #else UChar32 c; c = readUnicodeCharacter(); - if ( c == 0 ) return 0; + 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. - // +// 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 ) { + 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 ); + int ret = read(0, keys, 10); - if ( ret <= 0 ) { - printf( "\nret: %d\n", ret ); + if (ret <= 0) { + printf("\nret: %d\n", ret); } - for ( int i = 0; i < ret; ++i ) { - UChar32 key = static_cast<UChar32>( keys[i] ); + 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 + 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 { + } 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("%d x%02X (%s%s) ", key, key, prefixText, friendlyTextPtr); } - printf( "\x1b[1G\n" ); // go to first column of new line + 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 ); + 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 + EscapeSequenceProcessing::thisKeyMetaCtrl = 0; // no modifiers yet at initialDispatch + return EscapeSequenceProcessing::doDispatch(c, EscapeSequenceProcessing::initialDispatch); +#endif // #_WIN32 } /** @@ -1346,7 +1403,7 @@ static UChar32 linenoiseReadChar( void ) { * * @param lc pointer to a linenoiseCompletions struct */ -static void freeCompletions( linenoiseCompletions* lc ) { +static void freeCompletions(linenoiseCompletions* lc) { lc->completionStrings.clear(); } @@ -1357,16 +1414,16 @@ static void freeCompletions( linenoiseCompletions* lc ) { * @param c character to clean up * @return cleaned-up character */ -static int cleanupCtrl( int c ) { - if ( c & CTRL ) { +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 >= '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' ) ) { + if (d >= ctrlChar('A') && d <= ctrlChar('Z')) { c = c & ~CTRL; } } @@ -1385,254 +1442,266 @@ static const size_t completionCountCutoff = 100; * text as the user selects a proposed completion string, or cancels the completion attempt. * @param pi PromptBase struct holding information about the prompt and our screen position */ -int InputBuffer::completeLine( PromptBase& pi ) { +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] ) ) { + while (--startIndex >= 0) { + if (strchr(breakChars, buf32[startIndex])) { break; } } ++startIndex; int itemLength = pos - startIndex; - Utf32String unicodeCopy( &buf32[startIndex], itemLength ); - Utf8String parseItem( unicodeCopy ); + Utf32String unicodeCopy(&buf32[startIndex], itemLength); + Utf8String parseItem(unicodeCopy); // get a list of completions - completionCallback( reinterpret_cast< char * >( parseItem.get() ), &lc ); + completionCallback(reinterpret_cast<char*>(parseItem.get()), &lc); // if no completions, we are done - if ( lc.completionStrings.size() == 0 ) { + if (lc.completionStrings.size() == 0) { beep(); - freeCompletions( &lc ); + freeCompletions(&lc); return 0; } // at least one completion int longestCommonPrefix = 0; int displayLength = 0; - if ( lc.completionStrings.size() == 1 ) { + if (lc.completionStrings.size() == 1) { longestCommonPrefix = lc.completionStrings[0].length(); - } - else { + } else { bool keepGoing = true; - while ( keepGoing ) { - for ( size_t j = 0; j < lc.completionStrings.size() - 1; ++j ) { + 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 ) ) { + if ((0 == c1) || (0 == c2) || (c1 != c2)) { keepGoing = false; break; } } - if ( keepGoing ) { + if (keepGoing) { ++longestCommonPrefix; } } } - if ( lc.completionStrings.size() != 1 ) { // beep if ambiguous + 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 ) { + if (longestCommonPrefix > itemLength) { displayLength = len + longestCommonPrefix - itemLength; - if ( displayLength > buflen ) { + if (displayLength > buflen) { longestCommonPrefix -= displayLength - buflen; // don't overflow buffer - displayLength = buflen; // truncate the insertion - beep(); // and make a noise + 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 ); + 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 ); + memcpy(&displayText[tailIndex], + &buf32[pos], + sizeof(UChar32) * (displayLength - tailIndex + 1)); + copyString32(buf32, displayText.get(), buflen + 1); pos = startIndex + longestCommonPrefix; len = displayLength; - refreshLine( pi ); + 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 ) ); + 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 ); + 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 ) { + if (lc.completionStrings.size() > completionCountCutoff) { int savePos = pos; // move cursor to EOL to avoid overwriting the command line pos = len; - refreshLine( pi ); + refreshLine(pi); pos = savePos; - printf( "\nDisplay all %u possibilities? (y or n)", - static_cast<unsigned int>( lc.completionStrings.size() ) ); - fflush( stdout ); + 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' ) ) { + while (c != 'y' && c != 'Y' && c != 'n' && c != 'N' && c != ctrlChar('C')) { do { c = linenoiseReadChar(); - c = cleanupCtrl( c ); - } while ( c == static_cast<char>( -1 ) ); + 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; + 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 ) { + if (showCompletions) { int longestCompletion = 0; - for ( size_t j = 0; j < lc.completionStrings.size(); ++j) { + for (size_t j = 0; j < lc.completionStrings.size(); ++j) { itemLength = lc.completionStrings[j].length(); - if ( itemLength > longestCompletion ) { + if (itemLength > longestCompletion) { longestCompletion = itemLength; } } longestCompletion += 2; int columnCount = pi.promptScreenColumns / longestCompletion; - if ( columnCount < 1) { + if (columnCount < 1) { columnCount = 1; } - if ( ! onNewLine ) { // skip this if we showed "Display all %d possibilities?" + 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 ); + 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 ); + 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 ) { + 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 ) ); + 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; + 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"); } - else { - printf( "\n" ); - } - if ( stopList ) { + if (stopList) { break; } - for ( int column = 0; column < columnCount; ++column ) { - size_t index = ( column * rowCount ) + row; - if ( index < lc.completionStrings.size() ) { + 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); + 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 ); + 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 (!stopList || c == ctrlChar('C')) { + if (write(1, "\n", 1) == -1) + return 0; } - if ( write32( 1, pi.promptText.get(), pi.promptChars ) == -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; + if (pi.promptIndentation == 0 && pi.promptExtraLines > 0) + if (write(1, "\n", 1) == -1) + return 0; #endif pi.promptCursorRowOffset = pi.promptExtraLines; - refreshLine( pi ); + refreshLine(pi); return 0; } /** * Clear the screen ONLY (no redisplay of anything) */ -void linenoiseClearScreen( void ) { +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 ); + HANDLE screenHandle = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(screenHandle, &inf); + SetConsoleCursorPosition(screenHandle, coord); DWORD count; - FillConsoleOutputCharacterA( screenHandle, ' ', inf.dwSize.X * inf.dwSize.Y, coord, &count ); + FillConsoleOutputCharacterA(screenHandle, ' ', inf.dwSize.X * inf.dwSize.Y, coord, &count); #else - if ( write( 1, "\x1b[H\x1b[2J", 7 ) <= 0 ) return; + if (write(1, "\x1b[H\x1b[2J", 7) <= 0) + return; #endif } -void InputBuffer::clearScreen( PromptBase& pi ) { +void InputBuffer::clearScreen(PromptBase& pi) { linenoiseClearScreen(); - if ( write32( 1, pi.promptText.get(), pi.promptChars ) == -1 ) return; + 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; + if (pi.promptIndentation == 0 && pi.promptExtraLines > 0) + if (write(1, "\n", 1) == -1) + return; #endif pi.promptCursorRowOffset = pi.promptExtraLines; - refreshLine( pi ); + refreshLine(pi); } /** @@ -1642,30 +1711,32 @@ void InputBuffer::clearScreen( PromptBase& pi ) { * @param pi PromptBase struct holding information about the (old, static) prompt and our screen position * @param startChar the character that began the search, used to set the initial direction */ -int InputBuffer::incrementalHistorySearch( PromptBase& pi, int startChar ) { +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() ) ) ); + 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 ); + 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 + dynamicRefresh( + dp, buf32, historyLineLength, historyLinePosition); // draw user's text with our prompt // loop until we get an exit character int c; @@ -1673,187 +1744,199 @@ int InputBuffer::incrementalHistorySearch( PromptBase& pi, int startChar ) { bool useSearchedLine = true; bool searchAgain = false; UChar32* activeHistoryLine = 0; - while ( keepLooping ) { + 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; + 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 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() ); + // 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; + 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 +// 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; + 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; + // 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; + 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 + 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 ) { + if (keepLooping) { bufferSize = historyLineLength + 1; - activeHistoryLine = new UChar32[ bufferSize ]; - copyString8to32( activeHistoryLine, history[ historyIndex ], bufferSize, ucharCount, errorCode ); - if ( dp.searchTextLen > 0 ) { + 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 ) { + 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 ) { + 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 ) { + if (found) { historyIndex = historySearchIndex; historyLineLength = lineLength; historyLinePosition = lineSearchPos; break; - } - else if ( ( dp.direction > 0 ) ? ( historySearchIndex < historyLen - 1 ) : ( historySearchIndex > 0 ) ) { + } 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 ); + 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 { + lineSearchPos = (dp.direction > 0) ? 0 : (lineLength - dp.searchTextLen); + } else { beep(); break; } - }; // while + }; // while } - if ( activeHistoryLine ) { - delete [] activeHistoryLine; + 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 + activeHistoryLine = new UChar32[bufferSize]; + copyString8to32( + activeHistoryLine, history[historyIndex], bufferSize, ucharCount, errorCode); + dynamicRefresh(dp, + activeHistoryLine, + historyLineLength, + historyLinePosition); // draw user's text with our prompt } - } // while + } // 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 ); + Utf32String tempUnicode(pb.promptChars + 1); + copyString32(tempUnicode.get(), &pi.promptText[pi.promptLastLinePosition], pb.promptChars + 1); tempUnicode.initFromBuffer(); pb.promptText = tempUnicode; pb.promptExtraLines = 0; @@ -1863,48 +1946,48 @@ int InputBuffer::incrementalHistorySearch( PromptBase& pi, int startChar ) { pb.promptCursorRowOffset = dp.promptCursorRowOffset; pb.promptScreenColumns = pi.promptScreenColumns; pb.promptPreviousLen = dp.promptChars; - if ( useSearchedLine && activeHistoryLine ) { + if (useSearchedLine && activeHistoryLine) { historyRecallMostRecent = true; - copyString32( buf32, activeHistoryLine, buflen + 1 ); + copyString32(buf32, activeHistoryLine, buflen + 1); len = historyLineLength; pos = historyLinePosition; } - if ( activeHistoryLine ) { - delete [] activeHistoryLine; + if (activeHistoryLine) { + delete[] activeHistoryLine; } - dynamicRefresh( pb, buf32, len, pos ); // redraw the original prompt with current input + 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 + 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 } -static bool isCharacterAlphanumeric( UChar32 testChar ) { - return iswalnum( testChar ); +static bool isCharacterAlphanumeric(UChar32 testChar) { + return iswalnum(testChar); } -int InputBuffer::getInputLine( PromptBase& pi ) { - +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( "" ); + 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; + 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; + 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 @@ -1917,538 +2000,549 @@ int InputBuffer::getInputLine( PromptBase& pi ) { int terminatingKeystroke = -1; // if there is already text in the buffer, display it first - if ( len > 0 ) { - refreshLine( pi ); + if (len > 0) { + refreshLine(pi); } // loop collecting characters, respond to line editing characters - while ( true ) { + while (true) { int c; - if ( terminatingKeystroke == -1 ) { - c = linenoiseReadChar(); // get a new keystroke - } - else { + 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 + c = cleanupCtrl(c); // convert CTRL + <char> into normal ctrl - if ( c == 0 ) { + if (c == 0) { return len; } - if ( c == -1 ) { - refreshLine( pi ); + if (c == -1) { + refreshLine(pi); continue; } - if ( c == -2 ) { - if ( write32( 1, pi.promptText.get(), pi.promptChars ) == -1 ) return -1; - refreshLine( pi ); + 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. + 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 ); + c = completeLine(pi); - if ( c < 0 ) // return on error + if (c < 0) // return on error return len; - if ( c == 0 ) // read next character when 0 + 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; + 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 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] ) ) { + 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); } - 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; + break; - 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'; + 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; } - ++pos; - } - while ( pos < len && isCharacterAlphanumeric( buf32[pos] ) ) { - if ( buf32[pos] >= 'A' && buf32[pos] <= 'Z' ) { - buf32[pos] += 'a' - 'A'; + while (pos > 0 && isCharacterAlphanumeric(buf32[pos - 1])) { + --pos; } - ++pos; + refreshLine(pi); } - refreshLine( pi ); - } - break; + 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 ) { + case ctrlChar('C'): // ctrl-C, abort this line + killRing.lastAction = KillRing::actionOther; historyRecallMostRecent = false; - memmove( buf32 + pos, buf32 + pos + 1, sizeof( UChar32 ) * ( len - pos ) ); - --len; - refreshLine( pi ); - } - else if ( len == 0 ) { + errno = EAGAIN; --historyLen; - free( history[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; - } - break; - case META + 'd': // meta-D, kill word to right of cursor - case META + 'D': - if ( pos < len ) { + case META + 'c': // meta-C, give word initial Cap + case META + 'C': + killRing.lastAction = KillRing::actionOther; historyRecallMostRecent = false; - int endingPos = pos; - while ( endingPos < len && !isCharacterAlphanumeric( buf32[endingPos] ) ) { - ++endingPos; + 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); } - while ( endingPos < len && isCharacterAlphanumeric( buf32[endingPos] ) ) { - ++endingPos; + 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; } - 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; + 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 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( '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 ctrlChar('E'): // ctrl-E, move cursor to end of line + case END_KEY: + killRing.lastAction = KillRing::actionOther; + pos = len; + 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] ) ) { + 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); } - while ( pos < len && isCharacterAlphanumeric( buf32[pos] ) ) { - ++pos; - } - refreshLine( pi ); - } - break; + 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; + 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; - // 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] ) ) { + 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); } - while ( pos > 0 && isCharacterAlphanumeric( buf32[pos - 1] ) ) { - --pos; + 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.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; + 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('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('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 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'; + 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; } - ++pos; + while (pos < len && isCharacterAlphanumeric(buf32[pos])) { + if (buf32[pos] >= 'A' && buf32[pos] <= 'Z') { + buf32[pos] += 'a' - 'A'; + } + ++pos; + } + refreshLine(pi); } - refreshLine( pi ); - } - break; + 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; + 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()))); } - else if ( historyIndex >= historyLen ) { - historyIndex = historyLen - 1; - break; + 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); } - 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; + 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 ctrlChar('R'): // ctrl-R, reverse history search + case ctrlChar('S'): // ctrl-S, forward history search + terminatingKeystroke = incrementalHistorySearch(pi, c); + 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; + 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); } - refreshLine( pi ); - } - break; + 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; + 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.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; + 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; + 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; } - 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(); + while (pos < len && isCharacterAlphanumeric(buf32[pos])) { + if (buf32[pos] >= 'a' && buf32[pos] <= 'z') { + buf32[pos] += 'A' - 'a'; + } + ++pos; } + refreshLine(pi); } - else { - beep(); - } - } - break; + 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 ); + // 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; } - else { - memmove( buf32 + pos - killRing.lastYankSize, restoredText->get(), sizeof( UChar32 ) * ucharCount ); - memmove( buf32 + pos + ucharCount - killRing.lastYankSize, buf32 + pos, sizeof( UChar32 ) * ( len - pos + 1 ) ); + while (pos > 0 && buf32[pos - 1] != ' ') { + --pos; } - pos += ucharCount - killRing.lastYankSize; - len += ucharCount - killRing.lastYankSize; - killRing.lastYankSize = ucharCount; - refreshLine( pi ); - if (truncated) { + 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; } - } - beep(); - break; + 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; + 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; + // 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; - // 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(); + 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; - } - if ( len < buflen ) { - if ( isControlChar( c ) ) { // don't insert control characters + + // 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 == 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; + if (len < buflen) { + if (isControlChar(c)) { // don't insert control characters + beep(); + break; } - else { - refreshLine( pi ); + 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 } - 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; + break; } } return len; } -string preloadedBufferContents; // used with linenoisePreloadBuffer +string preloadedBufferContents; // used with linenoisePreloadBuffer string preloadErrorMessage; /** @@ -2459,35 +2553,34 @@ string preloadErrorMessage; * * @param preloadText text to begin with on the next call to linenoise() */ -void linenoisePreloadBuffer( const char* preloadText ) { - - if ( ! preloadText ) { +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 ); + 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 + 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 + if ('\n' == c || '\t' == c) { // note newline or tab whitespaceSeen = true; continue; } - if ( isControlChar( c ) ) { // remove other control characters, flag for message + if (isControlChar(c)) { // remove other control characters, flag for message controlsStripped = true; *pOut++ = ' '; continue; } - if ( whitespaceSeen ) { // convert whitespace to a single space + if (whitespaceSeen) { // convert whitespace to a single space *pOut++ = ' '; whitespaceSeen = false; } @@ -2496,18 +2589,18 @@ void linenoisePreloadBuffer( const char* preloadText ) { *pOut = 0; int processedLength = pOut - tempBuffer.get(); bool lineTruncated = false; - if ( processedLength > ( LINENOISE_MAX_LINE - 1 ) ) { + if (processedLength > (LINENOISE_MAX_LINE - 1)) { lineTruncated = true; - tempBuffer[ LINENOISE_MAX_LINE - 1 ] = 0; + tempBuffer[LINENOISE_MAX_LINE - 1] = 0; } preloadedBufferContents = tempBuffer.get(); - if ( controlsStripped ) { + if (controlsStripped) { preloadErrorMessage += " [Edited line: control characters were converted to spaces]\n"; } - if ( lineTruncated ) { + 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 ) ); + snprintf(buf, sizeof(buf), "%d to %d]\n", processedLength, (LINENOISE_MAX_LINE - 1)); preloadErrorMessage += buf; } } @@ -2520,111 +2613,109 @@ void linenoisePreloadBuffer( const char* preloadText ) { * @param prompt text of prompt to display to the user * @return the returned string belongs to the caller on return and must be freed to prevent memory leaks */ -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 ); +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 ) { + 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' ) ) { + 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() ); + return strdup(buf8.get()); // caller must free buffer + } else { + char* buf8 = strdup(preloadedBufferContents.c_str()); preloadedBufferContents.clear(); - return buf8; // caller must free buffer + return buf8; // caller must free buffer } - } - else { - if ( enableRawMode() == -1 ) { + } 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() ) ); + 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 ); + int count = ib.getInputLine(pi); disableRawMode(); - printf( "\n" ); - if ( count == -1 ) { + 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 + 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 ) { + } 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' ) { + int count = strlen(buf8.get()); + if (count > 0 && buf8[count - 1] == '\n') { --count; - buf8[ count ] = '\0'; + buf8[count] = '\0'; } - return strdup( buf8.get() ); // caller must free buffer + return strdup(buf8.get()); // caller must free buffer } } /* Register a callback function to be called for tab-completion. */ -void linenoiseSetCompletionCallback( linenoiseCompletionCallback* fn ) { +void linenoiseSetCompletionCallback(linenoiseCompletionCallback* fn) { completionCallback = fn; } -void linenoiseAddCompletion( linenoiseCompletions* lc, const char* str ) { - lc->completionStrings.push_back( Utf32String( reinterpret_cast<const UChar8*>( str ) ) ); +void linenoiseAddCompletion(linenoiseCompletions* lc, const char* str) { + lc->completionStrings.push_back(Utf32String(reinterpret_cast<const UChar8*>(str))); } -int linenoiseHistoryAdd( const char* line ) { - if ( historyMaxLen == 0 ) { +int linenoiseHistoryAdd(const char* line) { + if (historyMaxLen == 0) { return 0; } - if ( history == NULL ) { - history = reinterpret_cast< UChar8** >( malloc( sizeof( UChar8* ) * historyMaxLen ) ); + if (history == NULL) { + history = reinterpret_cast<UChar8**>(malloc(sizeof(UChar8*) * historyMaxLen)); if (history == NULL) { return 0; } - memset( history, 0, ( sizeof( char* ) * historyMaxLen ) ); + memset(history, 0, (sizeof(char*) * historyMaxLen)); } - UChar8* linecopy = reinterpret_cast< UChar8* >( strdup( line ) ); - if ( ! linecopy ) { + 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 ) ); + if (historyLen == historyMaxLen) { + free(history[0]); + memmove(history, history + 1, sizeof(char*) * (historyMaxLen - 1)); --historyLen; - if ( --historyPreviousIndex < -1 ) { + if (--historyPreviousIndex < -1) { historyPreviousIndex = -2; } } // convert newlines in multi-line code to spaces before storing UChar8* p = linecopy; - while ( *p ) { - if ( *p == '\n' ) { + while (*p) { + if (*p == '\n') { *p = ' '; } ++p; @@ -2634,25 +2725,25 @@ int linenoiseHistoryAdd( const char* line ) { return 1; } -int linenoiseHistorySetMaxLen( int len ) { - if ( len < 1 ) { +int linenoiseHistorySetMaxLen(int len) { + if (len < 1) { return 0; } - if ( history ) { + if (history) { int tocopy = historyLen; - UChar8** newHistory = reinterpret_cast< UChar8** >( malloc( sizeof( UChar8* ) * len ) ); - if ( newHistory == NULL ) { + UChar8** newHistory = reinterpret_cast<UChar8**>(malloc(sizeof(UChar8*) * len)); + if (newHistory == NULL) { return 0; } - if ( len < tocopy ) { + if (len < tocopy) { tocopy = len; } - memcpy( newHistory, history + historyMaxLen - tocopy, sizeof( UChar8* ) * tocopy ); - free( history ); + memcpy(newHistory, history + historyMaxLen - tocopy, sizeof(UChar8*) * tocopy); + free(history); history = newHistory; } historyMaxLen = len; - if ( historyLen > historyMaxLen ) { + if (historyLen > historyMaxLen) { historyLen = historyMaxLen; } return 1; @@ -2660,18 +2751,18 @@ int linenoiseHistorySetMaxLen( int len ) { /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ -int linenoiseHistorySave( const char* filename ) { - FILE* fp = fopen( filename, "wt" ); - if ( fp == NULL ) { +int linenoiseHistorySave(const char* filename) { + FILE* fp = fopen(filename, "wt"); + if (fp == NULL) { return -1; } - for ( int j = 0; j < historyLen; ++j ) { - if ( history[j][0] != '\0' ) { - fprintf ( fp, "%s\n", history[j] ); + for (int j = 0; j < historyLen; ++j) { + if (history[j][0] != '\0') { + fprintf(fp, "%s\n", history[j]); } } - fclose( fp ); + fclose(fp); return 0; } @@ -2680,25 +2771,25 @@ int linenoiseHistorySave( const char* filename ) { * * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ -int linenoiseHistoryLoad( const char* filename ) { - FILE *fp = fopen( filename, "rt" ); - if ( fp == NULL ) { +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' ); + while (fgets(buf, LINENOISE_MAX_LINE, fp) != NULL) { + char* p = strchr(buf, '\r'); + if (!p) { + p = strchr(buf, '\n'); } - if ( p ) { + if (p) { *p = '\0'; } - if ( p != buf ) { - linenoiseHistoryAdd( buf ); + if (p != buf) { + linenoiseHistoryAdd(buf); } } - fclose( fp ); + fclose(fp); return 0; } diff --git a/src/mongo/shell/linenoise.h b/src/mongo/shell/linenoise.h index 06edcbdfb88..1bdc2a76de8 100644 --- a/src/mongo/shell/linenoise.h +++ b/src/mongo/shell/linenoise.h @@ -38,17 +38,17 @@ struct linenoiseCompletions; -typedef void( linenoiseCompletionCallback )( const char *, linenoiseCompletions * ); -void linenoiseSetCompletionCallback( linenoiseCompletionCallback * fn ); -void linenoiseAddCompletion( linenoiseCompletions * lc, const char * str ); +typedef void(linenoiseCompletionCallback)(const char*, linenoiseCompletions*); +void linenoiseSetCompletionCallback(linenoiseCompletionCallback* fn); +void linenoiseAddCompletion(linenoiseCompletions* lc, const char* str); -char *linenoise( const char* prompt ); -void linenoisePreloadBuffer( const char* preloadText ); -int linenoiseHistoryAdd( const char* line ); -int linenoiseHistorySetMaxLen( int len ); -int linenoiseHistorySave( const char* filename ); -int linenoiseHistoryLoad( const char* filename ); -void linenoiseHistoryFree( void ); -void linenoiseClearScreen( void ); +char* linenoise(const char* prompt); +void linenoisePreloadBuffer(const char* preloadText); +int linenoiseHistoryAdd(const char* line); +int linenoiseHistorySetMaxLen(int len); +int linenoiseHistorySave(const char* filename); +int linenoiseHistoryLoad(const char* filename); +void linenoiseHistoryFree(void); +void linenoiseClearScreen(void); #endif /* __LINENOISE_H */ diff --git a/src/mongo/shell/linenoise_utf8.cpp b/src/mongo/shell/linenoise_utf8.cpp index 986e87259f6..1a01aec6696 100644 --- a/src/mongo/shell/linenoise_utf8.cpp +++ b/src/mongo/shell/linenoise_utf8.cpp @@ -45,21 +45,20 @@ namespace linenoise_utf8 { * 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 ) { +void copyString8to32(UChar32* uchar32output, + const UChar8* utf8input, + size_t outputBufferSizeInCharacters, + size_t& outputUnicodeCharacterCount, + int& conversionErrorCode) { conversionErrorCode = BadUTF8_no_error; - if ( outputBufferSizeInCharacters == 0 ) { + if (outputBufferSizeInCharacters == 0) { outputUnicodeCharacterCount = 0; return; } @@ -68,149 +67,126 @@ void copyString8to32( UChar32* pOut = uchar32output; UChar32 uchar32; int reducedBufferSize = outputBufferSizeInCharacters - 1; - while ( *pIn && ( pOut - uchar32output ) < reducedBufferSize ) { - + 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 + 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 ); + } 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 { + } 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 ); + } 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 { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 2; } - } - else { + } 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 ); + } 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 { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 2; } - } - else { + } 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 ); + } 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 { + } 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 ) ) { + else if ((pIn[1] >= 0x80) && (pIn[1] <= 0xBF)) { + if ((pIn[2] >= 0x80) && (pIn[2] <= 0xBF)) { conversionErrorCode |= BadUTF8_surrogate; pIn += 3; - } - else { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 2; } - } - else { + } 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 ); + } 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 { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 2; } - } - else { + } 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 ); + } 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 { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 3; } - } - else { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 2; } - } - else { + } 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 ); + } 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 { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 3; } - } - else { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 2; } - } - else { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 1; } - } - else { + } else { conversionErrorCode |= BadUTF8_invalid_byte; pIn += 1; } - if ( uchar32 != 0xFEFF ) { // do not store Byte Order Mark + if (uchar32 != 0xFEFF) { // do not store Byte Order Mark *pOut++ = uchar32; } } @@ -221,14 +197,14 @@ void copyString8to32( /** * 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 ) { +void copyString32(UChar32* dest32, const UChar32* source32, size_t destLengthInCharacters) { + if (destLengthInCharacters) { + while (*source32 && --destLengthInCharacters > 0) { *dest32++ = *source32++; } *dest32 = 0; @@ -239,39 +215,39 @@ void copyString32( UChar32* dest32, const UChar32* source32, size_t destLengthIn * 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 copyString32to8counted(UChar8* dest8, + const UChar32* source32, + size_t outputBufferSizeInBytes, + size_t charCount) { size_t outputUTF8ByteCount = 0; - if ( outputBufferSizeInBytes ) { + if (outputBufferSizeInBytes) { size_t reducedBufferSize = outputBufferSizeInBytes - 4; - while ( charCount-- && *source32 && outputUTF8ByteCount < reducedBufferSize ) { + while (charCount-- && *source32 && outputUTF8ByteCount < reducedBufferSize) { UChar32 c = *source32++; - if ( c <= 0x7F ) { + if (c <= 0x7F) { *dest8++ = c; outputUTF8ByteCount += 1; - } - else if ( c <= 0x7FF ) { - *dest8++ = 0xC0 | ( c >> 6 ); - *dest8++ = 0x80 | ( 0x3F & c ); + } 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 ); + } 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 ); + } else if (c <= 0x1FFFFF) { + *dest8++ = 0xF0 | (c >> 18); + *dest8++ = 0x80 | (0x3F & (c >> 12)); + *dest8++ = 0x80 | (0x3F & (c >> 6)); + *dest8++ = 0x80 | (0x3F & c); outputUTF8ByteCount += 4; } } @@ -283,25 +259,25 @@ size_t copyString32to8counted( UChar8* dest8, const UChar32* source32, size_t ou /** * 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 ); +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 strlen32(const UChar32* str32) { size_t length = 0; - while ( *str32++ ) { + while (*str32++) { ++length; } return length; @@ -315,9 +291,9 @@ size_t strlen32( const UChar32* str32 ) { * @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 ) { +int strncmp32(UChar32* first32, UChar32* second32, size_t length) { + while (length--) { + if (*first32 == 0 || *first32 != *second32) { return *first32 - *second32; } ++first32; @@ -334,27 +310,26 @@ int strncmp32( UChar32* first32, UChar32* second32, size_t length ) { * @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 ) { +int write32(int fileHandle, const UChar32* string32, unsigned int sourceLengthInCharacters) { size_t tempBufferBytes = 4 * sourceLengthInCharacters + 1; - std::unique_ptr<char[]> tempCharString( new char[ tempBufferBytes ] ); - size_t count = copyString32to8counted( reinterpret_cast<UChar8*>( tempCharString.get() ), - string32, - tempBufferBytes, - sourceLengthInCharacters ); + std::unique_ptr<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 ) { + 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); } #else - return write( fileHandle, tempCharString.get(), count ); + return write(fileHandle, tempCharString.get(), count); #endif } -} // namespace linenoise_utf8 +} // namespace linenoise_utf8 diff --git a/src/mongo/shell/linenoise_utf8.h b/src/mongo/shell/linenoise_utf8.h index 4cca38ad298..b459ea344dc 100644 --- a/src/mongo/shell/linenoise_utf8.h +++ b/src/mongo/shell/linenoise_utf8.h @@ -33,16 +33,12 @@ namespace linenoise_utf8 { -typedef unsigned char UChar8; // UTF-8 octet -typedef unsigned int UChar32; // Unicode code point +typedef unsigned char UChar8; // UTF-8 octet +typedef unsigned int UChar32; // Unicode code point // Error bits (or-ed together) returned from utf8toUChar32string // -enum BadUTF8 { - BadUTF8_no_error = 0x00, - BadUTF8_invalid_byte = 0x01, - BadUTF8_surrogate = 0x02 -}; +enum BadUTF8 { BadUTF8_no_error = 0x00, BadUTF8_invalid_byte = 0x01, BadUTF8_surrogate = 0x02 }; /** * Convert a null terminated UTF-8 std::string from UTF-8 and store it in a UChar32 destination buffer @@ -50,61 +46,63 @@ enum BadUTF8 { * 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 ); +void copyString8to32(UChar32* uchar32output, + const UChar8* utf8input, + size_t outputBufferSizeInCharacters, + size_t& outputUnicodeCharacterCount, + int& conversionErrorCode); /** * Copy a null terminated UChar32 std::string to a UChar32 destination buffer * Always null terminates the destination std::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 ); +void copyString32(UChar32* dest32, const UChar32* source32, size_t destLengthInCharacters); /** * Convert a specified number of UChar32 characters from a possibly null terminated UChar32 std::string to UTF-8 * and store it in a UChar8 destination buffer * Always null terminates the destination std::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 copyString32to8counted(UChar8* dest8, + const UChar32* source32, + size_t outputBufferSizeInBytes, + size_t charCount); /** * Convert a null terminated UChar32 std::string to UTF-8 and store it in a UChar8 destination buffer * Always null terminates the destination std::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 ); +size_t copyString32to8(UChar8* dest8, const UChar32* source32, size_t outputBufferSizeInBytes); /** * Count characters (i.e. Unicode code points, array elements) in a null terminated UChar32 string - * + * * @param str32 Source UChar32 string * @return std::string length in characters */ -size_t strlen32( const UChar32* str32 ); +size_t strlen32(const UChar32* str32); /** * Compare two UChar32 null-terminated strings with length parameter @@ -114,7 +112,7 @@ size_t strlen32( const UChar32* str32 ); * @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 ); +int strncmp32(UChar32* first32, UChar32* second32, size_t length); /** * Internally convert an array of UChar32 characters of specified length to UTF-8 and write it to fileHandle @@ -124,41 +122,55 @@ int strncmp32( UChar32* first32, UChar32* second32, size_t length ); * @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 ); +int write32(int fileHandle, const UChar32* string32, unsigned int sourceLengthInCharacters); /** * Template and classes for UChar8 and UChar32 strings */ template <typename char_type> struct UtfStringMixin { - typedef char_type char_t; // inherited + typedef char_type char_t; // inherited - UtfStringMixin() : _len( 0 ), _cap( 0 ), _chars( 0 ) {} + UtfStringMixin() : _len(0), _cap(0), _chars(0) {} - UtfStringMixin( const UtfStringMixin& other ) // copies like std::string - :_len( other._len ), _cap( other._len+1 ), _chars( other._chars ), _str( new char_t[_cap] ) - { - memcpy( _str.get(), other._str.get(), _cap * sizeof( char_t ) ); + UtfStringMixin(const UtfStringMixin& other) // copies like std::string + : _len(other._len), + _cap(other._len + 1), + _chars(other._chars), + _str(new char_t[_cap]) { + memcpy(_str.get(), other._str.get(), _cap * sizeof(char_t)); } - UtfStringMixin& operator= (UtfStringMixin copy) { - this->swap( copy ); + UtfStringMixin& operator=(UtfStringMixin copy) { + this->swap(copy); return *this; } - char_t* get() const { return _str.get(); } - char_t& operator[](size_t idx) { return _str[idx]; } - const char_t& operator[](size_t idx) const { return _str[idx]; } + char_t* get() const { + return _str.get(); + } + char_t& operator[](size_t idx) { + return _str[idx]; + } + const char_t& operator[](size_t idx) const { + return _str[idx]; + } - size_t length() const { return _len; } - size_t capacity() const { return _cap; } - size_t chars() const { return _chars; } + size_t length() const { + return _len; + } + size_t capacity() const { + return _cap; + } + size_t chars() const { + return _chars; + } - void swap( UtfStringMixin& other ) { - std::swap( _len, other._len ); - std::swap( _cap, other._cap ); - std::swap( _chars, other._chars ); - _str.swap( other._str ); + void swap(UtfStringMixin& other) { + std::swap(_len, other._len); + std::swap(_cap, other._cap); + std::swap(_chars, other._chars); + _str.swap(other._str); } protected: @@ -172,76 +184,74 @@ struct Utf32String; struct Utf8String : public UtfStringMixin<UChar8> { Utf8String() {} - explicit Utf8String( const UChar32* s, int chars = -1 ) { - if ( chars == -1 ) { - initFrom32( s, strlen32( s ) ); - } - else { - initFrom32( s, chars ); + explicit Utf8String(const UChar32* s, int chars = -1) { + if (chars == -1) { + initFrom32(s, strlen32(s)); + } else { + initFrom32(s, chars); } } - explicit Utf8String( const Utf32String& c ); // defined after utf32String + explicit Utf8String(const Utf32String& c); // defined after utf32String private: - void initFrom32( const UChar32* s, int chars ) { + void initFrom32(const UChar32* s, int chars) { _chars = chars; - _cap = _chars * sizeof( UChar32 ) + 1; - _str.reset( new char_t[_cap] ); - _len = copyString32to8counted( _str.get(), s, _cap, chars ); + _cap = _chars * sizeof(UChar32) + 1; + _str.reset(new char_t[_cap]); + _len = copyString32to8counted(_str.get(), s, _cap, chars); } }; struct Utf32String : public UtfStringMixin<UChar32> { Utf32String() {} - explicit Utf32String( const UChar32* s ) { - _chars = _len = strlen32( s ); + explicit Utf32String(const UChar32* s) { + _chars = _len = strlen32(s); _cap = _len + 1; - _str.reset( new UChar32[_cap] ); - memcpy( _str.get(), s, _cap * sizeof( UChar32 ) ); + _str.reset(new UChar32[_cap]); + memcpy(_str.get(), s, _cap * sizeof(UChar32)); } - explicit Utf32String( const UChar32* s, int textLen ) { + explicit Utf32String(const UChar32* s, int textLen) { _chars = _len = textLen; _cap = _len + 1; - _str.reset( new UChar32[_cap] ); - memcpy( _str.get(), s, _len * sizeof( UChar32 ) ); + _str.reset(new UChar32[_cap]); + memcpy(_str.get(), s, _len * sizeof(UChar32)); _str[_len] = 0; } - explicit Utf32String( const UChar8* s, int chars = -1 ) { - initFrom8( s, chars ); + explicit Utf32String(const UChar8* s, int chars = -1) { + initFrom8(s, chars); } - explicit Utf32String( const Utf8String& c ) { - initFrom8( c.get(), c.chars() ); + explicit Utf32String(const Utf8String& c) { + initFrom8(c.get(), c.chars()); } - explicit Utf32String( size_t reserve ) { + explicit Utf32String(size_t reserve) { _len = 0; _cap = reserve; _chars = 0; - _str.reset( new UChar32[_cap] ); + _str.reset(new UChar32[_cap]); _str[0] = 0; } - void initFromBuffer( void ) { - _chars = _len = strlen32( _str.get() ); + void initFromBuffer(void) { + _chars = _len = strlen32(_str.get()); } private: - void initFrom8( const UChar8* s, int chars ) { + void initFrom8(const UChar8* s, int chars) { Utf32String temp; - if ( chars == -1 ) { - temp._cap = strlen( reinterpret_cast<const char*>( s ) ) + 1; // worst case ASCII - } - else { + if (chars == -1) { + temp._cap = strlen(reinterpret_cast<const char*>(s)) + 1; // worst case ASCII + } else { temp._cap = chars + 1; } - temp._str.reset( new char_t[temp._cap] ); + temp._str.reset(new char_t[temp._cap]); int error; - copyString8to32( temp._str.get(), s, temp._cap, temp._chars, error ); + copyString8to32(temp._str.get(), s, temp._cap, temp._chars, error); temp._len = temp._chars; - this->swap( temp ); + this->swap(temp); } }; -inline Utf8String::Utf8String( const Utf32String& s ) { - initFrom32( s.get(), s.chars() ); +inline Utf8String::Utf8String(const Utf32String& s) { + initFrom32(s.get(), s.chars()); } -} // namespace linenoise_utf8 +} // namespace linenoise_utf8 diff --git a/src/mongo/shell/mk_wcwidth.cpp b/src/mongo/shell/mk_wcwidth.cpp index 2088c345fac..58e3a368293 100644 --- a/src/mongo/shell/mk_wcwidth.cpp +++ b/src/mongo/shell/mk_wcwidth.cpp @@ -64,28 +64,28 @@ #include <wchar.h> struct interval { - int first; - int last; + int first; + int last; }; /* auxiliary function for binary search in interval table */ -static int bisearch(int ucs, const struct interval *table, int max) { - int min = 0; - int mid; +static int bisearch(int ucs, const struct interval* table, int max) { + int min = 0; + int mid; - if (ucs < table[0].first || ucs > table[max].last) - return 0; - while (max >= min) { - mid = (min + max) / 2; - if (ucs > table[mid].last) - min = mid + 1; - else if (ucs < table[mid].first) - max = mid - 1; - else - return 1; - } + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } - return 0; + return 0; } @@ -121,102 +121,189 @@ static int bisearch(int ucs, const struct interval *table, int max) { * in ISO 10646. */ -int mk_wcwidth(int ucs) -{ - /* sorted list of non-overlapping intervals of non-spacing characters */ - /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ - static const struct interval combining[] = { - { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, - { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, - { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, - { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, - { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, - { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, - { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, - { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, - { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, - { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, - { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, - { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, - { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, - { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, - { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, - { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, - { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, - { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, - { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, - { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, - { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, - { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, - { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, - { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, - { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, - { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, - { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, - { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, - { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, - { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, - { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, - { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, - { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, - { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, - { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, - { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, - { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, - { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, - { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, - { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, - { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, - { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, - { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, - { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, - { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, - { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, - { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, - { 0xE0100, 0xE01EF } - }; +int mk_wcwidth(int ucs) { + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = {{0x0300, 0x036F}, + {0x0483, 0x0486}, + {0x0488, 0x0489}, + {0x0591, 0x05BD}, + {0x05BF, 0x05BF}, + {0x05C1, 0x05C2}, + {0x05C4, 0x05C5}, + {0x05C7, 0x05C7}, + {0x0600, 0x0603}, + {0x0610, 0x0615}, + {0x064B, 0x065E}, + {0x0670, 0x0670}, + {0x06D6, 0x06E4}, + {0x06E7, 0x06E8}, + {0x06EA, 0x06ED}, + {0x070F, 0x070F}, + {0x0711, 0x0711}, + {0x0730, 0x074A}, + {0x07A6, 0x07B0}, + {0x07EB, 0x07F3}, + {0x0901, 0x0902}, + {0x093C, 0x093C}, + {0x0941, 0x0948}, + {0x094D, 0x094D}, + {0x0951, 0x0954}, + {0x0962, 0x0963}, + {0x0981, 0x0981}, + {0x09BC, 0x09BC}, + {0x09C1, 0x09C4}, + {0x09CD, 0x09CD}, + {0x09E2, 0x09E3}, + {0x0A01, 0x0A02}, + {0x0A3C, 0x0A3C}, + {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, + {0x0A4B, 0x0A4D}, + {0x0A70, 0x0A71}, + {0x0A81, 0x0A82}, + {0x0ABC, 0x0ABC}, + {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, + {0x0ACD, 0x0ACD}, + {0x0AE2, 0x0AE3}, + {0x0B01, 0x0B01}, + {0x0B3C, 0x0B3C}, + {0x0B3F, 0x0B3F}, + {0x0B41, 0x0B43}, + {0x0B4D, 0x0B4D}, + {0x0B56, 0x0B56}, + {0x0B82, 0x0B82}, + {0x0BC0, 0x0BC0}, + {0x0BCD, 0x0BCD}, + {0x0C3E, 0x0C40}, + {0x0C46, 0x0C48}, + {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, + {0x0CBC, 0x0CBC}, + {0x0CBF, 0x0CBF}, + {0x0CC6, 0x0CC6}, + {0x0CCC, 0x0CCD}, + {0x0CE2, 0x0CE3}, + {0x0D41, 0x0D43}, + {0x0D4D, 0x0D4D}, + {0x0DCA, 0x0DCA}, + {0x0DD2, 0x0DD4}, + {0x0DD6, 0x0DD6}, + {0x0E31, 0x0E31}, + {0x0E34, 0x0E3A}, + {0x0E47, 0x0E4E}, + {0x0EB1, 0x0EB1}, + {0x0EB4, 0x0EB9}, + {0x0EBB, 0x0EBC}, + {0x0EC8, 0x0ECD}, + {0x0F18, 0x0F19}, + {0x0F35, 0x0F35}, + {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, + {0x0F71, 0x0F7E}, + {0x0F80, 0x0F84}, + {0x0F86, 0x0F87}, + {0x0F90, 0x0F97}, + {0x0F99, 0x0FBC}, + {0x0FC6, 0x0FC6}, + {0x102D, 0x1030}, + {0x1032, 0x1032}, + {0x1036, 0x1037}, + {0x1039, 0x1039}, + {0x1058, 0x1059}, + {0x1160, 0x11FF}, + {0x135F, 0x135F}, + {0x1712, 0x1714}, + {0x1732, 0x1734}, + {0x1752, 0x1753}, + {0x1772, 0x1773}, + {0x17B4, 0x17B5}, + {0x17B7, 0x17BD}, + {0x17C6, 0x17C6}, + {0x17C9, 0x17D3}, + {0x17DD, 0x17DD}, + {0x180B, 0x180D}, + {0x18A9, 0x18A9}, + {0x1920, 0x1922}, + {0x1927, 0x1928}, + {0x1932, 0x1932}, + {0x1939, 0x193B}, + {0x1A17, 0x1A18}, + {0x1B00, 0x1B03}, + {0x1B34, 0x1B34}, + {0x1B36, 0x1B3A}, + {0x1B3C, 0x1B3C}, + {0x1B42, 0x1B42}, + {0x1B6B, 0x1B73}, + {0x1DC0, 0x1DCA}, + {0x1DFE, 0x1DFF}, + {0x200B, 0x200F}, + {0x202A, 0x202E}, + {0x2060, 0x2063}, + {0x206A, 0x206F}, + {0x20D0, 0x20EF}, + {0x302A, 0x302F}, + {0x3099, 0x309A}, + {0xA806, 0xA806}, + {0xA80B, 0xA80B}, + {0xA825, 0xA826}, + {0xFB1E, 0xFB1E}, + {0xFE00, 0xFE0F}, + {0xFE20, 0xFE23}, + {0xFEFF, 0xFEFF}, + {0xFFF9, 0xFFFB}, + {0x10A01, 0x10A03}, + {0x10A05, 0x10A06}, + {0x10A0C, 0x10A0F}, + {0x10A38, 0x10A3A}, + {0x10A3F, 0x10A3F}, + {0x1D167, 0x1D169}, + {0x1D173, 0x1D182}, + {0x1D185, 0x1D18B}, + {0x1D1AA, 0x1D1AD}, + {0x1D242, 0x1D244}, + {0xE0001, 0xE0001}, + {0xE0020, 0xE007F}, + {0xE0100, 0xE01EF}}; - /* test for 8-bit control characters */ - if (ucs == 0) - return 0; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) - return -1; + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; - /* binary search in table of non-spacing characters */ - if (bisearch(ucs, combining, - sizeof(combining) / sizeof(struct interval) - 1)) - return 0; + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) + return 0; - /* if we arrive here, ucs is not a combining or C0/C1 control character */ + /* if we arrive here, ucs is not a combining or C0/C1 control character */ - return 1 + - (ucs >= 0x1100 && - (ucs <= 0x115f || /* Hangul Jamo init. consonants */ - ucs == 0x2329 || ucs == 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && - ucs != 0x303f) || /* CJK ... Yi */ - (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ - (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ - (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ - (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ - (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ - (ucs >= 0xffe0 && ucs <= 0xffe6) || - (ucs >= 0x20000 && ucs <= 0x2fffd) || - (ucs >= 0x30000 && ucs <= 0x3fffd))); + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || + ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))); } -int mk_wcswidth(const int *pwcs, size_t n) -{ - int w, width = 0; +int mk_wcswidth(const int* pwcs, size_t n) { + int w, width = 0; - for (;*pwcs && n-- > 0; pwcs++) - if ((w = mk_wcwidth(*pwcs)) < 0) - return -1; - else - width += w; + for (; *pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; - return width; + return width; } @@ -229,83 +316,182 @@ int mk_wcswidth(const int *pwcs, size_t n) * the traditional terminal character-width behaviour. It is not * otherwise recommended for general use. */ -int mk_wcwidth_cjk(int ucs) -{ - /* sorted list of non-overlapping intervals of East Asian Ambiguous - * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ - static const struct interval ambiguous[] = { - { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, - { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, - { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, - { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, - { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, - { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, - { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, - { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, - { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, - { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, - { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, - { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, - { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, - { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, - { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, - { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, - { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, - { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, - { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, - { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, - { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, - { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, - { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, - { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, - { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, - { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, - { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, - { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, - { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, - { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, - { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, - { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, - { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, - { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, - { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, - { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, - { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, - { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, - { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, - { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, - { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, - { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, - { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, - { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, - { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, - { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, - { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, - { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, - { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, - { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, - { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, - { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } - }; +int mk_wcwidth_cjk(int ucs) { + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ + static const struct interval ambiguous[] = {{0x00A1, 0x00A1}, + {0x00A4, 0x00A4}, + {0x00A7, 0x00A8}, + {0x00AA, 0x00AA}, + {0x00AE, 0x00AE}, + {0x00B0, 0x00B4}, + {0x00B6, 0x00BA}, + {0x00BC, 0x00BF}, + {0x00C6, 0x00C6}, + {0x00D0, 0x00D0}, + {0x00D7, 0x00D8}, + {0x00DE, 0x00E1}, + {0x00E6, 0x00E6}, + {0x00E8, 0x00EA}, + {0x00EC, 0x00ED}, + {0x00F0, 0x00F0}, + {0x00F2, 0x00F3}, + {0x00F7, 0x00FA}, + {0x00FC, 0x00FC}, + {0x00FE, 0x00FE}, + {0x0101, 0x0101}, + {0x0111, 0x0111}, + {0x0113, 0x0113}, + {0x011B, 0x011B}, + {0x0126, 0x0127}, + {0x012B, 0x012B}, + {0x0131, 0x0133}, + {0x0138, 0x0138}, + {0x013F, 0x0142}, + {0x0144, 0x0144}, + {0x0148, 0x014B}, + {0x014D, 0x014D}, + {0x0152, 0x0153}, + {0x0166, 0x0167}, + {0x016B, 0x016B}, + {0x01CE, 0x01CE}, + {0x01D0, 0x01D0}, + {0x01D2, 0x01D2}, + {0x01D4, 0x01D4}, + {0x01D6, 0x01D6}, + {0x01D8, 0x01D8}, + {0x01DA, 0x01DA}, + {0x01DC, 0x01DC}, + {0x0251, 0x0251}, + {0x0261, 0x0261}, + {0x02C4, 0x02C4}, + {0x02C7, 0x02C7}, + {0x02C9, 0x02CB}, + {0x02CD, 0x02CD}, + {0x02D0, 0x02D0}, + {0x02D8, 0x02DB}, + {0x02DD, 0x02DD}, + {0x02DF, 0x02DF}, + {0x0391, 0x03A1}, + {0x03A3, 0x03A9}, + {0x03B1, 0x03C1}, + {0x03C3, 0x03C9}, + {0x0401, 0x0401}, + {0x0410, 0x044F}, + {0x0451, 0x0451}, + {0x2010, 0x2010}, + {0x2013, 0x2016}, + {0x2018, 0x2019}, + {0x201C, 0x201D}, + {0x2020, 0x2022}, + {0x2024, 0x2027}, + {0x2030, 0x2030}, + {0x2032, 0x2033}, + {0x2035, 0x2035}, + {0x203B, 0x203B}, + {0x203E, 0x203E}, + {0x2074, 0x2074}, + {0x207F, 0x207F}, + {0x2081, 0x2084}, + {0x20AC, 0x20AC}, + {0x2103, 0x2103}, + {0x2105, 0x2105}, + {0x2109, 0x2109}, + {0x2113, 0x2113}, + {0x2116, 0x2116}, + {0x2121, 0x2122}, + {0x2126, 0x2126}, + {0x212B, 0x212B}, + {0x2153, 0x2154}, + {0x215B, 0x215E}, + {0x2160, 0x216B}, + {0x2170, 0x2179}, + {0x2190, 0x2199}, + {0x21B8, 0x21B9}, + {0x21D2, 0x21D2}, + {0x21D4, 0x21D4}, + {0x21E7, 0x21E7}, + {0x2200, 0x2200}, + {0x2202, 0x2203}, + {0x2207, 0x2208}, + {0x220B, 0x220B}, + {0x220F, 0x220F}, + {0x2211, 0x2211}, + {0x2215, 0x2215}, + {0x221A, 0x221A}, + {0x221D, 0x2220}, + {0x2223, 0x2223}, + {0x2225, 0x2225}, + {0x2227, 0x222C}, + {0x222E, 0x222E}, + {0x2234, 0x2237}, + {0x223C, 0x223D}, + {0x2248, 0x2248}, + {0x224C, 0x224C}, + {0x2252, 0x2252}, + {0x2260, 0x2261}, + {0x2264, 0x2267}, + {0x226A, 0x226B}, + {0x226E, 0x226F}, + {0x2282, 0x2283}, + {0x2286, 0x2287}, + {0x2295, 0x2295}, + {0x2299, 0x2299}, + {0x22A5, 0x22A5}, + {0x22BF, 0x22BF}, + {0x2312, 0x2312}, + {0x2460, 0x24E9}, + {0x24EB, 0x254B}, + {0x2550, 0x2573}, + {0x2580, 0x258F}, + {0x2592, 0x2595}, + {0x25A0, 0x25A1}, + {0x25A3, 0x25A9}, + {0x25B2, 0x25B3}, + {0x25B6, 0x25B7}, + {0x25BC, 0x25BD}, + {0x25C0, 0x25C1}, + {0x25C6, 0x25C8}, + {0x25CB, 0x25CB}, + {0x25CE, 0x25D1}, + {0x25E2, 0x25E5}, + {0x25EF, 0x25EF}, + {0x2605, 0x2606}, + {0x2609, 0x2609}, + {0x260E, 0x260F}, + {0x2614, 0x2615}, + {0x261C, 0x261C}, + {0x261E, 0x261E}, + {0x2640, 0x2640}, + {0x2642, 0x2642}, + {0x2660, 0x2661}, + {0x2663, 0x2665}, + {0x2667, 0x266A}, + {0x266C, 0x266D}, + {0x266F, 0x266F}, + {0x273D, 0x273D}, + {0x2776, 0x277F}, + {0xE000, 0xF8FF}, + {0xFFFD, 0xFFFD}, + {0xF0000, 0xFFFFD}, + {0x100000, 0x10FFFD}}; - /* binary search in table of non-spacing characters */ - if (bisearch(ucs, ambiguous, - sizeof(ambiguous) / sizeof(struct interval) - 1)) - return 2; + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, ambiguous, sizeof(ambiguous) / sizeof(struct interval) - 1)) + return 2; - return mk_wcwidth(ucs); + return mk_wcwidth(ucs); } -int mk_wcswidth_cjk(const int *pwcs, size_t n) -{ - int w, width = 0; +int mk_wcswidth_cjk(const int* pwcs, size_t n) { + int w, width = 0; - for (;*pwcs && n-- > 0; pwcs++) - if ((w = mk_wcwidth_cjk(*pwcs)) < 0) - return -1; - else - width += w; + for (; *pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth_cjk(*pwcs)) < 0) + return -1; + else + width += w; - return width; + return width; } diff --git a/src/mongo/shell/mk_wcwidth.h b/src/mongo/shell/mk_wcwidth.h index fa81dc61af2..763ca339c1d 100644 --- a/src/mongo/shell/mk_wcwidth.h +++ b/src/mongo/shell/mk_wcwidth.h @@ -62,4 +62,4 @@ */ extern int mk_wcwidth(int ucs); -extern int mk_wcswidth(const int *pwcs, size_t n); +extern int mk_wcswidth(const int* pwcs, size_t n); diff --git a/src/mongo/shell/shell_options.cpp b/src/mongo/shell/shell_options.cpp index c016e16e241..a54f2bbc567 100644 --- a/src/mongo/shell/shell_options.cpp +++ b/src/mongo/shell/shell_options.cpp @@ -47,297 +47,299 @@ namespace mongo { - using std::cout; - using std::endl; - using std::string; - using std::vector; +using std::cout; +using std::endl; +using std::string; +using std::vector; - ShellGlobalParams shellGlobalParams; +ShellGlobalParams shellGlobalParams; - Status addMongoShellOptions(moe::OptionSection* options) { +Status addMongoShellOptions(moe::OptionSection* options) { + options->addOptionChaining( + "shell", "shell", moe::Switch, "run the shell after executing files"); - options->addOptionChaining("shell", "shell", moe::Switch, - "run the shell after executing files"); + options->addOptionChaining("nodb", + "nodb", + moe::Switch, + "don't connect to mongod on startup - no 'db address' arg expected"); - options->addOptionChaining("nodb", "nodb", moe::Switch, - "don't connect to mongod on startup - no 'db address' arg expected"); + options->addOptionChaining( + "norc", "norc", moe::Switch, "will not run the \".mongorc.js\" file on start up"); - options->addOptionChaining("norc", "norc", moe::Switch, - "will not run the \".mongorc.js\" file on start up"); + options->addOptionChaining("quiet", "quiet", moe::Switch, "be less chatty"); - options->addOptionChaining("quiet", "quiet", moe::Switch, "be less chatty"); + options->addOptionChaining("port", "port", moe::String, "port to connect to"); - options->addOptionChaining("port", "port", moe::String, "port to connect to"); + options->addOptionChaining("host", "host", moe::String, "server to connect to"); - options->addOptionChaining("host", "host", moe::String, "server to connect to"); + options->addOptionChaining("eval", "eval", moe::String, "evaluate javascript"); - options->addOptionChaining("eval", "eval", moe::String, "evaluate javascript"); + moe::OptionSection authenticationOptions("Authentication Options"); - moe::OptionSection authenticationOptions("Authentication Options"); + authenticationOptions.addOptionChaining( + "username", "username,u", moe::String, "username for authentication"); - authenticationOptions.addOptionChaining("username", "username,u", moe::String, - "username for authentication"); + authenticationOptions.addOptionChaining( + "password", "password,p", moe::String, "password for authentication") + .setImplicit(moe::Value(std::string(""))); - authenticationOptions.addOptionChaining("password", "password,p", moe::String, - "password for authentication") - .setImplicit(moe::Value(std::string(""))); + authenticationOptions.addOptionChaining("authenticationDatabase", + "authenticationDatabase", + moe::String, + "user source (defaults to dbname)") + .setDefault(moe::Value(std::string(""))); - authenticationOptions.addOptionChaining("authenticationDatabase", "authenticationDatabase", - moe::String, "user source (defaults to dbname)") - .setDefault(moe::Value(std::string(""))); + authenticationOptions.addOptionChaining("authenticationMechanism", + "authenticationMechanism", + moe::String, + "authentication mechanism"); - authenticationOptions.addOptionChaining("authenticationMechanism", - "authenticationMechanism", moe::String, "authentication mechanism"); + authenticationOptions.addOptionChaining( + "gssapiServiceName", + "gssapiServiceName", + moe::String, + "Service name to use when authenticating using GSSAPI/Kerberos") + .setDefault(moe::Value(std::string(saslDefaultServiceName))); - authenticationOptions.addOptionChaining("gssapiServiceName", "gssapiServiceName", - moe::String, - "Service name to use when authenticating using GSSAPI/Kerberos") - .setDefault(moe::Value(std::string(saslDefaultServiceName))); + authenticationOptions.addOptionChaining( + "gssapiHostName", + "gssapiHostName", + moe::String, + "Remote host name to use for purpose of GSSAPI/Kerberos authentication"); - authenticationOptions.addOptionChaining("gssapiHostName", "gssapiHostName", moe::String, - "Remote host name to use for purpose of GSSAPI/Kerberos authentication"); + options->addSection(authenticationOptions); - options->addSection(authenticationOptions); + options->addOptionChaining("help", "help,h", moe::Switch, "show this usage information"); - options->addOptionChaining("help", "help,h", moe::Switch, "show this usage information"); + options->addOptionChaining("version", "version", moe::Switch, "show version information"); - options->addOptionChaining("version", "version", moe::Switch, "show version information"); + options->addOptionChaining("verbose", "verbose", moe::Switch, "increase verbosity"); - options->addOptionChaining("verbose", "verbose", moe::Switch, "increase verbosity"); + options->addOptionChaining( + "ipv6", "ipv6", moe::Switch, "enable IPv6 support (disabled by default)"); - options->addOptionChaining("ipv6", "ipv6", moe::Switch, - "enable IPv6 support (disabled by default)"); - - Status ret = Status::OK(); + Status ret = Status::OK(); #ifdef MONGO_CONFIG_SSL - ret = addSSLClientOptions(options); - if (!ret.isOK()) { - return ret; - } + ret = addSSLClientOptions(options); + if (!ret.isOK()) { + return ret; + } #endif - options->addOptionChaining("dbaddress", "dbaddress", moe::String, "dbaddress") - .hidden() - .positional(1, 1); - - options->addOptionChaining("files", "files", moe::StringVector, "files") - .hidden() - .positional(2, -1); - - // for testing, kill op will also be disabled automatically if the tests starts a mongo - // program - options->addOptionChaining("nokillop", "nokillop", moe::Switch, "nokillop") - .hidden(); - - // for testing, will kill op without prompting - options->addOptionChaining("autokillop", "autokillop", moe::Switch, "autokillop") - .hidden(); - - options->addOptionChaining("useLegacyWriteOps", - "useLegacyWriteOps", - moe::Switch, - "use legacy write ops instead of write commands").hidden(); - - options->addOptionChaining("writeMode", - "writeMode", - moe::String, - "mode to determine how writes are done:" - " commands, compatibility, legacy").hidden(); - - options->addOptionChaining("readMode", - "readMode", - moe::String, - "mode to determine how .find() queries are done:" - " commands, compatibility").hidden(); - - options->addOptionChaining("rpcProtocols", - "rpcProtocols", - moe::String, - " none, opQueryOnly, opCommandOnly, all").hidden(); - - return Status::OK(); - } + options->addOptionChaining("dbaddress", "dbaddress", moe::String, "dbaddress") + .hidden() + .positional(1, 1); - std::string getMongoShellHelp(StringData name, const moe::OptionSection& options) { - StringBuilder sb; - sb << "MongoDB shell version: " << mongo::versionString << "\n"; - sb << "usage: " << name << " [options] [db address] [file names (ending in .js)]\n" - << "db address can be:\n" - << " foo foo database on local machine\n" - << " 192.169.0.5/foo foo database on 192.168.0.5 machine\n" - << " 192.169.0.5:9999/foo foo database on 192.168.0.5 machine on port 9999\n" - << options.helpString() << "\n" - << "file names: a list of files to run. files have to end in .js and will exit after " - << "unless --shell is specified"; - return sb.str(); - } + options->addOptionChaining("files", "files", moe::StringVector, "files") + .hidden() + .positional(2, -1); - bool handlePreValidationMongoShellOptions(const moe::Environment& params, - const std::vector<std::string>& args) { - if (params.count("help")) { - std::cout << getMongoShellHelp(args[0], moe::startupOptions) << std::endl; - return false; - } - if (params.count("version")) { - cout << "MongoDB shell version: " << mongo::versionString << endl; - return false; - } - return true; - } + // for testing, kill op will also be disabled automatically if the tests starts a mongo + // program + options->addOptionChaining("nokillop", "nokillop", moe::Switch, "nokillop").hidden(); - Status storeMongoShellOptions(const moe::Environment& params, - const std::vector<std::string>& args) { + // for testing, will kill op without prompting + options->addOptionChaining("autokillop", "autokillop", moe::Switch, "autokillop").hidden(); - if (params.count("quiet")) { - mongo::serverGlobalParams.quiet = true; - } + options->addOptionChaining("useLegacyWriteOps", + "useLegacyWriteOps", + moe::Switch, + "use legacy write ops instead of write commands").hidden(); + + options->addOptionChaining("writeMode", + "writeMode", + moe::String, + "mode to determine how writes are done:" + " commands, compatibility, legacy").hidden(); + + options->addOptionChaining("readMode", + "readMode", + moe::String, + "mode to determine how .find() queries are done:" + " commands, compatibility").hidden(); + + options->addOptionChaining("rpcProtocols", + "rpcProtocols", + moe::String, + " none, opQueryOnly, opCommandOnly, all").hidden(); + + return Status::OK(); +} + +std::string getMongoShellHelp(StringData name, const moe::OptionSection& options) { + StringBuilder sb; + sb << "MongoDB shell version: " << mongo::versionString << "\n"; + sb << "usage: " << name << " [options] [db address] [file names (ending in .js)]\n" + << "db address can be:\n" + << " foo foo database on local machine\n" + << " 192.169.0.5/foo foo database on 192.168.0.5 machine\n" + << " 192.169.0.5:9999/foo foo database on 192.168.0.5 machine on port 9999\n" + << options.helpString() << "\n" + << "file names: a list of files to run. files have to end in .js and will exit after " + << "unless --shell is specified"; + return sb.str(); +} + +bool handlePreValidationMongoShellOptions(const moe::Environment& params, + const std::vector<std::string>& args) { + if (params.count("help")) { + std::cout << getMongoShellHelp(args[0], moe::startupOptions) << std::endl; + return false; + } + if (params.count("version")) { + cout << "MongoDB shell version: " << mongo::versionString << endl; + return false; + } + return true; +} + +Status storeMongoShellOptions(const moe::Environment& params, + const std::vector<std::string>& args) { + if (params.count("quiet")) { + mongo::serverGlobalParams.quiet = true; + } #ifdef MONGO_CONFIG_SSL - Status ret = storeSSLClientOptions(params); - if (!ret.isOK()) { - return ret; - } + Status ret = storeSSLClientOptions(params); + if (!ret.isOK()) { + return ret; + } #endif - if (params.count("ipv6")) { - mongo::enableIPv6(); - } - if (params.count("verbose")) { - logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(1)); - } + if (params.count("ipv6")) { + mongo::enableIPv6(); + } + if (params.count("verbose")) { + logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(1)); + } - if (params.count("port")) { - shellGlobalParams.port = params["port"].as<string>(); - } + if (params.count("port")) { + shellGlobalParams.port = params["port"].as<string>(); + } - if (params.count("host")) { - shellGlobalParams.dbhost = params["host"].as<string>(); - } + if (params.count("host")) { + shellGlobalParams.dbhost = params["host"].as<string>(); + } - if (params.count("eval")) { - shellGlobalParams.script = params["eval"].as<string>(); - } + if (params.count("eval")) { + shellGlobalParams.script = params["eval"].as<string>(); + } - if (params.count("username")) { - shellGlobalParams.username = params["username"].as<string>(); - } + if (params.count("username")) { + shellGlobalParams.username = params["username"].as<string>(); + } - if (params.count("password")) { - shellGlobalParams.usingPassword = true; - shellGlobalParams.password = params["password"].as<string>(); - } + if (params.count("password")) { + shellGlobalParams.usingPassword = true; + shellGlobalParams.password = params["password"].as<string>(); + } - if (params.count("authenticationDatabase")) { - shellGlobalParams.authenticationDatabase = - params["authenticationDatabase"].as<string>(); - } + if (params.count("authenticationDatabase")) { + shellGlobalParams.authenticationDatabase = params["authenticationDatabase"].as<string>(); + } - if (params.count("authenticationMechanism")) { - shellGlobalParams.authenticationMechanism = - params["authenticationMechanism"].as<string>(); - } + if (params.count("authenticationMechanism")) { + shellGlobalParams.authenticationMechanism = params["authenticationMechanism"].as<string>(); + } - if (params.count("gssapiServiceName")) { - shellGlobalParams.gssapiServiceName = params["gssapiServiceName"].as<string>(); - } + if (params.count("gssapiServiceName")) { + shellGlobalParams.gssapiServiceName = params["gssapiServiceName"].as<string>(); + } - if (params.count("gssapiHostName")) { - shellGlobalParams.gssapiHostName = params["gssapiHostName"].as<string>(); - } + if (params.count("gssapiHostName")) { + shellGlobalParams.gssapiHostName = params["gssapiHostName"].as<string>(); + } - if (params.count("shell")) { - shellGlobalParams.runShell = true; - } - if (params.count("nodb")) { - shellGlobalParams.nodb = true; - } - if (params.count("norc")) { - shellGlobalParams.norc = true; - } - if (params.count("files")) { - shellGlobalParams.files = params["files"].as< vector<string> >(); - } - if (params.count("nokillop")) { - mongo::shell_utils::_nokillop = true; - } - if (params.count("autokillop")) { - shellGlobalParams.autoKillOp = true; - } - if (params.count("useLegacyWriteOps")) { - shellGlobalParams.writeMode = "legacy"; - } - if (params.count("writeMode")) { - std::string mode = params["writeMode"].as<string>(); - if (mode != "commands" && mode != "legacy" && mode != "compatibility") { - throw MsgAssertionException(17396, - mongoutils::str::stream() << - "Unknown writeMode option: " << mode); - } - shellGlobalParams.writeMode = mode; + if (params.count("shell")) { + shellGlobalParams.runShell = true; + } + if (params.count("nodb")) { + shellGlobalParams.nodb = true; + } + if (params.count("norc")) { + shellGlobalParams.norc = true; + } + if (params.count("files")) { + shellGlobalParams.files = params["files"].as<vector<string>>(); + } + if (params.count("nokillop")) { + mongo::shell_utils::_nokillop = true; + } + if (params.count("autokillop")) { + shellGlobalParams.autoKillOp = true; + } + if (params.count("useLegacyWriteOps")) { + shellGlobalParams.writeMode = "legacy"; + } + if (params.count("writeMode")) { + std::string mode = params["writeMode"].as<string>(); + if (mode != "commands" && mode != "legacy" && mode != "compatibility") { + throw MsgAssertionException( + 17396, mongoutils::str::stream() << "Unknown writeMode option: " << mode); } - if (params.count("readMode")) { - std::string mode = params["readMode"].as<string>(); - if (mode != "commands" && mode != "compatibility") { - throw MsgAssertionException(17397, - mongoutils::str::stream() + shellGlobalParams.writeMode = mode; + } + if (params.count("readMode")) { + std::string mode = params["readMode"].as<string>(); + if (mode != "commands" && mode != "compatibility") { + throw MsgAssertionException(17397, + mongoutils::str::stream() << "Unknown readMode option: '" << mode << "'. Valid modes are: {commands, compatibility}"); - } - shellGlobalParams.readMode = mode; } - if (params.count("rpcProtocols")) { - std::string protos = params["rpcProtocols"].as<string>(); - auto parsedRPCProtos = rpc::parseProtocolSet(protos); - if (!parsedRPCProtos.isOK()) { - throw MsgAssertionException(28653, - str::stream() - << "Unknown RPC Protocols: '" << protos - << "'. Valid values are {none, opQueryOnly, " - << "opCommandOnly, all}"); - } - shellGlobalParams.rpcProtocols = parsedRPCProtos.getValue(); + shellGlobalParams.readMode = mode; + } + if (params.count("rpcProtocols")) { + std::string protos = params["rpcProtocols"].as<string>(); + auto parsedRPCProtos = rpc::parseProtocolSet(protos); + if (!parsedRPCProtos.isOK()) { + throw MsgAssertionException(28653, + str::stream() << "Unknown RPC Protocols: '" << protos + << "'. Valid values are {none, opQueryOnly, " + << "opCommandOnly, all}"); } + shellGlobalParams.rpcProtocols = parsedRPCProtos.getValue(); + } - /* This is a bit confusing, here are the rules: - * - * if nodb is set then all positional parameters are files - * otherwise the first positional parameter might be a dbaddress, but - * only if one of these conditions is met: - * - it contains no '.' after the last appearance of '\' or '/' - * - it doesn't end in '.js' and it doesn't specify a path to an existing file */ - if (params.count("dbaddress")) { - string dbaddress = params["dbaddress"].as<string>(); - if (shellGlobalParams.nodb) { - shellGlobalParams.files.insert( shellGlobalParams.files.begin(), dbaddress ); - } - else { - string basename = dbaddress.substr( dbaddress.find_last_of( "/\\" ) + 1 ); - if (basename.find_first_of( '.' ) == string::npos || - (basename.find(".js", basename.size() - 3) == string::npos && - !::mongo::shell_utils::fileExists(dbaddress))) { - shellGlobalParams.url = dbaddress; - } - else { - shellGlobalParams.files.insert( shellGlobalParams.files.begin(), dbaddress ); - } + /* This is a bit confusing, here are the rules: + * + * if nodb is set then all positional parameters are files + * otherwise the first positional parameter might be a dbaddress, but + * only if one of these conditions is met: + * - it contains no '.' after the last appearance of '\' or '/' + * - it doesn't end in '.js' and it doesn't specify a path to an existing file */ + if (params.count("dbaddress")) { + string dbaddress = params["dbaddress"].as<string>(); + if (shellGlobalParams.nodb) { + shellGlobalParams.files.insert(shellGlobalParams.files.begin(), dbaddress); + } else { + string basename = dbaddress.substr(dbaddress.find_last_of("/\\") + 1); + if (basename.find_first_of('.') == string::npos || + (basename.find(".js", basename.size() - 3) == string::npos && + !::mongo::shell_utils::fileExists(dbaddress))) { + shellGlobalParams.url = dbaddress; + } else { + shellGlobalParams.files.insert(shellGlobalParams.files.begin(), dbaddress); } } + } - if ( shellGlobalParams.url == "*" ) { - StringBuilder sb; - sb << "ERROR: " << "\"*\" is an invalid db address"; - sb << getMongoShellHelp(args[0], moe::startupOptions); - return Status(ErrorCodes::BadValue, sb.str()); - } - - return Status::OK(); + if (shellGlobalParams.url == "*") { + StringBuilder sb; + sb << "ERROR: " + << "\"*\" is an invalid db address"; + sb << getMongoShellHelp(args[0], moe::startupOptions); + return Status(ErrorCodes::BadValue, sb.str()); } - Status validateMongoShellOptions(const moe::Environment& params) { + return Status::OK(); +} + +Status validateMongoShellOptions(const moe::Environment& params) { #ifdef MONGO_CONFIG_SSL - Status ret = validateSSLMongoShellOptions(params); - if (!ret.isOK()) { - return ret; - } -#endif - return Status::OK(); + Status ret = validateSSLMongoShellOptions(params); + if (!ret.isOK()) { + return ret; } +#endif + return Status::OK(); +} } diff --git a/src/mongo/shell/shell_options.h b/src/mongo/shell/shell_options.h index a61e58ce9bf..cc4edb8e3e5 100644 --- a/src/mongo/shell/shell_options.h +++ b/src/mongo/shell/shell_options.h @@ -36,65 +36,64 @@ namespace mongo { - namespace optionenvironment { - class OptionSection; - class Environment; - } // namespace optionenvironment +namespace optionenvironment { +class OptionSection; +class Environment; +} // namespace optionenvironment - namespace moe = mongo::optionenvironment; +namespace moe = mongo::optionenvironment; - struct ShellGlobalParams { - std::string url; - std::string dbhost; - std::string port; - std::vector<std::string> files; +struct ShellGlobalParams { + std::string url; + std::string dbhost; + std::string port; + std::vector<std::string> files; - std::string username; - std::string password; - bool usingPassword; - std::string authenticationMechanism; - std::string authenticationDatabase; - std::string gssapiServiceName; - std::string gssapiHostName; + std::string username; + std::string password; + bool usingPassword; + std::string authenticationMechanism; + std::string authenticationDatabase; + std::string gssapiServiceName; + std::string gssapiHostName; - bool runShell; - bool nodb; - bool norc; + bool runShell; + bool nodb; + bool norc; - std::string script; + std::string script; - bool autoKillOp; - bool useWriteCommandsDefault; - std::string writeMode; + bool autoKillOp; + bool useWriteCommandsDefault; + std::string writeMode; - std::string readMode; + std::string readMode; - rpc::ProtocolSet rpcProtocols; + rpc::ProtocolSet rpcProtocols; - ShellGlobalParams() : autoKillOp(false), - useWriteCommandsDefault(true), - writeMode("commands"), - readMode("compatibility"), - rpcProtocols(rpc::supports::kOpQueryOnly) { - } - }; + ShellGlobalParams() + : autoKillOp(false), + useWriteCommandsDefault(true), + writeMode("commands"), + readMode("compatibility"), + rpcProtocols(rpc::supports::kOpQueryOnly) {} +}; - extern ShellGlobalParams shellGlobalParams; +extern ShellGlobalParams shellGlobalParams; - Status addMongoShellOptions(moe::OptionSection* options); +Status addMongoShellOptions(moe::OptionSection* options); - std::string getMongoShellHelp(StringData name, const moe::OptionSection& options); +std::string getMongoShellHelp(StringData name, const moe::OptionSection& options); - /** - * Handle options that should come before validation, such as "help". - * - * Returns false if an option was found that implies we should prematurely exit with success. - */ - bool handlePreValidationMongoShellOptions(const moe::Environment& params, - const std::vector<std::string>& args); +/** + * Handle options that should come before validation, such as "help". + * + * Returns false if an option was found that implies we should prematurely exit with success. + */ +bool handlePreValidationMongoShellOptions(const moe::Environment& params, + const std::vector<std::string>& args); - Status storeMongoShellOptions(const moe::Environment& params, - const std::vector<std::string>& args); +Status storeMongoShellOptions(const moe::Environment& params, const std::vector<std::string>& args); - Status validateMongoShellOptions(const moe::Environment& params); +Status validateMongoShellOptions(const moe::Environment& params); } diff --git a/src/mongo/shell/shell_options_init.cpp b/src/mongo/shell/shell_options_init.cpp index a19268e9f1a..ff6c0792ca5 100644 --- a/src/mongo/shell/shell_options_init.cpp +++ b/src/mongo/shell/shell_options_init.cpp @@ -35,34 +35,33 @@ #include "mongo/util/quick_exit.h" namespace mongo { - MONGO_GENERAL_STARTUP_OPTIONS_REGISTER(MongoShellOptions)(InitializerContext* context) { - return addMongoShellOptions(&moe::startupOptions); - } - - MONGO_STARTUP_OPTIONS_VALIDATE(MongoShellOptions)(InitializerContext* context) { - if (!handlePreValidationMongoShellOptions(moe::startupOptionsParsed, context->args())) { - quickExit(EXIT_SUCCESS); - } - Status ret = moe::startupOptionsParsed.validate(); - if (!ret.isOK()) { - return ret; - } - ret = validateMongoShellOptions(moe::startupOptionsParsed); - if (!ret.isOK()) { - return ret; - } +MONGO_GENERAL_STARTUP_OPTIONS_REGISTER(MongoShellOptions)(InitializerContext* context) { + return addMongoShellOptions(&moe::startupOptions); +} - return Status::OK(); +MONGO_STARTUP_OPTIONS_VALIDATE(MongoShellOptions)(InitializerContext* context) { + if (!handlePreValidationMongoShellOptions(moe::startupOptionsParsed, context->args())) { + quickExit(EXIT_SUCCESS); + } + Status ret = moe::startupOptionsParsed.validate(); + if (!ret.isOK()) { + return ret; } + ret = validateMongoShellOptions(moe::startupOptionsParsed); + if (!ret.isOK()) { + return ret; + } + + return Status::OK(); +} - MONGO_STARTUP_OPTIONS_STORE(MongoShellOptions)(InitializerContext* context) { - Status ret = storeMongoShellOptions(moe::startupOptionsParsed, context->args()); - if (!ret.isOK()) { - std::cerr << ret.toString() << std::endl; - std::cerr << "try '" << context->args()[0] << " --help' for more information" - << std::endl; - quickExit(EXIT_BADOPTIONS); - } - return Status::OK(); +MONGO_STARTUP_OPTIONS_STORE(MongoShellOptions)(InitializerContext* context) { + Status ret = storeMongoShellOptions(moe::startupOptionsParsed, context->args()); + if (!ret.isOK()) { + std::cerr << ret.toString() << std::endl; + std::cerr << "try '" << context->args()[0] << " --help' for more information" << std::endl; + quickExit(EXIT_BADOPTIONS); } + return Status::OK(); +} } diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index b70146559de..81dd1d5225f 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -48,330 +48,328 @@ namespace mongo { - using std::set; - using std::map; - using std::string; - - namespace JSFiles { - extern const JSFile servers; - extern const JSFile shardingtest; - extern const JSFile servers_misc; - extern const JSFile replsettest; - extern const JSFile replsetbridge; - } +using std::set; +using std::map; +using std::string; + +namespace JSFiles { +extern const JSFile servers; +extern const JSFile shardingtest; +extern const JSFile servers_misc; +extern const JSFile replsettest; +extern const JSFile replsetbridge; +} - namespace shell_utils { +namespace shell_utils { - std::string _dbConnect; - std::string _dbAuth; +std::string _dbConnect; +std::string _dbAuth; - const char *argv0 = 0; - void RecordMyLocation( const char *_argv0 ) { argv0 = _argv0; } +const char* argv0 = 0; +void RecordMyLocation(const char* _argv0) { + argv0 = _argv0; +} - // helpers +// helpers - BSONObj makeUndefined() { - BSONObjBuilder b; - b.appendUndefined( "" ); - return b.obj(); - } - const BSONObj undefinedReturn = makeUndefined(); +BSONObj makeUndefined() { + BSONObjBuilder b; + b.appendUndefined(""); + return b.obj(); +} +const BSONObj undefinedReturn = makeUndefined(); - BSONElement singleArg(const BSONObj& args) { - uassert( 12597 , "need to specify 1 argument" , args.nFields() == 1 ); - return args.firstElement(); - } +BSONElement singleArg(const BSONObj& args) { + uassert(12597, "need to specify 1 argument", args.nFields() == 1); + return args.firstElement(); +} - const char* getUserDir() { +const char* getUserDir() { #ifdef _WIN32 - return getenv( "USERPROFILE" ); + return getenv("USERPROFILE"); #else - return getenv( "HOME" ); + return getenv("HOME"); #endif - } +} - // real methods +// real methods - BSONObj Quit(const BSONObj& args, void* data) { - // If no arguments are given first element will be EOO, which - // converts to the integer value 0. - goingAwaySoon(); - int exit_code = int( args.firstElement().number() ); - quickExit(exit_code); - return undefinedReturn; - } +BSONObj Quit(const BSONObj& args, void* data) { + // If no arguments are given first element will be EOO, which + // converts to the integer value 0. + goingAwaySoon(); + int exit_code = int(args.firstElement().number()); + quickExit(exit_code); + return undefinedReturn; +} - BSONObj JSGetMemInfo( const BSONObj& args, void* data ) { - ProcessInfo pi; - uassert( 10258 , "processinfo not supported" , pi.supported() ); +BSONObj JSGetMemInfo(const BSONObj& args, void* data) { + ProcessInfo pi; + uassert(10258, "processinfo not supported", pi.supported()); - BSONObjBuilder e; - e.append( "virtual" , pi.getVirtualMemorySize() ); - e.append( "resident" , pi.getResidentSize() ); + BSONObjBuilder e; + e.append("virtual", pi.getVirtualMemorySize()); + e.append("resident", pi.getResidentSize()); - BSONObjBuilder b; - b.append( "ret" , e.obj() ); + BSONObjBuilder b; + b.append("ret", e.obj()); - return b.obj(); - } + return b.obj(); +} #if !defined(_WIN32) - ThreadLocalValue< unsigned int > _randomSeed; +ThreadLocalValue<unsigned int> _randomSeed; #endif - BSONObj JSSrand( const BSONObj &a, void* data ) { - uassert( 12518, "srand requires a single numeric argument", - a.nFields() == 1 && a.firstElement().isNumber() ); +BSONObj JSSrand(const BSONObj& a, void* data) { + uassert(12518, + "srand requires a single numeric argument", + a.nFields() == 1 && a.firstElement().isNumber()); #if !defined(_WIN32) - _randomSeed.set( static_cast< unsigned int >( a.firstElement().numberLong() ) ); // grab least significant digits + _randomSeed.set( + static_cast<unsigned int>(a.firstElement().numberLong())); // grab least significant digits #else - srand( static_cast< unsigned int >( a.firstElement().numberLong() ) ); + srand(static_cast<unsigned int>(a.firstElement().numberLong())); #endif - return undefinedReturn; - } + return undefinedReturn; +} - BSONObj JSRand( const BSONObj &a, void* data ) { - uassert( 12519, "rand accepts no arguments", a.nFields() == 0 ); - unsigned r; +BSONObj JSRand(const BSONObj& a, void* data) { + uassert(12519, "rand accepts no arguments", a.nFields() == 0); + unsigned r; #if !defined(_WIN32) - r = rand_r( &_randomSeed.getRef() ); + r = rand_r(&_randomSeed.getRef()); #else - r = rand(); + r = rand(); #endif - return BSON( "" << double( r ) / ( double( RAND_MAX ) + 1 ) ); - } + return BSON("" << double(r) / (double(RAND_MAX) + 1)); +} - BSONObj isWindows(const BSONObj& a, void* data) { - uassert( 13006, "isWindows accepts no arguments", a.nFields() == 0 ); +BSONObj isWindows(const BSONObj& a, void* data) { + uassert(13006, "isWindows accepts no arguments", a.nFields() == 0); #ifdef _WIN32 - return BSON( "" << true ); + return BSON("" << true); #else - return BSON( "" << false ); + return BSON("" << false); #endif - } +} - BSONObj isAddressSanitizerActive(const BSONObj& a, void* data) { - bool isSanitized = false; - // See the following for information on how we detect address sanitizer in clang and gcc. - // - // - http://clang.llvm.org/docs/AddressSanitizer.html#has-feature-address-sanitizer - // - https://gcc.gnu.org/ml/gcc-patches/2012-11/msg01827.html - // +BSONObj isAddressSanitizerActive(const BSONObj& a, void* data) { + bool isSanitized = false; +// See the following for information on how we detect address sanitizer in clang and gcc. +// +// - http://clang.llvm.org/docs/AddressSanitizer.html#has-feature-address-sanitizer +// - https://gcc.gnu.org/ml/gcc-patches/2012-11/msg01827.html +// #if defined(__has_feature) #if __has_feature(address_sanitizer) - isSanitized = true; + isSanitized = true; #endif #elif defined(__SANITIZE_ADDRESS__) - isSanitized = true; + isSanitized = true; #endif - return BSON( "" << isSanitized ); - } + return BSON("" << isSanitized); +} - BSONObj getBuildInfo(const BSONObj& a, void* data) { - uassert( 16822, "getBuildInfo accepts no arguments", a.nFields() == 0 ); - BSONObjBuilder b; - appendBuildInfo(b); - return BSON( "" << b.done() ); - } +BSONObj getBuildInfo(const BSONObj& a, void* data) { + uassert(16822, "getBuildInfo accepts no arguments", a.nFields() == 0); + BSONObjBuilder b; + appendBuildInfo(b); + return BSON("" << b.done()); +} - BSONObj isKeyTooLarge(const BSONObj& a, void* data) { - uassert(17428, "keyTooLarge takes exactly 2 arguments", a.nFields() == 2); - BSONObjIterator i(a); - BSONObj index = i.next().Obj(); - BSONObj doc = i.next().Obj(); +BSONObj isKeyTooLarge(const BSONObj& a, void* data) { + uassert(17428, "keyTooLarge takes exactly 2 arguments", a.nFields() == 2); + BSONObjIterator i(a); + BSONObj index = i.next().Obj(); + BSONObj doc = i.next().Obj(); - return BSON("" << isAnyIndexKeyTooLarge(index, doc)); - } + return BSON("" << isAnyIndexKeyTooLarge(index, doc)); +} - BSONObj validateIndexKey(const BSONObj& a, void* data) { - BSONObj key = a[0].Obj(); - Status indexValid = validateKeyPattern(key); - if (!indexValid.isOK()) { - return BSON("" << BSON("ok" << false << "type" - << indexValid.codeString() << "errmsg" << indexValid.reason())); - } - return BSON("" << BSON("ok" << true)); - } +BSONObj validateIndexKey(const BSONObj& a, void* data) { + BSONObj key = a[0].Obj(); + Status indexValid = validateKeyPattern(key); + if (!indexValid.isOK()) { + return BSON("" << BSON("ok" << false << "type" << indexValid.codeString() << "errmsg" + << indexValid.reason())); + } + return BSON("" << BSON("ok" << true)); +} - BSONObj replMonitorStats(const BSONObj& a, void* data) { - uassert(17134, "replMonitorStats requires a single string argument (the ReplSet name)", - a.nFields() == 1 && a.firstElement().type() == String); +BSONObj replMonitorStats(const BSONObj& a, void* data) { + uassert(17134, + "replMonitorStats requires a single string argument (the ReplSet name)", + a.nFields() == 1 && a.firstElement().type() == String); - ReplicaSetMonitorPtr rsm = ReplicaSetMonitor::get(a.firstElement().valuestrsafe()); - if (!rsm) { - return BSON("" << "no ReplSetMonitor exists by that name"); - } + ReplicaSetMonitorPtr rsm = ReplicaSetMonitor::get(a.firstElement().valuestrsafe()); + if (!rsm) { + return BSON("" + << "no ReplSetMonitor exists by that name"); + } - BSONObjBuilder result; - rsm->appendInfo(result); - return result.obj(); - } + BSONObjBuilder result; + rsm->appendInfo(result); + return result.obj(); +} - BSONObj useWriteCommandsDefault(const BSONObj& a, void* data) { - return BSON("" << shellGlobalParams.useWriteCommandsDefault); - } +BSONObj useWriteCommandsDefault(const BSONObj& a, void* data) { + return BSON("" << shellGlobalParams.useWriteCommandsDefault); +} - BSONObj writeMode(const BSONObj&, void*) { - return BSON("" << shellGlobalParams.writeMode); - } +BSONObj writeMode(const BSONObj&, void*) { + return BSON("" << shellGlobalParams.writeMode); +} - BSONObj readMode(const BSONObj&, void*) { - return BSON("" << shellGlobalParams.readMode); - } +BSONObj readMode(const BSONObj&, void*) { + return BSON("" << shellGlobalParams.readMode); +} - BSONObj interpreterVersion(const BSONObj& a, void* data) { - uassert( 16453, "interpreterVersion accepts no arguments", a.nFields() == 0 ); - return BSON( "" << globalScriptEngine->getInterpreterVersionString() ); - } +BSONObj interpreterVersion(const BSONObj& a, void* data) { + uassert(16453, "interpreterVersion accepts no arguments", a.nFields() == 0); + return BSON("" << globalScriptEngine->getInterpreterVersionString()); +} - void installShellUtils( Scope& scope ) { - scope.injectNative( "quit", Quit ); - scope.injectNative( "getMemInfo" , JSGetMemInfo ); - scope.injectNative( "_replMonitorStats" , replMonitorStats ); - scope.injectNative( "_srand" , JSSrand ); - scope.injectNative( "_rand" , JSRand ); - scope.injectNative( "_isWindows" , isWindows ); - scope.injectNative( "_isAddressSanitizerActive", isAddressSanitizerActive ); - scope.injectNative( "interpreterVersion", interpreterVersion ); - scope.injectNative( "getBuildInfo", getBuildInfo ); - scope.injectNative( "isKeyTooLarge", isKeyTooLarge ); - scope.injectNative( "validateIndexKey", validateIndexKey ); +void installShellUtils(Scope& scope) { + scope.injectNative("quit", Quit); + scope.injectNative("getMemInfo", JSGetMemInfo); + scope.injectNative("_replMonitorStats", replMonitorStats); + scope.injectNative("_srand", JSSrand); + scope.injectNative("_rand", JSRand); + scope.injectNative("_isWindows", isWindows); + scope.injectNative("_isAddressSanitizerActive", isAddressSanitizerActive); + scope.injectNative("interpreterVersion", interpreterVersion); + scope.injectNative("getBuildInfo", getBuildInfo); + scope.injectNative("isKeyTooLarge", isKeyTooLarge); + scope.injectNative("validateIndexKey", validateIndexKey); #ifndef MONGO_SAFE_SHELL - //can't launch programs - installShellUtilsLauncher( scope ); - installShellUtilsExtended( scope ); + // can't launch programs + installShellUtilsLauncher(scope); + installShellUtilsExtended(scope); #endif - } +} - void initScope( Scope &scope ) { - // Need to define this method before JSFiles::utils is executed. - scope.injectNative("_useWriteCommandsDefault", useWriteCommandsDefault); - scope.injectNative("_writeMode", writeMode); - scope.injectNative("_readMode", readMode); - scope.externalSetup(); - mongo::shell_utils::installShellUtils( scope ); - scope.execSetup(JSFiles::servers); - scope.execSetup(JSFiles::shardingtest); - scope.execSetup(JSFiles::servers_misc); - scope.execSetup(JSFiles::replsettest); - scope.execSetup(JSFiles::replsetbridge); - - scope.injectNative("benchRun", BenchRunner::benchRunSync); - scope.injectNative("benchRunSync", BenchRunner::benchRunSync); - scope.injectNative("benchStart", BenchRunner::benchStart); - scope.injectNative("benchFinish", BenchRunner::benchFinish); - - if ( !_dbConnect.empty() ) { - uassert( 12513, "connect failed", scope.exec( _dbConnect , "(connect)" , false , true , false ) ); - } - if ( !_dbAuth.empty() ) { - uassert( 12514, "login failed", scope.exec( _dbAuth , "(auth)" , true , true , false ) ); - } - } +void initScope(Scope& scope) { + // Need to define this method before JSFiles::utils is executed. + scope.injectNative("_useWriteCommandsDefault", useWriteCommandsDefault); + scope.injectNative("_writeMode", writeMode); + scope.injectNative("_readMode", readMode); + scope.externalSetup(); + mongo::shell_utils::installShellUtils(scope); + scope.execSetup(JSFiles::servers); + scope.execSetup(JSFiles::shardingtest); + scope.execSetup(JSFiles::servers_misc); + scope.execSetup(JSFiles::replsettest); + scope.execSetup(JSFiles::replsetbridge); + + scope.injectNative("benchRun", BenchRunner::benchRunSync); + scope.injectNative("benchRunSync", BenchRunner::benchRunSync); + scope.injectNative("benchStart", BenchRunner::benchStart); + scope.injectNative("benchFinish", BenchRunner::benchFinish); + + if (!_dbConnect.empty()) { + uassert(12513, "connect failed", scope.exec(_dbConnect, "(connect)", false, true, false)); + } + if (!_dbAuth.empty()) { + uassert(12514, "login failed", scope.exec(_dbAuth, "(auth)", true, true, false)); + } +} - Prompter::Prompter( const string &prompt ) : - _prompt( prompt ), - _confirmed() { - } +Prompter::Prompter(const string& prompt) : _prompt(prompt), _confirmed() {} - bool Prompter::confirm() { - if ( _confirmed ) { - return true; - } +bool Prompter::confirm() { + if (_confirmed) { + return true; + } - // The printf and scanf functions provide thread safe i/o. - - printf( "\n%s (y/n): ", _prompt.c_str() ); - - char yn = '\0'; - int nScanMatches = scanf( "%c", &yn ); - bool matchedY = ( nScanMatches == 1 && ( yn == 'y' || yn == 'Y' ) ); - - return _confirmed = matchedY; - } + // The printf and scanf functions provide thread safe i/o. - ConnectionRegistry::ConnectionRegistry() = default; + printf("\n%s (y/n): ", _prompt.c_str()); - void ConnectionRegistry::registerConnection( DBClientWithCommands &client ) { - BSONObj info; - if ( client.runCommand( "admin", BSON( "whatsmyuri" << 1 ), info ) ) { - string connstr = dynamic_cast<DBClientBase&>( client ).getServerAddress(); - stdx::lock_guard<stdx::mutex> lk( _mutex ); - _connectionUris[ connstr ].insert( info[ "you" ].str() ); - } - } + char yn = '\0'; + int nScanMatches = scanf("%c", &yn); + bool matchedY = (nScanMatches == 1 && (yn == 'y' || yn == 'Y')); - void ConnectionRegistry::killOperationsOnAllConnections( bool withPrompt ) const { - Prompter prompter( "do you want to kill the current op(s) on the server?" ); - stdx::lock_guard<stdx::mutex> lk( _mutex ); - for( map<string,set<string> >::const_iterator i = _connectionUris.begin(); - i != _connectionUris.end(); ++i ) { + return _confirmed = matchedY; +} - auto status = ConnectionString::parse(i->first); - if (!status.isOK()) { - continue; - } +ConnectionRegistry::ConnectionRegistry() = default; - const ConnectionString cs(status.getValue()); +void ConnectionRegistry::registerConnection(DBClientWithCommands& client) { + BSONObj info; + if (client.runCommand("admin", BSON("whatsmyuri" << 1), info)) { + string connstr = dynamic_cast<DBClientBase&>(client).getServerAddress(); + stdx::lock_guard<stdx::mutex> lk(_mutex); + _connectionUris[connstr].insert(info["you"].str()); + } +} - string errmsg; - std::unique_ptr<DBClientWithCommands> conn( cs.connect( errmsg ) ); - if ( !conn ) { - continue; - } - - const set<string>& uris = i->second; - - BSONObj currentOpRes; - conn->runPseudoCommand("admin", - "currentOp", - "$cmd.sys.inprog", {}, currentOpRes); - auto inprog = currentOpRes["inprog"].embeddedObject(); - BSONForEach( op, inprog ) { - if ( uris.count( op[ "client" ].String() ) ) { - if ( !withPrompt || prompter.confirm() ) { - BSONObjBuilder cmdBob; - BSONObj info; - cmdBob.append("op", op["opid"]); - auto cmdArgs = cmdBob.done(); - conn->runPseudoCommand("admin", "killOp", "$cmd.sys.killop", - cmdArgs, info); - } - else { - return; - } - } - } - } +void ConnectionRegistry::killOperationsOnAllConnections(bool withPrompt) const { + Prompter prompter("do you want to kill the current op(s) on the server?"); + stdx::lock_guard<stdx::mutex> lk(_mutex); + for (map<string, set<string>>::const_iterator i = _connectionUris.begin(); + i != _connectionUris.end(); + ++i) { + auto status = ConnectionString::parse(i->first); + if (!status.isOK()) { + continue; + } + + const ConnectionString cs(status.getValue()); + + string errmsg; + std::unique_ptr<DBClientWithCommands> conn(cs.connect(errmsg)); + if (!conn) { + continue; } - - ConnectionRegistry connectionRegistry; - bool _nokillop = false; - void onConnect( DBClientWithCommands &c ) { - if ( _nokillop ) { - return; + const set<string>& uris = i->second; + + BSONObj currentOpRes; + conn->runPseudoCommand("admin", "currentOp", "$cmd.sys.inprog", {}, currentOpRes); + auto inprog = currentOpRes["inprog"].embeddedObject(); + BSONForEach(op, inprog) { + if (uris.count(op["client"].String())) { + if (!withPrompt || prompter.confirm()) { + BSONObjBuilder cmdBob; + BSONObj info; + cmdBob.append("op", op["opid"]); + auto cmdArgs = cmdBob.done(); + conn->runPseudoCommand("admin", "killOp", "$cmd.sys.killop", cmdArgs, info); + } else { + return; + } } - c.setClientRPCProtocols(shellGlobalParams.rpcProtocols); - connectionRegistry.registerConnection( c ); } + } +} - bool fileExists(const std::string& file) { - try { +ConnectionRegistry connectionRegistry; + +bool _nokillop = false; +void onConnect(DBClientWithCommands& c) { + if (_nokillop) { + return; + } + c.setClientRPCProtocols(shellGlobalParams.rpcProtocols); + connectionRegistry.registerConnection(c); +} + +bool fileExists(const std::string& file) { + try { #ifdef _WIN32 - boost::filesystem::path p(toWideString(file.c_str())); + boost::filesystem::path p(toWideString(file.c_str())); #else - boost::filesystem::path p(file); + boost::filesystem::path p(file); #endif - return boost::filesystem::exists(p); - } - catch ( ... ) { - return false; - } - } + return boost::filesystem::exists(p); + } catch (...) { + return false; + } +} - stdx::mutex &mongoProgramOutputMutex(*(new stdx::mutex())); - } +stdx::mutex& mongoProgramOutputMutex(*(new stdx::mutex())); +} } diff --git a/src/mongo/shell/shell_utils.h b/src/mongo/shell/shell_utils.h index ee773e9e029..b00b10f2d9c 100644 --- a/src/mongo/shell/shell_utils.h +++ b/src/mongo/shell/shell_utils.h @@ -35,56 +35,58 @@ namespace mongo { - class Scope; - class DBClientWithCommands; - - namespace shell_utils { - - extern std::string _dbConnect; - extern std::string _dbAuth; - extern bool _nokillop; - - void RecordMyLocation( const char *_argv0 ); - void installShellUtils( Scope& scope ); - - void initScope( Scope &scope ); - void onConnect( DBClientWithCommands &c ); - - const char* getUserDir(); - - BSONElement singleArg(const BSONObj& args); - extern const BSONObj undefinedReturn; - - /** Prompt for confirmation from cin. */ - class Prompter { - public: - Prompter( const std::string &prompt ); - /** @return prompted confirmation or cached confirmation. */ - bool confirm(); - private: - const std::string _prompt; - bool _confirmed; - }; - - /** Registry of server connections. */ - class ConnectionRegistry { - public: - ConnectionRegistry(); - void registerConnection( DBClientWithCommands &client ); - void killOperationsOnAllConnections( bool withPrompt ) const; - private: - std::map<std::string,std::set<std::string> > _connectionUris; - mutable stdx::mutex _mutex; - }; - - extern ConnectionRegistry connectionRegistry; - - // This mutex helps the shell serialize output on exit, to avoid deadlocks at shutdown. So - // it also protects the global dbexitCalled. - extern stdx::mutex &mongoProgramOutputMutex; - - // Helper to tell if a file exists cross platform - // TODO: Remove this when we have a cross platform file utility library - bool fileExists(const std::string& file); - } +class Scope; +class DBClientWithCommands; + +namespace shell_utils { + +extern std::string _dbConnect; +extern std::string _dbAuth; +extern bool _nokillop; + +void RecordMyLocation(const char* _argv0); +void installShellUtils(Scope& scope); + +void initScope(Scope& scope); +void onConnect(DBClientWithCommands& c); + +const char* getUserDir(); + +BSONElement singleArg(const BSONObj& args); +extern const BSONObj undefinedReturn; + +/** Prompt for confirmation from cin. */ +class Prompter { +public: + Prompter(const std::string& prompt); + /** @return prompted confirmation or cached confirmation. */ + bool confirm(); + +private: + const std::string _prompt; + bool _confirmed; +}; + +/** Registry of server connections. */ +class ConnectionRegistry { +public: + ConnectionRegistry(); + void registerConnection(DBClientWithCommands& client); + void killOperationsOnAllConnections(bool withPrompt) const; + +private: + std::map<std::string, std::set<std::string>> _connectionUris; + mutable stdx::mutex _mutex; +}; + +extern ConnectionRegistry connectionRegistry; + +// This mutex helps the shell serialize output on exit, to avoid deadlocks at shutdown. So +// it also protects the global dbexitCalled. +extern stdx::mutex& mongoProgramOutputMutex; + +// Helper to tell if a file exists cross platform +// TODO: Remove this when we have a cross platform file utility library +bool fileExists(const std::string& file); +} } diff --git a/src/mongo/shell/shell_utils_extended.cpp b/src/mongo/shell/shell_utils_extended.cpp index 221d91b79fd..64cfc5773f9 100644 --- a/src/mongo/shell/shell_utils_extended.cpp +++ b/src/mongo/shell/shell_utils_extended.cpp @@ -47,215 +47,210 @@ namespace mongo { - using std::ifstream; - using std::string; - using std::stringstream; - - /** - * These utilities are thread safe but do not provide mutually exclusive access to resources - * identified by the caller. Dependent filesystem paths should not be accessed by different - * threads. - */ - namespace shell_utils { - - BSONObj listFiles(const BSONObj& _args, void* data) { - BSONObj cd = BSON( "0" << "." ); - BSONObj args = _args.isEmpty() ? cd : _args; - - uassert( 10257 , "need to specify 1 argument to listFiles" , args.nFields() == 1 ); - - BSONArrayBuilder lst; - - string rootname = args.firstElement().valuestrsafe(); - boost::filesystem::path root( rootname ); - stringstream ss; - ss << "listFiles: no such directory: " << rootname; - string msg = ss.str(); - uassert( 12581, msg.c_str(), boost::filesystem::exists( root ) ); - - boost::filesystem::directory_iterator end; - boost::filesystem::directory_iterator i( root); - - while ( i != end ) { - boost::filesystem::path p = *i; - BSONObjBuilder b; - b << "name" << p.generic_string(); - b << "baseName" << p.filename().generic_string(); - b.appendBool( "isDirectory", is_directory( p ) ); - if ( ! boost::filesystem::is_directory( p ) ) { - try { - b.append( "size" , (double)boost::filesystem::file_size( p ) ); - } - catch ( ... ) { - i++; - continue; - } - } - - lst.append( b.obj() ); +using std::ifstream; +using std::string; +using std::stringstream; + +/** + * These utilities are thread safe but do not provide mutually exclusive access to resources + * identified by the caller. Dependent filesystem paths should not be accessed by different + * threads. + */ +namespace shell_utils { + +BSONObj listFiles(const BSONObj& _args, void* data) { + BSONObj cd = BSON("0" + << "."); + BSONObj args = _args.isEmpty() ? cd : _args; + + uassert(10257, "need to specify 1 argument to listFiles", args.nFields() == 1); + + BSONArrayBuilder lst; + + string rootname = args.firstElement().valuestrsafe(); + boost::filesystem::path root(rootname); + stringstream ss; + ss << "listFiles: no such directory: " << rootname; + string msg = ss.str(); + uassert(12581, msg.c_str(), boost::filesystem::exists(root)); + + boost::filesystem::directory_iterator end; + boost::filesystem::directory_iterator i(root); + + while (i != end) { + boost::filesystem::path p = *i; + BSONObjBuilder b; + b << "name" << p.generic_string(); + b << "baseName" << p.filename().generic_string(); + b.appendBool("isDirectory", is_directory(p)); + if (!boost::filesystem::is_directory(p)) { + try { + b.append("size", (double)boost::filesystem::file_size(p)); + } catch (...) { i++; + continue; } - - BSONObjBuilder ret; - ret.appendArray( "", lst.done() ); - return ret.obj(); } - BSONObj ls(const BSONObj& args, void* data) { - BSONArrayBuilder ret; - BSONObj o = listFiles(args, data); - if( !o.isEmpty() ) { - for( BSONObj::iterator i = o.firstElement().Obj().begin(); i.more(); ) { - BSONObj f = i.next().Obj(); - string name = f["name"].String(); - if( f["isDirectory"].trueValue() ) { - name += '/'; - } - ret << name; - } + lst.append(b.obj()); + i++; + } + + BSONObjBuilder ret; + ret.appendArray("", lst.done()); + return ret.obj(); +} + +BSONObj ls(const BSONObj& args, void* data) { + BSONArrayBuilder ret; + BSONObj o = listFiles(args, data); + if (!o.isEmpty()) { + for (BSONObj::iterator i = o.firstElement().Obj().begin(); i.more();) { + BSONObj f = i.next().Obj(); + string name = f["name"].String(); + if (f["isDirectory"].trueValue()) { + name += '/'; } - return BSON( "" << ret.arr() ); + ret << name; } + } + return BSON("" << ret.arr()); +} - /** Set process wide current working directory. */ - BSONObj cd(const BSONObj& args, void* data) { - uassert(16830, - "cd requires one argument -- cd(directory)", - args.nFields() == 1); - uassert(16831, - "cd requires a string argument -- cd(directory)", - args.firstElement().type() == String); +/** Set process wide current working directory. */ +BSONObj cd(const BSONObj& args, void* data) { + uassert(16830, "cd requires one argument -- cd(directory)", args.nFields() == 1); + uassert(16831, + "cd requires a string argument -- cd(directory)", + args.firstElement().type() == String); #if defined(_WIN32) - std::wstring dir = toWideString(args.firstElement().String().c_str()); - if (SetCurrentDirectoryW(dir.c_str())) { - return BSONObj(); - } + std::wstring dir = toWideString(args.firstElement().String().c_str()); + if (SetCurrentDirectoryW(dir.c_str())) { + return BSONObj(); + } #else - std::string dir = args.firstElement().String(); - if (chdir(dir.c_str()) == 0) { - return BSONObj(); - } + std::string dir = args.firstElement().String(); + if (chdir(dir.c_str()) == 0) { + return BSONObj(); + } #endif - uasserted(16832, - mongoutils::str::stream() << "cd command failed: " - << errnoWithDescription()); - return BSONObj(); - } + uasserted(16832, mongoutils::str::stream() << "cd command failed: " << errnoWithDescription()); + return BSONObj(); +} - BSONObj pwd(const BSONObj&, void* data) { - boost::filesystem::path p = boost::filesystem::current_path(); - return BSON( "" << p.string() ); - } +BSONObj pwd(const BSONObj&, void* data) { + boost::filesystem::path p = boost::filesystem::current_path(); + return BSON("" << p.string()); +} - BSONObj hostname(const BSONObj&, void* data) { - return BSON( "" << getHostName() ); - } +BSONObj hostname(const BSONObj&, void* data) { + return BSON("" << getHostName()); +} - const int CANT_OPEN_FILE = 13300; - - BSONObj cat(const BSONObj& args, void* data) { - BSONElement e = singleArg(args); - stringstream ss; - ifstream f(e.valuestrsafe()); - uassert(CANT_OPEN_FILE, "couldn't open file", f.is_open() ); - - std::streamsize sz = 0; - while( 1 ) { - char ch = 0; - // slow...maybe change one day - f.get(ch); - if( ch == 0 ) break; - ss << ch; - sz += 1; - uassert(13301, "cat() : file to big to load as a variable", sz < 1024 * 1024 * 16); - } - return BSON( "" << ss.str() ); - } +const int CANT_OPEN_FILE = 13300; + +BSONObj cat(const BSONObj& args, void* data) { + BSONElement e = singleArg(args); + stringstream ss; + ifstream f(e.valuestrsafe()); + uassert(CANT_OPEN_FILE, "couldn't open file", f.is_open()); + + std::streamsize sz = 0; + while (1) { + char ch = 0; + // slow...maybe change one day + f.get(ch); + if (ch == 0) + break; + ss << ch; + sz += 1; + uassert(13301, "cat() : file to big to load as a variable", sz < 1024 * 1024 * 16); + } + return BSON("" << ss.str()); +} - BSONObj md5sumFile(const BSONObj& args, void* data) { - BSONElement e = singleArg(args); - stringstream ss; - FILE* f = fopen(e.valuestrsafe(), "rb"); - uassert(CANT_OPEN_FILE, "couldn't open file", f ); - ON_BLOCK_EXIT(fclose, f); - - md5digest d; - md5_state_t st; - md5_init(&st); - - enum {BUFLEN = 4*1024}; - char buffer[BUFLEN]; - int bytes_read; - while( (bytes_read = fread(buffer, 1, BUFLEN, f)) ) { - md5_append( &st , (const md5_byte_t*)(buffer) , bytes_read ); - } +BSONObj md5sumFile(const BSONObj& args, void* data) { + BSONElement e = singleArg(args); + stringstream ss; + FILE* f = fopen(e.valuestrsafe(), "rb"); + uassert(CANT_OPEN_FILE, "couldn't open file", f); + ON_BLOCK_EXIT(fclose, f); + + md5digest d; + md5_state_t st; + md5_init(&st); + + enum { BUFLEN = 4 * 1024 }; + char buffer[BUFLEN]; + int bytes_read; + while ((bytes_read = fread(buffer, 1, BUFLEN, f))) { + md5_append(&st, (const md5_byte_t*)(buffer), bytes_read); + } - md5_finish(&st, d); - return BSON( "" << digestToString( d ) ); - } + md5_finish(&st, d); + return BSON("" << digestToString(d)); +} - BSONObj mkdir(const BSONObj& args, void* data) { - uassert(16833, - "mkdir requires one argument -- mkdir(directory)", - args.nFields() == 1); - uassert(16834, - "mkdir requires a string argument -- mkdir(directory)", - args.firstElement().type() == String); - boost::filesystem::create_directories(args.firstElement().String()); - return BSON( "" << true ); - } +BSONObj mkdir(const BSONObj& args, void* data) { + uassert(16833, "mkdir requires one argument -- mkdir(directory)", args.nFields() == 1); + uassert(16834, + "mkdir requires a string argument -- mkdir(directory)", + args.firstElement().type() == String); + boost::filesystem::create_directories(args.firstElement().String()); + return BSON("" << true); +} - BSONObj removeFile(const BSONObj& args, void* data) { - BSONElement e = singleArg(args); - bool found = false; +BSONObj removeFile(const BSONObj& args, void* data) { + BSONElement e = singleArg(args); + bool found = false; - boost::filesystem::path root( e.valuestrsafe() ); - if ( boost::filesystem::exists( root ) ) { - found = true; - boost::filesystem::remove_all( root ); - } + boost::filesystem::path root(e.valuestrsafe()); + if (boost::filesystem::exists(root)) { + found = true; + boost::filesystem::remove_all(root); + } - BSONObjBuilder b; - b.appendBool( "removed" , found ); - return b.obj(); - } + BSONObjBuilder b; + b.appendBool("removed", found); + return b.obj(); +} - /** - * @param args - [ source, destination ] - * copies file 'source' to 'destination'. Errors if the 'destination' file already exists. - */ - BSONObj copyFile(const BSONObj& args, void* data) { - uassert(13619, "copyFile takes 2 arguments", args.nFields() == 2); +/** + * @param args - [ source, destination ] + * copies file 'source' to 'destination'. Errors if the 'destination' file already exists. + */ +BSONObj copyFile(const BSONObj& args, void* data) { + uassert(13619, "copyFile takes 2 arguments", args.nFields() == 2); - BSONObjIterator it(args); - const std::string source = it.next().str(); - const std::string destination = it.next().str(); + BSONObjIterator it(args); + const std::string source = it.next().str(); + const std::string destination = it.next().str(); - boost::filesystem::copy_file(source, destination); + boost::filesystem::copy_file(source, destination); - return undefinedReturn; - } + return undefinedReturn; +} - BSONObj getHostName(const BSONObj& a, void* data) { - uassert( 13411, "getHostName accepts no arguments", a.nFields() == 0 ); - char buf[260]; // HOST_NAME_MAX is usually 255 - verify(gethostname(buf, 260) == 0); - buf[259] = '\0'; - return BSON("" << buf); - } +BSONObj getHostName(const BSONObj& a, void* data) { + uassert(13411, "getHostName accepts no arguments", a.nFields() == 0); + char buf[260]; // HOST_NAME_MAX is usually 255 + verify(gethostname(buf, 260) == 0); + buf[259] = '\0'; + return BSON("" << buf); +} - void installShellUtilsExtended( Scope& scope ) { - scope.injectNative( "getHostName" , getHostName ); - scope.injectNative( "removeFile" , removeFile ); - scope.injectNative( "copyFile" , copyFile ); - scope.injectNative( "listFiles" , listFiles ); - scope.injectNative( "ls" , ls ); - scope.injectNative( "pwd", pwd ); - scope.injectNative( "cd", cd ); - scope.injectNative( "cat", cat ); - scope.injectNative( "hostname", hostname); - scope.injectNative( "md5sumFile", md5sumFile ); - scope.injectNative( "mkdir" , mkdir ); - } - } +void installShellUtilsExtended(Scope& scope) { + scope.injectNative("getHostName", getHostName); + scope.injectNative("removeFile", removeFile); + scope.injectNative("copyFile", copyFile); + scope.injectNative("listFiles", listFiles); + scope.injectNative("ls", ls); + scope.injectNative("pwd", pwd); + scope.injectNative("cd", cd); + scope.injectNative("cat", cat); + scope.injectNative("hostname", hostname); + scope.injectNative("md5sumFile", md5sumFile); + scope.injectNative("mkdir", mkdir); +} +} } diff --git a/src/mongo/shell/shell_utils_extended.h b/src/mongo/shell/shell_utils_extended.h index dc4c2d944e7..471fe98a73e 100644 --- a/src/mongo/shell/shell_utils_extended.h +++ b/src/mongo/shell/shell_utils_extended.h @@ -32,9 +32,9 @@ namespace mongo { - class Scope; - - namespace shell_utils { - void installShellUtilsExtended( Scope& scope ); - } +class Scope; + +namespace shell_utils { +void installShellUtilsExtended(Scope& scope); +} } diff --git a/src/mongo/shell/shell_utils_launcher.cpp b/src/mongo/shell/shell_utils_launcher.cpp index 6d4991e999a..ddba7fc8e28 100644 --- a/src/mongo/shell/shell_utils_launcher.cpp +++ b/src/mongo/shell/shell_utils_launcher.cpp @@ -39,15 +39,15 @@ #include <vector> #ifdef _WIN32 -# include <fcntl.h> -# include <io.h> -# define SIGKILL 9 +#include <fcntl.h> +#include <io.h> +#define SIGKILL 9 #else -# include <sys/socket.h> -# include <netinet/in.h> -# include <signal.h> -# include <sys/stat.h> -# include <sys/wait.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/wait.h> #endif #include "mongo/client/dbclientinterface.h" @@ -61,160 +61,166 @@ namespace mongo { - using std::unique_ptr; - using std::cout; - using std::endl; - using std::make_pair; - using std::map; - using std::pair; - using std::string; - using std::stringstream; - using std::vector; +using std::unique_ptr; +using std::cout; +using std::endl; +using std::make_pair; +using std::map; +using std::pair; +using std::string; +using std::stringstream; +using std::vector; - extern bool dbexitCalled; +extern bool dbexitCalled; #ifdef _WIN32 - inline int close(int fd) { return _close(fd); } - inline int read(int fd, void* buf, size_t size) { return _read(fd, buf, size); } - inline int pipe(int fds[2]) { return _pipe(fds, 4096, _O_TEXT | _O_NOINHERIT); } +inline int close(int fd) { + return _close(fd); +} +inline int read(int fd, void* buf, size_t size) { + return _read(fd, buf, size); +} +inline int pipe(int fds[2]) { + return _pipe(fds, 4096, _O_TEXT | _O_NOINHERIT); +} #endif - /** - * These utilities are thread safe but do not provide mutually exclusive access to resources - * identified by the caller. Resources identified by a pid or port should not be accessed - * by different threads. Dependent filesystem paths should not be accessed by different - * threads. - */ - namespace shell_utils { +/** + * These utilities are thread safe but do not provide mutually exclusive access to resources + * identified by the caller. Resources identified by a pid or port should not be accessed + * by different threads. Dependent filesystem paths should not be accessed by different + * threads. + */ +namespace shell_utils { - ProgramOutputMultiplexer programOutputLogger; +ProgramOutputMultiplexer programOutputLogger; - bool ProgramRegistry::isPortRegistered( int port ) const { - stdx::lock_guard<stdx::recursive_mutex> lk( _mutex ); - return _ports.count( port ) == 1; - } +bool ProgramRegistry::isPortRegistered(int port) const { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + return _ports.count(port) == 1; +} - ProcessId ProgramRegistry::pidForPort( int port ) const { - stdx::lock_guard<stdx::recursive_mutex> lk( _mutex ); - verify( isPortRegistered( port ) ); - return _ports.find( port )->second.first; - } +ProcessId ProgramRegistry::pidForPort(int port) const { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + verify(isPortRegistered(port)); + return _ports.find(port)->second.first; +} - int ProgramRegistry::portForPid(ProcessId pid) const { - stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); - for (map<int, pair<ProcessId, int> >::const_iterator it = _ports.begin(); - it != _ports.end(); ++it) - { - if (it->second.first == pid) return it->first; - } +int ProgramRegistry::portForPid(ProcessId pid) const { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + for (map<int, pair<ProcessId, int>>::const_iterator it = _ports.begin(); it != _ports.end(); + ++it) { + if (it->second.first == pid) + return it->first; + } - return -1; - } + return -1; +} - void ProgramRegistry::registerPort( int port, ProcessId pid, int output ) { - stdx::lock_guard<stdx::recursive_mutex> lk( _mutex ); - verify( !isPortRegistered( port ) ); - _ports.insert( make_pair( port, make_pair( pid, output ) ) ); - } +void ProgramRegistry::registerPort(int port, ProcessId pid, int output) { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + verify(!isPortRegistered(port)); + _ports.insert(make_pair(port, make_pair(pid, output))); +} - void ProgramRegistry::deletePort( int port ) { - stdx::lock_guard<stdx::recursive_mutex> lk( _mutex ); - if ( !isPortRegistered( port ) ) { - return; - } - close( _ports.find( port )->second.second ); - _ports.erase( port ); - } +void ProgramRegistry::deletePort(int port) { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + if (!isPortRegistered(port)) { + return; + } + close(_ports.find(port)->second.second); + _ports.erase(port); +} - void ProgramRegistry::getRegisteredPorts( vector<int> &ports ) { - stdx::lock_guard<stdx::recursive_mutex> lk( _mutex ); - for( map<int,pair<ProcessId,int> >::const_iterator i = _ports.begin(); i != _ports.end(); - ++i ) { - ports.push_back( i->first ); - } - } +void ProgramRegistry::getRegisteredPorts(vector<int>& ports) { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + for (map<int, pair<ProcessId, int>>::const_iterator i = _ports.begin(); i != _ports.end(); + ++i) { + ports.push_back(i->first); + } +} - bool ProgramRegistry::isPidRegistered( ProcessId pid ) const { - stdx::lock_guard<stdx::recursive_mutex> lk( _mutex ); - return _pids.count( pid ) == 1; - } +bool ProgramRegistry::isPidRegistered(ProcessId pid) const { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + return _pids.count(pid) == 1; +} - void ProgramRegistry::registerPid( ProcessId pid, int output ) { - stdx::lock_guard<stdx::recursive_mutex> lk( _mutex ); - verify( !isPidRegistered( pid ) ); - _pids.insert( make_pair( pid, output ) ); - } +void ProgramRegistry::registerPid(ProcessId pid, int output) { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + verify(!isPidRegistered(pid)); + _pids.insert(make_pair(pid, output)); +} - void ProgramRegistry::deletePid(ProcessId pid) { - stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); - if (!isPidRegistered(pid)) { - int port = portForPid(pid); - if (port < 0) return; - deletePort(port); - return; - } - close(_pids.find(pid)->second); - _pids.erase(pid); - } +void ProgramRegistry::deletePid(ProcessId pid) { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + if (!isPidRegistered(pid)) { + int port = portForPid(pid); + if (port < 0) + return; + deletePort(port); + return; + } + close(_pids.find(pid)->second); + _pids.erase(pid); +} - void ProgramRegistry::getRegisteredPids( vector<ProcessId> &pids ) { - stdx::lock_guard<stdx::recursive_mutex> lk( _mutex ); - for( map<ProcessId,int>::const_iterator i = _pids.begin(); i != _pids.end(); ++i ) { - pids.push_back( i->first ); - } - } +void ProgramRegistry::getRegisteredPids(vector<ProcessId>& pids) { + stdx::lock_guard<stdx::recursive_mutex> lk(_mutex); + for (map<ProcessId, int>::const_iterator i = _pids.begin(); i != _pids.end(); ++i) { + pids.push_back(i->first); + } +} - ProgramRegistry ®istry = *( new ProgramRegistry() ); +ProgramRegistry& registry = *(new ProgramRegistry()); - void goingAwaySoon() { - stdx::lock_guard<stdx::mutex> lk( mongoProgramOutputMutex ); - mongo::dbexitCalled = true; - } +void goingAwaySoon() { + stdx::lock_guard<stdx::mutex> lk(mongoProgramOutputMutex); + mongo::dbexitCalled = true; +} - void ProgramOutputMultiplexer::appendLine( int port, ProcessId pid, const char *line ) { - stdx::lock_guard<stdx::mutex> lk( mongoProgramOutputMutex ); - if( mongo::dbexitCalled ) throw "program is terminating"; - stringstream buf; - if ( port > 0 ) - buf << " m" << port << "| " << line; - else - buf << "sh" << pid << "| " << line; - printf( "%s\n", buf.str().c_str() ); // cout << buf.str() << endl; - fflush(stdout); // not implicit if stdout isn't directly outputting to a console. - _buffer << buf.str() << endl; - } +void ProgramOutputMultiplexer::appendLine(int port, ProcessId pid, const char* line) { + stdx::lock_guard<stdx::mutex> lk(mongoProgramOutputMutex); + if (mongo::dbexitCalled) + throw "program is terminating"; + stringstream buf; + if (port > 0) + buf << " m" << port << "| " << line; + else + buf << "sh" << pid << "| " << line; + printf("%s\n", buf.str().c_str()); // cout << buf.str() << endl; + fflush(stdout); // not implicit if stdout isn't directly outputting to a console. + _buffer << buf.str() << endl; +} - string ProgramOutputMultiplexer::str() const { - stdx::lock_guard<stdx::mutex> lk( mongoProgramOutputMutex ); - string ret = _buffer.str(); - size_t len = ret.length(); - if ( len > 100000 ) { - ret = ret.substr( len - 100000, 100000 ); - } - return ret; - } - - void ProgramOutputMultiplexer::clear() { - stdx::lock_guard<stdx::mutex> lk( mongoProgramOutputMutex ); - _buffer.str( "" ); - } - - ProgramRunner::ProgramRunner( const BSONObj &args ) { - verify( !args.isEmpty() ); +string ProgramOutputMultiplexer::str() const { + stdx::lock_guard<stdx::mutex> lk(mongoProgramOutputMutex); + string ret = _buffer.str(); + size_t len = ret.length(); + if (len > 100000) { + ret = ret.substr(len - 100000, 100000); + } + return ret; +} + +void ProgramOutputMultiplexer::clear() { + stdx::lock_guard<stdx::mutex> lk(mongoProgramOutputMutex); + _buffer.str(""); +} + +ProgramRunner::ProgramRunner(const BSONObj& args) { + verify(!args.isEmpty()); - string program( args.firstElement().valuestrsafe() ); - verify( !program.empty() ); - boost::filesystem::path programPath = findProgram(program); + string program(args.firstElement().valuestrsafe()); + verify(!program.empty()); + boost::filesystem::path programPath = findProgram(program); - string prefix( "mongod-" ); - bool isMongodProgram = - string("mongod") == program || - program.compare( 0, prefix.size(), prefix ) == 0; + string prefix("mongod-"); + bool isMongodProgram = + string("mongod") == program || program.compare(0, prefix.size(), prefix) == 0; - prefix = "mongos-"; - bool isMongosProgram = - string("mongos") == program || - program.compare( 0, prefix.size(), prefix ) == 0; + prefix = "mongos-"; + bool isMongosProgram = + string("mongos") == program || program.compare(0, prefix.size(), prefix) == 0; #if 0 if (isMongosProgram == "mongos") { @@ -227,618 +233,607 @@ namespace mongo { } #endif - _argv.push_back( programPath.string() ); + _argv.push_back(programPath.string()); - _port = -1; + _port = -1; - BSONObjIterator j( args ); - j.next(); // skip program name (handled above) - while(j.more()) { - BSONElement e = j.next(); - string str; - if ( e.isNumber() ) { - stringstream ss; - ss << e.number(); - str = ss.str(); - } - else { - verify( e.type() == mongo::String ); - str = e.valuestr(); - } - if ( str == "--port" ) - _port = -2; - else if ( _port == -2 ) - _port = strtol( str.c_str(), 0, 10 ); - _argv.push_back(str); - } + BSONObjIterator j(args); + j.next(); // skip program name (handled above) + while (j.more()) { + BSONElement e = j.next(); + string str; + if (e.isNumber()) { + stringstream ss; + ss << e.number(); + str = ss.str(); + } else { + verify(e.type() == mongo::String); + str = e.valuestr(); + } + if (str == "--port") + _port = -2; + else if (_port == -2) + _port = strtol(str.c_str(), 0, 10); + _argv.push_back(str); + } - if ( ! isMongodProgram && ! isMongosProgram && program != "mongobridge" ) - _port = 0; - else { - if ( _port <= 0 ) - log() << "error: a port number is expected when running " << program << " from the shell" << endl; - verify( _port > 0 ); - } - if ( _port > 0 ) { - bool haveDbForPort = registry.isPortRegistered( _port ); - if ( haveDbForPort ) { - log() << "already have db for port: " << _port << endl; - verify( !haveDbForPort ); - } - } + if (!isMongodProgram && !isMongosProgram && program != "mongobridge") + _port = 0; + else { + if (_port <= 0) + log() << "error: a port number is expected when running " << program + << " from the shell" << endl; + verify(_port > 0); + } + if (_port > 0) { + bool haveDbForPort = registry.isPortRegistered(_port); + if (haveDbForPort) { + log() << "already have db for port: " << _port << endl; + verify(!haveDbForPort); } + } +} - void ProgramRunner::start() { - int pipeEnds[ 2 ]; - int status = pipe(pipeEnds); - if (status != 0) { - error() << "failed to create pipe: " << errnoWithDescription() << endl; - fassertFailed(16701); - } - - fflush( 0 ); - launchProcess(pipeEnds[1]); //sets _pid +void ProgramRunner::start() { + int pipeEnds[2]; + int status = pipe(pipeEnds); + if (status != 0) { + error() << "failed to create pipe: " << errnoWithDescription() << endl; + fassertFailed(16701); + } - { - stringstream ss; - ss << "shell: started program (sh" << _pid << "): "; - for (unsigned i = 0; i < _argv.size(); i++) { - ss << " " << _argv[i]; - } - log() << ss.str() << endl; - } + fflush(0); + launchProcess(pipeEnds[1]); // sets _pid - if ( _port > 0 ) - registry.registerPort( _port, _pid, pipeEnds[ 1 ] ); - else - registry.registerPid( _pid, pipeEnds[ 1 ] ); - _pipe = pipeEnds[ 0 ]; + { + stringstream ss; + ss << "shell: started program (sh" << _pid << "): "; + for (unsigned i = 0; i < _argv.size(); i++) { + ss << " " << _argv[i]; } + log() << ss.str() << endl; + } - void ProgramRunner::operator()() { - try { - // This assumes there aren't any 0's in the mongo program output. - // Hope that's ok. - const unsigned bufSize = 128 * 1024; - char buf[ bufSize ]; - char temp[ bufSize ]; - char *start = buf; - while( 1 ) { - int lenToRead = ( bufSize - 1 ) - ( start - buf ); - if ( lenToRead <= 0 ) { - log() << "error: lenToRead: " << lenToRead << endl; - log() << "first 300: " << string(buf,0,300) << endl; - } - verify( lenToRead > 0 ); - int ret = read( _pipe, (void *)start, lenToRead ); - if( mongo::dbexitCalled ) - break; - verify( ret != -1 ); - start[ ret ] = '\0'; - if ( strlen( start ) != unsigned( ret ) ) - programOutputLogger.appendLine( _port, _pid, "WARNING: mongod wrote null bytes to output" ); - char *last = buf; - for( char *i = strchr( buf, '\n' ); i; last = i + 1, i = strchr( last, '\n' ) ) { - *i = '\0'; - programOutputLogger.appendLine( _port, _pid, last ); - } - if ( ret == 0 ) { - if ( *last ) - programOutputLogger.appendLine( _port, _pid, last ); - close( _pipe ); - break; - } - if ( last != buf ) { - strncpy( temp, last, bufSize ); - temp[ bufSize-1 ] = '\0'; - strncpy( buf, temp, bufSize ); - buf[ bufSize-1 ] = '\0'; - } - else { - verify( strlen( buf ) < bufSize ); - } - start = buf + strlen( buf ); - } - } - catch(...) { - } - } - - boost::filesystem::path ProgramRunner::findProgram( const string &prog ) { - boost::filesystem::path p = prog; + if (_port > 0) + registry.registerPort(_port, _pid, pipeEnds[1]); + else + registry.registerPid(_pid, pipeEnds[1]); + _pipe = pipeEnds[0]; +} + +void ProgramRunner::operator()() { + try { + // This assumes there aren't any 0's in the mongo program output. + // Hope that's ok. + const unsigned bufSize = 128 * 1024; + char buf[bufSize]; + char temp[bufSize]; + char* start = buf; + while (1) { + int lenToRead = (bufSize - 1) - (start - buf); + if (lenToRead <= 0) { + log() << "error: lenToRead: " << lenToRead << endl; + log() << "first 300: " << string(buf, 0, 300) << endl; + } + verify(lenToRead > 0); + int ret = read(_pipe, (void*)start, lenToRead); + if (mongo::dbexitCalled) + break; + verify(ret != -1); + start[ret] = '\0'; + if (strlen(start) != unsigned(ret)) + programOutputLogger.appendLine( + _port, _pid, "WARNING: mongod wrote null bytes to output"); + char* last = buf; + for (char* i = strchr(buf, '\n'); i; last = i + 1, i = strchr(last, '\n')) { + *i = '\0'; + programOutputLogger.appendLine(_port, _pid, last); + } + if (ret == 0) { + if (*last) + programOutputLogger.appendLine(_port, _pid, last); + close(_pipe); + break; + } + if (last != buf) { + strncpy(temp, last, bufSize); + temp[bufSize - 1] = '\0'; + strncpy(buf, temp, bufSize); + buf[bufSize - 1] = '\0'; + } else { + verify(strlen(buf) < bufSize); + } + start = buf + strlen(buf); + } + } catch (...) { + } +} + +boost::filesystem::path ProgramRunner::findProgram(const string& prog) { + boost::filesystem::path p = prog; #ifdef _WIN32 - // The system programs either come versioned in the form of <utility>-<major.minor> - // (e.g., mongorestore-2.4) or just <utility>. For windows, the appropriate extension - // needs to be appended. - // - if (p.extension() != ".exe") { - p = prog + ".exe"; - } + // The system programs either come versioned in the form of <utility>-<major.minor> + // (e.g., mongorestore-2.4) or just <utility>. For windows, the appropriate extension + // needs to be appended. + // + if (p.extension() != ".exe") { + p = prog + ".exe"; + } #endif - if( boost::filesystem::exists(p) ) { + if (boost::filesystem::exists(p)) { #ifndef _WIN32 - p = boost::filesystem::initial_path() / p; + p = boost::filesystem::initial_path() / p; #endif - return p; - } - - { - boost::filesystem::path t = boost::filesystem::current_path() / p; - if( boost::filesystem::exists(t) ) return t; - } + return p; + } - { - boost::filesystem::path t = boost::filesystem::initial_path() / p; - if( boost::filesystem::exists(t) ) return t; - } + { + boost::filesystem::path t = boost::filesystem::current_path() / p; + if (boost::filesystem::exists(t)) + return t; + } - return p; // not found; might find via system path - } + { + boost::filesystem::path t = boost::filesystem::initial_path() / p; + if (boost::filesystem::exists(t)) + return t; + } - void ProgramRunner::launchProcess( int child_stdout ) { + return p; // not found; might find via system path +} + +void ProgramRunner::launchProcess(int child_stdout) { #ifdef _WIN32 - stringstream ss; - for( unsigned i=0; i < _argv.size(); i++ ) { - if (i) ss << ' '; - if (_argv[i].find(' ') == string::npos) - ss << _argv[i]; - else { - ss << '"'; - // escape all embedded quotes - for (size_t j=0; j<_argv[i].size(); ++j) { - if (_argv[i][j]=='"') ss << '\\'; - ss << _argv[i][j]; - } - ss << '"'; - } - } + stringstream ss; + for (unsigned i = 0; i < _argv.size(); i++) { + if (i) + ss << ' '; + if (_argv[i].find(' ') == string::npos) + ss << _argv[i]; + else { + ss << '"'; + // escape all embedded quotes + for (size_t j = 0; j < _argv[i].size(); ++j) { + if (_argv[i][j] == '"') + ss << '\\'; + ss << _argv[i][j]; + } + ss << '"'; + } + } - string args = ss.str(); - - std::unique_ptr<TCHAR[]> args_tchar (new TCHAR[args.size() + 1]); - size_t i; - for(i=0; i < args.size(); i++) - args_tchar[i] = args[i]; - args_tchar[i] = 0; - - HANDLE h = (HANDLE)_get_osfhandle(child_stdout); - verify(h != INVALID_HANDLE_VALUE); - verify(SetHandleInformation(h, HANDLE_FLAG_INHERIT, 1)); - - STARTUPINFO si; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - si.hStdError = h; - si.hStdOutput = h; - si.dwFlags |= STARTF_USESTDHANDLES; - - PROCESS_INFORMATION pi; - ZeroMemory(&pi, sizeof(pi)); - - bool success = CreateProcess( NULL, args_tchar.get(), NULL, NULL, true, 0, NULL, NULL, &si, &pi) != 0; - if (!success) { - LPSTR lpMsgBuf=0; - DWORD dw = GetLastError(); - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - dw, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&lpMsgBuf, - 0, NULL ); - stringstream ss; - ss << "couldn't start process " << _argv[0] << "; " << lpMsgBuf; - uassert(14042, ss.str(), success); - LocalFree(lpMsgBuf); - } + string args = ss.str(); + + std::unique_ptr<TCHAR[]> args_tchar(new TCHAR[args.size() + 1]); + size_t i; + for (i = 0; i < args.size(); i++) + args_tchar[i] = args[i]; + args_tchar[i] = 0; + + HANDLE h = (HANDLE)_get_osfhandle(child_stdout); + verify(h != INVALID_HANDLE_VALUE); + verify(SetHandleInformation(h, HANDLE_FLAG_INHERIT, 1)); + + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.hStdError = h; + si.hStdOutput = h; + si.dwFlags |= STARTF_USESTDHANDLES; + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + bool success = + CreateProcess(NULL, args_tchar.get(), NULL, NULL, true, 0, NULL, NULL, &si, &pi) != 0; + if (!success) { + LPSTR lpMsgBuf = 0; + DWORD dw = GetLastError(); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&lpMsgBuf, + 0, + NULL); + stringstream ss; + ss << "couldn't start process " << _argv[0] << "; " << lpMsgBuf; + uassert(14042, ss.str(), success); + LocalFree(lpMsgBuf); + } - CloseHandle(pi.hThread); + CloseHandle(pi.hThread); - _pid = ProcessId::fromNative(pi.dwProcessId); - registry._handles.insert( make_pair( _pid, pi.hProcess ) ); + _pid = ProcessId::fromNative(pi.dwProcessId); + registry._handles.insert(make_pair(_pid, pi.hProcess)); #else - unique_ptr<const char *[]> argvStorage( new const char* [_argv.size()+1] ); - const char** argv = argvStorage.get(); - for (unsigned i=0; i < _argv.size(); i++) { - argv[i] = _argv[i].c_str(); - } - argv[_argv.size()] = 0; - - unique_ptr<const char *[]> envStorage( new const char* [2] ); - const char** env = envStorage.get(); - env[0] = NULL; - env[1] = NULL; + unique_ptr<const char* []> argvStorage(new const char* [_argv.size() + 1]); + const char** argv = argvStorage.get(); + for (unsigned i = 0; i < _argv.size(); i++) { + argv[i] = _argv[i].c_str(); + } + argv[_argv.size()] = 0; - pid_t nativePid = fork(); - _pid = ProcessId::fromNative(nativePid); - // Async signal unsafe functions should not be called in the child process. + unique_ptr<const char* []> envStorage(new const char* [2]); + const char** env = envStorage.get(); + env[0] = NULL; + env[1] = NULL; - verify( nativePid != -1 ); - if ( nativePid == 0 ) { - // DON'T ASSERT IN THIS BLOCK - very bad things will happen + pid_t nativePid = fork(); + _pid = ProcessId::fromNative(nativePid); + // Async signal unsafe functions should not be called in the child process. - if ( dup2( child_stdout, STDOUT_FILENO ) == -1 || - dup2( child_stdout, STDERR_FILENO ) == -1 ) { + verify(nativePid != -1); + if (nativePid == 0) { + // DON'T ASSERT IN THIS BLOCK - very bad things will happen - // Async signal unsafe code reporting a terminal error condition. - cout << "Unable to dup2 child output: " << errnoWithDescription() << endl; - quickExit(-1); //do not pass go, do not call atexit handlers - } + if (dup2(child_stdout, STDOUT_FILENO) == -1 || dup2(child_stdout, STDERR_FILENO) == -1) { + // Async signal unsafe code reporting a terminal error condition. + cout << "Unable to dup2 child output: " << errnoWithDescription() << endl; + quickExit(-1); // do not pass go, do not call atexit handlers + } - // NOTE execve is async signal safe, but it is not clear that execvp is async - // signal safe. - execvp( argv[ 0 ], const_cast<char**>(argv) ); + // NOTE execve is async signal safe, but it is not clear that execvp is async + // signal safe. + execvp(argv[0], const_cast<char**>(argv)); - // Async signal unsafe code reporting a terminal error condition. - cout << "Unable to start program " << argv[0] << ' ' << errnoWithDescription() << endl; - quickExit(-1); - } + // Async signal unsafe code reporting a terminal error condition. + cout << "Unable to start program " << argv[0] << ' ' << errnoWithDescription() << endl; + quickExit(-1); + } #endif - } +} - //returns true if process exited - bool wait_for_pid(ProcessId pid, bool block=true, int* exit_code=NULL) { +// returns true if process exited +bool wait_for_pid(ProcessId pid, bool block = true, int* exit_code = NULL) { #ifdef _WIN32 - verify(registry._handles.count(pid)); - HANDLE h = registry._handles[pid]; + verify(registry._handles.count(pid)); + HANDLE h = registry._handles[pid]; - if (block) { - if (WaitForSingleObject(h, INFINITE)) { - log() << "WaitForSingleObject failed: " << errnoWithDescription(); - } - } + if (block) { + if (WaitForSingleObject(h, INFINITE)) { + log() << "WaitForSingleObject failed: " << errnoWithDescription(); + } + } - DWORD tmp; - if(GetExitCodeProcess(h, &tmp)) { - if ( tmp == STILL_ACTIVE ) { - if (block) - log() << "Process is STILL_ACTIVE even after blocking"; - return false; - } - CloseHandle(h); - registry._handles.erase(pid); - if (exit_code) - *exit_code = tmp; - return true; - } - else { - log() << "GetExitCodeProcess failed: " << errnoWithDescription(); - return false; - } + DWORD tmp; + if (GetExitCodeProcess(h, &tmp)) { + if (tmp == STILL_ACTIVE) { + if (block) + log() << "Process is STILL_ACTIVE even after blocking"; + return false; + } + CloseHandle(h); + registry._handles.erase(pid); + if (exit_code) + *exit_code = tmp; + return true; + } else { + log() << "GetExitCodeProcess failed: " << errnoWithDescription(); + return false; + } #else - int tmp; - bool ret = (pid.toNative() == waitpid(pid.toNative(), &tmp, (block ? 0 : WNOHANG))); - if (exit_code) - *exit_code = WEXITSTATUS(tmp); - return ret; + int tmp; + bool ret = (pid.toNative() == waitpid(pid.toNative(), &tmp, (block ? 0 : WNOHANG))); + if (exit_code) + *exit_code = WEXITSTATUS(tmp); + return ret; #endif - } +} - BSONObj RawMongoProgramOutput( const BSONObj &args, void* data ) { - return BSON( "" << programOutputLogger.str() ); - } - - BSONObj ClearRawMongoProgramOutput( const BSONObj &args, void* data ) { - programOutputLogger.clear(); - return undefinedReturn; - } +BSONObj RawMongoProgramOutput(const BSONObj& args, void* data) { + return BSON("" << programOutputLogger.str()); +} - BSONObj CheckProgram(const BSONObj& args, void* data) { - ProcessId pid = ProcessId::fromNative(singleArg(args).numberInt()); - bool isDead = wait_for_pid(pid, false); - if (isDead) registry.deletePid(pid); - return BSON( string( "" ) << (!isDead) ); - } +BSONObj ClearRawMongoProgramOutput(const BSONObj& args, void* data) { + programOutputLogger.clear(); + return undefinedReturn; +} - BSONObj WaitProgram( const BSONObj& a, void* data ) { - ProcessId pid = ProcessId::fromNative(singleArg( a ).numberInt()); - int exit_code = -123456; // sentinel value - wait_for_pid( pid, true, &exit_code ); - registry.deletePid( pid ); - return BSON( string("") << exit_code); - } +BSONObj CheckProgram(const BSONObj& args, void* data) { + ProcessId pid = ProcessId::fromNative(singleArg(args).numberInt()); + bool isDead = wait_for_pid(pid, false); + if (isDead) + registry.deletePid(pid); + return BSON(string("") << (!isDead)); +} - BSONObj StartMongoProgram( const BSONObj &a, void* data ) { - _nokillop = true; - ProgramRunner r( a ); - r.start(); - stdx::thread t( r ); - return BSON( string( "" ) << r.pid().asLongLong() ); - } +BSONObj WaitProgram(const BSONObj& a, void* data) { + ProcessId pid = ProcessId::fromNative(singleArg(a).numberInt()); + int exit_code = -123456; // sentinel value + wait_for_pid(pid, true, &exit_code); + registry.deletePid(pid); + return BSON(string("") << exit_code); +} - BSONObj RunMongoProgram( const BSONObj &a, void* data ) { - ProgramRunner r( a ); - r.start(); - stdx::thread t( r ); - int exit_code = -123456; // sentinel value - wait_for_pid( r.pid(), true, &exit_code ); - if ( r.port() > 0 ) { - registry.deletePort( r.port() ); - } - else { - registry.deletePid( r.pid() ); - } - return BSON( string( "" ) << exit_code ); - } +BSONObj StartMongoProgram(const BSONObj& a, void* data) { + _nokillop = true; + ProgramRunner r(a); + r.start(); + stdx::thread t(r); + return BSON(string("") << r.pid().asLongLong()); +} - BSONObj RunProgram(const BSONObj &a, void* data) { - ProgramRunner r( a ); - r.start(); - stdx::thread t( r ); - int exit_code = -123456; // sentinel value - wait_for_pid(r.pid(), true, &exit_code); - registry.deletePid( r.pid() ); - return BSON( string( "" ) << exit_code ); - } +BSONObj RunMongoProgram(const BSONObj& a, void* data) { + ProgramRunner r(a); + r.start(); + stdx::thread t(r); + int exit_code = -123456; // sentinel value + wait_for_pid(r.pid(), true, &exit_code); + if (r.port() > 0) { + registry.deletePort(r.port()); + } else { + registry.deletePid(r.pid()); + } + return BSON(string("") << exit_code); +} - BSONObj ResetDbpath( const BSONObj &a, void* data ) { - verify( a.nFields() == 1 ); - string path = a.firstElement().valuestrsafe(); - verify( !path.empty() ); - if ( boost::filesystem::exists( path ) ) - boost::filesystem::remove_all( path ); - boost::filesystem::create_directory( path ); - return undefinedReturn; - } +BSONObj RunProgram(const BSONObj& a, void* data) { + ProgramRunner r(a); + r.start(); + stdx::thread t(r); + int exit_code = -123456; // sentinel value + wait_for_pid(r.pid(), true, &exit_code); + registry.deletePid(r.pid()); + return BSON(string("") << exit_code); +} - BSONObj PathExists( const BSONObj &a, void* data ) { - verify( a.nFields() == 1 ); - string path = a.firstElement().valuestrsafe(); - verify( !path.empty() ); - bool exists = boost::filesystem::exists(path); - return BSON( string( "" ) << exists ); - } +BSONObj ResetDbpath(const BSONObj& a, void* data) { + verify(a.nFields() == 1); + string path = a.firstElement().valuestrsafe(); + verify(!path.empty()); + if (boost::filesystem::exists(path)) + boost::filesystem::remove_all(path); + boost::filesystem::create_directory(path); + return undefinedReturn; +} - void copyDir( const boost::filesystem::path &from, const boost::filesystem::path &to ) { - boost::filesystem::directory_iterator end; - boost::filesystem::directory_iterator i( from ); - while( i != end ) { - boost::filesystem::path p = *i; - if ( p.leaf() != "mongod.lock" && p.leaf() != "WiredTiger.lock" ) { - if ( boost::filesystem::is_directory( p ) ) { - boost::filesystem::path newDir = to / p.leaf(); - boost::filesystem::create_directory( newDir ); - copyDir( p, newDir ); - } - else { - boost::filesystem::copy_file( p, to / p.leaf() ); - } - } - ++i; - } - } +BSONObj PathExists(const BSONObj& a, void* data) { + verify(a.nFields() == 1); + string path = a.firstElement().valuestrsafe(); + verify(!path.empty()); + bool exists = boost::filesystem::exists(path); + return BSON(string("") << exists); +} - // NOTE target dbpath will be cleared first - BSONObj CopyDbpath( const BSONObj &a, void* data ) { - verify( a.nFields() == 2 ); - BSONObjIterator i( a ); - string from = i.next().str(); - string to = i.next().str(); - verify( !from.empty() ); - verify( !to.empty() ); - if ( boost::filesystem::exists( to ) ) - boost::filesystem::remove_all( to ); - boost::filesystem::create_directory( to ); - copyDir( from, to ); - return undefinedReturn; - } +void copyDir(const boost::filesystem::path& from, const boost::filesystem::path& to) { + boost::filesystem::directory_iterator end; + boost::filesystem::directory_iterator i(from); + while (i != end) { + boost::filesystem::path p = *i; + if (p.leaf() != "mongod.lock" && p.leaf() != "WiredTiger.lock") { + if (boost::filesystem::is_directory(p)) { + boost::filesystem::path newDir = to / p.leaf(); + boost::filesystem::create_directory(newDir); + copyDir(p, newDir); + } else { + boost::filesystem::copy_file(p, to / p.leaf()); + } + } + ++i; + } +} + +// NOTE target dbpath will be cleared first +BSONObj CopyDbpath(const BSONObj& a, void* data) { + verify(a.nFields() == 2); + BSONObjIterator i(a); + string from = i.next().str(); + string to = i.next().str(); + verify(!from.empty()); + verify(!to.empty()); + if (boost::filesystem::exists(to)) + boost::filesystem::remove_all(to); + boost::filesystem::create_directory(to); + copyDir(from, to); + return undefinedReturn; +} - inline void kill_wrapper( ProcessId pid, int sig, int port, const BSONObj& opt ) { +inline void kill_wrapper(ProcessId pid, int sig, int port, const BSONObj& opt) { #ifdef _WIN32 - if (sig == SIGKILL || port == 0) { - verify( registry._handles.count(pid) ); - TerminateProcess(registry._handles[pid], 1); // returns failure for "zombie" processes. - return; - } + if (sig == SIGKILL || port == 0) { + verify(registry._handles.count(pid)); + TerminateProcess(registry._handles[pid], 1); // returns failure for "zombie" processes. + return; + } - std::string eventName = getShutdownSignalName(pid.asUInt32()); + std::string eventName = getShutdownSignalName(pid.asUInt32()); - HANDLE event = OpenEventA(EVENT_MODIFY_STATE, FALSE, eventName.c_str()); - if (event == NULL) { - int gle = GetLastError(); - if (gle != ERROR_FILE_NOT_FOUND) { - warning() << "kill_wrapper OpenEvent failed: " << errnoWithDescription(); - } - else { - log() << "kill_wrapper OpenEvent failed to open event to the process " - << pid.asUInt32() - << ". It has likely died already or server is running an older version." - << " Attempting to shutdown through admin command."; - - // Back-off to the old way of shutting down the server on Windows, in case we - // are managing a pre-2.6.0rc0 service, which did not have the event. - // - try { - DBClientConnection conn; - conn.connect("127.0.0.1:" + BSONObjBuilder::numStr(port)); - - BSONElement authObj = opt["auth"]; - - if (!authObj.eoo()){ - string errMsg; - conn.auth("admin", authObj["user"].String(), - authObj["pwd"].String(), errMsg); - - if (!errMsg.empty()) { - cout << "Failed to authenticate before shutdown: " - << errMsg << endl; - } - } - - BSONObj info; - BSONObjBuilder b; - b.append("shutdown", 1); - b.append("force", 1); - conn.runCommand("admin", b.done(), info); - } - catch (...) { - // Do nothing. This command never returns data to the client and the driver - // doesn't like that. - // + HANDLE event = OpenEventA(EVENT_MODIFY_STATE, FALSE, eventName.c_str()); + if (event == NULL) { + int gle = GetLastError(); + if (gle != ERROR_FILE_NOT_FOUND) { + warning() << "kill_wrapper OpenEvent failed: " << errnoWithDescription(); + } else { + log() << "kill_wrapper OpenEvent failed to open event to the process " << pid.asUInt32() + << ". It has likely died already or server is running an older version." + << " Attempting to shutdown through admin command."; + + // Back-off to the old way of shutting down the server on Windows, in case we + // are managing a pre-2.6.0rc0 service, which did not have the event. + // + try { + DBClientConnection conn; + conn.connect("127.0.0.1:" + BSONObjBuilder::numStr(port)); + + BSONElement authObj = opt["auth"]; + + if (!authObj.eoo()) { + string errMsg; + conn.auth("admin", authObj["user"].String(), authObj["pwd"].String(), errMsg); + + if (!errMsg.empty()) { + cout << "Failed to authenticate before shutdown: " << errMsg << endl; } } - return; + + BSONObj info; + BSONObjBuilder b; + b.append("shutdown", 1); + b.append("force", 1); + conn.runCommand("admin", b.done(), info); + } catch (...) { + // Do nothing. This command never returns data to the client and the driver + // doesn't like that. + // } + } + return; + } - ON_BLOCK_EXIT(CloseHandle, event); + ON_BLOCK_EXIT(CloseHandle, event); - bool result = SetEvent(event); - if (!result) { - error() << "kill_wrapper SetEvent failed: " << errnoWithDescription(); - return; - } + bool result = SetEvent(event); + if (!result) { + error() << "kill_wrapper SetEvent failed: " << errnoWithDescription(); + return; + } #else - int x = kill( pid.toNative(), sig ); - if ( x ) { - if ( errno == ESRCH ) { - } - else { - log() << "killFailed: " << errnoWithDescription() << endl; - verify( x == 0 ); - } - } + int x = kill(pid.toNative(), sig); + if (x) { + if (errno == ESRCH) { + } else { + log() << "killFailed: " << errnoWithDescription() << endl; + verify(x == 0); + } + } #endif - } +} - int killDb( int port, ProcessId _pid, int signal, const BSONObj& opt ) { - ProcessId pid; - int exitCode = 0; - if ( port > 0 ) { - if( !registry.isPortRegistered( port ) ) { - log() << "No db started on port: " << port << endl; - return 0; - } - pid = registry.pidForPort( port ); - } - else { - pid = _pid; - } +int killDb(int port, ProcessId _pid, int signal, const BSONObj& opt) { + ProcessId pid; + int exitCode = 0; + if (port > 0) { + if (!registry.isPortRegistered(port)) { + log() << "No db started on port: " << port << endl; + return 0; + } + pid = registry.pidForPort(port); + } else { + pid = _pid; + } - kill_wrapper( pid, signal, port, opt ); + kill_wrapper(pid, signal, port, opt); - int i = 0; - for( ; i < 130; ++i ) { - if ( i == 60 ) { - log() << "process on port " << port << ", with pid " << pid << " not terminated, sending sigkill" << endl; - kill_wrapper( pid, SIGKILL, port, opt ); - } - if(wait_for_pid(pid, false, &exitCode)) - break; - sleepmillis( 1000 ); - } - if ( i == 130 ) { - log() << "failed to terminate process on port " << port << ", with pid " << pid << endl; - verify( "Failed to terminate process" == 0 ); - } + int i = 0; + for (; i < 130; ++i) { + if (i == 60) { + log() << "process on port " << port << ", with pid " << pid + << " not terminated, sending sigkill" << endl; + kill_wrapper(pid, SIGKILL, port, opt); + } + if (wait_for_pid(pid, false, &exitCode)) + break; + sleepmillis(1000); + } + if (i == 130) { + log() << "failed to terminate process on port " << port << ", with pid " << pid << endl; + verify("Failed to terminate process" == 0); + } - if ( port > 0 ) { - registry.deletePort( port ); - } - else { - registry.deletePid( pid ); - } - // FIXME I think the intention here is to do an extra sleep only when SIGKILL is sent to the child process. - // We may want to change the 4 below to 29, since values of i greater than that indicate we sent a SIGKILL. - if ( i > 4 || signal == SIGKILL ) { - sleepmillis( 4000 ); // allow operating system to reclaim resources - } + if (port > 0) { + registry.deletePort(port); + } else { + registry.deletePid(pid); + } + // FIXME I think the intention here is to do an extra sleep only when SIGKILL is sent to the child process. + // We may want to change the 4 below to 29, since values of i greater than that indicate we sent a SIGKILL. + if (i > 4 || signal == SIGKILL) { + sleepmillis(4000); // allow operating system to reclaim resources + } - return exitCode; - } + return exitCode; +} - int killDb( int port, ProcessId _pid, int signal ) { - BSONObj dummyOpt; - return killDb( port, _pid, signal, dummyOpt ); - } - - int getSignal( const BSONObj &a ) { - int ret = SIGTERM; - if ( a.nFields() >= 2 ) { - BSONObjIterator i( a ); - i.next(); - BSONElement e = i.next(); - verify( e.isNumber() ); - ret = int( e.number() ); - } - return ret; - } +int killDb(int port, ProcessId _pid, int signal) { + BSONObj dummyOpt; + return killDb(port, _pid, signal, dummyOpt); +} - BSONObj getStopMongodOpts( const BSONObj &a ) { - if ( a.nFields() == 3 ) { - BSONObjIterator i( a ); - i.next(); - i.next(); - BSONElement e = i.next(); +int getSignal(const BSONObj& a) { + int ret = SIGTERM; + if (a.nFields() >= 2) { + BSONObjIterator i(a); + i.next(); + BSONElement e = i.next(); + verify(e.isNumber()); + ret = int(e.number()); + } + return ret; +} - if ( e.isABSONObj() ){ - return e.embeddedObject(); - } - } +BSONObj getStopMongodOpts(const BSONObj& a) { + if (a.nFields() == 3) { + BSONObjIterator i(a); + i.next(); + i.next(); + BSONElement e = i.next(); - return BSONObj(); + if (e.isABSONObj()) { + return e.embeddedObject(); } + } - /** stopMongoProgram(port[, signal]) */ - BSONObj StopMongoProgram( const BSONObj &a, void* data ) { - int nFields = a.nFields(); - verify( nFields >= 1 && nFields <= 3 ); - uassert( 15853 , "stopMongo needs a number" , a.firstElement().isNumber() ); - int port = int( a.firstElement().number() ); - int code = killDb( port, ProcessId::fromNative(0), getSignal( a ), getStopMongodOpts( a )); - log() << "shell: stopped mongo program on port " << port << endl; - return BSON( "" << (double)code ); - } + return BSONObj(); +} - BSONObj StopMongoProgramByPid( const BSONObj &a, void* data ) { - verify( a.nFields() == 1 || a.nFields() == 2 ); - uassert( 15852 , "stopMongoByPid needs a number" , a.firstElement().isNumber() ); - ProcessId pid = ProcessId::fromNative(int( a.firstElement().number() )); - int code = killDb( 0, pid, getSignal( a ) ); - log() << "shell: stopped mongo program on pid " << pid << endl; - return BSON( "" << (double)code ); - } +/** stopMongoProgram(port[, signal]) */ +BSONObj StopMongoProgram(const BSONObj& a, void* data) { + int nFields = a.nFields(); + verify(nFields >= 1 && nFields <= 3); + uassert(15853, "stopMongo needs a number", a.firstElement().isNumber()); + int port = int(a.firstElement().number()); + int code = killDb(port, ProcessId::fromNative(0), getSignal(a), getStopMongodOpts(a)); + log() << "shell: stopped mongo program on port " << port << endl; + return BSON("" << (double)code); +} - void KillMongoProgramInstances() { - vector< int > ports; - registry.getRegisteredPorts( ports ); - for( vector< int >::iterator i = ports.begin(); i != ports.end(); ++i ) - killDb( *i, ProcessId::fromNative(0), SIGTERM ); - vector< ProcessId > pids; - registry.getRegisteredPids( pids ); - for( vector< ProcessId >::iterator i = pids.begin(); i != pids.end(); ++i ) - killDb( 0, *i, SIGTERM ); - } +BSONObj StopMongoProgramByPid(const BSONObj& a, void* data) { + verify(a.nFields() == 1 || a.nFields() == 2); + uassert(15852, "stopMongoByPid needs a number", a.firstElement().isNumber()); + ProcessId pid = ProcessId::fromNative(int(a.firstElement().number())); + int code = killDb(0, pid, getSignal(a)); + log() << "shell: stopped mongo program on pid " << pid << endl; + return BSON("" << (double)code); +} - MongoProgramScope::~MongoProgramScope() { - DESTRUCTOR_GUARD( - KillMongoProgramInstances(); - ClearRawMongoProgramOutput( BSONObj(), 0 ); - ) - } +void KillMongoProgramInstances() { + vector<int> ports; + registry.getRegisteredPorts(ports); + for (vector<int>::iterator i = ports.begin(); i != ports.end(); ++i) + killDb(*i, ProcessId::fromNative(0), SIGTERM); + vector<ProcessId> pids; + registry.getRegisteredPids(pids); + for (vector<ProcessId>::iterator i = pids.begin(); i != pids.end(); ++i) + killDb(0, *i, SIGTERM); +} - void installShellUtilsLauncher( Scope& scope ) { - scope.injectNative( "_startMongoProgram", StartMongoProgram ); - scope.injectNative( "runProgram", RunProgram ); - scope.injectNative( "run", RunProgram ); - scope.injectNative( "_runMongoProgram", RunMongoProgram ); - scope.injectNative( "_stopMongoProgram", StopMongoProgram ); - scope.injectNative( "stopMongoProgramByPid", StopMongoProgramByPid ); - scope.injectNative( "rawMongoProgramOutput", RawMongoProgramOutput ); - scope.injectNative( "clearRawMongoProgramOutput", ClearRawMongoProgramOutput ); - scope.injectNative( "waitProgram" , WaitProgram ); - scope.injectNative( "checkProgram" , CheckProgram ); - scope.injectNative( "resetDbpath", ResetDbpath ); - scope.injectNative( "pathExists", PathExists ); - scope.injectNative( "copyDbpath", CopyDbpath ); - } - } +MongoProgramScope::~MongoProgramScope() { + DESTRUCTOR_GUARD(KillMongoProgramInstances(); ClearRawMongoProgramOutput(BSONObj(), 0);) +} + +void installShellUtilsLauncher(Scope& scope) { + scope.injectNative("_startMongoProgram", StartMongoProgram); + scope.injectNative("runProgram", RunProgram); + scope.injectNative("run", RunProgram); + scope.injectNative("_runMongoProgram", RunMongoProgram); + scope.injectNative("_stopMongoProgram", StopMongoProgram); + scope.injectNative("stopMongoProgramByPid", StopMongoProgramByPid); + scope.injectNative("rawMongoProgramOutput", RawMongoProgramOutput); + scope.injectNative("clearRawMongoProgramOutput", ClearRawMongoProgramOutput); + scope.injectNative("waitProgram", WaitProgram); + scope.injectNative("checkProgram", CheckProgram); + scope.injectNative("resetDbpath", ResetDbpath); + scope.injectNative("pathExists", PathExists); + scope.injectNative("copyDbpath", CopyDbpath); +} +} } diff --git a/src/mongo/shell/shell_utils_launcher.h b/src/mongo/shell/shell_utils_launcher.h index c0e038cc016..49ec94f91d9 100644 --- a/src/mongo/shell/shell_utils_launcher.h +++ b/src/mongo/shell/shell_utils_launcher.h @@ -41,88 +41,92 @@ namespace mongo { - class Scope; - - namespace shell_utils { - - // Scoped management of mongo program instances. Simple implementation: - // destructor kills all mongod instances created by the shell. - struct MongoProgramScope { - MongoProgramScope() {} // Avoid 'unused variable' warning. - ~MongoProgramScope(); - }; - void KillMongoProgramInstances(); - - void goingAwaySoon(); - void installShellUtilsLauncher( Scope& scope ); - - /** Record log lines from concurrent programs. All public members are thread safe. */ - class ProgramOutputMultiplexer { - public: - void appendLine( int port, ProcessId pid, const char *line ); - /** @return up to 100000 characters of the most recent log output. */ - std::string str() const; - void clear(); - private: - std::stringstream _buffer; - }; - - /** - * A registry of spawned programs that are identified by a bound port or else a system pid. - * All public member functions are thread safe. - * - * TODO: Clean this up to make the semantics more consistent between pids and ports - */ - class ProgramRegistry { - public: - - bool isPortRegistered( int port ) const; - /** @return pid for a registered port. */ - ProcessId pidForPort( int port ) const; - /** @return port (-1 if doesn't exist) for a registered pid. */ - int portForPid( ProcessId pid ) const; - /** Register an unregistered port. */ - void registerPort( int port, ProcessId pid, int output ); - void deletePort( int port ); - void getRegisteredPorts( std::vector<int> &ports ); - - bool isPidRegistered( ProcessId pid ) const; - /** Register an unregistered pid. */ - void registerPid( ProcessId pid, int output ); - void deletePid( ProcessId pid ); - void getRegisteredPids( std::vector<ProcessId> &pids ); - - private: - std::map<int,std::pair<ProcessId,int> > _ports; - std::map<ProcessId,int> _pids; - mutable boost::recursive_mutex _mutex; +class Scope; + +namespace shell_utils { + +// Scoped management of mongo program instances. Simple implementation: +// destructor kills all mongod instances created by the shell. +struct MongoProgramScope { + MongoProgramScope() {} // Avoid 'unused variable' warning. + ~MongoProgramScope(); +}; +void KillMongoProgramInstances(); + +void goingAwaySoon(); +void installShellUtilsLauncher(Scope& scope); + +/** Record log lines from concurrent programs. All public members are thread safe. */ +class ProgramOutputMultiplexer { +public: + void appendLine(int port, ProcessId pid, const char* line); + /** @return up to 100000 characters of the most recent log output. */ + std::string str() const; + void clear(); + +private: + std::stringstream _buffer; +}; + +/** + * A registry of spawned programs that are identified by a bound port or else a system pid. + * All public member functions are thread safe. + * + * TODO: Clean this up to make the semantics more consistent between pids and ports + */ +class ProgramRegistry { +public: + bool isPortRegistered(int port) const; + /** @return pid for a registered port. */ + ProcessId pidForPort(int port) const; + /** @return port (-1 if doesn't exist) for a registered pid. */ + int portForPid(ProcessId pid) const; + /** Register an unregistered port. */ + void registerPort(int port, ProcessId pid, int output); + void deletePort(int port); + void getRegisteredPorts(std::vector<int>& ports); + + bool isPidRegistered(ProcessId pid) const; + /** Register an unregistered pid. */ + void registerPid(ProcessId pid, int output); + void deletePid(ProcessId pid); + void getRegisteredPids(std::vector<ProcessId>& pids); + +private: + std::map<int, std::pair<ProcessId, int>> _ports; + std::map<ProcessId, int> _pids; + mutable boost::recursive_mutex _mutex; #ifdef _WIN32 - public: - std::map<ProcessId,HANDLE> _handles; +public: + std::map<ProcessId, HANDLE> _handles; #endif - }; - - /** Helper class for launching a program and logging its output. */ - class ProgramRunner { - public: - /** @param args The program's arguments, including the program name. */ - ProgramRunner( const BSONObj &args ); - /** Launch the program. */ - void start(); - /** Continuously read the program's output, generally from a special purpose thread. */ - void operator()(); - ProcessId pid() const { return _pid; } - int port() const { return _port; } - - private: - boost::filesystem::path findProgram( const std::string &prog ); - void launchProcess( int child_stdout ); - - std::vector<std::string> _argv; - int _port; - int _pipe; - ProcessId _pid; - }; +}; + +/** Helper class for launching a program and logging its output. */ +class ProgramRunner { +public: + /** @param args The program's arguments, including the program name. */ + ProgramRunner(const BSONObj& args); + /** Launch the program. */ + void start(); + /** Continuously read the program's output, generally from a special purpose thread. */ + void operator()(); + ProcessId pid() const { + return _pid; + } + int port() const { + return _port; } + +private: + boost::filesystem::path findProgram(const std::string& prog); + void launchProcess(int child_stdout); + + std::vector<std::string> _argv; + int _port; + int _pipe; + ProcessId _pid; +}; +} } |