When glob(3) is not in use, make sure that expmeta processes multi-byte characters correctly. Signed-off-by: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx> --- src/expand.c | 109 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/src/expand.c b/src/expand.c index b627c7a..714eae9 100644 --- a/src/expand.c +++ b/src/expand.c @@ -84,6 +84,7 @@ #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */ #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */ +#define RMESCAPE_EMETA 0x20 /* Remove backslashes too */ /* Add CTLESC when necessary. */ #define QUOTES_ESC (EXP_FULL | EXP_CASE) @@ -1387,15 +1388,13 @@ expandmeta(struct strlist *str) savelastp = exparg.lastp; INTOFF; - p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); + p = str->text; len = strlen(p); expdir_max = len + PATH_MAX; expdir = ckmalloc(expdir_max); expmeta(p, len, 0); ckfree(expdir); - if (p != str->text) - ckfree(p); INTON; if (exparg.lastp == savelastp) { /* @@ -1416,6 +1415,40 @@ nometa: } } +static void expmeta_rmescapes(char *enddir, char *name) +{ + preglob(strcpy(enddir, name), RMESCAPE_EMETA); +} + +static unsigned mbcharlen(char *p) +{ + int esc = 0; + + if (*++p == (char)CTLESC) + esc++; + + return esc + 3 + (unsigned char)p[esc]; +} + +static size_t skipesc(char *p) +{ + size_t esc = 0; + + if (p[esc] == (char)CTLMBCHAR) + esc += mbcharlen(p); + else if (p[esc] == (char)CTLESC) + esc++; + else if (p[esc] == '\\' && p[esc + 1]) { + while (p[++esc] == (char)CTLQUOTEMARK) + ; + if (p[esc] == (char)CTLMBCHAR) + esc += mbcharlen(p + esc); + else if (p[esc] == (char)CTLESC) + esc++; + } + + return esc; +} /* * Do metacharacter (i.e. *, ?, [...]) expansion. @@ -1425,17 +1458,18 @@ STATIC void expmeta(char *name, unsigned name_len, unsigned expdir_len) { char *enddir = expdir + expdir_len; - char *p; + struct stat64 statb; + struct dirent64 *dp; const char *cp; - char *start; char *endname; int metaflag; - struct stat64 statb; - DIR *dirp; - struct dirent64 *dp; - int atend; int matchdot; + char *start; + DIR *dirp; + char *pat; + char *p; int esc; + int c; metaflag = 0; start = name; @@ -1444,11 +1478,8 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len) metaflag = 1; else if (*p == '[') { char *q = p + 1; - if (*q == '!') - q++; for (;;) { - if (*q == '\\') - q++; + q += skipesc(q); if (*q == '/' || *q == '\0') break; if (*++q == ']') { @@ -1457,8 +1488,7 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len) } } } else { - if (*p == '\\' && p[1]) - esc++; + esc = skipesc(p); if (p[esc] == '/') { if (metaflag) break; @@ -1469,24 +1499,18 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len) if (metaflag == 0) { /* we've reached the end of the file name */ if (!expdir_len) return; - p = name; - do { - if (*p == '\\' && p[1]) - p++; - *enddir++ = *p; - } while (*p++); + expmeta_rmescapes(enddir, name); if (lstat64(expdir, &statb) >= 0) addfname(expdir); return; } endname = p; if (name < start) { - p = name; - do { - if (*p == '\\' && p[1]) - p++; - *enddir++ = *p++; - } while (p < start); + c = *start; + *start = 0; + expmeta_rmescapes(enddir, name); + *start = c; + enddir += strlen(enddir); } *enddir = 0; cp = expdir; @@ -1495,16 +1519,15 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len) cp = "."; if ((dirp = opendir(cp)) == NULL) return; - if (*endname == 0) { - atend = 1; - } else { - atend = 0; + c = *endname; + if (c) { *endname = '\0'; endname += esc + 1; } name_len -= endname - name; matchdot = 0; - p = start; + pat = preglob(start, RMESCAPE_ALLOC | RMESCAPE_HEAP); + p = pat; if (*p == '\\') p++; if (*p == '.') @@ -1512,8 +1535,8 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len) while (! int_pending() && (dp = readdir64(dirp)) != NULL) { if (dp->d_name[0] == '.' && ! matchdot) continue; - if (pmatch(start, dp->d_name)) { - if (atend) { + if (pmatch(pat, dp->d_name)) { + if (!c) { scopy(dp->d_name, enddir); addfname(expdir); } else { @@ -1536,9 +1559,11 @@ expmeta(char *name, unsigned name_len, unsigned expdir_len) } } } + if (pat != start) + ckfree(pat); closedir(dirp); - if (! atend) - endname[-esc - 1] = esc ? '\\' : '/'; + if (c) + endname[-esc - 1] = c; } @@ -1781,6 +1806,7 @@ _rmescapes(char *str, int flag) int notescaped; int globbing; int inquotes; + int expmeta; p = strpbrk(str, cqchars); if (!p) { @@ -1789,6 +1815,7 @@ _rmescapes(char *str, int flag) q = p; r = str; globbing = flag & RMESCAPE_GLOB; + expmeta = (flag & RMESCAPE_EMETA) ? RMESCAPE_GLOB : 0; if (flag & RMESCAPE_ALLOC) { size_t len = p - str; @@ -1828,6 +1855,12 @@ _rmescapes(char *str, int flag) } else if (*p == '\\') { /* naked back slash */ newnesc ^= notescaped; + /* naked backslashes can only occur outside quotes */ + inquotes = 0; + if (expmeta & ~newnesc) { + p++; + goto setnesc; + } } else if (*p == (char)CTLMBCHAR) { if (*++p == (char)CTLESC) p++; @@ -1838,7 +1871,9 @@ _rmescapes(char *str, int flag) goto setnesc; } else if (*p == (char)CTLESC) { p++; - if (notescaped) + if (expmeta) + ; + else if (notescaped) *q++ = '\\'; else if (inquotes) { *q++ = '\\'; -- 2.39.2