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;>&nbsp;</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>';
}?>