//========================================================================
// E library
//
// - String extension functions
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Jul-31 17:39:33
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//========================================================================
#ifndef _EStrings_h
#define _EStrings_h

#include <stdio.h>

#ifdef __APPLE__
#include <wchar.h>    // For wchar_t
#else
#include <malloc.h>   // For wchar_t
#include <stddef.h>   // For wchar_t
#endif

#include <sys/types.h>
#include <string.h>

#include <ETypes.h>

//#include <EMalloc.h>


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
extern "C" {
#endif


enum
{
	EASCII = 1,
	EUTF8
};

enum
{
	EZERO_TERMINATED = 1,
	EZERO_PADDED,
	ESPACE_PADDED
};


// Entry types in a log file
//
enum
{
	EStrLOG_ERROR = 0,
	EStrLOG_WARNING,
	EStrLOG_MESSAGE
};

#define	EStrHEXA    0x0001
#define	EStrASCII   0x0010
#define	EStrCOLORS  0x0020
#define EStrCOMMAS  0x0040
#define EStrALIGN   0x0080
#define EStrHEADER  0x0100

#define	EStrSTRNULL	0x01
#define	EStrSTRENTER	0x02

#ifdef UNIX
#define EStrNLCR          "\n"
#define EStrSLASH         '/'
#define EStrSLASH_STRING  "/"
#endif

#ifdef WINDOWS
#define EStrNLCR          "\012\015"
#define EStrSLASH         '\\'
#define EStrSLASH_STRING  "\\"
#endif

enum { EStrCOLOR_NORMAL = 0, EStrCOLOR_YELLOW, EStrCOLOR_RED, EStrCOLOR_GREEN, EStrCOLOR_PURPLE, EStrCOLOR_CYAN };

// Flags for EStr_IntegerToString
//
#define EStrPAD_WITH_ZEROES  0x01  // Pad with zeroes
#define EStrSIGNED           0x02  // Signed integer value
#define EStrLEFT_JUSTIFIED   0x04  // Left-justified
#define EStrADD_SPACE        0x08  // Space if plus
#define EStrSHOW_PLUS        0x10  // Show plus sign
#define EStrUPPER_CASE       0x20  // use 'ABCDEF' instead of 'abcdef'

typedef struct
{
	char  Single[4], Multi[4], SetStart[4], SetEnd[4], SetGrpStart[4], SetGrpSep[4], SetGrpEnd[4];
} EStrFilterTokens;

typedef short  EStrFilterChar;

#define EStrMAX_FIND_STRLEN  256

enum
{
	EStrFIND_FIRST,
	EStrFIND_FORWARD,
	EStrFIND_BACKWARD
};


typedef struct
{
	EIndex  Line;
	int     StatusCode;        // ELogWARNING etc.
	char*   Message;
} EStrLineAndStatus;
EListA_t(EStrLineAndStatus);

//----------------------------------------
// Search options
//----------------------------------------
typedef struct
{
	char   SearchPath[1024];
	EBool  CaseSensitive, Wildcards, WordsOnly, PauseAtEOF, InCFunctOnly, InteractiveSearch;
	EBool  Paused;
	char   WildcardChars[256];
	EBool  LeftWordTerminators[256];   // Flags for each ASCII values if they are left-word-terminators or not (for speedup)
	EBool  RightWordTerminators[256];  // Flags for each ASCII values if they are right-word-terminators or not (for speedup)
} EStrSearchOptions;


typedef struct
{
// Used by the application
	char*   Buffer;
	EIndex  DataStart;  // Start of data in the Buffer. This is where the application should read N bytes, where N is the number returned

// Internal
	EIndex  ProcessFrom;
	EIndex  DataLength;  // Number of bytes of valid data starting at DataStart
	EIndex  BufferSize;  // Number of bytes allocated for Buffer
	EBool   LastBlock;
	EIndex  BlockSize;
} EStrLineReader;


//static __inline__ unsigned long long _x86_GetCycleCount(void)

#ifdef __linux__
	#ifndef Ex86
		#define Ex86
	#endif
#endif

/*
#ifdef __APPLE__
	#ifndef Ex86
		#define Ex86
	#endif
#endif
*/


#ifdef Ex86
#define E_CPUGetCycleCount	_x86_GetCycleCount
#endif


#ifdef Ex86
static inline unsigned long long _x86_GetCycleCount(void)
{
  unsigned int	hi, lo;

  __asm__ __volatile__
  (
   "rdtsc"
   : "=a"(lo), "=d"(hi)
  );
  return((unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#endif


extern EBool   E_Powerof2(const EIndex PNumber, int* PExponent, EIndex* PNearestPowerOf2);

extern float   E_Fixed10To_f32(const int PValue, const int PDecimals);
extern double  E_Fixed10To_f64(const int PValue, const int PDecimals);


extern EBool	E_GetCPUInfo(int* PType, double* PCPUClock, EIndex* PNumOfCPUs);


// Shell escape sequences
//
extern char  ECodeNormal[];
extern char  ECodeNormal1[];

extern char  ECodeBright[];
extern char  ECodeDim[];
extern char  ECodeUnderline[];
extern char  ECodeFlash[];
extern char  ECodeReverseVideo[];
extern char  ECodeHidden[];
extern char  ECodeStrikeThrough[];

extern char  ECodeBlack[];
extern char  ECodeRed[];
extern char  ECoreRed1[];
extern char  ECodeGreen[];
extern char  ECodeGreen1[];

extern char  ECodeBlue[];
extern char  ECode0Blue[];

extern char  ECodeCyan[];
extern char  ECode01Cyan[];
extern char  ECodeCyan1[];

extern char  ECodeMagenta[];
extern char  ECodeMagenta1[];

extern char  ECodeYellow[];
extern char  ECode0Yellow[];

extern char  ECodeWhite[];

extern char	 ECodeEraseInLine[];
extern char	 ECodeBold[];
extern char	 ECode0Green[];
extern char	 ECode0BoldBlack[];
extern char  ECodeBoldGreen[];
extern char  ECodeBoldMagenta[];


extern char*  EStr_GetNormalColorCode(char* PPtr);
extern char*  EStr_GetRedCode(char* PPtr);
extern char*  EStr_GetGreenCode(char* PPtr);
extern char*  EStr_GetBlueCode(char* PPtr);
extern char*  EStr_GetCyanCode(char* PPtr);
extern char*  EStr_GetMagentaCode(char* PPtr);
extern char*  EStr_GetYellowCode(char* PPtr);

extern char   EStr_LowerCaseTable[256];
extern void   EStr_LowerCaseTableInit(void);
extern char   EStr_CharToLowerCase(const char PChar);

extern int    EStr_ProgressBarPrint(const char* PPrefix, int PPrintedChars, const double PPercentage, const int PLength);

extern void   EStr_HexaDump(const void* PBuffer, const EIndex PStartOffset, const EIndex PLength, const EIndex PBytesPerLine, const uint16_t PFlags);


extern void   EStr_DefaultSearchOptions(EStrSearchOptions* POptions);
extern EBool  EStr_FindString(const void* PText,  const EIndex PSPosition, const EIndex PBegin, const EIndex PEnd,  const void* PStringIn, const int PDirection, EIndex* PFoundPosition, EIndex* PFoundLength, EStrSearchOptions* POptions);

extern char*  EStr_MillisecondsToString(const ETime PTime, char* PTimeStr, const EBool PAlign);
extern char*  EStr_MicrosecondsToString(const ETime PTime, char* PTimeStr, const EBool PAlign);
extern char*  EStr_NanosecondsToString(const ETime PTime, char* PTimeStr, const EBool PAlign);

extern int    EStr_DecASCIIi32(char* PStr, int32_t PV, int PND);
extern int    EStr_DecASCIIZBi32(char* PStr, int32_t PV, int PND);
extern int    EStr_DecASCIIZBi64(char* PStr, int64_t PV, int PND);

extern char*  EStr_IntegerToString(long long PNum, int PNumberBase, int PFlags, int PFieldWidth, char* POutPtr);
extern char*  EStr_Int64ToStringHighlightThousands(const long long PValue, const int PFieldWidth, const char* PColor1, const char* PColor2, char* PPtr);

enum
{
	EZeros = 1,
	ESpaces
};

extern int    EStr_ByteSizeToString(long long PV, char* PStr, const EBool PDecimals, const uint8_t PPadding);
extern int    EStr_DoubleTokMGString(const double PV, const EBool PPadFromLeft, char* PStr);


extern EIndex  EStr_StringToToken(const char** PPtrP, char** PNID, EIndex* PLenAllocated);
extern EIndex  EStr_StringToTokenQ(const char** PPtrP, char** PNID, EIndex* PLenAllocated);
extern EIndex  EStr_StringToInt(const char* PPtr, const char PEndCharacter);
extern EIndex  EStr_StringToIntI(const char** PPtrRet);
extern EIndex  EStr_StringToIntIT(const char** PPtrRet, const char PEndCharacter);
extern int     EStr_StringToIntChk(const char* PPtr, int* PValueP);


extern int8_t  EStr_ASCII2Hex[256];extern EBool  EStr_ASCII2HexInited;
extern void    EStr_StringToIntHexInit(void);
extern int     EStr_StringToIntHex(const char* PStr);

extern double  EStr_StringToDouble(const char* PPtr);
extern double  EStr_StringToDoubleI(const char** PPtrRet);
extern double  EStr_StringToDoubleSkipSpaceAtEnd(const char** PPtrRet);

extern double  EStr_StringToDoubleSci(const char* PPtr);
extern double  EStr_StringToDoubleSciI(const char** PPtrRet, const char PEndCharacter, EBool* PFoundNumber);

extern int     EStr_UIntToString(const unsigned int PNum, char* POutPtr);

extern int     EStr_DoubleToString(const double PValue, const EBool PLeaveSpaceForSign, char* PTmpStr);

extern EIndex  EStr_Len(const char* PString);
extern EIndex  EStr_LenW(const wchar_t* PString);

extern EIndex  EStr_LenT(const char* PString, const uint8_t PEndChars);

extern char*   EStr_Append(char** PStr0, const char* PStr1, EIndex* PCharactersRemaining);

extern int     EStr_StringPadToLength(char* PString0, const int PLen);

extern char*   EStr_StringGetUnique(const char* PString, const char** PStrings, const EIndex PNumOfStrings);
extern char*   EStr_StringGetAndAddUniqueA(const char* PString, char*** PStringsP, EIndex* PNumOfStringsP, EIndex* PNumOfStringsAllocatedP);

extern EBool   EStr_Equal(const char* PString0, const char* PString1);
extern EBool   EStr_Equal8toW(const char* PString0, const wchar_t* PString1);
extern EBool   EStr_EqualW(const wchar_t* PString0, const wchar_t* PString1);

extern EBool   EStr_EqualNoCase(const char* PString0, const char* PString1);
extern EBool   EStr_NEqual(const char* PString0, const char* PString1, const int PLen);
extern EBool   EStr_NEqualNoCase(const char* PString0, const char* PString1, const int PLen);
extern EBool   EStr_MemEqual(const uint8_t* PString0, const uint8_t* PString1,  const EIndex PLen);

extern char**  EStr_StringListToArray(const char* PInStr, EIndex* PNumOfStringsRet, const char PSeparator);

extern int     EStr_FilterTokenize(const char* PFilterStr, const EStrFilterTokens* PFilterTokenStrings,  EStrFilterChar** PFilter, const EIndex PMaxLen,  const EBool PExactMatch);
extern int     EStr_FilterStrPass(char* PStrIn, char* PFilterStrIn, const EBool PCaseSensitive, EStrFilterTokens* PFilterTokenStrings);
extern int     EStrFilter_Pass(const EStrFilterChar* PFilterIn,  const char* PStrIn, const EBool PCaseSensitive);
extern int     EStrFilter_All(const EStrFilterChar* PFilter);
extern int     EStrFilter_HasWildcard(EStrFilterChar* PFilter);
extern void    EStrFilter_Print(const EStrFilterChar* PFilter);

extern int     EStr_GetFileNameLength(const char* PFullPath);
extern int     EStr_PathIsAbsolute(const char* PPath);
extern char*   EStr_CorrectDirName(const char* PPath);
extern char*   EStr_CorrectPath(const char* PPath);
extern const char*  EStr_GetFileName(const char* PFullStr);
extern const char*  EStr_GetFileNameExtension(const char* PFullStr);

extern EBool  EStr_FileNameExtensionsMatch(const char* PFileName, char** PExtensions);

extern char*  EStr_GetParentDir(char* PFullStr, char* PRet, const int PMaxLen);

extern char*   EStr_GetPathName(const char* PFullStr,  char* PRet, const EIndex PMaxLen);
extern EIndex  EStr_GetLastNonWildPos(const char* PPath, EStrFilterTokens* PFilterTokenStrings);
extern int     EStr_GetBasePath(const char* PPathS, const char** PExtensions, const EBool PRemoveNumber,  void* PPathD, const EIndex PMaxLen,  int* PNumberPtr, char** PExtensionD);
extern char*   EStr_GetColorCode(char* PPtr, const int PColor);


extern void   EStr_InitLogging(char* PFileName, char* PLogFileHeader);
extern void   EStr_LogToFile(const int PType, const char* PPrefix, const char* PMessage, ...);
extern void   EStr_Log(const int PType, const char* PPrefix, const char* PMessage, ...);


extern const char  EStr_HexaChars[];
extern const char  EStr_Months[][4];
extern EBool       EStr_TimeToString(const time_t PTime, const int PThisYear, char* PString);


extern char*      EStr_CurrentDir(const EBool PAddSlash);

extern char*      E_LaunchPath(const char* PCommandLine);

extern int        Printf(char* PMessage, ...);

extern EIndex     EStr_ASCIItoWChar(const char* PInStr, wchar_t* PResult);
extern EIndex     EStr_WCharNToASCII(const wchar_t* PSrc, const EIndex PLen,  const char PUseThisForInvalidChars,  char* PDst);

extern EIndex     EStr_UTF8toWChar(const char* PInStr, const EIndex PInLen,  wchar_t* PResult, const EIndex POutSize);
extern wchar_t*   EStr_DupUTF8toWChar(const char* PInStr);

extern EIndex     EStr_UTF32toUTF8(wchar_t const** PSrcStart, const EIndex PSrcLength,  char** PDstStart, const EIndex PDstLength, const EBool PStrict);


extern int        EFile_Load(const char* PFileName, uint8_t** PBufferRet, EIndex* PSizeRet);
extern int        EFile_Save(const char* PFileName, const void* PBuffer, const EIndex PBytes);
extern int        EFile_TextFileLoad(const char* PFileName, char** PBufferRet, EIndex* PSizeRet, const EBool PZeroTerminate);
extern int        EFile_OpenFileLoad(FILE* PFile, uint8_t** PBufferRet, EIndex* PSizeRet);

extern int        EFile_Copy(const char* PSrcName, const char* PDstName, const EBool PPreserveTimestamps);
extern int        EFile_MkDir(const char* PName, const EBool PRecursive, const int PVerbosity);
extern int        EFile_RmDir(const char* PPath);


extern const char*  EResult_ToString(const int PIOResult);
extern int          E_errnoToIOResult(const int Perrno);
extern void         E_errnoPrintString(const int Perrno);

extern void       EStr_TextPosToXY(const char* PText, const EIndex PTextPosition,  EIndex* PCol, EIndex* PLine);
extern EIndex     EStr_XYToTextPos(const char* PText,  const EIndex PCharX, const EIndex PCharY);
extern EIndex     EStr_XYToTextPosW(const wchar_t* PText,  const EIndex PCharX, const EIndex PCharY);


extern void       EStr_ReaderInit(EStrLineReader* PReader, const EIndex PBlockSize);
extern void       EStr_ReaderFreeContents(EStrLineReader* PReader);
extern EIndex     EStr_FileReadLine(FILE* PFile, EStrLineReader* PReader, char** PDataStart);
extern EIndex     EStr_GetLine(const char* PStr, const EIndex PMaxLen, EIndex* PLineLength);

extern void       EStr_PrintWithLineNumbers(const char* PStr, const char* PColorCode);
extern void       EStr_PrintWithLineNumbersAndStatusColor(const char* PStr, const char* PColorCode, EStrLineAndStatusListA* PLineStatus, const EBool PPrintRegularLines);

extern char*      EStr_CorrectFormatString(char* PStr);

extern char*      EStr_Labelify(const char* PSrc, char* PDst);

extern ETime      EStr_EXIFDateTimeStringToSeconds(const char* PStr);

extern char*      E_GetUserName(char* PResult, const int PLen);

extern uint8_t*   EStr_FlyWeightTable(const uint8_t* PStrings, const char* PPrefix, const char* PTableName, size_t* PSizeP);


static inline char* _TimeToColor(const ETime PMicroseconds)
{
	if(PMicroseconds < 1000) return(ECodeNormal);
	else if(PMicroseconds < 10000) return(ECodeGreen);
	else if(PMicroseconds < 100000) return(ECodeCyan);
	else if(PMicroseconds < 1000000) return(ECodeYellow);
	return(ECodeNormal);
}


#define E_NamedTime(pName) do\
{\
	const ETime  lNow = E_GetMicroseconds(), lInterval = lNow - LStartMicroseconds;\
	pName##_Microseconds += lInterval;\
} while(0)


#define E_TimeStampClear	LPrevMicroseconds = E_GetMicroseconds()

#define E_TimeStampS(pElapsed) do\
{\
	if(pElapsed##_Microseconds >= 1000)\
	{\
		char	lStr[256];EStr_MicrosecondsToString(pElapsed##_Microseconds, lStr, FALSE);\
		printf("%s() %s:%d %s%s %s%s\n", __FUNCTION__, __FILE__, __LINE__, _TimeToColor(pElapsed##_Microseconds), #pElapsed, lStr, ECodeNormal);fflush(stdout);\
	}\
}  while(0)

#define E_TimeStampFrom(pFrom, pName) do\
{\
	const ETime  lNow = E_GetMicroseconds(), lElapsed = lNow - pFrom;if(lElapsed >= 1000)\
	{\
		char	lStr[256];EStr_MicrosecondsToString(lElapsed, lStr, FALSE);\
		printf("%s() %s:%d %s%s %s%s\n", __FUNCTION__, __FILE__, __LINE__, _TimeToColor(lElapsed), pName, lStr, ECodeNormal);fflush(stdout);\
	}\
	LPrevMicroseconds = lNow;\
} while(0)\

#define E_TimeStamp(pName) do\
{\
	const ETime  lNow = E_GetMicroseconds(), lElapsed = lNow - LPrevMicroseconds;if(lElapsed >= 1000)\
	{\
		char	lStr[256];EStr_MicrosecondsToString(lElapsed, lStr, FALSE);\
		printf("%s() %s:%d %s%s %s%s\n", __FUNCTION__, __FILE__, __LINE__, _TimeToColor(lElapsed), pName, lStr, ECodeNormal);fflush(stdout);\
	}\
	LPrevMicroseconds = lNow;\
} while(0)\



//========================================================
// Output pValuesPerLine ints per line
// Input: an array of ints
//========================================================
#define E_IntsToString(pType, pElementSize,  pArray, pCount,  pValuesPerLine, pStrBuffer) do\
{\
	const int  lSingleValueStringWidth = 21;  /* 64-bit ints can take up to 20 digits, plus the sign	*/\
\
/* Allocate buffer for content string	*/\
\
	const int  lLineWidth = lSingleValueStringWidth + 2;  /* 1 value and a new-line (2 bytes for Bill's silly OS)	*/\
	char*      lPtr;\
	pStrBuffer = lPtr = (char*)DAE_Malloc(lLineWidth * pCount + 2);\
	if(pStrBuffer)\
	{\
		const int  lValuesPerLine = DAE_MaxValuesPerLine;\
		int        lValsInLine = 0;\
		EBool      lAddSpaceBefore = FALSE;\
\
		int  lC = 0;const t_##pType*  lElement = pArray;for(;lC < pCount;lC++, EStruct_Inc(lElement, pElementSize))\
		{\
			lValsInLine++;\
			if(lAddSpaceBefore) *lPtr++ = ' ';\
			lPtr += DAE_##pType##ToString(*lElement, lPtr);\
			if(lValsInLine > lValuesPerLine)\
			{\
				lValsInLine = 0;\
				if(lC < pCount - 1) *lPtr++ = '\n';\
				lAddSpaceBefore = FALSE;\
			}\
			else lAddSpaceBefore = TRUE;\
		}\
		*lPtr = 0;\
	}\
} while(0)


//========================================================
// Input: an array of struct with an int member (pField0)
// Output pValuesPerLine ints per line
//========================================================
#define E_Int1sToString(pType, pArray, pCount, pField0, pStrBuffer) do\
{\
	const int  lSingleValueStringWidth = 21;  /* 64-bit ints can take up to 20 digits, plus the sign	*/\
\
/* Allocate buffer for content string	*/\
\
	const int  lLineWidth = lSingleValueStringWidth + 2;  /* 1 value and a new-line (2 bytes for Bill's silly OS)	*/\
	char*      lPtr;\
	pStrBuffer = lPtr = (char*)DAE_Malloc(lLineWidth * pCount + 2);\
	if(pStrBuffer)\
	{\
		const pType*  lStruct = pArray;\
		const int     lValuesPerLine = DAE_MaxValuesPerLine;\
		int           lValsInLine = 0;\
		EBool         lAddSpaceBefore = FALSE;\
\
		int  lC = 1;for(;lC < pCount;lC++, lStruct++)\
		{\
		 lValsInLine++;\
		 if(lAddSpaceBefore) *lPtr++ = ' ';\
		 lPtr += DAE_Int32ToString(lStruct->pField0, lPtr);\
		 if(lValsInLine > lValuesPerLine)\
		 {\
			 lValsInLine = 0;\
			 if(lC < pCount - 1) *lPtr++ = '\n';\
			 lAddSpaceBefore = FALSE;\
		 }\
		 else lAddSpaceBefore = TRUE;\
		}\
		*lPtr = 0;\
	}\
} while(0)


//========================================================
// Output 1 float per line.
// Input: an array of float-type values
//========================================================
#define E_Float32sToString(pType, pElementSize,  pArray, pCount,  pStrBuffer) do\
{\
	const int  lSingleValueStringWidth = DAE_MaxFloat32LengthInASCII;\
	const int  lLineWidth = lSingleValueStringWidth + 2;  /* 1 value and a new-line (2 bytes for Bill's silly OS)	*/\
\
/* Allocate buffer for content string	*/\
\
	char*  lPtr;\
	pStrBuffer = lPtr = (char*)DAE_Malloc(lLineWidth * pCount + 2);\
	if(pStrBuffer)\
	{\
		const int  lValuesPerLine = DAE_MaxValuesPerLine;\
		int        lValsInLine = 0;\
		EBool      lAddSpaceBefore = FALSE;\
\
		int  lC = 0;const pType*  lElement = pArray;for(;lC < pCount;lC++, EStruct_Inc(lElement, pElementSize))\
		{\
			lValsInLine++;\
			if(lAddSpaceBefore) *lPtr++ = ' ';\
			lPtr += DAE_Float32ToString(*lElement, lPtr);\
			if(lValsInLine > lValuesPerLine)\
			{\
				lValsInLine = 0;\
				if(lC < pCount - 1) *lPtr++ = '\n';\
				lAddSpaceBefore = FALSE;\
			}\
			else lAddSpaceBefore = TRUE;\
		}\
		*lPtr = 0;\
	}\
} while(0)


//========================================================
// Output 1 float per line.
// Input: an array of float-type values
//========================================================
#define E_CoordinatesToString(pType, pElementSize,  pArray, pCount,  pStrBuffer)do \
{\
	const int  lSingleValueStringWidth = DAE_MaxCoordinateLengthInASCII;\
	const int  lLineWidth = lSingleValueStringWidth + 2;  /* 1 value and a new-line (2 bytes for Bill's silly OS)	*/\
\
/* Allocate buffer for content string	*/\
\
	char*  lPtr;\
	pStrBuffer = lPtr = (char*)DAE_Malloc(lLineWidth * pCount + 2);\
	if(pStrBuffer)\
	{\
		const int  lValuesPerLine = DAE_MaxValuesPerLine;\
		int        lValsInLine = 0;\
		EBool      lAddSpaceBefore = FALSE;\
\
		int	lC = 0;const pType*  lElement = pArray;for(;lC < pCount;lC++, EStruct_Inc(lElement, pElementSize))\
		{\
			lValsInLine++;\
			if(lAddSpaceBefore) *lPtr++ = ' ';\
			lPtr += DAE_CoordinateToString(*lElement, lPtr);\
			if(lValsInLine > lValuesPerLine)\
			{\
				lValsInLine = 0;\
				if(lC < pCount - 1) *lPtr++ = '\n';\
				lAddSpaceBefore = FALSE;\
			}\
			else lAddSpaceBefore = TRUE;\
		}\
		*lPtr = 0;\
	}\
} while(0)


//========================================================
// Output 2 floats per line. Uniform column width
// Input: an array of arbitrary struct with X and Y
//========================================================
#define E_Coord2sToString(pType, pElementSize,  pArray, pCount,  pStrBuffer) do\
{\
	const int  lSingleValueStringWidth = DAE_MaxCoordinateLengthInASCII;\
\
/* Allocate buffer for content string	*/\
\
	const int  lLineWidth = lSingleValueStringWidth * 2 + 3; /* 2 floats, 1 space and a new-line (2 bytes for Bill's silly OS)	*/\
	char*      lPtr;\
	pStrBuffer = lPtr = (char*)DAE_Malloc(lLineWidth * pCount + 2);\
	if(pStrBuffer)\
	{\
		const int  lValuesPerLine = DAE_MaxValuesPerLine;\
		int        lValsInLine = 0;\
		EBool      lAddSpaceBefore = FALSE;\
\
		int	lC = 0;const pType*  lElement = pArray;for(;lC < pCount;lC++, EStruct_Inc(lElement, pElementSize))\
		{\
			lValsInLine++;\
			if(lAddSpaceBefore) *lPtr++ = ' ';\
			lPtr += DAE_CoordinateToString(lElement->X, lPtr);*lPtr++ = ' ';\
			lPtr += DAE_CoordinateToString(lElement->Y, lPtr);\
			if(lValsInLine > lValuesPerLine)\
			{\
				lValsInLine = 0;\
				if(lC < pCount - 1) *lPtr++ = '\n';\
				lAddSpaceBefore = FALSE;\
			}\
			else lAddSpaceBefore = TRUE;\
		}\
		*lPtr = 0;\
	}\
} while(0)


//========================================================
// Output 3 floats per line. Variable column width
// Input: an array of arbitrary struct with X, Y and Z
// This version omits trailing zeroes in the decimals
//========================================================
#define E_Coord3sToString(pType, pElementSize,  pArray, pCount,  pField0, pField1, pField2,  pStrBuffer) do\
{\
	const int	 lSingleValueStringWidth = DAE_MaxCoordinateLengthInASCII;\
\
/* Allocate buffer for content string	*/\
\
	const int  lLineWidth = lSingleValueStringWidth * 3 + 4;	/* 3 floats, 2 spaces and a new-line (2 bytes for Bill's silly OS)	*/\
	char*      lPtr;\
	pStrBuffer = lPtr = (char*)DAE_Malloc(lLineWidth * pCount + 2);\
	if(pStrBuffer)\
	{\
		const int  lValuesPerLine = DAE_MaxValuesPerLine;\
		int        lValsInLine = 0;\
		EBool      lAddSpaceBefore = FALSE;\
\
		int  lC = 0;const pType*  lElement = pArray;for(;lC < pCount;lC++, EStruct_Inc(lElement, pElementSize))\
		{\
			lValsInLine++;\
			if(lAddSpaceBefore) *lPtr++ = ' ';\
			lPtr += DAE_CoordinateToString(lElement->pField0, lPtr);*lPtr++ = ' ';\
			lPtr += DAE_CoordinateToString(lElement->pField1, lPtr);*lPtr++ = ' ';\
			lPtr += DAE_CoordinateToString(lElement->pField2, lPtr);\
			if(lValsInLine > lValuesPerLine)\
			{\
				lValsInLine = 0;\
				if(lC < pCount - 1) *lPtr++ = '\n';\
				lAddSpaceBefore = FALSE;\
			}\
			else lAddSpaceBefore = TRUE;\
		}\
		*lPtr = 0;\
	}\
} while(0)


//========================================================
// Output strings, separated by ' '
//========================================================
#define E_StringList(pType, pElementSize,  pArray, pCount,  pStrBuffer) do\
{\
	int*  lSizes = EMalloc(sizeof(int) * pCount);\
	int   lStringSize = 0;\
\
/* Allocate buffer for content string	*/\
\
	{ int	lC = 0;for(;lC < pCount;lC++) { lSizes[lC] = strlen(pArray[lC]);lStringSize += lSizes[lC] + 2; } }\
\
	char*  lPtr;\
	pStrBuffer = lPtr = (char*)DAE_Malloc(lStringSize + 2);\
	if(pStrBuffer)\
	{\
		const int  lValuesPerLine = DAE_MaxValuesPerLine;\
		int        lValsInLine = 0;\
		EBool      lAddSpaceBefore = FALSE;\
		int  lC = 0;for(;lC < pCount;lC++)\
		{\
			lValsInLine++;\
			if(lAddSpaceBefore) *lPtr++ = ' ';\
			memcpy(lPtr, pArray[lC], lSizes[lC]);lPtr += lSizes[lC];\
			if(lValsInLine > lValuesPerLine)\
			{\
				lValsInLine = 0;\
				if(lC < pCount - 1) *lPtr++ = '\n';\
				lAddSpaceBefore = FALSE;\
			}\
			else lAddSpaceBefore = TRUE;\
		 }\
	}\
	*lPtr = 0;\
	EFree(lSizes);\
} while(0)


//========================================================
// Input: an array of EBool-type values
// Output boolean string with "true" or "false" values
//========================================================
#define E_BoolsToString(pType, pElementSize,  pArray, pCount,  mMaxValuesPerLine, pStrBuffer) do\
{\
	const int  lSingleValueStringWidth = 6;\
	const int  lLineWidth = lSingleValueStringWidth + 2;  /* 1 value and a new-line (2 bytes for Bill's silly OS)	*/\
\
/* Allocate buffer for content string	*/\
\
	char*  lPtr;\
	pStrBuffer = lPtr = (char*)DAE_Malloc(lLineWidth * pCount + 2);\
	if(pStrBuffer)\
	{\
		const int  lValuesPerLine = mMaxValuesPerLine;\
		int        lValsInLine = 0;\
		EBool      lAddSpaceBefore = FALSE;\
\
		int  lC = 0;const pType*  lElement = pArray;for(;lC < pCount;lC++, EStruct_Inc(lElement, pElementSize))\
		{\
			lValsInLine++;\
			if(lAddSpaceBefore) *lPtr++ = ' ';\
			if(*lElement) { memcpy(lPtr, "true", 4);lPtr += 4; } else { memcpy(lPtr, "false", 5);lPtr += 5; }\
			if(lValsInLine > lValuesPerLine)\
			{\
				lValsInLine = 0;\
				if(lC < pCount - 1) *lPtr++ = '\n';\
				lAddSpaceBefore = FALSE;\
			}\
			else lAddSpaceBefore = TRUE;\
		}\
		*lPtr = 0;\
	}\
} while(0)


//================================================
// Append an unsigned int to a string
//================================================
#define E_StringAddUInt(pStrBuffer, pStrBufferSize, pStrBufferSizeAllocated,  pV, pC, pSeparatorCharacter, pPtr) do\
{\
	const int  lMinLen = 16, lIncrement = lMinLen * 16;\
	if(pStrBufferSizeAllocated < pStrBufferSize + lMinLen) { pStrBuffer = (char*)ERealloc(pStrBuffer, pStrBufferSizeAllocated + lIncrement);pStrBufferSizeAllocated += lIncrement;pPtr = pStrBuffer + pStrBufferSize; }\
	if(pC > 0) { *(pPtr++) = pSeparatorCharacter;pStrBufferSize++; }  /* If the number is not the 1st one in a row, add a space in front of it 	*/\
	const int  lLen = EStr_UIntToString(pV, pPtr);pStrBufferSize += lLen;pPtr += lLen;\
} while(0)


//================================================
// Append a float to a string
//================================================
#define E_StringAddFloat(pStrBuffer, pStrBufferSize, pStrBufferSizeAllocated,  pV, pC, pPtr) do\
{\
	const int  lMinLen = 256, lIncrement = lMinLen * 4;\
	if(pStrBufferSizeAllocated < pStrBufferSize + lMinLen) { pStrBuffer = (char*)ERealloc(pStrBuffer, pStrBufferSizeAllocated + lIncrement);pStrBufferSizeAllocated += lIncrement;pPtr = pStrBuffer + pStrBufferSize; }\
	if(pC > 0) { *(pPtr++) = ' ';pStrBufferSize++; }  /* If the number is not the 1st one in a row, add a space in front of it 	*/\
	const int  lLen = EStr_DoubleToString(pV, FALSE, pPtr);pStrBufferSize += lLen;pPtr += lLen;\
} while(0)


#define EStrM_StringGetUnique(pBaseStr, pNSrc, pAccess,  pResult) do\
{\
	EIndex  lC = 0, lN = 0, lNA = 0;const EIndex  lNSrc = pNSrc;char**  lNames = NULL;for(;lC < lNSrc;lC++)\
	{\
		const char*  lSrc;pAccess(lC, lSrc);if(lSrc) ELst_PointerAddA(&lNames, &lN, &lNA, 8, lSrc);\
	}\
\
	char*  lResult = NULL;\
	if(lNames)\
	{\
		lResult = EStr_StringGetUnique(pBaseStr, (const char**)lNames, lN);\
		EFree(lNames);\
	}\
\
	if(lResult) pResult = lResult;\
	else pResult = EStrDup(pBaseStr);\
} while(0)


#ifdef __USE_GNU
#define HAVE_qsort_r  1
#define EQSort_r      qsort_r
#else
#define HAVE_qsort_r  0
typedef int (*ECmpFunc)(const void* P0, const void* P1,  void* PArg);
extern void  EQSort_r(void* const pbase, size_t total_elems, size_t size, ECmpFunc cmp, void* arg);
#endif


#if defined (__cplusplus) && (!(defined (__CPLUSLIBS)))
}	// Close scope of 'extern "C"' declaration
#endif

#endif	// _EStrings_h
