Plugin index
<?phpif (@txpinterface == 'admin') {
add_privs('rvm_plugin_diff', '1,2');
register_tab('extensions', 'rvm_plugin_diff', 'Plugin Diff');
register_callback('rvm_plugin_diff', 'rvm_plugin_diff');
}
/**
Diff routine uses some parts of the code found at this address:
http://www.holomind.de/phpnet/diff2.src.php
Copyright (C) 2003 Daniel Unterberger <diff.phpnet@holomind.de>
Copyright (C) 2005 Nils Knappmeier (next version)
License: GNU General Public License version 2 or later
http://www.gnu.org/licenses/gpl.html
**/
function rvm_plugin_diff($event, $step) {
pagetop('Plugin Diff');
$name = gps('name');
$nospace = gps('nospace') ? 1 : 0;
$context = is_numeric(gps('context')) ? intval(gps('context')) : '5';
$plugins = array();
$mplugins = safe_column('name', 'txp_plugin', 'code != code_restore');
$oplugins = safe_column('name', 'txp_plugin', 'code = code_restore');
asort($oplugins); asort($mplugins);
if ($mplugins) {
$plugins[] = ' ';
if ($oplugins) $plugins[] = ' ----- MODIFIED ----- ';
foreach($mplugins as $plugin) $plugins[] = $plugin;
}
if ($oplugins) {
$plugins[] = ' ';
if ($mplugins) $plugins[] = ' ----- ORIGINAL ----- ';
foreach($oplugins as $plugin) $plugins[] = $plugin;
}
if (isset($plugins[$name])) $name = $plugins[$name];
echo '<div style="border:1px solid #ccc;background:#f7f7f7;padding:1em;margin-left:2em;float:left;">';
echo form(
'<p>Plugin '.selectInput('name', $plugins, $name, 0).'</p>'.
'<p>Show <input type="text" name="context" maxlength="4" size="3" value="'.$context.'" class="edit" /> lines around each change</p>'.
'<p>'.checkbox('nospace','ignore',$nospace).' Ignore white space changes</p>'.
eInput('rvm_plugin_diff').
fInput('submit','','Show Changes','publish')
);
echo '</div>';
echo '<br style="clear:both" />';
$rs = safe_row('code,code_restore,name,version', 'txp_plugin', "name='".safe_escape($name)."'");
if (!$rs) return;
extract($rs);
unset($rs);
# force Unix-style line breaks
$code_restore = str_replace("\r\n", n, $code_restore);
$code_restore = str_replace("\r", n, $code_restore);
$code = str_replace("\r\n", n, $code);
$code = str_replace("\r", n, $code);
# split the source text into arrays of lines
$t1 = explode(n,trim($code_restore));
$t2 = explode(n,trim($code));
unset($code_restore,$code);
# build a reverse-index array using the line as key and line number as value
foreach($t1 as $i=>$x) $r1[$x][] = $i;
foreach($t2 as $i=>$x) $r2[$x][] = $i;
$a1=0; $a2=0; # start at beginning of each list
$actions = array();
$is_diff = false;
# walk this loop until we reach the end of one of the lists
while ($a1<count($t1) && $a2<count($t2)) {
# if we have a common element, save it and go to the next
if ($nospace ? trim($t1[$a1])==trim($t2[$a2]) : $t1[$a1]==$t2[$a2])
{ $actions[]=0; $a1++; $a2++; continue; }
# otherwise, find the shortest move (Manhattan-distance) from the
# current location
$is_diff = true;
$best1=count($t1); $best2=count($t2);
$s1=$a1; $s2=$a2;
while($s1+$s2 < $best1+$best2) {
$d=-1;
foreach((array)@$r1[$t2[$s2]] as $n)
if ($n>=$s1) { $d=$n; break; }
if ($d>=$s1 and $d+$s2 < $best1+$best2)
{ $best1=$d; $best2=$s2; }
$d=-1;
foreach((array)@$r2[$t1[$s1]] as $n)
if ($n>=$s2) { $d=$n; break; }
if ($d>=$s2 and $s1+$d < $best1+$best2)
{ $best1=$s1; $best2=$d; }
$s1++; $s2++;
}
while ($a1<$best1) { $actions[]=1; $a1++; } # deleted elements
while ($a2<$best2) { $actions[]=2; $a2++; } # added elements
}
unset($r1,$r2);
# we've reached the end of one list, now walk to the end of the other
while($a1<count($t1)) { $actions[]=1; $a1++; } # deleted elements
while($a2<count($t2)) { $actions[]=2; $a2++; } # added elements
# create $lines array with all needed info for further processing
$i1=$i2=0;
$lines = array();
foreach($actions as $act) {
$line = ($act==1 ? $t1[$i1] : $t2[$i2]);
$line_old = ($act==2 ? 0 : ++$i1);
$line_new = ($act==1 ? 0 : ++$i2);
$lines[] = array($line_old, $line_new, $line, 2);
}
# remember how much space the line numbers will need
$n1 = strlen(count($t1)); if ($n1 < 3) $n1 = 3; $n1++;
$n2 = strlen(count($t2)); if ($n2 < 3) $n2 = 3; $n2++;
unset($t1,$t2,$actions);
# mark lines that are needed to provide context
if ($is_diff) {
if (trim($context) != '') {
$count = 0;
foreach($lines as $nr => $line) {
if ($line[0] and $line[1]) {
if ($count) $count--;
else $lines[$nr][3]--;
}
else $count = $context;
}
$count = 0;
for($nr = count($lines)-1; $nr >= 0; $nr--) {
if ($lines[$nr][0] and $lines[$nr][1]) {
if ($count) $count--;
else $lines[$nr][3]--;
}
else $count = $context;
}
}
# unified diff
if (gps('format')) {
$udiff = '';
$diff = array(0,0,0,0);
foreach($lines as $line) {
if (!$line[3]) {
if ($out) {
$udiff .= $diff[4].$out;
$out = '';
$diff = array(0,0,0,0);
}
}
else {
if (!$diff[0]) $diff[0] = $line[0];
if (!$diff[1]) $diff[1] = $line[1];
if ($line[0]) $diff[2] = $line[0];
if ($line[1]) $diff[3] = $line[1];
$diff[4] = '@@ -'.$diff[0].','.($diff[2]+1-$diff[0]).' +'.$diff[1].','.($diff[3]+1-$diff[1]).' @@'.n;
$out .= (!$line[1] ? '-' : (!$line[0] ? '+' : ' ')) . $line[2] . n;
}
}
if ($out) $udiff .= $diff[4].$out;
# send unified diff file
if ($udiff) {
$plugin = $name.'-'.$version;
$time = strftime(' %Y-%m-%d %H:%M:%S.000000000 %z',time());
$udiff = '--- '.$plugin.'.orig'.$time.n.'+++ '.$plugin.'.new'.$time.n.$udiff;
ob_clean();
header('Content-Description: Plugin Diff');
header('Content-Type: text/plain;charset=utf-8');
header('Content-Length: ' . strlen($udiff));
header('Content-Disposition: attachment; filename="' . $plugin . '.diff"');
echo $udiff;
ob_flush();
flush();
exit;
}
}
# add inline diff markers
$ins = $del = array();
foreach($lines as $nr => $line) {
if ($line[0] and $line[1]) {
if ($del and count($del) == count($ins)) {
while($delnr = array_shift($del) and $insnr = array_shift($ins)) {
$begin = 0; $end = 0;
$delstr = $lines[$delnr][2];
$insstr = $lines[$insnr][2];
$max = min(strlen($insstr),strlen($delstr));
while($begin <= $max and substr($delstr,$begin,1) == substr($insstr,$begin,1)) $begin++;
while($end <= $max and substr($delstr,-$end-1,1) == substr($insstr,-$end-1,1)) $end++;
if (!$begin and !$end) continue;
foreach(array($delnr,$insnr) as $linenr) {
$str = $lines[$linenr][2];
$len = strlen($str) - $begin - $end;
if ($len < 0) { $end += $len; $len = 0; }
$lines[$linenr][2] = substr($str,0,$begin)."\r".substr($str,$begin,$len)."\r".($end?substr($str,-$end):'');
}
}
}
$ins = $del = array();
}
else {
if ($line[0]) $del[] = $nr;
else $ins[] = $nr;
}
}
}
# download link
if ($is_diff) echo '<p style="margin-left:3em"><a href="?event=rvm_plugin_diff'.a.'format=diff'.a.'name='.urlencode($name).a.'context='.$context.a.'nospace='.$nospace.'">Download in <strong>unified diff</strong> format</a></p>';
# color diff
$lstyle = ' style="color: #999; border-right:1px solid #ccc;padding:0em .5em 0em 0em;"';
echo '<pre>';
if ($is_diff) {
echo "<span$lstyle>".str_pad('Old', $n1, ' ', STR_PAD_LEFT).'</span>';
echo "<span$lstyle>".str_pad('New', $n2, ' ', STR_PAD_LEFT).'</span>'.n;
}
$skip = false;
foreach($lines as $line) {
if (!$line[3]) {
# empty space between diff parts
if (!$skip) echo '<div style="background:#f7f7f7;height:2em;border:1px solid #ccc";border-right:none;> </div>';
$skip = true;
continue;
}
else $skip = false;
if ($is_diff) echo "<span$lstyle>".str_pad(($line[0] ? $line[0] : ''),$n1,' ',STR_PAD_LEFT).'</span>';
echo "<span$lstyle>".str_pad(($line[1] ? $line[1] : ''),$n2,' ',STR_PAD_LEFT).'</span>';
if (!$line[1]) echo '<span style="background: #fdd;">';
if (!$line[0]) echo '<span style="background: #dfd;">';
$str = str_replace("\t",' ',htmlspecialchars($line[2]));
if (!$line[1] or !$line[0]) {
# color inline diff
$color = $line[0] ? '#eaa' : '#9e9';
$str = preg_replace('/\r(.*)\r/','<span style="background:'.$color.'">${1}</span>',$str);
}
echo ' '.$str;
if (!$line[0] or !$line[1]) echo '</span>';
echo n;
}
echo '</pre>';
}?>