Coverage Report

Created: 2026-11-12 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/token.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                    TTTTT   OOO   K   K  EEEEE  N   N                        %
7
%                      T    O   O  K  K   E      NN  N                        %
8
%                      T    O   O  KKK    EEE    N N N                        %
9
%                      T    O   O  K  K   E      N  NN                        %
10
%                      T     OOO   K   K  EEEEE  N   N                        %
11
%                                                                             %
12
%                                                                             %
13
%                         MagickCore Token Methods                            %
14
%                                                                             %
15
%                             Software Design                                 %
16
%                                  Cristy                                     %
17
%                              January 1993                                   %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/license/                                         %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
%
38
*/
39

40
/*
41
  Include declarations.
42
*/
43
#include "MagickCore/studio.h"
44
#include "MagickCore/exception.h"
45
#include "MagickCore/exception-private.h"
46
#include "MagickCore/image.h"
47
#include "MagickCore/image-private.h"
48
#include "MagickCore/locale-private.h"
49
#include "MagickCore/memory_.h"
50
#include "MagickCore/memory-private.h"
51
#include "MagickCore/string_.h"
52
#include "MagickCore/string-private.h"
53
#include "MagickCore/token.h"
54
#include "MagickCore/token-private.h"
55
#include "MagickCore/utility.h"
56
#include "MagickCore/utility-private.h"
57

58
/*
59
  Typedef declarations.
60
*/
61
struct _TokenInfo
62
{
63
  int
64
    state;
65
66
  MagickStatusType
67
    flag;
68
69
  ssize_t
70
    offset;
71
72
  char
73
    quote;
74
75
  size_t
76
    signature;
77
};
78

79
/*
80
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81
%                                                                             %
82
%                                                                             %
83
%                                                                             %
84
%   A c q u i r e T o k e n I n f o                                           %
85
%                                                                             %
86
%                                                                             %
87
%                                                                             %
88
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89
%
90
%  AcquireTokenInfo() allocates the TokenInfo structure.
91
%
92
%  The format of the AcquireTokenInfo method is:
93
%
94
%      TokenInfo *AcquireTokenInfo()
95
%
96
*/
97
MagickExport TokenInfo *AcquireTokenInfo(void)
98
530
{
99
530
  TokenInfo
100
530
    *token_info;
101
102
530
  token_info=(TokenInfo *) AcquireCriticalMemory(sizeof(*token_info));
103
530
  token_info->signature=MagickCoreSignature;
104
530
  return(token_info);
105
530
}
106

107
/*
108
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109
%                                                                             %
110
%                                                                             %
111
%                                                                             %
112
%   D e s t r o y T o k e n I n f o                                           %
113
%                                                                             %
114
%                                                                             %
115
%                                                                             %
116
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117
%
118
%  DestroyTokenInfo() deallocates memory associated with an TokenInfo
119
%  structure.
120
%
121
%  The format of the DestroyTokenInfo method is:
122
%
123
%      TokenInfo *DestroyTokenInfo(TokenInfo *token_info)
124
%
125
%  A description of each parameter follows:
126
%
127
%    o token_info: Specifies a pointer to an TokenInfo structure.
128
%
129
*/
130
MagickExport TokenInfo *DestroyTokenInfo(TokenInfo *token_info)
131
530
{
132
530
  assert(token_info != (TokenInfo *) NULL);
133
530
  assert(token_info->signature == MagickCoreSignature);
134
530
  if (IsEventLogging() != MagickFalse)
135
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
136
530
  token_info->signature=(~MagickCoreSignature);
137
530
  token_info=(TokenInfo *) RelinquishMagickMemory(token_info);
138
530
  return(token_info);
139
530
}
140

141
/*
142
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143
%                                                                             %
144
%                                                                             %
145
%                                                                             %
146
+   G e t N e x t T o k e n                                                   %
147
%                                                                             %
148
%                                                                             %
149
%                                                                             %
150
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151
%
152
%  GetNextToken() gets a token from the token stream.  A token is defined as
153
%  a sequence of characters delimited by whitespace (e.g. clip-path), a
154
%  sequence delimited with quotes (.e.g "Quote me"), or a sequence enclosed in
155
%  parenthesis (e.g. rgb(0,0,0)).  GetNextToken() also recognizes these
156
%  separator characters: ':', '=', ',', and ';'.  GetNextToken() returns the
157
%  length of the consumed token.
158
%
159
%  The format of the GetNextToken method is:
160
%
161
%      size_t GetNextToken(const char *magick_restrict start,
162
%        const char **magick_restrict end,const size_t extent,
163
%        char *magick_restrict token)
164
%
165
%  A description of each parameter follows:
166
%
167
%    o start: the start of the token sequence.
168
%
169
%    o end: point to the end of the token sequence.
170
%
171
%    o extent: maximum extent of the token.
172
%
173
%    o token: copy the token to this buffer.
174
%
175
*/
176
MagickExport magick_hot_spot size_t GetNextToken(
177
  const char *magick_restrict start,const char **magick_restrict end,
178
  const size_t extent,char *magick_restrict token)
179
4.73M
{
180
4.73M
  char
181
4.73M
    *magick_restrict q;
182
183
4.73M
  const char
184
4.73M
    *magick_restrict p;
185
186
4.73M
  double
187
4.73M
    value;
188
189
4.73M
  ssize_t
190
4.73M
    i;
191
192
4.73M
  assert(start != (const char *) NULL);
193
4.73M
  assert(token != (char *) NULL);
194
4.73M
  i=0;
195
4.73M
  p=start;
196
4.74M
  while ((isspace((int) ((unsigned char) *p)) != 0) && (*p != '\0'))
197
11.4k
    p++;
198
4.73M
  switch (*p)
199
4.73M
  {
200
10.2k
    case '\0':
201
10.2k
      break;
202
132k
    case '"':
203
434k
    case '\'':
204
442k
    case '`':
205
444k
    case '{':
206
444k
    {
207
444k
      char
208
444k
        escape;
209
210
444k
      switch (*p)
211
444k
      {
212
132k
        case '"': escape='"'; break;
213
302k
        case '\'': escape='\''; break;
214
7.43k
        case '`': escape='\''; break;
215
1.96k
        case '{': escape='}'; break;
216
0
        default: escape=(*p); break;
217
444k
      }
218
52.6M
      for (p++; *p != '\0'; p++)
219
52.6M
      {
220
52.6M
        if ((*p == '\\') && ((*(p+1) == escape) || (*(p+1) == '\\')))
221
15.5M
          p++;
222
37.0M
        else
223
37.0M
          if (*p == escape)
224
422k
            {
225
422k
              p++;
226
422k
              break;
227
422k
            }
228
52.1M
        if (i < (ssize_t) (extent-1))
229
52.1M
          token[i++]=(*p);
230
52.1M
        if ((size_t) (p-start) >= (extent-1))
231
368
          break;
232
52.1M
      }
233
444k
      break;
234
444k
    }
235
5.21k
    case '/':
236
5.21k
    {
237
5.21k
      if (i < (ssize_t) (extent-1))
238
5.21k
        token[i++]=(*p);
239
5.21k
      p++;
240
5.21k
      if ((*p == '>') || (*p == '/'))
241
2.53k
        {
242
2.53k
          if (i < (ssize_t) (extent-1))
243
2.53k
            token[i++]=(*p);
244
2.53k
          p++;
245
2.53k
        }
246
5.21k
      break;
247
444k
    }
248
4.27M
    default:
249
4.27M
    {
250
4.27M
      value=StringToDouble(p,&q);
251
4.27M
      (void) value;
252
4.27M
      if ((p != q) && (*p != ','))
253
1.05M
        {
254
3.38M
          for ( ; (p < q) && (*p != ','); p++)
255
2.32M
          {
256
2.32M
            if (i < (ssize_t) (extent-1))
257
2.32M
              token[i++]=(*p);
258
2.32M
            if ((size_t) (p-start) >= (extent-1))
259
2
              break;
260
2.32M
          }
261
1.05M
          if (*p == '%')
262
1.66k
            {
263
1.66k
              if (i < (ssize_t) (extent-1))
264
1.66k
                token[i++]=(*p);
265
1.66k
              p++;
266
1.66k
            }
267
1.05M
          break;
268
1.05M
        }
269
3.21M
      if ((*p != '\0') && (isalpha((int) ((unsigned char) *p)) == 0) &&
270
2.11M
          (*p != *DirectorySeparator) && (*p != '#') && (*p != '<'))
271
2.03M
        {
272
2.03M
          if (i < (ssize_t) (extent-1))
273
2.03M
            token[i++]=(*p);
274
2.03M
          p++;
275
2.03M
          break;
276
2.03M
        }
277
11.9M
      for ( ; *p != '\0'; p++)
278
11.8M
      {
279
11.8M
        if (((isspace((int) ((unsigned char) *p)) != 0) || (*p == '=') ||
280
10.8M
            (*p == ',') || (*p == ':') || (*p == ';')) && (*(p-1) != '\\'))
281
1.13M
          break;
282
10.7M
        if ((i > 0) && (*p == '<'))
283
14.4k
          break;
284
10.7M
        if (i < (ssize_t) (extent-1))
285
10.7M
          token[i++]=(*p);
286
10.7M
        if (*p == '>')
287
11.3k
          break;
288
10.7M
        if (*p == '(')
289
15.5k
          {
290
41.7M
            for (p++; *p != '\0'; p++)
291
41.7M
            {
292
41.7M
              if (i < (ssize_t) (extent-1))
293
41.7M
                token[i++]=(*p);
294
41.7M
              if ((*p == ')') && (*(p-1) != '\\'))
295
10.9k
                break;
296
41.6M
              if ((size_t) (p-start) >= (extent-1))
297
128
                break;
298
41.6M
            }
299
15.5k
            if (*p == '\0')
300
4.43k
              break;
301
15.5k
          }
302
10.7M
        if ((size_t) (p-start) >= (extent-1))
303
400
          break;
304
10.7M
      }
305
1.18M
      break;
306
3.21M
    }
307
4.73M
  }
308
4.73M
  token[i]='\0';
309
4.73M
  if (LocaleNCompare(token,"url(#",5) == 0)
310
904
    {
311
904
      q=strrchr(token,')');
312
904
      if (q != (char *) NULL)
313
487
        {
314
487
          *q='\0';
315
487
          (void) memmove(token,token+5,(size_t) (q-token-4));
316
487
        }
317
904
    }
318
7.54M
  while (isspace((int) ((unsigned char) *p)) != 0)
319
2.80M
    p++;
320
4.73M
  if (end != (const char **) NULL)
321
4.61M
    *end=(const char *) p;
322
4.73M
  return((size_t) (p-start+1));
323
4.73M
}
324

325
/*
326
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327
%                                                                             %
328
%                                                                             %
329
%                                                                             %
330
%   G l o b E x p r e s s i o n                                               %
331
%                                                                             %
332
%                                                                             %
333
%                                                                             %
334
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335
%
336
%  GlobExpression() returns MagickTrue if the expression matches the pattern.
337
%
338
%  The format of the GlobExpression function is:
339
%
340
%      MagickBooleanType GlobExpression(const char *magick_restrict expression,
341
%        const char *magick_restrict pattern,
342
%        const MagickBooleanType case_insensitive)
343
%
344
%  A description of each parameter follows:
345
%
346
%    o expression: Specifies a pointer to a text string containing a file name.
347
%
348
%    o pattern: Specifies a pointer to a text string containing a pattern.
349
%
350
%    o case_insensitive: set to MagickTrue to ignore the case when matching
351
%      an expression.
352
%
353
*/
354
355
static MagickBooleanType GlobExpression_(const char *magick_restrict expression,
356
  const char *magick_restrict pattern,const MagickBooleanType case_insensitive,
357
  const size_t depth)
358
89.9k
{
359
89.9k
  if (depth >= MagickMaxRecursionDepth)
360
0
    {
361
0
      errno=EOVERFLOW;
362
0
      return(MagickFalse);
363
0
    }
364
  /*
365
    Empty pattern or single '*' always matches.
366
  */
367
89.9k
  if (pattern == (const char *) NULL)
368
0
    return(MagickTrue);
369
89.9k
  if (GetUTFCode(pattern) == 0)
370
113
    return(MagickTrue);
371
89.8k
  if ((GetUTFCode(pattern) == '*') &&
372
9.70k
      (GetUTFCode(pattern+GetUTFOctets(pattern)) == 0))
373
41
    return(MagickTrue);
374
89.7k
  if ((strchr(pattern,'{') == NULL) &&
375
66.8k
      (strchr(pattern,'*') == NULL) &&
376
44.3k
      (strchr(pattern,'?') == NULL))
377
43.4k
    {
378
43.4k
      char
379
43.4k
        path[MagickPathExtent]= { 0 };
380
381
      /*
382
        If no glob characters exist, ensure no subimage specifier.
383
      */
384
43.4k
      GetPathComponent(pattern,SubimagePath,path);
385
43.4k
      if (*path != '\0')
386
12.1k
        return(MagickFalse);
387
43.4k
    }
388
91.6k
  while (GetUTFCode(pattern) != 0)
389
90.8k
  {
390
90.8k
    int
391
90.8k
      ecode = GetUTFCode(expression),
392
90.8k
      pcode = GetUTFCode(pattern);
393
394
90.8k
    if ((ecode == 0) && (pcode != '*') && (pcode != '{'))
395
14.8k
      break;
396
75.9k
    switch (pcode)
397
75.9k
    {
398
20.6k
      case '*':
399
20.6k
      {
400
20.6k
        do
401
22.8k
        {
402
          /*
403
            Skip consecutive '*'.
404
          */
405
22.8k
          pattern+=GetUTFOctets(pattern);
406
22.8k
        }
407
22.8k
        while (GetUTFCode(pattern) == '*');
408
75.7k
        while (1)
409
75.7k
        {
410
          /*
411
            Try to match at each position.
412
          */
413
75.7k
          if (GlobExpression_(expression,pattern,case_insensitive,depth+1) != MagickFalse)
414
367
            {
415
              /*
416
                Consume rest of expression and pattern.
417
              */
418
2.32k
              while (GetUTFCode(expression) != 0)
419
1.95k
                expression+=GetUTFOctets(expression);
420
5.53k
              while (GetUTFCode(pattern) != 0)
421
5.16k
                pattern+=GetUTFOctets(pattern);
422
367
              return(MagickTrue);
423
367
            }
424
75.3k
            if (GetUTFCode(expression) == 0)
425
20.3k
              break;
426
55.0k
            expression+=GetUTFOctets(expression);
427
55.0k
          }
428
20.3k
        return(MagickFalse);
429
20.6k
      }
430
12.0k
      case '?':
431
12.0k
      {
432
12.0k
        if (ecode == 0)
433
0
          return(MagickFalse);
434
12.0k
        pattern+=GetUTFOctets(pattern);
435
12.0k
        expression+=GetUTFOctets(expression);
436
12.0k
        break;
437
12.0k
      }
438
9.83k
      case '[':
439
9.83k
      {
440
9.83k
        const char
441
9.83k
          *p = pattern+GetUTFOctets(pattern),
442
9.83k
          *q = pattern+GetUTFOctets(pattern);
443
444
9.83k
        MagickBooleanType
445
9.83k
          matched = MagickFalse;
446
447
9.83k
        if (ecode == 0)
448
0
          return(MagickFalse);
449
51.9k
        while ((GetUTFCode(q) != 0) && (GetUTFCode(q) != ']'))
450
42.0k
          q+=GetUTFOctets(q);
451
9.83k
        if (GetUTFCode(q) == 0)
452
374
          return(MagickFalse);  /* malformed */
453
42.5k
        while (p < q)
454
33.0k
        {
455
33.0k
          const char
456
33.0k
            *next;
457
458
33.0k
          int
459
33.0k
            code = GetUTFCode(p);
460
461
33.0k
          size_t
462
33.0k
            octets = GetUTFOctets(p);
463
464
33.0k
          if (code == '\\')
465
1.38k
            {
466
1.38k
              p+=octets;
467
1.38k
              code=GetUTFCode(p);
468
1.38k
              octets=GetUTFOctets(p);
469
1.38k
            }
470
33.0k
          next=p+octets;
471
33.0k
          if ((next < q) && (GetUTFCode(next) == '-'))
472
3.50k
            {
473
3.50k
              int
474
3.50k
                ncode;
475
476
3.50k
              next+=GetUTFOctets(next);
477
3.50k
              ncode=GetUTFCode(next);
478
3.50k
              if (ncode == '\\')
479
1.10k
                {
480
1.10k
                  next+=GetUTFOctets(next);
481
1.10k
                  ncode=GetUTFCode(next);
482
1.10k
                }
483
3.50k
              if ((ecode >= code) && (ecode <= ncode))
484
542
                matched=MagickTrue;
485
3.50k
              p=next+GetUTFOctets(next);
486
3.50k
            }
487
29.5k
          else
488
29.5k
            {
489
29.5k
              if (ecode == code)
490
592
                matched=MagickTrue;
491
29.5k
              p+=octets;
492
29.5k
            }
493
33.0k
        }
494
        /*
495
          Skip consecutive '*'.
496
        */
497
9.46k
        if (matched == MagickFalse)
498
8.37k
          return(MagickFalse);
499
1.09k
        pattern=q+GetUTFOctets(q);  /* skip ']' */
500
1.09k
        expression+=GetUTFOctets(expression);
501
1.09k
        break;
502
9.46k
      }
503
15.4k
      case '{':
504
15.4k
      {
505
15.4k
        char
506
15.4k
          *a,
507
15.4k
          *alternative;
508
509
15.4k
        const char
510
15.4k
          *p,
511
15.4k
          *q;
512
513
15.4k
        size_t
514
15.4k
          remaining = MagickPathExtent;
515
516
15.4k
        pattern+=GetUTFOctets(pattern);  /* Skip '{' */
517
15.4k
        if (GetUTFCode(pattern) == 0)
518
722
          return(MagickFalse);
519
        /*
520
          End of brace expression: append remaining pattern.
521
        */
522
14.7k
        p=pattern;
523
53.3k
        while ((GetUTFCode(p) != 0) && (GetUTFCode(p) != '}'))
524
38.9k
        {
525
38.9k
#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__)
526
38.9k
          if (GetUTFCode(p) == '\\')
527
2.74k
            {
528
2.74k
              p+=GetUTFOctets(p);
529
2.74k
              if (GetUTFCode(p) == 0)
530
317
                break;
531
2.74k
            }
532
38.6k
#endif
533
38.6k
          p+=GetUTFOctets(p);
534
38.6k
        }
535
14.7k
        if (GetUTFCode(p) != '}')
536
3.11k
          return(MagickFalse);  /* malformed */
537
11.6k
        q=p+GetUTFOctets(p);
538
11.6k
        alternative=AcquireString(pattern);
539
11.6k
        a=alternative;
540
39.6k
        while (1)
541
39.6k
        {
542
39.6k
          int
543
39.6k
            code = GetUTFCode(pattern);
544
545
39.6k
          size_t
546
39.6k
            octets;
547
548
39.6k
          if ((code == 0) || (code == ',') || (code == '}'))
549
12.9k
            {
550
12.9k
              char
551
12.9k
                *subpattern;
552
553
12.9k
              MagickBooleanType
554
12.9k
                match;
555
556
              /*
557
                Try alternative as a full sub-pattern.
558
              */
559
12.9k
              *a='\0';
560
12.9k
              subpattern=AcquireString(alternative);
561
12.9k
              if (ConcatenateString(&subpattern,q) == MagickFalse)
562
0
                {
563
0
                  subpattern=DestroyString(subpattern);
564
0
                  alternative=DestroyString(alternative);
565
0
                  return(MagickFalse);
566
0
                }
567
12.9k
              match=GlobExpression_(expression,subpattern,case_insensitive,
568
12.9k
                depth+1);
569
12.9k
              subpattern=DestroyString(subpattern);
570
12.9k
              if (match != MagickFalse)
571
217
                {
572
                  /*
573
                    Consume rest of expression and pattern.
574
                  */
575
1.90k
                  while (GetUTFCode(expression) != 0)
576
1.68k
                    expression+=GetUTFOctets(expression);
577
217
                  pattern=q;
578
2.71k
                  while (GetUTFCode(pattern) != 0)
579
2.50k
                    pattern+=GetUTFOctets(pattern);
580
217
                  alternative=DestroyString(alternative);
581
217
                  return(MagickTrue);
582
217
                }
583
              /*
584
                Reset buffer for next alternative.
585
              */
586
12.7k
              a=alternative;
587
12.7k
              remaining=MagickPathExtent;
588
12.7k
              if (code == ',')
589
1.27k
                {
590
1.27k
                  pattern+=GetUTFOctets(pattern);  /* skip ',' */
591
1.27k
                  continue;
592
1.27k
                }
593
11.4k
              break;  /* '}' or end */
594
12.7k
            }
595
          /*
596
            Copy UTF-8 sequence into alternative.
597
          */
598
26.7k
          octets=GetUTFOctets(pattern);
599
26.7k
          if ((octets == 0) || (octets >= remaining))
600
0
            break;
601
26.7k
          (void) memcpy(a,pattern,octets);
602
26.7k
          a+=octets;
603
26.7k
          remaining-=octets;
604
26.7k
          pattern+=octets;
605
26.7k
        }
606
11.4k
        alternative=DestroyString(alternative);
607
11.4k
        return(MagickFalse);
608
11.6k
      }
609
0
#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__)
610
1.74k
      case '\\':
611
1.74k
      {
612
1.74k
        pattern+=GetUTFOctets(pattern);
613
1.74k
        if (GetUTFCode(pattern) == 0)
614
286
          return(MagickFalse);
615
1.45k
        magick_fallthrough;
616
1.45k
      }
617
0
#endif
618
17.6k
      default:
619
17.6k
      {
620
17.6k
        int
621
17.6k
          ec = ecode,
622
17.6k
          pc = pcode;
623
624
17.6k
        if (ecode == 0)
625
0
          return(MagickFalse);
626
17.6k
        if (case_insensitive != MagickFalse)
627
17.6k
          {
628
17.6k
            pc=LocaleToLowercase(pc);
629
17.6k
            ec=LocaleToLowercase(ec);
630
17.6k
          }
631
17.6k
        if (pc != ec)
632
16.7k
          return(MagickFalse);
633
836
        pattern+=GetUTFOctets(pattern);
634
836
        expression+=GetUTFOctets(expression);
635
836
        break;
636
17.6k
      }
637
75.9k
    }
638
75.9k
  }
639
15.6k
  while (GetUTFCode(pattern) == '*')
640
0
    pattern+=GetUTFOctets(pattern);
641
15.6k
  return(((GetUTFCode(expression) == 0) &&
642
15.5k
          (GetUTFCode(pattern) == 0)) ? MagickTrue : MagickFalse);
643
77.6k
}
644
645
MagickExport MagickBooleanType GlobExpression(
646
  const char *magick_restrict expression,const char *magick_restrict pattern,
647
  const MagickBooleanType case_insensitive)
648
1.29k
{
649
1.29k
  return(GlobExpression_(expression,pattern,case_insensitive,0));
650
1.29k
}
651

652
/*
653
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654
%                                                                             %
655
%                                                                             %
656
%                                                                             %
657
+     I s G l o b                                                             %
658
%                                                                             %
659
%                                                                             %
660
%                                                                             %
661
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662
%
663
%  IsGlob() returns MagickTrue if the path specification contains a globbing
664
%  pattern.
665
%
666
%  The format of the IsGlob method is:
667
%
668
%      MagickBooleanType IsGlob(const char *geometry)
669
%
670
%  A description of each parameter follows:
671
%
672
%    o path: the path.
673
%
674
*/
675
MagickPrivate MagickBooleanType IsGlob(const char *path)
676
31.1k
{
677
31.1k
  MagickBooleanType
678
31.1k
    status = MagickFalse;
679
680
31.1k
  const char
681
31.1k
    *p;
682
683
31.1k
  if (IsPathAccessible(path) != MagickFalse)
684
35
    return(MagickFalse);
685
1.08M
  for (p=path; *p != '\0'; p++)
686
1.05M
  {
687
1.05M
    switch (*p)
688
1.05M
    {
689
3.30k
      case '*':
690
5.59k
      case '?':
691
7.98k
      case '{':
692
10.3k
      case '}':
693
14.7k
      case '[':
694
20.3k
      case ']':
695
20.3k
      {
696
20.3k
        status=MagickTrue;
697
20.3k
        break;
698
14.7k
      }
699
1.03M
      default:
700
1.03M
        break;
701
1.05M
    }
702
1.05M
  }
703
31.0k
  return(status);
704
31.0k
}
705

706
/*
707
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
708
%                                                                             %
709
%                                                                             %
710
%                                                                             %
711
%   T o k e n i z e r                                                         %
712
%                                                                             %
713
%                                                                             %
714
%                                                                             %
715
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716
%
717
%  Tokenizer() is a generalized, finite state token parser.  It extracts tokens
718
%  one at a time from a string of characters.  The characters used for white
719
%  space, for break characters, and for quotes can be specified.  Also,
720
%  characters in the string can be preceded by a specifiable escape character
721
%  which removes any special meaning the character may have.
722
%
723
%  Here is some terminology:
724
%
725
%    o token: A single unit of information in the form of a group of
726
%      characters.
727
%
728
%    o white space: Apace that gets ignored (except within quotes or when
729
%      escaped), like blanks and tabs. in addition, white space terminates a
730
%      non-quoted token.
731
%
732
%    o break set: One or more characters that separates non-quoted tokens.
733
%      Commas are a common break character. The usage of break characters to
734
%      signal the end of a token is the same as that of white space, except
735
%      multiple break characters with nothing or only white space between
736
%      generate a null token for each two break characters together.
737
%
738
%      For example, if blank is set to be the white space and comma is set to
739
%      be the break character, the line
740
%
741
%        A, B, C ,  , DEF
742
%
743
%        ... consists of 5 tokens:
744
%
745
%        1)  "A"
746
%        2)  "B"
747
%        3)  "C"
748
%        4)  "" (the null string)
749
%        5)  "DEF"
750
%
751
%    o Quote character: A character that, when surrounding a group of other
752
%      characters, causes the group of characters to be treated as a single
753
%      token, no matter how many white spaces or break characters exist in
754
%      the group. Also, a token always terminates after the closing quote.
755
%      For example, if ' is the quote character, blank is white space, and
756
%      comma is the break character, the following string
757
%
758
%        A, ' B, CD'EF GHI
759
%
760
%        ... consists of 4 tokens:
761
%
762
%        1)  "A"
763
%        2)  " B, CD" (note the blanks & comma)
764
%        3)  "EF"
765
%        4)  "GHI"
766
%
767
%      The quote characters themselves do not appear in the resultant
768
%      tokens.  The double quotes are delimiters i use here for
769
%      documentation purposes only.
770
%
771
%    o Escape character: A character which itself is ignored but which
772
%      causes the next character to be used as is.  ^ and \ are often used
773
%      as escape characters. An escape in the last position of the string
774
%      gets treated as a "normal" (i.e., non-quote, non-white, non-break,
775
%      and non-escape) character. For example, assume white space, break
776
%      character, and quote are the same as in the above examples, and
777
%      further, assume that ^ is the escape character. Then, in the string
778
%
779
%        ABC, ' DEF ^' GH' I ^ J K^ L ^
780
%
781
%        ... there are 7 tokens:
782
%
783
%        1)  "ABC"
784
%        2)  " DEF ' GH"
785
%        3)  "I"
786
%        4)  " "     (a lone blank)
787
%        5)  "J"
788
%        6)  "K L"
789
%        7)  "^"     (passed as is at end of line)
790
%
791
%  The format of the Tokenizer method is:
792
%
793
%      int Tokenizer(TokenInfo *token_info,const unsigned flag,char *token,
794
%        const size_t max_token_length,const char *line,const char *white,
795
%        const char *break_set,const char *quote,const char escape,
796
%        char *breaker,int *next,char *quoted)
797
%
798
%  A description of each parameter follows:
799
%
800
%    o flag: right now, only the low order 3 bits are used.
801
%
802
%        1 => convert non-quoted tokens to upper case
803
%        2 => convert non-quoted tokens to lower case
804
%        0 => do not convert non-quoted tokens
805
%
806
%    o token: a character string containing the returned next token
807
%
808
%    o max_token_length: the maximum size of "token".  Characters beyond
809
%      "max_token_length" are truncated.
810
%
811
%    o string: the string to be parsed.
812
%
813
%    o white: a string of the valid white spaces.  example:
814
%
815
%        char whitesp[]={" \t"};
816
%
817
%      blank and tab will be valid white space.
818
%
819
%    o break: a string of the valid break characters. example:
820
%
821
%        char breakch[]={";,"};
822
%
823
%      semicolon and comma will be valid break characters.
824
%
825
%    o quote: a string of the valid quote characters. An example would be
826
%
827
%        char whitesp[]={"'\"");
828
%
829
%      (this causes single and double quotes to be valid) Note that a
830
%      token starting with one of these characters needs the same quote
831
%      character to terminate it.
832
%
833
%      for example:
834
%
835
%        "ABC '
836
%
837
%      is unterminated, but
838
%
839
%        "DEF" and 'GHI'
840
%
841
%      are properly terminated.  Note that different quote characters
842
%      can appear on the same line; only for a given token do the quote
843
%      characters have to be the same.
844
%
845
%    o escape: the escape character (NOT a string ... only one
846
%      allowed). Use zero if none is desired.
847
%
848
%    o breaker: the break character used to terminate the current
849
%      token.  If the token was quoted, this will be the quote used.  If
850
%      the token is the last one on the line, this will be zero.
851
%
852
%    o next: this variable points to the first character of the
853
%      next token.  it gets reset by "tokenizer" as it steps through the
854
%      string.  Set it to 0 upon initialization, and leave it alone
855
%      after that.  You can change it if you want to jump around in the
856
%      string or re-parse from the beginning, but be careful.
857
%
858
%    o quoted: set to True if the token was quoted and MagickFalse
859
%      if not.  You may need this information (for example:  in C, a
860
%      string with quotes around it is a character string, while one
861
%      without is an identifier).
862
%
863
%    o result: 0 if we haven't reached EOS (end of string), and 1
864
%      if we have.
865
%
866
*/
867
868
103k
#define IN_WHITE 0
869
170k
#define IN_TOKEN 1
870
348k
#define IN_QUOTE 2
871
39.3k
#define IN_OZONE 3
872
873
static ssize_t sindex(int c,const char *string)
874
613k
{
875
613k
  const char
876
613k
    *p;
877
878
927k
  for (p=string; *p != '\0'; p++)
879
363k
    if (c == (int) (*p))
880
49.3k
      return((ssize_t) (p-string));
881
564k
  return(-1);
882
613k
}
883
884
static void StoreToken(TokenInfo *token_info,char *string,
885
  size_t max_token_length,int c)
886
178k
{
887
178k
  ssize_t
888
178k
    i;
889
890
178k
  if ((token_info->offset < 0) ||
891
178k
      ((size_t) token_info->offset >= (max_token_length-1)))
892
0
    return;
893
178k
  i=token_info->offset++;
894
178k
  string[i]=(char) c;
895
178k
  if (token_info->state == IN_QUOTE)
896
34.1k
    return;
897
144k
  switch (token_info->flag & 0x03)
898
144k
  {
899
0
    case 1:
900
0
    {
901
0
      string[i]=(char) LocaleToUppercase(c);
902
0
      break;
903
0
    }
904
0
    case 2:
905
0
    {
906
0
      string[i]=(char) LocaleToLowercase(c);
907
0
      break;
908
0
    }
909
144k
    default:
910
144k
      break;
911
144k
  }
912
144k
}
913
914
MagickExport int Tokenizer(TokenInfo *token_info,const unsigned flag,
915
  char *token,const size_t max_token_length,const char *line,const char *white,
916
  const char *break_set,const char *quote,const char escape,char *breaker,
917
  int *next,char *quoted)
918
79.0k
{
919
79.0k
  int
920
79.0k
    c;
921
922
79.0k
  ssize_t
923
79.0k
    i;
924
925
79.0k
  *breaker='\0';
926
79.0k
  *quoted='\0';
927
79.0k
  if (line[*next] == '\0')
928
27.3k
    return(1);
929
51.7k
  token_info->state=IN_WHITE;
930
51.7k
  token_info->quote=(char) MagickFalse;
931
51.7k
  token_info->flag=flag;
932
248k
  for (token_info->offset=0; (int) line[*next] != 0; (*next)++)
933
229k
  {
934
229k
    c=(int) line[*next];
935
229k
    i=sindex(c,break_set);
936
229k
    if (i >= 0)
937
24.3k
      {
938
24.3k
        switch (token_info->state)
939
24.3k
        {
940
13.4k
          case IN_WHITE:
941
22.7k
          case IN_TOKEN:
942
22.9k
          case IN_OZONE:
943
22.9k
          {
944
22.9k
            (*next)++;
945
22.9k
            *breaker=break_set[i];
946
22.9k
            token[token_info->offset]='\0';
947
22.9k
            return(0);
948
22.7k
          }
949
1.39k
          case IN_QUOTE:
950
1.39k
          {
951
1.39k
            StoreToken(token_info,token,max_token_length,c);
952
1.39k
            break;
953
22.7k
          }
954
24.3k
        }
955
1.39k
        continue;
956
24.3k
      }
957
204k
    i=sindex(c,quote);
958
204k
    if (i >= 0)
959
24.9k
      {
960
24.9k
        switch (token_info->state)
961
24.9k
        {
962
11.4k
          case IN_WHITE:
963
11.4k
          {
964
11.4k
            token_info->state=IN_QUOTE;
965
11.4k
            token_info->quote=quote[i];
966
11.4k
            *quoted=(char) MagickTrue;
967
11.4k
            break;
968
0
          }
969
7.13k
          case IN_QUOTE:
970
7.13k
          {
971
7.13k
            if (quote[i] != token_info->quote)
972
0
              StoreToken(token_info,token,max_token_length,c);
973
7.13k
            else
974
7.13k
              {
975
7.13k
                token_info->state=IN_OZONE;
976
7.13k
                token_info->quote='\0';
977
7.13k
              }
978
7.13k
            break;
979
0
          }
980
3.80k
          case IN_TOKEN:
981
6.40k
          case IN_OZONE:
982
6.40k
          {
983
6.40k
            *breaker=(char) c;
984
6.40k
            token[token_info->offset]='\0';
985
6.40k
            return(0);
986
3.80k
          }
987
24.9k
        }
988
18.5k
        continue;
989
24.9k
      }
990
179k
    i=sindex(c,white);
991
179k
    if (i >= 0)
992
0
      {
993
0
        switch (token_info->state)
994
0
        {
995
0
          case IN_WHITE:
996
0
          case IN_OZONE:
997
0
            break;
998
0
          case IN_TOKEN:
999
0
          {
1000
0
            token_info->state=IN_OZONE;
1001
0
            break;
1002
0
          }
1003
0
          case IN_QUOTE:
1004
0
          {
1005
0
            StoreToken(token_info,token,max_token_length,c);
1006
0
            break;
1007
0
          }
1008
0
        }
1009
0
        continue;
1010
0
      }
1011
179k
    if (c == (int) escape)
1012
0
      {
1013
0
        if (line[(*next)+1] == '\0')
1014
0
          {
1015
0
            *breaker='\0';
1016
0
            StoreToken(token_info,token,max_token_length,c);
1017
0
            (*next)++;
1018
0
            token[token_info->offset]='\0';
1019
0
            return(0);
1020
0
          }
1021
0
        switch (token_info->state)
1022
0
        {
1023
0
          case IN_WHITE:
1024
0
          {
1025
0
            (*next)--;
1026
0
            token_info->state=IN_TOKEN;
1027
0
            break;
1028
0
          }
1029
0
          case IN_TOKEN:
1030
0
          case IN_QUOTE:
1031
0
          {
1032
0
            (*next)++;
1033
0
            c=(int) line[*next];
1034
0
            StoreToken(token_info,token,max_token_length,c);
1035
0
            break;
1036
0
          }
1037
0
          case IN_OZONE:
1038
0
          {
1039
0
            token[token_info->offset]='\0';
1040
0
            return(0);
1041
0
          }
1042
0
        }
1043
0
        continue;
1044
0
      }
1045
179k
    switch (token_info->state)
1046
179k
    {
1047
26.8k
      case IN_WHITE:
1048
26.8k
      {
1049
26.8k
        token_info->state=IN_TOKEN;
1050
26.8k
        StoreToken(token_info,token,max_token_length,c);
1051
26.8k
        break;
1052
0
      }
1053
117k
      case IN_TOKEN:
1054
150k
      case IN_QUOTE:
1055
150k
      {
1056
150k
        StoreToken(token_info,token,max_token_length,c);
1057
150k
        break;
1058
117k
      }
1059
2.92k
      case IN_OZONE:
1060
2.92k
      {
1061
2.92k
        token[token_info->offset]='\0';
1062
2.92k
        return(0);
1063
117k
      }
1064
179k
    }
1065
179k
  }
1066
19.5k
  token[token_info->offset]='\0';
1067
19.5k
  return(0);
1068
51.7k
}