1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2016 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.1 of the PHP license,       |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available at through the world-wide-web at                           |
10   | http://www.php.net/license/3_1.txt.                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Marcin Gibula <mg@iceni.pl>                                  |
16   +----------------------------------------------------------------------+
17 
18   $Id$
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "php_xdiff.h"
29 
30 #ifndef ZEND_ARG_INFO_WITH_DEFAULT_VALUE
31 #define ZEND_ARG_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, default_value) \
32 	ZEND_ARG_INFO(pass_by_ref, name)
33 #endif
34 #if PHP_VERSION_ID < 70200
35 #undef ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX
36 #define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
37 	static const zend_internal_arg_info name[] = { \
38 		{ (const char*)(zend_uintptr_t)(required_num_args), ( #class_name ), 0, return_reference, allow_null, 0 },
39 #endif
40 
41 #ifndef ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX
42 #define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
43 	ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, class_name, allow_null)
44 #endif
45 
46 #ifndef ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE
47 #define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) \
48 	ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)
49 #endif
50 
51 #include "xdiff_arginfo.h"
52 
53 #ifdef PHP_WIN32
54 /* libxdiff is compiled with /Zp1 */
55 # pragma pack(push, 1)
56 #endif
57 #include <xdiff.h>
58 #ifdef PHP_WIN32
59 # pragma pack(pop)
60 #endif
61 
62 /* Not exported by header file */
63 extern char libxdiff_version[];
64 
65 struct string_buffer {
66 	char *ptr;
67 	unsigned long size;
68 };
69 
70 static int load_mm_file(const char *filepath, mmfile_t *dest);
71 static int load_into_mm_file(const char *buffer, unsigned long size, mmfile_t *dest);
72 static int append_string(void *ptr, mmbuffer_t *buffer, int array_size);
73 static int append_stream(void *ptr, mmbuffer_t *buffer, int array_size);
74 static int init_string(struct string_buffer *string);
75 static void free_string(struct string_buffer *string);
76 static void invalidate_string(struct string_buffer *string);
77 
78 static int make_diff(char *filepath1, char *filepath2, xdemitcb_t *output, int context, int minimal);
79 static int make_diff_str(char *str1, int size1, char *str2, int size2,  xdemitcb_t *output, int context, int minimal);
80 static int make_bdiff(char *filepath1, char *filepath2, xdemitcb_t *output);
81 static int make_bdiff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output);
82 static int make_patch(char *file_path, char *patch_path, xdemitcb_t *output, xdemitcb_t *error, int flags);
83 static int make_patch_str(char *file, int size1, char *patch, int size2, xdemitcb_t *output, xdemitcb_t *error, int flags);
84 static int make_bpatch(char *file_path, char *patch_path, xdemitcb_t *output);
85 static int make_bpatch_str(char *file, int size1, char *patch, int size2, xdemitcb_t *output);
86 static int make_merge3(char *filepath1, char *filepath2, char *filepath3, xdemitcb_t *output, xdemitcb_t *error);
87 static int make_merge3_str(char *content1, int size1, char *content2, int size2, char *content3, int size3, xdemitcb_t *output, xdemitcb_t *error);
88 static int make_rabdiff(char *filepath1, char *filepath2, xdemitcb_t *output);
89 static int make_rabdiff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output);
90 
xdiff_malloc(void * foo,unsigned int size)91 static void *xdiff_malloc(void *foo, unsigned int size)
92 {
93 	return emalloc(size);
94 }
95 
xdiff_free(void * foo,void * ptr)96 static void xdiff_free(void *foo, void *ptr)
97 {
98 	if (ptr) {
99 		efree(ptr);
100 	}
101 }
102 
xdiff_realloc(void * foo,void * ptr,unsigned int nsize)103 static void *xdiff_realloc(void *foo, void *ptr, unsigned int nsize)
104 {
105 	return erealloc(ptr, nsize);
106 }
107 
108 static memallocator_t allocator = { NULL, xdiff_malloc, xdiff_free, xdiff_realloc };
109 
110 /* {{{ xdiff_module_entry
111  */
112 zend_module_entry xdiff_module_entry = {
113 #if ZEND_MODULE_API_NO >= 20010901
114 	STANDARD_MODULE_HEADER,
115 #endif
116 	"xdiff",
117 	ext_functions,
118 	PHP_MINIT(xdiff),
119 	NULL,
120 	NULL,
121 	NULL,
122 	PHP_MINFO(xdiff),
123 #if ZEND_MODULE_API_NO >= 20010901
124 	PHP_XDIFF_VERSION,
125 #endif
126 	STANDARD_MODULE_PROPERTIES
127 };
128 /* }}} */
129 
130 #ifdef COMPILE_DL_XDIFF
131 ZEND_GET_MODULE(xdiff)
132 #endif
133 
134 /* {{{ PHP_MINIT_FUNCTION
135  */
PHP_MINIT_FUNCTION(xdiff)136 PHP_MINIT_FUNCTION(xdiff)
137 {
138 	xdl_set_allocator(&allocator);
139 
140 	REGISTER_LONG_CONSTANT("XDIFF_PATCH_NORMAL", XDL_PATCH_NORMAL, CONST_CS | CONST_PERSISTENT);
141 	REGISTER_LONG_CONSTANT("XDIFF_PATCH_REVERSE", XDL_PATCH_REVERSE, CONST_CS | CONST_PERSISTENT);
142 	REGISTER_LONG_CONSTANT("XDIFF_PATCH_IGNORESPACE", XDL_PATCH_IGNOREBSPACE, CONST_CS | CONST_PERSISTENT);
143 
144 	return SUCCESS;
145 }
146 /* }}} */
147 
148 /* {{{ PHP_MINFO_FUNCTION
149  */
PHP_MINFO_FUNCTION(xdiff)150 PHP_MINFO_FUNCTION(xdiff)
151 {
152 	php_info_print_table_start();
153 	php_info_print_table_header(2, "xdiff support", "enabled");
154 	php_info_print_table_row(2, "extension version", PHP_XDIFF_VERSION);
155 	php_info_print_table_row(2, "libxdiff version", libxdiff_version);
156 	php_info_print_table_end();
157 }
158 /* }}} */
159 
160 /* {{{ proto mixed xdiff_string_diff(string str1, string str2, [int context, [bool minimal]])
161  */
PHP_FUNCTION(xdiff_string_diff)162 PHP_FUNCTION(xdiff_string_diff)
163 {
164 	zend_string *str1, *str2;
165 	int retval;
166 	zend_bool minimal = 0;
167 	zend_long context = 3;
168 	xdemitcb_t output;
169 	struct string_buffer string;
170 
171 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|lb", &str1, &str2, &context, &minimal) == FAILURE) {
172 		RETURN_FALSE;
173 	}
174 
175 	RETVAL_FALSE;
176 
177 	retval = init_string(&string);
178 	if (!retval)
179 		goto out;
180 
181 	output.priv= &string;
182 	output.outf = append_string;
183 
184 	make_diff_str(str1->val, str1->len, str2->val, str2->len, &output, context, minimal);
185 	RETVAL_STRINGL(string.ptr, string.size);
186 	free_string(&string);
187 out:
188 	return;
189 }
190 /* }}} */
191 
192 /* {{{ proto bool xdiff_file_diff(string file1, string file2, string dest, [int context, [bool minimal]])
193  */
PHP_FUNCTION(xdiff_file_diff)194 PHP_FUNCTION(xdiff_file_diff)
195 {
196 	zend_string *filepath1, *filepath2, *dest;
197 	int retval;
198 	zend_bool minimal = 0;
199 	zend_long context = 3;
200 	xdemitcb_t output;
201 	php_stream *output_stream;
202 
203 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS|lb", &filepath1, &filepath2, &dest, &context, &minimal) == FAILURE) {
204 		RETURN_FALSE;
205 	}
206 
207 	RETVAL_FALSE;
208 
209 	output_stream = php_stream_open_wrapper(dest->val, "wb", REPORT_ERRORS, NULL);
210 	if (!output_stream)
211 		goto out;
212 
213 	output.priv = output_stream;
214 	output.outf = append_stream;
215 
216 	retval = make_diff(filepath1->val, filepath2->val, &output, context, minimal);
217 	if (!retval)
218 		goto out_stream_close;
219 
220 	RETVAL_TRUE;
221 
222 out_stream_close:
223 	php_stream_close(output_stream);
224 out:
225 	return;
226 }
227 /* }}} */
228 
229 /* {{{ proto mixed xdiff_string_diff_binary(string str1, string str2)
230  */
PHP_FUNCTION(xdiff_string_bdiff)231 PHP_FUNCTION(xdiff_string_bdiff)
232 {
233 	zend_string *str1, *str2;
234 	int retval;
235 	xdemitcb_t output;
236 	struct string_buffer string;
237 
238 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &str1, &str2) == FAILURE) {
239 		RETURN_FALSE;
240 	}
241 
242 	RETVAL_FALSE;
243 
244 	retval = init_string(&string);
245 	if (!retval)
246 		goto out;
247 
248 	output.priv= &string;
249 	output.outf = append_string;
250 
251 	make_bdiff_str(str1->val, str1->len, str2->val, str2->len, &output);
252 	RETVAL_STRINGL(string.ptr, string.size);
253 	free_string(&string);
254 
255 out:
256 	return;
257 }
258 /* }}} */
259 
260 /* {{{ proto bool xdiff_file_diff_binary(string file1, string file2, string dest)
261  */
PHP_FUNCTION(xdiff_file_bdiff)262 PHP_FUNCTION(xdiff_file_bdiff)
263 {
264 	zend_string *filepath1, *filepath2, *result;
265 	int retval;
266 	xdemitcb_t output;
267 	php_stream *output_stream;
268 
269 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS", &filepath1, &filepath2, &result) == FAILURE) {
270 		RETURN_FALSE;
271 	}
272 
273 	RETVAL_FALSE;
274 
275 	output_stream = php_stream_open_wrapper(result->val, "wb", REPORT_ERRORS, NULL);
276 	if (!output_stream)
277 		goto out;
278 
279 	output.priv = output_stream;
280 	output.outf = append_stream;
281 
282 	retval = make_bdiff(filepath1->val, filepath2->val, &output);
283 	if (!retval)
284 		goto out_stream_close;
285 
286 	RETVAL_TRUE;
287 
288 out_stream_close:
289 	php_stream_close(output_stream);
290 out:
291 	return;
292 }
293 /* }}} */
294 
295 /* {{{ proto mixed xdiff_string_rabdiff(string str1, string str2)
296  */
PHP_FUNCTION(xdiff_string_rabdiff)297 PHP_FUNCTION(xdiff_string_rabdiff)
298 {
299 	zend_string *str1, *str2;
300 	int retval;
301 	xdemitcb_t output;
302 	struct string_buffer string;
303 
304 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &str1, &str2) == FAILURE) {
305 		RETURN_FALSE;
306 	}
307 
308 	RETVAL_FALSE;
309 
310 	retval = init_string(&string);
311 	if (!retval)
312 		goto out;
313 
314 	output.priv= &string;
315 	output.outf = append_string;
316 
317 	make_rabdiff_str(str1->val, str1->len, str2->val, str2->len, &output);
318 	RETVAL_STRINGL(string.ptr, string.size);
319 	free_string(&string);
320 
321 out:
322 	return;
323 }
324 /* }}} */
325 
326 /* {{{ proto bool xdiff_file_rabdiff(string file1, string file2, string dest)
327  */
PHP_FUNCTION(xdiff_file_rabdiff)328 PHP_FUNCTION(xdiff_file_rabdiff)
329 {
330 	zend_string *filepath1, *filepath2, *result;
331 	int retval;
332 	xdemitcb_t output;
333 	php_stream *output_stream;
334 
335 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS", &filepath1, &filepath2, &result) == FAILURE) {
336 		RETURN_FALSE;
337 	}
338 
339 	RETVAL_FALSE;
340 
341 	output_stream = php_stream_open_wrapper(result->val, "wb", REPORT_ERRORS, NULL);
342 	if (!output_stream)
343 		goto out;
344 
345 	output.priv = output_stream;
346 	output.outf = append_stream;
347 
348 	retval = make_rabdiff(filepath1->val, filepath2->val, &output);
349 	if (!retval)
350 		goto out_stream_close;
351 
352 	RETVAL_TRUE;
353 
354 out_stream_close:
355 	php_stream_close(output_stream);
356 out:
357 	return;
358 }
359 /* }}} */
360 
361 /* {{{ proto bool xdiff_file_bdiff_size(string file1, string file2, string dest)
362  */
PHP_FUNCTION(xdiff_file_bdiff_size)363 PHP_FUNCTION(xdiff_file_bdiff_size)
364 {
365 	zend_string *filepath;
366 	int retval;
367 	long result;
368 	mmfile_t file;
369 
370 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &filepath) == FAILURE) {
371 		RETURN_FALSE;
372 	}
373 
374 	RETVAL_FALSE;
375 
376 	retval = load_mm_file(filepath->val, &file);
377 	if (!retval)
378 		goto out;
379 
380 	result = xdl_bdiff_tgsize(&file);
381 	if (result < 0)
382 		goto out_free_mmfile;
383 
384 	RETVAL_LONG(result);
385 
386 out_free_mmfile:
387 	xdl_free_mmfile(&file);
388 out:
389 	return;
390 }
391 /* }}} */
392 
393 /* {{{ proto bool xdiff_string_bdiff_size(string file1, string file2, string dest)
394  */
PHP_FUNCTION(xdiff_string_bdiff_size)395 PHP_FUNCTION(xdiff_string_bdiff_size)
396 {
397 	zend_string *patch;
398 	int retval;
399 	long result;
400 	mmfile_t file;
401 
402 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &patch) == FAILURE) {
403 		RETURN_FALSE;
404 	}
405 
406 	RETVAL_FALSE;
407 
408 	retval = load_into_mm_file(patch->val, patch->len, &file);
409 	if (!retval)
410 		goto out;
411 
412 	result = xdl_bdiff_tgsize(&file);
413 	if (result < 0)
414 		goto out_free_mmfile;
415 
416 	RETVAL_LONG(result);
417 
418 out_free_mmfile:
419 	xdl_free_mmfile(&file);
420 out:
421 	return;
422 }
423 /* }}} */
424 
425 /* {{{ proto mixed xdiff_file_patch(string file, string patch, string dest [, int flags])
426  */
PHP_FUNCTION(xdiff_file_patch)427 PHP_FUNCTION(xdiff_file_patch)
428 {
429 	php_stream *output_stream;
430 	zend_string *src_path, *patch_path, *dest_path;
431 	int retval;
432 	zend_long flags = XDL_PATCH_NORMAL;	/* DIFF_PATCH_NORMAL */
433 	xdemitcb_t output, error_output;
434 	struct string_buffer error_string;
435 
436 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS|l", &src_path, &patch_path, &dest_path, &flags) == FAILURE) {
437 		RETURN_FALSE;
438 	}
439 
440 	RETVAL_FALSE;
441 
442 	output_stream = php_stream_open_wrapper(dest_path->val, "wb", REPORT_ERRORS, NULL);
443 	if (!output_stream)
444 		goto out;
445 
446 	output.outf = append_stream;
447 	output.priv = output_stream;
448 
449 	retval = init_string(&error_string);
450 	if (!retval)
451 		goto out_stream_close;
452 
453 	error_output.priv= &error_string;
454 	error_output.outf = append_string;
455 
456 	retval = make_patch(src_path->val, patch_path->val, &output, &error_output, flags);
457 	if (retval < 0)
458 		goto out_free_string;
459 
460 	if (error_string.size > 0) {
461 		RETVAL_STRINGL(error_string.ptr, error_string.size);
462 	} else {
463 		RETVAL_TRUE;
464 	}
465 
466 out_free_string:
467 	free_string(&error_string);
468 out_stream_close:
469 	php_stream_close(output_stream);
470 out:
471 	return;
472 }
473 /* }}} */
474 
475 /* {{{ proto string xdiff_string_patch(string file, string patch [, int flags, [string error]])
476  */
PHP_FUNCTION(xdiff_string_patch)477 PHP_FUNCTION(xdiff_string_patch)
478 {
479 	zval *error_ref = NULL;
480 	zend_string *src, *patch;
481 	int retval;
482 	zend_long flags = XDL_PATCH_NORMAL;	/* DIFF_PATCH_NORMAL */
483 	xdemitcb_t output, error_output;
484 	struct string_buffer output_string, error_string;
485 
486 	if (zend_parse_parameters_ex(0, ZEND_NUM_ARGS(), "SS|lz", &src, &patch, &flags, &error_ref) == FAILURE) {
487 		RETURN_FALSE;
488 	}
489 
490 	RETVAL_FALSE;
491 
492 	retval = init_string(&output_string);
493 	if (!retval)
494 		goto out;
495 
496 	output.priv = &output_string;
497 	output.outf = append_string;
498 
499 	retval = init_string(&error_string);
500 	if (!retval)
501 		goto out_free_output_string;
502 
503 	error_output.priv= &error_string;
504 	error_output.outf = append_string;
505 
506 	retval = make_patch_str(src->val, src->len, patch->val, patch->len, &output, &error_output, flags);
507 	if (retval < 0)
508 		goto out_free_error_string;
509 
510 	if (error_string.size > 0 && error_ref) {
511 		ZVAL_STRINGL(error_ref, error_string.ptr, error_string.size);
512 	}
513 
514 	if (output_string.size > 0) {
515 		RETVAL_STRINGL(output_string.ptr, output_string.size);
516 	} else {
517 		RETVAL_EMPTY_STRING();
518 	}
519 
520 out_free_error_string:
521 	free_string(&error_string);
522 out_free_output_string:
523 	free_string(&output_string);
524 out:
525 	return;
526 }
527 /* }}} */
528 
529 /* {{{ proto bool xdiff_file_patch_binary(string file, string patch, string dest)
530  */
PHP_FUNCTION(xdiff_file_bpatch)531 PHP_FUNCTION(xdiff_file_bpatch)
532 {
533 	php_stream *output_stream;
534 	zend_string *src_path, *patch_path, *dest_path;
535 	int retval;
536 	xdemitcb_t output;
537 
538 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS", &src_path, &patch_path, &dest_path) == FAILURE) {
539 		RETURN_FALSE;
540 	}
541 
542 	RETVAL_FALSE;
543 
544 	output_stream = php_stream_open_wrapper(dest_path->val, "wb", REPORT_ERRORS, NULL);
545 	if (!output_stream)
546 		goto out;
547 
548 	output.outf = append_stream;
549 	output.priv = output_stream;
550 
551 	retval = make_bpatch(src_path->val, patch_path->val, &output);
552 	php_stream_close(output_stream);
553 
554 	if (retval == 0)
555 		RETVAL_TRUE;
556 
557 out:
558 	return;
559 }
560 /* }}} */
561 
562 /* {{{ proto string xdiff_string_patch_binary(string str, string patch)
563  */
PHP_FUNCTION(xdiff_string_bpatch)564 PHP_FUNCTION(xdiff_string_bpatch)
565 {
566 	zend_string *src, *patch;
567 	int retval;
568 	xdemitcb_t output;
569 	struct string_buffer output_string;
570 
571 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &src, &patch) == FAILURE) {
572 		RETURN_FALSE;
573 	}
574 
575 	RETVAL_FALSE;
576 
577 	retval = init_string(&output_string);
578 	if (!retval)
579 		goto out;
580 
581 	output.priv = &output_string;
582 	output.outf = append_string;
583 
584 	retval = make_bpatch_str(src->val, src->len, patch->val, patch->len, &output);
585 	if (retval < 0)
586 		goto out_free_string;
587 
588 	RETVAL_STRINGL(output_string.ptr, output_string.size);
589 
590 out_free_string:
591 	free_string(&output_string);
592 out:
593 	return;
594 }
595 /* }}} */
596 
597 /* {{{ proto mixed xdiff_file_merge3(string file1, string file2, string file3, string dest)
598  */
PHP_FUNCTION(xdiff_file_merge3)599 PHP_FUNCTION(xdiff_file_merge3)
600 {
601 	zend_string *file1, *file2, *file3, *dest;
602 	php_stream *output_stream;
603 	struct string_buffer string;
604 	xdemitcb_t output, error_output;
605 	int retval;
606 
607 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSSS", &file1, &file2, &file3, &dest) == FAILURE) {
608 		RETURN_FALSE;
609 	}
610 
611 	RETVAL_FALSE;
612 
613 	output_stream = php_stream_open_wrapper(dest->val, "wb", REPORT_ERRORS, NULL);
614 	if (!output_stream)
615 		goto out;
616 
617 	output.priv = output_stream;
618 	output.outf = append_stream;
619 
620 	retval = init_string(&string);
621 	if (!retval)
622 		goto out_stream_close;
623 
624 	error_output.priv = &string;
625 	error_output.outf = append_string;
626 
627 	retval = make_merge3(file1->val, file2->val, file3->val, &output, &error_output);
628 	if (!retval)
629 		goto out_free_string;
630 
631 	if (string.size > 0) {
632 		RETVAL_STRINGL(string.ptr, string.size);
633 	} else {
634 		RETVAL_TRUE;
635 	}
636 
637 out_free_string:
638 	free_string(&string);
639 out_stream_close:
640 	php_stream_close(output_stream);
641 out:
642 	return;
643 }
644 /* }}} */
645 
646 /* {{{ proto string xdiff_string_merge3(string str1, string str2, string str3 [, string error])
647  */
PHP_FUNCTION(xdiff_string_merge3)648 PHP_FUNCTION(xdiff_string_merge3)
649 {
650 	zval *error_ref = NULL;
651 	zend_string *file1, *file2, *file3;
652 	struct string_buffer output_string, error_string;
653 	xdemitcb_t output, error_output;
654 	int retval;
655 
656 	if (zend_parse_parameters_ex(0, ZEND_NUM_ARGS(), "SSS|z", &file1,  &file2, &file3, &error_ref) == FAILURE) {
657 		RETURN_FALSE;
658 	}
659 
660 	RETVAL_FALSE;
661 
662 	retval = init_string(&output_string);
663 	if (!retval)
664 		goto out;
665 
666 	output.priv = &output_string;
667 	output.outf = append_string;
668 
669 	retval = init_string(&error_string);
670 	if (!retval)
671 		goto out_free_output_string;
672 
673 	error_output.priv = &error_string;
674 	error_output.outf = append_string;
675 
676 	retval = make_merge3_str(file1->val, file1->len, file2->val, file2->len, file3->val, file3->len, &output, &error_output);
677 	if (!retval)
678 		goto out_free_error_string;
679 
680 	if (error_string.size > 0 && error_ref) {
681 		ZVAL_STRINGL(error_ref, error_string.ptr, error_string.size);
682 	}
683 
684 	if (output_string.size > 0) {
685 		RETVAL_STRINGL(output_string.ptr, output_string.size);
686 	} else {
687 		RETVAL_TRUE;
688 	}
689 
690 out_free_error_string:
691 	free_string(&error_string);
692 out_free_output_string:
693 	free_string(&output_string);
694 out:
695 	return;
696 }
697 /* }}} */
698 
load_mm_file(const char * filepath,mmfile_t * dest)699 static int load_mm_file(const char *filepath, mmfile_t *dest)
700 {
701 	int retval;
702 	off_t filesize;
703 	void *ptr;
704 	php_stream *src;
705 	php_stream_statbuf stat;
706 
707 	src = php_stream_open_wrapper((char *) filepath, "rb", REPORT_ERRORS, NULL);
708 	if (!src)
709 		goto out;
710 
711 	retval = php_stream_stat(src, &stat);
712 	if (retval < 0)
713 		goto out_stream_close;
714 
715 	filesize = stat.sb.st_size;
716 
717 	retval = xdl_init_mmfile(dest, filesize, XDL_MMF_ATOMIC);
718 	if (retval < 0)
719 		goto out_stream_close;
720 
721 	ptr = xdl_mmfile_writeallocate(dest, (long) filesize);
722 	if (!ptr)
723 		goto out_free_mmfile;
724 
725 	php_stream_read(src, ptr, filesize);
726 	php_stream_close(src);
727 
728 	return 1;
729 
730 out_free_mmfile:
731 	xdl_free_mmfile(dest);
732 out_stream_close:
733 	php_stream_close(src);
734 out:
735 	return 0;
736 }
737 
load_into_mm_file(const char * buffer,unsigned long size,mmfile_t * dest)738 static int load_into_mm_file(const char *buffer, unsigned long size, mmfile_t *dest)
739 {
740 	int retval;
741 	void *ptr;
742 
743 	retval = xdl_init_mmfile(dest, size, XDL_MMF_ATOMIC);
744 	if (retval < 0)
745 		goto out;
746 
747 	ptr = xdl_mmfile_writeallocate(dest, (long) size);
748 	if (!ptr)
749 		goto out_free_mmfile;
750 
751 	memcpy(ptr, buffer, size);
752 	return 1;
753 
754 out_free_mmfile:
755 	xdl_free_mmfile(dest);
756 out:
757 	return 0;
758 }
759 
append_string(void * ptr,mmbuffer_t * buffer,int array_size)760 static int append_string(void *ptr, mmbuffer_t *buffer, int array_size)
761 {
762 	struct string_buffer *string = ptr;
763 	void *new_ptr;
764 	unsigned int i;
765 
766 	for (i = 0; i < array_size; i++) {
767 		new_ptr = erealloc(string->ptr, string->size + buffer[i].size + 1);
768 		if (!new_ptr) {
769 			efree(string->ptr);
770 			return -1;
771 		}
772 
773 		string->ptr = new_ptr;
774 		memcpy(string->ptr + string->size, buffer[i].ptr, buffer[i].size);
775 		string->size += buffer[i].size;
776 	}
777 	if (array_size) {
778 		string->ptr[string->size] = '\0';
779 	}
780 
781 	return 0;
782 }
783 
append_stream(void * ptr,mmbuffer_t * buffer,int array_size)784 static int append_stream(void *ptr, mmbuffer_t *buffer, int array_size)
785 {
786 	php_stream *stream = ptr;
787 	unsigned int i;
788 
789 	for (i = 0; i < array_size; i++) {
790 		php_stream_write(stream, buffer[i].ptr, buffer[i].size);
791 	}
792 
793 	return 1;
794 }
795 
init_string(struct string_buffer * string)796 static int init_string(struct string_buffer *string)
797 {
798 	string->ptr = emalloc(1);
799 	if (!string->ptr)
800 		return 0;
801 
802 	string->size = 0;
803 	memset(string->ptr, 0, 1);
804 
805 	return 1;
806 }
807 
invalidate_string(struct string_buffer * string)808 static void invalidate_string(struct string_buffer *string)
809 {
810 	string->ptr = NULL;
811 }
812 
free_string(struct string_buffer * string)813 static void free_string(struct string_buffer *string)
814 {
815 	if (string->ptr)
816 		efree(string->ptr);
817 }
818 
make_diff(char * filepath1,char * filepath2,xdemitcb_t * output,int context,int minimal)819 static int make_diff(char *filepath1, char *filepath2, xdemitcb_t *output, int context, int minimal)
820 {
821 	mmfile_t file1, file2;
822 	xpparam_t params;
823 	xdemitconf_t conf;
824 	int retval, result = 0;
825 
826 	retval = load_mm_file(filepath1, &file1);
827 	if (!retval)
828 		goto out;
829 
830 	retval = load_mm_file(filepath2, &file2);
831 	if (!retval)
832 		goto out_free_mmfile;
833 
834 	params.flags = (minimal) ? XDF_NEED_MINIMAL : 0;
835 	conf.ctxlen = abs(context);
836 
837 	retval = xdl_diff(&file1, &file2, &params, &conf, output);
838 	if (retval < 0)
839 		goto out_free_mmfile2;
840 
841 	result = 1;
842 
843 out_free_mmfile2:
844 	xdl_free_mmfile(&file2);
845 out_free_mmfile:
846 	xdl_free_mmfile(&file1);
847 out:
848 	return result;
849 }
850 
make_diff_str(char * str1,int size1,char * str2,int size2,xdemitcb_t * output,int context,int minimal)851 static int make_diff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output, int context, int minimal)
852 {
853 	mmfile_t file1, file2;
854 	xpparam_t params;
855 	xdemitconf_t conf;
856 	int retval, result = 0;
857 
858 	retval = load_into_mm_file(str1, size1, &file1);
859 	if (!retval)
860 		goto out;
861 
862 	retval = load_into_mm_file(str2, size2, &file2);
863 	if (!retval)
864 		goto out_free_mmfile;
865 
866 	params.flags = (minimal) ? XDF_NEED_MINIMAL : 0;
867 	conf.ctxlen = abs(context);
868 
869 	retval = xdl_diff(&file1, &file2, &params, &conf, output);
870 	if (retval < 0)
871 		goto out_free_mmfile2;
872 
873 	result = 1;
874 
875 out_free_mmfile2:
876 	xdl_free_mmfile(&file2);
877 out_free_mmfile:
878 	xdl_free_mmfile(&file1);
879 out:
880 	return result;
881 }
882 
make_bdiff(char * filepath1,char * filepath2,xdemitcb_t * output)883 static int make_bdiff(char *filepath1, char *filepath2, xdemitcb_t *output)
884 {
885 	mmfile_t file1, file2;
886 	bdiffparam_t params;
887 	int retval, result = 0;
888 
889 	retval = load_mm_file(filepath1, &file1);
890 	if (!retval)
891 		goto out;
892 
893 	retval = load_mm_file(filepath2, &file2);
894 	if (!retval)
895 		goto out_free_mmfile;
896 
897 	params.bsize = 16;
898 
899 	retval = xdl_bdiff(&file1, &file2, &params, output);
900 	if (retval < 0)
901 		goto out_free_mmfile2;
902 
903 	result = 1;
904 
905 out_free_mmfile2:
906 	xdl_free_mmfile(&file2);
907 out_free_mmfile:
908 	xdl_free_mmfile(&file1);
909 out:
910 	return result;
911 }
912 
make_bdiff_str(char * str1,int size1,char * str2,int size2,xdemitcb_t * output)913 static int make_bdiff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output)
914 {
915 	mmfile_t file1, file2;
916 	bdiffparam_t params;
917 	int retval, result = 0;
918 
919 	retval = load_into_mm_file(str1, size1, &file1);
920 	if (!retval)
921 		goto out;
922 
923 	retval = load_into_mm_file(str2, size2, &file2);
924 	if (!retval)
925 		goto out_free_mmfile;
926 
927 	params.bsize = 16;
928 
929 	retval = xdl_bdiff(&file1, &file2, &params, output);
930 	if (retval < 0)
931 		goto out_free_mmfile2;
932 
933 	result = 1;
934 
935 out_free_mmfile2:
936 	xdl_free_mmfile(&file2);
937 out_free_mmfile:
938 	xdl_free_mmfile(&file1);
939 out:
940 	return result;
941 }
942 
make_rabdiff(char * filepath1,char * filepath2,xdemitcb_t * output)943 static int make_rabdiff(char *filepath1, char *filepath2, xdemitcb_t *output)
944 {
945 	mmfile_t file1, file2;
946 	int retval, result = 0;
947 
948 	retval = load_mm_file(filepath1, &file1);
949 	if (!retval)
950 		goto out;
951 
952 	retval = load_mm_file(filepath2, &file2);
953 	if (!retval)
954 		goto out_free_mmfile;
955 
956 	retval = xdl_rabdiff(&file1, &file2, output);
957 	if (retval < 0)
958 		goto out_free_mmfile2;
959 
960 	result = 1;
961 
962 out_free_mmfile2:
963 	xdl_free_mmfile(&file2);
964 out_free_mmfile:
965 	xdl_free_mmfile(&file1);
966 out:
967 	return result;
968 }
969 
make_rabdiff_str(char * str1,int size1,char * str2,int size2,xdemitcb_t * output)970 static int make_rabdiff_str(char *str1, int size1, char *str2, int size2, xdemitcb_t *output)
971 {
972 	mmfile_t file1, file2;
973 	int retval, result = 0;
974 
975 	retval = load_into_mm_file(str1, size1, &file1);
976 	if (!retval)
977 		goto out;
978 
979 	retval = load_into_mm_file(str2, size2, &file2);
980 	if (!retval)
981 		goto out_free_mmfile;
982 
983 	retval = xdl_rabdiff(&file1, &file2, output);
984 	if (retval < 0)
985 		goto out_free_mmfile2;
986 
987 	result = 1;
988 
989 out_free_mmfile2:
990 	xdl_free_mmfile(&file2);
991 out_free_mmfile:
992 	xdl_free_mmfile(&file1);
993 out:
994 	return result;
995 }
996 
make_patch(char * file_path,char * patch_path,xdemitcb_t * output,xdemitcb_t * error,int flags)997 static int make_patch(char *file_path, char *patch_path, xdemitcb_t *output, xdemitcb_t *error, int flags)
998 {
999 	mmfile_t file, patch;
1000 	int retval, result = 0;
1001 
1002 	retval = load_mm_file(file_path, &file);
1003 	if (!retval)
1004 		goto out;
1005 
1006 	retval = load_mm_file(patch_path, &patch);
1007 	if (!retval)
1008 		goto out_free_mmfile;
1009 
1010 	retval = xdl_patch(&file, &patch, flags, output, error);
1011 	if (retval < 0)
1012 		goto out_free_mmfile2;
1013 
1014 	result = 1;
1015 
1016 out_free_mmfile2:
1017 	xdl_free_mmfile(&patch);
1018 out_free_mmfile:
1019 	xdl_free_mmfile(&file);
1020 out:
1021 	return result;
1022 }
1023 
make_patch_str(char * file,int size1,char * patch,int size2,xdemitcb_t * output,xdemitcb_t * error,int flags)1024 static int make_patch_str(char *file, int size1, char *patch, int size2, xdemitcb_t *output, xdemitcb_t *error, int flags)
1025 {
1026 	mmfile_t file_mm, patch_mm;
1027 	int retval, result = 0;
1028 
1029 	retval = load_into_mm_file(file, size1, &file_mm);
1030 	if (!retval)
1031 		goto out;
1032 
1033 	retval = load_into_mm_file(patch, size2, &patch_mm);
1034 	if (!retval)
1035 		goto out_free_mmfile;
1036 
1037 	retval = xdl_patch(&file_mm, &patch_mm, flags, output, error);
1038 	if (retval < 0)
1039 		goto out_free_mmfile2;
1040 
1041 	result = 1;
1042 
1043 out_free_mmfile2:
1044 	xdl_free_mmfile(&patch_mm);
1045 out_free_mmfile:
1046 	xdl_free_mmfile(&file_mm);
1047 out:
1048 	return result;
1049 }
1050 
make_bpatch(char * file_path,char * patch_path,xdemitcb_t * output)1051 static int make_bpatch(char *file_path, char *patch_path, xdemitcb_t *output)
1052 {
1053 	mmfile_t file_mm, patch_mm;
1054 	int retval, result = 0;
1055 
1056 	retval = load_mm_file(file_path, &file_mm);
1057 	if (!retval)
1058 		goto out;
1059 
1060 	retval = load_mm_file(patch_path, &patch_mm);
1061 	if (!retval)
1062 		goto out_free_mmfile;
1063 
1064 	retval = xdl_bpatch(&file_mm, &patch_mm, output);
1065 	if (retval < 0)
1066 		goto out_free_mmfile2;
1067 
1068 	result = 1;
1069 
1070 out_free_mmfile2:
1071 	xdl_free_mmfile(&patch_mm);
1072 out_free_mmfile:
1073 	xdl_free_mmfile(&file_mm);
1074 out:
1075 	return result;
1076 }
1077 
make_bpatch_str(char * file,int size1,char * patch,int size2,xdemitcb_t * output)1078 static int make_bpatch_str(char *file, int size1, char *patch, int size2, xdemitcb_t *output)
1079 {
1080 	mmfile_t file_mm, patch_mm;
1081 	int retval, result = 0;
1082 
1083 	retval = load_into_mm_file(file, size1, &file_mm);
1084 	if (!retval)
1085 		goto out;
1086 
1087 	retval = load_into_mm_file(patch, size2, &patch_mm);
1088 	if (!retval)
1089 		goto out_free_mmfile;
1090 
1091 	retval = xdl_bpatch(&file_mm, &patch_mm, output);
1092 	if (retval < 0)
1093 		goto out_free_mmfile2;
1094 
1095 	result = 1;
1096 
1097 out_free_mmfile2:
1098 	xdl_free_mmfile(&patch_mm);
1099 out_free_mmfile:
1100 	xdl_free_mmfile(&file_mm);
1101 out:
1102 	return result;
1103 }
1104 
make_merge3(char * filepath1,char * filepath2,char * filepath3,xdemitcb_t * output,xdemitcb_t * error)1105 static int make_merge3(char *filepath1, char *filepath2, char *filepath3, xdemitcb_t *output, xdemitcb_t *error)
1106 {
1107 	mmfile_t file1, file2, file3;
1108 	int retval, result = 0;
1109 
1110 	retval = load_mm_file(filepath1, &file1);
1111 	if (!retval)
1112 		goto out;
1113 
1114 	retval = load_mm_file(filepath2, &file2);
1115 	if (!retval)
1116 		goto out_free_mmfile;
1117 
1118 	retval = load_mm_file(filepath3, &file3);
1119 	if (!retval)
1120 		goto out_free_mmfile2;
1121 
1122 	retval = xdl_merge3(&file1, &file2, &file3, output, error);
1123 	if (retval < 0)
1124 		goto out_free_mmfile3;
1125 
1126 	result = 1;
1127 
1128 out_free_mmfile3:
1129 	xdl_free_mmfile(&file3);
1130 out_free_mmfile2:
1131 	xdl_free_mmfile(&file2);
1132 out_free_mmfile:
1133 	xdl_free_mmfile(&file1);
1134 out:
1135 	return result;
1136 }
1137 
make_merge3_str(char * content1,int size1,char * content2,int size2,char * content3,int size3,xdemitcb_t * output,xdemitcb_t * error)1138 static int make_merge3_str(char *content1, int size1, char *content2, int size2, char *content3, int size3, xdemitcb_t *output, xdemitcb_t *error)
1139 {
1140 	mmfile_t file1, file2, file3;
1141 	int retval, result = 0;
1142 
1143 	retval = load_into_mm_file(content1, size1, &file1);
1144 	if (!retval)
1145 		goto out;
1146 
1147 	retval = load_into_mm_file(content2, size2, &file2);
1148 	if (!retval)
1149 		goto out_free_mmfile;
1150 
1151 	retval = load_into_mm_file(content3, size3, &file3);
1152 	if (!retval)
1153 		goto out_free_mmfile2;
1154 
1155 	retval = xdl_merge3(&file1, &file2, &file3, output, error);
1156 	if (retval < 0)
1157 		goto out_free_mmfile3;
1158 
1159 	result = 1;
1160 
1161 out_free_mmfile3:
1162 	xdl_free_mmfile(&file3);
1163 out_free_mmfile2:
1164 	xdl_free_mmfile(&file2);
1165 out_free_mmfile:
1166 	xdl_free_mmfile(&file1);
1167 out:
1168 	return result;
1169 }
1170 
1171 /*
1172  * Local variables:
1173  * tab-width: 4
1174  * c-basic-offset: 4
1175  * End:
1176  * vim600: noet sw=4 ts=4 fdm=marker
1177  * vim<600: noet sw=4 ts=4
1178  */
1179