#!/usr/bin/python
__doc__="""%prog [options] [<directory>]

Removes all unversioned files from a subversion working copy directory
<directory>.
When <directory> is not specified the current directory is used.
<directory can also be a white space seperated list of directories.
"""
import os,sys,re,subprocess
from optparse import OptionParser

parser = OptionParser(__doc__)
parser.add_option("-r", "--recursive", action='store_true', dest="Recursive", default=False, help="Also clean subdirectories.")
parser.add_option("-s", "--skiputil", action='store_true', dest="SkipUtil", default=False, help="Skip util and tools directory.")
parser.add_option("-v", "--noverbose", action='store_true', dest="NoVerbose", default=False, help="No output.")

(g_Options, g_Args) = parser.parse_args()

if g_Args:
  for i in range(len(g_Args)):
    g_Args[i]=os.path.abspath(g_Args[i])
    if not os.path.isdir(g_Args[i]):
      parser.error("%s is not a valid directory"%g_Args[i])

NotWorkRe=re.compile("'\.' is not a working copy")

################################################################################

def Print (Message,NoVerbose=None,Append='\n'):
  if NoVerbose!=1:
    Message+=Append
    sys.stdout.write(Message)

################################################################################
class RunCommandException(Exception):
  def __init__(this,CommandLine,Output=None):
    this.CommandLine=CommandLine
    this.Output=Output
  def __str__(this):
    Ret="\n!!! RunCommand exception:\n-> Commandline:\n"
    Ret+=this.CommandLine
    if this.Output:
      Ret+="\n-> Output:\n"
      Ret+=this.Output
    else:
      Ret+="\n-> Command not found"
    Ret+="\n"
    return Ret
################################################################################
PIPE=subprocess.PIPE
STDOUT=subprocess.STDOUT

def RunCommand(Command):
  try:
    if sys.platform=='linux2':
      Process=subprocess.Popen(Command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=True)
    else:
      CREATE_NO_WINDOW=0x8000000
      Process=subprocess.Popen(Command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, creationflags=CREATE_NO_WINDOW, shell=True)
      import msvcrt
      msvcrt.setmode(Process.stdout.fileno(), os.O_TEXT)  # Threat output as ascii noy binary
  except:
    raise RunCommandException(Command)
  return Process

################################################################################
def DelTreeNoJunctions(Dir):
  try:
    os.rmdir(Dir)
    # empty dir or it was a junction, so we can return immediately
    return
  except:
    pass

  for root, dirs, files in os.walk(Dir, topdown=True):
    for name in files:
      os.remove(os.path.join(root,name))
    for dir in dirs:
      # first try just to remove the directory. This is to be sure that when it is a symbolic link
      # just the link is removed and not the complete contents where it points to
      try:
        os.rmdir(os.path.join(root,dir))
        del dirs[dir]  # do not walk this directory
      except:
        pass # go further in the directory tree
  # Now delete the empty directories left over from the previous walk
  for root, dirs, files in os.walk(Dir, topdown=False):
    for dir in dirs:
      os.rmdir(os.path.join(root,dir))
  os.rmdir(Dir)

################################################################################
def ProcessSvnLine(line,NoVerbose=None):
  if line:
    if NotWorkRe.search(line):
      Print(NotWorkRe.sub("'%s' is not a working copy directory. Not cleaning this directory."%(re.sub(r"\\",r"\\\\",Dir)),line))
      sys.exit(1)
    if line[0]=='?' or line[0]=='I':
      Item=re.sub("^\s+","",line[2:])
      if g_Options.SkipUtil:
        if Item[:5]=='util'+os.sep:
          return
        if Item[:6]=='tools'+os.sep:
          return
      if os.path.isdir(Item):
        Print('Deleting directory %s'%(os.path.abspath(Item)),NoVerbose)
        try:
          DelTreeNoJunctions(Item)
        except:
          print "Error deleting directory %s. Contains read-only files?"%Item
      else:
        Print('Deleting file %s'%(os.path.abspath(Item)),NoVerbose)
        try:
          os.remove(Item)
        except Exception,Details:
          print "Error deleting file %s. Is read-only? "%Item
          print Details
################################################################################
def CleanTree(Dir, NoVerbose=None):
  Print("Cleaning '"+Dir+"'")
  if g_Options.Recursive:
    Command='svn st --no-ignore'
  else:
    Command='svn st -N --no-ignore'
  Process=RunCommand(Command)
  StdOut=Process.stdout
  all_data=""
  while 1:
    done = Process.poll()
    if done == None or done == 0:
      line=StdOut.readline()
      all_data+=line
      line=line.rstrip()
      ProcessSvnLine(line,NoVerbose)
    if done == 0:
      data=StdOut.read()
      all_data+=data
      for line in data.splitlines():
        ProcessSvnLine(line,NoVerbose)
      break
    elif done != None:
      #error!
      print "error running svn command:", Command
      print all_data, StdOut.read()
      sys.exit(1)
################################################################################
if not g_Args:
  g_Args=["."]

for Dir in g_Args:
  os.chdir(Dir)
  CleanTree(Dir, g_Options.NoVerbose)