xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/ext/File/Glob/bsd_glob.c (revision 0:68f95e015346)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Guido van Rossum.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #if defined(LIBC_SCCS) && !defined(lint)
34 static char sccsid[] = "@(#)glob.c	8.3 (Berkeley) 10/13/93";
35 /* most changes between the version above and the one below have been ported:
36 static char sscsid[]=  "$OpenBSD: glob.c,v 1.8.10.1 2001/04/10 jason Exp $";
37  */
38 #endif /* LIBC_SCCS and not lint */
39 
40 /*
41  * glob(3) -- a superset of the one defined in POSIX 1003.2.
42  *
43  * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
44  *
45  * Optional extra services, controlled by flags not defined by POSIX:
46  *
47  * GLOB_QUOTE:
48  *	Escaping convention: \ inhibits any special meaning the following
49  *	character might have (except \ at end of string is retained).
50  * GLOB_MAGCHAR:
51  *	Set in gl_flags if pattern contained a globbing character.
52  * GLOB_NOMAGIC:
53  *	Same as GLOB_NOCHECK, but it will only append pattern if it did
54  *	not contain any magic characters.  [Used in csh style globbing]
55  * GLOB_ALTDIRFUNC:
56  *	Use alternately specified directory access functions.
57  * GLOB_TILDE:
58  *	expand ~user/foo to the /home/dir/of/user/foo
59  * GLOB_BRACE:
60  *	expand {1,2}{a,b} to 1a 1b 2a 2b
61  * gl_matchc:
62  *	Number of matches in the current invocation of glob.
63  * GLOB_ALPHASORT:
64  *	sort alphabetically like csh (case doesn't matter) instead of in ASCII
65  *	order
66  */
67 
68 #include <EXTERN.h>
69 #include <perl.h>
70 #include <XSUB.h>
71 
72 #include "bsd_glob.h"
73 #ifdef I_PWD
74 #	include <pwd.h>
75 #else
76 #if defined(HAS_PASSWD) && !defined(VMS)
77 	struct passwd *getpwnam(char *);
78 	struct passwd *getpwuid(Uid_t);
79 #endif
80 #endif
81 
82 #ifndef MAXPATHLEN
83 #  ifdef PATH_MAX
84 #    define	MAXPATHLEN	PATH_MAX
85 #    ifdef MACOS_TRADITIONAL
86 #      define	MAXPATHLEN	255
87 #    else
88 #      define	MAXPATHLEN	1024
89 #    endif
90 #  endif
91 #endif
92 
93 #ifdef I_LIMITS
94 #include <limits.h>
95 #endif
96 
97 #ifndef ARG_MAX
98 #  ifdef MACOS_TRADITIONAL
99 #    define		ARG_MAX		65536	/* Mac OS is actually unlimited */
100 #  else
101 #    ifdef _SC_ARG_MAX
102 #      define		ARG_MAX		(sysconf(_SC_ARG_MAX))
103 #    else
104 #      ifdef _POSIX_ARG_MAX
105 #        define		ARG_MAX		_POSIX_ARG_MAX
106 #      else
107 #        ifdef WIN32
108 #          define	ARG_MAX		14500	/* from VC's limits.h */
109 #        else
110 #          define	ARG_MAX		4096	/* from POSIX, be conservative */
111 #        endif
112 #      endif
113 #    endif
114 #  endif
115 #endif
116 
117 #define	BG_DOLLAR	'$'
118 #define	BG_DOT		'.'
119 #define	BG_EOS		'\0'
120 #define	BG_LBRACKET	'['
121 #define	BG_NOT		'!'
122 #define	BG_QUESTION	'?'
123 #define	BG_QUOTE	'\\'
124 #define	BG_RANGE	'-'
125 #define	BG_RBRACKET	']'
126 #ifdef MACOS_TRADITIONAL
127 #  define	BG_SEP	':'
128 #else
129 #  define	BG_SEP	'/'
130 #endif
131 #ifdef DOSISH
132 #define BG_SEP2		'\\'
133 #endif
134 #define	BG_STAR		'*'
135 #define	BG_TILDE	'~'
136 #define	BG_UNDERSCORE	'_'
137 #define	BG_LBRACE	'{'
138 #define	BG_RBRACE	'}'
139 #define	BG_SLASH	'/'
140 #define	BG_COMMA	','
141 
142 #ifndef GLOB_DEBUG
143 
144 #define	M_QUOTE		0x8000
145 #define	M_PROTECT	0x4000
146 #define	M_MASK		0xffff
147 #define	M_ASCII		0x00ff
148 
149 typedef U16 Char;
150 
151 #else
152 
153 #define	M_QUOTE		0x80
154 #define	M_PROTECT	0x40
155 #define	M_MASK		0xff
156 #define	M_ASCII		0x7f
157 
158 typedef U8 Char;
159 
160 #endif /* !GLOB_DEBUG */
161 
162 
163 #define	CHAR(c)		((Char)((c)&M_ASCII))
164 #define	META(c)		((Char)((c)|M_QUOTE))
165 #define	M_ALL		META('*')
166 #define	M_END		META(']')
167 #define	M_NOT		META('!')
168 #define	M_ONE		META('?')
169 #define	M_RNG		META('-')
170 #define	M_SET		META('[')
171 #define	ismeta(c)	(((c)&M_QUOTE) != 0)
172 
173 
174 static int	 compare(const void *, const void *);
175 static int	 ci_compare(const void *, const void *);
176 static int	 g_Ctoc(const Char *, char *, STRLEN);
177 static int	 g_lstat(Char *, Stat_t *, glob_t *);
178 static DIR	*g_opendir(Char *, glob_t *);
179 static Char	*g_strchr(Char *, int);
180 static int	 g_stat(Char *, Stat_t *, glob_t *);
181 static int	 glob0(const Char *, glob_t *);
182 static int	 glob1(Char *, Char *, glob_t *, size_t *);
183 static int	 glob2(Char *, Char *, Char *, Char *, Char *, Char *,
184 		       glob_t *, size_t *);
185 static int	 glob3(Char *, Char *, Char *, Char *, Char *, Char *,
186 		       Char *, Char *, glob_t *, size_t *);
187 static int	 globextend(const Char *, glob_t *, size_t *);
188 static const Char *
189 		 globtilde(const Char *, Char *, size_t, glob_t *);
190 static int	 globexp1(const Char *, glob_t *);
191 static int	 globexp2(const Char *, const Char *, glob_t *, int *);
192 static int	 match(Char *, Char *, Char *, int);
193 #ifdef GLOB_DEBUG
194 static void	 qprintf(const char *, Char *);
195 #endif /* GLOB_DEBUG */
196 
197 #ifdef PERL_IMPLICIT_CONTEXT
198 static Direntry_t *	my_readdir(DIR*);
199 
200 static Direntry_t *
my_readdir(DIR * d)201 my_readdir(DIR *d)
202 {
203 #ifndef NETWARE
204     return PerlDir_read(d);
205 #else
206     return (DIR *)PerlDir_read(d);
207 #endif
208 }
209 #else
210 
211 /* ReliantUNIX (OS formerly known as SINIX) defines readdir
212  * in LFS-mode to be a 64-bit version of readdir.  */
213 
214 #   ifdef sinix
215 static Direntry_t *    my_readdir(DIR*);
216 
217 static Direntry_t *
my_readdir(DIR * d)218 my_readdir(DIR *d)
219 {
220     return readdir(d);
221 }
222 #   else
223 
224 #       define	my_readdir	readdir
225 
226 #   endif
227 
228 #endif
229 
230 #ifdef MACOS_TRADITIONAL
231 #include <Files.h>
232 #include <Types.h>
233 #include <string.h>
234 
235 #define NO_UPDIR_ERR 1	/* updir resolving failed */
236 
237 static Boolean g_matchVol; /* global variable */
238 static short updir(char *path);
239 static short resolve_updirs(char *new_pattern);
240 static void remove_trColon(char *path);
241 static short glob_mark_Mac(Char *pathbuf, Char *pathend, Char *pathend_last);
242 static OSErr GetVolInfo(short volume, Boolean indexed, FSSpec *spec);
243 static void name_f_FSSpec(StrFileName volname, FSSpec *spec);
244 
245 #endif
246 
247 int
bsd_glob(const char * pattern,int flags,int (* errfunc)(const char *,int),glob_t * pglob)248 bsd_glob(const char *pattern, int flags,
249 	 int (*errfunc)(const char *, int), glob_t *pglob)
250 {
251 	const U8 *patnext;
252 	int c;
253 	Char *bufnext, *bufend, patbuf[MAXPATHLEN];
254 
255 #ifdef MACOS_TRADITIONAL
256 	char *new_pat, *p, *np;
257 	int err;
258 	size_t len;
259 #endif
260 
261 #ifndef MACOS_TRADITIONAL
262 	patnext = (U8 *) pattern;
263 #endif
264 	/* TODO: GLOB_APPEND / GLOB_DOOFFS aren't supported yet */
265 #if 0
266 	if (!(flags & GLOB_APPEND)) {
267 		pglob->gl_pathc = 0;
268 		pglob->gl_pathv = NULL;
269 		if (!(flags & GLOB_DOOFFS))
270 			pglob->gl_offs = 0;
271 	}
272 #else
273 	pglob->gl_pathc = 0;
274 	pglob->gl_pathv = NULL;
275 	pglob->gl_offs = 0;
276 #endif
277 	pglob->gl_flags = flags & ~GLOB_MAGCHAR;
278 	pglob->gl_errfunc = errfunc;
279 	pglob->gl_matchc = 0;
280 
281 	bufnext = patbuf;
282 	bufend = bufnext + MAXPATHLEN - 1;
283 #ifdef DOSISH
284 	/* Nasty hack to treat patterns like "C:*" correctly. In this
285 	 * case, the * should match any file in the current directory
286 	 * on the C: drive. However, the glob code does not treat the
287 	 * colon specially, so it looks for files beginning "C:" in
288 	 * the current directory. To fix this, change the pattern to
289 	 * add an explicit "./" at the start (just after the drive
290 	 * letter and colon - ie change to "C:./").
291 	 */
292 	if (isalpha(pattern[0]) && pattern[1] == ':' &&
293 	    pattern[2] != BG_SEP && pattern[2] != BG_SEP2 &&
294 	    bufend - bufnext > 4) {
295 		*bufnext++ = pattern[0];
296 		*bufnext++ = ':';
297 		*bufnext++ = '.';
298 		*bufnext++ = BG_SEP;
299 		patnext += 2;
300 	}
301 #endif
302 
303 #ifdef MACOS_TRADITIONAL
304 	/* Check if we need to match a volume name (e.g. '*HD:*') */
305 	g_matchVol = false;
306 	p = (char *) pattern;
307 	if (*p != BG_SEP) {
308 	    p++;
309 	    while (*p != BG_EOS) {
310 		if (*p == BG_SEP) {
311 		    g_matchVol = true;
312 		    break;
313 		}
314 		p++;
315 	    }
316 	}
317 
318 	/* Transform the pattern:
319 	 * (a) Resolve updirs, e.g.
320 	 *     '*:t*p::'       -> '*:'
321 	 *	   ':a*:tmp::::'   -> '::'
322 	 *	   ':base::t*p:::' -> '::'
323 	 *     '*HD::'         -> return 0 (error, quit silently)
324 	 *
325 	 * (b) Remove a single trailing ':', unless it's a "match volume only"
326 	 *     pattern like '*HD:'; e.g.
327 	 *     '*:tmp:' -> '*:tmp'  but
328 	 *     '*HD:'   -> '*HD:'
329 	 *     (If we don't do that, even filenames will have a trailing ':' in
330 	 *     the result.)
331 	 */
332 
333 	/* We operate on a copy of the pattern */
334 	len = strlen(pattern);
335 	New(0, new_pat, len + 1, char);
336 	if (new_pat == NULL)
337 	    return (GLOB_NOSPACE);
338 
339 	p = (char *) pattern;
340 	np = new_pat;
341 	while (*np++ = *p++) ;
342 
343 	/* Resolve updirs ... */
344 	err = resolve_updirs(new_pat);
345 	if (err) {
346 	    Safefree(new_pat);
347 	    /* The pattern is incorrect: tried to move
348 	       up above the volume root, see above.
349 	       We quit silently. */
350 	    return 0;
351 	}
352 	/* remove trailing colon ... */
353 	remove_trColon(new_pat);
354 	patnext = (U8 *) new_pat;
355 
356 #endif /* MACOS_TRADITIONAL */
357 
358 	if (flags & GLOB_QUOTE) {
359 		/* Protect the quoted characters. */
360 		while (bufnext < bufend && (c = *patnext++) != BG_EOS)
361 			if (c == BG_QUOTE) {
362 #ifdef DOSISH
363 				    /* To avoid backslashitis on Win32,
364 				     * we only treat \ as a quoting character
365 				     * if it precedes one of the
366 				     * metacharacters []-{}~\
367 				     */
368 				if ((c = *patnext++) != '[' && c != ']' &&
369 				    c != '-' && c != '{' && c != '}' &&
370 				    c != '~' && c != '\\') {
371 #else
372 				if ((c = *patnext++) == BG_EOS) {
373 #endif
374 					c = BG_QUOTE;
375 					--patnext;
376 				}
377 				*bufnext++ = c | M_PROTECT;
378 			} else
379 				*bufnext++ = c;
380 	} else
381 		while (bufnext < bufend && (c = *patnext++) != BG_EOS)
382 			*bufnext++ = c;
383 	*bufnext = BG_EOS;
384 
385 #ifdef MACOS_TRADITIONAL
386 	if (flags & GLOB_BRACE)
387 	    err = globexp1(patbuf, pglob);
388 	else
389 	    err = glob0(patbuf, pglob);
390 	Safefree(new_pat);
391 	return err;
392 #else
393 	if (flags & GLOB_BRACE)
394 	    return globexp1(patbuf, pglob);
395 	else
396 	    return glob0(patbuf, pglob);
397 #endif
398 }
399 
400 /*
401  * Expand recursively a glob {} pattern. When there is no more expansion
402  * invoke the standard globbing routine to glob the rest of the magic
403  * characters
404  */
405 static int
406 globexp1(const Char *pattern, glob_t *pglob)
407 {
408 	const Char* ptr = pattern;
409 	int rv;
410 
411 	/* Protect a single {}, for find(1), like csh */
412 	if (pattern[0] == BG_LBRACE && pattern[1] == BG_RBRACE && pattern[2] == BG_EOS)
413 		return glob0(pattern, pglob);
414 
415 	while ((ptr = (const Char *) g_strchr((Char *) ptr, BG_LBRACE)) != NULL)
416 		if (!globexp2(ptr, pattern, pglob, &rv))
417 			return rv;
418 
419 	return glob0(pattern, pglob);
420 }
421 
422 
423 /*
424  * Recursive brace globbing helper. Tries to expand a single brace.
425  * If it succeeds then it invokes globexp1 with the new pattern.
426  * If it fails then it tries to glob the rest of the pattern and returns.
427  */
428 static int
429 globexp2(const Char *ptr, const Char *pattern,
430 	 glob_t *pglob, int *rv)
431 {
432 	int     i;
433 	Char   *lm, *ls;
434 	const Char *pe, *pm, *pl;
435 	Char    patbuf[MAXPATHLEN];
436 
437 	/* copy part up to the brace */
438 	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
439 		;
440 	*lm = BG_EOS;
441 	ls = lm;
442 
443 	/* Find the balanced brace */
444 	for (i = 0, pe = ++ptr; *pe; pe++)
445 		if (*pe == BG_LBRACKET) {
446 			/* Ignore everything between [] */
447 			for (pm = pe++; *pe != BG_RBRACKET && *pe != BG_EOS; pe++)
448 				;
449 			if (*pe == BG_EOS) {
450 				/*
451 				 * We could not find a matching BG_RBRACKET.
452 				 * Ignore and just look for BG_RBRACE
453 				 */
454 				pe = pm;
455 			}
456 		} else if (*pe == BG_LBRACE)
457 			i++;
458 		else if (*pe == BG_RBRACE) {
459 			if (i == 0)
460 				break;
461 			i--;
462 		}
463 
464 	/* Non matching braces; just glob the pattern */
465 	if (i != 0 || *pe == BG_EOS) {
466 		*rv = glob0(patbuf, pglob);
467 		return 0;
468 	}
469 
470 	for (i = 0, pl = pm = ptr; pm <= pe; pm++) {
471 		switch (*pm) {
472 		case BG_LBRACKET:
473 			/* Ignore everything between [] */
474 			for (pl = pm++; *pm != BG_RBRACKET && *pm != BG_EOS; pm++)
475 				;
476 			if (*pm == BG_EOS) {
477 				/*
478 				 * We could not find a matching BG_RBRACKET.
479 				 * Ignore and just look for BG_RBRACE
480 				 */
481 				pm = pl;
482 			}
483 			break;
484 
485 		case BG_LBRACE:
486 			i++;
487 			break;
488 
489 		case BG_RBRACE:
490 			if (i) {
491 				i--;
492 				break;
493 			}
494 			/* FALLTHROUGH */
495 		case BG_COMMA:
496 			if (i && *pm == BG_COMMA)
497 				break;
498 			else {
499 				/* Append the current string */
500 				for (lm = ls; (pl < pm); *lm++ = *pl++)
501 					;
502 
503 				/*
504 				 * Append the rest of the pattern after the
505 				 * closing brace
506 				 */
507 				for (pl = pe + 1; (*lm++ = *pl++) != BG_EOS; )
508 					;
509 
510 				/* Expand the current pattern */
511 #ifdef GLOB_DEBUG
512 				qprintf("globexp2:", patbuf);
513 #endif /* GLOB_DEBUG */
514 				*rv = globexp1(patbuf, pglob);
515 
516 				/* move after the comma, to the next string */
517 				pl = pm + 1;
518 			}
519 			break;
520 
521 		default:
522 			break;
523 		}
524 	}
525 	*rv = 0;
526 	return 0;
527 }
528 
529 
530 
531 /*
532  * expand tilde from the passwd file.
533  */
534 static const Char *
535 globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob)
536 {
537 	char *h;
538 	const Char *p;
539 	Char *b, *eb;
540 
541 	if (*pattern != BG_TILDE || !(pglob->gl_flags & GLOB_TILDE))
542 		return pattern;
543 
544 	/* Copy up to the end of the string or / */
545 	eb = &patbuf[patbuf_len - 1];
546 	for (p = pattern + 1, h = (char *) patbuf;
547 	     h < (char*)eb && *p && *p != BG_SLASH; *h++ = (char)*p++)
548 		;
549 
550 	*h = BG_EOS;
551 
552 #if 0
553 	if (h == (char *)eb)
554 		return what;
555 #endif
556 
557 	if (((char *) patbuf)[0] == BG_EOS) {
558 		/*
559 		 * handle a plain ~ or ~/ by expanding $HOME
560 		 * first and then trying the password file
561 		 */
562 		if ((h = getenv("HOME")) == NULL) {
563 #ifdef HAS_PASSWD
564 			struct passwd *pwd;
565 			if ((pwd = getpwuid(getuid())) == NULL)
566 				return pattern;
567 			else
568 				h = pwd->pw_dir;
569 #else
570                         return pattern;
571 #endif
572 		}
573 	} else {
574 		/*
575 		 * Expand a ~user
576 		 */
577 #ifdef HAS_PASSWD
578 		struct passwd *pwd;
579 		if ((pwd = getpwnam((char*) patbuf)) == NULL)
580 			return pattern;
581 		else
582 			h = pwd->pw_dir;
583 #else
584                 return pattern;
585 #endif
586 	}
587 
588 	/* Copy the home directory */
589 	for (b = patbuf; b < eb && *h; *b++ = *h++)
590 		;
591 
592 	/* Append the rest of the pattern */
593 	while (b < eb && (*b++ = *p++) != BG_EOS)
594 		;
595 	*b = BG_EOS;
596 
597 	return patbuf;
598 }
599 
600 
601 /*
602  * The main glob() routine: compiles the pattern (optionally processing
603  * quotes), calls glob1() to do the real pattern matching, and finally
604  * sorts the list (unless unsorted operation is requested).  Returns 0
605  * if things went well, nonzero if errors occurred.  It is not an error
606  * to find no matches.
607  */
608 static int
609 glob0(const Char *pattern, glob_t *pglob)
610 {
611 	const Char *qpat, *qpatnext;
612 	int c, err, oldflags, oldpathc;
613 	Char *bufnext, patbuf[MAXPATHLEN];
614 	size_t limit = 0;
615 
616 #ifdef MACOS_TRADITIONAL
617 	if ( (*pattern == BG_TILDE) && (pglob->gl_flags & GLOB_TILDE) ) {
618 		return(globextend(pattern, pglob, &limit));
619 	}
620 #endif
621 
622 	qpat = globtilde(pattern, patbuf, MAXPATHLEN, pglob);
623 	qpatnext = qpat;
624 	oldflags = pglob->gl_flags;
625 	oldpathc = pglob->gl_pathc;
626 	bufnext = patbuf;
627 
628 	/* We don't need to check for buffer overflow any more. */
629 	while ((c = *qpatnext++) != BG_EOS) {
630 		switch (c) {
631 		case BG_LBRACKET:
632 			c = *qpatnext;
633 			if (c == BG_NOT)
634 				++qpatnext;
635 			if (*qpatnext == BG_EOS ||
636 			    g_strchr((Char *) qpatnext+1, BG_RBRACKET) == NULL) {
637 				*bufnext++ = BG_LBRACKET;
638 				if (c == BG_NOT)
639 					--qpatnext;
640 				break;
641 			}
642 			*bufnext++ = M_SET;
643 			if (c == BG_NOT)
644 				*bufnext++ = M_NOT;
645 			c = *qpatnext++;
646 			do {
647 				*bufnext++ = CHAR(c);
648 				if (*qpatnext == BG_RANGE &&
649 				    (c = qpatnext[1]) != BG_RBRACKET) {
650 					*bufnext++ = M_RNG;
651 					*bufnext++ = CHAR(c);
652 					qpatnext += 2;
653 				}
654 			} while ((c = *qpatnext++) != BG_RBRACKET);
655 			pglob->gl_flags |= GLOB_MAGCHAR;
656 			*bufnext++ = M_END;
657 			break;
658 		case BG_QUESTION:
659 			pglob->gl_flags |= GLOB_MAGCHAR;
660 			*bufnext++ = M_ONE;
661 			break;
662 		case BG_STAR:
663 			pglob->gl_flags |= GLOB_MAGCHAR;
664 			/* collapse adjacent stars to one,
665 			 * to avoid exponential behavior
666 			 */
667 			if (bufnext == patbuf || bufnext[-1] != M_ALL)
668 				*bufnext++ = M_ALL;
669 			break;
670 		default:
671 			*bufnext++ = CHAR(c);
672 			break;
673 		}
674 	}
675 	*bufnext = BG_EOS;
676 #ifdef GLOB_DEBUG
677 	qprintf("glob0:", patbuf);
678 #endif /* GLOB_DEBUG */
679 
680 	if ((err = glob1(patbuf, patbuf+MAXPATHLEN-1, pglob, &limit)) != 0) {
681 		pglob->gl_flags = oldflags;
682 		return(err);
683 	}
684 
685 	/*
686 	 * If there was no match we are going to append the pattern
687 	 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
688 	 * and the pattern did not contain any magic characters
689 	 * GLOB_NOMAGIC is there just for compatibility with csh.
690 	 */
691 	if (pglob->gl_pathc == oldpathc &&
692 	    ((pglob->gl_flags & GLOB_NOCHECK) ||
693 	      ((pglob->gl_flags & GLOB_NOMAGIC) &&
694 	       !(pglob->gl_flags & GLOB_MAGCHAR))))
695 	{
696 #ifdef GLOB_DEBUG
697 		printf("calling globextend from glob0\n");
698 #endif /* GLOB_DEBUG */
699 		pglob->gl_flags = oldflags;
700 		return(globextend(qpat, pglob, &limit));
701         }
702 	else if (!(pglob->gl_flags & GLOB_NOSORT))
703 		qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
704 		    pglob->gl_pathc - oldpathc, sizeof(char *),
705 		    (pglob->gl_flags & (GLOB_ALPHASORT|GLOB_NOCASE))
706 			? ci_compare : compare);
707 	pglob->gl_flags = oldflags;
708 	return(0);
709 }
710 
711 static int
712 ci_compare(const void *p, const void *q)
713 {
714 	const char *pp = *(const char **)p;
715 	const char *qq = *(const char **)q;
716 	int ci;
717 	while (*pp && *qq) {
718 		if (toLOWER(*pp) != toLOWER(*qq))
719 			break;
720 		++pp;
721 		++qq;
722 	}
723 	ci = toLOWER(*pp) - toLOWER(*qq);
724 	if (ci == 0)
725 		return compare(p, q);
726 	return ci;
727 }
728 
729 static int
730 compare(const void *p, const void *q)
731 {
732 	return(strcmp(*(char **)p, *(char **)q));
733 }
734 
735 static int
736 glob1(Char *pattern, Char *pattern_last, glob_t *pglob, size_t *limitp)
737 {
738 	Char pathbuf[MAXPATHLEN];
739 
740 	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
741 	if (*pattern == BG_EOS)
742 		return(0);
743 	return(glob2(pathbuf, pathbuf+MAXPATHLEN-1,
744 		     pathbuf, pathbuf+MAXPATHLEN-1,
745 		     pattern, pattern_last, pglob, limitp));
746 }
747 
748 /*
749  * The functions glob2 and glob3 are mutually recursive; there is one level
750  * of recursion for each segment in the pattern that contains one or more
751  * meta characters.
752  */
753 static int
754 glob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
755       Char *pattern, Char *pattern_last, glob_t *pglob, size_t *limitp)
756 {
757 	Stat_t sb;
758 	Char *p, *q;
759 	int anymeta;
760 
761 	/*
762 	 * Loop over pattern segments until end of pattern or until
763 	 * segment with meta character found.
764 	 */
765 	for (anymeta = 0;;) {
766 		if (*pattern == BG_EOS) {		/* End of pattern? */
767 			*pathend = BG_EOS;
768 			if (g_lstat(pathbuf, &sb, pglob))
769 				return(0);
770 
771 			if (((pglob->gl_flags & GLOB_MARK) &&
772 			    pathend[-1] != BG_SEP
773 #ifdef DOSISH
774 			    && pathend[-1] != BG_SEP2
775 #endif
776 			    ) && (S_ISDIR(sb.st_mode) ||
777 				  (S_ISLNK(sb.st_mode) &&
778 			    (g_stat(pathbuf, &sb, pglob) == 0) &&
779 			    S_ISDIR(sb.st_mode)))) {
780 #ifdef MACOS_TRADITIONAL
781 				short err;
782 				err = glob_mark_Mac(pathbuf, pathend, pathend_last);
783 				if (err)
784 					return (err);
785 #else
786 				if (pathend+1 > pathend_last)
787 					return (1);
788 				*pathend++ = BG_SEP;
789 				*pathend = BG_EOS;
790 #endif
791 			}
792 			++pglob->gl_matchc;
793 #ifdef GLOB_DEBUG
794                         printf("calling globextend from glob2\n");
795 #endif /* GLOB_DEBUG */
796 			return(globextend(pathbuf, pglob, limitp));
797 		}
798 
799 		/* Find end of next segment, copy tentatively to pathend. */
800 		q = pathend;
801 		p = pattern;
802 		while (*p != BG_EOS && *p != BG_SEP
803 #ifdef DOSISH
804 		       && *p != BG_SEP2
805 #endif
806 		       ) {
807 			if (ismeta(*p))
808 				anymeta = 1;
809 			if (q+1 > pathend_last)
810 				return (1);
811 			*q++ = *p++;
812 		}
813 
814 		if (!anymeta) {		/* No expansion, do next segment. */
815 			pathend = q;
816 			pattern = p;
817 			while (*pattern == BG_SEP
818 #ifdef DOSISH
819 			       || *pattern == BG_SEP2
820 #endif
821 			       ) {
822 				if (pathend+1 > pathend_last)
823 					return (1);
824 				*pathend++ = *pattern++;
825 			}
826 		} else
827 			/* Need expansion, recurse. */
828 			return(glob3(pathbuf, pathbuf_last, pathend,
829 				     pathend_last, pattern, pattern_last,
830 				     p, pattern_last, pglob, limitp));
831 	}
832 	/* NOTREACHED */
833 }
834 
835 static int
836 glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
837       Char *pattern, Char *pattern_last,
838       Char *restpattern, Char *restpattern_last, glob_t *pglob, size_t *limitp)
839 {
840 	register Direntry_t *dp;
841 	DIR *dirp;
842 	int err;
843 	int nocase;
844 	char buf[MAXPATHLEN];
845 
846 	/*
847 	 * The readdirfunc declaration can't be prototyped, because it is
848 	 * assigned, below, to two functions which are prototyped in glob.h
849 	 * and dirent.h as taking pointers to differently typed opaque
850 	 * structures.
851 	 */
852 	Direntry_t *(*readdirfunc)(DIR*);
853 
854 	if (pathend > pathend_last)
855 		return (1);
856 	*pathend = BG_EOS;
857 	errno = 0;
858 
859 #ifdef VMS
860         {
861 		Char *q = pathend;
862 		if (q - pathbuf > 5) {
863 			q -= 5;
864 			if (q[0] == '.' &&
865 			    tolower(q[1]) == 'd' && tolower(q[2]) == 'i' &&
866 			    tolower(q[3]) == 'r' && q[4] == '/')
867 			{
868 				q[0] = '/';
869 				q[1] = BG_EOS;
870 				pathend = q+1;
871 			}
872 		}
873         }
874 #endif
875 
876 #ifdef MACOS_TRADITIONAL
877 	if ((!*pathbuf) && (g_matchVol)) {
878 	    FSSpec spec;
879 	    short index;
880 	    StrFileName vol_name; /* unsigned char[64] on MacOS */
881 
882 	    err = 0;
883 	    nocase = ((pglob->gl_flags & GLOB_NOCASE) != 0);
884 
885 	    /* Get and match a list of volume names */
886 	    for (index = 0; !GetVolInfo(index+1, true, &spec); ++index) {
887 		register U8 *sc;
888 		register Char *dc;
889 
890 		name_f_FSSpec(vol_name, &spec);
891 
892 		/* Initial BG_DOT must be matched literally. */
893 		if (*vol_name == BG_DOT && *pattern != BG_DOT)
894 		    continue;
895 		dc = pathend;
896 		sc = (U8 *) vol_name;
897 		while (dc < pathend_last && (*dc++ = *sc++) != BG_EOS)
898 		    ;
899 		if (dc >= pathend_last) {
900 		    *dc = BG_EOS;
901 		    err = 1;
902 		    break;
903 		}
904 
905 		if (!match(pathend, pattern, restpattern, nocase)) {
906 		    *pathend = BG_EOS;
907 		    continue;
908 		}
909 		err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
910 		    restpattern, restpattern_last, pglob, limitp);
911 		if (err)
912 		    break;
913 	    }
914 	    return(err);
915 
916 	} else { /* open dir */
917 #endif /* MACOS_TRADITIONAL */
918 
919 	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
920 		/* TODO: don't call for ENOENT or ENOTDIR? */
921 		if (pglob->gl_errfunc) {
922 			if (g_Ctoc(pathbuf, buf, sizeof(buf)))
923 				return (GLOB_ABEND);
924 			if (pglob->gl_errfunc(buf, errno) ||
925 			    (pglob->gl_flags & GLOB_ERR))
926 				return (GLOB_ABEND);
927 		}
928 		return(0);
929 	}
930 
931 	err = 0;
932 	nocase = ((pglob->gl_flags & GLOB_NOCASE) != 0);
933 
934 	/* Search directory for matching names. */
935 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
936 		readdirfunc = (Direntry_t *(*)(DIR *))pglob->gl_readdir;
937 	else
938 		readdirfunc = my_readdir;
939 	while ((dp = (*readdirfunc)(dirp))) {
940 		register U8 *sc;
941 		register Char *dc;
942 
943 		/* Initial BG_DOT must be matched literally. */
944 		if (dp->d_name[0] == BG_DOT && *pattern != BG_DOT)
945 			continue;
946 		dc = pathend;
947 		sc = (U8 *) dp->d_name;
948 		while (dc < pathend_last && (*dc++ = *sc++) != BG_EOS)
949 			;
950 		if (dc >= pathend_last) {
951 			*dc = BG_EOS;
952 			err = 1;
953 			break;
954 		}
955 
956 		if (!match(pathend, pattern, restpattern, nocase)) {
957 			*pathend = BG_EOS;
958 			continue;
959 		}
960 		err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
961 			    restpattern, restpattern_last, pglob, limitp);
962 		if (err)
963 			break;
964 	}
965 
966 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
967 		(*pglob->gl_closedir)(dirp);
968 	else
969 		PerlDir_close(dirp);
970 	return(err);
971 
972 #ifdef MACOS_TRADITIONAL
973 	}
974 #endif
975 }
976 
977 
978 /*
979  * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
980  * add the new item, and update gl_pathc.
981  *
982  * This assumes the BSD realloc, which only copies the block when its size
983  * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
984  * behavior.
985  *
986  * Return 0 if new item added, error code if memory couldn't be allocated.
987  *
988  * Invariant of the glob_t structure:
989  *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
990  *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
991  */
992 static int
993 globextend(const Char *path, glob_t *pglob, size_t *limitp)
994 {
995 	register char **pathv;
996 	register int i;
997 	STRLEN newsize, len;
998 	char *copy;
999 	const Char *p;
1000 
1001 #ifdef GLOB_DEBUG
1002 	printf("Adding ");
1003         for (p = path; *p; p++)
1004                 (void)printf("%c", CHAR(*p));
1005         printf("\n");
1006 #endif /* GLOB_DEBUG */
1007 
1008 	newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
1009 	if (pglob->gl_pathv)
1010 		pathv = Renew(pglob->gl_pathv,newsize,char*);
1011 	else
1012 		New(0,pathv,newsize,char*);
1013 	if (pathv == NULL) {
1014 		if (pglob->gl_pathv) {
1015 			Safefree(pglob->gl_pathv);
1016 			pglob->gl_pathv = NULL;
1017 		}
1018 		return(GLOB_NOSPACE);
1019 	}
1020 
1021 	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
1022 		/* first time around -- clear initial gl_offs items */
1023 		pathv += pglob->gl_offs;
1024 		for (i = pglob->gl_offs; --i >= 0; )
1025 			*--pathv = NULL;
1026 	}
1027 	pglob->gl_pathv = pathv;
1028 
1029 	for (p = path; *p++;)
1030 		;
1031 	len = (STRLEN)(p - path);
1032 	*limitp += len;
1033 	New(0, copy, p-path, char);
1034 	if (copy != NULL) {
1035 		if (g_Ctoc(path, copy, len)) {
1036 			Safefree(copy);
1037 			return(GLOB_NOSPACE);
1038 		}
1039 		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
1040 	}
1041 	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
1042 
1043 	if ((pglob->gl_flags & GLOB_LIMIT) &&
1044 	    newsize + *limitp >= ARG_MAX) {
1045 		errno = 0;
1046 		return(GLOB_NOSPACE);
1047 	}
1048 
1049 	return(copy == NULL ? GLOB_NOSPACE : 0);
1050 }
1051 
1052 
1053 /*
1054  * pattern matching function for filenames.  Each occurrence of the *
1055  * pattern causes a recursion level.
1056  */
1057 static int
1058 match(register Char *name, register Char *pat, register Char *patend, int nocase)
1059 {
1060 	int ok, negate_range;
1061 	Char c, k;
1062 
1063 	while (pat < patend) {
1064 		c = *pat++;
1065 		switch (c & M_MASK) {
1066 		case M_ALL:
1067 			if (pat == patend)
1068 				return(1);
1069 			do
1070 			    if (match(name, pat, patend, nocase))
1071 				    return(1);
1072 			while (*name++ != BG_EOS)
1073 				;
1074 			return(0);
1075 		case M_ONE:
1076 			if (*name++ == BG_EOS)
1077 				return(0);
1078 			break;
1079 		case M_SET:
1080 			ok = 0;
1081 			if ((k = *name++) == BG_EOS)
1082 				return(0);
1083 			if ((negate_range = ((*pat & M_MASK) == M_NOT)) != BG_EOS)
1084 				++pat;
1085 			while (((c = *pat++) & M_MASK) != M_END)
1086 				if ((*pat & M_MASK) == M_RNG) {
1087 					if (nocase) {
1088 						if (tolower(c) <= tolower(k) && tolower(k) <= tolower(pat[1]))
1089 							ok = 1;
1090 					} else {
1091 						if (c <= k && k <= pat[1])
1092 							ok = 1;
1093 					}
1094 					pat += 2;
1095 				} else if (nocase ? (tolower(c) == tolower(k)) : (c == k))
1096 					ok = 1;
1097 			if (ok == negate_range)
1098 				return(0);
1099 			break;
1100 		default:
1101 			k = *name++;
1102 			if (nocase ? (tolower(k) != tolower(c)) : (k != c))
1103 				return(0);
1104 			break;
1105 		}
1106 	}
1107 	return(*name == BG_EOS);
1108 }
1109 
1110 /* Free allocated data belonging to a glob_t structure. */
1111 void
1112 bsd_globfree(glob_t *pglob)
1113 {
1114 	register int i;
1115 	register char **pp;
1116 
1117 	if (pglob->gl_pathv != NULL) {
1118 		pp = pglob->gl_pathv + pglob->gl_offs;
1119 		for (i = pglob->gl_pathc; i--; ++pp)
1120 			if (*pp)
1121 				Safefree(*pp);
1122 		Safefree(pglob->gl_pathv);
1123 		pglob->gl_pathv = NULL;
1124 	}
1125 }
1126 
1127 static DIR *
1128 g_opendir(register Char *str, glob_t *pglob)
1129 {
1130 	char buf[MAXPATHLEN];
1131 
1132 	if (!*str) {
1133 #ifdef MACOS_TRADITIONAL
1134 		strcpy(buf, ":");
1135 #else
1136 		strcpy(buf, ".");
1137 #endif
1138 	} else {
1139 		if (g_Ctoc(str, buf, sizeof(buf)))
1140 			return(NULL);
1141 	}
1142 
1143 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
1144 		return((*pglob->gl_opendir)(buf));
1145 
1146 	return(PerlDir_open(buf));
1147 }
1148 
1149 static int
1150 g_lstat(register Char *fn, Stat_t *sb, glob_t *pglob)
1151 {
1152 	char buf[MAXPATHLEN];
1153 
1154 	if (g_Ctoc(fn, buf, sizeof(buf)))
1155 		return(-1);
1156 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
1157 		return((*pglob->gl_lstat)(buf, sb));
1158 #ifdef HAS_LSTAT
1159 	return(PerlLIO_lstat(buf, sb));
1160 #else
1161 	return(PerlLIO_stat(buf, sb));
1162 #endif /* HAS_LSTAT */
1163 }
1164 
1165 static int
1166 g_stat(register Char *fn, Stat_t *sb, glob_t *pglob)
1167 {
1168 	char buf[MAXPATHLEN];
1169 
1170 	if (g_Ctoc(fn, buf, sizeof(buf)))
1171 		return(-1);
1172 	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
1173 		return((*pglob->gl_stat)(buf, sb));
1174 	return(PerlLIO_stat(buf, sb));
1175 }
1176 
1177 static Char *
1178 g_strchr(Char *str, int ch)
1179 {
1180 	do {
1181 		if (*str == ch)
1182 			return (str);
1183 	} while (*str++);
1184 	return (NULL);
1185 }
1186 
1187 static int
1188 g_Ctoc(register const Char *str, char *buf, STRLEN len)
1189 {
1190 	while (len--) {
1191 		if ((*buf++ = (char)*str++) == BG_EOS)
1192 			return (0);
1193 	}
1194 	return (1);
1195 }
1196 
1197 #ifdef GLOB_DEBUG
1198 static void
1199 qprintf(const char *str, register Char *s)
1200 {
1201 	register Char *p;
1202 
1203 	(void)printf("%s:\n", str);
1204 	for (p = s; *p; p++)
1205 		(void)printf("%c", CHAR(*p));
1206 	(void)printf("\n");
1207 	for (p = s; *p; p++)
1208 		(void)printf("%c", *p & M_PROTECT ? '"' : ' ');
1209 	(void)printf("\n");
1210 	for (p = s; *p; p++)
1211 		(void)printf("%c", ismeta(*p) ? '_' : ' ');
1212 	(void)printf("\n");
1213 }
1214 #endif /* GLOB_DEBUG */
1215 
1216 
1217 #ifdef MACOS_TRADITIONAL
1218 
1219 /* Replace the last occurrence of the pattern ":[^:]+::", e.g. ":lib::",
1220    with a single ':', if possible. It is not an error, if the pattern
1221    doesn't match (we return -1), but if there are two consecutive colons
1222    '::', there must be a preceding ':[^:]+'. Hence,  a volume path like
1223    "HD::" is considered to be an error (we return 1), that is, it can't
1224    be resolved. We return 0 on success.
1225 */
1226 
1227 static short
1228 updir(char *path)
1229 {
1230 	char *pb, *pe, *lastchar;
1231 	char *bgn_mark, *end_mark;
1232 	char *f, *m, *b; /* front, middle, back */
1233 	size_t len;
1234 
1235 	len = strlen(path);
1236 	lastchar = path + (len-1);
1237 	b = lastchar;
1238 	m = lastchar-1;
1239 	f = lastchar-2;
1240 
1241 	/* find a '[^:]::' (e.g. b::) pattern ... */
1242 	while ( !( (*f != BG_SEP) && (*m == BG_SEP) && (*b == BG_SEP) )
1243 	        && (f >= path)) {
1244 		f--;
1245 		m--;
1246 		b--;
1247 	}
1248 
1249 	if (f < path) { /* no (more) match */
1250 		return -1;
1251 	}
1252 
1253 	end_mark = b;
1254 
1255 	/* ... and now find its preceding colon ':' */
1256 	while ((*f != BG_SEP) && (f >= path)) {
1257 		f--;
1258 	}
1259 	if (f < path) {
1260 		/* No preceding colon found, must be a
1261 		   volume path. We can't move up the
1262 		   tree and that's an error */
1263 		return 1;
1264 	}
1265 	bgn_mark = f;
1266 
1267 	/* Shrink path, i.e. exclude all characters between
1268 	   bgn_mark and end_mark */
1269 
1270 	pb = bgn_mark;
1271 	pe = end_mark;
1272 	while (*pb++ = *pe++) ;
1273 	return 0;
1274 }
1275 
1276 
1277 /* Resolve all updirs in pattern. */
1278 
1279 static short
1280 resolve_updirs(char *new_pattern)
1281 {
1282 	short err;
1283 
1284 	do {
1285 		err = updir(new_pattern);
1286 	} while (!err);
1287 	if (err == 1) {
1288 		return NO_UPDIR_ERR;
1289 	}
1290 	return 0;
1291 }
1292 
1293 
1294 /* Remove a trailing colon from the path, but only if it's
1295    not a volume path (e.g. HD:) and not a path consisting
1296    solely of colons. */
1297 
1298 static void
1299 remove_trColon(char *path)
1300 {
1301 	char *lastchar, *lc;
1302 
1303 	/* if path matches the pattern /:[^:]+:$/, we can
1304 	   remove the trailing ':' */
1305 
1306 	lc = lastchar = path + (strlen(path) - 1);
1307 	if (*lastchar == BG_SEP) {
1308 		/* there's a trailing ':', there must be at least
1309 		   one preceding char != ':' and a preceding ':' */
1310 		lc--;
1311 		if ((*lc != BG_SEP) && (lc >= path)) {
1312 			lc--;
1313 		} else {
1314 			return;
1315 		}
1316 		while ((*lc != BG_SEP) && (lc >= path)) {
1317 			lc--;
1318 		}
1319 		if (lc >= path) {
1320 			/* ... there's a preceding ':', we remove
1321 			   the trailing colon */
1322 			*lastchar = BG_EOS;
1323 		}
1324 	}
1325 }
1326 
1327 
1328 /* With the GLOB_MARK flag on, we append a colon, if pathbuf
1329    is a directory. If the directory name contains no colons,
1330    e.g. 'lib', we can't simply append a ':', since this (e.g.
1331    'lib:') is not a valid (relative) path on Mac OS. Instead,
1332    we add a leading _and_ trailing ':'. */
1333 
1334 static short
1335 glob_mark_Mac(Char *pathbuf, Char *pathend, Char *pathend_last)
1336 {
1337 	Char *p, *pe;
1338 	Boolean is_file = true;
1339 
1340 	/* check if pathbuf contains a ':',
1341 	   i.e. is not a file name */
1342 	p = pathbuf;
1343 	while (*p != BG_EOS) {
1344 		if (*p == BG_SEP) {
1345 			is_file = false;
1346 			break;
1347 		}
1348 		p++;
1349 	}
1350 
1351 	if (is_file) {
1352 		if (pathend+2 > pathend_last) {
1353 			return (1);
1354 		}
1355 		/* right shift one char */
1356 		pe = p = pathend;
1357 		p--;
1358 		pathend++;
1359 		while (p >= pathbuf) {
1360 			*pe-- = *p--;
1361 		}
1362 		/* first char becomes a colon */
1363 		*pathbuf = BG_SEP;
1364 		/* append a colon */
1365 		*pathend++ = BG_SEP;
1366 		*pathend = BG_EOS;
1367 
1368 	} else {
1369 		if (pathend+1 > pathend_last) {
1370 			return (1);
1371 		}
1372 		*pathend++ = BG_SEP;
1373 		*pathend = BG_EOS;
1374 	}
1375 	return 0;
1376 }
1377 
1378 
1379 /* Return a FSSpec record for the specified volume
1380    (borrowed from MacPerl.xs). */
1381 
1382 static OSErr
1383 GetVolInfo(short volume, Boolean indexed, FSSpec* spec)
1384 {
1385 	OSErr		err; /* OSErr: 16-bit integer */
1386 	HParamBlockRec	pb;
1387 
1388 	pb.volumeParam.ioNamePtr	= spec->name;
1389 	pb.volumeParam.ioVRefNum	= indexed ? 0 : volume;
1390 	pb.volumeParam.ioVolIndex	= indexed ? volume : 0;
1391 
1392 	if (err = PBHGetVInfoSync(&pb))
1393 		return err;
1394 
1395 	spec->vRefNum	= pb.volumeParam.ioVRefNum;
1396 	spec->parID	= 1;
1397 
1398 	return noErr; /* 0 */
1399 }
1400 
1401 /* Extract a C name from a FSSpec. Note that there are
1402    no leading or trailing colons. */
1403 
1404 static void
1405 name_f_FSSpec(StrFileName name, FSSpec *spec)
1406 {
1407 	unsigned char *nc;
1408 	const short len = spec->name[0];
1409 	short i;
1410 
1411 	/* FSSpec.name is a Pascal string,
1412 	   convert it to C ... */
1413 	nc = name;
1414 	for (i=1; i<=len; i++) {
1415 		*nc++ = spec->name[i];
1416 	}
1417 	*nc = BG_EOS;
1418 }
1419 
1420 #endif /* MACOS_TRADITIONAL */
1421