/* strutil.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* */
/* AS-Portierung */
/* */
/* haeufig benoetigte String-Funktionen */
/* */
/*****************************************************************************/
#include "stdinc.h"
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include "dynstr.h"
#include "strutil.h"
#undef strlen /* VORSICHT, Rekursion!!! */
char HexStartCharacter; /* characters to use for 10,11,...35 */
char SplitByteCharacter; /* output large numbers per-byte with given split char */
/*--------------------------------------------------------------------------*/
/* eine bestimmte Anzahl Leerzeichen liefern */
const char *Blanks(int cnt)
{
static const char *BlkStr = " ";
static int BlkStrLen = 0;
if (!BlkStrLen)
if (cnt < 0)
cnt = 0;
if (cnt > BlkStrLen)
cnt = BlkStrLen;
return BlkStr + (BlkStrLen - cnt);
}
/*!------------------------------------------------------------------------
* \fn SysString(char *pDest, size_t DestSize, LargeWord i, int System, int Stellen, Boolean ForceLeadZero, char StartCharacter, char SplitCharacter)
* \brief convert number to string in given number system, leading zeros
* \param pDest where to write
* \param DestSize size of dest buffer
* \param i number to convert
* \param Stellen minimum length of output
* \param ForceLeadZero prepend zero if first character is no number
* \param System number system
* \param StartCharacter 'a' or 'A' for hex digits
* \param SplitCharacter split bytes if not NUL
* ------------------------------------------------------------------------ */
char *SysStringCore(char *pDest, char *pDestCurr, LargeWord Num, int System, int Stellen, char StartCharacter)
{
LargeWord Digit;
do
{
if (pDestCurr <= pDest)
break;
Digit = Num % System;
if (Digit < 10)
*(--pDestCurr) = Digit + '0';
else
*(--pDestCurr) = Digit - 10 + StartCharacter;
Num /= System;
Stellen--;
}
while ((Stellen > 0) || Num);
return pDestCurr;
}
int SysString(char *pDest, size_t DestSize, LargeWord Num, int System, int Stellen, Boolean ForceLeadZero, char StartCharacter, char SplitCharacter)
{
int Len = 0;
char *pDestCurr, *pDestNext;
if (DestSize < 1)
return 0;
if (Stellen > (int)DestSize - 1)
Stellen = DestSize - 1;
pDestCurr = pDest + DestSize - 1;
*pDestCurr = '\0';
if (SplitCharacter)
{
LargeWord Part;
int ThisLen;
static int SystemByteLen[37];
if (!SystemByteLen[System])
{
char Dummy[50];
SystemByteLen[System] = SysString(Dummy, sizeof(Dummy), 0xff, System, 0, False, StartCharacter, False);
}
do
{
Part = Num % 256;
Num = Num / 256;
pDestNext = SysStringCore(pDest, pDestCurr, Part, System, Num ? SystemByteLen[System] : Stellen, StartCharacter);
ThisLen = pDestCurr - pDestNext;
Len += ThisLen;
pDestCurr = pDestNext;
Stellen -= ThisLen;
if (Num)
{
if (pDestCurr <= pDest)
break;
*(--pDestCurr) = SplitCharacter;
Len++;
}
}
while ((Stellen > 0) || Num);
}
else
{
pDestNext = SysStringCore(pDest, pDestCurr, Num, System, Stellen, StartCharacter);
Len += pDestCurr - pDestNext;
pDestCurr = pDestNext;
}
if (ForceLeadZero
&& !isdigit(*pDestCurr
) && (pDestCurr
> pDest
))
{
*(--pDestCurr) = '0';
Len++;
}
if (pDestCurr != pDest)
strmov(pDest, pDestCurr);
return Len;
}
/*---------------------------------------------------------------------------*/
/* strdup() is not part of ANSI C89 */
char *as_strdup(const char *s)
{
char *ptr;
if (!s)
return NULL;
#ifdef CKMALLOC
if (!ptr)
{
fprintf(stderr
, "strdup: out of memory?\n");
}
#endif
if (ptr != 0)
return ptr;
}
/*---------------------------------------------------------------------------*/
/* ...so is snprintf... */
typedef enum { eNotSet, eSet, eFinished } tArgState;
typedef struct
{
tArgState ArgState[3];
Boolean InFormat, LeadZero, Signed, LeftAlign, AddPlus, ForceLeadZero, ForceUpper;
int Arg[3], CurrArg, IntSize;
} tFormatContext;
typedef struct
{
char *p_dest;
size_t dest_remlen;
as_dynstr_t *p_dynstr;
} dest_format_context_t;
static void ResetFormatContext(tFormatContext *pContext)
{
int z;
for (z = 0; z < 3; z++)
{
pContext->Arg[z] = 0;
pContext->ArgState[z] = eNotSet;
}
pContext->CurrArg = 0;
pContext->IntSize = 0;
pContext->InFormat =
pContext->LeadZero =
pContext->ForceLeadZero =
pContext->Signed =
pContext->LeftAlign =
pContext->AddPlus =
pContext->ForceUpper = False;
}
/*!------------------------------------------------------------------------
* \fn limit_minus_one(dest_format_context_t *p_dest_ctx, size_t cnt)
* \brief check if space is left to append given # of characters, plus trailing NUL
* \param p_dest_ctx destination context
* \param cnt requested # of characters to append
* \return actual # that can be appended
* ------------------------------------------------------------------------ */
static size_t limit_minus_one(dest_format_context_t *p_dest_ctx, size_t cnt)
{
/* anyway still enough space? */
if (p_dest_ctx->dest_remlen > cnt)
return cnt;
/* not enough space: try to realloc dynamic string dest */
if (p_dest_ctx->p_dynstr)
{
size_t curr_len = p_dest_ctx->p_dest - p_dest_ctx->p_dynstr->p_str;
size_t new_capacity = as_dynstr_roundup_len(curr_len + cnt + 1);
/* if realloc successful, pointer into string buffer must be adapted: */
if (!as_dynstr_realloc(p_dest_ctx->p_dynstr, new_capacity))
{
p_dest_ctx->p_dest = p_dest_ctx->p_dynstr->p_str + curr_len;
p_dest_ctx->dest_remlen = p_dest_ctx->p_dynstr->capacity - curr_len;
}
}
/* pathological case... */
if (!p_dest_ctx->dest_remlen)
return 0;
/* truncation */
else
return (cnt >= p_dest_ctx->dest_remlen) ? p_dest_ctx->dest_remlen - 1 : cnt;
}
/*!------------------------------------------------------------------------
* \fn append_pad(dest_format_context_t *p_dest_ctx, char src, size_t cnt)
* \brief append given character n times
* \param p_dest_ctx destination context
* \param src character to append
* \param cnt # of times to append
* \return actual # of characters appended
* ------------------------------------------------------------------------ */
static size_t append_pad(dest_format_context_t *p_dest_ctx, char src, size_t cnt)
{
cnt = limit_minus_one(p_dest_ctx, cnt);
if (cnt > 0)
{
memset(p_dest_ctx
->p_dest
, src
, cnt
);
p_dest_ctx->p_dest += cnt;
p_dest_ctx->dest_remlen -= cnt;
}
return cnt;
}
#if 0
static int FloatConvert(char *pDest, size_t DestSize, double Src, int Digits, Boolean TruncateTrailingZeros, char FormatType)
{
int DecPt;
int Sign, Result = 0;
char *pBuf, *pEnd, *pRun;
(void)FormatType;
if (DestSize < Digits + 6)
{
*pDest = '\0';
return Result;
}
if (Digits < 0)
Digits = 6;
pBuf = ecvt(Src, Digits + 1, &DecPt, &Sign);
pEnd
= pBuf
+ strlen(pBuf
) - 1;
if (TruncateTrailingZeros)
{
for (; pEnd > pBuf + 1; pEnd--)
if (*pEnd != '0')
break;
}
pRun = pDest;
if (Sign)
*pRun++ = '-';
*pRun++ = *pBuf;
*pRun++ = '.';
memcpy(pRun
, pBuf
+ 1, pEnd
- pBuf
); pRun
+= pEnd
- pBuf
;
*pRun = '\0';
Result = pRun - pDest;
Result += as_snprintf(pRun, DestSize - Result, "e%+02d", DecPt - 1);
return Result;
}
#else
static int FloatConvert(char *pDest, size_t DestSize, double Src, int Digits, Boolean TruncateTrailingZeros, char FormatType)
{
char Format[10];
(void)DestSize;
(void)TruncateTrailingZeros;
Format
[4] = (HexStartCharacter
== 'a') ? FormatType
: toupper(FormatType
);
sprintf(pDest
, Format
, Digits
, Src
);
}
#endif
/*!------------------------------------------------------------------------
* \fn append(dest_format_context_t *p_dest_ctx, const char *p_src, size_t cnt, tFormatContext *pFormatContext)
* \brief append given data, with possible left/right padding
* \param p_dest_ctx destination context
* \param p_src data to append
* \param cnt length of data to append
* \param pFormatContext formatting context
* \return actual # of characters appended
* ------------------------------------------------------------------------ */
static size_t append(dest_format_context_t *p_dest_ctx, const char *p_src, size_t cnt, tFormatContext *pFormatContext)
{
size_t pad_len, result = 0;
pad_len = (pFormatContext->Arg[0] > (int)cnt) ? pFormatContext->Arg[0] - cnt : 0;
if ((pad_len > 0) && !pFormatContext->LeftAlign)
result += append_pad(p_dest_ctx, ' ', pad_len);
cnt = limit_minus_one(p_dest_ctx, cnt);
if (cnt > 0)
{
memcpy(p_dest_ctx
->p_dest
, p_src
, cnt
);
p_dest_ctx->p_dest += cnt;
p_dest_ctx->dest_remlen -= cnt;
}
if ((pad_len > 0) && pFormatContext->LeftAlign)
result += append_pad(p_dest_ctx, ' ', pad_len);
if (pFormatContext->InFormat)
ResetFormatContext(pFormatContext);
return result + cnt;
}
/*!------------------------------------------------------------------------
* \fn vsprcatf_core(dest_format_context_t *p_dest_ctx, const char *pFormat, va_list ap)
* \brief The actual core routine to process the format string
* \param p_dest_ctx context describing destination
* \param pFormat format specifier
* \param ap format arguments
* \return # of characters appended
* ------------------------------------------------------------------------ */
static int vsprcatf_core(dest_format_context_t *p_dest_ctx, const char *pFormat, va_list ap)
{
const char *pFormatStart = pFormat;
int Result = 0;
size_t OrigLen
= strlen(p_dest_ctx
->p_dest
);
tFormatContext FormatContext;
LargeInt IntArg;
if (p_dest_ctx->dest_remlen > OrigLen)
p_dest_ctx->dest_remlen -= OrigLen;
else
p_dest_ctx->dest_remlen = 0;
p_dest_ctx->p_dest += OrigLen;
ResetFormatContext(&FormatContext);
for (; *pFormat; pFormat++)
if (FormatContext.InFormat)
switch (*pFormat)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
if (!FormatContext.CurrArg && !FormatContext.ArgState[FormatContext.CurrArg] && (*pFormat == '0'))
FormatContext.LeadZero = True;
FormatContext.Arg[FormatContext.CurrArg] = (FormatContext.Arg[FormatContext.CurrArg] * 10) + (*pFormat - '0');
FormatContext.ArgState[FormatContext.CurrArg] = eSet;
break;
}
case '-':
if (!FormatContext.CurrArg && !FormatContext.ArgState[FormatContext.CurrArg])
FormatContext.LeftAlign = True;
break;
case '+':
FormatContext.AddPlus = True;
break;
case '~':
FormatContext.ForceLeadZero = True;
break;
case '*':
FormatContext.
Arg[FormatContext.
CurrArg] = va_arg(ap
, int);
FormatContext.ArgState[FormatContext.CurrArg] = eFinished;
break;
case '.':
if (FormatContext.CurrArg < 3)
FormatContext.CurrArg++;
break;
case 'c':
{
Result += append(p_dest_ctx, &ch, 1, &FormatContext);
break;
}
case '%':
Result += append(p_dest_ctx, "%", 1, &FormatContext);
break;
case 'l':
{
FormatContext.IntSize++;
FormatContext.CurrArg = 2;
break;
}
case 'd':
{
if (FormatContext.IntSize >= 3)
IntArg
= va_arg(ap
, LargeInt
);
else
#ifndef NOLONGLONG
if (FormatContext.IntSize >= 2)
IntArg
= va_arg(ap
, long long);
else
#endif
if (FormatContext.IntSize >= 1)
else
FormatContext.Arg[1] = 10;
FormatContext.Signed = True;
goto IntCommon;
}
case 'u':
{
if (FormatContext.IntSize >= 3)
IntArg
= va_arg(ap
, LargeWord
);
else
#ifndef NOLONGLONG
if (FormatContext.IntSize >= 2)
IntArg
= va_arg(ap
, unsigned long long);
else
#endif
if (FormatContext.IntSize >= 1)
IntArg
= va_arg(ap
, unsigned long);
else
IntArg
= va_arg(ap
, unsigned);
goto IntCommon;
}
case 'x':
case 'X':
{
if (FormatContext.IntSize >= 3)
IntArg
= va_arg(ap
, LargeWord
);
else
#ifndef NOLONGLONG
if (FormatContext.IntSize >= 2)
IntArg
= va_arg(ap
, unsigned long long);
else
#endif
if (FormatContext.IntSize)
IntArg
= va_arg(ap
, unsigned long);
else
IntArg
= va_arg(ap
, unsigned);
FormatContext.Arg[1] = 16;
FormatContext.ForceUpper = as_isupper(*pFormat);
goto IntCommon;
}
IntCommon:
{
char Str[100], *pStr = Str;
int Cnt;
int NumPadZeros = 0;
if (FormatContext.Signed)
{
if (IntArg < 0)
{
*pStr++ = '-';
IntArg = 0 - IntArg;
}
else if (FormatContext.AddPlus)
*pStr++ = '+';
}
if (FormatContext.LeadZero)
{
NumPadZeros = FormatContext.Arg[0];
FormatContext.Arg[0] = 0;
}
Cnt = (pStr - Str)
+ SysString(pStr, sizeof(Str) - (pStr - Str), IntArg,
FormatContext.Arg[1] ? FormatContext.Arg[1] : 10,
NumPadZeros, FormatContext.ForceLeadZero,
FormatContext.ForceUpper ? 'A' : HexStartCharacter,
SplitByteCharacter);
if (Cnt > (int)sizeof(Str))
Cnt = sizeof(Str);
Result += append(p_dest_ctx, Str, Cnt, &FormatContext);
break;
}
case 'e':
case 'f':
case 'g':
{
char Str[100];
int Cnt;
Cnt
= FloatConvert
(Str
, sizeof(Str
), va_arg(ap
, double), FormatContext.
Arg[1], False
, *pFormat
);
if (Cnt > (int)sizeof(Str))
Cnt = sizeof(Str);
Result += append(p_dest_ctx, Str, Cnt, &FormatContext);
break;
}
case 's':
{
const char *pStr
= va_arg(ap
, char*);
size_t cnt = FormatContext.Arg[1]
? as_strnlen(pStr, FormatContext.Arg[1])
Result += append(p_dest_ctx, pStr, cnt, &FormatContext);
break;
}
default:
fprintf(stderr
, "invalid format: '%c' in '%s'\n", *pFormat
, pFormatStart
);
}
else if (*pFormat == '%')
FormatContext.InFormat = True;
else
Result += append(p_dest_ctx, pFormat, 1, &FormatContext);
if (p_dest_ctx->dest_remlen > 0)
*(p_dest_ctx->p_dest++) = '\0';
return Result;
}
/*!------------------------------------------------------------------------
* \fn as_vsdprcatf(as_dynstr_t *p_dest, const char *pFormat, va_list ap)
* \brief append to dynamic string by format
* \param p_dest string to be appended to
* \param pFormat format specifier
* \param ap format arguments
* \return # of characters appended
* ------------------------------------------------------------------------ */
int as_vsdprcatf(as_dynstr_t *p_dest, const char *pFormat, va_list ap)
{
dest_format_context_t ctx;
ctx.p_dest = p_dest->p_str;
ctx.dest_remlen = p_dest->capacity;
ctx.p_dynstr = p_dest;
return vsprcatf_core(&ctx, pFormat, ap);
}
/*!------------------------------------------------------------------------
* \fn as_vsdprintf(as_dynstr_t *p_dest, const char *pFormat, va_list ap)
* \brief print to dynamic string by format
* \param p_dest string to be appended to
* \param pFormat format specifier
* \param ap format arguments
* \return # of characters written
* ------------------------------------------------------------------------ */
int as_vsdprintf(as_dynstr_t *p_dest, const char *pFormat, va_list ap)
{
if (p_dest->capacity > 0)
p_dest->p_str[0] = '\0';
return as_vsdprcatf(p_dest, pFormat, ap);
}
/*!------------------------------------------------------------------------
* \fn as_sdprcatf(as_dynstr_t *p_dest, const char *pFormat, ...)
* \brief append to dynamic string by format
* \param p_dest string to be appended to
* \param pFormat format specifier
* \param ... format arguments
* \return # of characters written
* ------------------------------------------------------------------------ */
int as_sdprcatf(as_dynstr_t *p_dest, const char *pFormat, ...)
{
va_list ap;
int ret;
ret = as_vsdprcatf(p_dest, pFormat, ap);
return ret;
}
/*!------------------------------------------------------------------------
* \fn as_sdprintf(as_dynstr_t *p_dest, const char *pFormat, ...)
* \brief print to dynamic string by format
* \param p_dest string to be appended to
* \param pFormat format specifier
* \param ... format arguments
* \return # of characters written
* ------------------------------------------------------------------------ */
int as_sdprintf(as_dynstr_t *p_dest, const char *pFormat, ...)
{
va_list ap;
int ret;
ret = as_vsdprintf(p_dest, pFormat, ap);
return ret;
}
/*!------------------------------------------------------------------------
* \fn as_vsnprcatf(char *pDest, size_t DestSize, const char *pFormat, va_list ap)
* \brief append to string by format
* \param pDest string to be appended to
* \param DestSize capacity of string
* \param pFormat format specifier
* \param ap format arguments
* \return # of characters appended
* ------------------------------------------------------------------------ */
int as_vsnprcatf(char *pDest, size_t DestSize, const char *pFormat, va_list ap)
{
dest_format_context_t ctx;
if (DestSize == sizeof(char*))
{
fprintf(stderr
, "pointer size passed to as_vsnprcatf\n");
}
ctx.p_dest = pDest;
ctx.dest_remlen = DestSize;
ctx.p_dynstr = NULL;
return vsprcatf_core(&ctx, pFormat, ap);
}
/*!------------------------------------------------------------------------
* \fn as_vsnprintf(char *pDest, size_t DestSize, const char *pFormat, va_list ap)
* \brief print to string by format
* \param pDest string to be appended to
* \param DestSize capacity of string
* \param pFormat format specifier
* \param ap format arguments
* \return # of characters written
* ------------------------------------------------------------------------ */
int as_vsnprintf(char *pDest, size_t DestSize, const char *pFormat, va_list ap)
{
if (DestSize > 0)
*pDest = '\0';
return as_vsnprcatf(pDest, DestSize, pFormat, ap);
}
/*!------------------------------------------------------------------------
* \fn as_snprintf(char *pDest, size_t DestSize, const char *pFormat, ...)
* \brief print to string by format
* \param pDest string to be appended to
* \param DestSize capacity of string
* \param pFormat format specifier
* \param ... format arguments
* \return # of characters written
* ------------------------------------------------------------------------ */
int as_snprintf(char *pDest, size_t DestSize, const char *pFormat, ...)
{
va_list ap;
int Result;
if (DestSize > 0)
*pDest = '\0';
Result = as_vsnprcatf(pDest, DestSize, pFormat, ap);
return Result;
}
/*!------------------------------------------------------------------------
* \fn as_snprcatf(char *pDest, size_t DestSize, const char *pFormat, ...)
* \brief append to string by format
* \param pDest string to be appended to
* \param DestSize capacity of string
* \param pFormat format specifier
* \param ... format arguments
* \return # of characters appended
* ------------------------------------------------------------------------ */
int as_snprcatf(char *pDest, size_t DestSize, const char *pFormat, ...)
{
va_list ap;
int Result;
Result = as_vsnprcatf(pDest, DestSize, pFormat, ap);
return Result;
}
int as_strcasecmp(const char *src1, const char *src2)
{
if (!src1)
src1 = "";
if (!src2)
src2 = "";
{
if ((!*src1) && (!*src2))
return 0;
src1++;
src2++;
}
}
int as_strncasecmp(const char *src1, const char *src2, size_t len)
{
if (!src1)
src1 = "";
if (!src2)
src2 = "";
{
if (--len == 0)
return 0;
if ((!*src1) && (!*src2))
return 0;
src1++;
src2++;
}
}
#ifdef NEEDS_STRSTR
char *strstr(const char *haystack
, const char *needle
)
{
int z;
char *p;
for (z = 0; z <= lh - ln; z++)
if (strncmp(p
= haystack
+ z
, needle
, ln
) == 0)
return p;
return NULL;
}
#endif
/*!------------------------------------------------------------------------
* \fn strrmultchr(const char *haystack, const char *needles)
* \brief find the last occurence of either character in string
* \param haystack string to search in
* \param needles characters to search for
* \return last occurence or NULL
* ------------------------------------------------------------------------ */
char *strrmultchr(const char *haystack, const char *needles)
{
const char *pPos;
for (pPos
= haystack
+ strlen(haystack
) - 1; pPos
>= haystack
; pPos
--)
return (char*)pPos;
return NULL;
}
/*---------------------------------------------------------------------------*/
/* das originale strncpy plaettet alle ueberstehenden Zeichen mit Nullen */
size_t strmaxcpy(char *dest, const char *src, size_t Max)
{
/* leave room for terminating NUL */
if (!Max)
return 0;
if (cnt + 1 > Max)
cnt = Max - 1;
dest[cnt] = '\0';
return cnt;
}
/*---------------------------------------------------------------------------*/
/* einfuegen, mit Begrenzung */
size_t strmaxcat(char *Dest, const char *Src, size_t MaxLen)
{
if (TLen > (int)MaxLen - 1 - (int)DLen)
TLen = MaxLen - DLen - 1;
if (TLen > 0)
{
memcpy(Dest
+ DLen
, Src
, TLen
);
Dest[DLen + TLen] = '\0';
return DLen + TLen;
}
else
return DLen;
}
void strprep(char *Dest, const char *Src)
{
}
/*!------------------------------------------------------------------------
* \fn strmaxprep(char *p_dest, const char *p_src, size_t max_len)
* \brief prepend as much as possible from src to dest
* \param p_dest string to be prepended
* \param p_src string to prepend
* \param max_len capacity of p_dest
* ------------------------------------------------------------------------ */
void strmaxprep(char *p_dest, const char *p_src, size_t max_len)
{
size_t src_len
= strlen(p_src
),
assert(dest_len
+ 1 <= max_len
);
if (src_len > max_len - dest_len - 1)
src_len = max_len - dest_len - 1;
memmove(p_dest
+ src_len
, p_dest
, dest_len
+ 1);
}
/*!------------------------------------------------------------------------
* \fn strmaxprep2(char *p_dest, const char *p_src, size_t max_len)
* \brief prepend as much as possible from src to dest, and possibly truncate dest by that
* \param p_dest string to be prepended
* \param p_src string to prepend
* \param max_len capacity of p_dest
* ------------------------------------------------------------------------ */
void strmaxprep2(char *p_dest, const char *p_src, size_t max_len)
{
size_t src_len
= strlen(p_src
),
if (src_len >= max_len)
src_len = max_len - 1;
max_len -= src_len;
if (dest_len >= max_len)
dest_len = max_len - 1;
memmove(p_dest
+ src_len
, p_dest
, dest_len
+ 1);
}
void strins(char *Dest, const char *Src, int Pos)
{
}
void strmaxins(char *Dest, const char *Src, int Pos, size_t MaxLen)
{
size_t RLen;
if (RLen
> MaxLen
- strlen(Dest
))
}
int strlencmp(const char *pStr1, unsigned Str1Len,
const char *pStr2, unsigned Str2Len)
{
const char *p1, *p2, *p1End, *p2End;
int Diff;
for (p1 = pStr1, p1End = p1 + Str1Len,
p2 = pStr2, p2End = p2 + Str2Len;
p1 < p1End && p2 < p2End; p1++, p2++)
{
Diff = ((int)*p1) - ((int)*p2);
if (Diff)
return Diff;
}
return ((int)Str1Len) - ((int)Str2Len);
}
unsigned fstrlenprint(FILE *pFile, const char *pStr, unsigned StrLen)
{
unsigned Result = 0;
const char *pRun, *pEnd;
for (pRun = pStr, pEnd = pStr + StrLen; pRun < pEnd; pRun++)
if ((*pRun
== '\\') || (*pRun
== '"') || (*pRun
== ' ') || (!isprint(*pRun
)))
{
Result += 4;
}
else
{
Result++;
}
return Result;
}
size_t as_strnlen(const char *pStr, size_t MaxLen)
{
size_t Res = 0;
for (; (MaxLen > 0); MaxLen--, pStr++, Res++)
if (!*pStr)
break;
return Res;
}
/*!------------------------------------------------------------------------
* \fn strreplace(char *pHaystack, const char *pFrom, const char *pTo, size_t ToMaxLen, size_t HaystackSize)
* \brief replaces all occurences of From to To in Haystack
* \param pHaystack string to search in
* \param pFrom what to find
* \param pFrom what to find
* \param pTo what to replace it with
* \param ToMaxLen if not -1, max. length of pTo (not NUL-terminated)
* \param HaystackSize buffer capacity
* \return # of occurences
* ------------------------------------------------------------------------ */
int strreplace(char *pHaystack, const char *pFrom, const char *pTo, size_t ToMaxLen, size_t HaystackSize)
{
int HaystackLen = -1, FromLen = -1, ToLen = -1, Count = 0;
int HeadLen, TailLen;
char *pSearch, *pPos;
pSearch = pHaystack;
while (True)
{
/* find an occurence */
pPos
= strstr(pSearch
, pFrom
);
if (!pPos)
return Count;
/* compute some stuff upon first occurence when needed */
if (FromLen < 0)
{
HaystackLen
= strlen(pHaystack
);
}
ToLen
= (ToMaxLen
> 0) ? as_strnlen
(pTo
, ToMaxLen
) : strlen(pTo
);
/* See how much of the remainder behind 'To' still fits into buffer after replacement,
and move accordingly: */
HeadLen = pPos - pHaystack;
TailLen = HaystackLen - HeadLen - FromLen;
if (HeadLen + ToLen + TailLen >= (int)HaystackSize)
{
TailLen = HaystackSize - 1 - HeadLen - ToLen;
if (TailLen < 0)
TailLen = 0;
}
if (TailLen > 0)
memmove(pPos
+ ToLen
, pPos
+ FromLen
, TailLen
);
/* See how much of 'To' still fits into buffer, and set accordingly: */
if (HeadLen + ToLen >= (int)HaystackSize)
{
ToLen = HaystackSize - 1 - ToLen;
if (ToLen < 0)
ToLen = 0;
}
if (ToLen > 0)
/* Update length & terminate new string */
HaystackLen = HeadLen + ToLen + TailLen;
pHaystack[HaystackLen] = '\0';
/* continue searching behind replacement: */
pSearch = &pHaystack[HeadLen + ToLen];
Count++;
}
}
/*---------------------------------------------------------------------------*/
/* Bis Zeilenende lesen */
void ReadLn(FILE *Datei, char *Zeile)
{
char *ptr;
int l;
*Zeile = '\0';
ptr
= fgets(Zeile
, 256, Datei
);
if ((!ptr
) && (ferror(Datei
) != 0))
*Zeile = '\0';
if ((l > 0) && (Zeile[l - 1] == '\n'))
Zeile[--l] = '\0';
if ((l > 0) && (Zeile[l - 1] == '\r'))
Zeile[--l] = '\0';
if ((l > 0) && (Zeile[l - 1] == 26))
Zeile[--l] = '\0';
}
#if 0
static void dump(const char *pLine, unsigned Cnt)
{
unsigned z;
for (z = 0; z < Cnt; z++)
{
fprintf(stderr
, " %02x", pLine
[z
]);
if ((z & 15) == 15)
}
}
#endif
/*!------------------------------------------------------------------------
* \fn ReadLnCont(FILE *Datei, as_dynstr_t *p_line)
* \brief read line, regarding \ continuation characters
* \param Datei where to read from
* \param pLine dest buffer
* \return # of lines read
* ------------------------------------------------------------------------ */
size_t ReadLnCont(FILE *Datei, as_dynstr_t *p_line)
{
char *ptr, *pDest;
size_t l, Count, LineCount;
Boolean Terminated;
/* read from input until no continuation is present */
pDest = p_line->p_str;
LineCount = Count = 0;
while (1)
{
/* get a line from file, possibly reallocating until everything up to \n fits */
while (1)
{
if (p_line->capacity - Count < 128)
as_dynstr_realloc(p_line, p_line->capacity + 128);
pDest = p_line->p_str + Count;
*pDest = '\0';
ptr
= fgets(pDest
, p_line
->capacity
- Count
, Datei
);
if (!ptr)
{
*pDest = '\0';
break;
}
/* If we have a trailing \n, we read up to end of line: */
Terminated = ((l > 0) && (pDest[l - 1] == '\n'));
/* srtrip possible CR preceding LF: */
if (Terminated)
{
/* strip LF, and possible CR, and bail out: */
pDest[--l] = '\0';
if ((l > 0) && (pDest[l - 1] == '\r'))
pDest[--l] = '\0';
}
Count += l;
pDest += l;
if (Terminated)
break;
}
LineCount++;
if ((Count > 0) && (p_line->p_str[Count - 1] == 26))
p_line->p_str[--Count] = '\0';
/* optional line continuation */
if ((Count > 0) && (p_line->p_str[Count - 1] == '\\'))
p_line->p_str[--Count] = '\0';
else
break;
}
return LineCount;
}
/*!------------------------------------------------------------------------
* \fn DigitVal(char ch, int Base)
* \brief get value of hex digit
* \param ch digit
* \param Base Number System
* \return 0..Base-1 or -1 if no valid digit
* ------------------------------------------------------------------------ */
int DigitVal(char ch, int Base)
{
int Result;
/* Ziffern 0..9 ergeben selbiges */
if ((ch >= '0') && (ch <= '9'))
Result = ch - '0';
/* Grossbuchstaben fuer Hexziffern */
else if ((ch >= 'A') && (ch <= 'Z'))
Result = ch - 'A' + 10;
/* Kleinbuchstaben nicht vergessen...! */
else if ((ch >= 'a') && (ch <= 'z'))
Result = ch - 'a' + 10;
/* alles andere ist Schrott */
else
Result = -1;
return (Result >= Base) ? -1 : Result;
}
/*--------------------------------------------------------------------*/
/* Zahlenkonstante umsetzen: $ hex, % binaer, @ oktal */
/* inp: Eingabezeichenkette */
/* erg: Zeiger auf Ergebnis-Longint */
/* liefert TRUE, falls fehlerfrei, sonst FALSE */
LargeInt ConstLongInt(const char *inp, Boolean *pErr, LongInt Base)
{
static const char Prefixes[4] = { '$', '@', '%', '\0' }; /* die moeglichen Zahlensysteme */
static const char Postfixes[4] = { 'H', 'O', '\0', '\0' };
static const LongInt Bases[3] = { 16, 8, 2 }; /* die dazugehoerigen Basen */
LargeInt erg, val;
int z, vorz = 1; /* Vermischtes */
/* eventuelles Vorzeichen abspalten */
if (*inp == '-')
{
vorz = -1;
inp++;
InpLen--;
}
/* Sonderbehandlung 0x --> $ */
if ((InpLen >= 2)
&& (*inp == '0')
&& (as_toupper(inp[1]) == 'X'))
{
inp += 2;
InpLen -= 2;
Base = 16;
}
/* Jetzt das Zahlensystem feststellen. Vorgabe ist dezimal, was
sich aber durch den Initialwert von Base jederzeit aendern
laesst. Der break-Befehl verhindert, dass mehrere Basenzeichen
hintereinander eingegeben werden koennen */
else if (InpLen > 0)
{
for (z = 0; z < 3; z++)
if (*inp == Prefixes[z])
{
Base = Bases[z];
inp++;
InpLen--;
break;
}
else if (as_toupper(inp[InpLen - 1]) == Postfixes[z])
{
Base = Bases[z];
InpLen--;
break;
}
}
/* jetzt die Zahlenzeichen der Reihe nach durchverwursten */
erg = 0;
*pErr = False;
for(; InpLen > 0; inp++, InpLen--)
{
val = DigitVal(*inp, 16);
if (val < -0)
break;
/* entsprechend der Basis zulaessige Ziffer ? */
if (val >= Base)
break;
/* Zahl linksschieben, zusammenfassen, naechster bitte */
erg = erg * Base + val;
}
/* bis zum Ende durchgelaufen ? */
if (!InpLen)
{
/* Vorzeichen beruecksichtigen */
erg *= vorz;
*pErr = True;
}
return erg;
}
/*--------------------------------------------------------------------------*/
/* alle Leerzeichen aus einem String loeschen */
void KillBlanks(char *s)
{
char *z, *dest;
Boolean InSgl = False, InDbl = False, ThisEscaped = False, NextEscaped = False;
dest = s;
for (z = s; *z != '\0'; z++, ThisEscaped = NextEscaped)
{
NextEscaped = False;
switch (*z)
{
case '\'':
if (!InDbl && !ThisEscaped)
InSgl = !InSgl;
break;
case '"':
if (!InSgl && !ThisEscaped)
InDbl = !InDbl;
break;
case '\\':
if ((InSgl || InDbl) && !ThisEscaped)
NextEscaped = True;
break;
}
if (!as_isspace(*z) || InSgl || InDbl)
*dest++ = *z;
}
*dest = '\0';
}
int CopyNoBlanks(char *pDest, const char *pSrc, size_t MaxLen)
{
const char *pSrcRun;
char *pDestRun = pDest;
size_t Cnt = 0;
Byte Flags = 0;
char ch;
Boolean ThisEscaped, PrevEscaped;
/* leave space for NUL */
MaxLen--;
PrevEscaped = False;
for (pSrcRun = pSrc; *pSrcRun; pSrcRun++)
{
ch = *pSrcRun;
ThisEscaped = False;
switch (ch)
{
case '\'':
if (!(Flags & 2) && !PrevEscaped)
Flags ^= 1;
break;
case '"':
if (!(Flags & 1) && !PrevEscaped)
Flags ^= 2;
break;
case '\\':
if (!PrevEscaped)
ThisEscaped = True;
break;
}
if (!as_isspace(ch) || Flags)
*(pDestRun++) = ch;
if (++Cnt >= MaxLen)
break;
PrevEscaped = ThisEscaped;
}
*pDestRun = '\0';
return Cnt;
}
/*--------------------------------------------------------------------------*/
/* fuehrende Leerzeichen loeschen */
int KillPrefBlanks(char *s)
{
char *z = s;
while ((*z != '\0') && as_isspace(*z))
z++;
if (z != s)
strmov(s, z);
return z - s;
}
/*--------------------------------------------------------------------------*/
/* anhaengende Leerzeichen loeschen */
int KillPostBlanks(char *s)
{
int count = 0;
while ((z >= s) && as_isspace(*z))
{
*(z--) = '\0';
count++;
}
return count;
}
/*--------------------------------------------------------------------------*/
int strqcmp(const char *s1, const char *s2)
{
int erg = (*s1) - (*s2);
return (erg
!= 0) ? erg
: strcmp(s1
, s2
);
}
/*--------------------------------------------------------------------------*/
/* we need a strcpy() with a defined behaviour in case of overlapping source
and destination: */
char *strmov(char *pDest, const char *pSrc)
{
return pDest;
}
#ifdef __GNUC__
#ifdef strcpy
# undef strcpy
#endif
char *strcpy(char *pDest
, const char *pSrc
)
{
int Overlap = 0;
if (pSrc < pDest)
{
if (pSrc + l > pDest)
Overlap = 1;
}
else if (pSrc > pDest)
{
if (pDest + l > pSrc)
Overlap = 1;
}
else if (l > 0)
{
Overlap = 1;
}
if (Overlap)
{
fprintf(stderr
, "overlapping strcpy() called from address %p, resolve this address with addr2line and report to author\n",
__builtin_return_address(0));
}
return strmov(pDest, pSrc);
}
#endif
/*!------------------------------------------------------------------------
* \fn strmemcpy(char *pDest, size_t DestSize, const char *pSrc, size_t SrcLen)
* \brief copy string with length limitation
* \param pDest where to write
* \param DestSize destination capacity
* \param pSrc copy source
* \param SrcLen # of characters to copy at most
* \return actual, possibly limited length
* ------------------------------------------------------------------------ */
int strmemcpy(char *pDest, size_t DestSize, const char *pSrc, size_t SrcLen)
{
if (DestSize < SrcLen + 1)
SrcLen = DestSize - 1;
pDest[SrcLen] = '\0';
return SrcLen;
}
/*--------------------------------------------------------------------------*/
char *ParenthPos(char *pHaystack, char Needle)
{
char *pRun;
int Level = 0;
for (pRun = pHaystack; *pRun; pRun++)
{
switch (*pRun)
{
case '(':
Level++;
break;
case ')':
if (Level < 1)
return NULL;
Level--;
break;
default:
if (*pRun == Needle && !Level)
return pRun;
}
}
return NULL;
}
/*!------------------------------------------------------------------------
* \fn TabCompressed(char in)
* \brief replace TABs with spaces for error display
* \param in character to compress
* \return compressed result
* ------------------------------------------------------------------------ */
char TabCompressed(char in)
{
return (in == '\t') ? ' ' : (as_isprint(in) ? in : '*');
}
/*--------------------------------------------------------------------------*/
void strutil_init(void)
{
HexStartCharacter = 'A';
}