mercurial/diffhelpers.c
branchstable
changeset 33572 857876ebaed4
parent 33202 c1994c986d77
parent 33571 9a944e908ecf
child 33573 9e0fea06ae2c
equal deleted inserted replaced
33202:c1994c986d77 33572:857876ebaed4
     1 /*
       
     2  * diffhelpers.c - helper routines for mpatch
       
     3  *
       
     4  * Copyright 2007 Chris Mason <chris.mason@oracle.com>
       
     5  *
       
     6  * This software may be used and distributed according to the terms
       
     7  * of the GNU General Public License v2, incorporated herein by reference.
       
     8  */
       
     9 
       
    10 #include <Python.h>
       
    11 #include <stdlib.h>
       
    12 #include <string.h>
       
    13 
       
    14 #include "util.h"
       
    15 
       
    16 static char diffhelpers_doc[] = "Efficient diff parsing";
       
    17 static PyObject *diffhelpers_Error;
       
    18 
       
    19 
       
    20 /* fixup the last lines of a and b when the patch has no newline at eof */
       
    21 static void _fix_newline(PyObject *hunk, PyObject *a, PyObject *b)
       
    22 {
       
    23 	Py_ssize_t hunksz = PyList_Size(hunk);
       
    24 	PyObject *s = PyList_GET_ITEM(hunk, hunksz-1);
       
    25 	char *l = PyBytes_AsString(s);
       
    26 	Py_ssize_t alen = PyList_Size(a);
       
    27 	Py_ssize_t blen = PyList_Size(b);
       
    28 	char c = l[0];
       
    29 	PyObject *hline;
       
    30 	Py_ssize_t sz = PyBytes_GET_SIZE(s);
       
    31 
       
    32 	if (sz > 1 && l[sz-2] == '\r')
       
    33 		/* tolerate CRLF in last line */
       
    34 		sz -= 1;
       
    35 
       
    36 	hline = PyBytes_FromStringAndSize(l, sz-1);
       
    37 	if (!hline) {
       
    38 		return;
       
    39 	}
       
    40 
       
    41 	if (c == ' ' || c == '+') {
       
    42 		PyObject *rline = PyBytes_FromStringAndSize(l + 1, sz - 2);
       
    43 		PyList_SetItem(b, blen-1, rline);
       
    44 	}
       
    45 	if (c == ' ' || c == '-') {
       
    46 		Py_INCREF(hline);
       
    47 		PyList_SetItem(a, alen-1, hline);
       
    48 	}
       
    49 	PyList_SetItem(hunk, hunksz-1, hline);
       
    50 }
       
    51 
       
    52 /* python callable form of _fix_newline */
       
    53 static PyObject *
       
    54 fix_newline(PyObject *self, PyObject *args)
       
    55 {
       
    56 	PyObject *hunk, *a, *b;
       
    57 	if (!PyArg_ParseTuple(args, "OOO", &hunk, &a, &b))
       
    58 		return NULL;
       
    59 	_fix_newline(hunk, a, b);
       
    60 	return Py_BuildValue("l", 0);
       
    61 }
       
    62 
       
    63 #if (PY_VERSION_HEX < 0x02050000)
       
    64 static const char *addlines_format = "OOiiOO";
       
    65 #else
       
    66 static const char *addlines_format = "OOnnOO";
       
    67 #endif
       
    68 
       
    69 /*
       
    70  * read lines from fp into the hunk.  The hunk is parsed into two arrays
       
    71  * a and b.  a gets the old state of the text, b gets the new state
       
    72  * The control char from the hunk is saved when inserting into a, but not b
       
    73  * (for performance while deleting files)
       
    74  */
       
    75 static PyObject *
       
    76 addlines(PyObject *self, PyObject *args)
       
    77 {
       
    78 
       
    79 	PyObject *fp, *hunk, *a, *b, *x;
       
    80 	Py_ssize_t i;
       
    81 	Py_ssize_t lena, lenb;
       
    82 	Py_ssize_t num;
       
    83 	Py_ssize_t todoa, todob;
       
    84 	char *s, c;
       
    85 	PyObject *l;
       
    86 	if (!PyArg_ParseTuple(args, addlines_format,
       
    87 			      &fp, &hunk, &lena, &lenb, &a, &b))
       
    88 		return NULL;
       
    89 
       
    90 	while (1) {
       
    91 		todoa = lena - PyList_Size(a);
       
    92 		todob = lenb - PyList_Size(b);
       
    93 		num = todoa > todob ? todoa : todob;
       
    94 		if (num == 0)
       
    95 		    break;
       
    96 		for (i = 0; i < num; i++) {
       
    97 			x = PyFile_GetLine(fp, 0);
       
    98 			s = PyBytes_AsString(x);
       
    99 			c = *s;
       
   100 			if (strcmp(s, "\\ No newline at end of file\n") == 0) {
       
   101 				_fix_newline(hunk, a, b);
       
   102 				continue;
       
   103 			}
       
   104 			if (c == '\n') {
       
   105 				/* Some patches may be missing the control char
       
   106 				 * on empty lines. Supply a leading space. */
       
   107 				Py_DECREF(x);
       
   108 				x = PyBytes_FromString(" \n");
       
   109 			}
       
   110 			PyList_Append(hunk, x);
       
   111 			if (c == '+') {
       
   112 				l = PyBytes_FromString(s + 1);
       
   113 				PyList_Append(b, l);
       
   114 				Py_DECREF(l);
       
   115 			} else if (c == '-') {
       
   116 				PyList_Append(a, x);
       
   117 			} else {
       
   118 				l = PyBytes_FromString(s + 1);
       
   119 				PyList_Append(b, l);
       
   120 				Py_DECREF(l);
       
   121 				PyList_Append(a, x);
       
   122 			}
       
   123 			Py_DECREF(x);
       
   124 		}
       
   125 	}
       
   126 	return Py_BuildValue("l", 0);
       
   127 }
       
   128 
       
   129 /*
       
   130  * compare the lines in a with the lines in b.  a is assumed to have
       
   131  * a control char at the start of each line, this char is ignored in the
       
   132  * compare
       
   133  */
       
   134 static PyObject *
       
   135 testhunk(PyObject *self, PyObject *args)
       
   136 {
       
   137 
       
   138 	PyObject *a, *b;
       
   139 	long bstart;
       
   140 	Py_ssize_t alen, blen;
       
   141 	Py_ssize_t i;
       
   142 	char *sa, *sb;
       
   143 
       
   144 	if (!PyArg_ParseTuple(args, "OOl", &a, &b, &bstart))
       
   145 		return NULL;
       
   146 	alen = PyList_Size(a);
       
   147 	blen = PyList_Size(b);
       
   148 	if (alen > blen - bstart || bstart < 0) {
       
   149 		return Py_BuildValue("l", -1);
       
   150 	}
       
   151 	for (i = 0; i < alen; i++) {
       
   152 		sa = PyBytes_AsString(PyList_GET_ITEM(a, i));
       
   153 		sb = PyBytes_AsString(PyList_GET_ITEM(b, i + bstart));
       
   154 		if (strcmp(sa + 1, sb) != 0)
       
   155 			return Py_BuildValue("l", -1);
       
   156 	}
       
   157 	return Py_BuildValue("l", 0);
       
   158 }
       
   159 
       
   160 static PyMethodDef methods[] = {
       
   161 	{"addlines", addlines, METH_VARARGS, "add lines to a hunk\n"},
       
   162 	{"fix_newline", fix_newline, METH_VARARGS, "fixup newline counters\n"},
       
   163 	{"testhunk", testhunk, METH_VARARGS, "test lines in a hunk\n"},
       
   164 	{NULL, NULL}
       
   165 };
       
   166 
       
   167 #ifdef IS_PY3K
       
   168 static struct PyModuleDef diffhelpers_module = {
       
   169 	PyModuleDef_HEAD_INIT,
       
   170 	"diffhelpers",
       
   171 	diffhelpers_doc,
       
   172 	-1,
       
   173 	methods
       
   174 };
       
   175 
       
   176 PyMODINIT_FUNC PyInit_diffhelpers(void)
       
   177 {
       
   178 	PyObject *m;
       
   179 
       
   180 	m = PyModule_Create(&diffhelpers_module);
       
   181 	if (m == NULL)
       
   182 		return NULL;
       
   183 
       
   184 	diffhelpers_Error = PyErr_NewException("diffhelpers.diffhelpersError",
       
   185 											NULL, NULL);
       
   186 	Py_INCREF(diffhelpers_Error);
       
   187 	PyModule_AddObject(m, "diffhelpersError", diffhelpers_Error);
       
   188 
       
   189 	return m;
       
   190 }
       
   191 #else
       
   192 PyMODINIT_FUNC
       
   193 initdiffhelpers(void)
       
   194 {
       
   195 	Py_InitModule3("diffhelpers", methods, diffhelpers_doc);
       
   196 	diffhelpers_Error = PyErr_NewException("diffhelpers.diffhelpersError",
       
   197 	                                        NULL, NULL);
       
   198 }
       
   199 #endif