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)import fnmatch
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import glob
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import posixpath
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shutil
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import stat
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import zipfile
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)if sys.version_info < (2, 6, 0):
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sys.stderr.write("python 2.6 or later is required run this script\n")
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sys.exit(1)
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def IncludeFiles(filters, files):
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Filter files based on inclusion lists
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Return a list of files which match and of the Unix shell-style wildcards
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  provided, or return all the files if no filter is provided."""
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not filters:
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return files
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  match = set()
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for file_filter in filters:
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    match |= set(fnmatch.filter(files, file_filter))
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return [name for name in files if name in match]
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ExcludeFiles(filters, files):
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Filter files based on exclusions lists
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Return a list of files which do not match any of the Unix shell-style
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wildcards provided, or return all the files if no filter is provided."""
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not filters:
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return files
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  match = set()
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for file_filter in filters:
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    excludes = set(fnmatch.filter(files, file_filter))
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    match |= excludes
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return [name for name in files if name not in match]
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CopyPath(options, src, dst):
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """CopyPath from src to dst
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Copy a fully specified src to a fully specified dst.  If src and dst are
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  both files, the dst file is removed first to prevent error.  If and include
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  or exclude list are provided, the destination is first matched against that
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  filter."""
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.includes:
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not IncludeFiles(options.includes, [src]):
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.excludes:
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not ExcludeFiles(options.excludes, [src]):
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.verbose:
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'cp %s %s' % (src, dst)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the source is a single file, copy it individually
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.isfile(src):
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We can not copy over a directory with a file.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.exists(dst):
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not os.path.isfile(dst):
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        msg = "cp: cannot overwrite non-file '%s' with file." % dst
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise OSError(msg)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # If the destination exists as a file, remove it before copying to avoid
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # 'readonly' issues.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.remove(dst)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Now copy to the non-existent fully qualified target
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    shutil.copy(src, dst)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Otherwise it's a directory, ignore it unless allowed
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.isdir(src):
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not options.recursive:
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print "cp: omitting directory '%s'" % src
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We can not copy over a file with a directory.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.exists(dst):
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not os.path.isdir(dst):
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        msg = "cp: cannot overwrite non-directory '%s' with directory." % dst
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise OSError(msg)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # if it didn't exist, create the directory
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.makedirs(dst)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Now copy all members
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for filename in os.listdir(src):
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      srcfile = os.path.join(src, filename)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dstfile = os.path.join(dst, filename)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CopyPath(options, srcfile, dstfile)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Copy(args):
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A Unix cp style copy.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Copies multiple sources to a single destination using the normal cp
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  semantics.  In addition, it support inclusion and exclusion filters which
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  allows the copy to skip certain types of files."""
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parser = optparse.OptionParser(usage='usage: cp [Options] sources... dest')
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-R', '-r', '--recursive', dest='recursive', action='store_true',
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='copy directories recursively.')
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-v', '--verbose', dest='verbose', action='store_true',
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='verbose output.')
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '--include', dest='includes', action='append', default=[],
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='include files matching this expression.')
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '--exclude', dest='excludes', action='append', default=[],
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='exclude files matching this expression.')
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  options, files = parser.parse_args(args)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(files) < 2:
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parser.error('ERROR: expecting SOURCE(s) and DEST.')
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srcs = files[:-1]
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dst = files[-1]
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  src_list = []
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for src in srcs:
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    files = glob.glob(src)
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not files:
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise OSError('cp: no such file or directory: ' + src)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if files:
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      src_list.extend(files)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for src in src_list:
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If the destination is a directory, then append the basename of the src
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # to the destination.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.isdir(dst):
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CopyPath(options, src, os.path.join(dst, os.path.basename(src)))
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CopyPath(options, src, dst)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Mkdir(args):
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A Unix style mkdir"""
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser = optparse.OptionParser(usage='usage: mkdir [Options] DIRECTORY...')
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-p', '--parents', dest='parents', action='store_true',
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='ignore existing parents, create parents as needed.')
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-v', '--verbose', dest='verbose', action='store_true',
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='verbose output.')
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  options, dsts = parser.parse_args(args)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(dsts) < 1:
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parser.error('ERROR: expecting DIRECTORY...')
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for dst in dsts:
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if options.verbose:
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'mkdir ' + dst
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.makedirs(dst)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except OSError:
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if os.path.isdir(dst):
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if options.parents:
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        raise OSError('mkdir: Already exists: ' + dst)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise OSError('mkdir: Failed to create: ' + dst)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MovePath(options, src, dst):
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """MovePath from src to dst
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Moves the src to the dst much like the Unix style mv command, except it
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  only handles one source at a time.  Because of possible temporary failures
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  do to locks (such as anti-virus software on Windows), the function will retry
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  up to five times."""
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # if the destination is not an existing directory, then overwrite it
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.isdir(dst):
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dst = os.path.join(dst, os.path.basename(src))
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the destination exists, the remove it
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.exists(dst):
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if options.force:
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Remove(['-vfr', dst])
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if os.path.exists(dst):
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise OSError('mv: FAILED TO REMOVE ' + dst)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise OSError('mv: already exists ' + dst)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for _ in range(5):
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      os.rename(src, dst)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except OSError as error:
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'Failed on %s with %s, retrying' % (src, error)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      time.sleep(5)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print 'Gave up.'
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  raise OSError('mv: ' + error)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Move(args):
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parser = optparse.OptionParser(usage='usage: mv [Options] sources... dest')
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-v', '--verbose', dest='verbose', action='store_true',
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='verbose output.')
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-f', '--force', dest='force', action='store_true',
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='force, do not error it files already exist.')
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  options, files = parser.parse_args(args)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(files) < 2:
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parser.error('ERROR: expecting SOURCE... and DEST.')
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srcs = files[:-1]
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dst = files[-1]
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if options.verbose:
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'mv %s %s' % (' '.join(srcs), dst)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for src in srcs:
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MovePath(options, src, dst)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Remove(args):
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A Unix style rm.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Removes the list of paths.  Because of possible temporary failures do to locks
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (such as anti-virus software on Windows), the function will retry up to five
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  times."""
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser = optparse.OptionParser(usage='usage: rm [Options] PATHS...')
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-R', '-r', '--recursive', dest='recursive', action='store_true',
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='remove directories recursively.')
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-v', '--verbose', dest='verbose', action='store_true',
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='verbose output.')
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-f', '--force', dest='force', action='store_true',
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='force, do not error it files does not exist.')
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  options, files = parser.parse_args(args)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(files) < 1:
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parser.error('ERROR: expecting FILE...')
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for pattern in files:
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dst_files = glob.glob(pattern)
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if not dst_files:
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # Ignore non existing files when using force
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if options.force:
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          continue
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise OSError('rm: no such file or directory: ' + pattern)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for dst in dst_files:
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if options.verbose:
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          print 'rm ' + dst
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if os.path.isfile(dst) or os.path.islink(dst):
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          for i in range(5):
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            try:
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              # Check every time, since it may have been deleted after the
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              # previous failed attempt.
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              if os.path.isfile(dst) or os.path.islink(dst):
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                os.remove(dst)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              break
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            except OSError as error:
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              if i == 5:
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                print 'Gave up.'
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                raise OSError('rm: ' + str(error))
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              print 'Failed remove with %s, retrying' % error
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              time.sleep(5)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if options.recursive:
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          for i in range(5):
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            try:
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              if os.path.isdir(dst):
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                shutil.rmtree(dst)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              break
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            except OSError as error:
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              if i == 5:
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                print 'Gave up.'
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                raise OSError('rm: ' + str(error))
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              print 'Failed rmtree with %s, retrying' % error
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              time.sleep(5)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except OSError as error:
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print error
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def MakeZipPath(os_path, isdir, iswindows):
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Changes a path into zipfile format.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # doctest doesn't seem to honor r'' strings, so the backslashes need to be
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # escaped.
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  >>> MakeZipPath(r'C:\\users\\foobar\\blah', False, True)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'users/foobar/blah'
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  >>> MakeZipPath('/tmp/tmpfoobar/something', False, False)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'tmp/tmpfoobar/something'
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  >>> MakeZipPath('./somefile.txt', False, False)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'somefile.txt'
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  >>> MakeZipPath('somedir', True, False)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'somedir/'
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  >>> MakeZipPath('../dir/filename.txt', False, False)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '../dir/filename.txt'
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  >>> MakeZipPath('dir/../filename.txt', False, False)
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'filename.txt'
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  zip_path = os_path
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if iswindows:
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    import ntpath
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # zipfile paths are always posix-style. They also have the drive
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # letter and leading slashes removed.
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    zip_path = ntpath.splitdrive(os_path)[1].replace('\\', '/')
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if zip_path.startswith('/'):
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    zip_path = zip_path[1:]
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  zip_path = posixpath.normpath(zip_path)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # zipfile also always appends a slash to a directory name.
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if isdir:
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    zip_path += '/'
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return zip_path
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def OSMakeZipPath(os_path):
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return MakeZipPath(os_path, os.path.isdir(os_path), sys.platform == 'win32')
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def Zip(args):
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A Unix style zip.
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Compresses the listed files."""
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser = optparse.OptionParser(usage='usage: zip [Options] zipfile list')
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-r', dest='recursive', action='store_true',
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='recurse into directories')
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  parser.add_option(
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '-q', dest='quiet', action='store_true',
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default=False,
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help='quiet operation')
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  options, files = parser.parse_args(args)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(files) < 2:
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parser.error('ERROR: expecting ZIPFILE and LIST.')
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dest_zip = files[0]
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  src_args = files[1:]
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  src_files = []
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for src_arg in src_args:
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    globbed_src_args = glob.glob(src_arg)
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not globbed_src_args:
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not options.quiet:
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        print 'zip warning: name not matched: %s' % (src_arg,)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for src_file in globbed_src_args:
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      src_file = os.path.normpath(src_file)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      src_files.append(src_file)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if options.recursive and os.path.isdir(src_file):
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for root, dirs, files in os.walk(src_file):
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          for dirname in dirs:
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            src_files.append(os.path.join(root, dirname))
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          for filename in files:
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            src_files.append(os.path.join(root, filename))
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  zip_stream = None
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # zip_data represents a list of the data to be written or appended to the
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # zip_stream. It is a list of tuples:
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #   (OS file path, zip path/zip file info, and file data)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # In all cases one of the |os path| or the |file data| will be None.
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # |os path| is None when there is no OS file to write to the archive (i.e.
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the file data already existed in the archive). |file data| is None when the
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # file is new (never existed in the archive) or being updated.
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  zip_data = []
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_files_to_add = [OSMakeZipPath(src_file) for src_file in src_files]
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  zip_path_to_os_path_dict = dict((new_files_to_add[i], src_files[i])
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  for i in range(len(src_files)))
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  write_mode = 'a'
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    zip_stream = zipfile.ZipFile(dest_zip, 'r')
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    files_to_update = set(new_files_to_add).intersection(
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        set(zip_stream.namelist()))
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if files_to_update:
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # As far as I can tell, there is no way to update a zip entry using
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # zipfile; the best you can do is rewrite the archive.
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Iterate through the zipfile to maintain file order.
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      write_mode = 'w'
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for zip_path in zip_stream.namelist():
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if zip_path in files_to_update:
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          os_path = zip_path_to_os_path_dict[zip_path]
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          zip_data.append((os_path, zip_path, None))
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          new_files_to_add.remove(zip_path)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          file_bytes = zip_stream.read(zip_path)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          file_info = zip_stream.getinfo(zip_path)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          zip_data.append((None, file_info, file_bytes))
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except IOError:
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pass
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  finally:
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if zip_stream:
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      zip_stream.close()
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for zip_path in new_files_to_add:
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    zip_data.append((zip_path_to_os_path_dict[zip_path], zip_path, None))
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not zip_data:
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'zip error: Nothing to do! (%s)' % (dest_zip,)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    zip_stream = zipfile.ZipFile(dest_zip, write_mode, zipfile.ZIP_DEFLATED)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for os_path, file_info_or_zip_path, file_bytes in zip_data:
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if isinstance(file_info_or_zip_path, zipfile.ZipInfo):
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        zip_path = file_info_or_zip_path.filename
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        zip_path = file_info_or_zip_path
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if os_path:
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        st = os.stat(os_path)
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if stat.S_ISDIR(st.st_mode):
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # Python 2.6 on the buildbots doesn't support writing directories to
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # zip files. This was resolved in a later version of Python 2.6.
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # We'll work around it by writing an empty file with the correct
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # path. (This is basically what later versions do anyway.)
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info = zipfile.ZipInfo()
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info.filename = zip_path
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info.date_time = time.localtime(st.st_mtime)[0:6]
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info.compress_type = zip_stream.compression
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info.flag_bits = 0x00
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info.external_attr = (st[0] & 0xFFFF) << 16L
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info.CRC = 0
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info.compress_size = 0
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_info.file_size = 0
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_stream.writestr(zip_info, '')
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        else:
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          zip_stream.write(os_path, zip_path)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        zip_stream.writestr(file_info_or_zip_path, file_bytes)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not options.quiet:
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if zip_path in new_files_to_add:
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          operation = 'adding'
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          operation = 'updating'
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        zip_info = zip_stream.getinfo(zip_path)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (zip_info.compress_type == zipfile.ZIP_STORED or
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            zip_info.file_size == 0):
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          print '  %s: %s (stored 0%%)' % (operation, zip_path)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        elif zip_info.compress_type == zipfile.ZIP_DEFLATED:
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          print '  %s: %s (deflated %d%%)' % (operation, zip_path,
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              100 - zip_info.compress_size * 100 / zip_info.file_size)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  finally:
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    zip_stream.close()
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def FindExeInPath(filename):
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  env_path = os.environ.get('PATH', '')
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  paths = env_path.split(os.pathsep)
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def IsExecutableFile(path):
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return os.path.isfile(path) and os.access(path, os.X_OK)
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if os.path.sep in filename:
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if IsExecutableFile(filename):
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return filename
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for path in paths:
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    filepath = os.path.join(path, filename)
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if IsExecutableFile(filepath):
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return os.path.abspath(os.path.join(path, filename))
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def Which(args):
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """A Unix style which.
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Looks for all arguments in the PATH environment variable, and prints their
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path if they are executable files.
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Note: If you pass an argument with a path to which, it will just test if it
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  is executable, not if it is in the path.
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parser = optparse.OptionParser(usage='usage: which args...')
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  _, files = parser.parse_args(args)
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if not files:
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 0
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  retval = 0
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for filename in files:
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fullname = FindExeInPath(filename)
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if fullname:
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      print fullname
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      retval = 1
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return retval
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FuncMap = {
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'cp': Copy,
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'mkdir': Mkdir,
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'mv': Move,
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'rm': Remove,
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  'zip': Zip,
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  'which': Which,
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main(args):
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if not args:
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    print 'No command specified'
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    print 'Available commands: %s' % ' '.join(FuncMap)
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return 1
52890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  func_name = args[0]
52990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  func = FuncMap.get(func_name)
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not func:
53190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    print 'Do not recognize command: %s' % func_name
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'Available commands: %s' % ' '.join(FuncMap)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
53490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  try:
53590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return func(args[1:])
53690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  except KeyboardInterrupt:
53790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    print '%s: interrupted' % func_name
53890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return 1
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sys.exit(main(sys.argv[1:]))
542