1 // File name globbing
2 // Copyright (C) 2000 Core Technologies.
3 
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16 
17 #include	"includes.h"
18 
19 enum
20 {
21 	NO_USER=0,
22 	MATCH_FAILED,
23 	PATH_TOO_LONG,
24 	UNBALANCED_SQUARE_BRACKET,
25 	DANGLING_QUOTE
26 };
27 
28 static const char *errorDescriptions[]=
29 {
30 	"Failed to locate user",
31 	"Nothing matched",
32 	"Path grew too large",
33 	"Unbalanced [ or [^",
34 	"Incomplete \\ expression"
35 };
36 
37 typedef struct
38 {
39 	bool
40 		dontGlobLast,	// if true, the last element of the pattern will not be checked for existence if it contains no meta characters
41 		passNoMeta;		// if true, the entire pattern will not be checked for existence if it contains no meta characters
42 	bool
43 		hadMeta;		// tells if we have seen a meta character in the path so far
44 	UINT32
45 		numPaths;		// count of total paths so far
46 	char
47 		**paths;		// list of paths matching pattern
48 } GLOB_DATA;
49 
ExtractFileName(char * fullPath,char * filePart)50 static void ExtractFileName(char *fullPath,char *filePart)
51 // given fullPath which is considered to be a complete path and file name,
52 // truncate fullPath to just the path part, and copy the file part
53 // to filePart
54 // NOTE: filePart must be at least as large an array as fullPath
55 {
56 	int
57 		length;
58 
59 	length=strlen(fullPath);
60 	while(length>=0&&fullPath[length]!=PATH_SEP)
61 	{
62 		length--;
63 	}
64 	if(length>0)
65 	{
66 		strcpy(filePart,&(fullPath[length+1]));
67 		fullPath[length]='\0';
68 	}
69 	else
70 	{
71 		if(fullPath[0]==PATH_SEP)			// / at 0 is special
72 		{
73 			strcpy(filePart,&(fullPath[1]));
74 			fullPath[1]='\0';
75 		}
76 		else
77 		{
78 			strcpy(filePart,fullPath);
79 			fullPath[0]='\0';
80 		}
81 	}
82 }
83 
SplitPathAndFile(char * fullPath,char * pathPart,char * filePart)84 void SplitPathAndFile(char *fullPath,char *pathPart,char *filePart)
85 // split fullPath into pathPart, and filePart
86 // NOTE: pathPart and filePart must be at least MAXPATHLEN+1
87 // bytes long
88 // if fullPath is longer than MAXPATHLEN, parts will get truncated when copied
89 // NOTE: if fullPath is not a valid path, return the whole thing as filePart,
90 // and the current directory as the pathPart
91 {
92 	struct stat
93 		statOut;
94 	char
95 		tempPath[MAXPATHLEN+1];
96 
97 	if(realpath2(fullPath,pathPart))
98 	{
99 		if((stat(pathPart,&statOut)!=-1)&&S_ISDIR(statOut.st_mode))		// point to a file, or a directory?
100 		{
101 			filePart[0]='\0';					// points to a directory, so no file part, this is done
102 		}
103 		else
104 		{
105 			ExtractFileName(pathPart,filePart);	// pointed to a file, so separate them
106 		}
107 	}
108 	else
109 	{
110 		strncpy(tempPath,fullPath,MAXPATHLEN);	// copy this to separate it
111 		tempPath[MAXPATHLEN]='\0';
112 		ExtractFileName(tempPath,filePart);		// separate the pieces
113 		if(!realpath2(tempPath,pathPart))		// try to get the path to the unknown file part
114 		{
115 			if(!realpath2(".",pathPart))		// get the current directory
116 			{
117 				pathPart[0]='\0';				// just return empty string
118 			}
119 			strncpy(filePart,fullPath,MAXPATHLEN);
120 			filePart[MAXPATHLEN]='\0';
121 		}
122 	}
123 }
124 
ConcatPathAndFile(const char * path,const char * file,char * result)125 void ConcatPathAndFile(const char *path,const char *file,char *result)
126 // merge path and file to form a fully qualified file name
127 // result must be at least MAXPATHLEN+1 characters long
128 // NOTE: if path and file combine into a string longer than MAXPATHLEN,
129 // the combined string will be truncated
130 // NOTE ALSO, if file specifies a complete Path (starts with a PATH_SEP) then
131 // it alone is added to result
132 {
133 	int
134 		pathLength,
135 		fileLength;
136 
137 	if(file[0]!=PATH_SEP)
138 	{
139 		pathLength=strlen(path);
140 		if(pathLength>MAXPATHLEN)
141 		{
142 			pathLength=MAXPATHLEN;
143 		}
144 		strncpy(result,path,pathLength);
145 		if(pathLength&&(path[pathLength-1]!=PATH_SEP)&&(pathLength<MAXPATHLEN))
146 		{
147 			result[pathLength++]=PATH_SEP;
148 		}
149 	}
150 	else
151 	{
152 		pathLength=0;
153 	}
154 	fileLength=strlen(file);
155 	if(pathLength+fileLength>MAXPATHLEN)
156 	{
157 		fileLength=(MAXPATHLEN-pathLength);
158 	}
159 	strncpy(&(result[pathLength]),file,fileLength);
160 	result[pathLength+fileLength]='\0';
161 }
162 
163 // string list functions
164 
FreeStringList(char ** list)165 void FreeStringList(char **list)
166 // free a list of strings created by calling NewStringList
167 {
168 	UINT32
169 		index;
170 
171 	index=0;
172 	while(list&&list[index])
173 	{
174 		MDisposePtr(list[index]);
175 		index++;
176 	}
177 	MDisposePtr(list);
178 }
179 
CompareStrings(const void * i,const void * j)180 static int CompareStrings(const void *i,const void *j)
181 // compare two elements of the list
182 {
183 	char
184 		*elementA,
185 		*elementB;
186 
187 	elementA=*((char **)i);
188 	elementB=*((char **)j);
189 
190 	return(strcmp(elementA,elementB));
191 }
192 
SortStringList(char ** list,UINT32 numElements)193 void SortStringList(char **list,UINT32 numElements)
194 // run through any elements of list, and sort them
195 // by calling qsort
196 {
197 	if(list)
198 	{
199 		qsort((char *)list,numElements,sizeof(char **),CompareStrings);
200 	}
201 }
202 
ReplaceStringInList(const char * string,char ** list,UINT32 element)203 bool ReplaceStringInList(const char *string,char **list,UINT32 element)
204 // place string into list at the given position, freeing
205 // any string that was there
206 // NOTE: element must be in range
207 // if there is a problem leave the entry untouched, SetError, and return false
208 {
209 	char
210 		*newElement;
211 
212 	if((newElement=(char *)MNewPtr(strlen(string)+1)))
213 	{
214 		strcpy(newElement,string);
215 		MDisposePtr(list[element]);
216 		list[element]=newElement;
217 		return(true);
218 	}
219 	return(false);
220 }
221 
AddStringToList(const char * string,char *** list,UINT32 * numElements)222 bool AddStringToList(const char *string,char ***list,UINT32 *numElements)
223 // add string to list
224 // at any point, list can be deleted by calling FreeStringList
225 // if there is a problem, do not add the element, SetError, and return false
226 {
227 	char
228 		**newList;
229 	UINT32
230 		newLength;
231 
232 	newLength=sizeof(char *)*((*numElements)+2);
233 	if((newList=(char **)MResizePtr(*list,newLength)))
234 	{
235 		(*list)=newList;
236 		if(((*list)[*numElements]=(char *)MNewPtr(strlen(string)+1)))
237 		{
238 			strcpy((*list)[(*numElements)++],string);
239 			(*list)[*numElements]=NULL;									// keep the list terminated with a NULL
240 			return(true);
241 		}
242 	}
243 	return(false);
244 }
245 
NewStringList()246 char **NewStringList()
247 // Create an empty string list
248 // if there is a problem, SetError, return NULL
249 {
250 	char
251 		**list;
252 
253 	if((list=(char **)MNewPtr(sizeof(char *))))
254 	{
255 		list[0]=NULL;
256 	}
257 	return(list);
258 }
259 
260 // tilde expansion routines
261 
ExpandTildes(const char * pattern,char * newPattern,UINT32 newPatternLength)262 static bool ExpandTildes(const char *pattern,char *newPattern,UINT32 newPatternLength)
263 // expand tilde sequences in pattern, write output into newPattern
264 // if there is a problem, SetError, and return false
265 {
266 	bool
267 		done,
268 		fail;
269 	char
270 		character;
271 	UINT32
272 		inIndex,
273 		outIndex;
274 	char
275 		*home;
276 	struct passwd
277 		*passwordEntry;
278 	UINT32
279 		length;
280 
281 	fail=false;
282 	if(pattern[0]=='~')		// see if a tilde exists at the start of pattern
283 	{
284 		inIndex=1;			// skip the tilde
285 		outIndex=0;
286 		done=false;
287 		while(!done&&!fail&&(character=pattern[inIndex])&&outIndex<(newPatternLength-1))
288 		{
289 			switch(character)
290 			{
291 				case PATH_SEP:
292 					inIndex++;
293 					done=true;
294 					break;
295 				case '\\':
296 					inIndex++;
297 					if(!(newPattern[outIndex++]=pattern[inIndex]))
298 					{
299 						SetError(errorDescriptions[DANGLING_QUOTE]);
300 						fail=true;
301 					}
302 					break;
303 				default:
304 					newPattern[outIndex++]=character;
305 					inIndex++;
306 					break;
307 			}
308 		}
309 		if(!fail)
310 		{
311 			newPattern[outIndex]='\0';		// terminate the user name string
312 			home=NULL;						// no home found yet
313 			if(outIndex)					// name was specified
314 			{
315 				if((passwordEntry=getpwnam(newPattern)))
316 				{
317 					home=passwordEntry->pw_dir;
318 				}
319 			}
320 			else
321 			{
322 				if(!(home=getenv("HOME")))	// try to find environment variable
323 				{
324 					if((passwordEntry=getpwuid(getuid())))	// if no environment, try to get from password database
325 					{
326 						home=passwordEntry->pw_dir;
327 					}
328 				}
329 			}
330 			if(home)
331 			{
332 				strncpy(newPattern,home,newPatternLength-1);
333 				newPattern[newPatternLength-1]='\0';
334 				length=strlen(newPattern);
335 				if(length&&length<newPatternLength-1)
336 				{
337 					if(newPattern[length-1]!=PATH_SEP)	// add path separator if needed
338 					{
339 						newPattern[length++]=PATH_SEP;
340 						newPattern[length]='\0';
341 					}
342 				}
343 				strncat(newPattern,&pattern[inIndex],newPatternLength-length);
344 			}
345 			else
346 			{
347 				SetError(errorDescriptions[NO_USER]);	// user not there
348 				fail=true;
349 			}
350 		}
351 	}
352 	else
353 	{
354 		strncpy(newPattern,pattern,newPatternLength-1);
355 		newPattern[newPatternLength-1]='\0';
356 	}
357 	return(!fail);
358 }
359 
360 // wildcard expansion routines
361 
MatchRange(char character,const char * pattern,UINT32 * patternIndex,bool * haveMatch)362 static bool MatchRange(char character,const char *pattern,UINT32 *patternIndex,bool *haveMatch)
363 // a range is starting in pattern, so attempt to match character
364 // against it
365 {
366 	bool
367 		fail,
368 		done,
369 		isNot;
370 	char
371 		testChar,
372 		lastChar;
373 
374 	isNot=done=fail=false;
375 	if(pattern[*patternIndex]=='^')					// check for not flag
376 	{
377 		isNot=true;
378 		(*patternIndex)++;
379 	}
380 	lastChar='\0';									// start with bottom of range at 0
381 	*haveMatch=false;
382 	while(!done&&!fail&&(testChar=pattern[(*patternIndex)++]))
383 	{
384 		switch(testChar)
385 		{
386 			case '\\':
387 				if((testChar=pattern[(*patternIndex)++]))	// get next character to test
388 				{
389 					if(character==testChar)			// test it literally
390 					{
391 						*haveMatch=true;
392 					}
393 				}
394 				else
395 				{
396 					SetError(errorDescriptions[DANGLING_QUOTE]);
397 					fail=true;						// got \ at the end of the pattern
398 				}
399 				break;
400 			case '-':
401 				if((testChar=pattern[(*patternIndex)++]))		// get the next character for the range (if there is one)
402 				{
403 					switch(testChar)
404 					{
405 						case '\\':
406 							if((testChar=pattern[(*patternIndex)++]))
407 							{
408 								if(character>=lastChar&&character<=testChar)
409 								{
410 									*haveMatch=true;
411 								}
412 							}
413 							else
414 							{
415 								SetError(errorDescriptions[DANGLING_QUOTE]);
416 								fail=true;						// got \ at the end of the pattern
417 							}
418 							break;
419 						case ']':
420 							if(character>=lastChar)				// empty range at end, so take as infinite end
421 							{
422 								*haveMatch=true;
423 							}
424 							done=true;
425 							break;
426 						default:
427 							if(character>=lastChar&&character<=testChar)
428 							{
429 								*haveMatch=true;
430 							}
431 							break;
432 					}
433 				}
434 				else
435 				{
436 					SetError(errorDescriptions[UNBALANCED_SQUARE_BRACKET]);
437 					fail=true;
438 				}
439 				break;
440 			case ']':
441 				done=true;							// scanning is done (empty lists are allowed)
442 				break;
443 			default:								// otherwise it is normal, and just gets tested
444 				if(character==testChar)
445 				{
446 					*haveMatch=true;
447 				}
448 				break;
449 		}
450 		lastChar=testChar;							// remember this for next time around the loop
451 	}
452 	if(!done&&!fail)								// ran out of things to scan
453 	{
454 		SetError(errorDescriptions[UNBALANCED_SQUARE_BRACKET]);
455 		fail=true;
456 	}
457 	if(!fail)
458 	{
459 		if(isNot)
460 		{
461 			*haveMatch=!*haveMatch;
462 		}
463 	}
464 	return(!fail);
465 }
466 
MatchName(const char * name,const char * pattern,UINT32 * patternIndex,bool * haveMatch)467 static bool MatchName(const char *name,const char *pattern,UINT32 *patternIndex,bool *haveMatch)
468 // match name against pattern
469 // pattern points to the start of a pattern terminated with a '\0', or a PATH_SEP
470 // update patternIndex to point to the character that stopped the match
471 // if a match occurs, return true if have match
472 // if there is a problem SetError, and return false
473 {
474 	UINT32
475 		nameIndex;
476 	UINT32
477 		startPatternIndex;
478 	bool
479 		fail,
480 		done,
481 		matching;
482 	char
483 		character;
484 
485 	fail=*haveMatch=done=false;
486 	matching=true;
487 	nameIndex=0;
488 	while(!done&&matching)
489 	{
490 		switch(pattern[*patternIndex])
491 		{
492 			case '\0':				// ran out of characters of the pattern
493 			case PATH_SEP:
494 				matching=(name[nameIndex]=='\0');
495 				done=true;
496 				break;
497 			case '*':
498 				(*patternIndex)++;	// move past the *
499 				matching=false;
500 				do
501 				{
502 					startPatternIndex=*patternIndex;
503 					fail=!MatchName(&name[nameIndex],pattern,&startPatternIndex,&matching);
504 				}
505 				while(name[nameIndex++]&&!fail&&!matching);			// recurse trying to make a match
506 				if(matching)
507 				{
508 					*patternIndex=startPatternIndex;
509 					done=true;
510 				}
511 				break;
512 			case '?':
513 				(*patternIndex)++;
514 				matching=(name[nameIndex++]!='\0');
515 				break;
516 			case '[':
517 				(*patternIndex)++;
518 				if((character=name[nameIndex++]))
519 				{
520 					fail=!MatchRange(character,pattern,patternIndex,&matching);
521 				}
522 				else
523 				{
524 					matching=false;
525 				}
526 				break;
527 			case '\\':
528 				(*patternIndex)++;
529 				if((character=pattern[(*patternIndex)++]))
530 				{
531 					matching=(name[nameIndex++]==character);
532 				}
533 				else
534 				{
535 					SetError(errorDescriptions[DANGLING_QUOTE]);
536 					fail=true;
537 				}
538 				break;
539 			default:
540 				matching=(name[nameIndex++]==pattern[(*patternIndex)++]);
541 				break;
542 		}
543 	}
544 	if(!fail)
545 	{
546 		*haveMatch=matching;
547 	}
548 	return(!fail);
549 }
550 
551 // prototype this because it is mutually recursive with MoveFurther
552 static bool GlobAgainstDirectory(const char *pattern,UINT32 patternIndex,char *pathBuffer,UINT32 pathIndex,GLOB_DATA *data);
553 
MoveFurther(const char * pattern,UINT32 patternIndex,char * pathBuffer,UINT32 pathIndex,GLOB_DATA * data)554 static bool MoveFurther(const char *pattern,UINT32 patternIndex,char *pathBuffer,UINT32 pathIndex,GLOB_DATA *data)
555 // this attempts to add to pathBuffer by reading from pattern at patternIndex
556 // pattern data is added to pathBuffer so that all data containing no meta characters is copied
557 // up to the first PATH_SEP before data that does contain meta characters
558 // if there is a problem, SetError, and return false
559 {
560 	bool
561 		fail;
562 	bool
563 		done;
564 	UINT32
565 		lastPatternIndex,
566 		lastPathIndex;
567 	bool
568 		nextSpecial;
569 	bool
570 		hadMeta;					// tells if locally, we have seen any meta characters yet
571 
572 	fail=done=nextSpecial=hadMeta=false;
573 	lastPatternIndex=patternIndex;
574 	lastPathIndex=pathIndex;
575 	while(!done&&pathIndex<MAXPATHLEN)
576 	{
577 		// copy all of pattern that contains no meta characters until the next PATH_SEP into pathBuffer
578 		pathBuffer[pathIndex]=pattern[patternIndex];
579 		switch(pattern[patternIndex])
580 		{
581 			case '\0':				// ran out of characters of the pattern, so this is not path information
582 				if((!hadMeta&&data->dontGlobLast)||(!data->hadMeta&&data->passNoMeta))		// if no meta characters, then this IS the path (assuming we dont glob the last one)
583 				{
584 					pathBuffer[pathIndex]='\0';	// terminate the current path
585 					fail=!AddStringToList(pathBuffer,&data->paths,&data->numPaths);	// add this to the list
586 				}
587 				done=true;
588 				break;
589 			case '*':							// meta character (this means we stop here)
590 			case '?':
591 			case '[':
592 				if(nextSpecial)
593 				{
594 					patternIndex++;
595 					pathIndex++;
596 					nextSpecial=false;
597 				}
598 				else
599 				{
600 					hadMeta=true;				// remember locally that there were meta characters
601 					data->hadMeta=true;			// remember it globally as well
602 					done=true;
603 				}
604 				break;
605 			case PATH_SEP:
606 				patternIndex++;
607 				pathIndex++;
608 				lastPatternIndex=patternIndex;	// remember these
609 				lastPathIndex=pathIndex;
610 				nextSpecial=false;
611 				break;
612 			case '\\':
613 				if(nextSpecial)
614 				{
615 					patternIndex++;
616 					pathIndex++;
617 					nextSpecial=false;
618 				}
619 				else
620 				{
621 					nextSpecial=true;
622 					patternIndex++;				// skip the \, but do not move forward in path
623 				}
624 				break;
625 			default:
626 				patternIndex++;
627 				pathIndex++;
628 				nextSpecial=false;
629 				break;
630 		}
631 	}
632 	if(done)									// we got something
633 	{
634 		if(!((!hadMeta&&data->dontGlobLast)||(!data->hadMeta&&data->passNoMeta)))	// if we aren't passing this verbatim, then glob against it
635 		{
636 			patternIndex=lastPatternIndex;		// move back
637 			pathIndex=lastPathIndex;
638 			pathBuffer[pathIndex]='\0';			// terminate the current path
639 			fail=!GlobAgainstDirectory(pattern,patternIndex,pathBuffer,pathIndex,data);	// expand the pattern at the end of the current path
640 		}
641 	}
642 	else										// path was about to overflow output
643 	{
644 		SetError(errorDescriptions[PATH_TOO_LONG]);
645 		fail=true;
646 	}
647 	return(!fail);
648 
649 }
650 
GlobAgainstDirectory(const char * pattern,UINT32 patternIndex,char * pathBuffer,UINT32 pathIndex,GLOB_DATA * data)651 static bool GlobAgainstDirectory(const char *pattern,UINT32 patternIndex,char *pathBuffer,UINT32 pathIndex,GLOB_DATA *data)
652 // try pattern against all files at pathBuffer, adding each that matches completely, and calling
653 // move further on each one
654 // if there is some sort of problem, SetError, and return false
655 {
656 	DIR
657 		*directory;
658 	struct dirent
659 		*entry;
660 	bool
661 		fail;
662 	const char
663 		*path;
664 	UINT32
665 		length;
666 	UINT32
667 		newIndex;
668 	bool
669 		haveMatch;
670 
671 	fail=false;
672 	if(pathBuffer[0])
673 	{
674 		path=pathBuffer;						// point at the passed path if one given
675 	}
676 	else
677 	{
678 		path="./";								// if passed path is empty, it means current directory
679 	}
680 	if((directory=opendir(path)))				// if it does not open, assume it is because path did not point to something valid
681 	{
682 		while(!fail&&(entry=readdir(directory)))
683 		{
684 			newIndex=patternIndex;
685 			if(entry->d_name[0]!='.'||pattern[patternIndex]=='.')			// the first . must be matched exactly
686 			{
687 				if(MatchName(entry->d_name,pattern,&newIndex,&haveMatch))
688 				{
689 					if(haveMatch)				// was there a match?
690 					{
691 						length=strlen(entry->d_name);
692 						if(pathIndex+length<MAXPATHLEN)	// append to path (if there's room)
693 						{
694 							strcpy(&(pathBuffer[pathIndex]),entry->d_name);
695 							if(pattern[newIndex])		// see if there's more pattern left
696 							{
697 								fail=!MoveFurther(pattern,newIndex,pathBuffer,pathIndex+length,data);
698 							}
699 							else
700 							{
701 								fail=!AddStringToList(pathBuffer,&data->paths,&data->numPaths);
702 							}
703 						}
704 						else
705 						{
706 							SetError(errorDescriptions[PATH_TOO_LONG]);
707 							fail=true;
708 						}
709 					}
710 				}
711 				else
712 				{
713 					fail=true;
714 				}
715 			}
716 		}
717 		closedir(directory);
718 	}
719 	return(!fail);
720 }
721 
Glob(const char * path,const char * pattern,bool dontGlobLast,bool passNoMeta,UINT32 * numElements)722 char **Glob(const char *path,const char *pattern,bool dontGlobLast,bool passNoMeta,UINT32 *numElements)
723 // glob pattern into an array of paths, and return it
724 // if there is a problem, SetError, and return NULL
725 // path is the initial path to begin globbing at if pattern
726 // does not specify an absolute path
727 // NOTE: the returned paths must be deleted by a call to
728 // FreeStringList
729 // if dontGlobLast is true, the last element of pattern (if it contains
730 // no meta characters) will not be checked against its directory,
731 // but instead will be taken to exist, and will be placed into the output
732 // this is useful when trying to save to a globbed path, and the last element
733 // of the path is the name of a file which does not necessarily exist yet.
734 // if passNoMeta is true, any pattern that contains no wild card
735 // characters will be placed into the output without being checked
736 {
737 	GLOB_DATA
738 		data;
739 	char
740 		newPattern[MAXPATHLEN+1];					// tilde expand the pattern into here
741 	char
742 		pathBuffer[MAXPATHLEN+1];					// this is the place to build the output path
743 	UINT32
744 		length;
745 
746 	*numElements=0;									// no elements yet
747 	if((data.paths=NewStringList()))
748 	{
749 		data.numPaths=0;							// no paths so far
750 		data.dontGlobLast=dontGlobLast;				// set up flags that tell how to glob
751 		data.passNoMeta=passNoMeta;
752 		data.hadMeta=false;							// no meta characters seen so far
753 		if(ExpandTildes(pattern,&newPattern[0],MAXPATHLEN+1))	// expand tilde sequences in pattern, write output into newPattern
754 		{
755 			if(newPattern[0]==PATH_SEP)
756 			{
757 				pathBuffer[0]='\0';					// pattern is absolute, so ignore given path
758 			}
759 			else
760 			{
761 				strncpy(pathBuffer,path,MAXPATHLEN);	// use the passed path as a prefix to the glob
762 			}
763 			length=strlen(pathBuffer);
764 			if(length&&length<MAXPATHLEN)			// if we have something, check to see if it needs a PATH_SEP
765 			{
766 				if(pathBuffer[length-1]!=PATH_SEP)	// add separator to end of path
767 				{
768 					pathBuffer[length++]=PATH_SEP;
769 					pathBuffer[length]='\0';
770 				}
771 			}
772 			if(MoveFurther(&newPattern[0],0,pathBuffer,length,&data))
773 			{
774 				if(data.numPaths)
775 				{
776 					SortStringList(data.paths,data.numPaths);
777 					*numElements=data.numPaths;
778 					return(data.paths);
779 				}
780 				else
781 				{
782 					SetError(errorDescriptions[MATCH_FAILED]);
783 				}
784 			}
785 		}
786 		FreeStringList(data.paths);
787 	}
788 	return(NULL);
789 }
790 
GlobAll(const char * path,UINT32 * numElements)791 char **GlobAll(const char *path,UINT32 *numElements)
792 // return a sorted list of all of the directories/files at the end of path
793 // path is not expanded in any way.
794 // NOTE: the list will contain ONLY the last component of the path
795 // if there is a problem, SetError, and return NULL
796 // NOTE: the returned list must be later deleted by a call to
797 // FreeStringList
798 {
799 	char
800 		**files;
801 	DIR
802 		*directory;
803 	struct dirent
804 		*entry;
805 	bool
806 		fail;
807 
808 	fail=false;
809 	*numElements=0;								// no paths so far
810 	if((files=NewStringList()))
811 	{
812 		if((directory=opendir(path)))			// if it does not open, assume it is because path did not point to something valid, and return NULL
813 		{
814 			while(!fail&&(entry=readdir(directory)))
815 			{
816 				fail=!AddStringToList(entry->d_name,&files,numElements);
817 			}
818 			closedir(directory);
819 			if(!fail)
820 			{
821 				SortStringList(files,*numElements);
822 				return(files);
823 			}
824 		}
825 		else
826 		{
827 			SetStdCLibError();
828 		}
829 		FreeStringList(files);
830 	}
831 	*numElements=0;
832 	return(NULL);
833 }
834