| <?php |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if (!defined('IN_PHPBB')) |
| { |
| exit; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class diff |
| { |
| |
| |
| |
| |
| var $_edits; |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| |
| |
| function get_diff() |
| { |
| return $this->_edits; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| |
| function is_empty() |
| { |
| foreach ($this->_edits as $edit) |
| { |
| if (!is_a($edit, 'diff_op_copy')) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| function lcs() |
| { |
| $lcs = 0; |
| |
| foreach ($this->_edits as $edit) |
| { |
| if (is_a($edit, 'diff_op_copy')) |
| { |
| $lcs += sizeof($edit->orig); |
| } |
| } |
| return $lcs; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| function get_original() |
| { |
| $lines = array(); |
| |
| foreach ($this->_edits as $edit) |
| { |
| if ($edit->orig) |
| { |
| array_splice($lines, sizeof($lines), 0, $edit->orig); |
| } |
| } |
| return $lines; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| function get_final() |
| { |
| $lines = array(); |
| |
| foreach ($this->_edits as $edit) |
| { |
| if ($edit->final) |
| { |
| array_splice($lines, sizeof($lines), 0, $edit->final); |
| } |
| } |
| return $lines; |
| } |
| |
| |
| |
| |
| |
| |
| |
| function trim_newlines(&$line, $key) |
| { |
| $line = str_replace(array("\n", "\r"), '', $line); |
| } |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| class mapped_diff extends diff |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| class diff3 extends diff |
| { |
| |
| |
| |
| |
| var $_conflicting_blocks = 0; |
| |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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) |
| { |
| if ($edit->is_conflict()) |
| { |
| $this->_conflicting_blocks++; |
| } |
| } |
| |
| 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; |
| |
| $lines = array(); |
| |
| foreach ($this->_edits as $edit) |
| { |
| 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++; |
| } |
| else |
| { |
| $lines = array_merge($lines, $edit->merged()); |
| } |
| } |
| |
| return $lines; |
| } |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| function get_conflicts() |
| { |
| $conflicts = array(); |
| |
| foreach ($this->_edits as $edit) |
| { |
| if ($edit->is_conflict()) |
| { |
| $conflicts[] = array($edit->final1, $edit->final2); |
| } |
| } |
| |
| return $conflicts; |
| } |
| |
| |
| |
| |
| 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')) |
| { |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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)) |
| { |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
| } |
| |
| ?> |