ACE_TCHAR *
ACE_Ini_ImpExp::squish (ACE_TCHAR *src)
{
  ACE_TCHAR *cp = 0;

  if (src == 0)
    return 0;

  // Start at the end and work backwards over all whitespace.
  for (cp = src + ACE_OS::strlen (src) - 1;
       cp != src;
       --cp)
    if (!ACE_OS::ace_isspace (*cp))
      break;
  cp[1] = '\0';          // Chop trailing whitespace

  // Now start at the beginning and move over all whitespace.
  for (cp = src; ACE_OS::ace_isspace (*cp); ++cp)
    continue;

  return cp;
}

#include "ace/Configuration_Import_Export.h"
#include "ace/OS_Errno.h"
#include "ace/OS_NS_stdio.h"
#include "ace/OS_NS_ctype.h"
#include "ace/OS_NS_string.h"

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_Config_ImpExp_Base::ACE_Config_ImpExp_Base (ACE_Configuration& config)
  : config_ (config)
{
}

ACE_Config_ImpExp_Base::~ACE_Config_ImpExp_Base (void)
{
}

ACE_Registry_ImpExp::ACE_Registry_ImpExp (ACE_Configuration& config)
    : ACE_Config_ImpExp_Base (config)
{
}

ACE_Registry_ImpExp::~ACE_Registry_ImpExp (void)
{
}

// Imports the configuration database from filename.
// No existing data is removed.
int
ACE_Registry_ImpExp::import_config (const ACE_TCHAR* filename)
{
  if (0 == filename)
    {
      errno = EINVAL;
      return -1;
    }
  FILE* in = ACE_OS::fopen (filename, ACE_TEXT ("r"));
  if (!in)
    return -1;

  u_int buffer_size = 4096;
  u_int read_pos = 0;
  ACE_TCHAR *buffer = 0;
  ACE_NEW_NORETURN (buffer, ACE_TCHAR[buffer_size]);
  if (!buffer)
    {
      ACE_Errno_Guard guard (errno);
      (void) ACE_OS::fclose (in);
      return -1;
    }
  ACE_Configuration_Section_Key section;
  ACE_TCHAR *end = 0;

  while (ACE_OS::fgets (buffer+read_pos, buffer_size - read_pos, in))
    {
      // Check if we got all the line.
      end = ACE_OS::strrchr (buffer + read_pos,
                             ACE_TEXT ('\n')); // look for end of line
      if (!end) // we havn't reach the end of the line yet
        {
          // allocate a new buffer - double size the previous one
          ACE_TCHAR *temp_buffer;
          ACE_NEW_NORETURN (temp_buffer, ACE_TCHAR[buffer_size * 2]);
          if (!temp_buffer)
            {
              ACE_Errno_Guard guard (errno);
              delete [] buffer;
              (void) ACE_OS::fclose (in);
              return -1;
            }

          // copy the beginnning of the line
          ACE_OS::memcpy (temp_buffer, buffer, buffer_size);
          read_pos = buffer_size - 1;
          buffer_size *= 2;
          delete [] buffer;
          buffer = temp_buffer;
          continue;
        }
      read_pos = 0;

      // Check for a comment
      if (buffer[0] == ACE_TEXT (';') || buffer[0] == ACE_TEXT ('#'))
        continue;

      if (buffer[0] == ACE_TEXT ('['))
        {
          // We have a new section here, strip out the section name
          end = ACE_OS::strrchr (buffer, ACE_TEXT (']'));
          if (!end)
            {
              ACE_OS::fclose (in);
              delete [] buffer;
              return -3;
            }
          *end = 0;

          if (config_.expand_path (config_.root_section (), buffer + 1, section, 1))
            {
              ACE_OS::fclose (in);
              delete [] buffer;
              return -3;
            }
          continue;
        }              // end if firs char is a [

      if (buffer[0] == ACE_TEXT ('"'))
        {
          // we have a value
          end = ACE_OS::strchr (buffer+1, '"');
          if (!end)  // no closing quote, not a value so just skip it
            continue;

          // null terminate the name
          *end = 0;
          ACE_TCHAR* name = buffer + 1;
          end+=2;
          // determine the type
          if (*end == '\"')
            {
              // string type
              // truncate trailing "
              ++end;
              ACE_TCHAR* trailing = ACE_OS::strrchr (end, '"');
              if (trailing)
                *trailing = 0;
              if (config_.set_string_value (section, name, end))
                {
                  ACE_OS::fclose (in);
                  delete [] buffer;
                  return -4;
                }
            }
          else if (ACE_OS::strncmp (end, ACE_TEXT ("dword:"), 6) == 0)
            {
              // number type
              ACE_TCHAR* endptr = 0;
              unsigned long value = ACE_OS::strtoul (end + 6, &endptr, 16);
              if (config_.set_integer_value (section, name,
                                             static_cast<u_int> (value)))
                {
                  ACE_OS::fclose (in);
                  delete [] buffer;
                  return -4;
                }
            }
          else if (ACE_OS::strncmp (end, ACE_TEXT ("hex:"), 4) == 0)
            {
              // binary type
              size_t string_length = ACE_OS::strlen (end + 4);
              // divide by 3 to get the actual buffer length
              size_t length = string_length / 3;
              size_t remaining = length;
              u_char* data = 0;
              ACE_NEW_RETURN (data,
                              u_char[length],
                              -1);
              u_char* out = data;
              ACE_TCHAR* inb = end + 4;
              ACE_TCHAR* endptr = 0;
              while (remaining)
                {
                  u_char charin = (u_char) ACE_OS::strtoul (inb, &endptr, 16);
                  *out = charin;
                  ++out;
                  --remaining;
                  inb += 3;
                }
              if (config_.set_binary_value (section, name, data, length))
                {
                  ACE_OS::fclose (in);
                  delete [] data;
                  delete [] buffer;
                  return -4;
                }
              else
                delete [] data;
            }
          else
            {
              // invalid type, ignore
              continue;
            }
        }// end if first char is a "
      else
        {
          // if the first character is not a ", [, ;, or # we may be
          // processing a file in the old format.
          // Try and process the line as such and if it fails,
          // return an error
          int rc = process_previous_line_format (buffer, section);
          if (rc != 0)
            {
              ACE_OS::fclose (in);
              delete [] buffer;
              return rc;
            }
        }             // end if maybe old format
    }                 // end while fgets

  if (ferror (in))
    {
      ACE_OS::fclose (in);
      delete [] buffer;
      return -1;
    }

  ACE_OS::fclose (in);
  delete [] buffer;
  return 0;
}

// This method exports the entire configuration database to <filename>.
// Once the file is opened this method calls 'export_section' passing
// the root section.
int
ACE_Registry_ImpExp::export_config (const ACE_TCHAR* filename)
{
  if (0 == filename)
    {
      errno = EINVAL;
      return -1;
    }
  int result = -1;

  FILE* out = ACE_OS::fopen (filename, ACE_TEXT ("w"));
  if (out)
    {
      result = this->export_section (config_.root_section (),
                                     ACE_TEXT (""),
                                     out);
      // The data may have been buffered and will be flush on close,
      // so we need to check that the close succeeds.
      if (ACE_OS::fclose (out) < 0)
        result = -7;
    }
  return result;
}

// Method provided by derived classes in order to write one section
// to the file specified.  Called by export_config when exporting
// the entire configuration object.

int
ACE_Registry_ImpExp::export_section (const ACE_Configuration_Section_Key& section,
                                     const ACE_TString& path,
                                     FILE* out)
{
  // don't export the root
  if (path.length ())
    {
      // Write out the section header
      ACE_TString header = ACE_TEXT ("[");
      header += path;
      header += ACE_TEXT ("]");
      header += ACE_TEXT ("\n");
      if (ACE_OS::fputs (header.fast_rep (), out) < 0)
        return -1;
      // Write out each value
      int index = 0;
      ACE_TString name;
      ACE_Configuration::VALUETYPE type;
      ACE_TString line;
      ACE_TCHAR int_value[32];
      ACE_TCHAR bin_value[3];
      void* binary_data;
      size_t binary_length;
      ACE_TString string_value;
      while (!config_.enumerate_values (section, index, name, type))
        {
          line = ACE_TEXT ("\"") + name + ACE_TEXT ("\"=");
          switch (type)
            {
            case ACE_Configuration::INTEGER:
              {
                u_int value;
                if (config_.get_integer_value (section, name.fast_rep (), value))
                  return -2;
                ACE_OS::sprintf (int_value, ACE_TEXT ("%08x"), value);
                line += ACE_TEXT ("dword:");
                line += int_value;
                break;
              }
            case ACE_Configuration::STRING:
              {
                if (config_.get_string_value (section,
                                              name.fast_rep (),
                                              string_value))
                  return -2;
                line += ACE_TEXT ("\"");
                line += string_value + ACE_TEXT ("\"");
                break;
              }
#ifdef _WIN32
            case ACE_Configuration::INVALID:
              break;  // JDO added break.  Otherwise INVALID is processed
              // like BINARY. If that's correct, please remove the
              // break and these comments
#endif
            case ACE_Configuration::BINARY:
              {
                // not supported yet - maybe use BASE64 codeing?
                if (config_.get_binary_value (section,
                                              name.fast_rep (),
                                              binary_data,
                                              binary_length))
                  return -2;
                line += ACE_TEXT ("hex:");
                unsigned char* ptr = (unsigned char*)binary_data;
                while (binary_length)
                  {
                    if (ptr != binary_data)
                      {
                        line += ACE_TEXT (",");
                      }
                    ACE_OS::sprintf (bin_value, ACE_TEXT ("%02x"), *ptr);
                    line += bin_value;
                    --binary_length;
                    ++ptr;
                  }
                delete [] (char*) binary_data;
                break;
              }
            default:
              return -3;
            }
          line += ACE_TEXT ("\n");
          if (ACE_OS::fputs (line.fast_rep (), out) < 0)
            return -4;
          ++index;
        }
    }
  // Export all sub sections
  int index = 0;
  ACE_TString name;
  ACE_Configuration_Section_Key sub_key;
  ACE_TString sub_section;
  while (!config_.enumerate_sections (section, index, name))
    {
      ACE_TString sub_section (path);
      if (path.length ())
        sub_section += ACE_TEXT ("\\");
      sub_section += name;
      if (config_.open_section (section, name.fast_rep (), 0, sub_key))
        return -5;
      if (export_section (sub_key, sub_section.fast_rep (), out))
        return -6;
      ++index;
    }
  return 0;
}

// This method read the line format origionally used in ACE 5.1
int
ACE_Registry_ImpExp::process_previous_line_format (ACE_TCHAR* buffer,
                                                   ACE_Configuration_Section_Key& section)
{
  // Chop any cr/lf at the end of the line.
  ACE_TCHAR *endp = ACE_OS::strpbrk (buffer, ACE_TEXT ("\r\n"));
  if (endp != 0)
    *endp = '\0';

  // assume this is a value, read in the value name
  ACE_TCHAR* end = ACE_OS::strchr (buffer, '=');
  if (end)  // no =, not a value so just skip it
    {
      // null terminate the name
      *end = 0;
      ++end;
      // determine the type
      if (*end == '\"')
        {
          // string type
          if(config_.set_string_value (section, buffer, end + 1))
            return -4;
        }
      else if (*end == '#')
        {
          // number type
          u_int value = ACE_OS::atoi (end + 1);
          if (config_.set_integer_value (section, buffer, value))
            return -4;
        }
    }
  return 0;
}                // end read_previous_line_format

ACE_Ini_ImpExp::ACE_Ini_ImpExp (ACE_Configuration& config)
    : ACE_Config_ImpExp_Base (config)
{
}

ACE_Ini_ImpExp::~ACE_Ini_ImpExp (void)
{
}

// Method to read file and populate object.
int
ACE_Ini_ImpExp::import_config (const ACE_TCHAR* filename)
{
  if (0 == filename)
    {
      errno = EINVAL;
      return -1;
    }
  FILE* in = ACE_OS::fopen (filename, ACE_TEXT ("r"));
  if (!in)
    return -1;

  // @@ Make this a dynamic size!
  ACE_TCHAR buffer[4096];
  ACE_Configuration_Section_Key section;
  while (ACE_OS::fgets (buffer, sizeof buffer, in))
    {
      ACE_TCHAR *line = this->squish (buffer);
      // Check for a comment and blank line
      if (line[0] == ACE_TEXT (';')  ||
          line[0] == ACE_TEXT ('#')  ||
          line[0] == '\0')
        continue;

      if (line[0] == ACE_TEXT ('['))
        {
          // We have a new section here, strip out the section name
          ACE_TCHAR* end = ACE_OS::strrchr (line, ACE_TEXT (']'));
          if (!end)
            {
              ACE_OS::fclose (in);
              return -3;
            }
          *end = 0;

          if (config_.expand_path (config_.root_section (),
                                   line + 1,
                                   section,
                                   1))
            {
              ACE_OS::fclose (in);
              return -3;
            }

          continue;
        }

      // We have a line; name ends at equal sign.
      ACE_TCHAR *end = ACE_OS::strchr (line, ACE_TEXT ('='));
      if (end == 0)                            // No '='
        {
          ACE_OS::fclose (in);
          return -3;
        }
      *end++ = '\0';
      ACE_TCHAR *name = this->squish (line);
#if 0
      if (ACE_OS::strlen (name) == 0)          // No name; just an '='
        {
          ACE_OS::fclose (in);
          return -3;
        }
#endif
      // Now find the start of the value
      ACE_TCHAR *value = this->squish (end);
      size_t value_len = ACE_OS::strlen (value);
      if (value_len > 0)
        {
          // ACE 5.2 (and maybe earlier) exported strings may be enclosed
          // in quotes. If string is quote-delimited, strip the quotes.
          // Newer exported files don't have quote delimiters.
          if (value[0] == ACE_TEXT ('"') &&
              value[value_len - 1] == ACE_TEXT ('"'))
            {
              // Strip quotes off both ends.
              value[value_len - 1] = '\0';
              ++value;
            }
        }

      if (config_.set_string_value (section, name, value))
        {
          ACE_OS::fclose (in);
          return -4;
        }
    }             // end while fgets

  if (ferror (in))
    {
      ACE_OS::fclose (in);
      return -1;
    }

  ACE_OS::fclose (in);
  return 0;
}

// This method exports the entire configuration database to <filename>.
// Once the file is opened this method calls 'export_section' passing
// the root section.
int
ACE_Ini_ImpExp::export_config (const ACE_TCHAR* filename)
{
  if (0 == filename)
    {
      errno = EINVAL;
      return -1;
    }
  int result = -1;

  FILE* out = ACE_OS::fopen (filename, ACE_TEXT ("w"));
  if (out)
    {
      result = this->export_section (config_.root_section (),
                                     ACE_TEXT (""),
                                     out);
      // The data may have been buffered and will be flush on close,
      // so we need to check that the close succeeds.
      if (ACE_OS::fclose (out) < 0)
        result = -7;
    }
  return result;
}

// Method provided by derived classes in order to write one section to the
// file specified.  Called by export_config when exporting the entire
// configuration objet

int
ACE_Ini_ImpExp::export_section (const ACE_Configuration_Section_Key& section,
                                const ACE_TString& path,
                                FILE* out)
{
  // don't export the root
  if (path.length ())
    {
      // Write out the section header
      ACE_TString header = ACE_TEXT ("[");
      header += path;
      header += ACE_TEXT ("]\n");
      if (ACE_OS::fputs (header.fast_rep (), out) < 0)
        return -1;
      // Write out each value
      int index = 0;
      ACE_TString name;
      ACE_Configuration::VALUETYPE type;
      ACE_TString line;
      ACE_TCHAR int_value[32];
      ACE_TCHAR bin_value[3];
      void* binary_data;
      size_t binary_length;
      ACE_TString string_value;
      while (!config_.enumerate_values (section, index, name, type))
        {
          line = name + ACE_TEXT ("=");
          switch (type)
            {
            case ACE_Configuration::INTEGER:
              {
                u_int value;
                if (config_.get_integer_value (section, name.fast_rep (), value))
                  return -2;
                ACE_OS::sprintf (int_value, ACE_TEXT ("%08x"), value);
                line += int_value;
                break;
              }
            case ACE_Configuration::STRING:
              {
                if (config_.get_string_value (section,
                                              name.fast_rep (),
                                              string_value))
                  return -2;
                line += string_value;
                break;
              }
#ifdef _WIN32
            case ACE_Configuration::INVALID:
              break;  // JDO added break.  Otherwise INVALID is processed
              // like BINARY. If that's correct, please remove the
              // break and these comments
#endif
            case ACE_Configuration::BINARY:
              {
                // not supported yet - maybe use BASE64 codeing?
                if (config_.get_binary_value (section,
                                              name.fast_rep (),
                                              binary_data,
                                              binary_length))
                  return -2;
                line += ACE_TEXT ("\"");
                unsigned char* ptr = (unsigned char*)binary_data;
                while (binary_length)
                  {
                    if (ptr != binary_data)
                      {
                        line += ACE_TEXT (",");
                      }
                    ACE_OS::sprintf (bin_value, ACE_TEXT ("%02x"), *ptr);
                    line += bin_value;
                    --binary_length;
                    ++ptr;
                  }
                line += ACE_TEXT ("\"");
                delete [] (char *) binary_data;
                break;
              }
            default:
              return -3;

            }// end switch on type

          line += ACE_TEXT ("\n");
          if (ACE_OS::fputs (line.fast_rep (), out) < 0)
            return -4;
          ++index;
        }// end while enumerating values
    }
  // Export all sub sections
  int index = 0;
  ACE_TString name;
  ACE_Configuration_Section_Key sub_key;
  ACE_TString sub_section;
  while (!config_.enumerate_sections (section, index, name))
    {
      ACE_TString sub_section (path);
      if (path.length ())
        sub_section += ACE_TEXT ("\\");
      sub_section += name;
      if (config_.open_section (section, name.fast_rep (), 0, sub_key))
        return -5;
      if (export_section (sub_key, sub_section.fast_rep (), out))
        return -6;
      ++index;
    }
  return 0;

}

// Method to squish leading and trailing whitespaces from a string.
// Whitespace is defined as: spaces (' '), tabs ('\t') or end-of-line
// (cr/lf).  The terminating nul is moved up to expunge trailing
// whitespace and the returned pointer points at the first
// non-whitespace character in the string, which may be the nul
// terminator if the string is all whitespace.


FBasicToken::FBasicToken()
{
	InitToken();
}

void FBasicToken::InitToken(EPropertyType InConstType)
{
	ConstantType = InConstType;
	TokenType = FBasicToken::TOKEN_None;
	TokenName	 = NAME_None;
	StartPos	 = 0;
	StartLine	 = 0;
	*Identifier	 = 0;
	FMemory::Memzero(String, sizeof(Identifier));
}

void FBasicToken::Clone(const FBasicToken& Other)
{
	TokenType = Other.TokenType;
	TokenName = Other.TokenName;
	StartPos  = Other.StartPos;
	StartLine = Other.StartLine;

	FCString::Strncpy(Identifier, Other.Identifier, NAME_SIZE);
	FMemory::Memcpy(String, Other.String, sizeof(String));
}

bool FBasicToken::Matches(const TCHAR* Str, ESearchCase::Type SearchCase) const
{
	return (TokenType==TOKEN_Identifier || TokenType==TOKEN_Symbol) && ((SearchCase == ESearchCase::CaseSensitive) ? !FCString::Strcmp(Identifier, Str) : !FCString::Stricmp(Identifier, Str));
}

bool FBasicToken::Matches(const FName& Name) const
{
	return TokenType==TOKEN_Identifier && TokenName==Name;
}

bool FBasicToken::StartsWith(const TCHAR* Str, bool bCaseSensitive) const
{
	const int32 StrLength = FCString::Strlen(Str);
	return (TokenType==TOKEN_Identifier || TokenType==TOKEN_Symbol) && (bCaseSensitive ? (!FCString::Strncmp(Identifier, Str, StrLength)) : (!FCString::Strnicmp(Identifier, Str, StrLength)));
}

bool FBasicToken::IsBool() const
{
	return ConstantType == CPT_Bool || ConstantType == CPT_Bool8 || ConstantType == CPT_Bool16 || ConstantType == CPT_Bool32 || ConstantType == CPT_Bool64;
}

void FBasicToken::SetConstInt(int32 InInt)
{
	ConstantType = CPT_Int;
	Int			 = InInt;
	TokenType	 = TOKEN_Const;
}

void FBasicToken::SetConstBool(bool InBool)
{
	ConstantType = CPT_Bool;
	NativeBool   = InBool;
	TokenType    = TOKEN_Const;
}

void FBasicToken::SetConstFloat(float InFloat)
{
	ConstantType = CPT_Float;
	Float        = InFloat;
	TokenType    = TOKEN_Const;
}

void FBasicToken::SetConstName(FName InName)
{
	ConstantType = CPT_Name;
	*(FScriptName*)NameBytes = NameToScriptName(InName);
	TokenType = TOKEN_Const;
}

void FBasicToken::SetConstString(TCHAR* InString, int32 MaxLength)
{
	check(MaxLength>0);
	ConstantType = CPT_String;
	if( InString != String )
	{
		FCString::Strncpy(String, InString, MaxLength);
	}
	TokenType = TOKEN_Const;
}

void FBasicToken::SetGuid(TCHAR* InString, int32 MaxLength)
{
	SetConstString(InString, MaxLength);
	TokenType = TOKEN_Guid;
}
FString FBasicToken::GetConstantValue() const
{
	if (TokenType == TOKEN_Const)
	{
		switch (ConstantType)
		{
		case CPT_Byte:
			return FString::Printf(TEXT("%u"), Byte);
		case CPT_Int:
			return FString::Printf(TEXT("%i"), Int);
		case CPT_Bool:
			// Don't use GTrue/GFalse here because they can be localized
			return FString::Printf(TEXT("%s"), NativeBool ? *(FName::GetEntry(NAME_TRUE)->GetPlainNameString()) : *(FName::GetEntry(NAME_FALSE)->GetPlainNameString()));
		case CPT_Float:
			return FString::Printf(TEXT("%f"), Float);
		case CPT_Name:
			return FString::Printf(TEXT("%s"), *ScriptNameToName(*(FScriptName*)NameBytes).ToString());
		case CPT_String:
			return String;

		// unsupported (parsing never produces a constant token of these types:
		default:
			return TEXT("InvalidTypeForAToken");
		}
	}
	else
	{
		return TEXT("NotConstant");
	}
}

bool FBasicToken::GetConstInt(int32& ValOut) const
{
	if(TokenType==TOKEN_Const && ConstantType==CPT_Int)
	{
		ValOut = Int;
		return 1;
	}
	else if(TokenType==TOKEN_Const && ConstantType==CPT_Byte)
	{
		ValOut = Byte;
		return 1;
	}
	else if(TokenType==TOKEN_Const && ConstantType==CPT_Float && Float==FMath::TruncToInt(Float))
	{
		ValOut = (int32)Float;
		return 1;
	}
	else return 0;
}

/*******************************************************************************
 * FBasicTokenParser::FErrorState
*******************************************************************************/

void FBasicTokenParser::FErrorState::Throw(bool bLogFatal) const
{
	if (State != NoError)
	{
		FString ErrorCodeStr;
		switch (State)
		{
		case ParseError:
			ErrorCodeStr = TEXT("ParseError");
			break;
		case RequireError:
			ErrorCodeStr = TEXT("RequireError");
			break;

		default:
			ErrorCodeStr.AppendInt(State.GetValue());
		}

		FString ErrorString = FString::Printf(TEXT("FBasicTokenParser Error (%s): %s"), *ErrorCodeStr, *Description.ToString());
		// don't always log fatal (these could be presented as user facing errors),
		// but this is a good point to flip this bool on, to help catch the first
		// error in a possible chain of snowballing errors
		if (bLogFatal)
		{
			UE_LOG(LogTokenParser, Fatal, TEXT("%s"), *ErrorString);
		}
		else
		{
			UE_LOG(LogTokenParser, Error, TEXT("FErrorState::Throw: %s"), *ErrorString);
		}
	}
}

/*******************************************************************************
 * FBasicTokenParser
*******************************************************************************/
namespace BasicTokenParserImpl
{
	static bool IsNumericChar(TCHAR c)
	{
		return (c >= '0' && c <= '9');
	}

	static bool IsWhitespace(TCHAR c)
	{
		return FText::IsWhitespace(c);
	}

	static bool IsEOL(TCHAR c)
	{
		return c == TEXT('\n') || c == TEXT('\r') || c == 0;
	}

	static bool IsSymbol(TCHAR c)
	{
		return
			// should have been handled in different cases, but as a catchall:
			(c == '{') ||
			(c == '}') ||
			(c == '"') ||
			// enumerated operators from logic in K2Node_MathExpression.cpp
			(c == '|') ||
			(c == '&') ||
			(c == '~') ||
			(c == '^') ||
			(c == '!') ||
			(c == '<') ||
			(c == '>') ||
			(c == '=') ||
			(c == '+') ||
			(c == '-') ||
			(c == '*') ||
			(c == '/') ||
			(c == '%') ||
			(c == ':') ||
			(c == '(') ||
			(c == ')') ||
			(c == ',') ||
			// in terms of the current MathExpression node, these could all
			// technically be used as identifier names, but seeing as 1) this parser
			// is meant to be generic, and 2) we could leverage these symbols as
			// operators in the future, we want to make sure they're reserved
			(c == '`') ||
			(c == '[') ||
			(c == ']') ||
			(c == '\\') ||
			(c == ';') ||
			(c == '\'') ||
			(c == '@') ||
			(c == '#') ||
			(c == '$') ||
			(c == '.') ||
			(c == '?');
	}

	static bool IsIdentifierDelim(TCHAR c)
	{
		// attempt to be the opposite of:
		// for optimization purposes we don't have an IsLetter(), since
		// localization would make that a slow operation
		return IsSymbol(c) || IsWhitespace(c) || IsEOL(c);
	}
}

void FBasicTokenParser::ResetParser(const TCHAR* SourceBuffer, int32 StartingLineNumber)
{
	Input     = SourceBuffer;
	InputLen  = FCString::Strlen(Input);
	InputPos  = 0;
	PrevPos   = 0;
	PrevLine  = 1;
	InputLine = StartingLineNumber;

	ClearCachedComment();
	ClearErrorState();
}

void FBasicTokenParser::ClearCachedComment()
{
	// Can't call Reset as FString uses protected inheritance
	PrevComment.Empty( PrevComment.Len() );
}

bool FBasicTokenParser::GetToken(FBasicToken& Token, bool bNoConsts/* = false*/)
{
	using namespace BasicTokenParserImpl;

	// if the parser is in a bad state, then don't continue parsing (who
	// knows what will happen!?)
	if (!IsValid())
	{
		return false;
	}

	Token.TokenName	= NAME_None;
	TCHAR c = GetLeadingChar();
	TCHAR p = PeekChar();
	if( c == 0 )
	{
		UngetChar();
		return 0;
	}
	Token.StartPos		= PrevPos;
	Token.StartLine		= PrevLine;
	if( c=='{' )
	{
		// Alphanumeric token.
		int32 Length=0;
		Token.Identifier[Length++] = c;
		do
		{
			if( Length >= NAME_SIZE )
			{
				Length = ((int32)NAME_SIZE) - 1;
				Token.Identifier[Length]=0; // need this for the error description

				FText ErrorDesc = FText::Format(LOCTEXT("IdTooLong", "Identifer ({0}...) exceeds maximum length of {1}"), FText::FromString(Token.Identifier), FText::AsNumber((int32)NAME_SIZE));
				SetError(FErrorState::ParseError, ErrorDesc);

				break;
			}

			c = GetChar();
			if (c == 0)
			{
				SetError(FErrorState::ParseError, LOCTEXT("MissingBracket", "Missing closing bracket: }"));
				break;
			}

			Token.Identifier[Length++] = c;
		} while( c !='}' );

		Token.Identifier[Length]=0;
		Token.SetGuid(Token.Identifier);
		return IsValid();
	}
	// if const values are allowed, determine whether the non-identifier token represents a const
	else if ( !bNoConsts && (IsNumericChar(c) || ((c=='+' || c=='-') && IsNumericChar(p))) )
	{
		// Integer or floating point constant.
		bool  bIsFloat = 0;
		int32 Length   = 0;
		bool  bIsHex   = 0;
		do
		{
			if( c==TEXT('.') )
			{
				bIsFloat = true;
			}
			if( c==TEXT('X') || c == TEXT('x') )
			{
				bIsHex = true;
			}

			Token.Identifier[Length++] = c;
			if( Length >= NAME_SIZE )
			{
				Length = ((int32)NAME_SIZE) - 1;
				Token.Identifier[Length]=0; // need this for the error description

				FText ErrorDesc = FText::Format(LOCTEXT("IdTooLong", "Identifer ({0}...) exceeds maximum length of {1}"), FText::FromString(Token.Identifier), FText::AsNumber((int32)NAME_SIZE));
				SetError(FErrorState::ParseError, ErrorDesc);

				break;
			}
			c = FChar::ToUpper(GetChar());
		} while (IsNumericChar(c) || (!bIsFloat && c == TEXT('.')) || (!bIsHex && c == TEXT('X')) || (bIsHex && c >= TEXT('A') && c <= TEXT('F')));

		Token.Identifier[Length]=0;
		if (!bIsFloat || c != 'F')
		{
			UngetChar();
		}

		if (bIsFloat)
		{
			Token.SetConstFloat( FCString::Atof(Token.Identifier) );
		}
		else if (bIsHex)
		{
			TCHAR* End = Token.Identifier + FCString::Strlen(Token.Identifier);
			Token.SetConstInt( FCString::Strtoi(Token.Identifier,&End,0) );
		}
		else
		{
			Token.SetConstInt( FCString::Atoi(Token.Identifier) );
		}
		return IsValid();
	}
	else if( c=='"' )
	{
		TCHAR Temp[MAX_STRING_CONST_SIZE];
		int32 Length=0;
		c = GetChar(1);
		while( (c!='"') && !IsEOL(c) )
		{
			if( c=='\\' )
			{
				c = GetChar(1);
				if( IsEOL(c) )
				{
					break;
				}
				else if(c == 'n')
				{
					// Newline escape sequence.
					c = '\n';
				}
			}
			Temp[Length++] = c;
			if( Length >= MAX_STRING_CONST_SIZE )
			{
				Length = ((int32)MAX_STRING_CONST_SIZE) - 1;
				Temp[Length]=0; // need this for the error description

				FText ErrorDesc = FText::Format(LOCTEXT("StringConstTooLong", "String constant ({0}...) exceeds maximum of {1} characters"), FText::FromString(Temp), FText::AsNumber((int32)MAX_STRING_CONST_SIZE));
				SetError(FErrorState::ParseError, ErrorDesc);

				c = TEXT('\"');
				break;
			}
			c = GetChar(1);
		}
		Temp[Length]=0;

		if( c != '"' )
		{
			FText ErrorDesc = FText::Format(LOCTEXT("NoClosingQuote", "Unterminated quoted string ({0})"), FText::FromString(Temp));
			SetError(FErrorState::ParseError, ErrorDesc);

			UngetChar();
		}

		Token.SetConstString(Temp);
		return IsValid();
	}
	// this condition is meant to be a catchall that encompasses:
	// unfortunately we had to remove IsLetter(), as it was slow (to account for
	// different languages)
	// IsNumericChar() is here to catch when bNoConsts is true (we don't allow
	// identifiers to start with a number)
	else if (IsSymbol(c) || IsNumericChar(c) || IsWhitespace(c) || IsEOL(c))
	{
		int32 Length=0;
		Token.Identifier[Length++] = c;

#define PAIR(cc,dd) ((c==cc)&&(d==dd)) /* Comparison macro for convenience */
		TCHAR d = GetChar();
		if
		(	PAIR('<','<')
		||	PAIR('>','>')
		||	PAIR('!','=')
		||	PAIR('<','=')
		||	PAIR('>','=')
		||	PAIR('+','+')
		||	PAIR('-','-')
		||	PAIR('+','=')
		||	PAIR('-','=')
		||	PAIR('*','=')
		||	PAIR('/','=')
		||	PAIR('&','&')
		||	PAIR('|','|')
		||	PAIR('^','^')
		||	PAIR('=','=')
		||	PAIR('*','*')
		||	PAIR('~','=')
		||	PAIR(':',':')
		)
		{
			Token.Identifier[Length++] = d;
			if( c=='>' && d=='>' )
			{
				if( GetChar()=='>' )
					Token.Identifier[Length++] = '>';
				else
					UngetChar();
			}
		}
		else UngetChar();
#undef PAIR

		Token.Identifier[Length] = 0;
		Token.TokenType = FBasicToken::TOKEN_Symbol;

		// Lookup the token's global name.
		Token.TokenName = FName(Token.Identifier, FNAME_Find);

		return true;
	}
	else
	{
		// Alphanumeric token.
		int32 Length = 0;
		do
		{
			Token.Identifier[Length++] = c;
			if (Length >= NAME_SIZE)
			{
				Length = ((int32)NAME_SIZE) - 1;
				Token.Identifier[Length] = 0; // need this for the error description

				FText ErrorDesc = FText::Format(LOCTEXT("IdTooLong", "Identifer ({0}...) exceeds maximum length of {1}"), FText::FromString(Token.Identifier), FText::AsNumber((int32)NAME_SIZE));
				SetError(FErrorState::ParseError, ErrorDesc);

				break;
			}
			c = GetChar();
		} while (!IsIdentifierDelim(c));
		UngetChar();
		Token.Identifier[Length] = 0;

		// Assume this is an identifier unless we find otherwise.
		Token.TokenType = FBasicToken::TOKEN_Identifier;

		// Lookup the token's global name.
		Token.TokenName = FName(Token.Identifier, FNAME_Find);

		// If const values are allowed, determine whether the identifier represents a constant
		if (!bNoConsts)
		{
			// See if the identifier is part of a vector, rotation or other struct constant.
			if (Token.Matches(TEXT("true")))
			{
				Token.SetConstBool(true);
				return true;
			}
			else if (Token.Matches(TEXT("false")))
			{
				Token.SetConstBool(false);
				return true;
			}
		}

		return IsValid();
	}
}

bool FBasicTokenParser::GetRawToken(FBasicToken& Token, TCHAR StopChar/* = TCHAR('\n')*/)
{
	// if the parser is in a bad state, then don't continue parsing (who
	// knows what will happen!?)
	if (!IsValid())
	{
		return false;
	}

	// Get token after whitespace.
	TCHAR Temp[MAX_STRING_CONST_SIZE];
	int32 Length=0;
	TCHAR c = GetLeadingChar();
	while( !BasicTokenParserImpl::IsEOL(c) && c != StopChar )
	{
		if( (c=='/' && PeekChar()=='/') || (c=='/' && PeekChar()=='*') )
		{
			break;
		}
		Temp[Length++] = c;
		if( Length >= MAX_STRING_CONST_SIZE )
		{
			Temp[Length] = 0;

			FText ErrorDesc = FText::Format(LOCTEXT("IdTooLong", "Identifer ({0}...) exceeds maximum length of {1}"), FText::FromString(Temp), FText::AsNumber((int32)MAX_STRING_CONST_SIZE));
			SetError(FErrorState::ParseError, ErrorDesc);
		}
		c = GetChar(true);
	}
	UngetChar();

	// Get rid of trailing whitespace.
	while( Length>0 && (Temp[Length-1]==' ' || Temp[Length-1]==9 ) )
	{
		Length--;
	}
	Temp[Length]=0;

	Token.SetConstString(Temp);

	return Length>0;
}

bool FBasicTokenParser::GetRawTokenRespectingQuotes(FBasicToken& Token, TCHAR StopChar/* = TCHAR('\n')*/)
{
	// if the parser is in a bad state, then don't continue parsing (who
	// knows what will happen!?)
	if (!IsValid())
	{
		return false;
	}

	// Get token after whitespace.
	TCHAR Temp[MAX_STRING_CONST_SIZE];
	int32 Length=0;
	TCHAR c = GetLeadingChar();

	bool bInQuote = false;

	while( !BasicTokenParserImpl::IsEOL(c) && ((c != StopChar) || bInQuote) )
	{
		if( (c=='/' && PeekChar()=='/') || (c=='/' && PeekChar()=='*') )
		{
			break;
		}

		if (c == '"')
		{
			bInQuote = !bInQuote;
		}

		Temp[Length++] = c;
		if( Length >= MAX_STRING_CONST_SIZE )
		{
			Length = ((int32)MAX_STRING_CONST_SIZE) - 1;
			Temp[Length]=0; // needs to happen for the error description below

			FText ErrorDesc = FText::Format(LOCTEXT("IdTooLong", "Identifer ({0}...) exceeds maximum length of {1}"), FText::FromString(Temp), FText::AsNumber((int32)MAX_STRING_CONST_SIZE));
			SetError(FErrorState::ParseError, ErrorDesc);

			c = GetChar(true);
			break;
		}
		c = GetChar(true);
	}
	UngetChar();

	// Get rid of trailing whitespace.
	while( Length>0 && (Temp[Length-1]==' ' || Temp[Length-1]==9 ) )
	{
		Length--;
	}
	Temp[Length]=0;

	if (bInQuote)
	{
		FText ErrorDesc = FText::Format(LOCTEXT("NoClosingQuote", "Unterminated quoted string ({0})"), FText::FromString(Temp));
		SetError(FErrorState::ParseError, ErrorDesc);
	}
	Token.SetConstString(Temp);

	return Length>0 && IsValid();
}

bool FBasicTokenParser::GetIdentifier(FBasicToken& Token, bool bNoConsts)
{
	if (!GetToken(Token, bNoConsts))
	{
		return false;
	}

	if (Token.TokenType == FBasicToken::TOKEN_Identifier)
	{
		return true;
	}

	UngetToken(Token);
	return false;
}

bool FBasicTokenParser::GetSymbol(FBasicToken& Token)
{
	if (!GetToken(Token))
	{
		return false;
	}

	if (Token.TokenType == FBasicToken::TOKEN_Symbol)
	{
		return true;
	}

	UngetToken(Token);
	return false;
}

bool FBasicTokenParser::GetConstInt(int32& Result, const TCHAR* ErrorContext)
{
	FBasicToken Token;
	if (GetToken(Token))
	{
		if (Token.GetConstInt(Result))
		{
			return true;
		}
		else
		{
			UngetToken(Token);
		}
	}

	if (ErrorContext != NULL)
	{
		FText ErrorDesc = FText::Format(LOCTEXT("ContextualNoInt", "{0}: Missing expected integer constant"), FText::FromString(ErrorContext));
		SetError(FErrorState::ParseError, ErrorDesc);
	}
	return false;
}

void FBasicTokenParser::UngetToken(FBasicToken& Token)
{
	InputPos = Token.StartPos;
	InputLine = Token.StartLine;
}

TCHAR FBasicTokenParser::PeekChar()
{
	return (InputPos < InputLen) ? Input[InputPos] : 0;
}

TCHAR FBasicTokenParser::GetChar(bool bLiteral/* = false*/)
{
	// if the parser is in a bad state, then don't continue parsing (who
	// knows what will happen!?)... return a char signaling the end-of-stream
	if (!IsValid())
	{
		return 0;
	}

	int32 CommentCount = 0;

	PrevPos = InputPos;
	PrevLine = InputLine;

Loop:
	const TCHAR c = Input[InputPos++];
	if ( CommentCount > 0 )
	{
		// Record the character as a comment.
		PrevComment += c;
	}

	if (c == TEXT('\n'))
	{
		InputLine++;
	}
	else if (!bLiteral)
	{
		const TCHAR NextChar = PeekChar();
		if ( c==TEXT('/') && NextChar==TEXT('*') )
		{
			if ( CommentCount == 0 )
			{
				ClearCachedComment();
				// Record the slash and star.
				PrevComment += c;
				PrevComment += NextChar;
			}
			CommentCount++;
			InputPos++;
			goto Loop;
		}
		else if( c==TEXT('*') && NextChar==TEXT('/') )
		{
			if (--CommentCount < 0)
			{
				ClearCachedComment();
				SetError(FErrorState::ParseError, LOCTEXT("UnexpectedCommentClose", "Unexpected '*/' outside of comment"));
			}
			// Star already recorded; record the slash.
			PrevComment += Input[InputPos];

			InputPos++;
			goto Loop;
		}
	}

	if (CommentCount > 0)
	{
		if (c == 0)
		{
			ClearCachedComment();
			SetError(FErrorState::ParseError, LOCTEXT("NoCommentClose", "No end to a comment by the end of the expression"));
		}
		else
		{
			goto Loop;
		}
	}
	return c;
}

TCHAR FBasicTokenParser::GetLeadingChar()
{
	// if the parser is in a bad state, then don't continue parsing (who
	// knows what will happen!?)... return a char signaling the end-of-stream
	if (!IsValid())
	{
		return 0;
	}

	TCHAR TrailingCommentNewline = 0;
	for (;;)
	{
		bool MultipleNewlines = false;

		TCHAR c;

		// Skip blanks.
		do
		{
			c = GetChar();

			// Check if we've encountered another newline since the last one
			if (c == TrailingCommentNewline)
			{
				MultipleNewlines = true;
			}
		} while (FText::IsWhitespace(c));

		if (c != TEXT('/') || PeekChar() != TEXT('/'))
		{
			return c;
		}

		// Clear the comment if we've encountered newlines since the last comment
		if (MultipleNewlines)
		{
			ClearCachedComment();
		}

		// Record the first slash.  The first iteration of the loop will get the second slash.
		PrevComment += c;

		do
		{
			c = GetChar(true);
			if (c == 0)
				return c;
			PrevComment += c;
		} while (!BasicTokenParserImpl::IsEOL(c));

		TrailingCommentNewline = c;

		for (;;)
		{
			c = GetChar();
			if (c == 0)
				return c;
			if (c == TrailingCommentNewline || !BasicTokenParserImpl::IsEOL(c))
			{
				UngetChar();
				break;
			}

			PrevComment += c;
		}
	}
}

void FBasicTokenParser::UngetChar()
{
	InputPos = PrevPos;
	InputLine = PrevLine;
}

bool FBasicTokenParser::MatchIdentifier(FName Match)
{
	FBasicToken Token;
	if (!GetToken(Token))
	{
		return false;
	}

	if ((Token.TokenType == FBasicToken::TOKEN_Identifier) && (Token.TokenName == Match))
	{
		return true;
	}

	UngetToken(Token);
	return false;
}

bool FBasicTokenParser::MatchIdentifier(const TCHAR* Match)
{
	FBasicToken Token;
	if (GetToken(Token))
	{
		if (Token.TokenType == FBasicToken::TOKEN_Identifier && FCString::Stricmp(Token.Identifier, Match) == 0)
		{
			return true;
		}
		else
		{
			UngetToken(Token);
		}
	}

	return false;
}

bool FBasicTokenParser::PeekIdentifier(FName Match)
{
	FBasicToken Token;
	if (!GetToken(Token, true))
	{
		return false;
	}
	UngetToken(Token);
	return Token.TokenType == FBasicToken::TOKEN_Identifier && Token.TokenName == Match;
}

bool FBasicTokenParser::PeekIdentifier(const TCHAR* Match)
{
	FBasicToken Token;
	if (!GetToken(Token, true))
	{
		return false;
	}
	UngetToken(Token);
	return Token.TokenType == FBasicToken::TOKEN_Identifier && FCString::Stricmp(Token.Identifier, Match) == 0;
}

bool FBasicTokenParser::MatchSymbol(const TCHAR* Match)
{
	FBasicToken Token;

	if (GetToken(Token, /*bNoConsts=*/ true))
	{
		if (Token.TokenType == FBasicToken::TOKEN_Symbol && !FCString::Stricmp(Token.Identifier, Match))
		{
			return true;
		}
		else
		{
			UngetToken(Token);
		}
	}

	return false;
}

bool FBasicTokenParser::PeekSymbol(const TCHAR* Match)
{
	FBasicToken Token;
	if (!GetToken(Token, true))
	{
		return false;
	}
	UngetToken(Token);

	return Token.TokenType == FBasicToken::TOKEN_Symbol && FCString::Stricmp(Token.Identifier, Match) == 0;
}

bool FBasicTokenParser::RequireIdentifier(FName Match, const TCHAR* ErrorContext)
{
	if (!MatchIdentifier(Match))
	{
		FText ErrorDesc = FText::Format(LOCTEXT("MissingRequirement", "Missing '{0}' in {1}"), FText::FromName(Match), FText::FromString(ErrorContext));
		SetError(FErrorState::RequireError, ErrorDesc);
	}
	return IsValid();
}

bool FBasicTokenParser::RequireIdentifier(const TCHAR* Match, const TCHAR* ErrorContext)
{
	if (!MatchIdentifier(Match))
	{
		FText ErrorDesc = FText::Format(LOCTEXT("MissingRequirement", "Missing '{0}' in {1}"), FText::FromString(Match), FText::FromString(ErrorContext));
		SetError(FErrorState::RequireError, ErrorDesc);
	}
	return IsValid();
}

bool FBasicTokenParser::RequireSymbol(const TCHAR* Match, const TCHAR* ErrorContext)
{
	if (!MatchSymbol(Match))
	{
		FText ErrorDesc = FText::Format(LOCTEXT("MissingRequirement", "Missing '{0}' in {1}"), FText::FromString(Match), FText::FromString(ErrorContext));
		SetError(FErrorState::RequireError, ErrorDesc);
	}
	return IsValid();
}

bool FBasicTokenParser::RequireSemi()
{
	if( !MatchSymbol(TEXT(";")) )
	{
		FText ErrorDesc = LOCTEXT("MissingSemiColon", "Missing ';'");

		FBasicToken Token;
		if( GetToken(Token) )
		{
			ErrorDesc = FText::Format(LOCTEXT("MissingSemiBefore", "Missing ';' before '{0}'"), FText::FromString(Token.Identifier));
		}
		SetError(FErrorState::RequireError, ErrorDesc);
	}
	return IsValid();
}

void FBasicTokenParser::SetError(TEnumAsByte<FErrorState::EErrorType> ErrorCode, FText Description, bool bLogFatal)
{
	CurrentError.State = ErrorCode;
	CurrentError.Description = Description;
	CurrentError.Throw(bLogFatal);
}

FBasicTokenParser::FErrorState const& FBasicTokenParser::GetErrorState() const
{
	return CurrentError;
}

bool FBasicTokenParser::IsValid() const
{
	return (CurrentError.State == FErrorState::NoError);
}

void FBasicTokenParser::ClearErrorState()
{
	SetError(FErrorState::NoError, FText::GetEmpty());
}

#undef LOCTEXT_NAMESPACE


static long TimeInMillisImplementation()
{
    clock_t t = clock();
    return t;
}

/*
 * All rights reserved.
 *
 */

#include <time.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "CppUTest/TestHarness.h"
#undef malloc
#undef calloc
#undef realloc
#undef free
#undef strdup
#undef strndup

#define  far  // eliminate "meaningless type qualifier" warning

#include "CppUTest/PlatformSpecificFunctions.h"

static jmp_buf test_exit_jmp_buf[10];
static int jmp_buf_index = 0;

TestOutput::WorkingEnvironment PlatformSpecificGetWorkingEnvironment()
{
    return TestOutput::eclipse;
}

static void DummyPlatformSpecificRunTestInASeperateProcess(UtestShell* shell, TestPlugin*, TestResult* result)
{
    result->addFailure(TestFailure(shell, "-p doesn't work on this platform, as it is lacking fork.\b"));
}

static int DummyPlatformSpecificFork(void)
{
    return 0;
}

static int DummyPlatformSpecificWaitPid(int, int*, int)
{
    return 0;
}

void (*PlatformSpecificRunTestInASeperateProcess)(UtestShell* shell, TestPlugin* plugin, TestResult* result) =
        DummyPlatformSpecificRunTestInASeperateProcess;
int (*PlatformSpecificFork)(void) = DummyPlatformSpecificFork;
int (*PlatformSpecificWaitPid)(int, int*, int) = DummyPlatformSpecificWaitPid;

extern "C" {

static int PlatformSpecificSetJmpImplementation(void (*function) (void* data), void* data)
{
    if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
        jmp_buf_index++;
        function(data);
        jmp_buf_index--;
        return 1;
    }
    return 0;
}

static void PlatformSpecificLongJmpImplementation()
{
    jmp_buf_index--;
    longjmp(test_exit_jmp_buf[jmp_buf_index], 1);
}

static void PlatformSpecificRestoreJumpBufferImplementation()
{
    jmp_buf_index--;
}

void (*PlatformSpecificLongJmp)() = PlatformSpecificLongJmpImplementation;
int (*PlatformSpecificSetJmp)(void (*)(void*), void*) = PlatformSpecificSetJmpImplementation;
void (*PlatformSpecificRestoreJumpBuffer)() = PlatformSpecificRestoreJumpBufferImplementation;

///////////// Time in millis
/*
*  In Keil MDK-ARM, clock() default implementation used semihosting.
*  Resolutions is user adjustable (1 ms for now)
*/

///////////// Time in String

static const char* DummyTimeStringImplementation()
{
    time_t tm = 0;
    return ctime(&tm);
}

long (*GetPlatformSpecificTimeInMillis)() = TimeInMillisImplementation;
const char* (*GetPlatformSpecificTimeString)() = DummyTimeStringImplementation;

int (*PlatformSpecificVSNprintf)(char *str, size_t size, const char* format, va_list args) = vsnprintf;

static PlatformSpecificFile PlatformSpecificFOpenImplementation(const char* filename, const char* flag)
{
   return fopen(filename, flag);
}

static void PlatformSpecificFPutsImplementation(const char* str, PlatformSpecificFile file)
{
    fputs(str, (FILE*)file);
}

static void PlatformSpecificFCloseImplementation(PlatformSpecificFile file)
{
    fclose((FILE*)file);
}

static void PlatformSpecificFlushImplementation()
{
    fflush(stdout);
}

PlatformSpecificFile (*PlatformSpecificFOpen)(const char*, const char*) = PlatformSpecificFOpenImplementation;
void (*PlatformSpecificFPuts)(const char*, PlatformSpecificFile) = PlatformSpecificFPutsImplementation;
void (*PlatformSpecificFClose)(PlatformSpecificFile) = PlatformSpecificFCloseImplementation;

int (*PlatformSpecificPutchar)(int) = putchar;
void (*PlatformSpecificFlush)() = PlatformSpecificFlushImplementation;

void* (*PlatformSpecificMalloc)(size_t size) = malloc;
void* (*PlatformSpecificRealloc)(void*, size_t) = realloc;
void (*PlatformSpecificFree)(void* memory) = free;
void* (*PlatformSpecificMemCpy)(void*, const void*, size_t) = memcpy;
void* (*PlatformSpecificMemset)(void*, int, size_t) = memset;

void (*PlatformSpecificSrand)(unsigned int) = srand;
int (*PlatformSpecificRand)(void) = rand;

static int IsNanImplementation(double d)
{
    return isnan(d);
}

static int IsInfImplementation(double d)
{
    return isinf(d);
}

static int AtExitImplementation(void(*func)(void))
{
    return atexit(func);
}

double (*PlatformSpecificFabs)(double) = fabs;
int (*PlatformSpecificIsNan)(double) = IsNanImplementation;
int (*PlatformSpecificIsInf)(double) = IsInfImplementation;
int (*PlatformSpecificAtExit)(void(*func)(void)) = AtExitImplementation;

static PlatformSpecificMutex DummyMutexCreate(void)
{
    return 0;
}

static void DummyMutexLock(PlatformSpecificMutex)
{
}

static void DummyMutexUnlock(PlatformSpecificMutex)
{
}

static void DummyMutexDestroy(PlatformSpecificMutex)
{
}

PlatformSpecificMutex (*PlatformSpecificMutexCreate)(void) = DummyMutexCreate;
void (*PlatformSpecificMutexLock)(PlatformSpecificMutex) = DummyMutexLock;
void (*PlatformSpecificMutexUnlock)(PlatformSpecificMutex) = DummyMutexUnlock;
void (*PlatformSpecificMutexDestroy)(PlatformSpecificMutex) = DummyMutexDestroy;

}