diff mbox series

expand: Rewrite expmeta meta detection

Message ID ZlvLvnSw-0HcRWNL@gondor.apana.org.au (mailing list archive)
State Accepted
Delegated to: Herbert Xu
Headers show
Series expand: Rewrite expmeta meta detection | expand

Commit Message

Herbert Xu June 2, 2024, 1:32 a.m. UTC
Remove the meta detection in expandmeta and rely on the detection
in expmeta instead.

Replace the open-coded meta detection with one based on strpbrk.
This is slightly inaccurate with bracket expressions but the
difference is minor (only affecting patterns with an unquoted
']').

Move int_pending to the end of the loop so that it is only executed
after some work has been done.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
 src/expand.c | 116 +++++++++++++++++++--------------------------------
 1 file changed, 43 insertions(+), 73 deletions(-)
diff mbox series

Patch

diff --git a/src/expand.c b/src/expand.c
index cb579df..345c498 100644
--- a/src/expand.c
+++ b/src/expand.c
@@ -143,7 +143,6 @@  STATIC void addfname(char *);
 STATIC int patmatch(char *, const char *);
 STATIC int pmatch(char *, const char *);
 static size_t cvtnum(intmax_t num, int flags);
-STATIC size_t esclen(const char *, const char *);
 STATIC void varunset(const char *, const char *, const char *, int)
 	__attribute__((__noreturn__));
 
@@ -166,16 +165,19 @@  preglob(const char *pattern, int flag) {
 }
 
 
-STATIC size_t
-esclen(const char *start, const char *p) {
+static size_t mesclen(const char *start, const char *p, char mesc) {
 	size_t esc = 0;
 
-	while (p > start && *--p == (char)CTLESC) {
+	while (p > start && *--p == mesc) {
 		esc++;
 	}
 	return esc;
 }
 
+static size_t esclen(const char *start, const char *p) {
+	return mesclen(start, p, CTLESC);
+}
+
 static __attribute__((noinline)) unsigned mbnext(const char *p)
 {
 	unsigned start = 0;
@@ -1544,9 +1546,6 @@  static void addglob(const glob64_t *pglob)
 STATIC void
 expandmeta(struct strlist *str)
 {
-	static const char metachars[] = {
-		'*', '?', '[', 0
-	};
 	/* TODO - EXP_REDIR */
 
 	if (GLOB_IS_ENABLED)
@@ -1560,7 +1559,7 @@  expandmeta(struct strlist *str)
 
 		if (fflag)
 			goto nometa;
-		if (!strpbrk(str->text, metachars))
+		if (!strpbrk(str->text, "*?]"))
 			goto nometa;
 		savelastp = exparg.lastp;
 
@@ -1634,51 +1633,29 @@  static char *expmeta_rmescapes(char *enddir, const char *name)
 	return enddir - 1;
 }
 
-static int skipesc(char *p)
-{
-	unsigned short mb;
-	int esc = 0;
-
-	mb = mbnext(p);
-	if ((mb >> 8) > 3)
-		return (mb & 0xff) + (mb >> 8) - 1;
-
-	esc = mb & 0xff;
-
-	if (!esc && p[esc] == '\\' && p[esc + 1]) {
-		esc++;
-		mb = mbnext(p + esc);
-		esc += mb & 0xff;
-
-		if ((mb >> 8) > 3)
-			esc += (mb >> 8) - 1;
-	}
-
-	return esc;
-}
-
 /*
  * Do metacharacter (i.e. *, ?, [...]) expansion.
  */
 
 static char *expmeta(char *name, unsigned name_len, size_t expdir_len)
 {
+	const char mesc = FNMATCH_IS_ENABLED ? '\\' : CTLESC;
 	struct jmploc *volatile savehandler;
 	struct jmploc jmploc;
 	struct stat64 statb;
 	struct dirent64 *dp;
 	volatile int err;
 	char *endname;
+	char *zeroedp;
 	char *enddir;
-	int metaflag;
 	int matchdot;
+	unsigned esc;
 	char *start;
 	size_t len;
 	DIR *dirp;
 	char *pat;
 	char *cp;
 	char *p;
-	int esc;
 	int c;
 
 	*(DIR *volatile *)&dirp = NULL;
@@ -1690,32 +1667,16 @@  static char *expmeta(char *name, unsigned name_len, size_t expdir_len)
 	cp = growstackto(len);
 	enddir = cp + expdir_len;
 
-	metaflag = 0;
-	start = name;
-	for (p = name; esc = 0, *p; p += esc + 1) {
-		if (*p == '*' || *p == '?')
-			metaflag = 1;
-		else if (*p == '[') {
-			char *q = p + 1;
-			for (;;) {
-				q += skipesc(q);
-				if (*q == '/' || *q == '\0')
-					break;
-				if (*++q == ']') {
-					metaflag = 1;
-					break;
-				}
-			}
-		} else {
-			esc = skipesc(p);
-			if (p[esc] == '/') {
-				if (metaflag)
-					break;
-				start = p + esc + 1;
-			}
-		}
-	}
-	if (metaflag == 0) {	/* we've reached the end of the file name */
+	p = name;
+	esc = 0;
+	do {
+		p = strpbrk(p + esc, "*?]");
+		if (!p)
+			break;
+		esc = mesclen(name, p, mesc) & 1;
+	} while (esc);
+	/* No meta characters */
+	if (likely(!p)) {
 		if (!expdir_len)
 			goto out_opendir;
 		enddir = expmeta_rmescapes(enddir, name);
@@ -1723,37 +1684,44 @@  static char *expmeta(char *name, unsigned name_len, size_t expdir_len)
 			cp = addfnamealt(enddir, expdir_len);
 		goto out_opendir;
 	}
-	endname = p;
-	if (name < start) {
-		c = *start;
+	start = memrchr(name, '/', p - name);
+	if (start) {
+		c = *++start;
 		*start = 0;
 		enddir = expmeta_rmescapes(enddir, name);
 		*start = c;
-	}
+		expdir_len = enddir - cp;
+	} else
+		start = name;
 	*enddir = 0;
-	expdir_len = enddir - cp;
 
 	*(DIR *volatile *)&dirp = opendir(expdir_len ? cp : dotdir);
 	if (!dirp)
 		goto out_opendir;
-	c = *endname;
-	if (c) {
-		*endname = '\0';
-		endname += esc + 1;
+	esc = 0;
+	p = strchrnul(p + 1, '/');
+	zeroedp = p;
+	endname = p;
+	if (*p) {
+		esc = mesclen(name, p, mesc) & 1;
+		zeroedp -= esc;
+		endname++;
 	}
+	c = *zeroedp;
+	*zeroedp = '\0';
 	name_len -= endname - name;
 	matchdot = 0;
 	pat = start;
 	p = pat;
-	if (*p == (FNMATCH_IS_ENABLED ? '\\' : (char)CTLESC))
+	if (*p == mesc)
 		p++;
 	if (*p == '.')
 		matchdot++;
-	while (! int_pending() && (dp = readdir64(dirp)) != NULL) {
+	while ((dp = readdir64(dirp))) {
 		char *dname = dp->d_name;
 
 		if (*dname == '.' && !matchdot)
-			continue;
+			goto check_int;
 		len = strlen(dname) + 1;
 		p = dname;
 		if (!FNMATCH_IS_ENABLED) {
@@ -1774,9 +1742,11 @@  static char *expmeta(char *name, unsigned name_len, size_t expdir_len)
 			}
 			enddir = cp + expdir_len;
 		}
+check_int:
+		if (int_pending())
+			break;
 	}
-	if (c)
-		endname[-esc - 1] = c;
+	*zeroedp = c;
 
 out:
 	closedir(*(DIR *volatile *)&dirp);