Detabbed
[AROS.git] / rom / dos / patternmatching.c
blob6d7937cb2a323230352540ba6e8f6355e275a389
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Pattern matching and parsing functionality
6 Lang: English
7 */
9 #include <exec/memory.h>
10 #include <proto/exec.h>
11 #include <proto/utility.h>
12 #include <proto/dos.h>
13 #include <dos/dosextens.h>
14 #include <dos/dosasl.h>
16 #include "dos_intern.h"
19 A simple method for pattern matching with multiple wildcards:
20 I use markers that consist of both a pointer into the string
21 and one into the pattern. The marker simply follows the string
22 and everytime it hits a wildcard it's split into two new markers
23 (one to assume that the wildcard match has ended and one to assume
24 that it continues). If a marker doesn't fit any longer it's
25 removed and if all of them are gone the pattern mismatches.
26 OTOH if any of the markers reaches the end of both the string
27 and the pattern simultaneously the pattern matches the string.
31 * INPUTS
33 * pat -- Pattern string (as returned by ParsePatternXXXX())
34 * str -- The string to match against the pattern 'pat'
35 * case -- Determines if the case is important or not
36 * DOSBase -- dos.library base
41 BOOL patternMatch(CONST_STRPTR pat, CONST_STRPTR str, BOOL useCase,
42 struct DosLibrary *DOSBase)
44 CONST_STRPTR s;
45 BOOL match = FALSE;
47 struct markerarray ma;
48 struct markerarray *macur = &ma;
49 struct markerarray *cur2;
51 LONG macnt = 0;
52 LONG cnt2;
54 ULONG level;
55 UBYTE a, b, c, t;
57 LONG error;
59 #undef ERROR
60 #define ERROR(a) { error = (a); goto end; }
62 ma.next = ma.prev = NULL;
64 while(TRUE)
66 switch(*pat)
68 case P_REPBEG: /* _#(_a), _#a_ or _#[_a] */
69 PUSH(0, ++pat, str);
70 level = 1;
72 while(TRUE)
74 c = *pat++;
76 if(c == P_REPBEG)
78 level++;
80 else if(c == P_REPEND)
82 if(!--level)
84 break;
88 break;
90 case P_REPEND: /* #(a_)_ */
91 level = 1;
93 while(TRUE)
95 c = *--pat;
97 if(c == P_REPEND)
99 level++;
101 else if(c == P_REPBEG)
103 if(!--level)
105 break;
109 break;
111 case P_NOT: /* _~(_a) */
112 s = ++pat;
113 level = 1;
115 while(TRUE)
117 c = *s++;
119 if(c == P_NOT)
121 level++;
123 else if(c == P_NOTEND)
125 if(!--level)
127 break;
132 PUSH(1, s, str);
133 break;
135 case P_NOTEND: /* ~(a_)_ */
136 cnt2 = macnt;
137 cur2 = macur;
141 cnt2--;
143 if(cnt2 < 0)
145 cnt2 = 127;
146 cur2 = cur2->prev;
148 }while(!cur2->marker[cnt2].type);
150 if(!*str++)
152 macnt = cnt2;
153 macur=cur2;
155 else if(str > cur2->marker[cnt2].str)
157 cur2->marker[cnt2].str = str;
160 POP(t, pat, str);
162 if(t && *str)
164 PUSH(1, pat, str + 1);
167 break;
169 case P_ORSTART: /* ( */
170 s = ++pat;
171 level = 1;
173 while(TRUE)
175 c = *s++;
177 if(c == P_ORSTART)
179 level++;
181 else if(c == P_ORNEXT)
183 if(level == 1)
185 PUSH(0, s, str);
188 else if(c == P_OREND)
190 if(!--level)
192 break;
197 break;
199 case P_ORNEXT: /* | */
200 pat++;
201 level = 1;
203 while(TRUE)
205 c = *pat++;
207 if(c == P_ORSTART)
209 level++;
211 else if(c == P_OREND)
213 if(!--level)
215 break;
220 break;
222 case P_OREND: /* ) */
223 pat++;
224 break;
226 case P_SINGLE: /* ? */
227 pat++;
229 if(*str)
231 str++;
233 else
235 POP(t, pat, str);
237 if(t && *str)
239 PUSH(1, pat, str + 1);
243 break;
245 case P_CLASS: /* [ */
246 pat++;
248 while(TRUE)
250 a = b = *pat++;
252 if(a == P_CLASS)
254 POP(t, pat, str);
256 if(t && *str)
258 PUSH(1, pat, str + 1);
261 break;
264 if(*pat == '-')
266 b = *++pat;
268 if(b == P_CLASS)
270 b = 255;
274 if(useCase)
276 c = *str;
278 else
280 c = ToUpper(*str);
283 if(c >= a && c <= b)
285 str++;
287 while(*pat++ != P_CLASS)
290 break;
294 break;
296 case P_NOTCLASS: /* [~ */
297 if(!*str)
299 POP(t, pat, str);
301 if(t && *str)
303 PUSH(1, pat, str + 1);
306 break;
309 pat++;
311 while(TRUE)
313 a = b = *pat++;
315 if(a == P_CLASS)
317 str++;
318 break;
321 if(*pat == '-')
323 b = *++pat;
325 if(b == P_CLASS)
327 b = 255;
331 if(useCase)
333 c = *str;
335 else
337 c = ToUpper(*str);
340 if(c >= a && c <= b)
342 POP(t, pat, str);
344 if(t && *str)
346 PUSH(1, pat, str + 1);
349 break;
353 break;
355 case P_ANY: /* #? */
356 /* This often used pattern has extra treatment to be faster */
357 if(*str)
359 PUSH(0, pat, str + 1);
362 pat++;
363 break;
365 case 0:
366 if(!*str)
368 match = TRUE;
369 ERROR(0);
371 else
373 POP(t, pat, str);
375 if(t && *str)
377 PUSH(1, pat, str + 1);
380 break;
382 default:
384 UBYTE ch;
386 if(useCase)
388 ch = *str;
390 else
392 ch = ToUpper(*str);
395 if(*pat++ == ch)
397 str++;
399 else
401 POP(t, pat, str);
403 if(t && *str)
405 PUSH(1, pat, str + 1);
410 break;
412 } /* switch(*pat) */
413 } /* while(TRUE) */
415 end:
417 macur = ma.next;
419 while(macur != NULL)
421 struct markerarray *next = macur->next;
423 FreeMem(macur, sizeof(struct markerarray));
424 macur = next;
427 SetIoErr(error);
429 return match;
433 LONG patternParse(CONST_STRPTR Source, STRPTR Dest, LONG DestLength,
434 BOOL useCase, struct DosLibrary *DOSBase)
436 STRPTR stack;
437 STRPTR end;
438 UBYTE a;
439 LONG iswild = 0;
441 #undef ERROR
442 #define ERROR(a) { SetIoErr(a); return -1; }
443 stack = end = Dest + DestLength;
444 #define PUT(a) { if(Dest >= stack) ERROR(ERROR_BUFFER_OVERFLOW); *Dest++ = (a); }
446 if(!*Source)
448 PUT(0);
449 return 0;
452 while(*Source)
454 switch(*Source++)
456 case '#':
457 iswild = 1;
459 switch(*Source)
461 case '?':
462 Source++;
463 PUT(P_ANY);
464 break;
466 case ')':
467 case '\0':
468 ERROR(ERROR_BAD_TEMPLATE);
469 break;
471 default:
472 PUT(P_REPBEG);
473 *--stack = P_REPEND;
474 continue;
477 break;
479 case '~':
480 switch(*Source)
482 case '\0':
483 a = Source[-1];
484 PUT(useCase ? a : ToUpper(a));
485 break;
487 case ')':
488 ERROR(ERROR_BAD_TEMPLATE);
489 break;
491 default:
492 iswild = 1;
493 PUT(P_NOT);
494 *--stack = P_NOTEND;
495 continue;
498 break;
500 case '?':
501 iswild = 1;
502 PUT(P_SINGLE);
503 continue;
505 case '(':
506 PUT(P_ORSTART);
507 *--stack = P_OREND;
508 continue;
510 case '|':
511 iswild = 1;
513 if(stack == end)
515 ERROR(ERROR_BAD_TEMPLATE);
518 while(!(*stack == P_OREND || stack == end))
520 PUT(*stack++);
523 PUT(P_ORNEXT);
524 continue;
526 case ')':
527 while(!(stack == end || *stack == P_OREND))
529 PUT(*stack++);
532 if(stack == end)
534 ERROR(ERROR_BAD_TEMPLATE);
536 else
538 PUT(*stack++);
541 break;
543 case '[':
544 iswild = 1;
546 if(*Source == '~')
548 Source++;
549 PUT(P_NOTCLASS);
551 else
553 PUT(P_CLASS);
556 a = *Source++;
558 if(!a)
560 ERROR(ERROR_BAD_TEMPLATE);
565 if(a == '\'')
567 a=*Source++;
570 PUT(useCase ? a : ToUpper(a));
571 a = *Source++;
573 if(!a)
575 ERROR(ERROR_BAD_TEMPLATE);
577 } while(a != ']');
579 PUT(P_CLASS);
580 break;
582 case '*':
583 if (DOSBase->dl_Root->rn_Flags & RNF_WILDSTAR)
585 iswild = 1;
586 PUT(P_ANY);
588 else
590 PUT('*');
593 break;
595 case '%':
596 continue;
598 case '\'':
599 switch(*Source)
601 case '#':
602 case '*':
603 case '?':
604 case '(':
605 case '|':
606 case ')':
607 case '~':
608 case '[':
609 case ']':
610 case '%':
611 case '\'':
612 Source++;
613 default:
614 break;
617 /* Fall through */
618 default:
619 a = Source[-1];
620 PUT(useCase ? a : ToUpper(a));
621 break;
624 while(stack != end && *stack != P_OREND)
626 PUT(*stack++);
630 if(stack != end)
632 ERROR(ERROR_BAD_TEMPLATE);
635 PUT(0);
637 return iswild;