//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
 * cmd.cpp
 */

#include "cmd.h"


cTokenControl::cTokenControl ()
{
  dwLength = 0;
  dwMaxLength = 0;
  ppszTokens = NULL;  

}

cTokenControl::~cTokenControl ()
{
  if ((ppszTokens == NULL) && (dwLength != 0))
    {
      IntErr (_T("In destructor cTokenControl"));
      return;
    }

  /* free memory if something is there */
  for (DWORD dwCurr = 0; dwCurr < dwLength; dwCurr++)
    {
      free (ppszTokens[dwCurr]);      

    }

  free (ppszTokens);
}

BOOL 
cTokenControl::add (const TCHAR *const szStr, DWORD dwNumCharsWithNull)
{
  BOOL returnVal = FALSE;

  if (dwLength >= dwMaxLength)
    {
      /* expand array */
      dwMaxLength = dwMaxLength * 2 + 1;

      TCHAR ** ppszTemp = (TCHAR **) malloc (dwMaxLength * sizeof (*ppszTokens));
      
      if (ppszTemp == NULL)
	{
	  Error (_T("Couldn't get memory."));
	  goto cleanAndReturn;
	}
	  
      for (DWORD dwCurr = 0; dwCurr < dwLength; dwCurr++)
	{
	  ppszTemp[dwCurr] = ppszTokens[dwCurr];
	}

      free (ppszTokens);

      ppszTokens = ppszTemp;
    }

  /* add string */
  ppszTokens[dwLength] = (TCHAR *) malloc (sizeof (TCHAR) * dwNumCharsWithNull);

  if (ppszTokens[dwLength] == NULL)
    {
      Error (_T("Couldn't get memory."));
      goto cleanAndReturn;
    }

  _tcsncpy_s (ppszTokens[dwLength], dwNumCharsWithNull, szStr, _TRUNCATE);

  /* force null terminate */
  ppszTokens[dwLength][dwNumCharsWithNull - 1] = 0;
  
  dwLength++;
  
  returnVal = TRUE;

 cleanAndReturn:
  return (returnVal);
}

DWORD
cTokenControl::listLength ()
{
  return (dwLength);
}

TCHAR *& 
cTokenControl::operator[] (const DWORD index)
{
  return (ppszTokens[index]);
}


BOOL
tokenize (const TCHAR *const szStr, cTokenControl * tokens)
{
  if (!szStr || !tokens)
    {
      return (FALSE);
    }

  /***** make a copy */

  /* get size */
  DWORD dwNeedSize = _tcslen(szStr) + 1;

  if (dwNeedSize == 0)
    {
      /* overflow */
      return(FALSE);
    }

  TCHAR * szCopy = (TCHAR *) malloc (dwNeedSize * sizeof (*szStr));

  if (!szCopy)
    {
      Error (_T("Couldn't allocate memory."));
      return (FALSE);
    }
  
  _tcsncpy_s (szCopy, dwNeedSize, szStr, _TRUNCATE);
  /* force null term */
  szCopy[dwNeedSize - 1] = 0;

  /***** tokenize it */
  /* pointer only changes, so make underlying object constant */
  const TCHAR * tok = NULL;

  const TCHAR *const szSeps = _T(" ");

  tok = _tcstok (szCopy, szSeps);

  while (tok != NULL)
    {
      tokens->add (tok, _tcslen (tok) + 1);
      
      tok = _tcstok (NULL, szSeps);
    }

  free (szCopy);

  return (TRUE);
}  


/*
 * returns true if you can find it and false otherwise */

BOOL
findTokenI(cTokenControl * tokens,
	   const TCHAR *const szLookingFor,
	   DWORD * pdwIndex)
{
  /* start at zero */
  return (findTokenI (tokens, szLookingFor, 0, pdwIndex));
}

BOOL
findTokenI(cTokenControl * tokens,
	   const TCHAR *const szLookingFor, DWORD dwStartingIndex,
	   DWORD * pdwIndex)
{
  if (!tokens || !szLookingFor || !pdwIndex)
    {
      return(FALSE);
    }

  DWORD dwCurrIndex = dwStartingIndex;
  
  /* order matters */
  while ((dwCurrIndex < tokens->listLength()) && 
	 (_tcsicmp (szLookingFor, (*tokens)[dwCurrIndex]) != 0))
    {
      dwCurrIndex++;
    }

  if (dwCurrIndex >= tokens->listLength())
    {
      /* we didn't find it */
      return (FALSE);
    }
  else
    {
      *pdwIndex = dwCurrIndex;
      return (TRUE);
    }
}




/*
 * returns true if it is found and false if it isn't found or can't convert 
 */
DWORD
getOptionPlusTwo (cTokenControl * tokens, 
		  DWORD dwCurrSpot,
		  const TCHAR *const szName,
		  double * pdo1, double * pdo2)
{
  DWORD returnVal = CMD_ERROR;

  if (!tokens || !dwCurrSpot || !szName || !pdo1 || !pdo2)
    {
      goto cleanAndReturn;
    }

  if (dwCurrSpot >= tokens->listLength())
    {
      /* no tokens on the list, so bail out */
      returnVal = CMD_NOT_FOUND;
      goto cleanAndReturn;
    }

  if (_tcsicmp (szName, (*tokens)[dwCurrSpot]) != 0)
    {
      /* didn't fine it, so return */
      returnVal = CMD_NOT_FOUND;
      goto cleanAndReturn;
    }

  DWORD dwEnd;
  if ((DWordAdd (dwCurrSpot, 2, &dwEnd) != S_OK) || 
      (dwEnd >= tokens->listLength()))
    {
      Error (_T("Not enough params for %s."), szName);
      goto cleanAndReturn;
    }

  /* found it, so pull next two values */

  BOOL bLocalReturn = TRUE;

  if (!strToDouble ((*tokens)[dwCurrSpot + 1], pdo1))
    {
      /* it failed */
      Error (_T("Could not convert %s to a double.  Is the number in ")
	     _T("common decimal form?"), (*tokens)[dwCurrSpot + 1]);
      bLocalReturn = FALSE;
    }

  if (!strToDouble ((*tokens)[dwCurrSpot + 2], pdo2))
    {
      /* it failed */
      Error (_T("Could not convert %s to a double.  Is the number in ")
	     _T("common decimal form?"), (*tokens)[dwCurrSpot + 2]);
      bLocalReturn = FALSE;
    }

  if (!bLocalReturn)
    {
      goto cleanAndReturn;
    }

  returnVal = CMD_FOUND_IT;
 cleanAndReturn:
  
  return (returnVal);
}

/*
 * if couldn't find it or other error return false.  Else return true.
 * pullVal isn't touched on error.
 */
BOOL
getOptionTimeUlonglong (cTokenControl * tokens, 
			const TCHAR *const szCmdName, 
			ULONGLONG * pullVal)
{
  BOOL returnVal = FALSE;

  if (!tokens || !szCmdName || !pullVal)
    {
      return (FALSE);
    }

  DWORD dwIndex;

  if (!findTokenI (tokens, szCmdName, &dwIndex))
    {
      goto cleanAndReturn;
    }
  
  /* we found it */
  if ((dwIndex >= tokens->listLength() - 1) || (dwIndex == MAX_DWORD))
    {
      /* last param in list */
      Error (_T("%s given no parameter."), szCmdName);
      goto cleanAndReturn;
    }

  ULONGLONG ullTimeS; 
  
  /* overflow check is above */
  dwIndex++;

  /* there is one more param */
  if (!strToSeconds ((*tokens)[dwIndex], &ullTimeS))
    {
      Error (_T("Error on param %s.  Couldn't convert %s to ")
	     _T("seconds."),
	     szCmdName, (*tokens)[dwIndex]);
      goto cleanAndReturn;
    }

  *pullVal = ullTimeS;

  returnVal = TRUE;

 cleanAndReturn:
  return (returnVal);
}

/*
 * return true if the option is present and false otherwise
 */
BOOL
isOptionPresent (cTokenControl * tokens, 
		 const TCHAR *const szOption)
{
  if (!szOption)
    {
      return (FALSE);
    }

  DWORD dwIndex;

  if (findTokenI (tokens, szOption, &dwIndex))
    {
      return (TRUE);
    }
  else
    {
      return (FALSE);
    }
}
 
/*
 * get an option, converting to double.
 *
 * Returns true if value is found and converted and false otherwise.
 */
BOOL
getOptionDouble (cTokenControl * tokens, 
		 const TCHAR *const szCmdName, 
		 double * pdoVal)
{
  BOOL returnVal = FALSE;

  if (!tokens || !szCmdName || !pdoVal)
    {
      return (FALSE);
    }

  DWORD dwIndex;

  if (!findTokenI (tokens, szCmdName, &dwIndex))
    {
      goto cleanAndReturn;
    }
  
  /* we found it */
  if ((dwIndex >= tokens->listLength() - 1) || (dwIndex == MAX_DWORD))
    {
      /* last param in list */
      Error (_T("%s given no parameter."), szCmdName);
      goto cleanAndReturn;
    }

  /* overflow check is above */
  dwIndex++;

  double doTemp;

  /* there is one more param */
  if (!strToDouble ((*tokens)[dwIndex], &doTemp))
    {
      Error (_T("Error on param %s.  Couldn't convert %s to ")
	     _T("double."),
	     szCmdName, (*tokens)[dwIndex]);
      goto cleanAndReturn;
    }

  *pdoVal = doTemp;

  returnVal = TRUE;

 cleanAndReturn:
  return (returnVal);
}


/*
 * get an option, converting to double.
 *
 * Returns true if value is found and converted and false otherwise.
 */
BOOL
getOptionString (cTokenControl * tokens, 
		 const TCHAR *const szCmdName, 
		 TCHAR ** pszVal)
{
  BOOL returnVal = FALSE;

  if (!tokens || !szCmdName || !pszVal)
    {
      return (FALSE);
    }

  DWORD dwIndex;

  if (!findTokenI (tokens, szCmdName, &dwIndex))
    {
      goto cleanAndReturn;
    }
  
  /* we found it */
  if ((dwIndex >= tokens->listLength() - 1) || (dwIndex == MAX_DWORD))
    {
      /* last param in list */
      Error (_T("%s given no parameter."), szCmdName);
      goto cleanAndReturn;
    }

  /* overflow check is above */
  dwIndex++;

  *pszVal = (*tokens)[dwIndex];

  returnVal = TRUE;

 cleanAndReturn:
  return (returnVal);
}


BOOL
getOptionDWord (cTokenControl * tokens, 
		const TCHAR *const szCmdName, 
		DWORD * pdwVal)
{
  BOOL returnVal = FALSE;

  if (!tokens || !szCmdName || !pdwVal)
    {
      return (FALSE);
    }

  DWORD dwIndex;

  if (!findTokenI (tokens, szCmdName, &dwIndex))
    {
      goto cleanAndReturn;
    }
  
  /* we found it */
  if ((dwIndex >= tokens->listLength() - 1) || (dwIndex == MAX_DWORD))
    {
      /* last param in list */
      Error (_T("%s given no parameter."), szCmdName);
      goto cleanAndReturn;
    }

  /* overflow check is above */
  dwIndex++;

  TCHAR * endPtr;

  /* use strtoul to convert the numerical part, if possible */
  /* 10 is the base */
  unsigned long lNumericalPart = _tcstoul ((*tokens)[dwIndex], &endPtr, 10);

  if (!endPtr || *endPtr != _T('\0'))
    {
      Error (_T("Expected a number and got %s."), (*tokens)[dwIndex]);
      goto cleanAndReturn;
    }

  /* at this point we have the numerical part */
  if (lNumericalPart > MAX_DWORD)
    {
      Error (_T("Value is too large. %u > MAX_DWORD"), lNumericalPart);
      goto cleanAndReturn;
    }

  *pdwVal = (DWORD) lNumericalPart;

  returnVal = TRUE;

 cleanAndReturn:
  return (returnVal);
}
