summaryrefslogtreecommitdiff
path: root/dotfiles/system/.zsh/modules/Src/glob.c
diff options
context:
space:
mode:
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/glob.c')
-rw-r--r--dotfiles/system/.zsh/modules/Src/glob.c3913
1 files changed, 0 insertions, 3913 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/glob.c b/dotfiles/system/.zsh/modules/Src/glob.c
deleted file mode 100644
index ed2c90b..0000000
--- a/dotfiles/system/.zsh/modules/Src/glob.c
+++ /dev/null
@@ -1,3913 +0,0 @@
-/*
- * glob.c - filename generation
- *
- * This file is part of zsh, the Z shell.
- *
- * Copyright (c) 1992-1997 Paul Falstad
- * All rights reserved.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and to distribute modified versions of this software for any
- * purpose, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * In no event shall Paul Falstad or the Zsh Development Group be liable
- * to any party for direct, indirect, special, incidental, or consequential
- * damages arising out of the use of this software and its documentation,
- * even if Paul Falstad and the Zsh Development Group have been advised of
- * the possibility of such damage.
- *
- * Paul Falstad and the Zsh Development Group specifically disclaim any
- * warranties, including, but not limited to, the implied warranties of
- * merchantability and fitness for a particular purpose. The software
- * provided hereunder is on an "as is" basis, and Paul Falstad and the
- * Zsh Development Group have no obligation to provide maintenance,
- * support, updates, enhancements, or modifications.
- *
- */
-
-#include "zsh.mdh"
-#include "glob.pro"
-
-#if defined(OFF_T_IS_64_BIT) && defined(__GNUC__)
-# define ALIGN64 __attribute__((aligned(8)))
-#else
-# define ALIGN64
-#endif
-
-/* flag for CSHNULLGLOB */
-
-typedef struct gmatch *Gmatch;
-
-struct gmatch {
- /* Metafied file name */
- char *name;
- /* Unmetafied file name; embedded nulls can't occur in file names */
- char *uname;
- /*
- * Array of sort strings: one for each GS_EXEC sort type in
- * the glob qualifiers.
- */
- char **sortstrs;
- off_t size ALIGN64;
- long atime;
- long mtime;
- long ctime;
- long links;
- off_t _size ALIGN64;
- long _atime;
- long _mtime;
- long _ctime;
- long _links;
-#ifdef GET_ST_ATIME_NSEC
- long ansec;
- long _ansec;
-#endif
-#ifdef GET_ST_MTIME_NSEC
- long mnsec;
- long _mnsec;
-#endif
-#ifdef GET_ST_CTIME_NSEC
- long cnsec;
- long _cnsec;
-#endif
-};
-
-#define GS_NAME 1
-#define GS_DEPTH 2
-#define GS_EXEC 4
-
-#define GS_SHIFT_BASE 8
-
-#define GS_SIZE (GS_SHIFT_BASE)
-#define GS_ATIME (GS_SHIFT_BASE << 1)
-#define GS_MTIME (GS_SHIFT_BASE << 2)
-#define GS_CTIME (GS_SHIFT_BASE << 3)
-#define GS_LINKS (GS_SHIFT_BASE << 4)
-
-#define GS_SHIFT 5
-#define GS__SIZE (GS_SIZE << GS_SHIFT)
-#define GS__ATIME (GS_ATIME << GS_SHIFT)
-#define GS__MTIME (GS_MTIME << GS_SHIFT)
-#define GS__CTIME (GS_CTIME << GS_SHIFT)
-#define GS__LINKS (GS_LINKS << GS_SHIFT)
-
-#define GS_DESC (GS_SHIFT_BASE << (2*GS_SHIFT))
-#define GS_NONE (GS_SHIFT_BASE << (2*GS_SHIFT+1))
-
-#define GS_NORMAL (GS_SIZE | GS_ATIME | GS_MTIME | GS_CTIME | GS_LINKS)
-#define GS_LINKED (GS_NORMAL << GS_SHIFT)
-
-/**/
-int badcshglob;
-
-/**/
-int pathpos; /* position in pathbuf (needed by pattern code) */
-
-/*
- * pathname buffer (needed by pattern code).
- * It is currently believed the string in here is stored metafied and is
- * unmetafied temporarily as needed by system calls.
- */
-
-/**/
-char *pathbuf;
-
-typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figure. */
-
-/* modifier for unit conversions */
-
-#define TT_DAYS 0
-#define TT_HOURS 1
-#define TT_MINS 2
-#define TT_WEEKS 3
-#define TT_MONTHS 4
-#define TT_SECONDS 5
-
-#define TT_BYTES 0
-#define TT_POSIX_BLOCKS 1
-#define TT_KILOBYTES 2
-#define TT_MEGABYTES 3
-#define TT_GIGABYTES 4
-#define TT_TERABYTES 5
-
-
-typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *));
-
-struct qual {
- struct qual *next; /* Next qualifier, must match */
- struct qual *or; /* Alternative set of qualifiers to match */
- TestMatchFunc func; /* Function to call to test match */
- off_t data ALIGN64; /* Argument passed to function */
- int sense; /* Whether asserting or negating */
- int amc; /* Flag for which time to test (a, m, c) */
- int range; /* Whether to test <, > or = (as per signum) */
- int units; /* Multiplier for time or size, respectively */
- char *sdata; /* currently only: expression to eval */
-};
-
-/* Prefix, suffix for doing zle trickery */
-
-/**/
-mod_export char *glob_pre, *glob_suf;
-
-/* Element of a glob sort */
-struct globsort {
- /* Sort type */
- int tp;
- /* Sort code to eval, if type is GS_EXEC */
- char *exec;
-};
-
-/* Maximum entries in sort array */
-#define MAX_SORTS (12)
-
-/* struct to easily save/restore current state */
-
-struct globdata {
- int gd_pathpos;
- char *gd_pathbuf;
-
- int gd_matchsz; /* size of matchbuf */
- int gd_matchct; /* number of matches found */
- int gd_pathbufsz; /* size of pathbuf */
- int gd_pathbufcwd; /* where did we chdir()'ed */
- Gmatch gd_matchbuf; /* array of matches */
- Gmatch gd_matchptr; /* &matchbuf[matchct] */
- char *gd_colonmod; /* colon modifiers in qualifier list */
-
- /* Qualifiers pertaining to current pattern */
- struct qual *gd_quals;
-
- /* Other state values for current pattern */
- int gd_qualct, gd_qualorct;
- int gd_range, gd_amc, gd_units;
- int gd_gf_nullglob, gd_gf_markdirs, gd_gf_noglobdots, gd_gf_listtypes;
- int gd_gf_numsort;
- int gd_gf_follow, gd_gf_sorts, gd_gf_nsorts;
- struct globsort gd_gf_sortlist[MAX_SORTS];
- LinkList gd_gf_pre_words, gd_gf_post_words;
-
- char *gd_glob_pre, *gd_glob_suf;
-};
-
-/* The variable with the current globbing state and convenience macros */
-
-static struct globdata curglobdata;
-
-#define matchsz (curglobdata.gd_matchsz)
-#define matchct (curglobdata.gd_matchct)
-#define pathbufsz (curglobdata.gd_pathbufsz)
-#define pathbufcwd (curglobdata.gd_pathbufcwd)
-#define matchbuf (curglobdata.gd_matchbuf)
-#define matchptr (curglobdata.gd_matchptr)
-#define colonmod (curglobdata.gd_colonmod)
-#define quals (curglobdata.gd_quals)
-#define qualct (curglobdata.gd_qualct)
-#define qualorct (curglobdata.gd_qualorct)
-#define g_range (curglobdata.gd_range)
-#define g_amc (curglobdata.gd_amc)
-#define g_units (curglobdata.gd_units)
-#define gf_nullglob (curglobdata.gd_gf_nullglob)
-#define gf_markdirs (curglobdata.gd_gf_markdirs)
-#define gf_noglobdots (curglobdata.gd_gf_noglobdots)
-#define gf_listtypes (curglobdata.gd_gf_listtypes)
-#define gf_numsort (curglobdata.gd_gf_numsort)
-#define gf_follow (curglobdata.gd_gf_follow)
-#define gf_sorts (curglobdata.gd_gf_sorts)
-#define gf_nsorts (curglobdata.gd_gf_nsorts)
-#define gf_sortlist (curglobdata.gd_gf_sortlist)
-#define gf_pre_words (curglobdata.gd_gf_pre_words)
-#define gf_post_words (curglobdata.gd_gf_post_words)
-
-/* and macros for save/restore */
-
-#define save_globstate(N) \
- do { \
- queue_signals(); \
- memcpy(&(N), &curglobdata, sizeof(struct globdata)); \
- (N).gd_pathpos = pathpos; \
- (N).gd_pathbuf = pathbuf; \
- (N).gd_glob_pre = glob_pre; \
- (N).gd_glob_suf = glob_suf; \
- pathbuf = NULL; \
- unqueue_signals(); \
- } while (0)
-
-#define restore_globstate(N) \
- do { \
- queue_signals(); \
- zfree(pathbuf, pathbufsz); \
- memcpy(&curglobdata, &(N), sizeof(struct globdata)); \
- pathpos = (N).gd_pathpos; \
- pathbuf = (N).gd_pathbuf; \
- glob_pre = (N).gd_glob_pre; \
- glob_suf = (N).gd_glob_suf; \
- unqueue_signals(); \
- } while (0)
-
-/* pathname component in filename patterns */
-
-struct complist {
- Complist next;
- Patprog pat;
- int closure; /* 1 if this is a (foo/)# */
- int follow; /* 1 to go thru symlinks */
-};
-
-/* Add a component to pathbuf: This keeps track of how *
- * far we are into a file name, since each path component *
- * must be matched separately. */
-
-/**/
-static void
-addpath(char *s, int l)
-{
- DPUTS(!pathbuf, "BUG: pathbuf not initialised");
- while (pathpos + l + 1 >= pathbufsz)
- pathbuf = zrealloc(pathbuf, pathbufsz *= 2);
- while (l--)
- pathbuf[pathpos++] = *s++;
- pathbuf[pathpos++] = '/';
- pathbuf[pathpos] = '\0';
-}
-
-/* stat the filename s appended to pathbuf. l should be true for lstat, *
- * false for stat. If st is NULL, the file is only checked for existance. *
- * s == "" is treated as s == ".". This is necessary since on most systems *
- * foo/ can be used to reference a non-directory foo. Returns nonzero if *
- * the file does not exists. */
-
-/**/
-static int
-statfullpath(const char *s, struct stat *st, int l)
-{
- char buf[PATH_MAX+1];
-
- DPUTS(strlen(s) + !*s + pathpos - pathbufcwd >= PATH_MAX,
- "BUG: statfullpath(): pathname too long");
- strcpy(buf, pathbuf + pathbufcwd);
- strcpy(buf + pathpos - pathbufcwd, s);
- if (!*s && *buf) {
- /*
- * Don't add the '.' if the path so far is empty, since
- * then we get bogus empty strings inserted as files.
- */
- buf[pathpos - pathbufcwd] = '.';
- buf[pathpos - pathbufcwd + 1] = '\0';
- l = 0;
- }
- unmetafy(buf, NULL);
- if (!st) {
- char lbuf[1];
- return access(buf, F_OK) && (!l || readlink(buf, lbuf, 1) < 0);
- }
- return l ? lstat(buf, st) : stat(buf, st);
-}
-
-/* This may be set by qualifier functions to an array of strings to insert
- * into the list instead of the original string. */
-
-static char **inserts;
-
-/* add a match to the list */
-
-/**/
-static void
-insert(char *s, int checked)
-{
- struct stat buf, buf2, *bp;
- char *news = s;
- int statted = 0;
-
- queue_signals();
- inserts = NULL;
-
- if (gf_listtypes || gf_markdirs) {
- /* Add the type marker to the end of the filename */
- mode_t mode;
- checked = statted = 1;
- if (statfullpath(s, &buf, 1)) {
- unqueue_signals();
- return;
- }
- mode = buf.st_mode;
- if (gf_follow) {
- if (!S_ISLNK(mode) || statfullpath(s, &buf2, 0))
- memcpy(&buf2, &buf, sizeof(buf));
- statted |= 2;
- mode = buf2.st_mode;
- }
- if (gf_listtypes || S_ISDIR(mode)) {
- int ll = strlen(s);
-
- news = (char *) hcalloc(ll + 2);
- strcpy(news, s);
- news[ll] = file_type(mode);
- news[ll + 1] = '\0';
- }
- }
- if (qualct || qualorct) {
- /* Go through the qualifiers, rejecting the file if appropriate */
- struct qual *qo, *qn;
-
- if (!statted && statfullpath(s, &buf, 1)) {
- unqueue_signals();
- return;
- }
- news = dyncat(pathbuf, news);
-
- statted = 1;
- qo = quals;
- for (qn = qo; qn && qn->func;) {
- g_range = qn->range;
- g_amc = qn->amc;
- g_units = qn->units;
- if ((qn->sense & 2) && !(statted & 2)) {
- /* If (sense & 2), we're following links */
- if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0))
- memcpy(&buf2, &buf, sizeof(buf));
- statted |= 2;
- }
- bp = (qn->sense & 2) ? &buf2 : &buf;
- /* Reject the file if the function returned zero *
- * and the sense was positive (sense&1 == 0), or *
- * vice versa. */
- if ((!((qn->func) (news, bp, qn->data, qn->sdata))
- ^ qn->sense) & 1) {
- /* Try next alternative, or return if there are no more */
- if (!(qo = qo->or)) {
- unqueue_signals();
- return;
- }
- qn = qo;
- continue;
- }
- qn = qn->next;
- }
- } else if (!checked) {
- if (statfullpath(s, &buf, 1)) {
- unqueue_signals();
- return;
- }
- statted = 1;
- news = dyncat(pathbuf, news);
- } else
- news = dyncat(pathbuf, news);
-
- while (!inserts || (news = dupstring(*inserts++))) {
- if (colonmod) {
- /* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */
- char *mod = colonmod;
- modify(&news, &mod);
- }
- if (!statted && (gf_sorts & GS_NORMAL)) {
- statfullpath(s, &buf, 1);
- statted = 1;
- }
- if (!(statted & 2) && (gf_sorts & GS_LINKED)) {
- if (statted) {
- if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0))
- memcpy(&buf2, &buf, sizeof(buf));
- } else if (statfullpath(s, &buf2, 0))
- statfullpath(s, &buf2, 1);
- statted |= 2;
- }
- matchptr->name = news;
- if (statted & 1) {
- matchptr->size = buf.st_size;
- matchptr->atime = buf.st_atime;
- matchptr->mtime = buf.st_mtime;
- matchptr->ctime = buf.st_ctime;
- matchptr->links = buf.st_nlink;
-#ifdef GET_ST_ATIME_NSEC
- matchptr->ansec = GET_ST_ATIME_NSEC(buf);
-#endif
-#ifdef GET_ST_MTIME_NSEC
- matchptr->mnsec = GET_ST_MTIME_NSEC(buf);
-#endif
-#ifdef GET_ST_CTIME_NSEC
- matchptr->cnsec = GET_ST_CTIME_NSEC(buf);
-#endif
- }
- if (statted & 2) {
- matchptr->_size = buf2.st_size;
- matchptr->_atime = buf2.st_atime;
- matchptr->_mtime = buf2.st_mtime;
- matchptr->_ctime = buf2.st_ctime;
- matchptr->_links = buf2.st_nlink;
-#ifdef GET_ST_ATIME_NSEC
- matchptr->_ansec = GET_ST_ATIME_NSEC(buf2);
-#endif
-#ifdef GET_ST_MTIME_NSEC
- matchptr->_mnsec = GET_ST_MTIME_NSEC(buf2);
-#endif
-#ifdef GET_ST_CTIME_NSEC
- matchptr->_cnsec = GET_ST_CTIME_NSEC(buf2);
-#endif
- }
- matchptr++;
-
- if (++matchct == matchsz) {
- matchbuf = (Gmatch)zrealloc((char *)matchbuf,
- sizeof(struct gmatch) * (matchsz *= 2));
-
- matchptr = matchbuf + matchct;
- }
- if (!inserts)
- break;
- }
- unqueue_signals();
- return;
-}
-
-/* Do the globbing: scanner is called recursively *
- * with successive bits of the path until we've *
- * tried all of it. */
-
-/**/
-static void
-scanner(Complist q, int shortcircuit)
-{
- Patprog p;
- int closure;
- int pbcwdsav = pathbufcwd;
- int errssofar = errsfound;
- struct dirsav ds;
-
- if (!q || errflag)
- return;
- init_dirsav(&ds);
-
- if ((closure = q->closure)) {
- /* (foo/)# - match zero or more dirs */
- if (q->closure == 2) /* (foo/)## - match one or more dirs */
- q->closure = 1;
- else {
- scanner(q->next, shortcircuit);
- if (shortcircuit && shortcircuit == matchct)
- return;
- }
- }
- p = q->pat;
- /* Now the actual matching for the current path section. */
- if (p->flags & PAT_PURES) {
- /*
- * It's a straight string to the end of the path section.
- */
- char *str = (char *)p + p->startoff;
- int l = p->patmlen;
-
- if (l + !l + pathpos - pathbufcwd >= PATH_MAX) {
- int err;
-
- if (l >= PATH_MAX)
- return;
- err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0);
- if (err == -1)
- return;
- if (err) {
- zerr("current directory lost during glob");
- return;
- }
- pathbufcwd = pathpos;
- }
- if (q->next) {
- /* Not the last path section. Just add it to the path. */
- int oppos = pathpos;
-
- if (!errflag) {
- int add = 1;
-
- if (q->closure && *pathbuf) {
- if (!strcmp(str, "."))
- add = 0;
- else if (!strcmp(str, "..")) {
- struct stat sc, sr;
-
- add = (stat("/", &sr) || stat(unmeta(pathbuf), &sc) ||
- sr.st_ino != sc.st_ino ||
- sr.st_dev != sc.st_dev);
- }
- }
- if (add) {
- addpath(str, l);
- if (!closure || !statfullpath("", NULL, 1)) {
- scanner((q->closure) ? q : q->next, shortcircuit);
- if (shortcircuit && shortcircuit == matchct)
- return;
- }
- pathbuf[pathpos = oppos] = '\0';
- }
- }
- } else {
- if (str[l])
- str = dupstrpfx(str, l);
- insert(str, 0);
- if (shortcircuit && shortcircuit == matchct)
- return;
- }
- } else {
- /* Do pattern matching on current path section. */
- char *fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : ".";
- int dirs = !!q->next;
- DIR *lock = opendir(fn);
- char *subdirs = NULL;
- int subdirlen = 0;
-
- if (lock == NULL)
- return;
- while ((fn = zreaddir(lock, 1)) && !errflag) {
- /* prefix and suffix are zle trickery */
- if (!dirs && !colonmod &&
- ((glob_pre && !strpfx(glob_pre, fn))
- || (glob_suf && !strsfx(glob_suf, fn))))
- continue;
- errsfound = errssofar;
- if (pattry(p, fn)) {
- /* if this name matchs the pattern... */
- if (pbcwdsav == pathbufcwd &&
- strlen(fn) + pathpos - pathbufcwd >= PATH_MAX) {
- int err;
-
- DPUTS(pathpos == pathbufcwd,
- "BUG: filename longer than PATH_MAX");
- err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0);
- if (err == -1)
- break;
- if (err) {
- zerr("current directory lost during glob");
- break;
- }
- pathbufcwd = pathpos;
- }
- if (dirs) {
- int l;
-
- /*
- * If not the last component in the path:
- *
- * If we made an approximation in the new path segment,
- * then it is possible we made too many errors. For
- * example, (ab)#(cb)# will match the directory abcb
- * with one error if allowed to, even though it can
- * match with none. This will stop later parts of the
- * path matching, so we need to check by reducing the
- * maximum number of errors and seeing if the directory
- * still matches. Luckily, this is not a terribly
- * common case, since complex patterns typically occur
- * in the last part of the path which is not affected
- * by this problem.
- */
- if (errsfound > errssofar) {
- forceerrs = errsfound - 1;
- while (forceerrs >= errssofar) {
- errsfound = errssofar;
- if (!pattry(p, fn))
- break;
- forceerrs = errsfound - 1;
- }
- errsfound = forceerrs + 1;
- forceerrs = -1;
- }
- if (closure) {
- /* if matching multiple directories */
- struct stat buf;
-
- if (statfullpath(fn, &buf, !q->follow)) {
- if (errno != ENOENT && errno != EINTR &&
- errno != ENOTDIR && !errflag) {
- zwarn("%e: %s", errno, fn);
- }
- continue;
- }
- if (!S_ISDIR(buf.st_mode))
- continue;
- }
- l = strlen(fn) + 1;
- subdirs = hrealloc(subdirs, subdirlen, subdirlen + l
- + sizeof(int));
- strcpy(subdirs + subdirlen, fn);
- subdirlen += l;
- /* store the count of errors made so far, too */
- memcpy(subdirs + subdirlen, (char *)&errsfound,
- sizeof(int));
- subdirlen += sizeof(int);
- } else {
- /* if the last filename component, just add it */
- insert(fn, 1);
- if (shortcircuit && shortcircuit == matchct) {
- closedir(lock);
- return;
- }
- }
- }
- }
- closedir(lock);
- if (subdirs) {
- int oppos = pathpos;
-
- for (fn = subdirs; fn < subdirs+subdirlen; ) {
- int l = strlen(fn);
- addpath(fn, l);
- fn += l + 1;
- memcpy((char *)&errsfound, fn, sizeof(int));
- fn += sizeof(int);
- /* scan next level */
- scanner((q->closure) ? q : q->next, shortcircuit);
- if (shortcircuit && shortcircuit == matchct)
- return;
- pathbuf[pathpos = oppos] = '\0';
- }
- hrealloc(subdirs, subdirlen, 0);
- }
- }
- if (pbcwdsav < pathbufcwd) {
- if (restoredir(&ds))
- zerr("current directory lost during glob");
- zsfree(ds.dirname);
- if (ds.dirfd >= 0)
- close(ds.dirfd);
- pathbufcwd = pbcwdsav;
- }
- return;
-}
-
-/* This function tokenizes a zsh glob pattern */
-
-/**/
-static Complist
-parsecomplist(char *instr)
-{
- Patprog p1;
- Complist l1;
- char *str;
- int compflags = gf_noglobdots ? (PAT_FILE|PAT_NOGLD) : PAT_FILE;
-
- if (instr[0] == Star && instr[1] == Star) {
- int shortglob = 0;
- if (instr[2] == '/' || (instr[2] == Star && instr[3] == '/')
- || (shortglob = isset(GLOBSTARSHORT))) {
- /* Match any number of directories. */
- int follow;
-
- /* with three stars, follow symbolic links */
- follow = (instr[2] == Star);
- /*
- * With GLOBSTARSHORT, leave a star in place for the
- * pattern inside the directory.
- */
- instr += ((shortglob ? 1 : 3) + follow);
-
- /* Now get the next path component if there is one. */
- l1 = (Complist) zhalloc(sizeof *l1);
- if ((l1->next = parsecomplist(instr)) == NULL) {
- errflag |= ERRFLAG_ERROR;
- return NULL;
- }
- l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL);
- l1->closure = 1; /* ...zero or more times. */
- l1->follow = follow;
- return l1;
- }
- }
-
- /* Parse repeated directories such as (dir/)# and (dir/)## */
- if (*(str = instr) == zpc_special[ZPC_INPAR] &&
- !skipparens(Inpar, Outpar, (char **)&str) &&
- *str == zpc_special[ZPC_HASH] && str[-2] == '/') {
- instr++;
- if (!(p1 = patcompile(instr, compflags, &instr)))
- return NULL;
- if (instr[0] == '/' && instr[1] == Outpar && instr[2] == Pound) {
- int pdflag = 0;
-
- instr += 3;
- if (*instr == Pound) {
- pdflag = 1;
- instr++;
- }
- l1 = (Complist) zhalloc(sizeof *l1);
- l1->pat = p1;
- /* special case (/)# to avoid infinite recursion */
- l1->closure = (*((char *)p1 + p1->startoff)) ? 1 + pdflag : 0;
- l1->follow = 0;
- l1->next = parsecomplist(instr);
- return (l1->pat) ? l1 : NULL;
- }
- } else {
- /* parse single path component */
- if (!(p1 = patcompile(instr, compflags|PAT_FILET, &instr)))
- return NULL;
- /* then do the remaining path components */
- if (*instr == '/' || !*instr) {
- int ef = *instr == '/';
-
- l1 = (Complist) zhalloc(sizeof *l1);
- l1->pat = p1;
- l1->closure = 0;
- l1->next = ef ? parsecomplist(instr+1) : NULL;
- return (ef && !l1->next) ? NULL : l1;
- }
- }
- errflag |= ERRFLAG_ERROR;
- return NULL;
-}
-
-/* turn a string into a Complist struct: this has path components */
-
-/**/
-static Complist
-parsepat(char *str)
-{
- long assert;
- int ignore;
-
- patcompstart();
- /*
- * Check for initial globbing flags, so that they don't form
- * a bogus path component.
- */
- if ((*str == zpc_special[ZPC_INPAR] && str[1] == zpc_special[ZPC_HASH]) ||
- (*str == zpc_special[ZPC_KSH_AT] && str[1] == Inpar &&
- str[2] == zpc_special[ZPC_HASH])) {
- str += (*str == Inpar) ? 2 : 3;
- if (!patgetglobflags(&str, &assert, &ignore))
- return NULL;
- }
-
- /* Now there is no (#X) in front, we can check the path. */
- if (!pathbuf)
- pathbuf = zalloc(pathbufsz = PATH_MAX+1);
- DPUTS(pathbufcwd, "BUG: glob changed directory");
- if (*str == '/') { /* pattern has absolute path */
- str++;
- pathbuf[0] = '/';
- pathbuf[pathpos = 1] = '\0';
- } else /* pattern is relative to pwd */
- pathbuf[pathpos = 0] = '\0';
-
- return parsecomplist(str);
-}
-
-/* get number after qualifier */
-
-/**/
-static off_t
-qgetnum(char **s)
-{
- off_t v = 0;
-
- if (!idigit(**s)) {
- zerr("number expected");
- return 0;
- }
- while (idigit(**s))
- v = v * 10 + *(*s)++ - '0';
- return v;
-}
-
-/* get mode spec after qualifier */
-
-/**/
-static zlong
-qgetmodespec(char **s)
-{
- zlong yes = 0, no = 0, val, mask, t;
- char *p = *s, c, how, end;
-
- if ((c = *p) == '=' || c == Equals || c == '+' || c == '-' ||
- c == '?' || c == Quest || (c >= '0' && c <= '7')) {
- end = 0;
- c = 0;
- } else {
- end = (c == '<' ? '>' :
- (c == '[' ? ']' :
- (c == '{' ? '}' :
- (c == Inang ? Outang :
- (c == Inbrack ? Outbrack :
- (c == Inbrace ? Outbrace : c))))));
- p++;
- }
- do {
- mask = 0;
- while (((c = *p) == 'u' || c == 'g' || c == 'o' || c == 'a') && end) {
- switch (c) {
- case 'o': mask |= 01007; break;
- case 'g': mask |= 02070; break;
- case 'u': mask |= 04700; break;
- case 'a': mask |= 07777; break;
- }
- p++;
- }
- how = ((c == '+' || c == '-') ? c : '=');
- if (c == '+' || c == '-' || c == '=' || c == Equals)
- p++;
- val = 0;
- if (mask) {
- while ((c = *p++) != ',' && c != end) {
- switch (c) {
- case 'x': val |= 00111; break;
- case 'w': val |= 00222; break;
- case 'r': val |= 00444; break;
- case 's': val |= 06000; break;
- case 't': val |= 01000; break;
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- t = ((zlong) c - '0');
- val |= t | (t << 3) | (t << 6);
- break;
- default:
- zerr("invalid mode specification");
- return 0;
- }
- }
- if (how == '=' || how == '+') {
- yes |= val & mask;
- val = ~val;
- }
- if (how == '=' || how == '-')
- no |= val & mask;
- } else if (!(end && c == end) && c != ',' && c) {
- t = 07777;
- while ((c = *p) == '?' || c == Quest ||
- (c >= '0' && c <= '7')) {
- if (c == '?' || c == Quest) {
- t = (t << 3) | 7;
- val <<= 3;
- } else {
- t <<= 3;
- val = (val << 3) | ((zlong) c - '0');
- }
- p++;
- }
- if (end && c != end && c != ',') {
- zerr("invalid mode specification");
- return 0;
- }
- if (how == '=') {
- yes = (yes & ~t) | val;
- no = (no & ~t) | (~val & ~t);
- } else if (how == '+')
- yes |= val;
- else
- no |= val;
- } else {
- zerr("invalid mode specification");
- return 0;
- }
- } while (end && c != end);
-
- *s = p;
- return ((yes & 07777) | ((no & 07777) << 12));
-}
-
-static int
-gmatchcmp(Gmatch a, Gmatch b)
-{
- int i;
- off_t r = 0L;
- struct globsort *s;
- char **asortstrp = NULL, **bsortstrp = NULL;
-
- for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) {
- switch (s->tp & ~GS_DESC) {
- case GS_NAME:
- r = zstrcmp(b->uname, a->uname,
- gf_numsort ? SORTIT_NUMERICALLY : 0);
- break;
- case GS_DEPTH:
- {
- char *aptr = a->name, *bptr = b->name;
- int slasha = 0, slashb = 0;
- /* Count slashes. Trailing slashes don't count. */
- while (*aptr && *aptr == *bptr)
- aptr++, bptr++;
- /* Like I just said... */
- if ((!*aptr || !*bptr) && aptr > a->name && aptr[-1] == '/')
- aptr--, bptr--;
- if (*aptr)
- for (; aptr[1]; aptr++)
- if (*aptr == '/') {
- slasha = 1;
- break;
- }
- if (*bptr)
- for (; bptr[1]; bptr++)
- if (*bptr == '/') {
- slashb = 1;
- break;
- }
- r = slasha - slashb;
- }
- break;
- case GS_EXEC:
- if (!asortstrp) {
- asortstrp = a->sortstrs;
- bsortstrp = b->sortstrs;
- } else {
- asortstrp++;
- bsortstrp++;
- }
- r = zstrcmp(*bsortstrp, *asortstrp,
- gf_numsort ? SORTIT_NUMERICALLY : 0);
- break;
- case GS_SIZE:
- r = b->size - a->size;
- break;
- case GS_ATIME:
- r = a->atime - b->atime;
-#ifdef GET_ST_ATIME_NSEC
- if (!r)
- r = a->ansec - b->ansec;
-#endif
- break;
- case GS_MTIME:
- r = a->mtime - b->mtime;
-#ifdef GET_ST_MTIME_NSEC
- if (!r)
- r = a->mnsec - b->mnsec;
-#endif
- break;
- case GS_CTIME:
- r = a->ctime - b->ctime;
-#ifdef GET_ST_CTIME_NSEC
- if (!r)
- r = a->cnsec - b->cnsec;
-#endif
- break;
- case GS_LINKS:
- r = b->links - a->links;
- break;
- case GS__SIZE:
- r = b->_size - a->_size;
- break;
- case GS__ATIME:
- r = a->_atime - b->_atime;
-#ifdef GET_ST_ATIME_NSEC
- if (!r)
- r = a->_ansec - b->_ansec;
-#endif
- break;
- case GS__MTIME:
- r = a->_mtime - b->_mtime;
-#ifdef GET_ST_MTIME_NSEC
- if (!r)
- r = a->_mnsec - b->_mnsec;
-#endif
- break;
- case GS__CTIME:
- r = a->_ctime - b->_ctime;
-#ifdef GET_ST_CTIME_NSEC
- if (!r)
- r = a->_cnsec - b->_cnsec;
-#endif
- break;
- case GS__LINKS:
- r = b->_links - a->_links;
- break;
- }
- if (r)
- return (s->tp & GS_DESC) ?
- (r < 0L ? 1 : -1) :
- (r > 0L ? 1 : -1);
- }
- return 0;
-}
-
-/*
- * Duplicate a list of qualifiers using the `next' linkage (not the
- * `or' linkage). Return the head element and set *last (if last non-NULL)
- * to point to the last element of the new list. All allocation is on the
- * heap (or off the heap?)
- */
-static struct qual *dup_qual_list(struct qual *orig, struct qual **lastp)
-{
- struct qual *qfirst = NULL, *qlast = NULL;
-
- while (orig) {
- struct qual *qnew = (struct qual *)zhalloc(sizeof(struct qual));
- *qnew = *orig;
- qnew->next = qnew->or = NULL;
-
- if (!qfirst)
- qfirst = qnew;
- if (qlast)
- qlast->next = qnew;
- qlast = qnew;
-
- orig = orig->next;
- }
-
- if (lastp)
- *lastp = qlast;
- return qfirst;
-}
-
-
-/*
- * Get a glob string for execution, following e, P or + qualifiers.
- * Pointer is character after the e, P or +.
- */
-
-/**/
-static char *
-glob_exec_string(char **sp)
-{
- char sav, *tt, *sdata, *s = *sp;
- int plus;
-
- if (s[-1] == '+') {
- plus = 0;
- tt = itype_end(s, IIDENT, 0);
- if (tt == s)
- {
- zerr("missing identifier after `+'");
- return NULL;
- }
- } else {
- tt = get_strarg(s, &plus);
- if (!*tt)
- {
- zerr("missing end of string");
- return NULL;
- }
- }
-
- sav = *tt;
- *tt = '\0';
- sdata = dupstring(s + plus);
- untokenize(sdata);
- *tt = sav;
- if (sav)
- *sp = tt + plus;
- else
- *sp = tt;
-
- return sdata;
-}
-
-/*
- * Insert a glob match.
- * If there were words to prepend given by the P glob qualifier, do so.
- */
-static void
-insert_glob_match(LinkList list, LinkNode next, char *data)
-{
- if (gf_pre_words) {
- LinkNode added;
- for (added = firstnode(gf_pre_words); added; incnode(added)) {
- next = insertlinknode(list, next, dupstring(getdata(added)));
- }
- }
-
- next = insertlinknode(list, next, data);
-
- if (gf_post_words) {
- LinkNode added;
- for (added = firstnode(gf_post_words); added; incnode(added)) {
- next = insertlinknode(list, next, dupstring(getdata(added)));
- }
- }
-}
-
-/*
- * Return
- * 1 if str ends in bare glob qualifiers
- * 2 if str ends in non-bare glob qualifiers (#q)
- * 0 otherwise.
- *
- * str is the string to check.
- * sl is its length (to avoid recalculation).
- * nobareglob is 1 if bare glob qualifiers are not allowed.
- * *sp, if sp is not null, will be a pointer to the opening parenthesis.
- */
-
-/**/
-int
-checkglobqual(char *str, int sl, int nobareglob, char **sp)
-{
- char *s;
- int paren, ret = 1;
-
- if (str[sl - 1] != Outpar)
- return 0;
-
- /* Check these are really qualifiers, not a set of *
- * alternatives or exclusions. We can be more *
- * lenient with an explicit (#q) than with a bare *
- * set of qualifiers. */
- paren = 0;
- for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
- switch (*s) {
- case Outpar:
- paren++; /*FALLTHROUGH*/
- case Bar:
- if (!zpc_disables[ZPC_BAR])
- nobareglob = 1;
- break;
- case Tilde:
- if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
- nobareglob = 1;
- break;
- case Inpar:
- paren--;
- break;
- }
- if (s == str)
- break;
- }
- if (*s != Inpar)
- return 0;
- if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
- if (s[2] != 'q')
- return 0;
- ret = 2;
- } else if (nobareglob)
- return 0;
-
- if (sp)
- *sp = s;
-
- return ret;
-}
-
-/* Main entry point to the globbing code for filename globbing. *
- * np points to a node in the list which will be expanded *
- * into a series of nodes. */
-
-/**/
-void
-zglob(LinkList list, LinkNode np, int nountok)
-{
- struct qual *qo, *qn, *ql;
- LinkNode node = prevnode(np);
- char *str; /* the pattern */
- int sl; /* length of the pattern */
- Complist q; /* pattern after parsing */
- char *ostr = (char *)getdata(np); /* the pattern before the parser */
- /* chops it up */
- int first = 0, end = -1; /* index of first match to return */
- /* and index+1 of the last match */
- struct globdata saved; /* saved glob state */
- int nobareglob = !isset(BAREGLOBQUAL);
- int shortcircuit = 0; /* How many files to match; */
- /* 0 means no limit */
-
- if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) {
- if (!nountok)
- untokenize(ostr);
- return;
- }
- save_globstate(saved);
-
- str = dupstring(ostr);
- uremnode(list, np);
-
- /* quals will hold the complete list of qualifiers (file static). */
- quals = NULL;
- /*
- * qualct and qualorct indicate we have qualifiers in the last
- * alternative, or a set of alternatives, respectively. They
- * are not necessarily an accurate count, however.
- */
- qualct = qualorct = 0;
- /*
- * colonmod is a concatenated list of all colon modifiers found in
- * all sets of qualifiers.
- */
- colonmod = NULL;
- /* The gf_* flags are qualifiers which are applied globally. */
- gf_nullglob = isset(NULLGLOB);
- gf_markdirs = isset(MARKDIRS);
- gf_listtypes = gf_follow = 0;
- gf_noglobdots = unset(GLOBDOTS);
- gf_numsort = isset(NUMERICGLOBSORT);
- gf_sorts = gf_nsorts = 0;
- gf_pre_words = gf_post_words = NULL;
-
- /* Check for qualifiers */
- while (!nobareglob ||
- (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH])) {
- struct qual *newquals;
- char *s;
- int sense, qualsfound;
- off_t data;
- char *sdata, *newcolonmod, *ptr;
- int (*func) _((char *, Statptr, off_t, char *));
-
- /*
- * Initialise state variables for current file pattern.
- * newquals is the root for the linked list of all qualifiers.
- * qo is the root of the current list of alternatives.
- * ql is the end of the current alternative where the `next' will go.
- * qn is the current qualifier node to be added.
- *
- * Here is an attempt at a diagram. An `or' is added horizontally
- * to the top line, a `next' at the bottom of the right hand line.
- * `qn' is usually NULL unless a new `or' has just been added.
- *
- * quals -> x -> x -> qo
- * | | |
- * x x x
- * | |
- * x ql
- *
- * In fact, after each loop the complete set is in the file static
- * `quals'. Then, if we have a second set of qualifiers, we merge
- * the lists together. This is only tricky if one or both have an
- * `or' in them; then we need to distribute over all alternatives.
- */
- newquals = qo = qn = ql = NULL;
-
- sl = strlen(str);
- if (!(qualsfound = checkglobqual(str, sl, nobareglob, &s)))
- break;
-
- /* Real qualifiers found. */
- nobareglob = 1;
- sense = 0; /* bit 0 for match (0)/don't match (1) */
- /* bit 1 for follow links (2), don't (0) */
- data = 0; /* Any numerical argument required */
- sdata = NULL; /* Any list argument required */
- newcolonmod = NULL; /* Contains trailing colon modifiers */
-
- str[sl-1] = 0;
- *s++ = 0;
- if (qualsfound == 2)
- s += 2;
- for (ptr = s; *ptr; ptr++)
- if (*ptr == Dash)
- *ptr = '-';
- while (*s && !newcolonmod) {
- func = (int (*) _((char *, Statptr, off_t, char *)))0;
- if (*s == ',') {
- /* A comma separates alternative sets of qualifiers */
- s++;
- sense = 0;
- if (qualct) {
- qn = (struct qual *)hcalloc(sizeof *qn);
- qo->or = qn;
- qo = qn;
- qualorct++;
- qualct = 0;
- ql = NULL;
- }
- } else {
- switch (*s++) {
- case ':':
- /* Remaining arguments are history-type *
- * colon substitutions, handled separately. */
- newcolonmod = s - 1;
- untokenize(newcolonmod);
- if (colonmod) {
- /* remember we're searching backwards */
- colonmod = dyncat(newcolonmod, colonmod);
- } else
- colonmod = newcolonmod;
- break;
- case Hat:
- case '^':
- /* Toggle sense: go from positive to *
- * negative match and vice versa. */
- sense ^= 1;
- break;
- case '-':
- case Dash:
- /* Toggle matching of symbolic links */
- sense ^= 2;
- break;
- case '@':
- /* Match symbolic links */
- func = qualislnk;
- break;
- case Equals:
- case '=':
- /* Match sockets */
- func = qualissock;
- break;
- case 'p':
- /* Match named pipes */
- func = qualisfifo;
- break;
- case '/':
- /* Match directories */
- func = qualisdir;
- break;
- case '.':
- /* Match regular files */
- func = qualisreg;
- break;
- case '%':
- /* Match special files: block, *
- * character or any device */
- if (*s == 'b')
- s++, func = qualisblk;
- else if (*s == 'c')
- s++, func = qualischr;
- else
- func = qualisdev;
- break;
- case Star:
- /* Match executable plain files */
- func = qualiscom;
- break;
- case 'R':
- /* Match world-readable files */
- func = qualflags;
- data = 0004;
- break;
- case 'W':
- /* Match world-writeable files */
- func = qualflags;
- data = 0002;
- break;
- case 'X':
- /* Match world-executable files */
- func = qualflags;
- data = 0001;
- break;
- case 'A':
- func = qualflags;
- data = 0040;
- break;
- case 'I':
- func = qualflags;
- data = 0020;
- break;
- case 'E':
- func = qualflags;
- data = 0010;
- break;
- case 'r':
- /* Match files readable by current process */
- func = qualflags;
- data = 0400;
- break;
- case 'w':
- /* Match files writeable by current process */
- func = qualflags;
- data = 0200;
- break;
- case 'x':
- /* Match files executable by current process */
- func = qualflags;
- data = 0100;
- break;
- case 's':
- /* Match setuid files */
- func = qualflags;
- data = 04000;
- break;
- case 'S':
- /* Match setgid files */
- func = qualflags;
- data = 02000;
- break;
- case 't':
- func = qualflags;
- data = 01000;
- break;
- case 'd':
- /* Match device files by device number *
- * (as given by stat's st_dev element). */
- func = qualdev;
- data = qgetnum(&s);
- break;
- case 'l':
- /* Match files with the given no. of hard links */
- func = qualnlink;
- g_amc = -1;
- goto getrange;
- case 'U':
- /* Match files owned by effective user ID */
- func = qualuid;
- data = geteuid();
- break;
- case 'G':
- /* Match files owned by effective group ID */
- func = qualgid;
- data = getegid();
- break;
- case 'u':
- /* Match files owned by given user id */
- func = qualuid;
- /* either the actual uid... */
- if (idigit(*s))
- data = qgetnum(&s);
- else {
- /* ... or a user name */
- char sav, *tt;
- int arglen;
-
- /* Find matching delimiters */
- tt = get_strarg(s, &arglen);
- if (!*tt) {
- zerr("missing delimiter for 'u' glob qualifier");
- data = 0;
- } else {
-#ifdef USE_GETPWNAM
- struct passwd *pw;
- sav = *tt;
- *tt = '\0';
-
- if ((pw = getpwnam(s + arglen)))
- data = pw->pw_uid;
- else {
- zerr("unknown username '%s'", s + arglen);
- data = 0;
- }
- *tt = sav;
-#else /* !USE_GETPWNAM */
- sav = *tt;
- *tt = '\0';
- zerr("unable to resolve non-numeric username '%s'", s + arglen);
- *tt = sav;
- data = 0;
-#endif /* !USE_GETPWNAM */
- if (sav)
- s = tt + arglen;
- else
- s = tt;
- }
- }
- break;
- case 'g':
- /* Given gid or group id... works like `u' */
- func = qualgid;
- /* either the actual gid... */
- if (idigit(*s))
- data = qgetnum(&s);
- else {
- /* ...or a delimited group name. */
- char sav, *tt;
- int arglen;
-
- tt = get_strarg(s, &arglen);
- if (!*tt) {
- zerr("missing delimiter for 'g' glob qualifier");
- data = 0;
- } else {
-#ifdef USE_GETGRNAM
- struct group *gr;
- sav = *tt;
- *tt = '\0';
-
- if ((gr = getgrnam(s + arglen)))
- data = gr->gr_gid;
- else {
- zerr("unknown group");
- data = 0;
- }
- *tt = sav;
-#else /* !USE_GETGRNAM */
- sav = *tt;
- zerr("unknown group");
- data = 0;
-#endif /* !USE_GETGRNAM */
- if (sav)
- s = tt + arglen;
- else
- s = tt;
- }
- }
- break;
- case 'f':
- /* Match modes with chmod-spec. */
- func = qualmodeflags;
- data = qgetmodespec(&s);
- break;
- case 'F':
- func = qualnonemptydir;
- break;
- case 'M':
- /* Mark directories with a / */
- if ((gf_markdirs = !(sense & 1)))
- gf_follow = sense & 2;
- break;
- case 'T':
- /* Mark types in a `ls -F' type fashion */
- if ((gf_listtypes = !(sense & 1)))
- gf_follow = sense & 2;
- break;
- case 'N':
- /* Nullglob: remove unmatched patterns. */
- gf_nullglob = !(sense & 1);
- break;
- case 'D':
- /* Glob dots: match leading dots implicitly */
- gf_noglobdots = sense & 1;
- break;
- case 'n':
- /* Numeric glob sort */
- gf_numsort = !(sense & 1);
- break;
- case 'Y':
- {
- /* Short circuit: limit number of matches */
- const char *s_saved = s;
- shortcircuit = !(sense & 1);
- if (shortcircuit) {
- /* Parse the argument. */
- data = qgetnum(&s);
- if ((shortcircuit = data) != data) {
- /* Integer overflow */
- zerr("value too big: Y%s", s_saved);
- restore_globstate(saved);
- return;
- }
- }
- break;
- }
- case 'a':
- /* Access time in given range */
- g_amc = 0;
- func = qualtime;
- goto getrange;
- case 'm':
- /* Modification time in given range */
- g_amc = 1;
- func = qualtime;
- goto getrange;
- case 'c':
- /* Inode creation time in given range */
- g_amc = 2;
- func = qualtime;
- goto getrange;
- case 'L':
- /* File size (Length) in given range */
- func = qualsize;
- g_amc = -1;
- /* Get size multiplier */
- g_units = TT_BYTES;
- if (*s == 'p' || *s == 'P')
- g_units = TT_POSIX_BLOCKS, ++s;
- else if (*s == 'k' || *s == 'K')
- g_units = TT_KILOBYTES, ++s;
- else if (*s == 'm' || *s == 'M')
- g_units = TT_MEGABYTES, ++s;
-#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT)
- else if (*s == 'g' || *s == 'G')
- g_units = TT_GIGABYTES, ++s;
- else if (*s == 't' || *s == 'T')
- g_units = TT_TERABYTES, ++s;
-#endif
- getrange:
- /* Get time multiplier */
- if (g_amc >= 0) {
- g_units = TT_DAYS;
- if (*s == 'h')
- g_units = TT_HOURS, ++s;
- else if (*s == 'm')
- g_units = TT_MINS, ++s;
- else if (*s == 'w')
- g_units = TT_WEEKS, ++s;
- else if (*s == 'M')
- g_units = TT_MONTHS, ++s;
- else if (*s == 's')
- g_units = TT_SECONDS, ++s;
- else if (*s == 'd')
- ++s;
- }
- /* See if it's greater than, equal to, or less than */
- if ((g_range = *s == '+' ? 1 : IS_DASH(*s) ? -1 : 0))
- ++s;
- data = qgetnum(&s);
- break;
-
- case 'o':
- case 'O':
- {
- int t;
- char *send;
-
- if (gf_nsorts == MAX_SORTS) {
- zerr("too many glob sort specifiers");
- restore_globstate(saved);
- return;
- }
-
- /* usually just one character */
- send = s+1;
- switch (*s) {
- case 'n': t = GS_NAME; break;
- case 'L': t = GS_SIZE; break;
- case 'l': t = GS_LINKS; break;
- case 'a': t = GS_ATIME; break;
- case 'm': t = GS_MTIME; break;
- case 'c': t = GS_CTIME; break;
- case 'd': t = GS_DEPTH; break;
- case 'N': t = GS_NONE; break;
- case 'e':
- case '+':
- {
- t = GS_EXEC;
- if ((gf_sortlist[gf_nsorts].exec =
- glob_exec_string(&send)) == NULL)
- {
- restore_globstate(saved);
- return;
- }
- break;
- }
- default:
- zerr("unknown sort specifier");
- restore_globstate(saved);
- return;
- }
- if ((sense & 2) &&
- (t & (GS_SIZE|GS_ATIME|GS_MTIME|GS_CTIME|GS_LINKS)))
- t <<= GS_SHIFT; /* HERE: GS_EXEC? */
- if (t != GS_EXEC) {
- if (gf_sorts & t) {
- zerr("doubled sort specifier");
- restore_globstate(saved);
- return;
- }
- }
- gf_sorts |= t;
- gf_sortlist[gf_nsorts++].tp = t |
- (((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0);
- s = send;
- break;
- }
- case '+':
- case 'e':
- {
- char *tt;
-
- tt = glob_exec_string(&s);
-
- if (tt == NULL) {
- data = 0;
- } else {
- func = qualsheval;
- sdata = tt;
- }
- break;
- }
- case '[':
- case Inbrack:
- {
- char *os = --s;
- struct value v;
-
- v.isarr = SCANPM_WANTVALS;
- v.pm = NULL;
- v.end = -1;
- v.flags = 0;
- if (getindex(&s, &v, 0) || s == os) {
- zerr("invalid subscript");
- restore_globstate(saved);
- return;
- }
- first = v.start;
- end = v.end;
- break;
- }
- case 'P':
- {
- char *tt;
- tt = glob_exec_string(&s);
-
- if (tt != NULL)
- {
- LinkList *words = sense & 1 ? &gf_post_words : &gf_pre_words;
- if (!*words)
- *words = newlinklist();
- addlinknode(*words, tt);
- }
- break;
- }
- default:
- untokenize(--s);
- zerr("unknown file attribute: %c", *s);
- restore_globstate(saved);
- return;
- }
- }
- if (func) {
- /* Requested test is performed by function func */
- if (!qn)
- qn = (struct qual *)hcalloc(sizeof *qn);
- if (ql)
- ql->next = qn;
- ql = qn;
- if (!newquals)
- newquals = qo = qn;
- qn->func = func;
- qn->sense = sense;
- qn->data = data;
- qn->sdata = sdata;
- qn->range = g_range;
- qn->units = g_units;
- qn->amc = g_amc;
-
- qn = NULL;
- qualct++;
- }
- if (errflag) {
- restore_globstate(saved);
- return;
- }
- }
-
- if (quals && newquals) {
- /* Merge previous group of qualifiers with new set. */
- if (quals->or || newquals->or) {
- /* The hard case. */
- struct qual *qorhead = NULL, *qortail = NULL;
- /*
- * Distribute in the most trivial way, by creating
- * all possible combinations of the two sets and chaining
- * these into one long set of alternatives given
- * by qorhead and qortail.
- */
- for (qn = newquals; qn; qn = qn->or) {
- for (qo = quals; qo; qo = qo->or) {
- struct qual *qfirst, *qlast;
- int islast = !qn->or && !qo->or;
- /* Generate first set of qualifiers... */
- if (islast) {
- /* Last time round: don't bother copying. */
- qfirst = qn;
- for (qlast = qfirst; qlast->next;
- qlast = qlast->next)
- ;
- } else
- qfirst = dup_qual_list(qn, &qlast);
- /* ... link into new `or' chain ... */
- if (!qorhead)
- qorhead = qfirst;
- if (qortail)
- qortail->or = qfirst;
- qortail = qfirst;
- /* ... and concatenate second set. */
- qlast->next = islast ? qo : dup_qual_list(qo, NULL);
- }
- }
- quals = qorhead;
- } else {
- /*
- * Easy: we can just chain the qualifiers together.
- * This is an optimisation; the code above will work, too.
- * We retain the original left to right ordering --- remember
- * we are searching for sets of qualifiers from the right.
- */
- qn = newquals;
- for ( ; newquals->next; newquals = newquals->next)
- ;
- newquals->next = quals;
- quals = qn;
- }
- } else if (newquals)
- quals = newquals;
- }
- q = parsepat(str);
- if (!q || errflag) { /* if parsing failed */
- restore_globstate(saved);
- if (unset(BADPATTERN)) {
- if (!nountok)
- untokenize(ostr);
- insertlinknode(list, node, ostr);
- return;
- }
- errflag &= ~ERRFLAG_ERROR;
- zerr("bad pattern: %s", ostr);
- return;
- }
- if (!gf_nsorts) {
- gf_sortlist[0].tp = gf_sorts = (shortcircuit ? GS_NONE : GS_NAME);
- gf_nsorts = 1;
- }
- /* Initialise receptacle for matched files, *
- * expanded by insert() where necessary. */
- matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) *
- sizeof(struct gmatch));
- matchct = 0;
- pattrystart();
-
- /* The actual processing takes place here: matches go into *
- * matchbuf. This is the only top-level call to scanner(). */
- scanner(q, shortcircuit);
-
- /* Deal with failures to match depending on options */
- if (matchct)
- badcshglob |= 2; /* at least one cmd. line expansion O.K. */
- else if (!gf_nullglob) {
- if (isset(CSHNULLGLOB)) {
- badcshglob |= 1; /* at least one cmd. line expansion failed */
- } else if (isset(NOMATCH)) {
- zerr("no matches found: %s", ostr);
- zfree(matchbuf, 0);
- restore_globstate(saved);
- return;
- } else {
- /* treat as an ordinary string */
- untokenize(matchptr->name = dupstring(ostr));
- matchptr++;
- matchct = 1;
- }
- }
-
- if (!(gf_sortlist[0].tp & GS_NONE)) {
- /*
- * Get the strings to use for sorting by executing
- * the code chunk. We allow more than one of these.
- */
- int nexecs = 0;
- struct globsort *sortp;
- struct globsort *lastsortp = gf_sortlist + gf_nsorts;
- Gmatch gmptr;
-
- /* First find out if there are any GS_EXECs, counting them. */
- for (sortp = gf_sortlist; sortp < lastsortp; sortp++)
- {
- if (sortp->tp & GS_EXEC)
- nexecs++;
- }
-
- if (nexecs) {
- Gmatch tmpptr;
- int iexec = 0;
-
- /* Yes; allocate enough space for strings for each */
- for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++)
- tmpptr->sortstrs = (char **)zhalloc(nexecs*sizeof(char*));
-
- /* Loop over each one, incrementing iexec */
- for (sortp = gf_sortlist; sortp < lastsortp; sortp++)
- {
- /* Ignore unless this is a GS_EXEC */
- if (sortp->tp & GS_EXEC) {
- Eprog prog;
-
- if ((prog = parse_string(sortp->exec, 0))) {
- int ef = errflag, lv = lastval;
-
- /* Parsed OK, execute for each name */
- for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++) {
- setsparam("REPLY", ztrdup(tmpptr->name));
- execode(prog, 1, 0, "globsort");
- if (!errflag)
- tmpptr->sortstrs[iexec] =
- dupstring(getsparam("REPLY"));
- else
- tmpptr->sortstrs[iexec] = tmpptr->name;
- }
-
- /* Retain any user interrupt error status */
- errflag = ef | (errflag & ERRFLAG_INT);
- lastval = lv;
- } else {
- /* Failed, let's be safe */
- for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++)
- tmpptr->sortstrs[iexec] = tmpptr->name;
- }
-
- iexec++;
- }
- }
- }
-
- /*
- * Where necessary, create unmetafied version of names
- * for comparison. If no Meta characters just point
- * to original string. All on heap.
- */
- for (gmptr = matchbuf; gmptr < matchptr; gmptr++)
- {
- if (strchr(gmptr->name, Meta))
- {
- int dummy;
- gmptr->uname = dupstring(gmptr->name);
- unmetafy(gmptr->uname, &dummy);
- } else {
- gmptr->uname = gmptr->name;
- }
- }
-
- /* Sort arguments in to lexical (and possibly numeric) order. *
- * This is reversed to facilitate insertion into the list. */
- qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch),
- (int (*) _((const void *, const void *)))gmatchcmp);
- }
-
- if (first < 0) {
- first += matchct;
- if (first < 0)
- first = 0;
- }
- if (end < 0)
- end += matchct + 1;
- else if (end > matchct)
- end = matchct;
- if ((end -= first) > 0) {
- if (gf_sortlist[0].tp & GS_NONE) {
- /* Match list was never reversed, so insert back to front. */
- matchptr = matchbuf + matchct - first - 1;
- while (end-- > 0) {
- /* insert matches in the arg list */
- insert_glob_match(list, node, matchptr->name);
- matchptr--;
- }
- } else {
- matchptr = matchbuf + matchct - first - end;
- while (end-- > 0) {
- /* insert matches in the arg list */
- insert_glob_match(list, node, matchptr->name);
- matchptr++;
- }
- }
- } else if (!badcshglob && !isset(NOMATCH) && matchct == 1) {
- insert_glob_match(list, node, (--matchptr)->name);
- }
- zfree(matchbuf, 0);
-
- restore_globstate(saved);
-}
-
-/* Return the trailing character for marking file types */
-
-/**/
-mod_export char
-file_type(mode_t filemode)
-{
- if(S_ISBLK(filemode))
- return '#';
- else if(S_ISCHR(filemode))
- return '%';
- else if(S_ISDIR(filemode))
- return '/';
- else if(S_ISFIFO(filemode))
- return '|';
- else if(S_ISLNK(filemode))
- return '@';
- else if(S_ISREG(filemode))
- return (filemode & S_IXUGO) ? '*' : ' ';
- else if(S_ISSOCK(filemode))
- return '=';
- else
- return '?';
-}
-
-/* check to see if str is eligible for brace expansion */
-
-/**/
-mod_export int
-hasbraces(char *str)
-{
- char *lbr, *mbr, *comma;
-
- if (isset(BRACECCL)) {
- /* In this case, any properly formed brace expression *
- * will match and expand to the characters in between. */
- int bc, c;
-
- for (bc = 0; (c = *str); ++str)
- if (c == Inbrace) {
- if (!bc && str[1] == Outbrace)
- *str++ = '{', *str = '}';
- else
- bc++;
- } else if (c == Outbrace) {
- if (!bc)
- *str = '}';
- else if (!--bc)
- return 1;
- }
- return 0;
- }
- /* Otherwise we need to look for... */
- lbr = mbr = comma = NULL;
- for (;;) {
- switch (*str++) {
- case Inbrace:
- if (!lbr) {
- if (bracechardots(str-1, NULL, NULL))
- return 1;
- lbr = str - 1;
- if (IS_DASH(*str))
- str++;
- while (idigit(*str))
- str++;
- if (*str == '.' && str[1] == '.') {
- str++; str++;
- if (IS_DASH(*str))
- str++;
- while (idigit(*str))
- str++;
- if (*str == Outbrace &&
- (idigit(lbr[1]) || idigit(str[-1])))
- return 1;
- else if (*str == '.' && str[1] == '.') {
- str++; str++;
- if (IS_DASH(*str))
- str++;
- while (idigit(*str))
- str++;
- if (*str == Outbrace &&
- (idigit(lbr[1]) || idigit(str[-1])))
- return 1;
- }
- }
- } else {
- char *s = --str;
-
- if (skipparens(Inbrace, Outbrace, &str)) {
- *lbr = *s = '{';
- if (comma)
- str = comma;
- if (mbr && mbr < str)
- str = mbr;
- lbr = mbr = comma = NULL;
- } else if (!mbr)
- mbr = s;
- }
- break;
- case Outbrace:
- if (!lbr)
- str[-1] = '}';
- else if (comma)
- return 1;
- else {
- *lbr = '{';
- str[-1] = '}';
- if (mbr)
- str = mbr;
- mbr = lbr = NULL;
- }
- break;
- case Comma:
- if (!lbr)
- str[-1] = ',';
- else if (!comma)
- comma = str - 1;
- break;
- case '\0':
- if (lbr)
- *lbr = '{';
- if (!mbr && !comma)
- return 0;
- if (comma)
- str = comma;
- if (mbr && mbr < str)
- str = mbr;
- lbr = mbr = comma = NULL;
- break;
- }
- }
-}
-
-/* expand stuff like >>*.c */
-
-/**/
-int
-xpandredir(struct redir *fn, LinkList redirtab)
-{
- char *nam;
- struct redir *ff;
- int ret = 0;
- local_list1(fake);
-
- /* Stick the name in a list... */
- init_list1(fake, fn->name);
- /* ...which undergoes all the usual shell expansions */
- prefork(&fake, isset(MULTIOS) ? 0 : PREFORK_SINGLE, NULL);
- /* Globbing is only done for multios. */
- if (!errflag && isset(MULTIOS))
- globlist(&fake, 0);
- if (errflag)
- return 0;
- if (nonempty(&fake) && !nextnode(firstnode(&fake))) {
- /* Just one match, the usual case. */
- char *s = peekfirst(&fake);
- fn->name = s;
- untokenize(s);
- if (fn->type == REDIR_MERGEIN || fn->type == REDIR_MERGEOUT) {
- if (IS_DASH(s[0]) && !s[1])
- fn->type = REDIR_CLOSE;
- else if (s[0] == 'p' && !s[1])
- fn->fd2 = -2;
- else {
- while (idigit(*s))
- s++;
- if (!*s && s > fn->name)
- fn->fd2 = zstrtol(fn->name, NULL, 10);
- else if (fn->type == REDIR_MERGEIN)
- zerr("file number expected");
- else
- fn->type = REDIR_ERRWRITE;
- }
- }
- } else if (fn->type == REDIR_MERGEIN)
- zerr("file number expected");
- else {
- if (fn->type == REDIR_MERGEOUT)
- fn->type = REDIR_ERRWRITE;
- while ((nam = (char *)ugetnode(&fake))) {
- /* Loop over matches, duplicating the *
- * redirection for each file found. */
- ff = (struct redir *) zhalloc(sizeof *ff);
- *ff = *fn;
- ff->name = nam;
- addlinknode(redirtab, ff);
- ret = 1;
- }
- }
- return ret;
-}
-
-/*
- * Check for a brace expansion of the form {<char>..<char>}.
- * On input str must be positioned at an Inbrace, but the sequence
- * of characters beyond that has not necessarily been checked.
- * Return 1 if found else 0.
- *
- * The other parameters are optionaland if the function returns 1 are
- * used to return:
- * - *c1p: the first character in the expansion.
- * - *c2p: the final character in the expansion.
- */
-
-/**/
-static int
-bracechardots(char *str, convchar_t *c1p, convchar_t *c2p)
-{
- convchar_t cstart, cend;
- char *pnext = str + 1, *pconv, convstr[2];
- if (itok(*pnext)) {
- if (*pnext == Inbrace)
- return 0;
- convstr[0] = ztokens[*pnext - Pound];
- convstr[1] = '\0';
- pconv = convstr;
- } else
- pconv = pnext;
- MB_METACHARINIT();
- pnext += MB_METACHARLENCONV(pconv, &cstart);
- if (
-#ifdef MULTIBYTE_SUPPORT
- cstart == WEOF ||
-#else
- !cstart ||
-#endif
- pnext[0] != '.' || pnext[1] != '.')
- return 0;
- pnext += 2;
- if (!*pnext)
- return 0;
- if (itok(*pnext)) {
- if (*pnext == Inbrace)
- return 0;
- convstr[0] = ztokens[*pnext - Pound];
- convstr[1] = '\0';
- pconv = convstr;
- } else
- pconv = pnext;
- MB_METACHARINIT();
- pnext += MB_METACHARLENCONV(pconv, &cend);
- if (
-#ifdef MULTIBYTE_SUPPORT
- cend == WEOF ||
-#else
- !cend ||
-#endif
- *pnext != Outbrace)
- return 0;
- if (c1p)
- *c1p = cstart;
- if (c2p)
- *c2p = cend;
- return 1;
-}
-
-/* brace expansion */
-
-/**/
-mod_export void
-xpandbraces(LinkList list, LinkNode *np)
-{
- LinkNode node = (*np), last = prevnode(node);
- char *str = (char *)getdata(node), *str3 = str, *str2;
- int prev, bc, comma, dotdot;
-
- for (; *str != Inbrace; str++);
- /* First, match up braces and see what we have. */
- for (str2 = str, bc = comma = dotdot = 0; *str2; ++str2)
- if (*str2 == Inbrace)
- ++bc;
- else if (*str2 == Outbrace) {
- if (--bc == 0)
- break;
- } else if (bc == 1) {
- if (*str2 == Comma)
- ++comma; /* we have {foo,bar} */
- else if (*str2 == '.' && str2[1] == '.') {
- dotdot++; /* we have {num1..num2} */
- ++str2;
- }
- }
- DPUTS(bc, "BUG: unmatched brace in xpandbraces()");
- if (!comma && dotdot) {
- /* Expand range like 0..10 numerically: comma or recursive
- brace expansion take precedence. */
- char *dots, *p, *dots2 = NULL;
- LinkNode olast = last;
- /* Get the first number of the range */
- zlong rstart, rend;
- int err = 0, rev = 0, rincr = 1;
- int wid1, wid2, wid3, strp;
- convchar_t cstart, cend;
-
- if (bracechardots(str, &cstart, &cend)) {
- int lenalloc;
- /*
- * This is a character range.
- */
- if (cend < cstart) {
- convchar_t ctmp = cend;
- cend = cstart;
- cstart = ctmp;
- rev = 1;
- }
- uremnode(list, node);
- strp = str - str3;
- lenalloc = strp + strlen(str2+1) + 1;
- do {
-#ifdef MULTIBYTE_SUPPORT
- char *ncptr;
- int nclen;
- mb_charinit();
- ncptr = wcs_nicechar(cend, NULL, NULL);
- nclen = strlen(ncptr);
- p = zhalloc(lenalloc + nclen);
- memcpy(p, str3, strp);
- memcpy(p + strp, ncptr, nclen);
- strcpy(p + strp + nclen, str2 + 1);
-#else
- p = zhalloc(lenalloc + 1);
- memcpy(p, str3, strp);
- sprintf(p + strp, "%c", cend);
- strcat(p + strp, str2 + 1);
-#endif
- insertlinknode(list, last, p);
- if (rev) /* decreasing: add in reverse order. */
- last = nextnode(last);
- } while (cend-- > cstart);
- *np = nextnode(olast);
- return;
- }
-
- /* Get the first number of the range */
- rstart = zstrtol(str+1,&dots,10);
- rend = 0;
- wid1 = (dots - str) - 1;
- wid2 = (str2 - dots) - 2;
- wid3 = 0;
- strp = str - str3;
-
- if (dots == str + 1 || *dots != '.' || dots[1] != '.')
- err++;
- else {
- /* Get the last number of the range */
- rend = zstrtol(dots+2,&p,10);
- if (p == dots+2)
- err++;
- /* check for {num1..num2..incr} */
- if (p != str2) {
- wid2 = (p - dots) - 2;
- dots2 = p;
- if (dotdot == 2 && *p == '.' && p[1] == '.') {
- rincr = zstrtol(p+2, &p, 10);
- wid3 = p - dots2 - 2;
- if (p != str2 || !rincr)
- err++;
- } else
- err++;
- }
- }
- if (!err) {
- /* If either no. begins with a zero, pad the output with *
- * zeroes. Otherwise, set min width to 0 to suppress them.
- * str+1 is the first number in the range, dots+2 the last,
- * and dots2+2 is the increment if that's given. */
- /* TODO: sorry about this */
- int minw = (str[1] == '0' ||
- (IS_DASH(str[1]) && str[2] == '0'))
- ? wid1
- : (dots[2] == '0' ||
- (IS_DASH(dots[2]) && dots[3] == '0'))
- ? wid2
- : (dots2 && (dots2[2] == '0' ||
- (IS_DASH(dots2[2]) && dots2[3] == '0')))
- ? wid3
- : 0;
- if (rincr < 0) {
- /* Handle negative increment */
- rincr = -rincr;
- rev = !rev;
- }
- if (rstart > rend) {
- /* Handle decreasing ranges correctly. */
- zlong rt = rend;
- rend = rstart;
- rstart = rt;
- rev = !rev;
- } else if (rincr > 1) {
- /* when incr > 1, range is aligned to the highest number of str1,
- * compensate for this so that it is aligned to the first number */
- rend -= (rend - rstart) % rincr;
- }
- uremnode(list, node);
- for (; rend >= rstart; rend -= rincr) {
- /* Node added in at end, so do highest first */
- p = dupstring(str3);
-#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
- sprintf(p + strp, "%0*lld", minw, rend);
-#else
- sprintf(p + strp, "%0*ld", minw, (long)rend);
-#endif
- strcat(p + strp, str2 + 1);
- insertlinknode(list, last, p);
- if (rev) /* decreasing: add in reverse order. */
- last = nextnode(last);
- }
- *np = nextnode(olast);
- return;
- }
- }
- if (!comma && isset(BRACECCL)) { /* {a-mnop} */
- /* Here we expand each character to a separate node, *
- * but also ranges of characters like a-m. ccl is a *
- * set of flags saying whether each character is present; *
- * the final list is in lexical order. */
- char ccl[256], *p;
- unsigned char c1, c2;
- unsigned int len, pl;
- int lastch = -1;
-
- uremnode(list, node);
- memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0]));
- for (p = str + 1; p < str2;) {
- if (itok(c1 = *p++))
- c1 = ztokens[c1 - STOUC(Pound)];
- if ((char) c1 == Meta)
- c1 = 32 ^ *p++;
- if (itok(c2 = *p))
- c2 = ztokens[c2 - STOUC(Pound)];
- if ((char) c2 == Meta)
- c2 = 32 ^ p[1];
- if (IS_DASH((char)c1) && lastch >= 0 &&
- p < str2 && lastch <= (int)c2) {
- while (lastch < (int)c2)
- ccl[lastch++] = 1;
- lastch = -1;
- } else
- ccl[lastch = c1] = 1;
- }
- pl = str - str3;
- len = pl + strlen(++str2) + 2;
- for (p = ccl + 256; p-- > ccl;)
- if (*p) {
- c1 = p - ccl;
- if (imeta(c1)) {
- str = hcalloc(len + 1);
- str[pl] = Meta;
- str[pl+1] = c1 ^ 32;
- strcpy(str + pl + 2, str2);
- } else {
- str = hcalloc(len);
- str[pl] = c1;
- strcpy(str + pl + 1, str2);
- }
- memcpy(str, str3, pl);
- insertlinknode(list, last, str);
- }
- *np = nextnode(last);
- return;
- }
- prev = str++ - str3;
- str2++;
- uremnode(list, node);
- node = last;
- /* Finally, normal comma expansion *
- * str1{foo,bar}str2 -> str1foostr2 str1barstr2. *
- * Any number of intervening commas is allowed. */
- for (;;) {
- char *zz, *str4;
- int cnt;
-
- for (str4 = str, cnt = 0; cnt || (*str != Comma && *str !=
- Outbrace); str++) {
- if (*str == Inbrace)
- cnt++;
- else if (*str == Outbrace)
- cnt--;
- DPUTS(!*str, "BUG: illegal brace expansion");
- }
- /* Concatenate the string before the braces (str3), the section *
- * just found (str4) and the text after the braces (str2) */
- zz = (char *) hcalloc(prev + (str - str4) + strlen(str2) + 1);
- ztrncpy(zz, str3, prev);
- strncat(zz, str4, str - str4);
- strcat(zz, str2);
- /* and add this text to the argument list. */
- insertlinknode(list, node, zz);
- incnode(node);
- if (*str != Outbrace)
- str++;
- else
- break;
- }
- *np = nextnode(last);
-}
-
-/* check to see if a matches b (b is not a filename pattern) */
-
-/**/
-int
-matchpat(char *a, char *b)
-{
- Patprog p;
- int ret;
-
- queue_signals(); /* Protect PAT_STATIC */
-
- if (!(p = patcompile(b, PAT_STATIC, NULL))) {
- zerr("bad pattern: %s", b);
- ret = 0;
- } else
- ret = pattry(p, a);
-
- unqueue_signals();
-
- return ret;
-}
-
-/* do the ${foo%%bar}, ${foo#bar} stuff */
-/* please do not laugh at this code. */
-
-/* Having found a match in getmatch, decide what part of string
- * to return. The matched part starts b characters into string imd->ustr
- * and finishes e characters in: 0 <= b <= e <= imd->ulen on input
- * (yes, empty matches should work).
- *
- * imd->flags is a set of the SUB_* matches defined in zsh.h from
- * SUB_MATCH onwards; the lower parts are ignored.
- *
- * imd->replstr is the replacement string for a substitution
- *
- * imd->replstr is metafied and the values put in imd->repllist are metafied.
- */
-
-/**/
-static char *
-get_match_ret(Imatchdata imd, int b, int e)
-{
- char buf[80], *r, *p, *rr, *replstr = imd->replstr;
- int ll = 0, bl = 0, t = 0, add = 0, fl = imd->flags, i;
-
- /* Account for b and e referring to unmetafied string */
- for (p = imd->ustr; p < imd->ustr + b; p++)
- if (imeta(*p))
- add++;
- b += add;
- for (; p < imd->ustr + e; p++)
- if (imeta(*p))
- add++;
- e += add;
-
- /* Everything now refers to metafied lengths. */
- if (replstr || (fl & SUB_LIST)) {
- if (fl & SUB_DOSUBST) {
- replstr = dupstring(replstr);
- singsub(&replstr);
- untokenize(replstr);
- }
- if ((fl & (SUB_GLOBAL|SUB_LIST)) && imd->repllist) {
- /* We are replacing the chunk, just add this to the list */
- Repldata rd = (Repldata)
- ((fl & SUB_LIST) ? zalloc(sizeof(*rd)) : zhalloc(sizeof(*rd)));
- rd->b = b;
- rd->e = e;
- rd->replstr = replstr;
- if (fl & SUB_LIST)
- zaddlinknode(imd->repllist, rd);
- else
- addlinknode(imd->repllist, rd);
- return imd->mstr;
- }
- ll += strlen(replstr);
- }
- if (fl & SUB_MATCH) /* matched portion */
- ll += 1 + (e - b);
- if (fl & SUB_REST) /* unmatched portion */
- ll += 1 + (imd->mlen - (e - b));
- if (fl & SUB_BIND) {
- /* position of start of matched portion */
- sprintf(buf, "%d ", MB_METASTRLEN2END(imd->mstr, 0, imd->mstr+b) + 1);
- ll += (bl = strlen(buf));
- }
- if (fl & SUB_EIND) {
- /* position of end of matched portion */
- sprintf(buf + bl, "%d ",
- MB_METASTRLEN2END(imd->mstr, 0, imd->mstr+e) + 1);
- ll += (bl = strlen(buf));
- }
- if (fl & SUB_LEN) {
- /* length of matched portion */
- sprintf(buf + bl, "%d ", MB_METASTRLEN2END(imd->mstr+b, 0,
- imd->mstr+e));
- ll += (bl = strlen(buf));
- }
- if (bl)
- buf[bl - 1] = '\0';
-
- rr = r = (char *) hcalloc(ll);
-
- if (fl & SUB_MATCH) {
- /* copy matched portion to new buffer */
- for (i = b, p = imd->mstr + b; i < e; i++)
- *rr++ = *p++;
- t = 1;
- }
- if (fl & SUB_REST) {
- /* Copy unmatched portion to buffer. If both portions *
- * requested, put a space in between (why?) */
- if (t)
- *rr++ = ' ';
- /* there may be unmatched bits at both beginning and end of string */
- for (i = 0, p = imd->mstr; i < b; i++)
- *rr++ = *p++;
- if (replstr)
- for (p = replstr; *p; )
- *rr++ = *p++;
- for (i = e, p = imd->mstr + e; i < imd->mlen; i++)
- *rr++ = *p++;
- t = 1;
- }
- *rr = '\0';
- if (bl) {
- /* if there was a buffer (with a numeric result), add it; *
- * if there was other stuff too, stick in a space first. */
- if (t)
- *rr++ = ' ';
- strcpy(rr, buf);
- }
- return r;
-}
-
-static Patprog
-compgetmatch(char *pat, int *flp, char **replstrp)
-{
- Patprog p;
- /*
- * Flags to pattern compiler: use static buffer since we only
- * have one pattern at a time; we will try the must-match test ourselves,
- * so tell the pattern compiler we are scanning.
- */
-
- /* int patflags = PAT_STATIC|PAT_SCAN|PAT_NOANCH;*/
-
- /* Unfortunately, PAT_STATIC doesn't work if we have a replstr with
- * something like ${x#...} in it which will be singsub()ed below because
- * that would overwrite the pattern buffer. */
-
- int patflags = PAT_SCAN|PAT_NOANCH | (*replstrp ? 0 : PAT_STATIC);
-
- /*
- * Search is anchored to the end of the string if we want to match
- * it all, or if we are matching at the end of the string and not
- * using substrings.
- */
- if ((*flp & SUB_ALL) || ((*flp & SUB_END) && !(*flp & SUB_SUBSTR)))
- patflags &= ~PAT_NOANCH;
- p = patcompile(pat, patflags, NULL);
- if (!p) {
- zerr("bad pattern: %s", pat);
- return NULL;
- }
- if (*replstrp) {
- if (p->patnpar || (p->globend & GF_MATCHREF)) {
- /*
- * Either backreferences or match references, so we
- * need to re-substitute replstr each time round.
- */
- *flp |= SUB_DOSUBST;
- } else {
- singsub(replstrp);
- untokenize(*replstrp);
- }
- }
-
- return p;
-}
-
-/*
- * This is called from paramsubst to get the match for ${foo#bar} etc.
- * fl is a set of the SUB_* flags defined in zsh.h
- * *sp points to the string we have to modify. The n'th match will be
- * returned in *sp. The heap is used to get memory for the result string.
- * replstr is the replacement string from a ${.../orig/repl}, in
- * which case pat is the original.
- *
- * n is now ignored unless we are looking for a substring, in
- * which case the n'th match from the start is counted such that
- * there is no more than one match from each position.
- */
-
-/**/
-int
-getmatch(char **sp, char *pat, int fl, int n, char *replstr)
-{
- Patprog p;
-
- if (!(p = compgetmatch(pat, &fl, &replstr)))
- return 1;
-
- return igetmatch(sp, p, fl, n, replstr, NULL);
-}
-
-/*
- * This is the corresponding function for array variables.
- * Matching is done with the same pattern on each element.
- */
-
-/**/
-void
-getmatcharr(char ***ap, char *pat, int fl, int n, char *replstr)
-{
- char **arr = *ap, **pp;
- Patprog p;
-
- if (!(p = compgetmatch(pat, &fl, &replstr)))
- return;
-
- *ap = pp = hcalloc(sizeof(char *) * (arrlen(arr) + 1));
- while ((*pp = *arr++))
- if (igetmatch(pp, p, fl, n, replstr, NULL))
- pp++;
-}
-
-/*
- * Match against str using pattern pp; return a list of
- * Repldata matches in the linked list *repllistp; this is
- * in permanent storage and to be freed by freematchlist()
- */
-
-/**/
-mod_export int
-getmatchlist(char *str, Patprog p, LinkList *repllistp)
-{
- char **sp = &str;
-
- /*
- * We don't care if we have longest or shortest match, but SUB_LONG
- * is cheaper since the pattern code does that by default.
- * We need SUB_GLOBAL to get all matches.
- * We need SUB_SUBSTR to scan through for substrings.
- * We need SUB_LIST to activate the special handling of the list
- * passed in.
- */
- return igetmatch(sp, p, SUB_LONG|SUB_GLOBAL|SUB_SUBSTR|SUB_LIST,
- 0, NULL, repllistp);
-}
-
-static void
-freerepldata(void *ptr)
-{
- zfree(ptr, sizeof(struct repldata));
-}
-
-/**/
-mod_export void
-freematchlist(LinkList repllist)
-{
- freelinklist(repllist, freerepldata);
-}
-
-/**/
-static void
-set_pat_start(Patprog p, int offs)
-{
- /*
- * If we are messing around with the test string by advancing up
- * it from the start, we need to tell the pattern matcher that
- * a start-of-string assertion, i.e. (#s), should fail. Hence
- * we test whether the offset of the real start of string from
- * the actual start, passed as offs, is zero.
- */
- if (offs)
- p->flags |= PAT_NOTSTART;
- else
- p->flags &= ~PAT_NOTSTART;
-}
-
-/**/
-static void
-set_pat_end(Patprog p, char null_me)
-{
- /*
- * If we are messing around with the string by shortening it at the
- * tail, we need to tell the pattern matcher that an end-of-string
- * assertion, i.e. (#e), should fail. Hence we test whether
- * the character null_me about to be zapped is or is not already a null.
- */
- if (null_me)
- p->flags |= PAT_NOTEND;
- else
- p->flags &= ~PAT_NOTEND;
-}
-
-/**/
-#ifdef MULTIBYTE_SUPPORT
-
-/*
- * Increment *tp over character which may be multibyte.
- * Return number of bytes.
- * All unmetafied here.
- */
-
-/**/
-static int iincchar(char **tp, int left)
-{
- char *t = *tp;
- int mbclen = mb_charlenconv(t, left, NULL);
- *tp = t + mbclen;
-
- return mbclen;
-}
-
-/**/
-static int
-igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
- LinkList *repllistp)
-{
- char *s = *sp, *t, *tmatch, *send;
- /*
- * Note that ioff counts (possibly multibyte) characters in the
- * character set (Meta's are not included), while l counts characters in
- * the metafied string.
- *
- * umlen is a counter for (unmetafied) byte lengths---neither characters
- * nor raw byte indices; this is simply an optimisation for allocation.
- * umltot is the full length of the string in this scheme.
- *
- * l is the raw string length, used together with any pointers into
- * the string (typically t).
- */
- int ioff, l = strlen(*sp), matched = 1, umltot = ztrlen(*sp);
- int umlen, nmatches;
- struct patstralloc patstralloc;
- struct imatchdata imd;
-
- (void)patallocstr(p, s, l, umltot, 1, &patstralloc);
- s = patstralloc.alloced;
- DPUTS(!s, "forced patallocstr failed");
- send = s + umltot;
-
- imd.mstr = *sp;
- imd.mlen = l;
- imd.ustr = s;
- imd.ulen = umltot;
- imd.flags = fl;
- imd.replstr = replstr;
- imd.repllist = NULL;
-
- /* perform must-match test for complex closures */
- if (p->mustoff)
- {
- char *muststr = (char *)p + p->mustoff;
-
- matched = 0;
- if (p->patmlen <= umltot)
- {
- for (t = s; t <= send - p->patmlen; t++)
- {
- if (!memcmp(muststr, t, p->patmlen)) {
- matched = 1;
- break;
- }
- }
- }
- }
-
- /* in case we used the prog before... */
- p->flags &= ~(PAT_NOTSTART|PAT_NOTEND);
-
- if (fl & SUB_ALL) {
- int i = matched && pattrylen(p, s, umltot, 0, &patstralloc, 0);
- if (!i) {
- /* Perform under no-match conditions */
- umltot = 0;
- imd.replstr = NULL;
- }
- *sp = get_match_ret(&imd, 0, umltot);
- if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i)))
- return 0;
- return 1;
- }
- if (matched) {
- /*
- * The default behaviour is to match at the start; this
- * is modified by SUB_END and SUB_SUBSTR. SUB_END matches
- * at the end of the string instead of the start. SUB_SUBSTR
- * without SUB_END matches substrings searching from the start;
- * with SUB_END it matches substrings searching from the end.
- *
- * The possibilities are further modified by whether we want the
- * longest (SUB_LONG) or shortest possible match.
- *
- * SUB_START is only used in the case where we are also
- * forcing a match at the end (SUB_END with no SUB_SUBSTR,
- * with or without SUB_LONG), to indicate we should match
- * the entire string.
- */
- switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) {
- case 0:
- case SUB_LONG:
- /*
- * Largest/smallest possible match at head of string.
- * First get the longest match...
- */
- if (pattrylen(p, s, umltot, 0, &patstralloc, 0)) {
- /* patmatchlen returns unmetafied length in this case */
- int mlen = patmatchlen();
- if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) {
- send = s + mlen;
- /*
- * ... now we know whether it's worth looking for the
- * shortest, which we do by brute force.
- */
- mb_charinit();
- for (t = s, umlen = 0; t < send; ) {
- set_pat_end(p, *t);
- if (pattrylen(p, s, umlen, 0, &patstralloc, 0)) {
- mlen = patmatchlen();
- break;
- }
- umlen += iincchar(&t, send - t);
- }
- }
- *sp = get_match_ret(&imd, 0, mlen);
- return 1;
- }
- break;
-
- case SUB_END:
- /*
- * Smallest possible match at tail of string.
- * As we can only be sure we've got wide characters right
- * when going forwards, we need to match at every point
- * until we fail and record the last successful match.
- *
- * It's important that we return the last successful match
- * so that match, mbegin, mend and MATCH, MBEGIN, MEND are
- * correct.
- */
- mb_charinit();
- tmatch = NULL;
- for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) {
- set_pat_start(p, t-s);
- if (pattrylen(p, t, umlen, 0, &patstralloc, ioff))
- tmatch = t;
- if (fl & SUB_START)
- break;
- umlen -= iincchar(&t, send - t);
- }
- if (tmatch) {
- *sp = get_match_ret(&imd, tmatch - s, umltot);
- return 1;
- }
- if (!(fl & SUB_START) && pattrylen(p, s + umltot, 0, 0,
- &patstralloc, ioff)) {
- *sp = get_match_ret(&imd, umltot, umltot);
- return 1;
- }
- break;
-
- case (SUB_END|SUB_LONG):
- /* Largest possible match at tail of string: *
- * move forward along string until we get a match. *
- * Again there's no optimisation. */
- mb_charinit();
- for (ioff = 0, t = s, umlen = umltot; t <= send ; ioff++) {
- set_pat_start(p, t-s);
- if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) {
- *sp = get_match_ret(&imd, t-s, umltot);
- return 1;
- }
- if (fl & SUB_START)
- break;
- if (t == send)
- break;
- umlen -= iincchar(&t, send - t);
- }
- if (!(fl & SUB_START) && pattrylen(p, send, 0, 0,
- &patstralloc, ioff)) {
- *sp = get_match_ret(&imd, umltot, umltot);
- return 1;
- }
- break;
-
- case SUB_SUBSTR:
- /* Smallest at start, but matching substrings. */
- set_pat_start(p, l);
- if (!(fl & SUB_GLOBAL) &&
- pattrylen(p, send, 0, 0, &patstralloc, 0) &&
- !--n) {
- *sp = get_match_ret(&imd, 0, 0);
- return 1;
- } /* fall through */
- case (SUB_SUBSTR|SUB_LONG):
- /* longest or smallest at start with substrings */
- t = s;
- if (fl & SUB_GLOBAL) {
- imd.repllist = (fl & SUB_LIST) ? znewlinklist() : newlinklist();
- if (repllistp)
- *repllistp = imd.repllist;
- }
- ioff = 0; /* offset into string */
- umlen = umltot;
- mb_charinit();
- do {
- /* loop over all matches for global substitution */
- matched = 0;
- for (; t <= send; ioff++) {
- /* Find the longest match from this position. */
- set_pat_start(p, t-s);
- if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) {
- char *mpos = t + patmatchlen();
- if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) {
- char *ptr;
- int umlen2;
- /*
- * If searching for the shortest match,
- * start with a zero length and increase
- * it until we reach the longest possible
- * match, accepting the first successful
- * match.
- */
- for (ptr = t, umlen2 = 0; ptr < mpos;) {
- set_pat_end(p, *ptr);
- if (pattrylen(p, t, umlen2, 0,
- &patstralloc, ioff)) {
- mpos = t + patmatchlen();
- break;
- }
- umlen2 += iincchar(&ptr, mpos - ptr);
- }
- }
- if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) {
- *sp = get_match_ret(&imd, t-s, mpos-s);
- if (mpos == t)
- mpos += mb_charlenconv(mpos, send - mpos, NULL);
- }
- if (!(fl & SUB_GLOBAL)) {
- if (n) {
- /*
- * Looking for a later match: in this case,
- * we can continue looking for matches from
- * the next character, even if it overlaps
- * with what we just found.
- */
- umlen -= iincchar(&t, send - t);
- continue;
- } else {
- return 1;
- }
- }
- /*
- * For a global match, we need to skip the stuff
- * which is already marked for replacement.
- */
- matched = 1;
- if (t == send)
- break;
- while (t < mpos) {
- ioff++;
- umlen -= iincchar(&t, send - t);
- }
- break;
- }
- if (t == send)
- break;
- umlen -= iincchar(&t, send - t);
- }
- } while (matched && t < send);
- /*
- * check if we can match a blank string, if so do it
- * at the start. Goodness knows if this is a good idea
- * with global substitution, so it doesn't happen.
- */
- set_pat_start(p, l);
- if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG &&
- pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) {
- *sp = get_match_ret(&imd, 0, 0);
- return 1;
- }
- break;
-
- case (SUB_END|SUB_SUBSTR):
- case (SUB_END|SUB_LONG|SUB_SUBSTR):
- /* Longest/shortest at end, matching substrings. */
- if (!(fl & SUB_LONG)) {
- set_pat_start(p, l);
- if (pattrylen(p, send, 0, 0, &patstralloc, umltot) &&
- !--n) {
- *sp = get_match_ret(&imd, umltot, umltot);
- return 1;
- }
- }
- /*
- * If multibyte characters are present we need to start from the
- * beginning. This is a bit unpleasant because we can't tell in
- * advance how many times it will match and from where, so if n is
- * greater then 1 we will need to count the number of times it
- * matched and then go through again until we reach the right
- * point. (Either that or record every single match in a list,
- * which isn't stupid; it involves more memory management at this
- * level but less use of the pattern matcher.)
- */
- nmatches = 0;
- tmatch = NULL;
- mb_charinit();
- for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) {
- set_pat_start(p, t-s);
- if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) {
- nmatches++;
- tmatch = t;
- }
- umlen -= iincchar(&t, send - t);
- }
- if (nmatches) {
- char *mpos;
- if (n > 1) {
- /*
- * We need to find the n'th last match.
- */
- n = nmatches - n;
- mb_charinit();
- for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) {
- set_pat_start(p, t-s);
- if (pattrylen(p, t, umlen, 0, &patstralloc, ioff) &&
- !n--) {
- tmatch = t;
- break;
- }
- umlen -= iincchar(&t, send - t);
- }
- }
- mpos = tmatch + patmatchlen();
- /* Look for the shortest match if necessary */
- if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) {
- for (t = tmatch, umlen = 0; t < mpos; ) {
- set_pat_end(p, *t);
- if (pattrylen(p, tmatch, umlen, 0,
- &patstralloc, ioff)) {
- mpos = tmatch + patmatchlen();
- break;
- }
- umlen += iincchar(&t, mpos - t);
- }
- }
- *sp = get_match_ret(&imd, tmatch-s, mpos-s);
- return 1;
- }
- set_pat_start(p, l);
- if ((fl & SUB_LONG) && pattrylen(p, send, 0, 0,
- &patstralloc, umltot) &&
- !--n) {
- *sp = get_match_ret(&imd, umltot, umltot);
- return 1;
- }
- break;
- }
- }
-
- if (imd.repllist && nonempty(imd.repllist)) {
- /* Put all the bits of a global search and replace together. */
- LinkNode nd;
- Repldata rd;
- int lleft;
- char *ptr, *start;
- int i;
-
- /*
- * Use metafied string again.
- * Results from get_match_ret in repllist are all metafied.
- */
- s = *sp;
- if (!(fl & SUB_LIST)) {
- lleft = 0; /* size of returned string */
- i = 0; /* start of last chunk we got from *sp */
- for (nd = firstnode(imd.repllist); nd; incnode(nd)) {
- rd = (Repldata) getdata(nd);
- lleft += rd->b - i; /* previous chunk of *sp */
- lleft += strlen(rd->replstr); /* the replaced bit */
- i = rd->e; /* start of next chunk of *sp */
- }
- lleft += l - i; /* final chunk from *sp */
- start = t = zhalloc(lleft+1);
- i = 0;
- for (nd = firstnode(imd.repllist); nd; incnode(nd)) {
- rd = (Repldata) getdata(nd);
- memcpy(t, s + i, rd->b - i);
- t += rd->b - i;
- ptr = rd->replstr;
- while (*ptr)
- *t++ = *ptr++;
- i = rd->e;
- }
- memcpy(t, s + i, l - i);
- start[lleft] = '\0';
- *sp = (char *)start;
- }
- return 1;
- }
- if (fl & SUB_LIST) { /* safety: don't think this can happen */
- return 0;
- }
-
- /* munge the whole string: no match, so no replstr */
- imd.replstr = NULL;
- imd.repllist = NULL;
- *sp = get_match_ret(&imd, 0, 0);
- return (fl & SUB_RETFAIL) ? 0 : 1;
-}
-
-/**/
-#else
-
-/*
- * Increment pointer which may be on a Meta (x is a pointer variable),
- * returning the incremented value (i.e. like pre-increment).
- */
-#define METAINC(x) ((x) += (*(x) == Meta) ? 2 : 1)
-
-/**/
-static int
-igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
- LinkList *repllistp)
-{
- char *s = *sp, *t, *send;
- /*
- * Note that ioff and uml count characters in the character
- * set (Meta's are not included), while l counts characters in the
- * metafied string. umlen is a counter for (unmetafied) character
- * lengths.
- */
- int ioff, l = strlen(*sp), uml = ztrlen(*sp), matched = 1, umlen;
- struct patstralloc patstralloc;
- struct imatchdata imd;
-
- (void)patallocstr(p, s, l, uml, 1, &patstralloc);
- s = patstralloc.alloced;
- DPUTS(!s, "forced patallocstr failed");
- send = s + uml;
-
- imd.mstr = *sp;
- imd.mlen = l;
- imd.ustr = s;
- imd.ulen = uml;
- imd.flags = fl;
- imd.replstr = replstr;
- imd.repllist = NULL;
-
- /* perform must-match test for complex closures */
- if (p->mustoff)
- {
- char *muststr = (char *)p + p->mustoff;
-
- matched = 0;
- if (p->patmlen <= uml)
- {
- for (t = s; t <= send - p->patmlen; t++)
- {
- if (!memcmp(muststr, t, p->patmlen)) {
- matched = 1;
- break;
- }
- }
- }
- }
-
- /* in case we used the prog before... */
- p->flags &= ~(PAT_NOTSTART|PAT_NOTEND);
-
- if (fl & SUB_ALL) {
- int i = matched && pattrylen(p, s, uml, 0, &patstralloc, 0);
- if (!i)
- imd.replstr = NULL;
- *sp = get_match_ret(&imd, 0, i ? l : 0);
- if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i)))
- return 0;
- return 1;
- }
- if (matched) {
- switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) {
- case 0:
- case SUB_LONG:
- /*
- * Largest/smallest possible match at head of string.
- * First get the longest match...
- */
- if (pattrylen(p, s, uml, 0, &patstralloc, 0)) {
- /* patmatchlen returns metafied length, as we need */
- int mlen = patmatchlen();
- if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) {
- send = s + mlen;
- /*
- * ... now we know whether it's worth looking for the
- * shortest, which we do by brute force.
- */
- for (t = s, umlen = 0; t < s + mlen; METAINC(t), umlen++) {
- set_pat_end(p, *t);
- if (pattrylen(p, s, umlen, 0, &patstralloc, 0)) {
- mlen = patmatchlen();
- break;
- }
- }
- }
- *sp = get_match_ret(&imd, 0, mlen);
- return 1;
- }
- break;
-
- case SUB_END:
- /* Smallest possible match at tail of string: *
- * move back down string until we get a match. *
- * There's no optimization here. */
- for (ioff = uml, t = send, umlen = 0; t >= s;
- t--, ioff--, umlen++) {
- set_pat_start(p, t-s);
- if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) {
- *sp = get_match_ret(&imd, t - s, uml);
- return 1;
- }
- }
- break;
-
- case (SUB_END|SUB_LONG):
- /* Largest possible match at tail of string: *
- * move forward along string until we get a match. *
- * Again there's no optimisation. */
- for (ioff = 0, t = s, umlen = uml; t < send;
- ioff++, t++, umlen--) {
- set_pat_start(p, t-s);
- if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) {
- *sp = get_match_ret(&imd, t-s, uml);
- return 1;
- }
- }
- break;
-
- case SUB_SUBSTR:
- /* Smallest at start, but matching substrings. */
- set_pat_start(p, l);
- if (!(fl & SUB_GLOBAL) &&
- pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) {
- *sp = get_match_ret(&imd, 0, 0);
- return 1;
- } /* fall through */
- case (SUB_SUBSTR|SUB_LONG):
- /* longest or smallest at start with substrings */
- t = s;
- if (fl & SUB_GLOBAL) {
- imd.repllist = newlinklist();
- if (repllistp)
- *repllistp = imd.repllist;
- }
- ioff = 0; /* offset into string */
- umlen = uml;
- do {
- /* loop over all matches for global substitution */
- matched = 0;
- for (; t < send; t++, ioff++, umlen--) {
- /* Find the longest match from this position. */
- set_pat_start(p, t-s);
- if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) {
- char *mpos = t + patmatchlen();
- if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) {
- char *ptr;
- int umlen2;
- for (ptr = t, umlen2 = 0; ptr < mpos;
- ptr++, umlen2++) {
- set_pat_end(p, *ptr);
- if (pattrylen(p, t, ptr - t, umlen2,
- &patstralloc, ioff)) {
- mpos = t + patmatchlen();
- break;
- }
- }
- }
- if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) {
- *sp = get_match_ret(&imd, t-s, mpos-s);
- if (mpos == t)
- mpos++;
- }
- if (!(fl & SUB_GLOBAL)) {
- if (n) {
- /*
- * Looking for a later match: in this case,
- * we can continue looking for matches from
- * the next character, even if it overlaps
- * with what we just found.
- */
- continue;
- } else {
- return 1;
- }
- }
- /*
- * For a global match, we need to skip the stuff
- * which is already marked for replacement.
- */
- matched = 1;
- while (t < mpos) {
- ioff++;
- umlen--;
- t++;
- }
- break;
- }
- }
- } while (matched);
- /*
- * check if we can match a blank string, if so do it
- * at the start. Goodness knows if this is a good idea
- * with global substitution, so it doesn't happen.
- */
- set_pat_start(p, l);
- if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG &&
- pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) {
- *sp = get_match_ret(&imd, 0, 0);
- return 1;
- }
- break;
-
- case (SUB_END|SUB_SUBSTR):
- case (SUB_END|SUB_LONG|SUB_SUBSTR):
- /* Longest/shortest at end, matching substrings. */
- if (!(fl & SUB_LONG)) {
- set_pat_start(p, l);
- if (pattrylen(p, send, 0, 0, &patstralloc, uml) && !--n) {
- *sp = get_match_ret(&imd, uml, uml);
- return 1;
- }
- }
- for (ioff = uml - 1, t = send - 1, umlen = 1; t >= s;
- t--, ioff--, umlen++) {
- set_pat_start(p, t-s);
- if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff) &&
- !--n) {
- /* Found the longest match */
- char *mpos = t + patmatchlen();
- if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) {
- char *ptr;
- int umlen2;
- for (ptr = t, umlen2 = 0; ptr < mpos;
- ptr++, umlen2++) {
- set_pat_end(p, *ptr);
- if (pattrylen(p, t, umlen2, 0, &patstralloc,
- ioff)) {
- mpos = t + patmatchlen();
- break;
- }
- }
- }
- *sp = get_match_ret(&imd, t-s, mpos-s);
- return 1;
- }
- }
- set_pat_start(p, l);
- if ((fl & SUB_LONG) && pattrylen(p, send, 0, 0,
- &patstralloc, uml) &&
- !--n) {
- *sp = get_match_ret(&imd, uml, uml);
- return 1;
- }
- break;
- }
- }
-
- if (imd.repllist && nonempty(imd.repllist)) {
- /* Put all the bits of a global search and replace together. */
- LinkNode nd;
- Repldata rd;
- int lleft = 0; /* size of returned string */
- char *ptr, *start;
- int i;
-
- /*
- * Use metafied string again.
- * Results from get_match_ret in repllist are all metafied.
- */
- s = *sp;
- i = 0; /* start of last chunk we got from *sp */
- for (nd = firstnode(imd.repllist); nd; incnode(nd)) {
- rd = (Repldata) getdata(nd);
- lleft += rd->b - i; /* previous chunk of *sp */
- lleft += strlen(rd->replstr); /* the replaced bit */
- i = rd->e; /* start of next chunk of *sp */
- }
- lleft += l - i; /* final chunk from *sp */
- start = t = zhalloc(lleft+1);
- i = 0;
- for (nd = firstnode(imd.repllist); nd; incnode(nd)) {
- rd = (Repldata) getdata(nd);
- memcpy(t, s + i, rd->b - i);
- t += rd->b - i;
- ptr = rd->replstr;
- while (*ptr)
- *t++ = *ptr++;
- i = rd->e;
- }
- memcpy(t, s + i, l - i);
- start[lleft] = '\0';
- *sp = (char *)start;
- return 1;
- }
-
- /* munge the whole string: no match, so no replstr */
- imd.replstr = NULL;
- imd.repllist = NULL;
- *sp = get_match_ret(&imd, 0, 0);
- return 1;
-}
-
-/**/
-#endif /* MULTIBYTE_SUPPORT */
-
-/* blindly turn a string into a tokenised expression without lexing */
-
-/**/
-mod_export void
-tokenize(char *s)
-{
- zshtokenize(s, 0);
-}
-
-/*
- * shtokenize is used when we tokenize a string with GLOB_SUBST set.
- * In that case we need to retain backslashes when we turn the
- * pattern back into a string, so that the string is not
- * modified if it failed to match a pattern.
- *
- * It may be modified by the effect of SH_GLOB which turns off
- * various zsh-specific options.
- */
-
-/**/
-mod_export void
-shtokenize(char *s)
-{
- int flags = ZSHTOK_SUBST;
- if (isset(SHGLOB))
- flags |= ZSHTOK_SHGLOB;
- zshtokenize(s, flags);
-}
-
-/**/
-static void
-zshtokenize(char *s, int flags)
-{
- char *t;
- int bslash = 0;
-
- for (; *s; s++) {
- cont:
- switch (*s) {
- case Meta:
- /* skip both Meta and following character */
- s++;
- break;
- case Bnull:
- case Bnullkeep:
- case '\\':
- if (bslash) {
- s[-1] = (flags & ZSHTOK_SUBST) ? Bnullkeep : Bnull;
- break;
- }
- bslash = 1;
- continue;
- case '<':
- if (flags & ZSHTOK_SHGLOB)
- break;
- if (bslash) {
- s[-1] = (flags & ZSHTOK_SUBST) ? Bnullkeep : Bnull;
- break;
- }
- t = s;
- while (idigit(*++s));
- if (!IS_DASH(*s))
- goto cont;
- while (idigit(*++s));
- if (*s != '>')
- goto cont;
- *t = Inang;
- *s = Outang;
- break;
- case '(':
- case '|':
- case ')':
- if (flags & ZSHTOK_SHGLOB)
- break;
- /*FALLTHROUGH*/
- case '>':
- case '^':
- case '#':
- case '~':
- case '[':
- case ']':
- case '*':
- case '?':
- case '=':
- case '-':
- case '!':
- for (t = ztokens; *t; t++) {
- if (*t == *s) {
- if (bslash)
- s[-1] = (flags & ZSHTOK_SUBST) ? Bnullkeep : Bnull;
- else
- *s = (t - ztokens) + Pound;
- break;
- }
- }
- break;
- }
- bslash = 0;
- }
-}
-
-/* remove unnecessary Nulargs */
-
-/**/
-mod_export void
-remnulargs(char *s)
-{
- if (*s) {
- char *o = s, c;
-
- while ((c = *s++))
- if (c == Bnullkeep) {
- /*
- * An active backslash that needs to be turned back into
- * a real backslash for output. However, we don't
- * do that yet since we need to ignore it during
- * pattern matching.
- */
- continue;
- } else if (inull(c)) {
- char *t = s - 1;
-
- while ((c = *s++)) {
- if (c == Bnullkeep)
- *t++ = '\\';
- else if (!inull(c))
- *t++ = c;
- }
- *t = '\0';
- if (!*o) {
- o[0] = Nularg;
- o[1] = '\0';
- }
- break;
- }
- }
-}
-
-/* qualifier functions: mostly self-explanatory, see glob(). */
-
-/* device number */
-
-/**/
-static int
-qualdev(UNUSED(char *name), struct stat *buf, off_t dv, UNUSED(char *dummy))
-{
- return (off_t)buf->st_dev == dv;
-}
-
-/* number of hard links to file */
-
-/**/
-static int
-qualnlink(UNUSED(char *name), struct stat *buf, off_t ct, UNUSED(char *dummy))
-{
- return (g_range < 0 ? buf->st_nlink < ct :
- g_range > 0 ? buf->st_nlink > ct :
- buf->st_nlink == ct);
-}
-
-/* user ID */
-
-/**/
-static int
-qualuid(UNUSED(char *name), struct stat *buf, off_t uid, UNUSED(char *dummy))
-{
- return buf->st_uid == uid;
-}
-
-/* group ID */
-
-/**/
-static int
-qualgid(UNUSED(char *name), struct stat *buf, off_t gid, UNUSED(char *dummy))
-{
- return buf->st_gid == gid;
-}
-
-/* device special file? */
-
-/**/
-static int
-qualisdev(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy))
-{
- return S_ISBLK(buf->st_mode) || S_ISCHR(buf->st_mode);
-}
-
-/* block special file? */
-
-/**/
-static int
-qualisblk(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy))
-{
- return S_ISBLK(buf->st_mode);
-}
-
-/* character special file? */
-
-/**/
-static int
-qualischr(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy))
-{
- return S_ISCHR(buf->st_mode);
-}
-
-/* directory? */
-
-/**/
-static int
-qualisdir(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy))
-{
- return S_ISDIR(buf->st_mode);
-}
-
-/* FIFO? */
-
-/**/
-static int
-qualisfifo(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy))
-{
- return S_ISFIFO(buf->st_mode);
-}
-
-/* symbolic link? */
-
-/**/
-static int
-qualislnk(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy))
-{
- return S_ISLNK(buf->st_mode);
-}
-
-/* regular file? */
-
-/**/
-static int
-qualisreg(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy))
-{
- return S_ISREG(buf->st_mode);
-}
-
-/* socket? */
-
-/**/
-static int
-qualissock(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy))
-{
- return S_ISSOCK(buf->st_mode);
-}
-
-/* given flag is set in mode */
-
-/**/
-static int
-qualflags(UNUSED(char *name), struct stat *buf, off_t mod, UNUSED(char *dummy))
-{
- return mode_to_octal(buf->st_mode) & mod;
-}
-
-/* mode matches specification */
-
-/**/
-static int
-qualmodeflags(UNUSED(char *name), struct stat *buf, off_t mod, UNUSED(char *dummy))
-{
- long v = mode_to_octal(buf->st_mode), y = mod & 07777, n = mod >> 12;
-
- return ((v & y) == y && !(v & n));
-}
-
-/* regular executable file? */
-
-/**/
-static int
-qualiscom(UNUSED(char *name), struct stat *buf, UNUSED(off_t mod), UNUSED(char *dummy))
-{
- return S_ISREG(buf->st_mode) && (buf->st_mode & S_IXUGO);
-}
-
-/* size in required range? */
-
-/**/
-static int
-qualsize(UNUSED(char *name), struct stat *buf, off_t size, UNUSED(char *dummy))
-{
-#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT)
-# define QS_CAST_SIZE()
- zlong scaled = buf->st_size;
-#else
-# define QS_CAST_SIZE() (unsigned long)
- unsigned long scaled = (unsigned long)buf->st_size;
-#endif
-
- switch (g_units) {
- case TT_POSIX_BLOCKS:
- scaled += 511l;
- scaled /= 512l;
- break;
- case TT_KILOBYTES:
- scaled += 1023l;
- scaled /= 1024l;
- break;
- case TT_MEGABYTES:
- scaled += 1048575l;
- scaled /= 1048576l;
- break;
-#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT)
- case TT_GIGABYTES:
- scaled += ZLONG_CONST(1073741823);
- scaled /= ZLONG_CONST(1073741824);
- break;
- case TT_TERABYTES:
- scaled += ZLONG_CONST(1099511627775);
- scaled /= ZLONG_CONST(1099511627776);
- break;
-#endif
- }
-
- return (g_range < 0 ? scaled < QS_CAST_SIZE() size :
- g_range > 0 ? scaled > QS_CAST_SIZE() size :
- scaled == QS_CAST_SIZE() size);
-#undef QS_CAST_SIZE
-}
-
-/* time in required range? */
-
-/**/
-static int
-qualtime(UNUSED(char *name), struct stat *buf, off_t days, UNUSED(char *dummy))
-{
- time_t now, diff;
-
- time(&now);
- diff = now - (g_amc == 0 ? buf->st_atime : g_amc == 1 ? buf->st_mtime :
- buf->st_ctime);
- /* handle multipliers indicating units */
- switch (g_units) {
- case TT_DAYS:
- diff /= 86400l;
- break;
- case TT_HOURS:
- diff /= 3600l;
- break;
- case TT_MINS:
- diff /= 60l;
- break;
- case TT_WEEKS:
- diff /= 604800l;
- break;
- case TT_MONTHS:
- diff /= 2592000l;
- break;
- }
-
- return (g_range < 0 ? diff < days :
- g_range > 0 ? diff > days :
- diff == days);
-}
-
-/* evaluate a string */
-
-/**/
-static int
-qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str)
-{
- Eprog prog;
-
- if ((prog = parse_string(str, 0))) {
- int ef = errflag, lv = lastval, ret;
- int cshglob = badcshglob;
-
- unsetparam("reply");
- setsparam("REPLY", ztrdup(name));
- badcshglob = 0;
-
- execode(prog, 1, 0, "globqual");
-
- if ((ret = lastval))
- badcshglob |= cshglob;
- /* Retain any user interrupt error status */
- errflag = ef | (errflag & ERRFLAG_INT);
- lastval = lv;
-
- if (!(inserts = getaparam("reply")) &&
- !(inserts = gethparam("reply"))) {
- char *tmp;
-
- if ((tmp = getsparam("reply")) || (tmp = getsparam("REPLY"))) {
- static char *tmparr[2];
-
- tmparr[0] = tmp;
- tmparr[1] = NULL;
-
- inserts = tmparr;
- }
- }
-
- return !ret;
- }
- return 0;
-}
-
-/**/
-static int
-qualnonemptydir(char *name, struct stat *buf, UNUSED(off_t days), UNUSED(char *str))
-{
- DIR *dirh;
- struct dirent *de;
- int unamelen;
- char *uname = unmetafy(dupstring(name), &unamelen);
-
- if (!S_ISDIR(buf->st_mode))
- return 0;
-
- if (buf->st_nlink > 2)
- return 1;
-
- if (!(dirh = opendir(uname)))
- return 0;
-
- while ((de = readdir(dirh))) {
- if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
- closedir(dirh);
- return 1;
- }
- }
-
- closedir(dirh);
- return 0;
-}