# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

command.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "stdmisc.h"
00027 
00028 #include "nel/misc/command.h"
00029 
00030 using namespace std;
00031 using namespace NLMISC;
00032 
00033 namespace NLMISC {
00034 
00035 ICommand::TCommand *ICommand::Commands;
00036 bool ICommand::CommandsInit;
00037 
00038 ICommand::ICommand(const char *commandName, const char *commandHelp, const char *commandArgs)
00039 {
00040         // self registration
00041 
00042         if (!CommandsInit)
00043         {
00044                 //printf("create map\n");
00045                 Commands = new TCommand;
00046                 CommandsInit = true;
00047         }
00048 
00049         TCommand::iterator comm = (*Commands).find(commandName);
00050 
00051         if (comm != (*Commands).end ())
00052         {
00053                 // 2 commands have the same name
00054                 nlstopex (("There are 2 commands that have the same name in the project (command name '%s'), skip the second definition", commandName));
00055         }
00056         else
00057         {
00058                 // insert the new command in the map
00059                 //printf("add command\n");
00060                 HelpString = commandHelp;
00061                 CommandArgs = commandArgs;
00062                 _CommandName = commandName;
00063                 Type = Command;
00064                 (*Commands)[commandName] = this;
00065         }
00066 }
00067 
00068 ICommand::~ICommand()
00069 {
00070         // self deregistration
00071 
00072         if (!CommandsInit)
00073         {
00074                 // should never happen
00075                 nlstop;
00076                 return;
00077         }
00078 
00079         // find the command
00080 
00081         for (TCommand::iterator comm = (*Commands).begin(); comm != (*Commands).end(); comm++)
00082         {
00083                 if ((*comm).second == this)
00084                 {
00085                         //printf("remove command\n");
00086                         (*Commands).erase (comm);
00087 
00088                         if ((*Commands).size() == 0)
00089                         {
00090                                 // if the commands map is empty, destroy it
00091                                 //printf("delete map\n");
00092                                 delete Commands;
00093                                 CommandsInit = false;
00094                         }
00095                         
00096                         return;
00097                 }
00098         }
00099         // commands is not found
00100         nlstop;
00101 }
00102 
00103 void ICommand::execute (const std::string &commandWithArgs, CLog &log, bool quiet)
00104 {
00105         if (!quiet) log.displayNL ("Executing command : '%s'", commandWithArgs.c_str());
00106 
00107         // convert the buffer into string vector
00108 
00109         vector<string> args;
00110         string command;
00111 
00112         bool firstArg = true;
00113         uint i = 0;
00114         while (true)
00115         {
00116                 // skip whitespace
00117                 while (true)
00118                 {
00119                         if (i == commandWithArgs.size())
00120                         {
00121                                 goto end;
00122                         }
00123                         if (commandWithArgs[i] != ' ' && commandWithArgs[i] != '\t' && commandWithArgs[i] != '\n' && commandWithArgs[i] != '\r')
00124                         {
00125                                 break;
00126                         }
00127                         i++;
00128                 }
00129                 
00130                 // get param
00131                 string arg;
00132                 if (commandWithArgs[i] == '\"')
00133                 {
00134                         // starting with a quote "
00135                         i++;
00136                         while (true)
00137                         {
00138                                 if (i == commandWithArgs.size())
00139                                 {
00140                                         if (!quiet) log.displayNL ("Missing end quote character \"");
00141                                         return;
00142                                 }
00143                                 if (commandWithArgs[i] == '"')
00144                                 {
00145                                         i++;
00146                                         break;
00147                                 }
00148                                 if (commandWithArgs[i] == '\\')
00149                                 {
00150                                         // manage escape char backslash
00151                                         i++;
00152                                         if (i == commandWithArgs.size())
00153                                         {
00154                                                 if (!quiet) log.displayNL ("Missing character after the backslash \\ character");
00155                                                 return;
00156                                         }
00157                                         switch (commandWithArgs[i])
00158                                         {
00159                                                 case '\\':      arg += '\\'; break; // double backslash
00160                                                 case 'n':       arg += '\n'; break; // new line
00161                                                 case '"':       arg += '"'; break; // "
00162                                                 default:
00163                                                         if (!quiet) log.displayNL ("Unknown escape code '\\%c'", commandWithArgs[i]);
00164                                                         return;
00165                                         }
00166                                         i++;
00167                                 }
00168                                 else
00169                                 {
00170                                         arg += commandWithArgs[i++];
00171                                 }
00172                         }
00173                 }
00174                 else
00175                 {
00176                         // normal word
00177                         while (true)
00178                         {
00179                                 if (commandWithArgs[i] == '\\')
00180                                 {
00181                                         // manage escape char backslash
00182                                         i++;
00183                                         if (i == commandWithArgs.size())
00184                                         {
00185                                                 if (!quiet) log.displayNL ("Missing character after the backslash \\ character");
00186                                                 return;
00187                                         }
00188                                         switch (commandWithArgs[i])
00189                                         {
00190                                                 case '\\':      arg += '\\'; break; // double backslash
00191                                                 case 'n':       arg += '\n'; break; // new line
00192                                                 case '"':       arg += '"'; break; // "
00193                                                 default:
00194                                                         if (!quiet) log.displayNL ("Unknown escape code '\\%c'", commandWithArgs[i]);
00195                                                         return;
00196                                         }
00197                                         i++;
00198                                 }
00199                                 else
00200                                 {
00201                                         arg += commandWithArgs[i++];
00202                                 }
00203                                 if (i == commandWithArgs.size() || commandWithArgs[i] == ' ' || commandWithArgs[i] == '\t' || commandWithArgs[i] == '\n' || commandWithArgs[i] == '\r')
00204                                 {
00205                                         break;
00206                                 }
00207                         }
00208                 }
00209                 if (firstArg)
00210                 {
00211                         command = arg;
00212                         firstArg = false;
00213                 }
00214                 else
00215                 {
00216                         args.push_back (arg);
00217                 }
00218         }
00219 end:
00220 
00221 /* displays args fordebug purpose
00222         nlinfo ("c '%s'", command.c_str());
00223         for (uint t = 0; t < args.size (); t++)
00224         {
00225                 nlinfo ("p%d '%s'", t, args[t].c_str());
00226         }
00227 */
00228         
00229         // find the command     
00230         TCommand::iterator comm = (*Commands).find(command.c_str());
00231         if (comm == (*Commands).end ())
00232         {
00233                 // the command doesn't exist
00234                 if (!quiet) log.displayNL("Command '%s' not found, try 'help'", command.c_str());
00235         }
00236         else
00237         {
00238                 //printf("execute command\n");
00239                 if (!(*comm).second->execute (args, log))
00240                 {
00241                         if (!quiet) log.displayNL("Bad command usage, try 'help %s'", command.c_str());
00242                 }
00243         }
00244 }
00245 
00246 
00247 /*
00248  * Command name completion.
00249  * Case-sensitive. Displays the list after two calls with the same non-unique completion.
00250  * Completes commands used with prefixes (such as "help " for example) as well.
00251  */
00252 void ICommand::expand (std::string &commandName, NLMISC::CLog &log)
00253 {
00254         // Take out the string before the last separator and remember it as a prefix
00255         uint32 lastseppos = commandName.find_last_of( " " );
00256         string prefix;
00257         bool useprefix;
00258         if ( lastseppos != string::npos )
00259         {
00260                 prefix = commandName.substr( 0, lastseppos+1 );
00261                 commandName.erase( 0, lastseppos+1 );
00262                 useprefix = true;
00263         }
00264         else
00265         {
00266                 useprefix = false;
00267         }
00268 
00269         string lowerCommandName = strlwr(const_cast<const string &>(commandName));
00270         // Build the list of matching command names
00271         vector<string> matchingnames;
00272         for (TCommand::iterator comm = (*Commands).begin(); comm != (*Commands).end(); comm++)
00273         {
00274                 string first = strlwr(const_cast<const string &>((*comm).first));
00275                 if (first.find( lowerCommandName ) == 0)
00276                 {
00277                         matchingnames.push_back( (*comm).first );
00278                 }
00279         }
00280 
00281         // Do not complete if there is no result
00282         if ( matchingnames.empty() )
00283         {
00284                 log.displayNL( "No matching command" );
00285                 goto returnFromExpand;
00286         }
00287 
00288         // Complete if there is a single result
00289         if ( matchingnames.size() == 1 )
00290         {
00291                 commandName = matchingnames.front() + " ";
00292                 goto returnFromExpand;
00293         }
00294 
00295         // Try to complete to the common part if there are several results
00296         {
00297                 // Stop loop when a name size is i or names[i] are different
00298                 string commonstr = commandName;
00299                 uint i = commandName.size();
00300                 while ( true )
00301                 {
00302                         char letter = 0;
00303                         vector<string>::iterator imn;
00304                         for ( imn=matchingnames.begin(); imn!=matchingnames.end(); ++imn )
00305                         {
00306                                 // Return common string if the next letter is not the same in all matching names
00307                                 if ( ((*imn).size() == i) || ( (letter!=0) && ((*imn)[i] != letter) ) )
00308                                 {
00309                                         log.displayNL( "(Matching command not unique)" );
00310                                         static string lastCommandName;
00311                                         commandName = commonstr;
00312                                         if ( lastCommandName == commandName )
00313                                         {
00314                                                 // Display all the matching names 
00315                                                 vector<string>::iterator imn2;
00316                                                 stringstream ss;
00317                                                 ss << "Matching commands:" << endl;
00318                                                 for ( imn2=matchingnames.begin(); imn2!=matchingnames.end(); ++imn2 )
00319                                                 {
00320                                                         ss << " " << (*imn2);
00321                                                 }
00322                                                 log.displayNL( "%s", ss.str().c_str() );
00323                                         }
00324                                         lastCommandName = commandName;
00325                                         goto returnFromExpand;
00326                                 }
00327                                 // Add the next letter to the common string if it is the same in all matching names
00328                                 else if ( letter == 0 )
00329                                 {
00330                                         letter = (*imn)[i];
00331                                 }
00332                         }
00333                         commonstr += letter;
00334                         ++i;
00335                 }
00336         }
00337 
00338 returnFromExpand:
00339 
00340         // Put back the prefix
00341         if ( useprefix )
00342         {
00343                 commandName = prefix + commandName;
00344         }
00345 }
00346 
00347 
00348 void ICommand::serialCommands (IStream &f)
00349 {
00350         vector<CSerialCommand> cmd;
00351         for (TCommand::iterator comm = (*Commands).begin(); comm != (*Commands).end(); comm++)
00352         {
00353                 cmd.push_back (CSerialCommand ((*comm).first, (*comm).second->Type));
00354         }
00355         f.serialCont (cmd);
00356 }
00357 
00358 
00359 NLMISC_COMMAND(help,"display help on a specific variable/commands or on all variables and commands", "[<variable>|<command>]")
00360 {       
00361         if (args.size() == 0)
00362         {
00363                 // display all commands
00364                 for (TCommand::iterator comm = (*Commands).begin(); comm != (*Commands).end(); comm++)
00365                 {
00366                         log.displayNL("%-15s: %s", (*comm).first.c_str(), (*comm).second->HelpString.c_str());
00367                 }
00368         }
00369         else if (args.size() == 1)
00370         {
00371                 // display help of the command
00372                 TCommand::iterator comm = (*Commands).find(args[0].c_str());
00373                 if (comm == (*Commands).end ())
00374                 {
00375                         log.displayNL("command '%s' not found", args[0].c_str());
00376                 }
00377                 else
00378                 {
00379                         log.displayNL("%s", (*comm).second->HelpString.c_str());
00380                         log.displayNL("usage: %s %s", (*comm).first.c_str(), (*comm).second->CommandArgs.c_str(), (*comm).second->HelpString.c_str());
00381                 }
00382         }
00383         else
00384         {
00385                 return false;
00386         }
00387         return true;
00388 }
00389 
00390 } // NLMISC