From 85055ac97fa57ca339924cef719115d85bdf6c2e Mon Sep 17 00:00:00 2001 From: David M Date: Thu, 3 Jan 2008 17:00:40 +0000 Subject: oh boy... - Migrate code base to PHP 5.1+ git-svn-id: file:///svn/phpbb/trunk@8295 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/diff/diff.php | 6 +++--- phpBB/includes/diff/renderer.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 0b3d14dbda..801480df95 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -43,7 +43,7 @@ class diff * @param array $from_lines An array of strings. Typically these are lines from a file. * @param array $to_lines An array of strings. */ - function diff(&$from_content, &$to_content, $preserve_cr = true) + function __construct(&$from_content, &$to_content, $preserve_cr = true) { $diff_engine = &new diff_engine(); $this->_edits = $diff_engine->diff($from_content, $to_content, $preserve_cr); @@ -645,7 +645,7 @@ class diff3 extends diff */ class diff3_op { - function diff3_op($orig = false, $final1 = false, $final2 = false) + function __construct($orig = false, $final1 = false, $final2 = false) { $this->orig = $orig ? $orig : array(); $this->final1 = $final1 ? $final1 : array(); @@ -717,7 +717,7 @@ class diff3_op_copy extends diff3_op */ class diff3_block_builder { - function diff3_block_builder() + function __construct() { $this->_init(); } diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index 4157bc2cde..11745dab3d 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -51,7 +51,7 @@ class diff_renderer /** * Constructor. */ - function diff_renderer($params = array()) + function __construct($params = array()) { foreach ($params as $param => $value) { -- cgit v1.2.1 From 4b5fab372ff69a809144dee3a0a7f90c219fe400 Mon Sep 17 00:00:00 2001 From: Meik Sievertsen Date: Wed, 30 Jan 2008 16:14:39 +0000 Subject: merge with revision #r8350 git-svn-id: file:///svn/phpbb/trunk@8351 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/diff/renderer.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index 11745dab3d..7d5cd9e5d3 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -301,7 +301,7 @@ class diff_renderer_unified extends diff_renderer { return '
' . htmlspecialchars($this->_lines($lines, ' ')) . '
'; } - + function _added($lines) { return '
' . htmlspecialchars($this->_lines($lines, '+')) . '
'; @@ -448,7 +448,7 @@ class diff_renderer_inline extends diff_renderer // Therefore we split on words, but include all blocks of whitespace in the wordlist. $splitted_text_1 = $this->_split_on_words($text1, $nl); $splitted_text_2 = $this->_split_on_words($text2, $nl); - + $diff = &new diff($splitted_text_1, $splitted_text_2); unset($splitted_text_1, $splitted_text_2); @@ -463,7 +463,7 @@ class diff_renderer_inline extends diff_renderer { // Ignore \0; otherwise the while loop will never finish. $string = str_replace("\0", '', $string); - + $words = array(); $length = strlen($string); $pos = 0; @@ -537,7 +537,7 @@ class diff_renderer_raw extends diff_renderer { return $this->_lines($lines, ' '); } - + function _added($lines) { return $this->_lines($lines, '+'); @@ -603,7 +603,7 @@ class diff_renderer_side_by_side extends diff_renderer // Iterate through every header block of changes foreach ($this->lines as $header) { - $output .= 'Line ' . $header['oldline'] . '' . $user->lang['LINE'] . ' ' . $header['newline'] . ''; + $output .= '' . $user->lang['LINE'] . ' ' . $header['oldline'] . '' . $user->lang['LINE'] . ' ' . $header['newline'] . ''; // Each header block consists of a number of changes (add, remove, change). $current_context = ''; -- cgit v1.2.1 From 1b67e804224a4477031da03ff5108c621cf6d13b Mon Sep 17 00:00:00 2001 From: Meik Sievertsen Date: Mon, 28 Jul 2008 13:37:16 +0000 Subject: marge git-svn-id: file:///svn/phpbb/trunk@8696 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/diff/diff.php | 19 +++++++++++-------- phpBB/includes/diff/engine.php | 20 +++++++++++--------- phpBB/includes/diff/renderer.php | 28 ++++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 21 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 801480df95..487384ff8a 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -17,7 +17,7 @@ if (!defined('IN_PHPBB')) } /** -* Code from pear.php.net, Text_Diff-0.2.1 (beta) package +* Code from pear.php.net, Text_Diff-1.0.0 package * http://pear.php.net/package/Text_Diff/ * * Modified by phpBB Group to meet our coding standards @@ -26,6 +26,9 @@ if (!defined('IN_PHPBB')) * General API for generating and formatting diffs - the differences between * two sequences of strings. * +* Copyright 2004 Geoffrey T. Dairiki +* Copyright 2004-2008 The Horde Project (http://www.horde.org/) +* * @package diff * @author Geoffrey T. Dairiki */ @@ -45,7 +48,7 @@ class diff */ function __construct(&$from_content, &$to_content, $preserve_cr = true) { - $diff_engine = &new diff_engine(); + $diff_engine = new diff_engine(); $this->_edits = $diff_engine->diff($from_content, $to_content, $preserve_cr); } @@ -62,7 +65,7 @@ class diff * * Example: * - * $diff = &new diff($lines1, $lines2); + * $diff = new diff($lines1, $lines2); * $rev = $diff->reverse(); * * @@ -285,7 +288,7 @@ class diff_op var $orig; var $final; - function reverse() + function &reverse() { trigger_error('[diff] Abstract method', E_USER_ERROR); } @@ -413,7 +416,7 @@ class diff3 extends diff */ function diff3(&$orig, &$final1, &$final2) { - $diff_engine = &new diff_engine(); + $diff_engine = new diff_engine(); $diff_1 = $diff_engine->diff($orig, $final1); $diff_2 = $diff_engine->diff($orig, $final2); @@ -548,7 +551,7 @@ class diff3 extends diff function _diff3(&$edits1, &$edits2) { $edits = array(); - $bb = &new diff3_block_builder(); + $bb = new diff3_block_builder(); $e1 = current($edits1); $e2 = current($edits2); @@ -565,7 +568,7 @@ class diff3 extends diff } $ncopy = min($e1->norig(), $e2->norig()); - $edits[] = &new diff3_op_copy(array_slice($e1->orig, 0, $ncopy)); + $edits[] = new diff3_op_copy(array_slice($e1->orig, 0, $ncopy)); if ($e1->norig() > $ncopy) { @@ -759,7 +762,7 @@ class diff3_block_builder } else { - $edit = &new diff3_op($this->orig, $this->final1, $this->final2); + $edit = new diff3_op($this->orig, $this->final1, $this->final2); $this->_init(); return $edit; } diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php index 576839ea60..ebf1469d67 100644 --- a/phpBB/includes/diff/engine.php +++ b/phpBB/includes/diff/engine.php @@ -17,21 +17,20 @@ if (!defined('IN_PHPBB')) } /** -* Code from pear.php.net, Text_Diff-0.2.1 (beta) package -* http://pear.php.net/package/Text_Diff/ +* Code from pear.php.net, Text_Diff-1.0.0 package +* http://pear.php.net/package/Text_Diff/ (native engine) * * Modified by phpBB Group to meet our coding standards * and being able to integrate into phpBB * -* Class used internally by Diff to actually compute the diffs. This class is -* implemented using native PHP code. +* Class used internally by Text_Diff to actually compute the diffs. This +* class is implemented using native PHP code. * * The algorithm used here is mostly lifted from the perl module * Algorithm::Diff (version 1.06) by Ned Konz, which is available at: * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip * -* More ideas are taken from: -* http://www.ics.uci.edu/~eppstein/161/960229.html +* More ideas are taken from: http://www.ics.uci.edu/~eppstein/161/960229.html * * Some ideas (and a bit of code) are taken from analyze.c, of GNU * diffutils-2.7, which can be found at: @@ -41,6 +40,8 @@ if (!defined('IN_PHPBB')) * Geoffrey T. Dairiki . The original PHP version of this * code was written by him, and is used/adapted with his permission. * +* Copyright 2004-2008 The Horde Project (http://www.horde.org/) +* * @author Geoffrey T. Dairiki * @package diff * @@ -251,7 +252,7 @@ class diff_engine } } - $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks); + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks); for (; $x < $x1; $x++) { @@ -262,7 +263,8 @@ class diff_engine } $matches = $ymatches[$line]; - foreach ($matches as $y) + reset($matches); + while (list(, $y) = each($matches)) { if (empty($this->in_seq[$y])) { @@ -273,7 +275,7 @@ class diff_engine } // no reset() here - while (list($junk, $y) = each($matches)) + while (list(, $y) = each($matches)) { if ($y > $this->seq[$k - 1]) { diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index 7d5cd9e5d3..1fc5320db9 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -17,7 +17,7 @@ if (!defined('IN_PHPBB')) } /** -* Code from pear.php.net, Text_Diff-0.2.1 (beta) package +* Code from pear.php.net, Text_Diff-1.0.0 package * http://pear.php.net/package/Text_Diff/ * * Modified by phpBB Group to meet our coding standards @@ -28,6 +28,8 @@ if (!defined('IN_PHPBB')) * This class renders the diff in classic diff format. It is intended that * this class be customized via inheritance, to obtain fancier outputs. * +* Copyright 2004-2008 The Horde Project (http://www.horde.org/) +* * @package diff */ class diff_renderer @@ -105,7 +107,7 @@ class diff_renderer unset($diff3); - $diff = &new diff($diff_1, $diff_2); + $diff = new diff($diff_1, $diff_2); } $nlead = $this->_leading_context_lines; @@ -116,19 +118,24 @@ class diff_renderer foreach ($diffs as $i => $edit) { + // If these are unchanged (copied) lines, and we want to keep leading or trailing context lines, extract them from the copy block. if (is_a($edit, 'diff_op_copy')) { + // Do we have any diff blocks yet? if (is_array($block)) { + // How many lines to keep as context from the copy block. $keep = ($i == sizeof($diffs) - 1) ? $ntrail : $nlead + $ntrail; if (sizeof($edit->orig) <= $keep) { + // We have less lines in the block than we want for context => keep the whole block. $block[] = $edit; } else { if ($ntrail) { + // Create a new block with as many lines as we need for the trailing context. $context = array_slice($edit->orig, 0, $ntrail); $block[] = &new diff_op_copy($context); } @@ -137,12 +144,15 @@ class diff_renderer $block = false; } } + // Keep the copy block as the context for the next block. $context = $edit->orig; } else { + // Don't we have any diff blocks yet? if (!is_array($block)) { + // Extract context lines from the preceding copy block. $context = array_slice($context, sizeof($context) - $nlead); $x0 = $xi - sizeof($context); $y0 = $yi - sizeof($context); @@ -219,6 +229,16 @@ class diff_renderer $ybeg .= ',' . ($ybeg + $ylen - 1); } + // this matches the GNU Diff behaviour + if ($xlen && !$ylen) + { + $ybeg--; + } + else if (!$xlen) + { + $xbeg--; + } + return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; } @@ -449,11 +469,11 @@ class diff_renderer_inline extends diff_renderer $splitted_text_1 = $this->_split_on_words($text1, $nl); $splitted_text_2 = $this->_split_on_words($text2, $nl); - $diff = &new diff($splitted_text_1, $splitted_text_2); + $diff = new diff($splitted_text_1, $splitted_text_2); unset($splitted_text_1, $splitted_text_2); // Get the diff in inline format. - $renderer = &new diff_renderer_inline(array_merge($this->get_params(), array('split_level' => 'words'))); + $renderer = new diff_renderer_inline(array_merge($this->get_params(), array('split_level' => 'words'))); // Run the diff and get the output. return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; -- cgit v1.2.1 From c119d0b424636b647da4c631fa6afbd8b053b30e Mon Sep 17 00:00:00 2001 From: "Marek A. R" Date: Sat, 16 Aug 2008 22:32:24 +0000 Subject: More 5.3 compatibility. git-svn-id: file:///svn/phpbb/trunk@8767 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/diff/diff.php | 8 ++++---- phpBB/includes/diff/engine.php | 8 ++++---- phpBB/includes/diff/renderer.php | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 487384ff8a..0837ac90d1 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -324,7 +324,7 @@ class diff_op_copy extends diff_op function &reverse() { - $reverse = &new diff_op_copy($this->final, $this->orig); + $reverse = new diff_op_copy($this->final, $this->orig); return $reverse; } } @@ -345,7 +345,7 @@ class diff_op_delete extends diff_op function &reverse() { - $reverse = &new diff_op_add($this->orig); + $reverse = new diff_op_add($this->orig); return $reverse; } } @@ -366,7 +366,7 @@ class diff_op_add extends diff_op function &reverse() { - $reverse = &new diff_op_delete($this->final); + $reverse = new diff_op_delete($this->final); return $reverse; } } @@ -387,7 +387,7 @@ class diff_op_change extends diff_op function &reverse() { - $reverse = &new diff_op_change($this->final, $this->orig); + $reverse = new diff_op_change($this->final, $this->orig); return $reverse; } } diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php index ebf1469d67..d82afb048a 100644 --- a/phpBB/includes/diff/engine.php +++ b/phpBB/includes/diff/engine.php @@ -160,7 +160,7 @@ class diff_engine if ($copy) { - $edits[] = &new diff_op_copy($copy); + $edits[] = new diff_op_copy($copy); } // Find deletes & adds. @@ -178,15 +178,15 @@ class diff_engine if ($delete && $add) { - $edits[] = &new diff_op_change($delete, $add); + $edits[] = new diff_op_change($delete, $add); } else if ($delete) { - $edits[] = &new diff_op_delete($delete); + $edits[] = new diff_op_delete($delete); } else if ($add) { - $edits[] = &new diff_op_add($add); + $edits[] = new diff_op_add($add); } } diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index 1fc5320db9..7f7a3366b1 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -137,7 +137,7 @@ class diff_renderer { // Create a new block with as many lines as we need for the trailing context. $context = array_slice($edit->orig, 0, $ntrail); - $block[] = &new diff_op_copy($context); + $block[] = new diff_op_copy($context); } $output .= $this->_block($x0, $ntrail + $xi - $x0, $y0, $ntrail + $yi - $y0, $block); @@ -160,7 +160,7 @@ class diff_renderer if ($context) { - $block[] = &new diff_op_copy($context); + $block[] = new diff_op_copy($context); } } $block[] = $edit; -- cgit v1.2.1 From 794c5749696c9fa2595ed3a1d7c836a0d984e11c Mon Sep 17 00:00:00 2001 From: Meik Sievertsen Date: Sun, 22 Feb 2009 15:29:18 +0000 Subject: remove global and change $user-> to phpbb::$user-> git-svn-id: file:///svn/phpbb/trunk@9334 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/diff/diff.php | 8 +++----- phpBB/includes/diff/renderer.php | 14 ++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 0837ac90d1..674a3b58b4 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -439,8 +439,6 @@ class diff3 extends diff */ function merged_output($label1 = 'CURRENT_FILE', $label2 = 'NEW_FILE', $label_sep = 'DIFF_SEP_EXPLAIN', $get_conflicts = false, $merge_new = false) { - global $user; - if ($get_conflicts) { foreach ($this->_edits as $edit) @@ -454,9 +452,9 @@ class diff3 extends diff return $this->_conflicting_blocks; } - $label1 = (!empty($user->lang[$label1])) ? $user->lang[$label1] : $label1; - $label2 = (!empty($user->lang[$label2])) ? $user->lang[$label2] : $label2; - $label_sep = (!empty($user->lang[$label_sep])) ? $user->lang[$label_sep] : $label_sep; + $label1 = (!empty(phpbb::$user->lang[$label1])) ? phpbb::$user->lang[$label1] : $label1; + $label2 = (!empty(phpbb::$user->lang[$label2])) ? phpbb::$user->lang[$label2] : $label2; + $label_sep = (!empty(phpbb::$user->lang[$label_sep])) ? phpbb::$user->lang[$label_sep] : $label_sep; $lines = array(); diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index 7f7a3366b1..8194b63da6 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -598,15 +598,13 @@ class diff_renderer_side_by_side extends diff_renderer */ function get_diff_content($diff) { - global $user; - $output = ''; $output .= ' '; @@ -616,14 +614,14 @@ class diff_renderer_side_by_side extends diff_renderer // Is the diff empty? if (!sizeof($this->lines)) { - $output .= ''; + $output .= ''; } else { // Iterate through every header block of changes foreach ($this->lines as $header) { - $output .= ''; + $output .= ''; // Each header block consists of a number of changes (add, remove, change). $current_context = ''; -- cgit v1.2.1 From 4cbf6bc703bdadf716197b68a89b3438247ff022 Mon Sep 17 00:00:00 2001 From: Meik Sievertsen Date: Sun, 22 Mar 2009 16:34:26 +0000 Subject: Merge most changes from 3.0.x branch since the 25th december. (Captcha changes for refreshing captcha image not included) git-svn-id: file:///svn/phpbb/trunk@9404 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/diff/diff.php | 97 +++++++++++++++++++++++++++++----------- phpBB/includes/diff/engine.php | 2 +- phpBB/includes/diff/renderer.php | 2 +- 3 files changed, 72 insertions(+), 29 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 674a3b58b4..c5249ee3c1 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -17,7 +17,7 @@ if (!defined('IN_PHPBB')) } /** -* Code from pear.php.net, Text_Diff-1.0.0 package +* Code from pear.php.net, Text_Diff-1.1.0 package * http://pear.php.net/package/Text_Diff/ * * Modified by phpBB Group to meet our coding standards @@ -60,6 +60,48 @@ class diff return $this->_edits; } + /** + * returns the number of new (added) lines in a given diff. + * + * @since Text_Diff 1.1.0 + * + * @return integer The number of new lines + */ + function count_added_lines() + { + $count = 0; + + foreach ($this->_edits as $edit) + { + if (is_a($edit, 'diff_op_add') || is_a($edit, 'diff_op_change')) + { + $count += $edit->nfinal(); + } + } + return $count; + } + + /** + * Returns the number of deleted (removed) lines in a given diff. + * + * @since Text_Diff 1.1.0 + * + * @return integer The number of deleted lines + */ + function count_deleted_lines() + { + $count = 0; + + foreach ($this->_edits as $edit) + { + if (is_a($edit, 'diff_op_delete') || is_a($edit, 'diff_op_change')) + { + $count += $edit->norig(); + } + } + return $count; + } + /** * Computes a reversed diff. * @@ -427,31 +469,35 @@ class diff3 extends diff } /** - * Return merged output + * Return number of conflicts + */ + function get_num_conflicts() + { + $conflicts = 0; + + foreach ($this->_edits as $edit) + { + if ($edit->is_conflict()) + { + $conflicts++; + } + } + + return $conflicts; + } + + /** + * Get conflicts content for download. This is generally a merged file, but preserving conflicts and adding explanations to it. + * A user could then go through this file, search for the conflicts and changes the code accordingly. * * @param string $label1 the cvs file version/label from the original set of lines * @param string $label2 the cvs file version/label from the new set of lines * @param string $label_sep the explanation between label1 and label2 - more of a helper for the user - * @param bool $get_conflicts if set to true only the number of conflicts is returned - * @param bool $merge_new if set to true the merged output will have the new file contents on a conflicting merge * * @return mixed the merged output */ - function merged_output($label1 = 'CURRENT_FILE', $label2 = 'NEW_FILE', $label_sep = 'DIFF_SEP_EXPLAIN', $get_conflicts = false, $merge_new = false) + function get_conflicts_content($label1 = 'CURRENT_FILE', $label2 = 'NEW_FILE', $label_sep = 'DIFF_SEP_EXPLAIN') { - if ($get_conflicts) - { - foreach ($this->_edits as $edit) - { - if ($edit->is_conflict()) - { - $this->_conflicting_blocks++; - } - } - - return $this->_conflicting_blocks; - } - $label1 = (!empty(phpbb::$user->lang[$label1])) ? phpbb::$user->lang[$label1] : $label1; $label2 = (!empty(phpbb::$user->lang[$label2])) ? phpbb::$user->lang[$label2] : $label2; $label_sep = (!empty(phpbb::$user->lang[$label_sep])) ? phpbb::$user->lang[$label_sep] : $label_sep; @@ -462,15 +508,12 @@ class diff3 extends diff { if ($edit->is_conflict()) { - if (!$merge_new) - { - $lines = array_merge($lines, array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')), $edit->final1, array('=======' . ($label_sep ? ' ' . $label_sep : '')), $edit->final2, array('>>>>>>>' . ($label2 ? ' ' . $label2 : ''))); - } - else - { - $lines = array_merge($lines, $edit->final1); - } - $this->_conflicting_blocks++; + // Start conflict label + $label_start = array('<<<<<<< ' . $label1); + $label_mid = array('======= ' . $label_sep); + $label_end = array('>>>>>>> ' . $label2); + + $lines = array_merge($lines, $label_start, $edit->final1, $label_mid, $edit->final2, $label_end); } else { diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php index d82afb048a..eb0dcce395 100644 --- a/phpBB/includes/diff/engine.php +++ b/phpBB/includes/diff/engine.php @@ -17,7 +17,7 @@ if (!defined('IN_PHPBB')) } /** -* Code from pear.php.net, Text_Diff-1.0.0 package +* Code from pear.php.net, Text_Diff-1.1.0 package * http://pear.php.net/package/Text_Diff/ (native engine) * * Modified by phpBB Group to meet our coding standards diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index 8194b63da6..2d3ab88a26 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -17,7 +17,7 @@ if (!defined('IN_PHPBB')) } /** -* Code from pear.php.net, Text_Diff-1.0.0 package +* Code from pear.php.net, Text_Diff-1.1.0 package * http://pear.php.net/package/Text_Diff/ * * Modified by phpBB Group to meet our coding standards -- cgit v1.2.1 From 96fcbd972bb9e1bfb1398da14539dee447a20e7d Mon Sep 17 00:00:00 2001 From: Meik Sievertsen Date: Sun, 7 Jun 2009 18:00:37 +0000 Subject: moved diff git-svn-id: file:///svn/phpbb/trunk@9556 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/diff/diff.php | 823 ------------------------------------- phpBB/includes/diff/engine.php | 536 ------------------------ phpBB/includes/diff/renderer.php | 858 --------------------------------------- 3 files changed, 2217 deletions(-) delete mode 100644 phpBB/includes/diff/diff.php delete mode 100644 phpBB/includes/diff/engine.php delete mode 100644 phpBB/includes/diff/renderer.php (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php deleted file mode 100644 index c5249ee3c1..0000000000 --- a/phpBB/includes/diff/diff.php +++ /dev/null @@ -1,823 +0,0 @@ - -* Copyright 2004-2008 The Horde Project (http://www.horde.org/) -* -* @package diff -* @author Geoffrey T. Dairiki -*/ -class diff -{ - /** - * Array of changes. - * @var array - */ - var $_edits; - - /** - * Computes diffs between sequences of strings. - * - * @param array $from_lines An array of strings. Typically these are lines from a file. - * @param array $to_lines An array of strings. - */ - function __construct(&$from_content, &$to_content, $preserve_cr = true) - { - $diff_engine = new diff_engine(); - $this->_edits = $diff_engine->diff($from_content, $to_content, $preserve_cr); - } - - /** - * Returns the array of differences. - */ - function get_diff() - { - return $this->_edits; - } - - /** - * returns the number of new (added) lines in a given diff. - * - * @since Text_Diff 1.1.0 - * - * @return integer The number of new lines - */ - function count_added_lines() - { - $count = 0; - - foreach ($this->_edits as $edit) - { - if (is_a($edit, 'diff_op_add') || is_a($edit, 'diff_op_change')) - { - $count += $edit->nfinal(); - } - } - return $count; - } - - /** - * Returns the number of deleted (removed) lines in a given diff. - * - * @since Text_Diff 1.1.0 - * - * @return integer The number of deleted lines - */ - function count_deleted_lines() - { - $count = 0; - - foreach ($this->_edits as $edit) - { - if (is_a($edit, 'diff_op_delete') || is_a($edit, 'diff_op_change')) - { - $count += $edit->norig(); - } - } - return $count; - } - - /** - * Computes a reversed diff. - * - * Example: - * - * $diff = new diff($lines1, $lines2); - * $rev = $diff->reverse(); - * - * - * @return diff A Diff object representing the inverse of the original diff. - * Note that we purposely don't return a reference here, since - * this essentially is a clone() method. - */ - function reverse() - { - if (version_compare(zend_version(), '2', '>')) - { - $rev = clone($this); - } - else - { - $rev = $this; - } - - $rev->_edits = array(); - - foreach ($this->_edits as $edit) - { - $rev->_edits[] = $edit->reverse(); - } - - return $rev; - } - - /** - * Checks for an empty diff. - * - * @return boolean True if two sequences were identical. - */ - function is_empty() - { - foreach ($this->_edits as $edit) - { - if (!is_a($edit, 'diff_op_copy')) - { - return false; - } - } - return true; - } - - /** - * Computes the length of the Longest Common Subsequence (LCS). - * - * This is mostly for diagnostic purposes. - * - * @return integer The length of the LCS. - */ - function lcs() - { - $lcs = 0; - - foreach ($this->_edits as $edit) - { - if (is_a($edit, 'diff_op_copy')) - { - $lcs += sizeof($edit->orig); - } - } - return $lcs; - } - - /** - * Gets the original set of lines. - * - * This reconstructs the $from_lines parameter passed to the constructor. - * - * @return array The original sequence of strings. - */ - function get_original() - { - $lines = array(); - - foreach ($this->_edits as $edit) - { - if ($edit->orig) - { - array_splice($lines, sizeof($lines), 0, $edit->orig); - } - } - return $lines; - } - - /** - * Gets the final set of lines. - * - * This reconstructs the $to_lines parameter passed to the constructor. - * - * @return array The sequence of strings. - */ - function get_final() - { - $lines = array(); - - foreach ($this->_edits as $edit) - { - if ($edit->final) - { - array_splice($lines, sizeof($lines), 0, $edit->final); - } - } - return $lines; - } - - /** - * Removes trailing newlines from a line of text. This is meant to be used with array_walk(). - * - * @param string &$line The line to trim. - * @param integer $key The index of the line in the array. Not used. - */ - function trim_newlines(&$line, $key) - { - $line = str_replace(array("\n", "\r"), '', $line); - } - - /** - * Checks a diff for validity. - * - * This is here only for debugging purposes. - */ - function _check($from_lines, $to_lines) - { - if (serialize($from_lines) != serialize($this->get_original())) - { - trigger_error("[diff] Reconstructed original doesn't match", E_USER_ERROR); - } - - if (serialize($to_lines) != serialize($this->get_final())) - { - trigger_error("[diff] Reconstructed final doesn't match", E_USER_ERROR); - } - - $rev = $this->reverse(); - - if (serialize($to_lines) != serialize($rev->get_original())) - { - trigger_error("[diff] Reversed original doesn't match", E_USER_ERROR); - } - - if (serialize($from_lines) != serialize($rev->get_final())) - { - trigger_error("[diff] Reversed final doesn't match", E_USER_ERROR); - } - - $prevtype = null; - - foreach ($this->_edits as $edit) - { - if ($prevtype == get_class($edit)) - { - trigger_error("[diff] Edit sequence is non-optimal", E_USER_ERROR); - } - $prevtype = get_class($edit); - } - - return true; - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -*/ -class mapped_diff extends diff -{ - /** - * Computes a diff between sequences of strings. - * - * This can be used to compute things like case-insensitve diffs, or diffs - * which ignore changes in white-space. - * - * @param array $from_lines An array of strings. - * @param array $to_lines An array of strings. - * @param array $mapped_from_lines This array should have the same size number of elements as $from_lines. - * The elements in $mapped_from_lines and $mapped_to_lines are what is actually - * compared when computing the diff. - * @param array $mapped_to_lines This array should have the same number of elements as $to_lines. - */ - function mapped_diff(&$from_lines, &$to_lines, &$mapped_from_lines, &$mapped_to_lines) - { - if (sizeof($from_lines) != sizeof($mapped_from_lines) || sizeof($to_lines) != sizeof($mapped_to_lines)) - { - return false; - } - - parent::diff($mapped_from_lines, $mapped_to_lines); - - $xi = $yi = 0; - for ($i = 0; $i < sizeof($this->_edits); $i++) - { - $orig = &$this->_edits[$i]->orig; - if (is_array($orig)) - { - $orig = array_slice($from_lines, $xi, sizeof($orig)); - $xi += sizeof($orig); - } - - $final = &$this->_edits[$i]->final; - if (is_array($final)) - { - $final = array_slice($to_lines, $yi, sizeof($final)); - $yi += sizeof($final); - } - } - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -* -* @access private -*/ -class diff_op -{ - var $orig; - var $final; - - function &reverse() - { - trigger_error('[diff] Abstract method', E_USER_ERROR); - } - - function norig() - { - return ($this->orig) ? sizeof($this->orig) : 0; - } - - function nfinal() - { - return ($this->final) ? sizeof($this->final) : 0; - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -* -* @access private -*/ -class diff_op_copy extends diff_op -{ - function diff_op_copy($orig, $final = false) - { - if (!is_array($final)) - { - $final = $orig; - } - $this->orig = $orig; - $this->final = $final; - } - - function &reverse() - { - $reverse = new diff_op_copy($this->final, $this->orig); - return $reverse; - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -* -* @access private -*/ -class diff_op_delete extends diff_op -{ - function diff_op_delete($lines) - { - $this->orig = $lines; - $this->final = false; - } - - function &reverse() - { - $reverse = new diff_op_add($this->orig); - return $reverse; - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -* -* @access private -*/ -class diff_op_add extends diff_op -{ - function diff_op_add($lines) - { - $this->final = $lines; - $this->orig = false; - } - - function &reverse() - { - $reverse = new diff_op_delete($this->final); - return $reverse; - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -* -* @access private -*/ -class diff_op_change extends diff_op -{ - function diff_op_change($orig, $final) - { - $this->orig = $orig; - $this->final = $final; - } - - function &reverse() - { - $reverse = new diff_op_change($this->final, $this->orig); - return $reverse; - } -} - - -/** -* A class for computing three way diffs. -* -* @package diff -* @author Geoffrey T. Dairiki -*/ -class diff3 extends diff -{ - /** - * Conflict counter. - * @var integer - */ - var $_conflicting_blocks = 0; - - /** - * Computes diff between 3 sequences of strings. - * - * @param array $orig The original lines to use. - * @param array $final1 The first version to compare to. - * @param array $final2 The second version to compare to. - */ - function diff3(&$orig, &$final1, &$final2) - { - $diff_engine = new diff_engine(); - - $diff_1 = $diff_engine->diff($orig, $final1); - $diff_2 = $diff_engine->diff($orig, $final2); - - unset($engine); - - $this->_edits = $this->_diff3($diff_1, $diff_2); - } - - /** - * Return number of conflicts - */ - function get_num_conflicts() - { - $conflicts = 0; - - foreach ($this->_edits as $edit) - { - if ($edit->is_conflict()) - { - $conflicts++; - } - } - - return $conflicts; - } - - /** - * Get conflicts content for download. This is generally a merged file, but preserving conflicts and adding explanations to it. - * A user could then go through this file, search for the conflicts and changes the code accordingly. - * - * @param string $label1 the cvs file version/label from the original set of lines - * @param string $label2 the cvs file version/label from the new set of lines - * @param string $label_sep the explanation between label1 and label2 - more of a helper for the user - * - * @return mixed the merged output - */ - function get_conflicts_content($label1 = 'CURRENT_FILE', $label2 = 'NEW_FILE', $label_sep = 'DIFF_SEP_EXPLAIN') - { - $label1 = (!empty(phpbb::$user->lang[$label1])) ? phpbb::$user->lang[$label1] : $label1; - $label2 = (!empty(phpbb::$user->lang[$label2])) ? phpbb::$user->lang[$label2] : $label2; - $label_sep = (!empty(phpbb::$user->lang[$label_sep])) ? phpbb::$user->lang[$label_sep] : $label_sep; - - $lines = array(); - - foreach ($this->_edits as $edit) - { - if ($edit->is_conflict()) - { - // Start conflict label - $label_start = array('<<<<<<< ' . $label1); - $label_mid = array('======= ' . $label_sep); - $label_end = array('>>>>>>> ' . $label2); - - $lines = array_merge($lines, $label_start, $edit->final1, $label_mid, $edit->final2, $label_end); - } - else - { - $lines = array_merge($lines, $edit->merged()); - } - } - - return $lines; - } - - /** - * Merge the output and use the new file code for conflicts - */ - function merged_new_output() - { - $lines = array(); - - foreach ($this->_edits as $edit) - { - if ($edit->is_conflict()) - { - $lines = array_merge($lines, $edit->final2); - } - else - { - $lines = array_merge($lines, $edit->merged()); - } - } - - return $lines; - } - - /** - * Merge the output and use the original file code for conflicts - */ - function merged_orig_output() - { - $lines = array(); - - foreach ($this->_edits as $edit) - { - if ($edit->is_conflict()) - { - $lines = array_merge($lines, $edit->final1); - } - else - { - $lines = array_merge($lines, $edit->merged()); - } - } - - return $lines; - } - - /** - * Get conflicting block(s) - */ - function get_conflicts() - { - $conflicts = array(); - - foreach ($this->_edits as $edit) - { - if ($edit->is_conflict()) - { - $conflicts[] = array($edit->final1, $edit->final2); - } - } - - return $conflicts; - } - - /** - * @access private - */ - function _diff3(&$edits1, &$edits2) - { - $edits = array(); - $bb = new diff3_block_builder(); - - $e1 = current($edits1); - $e2 = current($edits2); - - while ($e1 || $e2) - { - if ($e1 && $e2 && is_a($e1, 'diff_op_copy') && is_a($e2, 'diff_op_copy')) - { - // We have copy blocks from both diffs. This is the (only) time we want to emit a diff3 copy block. - // Flush current diff3 diff block, if any. - if ($edit = $bb->finish()) - { - $edits[] = $edit; - } - - $ncopy = min($e1->norig(), $e2->norig()); - $edits[] = new diff3_op_copy(array_slice($e1->orig, 0, $ncopy)); - - if ($e1->norig() > $ncopy) - { - array_splice($e1->orig, 0, $ncopy); - array_splice($e1->final, 0, $ncopy); - } - else - { - $e1 = next($edits1); - } - - if ($e2->norig() > $ncopy) - { - array_splice($e2->orig, 0, $ncopy); - array_splice($e2->final, 0, $ncopy); - } - else - { - $e2 = next($edits2); - } - } - else - { - if ($e1 && $e2) - { - if ($e1->orig && $e2->orig) - { - $norig = min($e1->norig(), $e2->norig()); - $orig = array_splice($e1->orig, 0, $norig); - array_splice($e2->orig, 0, $norig); - $bb->input($orig); - } - else - { - $norig = 0; - } - - if (is_a($e1, 'diff_op_copy')) - { - $bb->out1(array_splice($e1->final, 0, $norig)); - } - - if (is_a($e2, 'diff_op_copy')) - { - $bb->out2(array_splice($e2->final, 0, $norig)); - } - } - - if ($e1 && ! $e1->orig) - { - $bb->out1($e1->final); - $e1 = next($edits1); - } - - if ($e2 && ! $e2->orig) - { - $bb->out2($e2->final); - $e2 = next($edits2); - } - } - } - - if ($edit = $bb->finish()) - { - $edits[] = $edit; - } - - return $edits; - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -* -* @access private -*/ -class diff3_op -{ - function __construct($orig = false, $final1 = false, $final2 = false) - { - $this->orig = $orig ? $orig : array(); - $this->final1 = $final1 ? $final1 : array(); - $this->final2 = $final2 ? $final2 : array(); - } - - function merged() - { - if (!isset($this->_merged)) - { - if ($this->final1 === $this->final2) - { - $this->_merged = &$this->final1; - } - else if ($this->final1 === $this->orig) - { - $this->_merged = &$this->final2; - } - else if ($this->final2 === $this->orig) - { - $this->_merged = &$this->final1; - } - else - { - $this->_merged = false; - } - } - - return $this->_merged; - } - - function is_conflict() - { - return ($this->merged() === false) ? true : false; - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -* -* @access private -*/ -class diff3_op_copy extends diff3_op -{ - function diff3_op_copy($lines = false) - { - $this->orig = $lines ? $lines : array(); - $this->final1 = &$this->orig; - $this->final2 = &$this->orig; - } - - function merged() - { - return $this->orig; - } - - function is_conflict() - { - return false; - } -} - -/** -* @package diff -* @author Geoffrey T. Dairiki -* -* @access private -*/ -class diff3_block_builder -{ - function __construct() - { - $this->_init(); - } - - function input($lines) - { - if ($lines) - { - $this->_append($this->orig, $lines); - } - } - - function out1($lines) - { - if ($lines) - { - $this->_append($this->final1, $lines); - } - } - - function out2($lines) - { - if ($lines) - { - $this->_append($this->final2, $lines); - } - } - - function is_empty() - { - return !$this->orig && !$this->final1 && !$this->final2; - } - - function finish() - { - if ($this->is_empty()) - { - return false; - } - else - { - $edit = new diff3_op($this->orig, $this->final1, $this->final2); - $this->_init(); - return $edit; - } - } - - function _init() - { - $this->orig = $this->final1 = $this->final2 = array(); - } - - function _append(&$array, $lines) - { - array_splice($array, sizeof($array), 0, $lines); - } -} - -?> \ No newline at end of file diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php deleted file mode 100644 index eb0dcce395..0000000000 --- a/phpBB/includes/diff/engine.php +++ /dev/null @@ -1,536 +0,0 @@ - 2, and some optimizations) are from -* Geoffrey T. Dairiki . The original PHP version of this -* code was written by him, and is used/adapted with his permission. -* -* Copyright 2004-2008 The Horde Project (http://www.horde.org/) -* -* @author Geoffrey T. Dairiki -* @package diff -* -* @access private -*/ -class diff_engine -{ - function diff(&$from_lines, &$to_lines, $preserve_cr = true) - { - // Remove empty lines... - // If preserve_cr is true, we basically only change \r\n and bare \r to \n to get the same carriage returns for both files - // If it is false, we try to only use \n once per line and ommit all empty lines to be able to get a proper data diff - - if (is_array($from_lines)) - { - $from_lines = implode("\n", $from_lines); - } - - if (is_array($to_lines)) - { - $to_lines = implode("\n", $to_lines); - } - - if ($preserve_cr) - { - $from_lines = explode("\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $from_lines))); - $to_lines = explode("\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $to_lines))); - } - else - { - $from_lines = explode("\n", preg_replace('#[\n\r]+#', "\n", $from_lines)); - $to_lines = explode("\n", preg_replace('#[\n\r]+#', "\n", $to_lines)); - } - - $n_from = sizeof($from_lines); - $n_to = sizeof($to_lines); - - $this->xchanged = $this->ychanged = $this->xv = $this->yv = $this->xind = $this->yind = array(); - unset($this->seq, $this->in_seq, $this->lcs); - - // Skip leading common lines. - for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) - { - if ($from_lines[$skip] !== $to_lines[$skip]) - { - break; - } - $this->xchanged[$skip] = $this->ychanged[$skip] = false; - } - - // Skip trailing common lines. - $xi = $n_from; - $yi = $n_to; - - for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) - { - if ($from_lines[$xi] !== $to_lines[$yi]) - { - break; - } - $this->xchanged[$xi] = $this->ychanged[$yi] = false; - } - - // Ignore lines which do not exist in both files. - for ($xi = $skip; $xi < $n_from - $endskip; $xi++) - { - $xhash[$from_lines[$xi]] = 1; - } - - for ($yi = $skip; $yi < $n_to - $endskip; $yi++) - { - $line = $to_lines[$yi]; - - if (($this->ychanged[$yi] = empty($xhash[$line]))) - { - continue; - } - $yhash[$line] = 1; - $this->yv[] = $line; - $this->yind[] = $yi; - } - - for ($xi = $skip; $xi < $n_from - $endskip; $xi++) - { - $line = $from_lines[$xi]; - - if (($this->xchanged[$xi] = empty($yhash[$line]))) - { - continue; - } - $this->xv[] = $line; - $this->xind[] = $xi; - } - - // Find the LCS. - $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv)); - - // Merge edits when possible. - $this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged); - $this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged); - - // Compute the edit operations. - $edits = array(); - $xi = $yi = 0; - - while ($xi < $n_from || $yi < $n_to) - { - // Skip matching "snake". - $copy = array(); - - while ($xi < $n_from && $yi < $n_to && !$this->xchanged[$xi] && !$this->ychanged[$yi]) - { - $copy[] = $from_lines[$xi++]; - $yi++; - } - - if ($copy) - { - $edits[] = new diff_op_copy($copy); - } - - // Find deletes & adds. - $delete = array(); - while ($xi < $n_from && $this->xchanged[$xi]) - { - $delete[] = $from_lines[$xi++]; - } - - $add = array(); - while ($yi < $n_to && $this->ychanged[$yi]) - { - $add[] = $to_lines[$yi++]; - } - - if ($delete && $add) - { - $edits[] = new diff_op_change($delete, $add); - } - else if ($delete) - { - $edits[] = new diff_op_delete($delete); - } - else if ($add) - { - $edits[] = new diff_op_add($add); - } - } - - return $edits; - } - - /** - * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF, - * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized segments. - * - * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an array of - * NCHUNKS+1 (X, Y) indexes giving the diving points between sub - * sequences. The first sub-sequence is contained in (X0, X1), (Y0, Y1), - * the second in (X1, X2), (Y1, Y2) and so on. Note that (X0, Y0) == - * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). - * - * This function assumes that the first lines of the specified portions of - * the two files do not match, and likewise that the last lines do not - * match. The caller must trim matching lines from the beginning and end - * of the portions it is going to specify. - */ - function _diag($xoff, $xlim, $yoff, $ylim, $nchunks) - { - $flip = false; - - if ($xlim - $xoff > $ylim - $yoff) - { - // Things seems faster (I'm not sure I understand why) when the shortest sequence is in X. - $flip = true; - list($xoff, $xlim, $yoff, $ylim) = array($yoff, $ylim, $xoff, $xlim); - } - - if ($flip) - { - for ($i = $ylim - 1; $i >= $yoff; $i--) - { - $ymatches[$this->xv[$i]][] = $i; - } - } - else - { - for ($i = $ylim - 1; $i >= $yoff; $i--) - { - $ymatches[$this->yv[$i]][] = $i; - } - } - - $this->lcs = 0; - $this->seq[0]= $yoff - 1; - $this->in_seq = array(); - $ymids[0] = array(); - - $numer = $xlim - $xoff + $nchunks - 1; - $x = $xoff; - - for ($chunk = 0; $chunk < $nchunks; $chunk++) - { - if ($chunk > 0) - { - for ($i = 0; $i <= $this->lcs; $i++) - { - $ymids[$i][$chunk - 1] = $this->seq[$i]; - } - } - - $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks); - - for (; $x < $x1; $x++) - { - $line = $flip ? $this->yv[$x] : $this->xv[$x]; - if (empty($ymatches[$line])) - { - continue; - } - $matches = $ymatches[$line]; - - reset($matches); - while (list(, $y) = each($matches)) - { - if (empty($this->in_seq[$y])) - { - $k = $this->_lcs_pos($y); - $ymids[$k] = $ymids[$k - 1]; - break; - } - } - - // no reset() here - while (list(, $y) = each($matches)) - { - if ($y > $this->seq[$k - 1]) - { - // Optimization: this is a common case: next match is just replacing previous match. - $this->in_seq[$this->seq[$k]] = false; - $this->seq[$k] = $y; - $this->in_seq[$y] = 1; - } - else if (empty($this->in_seq[$y])) - { - $k = $this->_lcs_pos($y); - $ymids[$k] = $ymids[$k - 1]; - } - } - } - } - - $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); - $ymid = $ymids[$this->lcs]; - - for ($n = 0; $n < $nchunks - 1; $n++) - { - $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); - $y1 = $ymid[$n] + 1; - $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); - } - $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); - - return array($this->lcs, $seps); - } - - function _lcs_pos($ypos) - { - $end = $this->lcs; - - if ($end == 0 || $ypos > $this->seq[$end]) - { - $this->seq[++$this->lcs] = $ypos; - $this->in_seq[$ypos] = 1; - return $this->lcs; - } - - $beg = 1; - while ($beg < $end) - { - $mid = (int)(($beg + $end) / 2); - if ($ypos > $this->seq[$mid]) - { - $beg = $mid + 1; - } - else - { - $end = $mid; - } - } - - $this->in_seq[$this->seq[$end]] = false; - $this->seq[$end] = $ypos; - $this->in_seq[$ypos] = 1; - - return $end; - } - - /** - * Finds LCS of two sequences. - * - * The results are recorded in the vectors $this->{x,y}changed[], by - * storing a 1 in the element for each line that is an insertion or - * deletion (ie. is not in the LCS). - * - * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1. - * - * Note that XLIM, YLIM are exclusive bounds. All line numbers are - * origin-0 and discarded lines are not counted. - */ - function _compareseq($xoff, $xlim, $yoff, $ylim) - { - // Slide down the bottom initial diagonal. - while ($xoff < $xlim && $yoff < $ylim && $this->xv[$xoff] == $this->yv[$yoff]) - { - ++$xoff; - ++$yoff; - } - - // Slide up the top initial diagonal. - while ($xlim > $xoff && $ylim > $yoff && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) - { - --$xlim; - --$ylim; - } - - if ($xoff == $xlim || $yoff == $ylim) - { - $lcs = 0; - } - else - { - // This is ad hoc but seems to work well. - // $nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); - // $nchunks = max(2,min(8,(int)$nchunks)); - $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; - list($lcs, $seps) = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); - } - - if ($lcs == 0) - { - // X and Y sequences have no common subsequence: mark all changed. - while ($yoff < $ylim) - { - $this->ychanged[$this->yind[$yoff++]] = 1; - } - - while ($xoff < $xlim) - { - $this->xchanged[$this->xind[$xoff++]] = 1; - } - } - else - { - // Use the partitions to split this problem into subproblems. - reset($seps); - $pt1 = $seps[0]; - - while ($pt2 = next($seps)) - { - $this->_compareseq($pt1[0], $pt2[0], $pt1[1], $pt2[1]); - $pt1 = $pt2; - } - } - } - - /** - * Adjusts inserts/deletes of identical lines to join changes as much as possible. - * - * We do something when a run of changed lines include a line at one end - * and has an excluded, identical line at the other. We are free to - * choose which identical line is included. 'compareseq' usually chooses - * the one at the beginning, but usually it is cleaner to consider the - * following identical line to be the "change". - * - * This is extracted verbatim from analyze.c (GNU diffutils-2.7). - */ - function _shift_boundaries($lines, &$changed, $other_changed) - { - $i = 0; - $j = 0; - - $len = sizeof($lines); - $other_len = sizeof($other_changed); - - while (1) - { - // Scan forward to find the beginning of another run of - // changes. Also keep track of the corresponding point in the other file. - // - // Throughout this code, $i and $j are adjusted together so that - // the first $i elements of $changed and the first $j elements of - // $other_changed both contain the same number of zeros (unchanged lines). - // - // Furthermore, $j is always kept so that $j == $other_len or $other_changed[$j] == false. - while ($j < $other_len && $other_changed[$j]) - { - $j++; - } - - while ($i < $len && ! $changed[$i]) - { - $i++; - $j++; - - while ($j < $other_len && $other_changed[$j]) - { - $j++; - } - } - - if ($i == $len) - { - break; - } - - $start = $i; - - // Find the end of this run of changes. - while (++$i < $len && $changed[$i]) - { - continue; - } - - do - { - // Record the length of this run of changes, so that we can later determine whether the run has grown. - $runlength = $i - $start; - - // Move the changed region back, so long as the previous unchanged line matches the last changed one. - // This merges with previous changed regions. - while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) - { - $changed[--$start] = 1; - $changed[--$i] = false; - - while ($start > 0 && $changed[$start - 1]) - { - $start--; - } - - while ($other_changed[--$j]) - { - continue; - } - } - - // Set CORRESPONDING to the end of the changed run, at the last point where it corresponds to a changed run in the - // other file. CORRESPONDING == LEN means no such point has been found. - $corresponding = $j < $other_len ? $i : $len; - - // Move the changed region forward, so long as the first changed line matches the following unchanged one. - // This merges with following changed regions. - // Do this second, so that if there are no merges, the changed region is moved forward as far as possible. - while ($i < $len && $lines[$start] == $lines[$i]) - { - $changed[$start++] = false; - $changed[$i++] = 1; - - while ($i < $len && $changed[$i]) - { - $i++; - } - - $j++; - if ($j < $other_len && $other_changed[$j]) - { - $corresponding = $i; - while ($j < $other_len && $other_changed[$j]) - { - $j++; - } - } - } - } - while ($runlength != $i - $start); - - // If possible, move the fully-merged run of changes back to a corresponding run in the other file. - while ($corresponding < $i) - { - $changed[--$start] = 1; - $changed[--$i] = 0; - - while ($other_changed[--$j]) - { - continue; - } - } - } - } -} - -?> \ No newline at end of file diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php deleted file mode 100644 index 2d3ab88a26..0000000000 --- a/phpBB/includes/diff/renderer.php +++ /dev/null @@ -1,858 +0,0 @@ - $value) - { - $v = '_' . $param; - if (isset($this->$v)) - { - $this->$v = $value; - } - } - } - - /** - * Get any renderer parameters. - * - * @return array All parameters of this renderer object. - */ - function get_params() - { - $params = array(); - foreach (get_object_vars($this) as $k => $v) - { - if ($k[0] == '_') - { - $params[substr($k, 1)] = $v; - } - } - - return $params; - } - - /** - * Renders a diff. - * - * @param diff &$diff A diff object. - * - * @return string The formatted output. - */ - function render(&$diff) - { - $xi = $yi = 1; - $block = false; - $context = array(); - - // Create a new diff object if it is a 3-way diff - if (is_a($diff, 'diff3')) - { - $diff3 = &$diff; - - $diff_1 = $diff3->get_original(); - $diff_2 = $diff3->merged_output(); - - unset($diff3); - - $diff = new diff($diff_1, $diff_2); - } - - $nlead = $this->_leading_context_lines; - $ntrail = $this->_trailing_context_lines; - - $output = $this->_start_diff(); - $diffs = $diff->get_diff(); - - foreach ($diffs as $i => $edit) - { - // If these are unchanged (copied) lines, and we want to keep leading or trailing context lines, extract them from the copy block. - if (is_a($edit, 'diff_op_copy')) - { - // Do we have any diff blocks yet? - if (is_array($block)) - { - // How many lines to keep as context from the copy block. - $keep = ($i == sizeof($diffs) - 1) ? $ntrail : $nlead + $ntrail; - if (sizeof($edit->orig) <= $keep) - { - // We have less lines in the block than we want for context => keep the whole block. - $block[] = $edit; - } - else - { - if ($ntrail) - { - // Create a new block with as many lines as we need for the trailing context. - $context = array_slice($edit->orig, 0, $ntrail); - $block[] = new diff_op_copy($context); - } - - $output .= $this->_block($x0, $ntrail + $xi - $x0, $y0, $ntrail + $yi - $y0, $block); - $block = false; - } - } - // Keep the copy block as the context for the next block. - $context = $edit->orig; - } - else - { - // Don't we have any diff blocks yet? - if (!is_array($block)) - { - // Extract context lines from the preceding copy block. - $context = array_slice($context, sizeof($context) - $nlead); - $x0 = $xi - sizeof($context); - $y0 = $yi - sizeof($context); - $block = array(); - - if ($context) - { - $block[] = new diff_op_copy($context); - } - } - $block[] = $edit; - } - - $xi += ($edit->orig) ? sizeof($edit->orig) : 0; - $yi += ($edit->final) ? sizeof($edit->final) : 0; - } - - if (is_array($block)) - { - $output .= $this->_block($x0, $xi - $x0, $y0, $yi - $y0, $block); - } - - return $output . $this->_end_diff(); - } - - function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) - { - $output = $this->_start_block($this->_block_header($xbeg, $xlen, $ybeg, $ylen)); - - foreach ($edits as $edit) - { - switch (get_class($edit)) - { - case 'diff_op_copy': - $output .= $this->_context($edit->orig); - break; - - case 'diff_op_add': - $output .= $this->_added($edit->final); - break; - - case 'diff_op_delete': - $output .= $this->_deleted($edit->orig); - break; - - case 'diff_op_change': - $output .= $this->_changed($edit->orig, $edit->final); - break; - } - } - - return $output . $this->_end_block(); - } - - function _start_diff() - { - return ''; - } - - function _end_diff() - { - return ''; - } - - function _block_header($xbeg, $xlen, $ybeg, $ylen) - { - if ($xlen > 1) - { - $xbeg .= ',' . ($xbeg + $xlen - 1); - } - - if ($ylen > 1) - { - $ybeg .= ',' . ($ybeg + $ylen - 1); - } - - // this matches the GNU Diff behaviour - if ($xlen && !$ylen) - { - $ybeg--; - } - else if (!$xlen) - { - $xbeg--; - } - - return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; - } - - function _start_block($header) - { - return $header . "\n"; - } - - function _end_block() - { - return ''; - } - - function _lines($lines, $prefix = ' ') - { - return $prefix . implode("\n$prefix", $lines) . "\n"; - } - - function _context($lines) - { - return $this->_lines($lines, ' '); - } - - function _added($lines) - { - return $this->_lines($lines, '> '); - } - - function _deleted($lines) - { - return $this->_lines($lines, '< '); - } - - function _changed($orig, $final) - { - return $this->_deleted($orig) . "---\n" . $this->_added($final); - } - - /** - * Our function to get the diff - */ - function get_diff_content($diff) - { - return $this->render($diff); - } -} - -/** -* Renders a unified diff -* @package diff -*/ -class diff_renderer_unified extends diff_renderer -{ - var $_leading_context_lines = 4; - var $_trailing_context_lines = 4; - - /** - * Our function to get the diff - */ - function get_diff_content($diff) - { - return nl2br($this->render($diff)); - } - - function _block_header($xbeg, $xlen, $ybeg, $ylen) - { - if ($xlen != 1) - { - $xbeg .= ',' . $xlen; - } - - if ($ylen != 1) - { - $ybeg .= ',' . $ylen; - } - return '
@@ -' . $xbeg . ' +' . $ybeg . ' @@
'; - } - - function _context($lines) - { - return '
' . htmlspecialchars($this->_lines($lines, ' ')) . '
'; - } - - function _added($lines) - { - return '
' . htmlspecialchars($this->_lines($lines, '+')) . '
'; - } - - function _deleted($lines) - { - return '
' . htmlspecialchars($this->_lines($lines, '-')) . '
'; - } - - function _changed($orig, $final) - { - return $this->_deleted($orig) . $this->_added($final); - } - - function _start_diff() - { - $start = '
'; - - return $start; - } - - function _end_diff() - { - return '
'; - } - - function _end_block() - { - return ''; - } -} - -/** -* "Inline" diff renderer. -* -* This class renders diffs in the Wiki-style "inline" format. -* -* @author Ciprian Popovici -* @package diff -*/ -class diff_renderer_inline extends diff_renderer -{ - var $_leading_context_lines = 10000; - var $_trailing_context_lines = 10000; - - // Prefix and suffix for inserted text - var $_ins_prefix = ''; - var $_ins_suffix = ''; - - // Prefix and suffix for deleted text - var $_del_prefix = ''; - var $_del_suffix = ''; - - var $_block_head = ''; - - // What are we currently splitting on? Used to recurse to show word-level - var $_split_level = 'lines'; - - /** - * Our function to get the diff - */ - function get_diff_content($diff) - { - return '
' . nl2br($this->render($diff)) . '
'; - } - - function _start_diff() - { - return ''; - } - - function _end_diff() - { - return ''; - } - - function _block_header($xbeg, $xlen, $ybeg, $ylen) - { - return $this->_block_head; - } - - function _start_block($header) - { - return $header; - } - - function _lines($lines, $prefix = ' ', $encode = true) - { - if ($encode) - { - array_walk($lines, array(&$this, '_encode')); - } - - if ($this->_split_level == 'words') - { - return implode('', $lines); - } - else - { - return implode("\n", $lines) . "\n"; - } - } - - function _added($lines) - { - array_walk($lines, array(&$this, '_encode')); - $lines[0] = $this->_ins_prefix . $lines[0]; - $lines[sizeof($lines) - 1] .= $this->_ins_suffix; - return $this->_lines($lines, ' ', false); - } - - function _deleted($lines, $words = false) - { - array_walk($lines, array(&$this, '_encode')); - $lines[0] = $this->_del_prefix . $lines[0]; - $lines[sizeof($lines) - 1] .= $this->_del_suffix; - return $this->_lines($lines, ' ', false); - } - - function _changed($orig, $final) - { - // If we've already split on words, don't try to do so again - just display. - if ($this->_split_level == 'words') - { - $prefix = ''; - while ($orig[0] !== false && $final[0] !== false && substr($orig[0], 0, 1) == ' ' && substr($final[0], 0, 1) == ' ') - { - $prefix .= substr($orig[0], 0, 1); - $orig[0] = substr($orig[0], 1); - $final[0] = substr($final[0], 1); - } - - return $prefix . $this->_deleted($orig) . $this->_added($final); - } - - $text1 = implode("\n", $orig); - $text2 = implode("\n", $final); - - // Non-printing newline marker. - $nl = "\0"; - - // We want to split on word boundaries, but we need to preserve whitespace as well. - // Therefore we split on words, but include all blocks of whitespace in the wordlist. - $splitted_text_1 = $this->_split_on_words($text1, $nl); - $splitted_text_2 = $this->_split_on_words($text2, $nl); - - $diff = new diff($splitted_text_1, $splitted_text_2); - unset($splitted_text_1, $splitted_text_2); - - // Get the diff in inline format. - $renderer = new diff_renderer_inline(array_merge($this->get_params(), array('split_level' => 'words'))); - - // Run the diff and get the output. - return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; - } - - function _split_on_words($string, $newline_escape = "\n") - { - // Ignore \0; otherwise the while loop will never finish. - $string = str_replace("\0", '', $string); - - $words = array(); - $length = strlen($string); - $pos = 0; - - $tab_there = true; - while ($pos < $length) - { - // Check for tabs... do not include them - if ($tab_there && substr($string, $pos, 1) === "\t") - { - $words[] = "\t"; - $pos++; - - continue; - } - else - { - $tab_there = false; - } - - // Eat a word with any preceding whitespace. - $spaces = strspn(substr($string, $pos), " \n"); - $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); - $words[] = str_replace("\n", $newline_escape, substr($string, $pos, $spaces + $nextpos)); - $pos += $spaces + $nextpos; - } - - return $words; - } - - function _encode(&$string) - { - $string = htmlspecialchars($string); - } -} - -/** -* "raw" diff renderer. -* This class could be used to output a raw unified patch file -* -* @package diff -*/ -class diff_renderer_raw extends diff_renderer -{ - var $_leading_context_lines = 4; - var $_trailing_context_lines = 4; - - /** - * Our function to get the diff - */ - function get_diff_content($diff) - { - return ''; - } - - function _block_header($xbeg, $xlen, $ybeg, $ylen) - { - if ($xlen != 1) - { - $xbeg .= ',' . $xlen; - } - - if ($ylen != 1) - { - $ybeg .= ',' . $ylen; - } - return '@@ -' . $xbeg . ' +' . $ybeg . ' @@'; - } - - function _context($lines) - { - return $this->_lines($lines, ' '); - } - - function _added($lines) - { - return $this->_lines($lines, '+'); - } - - function _deleted($lines) - { - return $this->_lines($lines, '-'); - } - - function _changed($orig, $final) - { - return $this->_deleted($orig) . $this->_added($final); - } -} - -/** -* "chora (Horde)" diff renderer - similar style. -* This renderer class is a modified human_readable function from the Horde Framework. -* -* @package diff -*/ -class diff_renderer_side_by_side extends diff_renderer -{ - var $_leading_context_lines = 3; - var $_trailing_context_lines = 3; - - var $lines = array(); - - // Hold the left and right columns of lines for change blocks. - var $cols; - var $state; - - var $data = false; - - /** - * Our function to get the diff - */ - function get_diff_content($diff) - { - $output = ''; - $output .= '
-   ' . $user->lang['LINE_UNMODIFIED'] . ' -   ' . $user->lang['LINE_ADDED'] . ' -   ' . $user->lang['LINE_MODIFIED'] . ' -   ' . $user->lang['LINE_REMOVED'] . ' +   ' . phpbb::$user->lang['LINE_UNMODIFIED'] . ' +   ' . phpbb::$user->lang['LINE_ADDED'] . ' +   ' . phpbb::$user->lang['LINE_MODIFIED'] . ' +   ' . phpbb::$user->lang['LINE_REMOVED'] . '
' . $user->lang['NO_VISIBLE_CHANGES'] . '
' . phpbb::$user->lang['NO_VISIBLE_CHANGES'] . '
' . $user->lang['LINE'] . ' ' . $header['oldline'] . '' . $user->lang['LINE'] . ' ' . $header['newline'] . '
' . phpbb::$user->lang['LINE'] . ' ' . $header['oldline'] . '' . phpbb::$user->lang['LINE'] . ' ' . $header['newline'] . '
- - -'; - - $this->render($diff); - - // Is the diff empty? - if (!sizeof($this->lines)) - { - $output .= ''; - } - else - { - // Iterate through every header block of changes - foreach ($this->lines as $header) - { - $output .= ''; - - // Each header block consists of a number of changes (add, remove, change). - $current_context = ''; - - foreach ($header['contents'] as $change) - { - if (!empty($current_context) && $change['type'] != 'empty') - { - $line = $current_context; - $current_context = ''; - - $output .= ' - '; - } - - switch ($change['type']) - { - case 'add': - $line = ''; - - foreach ($change['lines'] as $_line) - { - $line .= htmlspecialchars($_line) . '
'; - } - - $output .= ''; - break; - - case 'remove': - $line = ''; - - foreach ($change['lines'] as $_line) - { - $line .= htmlspecialchars($_line) . '
'; - } - - $output .= ''; - break; - - case 'empty': - $current_context .= htmlspecialchars($change['line']) . '
'; - break; - - case 'change': - // Pop the old/new stacks one by one, until both are empty. - $oldsize = sizeof($change['old']); - $newsize = sizeof($change['new']); - $left = $right = ''; - - for ($row = 0, $row_max = max($oldsize, $newsize); $row < $row_max; ++$row) - { - $left .= isset($change['old'][$row]) ? htmlspecialchars($change['old'][$row]) : ''; - $left .= '
'; - $right .= isset($change['new'][$row]) ? htmlspecialchars($change['new'][$row]) : ''; - $right .= '
'; - } - - $output .= ''; - - if (!empty($left)) - { - $output .= ''; - } - else if ($row < $oldsize) - { - $output .= ''; - } - else - { - $output .= ''; - } - - if (!empty($right)) - { - $output .= ''; - } - else if ($row < $newsize) - { - $output .= ''; - } - else - { - $output .= ''; - } - - $output .= ''; - break; - } - } - - if (!empty($current_context)) - { - $line = $current_context; - $current_context = ''; - - $output .= ''; - $output .= ''; - } - } - } - - $output .= '
-   ' . phpbb::$user->lang['LINE_UNMODIFIED'] . ' -   ' . phpbb::$user->lang['LINE_ADDED'] . ' -   ' . phpbb::$user->lang['LINE_MODIFIED'] . ' -   ' . phpbb::$user->lang['LINE_REMOVED'] . ' -
' . phpbb::$user->lang['NO_VISIBLE_CHANGES'] . '
' . phpbb::$user->lang['LINE'] . ' ' . $header['oldline'] . '' . phpbb::$user->lang['LINE'] . ' ' . $header['newline'] . '
' . ((strlen($line)) ? $line : ' ') . '
' . ((strlen($line)) ? $line : ' ') . '
 
' . ((strlen($line)) ? $line : ' ') . '
' . ((strlen($line)) ? $line : ' ') . '
 
' . $left . '
  
' . $right . '
  
' . ((strlen($line)) ? $line : ' ') . '
' . ((strlen($line)) ? $line : ' ') . '
'; - - return $output; - } - - function _start_diff() - { - $this->lines = array(); - - $this->data = false; - $this->cols = array(array(), array()); - $this->state = 'empty'; - - return ''; - } - - function _end_diff() - { - // Just flush any remaining entries in the columns stack. - switch ($this->state) - { - case 'add': - $this->data['contents'][] = array('type' => 'add', 'lines' => $this->cols[0]); - break; - - case 'remove': - // We have some removal lines pending in our stack, so flush them. - $this->data['contents'][] = array('type' => 'remove', 'lines' => $this->cols[0]); - break; - - case 'change': - // We have both remove and addition lines, so this is a change block. - $this->data['contents'][] = array('type' => 'change', 'old' => $this->cols[0], 'new' => $this->cols[1]); - break; - } - - if ($this->data !== false) - { - $this->lines[] = $this->data; - } - - return ''; - } - - function _block_header($xbeg, $xlen, $ybeg, $ylen) - { - // Push any previous header information to the return stack. - if ($this->data !== false) - { - $this->lines[] = $this->data; - } - - $this->data = array('type' => 'header', 'oldline' => $xbeg, 'newline' => $ybeg, 'contents' => array()); - $this->state = 'dump'; - } - - function _added($lines) - { - array_walk($lines, array(&$this, '_perform_add')); - } - - function _perform_add($line) - { - if ($this->state == 'empty') - { - return ''; - } - - // This is just an addition line. - if ($this->state == 'dump' || $this->state == 'add') - { - // Start adding to the addition stack. - $this->cols[0][] = $line; - $this->state = 'add'; - } - else - { - // This is inside a change block, so start accumulating lines. - $this->state = 'change'; - $this->cols[1][] = $line; - } - } - - function _deleted($lines) - { - array_walk($lines, array(&$this, '_perform_delete')); - } - - function _perform_delete($line) - { - // This is a removal line. - $this->state = 'remove'; - $this->cols[0][] = $line; - } - - function _context($lines) - { - array_walk($lines, array(&$this, '_perform_context')); - } - - function _perform_context($line) - { - // An empty block with no action. - switch ($this->state) - { - case 'add': - $this->data['contents'][] = array('type' => 'add', 'lines' => $this->cols[0]); - break; - - case 'remove': - // We have some removal lines pending in our stack, so flush them. - $this->data['contents'][] = array('type' => 'remove', 'lines' => $this->cols[0]); - break; - - case 'change': - // We have both remove and addition lines, so this is a change block. - $this->data['contents'][] = array('type' => 'change', 'old' => $this->cols[0], 'new' => $this->cols[1]); - break; - } - - $this->cols = array(array(), array()); - $this->data['contents'][] = array('type' => 'empty', 'line' => $line); - $this->state = 'dump'; - } - - function _changed($orig, $final) - { - return $this->_deleted($orig) . $this->_added($final); - } - -} - -?> \ No newline at end of file -- cgit v1.2.1 From 2e17e448deed073f8614bb555a8ef20c57291c2a Mon Sep 17 00:00:00 2001 From: Meik Sievertsen Date: Sun, 4 Oct 2009 18:14:59 +0000 Subject: Copy 3.0.x branch to trunk git-svn-id: file:///svn/phpbb/trunk@10211 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/diff/diff.php | 1148 ++++++++++++++++++++++++++++++++++++++ phpBB/includes/diff/engine.php | 554 ++++++++++++++++++ phpBB/includes/diff/renderer.php | 860 ++++++++++++++++++++++++++++ 3 files changed, 2562 insertions(+) create mode 100644 phpBB/includes/diff/diff.php create mode 100644 phpBB/includes/diff/engine.php create mode 100644 phpBB/includes/diff/renderer.php (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php new file mode 100644 index 0000000000..60af574b78 --- /dev/null +++ b/phpBB/includes/diff/diff.php @@ -0,0 +1,1148 @@ + +* Copyright 2004-2008 The Horde Project (http://www.horde.org/) +* +* @package diff +* @author Geoffrey T. Dairiki +*/ +class diff +{ + /** + * Array of changes. + * @var array + */ + var $_edits; + + /** + * Computes diffs between sequences of strings. + * + * @param array $from_lines An array of strings. Typically these are lines from a file. + * @param array $to_lines An array of strings. + */ + function diff(&$from_content, &$to_content, $preserve_cr = true) + { + $diff_engine = new diff_engine(); + $this->_edits = $diff_engine->diff($from_content, $to_content, $preserve_cr); + } + + /** + * Returns the array of differences. + */ + function get_diff() + { + return $this->_edits; + } + + /** + * returns the number of new (added) lines in a given diff. + * + * @since Text_Diff 1.1.0 + * + * @return integer The number of new lines + */ + function count_added_lines() + { + $count = 0; + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if (is_a($edit, 'diff_op_add') || is_a($edit, 'diff_op_change')) + { + $count += $edit->nfinal(); + } + } + return $count; + } + + /** + * Returns the number of deleted (removed) lines in a given diff. + * + * @since Text_Diff 1.1.0 + * + * @return integer The number of deleted lines + */ + function count_deleted_lines() + { + $count = 0; + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if (is_a($edit, 'diff_op_delete') || is_a($edit, 'diff_op_change')) + { + $count += $edit->norig(); + } + } + return $count; + } + + /** + * Computes a reversed diff. + * + * Example: + * + * $diff = new diff($lines1, $lines2); + * $rev = $diff->reverse(); + * + * + * @return diff A Diff object representing the inverse of the original diff. + * Note that we purposely don't return a reference here, since + * this essentially is a clone() method. + */ + function reverse() + { + if (version_compare(zend_version(), '2', '>')) + { + $rev = clone($this); + } + else + { + $rev = $this; + } + + $rev->_edits = array(); + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + $rev->_edits[] = $edit->reverse(); + } + + return $rev; + } + + /** + * Checks for an empty diff. + * + * @return boolean True if two sequences were identical. + */ + function is_empty() + { + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + // skip diff_op_copy + if (is_a($edit, 'diff_op_copy')) + { + continue; + } + + if (is_a($edit, 'diff_op_delete') || is_a($edit, 'diff_op_add')) + { + $orig = $edit->orig; + $final = $edit->final; + + // We can simplify one case where the array is usually supposed to be empty... + if (sizeof($orig) == 1 && trim($orig[0]) === '') $orig = array(); + if (sizeof($final) == 1 && trim($final[0]) === '') $final = array(); + + if (!$orig && !$final) + { + continue; + } + + return false; + } + + return false; + } + + return true; + } + + /** + * Computes the length of the Longest Common Subsequence (LCS). + * + * This is mostly for diagnostic purposes. + * + * @return integer The length of the LCS. + */ + function lcs() + { + $lcs = 0; + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if (is_a($edit, 'diff_op_copy')) + { + $lcs += sizeof($edit->orig); + } + } + return $lcs; + } + + /** + * Gets the original set of lines. + * + * This reconstructs the $from_lines parameter passed to the constructor. + * + * @return array The original sequence of strings. + */ + function get_original() + { + $lines = array(); + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if ($edit->orig) + { + array_splice($lines, sizeof($lines), 0, $edit->orig); + } + } + return $lines; + } + + /** + * Gets the final set of lines. + * + * This reconstructs the $to_lines parameter passed to the constructor. + * + * @return array The sequence of strings. + */ + function get_final() + { + $lines = array(); + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if ($edit->final) + { + array_splice($lines, sizeof($lines), 0, $edit->final); + } + } + return $lines; + } + + /** + * Removes trailing newlines from a line of text. This is meant to be used with array_walk(). + * + * @param string &$line The line to trim. + * @param integer $key The index of the line in the array. Not used. + */ + function trim_newlines(&$line, $key) + { + $line = str_replace(array("\n", "\r"), '', $line); + } + + /** + * Checks a diff for validity. + * + * This is here only for debugging purposes. + */ + function _check($from_lines, $to_lines) + { + if (serialize($from_lines) != serialize($this->get_original())) + { + trigger_error("[diff] Reconstructed original doesn't match", E_USER_ERROR); + } + + if (serialize($to_lines) != serialize($this->get_final())) + { + trigger_error("[diff] Reconstructed final doesn't match", E_USER_ERROR); + } + + $rev = $this->reverse(); + + if (serialize($to_lines) != serialize($rev->get_original())) + { + trigger_error("[diff] Reversed original doesn't match", E_USER_ERROR); + } + + if (serialize($from_lines) != serialize($rev->get_final())) + { + trigger_error("[diff] Reversed final doesn't match", E_USER_ERROR); + } + + $prevtype = null; + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if ($prevtype == get_class($edit)) + { + trigger_error("[diff] Edit sequence is non-optimal", E_USER_ERROR); + } + $prevtype = get_class($edit); + } + + return true; + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +*/ +class mapped_diff extends diff +{ + /** + * Computes a diff between sequences of strings. + * + * This can be used to compute things like case-insensitve diffs, or diffs + * which ignore changes in white-space. + * + * @param array $from_lines An array of strings. + * @param array $to_lines An array of strings. + * @param array $mapped_from_lines This array should have the same size number of elements as $from_lines. + * The elements in $mapped_from_lines and $mapped_to_lines are what is actually + * compared when computing the diff. + * @param array $mapped_to_lines This array should have the same number of elements as $to_lines. + */ + function mapped_diff(&$from_lines, &$to_lines, &$mapped_from_lines, &$mapped_to_lines) + { + if (sizeof($from_lines) != sizeof($mapped_from_lines) || sizeof($to_lines) != sizeof($mapped_to_lines)) + { + return false; + } + + parent::diff($mapped_from_lines, $mapped_to_lines); + + $xi = $yi = 0; + for ($i = 0; $i < sizeof($this->_edits); $i++) + { + $orig = &$this->_edits[$i]->orig; + if (is_array($orig)) + { + $orig = array_slice($from_lines, $xi, sizeof($orig)); + $xi += sizeof($orig); + } + + $final = &$this->_edits[$i]->final; + if (is_array($final)) + { + $final = array_slice($to_lines, $yi, sizeof($final)); + $yi += sizeof($final); + } + } + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +* +* @access private +*/ +class diff_op +{ + var $orig; + var $final; + + function &reverse() + { + trigger_error('[diff] Abstract method', E_USER_ERROR); + } + + function norig() + { + return ($this->orig) ? sizeof($this->orig) : 0; + } + + function nfinal() + { + return ($this->final) ? sizeof($this->final) : 0; + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +* +* @access private +*/ +class diff_op_copy extends diff_op +{ + function diff_op_copy($orig, $final = false) + { + if (!is_array($final)) + { + $final = $orig; + } + $this->orig = $orig; + $this->final = $final; + } + + function &reverse() + { + $reverse = new diff_op_copy($this->final, $this->orig); + return $reverse; + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +* +* @access private +*/ +class diff_op_delete extends diff_op +{ + function diff_op_delete($lines) + { + $this->orig = $lines; + $this->final = false; + } + + function &reverse() + { + $reverse = new diff_op_add($this->orig); + return $reverse; + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +* +* @access private +*/ +class diff_op_add extends diff_op +{ + function diff_op_add($lines) + { + $this->final = $lines; + $this->orig = false; + } + + function &reverse() + { + $reverse = new diff_op_delete($this->final); + return $reverse; + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +* +* @access private +*/ +class diff_op_change extends diff_op +{ + function diff_op_change($orig, $final) + { + $this->orig = $orig; + $this->final = $final; + } + + function &reverse() + { + $reverse = new diff_op_change($this->final, $this->orig); + return $reverse; + } +} + + +/** +* A class for computing three way diffs. +* +* @package diff +* @author Geoffrey T. Dairiki +*/ +class diff3 extends diff +{ + /** + * Conflict counter. + * @var integer + */ + var $_conflicting_blocks = 0; + + /** + * Computes diff between 3 sequences of strings. + * + * @param array $orig The original lines to use. + * @param array $final1 The first version to compare to. + * @param array $final2 The second version to compare to. + */ + function diff3(&$orig, &$final1, &$final2, $preserve_cr = true) + { + $diff_engine = new diff_engine(); + + $diff_1 = $diff_engine->diff($orig, $final1, $preserve_cr); + $diff_2 = $diff_engine->diff($orig, $final2, $preserve_cr); + + unset($diff_engine); + + $this->_edits = $this->_diff3($diff_1, $diff_2); + } + + /** + * Return number of conflicts + */ + function get_num_conflicts() + { + $conflicts = 0; + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if ($edit->is_conflict()) + { + $conflicts++; + } + } + + return $conflicts; + } + + /** + * Get conflicts content for download. This is generally a merged file, but preserving conflicts and adding explanations to it. + * A user could then go through this file, search for the conflicts and changes the code accordingly. + * + * @param string $label1 the cvs file version/label from the original set of lines + * @param string $label2 the cvs file version/label from the new set of lines + * @param string $label_sep the explanation between label1 and label2 - more of a helper for the user + * + * @return mixed the merged output + */ + function get_conflicts_content($label1 = 'CURRENT_FILE', $label2 = 'NEW_FILE', $label_sep = 'DIFF_SEP_EXPLAIN') + { + global $user; + + $label1 = (!empty($user->lang[$label1])) ? $user->lang[$label1] : $label1; + $label2 = (!empty($user->lang[$label2])) ? $user->lang[$label2] : $label2; + $label_sep = (!empty($user->lang[$label_sep])) ? $user->lang[$label_sep] : $label_sep; + + $lines = array(); + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if ($edit->is_conflict()) + { + // Start conflict label + $label_start = array('<<<<<<< ' . $label1); + $label_mid = array('======= ' . $label_sep); + $label_end = array('>>>>>>> ' . $label2); + + $lines = array_merge($lines, $label_start, $edit->final1, $label_mid, $edit->final2, $label_end); + $this->_conflicting_blocks++; + } + else + { + $lines = array_merge($lines, $edit->merged()); + } + } + + return $lines; + } + + /** + * Return merged output (used by the renderer) + * + * @return mixed the merged output + */ + function merged_output() + { + return $this->get_conflicts_content(); + } + + /** + * Merge the output and use the new file code for conflicts + */ + function merged_new_output() + { + $lines = array(); + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if ($edit->is_conflict()) + { + $lines = array_merge($lines, $edit->final2); + } + else + { + $lines = array_merge($lines, $edit->merged()); + } + } + + return $lines; + } + + /** + * Merge the output and use the original file code for conflicts + */ + function merged_orig_output() + { + $lines = array(); + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if ($edit->is_conflict()) + { + $lines = array_merge($lines, $edit->final1); + } + else + { + $lines = array_merge($lines, $edit->merged()); + } + } + + return $lines; + } + + /** + * Get conflicting block(s) + */ + function get_conflicts() + { + $conflicts = array(); + + for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) + { + $edit = $this->_edits[$i]; + + if ($edit->is_conflict()) + { + $conflicts[] = array($edit->final1, $edit->final2); + } + } + + return $conflicts; + } + + /** + * @access private + */ + function _diff3(&$edits1, &$edits2) + { + $edits = array(); + $bb = new diff3_block_builder(); + + $e1 = current($edits1); + $e2 = current($edits2); + + while ($e1 || $e2) + { + if ($e1 && $e2 && is_a($e1, 'diff_op_copy') && is_a($e2, 'diff_op_copy')) + { + // We have copy blocks from both diffs. This is the (only) time we want to emit a diff3 copy block. + // Flush current diff3 diff block, if any. + if ($edit = $bb->finish()) + { + $edits[] = $edit; + } + + $ncopy = min($e1->norig(), $e2->norig()); + $edits[] = new diff3_op_copy(array_slice($e1->orig, 0, $ncopy)); + + if ($e1->norig() > $ncopy) + { + array_splice($e1->orig, 0, $ncopy); + array_splice($e1->final, 0, $ncopy); + } + else + { + $e1 = next($edits1); + } + + if ($e2->norig() > $ncopy) + { + array_splice($e2->orig, 0, $ncopy); + array_splice($e2->final, 0, $ncopy); + } + else + { + $e2 = next($edits2); + } + } + else + { + if ($e1 && $e2) + { + if ($e1->orig && $e2->orig) + { + $norig = min($e1->norig(), $e2->norig()); + $orig = array_splice($e1->orig, 0, $norig); + array_splice($e2->orig, 0, $norig); + $bb->input($orig); + } + else + { + $norig = 0; + } + + if (is_a($e1, 'diff_op_copy')) + { + $bb->out1(array_splice($e1->final, 0, $norig)); + } + + if (is_a($e2, 'diff_op_copy')) + { + $bb->out2(array_splice($e2->final, 0, $norig)); + } + } + + if ($e1 && ! $e1->orig) + { + $bb->out1($e1->final); + $e1 = next($edits1); + } + + if ($e2 && ! $e2->orig) + { + $bb->out2($e2->final); + $e2 = next($edits2); + } + } + } + + if ($edit = $bb->finish()) + { + $edits[] = $edit; + } + + return $edits; + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +* +* @access private +*/ +class diff3_op +{ + function diff3_op($orig = false, $final1 = false, $final2 = false) + { + $this->orig = $orig ? $orig : array(); + $this->final1 = $final1 ? $final1 : array(); + $this->final2 = $final2 ? $final2 : array(); + } + + function merged() + { + if (!isset($this->_merged)) + { + // Prepare the arrays before we compare them. ;) + $this->solve_prepare(); + + if ($this->final1 === $this->final2) + { + $this->_merged = &$this->final1; + } + else if ($this->final1 === $this->orig) + { + $this->_merged = &$this->final2; + } + else if ($this->final2 === $this->orig) + { + $this->_merged = &$this->final1; + } + else + { + // The following tries to aggressively solve conflicts... + $this->_merged = false; + $this->solve_conflict(); + } + } + + return $this->_merged; + } + + function is_conflict() + { + return ($this->merged() === false) ? true : false; + } + + /** + * Function to prepare the arrays for comparing - we want to skip over newline changes + * @author acydburn + */ + function solve_prepare() + { + // We can simplify one case where the array is usually supposed to be empty... + if (sizeof($this->orig) == 1 && trim($this->orig[0]) === '') $this->orig = array(); + if (sizeof($this->final1) == 1 && trim($this->final1[0]) === '') $this->final1 = array(); + if (sizeof($this->final2) == 1 && trim($this->final2[0]) === '') $this->final2 = array(); + + // Now we only can have the case where the only difference between arrays are newlines, so compare all cases + + // First, some strings we can compare... + $orig = $final1 = $final2 = ''; + + foreach ($this->orig as $null => $line) $orig .= trim($line); + foreach ($this->final1 as $null => $line) $final1 .= trim($line); + foreach ($this->final2 as $null => $line) $final2 .= trim($line); + + // final1 === final2 + if ($final1 === $final2) + { + // We preserve the part which will be used in the merge later + $this->final2 = $this->final1; + } + // final1 === orig + else if ($final1 === $orig) + { + // Here it does not really matter what we choose, but we will use the new code + $this->orig = $this->final1; + } + // final2 === orig + else if ($final2 === $orig) + { + // Here it does not really matter too (final1 will be used), but we will use the new code + $this->orig = $this->final2; + } + } + + /** + * Find code portions from $orig in $final1 and use $final2 as merged instance if provided + * @author acydburn + */ + function _compare_conflict_seq($orig, $final1, $final2 = false) + { + $result = array('merge_found' => false, 'merge' => array()); + + $_orig = &$this->$orig; + $_final1 = &$this->$final1; + + // Ok, we basically search for $orig in $final1 + $compare_seq = sizeof($_orig); + + // Go through the conflict code + for ($i = 0, $j = 0, $size = sizeof($_final1); $i < $size; $i++, $j = $i) + { + $line = $_final1[$i]; + $skip = 0; + + for ($x = 0; $x < $compare_seq; $x++) + { + // Try to skip all matching lines + if (trim($line) === trim($_orig[$x])) + { + $line = (++$j < $size) ? $_final1[$j] : $line; + $skip++; + } + } + + if ($skip === $compare_seq) + { + $result['merge_found'] = true; + + if ($final2 !== false) + { + $result['merge'] = array_merge($result['merge'], $this->$final2); + } + $i += ($skip - 1); + } + else if ($final2 !== false) + { + $result['merge'][] = $line; + } + } + + return $result; + } + + /** + * Tries to solve conflicts aggressively based on typical "assumptions" + * @author acydburn + */ + function solve_conflict() + { + $this->_merged = false; + + // CASE ONE: orig changed into final2, but modified/unknown code in final1. + // IF orig is found "as is" in final1 we replace the code directly in final1 and populate this as final2/merge + if (sizeof($this->orig) && sizeof($this->final2)) + { + $result = $this->_compare_conflict_seq('orig', 'final1', 'final2'); + + if ($result['merge_found']) + { + $this->final2 = $result['merge']; + $this->_merged = &$this->final2; + return; + } + + $result = $this->_compare_conflict_seq('final2', 'final1'); + + if ($result['merge_found']) + { + $this->_merged = &$this->final1; + return; + } + + // Try to solve $Id$ issues. ;) + if (sizeof($this->orig) == 1 && sizeof($this->final1) == 1 && sizeof($this->final2) == 1) + { + $match = '#^' . preg_quote('* @version $Id: ', '#') . '[a-z\._\- ]+[0-9]+ [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9\:Z]+ [a-z0-9_\- ]+\$$#'; + + if (preg_match($match, $this->orig[0]) && preg_match($match, $this->final1[0]) && preg_match($match, $this->final2[0])) + { + $this->_merged = &$this->final2; + return; + } + } + + $second_run = false; + + // Try to solve issues where the only reason why the above did not work is a newline being removed in the final1 code but exist in the orig/final2 code + if (trim($this->orig[0]) === '' && trim($this->final2[0]) === '') + { + unset($this->orig[0], $this->final2[0]); + $this->orig = array_values($this->orig); + $this->final2 = array_values($this->final2); + + $second_run = true; + } + + // The same is true for a line at the end. ;) + if (sizeof($this->orig) && sizeof($this->final2) && sizeof($this->orig) === sizeof($this->final2) && trim($this->orig[sizeof($this->orig)-1]) === '' && trim($this->final2[sizeof($this->final2)-1]) === '') + { + unset($this->orig[sizeof($this->orig)-1], $this->final2[sizeof($this->final2)-1]); + $this->orig = array_values($this->orig); + $this->final2 = array_values($this->final2); + + $second_run = true; + } + + if ($second_run) + { + $result = $this->_compare_conflict_seq('orig', 'final1', 'final2'); + + if ($result['merge_found']) + { + $this->final2 = $result['merge']; + $this->_merged = &$this->final2; + return; + } + + $result = $this->_compare_conflict_seq('final2', 'final1'); + + if ($result['merge_found']) + { + $this->_merged = &$this->final1; + return; + } + } + + return; + } + + // CASE TWO: Added lines from orig to final2 but final1 had added lines too. Just merge them. + if (!sizeof($this->orig) && $this->final1 !== $this->final2 && sizeof($this->final1) && sizeof($this->final2)) + { + $result = $this->_compare_conflict_seq('final2', 'final1'); + + if ($result['merge_found']) + { + $this->final2 = $this->final1; + $this->_merged = &$this->final1; + } + else + { + $result = $this->_compare_conflict_seq('final1', 'final2'); + + if (!$result['merge_found']) + { + $this->final2 = array_merge($this->final1, $this->final2); + $this->_merged = &$this->final2; + } + else + { + $this->final2 = $this->final1; + $this->_merged = &$this->final1; + } + } + + return; + } + + // CASE THREE: Removed lines (orig has the to-remove line(s), but final1 has additional lines which does not need to be removed). Just remove orig from final1 and then use final1 as final2/merge + if (!sizeof($this->final2) && sizeof($this->orig) && sizeof($this->final1) && $this->orig !== $this->final1) + { + $result = $this->_compare_conflict_seq('orig', 'final1'); + + if (!$result['merge_found']) + { + return; + } + + // First of all, try to find the code in orig in final1. ;) + $compare_seq = sizeof($this->orig); + $begin = $end = -1; + $j = 0; + + for ($i = 0, $size = sizeof($this->final1); $i < $size; $i++) + { + $line = $this->final1[$i]; + + if (trim($line) === trim($this->orig[$j])) + { + // Mark begin + if ($begin === -1) + { + $begin = $i; + } + + // End is always $i, the last found line + $end = $i; + + if (isset($this->orig[$j+1])) + { + $j++; + } + } + } + + if ($begin !== -1 && $begin + ($compare_seq - 1) == $end) + { + foreach ($this->final1 as $i => $line) + { + if ($i < $begin || $i > $end) + { + $merged[] = $line; + } + } + + $this->final2 = $merged; + $this->_merged = &$this->final2; + } + + return; + } + + return; + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +* +* @access private +*/ +class diff3_op_copy extends diff3_op +{ + function diff3_op_copy($lines = false) + { + $this->orig = $lines ? $lines : array(); + $this->final1 = &$this->orig; + $this->final2 = &$this->orig; + } + + function merged() + { + return $this->orig; + } + + function is_conflict() + { + return false; + } +} + +/** +* @package diff +* @author Geoffrey T. Dairiki +* +* @access private +*/ +class diff3_block_builder +{ + function diff3_block_builder() + { + $this->_init(); + } + + function input($lines) + { + if ($lines) + { + $this->_append($this->orig, $lines); + } + } + + function out1($lines) + { + if ($lines) + { + $this->_append($this->final1, $lines); + } + } + + function out2($lines) + { + if ($lines) + { + $this->_append($this->final2, $lines); + } + } + + function is_empty() + { + return !$this->orig && !$this->final1 && !$this->final2; + } + + function finish() + { + if ($this->is_empty()) + { + return false; + } + else + { + $edit = new diff3_op($this->orig, $this->final1, $this->final2); + $this->_init(); + return $edit; + } + } + + function _init() + { + $this->orig = $this->final1 = $this->final2 = array(); + } + + function _append(&$array, $lines) + { + array_splice($array, sizeof($array), 0, $lines); + } +} + +?> \ No newline at end of file diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php new file mode 100644 index 0000000000..982149457d --- /dev/null +++ b/phpBB/includes/diff/engine.php @@ -0,0 +1,554 @@ + 2, and some optimizations) are from +* Geoffrey T. Dairiki . The original PHP version of this +* code was written by him, and is used/adapted with his permission. +* +* Copyright 2004-2008 The Horde Project (http://www.horde.org/) +* +* @author Geoffrey T. Dairiki +* @package diff +* +* @access private +*/ +class diff_engine +{ + /** + * If set to true we trim all lines before we compare them. This ensures that sole space/tab changes do not trigger diffs. + */ + var $skip_whitespace_changes = true; + + function diff(&$from_lines, &$to_lines, $preserve_cr = true) + { + // Remove empty lines... + // If preserve_cr is true, we basically only change \r\n and bare \r to \n to get the same carriage returns for both files + // If it is false, we try to only use \n once per line and ommit all empty lines to be able to get a proper data diff + + if (is_array($from_lines)) + { + $from_lines = implode("\n", $from_lines); + } + + if (is_array($to_lines)) + { + $to_lines = implode("\n", $to_lines); + } + + if ($preserve_cr) + { + $from_lines = explode("\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $from_lines))); + $to_lines = explode("\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $to_lines))); + } + else + { + $from_lines = explode("\n", preg_replace('#[\n\r]+#', "\n", $from_lines)); + $to_lines = explode("\n", preg_replace('#[\n\r]+#', "\n", $to_lines)); + } + + $n_from = sizeof($from_lines); + $n_to = sizeof($to_lines); + + $this->xchanged = $this->ychanged = $this->xv = $this->yv = $this->xind = $this->yind = array(); + unset($this->seq, $this->in_seq, $this->lcs); + + // Skip leading common lines. + for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) + { + if (trim($from_lines[$skip]) !== trim($to_lines[$skip])) + { + break; + } + $this->xchanged[$skip] = $this->ychanged[$skip] = false; + } + + // Skip trailing common lines. + $xi = $n_from; + $yi = $n_to; + + for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) + { + if (trim($from_lines[$xi]) !== trim($to_lines[$yi])) + { + break; + } + $this->xchanged[$xi] = $this->ychanged[$yi] = false; + } + + // Ignore lines which do not exist in both files. + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) + { + if ($this->skip_whitespace_changes) $xhash[trim($from_lines[$xi])] = 1; else $xhash[$from_lines[$xi]] = 1; + } + + for ($yi = $skip; $yi < $n_to - $endskip; $yi++) + { + $line = ($this->skip_whitespace_changes) ? trim($to_lines[$yi]) : $to_lines[$yi]; + + if (($this->ychanged[$yi] = empty($xhash[$line]))) + { + continue; + } + $yhash[$line] = 1; + $this->yv[] = $line; + $this->yind[] = $yi; + } + + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) + { + $line = ($this->skip_whitespace_changes) ? trim($from_lines[$xi]) : $from_lines[$xi]; + + if (($this->xchanged[$xi] = empty($yhash[$line]))) + { + continue; + } + $this->xv[] = $line; + $this->xind[] = $xi; + } + + // Find the LCS. + $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv)); + + // Merge edits when possible. + if ($this->skip_whitespace_changes) + { + $from_lines_clean = array_map('trim', $from_lines); + $to_lines_clean = array_map('trim', $to_lines); + + $this->_shift_boundaries($from_lines_clean, $this->xchanged, $this->ychanged); + $this->_shift_boundaries($to_lines_clean, $this->ychanged, $this->xchanged); + + unset($from_lines_clean, $to_lines_clean); + } + else + { + $this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged); + $this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged); + } + + // Compute the edit operations. + $edits = array(); + $xi = $yi = 0; + + while ($xi < $n_from || $yi < $n_to) + { + // Skip matching "snake". + $copy = array(); + + while ($xi < $n_from && $yi < $n_to && !$this->xchanged[$xi] && !$this->ychanged[$yi]) + { + $copy[] = $from_lines[$xi++]; + $yi++; + } + + if ($copy) + { + $edits[] = new diff_op_copy($copy); + } + + // Find deletes & adds. + $delete = array(); + while ($xi < $n_from && $this->xchanged[$xi]) + { + $delete[] = $from_lines[$xi++]; + } + + $add = array(); + while ($yi < $n_to && $this->ychanged[$yi]) + { + $add[] = $to_lines[$yi++]; + } + + if ($delete && $add) + { + $edits[] = new diff_op_change($delete, $add); + } + else if ($delete) + { + $edits[] = new diff_op_delete($delete); + } + else if ($add) + { + $edits[] = new diff_op_add($add); + } + } + + return $edits; + } + + /** + * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF, + * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized segments. + * + * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an array of + * NCHUNKS+1 (X, Y) indexes giving the diving points between sub + * sequences. The first sub-sequence is contained in (X0, X1), (Y0, Y1), + * the second in (X1, X2), (Y1, Y2) and so on. Note that (X0, Y0) == + * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). + * + * This function assumes that the first lines of the specified portions of + * the two files do not match, and likewise that the last lines do not + * match. The caller must trim matching lines from the beginning and end + * of the portions it is going to specify. + */ + function _diag($xoff, $xlim, $yoff, $ylim, $nchunks) + { + $flip = false; + + if ($xlim - $xoff > $ylim - $yoff) + { + // Things seems faster (I'm not sure I understand why) when the shortest sequence is in X. + $flip = true; + list($xoff, $xlim, $yoff, $ylim) = array($yoff, $ylim, $xoff, $xlim); + } + + if ($flip) + { + for ($i = $ylim - 1; $i >= $yoff; $i--) + { + $ymatches[$this->xv[$i]][] = $i; + } + } + else + { + for ($i = $ylim - 1; $i >= $yoff; $i--) + { + $ymatches[$this->yv[$i]][] = $i; + } + } + + $this->lcs = 0; + $this->seq[0]= $yoff - 1; + $this->in_seq = array(); + $ymids[0] = array(); + + $numer = $xlim - $xoff + $nchunks - 1; + $x = $xoff; + + for ($chunk = 0; $chunk < $nchunks; $chunk++) + { + if ($chunk > 0) + { + for ($i = 0; $i <= $this->lcs; $i++) + { + $ymids[$i][$chunk - 1] = $this->seq[$i]; + } + } + + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks); + + for (; $x < $x1; $x++) + { + $line = $flip ? $this->yv[$x] : $this->xv[$x]; + if (empty($ymatches[$line])) + { + continue; + } + $matches = $ymatches[$line]; + + reset($matches); + while (list(, $y) = each($matches)) + { + if (empty($this->in_seq[$y])) + { + $k = $this->_lcs_pos($y); + $ymids[$k] = $ymids[$k - 1]; + break; + } + } + + // no reset() here + while (list(, $y) = each($matches)) + { + if ($y > $this->seq[$k - 1]) + { + // Optimization: this is a common case: next match is just replacing previous match. + $this->in_seq[$this->seq[$k]] = false; + $this->seq[$k] = $y; + $this->in_seq[$y] = 1; + } + else if (empty($this->in_seq[$y])) + { + $k = $this->_lcs_pos($y); + $ymids[$k] = $ymids[$k - 1]; + } + } + } + } + + $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); + $ymid = $ymids[$this->lcs]; + + for ($n = 0; $n < $nchunks - 1; $n++) + { + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); + $y1 = $ymid[$n] + 1; + $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); + } + $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); + + return array($this->lcs, $seps); + } + + function _lcs_pos($ypos) + { + $end = $this->lcs; + + if ($end == 0 || $ypos > $this->seq[$end]) + { + $this->seq[++$this->lcs] = $ypos; + $this->in_seq[$ypos] = 1; + return $this->lcs; + } + + $beg = 1; + while ($beg < $end) + { + $mid = (int)(($beg + $end) / 2); + if ($ypos > $this->seq[$mid]) + { + $beg = $mid + 1; + } + else + { + $end = $mid; + } + } + + $this->in_seq[$this->seq[$end]] = false; + $this->seq[$end] = $ypos; + $this->in_seq[$ypos] = 1; + + return $end; + } + + /** + * Finds LCS of two sequences. + * + * The results are recorded in the vectors $this->{x,y}changed[], by + * storing a 1 in the element for each line that is an insertion or + * deletion (ie. is not in the LCS). + * + * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1. + * + * Note that XLIM, YLIM are exclusive bounds. All line numbers are + * origin-0 and discarded lines are not counted. + */ + function _compareseq($xoff, $xlim, $yoff, $ylim) + { + // Slide down the bottom initial diagonal. + while ($xoff < $xlim && $yoff < $ylim && $this->xv[$xoff] == $this->yv[$yoff]) + { + ++$xoff; + ++$yoff; + } + + // Slide up the top initial diagonal. + while ($xlim > $xoff && $ylim > $yoff && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) + { + --$xlim; + --$ylim; + } + + if ($xoff == $xlim || $yoff == $ylim) + { + $lcs = 0; + } + else + { + // This is ad hoc but seems to work well. + // $nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); + // $nchunks = max(2,min(8,(int)$nchunks)); + $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; + list($lcs, $seps) = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); + } + + if ($lcs == 0) + { + // X and Y sequences have no common subsequence: mark all changed. + while ($yoff < $ylim) + { + $this->ychanged[$this->yind[$yoff++]] = 1; + } + + while ($xoff < $xlim) + { + $this->xchanged[$this->xind[$xoff++]] = 1; + } + } + else + { + // Use the partitions to split this problem into subproblems. + reset($seps); + $pt1 = $seps[0]; + + while ($pt2 = next($seps)) + { + $this->_compareseq($pt1[0], $pt2[0], $pt1[1], $pt2[1]); + $pt1 = $pt2; + } + } + } + + /** + * Adjusts inserts/deletes of identical lines to join changes as much as possible. + * + * We do something when a run of changed lines include a line at one end + * and has an excluded, identical line at the other. We are free to + * choose which identical line is included. 'compareseq' usually chooses + * the one at the beginning, but usually it is cleaner to consider the + * following identical line to be the "change". + * + * This is extracted verbatim from analyze.c (GNU diffutils-2.7). + */ + function _shift_boundaries($lines, &$changed, $other_changed) + { + $i = 0; + $j = 0; + + $len = sizeof($lines); + $other_len = sizeof($other_changed); + + while (1) + { + // Scan forward to find the beginning of another run of + // changes. Also keep track of the corresponding point in the other file. + // + // Throughout this code, $i and $j are adjusted together so that + // the first $i elements of $changed and the first $j elements of + // $other_changed both contain the same number of zeros (unchanged lines). + // + // Furthermore, $j is always kept so that $j == $other_len or $other_changed[$j] == false. + while ($j < $other_len && $other_changed[$j]) + { + $j++; + } + + while ($i < $len && ! $changed[$i]) + { + $i++; + $j++; + + while ($j < $other_len && $other_changed[$j]) + { + $j++; + } + } + + if ($i == $len) + { + break; + } + + $start = $i; + + // Find the end of this run of changes. + while (++$i < $len && $changed[$i]) + { + continue; + } + + do + { + // Record the length of this run of changes, so that we can later determine whether the run has grown. + $runlength = $i - $start; + + // Move the changed region back, so long as the previous unchanged line matches the last changed one. + // This merges with previous changed regions. + while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) + { + $changed[--$start] = 1; + $changed[--$i] = false; + + while ($start > 0 && $changed[$start - 1]) + { + $start--; + } + + while ($other_changed[--$j]) + { + continue; + } + } + + // Set CORRESPONDING to the end of the changed run, at the last point where it corresponds to a changed run in the + // other file. CORRESPONDING == LEN means no such point has been found. + $corresponding = $j < $other_len ? $i : $len; + + // Move the changed region forward, so long as the first changed line matches the following unchanged one. + // This merges with following changed regions. + // Do this second, so that if there are no merges, the changed region is moved forward as far as possible. + while ($i < $len && $lines[$start] == $lines[$i]) + { + $changed[$start++] = false; + $changed[$i++] = 1; + + while ($i < $len && $changed[$i]) + { + $i++; + } + + $j++; + if ($j < $other_len && $other_changed[$j]) + { + $corresponding = $i; + while ($j < $other_len && $other_changed[$j]) + { + $j++; + } + } + } + } + while ($runlength != $i - $start); + + // If possible, move the fully-merged run of changes back to a corresponding run in the other file. + while ($corresponding < $i) + { + $changed[--$start] = 1; + $changed[--$i] = 0; + + while ($other_changed[--$j]) + { + continue; + } + } + } + } +} + +?> \ No newline at end of file diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php new file mode 100644 index 0000000000..5cb1b6ada9 --- /dev/null +++ b/phpBB/includes/diff/renderer.php @@ -0,0 +1,860 @@ + $value) + { + $v = '_' . $param; + if (isset($this->$v)) + { + $this->$v = $value; + } + } + } + + /** + * Get any renderer parameters. + * + * @return array All parameters of this renderer object. + */ + function get_params() + { + $params = array(); + foreach (get_object_vars($this) as $k => $v) + { + if ($k[0] == '_') + { + $params[substr($k, 1)] = $v; + } + } + + return $params; + } + + /** + * Renders a diff. + * + * @param diff &$diff A diff object. + * + * @return string The formatted output. + */ + function render(&$diff) + { + $xi = $yi = 1; + $block = false; + $context = array(); + + // Create a new diff object if it is a 3-way diff + if (is_a($diff, 'diff3')) + { + $diff3 = &$diff; + + $diff_1 = $diff3->get_original(); + $diff_2 = $diff3->merged_output(); + + unset($diff3); + + $diff = new diff($diff_1, $diff_2); + } + + $nlead = $this->_leading_context_lines; + $ntrail = $this->_trailing_context_lines; + + $output = $this->_start_diff(); + $diffs = $diff->get_diff(); + + foreach ($diffs as $i => $edit) + { + // If these are unchanged (copied) lines, and we want to keep leading or trailing context lines, extract them from the copy block. + if (is_a($edit, 'diff_op_copy')) + { + // Do we have any diff blocks yet? + if (is_array($block)) + { + // How many lines to keep as context from the copy block. + $keep = ($i == sizeof($diffs) - 1) ? $ntrail : $nlead + $ntrail; + if (sizeof($edit->orig) <= $keep) + { + // We have less lines in the block than we want for context => keep the whole block. + $block[] = $edit; + } + else + { + if ($ntrail) + { + // Create a new block with as many lines as we need for the trailing context. + $context = array_slice($edit->orig, 0, $ntrail); + $block[] = new diff_op_copy($context); + } + + $output .= $this->_block($x0, $ntrail + $xi - $x0, $y0, $ntrail + $yi - $y0, $block); + $block = false; + } + } + // Keep the copy block as the context for the next block. + $context = $edit->orig; + } + else + { + // Don't we have any diff blocks yet? + if (!is_array($block)) + { + // Extract context lines from the preceding copy block. + $context = array_slice($context, sizeof($context) - $nlead); + $x0 = $xi - sizeof($context); + $y0 = $yi - sizeof($context); + $block = array(); + + if ($context) + { + $block[] = new diff_op_copy($context); + } + } + $block[] = $edit; + } + + $xi += ($edit->orig) ? sizeof($edit->orig) : 0; + $yi += ($edit->final) ? sizeof($edit->final) : 0; + } + + if (is_array($block)) + { + $output .= $this->_block($x0, $xi - $x0, $y0, $yi - $y0, $block); + } + + return $output . $this->_end_diff(); + } + + function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) + { + $output = $this->_start_block($this->_block_header($xbeg, $xlen, $ybeg, $ylen)); + + foreach ($edits as $edit) + { + switch (get_class($edit)) + { + case 'diff_op_copy': + $output .= $this->_context($edit->orig); + break; + + case 'diff_op_add': + $output .= $this->_added($edit->final); + break; + + case 'diff_op_delete': + $output .= $this->_deleted($edit->orig); + break; + + case 'diff_op_change': + $output .= $this->_changed($edit->orig, $edit->final); + break; + } + } + + return $output . $this->_end_block(); + } + + function _start_diff() + { + return ''; + } + + function _end_diff() + { + return ''; + } + + function _block_header($xbeg, $xlen, $ybeg, $ylen) + { + if ($xlen > 1) + { + $xbeg .= ',' . ($xbeg + $xlen - 1); + } + + if ($ylen > 1) + { + $ybeg .= ',' . ($ybeg + $ylen - 1); + } + + // this matches the GNU Diff behaviour + if ($xlen && !$ylen) + { + $ybeg--; + } + else if (!$xlen) + { + $xbeg--; + } + + return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; + } + + function _start_block($header) + { + return $header . "\n"; + } + + function _end_block() + { + return ''; + } + + function _lines($lines, $prefix = ' ') + { + return $prefix . implode("\n$prefix", $lines) . "\n"; + } + + function _context($lines) + { + return $this->_lines($lines, ' '); + } + + function _added($lines) + { + return $this->_lines($lines, '> '); + } + + function _deleted($lines) + { + return $this->_lines($lines, '< '); + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . "---\n" . $this->_added($final); + } + + /** + * Our function to get the diff + */ + function get_diff_content($diff) + { + return $this->render($diff); + } +} + +/** +* Renders a unified diff +* @package diff +*/ +class diff_renderer_unified extends diff_renderer +{ + var $_leading_context_lines = 4; + var $_trailing_context_lines = 4; + + /** + * Our function to get the diff + */ + function get_diff_content($diff) + { + return nl2br($this->render($diff)); + } + + function _block_header($xbeg, $xlen, $ybeg, $ylen) + { + if ($xlen != 1) + { + $xbeg .= ',' . $xlen; + } + + if ($ylen != 1) + { + $ybeg .= ',' . $ylen; + } + return '
@@ -' . $xbeg . ' +' . $ybeg . ' @@
'; + } + + function _context($lines) + { + return '
' . htmlspecialchars($this->_lines($lines, ' ')) . '
'; + } + + function _added($lines) + { + return '
' . htmlspecialchars($this->_lines($lines, '+')) . '
'; + } + + function _deleted($lines) + { + return '
' . htmlspecialchars($this->_lines($lines, '-')) . '
'; + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . $this->_added($final); + } + + function _start_diff() + { + $start = '
'; + + return $start; + } + + function _end_diff() + { + return '
'; + } + + function _end_block() + { + return ''; + } +} + +/** +* "Inline" diff renderer. +* +* This class renders diffs in the Wiki-style "inline" format. +* +* @author Ciprian Popovici +* @package diff +*/ +class diff_renderer_inline extends diff_renderer +{ + var $_leading_context_lines = 10000; + var $_trailing_context_lines = 10000; + + // Prefix and suffix for inserted text + var $_ins_prefix = ''; + var $_ins_suffix = ''; + + // Prefix and suffix for deleted text + var $_del_prefix = ''; + var $_del_suffix = ''; + + var $_block_head = ''; + + // What are we currently splitting on? Used to recurse to show word-level + var $_split_level = 'lines'; + + /** + * Our function to get the diff + */ + function get_diff_content($diff) + { + return '
' . nl2br($this->render($diff)) . '
'; + } + + function _start_diff() + { + return ''; + } + + function _end_diff() + { + return ''; + } + + function _block_header($xbeg, $xlen, $ybeg, $ylen) + { + return $this->_block_head; + } + + function _start_block($header) + { + return $header; + } + + function _lines($lines, $prefix = ' ', $encode = true) + { + if ($encode) + { + array_walk($lines, array(&$this, '_encode')); + } + + if ($this->_split_level == 'words') + { + return implode('', $lines); + } + else + { + return implode("\n", $lines) . "\n"; + } + } + + function _added($lines) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_ins_prefix . $lines[0]; + $lines[sizeof($lines) - 1] .= $this->_ins_suffix; + return $this->_lines($lines, ' ', false); + } + + function _deleted($lines, $words = false) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_del_prefix . $lines[0]; + $lines[sizeof($lines) - 1] .= $this->_del_suffix; + return $this->_lines($lines, ' ', false); + } + + function _changed($orig, $final) + { + // If we've already split on words, don't try to do so again - just display. + if ($this->_split_level == 'words') + { + $prefix = ''; + while ($orig[0] !== false && $final[0] !== false && substr($orig[0], 0, 1) == ' ' && substr($final[0], 0, 1) == ' ') + { + $prefix .= substr($orig[0], 0, 1); + $orig[0] = substr($orig[0], 1); + $final[0] = substr($final[0], 1); + } + + return $prefix . $this->_deleted($orig) . $this->_added($final); + } + + $text1 = implode("\n", $orig); + $text2 = implode("\n", $final); + + // Non-printing newline marker. + $nl = "\0"; + + // We want to split on word boundaries, but we need to preserve whitespace as well. + // Therefore we split on words, but include all blocks of whitespace in the wordlist. + $splitted_text_1 = $this->_split_on_words($text1, $nl); + $splitted_text_2 = $this->_split_on_words($text2, $nl); + + $diff = new diff($splitted_text_1, $splitted_text_2); + unset($splitted_text_1, $splitted_text_2); + + // Get the diff in inline format. + $renderer = new diff_renderer_inline(array_merge($this->get_params(), array('split_level' => 'words'))); + + // Run the diff and get the output. + return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; + } + + function _split_on_words($string, $newline_escape = "\n") + { + // Ignore \0; otherwise the while loop will never finish. + $string = str_replace("\0", '', $string); + + $words = array(); + $length = strlen($string); + $pos = 0; + + $tab_there = true; + while ($pos < $length) + { + // Check for tabs... do not include them + if ($tab_there && substr($string, $pos, 1) === "\t") + { + $words[] = "\t"; + $pos++; + + continue; + } + else + { + $tab_there = false; + } + + // Eat a word with any preceding whitespace. + $spaces = strspn(substr($string, $pos), " \n"); + $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); + $words[] = str_replace("\n", $newline_escape, substr($string, $pos, $spaces + $nextpos)); + $pos += $spaces + $nextpos; + } + + return $words; + } + + function _encode(&$string) + { + $string = htmlspecialchars($string); + } +} + +/** +* "raw" diff renderer. +* This class could be used to output a raw unified patch file +* +* @package diff +*/ +class diff_renderer_raw extends diff_renderer +{ + var $_leading_context_lines = 4; + var $_trailing_context_lines = 4; + + /** + * Our function to get the diff + */ + function get_diff_content($diff) + { + return ''; + } + + function _block_header($xbeg, $xlen, $ybeg, $ylen) + { + if ($xlen != 1) + { + $xbeg .= ',' . $xlen; + } + + if ($ylen != 1) + { + $ybeg .= ',' . $ylen; + } + return '@@ -' . $xbeg . ' +' . $ybeg . ' @@'; + } + + function _context($lines) + { + return $this->_lines($lines, ' '); + } + + function _added($lines) + { + return $this->_lines($lines, '+'); + } + + function _deleted($lines) + { + return $this->_lines($lines, '-'); + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . $this->_added($final); + } +} + +/** +* "chora (Horde)" diff renderer - similar style. +* This renderer class is a modified human_readable function from the Horde Framework. +* +* @package diff +*/ +class diff_renderer_side_by_side extends diff_renderer +{ + var $_leading_context_lines = 3; + var $_trailing_context_lines = 3; + + var $lines = array(); + + // Hold the left and right columns of lines for change blocks. + var $cols; + var $state; + + var $data = false; + + /** + * Our function to get the diff + */ + function get_diff_content($diff) + { + global $user; + + $output = ''; + $output .= ' + + +'; + + $this->render($diff); + + // Is the diff empty? + if (!sizeof($this->lines)) + { + $output .= ''; + } + else + { + // Iterate through every header block of changes + foreach ($this->lines as $header) + { + $output .= ''; + + // Each header block consists of a number of changes (add, remove, change). + $current_context = ''; + + foreach ($header['contents'] as $change) + { + if (!empty($current_context) && $change['type'] != 'empty') + { + $line = $current_context; + $current_context = ''; + + $output .= ' + '; + } + + switch ($change['type']) + { + case 'add': + $line = ''; + + foreach ($change['lines'] as $_line) + { + $line .= htmlspecialchars($_line) . '
'; + } + + $output .= ''; + break; + + case 'remove': + $line = ''; + + foreach ($change['lines'] as $_line) + { + $line .= htmlspecialchars($_line) . '
'; + } + + $output .= ''; + break; + + case 'empty': + $current_context .= htmlspecialchars($change['line']) . '
'; + break; + + case 'change': + // Pop the old/new stacks one by one, until both are empty. + $oldsize = sizeof($change['old']); + $newsize = sizeof($change['new']); + $left = $right = ''; + + for ($row = 0, $row_max = max($oldsize, $newsize); $row < $row_max; ++$row) + { + $left .= isset($change['old'][$row]) ? htmlspecialchars($change['old'][$row]) : ''; + $left .= '
'; + $right .= isset($change['new'][$row]) ? htmlspecialchars($change['new'][$row]) : ''; + $right .= '
'; + } + + $output .= ''; + + if (!empty($left)) + { + $output .= ''; + } + else if ($row < $oldsize) + { + $output .= ''; + } + else + { + $output .= ''; + } + + if (!empty($right)) + { + $output .= ''; + } + else if ($row < $newsize) + { + $output .= ''; + } + else + { + $output .= ''; + } + + $output .= ''; + break; + } + } + + if (!empty($current_context)) + { + $line = $current_context; + $current_context = ''; + + $output .= ''; + $output .= ''; + } + } + } + + $output .= '
+   ' . $user->lang['LINE_UNMODIFIED'] . ' +   ' . $user->lang['LINE_ADDED'] . ' +   ' . $user->lang['LINE_MODIFIED'] . ' +   ' . $user->lang['LINE_REMOVED'] . ' +
' . $user->lang['NO_VISIBLE_CHANGES'] . '
' . $user->lang['LINE'] . ' ' . $header['oldline'] . '' . $user->lang['LINE'] . ' ' . $header['newline'] . '
' . ((strlen($line)) ? $line : ' ') . '
' . ((strlen($line)) ? $line : ' ') . '
 
' . ((strlen($line)) ? $line : ' ') . '
' . ((strlen($line)) ? $line : ' ') . '
 
' . $left . '
  
' . $right . '
  
' . ((strlen($line)) ? $line : ' ') . '
' . ((strlen($line)) ? $line : ' ') . '
'; + + return $output; + } + + function _start_diff() + { + $this->lines = array(); + + $this->data = false; + $this->cols = array(array(), array()); + $this->state = 'empty'; + + return ''; + } + + function _end_diff() + { + // Just flush any remaining entries in the columns stack. + switch ($this->state) + { + case 'add': + $this->data['contents'][] = array('type' => 'add', 'lines' => $this->cols[0]); + break; + + case 'remove': + // We have some removal lines pending in our stack, so flush them. + $this->data['contents'][] = array('type' => 'remove', 'lines' => $this->cols[0]); + break; + + case 'change': + // We have both remove and addition lines, so this is a change block. + $this->data['contents'][] = array('type' => 'change', 'old' => $this->cols[0], 'new' => $this->cols[1]); + break; + } + + if ($this->data !== false) + { + $this->lines[] = $this->data; + } + + return ''; + } + + function _block_header($xbeg, $xlen, $ybeg, $ylen) + { + // Push any previous header information to the return stack. + if ($this->data !== false) + { + $this->lines[] = $this->data; + } + + $this->data = array('type' => 'header', 'oldline' => $xbeg, 'newline' => $ybeg, 'contents' => array()); + $this->state = 'dump'; + } + + function _added($lines) + { + array_walk($lines, array(&$this, '_perform_add')); + } + + function _perform_add($line) + { + if ($this->state == 'empty') + { + return ''; + } + + // This is just an addition line. + if ($this->state == 'dump' || $this->state == 'add') + { + // Start adding to the addition stack. + $this->cols[0][] = $line; + $this->state = 'add'; + } + else + { + // This is inside a change block, so start accumulating lines. + $this->state = 'change'; + $this->cols[1][] = $line; + } + } + + function _deleted($lines) + { + array_walk($lines, array(&$this, '_perform_delete')); + } + + function _perform_delete($line) + { + // This is a removal line. + $this->state = 'remove'; + $this->cols[0][] = $line; + } + + function _context($lines) + { + array_walk($lines, array(&$this, '_perform_context')); + } + + function _perform_context($line) + { + // An empty block with no action. + switch ($this->state) + { + case 'add': + $this->data['contents'][] = array('type' => 'add', 'lines' => $this->cols[0]); + break; + + case 'remove': + // We have some removal lines pending in our stack, so flush them. + $this->data['contents'][] = array('type' => 'remove', 'lines' => $this->cols[0]); + break; + + case 'change': + // We have both remove and addition lines, so this is a change block. + $this->data['contents'][] = array('type' => 'change', 'old' => $this->cols[0], 'new' => $this->cols[1]); + break; + } + + $this->cols = array(array(), array()); + $this->data['contents'][] = array('type' => 'empty', 'line' => $line); + $this->state = 'dump'; + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . $this->_added($final); + } + +} + +?> \ No newline at end of file -- cgit v1.2.1 From af5b9a96409d788733fcb1ff367e0c7fb0583702 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Tue, 9 Nov 2010 08:59:25 +0100 Subject: [ticket/9556] Drop php closing tags, add trailing newline Closing tags converted using Oleg's script. remove-php-end-tags.py -a . Trailing newlines added using the following where $ext is file extension. find . -type f -name "*.$ext" -print | xargs printf "e %s\nw\n" | ed -s; Extensions: php, css, html, js, xml. PHPBB3-9556 --- phpBB/includes/diff/diff.php | 2 -- phpBB/includes/diff/engine.php | 2 -- phpBB/includes/diff/renderer.php | 2 -- 3 files changed, 6 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 60af574b78..3ab546f03c 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -1144,5 +1144,3 @@ class diff3_block_builder array_splice($array, sizeof($array), 0, $lines); } } - -?> \ No newline at end of file diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php index 982149457d..1aa052e86e 100644 --- a/phpBB/includes/diff/engine.php +++ b/phpBB/includes/diff/engine.php @@ -550,5 +550,3 @@ class diff_engine } } } - -?> \ No newline at end of file diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index 5cb1b6ada9..957491365b 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -856,5 +856,3 @@ class diff_renderer_side_by_side extends diff_renderer } } - -?> \ No newline at end of file -- cgit v1.2.1 From 7a04c9048c110f0bd21ea3e9e869e17b408d640e Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 31 Dec 2011 13:32:52 +0000 Subject: [ticket/9916] Updating header license and removing Version $Id$ PHPBB3-9916 --- phpBB/includes/diff/diff.php | 3 +-- phpBB/includes/diff/engine.php | 3 +-- phpBB/includes/diff/renderer.php | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 3ab546f03c..1ffa3429b5 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -2,9 +2,8 @@ /** * * @package diff -* @version $Id$ * @copyright (c) 2006 phpBB Group -* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 * */ diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php index 1aa052e86e..dc1ca5875f 100644 --- a/phpBB/includes/diff/engine.php +++ b/phpBB/includes/diff/engine.php @@ -2,9 +2,8 @@ /** * * @package diff -* @version $Id$ * @copyright (c) 2006 phpBB Group -* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 * */ diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index 957491365b..eacd42d760 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -2,9 +2,8 @@ /** * * @package diff -* @version $Id$ * @copyright (c) 2006 phpBB Group -* @license http://opensource.org/licenses/gpl-license.php GNU Public License +* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 * */ -- cgit v1.2.1 From a759704b39fc1c1353f865a633759b1369589b67 Mon Sep 17 00:00:00 2001 From: Yuriy Rusko Date: Tue, 27 May 2014 20:18:06 +0200 Subject: [ticket/12594] Remove @package tags and update file headers PHPBB3-12594 --- phpBB/includes/diff/diff.php | 10 +++++++--- phpBB/includes/diff/engine.php | 10 +++++++--- phpBB/includes/diff/renderer.php | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 1ffa3429b5..3260a0cb00 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -1,9 +1,13 @@ +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. * */ diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php index dc1ca5875f..d4ce4147a3 100644 --- a/phpBB/includes/diff/engine.php +++ b/phpBB/includes/diff/engine.php @@ -1,9 +1,13 @@ +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. * */ diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index eacd42d760..a3ece15c3a 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -1,9 +1,13 @@ +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. * */ -- cgit v1.2.1 From 55e1f02151adbe9d66a2d87c00c4f1959739aa36 Mon Sep 17 00:00:00 2001 From: Yuriy Rusko Date: Wed, 28 May 2014 01:05:46 +0200 Subject: [ticket/12594] Replace phpBB Group with phpBB Limited PHPBB3-12594 --- phpBB/includes/diff/diff.php | 2 +- phpBB/includes/diff/engine.php | 2 +- phpBB/includes/diff/renderer.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 3260a0cb00..dd0fbcee02 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -23,7 +23,7 @@ if (!defined('IN_PHPBB')) * Code from pear.php.net, Text_Diff-1.1.0 package * http://pear.php.net/package/Text_Diff/ * -* Modified by phpBB Group to meet our coding standards +* Modified by phpBB Limited to meet our coding standards * and being able to integrate into phpBB * * General API for generating and formatting diffs - the differences between diff --git a/phpBB/includes/diff/engine.php b/phpBB/includes/diff/engine.php index d4ce4147a3..bc21b3b9ba 100644 --- a/phpBB/includes/diff/engine.php +++ b/phpBB/includes/diff/engine.php @@ -23,7 +23,7 @@ if (!defined('IN_PHPBB')) * Code from pear.php.net, Text_Diff-1.1.0 package * http://pear.php.net/package/Text_Diff/ (native engine) * -* Modified by phpBB Group to meet our coding standards +* Modified by phpBB Limited to meet our coding standards * and being able to integrate into phpBB * * Class used internally by Text_Diff to actually compute the diffs. This diff --git a/phpBB/includes/diff/renderer.php b/phpBB/includes/diff/renderer.php index a3ece15c3a..6b7f07cf9c 100644 --- a/phpBB/includes/diff/renderer.php +++ b/phpBB/includes/diff/renderer.php @@ -23,7 +23,7 @@ if (!defined('IN_PHPBB')) * Code from pear.php.net, Text_Diff-1.1.0 package * http://pear.php.net/package/Text_Diff/ * -* Modified by phpBB Group to meet our coding standards +* Modified by phpBB Limited to meet our coding standards * and being able to integrate into phpBB * * A class to render Diffs in different formats. -- cgit v1.2.1 From 19b9df7e630031e3bdf0640a91f7025da3a00257 Mon Sep 17 00:00:00 2001 From: n-aleha Date: Sat, 17 May 2014 03:43:34 +0300 Subject: [ticket/12557] Fix doc block errors found by Sami PHPBB3-12557 --- phpBB/includes/diff/diff.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index dd0fbcee02..d4bd60c988 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -46,8 +46,9 @@ class diff /** * Computes diffs between sequences of strings. * - * @param array $from_lines An array of strings. Typically these are lines from a file. - * @param array $to_lines An array of strings. + * @param array $from_lines An array of strings. Typically these are lines from a file. + * @param array $to_lines An array of strings. + * @param bool $preserve_cr If true, \r is replaced by a new line in the diff output */ function diff(&$from_content, &$to_content, $preserve_cr = true) { @@ -491,9 +492,10 @@ class diff3 extends diff /** * Computes diff between 3 sequences of strings. * - * @param array $orig The original lines to use. - * @param array $final1 The first version to compare to. - * @param array $final2 The second version to compare to. + * @param array $orig The original lines to use. + * @param array $final1 The first version to compare to. + * @param array $final2 The second version to compare to. + * @param bool $preserve_cr If true, \r is replaced by a new line in the diff output */ function diff3(&$orig, &$final1, &$final2, $preserve_cr = true) { -- cgit v1.2.1 From 8ac8c9ec616d8a53cdf02459ae08b2b44e7fd4cb Mon Sep 17 00:00:00 2001 From: n-aleha Date: Mon, 19 May 2014 05:50:29 +0300 Subject: [ticket/12557] Fix doc block errors found by Sami More corrections. PHPBB3-12557 --- phpBB/includes/diff/diff.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index d4bd60c988..17fa726352 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -46,8 +46,8 @@ class diff /** * Computes diffs between sequences of strings. * - * @param array $from_lines An array of strings. Typically these are lines from a file. - * @param array $to_lines An array of strings. + * @param array $from_content An array of strings. Typically these are lines from a file. + * @param array $to_content An array of strings. * @param bool $preserve_cr If true, \r is replaced by a new line in the diff output */ function diff(&$from_content, &$to_content, $preserve_cr = true) -- cgit v1.2.1 From 6d7689f581da281587a999e82007dda283dd590b Mon Sep 17 00:00:00 2001 From: n-aleha Date: Wed, 28 May 2014 20:40:44 +0300 Subject: [ticket/12557] Fix var $preserve_cr description PHPBB3-12557 --- phpBB/includes/diff/diff.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index 17fa726352..e16a549ef7 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -495,7 +495,8 @@ class diff3 extends diff * @param array $orig The original lines to use. * @param array $final1 The first version to compare to. * @param array $final2 The second version to compare to. - * @param bool $preserve_cr If true, \r is replaced by a new line in the diff output + * @param bool $preserve_cr If true, \r\n and bare \r are replaced by a new line + * in the diff output */ function diff3(&$orig, &$final1, &$final2, $preserve_cr = true) { -- cgit v1.2.1 From 9d285bdcf51f6e7b7a53e68eb377d4c70c184844 Mon Sep 17 00:00:00 2001 From: n-aleha Date: Thu, 7 Aug 2014 11:39:04 +0300 Subject: [ticket/12557] Fix pass by reference doc blocks PHPBB3-12557 --- phpBB/includes/diff/diff.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'phpBB/includes/diff') diff --git a/phpBB/includes/diff/diff.php b/phpBB/includes/diff/diff.php index e16a549ef7..d307880c4b 100644 --- a/phpBB/includes/diff/diff.php +++ b/phpBB/includes/diff/diff.php @@ -46,8 +46,8 @@ class diff /** * Computes diffs between sequences of strings. * - * @param array $from_content An array of strings. Typically these are lines from a file. - * @param array $to_content An array of strings. + * @param array &$from_content An array of strings. Typically these are lines from a file. + * @param array &$to_content An array of strings. * @param bool $preserve_cr If true, \r is replaced by a new line in the diff output */ function diff(&$from_content, &$to_content, $preserve_cr = true) @@ -492,9 +492,9 @@ class diff3 extends diff /** * Computes diff between 3 sequences of strings. * - * @param array $orig The original lines to use. - * @param array $final1 The first version to compare to. - * @param array $final2 The second version to compare to. + * @param array &$orig The original lines to use. + * @param array &$final1 The first version to compare to. + * @param array &$final2 The second version to compare to. * @param bool $preserve_cr If true, \r\n and bare \r are replaced by a new line * in the diff output */ -- cgit v1.2.1