summaryrefslogtreecommitdiff
path: root/shell/dbshell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'shell/dbshell.cpp')
-rw-r--r--shell/dbshell.cpp815
1 files changed, 0 insertions, 815 deletions
diff --git a/shell/dbshell.cpp b/shell/dbshell.cpp
deleted file mode 100644
index 34f2a346436..00000000000
--- a/shell/dbshell.cpp
+++ /dev/null
@@ -1,815 +0,0 @@
-// dbshell.cpp
-/*
- * Copyright 2010 10gen Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pch.h"
-#include <stdio.h>
-#include <string.h>
-
-
-#define USE_LINENOISE
-#include "../third_party/linenoise/linenoise.h"
-
-#include "../scripting/engine.h"
-#include "../client/dbclient.h"
-#include "../util/unittest.h"
-#include "../db/cmdline.h"
-#include "utils.h"
-#include "../util/password.h"
-#include "../util/version.h"
-#include "../util/goodies.h"
-#include "../db/repl/rs_member.h"
-
-using namespace std;
-using namespace boost::filesystem;
-using namespace mongo;
-
-string historyFile;
-bool gotInterrupted = 0;
-bool inMultiLine = 0;
-static volatile bool atPrompt = false; // can eval before getting to prompt
-bool autoKillOp = false;
-
-
-#if defined(USE_LINENOISE) && !defined(__freebsd__) && !defined(__openbsd__) && !defined(_WIN32)
-// this is for ctrl-c handling
-#include <setjmp.h>
-jmp_buf jbuf;
-#endif
-
-#if defined(USE_LINENOISE)
-#define USE_TABCOMPLETION
-#endif
-
-
-namespace mongo {
-
- Scope * shellMainScope;
-
- extern bool dbexitCalled;
-}
-
-void generateCompletions( const string& prefix , vector<string>& all ) {
- if ( prefix.find( '"' ) != string::npos )
- return;
-
- BSONObj args = BSON("0" << prefix);
- shellMainScope->invokeSafe("function(x) {shellAutocomplete(x)}", &args, 0, 1000);
- BSONObjBuilder b;
- shellMainScope->append( b , "" , "__autocomplete__" );
- BSONObj res = b.obj();
- BSONObj arr = res.firstElement().Obj();
-
- BSONObjIterator i(arr);
- while ( i.more() ) {
- BSONElement e = i.next();
- all.push_back( e.String() );
- }
-
-}
-
-#ifdef USE_TABCOMPLETION
-void completionHook(const char* text , linenoiseCompletions* lc ) {
- vector<string> all;
- generateCompletions( text , all );
-
- for ( unsigned i=0; i<all.size(); i++ )
- linenoiseAddCompletion( lc , (char*)all[i].c_str() );
-
-}
-#endif
-
-void shellHistoryInit() {
-#ifdef USE_LINENOISE
- stringstream ss;
- char * h = getenv( "HOME" );
- if ( h )
- ss << h << "/";
- ss << ".dbshell";
- historyFile = ss.str();
-
- linenoiseHistoryLoad( (char*)historyFile.c_str() );
-#ifdef USE_TABCOMPLETION
- linenoiseSetCompletionCallback( completionHook );
-#endif
-
-#else
- //cout << "type \"exit\" to exit" << endl;
-#endif
-}
-void shellHistoryDone() {
-#ifdef USE_LINENOISE
- linenoiseHistorySave( (char*)historyFile.c_str() );
-#endif
-}
-void shellHistoryAdd( const char * line ) {
-#ifdef USE_LINENOISE
- if ( line[0] == '\0' )
- return;
-
- // dont record duplicate lines
- static string lastLine;
- if (lastLine == line)
- return;
- lastLine = line;
-
- if ( strstr( line, ".auth") == NULL &&
- strstr( line, ".addUser") == NULL )
- {
- linenoiseHistoryAdd( line );
- }
-#endif
-}
-
-void intr( int sig ) {
-#ifdef CTRLC_HANDLE
- longjmp( jbuf , 1 );
-#endif
-}
-
-void killOps() {
- if ( mongo::shellUtils::_nokillop || mongo::shellUtils::_allMyUris.size() == 0 )
- return;
-
- if ( atPrompt )
- return;
-
- sleepmillis(10); // give current op a chance to finish
-
- for( map< string, set<string> >::const_iterator i = shellUtils::_allMyUris.begin(); i != shellUtils::_allMyUris.end(); ++i ) {
- string errmsg;
- ConnectionString cs = ConnectionString::parse(i->first, errmsg);
- if (!cs.isValid()) continue;
- boost::scoped_ptr<DBClientWithCommands> conn (cs.connect(errmsg));
- if (!conn) continue;
-
- const set<string>& uris = i->second;
-
- BSONObj inprog = conn->findOne("admin.$cmd.sys.inprog", Query())["inprog"].embeddedObject().getOwned();
- BSONForEach(op, inprog) {
- if ( uris.count(op["client"].String()) ) {
- ONCE if ( !autoKillOp ) {
- cout << endl << "do you want to kill the current op(s) on the server? (y/n): ";
- cout.flush();
-
- char yn;
- cin >> yn;
-
- if (yn != 'y' && yn != 'Y')
- return;
- }
-
- conn->findOne("admin.$cmd.sys.killop", QUERY("op"<< op["opid"]));
- }
- }
- }
-}
-
-void quitNicely( int sig ) {
- mongo::dbexitCalled = true;
- if ( sig == SIGINT && inMultiLine ) {
- gotInterrupted = 1;
- return;
- }
-
-#if !defined(_WIN32)
- if ( sig == SIGPIPE )
- mongo::rawOut( "mongo got signal SIGPIPE\n" );
-#endif
-
- killOps();
- shellHistoryDone();
- exit(0);
-}
-
-char * shellReadline( const char * prompt , int handlesigint = 0 ) {
-
- atPrompt = true;
-#ifdef USE_LINENOISE
-
-
-#ifdef CTRLC_HANDLE
- if ( ! handlesigint ) {
- char* ret = linenoise( prompt );
- atPrompt = false;
- return ret;
- }
- if ( setjmp( jbuf ) ) {
- gotInterrupted = 1;
- sigrelse(SIGINT);
- signal( SIGINT , quitNicely );
- return 0;
- }
- signal( SIGINT , intr );
-#endif
-
- char * ret = linenoise( prompt );
- signal( SIGINT , quitNicely );
- atPrompt = false;
- return ret;
-#else
- printf("%s", prompt); cout.flush();
- char * buf = new char[1024];
- char * l = fgets( buf , 1024 , stdin );
- int len = strlen( buf );
- if ( len )
- buf[len-1] = 0;
- atPrompt = false;
- return l;
-#endif
-}
-
-#ifdef _WIN32
-char * strsignal(int sig){
- switch (sig){
- case SIGINT: return "SIGINT";
- case SIGTERM: return "SIGTERM";
- case SIGABRT: return "SIGABRT";
- case SIGSEGV: return "SIGSEGV";
- case SIGFPE: return "SIGFPE";
- default: return "unknown";
- }
-}
-#endif
-
-void quitAbruptly( int sig ) {
- ostringstream ossSig;
- ossSig << "mongo got signal " << sig << " (" << strsignal( sig ) << "), stack trace: " << endl;
- mongo::rawOut( ossSig.str() );
-
- ostringstream ossBt;
- mongo::printStackTrace( ossBt );
- mongo::rawOut( ossBt.str() );
-
- mongo::shellUtils::KillMongoProgramInstances();
- exit(14);
-}
-
-// this will be called in certain c++ error cases, for example if there are two active
-// exceptions
-void myterminate() {
- mongo::rawOut( "terminate() called in shell, printing stack:" );
- mongo::printStackTrace();
- exit(14);
-}
-
-void setupSignals() {
- signal( SIGINT , quitNicely );
- signal( SIGTERM , quitNicely );
- signal( SIGABRT , quitAbruptly );
- signal( SIGSEGV , quitAbruptly );
- signal( SIGFPE , quitAbruptly );
-
-#if !defined(_WIN32) // surprisingly these are the only ones that don't work on windows
- signal( SIGPIPE , quitNicely ); // Maybe just log and continue?
- signal( SIGBUS , quitAbruptly );
-#endif
-
- set_terminate( myterminate );
-}
-
-string fixHost( string url , string host , string port ) {
- //cout << "fixHost url: " << url << " host: " << host << " port: " << port << endl;
-
- if ( host.size() == 0 && port.size() == 0 ) {
- if ( url.find( "/" ) == string::npos ) {
- // check for ips
- if ( url.find( "." ) != string::npos )
- return url + "/test";
-
- if ( url.rfind( ":" ) != string::npos &&
- isdigit( url[url.rfind(":")+1] ) )
- return url + "/test";
- }
- return url;
- }
-
- if ( url.find( "/" ) != string::npos ) {
- cerr << "url can't have host or port if you specify them individually" << endl;
- exit(-1);
- }
-
- if ( host.size() == 0 )
- host = "127.0.0.1";
-
- string newurl = host;
- if ( port.size() > 0 )
- newurl += ":" + port;
- else if (host.find(':') == string::npos) {
- // need to add port with IPv6 addresses
- newurl += ":27017";
- }
-
- newurl += "/" + url;
-
- return newurl;
-}
-
-static string OpSymbols = "~!%^&*-+=|:,<>/?.";
-
-bool isOpSymbol( char c ) {
- for ( size_t i = 0; i < OpSymbols.size(); i++ )
- if ( OpSymbols[i] == c ) return true;
- return false;
-}
-
-bool isBalanced( string code ) {
- int brackets = 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' )
- i++;
- }
- continue;
- case '{': brackets++; break;
- case '}': if ( brackets <= 0 ) return true; brackets--; break;
- case '(': parens++; break;
- case ')': if ( parens <= 0 ) return true; parens--; break;
- case '"':
- i++;
- while ( i < code.size() && code[i] != '"' ) i++;
- break;
- case '\'':
- i++;
- while ( i < code.size() && code[i] != '\'' ) i++;
- 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;
- }
-
- if ( isOpSymbol( code[i] )) danglingOp = true;
- else if (! std::isspace( code[i] )) danglingOp = false;
- }
-
- return brackets == 0 && parens == 0 && !danglingOp;
-}
-
-using mongo::asserted;
-
-struct BalancedTest : public mongo::UnitTest {
-public:
- void run() {
- assert( isBalanced( "x = 5" ) );
- assert( isBalanced( "function(){}" ) );
- assert( isBalanced( "function(){\n}" ) );
- assert( ! isBalanced( "function(){" ) );
- assert( isBalanced( "x = \"{\";" ) );
- assert( isBalanced( "// {" ) );
- assert( ! isBalanced( "// \n {" ) );
- assert( ! isBalanced( "\"//\" {" ) );
- assert( isBalanced( "{x:/x\\//}" ) );
- assert( ! isBalanced( "{ \\/// }" ) );
- assert( isBalanced( "x = 5 + y ") );
- assert( ! isBalanced( "x = ") );
- assert( ! isBalanced( "x = // hello") );
- assert( ! isBalanced( "x = 5 +") );
- assert( isBalanced( " x ++") );
- assert( isBalanced( "-- x") );
- assert( !isBalanced( "a.") );
- assert( !isBalanced( "a. ") );
- assert( isBalanced( "a.b") );
- }
-} balnaced_test;
-
-string finishCode( string code ) {
- while ( ! isBalanced( code ) ) {
- inMultiLine = 1;
- code += "\n";
- // cancel multiline if two blank lines are entered
- if ( code.find("\n\n\n") != string::npos )
- return ";";
- char * line = shellReadline("... " , 1 );
- if ( gotInterrupted )
- return "";
- if ( ! line )
- return "";
-
- while (startsWith(line, "... "))
- line += 4;
-
- code += line;
- }
- return code;
-}
-
-#include <boost/program_options.hpp>
-namespace po = boost::program_options;
-
-void show_help_text(const char* name, po::options_description options) {
- cout << "MongoDB shell version: " << mongo::versionString << endl;
- cout << "usage: " << name << " [options] [db address] [file names (ending in .js)]" << endl
- << "db address can be:" << endl
- << " foo foo database on local machine" << endl
- << " 192.169.0.5/foo foo database on 192.168.0.5 machine" << endl
- << " 192.169.0.5:9999/foo foo database on 192.168.0.5 machine on port 9999" << endl
- << options << endl
- << "file names: a list of files to run. files have to end in .js and will exit after "
- << "unless --shell is specified" << endl;
-};
-
-bool fileExists( string file ) {
- try {
- path p(file);
- return boost::filesystem::exists( file );
- }
- catch (...) {
- return false;
- }
-}
-
-namespace mongo {
- extern bool isShell;
- extern DBClientWithCommands *latestConn;
-}
-
-string sayReplSetMemberState() {
- try {
- if( latestConn ) {
- BSONObj info;
- if( latestConn->runCommand("admin", BSON( "replSetGetStatus" << 1 << "forShell" << 1 ) , info ) ) {
- stringstream ss;
- ss << info["set"].String() << ':';
- int s = info["myState"].Int();
- MemberState ms(s);
- return ms.toString();
- }
- else if( str::equals(info.getStringField("info"), "mongos") ) {
- return "mongos";
- }
- }
- }
- catch( std::exception& e ) {
- log(1) << "error in sayReplSetMemberState:" << e.what() << endl;
- }
- return "";
-}
-
-int _main(int argc, char* argv[]) {
- mongo::isShell = true;
- setupSignals();
-
- mongo::shellUtils::RecordMyLocation( argv[ 0 ] );
-
- string url = "test";
- string dbhost;
- string port;
- vector<string> files;
-
- string username;
- string password;
-
- bool runShell = false;
- bool nodb = false;
- bool norc = false;
-
- string script;
-
- po::options_description shell_options("options");
- po::options_description hidden_options("Hidden options");
- po::options_description cmdline_options("Command line options");
- po::positional_options_description positional_options;
-
- shell_options.add_options()
- ("shell", "run the shell after executing files")
- ("nodb", "don't connect to mongod on startup - no 'db address' arg expected")
- ("norc", "will not run the \".mongorc.js\" file on start up")
- ("quiet", "be less chatty" )
- ("port", po::value<string>(&port), "port to connect to")
- ("host", po::value<string>(&dbhost), "server to connect to")
- ("eval", po::value<string>(&script), "evaluate javascript")
- ("username,u", po::value<string>(&username), "username for authentication")
- ("password,p", new mongo::PasswordValue(&password),
- "password for authentication")
- ("help,h", "show this usage information")
- ("version", "show version information")
- ("verbose", "increase verbosity")
- ("ipv6", "enable IPv6 support (disabled by default)")
-#ifdef MONGO_SSL
- ("ssl", "use all for connections")
-#endif
- ;
-
- hidden_options.add_options()
- ("dbaddress", po::value<string>(), "dbaddress")
- ("files", po::value< vector<string> >(), "files")
- ("nokillop", "nokillop") // for testing, kill op will also be disabled automatically if the tests starts a mongo program
- ("autokillop", "autokillop") // for testing, will kill op without prompting
- ;
-
- positional_options.add("dbaddress", 1);
- positional_options.add("files", -1);
-
- cmdline_options.add(shell_options).add(hidden_options);
-
- po::variables_map params;
-
- /* using the same style as db.cpp uses because eventually we're going
- * to merge some of this stuff. */
- int command_line_style = (((po::command_line_style::unix_style ^
- po::command_line_style::allow_guessing) |
- po::command_line_style::allow_long_disguise) ^
- po::command_line_style::allow_sticky);
-
- try {
- po::store(po::command_line_parser(argc, argv).options(cmdline_options).
- positional(positional_options).
- style(command_line_style).run(), params);
- po::notify(params);
- }
- catch (po::error &e) {
- cout << "ERROR: " << e.what() << endl << endl;
- show_help_text(argv[0], shell_options);
- return mongo::EXIT_BADOPTIONS;
- }
-
- // hide password from ps output
- for (int i=0; i < (argc-1); ++i) {
- if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--password")) {
- char* arg = argv[i+1];
- while (*arg) {
- *arg++ = 'x';
- }
- }
- }
-
- if (params.count("shell")) {
- runShell = true;
- }
- if (params.count("nodb")) {
- nodb = true;
- }
- if (params.count("norc")) {
- norc = true;
- }
- if (params.count("help")) {
- show_help_text(argv[0], shell_options);
- return mongo::EXIT_CLEAN;
- }
- if (params.count("files")) {
- files = params["files"].as< vector<string> >();
- }
- if (params.count("version")) {
- cout << "MongoDB shell version: " << mongo::versionString << endl;
- return mongo::EXIT_CLEAN;
- }
- if (params.count("quiet")) {
- mongo::cmdLine.quiet = true;
- }
-#ifdef MONGO_SSL
- if (params.count("ssl")) {
- mongo::cmdLine.sslOnNormalPorts = true;
- }
-#endif
- if (params.count("nokillop")) {
- mongo::shellUtils::_nokillop = true;
- }
- if (params.count("autokillop")) {
- autoKillOp = true;
- }
-
-
-
- /* 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 (nodb) {
- files.insert(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 && !fileExists(dbaddress))) {
- url = dbaddress;
- }
- else {
- files.insert(files.begin(), dbaddress);
- }
- }
- }
- if (params.count("ipv6")) {
- mongo::enableIPv6();
- }
- if (params.count("verbose")) {
- logLevel = 1;
- }
-
- if ( ! mongo::cmdLine.quiet )
- cout << "MongoDB shell version: " << mongo::versionString << endl;
-
- mongo::UnitTest::runTests();
-
- if ( !nodb ) { // connect to db
- //if ( ! mongo::cmdLine.quiet ) cout << "url: " << url << endl;
-
- stringstream ss;
- if ( mongo::cmdLine.quiet )
- ss << "__quiet = true;";
- ss << "db = connect( \"" << fixHost( url , dbhost , port ) << "\")";
-
- mongo::shellUtils::_dbConnect = ss.str();
-
- if ( params.count( "password" )
- && ( password.empty() ) ) {
- password = mongo::askPassword();
- }
-
- if ( username.size() && password.size() ) {
- stringstream ss;
- ss << "if ( ! db.auth( \"" << username << "\" , \"" << password << "\" ) ){ throw 'login failed'; }";
- mongo::shellUtils::_dbAuth = ss.str();
- }
-
- }
-
- mongo::ScriptEngine::setConnectCallback( mongo::shellUtils::onConnect );
- mongo::ScriptEngine::setup();
- mongo::globalScriptEngine->setScopeInitCallback( mongo::shellUtils::initScope );
- auto_ptr< mongo::Scope > scope( mongo::globalScriptEngine->newScope() );
- shellMainScope = scope.get();
-
- if( runShell )
- cout << "type \"help\" for help" << endl;
-
- if ( !script.empty() ) {
- mongo::shellUtils::MongoProgramScope s;
- if ( ! scope->exec( script , "(shell eval)" , true , true , false ) )
- return -4;
- }
-
- for (size_t i = 0; i < files.size(); i++) {
- mongo::shellUtils::MongoProgramScope s;
-
- if ( files.size() > 1 )
- cout << "loading file: " << files[i] << endl;
-
- if ( ! scope->execFile( files[i] , false , true , false ) ) {
- cout << "failed to load: " << files[i] << endl;
- return -3;
- }
- }
-
- if ( files.size() == 0 && script.empty() ) {
- runShell = true;
- }
-
- if ( runShell ) {
-
- mongo::shellUtils::MongoProgramScope s;
-
- if (!norc) {
- string rcLocation;
-#ifndef _WIN32
- if ( getenv("HOME") != NULL )
- rcLocation = str::stream() << getenv("HOME") << "/.mongorc.js" ;
-#else
- if ( getenv("HOMEDRIVE") != NULL && getenv("HOMEPATH") != NULL )
- rcLocation = str::stream() << getenv("HOMEDRIVE") << getenv("HOMEPATH") << "\\.mongorc.js";
-#endif
- if ( !rcLocation.empty() && fileExists(rcLocation) ) {
- if ( ! scope->execFile( rcLocation , false , true , false , 0 ) ) {
- cout << "The \".mongorc.js\" file located in your home folder could not be executed" << endl;
- return -5;
- }
- }
- }
-
- shellHistoryInit();
-
- string prompt;
- int promptType;
-
- //v8::Handle<v8::Object> shellHelper = baseContext_->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject();
-
- while ( 1 ) {
- inMultiLine = 0;
- gotInterrupted = 0;
-// shellMainScope->localConnect;
- //DBClientWithCommands *c = getConnection( JSContext *cx, JSObject *obj );
-
- promptType = scope->type("prompt");
- if (promptType == String){
- prompt = scope->getString("prompt");
- } else if (promptType == Code) {
- scope->exec("__prompt__ = prompt();", "", false, false, false, 0);
- prompt = scope->getString("__prompt__");
- } else {
- prompt = sayReplSetMemberState()+"> ";
- }
-
- char * line = shellReadline( prompt.c_str() );
-
- if ( line ) {
- while (startsWith(line, "> "))
- line += 2;
-
- while ( line[0] == ' ' )
- line++;
- }
-
- if ( ! line || ( strlen(line) == 4 && strstr( line , "exit" ) ) ) {
- cout << "bye" << endl;
- break;
- }
-
- string code = line;
- if ( code == "exit" || code == "exit;" ) {
- break;
- }
- if ( code.size() == 0 )
- continue;
-
- code = finishCode( code );
- if ( gotInterrupted ) {
- cout << endl;
- continue;
- }
-
- if ( code.size() == 0 )
- break;
-
- bool wascmd = false;
- {
- string cmd = line;
- if ( cmd.find( " " ) > 0 )
- cmd = cmd.substr( 0 , cmd.find( " " ) );
-
- 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 );
- wascmd = true;
- }
- }
- catch ( std::exception& e ) {
- cout << "error2:" << e.what() << endl;
- wascmd = true;
- }
- }
-
- }
-
- 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 ) {
- cout << "error:" << e.what() << endl;
- }
- }
-
- shellHistoryAdd( line );
- }
-
- shellHistoryDone();
- }
-
- mongo::dbexitCalled = true;
- return 0;
-}
-
-int main(int argc, char* argv[]) {
- static mongo::StaticObserver staticObserver;
- try {
- return _main( argc , argv );
- }
- catch ( mongo::DBException& e ) {
- cerr << "exception: " << e.what() << endl;
- return -1;
- }
-}
-
-