Version 3.0.5
This commit is contained in:
1 parent
19b38c7c74
commit
1d4720f3e2
3755 files changed
+508378
-664541
No files matched your search
@@ -0,0 +1,806 @@
|
||||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
||||
|
||||
/**
|
||||
* Converts to and from JSON format.
|
||||
*
|
||||
* JSON (JavaScript Object Notation) is a lightweight data-interchange
|
||||
* format. It is easy for humans to read and write. It is easy for machines
|
||||
* to parse and generate. It is based on a subset of the JavaScript
|
||||
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
|
||||
* This feature can also be found in Python. JSON is a text format that is
|
||||
* completely language independent but uses conventions that are familiar
|
||||
* to programmers of the C-family of languages, including C, C++, C#, Java,
|
||||
* JavaScript, Perl, TCL, and many others. These properties make JSON an
|
||||
* ideal data-interchange language.
|
||||
*
|
||||
* This package provides a simple encoder and decoder for JSON notation. It
|
||||
* is intended for use with client-side Javascript applications that make
|
||||
* use of HTTPRequest to perform server communication functions - data can
|
||||
* be encoded into JSON notation for use in a client-side javascript, or
|
||||
* decoded from incoming Javascript requests. JSON format is native to
|
||||
* Javascript, and can be directly eval()'ed with no further parsing
|
||||
* overhead
|
||||
*
|
||||
* All strings should be in ASCII or UTF-8 format!
|
||||
*
|
||||
* LICENSE: Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met: Redistributions of source code must retain the
|
||||
* above copyright notice, this list of conditions and the following
|
||||
* disclaimer. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* @category
|
||||
* @package Services_JSON
|
||||
* @author Michal Migurski <mike-json@teczno.com>
|
||||
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
|
||||
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
|
||||
* @copyright 2005 Michal Migurski
|
||||
* @version CVS: $Id: JSON.php 268 2009-05-20 13:25:22Z bfarber $
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php
|
||||
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_SLICE', 1);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_IN_STR', 2);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_IN_ARR', 3);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_IN_OBJ', 4);
|
||||
|
||||
/**
|
||||
* Marker constant for Services_JSON::decode(), used to flag stack state
|
||||
*/
|
||||
define('SERVICES_JSON_IN_CMT', 5);
|
||||
|
||||
/**
|
||||
* Behavior switch for Services_JSON::decode()
|
||||
*/
|
||||
define('SERVICES_JSON_LOOSE_TYPE', 16);
|
||||
|
||||
/**
|
||||
* Behavior switch for Services_JSON::decode()
|
||||
*/
|
||||
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
|
||||
|
||||
/**
|
||||
* Converts to and from JSON format.
|
||||
*
|
||||
* Brief example of use:
|
||||
*
|
||||
* <code>
|
||||
* // create a new instance of Services_JSON
|
||||
* $json = new Services_JSON();
|
||||
*
|
||||
* // convert a complexe value to JSON notation, and send it to the browser
|
||||
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
|
||||
* $output = $json->encode($value);
|
||||
*
|
||||
* print($output);
|
||||
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
|
||||
*
|
||||
* // accept incoming POST data, assumed to be in JSON notation
|
||||
* $input = file_get_contents('php://input', 1000000);
|
||||
* $value = $json->decode($input);
|
||||
* </code>
|
||||
*/
|
||||
class Services_JSON
|
||||
{
|
||||
/**
|
||||
* constructs a new JSON instance
|
||||
*
|
||||
* @param int $use object behavior flags; combine with boolean-OR
|
||||
*
|
||||
* possible values:
|
||||
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
|
||||
* "{...}" syntax creates associative arrays
|
||||
* instead of objects in decode().
|
||||
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
|
||||
* Values which can't be encoded (e.g. resources)
|
||||
* appear as NULL instead of throwing errors.
|
||||
* By default, a deeply-nested resource will
|
||||
* bubble up with an error, so all return values
|
||||
* from encode() should be checked with isError()
|
||||
*/
|
||||
function Services_JSON($use = 0)
|
||||
{
|
||||
$this->use = $use;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a string from one UTF-16 char to one UTF-8 char
|
||||
*
|
||||
* Normally should be handled by mb_convert_encoding, but
|
||||
* provides a slower PHP-only method for installations
|
||||
* that lack the multibye string extension.
|
||||
*
|
||||
* @param string $utf16 UTF-16 character
|
||||
* @return string UTF-8 character
|
||||
* @access private
|
||||
*/
|
||||
function utf162utf8($utf16)
|
||||
{
|
||||
// oh please oh please oh please oh please oh please
|
||||
if(function_exists('mb_convert_encoding')) {
|
||||
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
|
||||
}
|
||||
|
||||
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
|
||||
|
||||
switch(true) {
|
||||
case ((0x7F & $bytes) == $bytes):
|
||||
// this case should never be reached, because we are in ASCII range
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0x7F & $bytes);
|
||||
|
||||
case (0x07FF & $bytes) == $bytes:
|
||||
// return a 2-byte UTF-8 character
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0xC0 | (($bytes >> 6) & 0x1F))
|
||||
. chr(0x80 | ($bytes & 0x3F));
|
||||
|
||||
case (0xFFFF & $bytes) == $bytes:
|
||||
// return a 3-byte UTF-8 character
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0xE0 | (($bytes >> 12) & 0x0F))
|
||||
. chr(0x80 | (($bytes >> 6) & 0x3F))
|
||||
. chr(0x80 | ($bytes & 0x3F));
|
||||
}
|
||||
|
||||
// ignoring UTF-32 for now, sorry
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a string from one UTF-8 char to one UTF-16 char
|
||||
*
|
||||
* Normally should be handled by mb_convert_encoding, but
|
||||
* provides a slower PHP-only method for installations
|
||||
* that lack the multibye string extension.
|
||||
*
|
||||
* @param string $utf8 UTF-8 character
|
||||
* @return string UTF-16 character
|
||||
* @access private
|
||||
*/
|
||||
function utf82utf16($utf8)
|
||||
{
|
||||
// oh please oh please oh please oh please oh please
|
||||
if(function_exists('mb_convert_encoding')) {
|
||||
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
|
||||
}
|
||||
|
||||
switch(strlen($utf8)) {
|
||||
case 1:
|
||||
// this case should never be reached, because we are in ASCII range
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return $utf8;
|
||||
|
||||
case 2:
|
||||
// return a UTF-16 character from a 2-byte UTF-8 char
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr(0x07 & (ord($utf8{0}) >> 2))
|
||||
. chr((0xC0 & (ord($utf8{0}) << 6))
|
||||
| (0x3F & ord($utf8{1})));
|
||||
|
||||
case 3:
|
||||
// return a UTF-16 character from a 3-byte UTF-8 char
|
||||
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
return chr((0xF0 & (ord($utf8{0}) << 4))
|
||||
| (0x0F & (ord($utf8{1}) >> 2)))
|
||||
. chr((0xC0 & (ord($utf8{1}) << 6))
|
||||
| (0x7F & ord($utf8{2})));
|
||||
}
|
||||
|
||||
// ignoring UTF-32 for now, sorry
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* encodes an arbitrary variable into JSON format
|
||||
*
|
||||
* @param mixed $var any number, boolean, string, array, or object to be encoded.
|
||||
* see argument 1 to Services_JSON() above for array-parsing behavior.
|
||||
* if var is a strng, note that encode() always expects it
|
||||
* to be in ASCII or UTF-8 format!
|
||||
*
|
||||
* @return mixed JSON string representation of input var or an error if a problem occurs
|
||||
* @access public
|
||||
*/
|
||||
function encode($var)
|
||||
{
|
||||
switch (gettype($var)) {
|
||||
case 'boolean':
|
||||
return $var ? 'true' : 'false';
|
||||
|
||||
case 'NULL':
|
||||
return 'null';
|
||||
|
||||
case 'integer':
|
||||
return (int) $var;
|
||||
|
||||
case 'double':
|
||||
case 'float':
|
||||
return (float) $var;
|
||||
|
||||
case 'string':
|
||||
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
|
||||
$ascii = '';
|
||||
$strlen_var = strlen($var);
|
||||
|
||||
/*
|
||||
* Iterate over every character in the string,
|
||||
* escaping with a slash or encoding to UTF-8 where necessary
|
||||
*/
|
||||
for ($c = 0; $c < $strlen_var; ++$c) {
|
||||
|
||||
$ord_var_c = ord($var{$c});
|
||||
|
||||
switch (true) {
|
||||
case $ord_var_c == 0x08:
|
||||
$ascii .= '\b';
|
||||
break;
|
||||
case $ord_var_c == 0x09:
|
||||
$ascii .= '\t';
|
||||
break;
|
||||
case $ord_var_c == 0x0A:
|
||||
$ascii .= '\n';
|
||||
break;
|
||||
case $ord_var_c == 0x0C:
|
||||
$ascii .= '\f';
|
||||
break;
|
||||
case $ord_var_c == 0x0D:
|
||||
$ascii .= '\r';
|
||||
break;
|
||||
|
||||
case $ord_var_c == 0x22:
|
||||
case $ord_var_c == 0x2F:
|
||||
case $ord_var_c == 0x5C:
|
||||
// double quote, slash, slosh
|
||||
$ascii .= '\\'.$var{$c};
|
||||
break;
|
||||
|
||||
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
|
||||
// characters U-00000000 - U-0000007F (same as ASCII)
|
||||
$ascii .= $var{$c};
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xE0) == 0xC0):
|
||||
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
|
||||
$c += 1;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xF0) == 0xE0):
|
||||
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var{$c + 1}),
|
||||
ord($var{$c + 2}));
|
||||
$c += 2;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xF8) == 0xF0):
|
||||
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var{$c + 1}),
|
||||
ord($var{$c + 2}),
|
||||
ord($var{$c + 3}));
|
||||
$c += 3;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xFC) == 0xF8):
|
||||
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var{$c + 1}),
|
||||
ord($var{$c + 2}),
|
||||
ord($var{$c + 3}),
|
||||
ord($var{$c + 4}));
|
||||
$c += 4;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
|
||||
case (($ord_var_c & 0xFE) == 0xFC):
|
||||
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$char = pack('C*', $ord_var_c,
|
||||
ord($var{$c + 1}),
|
||||
ord($var{$c + 2}),
|
||||
ord($var{$c + 3}),
|
||||
ord($var{$c + 4}),
|
||||
ord($var{$c + 5}));
|
||||
$c += 5;
|
||||
$utf16 = $this->utf82utf16($char);
|
||||
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return '"'.$ascii.'"';
|
||||
|
||||
case 'array':
|
||||
/*
|
||||
* As per JSON spec if any array key is not an integer
|
||||
* we must treat the the whole array as an object. We
|
||||
* also try to catch a sparsely populated associative
|
||||
* array with numeric keys here because some JS engines
|
||||
* will create an array with empty indexes up to
|
||||
* max_index which can cause memory issues and because
|
||||
* the keys, which may be relevant, will be remapped
|
||||
* otherwise.
|
||||
*
|
||||
* As per the ECMA and JSON specification an object may
|
||||
* have any string as a property. Unfortunately due to
|
||||
* a hole in the ECMA specification if the key is a
|
||||
* ECMA reserved word or starts with a digit the
|
||||
* parameter is only accessible using ECMAScript's
|
||||
* bracket notation.
|
||||
*/
|
||||
|
||||
// treat as a JSON object
|
||||
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
|
||||
$properties = array_map(array($this, 'name_value'),
|
||||
array_keys($var),
|
||||
array_values($var));
|
||||
|
||||
foreach($properties as $property) {
|
||||
if(Services_JSON::isError($property)) {
|
||||
return $property;
|
||||
}
|
||||
}
|
||||
|
||||
return '{' . join(',', $properties) . '}';
|
||||
}
|
||||
|
||||
// treat it like a regular array
|
||||
$elements = array_map(array($this, 'encode'), $var);
|
||||
|
||||
foreach($elements as $element) {
|
||||
if(Services_JSON::isError($element)) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return '[' . join(',', $elements) . ']';
|
||||
|
||||
case 'object':
|
||||
$vars = get_object_vars($var);
|
||||
|
||||
$properties = array_map(array($this, 'name_value'),
|
||||
array_keys($vars),
|
||||
array_values($vars));
|
||||
|
||||
foreach($properties as $property) {
|
||||
if(Services_JSON::isError($property)) {
|
||||
return $property;
|
||||
}
|
||||
}
|
||||
|
||||
return '{' . join(',', $properties) . '}';
|
||||
|
||||
default:
|
||||
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
|
||||
? 'null'
|
||||
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* array-walking function for use in generating JSON-formatted name-value pairs
|
||||
*
|
||||
* @param string $name name of key to use
|
||||
* @param mixed $value reference to an array element to be encoded
|
||||
*
|
||||
* @return string JSON-formatted name-value pair, like '"name":value'
|
||||
* @access private
|
||||
*/
|
||||
function name_value($name, $value)
|
||||
{
|
||||
$encoded_value = $this->encode($value);
|
||||
|
||||
if(Services_JSON::isError($encoded_value)) {
|
||||
return $encoded_value;
|
||||
}
|
||||
|
||||
return $this->encode(strval($name)) . ':' . $encoded_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* reduce a string by removing leading and trailing comments and whitespace
|
||||
*
|
||||
* @param $str string string value to strip of comments and whitespace
|
||||
*
|
||||
* @return string string value stripped of comments and whitespace
|
||||
* @access private
|
||||
*/
|
||||
function reduce_string($str)
|
||||
{
|
||||
$str = preg_replace(array(
|
||||
|
||||
// eliminate single line comments in '// ...' form
|
||||
'#^\s*//(.+)$#m',
|
||||
|
||||
// eliminate multi-line comments in '/* ... */' form, at start of string
|
||||
'#^\s*/\*(.+)\*/#Us',
|
||||
|
||||
// eliminate multi-line comments in '/* ... */' form, at end of string
|
||||
'#/\*(.+)\*/\s*$#Us'
|
||||
|
||||
), '', $str);
|
||||
|
||||
// eliminate extraneous space
|
||||
return trim($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* decodes a JSON string into appropriate variable
|
||||
*
|
||||
* @param string $str JSON-formatted string
|
||||
*
|
||||
* @return mixed number, boolean, string, array, or object
|
||||
* corresponding to given JSON input string.
|
||||
* See argument 1 to Services_JSON() above for object-output behavior.
|
||||
* Note that decode() always returns strings
|
||||
* in ASCII or UTF-8 format!
|
||||
* @access public
|
||||
*/
|
||||
function decode($str)
|
||||
{
|
||||
$str = $this->reduce_string($str);
|
||||
|
||||
switch (strtolower($str)) {
|
||||
case 'true':
|
||||
return true;
|
||||
|
||||
case 'false':
|
||||
return false;
|
||||
|
||||
case 'null':
|
||||
return null;
|
||||
|
||||
default:
|
||||
$m = array();
|
||||
|
||||
if (is_numeric($str)) {
|
||||
// Lookie-loo, it's a number
|
||||
|
||||
// This would work on its own, but I'm trying to be
|
||||
// good about returning integers where appropriate:
|
||||
// return (float)$str;
|
||||
|
||||
// Return float or int, as appropriate
|
||||
return ((float)$str == (integer)$str)
|
||||
? (integer)$str
|
||||
: (float)$str;
|
||||
|
||||
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
|
||||
// STRINGS RETURNED IN UTF-8 FORMAT
|
||||
$delim = substr($str, 0, 1);
|
||||
$chrs = substr($str, 1, -1);
|
||||
$utf8 = '';
|
||||
$strlen_chrs = strlen($chrs);
|
||||
|
||||
for ($c = 0; $c < $strlen_chrs; ++$c) {
|
||||
|
||||
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
||||
$ord_chrs_c = ord($chrs{$c});
|
||||
|
||||
switch (true) {
|
||||
case $substr_chrs_c_2 == '\b':
|
||||
$utf8 .= chr(0x08);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\t':
|
||||
$utf8 .= chr(0x09);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\n':
|
||||
$utf8 .= chr(0x0A);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\f':
|
||||
$utf8 .= chr(0x0C);
|
||||
++$c;
|
||||
break;
|
||||
case $substr_chrs_c_2 == '\r':
|
||||
$utf8 .= chr(0x0D);
|
||||
++$c;
|
||||
break;
|
||||
|
||||
case $substr_chrs_c_2 == '\\"':
|
||||
case $substr_chrs_c_2 == '\\\'':
|
||||
case $substr_chrs_c_2 == '\\\\':
|
||||
case $substr_chrs_c_2 == '\\/':
|
||||
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
|
||||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
|
||||
$utf8 .= $chrs{++$c};
|
||||
}
|
||||
break;
|
||||
|
||||
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
|
||||
// single, escaped unicode character
|
||||
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
|
||||
. chr(hexdec(substr($chrs, ($c + 4), 2)));
|
||||
$utf8 .= $this->utf162utf8($utf16);
|
||||
$c += 5;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
|
||||
$utf8 .= $chrs{$c};
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xE0) == 0xC0:
|
||||
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
||||
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 2);
|
||||
++$c;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xF0) == 0xE0:
|
||||
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 3);
|
||||
$c += 2;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xF8) == 0xF0:
|
||||
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 4);
|
||||
$c += 3;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xFC) == 0xF8:
|
||||
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 5);
|
||||
$c += 4;
|
||||
break;
|
||||
|
||||
case ($ord_chrs_c & 0xFE) == 0xFC:
|
||||
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
||||
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
||||
$utf8 .= substr($chrs, $c, 6);
|
||||
$c += 5;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $utf8;
|
||||
|
||||
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
|
||||
// array, or object notation
|
||||
|
||||
if ($str{0} == '[') {
|
||||
$stk = array(SERVICES_JSON_IN_ARR);
|
||||
$arr = array();
|
||||
} else {
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$stk = array(SERVICES_JSON_IN_OBJ);
|
||||
$obj = array();
|
||||
} else {
|
||||
$stk = array(SERVICES_JSON_IN_OBJ);
|
||||
$obj = new stdClass();
|
||||
}
|
||||
}
|
||||
|
||||
array_push($stk, array('what' => SERVICES_JSON_SLICE,
|
||||
'where' => 0,
|
||||
'delim' => false));
|
||||
|
||||
$chrs = substr($str, 1, -1);
|
||||
$chrs = $this->reduce_string($chrs);
|
||||
|
||||
if ($chrs == '') {
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
return $arr;
|
||||
|
||||
} else {
|
||||
return $obj;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//print("\nparsing {$chrs}\n");
|
||||
|
||||
$strlen_chrs = strlen($chrs);
|
||||
|
||||
for ($c = 0; $c <= $strlen_chrs; ++$c) {
|
||||
|
||||
$top = end($stk);
|
||||
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
||||
|
||||
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
|
||||
// found a comma that is not inside a string, array, etc.,
|
||||
// OR we've reached the end of the character list
|
||||
$slice = substr($chrs, $top['where'], ($c - $top['where']));
|
||||
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
|
||||
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
||||
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
// we are in an array, so just push an element onto the stack
|
||||
array_push($arr, $this->decode($slice));
|
||||
|
||||
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
||||
// we are in an object, so figure
|
||||
// out the property name and set an
|
||||
// element in an associative array,
|
||||
// for now
|
||||
$parts = array();
|
||||
|
||||
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
||||
// "name":value pair
|
||||
$key = $this->decode($parts[1]);
|
||||
$val = $this->decode($parts[2]);
|
||||
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$obj[$key] = $val;
|
||||
} else {
|
||||
$obj->$key = $val;
|
||||
}
|
||||
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
||||
// name:value pair, where name is unquoted
|
||||
$key = $parts[1];
|
||||
$val = $this->decode($parts[2]);
|
||||
|
||||
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
||||
$obj[$key] = $val;
|
||||
} else {
|
||||
$obj->$key = $val;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
|
||||
// found a quote, and we are not inside a string
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
|
||||
//print("Found start of string at {$c}\n");
|
||||
|
||||
} elseif (($chrs{$c} == $top['delim']) &&
|
||||
($top['what'] == SERVICES_JSON_IN_STR) &&
|
||||
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
|
||||
// found a quote, we're in a string, and it's not escaped
|
||||
// we know that it's not escaped becase there is _not_ an
|
||||
// odd number of backslashes at the end of the string so far
|
||||
array_pop($stk);
|
||||
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
|
||||
|
||||
} elseif (($chrs{$c} == '[') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a left-bracket, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
|
||||
//print("Found start of array at {$c}\n");
|
||||
|
||||
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
|
||||
// found a right-bracket, and we're in an array
|
||||
array_pop($stk);
|
||||
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
||||
|
||||
} elseif (($chrs{$c} == '{') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a left-brace, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
|
||||
//print("Found start of object at {$c}\n");
|
||||
|
||||
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
|
||||
// found a right-brace, and we're in an object
|
||||
array_pop($stk);
|
||||
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
||||
|
||||
} elseif (($substr_chrs_c_2 == '/*') &&
|
||||
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
||||
// found a comment start, and we are in an array, object, or slice
|
||||
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
|
||||
$c++;
|
||||
//print("Found start of comment at {$c}\n");
|
||||
|
||||
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
|
||||
// found a comment end, and we're in one now
|
||||
array_pop($stk);
|
||||
$c++;
|
||||
|
||||
for ($i = $top['where']; $i <= $c; ++$i)
|
||||
$chrs = substr_replace($chrs, ' ', $i, 1);
|
||||
|
||||
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
||||
return $arr;
|
||||
|
||||
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
||||
return $obj;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Ultimately, this should just call PEAR::isError()
|
||||
*/
|
||||
function isError($data, $code = null)
|
||||
{
|
||||
if (class_exists('pear')) {
|
||||
return PEAR::isError($data, $code);
|
||||
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
|
||||
is_subclass_of($data, 'services_json_error'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (class_exists('PEAR_Error')) {
|
||||
|
||||
class Services_JSON_Error extends PEAR_Error
|
||||
{
|
||||
function Services_JSON_Error($message = 'unknown error', $code = null,
|
||||
$mode = null, $options = null, $userinfo = null)
|
||||
{
|
||||
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/**
|
||||
* @todo Ultimately, this class shall be descended from PEAR_Error
|
||||
*/
|
||||
class Services_JSON_Error
|
||||
{
|
||||
function Services_JSON_Error($message = 'unknown error', $code = null,
|
||||
$mode = null, $options = null, $userinfo = null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1 @@
|
||||
<html>␍<head><title>IP.Board - Bulletin Board System</title></head>␍<body>␍<h1>403: IP.Board -> Forbidden</h1>␍<hr>␍You have reached this page in error, please use your back button to return to the forum.␍<hr>␍<a href="http://www.invisionboard.com/">IP.Board</a>␍</body>␍</html>␍
|
||||
@@ -1,14 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* Text_Diff
|
||||
*
|
||||
* General API for generating and formatting diffs - the differences between
|
||||
* two sequences of strings.
|
||||
*
|
||||
* The PHP diff code used in this package was originally written by Geoffrey
|
||||
* T. Dairiki and is used with his permission.
|
||||
* The original PHP version of this code was written by Geoffrey T. Dairiki
|
||||
* <dairiki@dairiki.org>, and is used/adapted with his permission.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff.php,v 1.10 2004/11/01 11:51:15 mdjukic Exp $
|
||||
* $Horde: framework/Text_Diff/Diff.php,v 1.11.2.11 2008/02/24 10:57:46 jan Exp $
|
||||
*
|
||||
* Copyright 2004 Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* Copyright 2004-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
@@ -18,29 +22,38 @@ class Text_Diff {
|
||||
/**
|
||||
* Array of changes.
|
||||
*
|
||||
* @var array $_edits
|
||||
* @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.
|
||||
* @param string $engine Name of the diffing engine to use. 'auto'
|
||||
* will automatically select the best.
|
||||
* @param array $params Parameters to pass to the diffing engine.
|
||||
* Normally an array of two arrays, each
|
||||
* containing the lines from a file.
|
||||
*/
|
||||
function Text_Diff($from_lines, $to_lines)
|
||||
function Text_Diff($engine, $params)
|
||||
{
|
||||
array_walk($from_lines, array($this, '_trimNewlines'));
|
||||
array_walk($to_lines, array($this, '_trimNewlines'));
|
||||
|
||||
if (extension_loaded('xdiff')) {
|
||||
$engine = &new Text_Diff_Engine_xdiff();
|
||||
} else {
|
||||
$engine = &new Text_Diff_Engine_native();
|
||||
// Backward compatibility workaround.
|
||||
if (!is_string($engine)) {
|
||||
$params = array($engine, $params);
|
||||
$engine = 'auto';
|
||||
}
|
||||
|
||||
$this->_edits = $engine->diff($from_lines, $to_lines);
|
||||
if ($engine == 'auto') {
|
||||
$engine = extension_loaded('xdiff') ? 'xdiff' : 'native';
|
||||
} else {
|
||||
$engine = basename($engine);
|
||||
}
|
||||
|
||||
require_once 'Text/Diff/Engine/' . $engine . '.php';
|
||||
$class = 'Text_Diff_Engine_' . $engine;
|
||||
$diff_engine = new $class();
|
||||
|
||||
$this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,13 +63,53 @@ class Text_Diff {
|
||||
{
|
||||
return $this->_edits;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the number of new (added) lines in a given diff.
|
||||
*
|
||||
* @since Text_Diff 1.1.0
|
||||
* @since Horde 3.2
|
||||
*
|
||||
* @return integer The number of new lines
|
||||
*/
|
||||
function countAddedLines()
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->_edits as $edit) {
|
||||
if (is_a($edit, 'Text_Diff_Op_add') ||
|
||||
is_a($edit, 'Text_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
|
||||
* @since Horde 3.2
|
||||
*
|
||||
* @return integer The number of deleted lines
|
||||
*/
|
||||
function countDeletedLines()
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($this->_edits as $edit) {
|
||||
if (is_a($edit, 'Text_Diff_Op_delete') ||
|
||||
is_a($edit, 'Text_Diff_Op_change')) {
|
||||
$count += $edit->norig();
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a reversed diff.
|
||||
*
|
||||
* Example:
|
||||
* <code>
|
||||
* $diff = &new Text_Diff($lines1, $lines2);
|
||||
* $diff = new Text_Diff($lines1, $lines2);
|
||||
* $rev = $diff->reverse();
|
||||
* </code>
|
||||
*
|
||||
@@ -68,7 +121,7 @@ class Text_Diff {
|
||||
function reverse()
|
||||
{
|
||||
if (version_compare(zend_version(), '2', '>')) {
|
||||
$rev = clone($obj);
|
||||
$rev = clone($this);
|
||||
} else {
|
||||
$rev = $this;
|
||||
}
|
||||
@@ -97,9 +150,9 @@ class Text_Diff {
|
||||
/**
|
||||
* Computes the length of the Longest Common Subsequence (LCS).
|
||||
*
|
||||
* This is mostly for diagnostic purposed.
|
||||
* This is mostly for diagnostic purposes.
|
||||
*
|
||||
* @return int The length of the LCS.
|
||||
* @return integer The length of the LCS.
|
||||
*/
|
||||
function lcs()
|
||||
{
|
||||
@@ -155,11 +208,48 @@ class Text_Diff {
|
||||
* @param string $line The line to trim.
|
||||
* @param integer $key The index of the line in the array. Not used.
|
||||
*/
|
||||
function _trimNewlines(&$line, $key)
|
||||
function trimNewlines(&$line, $key)
|
||||
{
|
||||
$line = str_replace(array("\n", "\r"), '', $line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the location of the system temporary directory.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @return string A directory name which can be used for temp files.
|
||||
* Returns false if one could not be found.
|
||||
*/
|
||||
function _getTempDir()
|
||||
{
|
||||
$tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp',
|
||||
'c:\windows\temp', 'c:\winnt\temp');
|
||||
|
||||
/* Try PHP's upload_tmp_dir directive. */
|
||||
$tmp = ini_get('upload_tmp_dir');
|
||||
|
||||
/* Otherwise, try to determine the TMPDIR environment variable. */
|
||||
if (!strlen($tmp)) {
|
||||
$tmp = getenv('TMPDIR');
|
||||
}
|
||||
|
||||
/* If we still cannot determine a value, then cycle through a list of
|
||||
* preset possibilities. */
|
||||
while (!strlen($tmp) && count($tmp_locations)) {
|
||||
$tmp_check = array_shift($tmp_locations);
|
||||
if (@is_dir($tmp_check)) {
|
||||
$tmp = $tmp_check;
|
||||
}
|
||||
}
|
||||
|
||||
/* If it is still empty, we have failed, so return false; otherwise
|
||||
* return the directory determined. */
|
||||
return strlen($tmp) ? $tmp : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a diff for validity.
|
||||
*
|
||||
@@ -196,8 +286,6 @@ class Text_Diff {
|
||||
}
|
||||
|
||||
/**
|
||||
* $Horde: framework/Text_Diff/Diff.php,v 1.10 2004/11/01 11:51:15 mdjukic Exp $
|
||||
*
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
*/
|
||||
@@ -222,8 +310,8 @@ class Text_MappedDiff extends Text_Diff {
|
||||
function Text_MappedDiff($from_lines, $to_lines,
|
||||
$mapped_from_lines, $mapped_to_lines)
|
||||
{
|
||||
@assert(count($from_lines) == count($mapped_from_lines));
|
||||
@assert(count($to_lines) == count($mapped_to_lines));
|
||||
assert(count($from_lines) == count($mapped_from_lines));
|
||||
assert(count($to_lines) == count($mapped_to_lines));
|
||||
|
||||
parent::Text_Diff($mapped_from_lines, $mapped_to_lines);
|
||||
|
||||
@@ -245,497 +333,18 @@ class Text_MappedDiff extends Text_Diff {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used internally by Diff to actually compute the diffs. This class
|
||||
* uses the xdiff PECL package (http://pecl.php.net/package/xdiff) to compute
|
||||
* the differences between the two input arrays.
|
||||
*
|
||||
* @author Jon Parise <jon@horde.org>
|
||||
* @package Text_Diff
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_Engine_xdiff {
|
||||
|
||||
function diff($from_lines, $to_lines)
|
||||
{
|
||||
/* Convert the two input arrays into strings for xdiff processing. */
|
||||
$from_string = implode("\n", $from_lines);
|
||||
$to_string = implode("\n", $to_lines);
|
||||
|
||||
/* Diff the two strings and convert the result to an array. */
|
||||
$diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
|
||||
$diff = explode("\n", $diff);
|
||||
|
||||
/* Walk through the diff one line at a time. We build the $edits
|
||||
* array of diff operations by reading the first character of the
|
||||
* xdiff output (which is in the "unified diff" format).
|
||||
*
|
||||
* Note that we don't have enough information to detect "changed"
|
||||
* lines using this approach, so we can't add Text_Diff_Op_changed
|
||||
* instances to the $edits array. The result is still perfectly
|
||||
* valid, albeit a little less descriptive and efficient. */
|
||||
$edits = array();
|
||||
foreach ($diff as $line) {
|
||||
switch ($line[0]) {
|
||||
case ' ':
|
||||
$edits[] = &new Text_Diff_Op_copy(array(substr($line, 1)));
|
||||
break;
|
||||
|
||||
case '+':
|
||||
$edits[] = &new Text_Diff_Op_add(array(substr($line, 1)));
|
||||
break;
|
||||
|
||||
case '-':
|
||||
$edits[] = &new Text_Diff_Op_delete(array(substr($line, 1)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $edits;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used internally by 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
|
||||
*
|
||||
* Some ideas (and a bit of code) are from from analyze.c, from GNU
|
||||
* diffutils-2.7, which can be found at:
|
||||
* ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
|
||||
*
|
||||
* Some ideas (subdivision by NCHUNKS > 2, and some optimizations) are from
|
||||
* Geoffrey T. Dairiki <dairiki@dairiki.org>. The original PHP version of this
|
||||
* code was written by him, and is used/adapted with his permission.
|
||||
*
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @package Text_Diff
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_Engine_native {
|
||||
|
||||
function diff($from_lines, $to_lines)
|
||||
{
|
||||
$n_from = count($from_lines);
|
||||
$n_to = count($to_lines);
|
||||
|
||||
$this->xchanged = $this->ychanged = array();
|
||||
$this->xv = $this->yv = array();
|
||||
$this->xind = $this->yind = array();
|
||||
unset($this->seq);
|
||||
unset($this->in_seq);
|
||||
unset($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, count($this->xv), 0, count($this->yv));
|
||||
|
||||
// Merge edits when possible.
|
||||
$this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged);
|
||||
$this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged);
|
||||
|
||||
// Compute the edit operations.
|
||||
$edits = array();
|
||||
$xi = $yi = 0;
|
||||
while ($xi < $n_from || $yi < $n_to) {
|
||||
@assert($yi < $n_to || $this->xchanged[$xi]);
|
||||
@assert($xi < $n_from || $this->ychanged[$yi]);
|
||||
|
||||
// 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 Text_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 Text_Diff_Op_change($delete, $add);
|
||||
} elseif ($delete) {
|
||||
$edits[] = &new Text_Diff_Op_delete($delete);
|
||||
} elseif ($add) {
|
||||
$edits[] = &new Text_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];
|
||||
foreach ($matches as $y) {
|
||||
if (empty($this->in_seq[$y])) {
|
||||
$k = $this->_lcsPos($y);
|
||||
@assert($k > 0);
|
||||
$ymids[$k] = $ymids[$k - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (list($junk, $y) = each($matches)) {
|
||||
if ($y > $this->seq[$k - 1]) {
|
||||
@assert($y < $this->seq[$k]);
|
||||
/* 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;
|
||||
} elseif (empty($this->in_seq[$y])) {
|
||||
$k = $this->_lcsPos($y);
|
||||
@assert($k > 0);
|
||||
$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 _lcsPos($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;
|
||||
}
|
||||
}
|
||||
|
||||
@assert($ypos != $this->seq[$end]);
|
||||
|
||||
$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 _shiftBoundaries($lines, &$changed, $other_changed)
|
||||
{
|
||||
$i = 0;
|
||||
$j = 0;
|
||||
|
||||
@assert('count($lines) == count($changed)');
|
||||
$len = count($lines);
|
||||
$other_len = count($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]) {
|
||||
@assert('$j < $other_len && ! $other_changed[$j]');
|
||||
$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--;
|
||||
}
|
||||
@assert('$j > 0');
|
||||
while ($other_changed[--$j]) {
|
||||
continue;
|
||||
}
|
||||
@assert('$j >= 0 && !$other_changed[$j]');
|
||||
}
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
|
||||
@assert('$j < $other_len && ! $other_changed[$j]');
|
||||
$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;
|
||||
@assert('$j > 0');
|
||||
while ($other_changed[--$j]) {
|
||||
continue;
|
||||
}
|
||||
@assert('$j >= 0 && !$other_changed[$j]');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @access private
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_Op {
|
||||
|
||||
var $orig;
|
||||
var $final;
|
||||
|
||||
function reverse()
|
||||
function &reverse()
|
||||
{
|
||||
trigger_error('Abstract method', E_USER_ERROR);
|
||||
}
|
||||
@@ -755,7 +364,8 @@ class Text_Diff_Op {
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @access private
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_Op_copy extends Text_Diff_Op {
|
||||
|
||||
@@ -770,7 +380,8 @@ class Text_Diff_Op_copy extends Text_Diff_Op {
|
||||
|
||||
function &reverse()
|
||||
{
|
||||
return $reverse = &new Text_Diff_Op_copy($this->final, $this->orig);
|
||||
$reverse = new Text_Diff_Op_copy($this->final, $this->orig);
|
||||
return $reverse;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -778,7 +389,8 @@ class Text_Diff_Op_copy extends Text_Diff_Op {
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @access private
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_Op_delete extends Text_Diff_Op {
|
||||
|
||||
@@ -790,7 +402,8 @@ class Text_Diff_Op_delete extends Text_Diff_Op {
|
||||
|
||||
function &reverse()
|
||||
{
|
||||
return $reverse = &new Text_Diff_Op_add($this->orig);
|
||||
$reverse = new Text_Diff_Op_add($this->orig);
|
||||
return $reverse;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -798,7 +411,8 @@ class Text_Diff_Op_delete extends Text_Diff_Op {
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @access private
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_Op_add extends Text_Diff_Op {
|
||||
|
||||
@@ -810,7 +424,8 @@ class Text_Diff_Op_add extends Text_Diff_Op {
|
||||
|
||||
function &reverse()
|
||||
{
|
||||
return $reverse = &new Text_Diff_Op_delete($this->final);
|
||||
$reverse = new Text_Diff_Op_delete($this->final);
|
||||
return $reverse;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -818,7 +433,8 @@ class Text_Diff_Op_add extends Text_Diff_Op {
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @access private
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_Op_change extends Text_Diff_Op {
|
||||
|
||||
@@ -830,7 +446,8 @@ class Text_Diff_Op_change extends Text_Diff_Op {
|
||||
|
||||
function &reverse()
|
||||
{
|
||||
return $reverse = &new Text_Diff_Op_change($this->final, $this->orig);
|
||||
$reverse = new Text_Diff_Op_change($this->final, $this->orig);
|
||||
return $reverse;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<html>␍<head><title>IP.Board - Bulletin Board System</title></head>␍<body>␍<h1>403: IP.Board -> Forbidden</h1>␍<hr>␍You have reached this page in error, please use your back button to return to the forum.␍<hr>␍<a href="http://www.invisionboard.com/">IP.Board</a>␍</body>␍</html>␍
|
||||
@@ -0,0 +1,438 @@
|
||||
<?php
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* Some ideas (and a bit of code) are taken from analyze.c, of GNU
|
||||
* diffutils-2.7, which can be found at:
|
||||
* ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz
|
||||
*
|
||||
* Some ideas (subdivision by NCHUNKS > 2, and some optimizations) are from
|
||||
* Geoffrey T. Dairiki <dairiki@dairiki.org>. The original PHP version of this
|
||||
* code was written by him, and is used/adapted with his permission.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/Engine/native.php,v 1.7.2.4 2008/01/04 10:38:10 jan Exp $
|
||||
*
|
||||
* Copyright 2004-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @package Text_Diff
|
||||
*/
|
||||
class Text_Diff_Engine_native {
|
||||
|
||||
function diff($from_lines, $to_lines)
|
||||
{
|
||||
array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
|
||||
array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
|
||||
|
||||
$n_from = count($from_lines);
|
||||
$n_to = count($to_lines);
|
||||
|
||||
$this->xchanged = $this->ychanged = array();
|
||||
$this->xv = $this->yv = array();
|
||||
$this->xind = $this->yind = array();
|
||||
unset($this->seq);
|
||||
unset($this->in_seq);
|
||||
unset($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, count($this->xv), 0, count($this->yv));
|
||||
|
||||
// Merge edits when possible.
|
||||
$this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged);
|
||||
$this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged);
|
||||
|
||||
// Compute the edit operations.
|
||||
$edits = array();
|
||||
$xi = $yi = 0;
|
||||
while ($xi < $n_from || $yi < $n_to) {
|
||||
assert($yi < $n_to || $this->xchanged[$xi]);
|
||||
assert($xi < $n_from || $this->ychanged[$yi]);
|
||||
|
||||
// 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 Text_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 Text_Diff_Op_change($delete, $add);
|
||||
} elseif ($delete) {
|
||||
$edits[] = new Text_Diff_Op_delete($delete);
|
||||
} elseif ($add) {
|
||||
$edits[] = new Text_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->_lcsPos($y);
|
||||
assert($k > 0);
|
||||
$ymids[$k] = $ymids[$k - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (list(, $y) = each($matches)) {
|
||||
if ($y > $this->seq[$k - 1]) {
|
||||
assert($y <= $this->seq[$k]);
|
||||
/* 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;
|
||||
} elseif (empty($this->in_seq[$y])) {
|
||||
$k = $this->_lcsPos($y);
|
||||
assert($k > 0);
|
||||
$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 _lcsPos($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;
|
||||
}
|
||||
}
|
||||
|
||||
assert($ypos != $this->seq[$end]);
|
||||
|
||||
$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 _shiftBoundaries($lines, &$changed, $other_changed)
|
||||
{
|
||||
$i = 0;
|
||||
$j = 0;
|
||||
|
||||
assert('count($lines) == count($changed)');
|
||||
$len = count($lines);
|
||||
$other_len = count($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]) {
|
||||
assert('$j < $other_len && ! $other_changed[$j]');
|
||||
$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--;
|
||||
}
|
||||
assert('$j > 0');
|
||||
while ($other_changed[--$j]) {
|
||||
continue;
|
||||
}
|
||||
assert('$j >= 0 && !$other_changed[$j]');
|
||||
}
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
|
||||
assert('$j < $other_len && ! $other_changed[$j]');
|
||||
$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;
|
||||
assert('$j > 0');
|
||||
while ($other_changed[--$j]) {
|
||||
continue;
|
||||
}
|
||||
assert('$j >= 0 && !$other_changed[$j]');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/**
|
||||
* Class used internally by Diff to actually compute the diffs.
|
||||
*
|
||||
* This class uses the Unix `diff` program via shell_exec to compute the
|
||||
* differences between the two input arrays.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/Engine/shell.php,v 1.6.2.3 2008/01/04 10:37:27 jan Exp $
|
||||
*
|
||||
* Copyright 2007-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @author Milian Wolff <mail@milianw.de>
|
||||
* @package Text_Diff
|
||||
* @since 0.3.0
|
||||
*/
|
||||
class Text_Diff_Engine_shell {
|
||||
|
||||
/**
|
||||
* Path to the diff executable
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $_diffCommand = 'diff';
|
||||
|
||||
/**
|
||||
* Returns the array of differences.
|
||||
*
|
||||
* @param array $from_lines lines of text from old file
|
||||
* @param array $to_lines lines of text from new file
|
||||
*
|
||||
* @return array all changes made (array with Text_Diff_Op_* objects)
|
||||
*/
|
||||
function diff($from_lines, $to_lines)
|
||||
{
|
||||
array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
|
||||
array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
|
||||
|
||||
$temp_dir = Text_Diff::_getTempDir();
|
||||
|
||||
// Execute gnu diff or similar to get a standard diff file.
|
||||
$from_file = tempnam($temp_dir, 'Text_Diff');
|
||||
$to_file = tempnam($temp_dir, 'Text_Diff');
|
||||
$fp = fopen($from_file, 'w');
|
||||
fwrite($fp, implode("\n", $from_lines));
|
||||
fclose($fp);
|
||||
$fp = fopen($to_file, 'w');
|
||||
fwrite($fp, implode("\n", $to_lines));
|
||||
fclose($fp);
|
||||
$diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file);
|
||||
unlink($from_file);
|
||||
unlink($to_file);
|
||||
|
||||
if (is_null($diff)) {
|
||||
// No changes were made
|
||||
return array(new Text_Diff_Op_copy($from_lines));
|
||||
}
|
||||
|
||||
$from_line_no = 1;
|
||||
$to_line_no = 1;
|
||||
$edits = array();
|
||||
|
||||
// Get changed lines by parsing something like:
|
||||
// 0a1,2
|
||||
// 1,2c4,6
|
||||
// 1,5d6
|
||||
preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff,
|
||||
$matches, PREG_SET_ORDER);
|
||||
|
||||
foreach ($matches as $match) {
|
||||
if (!isset($match[5])) {
|
||||
// This paren is not set every time (see regex).
|
||||
$match[5] = false;
|
||||
}
|
||||
|
||||
if ($match[3] == 'a') {
|
||||
$from_line_no--;
|
||||
}
|
||||
|
||||
if ($match[3] == 'd') {
|
||||
$to_line_no--;
|
||||
}
|
||||
|
||||
if ($from_line_no < $match[1] || $to_line_no < $match[4]) {
|
||||
// copied lines
|
||||
assert('$match[1] - $from_line_no == $match[4] - $to_line_no');
|
||||
array_push($edits,
|
||||
new Text_Diff_Op_copy(
|
||||
$this->_getLines($from_lines, $from_line_no, $match[1] - 1),
|
||||
$this->_getLines($to_lines, $to_line_no, $match[4] - 1)));
|
||||
}
|
||||
|
||||
switch ($match[3]) {
|
||||
case 'd':
|
||||
// deleted lines
|
||||
array_push($edits,
|
||||
new Text_Diff_Op_delete(
|
||||
$this->_getLines($from_lines, $from_line_no, $match[2])));
|
||||
$to_line_no++;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
// changed lines
|
||||
array_push($edits,
|
||||
new Text_Diff_Op_change(
|
||||
$this->_getLines($from_lines, $from_line_no, $match[2]),
|
||||
$this->_getLines($to_lines, $to_line_no, $match[5])));
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
// added lines
|
||||
array_push($edits,
|
||||
new Text_Diff_Op_add(
|
||||
$this->_getLines($to_lines, $to_line_no, $match[5])));
|
||||
$from_line_no++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($from_lines)) {
|
||||
// Some lines might still be pending. Add them as copied
|
||||
array_push($edits,
|
||||
new Text_Diff_Op_copy(
|
||||
$this->_getLines($from_lines, $from_line_no,
|
||||
$from_line_no + count($from_lines) - 1),
|
||||
$this->_getLines($to_lines, $to_line_no,
|
||||
$to_line_no + count($to_lines) - 1)));
|
||||
}
|
||||
|
||||
return $edits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get lines from either the old or new text
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param array &$text_lines Either $from_lines or $to_lines
|
||||
* @param int &$line_no Current line number
|
||||
* @param int $end Optional end line, when we want to chop more
|
||||
* than one line.
|
||||
*
|
||||
* @return array The chopped lines
|
||||
*/
|
||||
function _getLines(&$text_lines, &$line_no, $end = false)
|
||||
{
|
||||
if (!empty($end)) {
|
||||
$lines = array();
|
||||
// We can shift even more
|
||||
while ($line_no <= $end) {
|
||||
array_push($lines, array_shift($text_lines));
|
||||
$line_no++;
|
||||
}
|
||||
} else {
|
||||
$lines = array(array_shift($text_lines));
|
||||
$line_no++;
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* Parses unified or context diffs output from eg. the diff utility.
|
||||
*
|
||||
* Example:
|
||||
* <code>
|
||||
* $patch = file_get_contents('example.patch');
|
||||
* $diff = new Text_Diff('string', array($patch));
|
||||
* $renderer = new Text_Diff_Renderer_inline();
|
||||
* echo $renderer->render($diff);
|
||||
* </code>
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/Engine/string.php,v 1.5.2.5 2008/09/10 08:31:58 jan Exp $
|
||||
*
|
||||
* Copyright 2005 Örjan Persson <o@42mm.org>
|
||||
* Copyright 2005-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @author Örjan Persson <o@42mm.org>
|
||||
* @package Text_Diff
|
||||
* @since 0.2.0
|
||||
*/
|
||||
class Text_Diff_Engine_string {
|
||||
|
||||
/**
|
||||
* Parses a unified or context diff.
|
||||
*
|
||||
* First param contains the whole diff and the second can be used to force
|
||||
* a specific diff type. If the second parameter is 'autodetect', the
|
||||
* diff will be examined to find out which type of diff this is.
|
||||
*
|
||||
* @param string $diff The diff content.
|
||||
* @param string $mode The diff mode of the content in $diff. One of
|
||||
* 'context', 'unified', or 'autodetect'.
|
||||
*
|
||||
* @return array List of all diff operations.
|
||||
*/
|
||||
function diff($diff, $mode = 'autodetect')
|
||||
{
|
||||
if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
|
||||
return PEAR::raiseError('Type of diff is unsupported');
|
||||
}
|
||||
|
||||
if ($mode == 'autodetect') {
|
||||
$context = strpos($diff, '***');
|
||||
$unified = strpos($diff, '---');
|
||||
if ($context === $unified) {
|
||||
return PEAR::raiseError('Type of diff could not be detected');
|
||||
} elseif ($context === false || $unified === false) {
|
||||
$mode = $context !== false ? 'context' : 'unified';
|
||||
} else {
|
||||
$mode = $context < $unified ? 'context' : 'unified';
|
||||
}
|
||||
}
|
||||
|
||||
// Split by new line and remove the diff header, if there is one.
|
||||
$diff = explode("\n", $diff);
|
||||
if (($mode == 'context' && strpos($diff[0], '***') === 0) ||
|
||||
($mode == 'unified' && strpos($diff[0], '---') === 0)) {
|
||||
array_shift($diff);
|
||||
array_shift($diff);
|
||||
}
|
||||
|
||||
if ($mode == 'context') {
|
||||
return $this->parseContextDiff($diff);
|
||||
} else {
|
||||
return $this->parseUnifiedDiff($diff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array containing the unified diff.
|
||||
*
|
||||
* @param array $diff Array of lines.
|
||||
*
|
||||
* @return array List of all diff operations.
|
||||
*/
|
||||
function parseUnifiedDiff($diff)
|
||||
{
|
||||
$edits = array();
|
||||
$end = count($diff) - 1;
|
||||
for ($i = 0; $i < $end;) {
|
||||
$diff1 = array();
|
||||
switch (substr($diff[$i], 0, 1)) {
|
||||
case ' ':
|
||||
do {
|
||||
$diff1[] = substr($diff[$i], 1);
|
||||
} while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
|
||||
$edits[] = new Text_Diff_Op_copy($diff1);
|
||||
break;
|
||||
|
||||
case '+':
|
||||
// get all new lines
|
||||
do {
|
||||
$diff1[] = substr($diff[$i], 1);
|
||||
} while (++$i < $end && substr($diff[$i], 0, 1) == '+');
|
||||
$edits[] = new Text_Diff_Op_add($diff1);
|
||||
break;
|
||||
|
||||
case '-':
|
||||
// get changed or removed lines
|
||||
$diff2 = array();
|
||||
do {
|
||||
$diff1[] = substr($diff[$i], 1);
|
||||
} while (++$i < $end && substr($diff[$i], 0, 1) == '-');
|
||||
|
||||
while ($i < $end && substr($diff[$i], 0, 1) == '+') {
|
||||
$diff2[] = substr($diff[$i++], 1);
|
||||
}
|
||||
if (count($diff2) == 0) {
|
||||
$edits[] = new Text_Diff_Op_delete($diff1);
|
||||
} else {
|
||||
$edits[] = new Text_Diff_Op_change($diff1, $diff2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $edits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array containing the context diff.
|
||||
*
|
||||
* @param array $diff Array of lines.
|
||||
*
|
||||
* @return array List of all diff operations.
|
||||
*/
|
||||
function parseContextDiff(&$diff)
|
||||
{
|
||||
$edits = array();
|
||||
$i = $max_i = $j = $max_j = 0;
|
||||
$end = count($diff) - 1;
|
||||
while ($i < $end && $j < $end) {
|
||||
while ($i >= $max_i && $j >= $max_j) {
|
||||
// Find the boundaries of the diff output of the two files
|
||||
for ($i = $j;
|
||||
$i < $end && substr($diff[$i], 0, 3) == '***';
|
||||
$i++);
|
||||
for ($max_i = $i;
|
||||
$max_i < $end && substr($diff[$max_i], 0, 3) != '---';
|
||||
$max_i++);
|
||||
for ($j = $max_i;
|
||||
$j < $end && substr($diff[$j], 0, 3) == '---';
|
||||
$j++);
|
||||
for ($max_j = $j;
|
||||
$max_j < $end && substr($diff[$max_j], 0, 3) != '***';
|
||||
$max_j++);
|
||||
}
|
||||
|
||||
// find what hasn't been changed
|
||||
$array = array();
|
||||
while ($i < $max_i &&
|
||||
$j < $max_j &&
|
||||
strcmp($diff[$i], $diff[$j]) == 0) {
|
||||
$array[] = substr($diff[$i], 2);
|
||||
$i++;
|
||||
$j++;
|
||||
}
|
||||
|
||||
while ($i < $max_i && ($max_j-$j) <= 1) {
|
||||
if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
|
||||
break;
|
||||
}
|
||||
$array[] = substr($diff[$i++], 2);
|
||||
}
|
||||
|
||||
while ($j < $max_j && ($max_i-$i) <= 1) {
|
||||
if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
|
||||
break;
|
||||
}
|
||||
$array[] = substr($diff[$j++], 2);
|
||||
}
|
||||
if (count($array) > 0) {
|
||||
$edits[] = new Text_Diff_Op_copy($array);
|
||||
}
|
||||
|
||||
if ($i < $max_i) {
|
||||
$diff1 = array();
|
||||
switch (substr($diff[$i], 0, 1)) {
|
||||
case '!':
|
||||
$diff2 = array();
|
||||
do {
|
||||
$diff1[] = substr($diff[$i], 2);
|
||||
if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
|
||||
$diff2[] = substr($diff[$j++], 2);
|
||||
}
|
||||
} while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
|
||||
$edits[] = new Text_Diff_Op_change($diff1, $diff2);
|
||||
break;
|
||||
|
||||
case '+':
|
||||
do {
|
||||
$diff1[] = substr($diff[$i], 2);
|
||||
} while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
|
||||
$edits[] = new Text_Diff_Op_add($diff1);
|
||||
break;
|
||||
|
||||
case '-':
|
||||
do {
|
||||
$diff1[] = substr($diff[$i], 2);
|
||||
} while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
|
||||
$edits[] = new Text_Diff_Op_delete($diff1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($j < $max_j) {
|
||||
$diff2 = array();
|
||||
switch (substr($diff[$j], 0, 1)) {
|
||||
case '+':
|
||||
do {
|
||||
$diff2[] = substr($diff[$j++], 2);
|
||||
} while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
|
||||
$edits[] = new Text_Diff_Op_add($diff2);
|
||||
break;
|
||||
|
||||
case '-':
|
||||
do {
|
||||
$diff2[] = substr($diff[$j++], 2);
|
||||
} while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
|
||||
$edits[] = new Text_Diff_Op_delete($diff2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $edits;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* Class used internally by Diff to actually compute the diffs.
|
||||
*
|
||||
* This class uses the xdiff PECL package (http://pecl.php.net/package/xdiff)
|
||||
* to compute the differences between the two input arrays.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/Engine/xdiff.php,v 1.4.2.3 2008/01/04 10:37:27 jan Exp $
|
||||
*
|
||||
* Copyright 2004-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @author Jon Parise <jon@horde.org>
|
||||
* @package Text_Diff
|
||||
*/
|
||||
class Text_Diff_Engine_xdiff {
|
||||
|
||||
/**
|
||||
*/
|
||||
function diff($from_lines, $to_lines)
|
||||
{
|
||||
array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
|
||||
array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
|
||||
|
||||
/* Convert the two input arrays into strings for xdiff processing. */
|
||||
$from_string = implode("\n", $from_lines);
|
||||
$to_string = implode("\n", $to_lines);
|
||||
|
||||
/* Diff the two strings and convert the result to an array. */
|
||||
$diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
|
||||
$diff = explode("\n", $diff);
|
||||
|
||||
/* Walk through the diff one line at a time. We build the $edits
|
||||
* array of diff operations by reading the first character of the
|
||||
* xdiff output (which is in the "unified diff" format).
|
||||
*
|
||||
* Note that we don't have enough information to detect "changed"
|
||||
* lines using this approach, so we can't add Text_Diff_Op_changed
|
||||
* instances to the $edits array. The result is still perfectly
|
||||
* valid, albeit a little less descriptive and efficient. */
|
||||
$edits = array();
|
||||
foreach ($diff as $line) {
|
||||
switch ($line[0]) {
|
||||
case ' ':
|
||||
$edits[] = new Text_Diff_Op_copy(array(substr($line, 1)));
|
||||
break;
|
||||
|
||||
case '+':
|
||||
$edits[] = new Text_Diff_Op_add(array(substr($line, 1)));
|
||||
break;
|
||||
|
||||
case '-':
|
||||
$edits[] = new Text_Diff_Op_delete(array(substr($line, 1)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $edits;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* $Horde: framework/Text_Diff/Diff/Mapped.php,v 1.3.2.3 2008/01/04 10:37:27 jan Exp $
|
||||
*
|
||||
* Copyright 2007-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
*/
|
||||
class Text_Diff_Mapped extends Text_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 Text_Diff_Mapped($from_lines, $to_lines,
|
||||
$mapped_from_lines, $mapped_to_lines)
|
||||
{
|
||||
assert(count($from_lines) == count($mapped_from_lines));
|
||||
assert(count($to_lines) == count($mapped_to_lines));
|
||||
|
||||
parent::Text_Diff($mapped_from_lines, $mapped_to_lines);
|
||||
|
||||
$xi = $yi = 0;
|
||||
for ($i = 0; $i < count($this->_edits); $i++) {
|
||||
$orig = &$this->_edits[$i]->orig;
|
||||
if (is_array($orig)) {
|
||||
$orig = array_slice($from_lines, $xi, count($orig));
|
||||
$xi += count($orig);
|
||||
}
|
||||
|
||||
$final = &$this->_edits[$i]->final;
|
||||
if (is_array($final)) {
|
||||
$final = array_slice($to_lines, $yi, count($final));
|
||||
$yi += count($final);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,12 @@
|
||||
* This class renders the diff in classic diff format. It is intended that
|
||||
* this class be customized via inheritance, to obtain fancier outputs.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/Renderer.php,v 1.8 2005/05/02 19:44:53 chuck Exp $
|
||||
* $Horde: framework/Text_Diff/Diff/Renderer.php,v 1.5.10.10 2008/01/04 10:37:27 jan Exp $
|
||||
*
|
||||
* Copyright 2004-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @package Text_Diff
|
||||
*/
|
||||
@@ -47,7 +52,14 @@ class Text_Diff_Renderer {
|
||||
*/
|
||||
function getParams()
|
||||
{
|
||||
return get_object_vars($this);
|
||||
$params = array();
|
||||
foreach (get_object_vars($this) as $k => $v) {
|
||||
if ($k[0] == '_') {
|
||||
$params[substr($k, 1)] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,31 +80,47 @@ class Text_Diff_Renderer {
|
||||
|
||||
$output = $this->_startDiff();
|
||||
|
||||
foreach ($diff->getDiff() as $edit) {
|
||||
$diffs = $diff->getDiff();
|
||||
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, 'Text_Diff_Op_copy')) {
|
||||
/* Do we have any diff blocks yet? */
|
||||
if (is_array($block)) {
|
||||
if (count($edit->orig) <= $nlead + $ntrail) {
|
||||
/* How many lines to keep as context from the copy
|
||||
* block. */
|
||||
$keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
|
||||
if (count($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 Text_Diff_Op_copy($context);
|
||||
$block[] = new Text_Diff_Op_copy($context);
|
||||
}
|
||||
/* @todo */
|
||||
$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, count($context) - $nlead);
|
||||
$x0 = $xi - count($context);
|
||||
$y0 = $yi - count($context);
|
||||
$block = array();
|
||||
if ($context) {
|
||||
$block[] = &new Text_Diff_Op_copy($context);
|
||||
$block[] = new Text_Diff_Op_copy($context);
|
||||
}
|
||||
}
|
||||
$block[] = $edit;
|
||||
@@ -161,6 +189,13 @@ class Text_Diff_Renderer {
|
||||
$ybeg .= ',' . ($ybeg + $ylen - 1);
|
||||
}
|
||||
|
||||
// this matches the GNU Diff behaviour
|
||||
if ($xlen && !$ylen) {
|
||||
$ybeg--;
|
||||
} elseif (!$xlen) {
|
||||
$xbeg--;
|
||||
}
|
||||
|
||||
return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
|
||||
}
|
||||
|
||||
@@ -173,31 +208,25 @@ class Text_Diff_Renderer {
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Added by Matt Mecham
|
||||
* Space between prefix and string result
|
||||
* 25-07-2005
|
||||
*
|
||||
*/
|
||||
|
||||
function _lines($lines, $prefix = ' ')
|
||||
{
|
||||
return $prefix . ' ' . implode("\n$prefix", $lines) . "\n";
|
||||
return $prefix . implode("\n$prefix", $lines) . "\n";
|
||||
}
|
||||
|
||||
function _context($lines)
|
||||
{
|
||||
return $this->_lines($lines);
|
||||
return $this->_lines($lines, ' ');
|
||||
}
|
||||
|
||||
function _added($lines)
|
||||
{
|
||||
return $this->_lines($lines, '>');
|
||||
return $this->_lines($lines, '> ');
|
||||
}
|
||||
|
||||
function _deleted($lines)
|
||||
{
|
||||
return $this->_lines($lines, '<');
|
||||
return $this->_lines($lines, '< ');
|
||||
}
|
||||
|
||||
function _changed($orig, $final)
|
||||
@@ -205,4 +234,4 @@ class Text_Diff_Renderer {
|
||||
return $this->_deleted($orig) . "---\n" . $this->_added($final);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* "Context" diff renderer.
|
||||
*
|
||||
* This class renders the diff in classic "context diff" format.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/Renderer/context.php,v 1.3.2.3 2008/01/04 10:37:27 jan Exp $
|
||||
*
|
||||
* Copyright 2004-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @package Text_Diff
|
||||
*/
|
||||
|
||||
/** Text_Diff_Renderer */
|
||||
require_once 'Text/Diff/Renderer.php';
|
||||
|
||||
/**
|
||||
* @package Text_Diff
|
||||
*/
|
||||
class Text_Diff_Renderer_context extends Text_Diff_Renderer {
|
||||
|
||||
/**
|
||||
* Number of leading context "lines" to preserve.
|
||||
*/
|
||||
var $_leading_context_lines = 4;
|
||||
|
||||
/**
|
||||
* Number of trailing context "lines" to preserve.
|
||||
*/
|
||||
var $_trailing_context_lines = 4;
|
||||
|
||||
var $_second_block = '';
|
||||
|
||||
function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
|
||||
{
|
||||
if ($xlen != 1) {
|
||||
$xbeg .= ',' . $xlen;
|
||||
}
|
||||
if ($ylen != 1) {
|
||||
$ybeg .= ',' . $ylen;
|
||||
}
|
||||
$this->_second_block = "--- $ybeg ----\n";
|
||||
return "***************\n*** $xbeg ****";
|
||||
}
|
||||
|
||||
function _endBlock()
|
||||
{
|
||||
return $this->_second_block;
|
||||
}
|
||||
|
||||
function _context($lines)
|
||||
{
|
||||
$this->_second_block .= $this->_lines($lines, ' ');
|
||||
return $this->_lines($lines, ' ');
|
||||
}
|
||||
|
||||
function _added($lines)
|
||||
{
|
||||
$this->_second_block .= $this->_lines($lines, '+ ');
|
||||
return '';
|
||||
}
|
||||
|
||||
function _deleted($lines)
|
||||
{
|
||||
return $this->_lines($lines, '- ');
|
||||
}
|
||||
|
||||
function _changed($orig, $final)
|
||||
{
|
||||
$this->_second_block .= $this->_lines($final, '! ');
|
||||
return $this->_lines($orig, '! ');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<html>␍<head><title>Invision Power Board - Bulletin Board System</title></head>␍<body>␍<h1>403: Invision Power Board -> Forbidden</h1>␍<hr>␍You have reached this Page in error, please use␍your back button to return to Invision Board.␍<hr>␍<a href="http://anonym.to/http://www.invisionboard.com/">Invision Power Board</a>␍</body>␍</html>
|
||||
<html>␍<head><title>IP.Board - Bulletin Board System</title></head>␍<body>␍<h1>403: IP.Board -> Forbidden</h1>␍<hr>␍You have reached this page in error, please use your back button to return to the forum.␍<hr>␍<a href="http://www.invisionboard.com/">IP.Board</a>␍</body>␍</html>␍
|
||||
@@ -2,9 +2,24 @@
|
||||
/**
|
||||
* "Inline" diff renderer.
|
||||
*
|
||||
* This class renders diffs in the Wiki-style "inline" format.
|
||||
* $Horde: framework/Text_Diff/Diff/Renderer/inline.php,v 1.4.10.14 2008/01/04 10:37:27 jan Exp $
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/Renderer/inline.php,v 1.11 2005/05/02 19:49:39 chuck Exp $
|
||||
* Copyright 2004-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @author Ciprian Popovici
|
||||
* @package Text_Diff
|
||||
*/
|
||||
|
||||
/** Text_Diff_Renderer */
|
||||
require_once 'Text/Diff/Renderer.php';
|
||||
|
||||
/**
|
||||
* "Inline" diff renderer.
|
||||
*
|
||||
* This class renders diffs in the Wiki-style "inline" format.
|
||||
*
|
||||
* @author Ciprian Popovici
|
||||
* @package Text_Diff
|
||||
@@ -52,15 +67,6 @@ class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
|
||||
*/
|
||||
var $_split_level = 'lines';
|
||||
|
||||
function _lines($lines, $prefix = ' ')
|
||||
{
|
||||
if ($this->_split_level == 'words') {
|
||||
return implode('', $lines);
|
||||
} else {
|
||||
return implode("\n", $lines) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
|
||||
{
|
||||
return $this->_block_header;
|
||||
@@ -71,18 +77,33 @@ class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
|
||||
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[count($lines) - 1] .= $this->_ins_suffix;
|
||||
return $this->_lines($lines);
|
||||
return $this->_lines($lines, ' ', false);
|
||||
}
|
||||
|
||||
function _deleted($lines, $words = false)
|
||||
{
|
||||
array_walk($lines, array(&$this, '_encode'));
|
||||
$lines[0] = $this->_del_prefix . $lines[0];
|
||||
$lines[count($lines) - 1] .= $this->_del_suffix;
|
||||
return $this->_lines($lines);
|
||||
return $this->_lines($lines, ' ', false);
|
||||
}
|
||||
|
||||
function _changed($orig, $final)
|
||||
@@ -91,7 +112,9 @@ class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
|
||||
* display. */
|
||||
if ($this->_split_level == 'words') {
|
||||
$prefix = '';
|
||||
while (substr($orig[0], 0, 1) == substr($final[0], 0, 1)) {
|
||||
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);
|
||||
@@ -108,12 +131,12 @@ class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
|
||||
/* 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. */
|
||||
$diff = &new Text_Diff($this->_splitOnWords($text1, $nl),
|
||||
$this->_splitOnWords($text2, $nl));
|
||||
$diff = new Text_Diff($this->_splitOnWords($text1, $nl),
|
||||
$this->_splitOnWords($text2, $nl));
|
||||
|
||||
/* Get the diff in inline format. */
|
||||
$renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(),
|
||||
array('split_level' => 'words')));
|
||||
$renderer = new Text_Diff_Renderer_inline(array_merge($this->getParams(),
|
||||
array('split_level' => 'words')));
|
||||
|
||||
/* Run the diff and get the output. */
|
||||
return str_replace($nl, "\n", $renderer->render($diff)) . "\n";
|
||||
@@ -121,14 +144,17 @@ class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
|
||||
|
||||
function _splitOnWords($string, $newlineEscape = "\n")
|
||||
{
|
||||
// Ignore \0; otherwise the while loop will never finish.
|
||||
$string = str_replace("\0", '', $string);
|
||||
|
||||
$words = array();
|
||||
$length = strlen($string);
|
||||
$pos = 0;
|
||||
|
||||
while ($pos < $length) {
|
||||
// Eat a word with any preceding whitespace.
|
||||
$spaces = strspn($string, " \n", $pos);
|
||||
$nextpos = strcspn($string, " \n", $pos + $spaces);
|
||||
$spaces = strspn(substr($string, $pos), " \n");
|
||||
$nextpos = strcspn(substr($string, $pos + $spaces), " \n");
|
||||
$words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos));
|
||||
$pos += $spaces + $nextpos;
|
||||
}
|
||||
@@ -136,4 +162,9 @@ class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
|
||||
return $words;
|
||||
}
|
||||
|
||||
}
|
||||
function _encode(&$string)
|
||||
{
|
||||
$string = htmlspecialchars($string);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,8 +4,21 @@
|
||||
*
|
||||
* This class renders the diff in classic "unified diff" format.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/Renderer/unified.php,v 1.4 2005/03/07 14:58:30 jan Exp $
|
||||
* $Horde: framework/Text_Diff/Diff/Renderer/unified.php,v 1.3.10.6 2008/01/04 10:37:27 jan Exp $
|
||||
*
|
||||
* Copyright 2004-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @author Ciprian Popovici
|
||||
* @package Text_Diff
|
||||
*/
|
||||
|
||||
/** Text_Diff_Renderer */
|
||||
require_once 'Text/Diff/Renderer.php';
|
||||
|
||||
/**
|
||||
* @package Text_Diff
|
||||
*/
|
||||
class Text_Diff_Renderer_unified extends Text_Diff_Renderer {
|
||||
@@ -31,6 +44,11 @@ class Text_Diff_Renderer_unified extends Text_Diff_Renderer {
|
||||
return "@@ -$xbeg +$ybeg @@";
|
||||
}
|
||||
|
||||
function _context($lines)
|
||||
{
|
||||
return $this->_lines($lines, ' ');
|
||||
}
|
||||
|
||||
function _added($lines)
|
||||
{
|
||||
return $this->_lines($lines, '+');
|
||||
@@ -46,4 +64,4 @@ class Text_Diff_Renderer_unified extends Text_Diff_Renderer {
|
||||
return $this->_deleted($orig) . $this->_added($final);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
/**
|
||||
* A class for computing three way diffs.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff/ThreeWay.php,v 1.3.2.3 2008/01/04 10:37:27 jan Exp $
|
||||
*
|
||||
* Copyright 2007-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @package Text_Diff
|
||||
* @since 0.3.0
|
||||
*/
|
||||
|
||||
/** Text_Diff */
|
||||
require_once 'Text/Diff.php';
|
||||
|
||||
/**
|
||||
* A class for computing three way diffs.
|
||||
*
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
*/
|
||||
class Text_Diff_ThreeWay extends Text_Diff {
|
||||
|
||||
/**
|
||||
* Conflict counter.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $_conflictingBlocks = 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 Text_Diff_ThreeWay($orig, $final1, $final2)
|
||||
{
|
||||
if (extension_loaded('xdiff')) {
|
||||
$engine = new Text_Diff_Engine_xdiff();
|
||||
} else {
|
||||
$engine = new Text_Diff_Engine_native();
|
||||
}
|
||||
|
||||
$this->_edits = $this->_diff3($engine->diff($orig, $final1),
|
||||
$engine->diff($orig, $final2));
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
function mergedOutput($label1 = false, $label2 = false)
|
||||
{
|
||||
$lines = array();
|
||||
foreach ($this->_edits as $edit) {
|
||||
if ($edit->isConflict()) {
|
||||
/* FIXME: this should probably be moved somewhere else. */
|
||||
$lines = array_merge($lines,
|
||||
array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')),
|
||||
$edit->final1,
|
||||
array("======="),
|
||||
$edit->final2,
|
||||
array('>>>>>>>' . ($label2 ? ' ' . $label2 : '')));
|
||||
$this->_conflictingBlocks++;
|
||||
} else {
|
||||
$lines = array_merge($lines, $edit->merged());
|
||||
}
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
function _diff3($edits1, $edits2)
|
||||
{
|
||||
$edits = array();
|
||||
$bb = new Text_Diff_ThreeWay_BlockBuilder();
|
||||
|
||||
$e1 = current($edits1);
|
||||
$e2 = current($edits2);
|
||||
while ($e1 || $e2) {
|
||||
if ($e1 && $e2 && is_a($e1, 'Text_Diff_Op_copy') && is_a($e2, 'Text_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());
|
||||
assert($ncopy > 0);
|
||||
$edits[] = new Text_Diff_ThreeWay_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);
|
||||
}
|
||||
|
||||
if (is_a($e1, 'Text_Diff_Op_copy')) {
|
||||
$bb->out1(array_splice($e1->final, 0, $norig));
|
||||
}
|
||||
|
||||
if (is_a($e2, 'Text_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 Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_ThreeWay_Op {
|
||||
|
||||
function Text_Diff_ThreeWay_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;
|
||||
} elseif ($this->final1 === $this->orig) {
|
||||
$this->_merged = &$this->final2;
|
||||
} elseif ($this->final2 === $this->orig) {
|
||||
$this->_merged = &$this->final1;
|
||||
} else {
|
||||
$this->_merged = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_merged;
|
||||
}
|
||||
|
||||
function isConflict()
|
||||
{
|
||||
return $this->merged() === false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_ThreeWay_Op_copy extends Text_Diff_ThreeWay_Op {
|
||||
|
||||
function Text_Diff_ThreeWay_Op_Copy($lines = false)
|
||||
{
|
||||
$this->orig = $lines ? $lines : array();
|
||||
$this->final1 = &$this->orig;
|
||||
$this->final2 = &$this->orig;
|
||||
}
|
||||
|
||||
function merged()
|
||||
{
|
||||
return $this->orig;
|
||||
}
|
||||
|
||||
function isConflict()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff_ThreeWay_BlockBuilder {
|
||||
|
||||
function Text_Diff_ThreeWay_BlockBuilder()
|
||||
{
|
||||
$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 isEmpty()
|
||||
{
|
||||
return !$this->orig && !$this->final1 && !$this->final2;
|
||||
}
|
||||
|
||||
function finish()
|
||||
{
|
||||
if ($this->isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
$edit = new Text_Diff_ThreeWay_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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<html>␍<head><title>Invision Power Board - Bulletin Board System</title></head>␍<body>␍<h1>403: Invision Power Board -> Forbidden</h1>␍<hr>␍You have reached this Page in error, please use␍your back button to return to Invision Board.␍<hr>␍<a href="http://anonym.to/http://www.invisionboard.com/">Invision Power Board</a>␍</body>␍</html>
|
||||
<html>␍<head><title>IP.Board - Bulletin Board System</title></head>␍<body>␍<h1>403: IP.Board -> Forbidden</h1>␍<hr>␍You have reached this page in error, please use your back button to return to the forum.␍<hr>␍<a href="http://www.invisionboard.com/">IP.Board</a>␍</body>␍</html>␍
|
||||
@@ -1,12 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* A class for computing three way diffs.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff3.php,v 1.2.10.6 2008/01/04 10:37:26 jan Exp $
|
||||
*
|
||||
* Copyright 2007-2008 The Horde Project (http://www.horde.org/)
|
||||
*
|
||||
* See the enclosed file COPYING for license information (LGPL). If you did
|
||||
* not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
||||
*
|
||||
* @package Text_Diff
|
||||
* @since 0.3.0
|
||||
*/
|
||||
|
||||
/** Text_Diff */
|
||||
require_once 'Text/Diff.php';
|
||||
|
||||
/**
|
||||
* A class for computing three way diffs.
|
||||
*
|
||||
* $Horde: framework/Text_Diff/Diff3.php,v 1.2 2004/10/13 09:30:20 jan Exp $
|
||||
*
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
*/
|
||||
@@ -15,7 +27,7 @@ class Text_Diff3 extends Text_Diff {
|
||||
/**
|
||||
* Conflict counter.
|
||||
*
|
||||
* @var integer $_conflictCounter
|
||||
* @var integer
|
||||
*/
|
||||
var $_conflictingBlocks = 0;
|
||||
|
||||
@@ -29,15 +41,17 @@ class Text_Diff3 extends Text_Diff {
|
||||
function Text_Diff3($orig, $final1, $final2)
|
||||
{
|
||||
if (extension_loaded('xdiff')) {
|
||||
$engine = &new Text_Diff_Engine_xdiff();
|
||||
$engine = new Text_Diff_Engine_xdiff();
|
||||
} else {
|
||||
$engine = &new Text_Diff_Engine_native();
|
||||
$engine = new Text_Diff_Engine_native();
|
||||
}
|
||||
|
||||
$this->_edits = $this->_diff3($engine->diff($orig, $final1),
|
||||
$engine->diff($orig, $final2));
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
function mergedOutput($label1 = false, $label2 = false)
|
||||
{
|
||||
$lines = array();
|
||||
@@ -65,7 +79,7 @@ class Text_Diff3 extends Text_Diff {
|
||||
function _diff3($edits1, $edits2)
|
||||
{
|
||||
$edits = array();
|
||||
$bb = &new Text_Diff3_BlockBuilder();
|
||||
$bb = new Text_Diff3_BlockBuilder();
|
||||
|
||||
$e1 = current($edits1);
|
||||
$e2 = current($edits2);
|
||||
@@ -80,7 +94,7 @@ class Text_Diff3 extends Text_Diff {
|
||||
|
||||
$ncopy = min($e1->norig(), $e2->norig());
|
||||
assert($ncopy > 0);
|
||||
$edits[] = &new Text_Diff3_Op_copy(array_slice($e1->orig, 0, $ncopy));
|
||||
$edits[] = new Text_Diff3_Op_copy(array_slice($e1->orig, 0, $ncopy));
|
||||
|
||||
if ($e1->norig() > $ncopy) {
|
||||
array_splice($e1->orig, 0, $ncopy);
|
||||
@@ -136,7 +150,8 @@ class Text_Diff3 extends Text_Diff {
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @access private
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff3_Op {
|
||||
|
||||
@@ -174,7 +189,8 @@ class Text_Diff3_Op {
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @access private
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff3_Op_copy extends Text_Diff3_Op {
|
||||
|
||||
@@ -200,7 +216,8 @@ class Text_Diff3_Op_copy extends Text_Diff3_Op {
|
||||
/**
|
||||
* @package Text_Diff
|
||||
* @author Geoffrey T. Dairiki <dairiki@dairiki.org>
|
||||
* @access private
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Text_Diff3_BlockBuilder {
|
||||
|
||||
@@ -240,7 +257,7 @@ class Text_Diff3_BlockBuilder {
|
||||
if ($this->isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
$edit = &new Text_Diff3_Op($this->orig, $this->final1, $this->final2);
|
||||
$edit = new Text_Diff3_Op($this->orig, $this->final1, $this->final2);
|
||||
$this->_init();
|
||||
return $edit;
|
||||
}
|
||||
@@ -256,4 +273,4 @@ class Text_Diff3_BlockBuilder {
|
||||
array_splice($array, sizeof($array), 0, $lines);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<html>␍<head><title>Invision Power Board - Bulletin Board System</title></head>␍<body>␍<h1>403: Invision Power Board -> Forbidden</h1>␍<hr>␍You have reached this Page in error, please use␍your back button to return to Invision Board.␍<hr>␍<a href="http://anonym.to/http://www.invisionboard.com/">Invision Power Board</a>␍</body>␍</html>
|
||||
<html>␍<head><title>IP.Board - Bulletin Board System</title></head>␍<body>␍<h1>403: IP.Board -> Forbidden</h1>␍<hr>␍You have reached this page in error, please use your back button to return to the forum.␍<hr>␍<a href="http://www.invisionboard.com/">IP.Board</a>␍</body>␍</html>␍
|
||||
@@ -1,142 +1,281 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE package SYSTEM "http://pear.php.net/dtd/package-1.0">
|
||||
<package version="1.0" packagerversion="1.4.0a9">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package packagerversion="1.7.2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
|
||||
<name>Text_Diff</name>
|
||||
<channel>pear.php.net</channel>
|
||||
<summary>Engine for performing and rendering text diffs</summary>
|
||||
<description>This package provides a text-based diff engine and renderers for multiple diff output formats.
|
||||
</description>
|
||||
<maintainers>
|
||||
<maintainer>
|
||||
<user>chagenbu</user>
|
||||
<name>Chuck Hagenbuch</name>
|
||||
<email>chuck@horde.org</email>
|
||||
<role>lead</role>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<user>jon</user>
|
||||
<name>Jon Parise</name>
|
||||
<email>jon@horde.org</email>
|
||||
<role>lead</role>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<user>pmjones</user>
|
||||
<name>Paul M Jones</name>
|
||||
<email>pmjones@memphis.edu</email>
|
||||
<role>lead</role>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<user>yunosh</user>
|
||||
<name>Jan Schneider</name>
|
||||
<email>jan@horde.org</email>
|
||||
<role>lead</role>
|
||||
</maintainer>
|
||||
</maintainers>
|
||||
<release>
|
||||
<version>0.0.5</version>
|
||||
<date>2005-05-05</date>
|
||||
<license>LGPL</license>
|
||||
<state>beta</state>
|
||||
<notes>* Fixed variable name in Text_MappedDiff.
|
||||
<description>This package provides a text-based diff engine and renderers for multiple diff output formats.</description>
|
||||
<lead>
|
||||
<name>Chuck Hagenbuch</name>
|
||||
<user>chagenbu</user>
|
||||
<email>chuck@horde.org</email>
|
||||
<active>yes</active>
|
||||
</lead>
|
||||
<lead>
|
||||
<name>Jan Schneider</name>
|
||||
<user>yunosh</user>
|
||||
<email>jan@horde.org</email>
|
||||
<active>yes</active>
|
||||
</lead>
|
||||
<lead>
|
||||
<name>Paul M Jones</name>
|
||||
<user>pmjones</user>
|
||||
<email>pmjones@memphis.edu</email>
|
||||
<active>no</active>
|
||||
</lead>
|
||||
<date>2008-09-10</date>
|
||||
<time>10:52:52</time>
|
||||
<version>
|
||||
<release>1.1.0</release>
|
||||
<api>1.1.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>stable</release>
|
||||
<api>stable</api>
|
||||
</stability>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Add countAddedLines() and countDeletedLines() methods (Christian King, PEAR Request #13183).
|
||||
* Improve string engine if patch data has no header (PEAR Bug #14533).
|
||||
* Fix autodetection of diff type in string engine (PEAR Bug #14625).</notes>
|
||||
<contents>
|
||||
<dir name="/">
|
||||
<file baseinstalldir="/Text" md5sum="b30ce3240eed85e2114b93e28095af90" name="Diff/Engine/native.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="4ccb7b5ea80041fcd6e5c5cd958931fa" name="Diff/Engine/shell.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="1daaca651ed46e5711b43e47e91e077f" name="Diff/Engine/string.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="05f00fc5c59664199eb533ff825a386c" name="Diff/Engine/xdiff.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="1263babf64df086001944b699014e59d" name="Diff/Renderer/context.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="e001548ef0b37702633a421cf7eed83d" name="Diff/Renderer/inline.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="c4dbae31ce515995100f6ad27a8c9c4e" name="Diff/Renderer/unified.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="96b1dc3ba33aba32a4b5662b22d8b58a" name="Diff/Mapped.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="d5ac2cccaacad465caa41fae23eae8ad" name="Diff/Renderer.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="abf74995fef0ba23d50dfc6a5652b5e1" name="Diff/ThreeWay.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="5fbdf5388905ed07026b876126aeee5a" name="docs/examples/1.txt" role="doc" />
|
||||
<file baseinstalldir="/Text" md5sum="011c4dad4506a4843ded1f12cab69909" name="docs/examples/2.txt" role="doc" />
|
||||
<file baseinstalldir="/Text" md5sum="6082891c438ee1ba70fd89b9b4e241e8" name="docs/examples/diff.php" role="doc" />
|
||||
<file baseinstalldir="/Text" md5sum="5fbdf5388905ed07026b876126aeee5a" name="tests/1.txt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="011c4dad4506a4843ded1f12cab69909" name="tests/2.txt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="308bfdcfb801c46d41d5d24b9fe003f4" name="tests/3.txt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="76af0d7919f307177aef831e62e48640" name="tests/4.txt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="ea33106f5d970bf5dc318b752c95e343" name="tests/5.txt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="38f7931505de496d7312a830b2880f2c" name="tests/6.txt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="aa40a9c049e89eb8c6f5b0f47e1aabd9" name="tests/context.patch" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="b0143ef7d5e9a1ace0ff747165f27c1f" name="tests/context.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="2a88e4c49d51bbdced9a4bd8a8a657c5" name="tests/context2.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="43e8b69ff75bc0e7ecab5b4e87827d19" name="tests/diff.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="2c2f13e357f691f3922d07f6e70fa43d" name="tests/inline.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="05e94365c047905009b1913a3600036f" name="tests/inline2.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="e05b5cf54cfaf541663fa802724e9549" name="tests/pear_bug12740.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="7949426acc1e64d6c10338bf6382257d" name="tests/pear_bug4879.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="7fb3cda23ca78409f72ac233784ff1a1" name="tests/pear_bug6251.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="c4d40a654a584d3f939dca33fff0a5c4" name="tests/pear_bug6428.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="e7011d4030f4a020d24efb6f1673a1bc" name="tests/string.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="359096964862ff26e28cc44609ca237f" name="tests/unified.patch" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="77977a033a539c5ccadaf728b95a1a2c" name="tests/unified.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="dab2830cf62f341f1046b883948c1851" name="tests/unified2.phpt" role="test" />
|
||||
<file baseinstalldir="/Text" md5sum="3f715ec0e86162bddc1ea794a04aa4d3" name="Diff.php" role="php" />
|
||||
<file baseinstalldir="/Text" md5sum="7b57773de584d2408214cb2c7a0d0a2e" name="Diff3.php" role="php" />
|
||||
</dir>
|
||||
</contents>
|
||||
<dependencies>
|
||||
<required>
|
||||
<php>
|
||||
<min>4.2.0</min>
|
||||
</php>
|
||||
<pearinstaller>
|
||||
<min>1.4.0b1</min>
|
||||
</pearinstaller>
|
||||
</required>
|
||||
<optional>
|
||||
<extension>
|
||||
<name>xdiff</name>
|
||||
</extension>
|
||||
</optional>
|
||||
</dependencies>
|
||||
<phprelease />
|
||||
<changelog>
|
||||
<release>
|
||||
<version>
|
||||
<release>1.0.0</release>
|
||||
<api>1.0.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>stable</release>
|
||||
<api>stable</api>
|
||||
</stability>
|
||||
<date>2008-01-31</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Stable release; final non-bugfix PHP 4 release.</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.3.2</release>
|
||||
<api>0.3.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2007-01-04</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Fixed assertion in the native engine with PHP 5 (PEAR Bug #12740).</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.3.1</release>
|
||||
<api>0.3.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2007-10-28</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Fixed off-by-one errors with line numbers in default renderer output (Milian Wolff, PEAR Bugs #7839, #12111).
|
||||
* Restore compatibility with PHP 4 in the shell diff engine.</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.3.0</release>
|
||||
<api>0.3.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2007-09-20</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Converted to package.xml 2.0.
|
||||
* Added context diff renderer.
|
||||
* Stripped nul characters to avoid infinite loops (PEAR Bug #7921).
|
||||
* Fix assertion with PHP 5 (PEAR Bug #10423).
|
||||
* Add shell diff engine (Milian Wolff, PEAR Request #12051).</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.2.1</release>
|
||||
<api>0.2.1</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2006-02-06</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Fix Text_Diff::reverse() method with PHP 5 (PEAR Bug #6699).</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.2.0</release>
|
||||
<api>0.2.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2006-01-06</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Fix reference warnings with PHP 4.4 and PHP 5.1.
|
||||
* Fix too many trailing context lines for the last diff chunk. (PEAR Bug #6251).
|
||||
* Add diff engine for existing diffs/patches (Örjan Persson, PEAR Request #5373).
|
||||
* Fix problems with numbers, whitespace-only changes and the inline renderer (PEAR Bug #6428.</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.1.1</release>
|
||||
<api>0.1.1</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2005-08-19</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Restore compatibility with PHP 4.2.0 (PEAR Bug #5152).</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.1.0</release>
|
||||
<api>0.1.0</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2005-08-16</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Fix Renderer::getParams() (PEAR Bug #4549)
|
||||
* Encode HTML entities in the inline renderer (Horde Bug #2181)
|
||||
* Fix endless loop in inline renderer with certain whitespaces (PEAR Bug #4879)</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.0.5</release>
|
||||
<api>0.0.5</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2005-05-04</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Fixed variable name in Text_MappedDiff.
|
||||
* Removed output buffering code.
|
||||
* Fixed additional empty lines being added with the inline renderer (Bug 3730).
|
||||
* Fixed multiple calls to _endBlock per block (Bug 4065).
|
||||
</notes>
|
||||
<deps>
|
||||
<dep type="php" rel="ge" version="4.2.0"/>
|
||||
<dep type="ext" rel="has" optional="yes">xdiff</dep>
|
||||
</deps>
|
||||
<provides type="class" name="Text_Diff" />
|
||||
<provides type="class" name="Text_Diff3" />
|
||||
<provides type="class" name="Text_MappedDiff" />
|
||||
<provides type="class" name="Text_Diff_Renderer" />
|
||||
<provides type="class" name="Text_Diff_Engine_xdiff" />
|
||||
<provides type="class" name="Text_Diff_Engine_native" />
|
||||
<provides type="class" name="Text_Diff_Op" />
|
||||
<provides type="class" name="Text_Diff_Op_copy" extends="Text_Diff_Op" />
|
||||
<provides type="class" name="Text_Diff_Op_delete" extends="Text_Diff_Op" />
|
||||
<provides type="class" name="Text_Diff_Op_add" extends="Text_Diff_Op" />
|
||||
<provides type="class" name="Text_Diff_Op_change" extends="Text_Diff_Op" />
|
||||
<provides type="function" name="Text_Diff::getDiff" />
|
||||
<provides type="function" name="Text_Diff::reverse" />
|
||||
<provides type="function" name="Text_Diff::isEmpty" />
|
||||
<provides type="function" name="Text_Diff::lcs" />
|
||||
<provides type="function" name="Text_Diff::getOriginal" />
|
||||
<provides type="function" name="Text_Diff::getFinal" />
|
||||
<provides type="function" name="Text_Diff_Engine_xdiff::diff" />
|
||||
<provides type="function" name="Text_Diff_Engine_native::diff" />
|
||||
<provides type="function" name="Text_Diff_Op::reverse" />
|
||||
<provides type="function" name="Text_Diff_Op::norig" />
|
||||
<provides type="function" name="Text_Diff_Op::nfinal" />
|
||||
<provides type="function" name="Text_Diff_Op_copy::reverse" />
|
||||
<provides type="function" name="Text_Diff_Op_delete::reverse" />
|
||||
<provides type="function" name="Text_Diff_Op_add::reverse" />
|
||||
<provides type="function" name="Text_Diff_Op_change::reverse" />
|
||||
<provides type="class" name="Text_Diff3_Op" />
|
||||
<provides type="class" name="Text_Diff3_Op_copy" extends="Text_Diff3_Op" />
|
||||
<provides type="class" name="Text_Diff3_BlockBuilder" />
|
||||
<provides type="function" name="Text_Diff3::mergedOutput" />
|
||||
<provides type="function" name="Text_Diff3_Op::merged" />
|
||||
<provides type="function" name="Text_Diff3_Op::isConflict" />
|
||||
<provides type="function" name="Text_Diff3_Op_copy::merged" />
|
||||
<provides type="function" name="Text_Diff3_Op_copy::isConflict" />
|
||||
<provides type="function" name="Text_Diff3_BlockBuilder::input" />
|
||||
<provides type="function" name="Text_Diff3_BlockBuilder::out1" />
|
||||
<provides type="function" name="Text_Diff3_BlockBuilder::out2" />
|
||||
<provides type="function" name="Text_Diff3_BlockBuilder::isEmpty" />
|
||||
<provides type="function" name="Text_Diff3_BlockBuilder::finish" />
|
||||
<provides type="function" name="Text_Diff_Renderer::getParams" />
|
||||
<provides type="function" name="Text_Diff_Renderer::render" />
|
||||
<provides type="class" name="Text_Diff_Renderer_inline" extends="Text_Diff_Renderer" />
|
||||
<provides type="class" name="Text_Diff_Renderer_unified" extends="Text_Diff_Renderer" />
|
||||
<filelist>
|
||||
<file role="php" baseinstalldir="/Text" md5sum="16ca4ec69bf44ecd1467ebb5ee70b5df" name="Diff.php"/>
|
||||
<file role="php" baseinstalldir="/Text" md5sum="7a1c6842b8b25822db3e37e5b9663da1" name="Diff3.php"/>
|
||||
<file role="php" baseinstalldir="/Text" md5sum="a32f238fd2bf80cce18a745df89d7e43" name="Diff/Renderer.php"/>
|
||||
<file role="php" baseinstalldir="/Text" md5sum="82ea5ce1668a6bc38ee7ece1020433b9" name="Diff/Renderer/inline.php"/>
|
||||
<file role="php" baseinstalldir="/Text" md5sum="37211108109573d335251c690293d7b9" name="Diff/Renderer/unified.php"/>
|
||||
<file role="doc" baseinstalldir="/Text" md5sum="d0e483e9f20738417d7a2a4fa56e074f" name="docs/examples/diff.php"/>
|
||||
<file role="doc" baseinstalldir="/Text" md5sum="5fbdf5388905ed07026b876126aeee5a" name="docs/examples/1.txt"/>
|
||||
<file role="doc" baseinstalldir="/Text" md5sum="011c4dad4506a4843ded1f12cab69909" name="docs/examples/2.txt"/>
|
||||
<file role="test" baseinstalldir="/Text" md5sum="5fbdf5388905ed07026b876126aeee5a" name="tests/1.txt"/>
|
||||
<file role="test" baseinstalldir="/Text" md5sum="011c4dad4506a4843ded1f12cab69909" name="tests/2.txt"/>
|
||||
<file role="test" baseinstalldir="/Text" md5sum="adbb8551c010b970f9feb184a023c79d" name="tests/diff.phpt"/>
|
||||
<file role="test" baseinstalldir="/Text" md5sum="7c2df75e9afc32c9fe4546f0322ebb0d" name="tests/inline.phpt"/>
|
||||
<file role="test" baseinstalldir="/Text" md5sum="3b1e784b27e9215081ec65340bc7621a" name="tests/inline2.phpt"/>
|
||||
<file role="test" baseinstalldir="/Text" md5sum="b8032a56a1ca4086caa4e90e76f1c486" name="tests/unified.phpt"/>
|
||||
<file role="test" baseinstalldir="/Text" md5sum="f71a1255bdc21a43f03ee070df7c99b9" name="tests/unified2.phpt"/>
|
||||
</filelist>
|
||||
</release>
|
||||
<changelog>
|
||||
<release>
|
||||
<version>0.0.4</version>
|
||||
<date>2004-10-13</date>
|
||||
<state>beta</state>
|
||||
<notes>* Generalized Diff Renderer parameter handling.
|
||||
* Added an inline renderer contributed by Ciprian Popovici (ciprian@zuavra.net).
|
||||
* Fixed PHPDoc.
|
||||
* Fixed a PHP 5 incompatibility.
|
||||
</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>0.0.3</version>
|
||||
<date>2004-05-13</date>
|
||||
<state>alpha</state>
|
||||
<notes>Renamed final() and orig() methods to avoid conflict with final keyword in PHP5
|
||||
</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>0.0.2</version>
|
||||
<date>2004-03-16</date>
|
||||
<state>alpha</state>
|
||||
<notes>Added optional xdiff support
|
||||
</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>0.0.1</version>
|
||||
<date>2004-01-09</date>
|
||||
<state>alpha</state>
|
||||
<notes>Initial package release
|
||||
</notes>
|
||||
</release>
|
||||
* Fixed multiple calls to _endBlock per block (Bug 4065).</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.0.4</release>
|
||||
<api>0.0.4</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>beta</release>
|
||||
<api>beta</api>
|
||||
</stability>
|
||||
<date>2004-10-13</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>* Generalized Diff Renderer parameter handling.
|
||||
* Added an inline renderer contributed by Ciprian Popovici (ciprian@zuavra.net).
|
||||
* Fixed PHPDoc.
|
||||
* Fixed a PHP 5 incompatibility.</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.0.3</release>
|
||||
<api>0.0.3</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>alpha</release>
|
||||
<api>alpha</api>
|
||||
</stability>
|
||||
<date>2004-05-13</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>Renamed final() and orig() methods to avoid conflict with final keyword in PHP5</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.0.2</release>
|
||||
<api>0.0.2</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>alpha</release>
|
||||
<api>alpha</api>
|
||||
</stability>
|
||||
<date>2004-03-16</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>Added optional xdiff support</notes>
|
||||
</release>
|
||||
<release>
|
||||
<version>
|
||||
<release>0.0.1</release>
|
||||
<api>0.0.1</api>
|
||||
</version>
|
||||
<stability>
|
||||
<release>alpha</release>
|
||||
<api>alpha</api>
|
||||
</stability>
|
||||
<date>2004-01-09</date>
|
||||
<license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
|
||||
<notes>Initial package release</notes>
|
||||
</release>
|
||||
</changelog>
|
||||
</package>
|
||||
@@ -1 +1 @@
|
||||
<html>␍<head><title>Invision Power Board - Bulletin Board System</title></head>␍<body>␍<h1>403: Invision Power Board -> Forbidden</h1>␍<hr>␍You have reached this Page in error, please use␍your back button to return to Invision Board.␍<hr>␍<a href="http://anonym.to/http://www.invisionboard.com/">Invision Power Board</a>␍</body>␍</html>
|
||||
<html>␍<head><title>IP.Board - Bulletin Board System</title></head>␍<body>␍<h1>403: IP.Board -> Forbidden</h1>␍<hr>␍You have reached this page in error, please use your back button to return to the forum.␍<hr>␍<a href="http://www.invisionboard.com/">IP.Board</a>␍</body>␍</html>␍
|
||||
Reference in new issue
Block a user