1#!/usr/bin/env python 2# Copyright (c) 2012 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Extracts a single file from a CAB archive.""" 7 8from __future__ import print_function 9 10import os 11import shutil 12import subprocess 13import sys 14import tempfile 15 16def run_quiet(*args): 17 """Run 'expand' suppressing noisy output. Returns returncode from process.""" 18 popen = subprocess.Popen(args, stdout=subprocess.PIPE) 19 out, _ = popen.communicate() 20 if popen.returncode: 21 # expand emits errors to stdout, so if we fail, then print that out. 22 print(out) 23 return popen.returncode 24 25def main(): 26 if len(sys.argv) != 4: 27 print('Usage: extract_from_cab.py cab_path archived_file output_dir') 28 return 1 29 30 [cab_path, archived_file, output_dir] = sys.argv[1:] 31 32 # Expand.exe does its work in a fixed-named temporary directory created within 33 # the given output directory. This is a problem for concurrent extractions, so 34 # create a unique temp dir within the desired output directory to work around 35 # this limitation. 36 temp_dir = tempfile.mkdtemp(dir=output_dir) 37 38 try: 39 # Invoke the Windows expand utility to extract the file. 40 level = run_quiet('expand', cab_path, '-F:' + archived_file, temp_dir) 41 if level == 0: 42 # Move the output file into place, preserving expand.exe's behavior of 43 # paving over any preexisting file. 44 output_file = os.path.join(output_dir, archived_file) 45 try: 46 os.remove(output_file) 47 except OSError: 48 pass 49 os.rename(os.path.join(temp_dir, archived_file), output_file) 50 finally: 51 shutil.rmtree(temp_dir, True) 52 53 if level != 0: 54 return level 55 56 # The expand utility preserves the modification date and time of the archived 57 # file. Touch the extracted file. This helps build systems that compare the 58 # modification times of input and output files to determine whether to do an 59 # action. 60 os.utime(os.path.join(output_dir, archived_file), None) 61 return 0 62 63 64if __name__ == '__main__': 65 sys.exit(main()) 66