1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-13 03:32:24 +00:00

Re-match '.' and '..' in tab completion (re: 5312a59d, aad74597)

Turns out there is a bona fide, honest-to-goodness use case for
matching '.' and '..' in globbing after all. It's when globbing is
used as the backend mechanism for file name completion in
interactive shell editors. A tab invisibly adds a * at the end of
the word to the left of your cursor and the resulting pattern is
expanded. In 5312a59d, this broke for '.' and '..'.

Typing '.' followed by two tabs should result in a menu that
includes './' and '../'. Typing '..' followed by a tab should
result in '../', (or a menu that includes it if there are files
with names starting with '..'). This is the behaviour in 93u+ and
we should maintain this.

To restore this functionality without reintroducing the harmful
behaviour fixed in the referenced commits, we should special-case
this, allowing '.' and '..' to match only for file name completion.

src/lib/libast/include/glob.h:
- Fix an inaccurate comment: the GLOB_COMPLETE flag is used for
  command completion, not file name completion. This is very clear
  from reading the path_expand() function in sh/expand.c.
- Add new GLOB_FCOMPLETE flag for file name completion.

src/lib/libast/misc/glob.c:
- Adapt flags mask to fit the new flag.
- glob_dir(): If GLOB_FCOMPLETE is passed, allow '.' and '..' to
  match even if expanded from a pattern.
- Clarify the fix from aad74597 with an extended comment based on
  <https://github.com/ksh93/ksh/issues/146#issuecomment-790991990>.

src/cmd/ksh93/sh/expand.c: path_expand():
- If we're in the SH_FCOMPLETE (file name completion) state, then
  pass the new GLOB_FCOMPLETE flag to AST glob(3).

Fixes: https://github.com/ksh93/ksh/issues/372
Thanks to @fbrau for the bug report.
This commit is contained in:
Martijn Dekker 2021-12-13 01:33:26 +01:00
parent e54001d58b
commit fc752b574a
6 changed files with 42 additions and 6 deletions

5
NEWS
View file

@ -3,6 +3,11 @@ For full details, see the git log at: https://github.com/ksh93/ksh
Any uppercase BUG_* names are modernish shell bug IDs.
2021-12-13:
- Fixed a bug introduced on 2020-08-09 that prevented '.' and '..' from
being completed when using file name tab completion.
2021-12-11:
- Fixed two more crashing bugs that occurred if ksh received a signal (such

View file

@ -21,7 +21,7 @@
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
#define SH_RELEASE_DATE "2021-12-11" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_DATE "2021-12-13" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */

View file

@ -84,7 +84,7 @@ int path_expand(Shell_t *shp,const char *pattern, struct argnod **arghead)
if(sh_isoption(SH_GLOBCASEDET))
flags |= GLOB_DCASE;
#endif
if(sh_isstate(SH_COMPLETE))
if(sh_isstate(SH_COMPLETE)) /* command completion */
{
extra += scantree(shp->alias_tree,pattern,arghead);
extra += scantree(shp->fun_tree,pattern,arghead);
@ -92,6 +92,8 @@ int path_expand(Shell_t *shp,const char *pattern, struct argnod **arghead)
flags |= GLOB_COMPLETE;
flags &= ~GLOB_NOCHECK;
}
if(sh_isstate(SH_FCOMPLETE)) /* file name completion */
flags |= GLOB_FCOMPLETE;
gp->gl_fignore = nv_getval(sh_scoped(shp,FIGNORENOD));
if(suflen)
gp->gl_suffix = sufstr;

View file

@ -929,5 +929,24 @@ r > \)
r one two three end
!
# err_exit #
((SHOPT_VSH || SHOPT_ESH)) && tst $LINENO <<"!"
L tab completion of '.' and '..'
# https://github.com/ksh93/ksh/issues/372
d 15
# typing '.' followed by two tabs should show a menu that includes "number) ../"
p :test-1:
w : .\t\t
u ) \.\./\r\n$
# typing '..' followed by a tab should complete to '../' (as it is
# known that there are no files starting with '..' in the test PWD)
p :test-2:
w : ..\t
r : \.\./\r\n$
!
# ======
exit $((Errors<125?Errors:125))

View file

@ -113,7 +113,7 @@ struct _glob_
#define GLOB_STARSTAR 0x0080 /* enable [/]**[/] expansion */
#define GLOB_BRACE 0x0100 /* enable {...} expansion */
#define GLOB_ICASE 0x0200 /* ignore case on match */
#define GLOB_COMPLETE 0x0400 /* shell file completion */
#define GLOB_COMPLETE 0x0400 /* shell command completion */
#define GLOB_AUGMENTED 0x0800 /* augmented shell patterns */
#define GLOB_STACK 0x1000 /* allocate on current stack */
#define GLOB_LIST 0x2000 /* just create gl_list */
@ -121,6 +121,7 @@ struct _glob_
#define GLOB_DISC 0x8000 /* discipline initialized */
#define GLOB_GROUP 0x10000 /* REG_SHELL_GROUP */
#define GLOB_DCASE 0x20000 /* detect FS case insensitivity */
#define GLOB_FCOMPLETE 0x40000 /* shell file name completion */
/* gl_status */
#define GLOB_NOTDIR 0x0001 /* last gl_dirnext() not a dir */

View file

@ -43,7 +43,7 @@
* for flags. If a new GLOB_* flag bit is added to glob.h, these must be adapted accordingly.
*/
#define GLOB_MAGIC 0xAAA80000 /* 10101010101010000000000000000000 */
#define GLOB_FLAGMASK 0x0003FFFF /* 00000000000000111111111111111111 */
#define GLOB_FLAGMASK 0x0007FFFF /* 00000000000001111111111111111111 */
#define MATCH_RAW 1
#define MATCH_MAKE 2
@ -543,9 +543,18 @@ skip:
*restore2 = gp->gl_delim;
while ((name = (*gp->gl_dirnext)(gp, dirf)) && !*gp->gl_intr)
{
/*
* For security and usability, only match '..' or '.' as the final element if:
* - its' specified literally, or
* - we're in file name completion mode.
* To avoid breaking globstar, make sure that '.' or '..' is skipped if, and only if, it is the
* final element in the pattern (i.e., if 'pat' does not contain a slash) and is not specified
* literally as '.' or '..', i.e. only if '.' or '..' was actually resolved from a glob pattern.
*/
if (!(matchdir && (pat[0] == '.' && (!pat[1] || pat[1] == '.' && !pat[2]) || strchr(pat,'/')))
&& name[0] == '.' && (!name[1] || name[1] == '.' && !name[2]))
continue; /* never match '.' or '..' as final element unless specified literally */
&& name[0] == '.' && (!name[1] || name[1] == '.' && !name[2])
&& !(gp->gl_flags & GLOB_FCOMPLETE))
continue;
if (notdir = (gp->gl_status & GLOB_NOTDIR))
gp->gl_status &= ~GLOB_NOTDIR;
if (ire && !regexec(ire, name, 0, NiL, 0))