1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-13 11:42:21 +00:00

Add --globcasedetect shell option for globbing and completion

One of the best-kept secrets of libast/ksh93 is that the code
includes support for case-insensitive file name generation (a.k.a.
pathname expansion, a.k.a. globbing) as well as case-insensitive
file name completion on interactive shells, depending on whether
the file system is case-insensitive or not. This is transparently
determined for each directory, so a path pattern that spans
multiple file systems can be part case-sensitive and part case-
insensitive. In more precise terms, each slash-separated path name
component pattern P is treated as ~(i:P) if its parent directory
exists on a case-insensitive file system. I recently discovered
this while dealing with <https://github.com/ksh93/ksh/issues/223>.

However, that support is dead code on almost all current systems.
It depends on pathconf(2) having a _PC_PATH_ATTRIBUTES selector.
The 'c' attribute is supposedly returned if the given directory is
on a case insensitive file system. There are other attributes as
well (at least 'l', see src/lib/libcmd/rm.c). However, I have been
unable to find any system, current or otherwise, that has
_PC_PATH_ATTRIBUTES. Google and mailing list searches yield no
relevant results at all. If anyone knows of such a system, please
add a comment to this commit on GitHub, or email me.

An exception is Cygwin/Windows, on which the "c" attribute was
simply hardcoded, so globbing/completion is always case-
insensitive. As of Windows 10, that is wrong, as it added the
possibility to mount case-sensitive file systems.

On the other hand, this was never activated on the Mac, even
though macOS has always used a case-insensitive file like Windows.
But, being UNIX, it can also mount case-sensitive file systems.

Finally, Linux added the possibility to create individual case-
insensitive ext4 directories fairly recently, in version 5.2.
https://www.collabora.com/news-and-blog/blog/2020/08/27/using-the-linux-kernel-case-insensitive-feature-in-ext4/

So, since this functionality latently exists in the code base, and
three popular OSs now have relevant file system support, we might
as well make it usable on those systems. It's a nice idea, as it
intuitively makes sense for globbing and completion behaviour to
auto-adapt to file system case insensitivity on a per-directory
basis. No other shell does this, so it's a nice selling point, too.

However, the way it is coded, this is activated unconditionally on
supported systems. That is not a good idea. It will surprise users.
Since globbing is used with commands like 'rm', we do not want
surprises. So this commit makes it conditional upon a new shell
option called 'globcasedetect'. This option is only compiled into
ksh on systems where we can actually detect FS case insensitivity.

To implement this, libast needs some public API additions first.

*** libast changes ***

src/lib/libast/features/lib:
- Add probes for the linux/fs.h and sys/ioctl.h headers.
  Linux needs these to use ioctl(2) in pathicase(3) (see below).

src/lib/libast/path/pathicase.c,
src/lib/libast/include/ast.h,
src/lib/libast/man/path.3,
src/lib/libast/Mamfile:
- Add new pathicase(3) public API function. This uses whatever
  OS-specific method it can detect at compile time to determine if
  a particular path is on a case-insensitive file system. If no
  method is available, it only sets errno to ENOSYS and returns -1.
  Currently known to work on: macOS, Cygwin, Linux 5.2+, QNX 7.0+.
- On systems (if any) that have the mysterious _PC_PATH_ATTRIBUTES
  selector for pathconf(2), call astconf(3) and check for the 'c'
  attribute to determine case insensitivity. This should preserve
  compatibility with any such system.

src/lib/libast/port/astconf.c:
- dynamic[]: As case-insensitive globbing is now optional on all
  systems, do not set the 'c' attribute by default on _WINIX
  (Cygwin/Windows) systems.
- format(): On systems that do not have _PC_PATH_ATTRIBUTES, call
  pathicase(3) to determine the value for the "c" (case
  insensitive) attribute only. This is for compatibility as it is
  more efficient to call pathicase(3) directly.

src/lib/libast/misc/glob.c,
src/lib/libast/include/glob.h:
- Add new GLOB_DCASE public API flag to glob(3). This is like
  GLOB_ICASE (case-insensitive matching) except it only makes the
  match case-insensitive if the file system for the current
  pathname component is determined to be case-insensitive.
- gl_attr(): For efficiency, call pathicase(3) directly instead of
  via astconf(3).
- glob_dir(): Only call gl_attr() to determine file system case
  insensitivity if the GLOB_DCASE flag was passed. This makes case
  insensitive globbing optional on all systems.
- glob(): The options bitmask needs to be widened to fit the new
  GLOB_DCASE option. Define this centrally in a new GLOB_FLAGMASK
  macro so it is easy to change it along with GLOB_MAGIC (which
  uses the remaining bits for a sanity check bit pattern).

src/lib/libast/path/pathexists.c:
- For efficiency, call pathicase(3) directly instead of via
  astconf(3).

*** ksh changes ***

src/cmd/ksh93/features/options,
src/cmd/ksh93/SHOPT.sh:
- Add new SHOPT_GLOBCASEDET compile-time option. Set it to probe
  (empty) by default so that the shell option is compiled in on
  supported systems only, which is determined by new iffe feature
  test that checks if pathicase(3) returns an ENOSYS error.

src/cmd/ksh93/data/options.c,
src/cmd/ksh93/include/shell.h:
- Add -o globcasedetect shell option if compiling with
  SHOPT_GLOBCASEDET.

src/cmd/ksh93/sh/expand.c: path_expand():
- Pass the new GLOB_DCASE flag to glob(3) if the
  globcasedetect/SH_GLOBCASEDET shell option is set.

src/cmd/ksh93/edit/completion.c:
- While file listing/completion is based on globbing and
  automatically becomes case-insensitive when globbing does, it
  needs some additional handling to make a string comparison
  case-insensitive in corresponding cases. Otherwise, partial
  completions may be deleted from the command line upon pressing
  tab. This code was already in ksh 93u+ and just needs to be
  made conditional upon SHOPT_GLOBCASEDET and globcasedetect.
- For efficiency, call pathicase(3) directly instead of via
  astconf(3).

src/cmd/ksh93/sh.1:
- Document the new globcasedetect shell option.
This commit is contained in:
Martijn Dekker 2021-03-22 15:01:03 +00:00
parent 71bfe0283d
commit 71934570bf
18 changed files with 192 additions and 17 deletions

16
NEWS
View file

@ -3,6 +3,22 @@ For full details, see the git log at: https://github.com/ksh93/ksh
Any uppercase BUG_* names are modernish shell bug IDs.
2021-03-22:
- A new --globcasedetect shell option is added to ksh on OSs where we can check
for a case-insensitive file system (currently macOS, Windows/Cygwin, Linux
5.2+, and QNX 7.0+). When this option is turned on, file name generation
(globbing), as well as file name tab completion on interactive shells,
automatically become case-insensitive on file systems where the difference
between upper- and lowercase is ignored for file names. This is transparently
determined for each directory, so a path pattern that spans multiple file
systems can be part case-sensitive and part case-insensitive.
The option is not compiled into ksh on systems where we do not know of a
method to check for file system case insensitivity. The shell option can be
force-compiled by setting SHOPT_GLOBCASEDET to 1 in src/cmd/ksh93/SHOPT.sh,
but it won't have any effect on non-supported systems, so this is not
recommended. It can be removed from ksh by setting SHOPT_GLOBCASEDET to 0.
2021-03-17:
- Fixed a bug with file name completion on the interactive shell in multibyte

View file

@ -20,6 +20,7 @@ SHOPT EDPREDICT=1 # predictive editing
SHOPT ESH=1 # emacs/gmacs edit mode
SHOPT FILESCAN=1 # fast file scan
SHOPT FIXEDARRAY=1 # fixed dimension indexed array
SHOPT GLOBCASEDET= # -o globcasedetect: adapt globbing/completion to case-insensitive file systems
SHOPT HISTEXPAND=1 # csh-style history file expansions
SHOPT KIA= # ksh -R <outfile> <script> generates cross-ref database from script
SHOPT MULTIBYTE=1 # multibyte character handling

View file

@ -43,6 +43,9 @@ const Shtable_t shtab_options[] =
"errexit", SH_ERREXIT,
"noexec", SH_NOEXEC,
"noglob", SH_NOGLOB,
#if SHOPT_GLOBCASEDET
"globcasedetect", SH_GLOBCASEDET,
#endif
"globstar", SH_GLOBSTARS,
#if SHOPT_ESH
"gmacs", SH_GMACS,

View file

@ -59,6 +59,9 @@ static char *fmtx(const char *string)
return(stakptr(offset));
}
#if !SHOPT_GLOBCASEDET
#define charcmp(a,b,dummy) (a==b)
#else
static int charcmp(int a, int b, int nocase)
{
if(nocase)
@ -78,6 +81,7 @@ static int charcmp(int a, int b, int nocase)
}
return(a==b);
}
#endif /* !SHOPT_GLOBCASEDET */
/*
* overwrites <str> to common prefix of <str> and <newstr>
@ -384,8 +388,10 @@ int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count)
*dir = 0;
saveout = begin;
}
if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0))
nocase = (strchr(saveout,'c')!=0);
#if SHOPT_GLOBCASEDET
if(sh_isoption(SH_GLOBCASEDET))
nocase = (pathicase(saveout) > 0);
#endif
if(dir)
*dir = c;
/* just expand until name is unique */

View file

@ -54,3 +54,18 @@ cat{
# undef SHOPT_MULTIBYTE
#endif
}end
tst note{ can we probe file system case insensitivity }end output{
#include <ast.h>
#include <error.h>
#include <stdio.h>
static char *o = "SHOPT_GLOBCASEDET";
int main(int argc,char *argv[])
{
int r;
r = pathicase("/");
r = (r > -1 || errno != ENOSYS);
printf("#ifndef %s\n# define %s\t%d\n#endif\n", o, o, r);
return !r;
}
}end

View file

@ -133,6 +133,9 @@ typedef union Shnode_u Shnode_t;
#define SH_DICTIONARY 30
#define SH_PIPEFAIL 32
#define SH_GLOBSTARS 33
#if SHOPT_GLOBCASEDET
#define SH_GLOBCASEDET 34
#endif
#define SH_RC 35
#define SH_SHOWME 36
#define SH_LETOCTAL 37

View file

@ -20,7 +20,7 @@
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
#define SH_RELEASE_SVER "1.0.0-alpha" /* semantic version number: https://semver.org */
#define SH_RELEASE_DATE "2021-03-17" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_DATE "2021-03-22" /* 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

@ -2771,7 +2771,9 @@ currently implemented.
The remainder of the pattern uses System V regular expression syntax.
.TP
.B i
Treat the match as case insensitive.
Always treat the match as case-insensitive, regardless of the
.B globcasedetect
shell option.
.TP
.B g
File the longest match (greedy). This is the default.
@ -6008,7 +6010,7 @@ omitted, then \f2type\^\fP must be an indexed array variable with at
least two elements and the values are taken from this array variable.
If
.B -i
is specified the values are case insensitive.
is specified the values are case-insensitive.
Declaration commands are created as special builtins that cannot be
removed or overridden by shell functions.
.TP
@ -7122,6 +7124,22 @@ style in-line editor for command entry.
Same as
.BR \-e .
.TP 8
.B globcasedetect
When this option is turned on, globbing (see
.I "File Name Generation\^"
above) and
file name listing and completion (see
.I "In-line Editing Options\^"
above) automatically become case-insensitive on file systems where the
difference between upper- and lowercase is ignored for file names. This is
transparently determined for each directory, so a path pattern that spans
multiple file systems can be part case-sensitive and part case-insensitive.
In more precise terms, each slash-separated path name component pattern \fIp\fR
is treated as \fB~(i:\fR\fIp\fR\fB)\fR
if its parent directory exists on a case-insensitive file system.
This option is only present on operating systems that support
case-insensitive file systems.
.TP 8
.B globstar
Same as
.BR \-G .

View file

@ -92,6 +92,10 @@ int path_expand(Shell_t *shp,const char *pattern, struct argnod **arghead)
flags |= GLOB_MARK;
if(sh_isoption(SH_GLOBSTARS))
flags |= GLOB_STARSTAR;
#if SHOPT_GLOBCASEDET
if(sh_isoption(SH_GLOBCASEDET))
flags |= GLOB_DCASE;
#endif
if(sh_isstate(SH_COMPLETE))
{
#if KSHELL

View file

@ -1028,6 +1028,15 @@ make install
prev path/pathfind.c
exec - ${CC} ${mam_cc_FLAGS} ${KSH_RELFLAGS} ${CCFLAGS} -I. -Icomp -Iinclude -Istd -D_PACKAGE_ast -c path/pathfind.c
done pathfind.o generated
make pathicase.o
make path/pathicase.c
prev include/error.h implicit
prev include/ast.h implicit
done path/pathicase.c
meta pathicase.o %.c>%.o path/pathicase.c pathicase
prev path/pathicase.c
exec - ${CC} ${mam_cc_FLAGS} ${KSH_RELFLAGS} ${CCFLAGS} -I. -Icomp -Iinclude -Istd -D_PACKAGE_ast -c path/pathicase.c
done pathicase.o generated
make pathkey.o
make path/pathkey.c
prev ast_api.h implicit
@ -6081,7 +6090,7 @@ make install
exec - ${CC} ${mam_cc_FLAGS} ${KSH_RELFLAGS} ${CCFLAGS} -I. -Icomp -Iinclude -Istd -D__OBSOLETE__=20120101 -D_PACKAGE_ast -c disc/sfstrtmp.c
done sfstrtmp.o generated
exec - ${AR} rc libast.a state.o transition.o opendir.o readdir.o rewinddir.o seekdir.o telldir.o getcwd.o fastfind.o hashalloc.o hashdump.o hashfree.o hashlast.o hashlook.o hashscan.o hashsize.o hashview.o hashwalk.o memhash.o memsum.o strhash.o strkey.o strsum.o stracmp.o strnacmp.o ccmap.o ccmapid.o ccnative.o chresc.o chrtoi.o
exec - ${AR} rc libast.a streval.o strexpr.o strmatch.o strcopy.o modei.o modex.o strmode.o strlcat.o strlcpy.o strlook.o strncopy.o strsearch.o strpsearch.o stresc.o stropt.o strtape.o strpcmp.o strnpcmp.o strvcmp.o strnvcmp.o tok.o tokline.o tokscan.o pathaccess.o pathcat.o pathcanon.o pathcheck.o pathpath.o pathexists.o pathfind.o pathkey.o pathprobe.o pathrepl.o pathnative.o pathposix.o pathtemp.o pathtmp.o pathstat.o pathgetlink.o pathsetlink.o pathbin.o pathshell.o pathcd.o pathprog.o ftwalk.o ftwflags.o fts.o astintercept.o conformance.o getenv.o setenviron.o optget.o optjoin.o optesc.o optctx.o strsort.o struniq.o magic.o mime.o mimetype.o signal.o sigflag.o systrace.o error.o errorf.o errormsg.o errorx.o localeconv.o setlocale.o translate.o catopen.o iconv.o lc.o lctab.o mc.o base64.o recfmt.o recstr.o reclen.o fmtrec.o fmtbase.o fmtbuf.o fmtclock.o fmtdev.o fmtelapsed.o fmterror.o fmtesc.o fmtfmt.o fmtfs.o fmtident.o fmtint.o fmtip4.o fmtip6.o fmtls.o fmtmatch.o fmtmode.o fmtnum.o fmtperm.o fmtre.o fmttime.o
exec - ${AR} rc libast.a streval.o strexpr.o strmatch.o strcopy.o modei.o modex.o strmode.o strlcat.o strlcpy.o strlook.o strncopy.o strsearch.o strpsearch.o stresc.o stropt.o strtape.o strpcmp.o strnpcmp.o strvcmp.o strnvcmp.o tok.o tokline.o tokscan.o pathaccess.o pathcat.o pathcanon.o pathcheck.o pathpath.o pathexists.o pathfind.o pathicase.o pathkey.o pathprobe.o pathrepl.o pathnative.o pathposix.o pathtemp.o pathtmp.o pathstat.o pathgetlink.o pathsetlink.o pathbin.o pathshell.o pathcd.o pathprog.o ftwalk.o ftwflags.o fts.o astintercept.o conformance.o getenv.o setenviron.o optget.o optjoin.o optesc.o optctx.o strsort.o struniq.o magic.o mime.o mimetype.o signal.o sigflag.o systrace.o error.o errorf.o errormsg.o errorx.o localeconv.o setlocale.o translate.o catopen.o iconv.o lc.o lctab.o mc.o base64.o recfmt.o recstr.o reclen.o fmtrec.o fmtbase.o fmtbuf.o fmtclock.o fmtdev.o fmtelapsed.o fmterror.o fmtesc.o fmtfmt.o fmtfs.o fmtident.o fmtint.o fmtip4.o fmtip6.o fmtls.o fmtmatch.o fmtmode.o fmtnum.o fmtperm.o fmtre.o fmttime.o
exec - ${AR} rc libast.a fmtuid.o fmtgid.o fmtsignal.o fmtscale.o fmttmx.o fmttv.o fmtversion.o strelapsed.o strperm.o struid.o strgid.o strtoip4.o strtoip6.o stack.o stk.o swapget.o swapmem.o swapop.o swapput.o sigdata.o sigcrit.o sigunblock.o procopen.o procclose.o procrun.o procfree.o tmdate.o tmequiv.o tmfix.o tmfmt.o tmform.o tmgoff.o tminit.o tmleap.o tmlex.o tmlocale.o tmmake.o tmpoff.o tmscan.o tmsleep.o tmtime.o tmtype.o tmweek.o tmword.o tmzone.o tmxdate.o tmxduration.o tmxfmt.o tmxgettime.o tmxleap.o tmxmake.o tmxscan.o tmxsettime.o tmxsleep.o tmxtime.o tmxtouch.o tvcmp.o tvgettime.o tvsettime.o tvsleep.o tvtouch.o cmdarg.o vecargs.o vecfile.o vecfree.o vecload.o vecstring.o univdata.o touch.o mnt.o debug.o memccpy.o memchr.o memcmp.o memcpy.o memdup.o memmove.o memset.o mkdir.o mkfifo.o mknod.o rmdir.o remove.o rename.o link.o unlink.o strdup.o strchr.o strrchr.o strstr.o strtod.o strtold.o strtol.o strtoll.o strtoul.o strtoull.o strton.o strtonll.o strntod.o strntold.o strnton.o
exec - ${AR} rc libast.a strntonll.o strntol.o strntoll.o strntoul.o strntoull.o strcasecmp.o strncasecmp.o strerror.o mktemp.o tmpnam.o fsync.o execlp.o execve.o execvp.o execvpe.o spawnveg.o vfork.o killpg.o hsearch.o tsearch.o getlogin.o putenv.o setenv.o unsetenv.o lstat.o statvfs.o eaccess.o gross.o omitted.o readlink.o symlink.o getpgrp.o setpgid.o setsid.o waitpid.o creat64.o fcntl.o open.o atexit.o getdents.o getwd.o dup2.o errno.o getpreroot.o ispreroot.o realopen.o setpreroot.o getgroups.o mount.o system.o iblocks.o modedata.o tmdata.o memfatal.o sfkeyprintf.o sfdcdio.o sfdcdos.o sfdcfilter.o sfdcseekable.o sfdcslow.o sfdcsubstr.o sfdctee.o sfdcunion.o sfdcmore.o sfdcprefix.o wc.o wc2utf8.o basename.o closelog.o dirname.o fmtmsglib.o fnmatch.o ftw.o getdate.o getsubopt.o glob.o nftw.o openlog.o re_comp.o resolvepath.o realpath.o regcmp.o regexp.o setlogmask.o strftime.o strptime.o swab.o syslog.o tempnam.o wordexp.o mktime.o regalloc.o regclass.o regcoll.o regcomp.o regcache.o regdecomp.o regerror.o regexec.o regfatal.o reginit.o
exec - ${AR} rc libast.a regnexec.o regsubcomp.o regsubexec.o regsub.o regrecord.o regrexec.o regstat.o dtclose.o dtdisc.o dthash.o dtlist.o dtmethod.o dtopen.o dtstat.o dtstrhash.o dttree.o dtuser.o dtview.o dtwalk.o dtnew.o dtcomp.o sfclose.o sfclrlock.o sfdisc.o sfdlen.o sfexcept.o sfgetl.o sfgetu.o sfcvt.o sfecvt.o sffcvt.o sfextern.o sffilbuf.o sfflsbuf.o sfprints.o sfgetd.o sfgetr.o sfllen.o sfmode.o sfmove.o sfnew.o sfpkrd.o sfnotify.o sfnputc.o sfopen.o sfpeek.o sfpoll.o sfpool.o sfpopen.o sfprintf.o sfputd.o sfputl.o sfputr.o sfputu.o sfrd.o sfread.o sfreserve.o sfscanf.o sfseek.o sfset.o sfsetbuf.o sfsetfd.o sfsize.o sfsk.o sfstack.o sfstrtod.o sfsync.o sfswap.o sftable.o sftell.o sftmp.o sfungetc.o sfvprintf.o sfvscanf.o sfwr.o sfwrite.o sfpurge.o sfraise.o sfwalk.o sfgetm.o sfmutex.o sfputm.o sfresize.o _sfclrerr.o _sfeof.o _sferror.o _sffileno.o _sfopen.o _sfstacked.o _sfvalue.o _sfgetc.o _sfgetl.o _sfgetl2.o _sfgetu.o _sfgetu2.o _sfdlen.o _sfllen.o _sfslen.o _sfulen.o _sfputc.o _sfputd.o _sfputl.o _sfputm.o

View file

@ -4,6 +4,7 @@ cmd universe
hdr dirent,direntry,filio,fmtmsg,fnmatch,jioctl,libgen,limits
hdr locale,ndir,nl_types,process,spawn,syslog,utime,vfork
hdr linux/fs,sys/ioctl
hdr wchar note{ <wchar.h> and isw*() really work }end execute{
#include <wchar.h>
int

View file

@ -317,6 +317,7 @@ extern int pathcheck(const char*, const char*, Pathcheck_t*);
extern int pathexists(char*, int);
extern char* pathfind(const char*, const char*, const char*, char*, size_t);
extern int pathgetlink(const char*, char*, int);
extern int pathicase(const char*);
extern int pathinclude(const char*);
extern char* pathkey(char*, char*, const char*, const char*, const char*);
extern char* pathkey_20100601(const char*, const char*, const char*, char*, size_t, char*, size_t);

View file

@ -94,6 +94,11 @@ struct _glob_
};
/*
* The standard/extended interface GLOB_* flags below must
* fit in the GLOB_FLAGMASK bitmask defined in misc/glob.c.
*/
/* standard interface */
#define GLOB_APPEND 0x0001 /* append to previous */
#define GLOB_DOOFFS 0x0002 /* gl_offs defines argv offset */
@ -113,8 +118,8 @@ struct _glob_
#define GLOB_LIST 0x2000 /* just create gl_list */
#define GLOB_ALTDIRFUNC 0x4000 /* gnu discipline functions */
#define GLOB_DISC 0x8000 /* discipline initialized */
#define GLOB_GROUP 0x10000 /* REG_SHELL_GROUP */
#define GLOB_DCASE 0x20000 /* detect FS case insensitivity */
/* gl_status */
#define GLOB_NOTDIR 0x0001 /* last gl_dirnext() not a dir */

View file

@ -50,6 +50,7 @@ char* pathcat(char* \fIpath\fP, const char* \fIdirs\fP, int \fIsep\fP, const
char* pathcd(char* \fIpath\fP, const char* \fIhome\fP);
int pathcheck(const char* \fIpackage\fP, const char* \fItool\fP, Pathcheck_t* \fIpc\fP);
int pathgetlink(const char* \fIname\fP, char* \fIbuf\fP, int \fIsiz\fP);
int pathicase(const char* \fIpath\fP);
char* pathkey(char* \fIkey\fP, char* \fIattr\fP, const char* \fIlang\fP, const char* \fIpath\fP);
char* pathnext(char* \fIpath\fP, char* \fIextra\fP, long* \fIvisits\fP);
char* pathpath(char* \fIpath\fP, const char* \fIp\fP, const char* \fIa\fP, int \fImode\fP);
@ -230,6 +231,32 @@ by converting non-standard dynamic link text to
.L pathsetsymlink
converts in the other direction.
.PP
.L pathicase
uses an operating system-specific method, if available,
to determine if a file system uses case-insensitive file names.
The return value is
.B 1
if the file system associated with the
.I path
is case-insensitive (regardless of case preservation),
.B 0
if it is not, or
.B \-1
if an error occurred. On error,
.L errno
is set by the system-specific function that was used.
If the operating system does not have a known method
to determine file system case insensitivity for the
.IR path ,
then
.L pathicase
returns
.B \-1
with
.L errno
set to
.LR ENOSYS .
.PP
.L pathkey
generates in
.I key

View file

@ -37,7 +37,12 @@
#include <ctype.h>
#include <regex.h>
#define GLOB_MAGIC 0xaaaa0000
/*
* GLOB_MAGIC is used for sanity checking. Its significant bits must not overlap with those used
* 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 MATCH_RAW 1
#define MATCH_MAKE 2
@ -143,7 +148,9 @@ gl_type(glob_t* gp, const char* path, int flags)
static int
gl_attr(glob_t* gp, const char* path, int flags)
{
return strchr(astconf("PATH_ATTRIBUTES", path, NiL), 'c') ? GLOB_ICASE : 0;
NOT_USED(gp);
NOT_USED(flags);
return pathicase(path) > 0 ? GLOB_ICASE : 0;
}
/*
@ -485,7 +492,9 @@ skip:
|| t1 == GLOB_SYM && pat[0]=='*' && pat[1]=='\0') /* follow symlinks to dirs for non-globstar components */
&& (dirf = (*gp->gl_diropen)(gp, dirname)))
{
if (!(gp->re_flags & REG_ICASE) && ((*gp->gl_attr)(gp, dirname, 0) & GLOB_ICASE))
if (!(gp->re_flags & REG_ICASE)
&& (gp->gl_flags & GLOB_DCASE)
&& ((*gp->gl_attr)(gp, dirname, 0) & GLOB_ICASE))
{
if (!prei)
{
@ -613,7 +622,7 @@ glob(const char* pattern, int flags, int (*errfn)(const char*, int), register gl
}
else
{
gp->gl_flags = (flags&0xffff)|GLOB_MAGIC;
gp->gl_flags = (flags & GLOB_FLAGMASK) | GLOB_MAGIC;
gp->re_flags = REG_SHELL|REG_NOSUB|REG_LEFT|REG_RIGHT|((flags&GLOB_AUGMENTED)?REG_AUGMENTED:0);
gp->gl_pathc = 0;
gp->gl_ignore = 0;
@ -730,7 +739,7 @@ glob(const char* pattern, int flags, int (*errfn)(const char*, int), register gl
f &= ~GLOB_STARSTAR;
continue;
case ')':
flags = (gp->gl_flags = f) & 0xffff;
flags = (gp->gl_flags = f) & GLOB_FLAGMASK;
if (f & GLOB_ICASE)
gp->re_flags |= REG_ICASE;
else

View file

@ -63,7 +63,7 @@ pathexists(char* path, int mode)
t = &tree;
e = (c = *path) == '/' ? path + 1 : path;
cmp = strchr(astconf("PATH_ATTRIBUTES", path, NiL), 'c') ? strcasecmp : strcmp;
cmp = pathicase(path) > 0 ? strcasecmp : strcmp;
if ((ast.locale.set & (AST_LC_debug|AST_LC_find)) == (AST_LC_debug|AST_LC_find))
sfprintf(sfstderr, "locale test %s\n", path);
while (c)

View file

@ -0,0 +1,59 @@
/***********************************************************************
* *
* This file is part of the ksh 93u+m package *
* Copyright (c) 2021 Contributors to ksh 93u+m *
* <https://github.com/ksh93/ksh> *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* *
* A copy of the License is available at *
* http://www.eclipse.org/org/documents/epl-v10.html *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Martijn Dekker <martijn@inlv.org> *
* *
***********************************************************************/
#include <ast.h>
#include <error.h>
#if _hdr_linux_fs && _hdr_sys_ioctl
#include <linux/fs.h>
#include <sys/ioctl.h>
#endif
/*
* Return 1 if the given path is on a case insensitive file system, 0 if not, -1 on error.
*/
int
pathicase(const char *path)
{
#ifdef _PC_PATH_ATTRIBUTES
/* AT&T mystery system (AST UWIN?) */
char *a = astconf("PATH_ATTRIBUTES", path, (char*)0);
return a ? strchr(a,'c') != 0 : -1;
#elif _lib_pathconf && defined(_PC_CASE_SENSITIVE)
/* macOS; QNX 7.0+ */
long r = pathconf(path, _PC_CASE_SENSITIVE);
return r < 0L ? -1 : r == 0L;
#elif _lib_pathconf && defined(_PC_CASE_INSENSITIVE)
/* Cygwin */
long r = pathconf(path, _PC_CASE_INSENSITIVE);
return r < 0L ? -1 : r > 0L;
#elif _hdr_linux_fs && _hdr_sys_ioctl && defined(FS_IOC_GETFLAGS) && defined(FS_CASEFOLD_FL)
/* Linux 5.2+ */
int attr = 0, fd, r;
if ((fd = open(path, O_RDONLY|O_NONBLOCK)) < 0)
return -1;
r = ioctl(fd, FS_IOC_GETFLAGS, &attr);
close(fd);
return r < 0 ? -1 : attr & FS_CASEFOLD_FL != 0;
#elif _WINIX || __APPLE__
/* Windows or Mac without pathconf probe: assume case insensitive */
return 1;
#else
/* Not implemented */
errno = ENOSYS;
return -1;
#endif
}

View file

@ -221,11 +221,7 @@ static Feature_t dynamic[] =
{
&dynamic[OP_path_attributes+1],
"PATH_ATTRIBUTES",
#if _WINIX
"c",
#else
&null[0],
#endif
&null[0],
0,
15,
@ -706,6 +702,8 @@ format(register Feature_t* fp, const char* path, const char* value, unsigned int
}
*s = 0;
}
#else
fp->value = pathicase(path) > 0 ? "c" : null;
#endif
break;