1 # Copyright 1999-2017 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 # @ECLASS: cdrom.eclass
7 # @BLURB: Functions for CD-ROM handling
9 # Acquire CD(s) for those lovely CD-based emerges. Yes, this violates
10 # the whole "non-interactive" policy, but damnit I want CD support!
12 # Do not call these functions in pkg_* phases like pkg_setup as they
13 # should not be used for binary packages. Most packages using this
14 # eclass will require RESTRICT="bindist" but the point still stands.
15 # The functions are generally called in src_unpack.
17 if [[ -z ${_CDROM_ECLASS} ]]; then
22 # @ECLASS-VARIABLE: CDROM_OPTIONAL
25 # By default, the eclass sets PROPERTIES="interactive" on the assumption
26 # that people will be using these. If your package optionally supports
27 # disc-based installs then set this to "yes" and we'll set things
28 # conditionally based on USE="cdinstall".
29 if [[ ${CDROM_OPTIONAL} == "yes" ]] ; then
31 PROPERTIES="cdinstall? ( interactive )"
33 PROPERTIES="interactive"
36 # @FUNCTION: cdrom_get_cds
37 # @USAGE: <cd1 file>[:alt cd1 file] [cd2 file[:alt cd2 file]] [...]
39 # Attempt to locate a CD based upon a file that is on the CD.
41 # If the data spans multiple discs then additional arguments can be
42 # given to check for more files. Call cdrom_load_next_cd() to scan for
43 # the next disc in the set.
45 # Sometimes it is necessary to support alternative CD "sets" where the
46 # contents differ. Alternative files for each disc can be appended to
47 # each argument, separated by the : character. This feature is
48 # frequently used to support installing from an existing installation.
49 # Note that after the first disc is detected, the set is locked so
50 # cdrom_load_next_cd() will only scan for files in that specific set on
53 # The given files can be within named subdirectories. It is not
54 # necessary to specify different casings of the same filename as
55 # matching is done case-insensitively. Filenames can include special
56 # characters such as spaces. Only : is not allowed.
58 # If you don't want each disc to be referred to as "CD #1", "CD #2",
59 # etc. then you can optionally provide your own names. Set CDROM_NAME
60 # for a single disc, CDROM_NAMES as an array for multiple discs, or
61 # individual CDROM_NAME_# variables for each disc starting from 1.
63 # Despite what you may have seen in older ebuilds, it has never been
64 # possible to provide per-set disc names. This would not make sense as
65 # all the names are initially displayed before the first disc has been
66 # detected. As a workaround, you can redefine the name variable(s)
67 # after the first disc has been detected.
69 # This function ends with a cdrom_load_next_cd() call to scan for the
70 # first disc. For more details about variables read and written by this
71 # eclass, see that function's description.
74 export CDROM_CURRENT_CD=0 CDROM_CHECKS=( "${@}" )
76 # If the user has set CD_ROOT or CD_ROOT_1, don't bother informing
77 # them about which discs are needed as they presumably already know.
78 if [[ -n ${CD_ROOT}${CD_ROOT_1} ]] ; then
82 elif [[ ${#} -eq 1 ]] ; then
83 einfo "This ebuild will need the ${CDROM_NAME:-CD for ${PN}}"
85 einfo "If you do not have the CD, but have the data files"
86 einfo "mounted somewhere on your filesystem, just export"
87 einfo "the variable CD_ROOT so that it points to the"
88 einfo "directory containing the files."
91 einfo "export CD_ROOT=/mnt/cdrom"
97 einfo "This package may need access to ${#} CDs."
99 for cdcnt in $(seq ${#}); do
100 local var=CDROM_NAME_${cdcnt}
101 [[ ! -z ${!var} ]] && einfo " CD ${cdcnt}: ${!var}"
104 einfo "If you do not have the CDs, but have the data files"
105 einfo "mounted somewhere on your filesystem, just export"
106 einfo "the following variables so they point to the right place:"
107 einfo $(printf "CD_ROOT_%d " $(seq ${#}))
109 einfo "Or, if you have all the files in the same place, or"
110 einfo "you only have one CD, you can export CD_ROOT"
111 einfo "and that place will be used as the same data source"
112 einfo "for all the CDs."
115 einfo "export CD_ROOT=/mnt/cdrom"
119 # Scan for the first disc.
123 # @FUNCTION: cdrom_load_next_cd
125 # If multiple arguments were given to cdrom_get_cds() then you can call
126 # this function to scan for the next disc. This function is also called
127 # implicitly to scan for the first disc.
129 # The file(s) given to cdrom_get_cds() are scanned for on any mounted
130 # filesystem that resembles optical media. If no match is found then
131 # the user is prompted to insert and mount the disc and press enter to
132 # rescan. This will loop continuously until a match is found or the
133 # user aborts with Ctrl+C.
135 # The user can override the scan location by setting CD_ROOT for a
136 # single disc, CD_ROOT if multiple discs are merged into the same
137 # directory tree (useful for existing installations), or individual
138 # CD_ROOT_# variables for each disc starting from 1. If no match is
139 # found then the function dies with an error as a rescan will not help
142 # Users wanting to set CD_ROOT or CD_ROOT_# for specific packages
143 # persistently can do so using Portage's /etc/portage/env feature.
145 # Regardless of which scanning method is used, several variables are set
146 # by this function for you to use:
148 # CDROM_ROOT: Root path of the detected disc.
149 # CDROM_MATCH: Path of the matched file, relative to CDROM_ROOT.
150 # CDROM_ABSMATCH: Absolute path of the matched file.
151 # CDROM_SET: The matching set number, starting from 0.
153 # The casing of CDROM_MATCH may not be the same as the argument given to
154 # cdrom_get_cds() as matching is done case-insensitively. You should
155 # therefore use this variable (or CDROM_ABSMATCH) when performing file
156 # operations to ensure the file is found. Use newins rather than doins
157 # to keep the final result consistent and take advantage of Bash
158 # case-conversion features like ${FOO,,}.
160 # Chances are that you'll need more than just the matched file from each
161 # disc though. You should not assume the casing of these files either
162 # but dealing with this goes beyond the scope of this ebuild. For a
163 # good example, see games-action/descent2-data, which combines advanced
164 # globbing with advanced tar features to concisely deal with
165 # case-insensitive matching, case conversion, file moves, and
166 # conditional exclusion.
168 # Copying directly from a mounted disc using doins/newins will remove
169 # any read-only permissions but be aware of these when copying to an
170 # intermediate directory first. Attempting to clean a build directory
171 # containing read-only files as a non-root user will result in an error.
172 # If you're using tar as suggested above then you can easily work around
173 # this with --mode=u+w.
175 # Note that you can only go forwards in the disc list, so make sure you
176 # only call this function when you're done using the current disc.
178 # If you cd to any location within CDROM_ROOT then remember to leave the
179 # directory before calling this function again, otherwise the user won't
180 # be able to unmount the current disc.
181 cdrom_load_next_cd() {
182 local showedmsg=0 showjolietmsg=0
185 ((++CDROM_CURRENT_CD))
191 : CD_ROOT_${CDROM_CURRENT_CD}
192 export CDROM_ROOT=${CD_ROOT:-${!_}}
193 IFS=: read -r -a cdset -d "" <<< "${CDROM_CHECKS[$((${CDROM_CURRENT_CD} - 1))]}"
195 for i in $(seq ${CDROM_SET:-0} ${CDROM_SET:-$((${#cdset[@]} - 1))}); do
196 local f=${cdset[${i}]} point= node= fs= opts=
198 if [[ -z ${CDROM_ROOT} ]] ; then
199 while read point node fs opts ; do
200 has "${fs}" cd9660 iso9660 udf || continue
201 point=${point//\040/ }
202 export CDROM_MATCH=$(_cdrom_glob_match "${point}" "${f}")
203 [[ -z ${CDROM_MATCH} ]] && continue
204 export CDROM_ROOT=${point}
205 done <<< "$(get_mounts)"
207 export CDROM_MATCH=$(_cdrom_glob_match "${CDROM_ROOT}" "${f}")
210 if [[ -n ${CDROM_MATCH} ]] ; then
211 export CDROM_ABSMATCH=${CDROM_ROOT}/${CDROM_MATCH}
212 export CDROM_SET=${i}
217 # If we get here then we were unable to locate a match. If
218 # CDROM_ROOT is non-empty then this implies that a CD_ROOT
219 # variable was given and we should therefore abort immediately.
220 if [[ -n ${CDROM_ROOT} ]] ; then
221 die "unable to locate CD #${CDROM_CURRENT_CD} root at ${CDROM_ROOT}"
224 if [[ ${showedmsg} -eq 0 ]] ; then
225 if [[ ${#CDROM_CHECKS[@]} -eq 1 ]] ; then
226 einfo "Please insert+mount the ${CDROM_NAME:-CD for ${PN}} now !"
228 local var="CDROM_NAME_${CDROM_CURRENT_CD}"
229 if [[ -z ${!var} ]] ; then
230 einfo "Please insert+mount CD #${CDROM_CURRENT_CD} for ${PN} now !"
232 einfo "Please insert+mount the ${!var} now !"
238 einfo "Press return to scan for the CD again"
239 einfo "or hit CTRL+C to abort the emerge."
241 if [[ ${showjolietmsg} -eq 0 ]] ; then
245 ewarn "If you are having trouble with the detection"
246 ewarn "of your CD, it is possible that you do not have"
247 ewarn "Joliet support enabled in your kernel. Please"
248 ewarn "check that CONFIG_JOLIET is enabled in your kernel."
250 read || die "something is screwed with your system"
253 einfo "Found CD #${CDROM_CURRENT_CD} root at ${CDROM_ROOT}"
256 # @FUNCTION: _cdrom_glob_match
257 # @USAGE: <root directory> <path>
260 # Locates the given path ($2) within the given root directory ($1)
261 # case-insensitively and returns the first actual matching path. This
262 # eclass previously used "find -iname" but it only checked the file
263 # case-insensitively and not the directories. There is "find -ipath"
264 # but this does not intelligently skip non-matching paths, making it
265 # slow. Case-insensitive matching can only be applied to patterns so
266 # extended globbing is used to turn regular strings into patterns. All
267 # special characters are escaped so don't worry about breaking this.
268 _cdrom_glob_match() {
269 # The following line turns this:
270 # foo*foo/bar bar/baz/file.zip
273 # ?(foo\*foo)/?(bar\ bar)/?(baz)/?(file\.zip)
275 # This turns every path component into an escaped extended glob
276 # pattern to allow case-insensitive matching. Globs cannot span
277 # directories so each component becomes an individual pattern.
278 local p=\?\($(sed -e 's:[^A-Za-z0-9/]:\\\0:g' -e 's:/:)/?(:g' <<< "$2" || die)\)
280 cd "$1" 2>/dev/null || return
281 shopt -s extglob nocaseglob nullglob || die
282 # The first person to make this work without an eval wins a
283 # cookie. It breaks without it when spaces are present.
284 eval "ARRAY=( ${p%\?()} )"
289 # @FUNCTION: _cdrom_set_names
292 # Populate CDROM_NAME_# variables with the CDROM_NAMES array.
294 if [[ -n ${CDROM_NAMES} ]] ; then
296 for i in $(seq ${#CDROM_NAMES[@]}); do
297 export CDROM_NAME_${i}="${CDROM_NAMES[$((${i} - 1))]}"