diff options
Diffstat (limited to 'ext/File/Glob/bsd_glob.c')
-rw-r--r-- | ext/File/Glob/bsd_glob.c | 353 |
1 files changed, 352 insertions, 1 deletions
diff --git a/ext/File/Glob/bsd_glob.c b/ext/File/Glob/bsd_glob.c index fa601fc81f..d0d4a9186a 100644 --- a/ext/File/Glob/bsd_glob.c +++ b/ext/File/Glob/bsd_glob.c @@ -206,6 +206,23 @@ my_readdir(DIR *d) #define my_readdir readdir #endif +#ifdef MACOS_TRADITIONAL +#include <Files.h> +#include <Types.h> +#include <string.h> + +#define NO_UPDIR_ERR 1 /* updir resolving failed */ + +static Boolean g_matchVol; /* global variable */ +static short updir(char *path); +static short resolve_updirs(char *new_pattern); +static void remove_trColon(char *path); +static short glob_mark_Mac(Char *pathbuf, Char *pathend, Char *pathend_last); +static OSErr GetVolInfo(short volume, Boolean indexed, FSSpec *spec); +static void name_f_FSSpec(StrFileName volname, FSSpec *spec); + +#endif + int bsd_glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob) @@ -214,7 +231,15 @@ bsd_glob(const char *pattern, int flags, int c; Char *bufnext, *bufend, patbuf[MAXPATHLEN]; +#ifdef MACOS_TRADITIONAL + char *new_pat, *p, *np; + int err; + size_t len; +#endif + +#ifndef MACOS_TRADITIONAL patnext = (U8 *) pattern; +#endif if (!(flags & GLOB_APPEND)) { pglob->gl_pathc = 0; pglob->gl_pathv = NULL; @@ -246,6 +271,62 @@ bsd_glob(const char *pattern, int flags, patnext += 2; } #endif + +#ifdef MACOS_TRADITIONAL + /* Check if we need to match a volume name (e.g. '*HD:*') */ + g_matchVol = false; + p = (char *) pattern; + if (*p != BG_SEP) { + p++; + while (*p != BG_EOS) { + if (*p == BG_SEP) { + g_matchVol = true; + break; + } + p++; + } + } + + /* Transform the pattern: + * (a) Resolve updirs, e.g. + * '*:t*p::' -> '*:' + * ':a*:tmp::::' -> '::' + * ':base::t*p:::' -> '::' + * '*HD::' -> return 0 (error, quit silently) + * + * (b) Remove a single trailing ':', unless it's a "match volume only" + * pattern like '*HD:'; e.g. + * '*:tmp:' -> '*:tmp' but + * '*HD:' -> '*HD:' + * (If we don't do that, even filenames will have a trailing ':' in + * the result.) + */ + + /* We operate on a copy of the pattern */ + len = strlen(pattern); + New(0, new_pat, len + 1, char); + if (new_pat == NULL) + return (GLOB_NOSPACE); + + p = (char *) pattern; + np = new_pat; + while (*np++ = *p++) ; + + /* Resolve updirs ... */ + err = resolve_updirs(new_pat); + if (err) { + Safefree(new_pat); + /* The pattern is incorrect: tried to move + up above the volume root, see above. + We quit silently. */ + return 0; + } + /* remove trailing colon ... */ + remove_trColon(new_pat); + patnext = (U8 *) new_pat; + +#endif /* MACOS_TRADITIONAL */ + if (flags & GLOB_QUOTE) { /* Protect the quoted characters. */ while (bufnext < bufend && (c = *patnext++) != BG_EOS) @@ -273,10 +354,19 @@ bsd_glob(const char *pattern, int flags, *bufnext++ = c; *bufnext = BG_EOS; +#ifdef MACOS_TRADITIONAL + if (flags & GLOB_BRACE) + err = globexp1(patbuf, pglob); + else + err = glob0(patbuf, pglob); + Safefree(new_pat); + return err; +#else if (flags & GLOB_BRACE) return globexp1(patbuf, pglob); else return glob0(patbuf, pglob); +#endif } /* @@ -582,7 +672,7 @@ glob0(const Char *pattern, glob_t *pglob) } else if (!(pglob->gl_flags & GLOB_NOSORT)) qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, - pglob->gl_pathc - oldpathc, sizeof(char *), + pglob->gl_pathc - oldpathc, sizeof(char *), (pglob->gl_flags & (GLOB_ALPHASORT|GLOB_NOCASE)) ? ci_compare : compare); pglob->gl_flags = oldflags; @@ -658,10 +748,17 @@ glob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, (S_ISLNK(sb.st_mode) && (g_stat(pathbuf, &sb, pglob) == 0) && S_ISDIR(sb.st_mode)))) { +#ifdef MACOS_TRADITIONAL + short err; + err = glob_mark_Mac(pathbuf, pathend, pathend_last); + if (err) + return (err); +#else if (pathend+1 > pathend_last) return (1); *pathend++ = BG_SEP; *pathend = BG_EOS; +#endif } ++pglob->gl_matchc; #ifdef GLOB_DEBUG @@ -746,6 +843,50 @@ glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, } } #endif + +#ifdef MACOS_TRADITIONAL + if ((!*pathbuf) && (g_matchVol)) { + FSSpec spec; + short index; + StrFileName vol_name; /* unsigned char[64] on MacOS */ + + err = 0; + nocase = ((pglob->gl_flags & GLOB_NOCASE) != 0); + + /* Get and match a list of volume names */ + for (index = 0; !GetVolInfo(index+1, true, &spec); ++index) { + register U8 *sc; + register Char *dc; + + name_f_FSSpec(vol_name, &spec); + + /* Initial BG_DOT must be matched literally. */ + if (*vol_name == BG_DOT && *pattern != BG_DOT) + continue; + dc = pathend; + sc = (U8 *) vol_name; + while (dc < pathend_last && (*dc++ = *sc++) != BG_EOS) + ; + if (dc >= pathend_last) { + *dc = BG_EOS; + err = 1; + break; + } + + if (!match(pathend, pattern, restpattern, nocase)) { + *pathend = BG_EOS; + continue; + } + err = glob2(pathbuf, pathbuf_last, --dc, pathend_last, + restpattern, restpattern_last, pglob, limitp); + if (err) + break; + } + return(err); + + } else { /* open dir */ +#endif /* MACOS_TRADITIONAL */ + if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { /* TODO: don't call for ENOENT or ENOTDIR? */ if (pglob->gl_errfunc) { @@ -798,6 +939,10 @@ glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last, else PerlDir_close(dirp); return(err); + +#ifdef MACOS_TRADITIONAL + } +#endif } @@ -1038,3 +1183,209 @@ qprintf(const char *str, register Char *s) (void)printf("\n"); } #endif /* GLOB_DEBUG */ + + +#ifdef MACOS_TRADITIONAL + +/* Replace the last occurence of the pattern ":[^:]+::", e.g. ":lib::", + with a single ':', if possible. It is not an error, if the pattern + doesn't match (we return -1), but if there are two consecutive colons + '::', there must be a preceding ':[^:]+'. Hence, a volume path like + "HD::" is considered to be an error (we return 1), that is, it can't + be resolved. We return 0 on success. +*/ + +static short +updir(char *path) +{ + char *pb, *pe, *lastchar; + char *bgn_mark, *end_mark; + char *f, *m, *b; /* front, middle, back */ + size_t len; + + len = strlen(path); + lastchar = path + (len-1); + b = lastchar; + m = lastchar-1; + f = lastchar-2; + + /* find a '[^:]::' (e.g. b::) pattern ... */ + while ( !( (*f != BG_SEP) && (*m == BG_SEP) && (*b == BG_SEP) ) + && (f >= path)) { + f--; + m--; + b--; + } + + if (f < path) { /* no (more) match */ + return -1; + } + + end_mark = b; + + /* ... and now find its preceding colon ':' */ + while ((*f != BG_SEP) && (f >= path)) { + f--; + } + if (f < path) { + /* No preceding colon found, must be a + volume path. We can't move up the + tree and that's an error */ + return 1; + } + bgn_mark = f; + + /* Shrink path, i.e. exclude all characters between + bgn_mark and end_mark */ + + pb = bgn_mark; + pe = end_mark; + while (*pb++ = *pe++) ; + return 0; +} + + +/* Resolve all updirs in pattern. */ + +static short +resolve_updirs(char *new_pattern) +{ + short err; + + do { + err = updir(new_pattern); + } while (!err); + if (err == 1) { + return NO_UPDIR_ERR; + } + return 0; +} + + +/* Remove a trailing colon from the path, but only if it's + not a volume path (e.g. HD:) and not a path consisting + solely of colons. */ + +static void +remove_trColon(char *path) +{ + char *lastchar, *lc; + + /* if path matches the pattern /:[^:]+:$/, we can + remove the trailing ':' */ + + lc = lastchar = path + (strlen(path) - 1); + if (*lastchar == BG_SEP) { + /* there's a trailing ':', there must be at least + one preceding char != ':' and a preceding ':' */ + lc--; + if ((*lc != BG_SEP) && (lc >= path)) { + lc--; + } else { + return; + } + while ((*lc != BG_SEP) && (lc >= path)) { + lc--; + } + if (lc >= path) { + /* ... there's a preceding ':', we remove + the trailing colon */ + *lastchar = BG_EOS; + } + } +} + + +/* With the GLOB_MARK flag on, we append a colon, if pathbuf + is a directory. If the directory name contains no colons, + e.g. 'lib', we can't simply append a ':', since this (e.g. + 'lib:') is not a valid (relative) path on Mac OS. Instead, + we add a leading _and_ trailing ':'. */ + +static short +glob_mark_Mac(Char *pathbuf, Char *pathend, Char *pathend_last) +{ + Char *p, *pe; + Boolean is_file = true; + + /* check if pathbuf contains a ':', + i.e. is not a file name */ + p = pathbuf; + while (*p != BG_EOS) { + if (*p == BG_SEP) { + is_file = false; + break; + } + p++; + } + + if (is_file) { + if (pathend+2 > pathend_last) { + return (1); + } + /* right shift one char */ + pe = p = pathend; + p--; + pathend++; + while (p >= pathbuf) { + *pe-- = *p--; + } + /* first char becomes a colon */ + *pathbuf = BG_SEP; + /* append a colon */ + *pathend++ = BG_SEP; + *pathend = BG_EOS; + + } else { + if (pathend+1 > pathend_last) { + return (1); + } + *pathend++ = BG_SEP; + *pathend = BG_EOS; + } + return 0; +} + + +/* Return a FSSpec record for the specified volume + (borrowed from MacPerl.xs). */ + +static OSErr +GetVolInfo(short volume, Boolean indexed, FSSpec* spec) +{ + OSErr err; /* OSErr: 16-bit integer */ + HParamBlockRec pb; + + pb.volumeParam.ioNamePtr = spec->name; + pb.volumeParam.ioVRefNum = indexed ? 0 : volume; + pb.volumeParam.ioVolIndex = indexed ? volume : 0; + + if (err = PBHGetVInfoSync(&pb)) + return err; + + spec->vRefNum = pb.volumeParam.ioVRefNum; + spec->parID = 1; + + return noErr; /* 0 */ +} + +/* Extract a C name from a FSSpec. Note that there are + no leading or trailing colons. */ + +static void +name_f_FSSpec(StrFileName name, FSSpec *spec) +{ + unsigned char *nc; + const short len = spec->name[0]; + short i; + + /* FSSpec.name is a Pascal string, + convert it to C ... */ + nc = name; + for (i=1; i<=len; i++) { + *nc++ = spec->name[i]; + } + *nc = BG_EOS; +} + +#endif /* MACOS_TRADITIONAL */ |