net-irc/limnoria: use HTTPS for GitHub and HOMEPAGE
[gentoo.git] / eclass / cdrom.eclass
1 # Copyright 1999-2017 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 # @ECLASS: cdrom.eclass
5 # @MAINTAINER:
6 # games@gentoo.org
7 # @BLURB: Functions for CD-ROM handling
8 # @DESCRIPTION:
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!
11 #
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.
16
17 if [[ -z ${_CDROM_ECLASS} ]]; then
18 _CDROM_ECLASS=1
19
20 inherit portability
21
22 # @ECLASS-VARIABLE: CDROM_OPTIONAL
23 # @DEFAULT_UNSET
24 # @DESCRIPTION:
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
30         IUSE="cdinstall"
31         PROPERTIES="cdinstall? ( interactive )"
32 else
33         PROPERTIES="interactive"
34 fi
35
36 # @FUNCTION: cdrom_get_cds
37 # @USAGE: <cd1 file>[:alt cd1 file] [cd2 file[:alt cd2 file]] [...]
38 # @DESCRIPTION:
39 # Attempt to locate a CD based upon a file that is on the CD.
40 #
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.
44 #
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
51 # subsequent discs.
52 #
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.
57 #
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.
62 #
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.
68 #
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.
72 cdrom_get_cds() {
73         unset CDROM_SET
74         export CDROM_CURRENT_CD=0 CDROM_CHECKS=( "${@}" )
75
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
79                 :
80
81         # Single disc info.
82         elif [[ ${#} -eq 1 ]] ; then
83                 einfo "This ebuild will need the ${CDROM_NAME:-CD for ${PN}}"
84                 echo
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."
89                 echo
90                 einfo "For example:"
91                 einfo "export CD_ROOT=/mnt/cdrom"
92                 echo
93
94         # Multi disc info.
95         else
96                 _cdrom_set_names
97                 einfo "This package may need access to ${#} CDs."
98                 local cdcnt
99                 for cdcnt in $(seq ${#}); do
100                         local var=CDROM_NAME_${cdcnt}
101                         [[ ! -z ${!var} ]] && einfo " CD ${cdcnt}: ${!var}"
102                 done
103                 echo
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 ${#}))
108                 echo
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."
113                 echo
114                 einfo "For example:"
115                 einfo "export CD_ROOT=/mnt/cdrom"
116                 echo
117         fi
118
119         # Scan for the first disc.
120         cdrom_load_next_cd
121 }
122
123 # @FUNCTION: cdrom_load_next_cd
124 # @DESCRIPTION:
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.
128 #
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.
134 #
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
140 # in this instance.
141 #
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.
144 #
145 # Regardless of which scanning method is used, several variables are set
146 # by this function for you to use:
147 #
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.
152 #
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,,}.
159 #
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.
167 #
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.
174 #
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.
177 #
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
183
184         unset CDROM_ROOT
185         ((++CDROM_CURRENT_CD))
186
187         _cdrom_set_names
188
189         while true ; do
190                 local i cdset
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))]}"
194
195                 for i in $(seq ${CDROM_SET:-0} ${CDROM_SET:-$((${#cdset[@]} - 1))}); do
196                         local f=${cdset[${i}]} point= node= fs= opts=
197
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)"
206                         else
207                                 export CDROM_MATCH=$(_cdrom_glob_match "${CDROM_ROOT}" "${f}")
208                         fi
209
210                         if [[ -n ${CDROM_MATCH} ]] ; then
211                                 export CDROM_ABSMATCH=${CDROM_ROOT}/${CDROM_MATCH}
212                                 export CDROM_SET=${i}
213                                 break 2
214                         fi
215                 done
216
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}"
222                 fi
223
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 !"
227                         else
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 !"
231                                 else
232                                         einfo "Please insert+mount the ${!var} now !"
233                                 fi
234                         fi
235                         showedmsg=1
236                 fi
237
238                 einfo "Press return to scan for the CD again"
239                 einfo "or hit CTRL+C to abort the emerge."
240
241                 if [[ ${showjolietmsg} -eq 0 ]] ; then
242                         showjolietmsg=1
243                 else
244                         echo
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."
249                 fi
250                 read || die "something is screwed with your system"
251         done
252
253         einfo "Found CD #${CDROM_CURRENT_CD} root at ${CDROM_ROOT}"
254 }
255
256 # @FUNCTION: _cdrom_glob_match
257 # @USAGE: <root directory> <path>
258 # @INTERNAL
259 # @DESCRIPTION:
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
271         #
272         # Into this:
273         #  ?(foo\*foo)/?(bar\ bar)/?(baz)/?(file\.zip)
274         #
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)\)
279         (
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%\?()} )"
285                 echo ${ARRAY[0]}
286         )
287 }
288
289 # @FUNCTION: _cdrom_set_names
290 # @INTERNAL
291 # @DESCRIPTION:
292 # Populate CDROM_NAME_# variables with the CDROM_NAMES array.
293 _cdrom_set_names() {
294         if [[ -n ${CDROM_NAMES} ]] ; then
295                 local i
296                 for i in $(seq ${#CDROM_NAMES[@]}); do
297                         export CDROM_NAME_${i}="${CDROM_NAMES[$((${i} - 1))]}"
298                 done
299         fi
300 }
301
302 fi