From 5312a59d5a75b0c8edf9d83de09bc0437b26203d Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Mon, 10 Aug 2020 00:02:23 +0100 Subject: [PATCH] Skip '.' and '..' when globbing patterns like .* There are convincing arguments why including '.' and '..' in the result of pathname expansion is actively harmful. See: https://www.austingroupbugs.net/view.php?id=1228 https://github.com/ksh93/ksh/issues/58#issuecomment-653716846 pdksh, mksh and zsh already skip these special traversal names in all cases. This commit makes ksh act like these shells. Since passing '.' and especially '..' as arguments to commands like 'chmod -R' and 'cp -r' may cause harm, this change seems likely to fix more legacy scripts than it breaks. I'm unaware of anyone ever having come up with a concrete use case for the old behaviour. This change also fixes the bug that '.' and '..' failed to be ignored as documented if FIGNORE is set. src/lib/libast/misc/glob.c: glob_dir(): - Explicitly skip any matching '.' and '..' in all cases. src/cmd/ksh93/tests/glob.sh: - Add test_glob() tests for '*' and '.*'. src/cmd/ksh93/sh.1: File Name Generation: - Update to match new behaviour. Resolves: https://github.com/ksh93/ksh/issues/58 --- NEWS | 8 ++++++++ src/cmd/ksh93/include/version.h | 2 +- src/cmd/ksh93/sh.1 | 10 +++++----- src/cmd/ksh93/tests/glob.sh | 8 ++++++++ src/lib/libast/misc/glob.c | 4 +++- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index d04460288..0d35bb77e 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,14 @@ For full details, see the git log at: https://github.com/ksh93/ksh Any uppercase BUG_* names are modernish shell bug IDs. +2020-08-09: + +- File name generation (a.k.a. pathname expansion, a.k.a. globbing) now + never matches the special navigational names '.' (current directory) and + '..' (parent directory). This change makes a pattern like .* useful; it + now matches all hidden files (dotfiles) in the current directory, without + the harmful inclusion of '.' and '..'. + 2020-08-08: - Argument checking in the 'redirect' builtin command (see 2020-06-11) has diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index 9c60971ab..191237b43 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -17,4 +17,4 @@ * David Korn * * * ***********************************************************************/ -#define SH_RELEASE "93u+m 2020-08-08" +#define SH_RELEASE "93u+m 2020-08-09" diff --git a/src/cmd/ksh93/sh.1 b/src/cmd/ksh93/sh.1 index 000cb2975..3e3c540ef 100644 --- a/src/cmd/ksh93/sh.1 +++ b/src/cmd/ksh93/sh.1 @@ -2426,6 +2426,11 @@ that component of the filename is left unchanged unless the pattern is prefixed with .B \(ap(N)\fP in which case it is removed as described below. +The special traversal names +.B . +and +.B .. +are never matched. If .SM .B FIGNORE @@ -2435,11 +2440,6 @@ that matches the pattern defined by the value of .SM .B FIGNORE is ignored when generating the matching filenames. -The names -.B . -and -.B .. -are also ignored. If .SM .B FIGNORE diff --git a/src/cmd/ksh93/tests/glob.sh b/src/cmd/ksh93/tests/glob.sh index 8dec730ae..a42f468b3 100755 --- a/src/cmd/ksh93/tests/glob.sh +++ b/src/cmd/ksh93/tests/glob.sh @@ -276,10 +276,18 @@ test_glob ' ' * FIGNORE= test_glob '' */man*/sh.* +test_glob '<.x> <.y>
' * +test_glob '<.x> <.y>' .* + +FIGNORE='@(*[abcd]*)' +test_glob '<.x> <.y>' * +test_glob '<.x> <.y>' .* unset FIGNORE test_glob '
' * +test_glob '<.x> <.y>' .* GLOBIGNORE='.*:*' set -- * diff --git a/src/lib/libast/misc/glob.c b/src/lib/libast/misc/glob.c index f05b40e8e..b9fcea38d 100644 --- a/src/lib/libast/misc/glob.c +++ b/src/lib/libast/misc/glob.c @@ -528,11 +528,13 @@ skip: *restore2 = gp->gl_delim; while ((name = (*gp->gl_dirnext)(gp, dirf)) && !*gp->gl_intr) { + if (name[0] == '.' && (!name[1] || name[1] == '.' && !name[2])) + continue; /* do not ever match '.' or '..' */ if (notdir = (gp->gl_status & GLOB_NOTDIR)) gp->gl_status &= ~GLOB_NOTDIR; if (ire && !regexec(ire, name, 0, NiL, 0)) continue; - if (matchdir && (name[0] != '.' || name[1] && (name[1] != '.' || name[2])) && !notdir) + if (matchdir && !notdir) addmatch(gp, prefix, name, matchdir, NiL, anymeta); if (!regexec(pre, name, 0, NiL, 0)) {
' ?? test_glob '' */man*/sh.* +test_glob '