zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: misc glob fixes
@ 1998-05-09 22:17 Zoltan Hidvegi
  1998-05-11  9:36 ` Andrew Main
  0 siblings, 1 reply; 3+ messages in thread
From: Zoltan Hidvegi @ 1998-05-09 22:17 UTC (permalink / raw)
  To: Zsh hacking and development

This patch fixes several glob problems.

First, many systems ignore trailing slashes on filenames.  As a result */
will match all files instead of just directories as required by common
sense and the POSIX standard.  The solution is to stat/lstat/access
`filename/.' instead of `filename/'.

The next problem was introduced by patch 3285 which fixes the (-T) and
(-M) qualifier behavior.  The problem is the these will give no maches
found on a dangling symlink.

A somewhat related promlem occurs when the T or M qualifier is applied
together with some other mode qualifier.  For example *(T-/) will not
match symbolic links to directories.  That's because T will add the @ to
the ent of the symlink to mark it and the stat will be done on this
modified name.

The patch combines exists and getfullpatch into
statfullpath(const char *s, struct stat *st, int l).
This combines the current glob directory with the filename s.  l is a
flag to follow symlinks.  If st is NULL access is used instead of stat.
That's the same as before: exists used access.  access is faster on
certain filesystems.  Also some filesystems do not allow stat on a file,
although access returns success on the file.  If I remember correctly
this is true for AFS when the file is in a directory which is listable
but not readable.  * will work in such directories, but *(T) will give no
match, since if the lstat fails, it assumes that the file does not
exists.  This may be considered a bug, but it is really because AFS does
not follow the usual Unix semantics, so I do not think it should be
fixed.

All of these are already in 3.0.5

Zoli


*** Src/glob.c.orig	Sat May  9 15:06:05 1998
--- Src/glob.c	Sat May  9 16:32:32 1998
*************** static char *pptr;		/* current place in 
*** 128,142 ****
  static Comp tail;
  static int first;		/* are leading dots special? */
  
- /**/
- static int
- exists(char *s)
- {
-     char *us = unmeta(s);
- 
-     return access(us, F_OK) == 0 || readlink(us,NULL,0) == 0;
- }
- 
  /* 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.                            */
--- 128,133 ----
*************** addpath(char *s)
*** 153,172 ****
      pathbuf[pathpos] = '\0';
  }
  
! /* return full path for s, which has path as *
!  * already added to pathbuf                  */
  
  /**/
! static char *
! getfullpath(char *s)
  {
!     static char buf[PATH_MAX];
  
!     DPUTS(strlen(s) + pathpos - pathbufcwd >= PATH_MAX,
! 	  "BUG: getfullpath(): pathname too long");
      strcpy(buf, pathbuf + pathbufcwd);
      strcpy(buf + pathpos - pathbufcwd, s);
!     return buf;
  }
  
  /* add a match to the list */
--- 144,174 ----
      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 chechked 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];
  
!     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[pathpos - pathbufcwd] = '.';
! 	buf[pathpos - pathbufcwd + 1] = '\0';
! 	l = 0;
!     }
!     unmetafy(buf, NULL);
!     if (!st)
! 	return access(buf, F_OK) && (!l || readlink(buf, NULL, 0));
!     return l ? lstat(buf, st) : stat(buf, st);
  }
  
  /* add a match to the list */
*************** static void
*** 176,245 ****
  insert(char *s, int checked)
  {
      struct stat buf, buf2, *bp;
      int statted = 0;
  
      if (gf_listtypes || gf_markdirs) {
  	/* Add the type marker to the end of the filename */
  	checked = statted = 1;
! 	if (gf_follow ? stat(unmetafy(getfullpath(s), NULL), &buf)
! 	    : lstat(unmetafy(getfullpath(s), NULL), &buf))
  	    return;
! 	if (gf_listtypes || S_ISDIR(buf.st_mode)) {
! 	    char *t;
  	    int ll = strlen(s);
  
! 	    t = (char *)ncalloc(ll + 2);
! 	    strcpy(t, s);
! 	    t[ll] = file_type(buf.st_mode);
! 	    t[ll + 1] = '\0';
! 	    s = t;
  	}
      }
      if (qualct || qualorct) {
  	/* Go through the qualifiers, rejecting the file if appropriate */
  	struct qual *qo, *qn;
- 	int t = 0;		/* reject file unless t is set */
  
! 	if (!statted && lstat(unmetafy(getfullpath(s), NULL), &buf))
  	    return;
! 	statted = 0;
! 	for (qo = quals; qo && !t; qo = qo->or) {
! 	    t = 1;
! 	    for (qn = qo; t && qn && qn->func; qn = qn->next) {
! 		range = qn->range;
! 		amc = qn->amc;
! 		units = qn->units;
! 		if ((qn->sense & 2) && !statted) {
! 		    /* If (sense & 2), we're following links */
! 		    if (!S_ISLNK(buf.st_mode) ||
! 			stat(unmetafy(getfullpath(s), NULL), &buf2))
! 			memcpy(&buf2, &buf, sizeof(buf));
! 		    statted = 1;
! 		}
! 		bp = (qn->sense & 2) ? &buf2 : &buf;
! 		/* Reject the file if the function returned zero *
! 		 * and the sense was positive (sense == 0), or   *
! 		 * vice versa.                                   */
! 		if (!(!!((qn->func) (bp, qn->data)) ^
! 		      (qn->sense & 1))) {
! 		    t = 0;
! 		    break;
! 		}
  	    }
  	}
! 	if (!t)
! 	    return;
!     } else if (!checked && !exists(getfullpath(s)))
  	return;
  
!     s = dyncat(pathbuf, s);
      if (colonmod) {
  	/* Handle the remainder of the qualifer:  e.g. (:r:s/foo/bar/). */
! 	char *cm2 = colonmod;
! 
! 	modify(&s, &cm2);
      }
!     *matchptr++ = s;
      if (++matchct == matchsz) {
  	matchbuf = (char **)realloc((char *)matchbuf,
  				    sizeof(char **) * (matchsz *= 2));
--- 178,248 ----
  insert(char *s, int checked)
  {
      struct stat buf, buf2, *bp;
+     char *news = s;
      int statted = 0;
  
      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))
  	    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 *)ncalloc(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))
  	    return;
! 	qo = quals;
! 	for (qn = qo; qn && qn->func;) {
! 	    range = qn->range;
! 	    amc = qn->amc;
! 	    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) (bp, qn->data)) ^ qn->sense) & 1) {
+ 		/* Try next alternative, or return if there are no more */
+ 		if (!(qo = qo->or))
+ 		    return;
+ 		qn = qo;
+ 		continue;
+ 	    }
+ 	    qn = qn->next;
  	}
!     } else if (!checked && statfullpath(s, NULL, 1))
  	return;
  
!     news = dyncat(pathbuf, news);
      if (colonmod) {
  	/* Handle the remainder of the qualifer:  e.g. (:r:s/foo/bar/). */
! 	s = colonmod;
! 	modify(&news, &s);
      }
!     *matchptr++ = news;
      if (++matchct == matchsz) {
  	matchbuf = (char **)realloc((char *)matchbuf,
  				    sizeof(char **) * (matchsz *= 2));
*************** scanner(Complist q)
*** 315,321 ****
  	/* It's a straight string to the end of the path section. */
  	int l = strlen(c->str);
  
! 	if (l + pathpos - pathbufcwd >= PATH_MAX) {
  	    int err;
  
  	    if (l >= PATH_MAX)
--- 318,324 ----
  	/* It's a straight string to the end of the path section. */
  	int l = strlen(c->str);
  
! 	if (l + !l + pathpos - pathbufcwd >= PATH_MAX) {
  	    int err;
  
  	    if (l >= PATH_MAX)
*************** scanner(Complist q)
*** 335,341 ****
  
  	    if (!errflag && !(q->closure && !strcmp(c->str, "."))) {
  		addpath(c->str);
! 		if (!closure || exists(pathbuf))
  		    scanner((q->closure) ? q : q->next);
  		pathbuf[pathpos = oppos] = '\0';
  	    }
--- 338,344 ----
  
  	    if (!errflag && !(q->closure && !strcmp(c->str, "."))) {
  		addpath(c->str);
! 		if (!closure || statfullpath("", NULL, 1))
  		    scanner((q->closure) ? q : q->next);
  		pathbuf[pathpos = oppos] = '\0';
  	    }
*************** scanner(Complist q)
*** 391,399 ****
  			/* if matching multiple directories */
  			struct stat buf;
  
! 			if ((q->follow ?
! 			     stat(unmeta(getfullpath(fn)), &buf) :
! 			     lstat(unmeta(getfullpath(fn)), &buf)) == -1) {
  			    if (errno != ENOENT && errno != EINTR &&
  				errno != ENOTDIR && !errflag) {
  				zerr("%e: %s", fn, errno);
--- 394,400 ----
  			/* if matching multiple directories */
  			struct stat buf;
  
! 			if (statfullpath(fn, &buf, !q->follow)) {
  			    if (errno != ENOENT && errno != EINTR &&
  				errno != ENOTDIR && !errflag) {
  				zerr("%e: %s", fn, errno);


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: PATCH: misc glob fixes
  1998-05-09 22:17 PATCH: misc glob fixes Zoltan Hidvegi
@ 1998-05-11  9:36 ` Andrew Main
  1998-05-11 15:49   ` Zoltan Hidvegi
  0 siblings, 1 reply; 3+ messages in thread
From: Andrew Main @ 1998-05-11  9:36 UTC (permalink / raw)
  To: Zoltan Hidvegi; +Cc: zsh-workers

Zoltan Hidvegi wrote:
>First, many systems ignore trailing slashes on filenames.  As a result */
>will match all files instead of just directories as required by common
>sense and the POSIX standard.  The solution is to stat/lstat/access
>`filename/.' instead of `filename/'.

But "filename/." can't be statted if the directory is not searchable
by the zsh process, whereas POSIX would require the glob to succeed.
It would be better to stat "filename/" and test that the result has
type directory.

-zefram


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: PATCH: misc glob fixes
  1998-05-11  9:36 ` Andrew Main
@ 1998-05-11 15:49   ` Zoltan Hidvegi
  0 siblings, 0 replies; 3+ messages in thread
From: Zoltan Hidvegi @ 1998-05-11 15:49 UTC (permalink / raw)
  To: Andrew Main; +Cc: zsh-workers

Zefram wrote:
> Zoltan Hidvegi wrote:
> >First, many systems ignore trailing slashes on filenames.  As a result */
> >will match all files instead of just directories as required by common
> >sense and the POSIX standard.  The solution is to stat/lstat/access
> >`filename/.' instead of `filename/'.
> 
> But "filename/." can't be statted if the directory is not searchable
> by the zsh process, whereas POSIX would require the glob to succeed.
> It would be better to stat "filename/" and test that the result has
> type directory.

Actually I'm not sure about that.  POSIX requites that pathname
components other than the last component should be searchable.  You
can think filename/ as two components, the second component is empty.

I tested many shells (various old Korn and Bourne shells) and none of
them matches unsearchable directories with */

Also note that on Linux you need search permission on filename in
order to stat filename/, so on Linux sometimes you are not allowed to
stat filename/ while you are allowed to stat filename.  The same is
true for the good old Ultrix 4.2, and perhaps other systems.  On these
systems the patch has no effect on the shell behavior.  I think the
shell should behave the same way on all systems, and it should behave
like sh and ksh, so I think that the patch implements the correct
behavior.

Zoli


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~1998-05-11 15:59 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-05-09 22:17 PATCH: misc glob fixes Zoltan Hidvegi
1998-05-11  9:36 ` Andrew Main
1998-05-11 15:49   ` Zoltan Hidvegi

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).