Files
redAcore/SelectableBare.cpp
Charl Wentzel 5899bf9892 Merge branch 'master' into HitNotBLE
# Conflicts:
#	SelectableCore.cpp
#	SelectableCore.h
2019-04-03 17:02:52 +02:00

961 lines
28 KiB
C++

/*
* SelectableBare.cpp
*
* Created on: Feb 2019
* Author: wentzelc
*/
// Standard C/C++ Libraries
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
// redA Libraries
#include "ApplicationCore.h"
#include "SelectableCore.h"
//---------------------------------------------------------------------------
// Global Vars
extern char * ProcessName;
extern CApplication * Application;
//---------------------------------------------------------------------------
CSelectableBare::CSelectableBare( const char * pName, const char * pType ) : CFunctionCore( pName, pType )
{
// Quick access
Selector = Application->Selector;
// Handles
FirstHandle = NULL;
}
//---------------------------------------------------------------------------
CSelectableBare::~CSelectableBare()
{
THandle * NextHandle = NULL;
// Destroy File Handles
while (FirstHandle)
{
// Close handle if open
if ((FirstHandle->State == csOpen) || (FirstHandle->State == csWaitingtoOpen)) {
Close( FirstHandle, false );
}
NextHandle = FirstHandle->Next;
DestroyHandle( FirstHandle );
FirstHandle = NextHandle;
}
}
//---------------------------------------------------------------------------
CDataMember * CSelectableBare::GetHandleAddress( THandle * Handle, const char * HandleRef )
{
CDataMember * AddressDef = NULL;
char NamePath[100];
char * Address;
// Handle renamed?
sprintf( NamePath, "Application/Addresses/Rename/%s", HandleRef );
if (!(Address = (char*)Application->Config->GetChStr( NamePath, NULL, false ))) {
Address = (char*)HandleRef;
}
// Get address def
if ((AddressDef = Application->AddressList->GetChild( Address ))) {
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Use address '%s' ('%s')",
ProcessName, Name, Handle->Name, Address, HandleRef );
}
return AddressDef;
}
//---------------------------------------------------------------------------
THandle * CSelectableBare::CreateHandle( const char * HandleName, bool CreateChannel )
{
THandle ** Handle = NULL;
// Find Handle by Name or get end of list
Handle = &FirstHandle;
while ( *Handle && strcmp( HandleName, (*Handle)->Name ))
Handle = &((*Handle)->Next);
// Create if necessary
if (!*Handle)
{
// Create File handle at end of list
*Handle = (THandle*)calloc( 1, sizeof(THandle) );
// Set name
if (HandleName) {
(*Handle)->Name = (char*)malloc( strlen(HandleName)+1 );
strcpy( (*Handle)->Name, HandleName );
}
// Set File Descriptor
(*Handle)->FD = -1;
// Log event
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Created",
ProcessName, Name, HandleName );
}
// Create Matching Channel
if (CreateChannel) {
(*Handle)->Channel = AddChannel( HandleName );
}
return *Handle;
}
//---------------------------------------------------------------------------
bool CSelectableBare::RemoveHandle( THandle * Handle )
{
THandle ** HandlePtr = NULL;
// Validate
if (!Handle || (Handle->State == csOpen) || (Handle->State == csWaitingtoOpen)) {
return false;
}
// Find in List
HandlePtr = &FirstHandle;
while (*HandlePtr && (*HandlePtr != Handle)) {
HandlePtr = &((*HandlePtr)->Next);
}
// Remove from list if found
if (*HandlePtr) {
*HandlePtr = (*HandlePtr)->Next;
}
// Log event
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Removed",
ProcessName, Name, Handle->Name );
// Destroy Child handle
DestroyHandle( Handle );
return true;
}
//---------------------------------------------------------------------------
bool CSelectableBare::DestroyHandle( THandle * Handle )
{
// Validate Handle
if (!Handle)
return false;
// Clear parameters
if (Handle->Name)
free( Handle->Name );
if (Handle->Path)
free( Handle->Path );
if (Handle->HostName)
free( Handle->HostName );
if (Handle->PortName)
free( Handle->PortName );
if (Handle->AddressInfo)
freeaddrinfo( Handle->AddressList );
// Destroy Buffers
if (Handle->InBuffer)
delete Handle->InBuffer;
if (Handle->OutBuffer)
delete Handle->OutBuffer;
// Clear Input Markers
if (Handle->InMarker)
free( Handle->InMarker );
// Destroy Pointer
free( Handle );
return true;
}
//---------------------------------------------------------------------------
bool CSelectableBare::ClearHandle( THandle * Handle )
{
// Validate
if (!Handle) {
return false;
}
// Reset Type related parameters
if (Handle->Path) {
free( Handle->Path );
Handle->Path = NULL;
}
if (Handle->HostName) {
free( Handle->HostName );
Handle->HostName = NULL;
}
if (Handle->PortName) {
free( Handle->PortName );
Handle->PortName = NULL;
}
if (Handle->AddressList) {
freeaddrinfo( Handle->AddressList );
Handle->AddressList = NULL;
Handle->AddressInfo = NULL;
}
// Reset Parameters
Handle->Type = ctNone;
// Log event
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Set as None",
ProcessName, Name, Handle->Name );
return true;
}
//---------------------------------------------------------------------------
bool CSelectableBare::SetCallback( THandle * Handle, EConnectState pState, FHandleCallback pCallback )
{
// Validate
if (!Handle) {
return false;
}
// Set callback
Handle->StateCallback[ (int)pState ] = pCallback;
return true;
}
//---------------------------------------------------------------------------
bool CSelectableBare::SetAutoManage( THandle * Handle, bool AutoManage, bool Persistent, int ReopenDelay, int CloseTimeout )
{
// Validate
if (!Handle) {
return false;
}
// Set params
Handle->AutoManage = AutoManage;
Handle->Persistent = Persistent;
Handle->ReopenDelay = ReopenDelay;
Handle->CloseTimeout = CloseTimeout;
return true;
}
//---------------------------------------------------------------------------
bool CSelectableBare::SetInBuffer( THandle * Handle, int InBufSize, int InTimeout, const char * InMarker, int InMarkerLen )
{
// Validate
if (!Handle) {
return false;
}
// Input Buffer
if (Handle->InBuffer) {
delete Handle->InBuffer;
Handle->InBuffer = NULL;
}
if (InBufSize) {
Handle->InBuffer = (CRollingBuffer*) new CRollingBuffer( InBufSize );
}
// Set Input Timeout
Handle->InTimeout = InTimeout;
// Set Input Markers
if (InMarkerLen && InMarker) {
Handle->InMarkerLen = InMarkerLen;
Handle->InMarker = (char *)malloc( InMarkerLen+1 );
memcpy( Handle->InMarker, InMarker, InMarkerLen );
Handle->InMarker[InMarkerLen] = 0;
}
return true;
}
//---------------------------------------------------------------------------
bool CSelectableBare::SetOutBuffer( THandle * Handle, int OutBufSize )
{
// Validate
if (!Handle) {
return false;
}
// Output Buffer
if (Handle->OutBuffer) {
delete Handle->OutBuffer;
Handle->OutBuffer = NULL;
}
if (OutBufSize) {
Handle->OutBuffer = (CRollingBuffer*) new CRollingBuffer( OutBufSize );
}
return true;
}
//---------------------------------------------------------------------------
bool CSelectableBare::ProcessInputBuffer( THandle * Handle, bool Force )
{
int Pos = 0;
int Len = 0;
char * Data = NULL;
// Check if buffered data
if (!Handle || !Handle->InBuffer || !Handle->InBuffer->Len()) {
return false;
}
// Check if forced processed
if (Force || !Handle->InMarkerLen)
{
// Show Packet
Len = Handle->InBuffer->Peek( &Data );
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - IN-T:",
ProcessName, Name, Handle->Name );
// Write buffer to Outputs
if ((Handle->Type == ctTCPremote) || (Handle->Type == ctUNIXremote)) {
Output( Handle->Parent->Channel, Data, Len );
} else {
Output( Handle->Channel, Data, Len );
}
// Clear processed bytes from buffer
Handle->InBuffer->Clear( Len );
}
else
{
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - IN:",
ProcessName, Name, Handle->Name );
// Search for end of packet marker
while (Handle->InBuffer->FindStr( Handle->InMarker, Handle->InMarkerLen, Pos ))
{
// Show Packet
Len = Handle->InBuffer->Peek( &Data, 0, Pos+Handle->InMarkerLen );
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - IN-M:",
ProcessName, Name, Handle->Name );
// Write buffer to Outputs
if ((Handle->Type == ctTCPremote) || (Handle->Type == ctUNIXremote)) {
Output( Handle->Parent->Channel, Data, Len );
} else {
Output( Handle->Channel, Data, Len );
}
// Clear processed bytes from buffer
Handle->InBuffer->Clear( Len );
}
}
return true;
}
//---------------------------------------------------------------------------
int CSelectableBare::Open( THandle * Handle, bool DelayResolve )
{
THandle * NewHandle = NULL;
// Validate
if (!Handle || (Handle->Type == ctNone)) {
return -1;
} else if (Handle->State == csOpen) {
return Handle->FD;
}
// Check if port exits
if (access( Handle->Path, F_OK ) != 0)
{
// Log event
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Path not found [%s]",
ProcessName, Name, Handle->Name, Handle->Path );
return -1;
}
// Open Port
Handle->FD = open( Handle->Path, O_RDWR );
if (Handle->FD == -1)
{
// Log event
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Could not open Path [%s]",
ProcessName, Name, Handle->Name, Handle->Path );
return -1;
}
// Log Event
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Path opened [%s]",
ProcessName, Name, Handle->Name, Handle->Path );
// Add to Select Lists
if (Selector) {
Selector->Add( Handle->FD, true, false, Handle, this );
}
// Set state
ChangeState( Handle, csOpen );
// Set timer (for re-open or auto-close)
SetStartTime( &Handle->LastAction );
return (NewHandle)? NewHandle->FD : -1;
};
//---------------------------------------------------------------------------
// Delete socket
bool CSelectableBare::Close( THandle * Handle, bool QuickReopen )
{
bool Fail;
// Validate
if (!Handle || (Handle->FD == -1))
return false;
// Close Handle
Fail = (close( Handle->FD ))? true : false;
ChangeState( Handle, ((Fail)? csFailed : csClosed) );
// Start timer (for re-open)
if (QuickReopen)
ClearStartTime( &(Handle->LastAction) );
else
SetStartTime( &(Handle->LastAction) );
// Show action
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - %s",
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed") );
// Remove from Select List
if (!Fail && Selector) {
if (Handle->Type != ctUDPremote) {
Selector->Remove( Handle->FD, true, true );
}
}
// Reset FD
Handle->FD = ((Fail)? Handle->FD : -1);
return true;
}
//---------------------------------------------------------------------------
// Device Interface
bool CSelectableBare::Read( THandle * Handle )
{
int BytesRead = 0;
int BytesWaiting = -1;
// Validate
if (!Handle || (Handle->State == csNone) || (Handle->State == csFailed) || (Handle->State == csClosed)) {
return false;
}
// Log Read Event
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Read Event",
ProcessName, Name, Handle->Name );
// Check if socket ready (non-block open in progress)
if (Handle->State == csWaitingtoOpen)
{
Open( Handle );
// Reset Timer (for auto-close)
SetStartTime( &(Handle->LastAction) );
return true;
}
// Check if anything to read
ioctl( Handle->FD, FIONREAD, &BytesWaiting );
// Error on port
if (BytesWaiting < 0)
{
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Data waiting error (%s)",
ProcessName, Name, Handle->Name, strerror(errno) );
// Close Handle
Close( Handle, false );
return false;
}
// Validate
if (Handle->State != csOpen) {
return false;
}
// Read File directly into buffer
if (Handle->InBuffer)
{
errno = 0;
BytesRead = Handle->InBuffer->ReadFromFD( Handle->FD, BytesWaiting );
// Report failure
if (errno) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Error reading data [%d/%d] (%s)",
ProcessName, Name, Handle->Name, -BytesRead, BytesWaiting, strerror(errno) );
}
else if (BytesRead < BytesWaiting) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Incomplete data read [%d/%d]",
ProcessName, Name, Handle->Name, BytesRead, BytesWaiting );
}
if (BytesRead != 0) {
// Process Buffer
ProcessInputBuffer( Handle, false );
}
}
// Reset timer
SetStartTime( &(Handle->InStart) );
return (bool)BytesRead;
}
//---------------------------------------------------------------------------
bool CSelectableBare::Write( THandle * Handle )
{
int Len = 0;
char * Data = NULL;
int BytesWritten = 0;
// Validate
if (!Handle)
return false;
// Is Handle open?
if ((Handle->State == csNone) || (Handle->State == csFailed) || (Handle->State == csClosed))
{
// May it be opened?
if (!Handle->AutoManage)
{
// Must be opened manually
return false;
}
else if (Timeout( Handle->LastAction, Handle->ReopenDelay ))
{
// Attempt to re-open port
Open( Handle, true );
}
}
// Log Ready for Write Event
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Write Event",
ProcessName, Name, Handle->Name );
if (Handle->State == csWaitingtoOpen)
{
// Complete socket open process
Open( Handle );
// Reset Timer (for auto-close)
SetStartTime( &(Handle->LastAction) );
// Remove from set for select write
if (Selector) {
Selector->Remove( Handle->FD, false, true );
}
return true;
}
else if (Handle->State == csOpen)
{
if (Handle->OutBuffer)
{
// Write directly to handle / socket
errno = 0;
BytesWritten = Handle->OutBuffer->WriteToFD( Handle->FD );
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - OUT:",
ProcessName, Name, Handle->Name );
// Report failure
if (errno) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Error sending data [%d/%d] (%s)",
ProcessName, Name, Handle->Name, -BytesWritten, Len, strerror(errno) );
}
else if (BytesWritten < Len) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Incomplete data sent [%d/%d]",
ProcessName, Name, Handle->Name, BytesWritten, Len );
}
if (BytesWritten != 0)
{
// Update Buffer
Handle->OutBuffer->Clear( (BytesWritten > 0)? BytesWritten : -BytesWritten ); // negative value reported if error occurred
// Check if Buffer empty
if (!Handle->OutBuffer->Len()) {
// Remove from Select Write list
if (Selector) {
Selector->Remove( Handle->FD, false, true );
}
}
// Reset timeout
SetStartTime( &(Handle->LastAction) );
}
}
else
{
// No Output buffer to write from, so remove from Write list
if (Selector) {
Selector->Remove( Handle->FD, false, true );
}
}
return true;
}
return false;
}
//---------------------------------------------------------------------------
int CSelectableBare::ReadFromFD( int FD, char * Data, int MaxLen )
{
int BytesRead = 0;
int TotalRead = 0;
int DataRemain = MaxLen;
bool Error = false;
// Check if buffer created
if ((FD == -1) || (MaxLen < 1)) {
return 0;
}
// Read Data into buffer
while (DataRemain)
{
// Read from file descriptor
BytesRead = read( FD, &Data[TotalRead], DataRemain );
if ((BytesRead < 0)) {
Error = true;
errno = (!BytesRead)? 0 : errno; // No error if no bytes written
break;
}
// Update Data Pointers
TotalRead += BytesRead;
DataRemain -= BytesRead;
if (DataRemain) {
usleep( 500 );
}
}
return (Error)? -TotalRead : TotalRead; // Report negative total on error
}
//---------------------------------------------------------------------------
int CSelectableBare::WriteToFD( int FD, const char * Data, int Len, bool Force )
{
int BytesWritten = 0;
int TotalWritten = 0;
int DataRemain = (Len != -1)? Len : (Data)? strlen(Data) : 0;
bool Error = false;
// Check if buffer created
if ((FD == -1) || !DataRemain) {
return 0;
}
// Read Data into buffer
while (DataRemain)
{
// Read from file descriptor
BytesWritten = write( FD, &Data[TotalWritten], DataRemain );
if ((BytesWritten <= 0) && (!Force || (errno != EAGAIN))) {
Error = true;
errno = (!BytesWritten)? 0 : errno; // No error if no bytes written
break;
}
// Update Data Pointers
TotalWritten += BytesWritten;
DataRemain -= BytesWritten;
if (DataRemain) {
usleep( 500 );
}
}
return (Error)? -TotalWritten : TotalWritten; // Report negative total on error
}
//---------------------------------------------------------------------------
int CSelectableBare::Input( const char * ChannelName, const char * Data, int Len )
{
TChannel * Channel = NULL;
THandle * Handle = NULL;
int HandleCount = 0;
int TempWritten = 0;
int BytesWritten = 0;
// Validate
if (!ChannelName || !Data) {
return 0;
}
// Get Channel
if (!(Channel = GetChannel( ChannelName ))) {
// Channel not found
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input rejected, Channel not found",
ProcessName, Name, ChannelName );
return 0;
}
else if (!Channel->InputEnabled) {
// Channel disabled
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input rejected, Channel input disabled",
ProcessName, Name, ChannelName );
return 0;
}
// Log event
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Channel '%s' - IN:",
ProcessName, Name, ChannelName );
// Find Linked handle
Handle = FirstHandle;
while( Handle )
{
if (Handle->Channel && !strcasecmp( ChannelName, Handle->Channel->Name ))
{
// Input to Handle
TempWritten = OutputHandle( Handle, Data, Len );
BytesWritten = (TempWritten > BytesWritten)? TempWritten : BytesWritten;
HandleCount++;
}
Handle = Handle->Next;
}
if (!HandleCount) {
// Handle not found
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input rejected, No Handles found",
ProcessName, Name, ChannelName );
return 0;
}
return BytesWritten;
}
//---------------------------------------------------------------------------
int CSelectableBare::OutputHandle( THandle * Handle, const char * Data, int Len )
{
int BytesWritten = 0;
int DataLen = (Len != -1)? Len : (Data)? strlen(Data) : 0;
if ((Handle->State != csOpen))
{
// Check if auto-managed handle
if (!Handle->AutoManage)
{
// Handle is not open or auto-managed
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Handle not Open (not auto-managed)",
ProcessName, Name, Handle->Name );
return 0;
}
else if (Timeout( Handle->LastAction, Handle->ReopenDelay ))
{
// Complete opening process
Open( Handle, true );
// Check if Handle is open
if (Handle->State == csOpenRequest) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Request to resolve (auto-managed) Handle",
ProcessName, Name, Handle->Name );
return 0;
}
else if (Handle->State == csWaitingtoOpen) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Waiting to open (auto-managed) Handle",
ProcessName, Name, Handle->Name );
return 0;
}
else if (Handle->State != csOpen) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, (auto-managed) Handle failed to Open",
ProcessName, Name, Handle->Name );
return 0;
}
}
else
{
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Retry (auto-managed) Handle re-open in %d ms",
ProcessName, Name, Handle->Name, TimeLeft( Handle->LastAction, Handle->ReopenDelay) );
return 0;
}
}
// Check packet length
if (Len == -1) {
Len = strlen( Data );
}
// Decide where to put data
if (Handle->OutBuffer)
{
// Write to buffer
BytesWritten = Handle->OutBuffer->Push( true, Data, Len );
// Add to select write list
if (BytesWritten && Selector) {
Selector->Add( Handle->FD, false, true, Handle, this );
}
}
else
{
// Write directly to handle / socket
errno = 0;
BytesWritten = WriteToFD( Handle->FD, Data, Len, true );
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - OUT:",
ProcessName, Name, Handle->Name );
// Report failure
if (errno) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Error sending data [%d/%d] (%s)",
ProcessName, Name, Handle->Name, -BytesWritten, DataLen, strerror(errno) );
}
else if (BytesWritten < DataLen) {
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Incomplete data sent [%d/%d]",
ProcessName, Name, Handle->Name, BytesWritten, DataLen );
}
if (BytesWritten != 0) {
// Reset timeout
SetStartTime( &(Handle->LastAction) );
}
}
return BytesWritten;
}
//---------------------------------------------------------------------------
bool CSelectableBare::Process()
{
THandle * Handle = NULL;
// Check all handles
Handle = FirstHandle;
while (Handle)
{
// Auto manage handles
if ((Handle->State == csOpenRequest))
{
// Resolve then open socket
if (Timeout( Handle->LastAction, Handle->ResolveDelay )) {
Open( Handle, false );
}
}
else if (((Handle->State != csOpen) && Handle->AutoManage && Handle->Persistent) )
{
// Try to re-open port after delay
if (Timeout( Handle->LastAction, Handle->ReopenDelay )) {
Open( Handle, false );
}
}
// Check Input buffers
if (Handle->InBuffer && (Handle->InBuffer->Len() > 0))
{
// Check duration since last PortIn
if (Timeout( Handle->InStart, Handle->InTimeout ))
{
// Process Input
ProcessInputBuffer( Handle, true );
// Reset timer
ClearStartTime( &(Handle->InStart) );
}
}
// Check for auto close (but not on servers)
if ((Handle->State == csOpen) && (Handle->Type != ctTCPserver) && (Handle->Type != ctUNIXserver) && Handle->AutoManage && !Handle->Persistent)
{
// Close port after timeout
if (Timeout( Handle->LastAction, Handle->CloseTimeout )) {
Close( Handle, true );
}
}
Handle = Handle->Next;
}
return true;
}
//---------------------------------------------------------------------------
bool CSelectableBare::BuildArgs( const char * ExecPath, int &Count, char * Args[] )
{
bool ParamStarted = false;
bool OpenQuotes = false;
char * MatchPos = NULL;
char * StartPos = NULL;
int Len;
// Validate
if (!ExecPath || !*ExecPath) {
return false;
}
// Split params
MatchPos = (char*)ExecPath;
for (;;)
{
// Look for whitespace
if (!ParamStarted)
{
// Quotes starts quoted parameter
if (*MatchPos == '"') {
ParamStarted = true;
OpenQuotes = true;
StartPos = MatchPos+1; // Skip starting quote
}
// Non-whitespace starts normal parameter
else if ((*MatchPos != ' ') && (*MatchPos != 0)) {
ParamStarted = true;
StartPos = MatchPos;
}
}
else if (OpenQuotes)
{
// Another quote ends parameter
if (*MatchPos == '"') {
Len = MatchPos-StartPos-1; // Skip end quote
Args[Count] = (char*)malloc( Len+1 );
strncpy( Args[Count], StartPos, Len );
Args[Count][Len] = 0;
Count++;
ParamStarted = false;
OpenQuotes = false;
}
}
else
{
// Whitespace ends parameter
if ((*MatchPos == ' ') || (*MatchPos == 0)) {
Len = MatchPos-StartPos;
Args[Count] = (char*)malloc( Len+1 );
strncpy( Args[Count], StartPos, Len );
Args[Count][Len] = 0;
Count++;
ParamStarted = false;
}
}
// Next char, unless NULL
if (*MatchPos)
MatchPos++;
else
break;
}
// Check all parameters closed
if (ParamStarted) {
Count = 0;
Args[0] = NULL;
return false;
}
// Set last Param to NULL
Args[Count] = NULL;
return true;
}
//---------------------------------------------------------------------------