3 #include <dos/dosextens.h>
4 #include <dos/dosasl.h>
5 #include <proto/exec.h>
12 /****************************************************************************************/
14 #define MATCHFUNCS_NO_DUPLOCK 0
16 /****************************************************************************************/
18 #define COMPTYPE_NORMAL 1
19 #define COMPTYPE_PATTERN 2
20 #define COMPTYPE_UNKNOWN 3
22 /****************************************************************************************/
24 LONG
My_MatchNext(struct AnchorPath
*AP
);
26 /****************************************************************************************/
29 static void showacflags(struct AChain
*ac
)
31 BYTE flags
= ac
->an_Flags
;
35 if (flags
& DDF_PatternBit
)
37 flags
&= ~DDF_PatternBit
;
38 printf("DDF_PatternBit ");
41 if (flags
& DDF_ExaminedBit
)
43 flags
&= ~DDF_ExaminedBit
;
44 printf("DDF_ExaminedBit ");
47 if (flags
& DDF_Completed
)
49 flags
&= ~DDF_Completed
;
50 printf("DDF_Completed ");
53 if (flags
& DDF_AllBit
)
59 if (flags
& DDF_Single
)
62 printf("DDF_Single ");
67 printf("UNKNOWN = %8x ", flags
);
73 static void showaclist(struct AChain
*ac
)
77 printf("achain: address = %p flags = %x ", ac
, ac
->an_Flags
);
79 printf(" string=\"%s\"\n", ac
->an_String
);
85 /****************************************************************************************/
87 static void RemoveTrailingSlash(STRPTR s
)
93 if ((s
[len
- 1] == '/') &&
101 /****************************************************************************************/
103 static struct AChain
*Match_AllocAChain(LONG extrasize
, struct DosLibrary
*DOSBase
)
105 return AllocVec(sizeof(struct AChain
) + extrasize
, MEMF_PUBLIC
| MEMF_CLEAR
);
108 /****************************************************************************************/
110 static void Match_FreeAChain(struct AChain
*ac
, struct DosLibrary
*DOSBase
)
118 /*****************************************************************************************
120 The job of BuildAChainList is to split the pattern string passed to MatchFirst into
121 path components. Most imporant rules (as found out after hours of testing on Amiga):
123 - Each path component containing a pattern string is put into a single AChain
124 - If there are several successive path components *without* pattern then this
125 are merged into one single AChain.
126 - No matter what: the last path component always gets into its own single AChain.
128 Examples: [<???>] is one AChain
131 pictures/#? [pictures} [#?]
133 work:pictures [work:} [pictures]
134 work:pictures/#? [work:pictures] [#?]
135 work:pictures/aros [work:pictures] [aros]
136 work:pictures/aros/games [work:pictures/aros] [games]
137 work:#?/aros/games [work:] [#?] [aros] [games}
138 work:#?/#?/aros/games/quake [work:} [#?] [#?] [aros/games] [quake]
140 *****************************************************************************************/
142 static LONG
BuildAChainList(STRPTR pattern
, struct AnchorPath
*AP
,
143 struct AChain
**retac
, struct DosLibrary
*DOSBase
)
145 struct AChain
*baseac
= 0, *prevac
= 0, *ac
;
146 STRPTR patterncopy
= 0;
147 STRPTR patternstart
, patternend
, patternpos
;
149 WORD comptype
= COMPTYPE_UNKNOWN
;
156 len
= strlen(pattern
);
158 patterncopy
= AllocVec(len
+ 1, MEMF_PUBLIC
);
161 error
= ERROR_NO_FREE_STORE
;
165 strcpy(patterncopy
, pattern
);
167 RemoveTrailingSlash(patterncopy
);
169 patternstart
= patterncopy
;
171 patternpos
= strchr(patterncopy
, ':');
174 comptype
= COMPTYPE_UNKNOWN
;
175 patternpos
= patternstart
;
176 patternend
= patternstart
;
180 comptype
= COMPTYPE_NORMAL
;
181 patternend
= patternpos
++;
192 if (comptype
== COMPTYPE_UNKNOWN
)
194 comptype
= COMPTYPE_NORMAL
;
195 patternend
= patternpos
;
197 else if (comptype
== COMPTYPE_NORMAL
)
199 patternend
= patternpos
;
202 if (comptype
== COMPTYPE_PATTERN
)
204 patternend
= patternpos
;
210 if (comptype
== COMPTYPE_UNKNOWN
)
212 comptype
= COMPTYPE_NORMAL
;
213 patternend
= patternpos
;
216 if (comptype
== COMPTYPE_NORMAL
)
221 patternend
= patternpos
;
224 else if ((c
== '#') ||
235 if (comptype
== COMPTYPE_NORMAL
)
239 comptype
= COMPTYPE_PATTERN
;
246 len
= (LONG
)(patternend
- patternstart
+ 2);
247 if (comptype
== COMPTYPE_PATTERN
) len
= len
* 2 + 2;
249 ac
= Match_AllocAChain(len
, DOSBase
);
252 error
= ERROR_NO_FREE_STORE
;
256 if (comptype
== COMPTYPE_NORMAL
)
258 if (*patternend
== '\0')
260 strcpy(ac
->an_String
, patternstart
);
263 patternend
[1] = '\0';
264 strcpy(ac
->an_String
, patternstart
);
268 } /* if (comptype == COMPTYPE_NORMAL) */
271 if (*patternend
== '\0')
273 i
= ParsePatternNoCase(patternstart
, ac
->an_String
, len
);
278 patternend
[1] = '\0';
279 i
= ParsePatternNoCase(patternstart
, ac
->an_String
, len
);
285 error
= ERROR_BAD_TEMPLATE
;
286 Match_FreeAChain(ac
, DOSBase
);ac
= 0;
292 ac
->an_Flags
|= DDF_PatternBit
;
293 AP
->ap_Flags
|= APF_ITSWILD
;
296 } /* if (comptype == COMPTYPE_NORMAL) else ... */
298 RemoveTrailingSlash(ac
->an_String
);
306 prevac
->an_Child
= ac
;
307 ac
->an_Parent
= prevac
;
312 patternpos
= patternend
;
313 comptype
= COMPTYPE_UNKNOWN
;
314 patternstart
= patternend
= patternpos
+ 1;
317 } while (*patternpos
++ != '\0');
320 if (patterncopy
) FreeVec(patterncopy
);
324 #if MATCHFUNCS_NO_DUPLOCK
326 ** No DupLock() here, because then we would have to UnLock it in MatchEnd
327 ** and we would not know any valid lock to which we could CurrentDir after,
328 ** because we must make sure there is a valid CurrentDir after MatchEnd.
331 baseac
->an_Lock
= CurrentDir(0);
332 CurrentDir(baseac
->an_Lock
);
338 AP
->ap_Flags
|= APF_NOMEMERR
;
342 #define nextac prevac /* to not have to add another variable */
347 nextac
= ac
->an_Child
;
348 Match_FreeAChain(ac
, DOSBase
);
357 /****************************************************************************************/
359 static LONG
Match_MakeResult(struct AnchorPath
*AP
, struct DosLibrary
*DOSBase
)
363 AP
->ap_Info
= AP
->ap_Current
->an_Info
;
367 if (NameFromLock(AP
->ap_Current
->an_Lock
, AP
->ap_Buf
, AP
->ap_Strlen
))
369 if (!AddPart(AP
->ap_Buf
, AP
->ap_Current
->an_Info
.fib_FileName
, AP
->ap_Strlen
))
381 /****************************************************************************************/
383 LONG
My_MatchFirst(STRPTR pat
, struct AnchorPath
*AP
)
392 error
= BuildAChainList(pat
, AP
, &ac
, DOSBase
);
395 AP
->ap_Base
= AP
->ap_Current
= ac
;
397 error
= My_MatchNext(AP
);
399 } /* if (error == 0) */
401 printf("My_MatchFirst: returning %ld. Ac = %p\n", error
, ac
);
408 /****************************************************************************************/
410 LONG
My_MatchNext(struct AnchorPath
*AP
)
412 struct AChain
*ac
= AP
->ap_Current
;
413 BPTR origdir
, old_current_lock
;
416 origdir
= CurrentDir(0);
419 old_current_lock
= ac
->an_Lock
;
421 AP
->ap_Flags
&= ~APF_DIDDIR
;
424 ** Check if we are asked to enter a directory, but only do this
425 ** if it is really possible
428 if ((AP
->ap_Flags
& APF_DODIR
) &&
429 (ac
->an_Flags
& DDF_ExaminedBit
) &&
430 (ac
->an_Info
.fib_DirEntryType
> 0) &&
431 (ac
->an_Child
== NULL
))
434 ** Alloc a new AChain. Make it the active one. Set its string to "#?" and
435 ** mark it with DDF_AllBit Flag to indicate that this is a "APF_DODIR-AChain".
436 ** This is important for "back steppings", because "APF_DODIR-AChains" must
437 ** be removed and freed then and the user must be notified about the leaving
438 ** of a APF_DODIR-AChain with APF_DIDDIR.
441 if ((ac
->an_Child
= Match_AllocAChain(1, DOSBase
)))
443 ac
->an_Child
->an_Parent
= ac
;
447 ac
->an_String
[0] = P_ANY
;
448 ac
->an_String
[1] = 0;
449 ac
->an_Flags
= DDF_PatternBit
| DDF_AllBit
;
453 ** If the allocation did not work, we simple ignore APF_DODIR. Just like if
454 ** the user did not set this flag. Good idea or bad idea?
459 /* Main loop for AChain traversing */
463 BOOL must_go_back
= FALSE
;
465 /* Check for user breaks (CTRL_C, ...) */
467 if (AP
->ap_BreakBits
)
469 AP
->ap_FoundBreak
= CheckSignal(AP
->ap_BreakBits
);
470 if (AP
->ap_FoundBreak
)
477 /* Check if AChain must be "setup" */
479 if (!(ac
->an_Flags
& DDF_ExaminedBit
))
482 ** This AChain must be "setup". First AChain->an_Lock must point
483 ** to the parent directory, that is the directory where this
489 CurrentDir(ac
->an_Parent
->an_Lock
);
490 ac
->an_Lock
= Lock(ac
->an_Parent
->an_Info
.fib_FileName
, SHARED_LOCK
);
499 #if !MATCHFUNCS_NO_DUPLOCK
502 ac
->an_Lock
= DupLock(origdir
);
511 ** If there was no ac->an_Parent then we are dealing with the
512 ** first AChain whose lock was already setup in BuildAChainList
515 CurrentDir(ac
->an_Lock
);
517 if (ac
->an_Flags
& DDF_PatternBit
)
520 ** If this is a pattern AChain we first Examine here our
521 ** parent directory, so that it then can be traversed with
525 if (!Examine(ac
->an_Lock
, &ac
->an_Info
))
530 ac
->an_Flags
|= DDF_ExaminedBit
;
532 } /* if (ac->an_Flags & DDF_PatternBit) */
539 ** This is a normal AChain (no pattern). Try to lock it
540 ** to see if it exists.
543 if (!(lock
= Lock(ac
->an_String
, SHARED_LOCK
)))
545 /* It does not exist, so if possible go back one step */
555 /* if going back is not possible get error code and exit */
560 } /* if (!(lock = Lock(ac->an_String, SHARED_LOCK))) */
563 /* The File/Direcory ac->an_String exists */
565 success
= Examine(lock
, &ac
->an_Info
);
571 ** Examine()ing the file/directory did not
572 ** work, although the lock was successful!?.
573 ** Get error code and exit
581 ** This strcpy is necessary, because in case
582 ** of empty ac->an_String("") fib_FileName would
583 ** get parent directory name which it must not!
586 strcpy(ac
->an_Info
.fib_FileName
, ac
->an_String
);
588 ac
->an_Flags
|= DDF_ExaminedBit
;
591 ** If this is a file, but there are still more path components to
592 ** follow then we have to go back one step (AChain)
595 if (ac
->an_Child
&& (ac
->an_Info
.fib_DirEntryType
< 0))
603 ** Here we either have found a matching file/directory (result)
604 ** or, if ac->an_Child != NULL we have still to continue walking
605 ** through the AChains until we are in the last one. This all
606 ** happens further below
609 } /* if (!(lock = Lock(ac->an_String, SHARED_LOCK))) else ... */
611 } /* if (ac->an_Flags & DDF_PatternBit) else ... */
613 } /* if (!(ac->an_Flags & DDF_ExaminedBit)) */
617 ** This AChain was already setup.
619 ** When an AChain which is *not* a pattern already had DDF_PatternBit
620 ** set, then this means ERROR_NO_MORE_ENTRIES, so we try to go back
624 if (!(ac
->an_Flags
& DDF_PatternBit
))
633 ** Here we can be sure that the actual AChain is setup, ie: it will
634 ** have ac->an_Lock set correctly and to indicate this DDF_ExaminedBit
638 CurrentDir(ac
->an_Lock
);
640 if (ac
->an_Flags
& DDF_PatternBit
)
642 if(ExNext(ac
->an_Lock
, &ac
->an_Info
))
644 if (MatchPatternNoCase(ac
->an_String
, ac
->an_Info
.fib_FileName
))
647 ** This file matches the pattern in ac->an_String. If there
648 ** are no more AChains to follow then we have found a matching
649 ** file/directory (a result) --> break.
652 if (!ac
->an_Child
) break;
655 /* Did not match. Go to top of "for(;;)" loop */
662 if (error
!= ERROR_NO_MORE_ENTRIES
) goto done
;
669 } /* if (ac->an_Flags & DDF_PatternBit) */
672 ** Handle the cases where we must (try to) go back to the previous AChain.
673 ** This can happen if the actual AChain turned out to be a file although
674 ** there are still more AChains to follow [1]. Or if the actual AChain did not
675 ** exist at all [2]. Or if in a pattern AChain ExNext() told us that there are
676 ** no more entries [3]. Or if we were getting to a normal (no pattern) AChain
677 ** which was already setup (DDF_ExaminedBit) [4].
682 /* Check if going back is possible at all */
686 error
= ERROR_NO_MORE_ENTRIES
;
690 /* Yep. It is possible. So let's cleanup the AChain. */
692 CurrentDir(ac
->an_Parent
->an_Lock
);
697 ac
->an_Flags
&= ~DDF_ExaminedBit
;
699 /* Make ac and AP->ap_Current point to the previous AChain */
701 AP
->ap_Current
= ac
->an_Parent
;
704 ** If this was an APF_DODIR Achain (indicated by DDF_AllBit)
705 ** then the AChain must be unlinked and freed. And the user
706 ** must be informed about the leaving with APF_DIDDIR and
707 ** a "result" in AnchorPath which points to the directory which
711 if (ac
->an_Flags
& DDF_AllBit
)
713 AP
->ap_Current
->an_Child
= NULL
;
714 Match_FreeAChain(ac
, DOSBase
);
715 AP
->ap_Flags
|= APF_DIDDIR
;
717 /* go out of for(;;) loop --> MakeResult */
724 } /* if (must_go_back) */
730 ** We have reached the last AChain. And this means that
731 ** we have found a matching file/directory :-)). Go out of
732 ** for(;;) loop --> MakeResult
744 error
= Match_MakeResult(AP
, DOSBase
);
749 AP
->ap_Flags
&= ~APF_DODIR
;
751 if (old_current_lock
!= AP
->ap_Current
->an_Lock
)
753 AP
->ap_Flags
|= APF_DirChanged
;
757 AP
->ap_Flags
&= ~APF_DirChanged
;
766 /****************************************************************************************/
768 void My_MatchEnd(struct AnchorPath
*AP
)
770 struct AChain
*ac
= AP
->ap_Base
, *acnext
;
775 #if MATCHFUNCS_NO_DUPLOCK
777 ** CurrentDir to a valid lock, ie. one that will not be
778 ** killed further below
781 CurrentDir(ac
->an_Lock
);
785 acnext
= ac
->an_Child
;
788 ** Dont unlock lock in first AChain because it is the same
789 ** as the current directory when MatchFirst was called. And
790 ** this lock was not DupLock()ed!!!
794 #if MATCHFUNCS_NO_DUPLOCK
795 && (ac
!= AP
->ap_Base
)
802 Match_FreeAChain(ac
, DOSBase
);
808 AP
->ap_Current
= NULL
;
812 /****************************************************************************************/
814 #define ARG_TEMPLATE "FILE/A,ALL/S"
819 /****************************************************************************************/
822 static char *filename
;
824 static struct RDArgs
*myargs
;
825 static LONG args
[NUM_ARGS
];
827 /****************************************************************************************/
829 static void cleanup(char *msg
)
831 if (msg
) printf("newmatch: %s\n", msg
);
833 if (myargs
) FreeArgs(myargs
);
838 /****************************************************************************************/
840 static void doserror(void)
842 Fault(IoErr(), 0, s
, 255);
846 /****************************************************************************************/
848 static void getarguments(void)
850 if (!(myargs
= ReadArgs(ARG_TEMPLATE
, args
, 0)))
855 filename
= (char *)args
[ARG_FILE
];
856 all
= args
[ARG_ALL
] ? TRUE
: FALSE
;
859 /****************************************************************************************/
861 static void my_matchme(char *pattern
, BOOL all
)
863 struct AnchorPath stackap
[2], *AP
;
866 AP
= (struct AnchorPath
*)((((ULONG
)stackap
) + 3) & ~3);
868 memset(AP
, 0, sizeof(struct AnchorPath
));
870 error
= My_MatchFirst(pattern
, AP
);
874 printf("MatchFirst: error = %ld\n", error
);
878 printf("direntrytype = %ld\n", AP
->ap_Info
.fib_DirEntryType
);
879 if (!(AP
->ap_Flags
& APF_ITSWILD
) &&
880 (AP
->ap_Info
.fib_DirEntryType
> 0))
882 /* pattern was an explicitely named directory */
883 AP
->ap_Flags
|= APF_DODIR
;
886 printf("ap_Flags = %x\n", AP
->ap_Flags
);
887 NameFromLock(AP
->ap_Current
->an_Lock
, s
, 300);
888 printf("BaseLock = \"%s\"\n", s
);
890 showaclist(AP
->ap_Base
);
894 if (AP
->ap_Flags
& APF_DIDDIR
)
898 if (all
&& (AP
->ap_Info
.fib_DirEntryType
> 0))
900 AP
->ap_Flags
|= APF_DODIR
;
901 printf("DOING DIR: ");
904 printf("fib_FileName = \"%s\"\n", AP
->ap_Info
.fib_FileName
);
906 error
= My_MatchNext(AP
);
915 /****************************************************************************************/
916 /****************************************************************************************/
921 my_matchme(filename
, all
);
926 /****************************************************************************************/