diff options
author | James Le Cuirot <chewi@gentoo.org> | 2017-04-16 10:36:07 +0100 |
---|---|---|
committer | James Le Cuirot <chewi@gentoo.org> | 2017-04-27 22:41:21 +0100 |
commit | fde70c442fd394bfd912d266b8442d86b76dea1a (patch) | |
tree | 9ce06d8f0b474cec328899d15b1c2c98222f2f3e /eclass/cdrom.eclass | |
parent | net-p2p/syncthing: remove myself as maintainer (diff) | |
download | gentoo-fde70c442fd394bfd912d266b8442d86b76dea1a.tar.gz gentoo-fde70c442fd394bfd912d266b8442d86b76dea1a.tar.bz2 gentoo-fde70c442fd394bfd912d266b8442d86b76dea1a.zip |
cdrom.eclass: Detect case-insensitively and handle special characters
This eclass previously used "find -iname" but it only checked the file
case-insensitively and not the directories. There is "find -ipath" but
this does not intelligently skip non-matching paths, making it
slow. Globbing is used here instead.
The : character has always been used to delimit paths given to
cdrom_get_cds, which makes sense because : generally isn't allowed on
CDs, while whitespace is. Despite that, whitespace was not being
handled properly and neither were wildcard characters. Now all special
characters are automatically escaped.
Diffstat (limited to 'eclass/cdrom.eclass')
-rw-r--r-- | eclass/cdrom.eclass | 58 |
1 files changed, 44 insertions, 14 deletions
diff --git a/eclass/cdrom.eclass b/eclass/cdrom.eclass index 41488d2446c2..a51270d33e94 100644 --- a/eclass/cdrom.eclass +++ b/eclass/cdrom.eclass @@ -79,12 +79,13 @@ cdrom_get_cds() { export CDROM_ROOT=${CD_ROOT_1:-${CD_ROOT}} einfo "Found CD #${CDROM_CURRENT_CD} root at ${CDROM_ROOT}" export CDROM_SET=-1 - for f in ${CDROM_CHECK_1//:/ } ; do + local IFS=: + for f in ${CDROM_CHECK_1} ; do + unset IFS ((++CDROM_SET)) - [[ -e ${CDROM_ROOT}/${f} ]] && break + export CDROM_MATCH=$(_cdrom_glob_match "${CDROM_ROOT}" "${f}") + [[ -n ${CDROM_MATCH} ]] && return done - export CDROM_MATCH=${f} - return fi # User didn't help us out so lets make sure they know they can @@ -181,28 +182,24 @@ _cdrom_locate_file_on_cd() { local showedmsg=0 showjolietmsg=0 while [[ -z ${CDROM_ROOT} ]] ; do - local i=0 - local -a cdset=(${*//:/ }) + local i=0 cdset + IFS=: read -r -a cdset -d "" <<< "${*}" + if [[ -n ${CDROM_SET} ]] ; then - cdset=(${cdset[${CDROM_SET}]}) + cdset=( "${cdset[${CDROM_SET}]}" ) fi while [[ -n ${cdset[${i}]} ]] ; do - local dir=$(dirname ${cdset[${i}]}) - local file=$(basename ${cdset[${i}]}) - local point= node= fs= foo= while read point node fs foo ; do [[ " cd9660 iso9660 udf " != *" ${fs} "* ]] && \ ! [[ ${fs} == "subfs" && ",${opts}," == *",fs=cdfss,"* ]] \ && continue point=${point//\040/ } - [[ ! -d ${point}/${dir} ]] && continue - [[ -z $(find "${point}/${dir}" -maxdepth 1 -iname "${file}") ]] \ - && continue + export CDROM_MATCH=$(_cdrom_glob_match "${point}" "${cdset[${i}]}") + [[ -z ${CDROM_MATCH} ]] && continue export CDROM_ROOT=${point} export CDROM_SET=${i} - export CDROM_MATCH=${cdset[${i}]} return done <<< "$(get_mounts)" @@ -243,4 +240,37 @@ _cdrom_locate_file_on_cd() { done } +# @FUNCTION: _cdrom_glob_match +# @USAGE: <root directory> <path> +# @INTERNAL +# @DESCRIPTION: +# Locates the given path ($2) within the given root directory ($1) +# case-insensitively and returns the first actual matching path. This +# eclass previously used "find -iname" but it only checked the file +# case-insensitively and not the directories. There is "find -ipath" +# but this does not intelligently skip non-matching paths, making it +# slow. Case-insensitive matching can only be applied to patterns so +# extended globbing is used to turn regular strings into patterns. All +# special characters are escaped so don't worry about breaking this. +_cdrom_glob_match() { + # The following line turns this: + # foo*foo/bar bar/baz/file.zip + # + # Into this: + # ?(foo\*foo)/?(bar\ bar)/?(baz)/?(file\.zip) + # + # This turns every path component into an escaped extended glob + # pattern to allow case-insensitive matching. Globs cannot span + # directories so each component becomes an individual pattern. + local p=\?\($(sed -e 's:[^A-Za-z0-9/]:\\\0:g' -e 's:/:)/?(:g' <<< "$2" || die)\) + ( + cd "$1" 2>/dev/null || return + shopt -s extglob nocaseglob nullglob || die + # The first person to make this work without an eval wins a + # cookie. It breaks without it when spaces are present. + eval "ARRAY=( ${p} )" + echo ${ARRAY[0]} + ) +} + fi |