1 |
commit: fde70c442fd394bfd912d266b8442d86b76dea1a |
2 |
Author: James Le Cuirot <chewi <AT> gentoo <DOT> org> |
3 |
AuthorDate: Sun Apr 16 09:36:07 2017 +0000 |
4 |
Commit: James Le Cuirot <chewi <AT> gentoo <DOT> org> |
5 |
CommitDate: Thu Apr 27 21:41:21 2017 +0000 |
6 |
URL: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=fde70c44 |
7 |
|
8 |
cdrom.eclass: Detect case-insensitively and handle special characters |
9 |
|
10 |
This eclass previously used "find -iname" but it only checked the file |
11 |
case-insensitively and not the directories. There is "find -ipath" but |
12 |
this does not intelligently skip non-matching paths, making it |
13 |
slow. Globbing is used here instead. |
14 |
|
15 |
The : character has always been used to delimit paths given to |
16 |
cdrom_get_cds, which makes sense because : generally isn't allowed on |
17 |
CDs, while whitespace is. Despite that, whitespace was not being |
18 |
handled properly and neither were wildcard characters. Now all special |
19 |
characters are automatically escaped. |
20 |
|
21 |
eclass/cdrom.eclass | 58 ++++++++++++++++++++++++++++++++++++++++------------- |
22 |
1 file changed, 44 insertions(+), 14 deletions(-) |
23 |
|
24 |
diff --git a/eclass/cdrom.eclass b/eclass/cdrom.eclass |
25 |
index 41488d2446c..a51270d33e9 100644 |
26 |
--- a/eclass/cdrom.eclass |
27 |
+++ b/eclass/cdrom.eclass |
28 |
@@ -79,12 +79,13 @@ cdrom_get_cds() { |
29 |
export CDROM_ROOT=${CD_ROOT_1:-${CD_ROOT}} |
30 |
einfo "Found CD #${CDROM_CURRENT_CD} root at ${CDROM_ROOT}" |
31 |
export CDROM_SET=-1 |
32 |
- for f in ${CDROM_CHECK_1//:/ } ; do |
33 |
+ local IFS=: |
34 |
+ for f in ${CDROM_CHECK_1} ; do |
35 |
+ unset IFS |
36 |
((++CDROM_SET)) |
37 |
- [[ -e ${CDROM_ROOT}/${f} ]] && break |
38 |
+ export CDROM_MATCH=$(_cdrom_glob_match "${CDROM_ROOT}" "${f}") |
39 |
+ [[ -n ${CDROM_MATCH} ]] && return |
40 |
done |
41 |
- export CDROM_MATCH=${f} |
42 |
- return |
43 |
fi |
44 |
|
45 |
# User didn't help us out so lets make sure they know they can |
46 |
@@ -181,28 +182,24 @@ _cdrom_locate_file_on_cd() { |
47 |
local showedmsg=0 showjolietmsg=0 |
48 |
|
49 |
while [[ -z ${CDROM_ROOT} ]] ; do |
50 |
- local i=0 |
51 |
- local -a cdset=(${*//:/ }) |
52 |
+ local i=0 cdset |
53 |
+ IFS=: read -r -a cdset -d "" <<< "${*}" |
54 |
+ |
55 |
if [[ -n ${CDROM_SET} ]] ; then |
56 |
- cdset=(${cdset[${CDROM_SET}]}) |
57 |
+ cdset=( "${cdset[${CDROM_SET}]}" ) |
58 |
fi |
59 |
|
60 |
while [[ -n ${cdset[${i}]} ]] ; do |
61 |
- local dir=$(dirname ${cdset[${i}]}) |
62 |
- local file=$(basename ${cdset[${i}]}) |
63 |
- |
64 |
local point= node= fs= foo= |
65 |
while read point node fs foo ; do |
66 |
[[ " cd9660 iso9660 udf " != *" ${fs} "* ]] && \ |
67 |
! [[ ${fs} == "subfs" && ",${opts}," == *",fs=cdfss,"* ]] \ |
68 |
&& continue |
69 |
point=${point//\040/ } |
70 |
- [[ ! -d ${point}/${dir} ]] && continue |
71 |
- [[ -z $(find "${point}/${dir}" -maxdepth 1 -iname "${file}") ]] \ |
72 |
- && continue |
73 |
+ export CDROM_MATCH=$(_cdrom_glob_match "${point}" "${cdset[${i}]}") |
74 |
+ [[ -z ${CDROM_MATCH} ]] && continue |
75 |
export CDROM_ROOT=${point} |
76 |
export CDROM_SET=${i} |
77 |
- export CDROM_MATCH=${cdset[${i}]} |
78 |
return |
79 |
done <<< "$(get_mounts)" |
80 |
|
81 |
@@ -243,4 +240,37 @@ _cdrom_locate_file_on_cd() { |
82 |
done |
83 |
} |
84 |
|
85 |
+# @FUNCTION: _cdrom_glob_match |
86 |
+# @USAGE: <root directory> <path> |
87 |
+# @INTERNAL |
88 |
+# @DESCRIPTION: |
89 |
+# Locates the given path ($2) within the given root directory ($1) |
90 |
+# case-insensitively and returns the first actual matching path. This |
91 |
+# eclass previously used "find -iname" but it only checked the file |
92 |
+# case-insensitively and not the directories. There is "find -ipath" |
93 |
+# but this does not intelligently skip non-matching paths, making it |
94 |
+# slow. Case-insensitive matching can only be applied to patterns so |
95 |
+# extended globbing is used to turn regular strings into patterns. All |
96 |
+# special characters are escaped so don't worry about breaking this. |
97 |
+_cdrom_glob_match() { |
98 |
+ # The following line turns this: |
99 |
+ # foo*foo/bar bar/baz/file.zip |
100 |
+ # |
101 |
+ # Into this: |
102 |
+ # ?(foo\*foo)/?(bar\ bar)/?(baz)/?(file\.zip) |
103 |
+ # |
104 |
+ # This turns every path component into an escaped extended glob |
105 |
+ # pattern to allow case-insensitive matching. Globs cannot span |
106 |
+ # directories so each component becomes an individual pattern. |
107 |
+ local p=\?\($(sed -e 's:[^A-Za-z0-9/]:\\\0:g' -e 's:/:)/?(:g' <<< "$2" || die)\) |
108 |
+ ( |
109 |
+ cd "$1" 2>/dev/null || return |
110 |
+ shopt -s extglob nocaseglob nullglob || die |
111 |
+ # The first person to make this work without an eval wins a |
112 |
+ # cookie. It breaks without it when spaces are present. |
113 |
+ eval "ARRAY=( ${p} )" |
114 |
+ echo ${ARRAY[0]} |
115 |
+ ) |
116 |
+} |
117 |
+ |
118 |
fi |