Gentoo Archives: gentoo-dev

From: Mike Frysinger <vapier@g.o>
To: gentoo-dev@l.g.o
Subject: Re: [gentoo-dev] unpacker.eclass
Date: Thu, 02 Feb 2012 17:25:30
Message-Id: 201202021224.36215.vapier@gentoo.org
In Reply to: [gentoo-dev] unpacker.eclass by Mike Frysinger
1 here's v2
2 -mike
3
4 # Copyright 1999-2012 Gentoo Foundation
5 # Distributed under the terms of the GNU General Public License v2
6 # $Header: /var/cvsroot/gentoo-x86/eclass/eutils.eclass,v 1.377 2012/01/03 08:45:36 jlec Exp $
7
8 # @ECLASS: unpacker.eclass
9 # @MAINTAINER:
10 # base-system@g.o
11 # @BLURB: helpers for extraneous file formats and consistent behavior across EAPIs
12 # @DESCRIPTION:
13 # Some extraneous file formats are not part of PMS, or are only in certain
14 # EAPIs. Rather than worrying about that, support the crazy cruft here
15 # and for all EAPI versions.
16
17 # Possible todos:
18 # - merge rpm unpacking
19 # - support partial unpacks?
20
21 if [[ ${___ECLASS_ONCE_UNPACKER} != "recur -_+^+_- spank" ]] ; then
22 ___ECLASS_ONCE_UNPACKER="recur -_+^+_- spank"
23
24 # @ECLASS-VARIABLE: UNPACKER_BZ2
25 # @DEFAULT_UNSET
26 # @DESCRIPTION:
27 # Utility to use to decompress bzip2 files. Will dynamically pick between
28 # `pbzip2` and `bzip2`. Make sure your choice accepts the "-c" option.
29 # Note: this is meant for users to set, not ebuilds.
30
31 # for internal use only (unpack_pdv and unpack_makeself)
32 find_unpackable_file() {
33 local src=$1
34 if [[ -z ${src} ]] ; then
35 src=${DISTDIR}/${A}
36 else
37 if [[ ${src} == ./* ]] ; then
38 : # already what we want
39 elif [[ -e ${DISTDIR}/${src} ]] ; then
40 src=${DISTDIR}/${src}
41 elif [[ -e ${PWD}/${src} ]] ; then
42 src=${PWD}/${src}
43 elif [[ -e ${src} ]] ; then
44 src=${src}
45 fi
46 fi
47 [[ ! -e ${src} ]] && return 1
48 echo "${src}"
49 }
50
51 unpack_banner() {
52 echo ">>> Unpacking ${1##*/} to ${PWD}"
53 }
54
55 # @FUNCTION: unpack_pdv
56 # @USAGE: <file to unpack> <size of off_t>
57 # @DESCRIPTION:
58 # Unpack those pesky pdv generated files ...
59 # They're self-unpacking programs with the binary package stuffed in
60 # the middle of the archive. Valve seems to use it a lot ... too bad
61 # it seems to like to segfault a lot :(. So lets take it apart ourselves.
62 #
63 # You have to specify the off_t size ... I have no idea how to extract that
64 # information out of the binary executable myself. Basically you pass in
65 # the size of the off_t type (in bytes) on the machine that built the pdv
66 # archive.
67 #
68 # One way to determine this is by running the following commands:
69 #
70 # @CODE
71 # strings <pdv archive> | grep lseek
72 # strace -elseek <pdv archive>
73 # @CODE
74 #
75 # Basically look for the first lseek command (we do the strings/grep because
76 # sometimes the function call is _llseek or something) and steal the 2nd
77 # parameter. Here is an example:
78 #
79 # @CODE
80 # vapier@vapier 0 pdv_unpack # strings hldsupdatetool.bin | grep lseek
81 # lseek
82 # vapier@vapier 0 pdv_unpack # strace -elseek ./hldsupdatetool.bin
83 # lseek(3, -4, SEEK_END) = 2981250
84 # @CODE
85 #
86 # Thus we would pass in the value of '4' as the second parameter.
87 unpack_pdv() {
88 local src=$(find_unpackable_file "$1")
89 local sizeoff_t=$2
90
91 [[ -z ${src} ]] && die "Could not locate source for '$1'"
92 [[ -z ${sizeoff_t} ]] && die "No idea what off_t size was used for this pdv :("
93
94 unpack_banner "${src}"
95
96 local metaskip=$(tail -c ${sizeoff_t} "${src}" | hexdump -e \"%i\")
97 local tailskip=$(tail -c $((${sizeoff_t}*2)) "${src}" | head -c ${sizeoff_t} | hexdump -e \"%i\")
98
99 # grab metadata for debug reasons
100 local metafile=$(emktemp)
101 tail -c +$((${metaskip}+1)) "${src}" > "${metafile}"
102
103 # rip out the final file name from the metadata
104 local datafile=$(tail -c +$((${metaskip}+1)) "${src}" | strings | head -n 1)
105 datafile=$(basename "${datafile}")
106
107 # now lets uncompress/untar the file if need be
108 local tmpfile=$(emktemp)
109 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null | head -c 512 > ${tmpfile}
110
111 local iscompressed=$(file -b "${tmpfile}")
112 if [[ ${iscompressed:0:8} == "compress" ]] ; then
113 iscompressed=1
114 mv ${tmpfile}{,.Z}
115 gunzip ${tmpfile}
116 else
117 iscompressed=0
118 fi
119 local istar=$(file -b "${tmpfile}")
120 if [[ ${istar:0:9} == "POSIX tar" ]] ; then
121 istar=1
122 else
123 istar=0
124 fi
125
126 #for some reason gzip dies with this ... dd cant provide buffer fast enough ?
127 #dd if=${src} ibs=${metaskip} count=1 \
128 # | dd ibs=${tailskip} skip=1 \
129 # | gzip -dc \
130 # > ${datafile}
131 if [ ${iscompressed} -eq 1 ] ; then
132 if [ ${istar} -eq 1 ] ; then
133 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null \
134 | head -c $((${metaskip}-${tailskip})) \
135 | tar -xzf -
136 else
137 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null \
138 | head -c $((${metaskip}-${tailskip})) \
139 | gzip -dc \
140 > ${datafile}
141 fi
142 else
143 if [ ${istar} -eq 1 ] ; then
144 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null \
145 | head -c $((${metaskip}-${tailskip})) \
146 | tar --no-same-owner -xf -
147 else
148 tail -c +$((${tailskip}+1)) ${src} 2>/dev/null \
149 | head -c $((${metaskip}-${tailskip})) \
150 > ${datafile}
151 fi
152 fi
153 true
154 #[ -s "${datafile}" ] || die "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')"
155 #assert "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')"
156 }
157
158 # @FUNCTION: unpack_makeself
159 # @USAGE: [file to unpack] [offset] [tail|dd]
160 # @DESCRIPTION:
161 # Unpack those pesky makeself generated files ...
162 # They're shell scripts with the binary package tagged onto
163 # the end of the archive. Loki utilized the format as does
164 # many other game companies.
165 #
166 # If the file is not specified, then ${A} is used. If the
167 # offset is not specified then we will attempt to extract
168 # the proper offset from the script itself.
169 unpack_makeself() {
170 local src_input=${1:-${A}}
171 local src=$(find_unpackable_file "${src_input}")
172 local skip=$2
173 local exe=$3
174
175 [[ -z ${src} ]] && die "Could not locate source for '${src_input}'"
176
177 unpack_banner "${src}"
178
179 if [[ -z ${skip} ]] ; then
180 local ver=$(grep -m1 -a '#.*Makeself' "${src}" | awk '{print $NF}')
181 local skip=0
182 exe=tail
183 case ${ver} in
184 1.5.*|1.6.0-nv) # tested 1.5.{3,4,5} ... guessing 1.5.x series is same
185 skip=$(grep -a ^skip= "${src}" | cut -d= -f2)
186 ;;
187 2.0|2.0.1)
188 skip=$(grep -a ^$'\t'tail "${src}" | awk '{print $2}' | cut -b2-)
189 ;;
190 2.1.1)
191 skip=$(grep -a ^offset= "${src}" | awk '{print $2}' | cut -b2-)
192 (( skip++ ))
193 ;;
194 2.1.2)
195 skip=$(grep -a ^offset= "${src}" | awk '{print $3}' | head -n 1)
196 (( skip++ ))
197 ;;
198 2.1.3)
199 skip=`grep -a ^offset= "${src}" | awk '{print $3}'`
200 (( skip++ ))
201 ;;
202 2.1.4|2.1.5)
203 skip=$(grep -a offset=.*head.*wc "${src}" | awk '{print $3}' | head -n 1)
204 skip=$(head -n ${skip} "${src}" | wc -c)
205 exe="dd"
206 ;;
207 *)
208 eerror "I'm sorry, but I was unable to support the Makeself file."
209 eerror "The version I detected was '${ver}'."
210 eerror "Please file a bug about the file ${src##*/} at"
211 eerror "http://bugs.gentoo.org/ so that support can be added."
212 die "makeself version '${ver}' not supported"
213 ;;
214 esac
215 debug-print "Detected Makeself version ${ver} ... using ${skip} as offset"
216 fi
217 case ${exe} in
218 tail) exe="tail -n +${skip} '${src}'";;
219 dd) exe="dd ibs=${skip} skip=1 if='${src}'";;
220 *) die "makeself cant handle exe '${exe}'"
221 esac
222
223 # lets grab the first few bytes of the file to figure out what kind of archive it is
224 local filetype tmpfile=$(emktemp)
225 eval ${exe} 2>/dev/null | head -c 512 > "${tmpfile}"
226 filetype=$(file -b "${tmpfile}") || die
227 case ${filetype} in
228 *tar\ archive*)
229 eval ${exe} | tar --no-same-owner -xf -
230 ;;
231 bzip2*)
232 eval ${exe} | bzip2 -dc | tar --no-same-owner -xf -
233 ;;
234 gzip*)
235 eval ${exe} | tar --no-same-owner -xzf -
236 ;;
237 compress*)
238 eval ${exe} | gunzip | tar --no-same-owner -xf -
239 ;;
240 *)
241 eerror "Unknown filetype \"${filetype}\" ?"
242 false
243 ;;
244 esac
245 assert "failure unpacking (${filetype}) makeself ${src##*/} ('${ver}' +${skip})"
246 }
247
248 # @FUNCTION: unpack_deb
249 # @USAGE: <one deb to unpack>
250 # @DESCRIPTION:
251 # Unpack a Debian .deb archive in style.
252 unpack_deb() {
253 [[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>"
254
255 local deb=$(find_unpackable_file "$1")
256
257 unpack_banner "${deb}"
258
259 ar x "${deb}"
260 unpack ./data.tar*
261 }
262
263 # @FUNCTION: _unpacker
264 # @USAGE: <one archive to unpack>
265 # @INTERNAL
266 # @DESCRIPTION:
267 # Unpack the specified archive. We only operate on one archive here
268 # to keep down on the looping logic (that is handled by `unpacker`).
269 _unpacker() {
270 [[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>"
271
272 local a=$1
273 local m=$(echo "${a}" | tr '[:upper:]' '[:lower:]')
274 a=$(find_unpackable_file "${a}")
275
276 # first figure out the decompression method
277 case ${m} in
278 *.bz2|*.tbz|*.tbz2)
279 local bzcmd=${PORTAGE_BZIP2_COMMAND:-$(type -P pbzip2 || bzip2)}
280 local bzuncmd=${PORTAGE_BUNZIP2_COMMAND:-${PORTAGE_BZIP2_COMMAND} -d}
281 : ${UNPACKER_BZ2:=${bzuncmd}}
282 comp="${UNPACKER_BZ2} -c"
283 ;;
284 *.z|*.gz|*.tgz)
285 comp="gzip -dc" ;;
286 *.lzma|*.xz|*.txz)
287 comp="xz -dc" ;;
288 *) comp="" ;;
289 esac
290
291 # then figure out if there are any archiving aspects
292 case ${m} in
293 *.tgz|*.tbz|*.tbz2|*.txz|*.tar.*|*.tar)
294 arch="tar --no-same-owner -xof" ;;
295 *.deb)
296 arch="unpack_deb" ;;
297 *.run)
298 arch="unpack_makeself" ;;
299 *) arch="" ;;
300 esac
301
302 # finally do the unpack
303 if [[ -z ${arch}${comp} ]] ; then
304 unpack "${a}"
305 return $?
306 fi
307
308 [[ ${arch} != unpack_* ]] && unpack_banner "${a}"
309
310 if [[ -z ${arch} ]] ; then
311 ${comp} "${a}" > "${a%.*}"
312 elif [[ -z ${comp} ]] ; then
313 ${arch} "${a}"
314 else
315 ${comp} "${a}" | ${arch} -
316 fi
317
318 assert "unpacking ${a} failed (comp=${comp} arch=${arch})"
319 }
320
321 # @FUNCTION: unpacker
322 # @USAGE: [archives to unpack]
323 # @DESCRIPTION:
324 # This works in the same way that `unpack` does. If you don't specify
325 # any files, it will default to ${A}.
326 unpacker() {
327 local a
328 [[ $# -eq 0 ]] && set -- ${A}
329 for a ; do _unpacker "${a}" ; done
330 }
331
332 # @FUNCTION: unpacker_src_unpack
333 # @DESCRIPTION:
334 # Run `unpacker` to unpack all our stuff.
335 unpacker_src_unpack() {
336 unpacker
337 }
338
339 # @FUNCTION: unpacker_src_uri_depends
340 # @USAGE: [archives that we will unpack]
341 # @RETURN: Dependencies needed to unpack all the archives
342 # @DESCRIPTION:
343 # Walk all the specified files (defaults to $SRC_URI) and figure out the
344 # dependencies that are needed to unpack things.
345 #
346 # Note: USE flags are not yet handled.
347 unpacker_src_uri_depends() {
348 local uri deps d
349
350 [[ $# -eq 0 ]] && set -- ${SRC_URI}
351
352 for uri in "$@" ; do
353 case ${uri} in
354 *.rar|*.RAR)
355 d="app-arch/unrar" ;;
356 *.7z)
357 d="app-arch/p7zip" ;;
358 *.xz)
359 d="app-arch/xz-utils" ;;
360 esac
361 deps+=" ${d}"
362 done
363
364 echo "${deps}"
365 }
366
367 EXPORT_FUNCTIONS src_unpack
368
369 fi

Attachments

File name MIME type
signature.asc application/pgp-signature