/*
Copyright (c) 2015 Colum Paget <colums.projects@googlemail.com>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

#ifndef LIBUSEFUL_STRING_H
#define LIBUSEFUL_STRING_H

/*****************************************************************************
Functions related to resizeable strings.

From version 4.0 libUseful uses 'StrLenCache-ing'. This dramatically speeds up
programs that deal with long strings, as it's no longer needed to iterate 
through the entire string to calculate its length. Instead the lengths of the
most recently used strings are held in a cache. This means less data passes
through the CPU's L1/L2/L3 cache, which can also further speed things up.

The downside to this is that you can no longer just set a character to the 
null character (ascii zero or '/0') in order to truncate the string, because
the cache will still think the string has it's old length and functions like
CatStr will misbehave, as characters will be added *after* the terminating 
null character, and so lost. This you must use the 'StrTrunc', 'StrTruncChar'
and 'StrRTruncChar' functions to truncate strings

Although, since the above was written, modern CPUs that have a strlen opcode
are faster, but the difference is close enough to be hard to detect. 
*****************************************************************************/

#include <stdarg.h>
#include <string.h> //for strlen, used below in StrLen
#include "defines.h"
#include "GeneralFunctions.h"
#include "StrLenCache.h"

//Used with 'MatchTokenFromList'
#define MATCH_TOKEN_PART 1
#define MATCH_TOKEN_CASE 2

#ifdef __cplusplus
extern "C" {
#endif


// this is for compatiblity with older programs that use 'DestroyString', whose name
// was later changed to 'Destroy' as you can use it to free any object. It doesn't
// crash if you pass it a NULL pointer
#define DestroyString(s) (Destroy(s))

//A few very simple and frequently used functions can be reduced
//down to macros using weird stuff like the ternary condition
//operator '?' and the dreaded comma operator ','

//return length of a string. Doesn't crash if string is NULL, just returns 0
#define StrLen(str) ( (str) ? strlen(str) : 0 )


//if a string is not null, and not empty (contains chars) then return true. Doesn't call 'strlen' or iterate through entire string
//so is more efficient then using StrLen for the same purpose
#define StrValid(str) ( (str && (*(const char *) str != '\0')) ? TRUE : FALSE )


//returns true if string is NULL or empty
#define StrEnd(str) ( (str &&  (*(const char *) str != '\0')) ? FALSE : TRUE )

//copy a number of strings into Dest. Like this:
//   Dest=MCopyStr(Dest, Str1, Str2, Str3, NULL);
#define MCopyStr(Dest, ...) InternalMCopyStr(Dest, __VA_ARGS__, NULL)

//list MCopyStr but concatanantes strings onto Dest rather than copying into it
#define MCatStr(Dest, ...) InternalMCatStr(Dest, __VA_ARGS__, NULL)

//return a copy of a string
#define CloneStr(Str) (CopyStr(NULL,Str))

//Concat 'Src' onto 'Dest'
//yes, we need the strlen even though it means traversing the string twice. 
//We need to know how much room 'realloc' needs
#define CatStr(Dest, Src) (CatStrLen(Dest,Src,StrLen(Src)))


//Quote some standard chars in a string with '\'. 
#define EnquoteStr(Dest, Src) (QuoteCharsInStr((Dest), (Src), "'\"\r\n"))




//uppercase a string
char *strupr(char *Str);

//lowercase a string
char *strlwr(char *Str);

//replace every instance of character 'c1' with character 'c2'
char *strrep(char *Str, char c1, char c2);

//replace any character in the list 'oldchars' with the charcter 'newchar
char *strmrep(char *str, char *oldchars, char newchar);

//strtol, except only considering the first 'len' bytes
int strntol(const char **ptr, int len, int radix, long *value);

//returns 'true' if 'str' is 'true' or 'y' (case insensitive) or any non-zero number
int strtobool(const char *str);

// returns true if string only contains alphabetic characters
int istext(const char *Str);

//returns true if string only contains digits
int isnum(const char *Str);

int strcount(const char *Str, char Char);


//allocate or reallocate 'Len' bytes of memory to a resizeable string
char *SetStrLen(char *Str, size_t Len);

//strcmp that won't crash if str is null
int CompareStr(const char *S1, const char *S2);

//strncmp that won't crash if str is null
int CompareStrLen(const char *S1, const char *S2, size_t len);

//strcasecmp that won't creash if str is null
int CompareStrNoCase(const char *S1, const char *S2);

//strncasecmp that won't creash if str is null
int CompareStrLenNoCase(const char *S1, const char *S2, size_t len);


//copy Len bytes from Src to Dest, resizing Dest if needed and return Dest
char *CopyStrLen(char *Dest, const char *Src, size_t Len);

//copy Src to Dest, resizing Dest as necessary
char *CopyStr(char *Dest, const char *Src);

//concatanate Len bytes from Src to Dest, resizing Dest if needed and return Dest
char *CatStrLen(char *Dest, const char *Src, size_t Len);

//Pad a str with 'PadLen' copies of character 'Pad'
char *PadStr(char*Dest, char Pad, int PadLen);

//Pad a str TO 'PadLen' with copies of character 'Pad'
char *PadStrTo(char*Dest, char Pad, int PadLen);

//copy a string and pad it with PadLen chars
char *CopyPadStr(char*Dest, const char *Src, char Pad, int PadLen);

//copy a string and pad it TO PadLen  length
char *CopyPadStrTo(char*Dest, const char *Src, char Pad, int PadLen);

char *ReplaceStr(char *RetStr, const char *Input, const char *Old, const char *New);

//these are the quivalent of 'sprintf', except with resizable strings. Furthermore %n is not supported.
char *VFormatStr(char *RetStr, const char *Format, va_list);
char *FormatStr(char *RetStr, const char *Format, ...);

//Add a single char to string. Has to internally strlen the string to find the end, so this can be slow if doing a lot
//of char add operations
char *AddCharToStr(char *Buffer, char Char);

//add char to a string of length 'Len'. Avoids doing a strlen so may be more efficient
char *AddCharToBuffer(char *Buffer, size_t Len, char Char);

//add 'Len' Bytes to a string already containing 'DestLen' characters
char *AddBytesToBuffer(char *Dest, size_t DestLen, char *Bytes, size_t Len);
//Truncate a string to so many chars, WITHOUT CHECKING IT'S GOT THAT MANY
//this saves a strlen and is mostly for internal use. 
//It does at least check that the string is not NULL
char *StrUnsafeTrunc(char *Str, int Len);


//Truncate a string to so many chars
char *StrTrunc(char *Str, int Len);

//Truncate a string to a terminator character. 
//Returns 'TRUE' if character found, 'FALSE' otherwise
int StrTruncChar(char *Str, char Term);

//Truncate a string to a terminator character but search for character from end of string ('R' for 'Reverse')
//Returns 'TRUE' if character found, 'FALSE' otherwise
int StrRTruncChar(char *Str, char Term);

//strip trailing whitespace from a string. Though string is returned, it is never realloced.
char *StripTrailingWhitespace(char *Str);

//strip leading whitespace from a string. Though string is returned, it is never realloced.
char *StripLeadingWhitespace(char *Str);

//strip leading and trailing whitespace. Though string is returned, it is never realloced.
char *StringTrim(char *Str);

//strip carriage-return and linefeed characters from the end of a string
char *StripCRLF(char *Str);

//If a string starts with a char in 'StartChars' then strip both that, and it's companion in 'EndChars'
//e.g.  StripStartEndChars(Str, "[({", "])}");
//if the string starts with '(', then the matching ')' is looked up in end chars and removed from the end if present
//if '(' isn't matched by a ')' at the end, then it's not removed
//StripStartEndChars returns the changed string, but as the strip is done using memmove there's no need
//to write:
//  Str=StripStartEndChars(Str, "(", ")");
//it's enough to write:
//  Str=StripStartEndChars(Str, "(", ")");
char *StripStartEndChars(char *Str, const char *StartChars, const char *EndChars);

//Strip either \" or \' characters surrounding a string. Only strips if the string starts and
//ends with the same character (either \" or \'). Whitespace at start of string stripped too
//DOESN'T strip backticks. 
char *StripQuotes(char *Str);

//for any of the chars listed in 'QuoteChars' quote them using '\' style quotes.
char *QuoteCharsInStr(char *Buffer, const char *String, const char *QuoteChars);

//undo '\' style quoting and 
char *UnQuoteStr(char *Buffer, const char *Line);

//given a list of strings. match 'Token' against them and return the index of the first one to match
// 'Flags' can be:

//  MATCH_TOKEN_CASE    - perform a case sensitive match, default is case insensitive
//	MATCH_TOKEN_PART    - match just the length of the token in the list. So 'str' would match against 'string'
int MatchTokenFromList(const char *Token,const char **List, int Flags);


//Varidic Versions of CopyStr and CatStr. Always call these through the #defines above, as those add NULL
//in case you forgot to do so
char *InternalMCatStr(char *, const char *, ...);
char *InternalMCopyStr(char *, const char *, ...);

#ifdef __cplusplus
}
#endif




#endif

