Subversion Repositories pentevo

Rev

Details | Last modification | View Log | RSS feed

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