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