Subversion Repositories pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

  1. /* strutil.c */
  2. /*****************************************************************************/
  3. /* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
  4. /*                                                                           */
  5. /* AS-Portierung                                                             */
  6. /*                                                                           */
  7. /* haeufig benoetigte String-Funktionen                                      */
  8. /*                                                                           */
  9. /*****************************************************************************/
  10.  
  11. #include "stdinc.h"
  12. #include <ctype.h>
  13. #include <string.h>
  14. #include <stdarg.h>
  15. #include <assert.h>
  16.  
  17. #include "dynstr.h"
  18. #include "strutil.h"
  19. #undef strlen   /* VORSICHT, Rekursion!!! */
  20.  
  21. char HexStartCharacter;     /* characters to use for 10,11,...35 */
  22. char SplitByteCharacter;    /* output large numbers per-byte with given split char */
  23.  
  24. /*--------------------------------------------------------------------------*/
  25. /* eine bestimmte Anzahl Leerzeichen liefern */
  26.  
  27. const char *Blanks(int cnt)
  28. {
  29.   static const char *BlkStr = "                                                                                                           ";
  30.   static int BlkStrLen = 0;
  31.  
  32.   if (!BlkStrLen)
  33.     BlkStrLen = strlen(BlkStr);
  34.  
  35.   if (cnt < 0)
  36.     cnt = 0;
  37.   if (cnt > BlkStrLen)
  38.     cnt = BlkStrLen;
  39.  
  40.   return BlkStr + (BlkStrLen - cnt);
  41. }
  42.  
  43. /*!------------------------------------------------------------------------
  44.  * \fn     SysString(char *pDest, size_t DestSize, LargeWord i, int System, int Stellen, Boolean ForceLeadZero, char StartCharacter, char SplitCharacter)
  45.  * \brief  convert number to string in given number system, leading zeros
  46.  * \param  pDest where to write
  47.  * \param  DestSize size of dest buffer
  48.  * \param  i number to convert
  49.  * \param  Stellen minimum length of output
  50.  * \param  ForceLeadZero prepend zero if first character is no number
  51.  * \param  System number system
  52.  * \param  StartCharacter 'a' or 'A' for hex digits
  53.  * \param  SplitCharacter split bytes if not NUL
  54.  * ------------------------------------------------------------------------ */
  55.  
  56. char *SysStringCore(char *pDest, char *pDestCurr, LargeWord Num, int System, int Stellen, char StartCharacter)
  57. {
  58.   LargeWord Digit;
  59.  
  60.   do
  61.   {
  62.     if (pDestCurr <= pDest)
  63.       break;
  64.     Digit = Num % System;
  65.     if (Digit < 10)
  66.       *(--pDestCurr) = Digit + '0';
  67.     else
  68.       *(--pDestCurr) = Digit - 10 + StartCharacter;
  69.     Num /= System;
  70.     Stellen--;
  71.   }
  72.   while ((Stellen > 0) || Num);
  73.   return pDestCurr;
  74. }
  75.  
  76. int SysString(char *pDest, size_t DestSize, LargeWord Num, int System, int Stellen, Boolean ForceLeadZero, char StartCharacter, char SplitCharacter)
  77. {
  78.   int Len = 0;
  79.   char *pDestCurr, *pDestNext;
  80.  
  81.   if (DestSize < 1)
  82.     return 0;
  83.  
  84.   if (Stellen > (int)DestSize - 1)
  85.     Stellen = DestSize - 1;
  86.  
  87.   pDestCurr = pDest + DestSize - 1;
  88.   *pDestCurr = '\0';
  89.   if (SplitCharacter)
  90.   {
  91.     LargeWord Part;
  92.     int ThisLen;
  93.     static int SystemByteLen[37];
  94.  
  95.     if (!SystemByteLen[System])
  96.     {
  97.       char Dummy[50];
  98.  
  99.       SystemByteLen[System] = SysString(Dummy, sizeof(Dummy), 0xff, System, 0, False, StartCharacter, False);
  100.     }
  101.  
  102.     do
  103.     {
  104.       Part = Num % 256;
  105.       Num = Num / 256;
  106.       pDestNext = SysStringCore(pDest, pDestCurr, Part, System, Num ? SystemByteLen[System] : Stellen, StartCharacter);
  107.       ThisLen = pDestCurr - pDestNext;
  108.       Len += ThisLen;
  109.       pDestCurr = pDestNext;
  110.       Stellen -= ThisLen;
  111.       if (Num)
  112.       {
  113.         if (pDestCurr <= pDest)
  114.           break;
  115.         *(--pDestCurr) = SplitCharacter;
  116.         Len++;
  117.       }
  118.     }
  119.     while ((Stellen > 0) || Num);
  120.   }
  121.   else
  122.   {
  123.     pDestNext = SysStringCore(pDest, pDestCurr, Num, System, Stellen, StartCharacter);
  124.     Len += pDestCurr - pDestNext;
  125.     pDestCurr = pDestNext;
  126.   }
  127.  
  128.   if (ForceLeadZero && !isdigit(*pDestCurr) && (pDestCurr > pDest))
  129.   {
  130.     *(--pDestCurr) = '0';
  131.     Len++;
  132.   }
  133.  
  134.   if (pDestCurr != pDest)
  135.     strmov(pDest, pDestCurr);
  136.   return Len;
  137. }
  138.  
  139. /*---------------------------------------------------------------------------*/
  140. /* strdup() is not part of ANSI C89 */
  141.  
  142. char *as_strdup(const char *s)
  143. {
  144.   char *ptr;
  145.  
  146.   if (!s)
  147.     return NULL;
  148.   ptr = (char *) malloc(strlen(s) + 1);
  149. #ifdef CKMALLOC
  150.   if (!ptr)
  151.   {
  152.     fprintf(stderr, "strdup: out of memory?\n");
  153.     exit(255);
  154.   }
  155. #endif
  156.   if (ptr != 0)
  157.     strcpy(ptr, s);
  158.   return ptr;
  159. }
  160. /*---------------------------------------------------------------------------*/
  161. /* ...so is snprintf... */
  162.  
  163. typedef enum { eNotSet, eSet, eFinished } tArgState;
  164.  
  165. typedef struct
  166. {
  167.   tArgState ArgState[3];
  168.   Boolean InFormat, LeadZero, Signed, LeftAlign, AddPlus, ForceLeadZero, ForceUpper;
  169.   int Arg[3], CurrArg, IntSize;
  170. } tFormatContext;
  171.  
  172. typedef struct
  173. {
  174.   char *p_dest;
  175.   size_t dest_remlen;
  176.   as_dynstr_t *p_dynstr;
  177. } dest_format_context_t;
  178.  
  179. static void ResetFormatContext(tFormatContext *pContext)
  180. {
  181.   int z;
  182.  
  183.   for (z = 0; z < 3; z++)
  184.   {
  185.     pContext->Arg[z] = 0;
  186.     pContext->ArgState[z] = eNotSet;
  187.   }
  188.   pContext->CurrArg = 0;
  189.   pContext->IntSize = 0;
  190.   pContext->InFormat =
  191.   pContext->LeadZero =
  192.   pContext->ForceLeadZero =
  193.   pContext->Signed =
  194.   pContext->LeftAlign =
  195.   pContext->AddPlus =
  196.   pContext->ForceUpper = False;
  197. }
  198.  
  199. /*!------------------------------------------------------------------------
  200.  * \fn     limit_minus_one(dest_format_context_t *p_dest_ctx, size_t cnt)
  201.  * \brief  check if space is left to append given # of characters, plus trailing NUL
  202.  * \param  p_dest_ctx destination context
  203.  * \param  cnt requested # of characters to append
  204.  * \return actual # that can be appended
  205.  * ------------------------------------------------------------------------ */
  206.  
  207. static size_t limit_minus_one(dest_format_context_t *p_dest_ctx, size_t cnt)
  208. {
  209.   /* anyway still enough space? */
  210.  
  211.   if (p_dest_ctx->dest_remlen > cnt)
  212.     return cnt;
  213.  
  214.   /* not enough space: try to realloc dynamic string dest */
  215.  
  216.   if (p_dest_ctx->p_dynstr)
  217.   {
  218.     size_t curr_len = p_dest_ctx->p_dest - p_dest_ctx->p_dynstr->p_str;
  219.     size_t new_capacity = as_dynstr_roundup_len(curr_len + cnt + 1);
  220.  
  221.     /* if realloc successful, pointer into string buffer must be adapted: */
  222.  
  223.     if (!as_dynstr_realloc(p_dest_ctx->p_dynstr, new_capacity))
  224.     {
  225.       p_dest_ctx->p_dest = p_dest_ctx->p_dynstr->p_str + curr_len;
  226.       p_dest_ctx->dest_remlen = p_dest_ctx->p_dynstr->capacity - curr_len;
  227.     }
  228.   }
  229.  
  230.   /* pathological case... */
  231.  
  232.   if (!p_dest_ctx->dest_remlen)
  233.     return 0;
  234.  
  235.   /* truncation */
  236.  
  237.   else
  238.     return (cnt >= p_dest_ctx->dest_remlen) ? p_dest_ctx->dest_remlen - 1 : cnt;
  239. }
  240.  
  241. /*!------------------------------------------------------------------------
  242.  * \fn     append_pad(dest_format_context_t *p_dest_ctx, char src, size_t cnt)
  243.  * \brief  append given character n times
  244.  * \param  p_dest_ctx destination context
  245.  * \param  src character to append
  246.  * \param  cnt # of times to append
  247.  * \return actual # of characters appended
  248.  * ------------------------------------------------------------------------ */
  249.  
  250. static size_t append_pad(dest_format_context_t *p_dest_ctx, char src, size_t cnt)
  251. {
  252.   cnt = limit_minus_one(p_dest_ctx, cnt);
  253.  
  254.   if (cnt > 0)
  255.   {
  256.     memset(p_dest_ctx->p_dest, src, cnt);
  257.     p_dest_ctx->p_dest += cnt;
  258.     p_dest_ctx->dest_remlen -= cnt;
  259.   }
  260.   return cnt;
  261. }
  262.  
  263. #if 0
  264. static int FloatConvert(char *pDest, size_t DestSize, double Src, int Digits, Boolean TruncateTrailingZeros, char FormatType)
  265. {
  266.   int DecPt;
  267.   int Sign, Result = 0;
  268.   char *pBuf, *pEnd, *pRun;
  269.  
  270.   (void)FormatType;
  271.  
  272.   if (DestSize < Digits + 6)
  273.   {
  274.     *pDest = '\0';
  275.     return Result;
  276.   }
  277.  
  278.   if (Digits < 0)
  279.     Digits = 6;
  280.  
  281.   pBuf = ecvt(Src, Digits + 1, &DecPt, &Sign);
  282.   puts(pBuf);
  283.   pEnd = pBuf + strlen(pBuf) - 1;
  284.   if (TruncateTrailingZeros)
  285.   {
  286.     for (; pEnd > pBuf + 1; pEnd--)
  287.       if (*pEnd != '0')
  288.         break;
  289.   }
  290.  
  291.   pRun = pDest;
  292.   if (Sign)
  293.     *pRun++ = '-';
  294.   *pRun++ = *pBuf;
  295.   *pRun++ = '.';
  296.   memcpy(pRun, pBuf + 1, pEnd - pBuf); pRun += pEnd - pBuf;
  297.   *pRun = '\0';
  298.   Result = pRun - pDest;
  299.   Result += as_snprintf(pRun, DestSize - Result, "e%+02d", DecPt - 1);
  300.   return Result;
  301. }
  302. #else
  303. static int FloatConvert(char *pDest, size_t DestSize, double Src, int Digits, Boolean TruncateTrailingZeros, char FormatType)
  304. {
  305.   char Format[10];
  306.  
  307.   (void)DestSize;
  308.   (void)TruncateTrailingZeros;
  309.   strcpy(Format, "%0.*e");
  310.   Format[4] = (HexStartCharacter == 'a') ? FormatType : toupper(FormatType);
  311.   sprintf(pDest, Format, Digits, Src);
  312.   return strlen(pDest);
  313. }
  314. #endif
  315.  
  316. /*!------------------------------------------------------------------------
  317.  * \fn     append(dest_format_context_t *p_dest_ctx, const char *p_src, size_t cnt, tFormatContext *pFormatContext)
  318.  * \brief  append given data, with possible left/right padding
  319.  * \param  p_dest_ctx destination context
  320.  * \param  p_src data to append
  321.  * \param  cnt length of data to append
  322.  * \param  pFormatContext formatting context
  323.  * \return actual # of characters appended
  324.  * ------------------------------------------------------------------------ */
  325.  
  326. static size_t append(dest_format_context_t *p_dest_ctx, const char *p_src, size_t cnt, tFormatContext *pFormatContext)
  327. {
  328.   size_t pad_len, result = 0;
  329.  
  330.   pad_len = (pFormatContext->Arg[0] > (int)cnt) ? pFormatContext->Arg[0] - cnt : 0;
  331.  
  332.   if ((pad_len > 0) && !pFormatContext->LeftAlign)
  333.     result += append_pad(p_dest_ctx, ' ', pad_len);
  334.  
  335.   cnt = limit_minus_one(p_dest_ctx, cnt);
  336.   if (cnt > 0)
  337.   {
  338.     memcpy(p_dest_ctx->p_dest, p_src, cnt);
  339.     p_dest_ctx->p_dest += cnt;
  340.     p_dest_ctx->dest_remlen -= cnt;
  341.   }
  342.  
  343.   if ((pad_len > 0) && pFormatContext->LeftAlign)
  344.     result += append_pad(p_dest_ctx, ' ', pad_len);
  345.  
  346.   if (pFormatContext->InFormat)
  347.     ResetFormatContext(pFormatContext);
  348.  
  349.   return result + cnt;
  350. }
  351.  
  352. /*!------------------------------------------------------------------------
  353.  * \fn     vsprcatf_core(dest_format_context_t *p_dest_ctx, const char *pFormat, va_list ap)
  354.  * \brief  The actual core routine to process the format string
  355.  * \param  p_dest_ctx context describing destination
  356.  * \param  pFormat format specifier
  357.  * \param  ap format arguments
  358.  * \return # of characters appended
  359.  * ------------------------------------------------------------------------ */
  360.  
  361. static int vsprcatf_core(dest_format_context_t *p_dest_ctx, const char *pFormat, va_list ap)
  362. {
  363.   const char *pFormatStart = pFormat;
  364.   int Result = 0;
  365.   size_t OrigLen = strlen(p_dest_ctx->p_dest);
  366.   tFormatContext FormatContext;
  367.   LargeInt IntArg;
  368.  
  369.   if (p_dest_ctx->dest_remlen > OrigLen)
  370.     p_dest_ctx->dest_remlen -= OrigLen;
  371.   else
  372.     p_dest_ctx->dest_remlen = 0;
  373.   p_dest_ctx->p_dest += OrigLen;
  374.  
  375.   ResetFormatContext(&FormatContext);
  376.   for (; *pFormat; pFormat++)
  377.     if (FormatContext.InFormat)
  378.       switch (*pFormat)
  379.       {
  380.         case '0': case '1': case '2': case '3': case '4':
  381.         case '5': case '6': case '7': case '8': case '9':
  382.         {
  383.           if (!FormatContext.CurrArg && !FormatContext.ArgState[FormatContext.CurrArg] && (*pFormat == '0'))
  384.             FormatContext.LeadZero = True;
  385.           FormatContext.Arg[FormatContext.CurrArg] = (FormatContext.Arg[FormatContext.CurrArg] * 10) + (*pFormat - '0');
  386.           FormatContext.ArgState[FormatContext.CurrArg] = eSet;
  387.           break;
  388.         }
  389.         case '-':
  390.           if (!FormatContext.CurrArg && !FormatContext.ArgState[FormatContext.CurrArg])
  391.             FormatContext.LeftAlign = True;
  392.           break;
  393.         case '+':
  394.           FormatContext.AddPlus = True;
  395.           break;
  396.         case '~':
  397.           FormatContext.ForceLeadZero = True;
  398.           break;
  399.         case '*':
  400.           FormatContext.Arg[FormatContext.CurrArg] = va_arg(ap, int);
  401.           FormatContext.ArgState[FormatContext.CurrArg] = eFinished;
  402.           break;
  403.         case '.':
  404.           if (FormatContext.CurrArg < 3)
  405.             FormatContext.CurrArg++;
  406.           break;
  407.         case 'c':
  408.         {
  409.           char ch = va_arg(ap, int);
  410.  
  411.           Result += append(p_dest_ctx, &ch, 1, &FormatContext);
  412.           break;
  413.         }
  414.         case '%':
  415.           Result += append(p_dest_ctx, "%", 1, &FormatContext);
  416.           break;
  417.         case 'l':
  418.         {
  419.           FormatContext.IntSize++;
  420.           FormatContext.CurrArg = 2;
  421.           break;
  422.         }
  423.         case 'd':
  424.         {
  425.           if (FormatContext.IntSize >= 3)
  426.             IntArg = va_arg(ap, LargeInt);
  427.           else
  428. #ifndef NOLONGLONG
  429.           if (FormatContext.IntSize >= 2)
  430.             IntArg = va_arg(ap, long long);
  431.           else
  432. #endif
  433.           if (FormatContext.IntSize >= 1)
  434.             IntArg = va_arg(ap, long);
  435.           else
  436.             IntArg = va_arg(ap, int);
  437.           FormatContext.Arg[1] = 10;
  438.           FormatContext.Signed = True;
  439.           goto IntCommon;
  440.         }
  441.         case 'u':
  442.         {
  443.           if (FormatContext.IntSize >= 3)
  444.             IntArg = va_arg(ap, LargeWord);
  445.           else
  446. #ifndef NOLONGLONG
  447.           if (FormatContext.IntSize >= 2)
  448.             IntArg = va_arg(ap, unsigned long long);
  449.           else
  450. #endif
  451.           if (FormatContext.IntSize >= 1)
  452.             IntArg = va_arg(ap, unsigned long);
  453.           else
  454.             IntArg = va_arg(ap, unsigned);
  455.           goto IntCommon;
  456.         }
  457.         case 'x':
  458.         case 'X':
  459.         {
  460.           if (FormatContext.IntSize >= 3)
  461.             IntArg = va_arg(ap, LargeWord);
  462.           else
  463. #ifndef NOLONGLONG
  464.           if (FormatContext.IntSize >= 2)
  465.             IntArg = va_arg(ap, unsigned long long);
  466.           else
  467. #endif
  468.           if (FormatContext.IntSize)
  469.             IntArg = va_arg(ap, unsigned long);
  470.           else
  471.             IntArg = va_arg(ap, unsigned);
  472.           FormatContext.Arg[1] = 16;
  473.           FormatContext.ForceUpper = as_isupper(*pFormat);
  474.           goto IntCommon;
  475.         }
  476.         IntCommon:
  477.         {
  478.           char Str[100], *pStr = Str;
  479.           int Cnt;
  480.           int NumPadZeros = 0;
  481.  
  482.           if (FormatContext.Signed)
  483.           {
  484.             if (IntArg < 0)
  485.             {
  486.               *pStr++ = '-';
  487.               IntArg = 0 - IntArg;
  488.             }
  489.             else if (FormatContext.AddPlus)
  490.               *pStr++ = '+';
  491.           }
  492.           if (FormatContext.LeadZero)
  493.           {
  494.             NumPadZeros = FormatContext.Arg[0];
  495.             FormatContext.Arg[0] = 0;
  496.           }
  497.           Cnt = (pStr - Str)
  498.               + SysString(pStr, sizeof(Str) - (pStr - Str), IntArg,
  499.                           FormatContext.Arg[1] ? FormatContext.Arg[1] : 10,
  500.                           NumPadZeros, FormatContext.ForceLeadZero,
  501.                           FormatContext.ForceUpper ? 'A' : HexStartCharacter,
  502.                           SplitByteCharacter);
  503.           if (Cnt > (int)sizeof(Str))
  504.             Cnt = sizeof(Str);
  505.           Result += append(p_dest_ctx, Str, Cnt, &FormatContext);
  506.           break;
  507.         }
  508.         case 'e':
  509.         case 'f':
  510.         case 'g':
  511.         {
  512.           char Str[100];
  513.           int Cnt;
  514.  
  515.           Cnt = FloatConvert(Str, sizeof(Str), va_arg(ap, double), FormatContext.Arg[1], False, *pFormat);
  516.           if (Cnt > (int)sizeof(Str))
  517.             Cnt = sizeof(Str);
  518.           Result += append(p_dest_ctx, Str, Cnt, &FormatContext);
  519.           break;
  520.         }
  521.         case 's':
  522.         {
  523.           const char *pStr = va_arg(ap, char*);
  524.           size_t cnt = FormatContext.Arg[1]
  525.                      ? as_strnlen(pStr, FormatContext.Arg[1])
  526.                      : strlen(pStr);
  527.  
  528.           Result += append(p_dest_ctx, pStr, cnt, &FormatContext);
  529.           break;
  530.         }
  531.         default:
  532.           fprintf(stderr, "invalid format: '%c' in '%s'\n", *pFormat, pFormatStart);
  533.           exit(255);
  534.       }
  535.     else if (*pFormat == '%')
  536.       FormatContext.InFormat = True;
  537.     else
  538.       Result += append(p_dest_ctx, pFormat, 1, &FormatContext);
  539.  
  540.   if (p_dest_ctx->dest_remlen > 0)
  541.     *(p_dest_ctx->p_dest++) = '\0';
  542.   return Result;
  543. }
  544.  
  545. /*!------------------------------------------------------------------------
  546.  * \fn     as_vsdprcatf(as_dynstr_t *p_dest, const char *pFormat, va_list ap)
  547.  * \brief  append to dynamic string by format
  548.  * \param  p_dest string to be appended to
  549.  * \param  pFormat format specifier
  550.  * \param  ap format arguments
  551.  * \return # of characters appended
  552.  * ------------------------------------------------------------------------ */
  553.  
  554. int as_vsdprcatf(as_dynstr_t *p_dest, const char *pFormat, va_list ap)
  555. {
  556.   dest_format_context_t ctx;
  557.  
  558.   ctx.p_dest = p_dest->p_str;
  559.   ctx.dest_remlen = p_dest->capacity;
  560.   ctx.p_dynstr = p_dest;
  561.   return vsprcatf_core(&ctx, pFormat, ap);
  562. }
  563.  
  564. /*!------------------------------------------------------------------------
  565.  * \fn     as_vsdprintf(as_dynstr_t *p_dest, const char *pFormat, va_list ap)
  566.  * \brief  print to dynamic string by format
  567.  * \param  p_dest string to be appended to
  568.  * \param  pFormat format specifier
  569.  * \param  ap format arguments
  570.  * \return # of characters written
  571.  * ------------------------------------------------------------------------ */
  572.  
  573. int as_vsdprintf(as_dynstr_t *p_dest, const char *pFormat, va_list ap)
  574. {
  575.   if (p_dest->capacity > 0)
  576.     p_dest->p_str[0] = '\0';
  577.   return as_vsdprcatf(p_dest, pFormat, ap);
  578. }
  579.  
  580. /*!------------------------------------------------------------------------
  581.  * \fn     as_sdprcatf(as_dynstr_t *p_dest, const char *pFormat, ...)
  582.  * \brief  append to dynamic string by format
  583.  * \param  p_dest string to be appended to
  584.  * \param  pFormat format specifier
  585.  * \param  ... format arguments
  586.  * \return # of characters written
  587.  * ------------------------------------------------------------------------ */
  588.  
  589. int as_sdprcatf(as_dynstr_t *p_dest, const char *pFormat, ...)
  590. {
  591.   va_list ap;
  592.   int ret;
  593.  
  594.   va_start(ap, pFormat);
  595.   ret = as_vsdprcatf(p_dest, pFormat, ap);
  596.   va_end(ap);
  597.   return ret;
  598. }
  599.  
  600. /*!------------------------------------------------------------------------
  601.  * \fn     as_sdprintf(as_dynstr_t *p_dest, const char *pFormat, ...)
  602.  * \brief  print to dynamic string by format
  603.  * \param  p_dest string to be appended to
  604.  * \param  pFormat format specifier
  605.  * \param  ... format arguments
  606.  * \return # of characters written
  607.  * ------------------------------------------------------------------------ */
  608.  
  609. int as_sdprintf(as_dynstr_t *p_dest, const char *pFormat, ...)
  610. {
  611.   va_list ap;
  612.   int ret;
  613.  
  614.   va_start(ap, pFormat);
  615.   ret = as_vsdprintf(p_dest, pFormat, ap);
  616.   va_end(ap);
  617.   return ret;
  618. }
  619.  
  620. /*!------------------------------------------------------------------------
  621.  * \fn     as_vsnprcatf(char *pDest, size_t DestSize, const char *pFormat, va_list ap)
  622.  * \brief  append to string by format
  623.  * \param  pDest string to be appended to
  624.  * \param  DestSize capacity of string
  625.  * \param  pFormat format specifier
  626.  * \param  ap format arguments
  627.  * \return # of characters appended
  628.  * ------------------------------------------------------------------------ */
  629.  
  630. int as_vsnprcatf(char *pDest, size_t DestSize, const char *pFormat, va_list ap)
  631. {
  632.   dest_format_context_t ctx;
  633.  
  634.   if (DestSize == sizeof(char*))
  635.   {
  636.     fprintf(stderr, "pointer size passed to as_vsnprcatf\n");
  637.     exit(2);
  638.   }
  639.  
  640.   ctx.p_dest = pDest;
  641.   ctx.dest_remlen = DestSize;
  642.   ctx.p_dynstr = NULL;
  643.   return vsprcatf_core(&ctx, pFormat, ap);
  644. }
  645.  
  646. /*!------------------------------------------------------------------------
  647.  * \fn     as_vsnprintf(char *pDest, size_t DestSize, const char *pFormat, va_list ap)
  648.  * \brief  print to string by format
  649.  * \param  pDest string to be appended to
  650.  * \param  DestSize capacity of string
  651.  * \param  pFormat format specifier
  652.  * \param  ap format arguments
  653.  * \return # of characters written
  654.  * ------------------------------------------------------------------------ */
  655.  
  656. int as_vsnprintf(char *pDest, size_t DestSize, const char *pFormat, va_list ap)
  657. {
  658.   if (DestSize > 0)
  659.     *pDest = '\0';
  660.   return as_vsnprcatf(pDest, DestSize, pFormat, ap);
  661. }
  662.  
  663. /*!------------------------------------------------------------------------
  664.  * \fn     as_snprintf(char *pDest, size_t DestSize, const char *pFormat, ...)
  665.  * \brief  print to string by format
  666.  * \param  pDest string to be appended to
  667.  * \param  DestSize capacity of string
  668.  * \param  pFormat format specifier
  669.  * \param  ... format arguments
  670.  * \return # of characters written
  671.  * ------------------------------------------------------------------------ */
  672.  
  673. int as_snprintf(char *pDest, size_t DestSize, const char *pFormat, ...)
  674. {
  675.   va_list ap;
  676.   int Result;
  677.  
  678.   va_start(ap, pFormat);
  679.   if (DestSize > 0)
  680.     *pDest = '\0';
  681.   Result = as_vsnprcatf(pDest, DestSize, pFormat, ap);
  682.   va_end(ap);
  683.   return Result;
  684. }
  685.  
  686. /*!------------------------------------------------------------------------
  687.  * \fn     as_snprcatf(char *pDest, size_t DestSize, const char *pFormat, ...)
  688.  * \brief  append to string by format
  689.  * \param  pDest string to be appended to
  690.  * \param  DestSize capacity of string
  691.  * \param  pFormat format specifier
  692.  * \param  ... format arguments
  693.  * \return # of characters appended
  694.  * ------------------------------------------------------------------------ */
  695.  
  696. int as_snprcatf(char *pDest, size_t DestSize, const char *pFormat, ...)
  697. {
  698.   va_list ap;
  699.   int Result;
  700.  
  701.   va_start(ap, pFormat);
  702.   Result = as_vsnprcatf(pDest, DestSize, pFormat, ap);
  703.   va_end(ap);
  704.   return Result;
  705. }
  706.  
  707. int as_strcasecmp(const char *src1, const char *src2)
  708. {
  709.   if (!src1)
  710.     src1 = "";
  711.   if (!src2)
  712.     src2 = "";
  713.   while (tolower(*src1) == tolower(*src2))
  714.   {
  715.     if ((!*src1) && (!*src2))
  716.       return 0;
  717.     src1++;
  718.     src2++;
  719.   }
  720.   return ((int) tolower(*src1)) - ((int) tolower(*src2));
  721. }      
  722.  
  723. int as_strncasecmp(const char *src1, const char *src2, size_t len)
  724. {
  725.   if (!src1)
  726.     src1 = "";
  727.   if (!src2)
  728.     src2 = "";
  729.   while (tolower(*src1) == tolower(*src2))
  730.   {
  731.     if (--len == 0)
  732.       return 0;
  733.     if ((!*src1) && (!*src2))
  734.       return 0;
  735.     src1++;
  736.     src2++;
  737.   }
  738.   return ((int) tolower(*src1)) - ((int) tolower(*src2));
  739. }      
  740.  
  741. #ifdef NEEDS_STRSTR
  742. char *strstr(const char *haystack, const char *needle)
  743. {
  744.   int lh = strlen(haystack), ln = strlen(needle);
  745.   int z;
  746.   char *p;
  747.  
  748.   for (z = 0; z <= lh - ln; z++)
  749.     if (strncmp(p = haystack + z, needle, ln) == 0)
  750.       return p;
  751.   return NULL;
  752. }
  753. #endif
  754.  
  755. /*!------------------------------------------------------------------------
  756.  * \fn     strrmultchr(const char *haystack, const char *needles)
  757.  * \brief  find the last occurence of either character in string
  758.  * \param  haystack string to search in
  759.  * \param  needles characters to search for
  760.  * \return last occurence or NULL
  761.  * ------------------------------------------------------------------------ */
  762.  
  763. char *strrmultchr(const char *haystack, const char *needles)
  764. {
  765.   const char *pPos;
  766.  
  767.   for (pPos = haystack + strlen(haystack) - 1; pPos >= haystack; pPos--)
  768.     if (strchr(needles, *pPos))
  769.       return (char*)pPos;
  770.   return NULL;
  771. }
  772.  
  773. /*---------------------------------------------------------------------------*/
  774. /* das originale strncpy plaettet alle ueberstehenden Zeichen mit Nullen */
  775.  
  776. size_t strmaxcpy(char *dest, const char *src, size_t Max)
  777. {
  778.   size_t cnt = strlen(src);
  779.  
  780.   /* leave room for terminating NUL */
  781.  
  782.   if (!Max)
  783.     return 0;
  784.   if (cnt + 1 > Max)
  785.     cnt = Max - 1;
  786.   memcpy(dest, src, cnt);
  787.   dest[cnt] = '\0';
  788.   return cnt;
  789. }
  790.  
  791. /*---------------------------------------------------------------------------*/
  792. /* einfuegen, mit Begrenzung */
  793.  
  794. size_t strmaxcat(char *Dest, const char *Src, size_t MaxLen)
  795. {
  796.   int TLen = strlen(Src);
  797.   size_t DLen = strlen(Dest);
  798.  
  799.   if (TLen > (int)MaxLen - 1 - (int)DLen)
  800.     TLen = MaxLen - DLen - 1;
  801.   if (TLen > 0)
  802.   {
  803.     memcpy(Dest + DLen, Src, TLen);
  804.     Dest[DLen + TLen] = '\0';
  805.     return DLen + TLen;
  806.   }
  807.   else
  808.     return DLen;
  809. }
  810.  
  811. void strprep(char *Dest, const char *Src)
  812. {
  813.   memmove(Dest + strlen(Src), Dest, strlen(Dest) + 1);
  814.   memmove(Dest, Src, strlen(Src));
  815. }
  816.  
  817. /*!------------------------------------------------------------------------
  818.  * \fn     strmaxprep(char *p_dest, const char *p_src, size_t max_len)
  819.  * \brief  prepend as much as possible from src to dest
  820.  * \param  p_dest string to be prepended
  821.  * \param  p_src string to prepend
  822.  * \param  max_len capacity of p_dest
  823.  * ------------------------------------------------------------------------ */
  824.  
  825. void strmaxprep(char *p_dest, const char *p_src, size_t max_len)
  826. {
  827.   size_t src_len = strlen(p_src),
  828.          dest_len = strlen(p_dest);
  829.  
  830.   assert(dest_len + 1 <= max_len);
  831.   if (src_len > max_len - dest_len - 1)
  832.     src_len = max_len - dest_len - 1;
  833.   memmove(p_dest + src_len, p_dest, dest_len + 1);
  834.   memmove(p_dest, p_src, src_len);
  835. }
  836.  
  837. /*!------------------------------------------------------------------------
  838.  * \fn     strmaxprep2(char *p_dest, const char *p_src, size_t max_len)
  839.  * \brief  prepend as much as possible from src to dest, and possibly truncate dest by that
  840.  * \param  p_dest string to be prepended
  841.  * \param  p_src string to prepend
  842.  * \param  max_len capacity of p_dest
  843.  * ------------------------------------------------------------------------ */
  844.  
  845. void strmaxprep2(char *p_dest, const char *p_src, size_t max_len)
  846. {
  847.   size_t src_len = strlen(p_src),
  848.          dest_len = strlen(p_dest);
  849.  
  850.   assert(max_len > 0);
  851.   if (src_len >= max_len)
  852.     src_len = max_len - 1;
  853.   max_len -= src_len;
  854.   if (dest_len >= max_len)
  855.     dest_len = max_len - 1;
  856.   memmove(p_dest + src_len, p_dest, dest_len + 1);
  857.   memmove(p_dest, p_src, src_len);
  858. }
  859.  
  860. void strins(char *Dest, const char *Src, int Pos)
  861. {
  862.   memmove(Dest + Pos + strlen(Src), Dest + Pos, strlen(Dest) + 1 - Pos);
  863.   memmove(Dest + Pos, Src, strlen(Src));
  864. }
  865.  
  866. void strmaxins(char *Dest, const char *Src, int Pos, size_t MaxLen)
  867. {
  868.   size_t RLen;
  869.  
  870.   RLen = strlen(Src);
  871.   if (RLen > MaxLen - strlen(Dest))
  872.     RLen = MaxLen - strlen(Dest);
  873.   memmove(Dest + Pos + RLen, Dest + Pos, strlen(Dest) + 1 - Pos);
  874.   memmove(Dest + Pos, Src, RLen);
  875. }
  876.  
  877. int strlencmp(const char *pStr1, unsigned Str1Len,
  878.               const char *pStr2, unsigned Str2Len)
  879. {
  880.   const char *p1, *p2, *p1End, *p2End;
  881.   int Diff;
  882.  
  883.   for (p1 = pStr1, p1End = p1 + Str1Len,
  884.        p2 = pStr2, p2End = p2 + Str2Len;
  885.        p1 < p1End && p2 < p2End; p1++, p2++)
  886.   {
  887.     Diff = ((int)*p1) - ((int)*p2);
  888.     if (Diff)
  889.       return Diff;
  890.   }
  891.   return ((int)Str1Len) - ((int)Str2Len);
  892. }
  893.  
  894. unsigned fstrlenprint(FILE *pFile, const char *pStr, unsigned StrLen)
  895. {
  896.   unsigned Result = 0;
  897.   const char *pRun, *pEnd;
  898.  
  899.   for (pRun = pStr, pEnd = pStr + StrLen; pRun < pEnd; pRun++)
  900.     if ((*pRun == '\\') || (*pRun == '"') || (*pRun == ' ') || (!isprint(*pRun)))
  901.     {
  902.       fprintf(pFile, "\\%03d", *pRun);
  903.       Result += 4;
  904.     }
  905.     else
  906.     {
  907.       fputc(*pRun, pFile);
  908.       Result++;
  909.     }
  910.  
  911.   return Result;
  912. }
  913.  
  914. size_t as_strnlen(const char *pStr, size_t MaxLen)
  915. {
  916.   size_t Res = 0;
  917.  
  918.   for (; (MaxLen > 0); MaxLen--, pStr++, Res++)
  919.     if (!*pStr)
  920.       break;
  921.   return Res;
  922. }
  923.  
  924. /*!------------------------------------------------------------------------
  925.  * \fn     strreplace(char *pHaystack, const char *pFrom, const char *pTo, size_t ToMaxLen, size_t HaystackSize)
  926.  * \brief  replaces all occurences of From to To in Haystack
  927.  * \param  pHaystack string to search in
  928.  * \param  pFrom what to find
  929.  * \param  pFrom what to find
  930.  * \param  pTo what to replace it with
  931.  * \param  ToMaxLen if not -1, max. length of pTo (not NUL-terminated)
  932.  * \param  HaystackSize buffer capacity
  933.  * \return # of occurences
  934.  * ------------------------------------------------------------------------ */
  935.  
  936. int strreplace(char *pHaystack, const char *pFrom, const char *pTo, size_t ToMaxLen, size_t HaystackSize)
  937. {
  938.   int HaystackLen = -1, FromLen = -1, ToLen = -1, Count = 0;
  939.   int HeadLen, TailLen;
  940.   char *pSearch, *pPos;
  941.  
  942.   pSearch = pHaystack;
  943.   while (True)
  944.   {
  945.     /* find an occurence */
  946.  
  947.     pPos = strstr(pSearch, pFrom);
  948.     if (!pPos)
  949.       return Count;
  950.  
  951.     /* compute some stuff upon first occurence when needed */
  952.  
  953.     if (FromLen < 0)
  954.     {
  955.       HaystackLen = strlen(pHaystack);
  956.       FromLen = strlen(pFrom);
  957.     }
  958.     ToLen = (ToMaxLen > 0) ? as_strnlen(pTo, ToMaxLen) : strlen(pTo);
  959.  
  960.     /* See how much of the remainder behind 'To' still fits into buffer after replacement,
  961.        and move accordingly: */
  962.  
  963.     HeadLen = pPos - pHaystack;
  964.     TailLen = HaystackLen - HeadLen - FromLen;
  965.     if (HeadLen + ToLen + TailLen >= (int)HaystackSize)
  966.     {
  967.       TailLen = HaystackSize - 1 - HeadLen - ToLen;
  968.       if (TailLen < 0)
  969.         TailLen = 0;
  970.     }
  971.     if (TailLen > 0)
  972.       memmove(pPos + ToLen, pPos + FromLen, TailLen);
  973.  
  974.     /* See how much of 'To' still fits into buffer, and set accordingly: */
  975.  
  976.     if (HeadLen + ToLen >= (int)HaystackSize)
  977.     {
  978.       ToLen = HaystackSize - 1 - ToLen;
  979.       if (ToLen < 0)
  980.         ToLen = 0;
  981.     }
  982.     if (ToLen > 0)
  983.       memcpy(pPos, pTo, ToLen);
  984.  
  985.     /* Update length & terminate new string */
  986.  
  987.     HaystackLen = HeadLen + ToLen + TailLen;
  988.     pHaystack[HaystackLen] = '\0';
  989.  
  990.     /* continue searching behind replacement: */
  991.  
  992.     pSearch = &pHaystack[HeadLen + ToLen];
  993.  
  994.     Count++;
  995.   }
  996. }
  997.  
  998. /*---------------------------------------------------------------------------*/
  999. /* Bis Zeilenende lesen */
  1000.  
  1001. void ReadLn(FILE *Datei, char *Zeile)
  1002. {
  1003.   char *ptr;
  1004.   int l;
  1005.  
  1006.   *Zeile = '\0';
  1007.   ptr = fgets(Zeile, 256, Datei);
  1008.   if ((!ptr) && (ferror(Datei) != 0))
  1009.     *Zeile = '\0';
  1010.   l = strlen(Zeile);
  1011.   if ((l > 0) && (Zeile[l - 1] == '\n'))
  1012.     Zeile[--l] = '\0';
  1013.   if ((l > 0) && (Zeile[l - 1] == '\r'))
  1014.     Zeile[--l] = '\0';
  1015.   if ((l > 0) && (Zeile[l - 1] == 26))
  1016.     Zeile[--l] = '\0';
  1017. }
  1018.  
  1019. #if 0
  1020.  
  1021. static void dump(const char *pLine, unsigned Cnt)
  1022. {
  1023.   unsigned z;
  1024.  
  1025.   fputc('\n', stderr);
  1026.   for (z = 0; z < Cnt; z++)
  1027.   {
  1028.     fprintf(stderr, " %02x", pLine[z]);
  1029.     if ((z & 15) == 15)
  1030.       fputc('\n', stderr);
  1031.   }
  1032.   fputc('\n', stderr);
  1033. }
  1034.  
  1035. #endif
  1036.  
  1037. /*!------------------------------------------------------------------------
  1038.  * \fn     ReadLnCont(FILE *Datei, as_dynstr_t *p_line)
  1039.  * \brief  read line, regarding \ continuation characters
  1040.  * \param  Datei where to read from
  1041.  * \param  pLine dest buffer
  1042.  * \return # of lines read
  1043.  * ------------------------------------------------------------------------ */
  1044.  
  1045. size_t ReadLnCont(FILE *Datei, as_dynstr_t *p_line)
  1046. {
  1047.   char *ptr, *pDest;
  1048.   size_t l, Count, LineCount;
  1049.   Boolean Terminated;
  1050.  
  1051.   /* read from input until no continuation is present */
  1052.  
  1053.   pDest = p_line->p_str;
  1054.   LineCount = Count = 0;
  1055.   while (1)
  1056.   {
  1057.     /* get a line from file, possibly reallocating until everything up to \n fits */
  1058.  
  1059.     while (1)
  1060.     {
  1061.       if (p_line->capacity - Count < 128)
  1062.         as_dynstr_realloc(p_line, p_line->capacity + 128);
  1063.  
  1064.       pDest = p_line->p_str + Count;
  1065.       *pDest = '\0';
  1066.       ptr = fgets(pDest, p_line->capacity - Count, Datei);
  1067.       if (!ptr)
  1068.       {
  1069.         if (ferror(Datei) != 0)
  1070.           *pDest = '\0';
  1071.         break;
  1072.       }
  1073.  
  1074.       /* If we have a trailing \n, we read up to end of line: */
  1075.  
  1076.       l = strlen(pDest);
  1077.       Terminated = ((l > 0) && (pDest[l - 1] == '\n'));
  1078.  
  1079.       /* srtrip possible CR preceding LF: */
  1080.  
  1081.       if (Terminated)
  1082.       {
  1083.         /* strip LF, and possible CR, and bail out: */
  1084.  
  1085.         pDest[--l] = '\0';
  1086.         if ((l > 0) && (pDest[l - 1] == '\r'))
  1087.           pDest[--l] = '\0';
  1088.       }
  1089.  
  1090.       Count += l;
  1091.       pDest += l;
  1092.  
  1093.       if (Terminated)
  1094.         break;
  1095.     }
  1096.  
  1097.     LineCount++;
  1098.     if ((Count > 0) && (p_line->p_str[Count - 1] == 26))
  1099.       p_line->p_str[--Count] = '\0';
  1100.  
  1101.     /* optional line continuation */
  1102.  
  1103.     if ((Count > 0) && (p_line->p_str[Count - 1] == '\\'))
  1104.       p_line->p_str[--Count] = '\0';
  1105.     else
  1106.       break;
  1107.   }
  1108.  
  1109.   return LineCount;
  1110. }
  1111.  
  1112. /*!------------------------------------------------------------------------
  1113.  * \fn     DigitVal(char ch, int Base)
  1114.  * \brief  get value of hex digit
  1115.  * \param  ch digit
  1116.  * \param  Base Number System
  1117.  * \return 0..Base-1 or -1 if no valid digit
  1118.  * ------------------------------------------------------------------------ */
  1119.  
  1120. int DigitVal(char ch, int Base)
  1121. {
  1122.   int Result;
  1123.  
  1124.   /* Ziffern 0..9 ergeben selbiges */
  1125.  
  1126.   if ((ch >= '0') && (ch <= '9'))
  1127.     Result = ch - '0';
  1128.  
  1129.   /* Grossbuchstaben fuer Hexziffern */
  1130.  
  1131.   else if ((ch >= 'A') && (ch <= 'Z'))
  1132.     Result = ch - 'A' + 10;
  1133.  
  1134.   /* Kleinbuchstaben nicht vergessen...! */
  1135.  
  1136.   else if ((ch >= 'a') && (ch <= 'z'))
  1137.     Result = ch - 'a' + 10;
  1138.  
  1139.   /* alles andere ist Schrott */
  1140.  
  1141.   else
  1142.     Result = -1;
  1143.  
  1144.   return (Result >= Base) ? -1 : Result;
  1145. }
  1146.  
  1147. /*--------------------------------------------------------------------*/
  1148. /* Zahlenkonstante umsetzen: $ hex, % binaer, @ oktal */
  1149. /* inp: Eingabezeichenkette */
  1150. /* erg: Zeiger auf Ergebnis-Longint */
  1151. /* liefert TRUE, falls fehlerfrei, sonst FALSE */
  1152.  
  1153. LargeInt ConstLongInt(const char *inp, Boolean *pErr, LongInt Base)
  1154. {
  1155.   static const char Prefixes[4] = { '$', '@', '%', '\0' }; /* die moeglichen Zahlensysteme */
  1156.   static const char Postfixes[4] = { 'H', 'O', '\0', '\0' };
  1157.   static const LongInt Bases[3] = { 16, 8, 2 };            /* die dazugehoerigen Basen */
  1158.   LargeInt erg, val;
  1159.   int z, vorz = 1;  /* Vermischtes */
  1160.   int InpLen = strlen(inp);
  1161.  
  1162.   /* eventuelles Vorzeichen abspalten */
  1163.  
  1164.   if (*inp == '-')
  1165.   {
  1166.     vorz = -1;
  1167.     inp++;
  1168.     InpLen--;
  1169.   }
  1170.  
  1171.   /* Sonderbehandlung 0x --> $ */
  1172.  
  1173.   if ((InpLen >= 2)
  1174.    && (*inp == '0')
  1175.    && (as_toupper(inp[1]) == 'X'))
  1176.   {
  1177.     inp += 2;
  1178.     InpLen -= 2;
  1179.     Base = 16;
  1180.   }
  1181.  
  1182.   /* Jetzt das Zahlensystem feststellen.  Vorgabe ist dezimal, was
  1183.      sich aber durch den Initialwert von Base jederzeit aendern
  1184.      laesst.  Der break-Befehl verhindert, dass mehrere Basenzeichen
  1185.      hintereinander eingegeben werden koennen */
  1186.  
  1187.   else if (InpLen > 0)
  1188.   {
  1189.     for (z = 0; z < 3; z++)
  1190.       if (*inp == Prefixes[z])
  1191.       {
  1192.         Base = Bases[z];
  1193.         inp++;
  1194.         InpLen--;
  1195.         break;
  1196.       }
  1197.       else if (as_toupper(inp[InpLen - 1]) == Postfixes[z])
  1198.       {
  1199.         Base = Bases[z];
  1200.         InpLen--;
  1201.         break;
  1202.       }
  1203.   }
  1204.  
  1205.   /* jetzt die Zahlenzeichen der Reihe nach durchverwursten */
  1206.  
  1207.   erg = 0;
  1208.   *pErr = False;
  1209.   for(; InpLen > 0; inp++, InpLen--)
  1210.   {
  1211.     val = DigitVal(*inp, 16);
  1212.     if (val < -0)
  1213.       break;
  1214.  
  1215.     /* entsprechend der Basis zulaessige Ziffer ? */
  1216.  
  1217.     if (val >= Base)
  1218.       break;
  1219.  
  1220.     /* Zahl linksschieben, zusammenfassen, naechster bitte */
  1221.  
  1222.     erg = erg * Base + val;
  1223.   }
  1224.  
  1225.   /* bis zum Ende durchgelaufen ? */
  1226.  
  1227.   if (!InpLen)
  1228.   {
  1229.     /* Vorzeichen beruecksichtigen */
  1230.  
  1231.     erg *= vorz;
  1232.     *pErr = True;
  1233.   }
  1234.  
  1235.   return erg;
  1236. }
  1237.  
  1238. /*--------------------------------------------------------------------------*/
  1239. /* alle Leerzeichen aus einem String loeschen */
  1240.  
  1241. void KillBlanks(char *s)
  1242. {
  1243.   char *z, *dest;
  1244.   Boolean InSgl = False, InDbl = False, ThisEscaped = False, NextEscaped = False;
  1245.  
  1246.   dest = s;
  1247.   for (z = s; *z != '\0'; z++, ThisEscaped = NextEscaped)
  1248.   {
  1249.     NextEscaped = False;
  1250.     switch (*z)
  1251.     {
  1252.       case '\'':
  1253.         if (!InDbl && !ThisEscaped)
  1254.           InSgl = !InSgl;
  1255.         break;
  1256.       case '"':
  1257.         if (!InSgl && !ThisEscaped)
  1258.           InDbl = !InDbl;
  1259.         break;
  1260.       case '\\':
  1261.         if ((InSgl || InDbl) && !ThisEscaped)
  1262.           NextEscaped = True;
  1263.         break;
  1264.     }
  1265.     if (!as_isspace(*z) || InSgl || InDbl)
  1266.       *dest++ = *z;
  1267.   }
  1268.   *dest = '\0';
  1269. }
  1270.  
  1271. int CopyNoBlanks(char *pDest, const char *pSrc, size_t MaxLen)
  1272. {
  1273.   const char *pSrcRun;
  1274.   char *pDestRun = pDest;
  1275.   size_t Cnt = 0;
  1276.   Byte Flags = 0;
  1277.   char ch;
  1278.   Boolean ThisEscaped, PrevEscaped;
  1279.  
  1280.   /* leave space for NUL */
  1281.  
  1282.   MaxLen--;
  1283.  
  1284.   PrevEscaped = False;
  1285.   for (pSrcRun = pSrc; *pSrcRun; pSrcRun++)
  1286.   {
  1287.     ch = *pSrcRun;
  1288.     ThisEscaped = False;
  1289.     switch (ch)
  1290.     {
  1291.       case '\'':
  1292.         if (!(Flags & 2) && !PrevEscaped)
  1293.           Flags ^= 1;
  1294.         break;
  1295.       case '"':
  1296.         if (!(Flags & 1) && !PrevEscaped)
  1297.           Flags ^= 2;
  1298.         break;
  1299.       case '\\':
  1300.         if (!PrevEscaped)
  1301.           ThisEscaped = True;
  1302.         break;
  1303.     }
  1304.     if (!as_isspace(ch) || Flags)
  1305.       *(pDestRun++) = ch;
  1306.     if (++Cnt >= MaxLen)
  1307.       break;
  1308.     PrevEscaped = ThisEscaped;
  1309.   }
  1310.   *pDestRun = '\0';
  1311.  
  1312.   return Cnt;
  1313. }
  1314.  
  1315. /*--------------------------------------------------------------------------*/
  1316. /* fuehrende Leerzeichen loeschen */
  1317.  
  1318. int KillPrefBlanks(char *s)
  1319. {
  1320.   char *z = s;
  1321.  
  1322.   while ((*z != '\0') && as_isspace(*z))
  1323.     z++;
  1324.   if (z != s)
  1325.     strmov(s, z);
  1326.   return z - s;
  1327. }
  1328.  
  1329. /*--------------------------------------------------------------------------*/
  1330. /* anhaengende Leerzeichen loeschen */
  1331.  
  1332. int KillPostBlanks(char *s)
  1333. {
  1334.   char *z = s + strlen(s) - 1;
  1335.   int count = 0;
  1336.  
  1337.   while ((z >= s) && as_isspace(*z))
  1338.   {
  1339.     *(z--) = '\0';
  1340.     count++;
  1341.   }
  1342.   return count;
  1343. }
  1344.  
  1345. /*--------------------------------------------------------------------------*/
  1346.  
  1347. int strqcmp(const char *s1, const char *s2)
  1348. {
  1349.   int erg = (*s1) - (*s2);
  1350.  
  1351.   return (erg != 0) ? erg : strcmp(s1, s2);
  1352. }
  1353.  
  1354. /*--------------------------------------------------------------------------*/
  1355.  
  1356. /* we need a strcpy() with a defined behaviour in case of overlapping source
  1357.    and destination: */
  1358.  
  1359. char *strmov(char *pDest, const char *pSrc)
  1360. {
  1361.   memmove(pDest, pSrc, strlen(pSrc) + 1);
  1362.   return pDest;
  1363. }
  1364.  
  1365. #ifdef __GNUC__
  1366.  
  1367. #ifdef strcpy
  1368. # undef strcpy
  1369. #endif
  1370. char *strcpy(char *pDest, const char *pSrc)
  1371. {
  1372.   int l = strlen(pSrc) + 1;
  1373.   int Overlap = 0;
  1374.  
  1375.   if (pSrc < pDest)
  1376.   {
  1377.     if (pSrc + l > pDest)
  1378.       Overlap = 1;
  1379.   }
  1380.   else if (pSrc > pDest)
  1381.   {
  1382.     if (pDest + l > pSrc)
  1383.       Overlap = 1;
  1384.   }
  1385.   else if (l > 0)
  1386.   {
  1387.     Overlap = 1;
  1388.   }
  1389.  
  1390.   if (Overlap)
  1391.   {
  1392.     fprintf(stderr, "overlapping strcpy() called from address %p, resolve this address with addr2line and report to author\n",
  1393.             __builtin_return_address(0));
  1394.     abort();
  1395.   }
  1396.  
  1397.   return strmov(pDest, pSrc);
  1398. }
  1399.  
  1400. #endif
  1401.  
  1402. /*!------------------------------------------------------------------------
  1403.  * \fn     strmemcpy(char *pDest, size_t DestSize, const char *pSrc, size_t SrcLen)
  1404.  * \brief  copy string with length limitation
  1405.  * \param  pDest where to write
  1406.  * \param  DestSize destination capacity
  1407.  * \param  pSrc copy source
  1408.  * \param  SrcLen # of characters to copy at most
  1409.  * \return actual, possibly limited length
  1410.  * ------------------------------------------------------------------------ */
  1411.  
  1412. int strmemcpy(char *pDest, size_t DestSize, const char *pSrc, size_t SrcLen)
  1413. {
  1414.   if (DestSize < SrcLen + 1)
  1415.     SrcLen = DestSize - 1;
  1416.   memmove(pDest, pSrc, SrcLen);
  1417.   pDest[SrcLen] = '\0';
  1418.   return SrcLen;
  1419. }
  1420.  
  1421. /*--------------------------------------------------------------------------*/
  1422.  
  1423. char *ParenthPos(char *pHaystack, char Needle)
  1424. {
  1425.   char *pRun;
  1426.   int Level = 0;
  1427.  
  1428.   for (pRun = pHaystack; *pRun; pRun++)
  1429.   {
  1430.     switch (*pRun)
  1431.     {
  1432.       case '(':
  1433.         Level++;
  1434.         break;
  1435.       case ')':
  1436.         if (Level < 1)
  1437.           return NULL;
  1438.         Level--;
  1439.         break;
  1440.       default:
  1441.         if (*pRun == Needle && !Level)
  1442.           return pRun;
  1443.     }
  1444.   }
  1445.   return NULL;
  1446. }
  1447.  
  1448. /*!------------------------------------------------------------------------
  1449.  * \fn     TabCompressed(char in)
  1450.  * \brief  replace TABs with spaces for error display
  1451.  * \param  in character to compress
  1452.  * \return compressed result
  1453.  * ------------------------------------------------------------------------ */
  1454.  
  1455. char TabCompressed(char in)
  1456. {
  1457.   return (in == '\t') ? ' ' : (as_isprint(in) ? in : '*');
  1458. }
  1459.  
  1460. /*--------------------------------------------------------------------------*/
  1461.  
  1462. void strutil_init(void)
  1463. {
  1464.   HexStartCharacter = 'A';
  1465. }
  1466.