15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Extracts a single file from a CAB archive."""
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shutil
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tempfile
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def run_quiet(*args):
150f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  """Run 'expand' suppressing noisy output. Returns returncode from process."""
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  popen = subprocess.Popen(args, stdout=subprocess.PIPE)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  out, _ = popen.communicate()
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if popen.returncode:
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # expand emits errors to stdout, so if we fail, then print that out.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print out
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return popen.returncode
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main():
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(sys.argv) != 4:
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'Usage: extract_from_cab.py cab_path archived_file output_dir'
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  [cab_path, archived_file, output_dir] = sys.argv[1:]
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Expand.exe does its work in a fixed-named temporary directory created within
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the given output directory. This is a problem for concurrent extractions, so
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # create a unique temp dir within the desired output directory to work around
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # this limitation.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  temp_dir = tempfile.mkdtemp(dir=output_dir)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Invoke the Windows expand utility to extract the file.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    level = run_quiet('expand', cab_path, '-F:' + archived_file, temp_dir)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if level == 0:
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Move the output file into place, preserving expand.exe's behavior of
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # paving over any preexisting file.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output_file = os.path.join(output_dir, archived_file)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try:
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        os.remove(output_file)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      except OSError:
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pass
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.rename(os.path.join(temp_dir, archived_file), output_file)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  finally:
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    shutil.rmtree(temp_dir, True)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if level != 0:
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return level
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The expand utility preserves the modification date and time of the archived
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # file. Touch the extracted file. This helps build systems that compare the
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # modification times of input and output files to determine whether to do an
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # action.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  os.utime(os.path.join(output_dir, archived_file), None)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(main())
64