michael@0: #!/usr/bin/env python michael@0: # Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: # Use of this source code is governed by a BSD-style license that can be michael@0: # found in the LICENSE file. michael@0: michael@0: """Extracts a single file from a CAB archive.""" michael@0: michael@0: import os michael@0: import shutil michael@0: import subprocess michael@0: import sys michael@0: import tempfile michael@0: michael@0: def run_quiet(*args): michael@0: """Run 'expand' supressing noisy output. Returns returncode from process.""" michael@0: popen = subprocess.Popen(args, stdout=subprocess.PIPE) michael@0: out, _ = popen.communicate() michael@0: if popen.returncode: michael@0: # expand emits errors to stdout, so if we fail, then print that out. michael@0: print out michael@0: return popen.returncode michael@0: michael@0: def main(): michael@0: if len(sys.argv) != 4: michael@0: print 'Usage: extract_from_cab.py cab_path archived_file output_dir' michael@0: return 1 michael@0: michael@0: [cab_path, archived_file, output_dir] = sys.argv[1:] michael@0: michael@0: # Expand.exe does its work in a fixed-named temporary directory created within michael@0: # the given output directory. This is a problem for concurrent extractions, so michael@0: # create a unique temp dir within the desired output directory to work around michael@0: # this limitation. michael@0: temp_dir = tempfile.mkdtemp(dir=output_dir) michael@0: michael@0: try: michael@0: # Invoke the Windows expand utility to extract the file. michael@0: level = run_quiet('expand', cab_path, '-F:' + archived_file, temp_dir) michael@0: if level == 0: michael@0: # Move the output file into place, preserving expand.exe's behavior of michael@0: # paving over any preexisting file. michael@0: output_file = os.path.join(output_dir, archived_file) michael@0: try: michael@0: os.remove(output_file) michael@0: except OSError: michael@0: pass michael@0: os.rename(os.path.join(temp_dir, archived_file), output_file) michael@0: finally: michael@0: shutil.rmtree(temp_dir, True) michael@0: michael@0: if level != 0: michael@0: return level michael@0: michael@0: # The expand utility preserves the modification date and time of the archived michael@0: # file. Touch the extracted file. This helps build systems that compare the michael@0: # modification times of input and output files to determine whether to do an michael@0: # action. michael@0: os.utime(os.path.join(output_dir, archived_file), None) michael@0: return 0 michael@0: michael@0: michael@0: if __name__ == '__main__': michael@0: sys.exit(main())