/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ ////////////////////////////////////////////////////////////////////////////// // // // pfConsoleCmd Functions // // // ////////////////////////////////////////////////////////////////////////////// #include "pfConsoleCmd.h" #include "hsUtils.h" ////////////////////////////////////////////////////////////////////////////// //// pfConsoleCmdGroup Stuff ///////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// pfConsoleCmdGroup *pfConsoleCmdGroup::fBaseCmdGroup = nil; UInt32 pfConsoleCmdGroup::fBaseCmdGroupRef = 0; //// Constructor & Destructor //////////////////////////////////////////////// pfConsoleCmdGroup::pfConsoleCmdGroup( char *name, char *parent ) { Dummy(); DummyJunior(); DummyNet(); DummyAvatar(); DummyCCR(); fNext = nil; fPrevPtr = nil; fCommands = nil; fSubGroups = nil; if( name == nil ) { /// Create base hsStrncpy( fName, "base", sizeof( fName ) ); fParentGroup = nil; } else { pfConsoleCmdGroup *group = GetBaseGroup(); if( parent != nil && parent[ 0 ] != 0 ) { group = group->FindSubGroupRecurse( parent ); hsAssert( group != nil, "Trying to register group under nonexistant group!" ); } hsStrncpy( fName, name, sizeof( fName ) ); group->AddSubGroup( this ); fParentGroup = group; } } pfConsoleCmdGroup::~pfConsoleCmdGroup() { if( this != fBaseCmdGroup ) { Unlink(); DecBaseCmdGroupRef(); } } //// GetBaseGroup //////////////////////////////////////////////////////////// pfConsoleCmdGroup *pfConsoleCmdGroup::GetBaseGroup( void ) { if( fBaseCmdGroup == nil ) { /// Initialize base group fBaseCmdGroup = TRACKED_NEW pfConsoleCmdGroup( nil, nil ); } return fBaseCmdGroup; } //// DecBaseCmdGroupRef ////////////////////////////////////////////////////// void pfConsoleCmdGroup::DecBaseCmdGroupRef( void ) { fBaseCmdGroupRef--; if( fBaseCmdGroupRef == 0 ) { delete fBaseCmdGroup; fBaseCmdGroup = nil; } } //// Add Functions /////////////////////////////////////////////////////////// void pfConsoleCmdGroup::AddCommand( pfConsoleCmd *cmd ) { cmd->Link( &fCommands ); fBaseCmdGroupRef++; } void pfConsoleCmdGroup::AddSubGroup( pfConsoleCmdGroup *group ) { group->Link( &fSubGroups ); fBaseCmdGroupRef++; } //// FindCommand ///////////////////////////////////////////////////////////// // No longer recursive. pfConsoleCmd *pfConsoleCmdGroup::FindCommand( char *name ) { pfConsoleCmd *cmd; hsAssert( name != nil, "nil name passed to FindCommand()" ); /// Only search locally for( cmd = fCommands; cmd != nil; cmd = cmd->GetNext() ) { if( strcmp( cmd->GetName(), name ) == 0 ) return cmd; } return nil; } //// FindNestedPartialCommand //////////////////////////////////////////////// // Okay. YAFF. This one searches through the group and its children looking // for a partial command based on the string given. The counter determines // how many matches it skips before returning a match. (That way you can // cycle through matches by sending 1 + the last counter every time). pfConsoleCmd *pfConsoleCmdGroup::FindNestedPartialCommand( char *name, UInt32 *counter ) { pfConsoleCmd *cmd; pfConsoleCmdGroup *group; hsAssert( name != nil, "nil name passed to FindNestedPartialCommand()" ); hsAssert( counter != nil, "nil counter passed to FindNestedPartialCommand()" ); // Try us for( cmd = fCommands; cmd != nil; cmd = cmd->GetNext() ) { if( _strnicmp( cmd->GetName(), name, strlen( name ) ) == 0 ) { if( *counter == 0 ) return cmd; (*counter)--; } } // Try children for( group = fSubGroups; group != nil; group = group->GetNext() ) { cmd = group->FindNestedPartialCommand( name, counter ); if( cmd != nil ) return cmd; } return nil; } //// FindSubGroup //////////////////////////////////////////////////////////// pfConsoleCmdGroup *pfConsoleCmdGroup::FindSubGroup( char *name ) { pfConsoleCmdGroup *group; hsAssert( name != nil, "nil name passed to FindSubGroup()" ); /// Only search locally for( group = fSubGroups; group != nil; group = group->GetNext() ) { if( strcmp( group->GetName(), name ) == 0 ) return group; } return nil; } //// FindSubGroupRecurse ///////////////////////////////////////////////////// // Resurces through a string, finding the final subgroup that the string // represents. Parses with spaces, _ or . as the separators. Copies string. pfConsoleCmdGroup *pfConsoleCmdGroup::FindSubGroupRecurse( const char *name ) { char *ptr, *string; pfConsoleCmdGroup *group; static char seps[] = " ._"; string = TRACKED_NEW char[ strlen( name ) + 1 ]; hsAssert( string != nil, "Cannot allocate string in FindSubGroupRecurse()" ); strcpy( string, name ); /// Scan for subgroups group = pfConsoleCmdGroup::GetBaseGroup(); ptr = strtok( string, seps ); while( ptr != nil ) { // Take this token and check to see if it's a group group = group->FindSubGroup( ptr ); hsAssert( group != nil, "Invalid group name to FindSubGroupRecurse()" ); ptr = strtok( nil, seps ); } delete [] string; return group; } //// FindCommandNoCase /////////////////////////////////////////////////////// // Case-insensitive version of FindCommand. pfConsoleCmd *pfConsoleCmdGroup::FindCommandNoCase( char *name, UInt8 flags, pfConsoleCmd *start ) { pfConsoleCmd *cmd; hsAssert( name != nil, "nil name passed to FindCommandNoCase()" ); /// Only search locally if( start == nil ) start = fCommands; else start = start->GetNext(); if( flags & kFindPartial ) { for( cmd = start; cmd != nil; cmd = cmd->GetNext() ) { if( _strnicmp( cmd->GetName(), name, strlen( name ) ) == 0 ) return cmd; } } else { for( cmd = start; cmd != nil; cmd = cmd->GetNext() ) { if( stricmp( cmd->GetName(), name ) == 0 ) return cmd; } } return nil; } //// FindSubGroupNoCase ////////////////////////////////////////////////////// pfConsoleCmdGroup *pfConsoleCmdGroup::FindSubGroupNoCase( char *name, UInt8 flags, pfConsoleCmdGroup *start ) { pfConsoleCmdGroup *group; hsAssert( name != nil, "nil name passed to FindSubGroupNoCase()" ); /// Only search locally if( start == nil ) start = fSubGroups; else start = start->GetNext(); if( flags & kFindPartial ) { for( group = start; group != nil; group = group->GetNext() ) { if( _strnicmp( group->GetName(), name, strlen( name ) ) == 0 ) return group; } } else { for( group = start; group != nil; group = group->GetNext() ) { if( stricmp( group->GetName(), name ) == 0 ) return group; } } return nil; } //// Link & Unlink /////////////////////////////////////////////////////////// void pfConsoleCmdGroup::Link( pfConsoleCmdGroup **prevPtr ) { hsAssert( fNext == nil && fPrevPtr == nil, "Trying to link console group that's already linked!" ); fNext = *prevPtr; if( *prevPtr ) (*prevPtr)->fPrevPtr = &fNext; fPrevPtr = prevPtr; *fPrevPtr = this; } void pfConsoleCmdGroup::Unlink( void ) { hsAssert( fNext != nil || fPrevPtr != nil, "Trying to unlink console group that isn't linked!" ); if( fNext ) fNext->fPrevPtr = fPrevPtr; *fPrevPtr = fNext; } int pfConsoleCmdGroup::IterateCommands(pfConsoleCmdIterator* t, int depth) { pfConsoleCmd *cmd; cmd = this->GetFirstCommand(); while(cmd) { t->ProcessCmd(cmd,depth); cmd = cmd->GetNext(); } pfConsoleCmdGroup *grp; grp = this->GetFirstSubGroup(); while(grp) { if(t->ProcessGroup(grp, depth)) grp->IterateCommands(t, depth+1); grp = grp->GetNext(); } return 0; } ////////////////////////////////////////////////////////////////////////////// //// pfConsoleCmd Functions ////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// char pfConsoleCmd::fSigTypes[ kNumTypes ][ 8 ] = { "int", "float", "bool", "string", "char", "void", "..." }; pfConsoleCmd::pfConsoleCmd( char *group, char *name, char *paramList, char *help, pfConsoleCmdPtr func, hsBool localOnly ) { fNext = nil; fPrevPtr = nil; fFunction = func; fLocalOnly = localOnly; hsStrncpy( fName, name, sizeof( fName ) ); fHelpString = help; ICreateSignature( paramList ); Register( group, name ); } pfConsoleCmd::~pfConsoleCmd() { int i; for( i = 0; i < fSigLabels.GetCount(); i++ ) { if( fSigLabels[ i ] != nil ) delete [] fSigLabels[ i ]; } Unregister(); fSignature.Reset(); fSigLabels.Reset(); } //// ICreateSignature //////////////////////////////////////////////////////// // Creates the signature and sig labels based on the given string. void pfConsoleCmd::ICreateSignature( char *paramList ) { static char seps[] = " :-"; char params[ 256 ]; char *ptr, *nextPtr, *tok, *tok2; int i; /// Simple check if( paramList == nil ) { fSignature.Push( kAny ); fSigLabels.Push( (char *)nil ); return; } /// So we can do stuff to it hsAssert( strlen( paramList ) < sizeof( params ), "Make the (#*$& params string larger!" ); hsStrcpy( params, paramList ); fSignature.Empty(); fSigLabels.Empty(); /// Loop through all the types given in the list ptr = params; do { /// Find break nextPtr = strchr( ptr, ',' ); if( nextPtr != nil ) { *nextPtr = 0; nextPtr++; } /// Do this param tok = strtok( ptr, seps ); if( tok == nil && ptr == params ) break; hsAssert( tok != nil, "Bad parameter list for console command!" ); tok2 = strtok( nil, seps ); if( tok2 != nil ) { // Type and label: assume label second fSigLabels.Push( hsStrcpy( tok2 ) ); } else fSigLabels.Push( (char *)nil ); // Find type for( i = 0; i < kNumTypes; i++ ) { if( strcmp( fSigTypes[ i ], tok ) == 0 ) { fSignature.Push( (UInt8)i ); break; } } hsAssert( i < kNumTypes, "Bad parameter type in console command parameter list!" ); } while( ( ptr = nextPtr ) != nil ); } //// Register //////////////////////////////////////////////////////////////// // Finds the group this command should be in and registers it with that // group. void pfConsoleCmd::Register( char *group, char *name ) { pfConsoleCmdGroup *g; if( group == nil || group[ 0 ] == 0 ) { g = pfConsoleCmdGroup::GetBaseGroup(); g->AddCommand( this ); } else { g = pfConsoleCmdGroup::FindSubGroupRecurse( group ); hsAssert( g != nil, "Trying to register command under nonexistant group!" ); g->AddCommand( this ); } fParentGroup = g; } //// Unregister ////////////////////////////////////////////////////////////// void pfConsoleCmd::Unregister( void ) { Unlink(); pfConsoleCmdGroup::DecBaseCmdGroupRef(); } //// Execute ///////////////////////////////////////////////////////////////// // Run da thing! void pfConsoleCmd::Execute( Int32 numParams, pfConsoleCmdParam *params, void (*PrintFn)( const char * ) ) { fFunction( numParams, params, PrintFn ); } //// Link & Unlink /////////////////////////////////////////////////////////// void pfConsoleCmd::Link( pfConsoleCmd **prevPtr ) { hsAssert( fNext == nil && fPrevPtr == nil, "Trying to link console command that's already linked!" ); fNext = *prevPtr; if( *prevPtr ) (*prevPtr)->fPrevPtr = &fNext; fPrevPtr = prevPtr; *fPrevPtr = this; } void pfConsoleCmd::Unlink( void ) { hsAssert( fNext != nil || fPrevPtr != nil, "Trying to unlink console command that isn't linked!" ); if( fNext ) fNext->fPrevPtr = fPrevPtr; *fPrevPtr = fNext; } //// GetSigEntry ///////////////////////////////////////////////////////////// UInt8 pfConsoleCmd::GetSigEntry( UInt8 i ) { if( fSignature.GetCount() == 0 ) return kNone; if( i < fSignature.GetCount() ) { if( fSignature[ i ] == kEtc ) return kAny; return fSignature[ i ]; } if( fSignature[ fSignature.GetCount() - 1 ] == kEtc ) return kAny; return kNone; } //// GetSignature //////////////////////////////////////////////////////////// // Gets the signature of the command as a string. Format is: // name [ type param [, type param ... ] ] // WARNING: uses a static buffer, so don't rely on the contents if you call // it more than once! (You shouldn't need to, though) const char *pfConsoleCmd::GetSignature( void ) { static char string[ 256 ]; int i; char pStr[ 128 ]; strcpy( string, fName ); for( i = 0; i < fSignature.GetCount(); i++ ) { if( fSigLabels[ i ] == nil ) sprintf( pStr, "%s", fSigTypes[ fSignature[ i ] ] ); else sprintf( pStr, "%s %s", fSigTypes[ fSignature[ i ] ], fSigLabels[ i ] ); hsAssert( strlen( string ) + strlen( pStr ) + 2 < sizeof( string ), "Not enough room for signature string" ); strcat( string, ( i > 0 ) ? ", " : " " ); strcat( string, pStr ); } return string; } ////////////////////////////////////////////////////////////////////////////// //// pfConsoleCmdParam Functions ///////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //// Conversion Functions //////////////////////////////////////////////////// const int & pfConsoleCmdParam::IToInt( void ) const { hsAssert( fType == kInt || fType == kAny, "Trying to use a non-int parameter as an int!" ); static int i; if( fType == kAny ) { hsAssert( fValue.s != nil, "Weird parameter during conversion" ); i = atoi( fValue.s ); return i; } return fValue.i; } const float & pfConsoleCmdParam::IToFloat( void ) const { hsAssert( fType == kFloat || fType == kAny, "Trying to use a non-float parameter as a float!" ); static float f; if( fType == kAny ) { hsAssert( fValue.s != nil, "Weird parameter during conversion" ); f = (float)atof( fValue.s ); return f; } return fValue.f; } const bool & pfConsoleCmdParam::IToBool( void ) const { hsAssert( fType == kBool || fType == kAny, "Trying to use a non-bool parameter as a bool!" ); static bool b; if( fType == kAny ) { hsAssert( fValue.s != nil, "Weird parameter during conversion" ); if( atoi( fValue.s ) > 0 || stricmp( fValue.s, "true" ) == 0 ) b = true; else b = false; return b; } return fValue.b; } const pfConsoleCmdParam::CharPtr & pfConsoleCmdParam::IToString( void ) const { hsAssert( fType == kString || fType == kAny, "Trying to use a non-string parameter as a string!" ); return fValue.s; } const char & pfConsoleCmdParam::IToChar( void ) const { hsAssert( fType == kChar || fType == kAny, "Trying to use a non-char parameter as a char!" ); static char c; if( fType == kAny ) { hsAssert( fValue.s != nil, "Weird parameter during conversion" ); c = fValue.s[ 0 ]; return c; } return fValue.c; }