virtualenv/res/lib/patch.py
author durandn
Thu, 28 May 2015 18:33:43 +0200
changeset 261 ad6a491e365c
parent 0 87104b7cb3d6
permissions -rw-r--r--
Updated haystack and guardian + removed old/unused lib from virtualenv/res/src
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
0
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
""" Patch utility to apply unified diffs
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
    Brute-force line-by-line non-recursive parsing 
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
    Copyright (c) 2008-2010 anatoly techtonik
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
    Available under the terms of MIT license
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
    Project home: http://code.google.com/p/python-patch/
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
    $Id: patch.py 76 2010-04-08 19:10:21Z techtonik $
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
    $HeadURL: https://python-patch.googlecode.com/svn/trunk/patch.py $
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
"""
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
__author__ = "techtonik.rainforce.org"
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
__version__ = "10.04"
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
import copy
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
import logging
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
import re
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
# cStringIO doesn't support unicode in 2.5
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
from StringIO import StringIO
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
from logging import debug, info, warning
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
from os.path import exists, isfile, abspath
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
from os import unlink
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
#------------------------------------------------
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
# Logging is controlled by "python_patch" logger
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
debugmode = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
logger = logging.getLogger("python_patch")
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
loghandler = logging.StreamHandler()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
logger.addHandler(loghandler)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
debug = logger.debug
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
info = logger.info
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
warning = logger.warning
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
#: disable library logging by default
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
logger.setLevel(logging.CRITICAL)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
#------------------------------------------------
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
def fromfile(filename):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
  """ Parse patch file and return Patch() object
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
  """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
  info("reading patch from file %s" % filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
  fp = open(filename, "rb")
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
  patch = Patch(fp)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
  fp.close()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
  return patch
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
def fromstring(s):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
  """ Parse text string and return Patch() object
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
  """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
  return Patch(
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
           StringIO.StringIO(s)    
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
         )
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
class HunkInfo(object):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
  """ Parsed hunk data container (hunk starts with @@ -R +R @@) """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
  def __init__(self):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
    self.startsrc=None #: line count starts with 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
    self.linessrc=None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
    self.starttgt=None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
    self.linestgt=None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
    self.invalid=False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
    self.text=[]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
  def copy(self):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
    return copy.copy(self)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
#  def apply(self, estream):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
#    """ write hunk data into enumerable stream
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
#        return strings one by one until hunk is
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
#        over
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
#
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
#        enumerable stream are tuples (lineno, line)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
#        where lineno starts with 0
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
#    """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
#    pass
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
class Patch(object):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
  def __init__(self, stream=None):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
    # define Patch data members
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
    # table with a row for every source file
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
    #: list of source filenames
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
    self.source=None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
    self.target=None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
    #: list of lists of hunks
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
    self.hunks=None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
    #: file endings statistics for every hunk
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
    self.hunkends=None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
    if stream:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
      self.parse(stream)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
  def copy(self):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
    return copy.copy(self)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
  def parse(self, stream):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
    """ parse unified diff """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
    self.source = []
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
    self.target = []
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
    self.hunks = []
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
    self.hunkends = []
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
    # define possible file regions that will direct the parser flow
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
    header = False    # comments before the patch body
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
    filenames = False # lines starting with --- and +++
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
    hunkhead = False  # @@ -R +R @@ sequence
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
    hunkbody = False  #
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
    hunkskip = False  # skipping invalid hunk mode
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
    header = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
    lineends = dict(lf=0, crlf=0, cr=0)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
    nextfileno = 0
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
    nexthunkno = 0    #: even if index starts with 0 user messages number hunks from 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
    # hunkinfo holds parsed values, hunkactual - calculated
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
    hunkinfo = HunkInfo()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
    hunkactual = dict(linessrc=None, linestgt=None)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
    fe = enumerate(stream)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
    for lineno, line in fe:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
      # analyze state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
      if header and line.startswith("--- "):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
        header = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
        # switch to filenames state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
        filenames = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
      #: skip hunkskip and hunkbody code until you read definition of hunkhead
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
      if hunkbody:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
        # process line first
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
        if re.match(r"^[- \+\\]", line):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
            # gather stats about line endings
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
            if line.endswith("\r\n"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
              self.hunkends[nextfileno-1]["crlf"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
            elif line.endswith("\n"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
              self.hunkends[nextfileno-1]["lf"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
            elif line.endswith("\r"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
              self.hunkends[nextfileno-1]["cr"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
              
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
            if line.startswith("-"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
              hunkactual["linessrc"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
            elif line.startswith("+"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
              hunkactual["linestgt"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
            elif not line.startswith("\\"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
              hunkactual["linessrc"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
              hunkactual["linestgt"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
            hunkinfo.text.append(line)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
            # todo: handle \ No newline cases
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
        else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
            warning("invalid hunk no.%d at %d for target file %s" % (nexthunkno, lineno+1, self.target[nextfileno-1]))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
            # add hunk status node
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
            self.hunks[nextfileno-1].append(hunkinfo.copy())
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
            self.hunks[nextfileno-1][nexthunkno-1]["invalid"] = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
            # switch to hunkskip state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
            hunkbody = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
            hunkskip = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
        # check exit conditions
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
        if hunkactual["linessrc"] > hunkinfo.linessrc or hunkactual["linestgt"] > hunkinfo.linestgt:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
            warning("extra hunk no.%d lines at %d for target %s" % (nexthunkno, lineno+1, self.target[nextfileno-1]))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
            # add hunk status node
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
            self.hunks[nextfileno-1].append(hunkinfo.copy())
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
            self.hunks[nextfileno-1][nexthunkno-1]["invalid"] = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
            # switch to hunkskip state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
            hunkbody = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
            hunkskip = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
        elif hunkinfo.linessrc == hunkactual["linessrc"] and hunkinfo.linestgt == hunkactual["linestgt"]:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
            self.hunks[nextfileno-1].append(hunkinfo.copy())
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
            # switch to hunkskip state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
            hunkbody = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
            hunkskip = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
            # detect mixed window/unix line ends
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
            ends = self.hunkends[nextfileno-1]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
            if ((ends["cr"]!=0) + (ends["crlf"]!=0) + (ends["lf"]!=0)) > 1:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
              warning("inconsistent line ends in patch hunks for %s" % self.source[nextfileno-1])
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
            if debugmode:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
              debuglines = dict(ends)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
              debuglines.update(file=self.target[nextfileno-1], hunk=nexthunkno)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
              debug("crlf: %(crlf)d  lf: %(lf)d  cr: %(cr)d\t - file: %(file)s hunk: %(hunk)d" % debuglines)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
      if hunkskip:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
        match = re.match("^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?", line)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
        if match:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
          # switch to hunkhead state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
          hunkskip = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
          hunkhead = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
        elif line.startswith("--- "):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
          # switch to filenames state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
          hunkskip = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
          filenames = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
          if debugmode and len(self.source) > 0:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
            debug("- %2d hunks for %s" % (len(self.hunks[nextfileno-1]), self.source[nextfileno-1]))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
      if filenames:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
        if line.startswith("--- "):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
          if nextfileno in self.source:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
            warning("skipping invalid patch for %s" % self.source[nextfileno])
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
            del self.source[nextfileno]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
            # double source filename line is encountered
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
            # attempt to restart from this second line
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
          re_filename = "^--- ([^\t]+)"
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
          match = re.match(re_filename, line)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
          # todo: support spaces in filenames
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
          if match:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
            self.source.append(match.group(1).strip())
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
          else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
            warning("skipping invalid filename at line %d" % lineno)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
            # switch back to header state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
            filenames = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
            header = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
        elif not line.startswith("+++ "):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
          if nextfileno in self.source:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
            warning("skipping invalid patch with no target for %s" % self.source[nextfileno])
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
            del self.source[nextfileno]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
          else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
            # this should be unreachable
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
            warning("skipping invalid target patch")
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
          filenames = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
          header = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
        else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
          if nextfileno in self.target:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
            warning("skipping invalid patch - double target at line %d" % lineno)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
            del self.source[nextfileno]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
            del self.target[nextfileno]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
            nextfileno -= 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
            # double target filename line is encountered
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
            # switch back to header state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
            filenames = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
            header = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
          else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
            re_filename = "^\+\+\+ ([^\t]+)"
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
            match = re.match(re_filename, line)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
            if not match:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
              warning("skipping invalid patch - no target filename at line %d" % lineno)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
              # switch back to header state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
              filenames = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
              header = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
            else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
              self.target.append(match.group(1).strip())
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
              nextfileno += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
              # switch to hunkhead state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
              filenames = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
              hunkhead = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265
              nexthunkno = 0
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   266
              self.hunks.append([])
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   267
              self.hunkends.append(lineends.copy())
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   268
              continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   269
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   270
      if hunkhead:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   271
        match = re.match("^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?", line)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   272
        if not match:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   273
          if nextfileno-1 not in self.hunks:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   274
            warning("skipping invalid patch with no hunks for file %s" % self.target[nextfileno-1])
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   275
            # switch to header state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   276
            hunkhead = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   277
            header = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   278
            continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   279
          else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   280
            # switch to header state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   281
            hunkhead = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   282
            header = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   283
        else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   284
          hunkinfo.startsrc = int(match.group(1))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   285
          hunkinfo.linessrc = 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   286
          if match.group(3): hunkinfo.linessrc = int(match.group(3))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   287
          hunkinfo.starttgt = int(match.group(4))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   288
          hunkinfo.linestgt = 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   289
          if match.group(6): hunkinfo.linestgt = int(match.group(6))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   290
          hunkinfo.invalid = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   291
          hunkinfo.text = []
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   292
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   293
          hunkactual["linessrc"] = hunkactual["linestgt"] = 0
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   294
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   295
          # switch to hunkbody state
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   296
          hunkhead = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   297
          hunkbody = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   298
          nexthunkno += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   299
          continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   300
    else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   301
      if not hunkskip:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   302
        warning("patch file incomplete - %s" % filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   303
        # sys.exit(?)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   304
      else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   305
        # duplicated message when an eof is reached
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   306
        if debugmode and len(self.source) > 0:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   307
            debug("- %2d hunks for %s" % (len(self.hunks[nextfileno-1]), self.source[nextfileno-1]))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   308
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   309
    info("total files: %d  total hunks: %d" % (len(self.source), sum(len(hset) for hset in self.hunks)))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   310
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   311
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   312
  def apply(self):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   313
    """ apply parsed patch """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   314
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   315
    total = len(self.source)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   316
    for fileno, filename in enumerate(self.source):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   317
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   318
      f2patch = filename
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   319
      if not exists(f2patch):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   320
        f2patch = self.target[fileno]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   321
        if not exists(f2patch):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   322
          warning("source/target file does not exist\n--- %s\n+++ %s" % (filename, f2patch))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   323
          continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   324
      if not isfile(f2patch):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   325
        warning("not a file - %s" % f2patch)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   326
        continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   327
      filename = f2patch
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   328
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   329
      info("processing %d/%d:\t %s" % (fileno+1, total, filename))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   330
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   331
      # validate before patching
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   332
      f2fp = open(filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   333
      hunkno = 0
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   334
      hunk = self.hunks[fileno][hunkno]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   335
      hunkfind = []
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   336
      hunkreplace = []
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   337
      validhunks = 0
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   338
      canpatch = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   339
      for lineno, line in enumerate(f2fp):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   340
        if lineno+1 < hunk.startsrc:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   341
          continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   342
        elif lineno+1 == hunk.startsrc:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   343
          hunkfind = [x[1:].rstrip("\r\n") for x in hunk.text if x[0] in " -"]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   344
          hunkreplace = [x[1:].rstrip("\r\n") for x in hunk.text if x[0] in " +"]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   345
          #pprint(hunkreplace)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   346
          hunklineno = 0
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   347
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   348
          # todo \ No newline at end of file
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   349
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   350
        # check hunks in source file
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   351
        if lineno+1 < hunk.startsrc+len(hunkfind)-1:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   352
          if line.rstrip("\r\n") == hunkfind[hunklineno]:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   353
            hunklineno+=1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   354
          else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   355
            debug("hunk no.%d doesn't match source file %s" % (hunkno+1, filename))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   356
            # file may be already patched, but we will check other hunks anyway
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   357
            hunkno += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   358
            if hunkno < len(self.hunks[fileno]):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   359
              hunk = self.hunks[fileno][hunkno]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   360
              continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   361
            else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   362
              break
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   363
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   364
        # check if processed line is the last line
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   365
        if lineno+1 == hunk.startsrc+len(hunkfind)-1:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   366
          debug("file %s hunk no.%d -- is ready to be patched" % (filename, hunkno+1))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   367
          hunkno+=1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   368
          validhunks+=1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   369
          if hunkno < len(self.hunks[fileno]):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   370
            hunk = self.hunks[fileno][hunkno]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   371
          else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   372
            if validhunks == len(self.hunks[fileno]):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   373
              # patch file
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   374
              canpatch = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   375
              break
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   376
      else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   377
        if hunkno < len(self.hunks[fileno]):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   378
          warning("premature end of source file %s at hunk %d" % (filename, hunkno+1))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   379
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   380
      f2fp.close()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   381
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   382
      if validhunks < len(self.hunks[fileno]):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   383
        if self._match_file_hunks(filename, self.hunks[fileno]):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   384
          warning("already patched  %s" % filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   385
        else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   386
          warning("source file is different - %s" % filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   387
      if canpatch:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   388
        backupname = filename+".orig"
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   389
        if exists(backupname):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   390
          warning("can't backup original file to %s - aborting" % backupname)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   391
        else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   392
          import shutil
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   393
          shutil.move(filename, backupname)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   394
          if self.write_hunks(backupname, filename, self.hunks[fileno]):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   395
            warning("successfully patched %s" % filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   396
            unlink(backupname)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   397
          else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   398
            warning("error patching file %s" % filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   399
            shutil.copy(filename, filename+".invalid")
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   400
            warning("invalid version is saved to %s" % filename+".invalid")
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   401
            # todo: proper rejects
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   402
            shutil.move(backupname, filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   403
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   404
    # todo: check for premature eof
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   405
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   406
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   407
  def can_patch(self, filename):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   408
    """ Check if specified filename can be patched. Returns None if file can
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   409
    not be found among source filenames. False if patch can not be applied
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   410
    clearly. True otherwise.
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   411
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   412
    :returns: True, False or None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   413
    """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   414
    idx = self._get_file_idx(filename, source=True)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   415
    if idx == None:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   416
      return None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   417
    return self._match_file_hunks(filename, self.hunks[idx])
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   418
    
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   419
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   420
  def _match_file_hunks(self, filepath, hunks):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   421
    matched = True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   422
    fp = open(abspath(filepath))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   423
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   424
    class NoMatch(Exception):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   425
      pass
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   426
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   427
    lineno = 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   428
    line = fp.readline()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   429
    hno = None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   430
    try:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   431
      for hno, h in enumerate(hunks):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   432
        # skip to first line of the hunk
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   433
        while lineno < h.starttgt:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   434
          if not len(line): # eof
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   435
            debug("check failed - premature eof before hunk: %d" % (hno+1))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   436
            raise NoMatch
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   437
          line = fp.readline()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   438
          lineno += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   439
        for hline in h.text:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   440
          if hline.startswith("-"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   441
            continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   442
          if not len(line):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   443
            debug("check failed - premature eof on hunk: %d" % (hno+1))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   444
            # todo: \ No newline at the end of file
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   445
            raise NoMatch
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   446
          if line.rstrip("\r\n") != hline[1:].rstrip("\r\n"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   447
            debug("file is not patched - failed hunk: %d" % (hno+1))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   448
            raise NoMatch
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   449
          line = fp.readline()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   450
          lineno += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   451
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   452
    except NoMatch:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   453
      matched = False
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   454
      # todo: display failed hunk, i.e. expected/found
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   455
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   456
    fp.close()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   457
    return matched
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   458
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   459
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   460
  def patch_stream(self, instream, hunks):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   461
    """ Generator that yields stream patched with hunks iterable
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   462
    
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   463
        Converts lineends in hunk lines to the best suitable format
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   464
        autodetected from input
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   465
    """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   466
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   467
    # todo: At the moment substituted lineends may not be the same
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   468
    #       at the start and at the end of patching. Also issue a
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   469
    #       warning/throw about mixed lineends (is it really needed?)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   470
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   471
    hunks = iter(hunks)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   472
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   473
    srclineno = 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   474
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   475
    lineends = {'\n':0, '\r\n':0, '\r':0}
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   476
    def get_line():
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   477
      """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   478
      local utility function - return line from source stream
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   479
      collecting line end statistics on the way
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   480
      """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   481
      line = instream.readline()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   482
        # 'U' mode works only with text files
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   483
      if line.endswith("\r\n"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   484
        lineends["\r\n"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   485
      elif line.endswith("\n"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   486
        lineends["\n"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   487
      elif line.endswith("\r"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   488
        lineends["\r"] += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   489
      return line
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   490
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   491
    for hno, h in enumerate(hunks):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   492
      debug("hunk %d" % (hno+1))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   493
      # skip to line just before hunk starts
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   494
      while srclineno < h.startsrc:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   495
        yield get_line()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   496
        srclineno += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   497
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   498
      for hline in h.text:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   499
        # todo: check \ No newline at the end of file
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   500
        if hline.startswith("-") or hline.startswith("\\"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   501
          get_line()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   502
          srclineno += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   503
          continue
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   504
        else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   505
          if not hline.startswith("+"):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   506
            get_line()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   507
            srclineno += 1
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   508
          line2write = hline[1:]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   509
          # detect if line ends are consistent in source file
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   510
          if sum([bool(lineends[x]) for x in lineends]) == 1:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   511
            newline = [x for x in lineends if lineends[x] != 0][0]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   512
            yield line2write.rstrip("\r\n")+newline
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   513
          else: # newlines are mixed
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   514
            yield line2write
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   515
     
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   516
    for line in instream:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   517
      yield line
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   518
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   519
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   520
  def write_hunks(self, srcname, tgtname, hunks):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   521
    src = open(srcname, "rb")
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   522
    tgt = open(tgtname, "wb")
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   523
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   524
    debug("processing target file %s" % tgtname)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   525
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   526
    tgt.writelines(self.patch_stream(src, hunks))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   527
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   528
    tgt.close()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   529
    src.close()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   530
    return True
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   531
  
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   532
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   533
  def _get_file_idx(self, filename, source=None):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   534
    """ Detect index of given filename within patch.
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   535
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   536
        :param filename:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   537
        :param source: search filename among sources (True),
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   538
                       targets (False), or both (None)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   539
        :returns: int or None
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   540
    """
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   541
    filename = abspath(filename)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   542
    if source == True or source == None:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   543
      for i,fnm in enumerate(self.source):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   544
        if filename == abspath(fnm):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   545
          return i  
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   546
    if source == False or source == None:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   547
      for i,fnm in enumerate(self.target):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   548
        if filename == abspath(fnm):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   549
          return i  
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   550
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   551
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   552
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   553
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   554
from optparse import OptionParser
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   555
from os.path import exists
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   556
import sys
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   557
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   558
if __name__ == "__main__":
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   559
  opt = OptionParser(usage="%prog [options] unipatch-file", version="python-patch %s" % __version__)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   560
  opt.add_option("--debug", action="store_true", dest="debugmode", help="debug mode")
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   561
  (options, args) = opt.parse_args()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   562
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   563
  if not args:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   564
    opt.print_version()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   565
    opt.print_help()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   566
    sys.exit()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   567
  debugmode = options.debugmode
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   568
  patchfile = args[0]
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   569
  if not exists(patchfile) or not isfile(patchfile):
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   570
    sys.exit("patch file does not exist - %s" % patchfile)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   571
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   572
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   573
  if debugmode:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   574
    loglevel = logging.DEBUG
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   575
    logformat = "%(levelname)8s %(message)s"
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   576
  else:
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   577
    loglevel = logging.INFO
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   578
    logformat = "%(message)s"
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   579
  logger.setLevel(loglevel)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   580
  loghandler.setFormatter(logging.Formatter(logformat))
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   581
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   582
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   583
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   584
  patch = fromfile(patchfile)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   585
  #pprint(patch)
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   586
  patch.apply()
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   587
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   588
  # todo: document and test line ends handling logic - patch.py detects proper line-endings
87104b7cb3d6 first version of file organization
ymh <ymh.work@gmail.com>
parents:
diff changeset
   589
  #       for inserted hunks and issues a warning if patched file has incosistent line ends