//=============================================================================// // // Purpose: Console Commands // //=============================================================================// #include "tier0/tslist.h" #include "tier0/memstd.h" #include "tier0/commandline.h" #include "tier1/cmd.h" #include "tier1/cvar.h" #include "tier1/characterset.h" #include "tier1/utlstring.h" #include "tier1/utlbuffer.h" //----------------------------------------------------------------------------- // Global methods //----------------------------------------------------------------------------- static characterset_t s_BreakSet; static bool s_bBuiltBreakSet = false; //----------------------------------------------------------------------------- // Tokenizer class //----------------------------------------------------------------------------- CCommand::CCommand() { if (!s_bBuiltBreakSet) { s_bBuiltBreakSet = true; CharacterSetBuild(&s_BreakSet, "{}()':"); } Reset(); } //----------------------------------------------------------------------------- // Purpose: constructor // Input : nArgC - // **ppArgV - // source - //----------------------------------------------------------------------------- CCommand::CCommand(int nArgC, const char** ppArgV, cmd_source_t source) { Assert(nArgC > 0); if (!s_bBuiltBreakSet) { s_bBuiltBreakSet = true; CharacterSetBuild(&s_BreakSet, "{}()':"); } Reset(); char* pBuf = m_pArgvBuffer; char* pSBuf = m_pArgSBuffer; m_nArgc = nArgC; for (int i = 0; i < nArgC; ++i) { m_ppArgv[i] = pBuf; ssize_t nLen = strlen(ppArgV[i]); memcpy(pBuf, ppArgV[i], nLen + 1); if (i == 0) { m_nArgv0Size = nLen; } pBuf += nLen + 1; bool bContainsSpace = strchr(ppArgV[i], ' ') != NULL; if (bContainsSpace) { *pSBuf++ = '\"'; } memcpy(pSBuf, ppArgV[i], nLen); pSBuf += nLen; if (bContainsSpace) { *pSBuf++ = '\"'; } if (i != nArgC - 1) { *pSBuf++ = ' '; } } m_nQueuedVal = source; } characterset_t* CCommand::DefaultBreakSet() { return &s_BreakSet; } //----------------------------------------------------------------------------- // Purpose: tokenizer // Input : *pCommand - // source - // *pBreakSet - // Output : true on success, false on failure //----------------------------------------------------------------------------- bool CCommand::Tokenize(const char* pCommand, cmd_source_t source, characterset_t* pBreakSet) { Reset(); m_nQueuedVal = source; if (!pCommand) return false; // Use default break set if (!pBreakSet) { pBreakSet = &s_BreakSet; } // Copy the current command into a temp buffer // NOTE: This is here to avoid the pointers returned by DequeueNextCommand // to become invalid by calling AddText. Is there a way we can avoid the memcpy? size_t nLen = Q_strlen(pCommand); if (nLen >= COMMAND_MAX_LENGTH - 1) { Warning(eDLL_T::COMMON, "%s: Encountered command which overflows the tokenizer buffer... Skipped!\n", __FUNCTION__); return false; } memcpy(m_pArgSBuffer, pCommand, nLen + 1); // Parse the current command into the current command buffer CUtlBuffer bufParse(m_pArgSBuffer, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY); ssize_t nArgvBufferSize = 0; while (bufParse.IsValid() && (m_nArgc < COMMAND_MAX_ARGC)) { char* pArgvBuf = &m_pArgvBuffer[nArgvBufferSize]; ssize_t nMaxLen = COMMAND_MAX_LENGTH - nArgvBufferSize; ssize_t nStartGet = bufParse.TellGet(); ssize_t nSize = bufParse.ParseToken(pBreakSet, pArgvBuf, nMaxLen); if (nSize < 0) break; // Check for overflow condition if (nMaxLen == nSize) { Reset(); return false; } if (m_nArgc == 1) { // Deal with the case where the arguments were quoted m_nArgv0Size = bufParse.TellGet(); bool bFoundEndQuote = m_pArgSBuffer[m_nArgv0Size - 1] == '\"'; if (bFoundEndQuote) { --m_nArgv0Size; } m_nArgv0Size -= nSize; Assert(m_nArgv0Size != 0); // The StartGet check is to handle this case: "foo"bar // which will parse into 2 different args. ArgS should point to bar. bool bFoundStartQuote = (m_nArgv0Size > nStartGet) && (m_pArgSBuffer[m_nArgv0Size - 1] == '\"'); Assert(bFoundEndQuote == bFoundStartQuote); if (bFoundStartQuote) { --m_nArgv0Size; } } m_ppArgv[m_nArgc++] = pArgvBuf; if (m_nArgc >= COMMAND_MAX_ARGC) { Warning(eDLL_T::COMMON, "%s: Encountered command which overflows the argument buffer... Clamped!\n", __FUNCTION__); } nArgvBufferSize += nSize + 1; Assert(nArgvBufferSize <= COMMAND_MAX_LENGTH); } return true; } //----------------------------------------------------------------------------- // Purpose: return boolean depending on if the string only has digits in it // Input : svString - //----------------------------------------------------------------------------- bool CCommand::HasOnlyDigits(int nIndex) const { const char* source = Arg(nIndex); for (size_t i = 0; source[i] != '\0'; i++) { if (!V_isdigit(source[i])) { return false; } } return true; } //----------------------------------------------------------------------------- // Purpose: reset //----------------------------------------------------------------------------- void CCommand::Reset() { m_nArgc = 0; m_nArgv0Size = 0; m_pArgSBuffer[0] = 0; m_nQueuedVal = cmd_source_t::kCommandSrcInvalid; }