aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbuild/build_diff.php408
-rw-r--r--build/build_helper.php485
-rw-r--r--build/diff_class.php1677
-rwxr-xr-xbuild/package.php521
4 files changed, 3091 insertions, 0 deletions
diff --git a/build/build_diff.php b/build/build_diff.php
new file mode 100755
index 0000000000..ab3bb774b7
--- /dev/null
+++ b/build/build_diff.php
@@ -0,0 +1,408 @@
+#!/usr/bin/env php
+<?php
+/**
+*
+* @package build
+* @version $Id$
+* @copyright (c) 2010 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+// CONFIG - Begin
+$substitute_old = '3.0.6';
+$substitute_new = '3.0.7-RC1';
+$simple_name_old = 'phpbb306';
+$simple_name_new = 'phpbb307-RC1';
+$echo_changes = false;
+// Set this to true to just compress the changes and do not build them again
+// This should be used for building custom modified txt file. ;)
+$package_changed_files = false;
+// CONFIG - End
+
+//$debug_file = 'includes/functions_user.php'; //'styles/prosilver/style.cfg';
+$debug_file = false;
+
+if ($debug_file !== false)
+{
+ $echo_changes = false;
+}
+
+$s_name = 'save_' . $substitute_old . '_to_' . $substitute_new;
+
+$location = dirname(__FILE__);
+
+if (!$package_changed_files)
+{
+ if (!$echo_changes)
+ {
+ // Cleanup...
+ run_command("rm -R $location/save/*");
+
+ // Create directory...
+ run_command("mkdir $location/save/{$s_name}");
+ run_command("mkdir $location/save/{$s_name}/language");
+ run_command("mkdir $location/save/{$s_name}/prosilver");
+ run_command("mkdir $location/save/{$s_name}/subsilver2");
+ }
+}
+
+// Build code changes and place them into 'save'
+if (!$package_changed_files)
+{
+ build_code_changes('language');
+ build_code_changes('prosilver');
+ build_code_changes('subsilver2');
+}
+
+// Package code changes
+$code_changes_filename = 'phpBB-' . $substitute_old . '_to_' . $substitute_new . '-codechanges';
+
+if (!$echo_changes)
+{
+ // Now compress the files...
+ // Build Main phpBB Release
+ $compress_programs = array(
+// 'tar.gz' => 'tar -czf',
+ 'tar.bz2' => 'tar -cjf',
+ 'zip' => 'zip -r'
+ );
+
+ chdir($location . '/save/' . $s_name);
+ foreach ($compress_programs as $extension => $compress_command)
+ {
+ echo "Packaging code changes for $extension\n";
+ run_command("rm ./../../release_files/{$code_changes_filename}.{$extension}");
+ flush();
+
+ // Build Package
+ run_command("$compress_command ./../../release_files/{$code_changes_filename}.{$extension} *");
+
+ // Build MD5 Sum
+ run_command("md5sum ./../../release_files/{$code_changes_filename}.{$extension} > ./../../release_files/{$code_changes_filename}.{$extension}.md5");
+ flush();
+ }
+}
+
+/**
+* $output_format can be: language, prosilver and subsilver2
+*/
+function build_code_changes($output_format)
+{
+ global $substitute_new, $substitute_old, $simple_name_old, $simple_name_new, $echo_changes, $package_changed_files, $location, $debug_file, $s_name;
+
+ // Global array holding the data entries
+ $data = array(
+ 'header' => array(),
+ 'diff' => array(),
+ );
+
+ // Read diff file and prepare the output filedata...
+ //$patch_filename = '../new_version/patches/phpBB-' . $substitute_old . '_to_' . $substitute_new . '.patch';
+ $release_filename = 'phpbb-' . $substitute_old . '_to_' . $substitute_new . '_' . $output_format . '.txt';
+
+ if (!$package_changed_files)
+ {
+ if (!$echo_changes)
+ {
+ $fp = fopen('save/' . $s_name . '/' . $output_format . '/' . $release_filename, 'wb');
+
+ if (!$fp)
+ {
+ die('Unable to create ' . $release_filename);
+ }
+ }
+ }
+
+ include_once($location . '/build_helper.php');
+ $package = new build_package(array($substitute_old, $substitute_new), false);
+
+ $titles = array(
+ 'language' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' Language Pack Changes',
+ 'prosilver' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' prosilver Changes',
+ 'subsilver2' => 'phpBB ' . $substitute_old . ' to phpBB ' . $substitute_new . ' subsilver2 Changes',
+ );
+
+ $data['header'] = array(
+ 'title' => $titles[$output_format],
+ 'intro' => '
+
+These are the ' . $titles[$output_format] . ' summed up into a little Mod. These changes are only partial and do not include any code changes, therefore not meant for updating phpBB.
+
+ ',
+ 'included_files' => array(),
+ );
+
+ // We collect the files we want to diff first (ironically we grab this from a diff file)
+ if (!$echo_changes)
+ {
+ echo "\n\nCollecting Filenames:";
+ }
+
+ // We re-create the patch file
+ foreach ($package->old_packages as $_package_name => $dest_package_filename)
+ {
+ chdir($package->locations['old_versions']);
+
+ if (!$echo_changes)
+ {
+ echo "\n\n" . 'Creating patch/diff files for phpBB-' . $dest_package_filename . $package->get('new_version_number');
+ }
+
+ $dest_package_filename = $location . '/save/' . $s_name . '/phpBB-' . $dest_package_filename . $package->get('new_version_number') . '.patch';
+ $package->run_command('diff ' . $package->diff_options . ' ' . $_package_name . ' ' . $package->get('simple_name') . ' > ' . $dest_package_filename);
+
+ // Parse this diff to determine file changes from the checked versions and save them
+ $result = $package->collect_diff_files($dest_package_filename, $_package_name);
+ $package->run_command('rm ' . $dest_package_filename);
+ }
+
+ chdir($location);
+
+ $filenames = array();
+ foreach ($result['files'] as $filename)
+ {
+ if ($debug_file !== false && $filename != $debug_file)
+ {
+ continue;
+ }
+
+ // Decide which files to compare...
+ switch ($output_format)
+ {
+ case 'language':
+ if (strpos($filename, 'language/en/') !== 0)
+ {
+ continue 2;
+ }
+ break;
+
+ case 'prosilver':
+ if (strpos($filename, 'styles/prosilver/') !== 0)
+ {
+ continue 2;
+ }
+ break;
+
+ case 'subsilver2':
+ if (strpos($filename, 'styles/subsilver2/') !== 0)
+ {
+ continue 2;
+ }
+ break;
+ }
+
+ if (!file_exists($location . '/old_versions/' . $simple_name_old . '/' . $filename))
+ {
+ // New file... include it
+ $data['header']['included_files'][] = array(
+ 'old' => $location . '/old_versions/' . $simple_name_old . '/' . $filename,
+ 'new' => $location . '/old_versions/' . $simple_name_new . '/' . $filename,
+ 'phpbb_filename' => $filename,
+ );
+ continue;
+ }
+
+ $filenames[] = array(
+ 'old' => $location . '/old_versions/' . $simple_name_old . '/' . $filename,
+ 'new' => $location . '/old_versions/' . $simple_name_new . '/' . $filename,
+ 'phpbb_filename' => $filename,
+ );
+ }
+
+ // Now let us go through the filenames list and create a more comprehensive diff
+ if (!$echo_changes)
+ {
+ fwrite($fp, build_header($output_format, $filenames, $data['header']));
+ }
+ else
+ {
+ //echo build_header('text', $filenames, $data['header']);
+ }
+
+ // Copy files...
+ $files_to_copy = array();
+
+ foreach ($data['header']['included_files'] as $filename)
+ {
+ $files_to_copy[] = $filename['phpbb_filename'];
+ }
+
+ // First step is to copy the new version over (clean structure)
+ if (!$echo_changes && sizeof($files_to_copy))
+ {
+ foreach ($files_to_copy as $file)
+ {
+ // Create directory?
+ $dirname = dirname($file);
+
+ if ($dirname)
+ {
+ $dirname = explode('/', $dirname);
+ $__dir = array();
+
+ foreach ($dirname as $i => $dir)
+ {
+ $__dir[] = $dir;
+ run_command("mkdir -p $location/save/" . $s_name . '/' . $output_format . '/' . implode('/', $__dir));
+ }
+ }
+
+ $source_file = $location . '/new_version/phpBB3/' . $file;
+ $dest_file = $location . '/save/' . $s_name . '/' . $output_format . '/';
+ $dest_file .= $file;
+
+ $command = "cp -p $source_file $dest_file";
+ $result = trim(`$command`);
+ echo "- Copied File: " . $source_file . " -> " . $dest_file . "\n";
+ }
+ }
+
+ include_once('diff_class.php');
+
+ if (!$echo_changes)
+ {
+ echo "\n\nDiffing Codebases:";
+ }
+
+ foreach ($filenames as $file_ary)
+ {
+ if (!file_exists($file_ary['old']))
+ {
+ $lines1 = array();
+ }
+ else
+ {
+ $lines1 = file($file_ary['old']);
+ }
+ $lines2 = file($file_ary['new']);
+
+ if (!sizeof($lines1))
+ {
+ // New File
+ }
+ else
+ {
+ $diff = new Diff($lines1, $lines2);
+ $fmt = new BBCodeDiffFormatter(false, 5, $debug_file);
+
+ if (!$echo_changes)
+ {
+ fwrite($fp, $fmt->format_open($file_ary['phpbb_filename']));
+ fwrite($fp, $fmt->format($diff, $lines1));
+ fwrite($fp, $fmt->format_close($file_ary['phpbb_filename']));
+ }
+ else
+ {
+ echo $fmt->format_open($file_ary['phpbb_filename']);
+ echo $fmt->format($diff, $lines1);
+ echo $fmt->format_close($file_ary['phpbb_filename']);
+ }
+
+ if ($debug_file !== false)
+ {
+ echo $fmt->format_open($file_ary['phpbb_filename']);
+ echo $fmt->format($diff, $lines1);
+ echo $fmt->format_close($file_ary['phpbb_filename']);
+ exit;
+ }
+ }
+ }
+
+ if (!$echo_changes)
+ {
+ fwrite($fp, build_footer($output_format));
+
+ // Close file
+ fclose($fp);
+
+ chmod('save/' . $s_name . '/' . $output_format . '/' . $release_filename, 0666);
+ }
+ else
+ {
+ echo build_footer($output_format);
+ }
+}
+
+/**
+* Build Footer
+*/
+function build_footer($mode)
+{
+ $html = '';
+
+ $html .= "# \n";
+ $html .= "#-----[ SAVE/CLOSE ALL FILES ]------------------------------------------ \n";
+ $html .= "# \n";
+ $html .= "# EoM";
+
+ return $html;
+}
+
+/**
+* Build Header
+*/
+function build_header($mode, $filenames, $header)
+{
+ global $substitute_old;
+
+ $html = '';
+
+ $html .= "############################################################## \n";
+ $html .= "## Title: " . $header['title'] . "\n";
+ $html .= "## Author: naderman < naderman@phpbb.com > (Nils Adermann) http://www.phpbb.com \n";
+ $html .= "## Description: \n";
+
+ $intr = explode("\n", $header['intro']);
+ $introduction = '';
+ foreach ($intr as $_line)
+ {
+ $introduction .= wordwrap($_line, 80) . "\n";
+ }
+ $intr = explode("\n", $introduction);
+
+ foreach ($intr as $_line)
+ {
+ $html .= "## " . $_line . "\n";
+ }
+ $html .= "## \n";
+ $html .= "## Files To Edit: \n";
+
+ foreach ($filenames as $file_ary)
+ {
+ $html .= "## " . $file_ary['phpbb_filename'] . "\n";
+ }
+ $html .= "##\n";
+ if (sizeof($header['included_files']))
+ {
+ $html .= "## Included Files: \n";
+ foreach ($header['included_files'] as $filename)
+ {
+ $html .= "## {$filename['phpbb_filename']}\n";
+ }
+ }
+ $html .= "## License: http://opensource.org/licenses/gpl-license.php GNU General Public License v2 \n";
+ $html .= "############################################################## \n";
+ $html .= "\n";
+
+ // COPY Statement?
+ if (sizeof($header['included_files']))
+ {
+ $html .= "#\n#-----[ COPY ]------------------------------------------\n#\n";
+ foreach ($header['included_files'] as $filename)
+ {
+ $html .= "copy {$filename['phpbb_filename']} to {$filename['phpbb_filename']}\n";
+ }
+ $html .= "\n";
+ }
+
+ return $html;
+}
+
+function run_command($command)
+{
+ $result = trim(`$command`);
+ echo "\n- Command Run: " . $command . "\n";
+}
+
+?> \ No newline at end of file
diff --git a/build/build_helper.php b/build/build_helper.php
new file mode 100644
index 0000000000..2bae32218b
--- /dev/null
+++ b/build/build_helper.php
@@ -0,0 +1,485 @@
+<?php
+/**
+*
+* @package build
+* @version $Id$
+* @copyright (c) 2010 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+class build_package
+{
+ var $package_infos;
+ var $old_packages;
+ var $versions;
+ var $locations;
+ var $clean_directory_structure;
+ var $files_to_copy;
+ var $files_to_remove;
+ var $remove_from_diff_structure;
+
+ // -c - context diff
+ // -r - compare recursive
+ // -N - Treat missing files as empty
+ // -E - Ignore tab expansions
+ // not used: -b - Ignore space changes.
+ // -w - Ignore all whitespace
+ // -B - Ignore blank lines
+ // -d - Try to find smaller set of changes
+ var $diff_options = '-crNEBwd';
+ var $diff_options_long = '-x images -crNEB'; // -x fonts -x imageset //imageset not used here, because it includes the imageset.cfg file. ;)
+
+ var $verbose = false;
+ var $status_begun = false;
+ var $num_dots = 0;
+
+ function build_package($versions, $verbose = false)
+ {
+ $this->versions = $versions;
+ $this->verbose = $verbose;
+
+ // Get last two entries
+ $_latest = $this->versions[sizeof($this->versions) - 1];
+ $_before = $this->versions[sizeof($this->versions) - 2];
+
+ $this->locations = array(
+ 'new_version' => dirname(dirname(__FILE__)) . '/phpBB/',
+ 'old_versions' => dirname(__FILE__) . '/old_versions/',
+ 'root' => dirname(__FILE__) . '/',
+ 'package_dir' => dirname(__FILE__) . '/new_version/'
+ );
+
+ $this->package_infos = array(
+ 'package_name' => 'phpBB3',
+ 'name_prefix' => 'phpbb',
+ 'simple_name' => 'phpbb' . str_replace('.', '', $_latest),
+ 'new_version_number' => $_latest,
+ 'short_version_number' => str_replace('.', '', $_latest),
+ 'release_filename' => 'phpBB-' . $_latest,
+ 'last_version' => 'phpbb' . str_replace('.', '', $_before),
+ 'last_version_number' => $_before,
+ );
+
+ $this->package_infos['dest_dir'] = $this->locations['package_dir'] . $this->package_infos['package_name'];
+ $this->package_infos['diff_dir'] = $this->locations['old_versions'] . $this->package_infos['simple_name'];
+ $this->package_infos['patch_directory'] = $this->locations['package_dir'] . 'patches';
+ $this->package_infos['files_directory'] = $this->locations['package_dir'] . 'files';
+ $this->package_infos['update_directory'] = $this->locations['package_dir'] . 'update';
+ $this->package_infos['release_directory'] = $this->locations['package_dir'] . 'release_files';
+
+ // Old packages always exclude the latest version. ;)
+ $this->old_packages = array();
+
+ foreach ($this->versions as $package_version)
+ {
+ if ($package_version == $_latest)
+ {
+ continue;
+ }
+
+ $this->old_packages['phpbb' . str_replace('.', '', $package_version)] = $package_version . '_to_';
+ }
+
+ // We need to make sure this is up to date with the latest version
+ $this->clean_directory_structure = array(
+ 'adm' => array(
+ 'images' => '',
+ 'style' => '',
+ ),
+ 'cache' => '',
+ 'docs' => '',
+ 'download' => '',
+ 'files' => '',
+ 'images' => array(
+ 'avatars' => array(
+ 'gallery' => '',
+ 'upload' => '',
+ ),
+ 'icons' => array(
+ 'misc' => '',
+ 'smile' => '',
+ ),
+ 'ranks' => '',
+ 'smilies' => '',
+ 'upload_icons' => '',
+ ),
+ 'includes' => array(
+ 'acm' => '',
+ 'acp' => array(
+ 'info' => '',
+ ),
+ 'auth' => '',
+ 'captcha' => array(
+ 'plugins' => '',
+ ),
+ 'diff' => '',
+ 'db' => '',
+ 'hooks' => '',
+ 'mcp' => array(
+ 'info' => '',
+ ),
+ 'questionnaire' => '',
+ 'search' => '',
+ 'ucp' => array(
+ 'info' => '',
+ ),
+ 'utf' => array(
+ 'data' => '',
+ ),
+ ),
+ 'install' => array(
+ 'convertors'=> '',
+ 'schemas' => '',
+// 'data' => '',
+ ),
+ 'language' => array(
+ 'en' => array(
+ 'acp' => '',
+ 'email' => '',
+ 'mods' => '',
+ ),
+ ),
+ 'store' => '',
+ 'styles' => array(
+ 'subsilver2' => array(
+ 'imageset' => array(
+ 'en' => '',
+ ),
+ 'template' => '',
+ 'theme' => array(
+ 'images' => '',
+ ),
+ ),
+ 'prosilver' => array(
+ 'imageset' => array(
+ 'en' => '',
+ ),
+ 'template' => '',
+ 'theme' => array(
+ 'images' => '',
+ ),
+ ),
+ ),
+ );
+
+ // Files to remove (not include within package)
+ $this->files_to_remove = array(); //array('includes/utf/data/recode_cjk.php');
+
+ // Files within the main directory to copy - do not include config.php
+ $this->files_to_copy = array(
+ '.htaccess', 'common.php', 'cron.php', 'faq.php', 'feed.php', 'index.php', 'mcp.php', 'memberlist.php', 'posting.php', 'report.php',
+ 'search.php', 'style.php', 'ucp.php', 'viewforum.php', 'viewonline.php', 'viewtopic.php'
+ );
+
+ // These files/directories will be removed and not used for creating the patch files
+ $this->remove_from_diff_structure = array(
+ 'config.php', 'cache', 'docs', 'files', 'install', 'store', 'develop'
+ );
+
+ // Writeable directories
+ $this->writeable = array('cache', 'store', 'images/avatars/upload', 'files');
+
+ // Fill the rest of the files_to_copy array
+ foreach ($this->clean_directory_structure as $cur_dir => $dir_struct)
+ {
+ $this->_fill_files_to_copy($this->locations['new_version'] . $cur_dir, $cur_dir, $dir_struct);
+ }
+ }
+
+ function get($var)
+ {
+ return $this->package_infos[$var];
+ }
+
+ function _fill_files_to_copy($directory, $cur_dir, $dir_struct)
+ {
+ $dh = opendir($directory);
+
+ while ($file = readdir($dh))
+ {
+ if (is_file($directory . '/' . $file) && $file != '.' && $file != '..')
+ {
+ $_loc = str_replace($this->locations['new_version'], '', $directory . '/' . $file);
+
+ if (in_array($_loc, $this->files_to_remove))
+ {
+ continue;
+ }
+
+ $this->files_to_copy[] = $cur_dir . '/' . $file;
+ }
+ }
+ closedir($dh);
+
+ if (is_array($dir_struct))
+ {
+ foreach ($dir_struct as $_cur_dir => $_dir_struct)
+ {
+ $this->_fill_files_to_copy($directory . '/' . $_cur_dir, $cur_dir . '/' . $_cur_dir, $_dir_struct);
+ }
+ }
+ }
+
+ function adjust_permissions($directory)
+ {
+ $dh = opendir($directory);
+
+ while ($file = readdir($dh))
+ {
+ if ($file == '.' || $file == '..' || $file == '.svn')
+ {
+ continue;
+ }
+
+ // If file, then 644
+ if (is_file($directory . '/' . $file))
+ {
+ chmod($directory . '/' . $file, 0644);
+ }
+ else if (is_dir($directory . '/' . $file))
+ {
+ $_loc = str_replace($this->package_infos['dest_dir'] . '/', '', $directory . '/' . $file);
+
+ // If directory is within the writeable chmod to 777, else 755
+ $mode = (in_array($_loc, $this->writeable)) ? 0777 : 0755;
+ chmod($directory . '/' . $file, $mode);
+
+ // Now traverse to the directory
+ $this->adjust_permissions($directory . '/' . $file);
+ }
+ }
+ closedir($dh);
+ }
+
+ function begin_status($headline)
+ {
+ if ($this->status_begun)
+ {
+ echo "\nDone.\n\n";
+ }
+
+ $this->num_dots = 0;
+
+ echo $headline . "\n ";
+
+ $this->status_begun = true;
+ }
+
+ function run_command($command)
+ {
+ $result = trim(`$command`);
+
+ if ($this->verbose)
+ {
+ echo " command : " . getcwd() . '$ ' . $command . "\n";
+ echo " result : " . $result . "\n";
+ }
+ else
+ {
+ if ($this->num_dots > 70)
+ {
+ echo "\n";
+ $this->num_dots = 0;
+ }
+ echo '.';
+ $this->num_dots++;
+ }
+
+ flush();
+ }
+
+ function create_directory($directory, $dir_struct)
+ {
+ if (!file_exists($directory))
+ {
+ $this->run_command("mkdir $directory");
+ }
+
+ if (is_array($dir_struct))
+ {
+ foreach ($dir_struct as $_dir => $_dir_struct)
+ {
+ $this->create_directory($directory . '/' . $_dir, $_dir_struct);
+ }
+ }
+ }
+
+ function collect_diff_files($diff_filename, $package_name)
+ {
+ $diff_result = $binary = array();
+ $diff_contents = file($diff_filename);
+
+ $special_diff_contents = array();
+
+ foreach ($diff_contents as $num => $line)
+ {
+ $line = trim($line);
+
+ if (!$line)
+ {
+ continue;
+ }
+
+ // Special diff content?
+ if (strpos($line, 'diff ' . $this->diff_options . ' ') === 0 || strpos($line, '*** ') === 0 || strpos($line, '--- ') === 0 || (strpos($line, ' Exp $') !== false && strpos($line, '$Id:') !== false))
+ {
+ $special_diff_contents[] = $line;
+ }
+ else if (strpos($line, 'diff ' . $this->diff_options . ' ') === 0 || strpos($line, '*** ') === 0 || strpos($line, '--- ') === 0 || (strpos($line, ' Exp $') !== false && strpos($line, '$Id:') !== false) || (strpos($line, ' $') !== false && strpos($line, '$Id:') !== false))
+ {
+ $special_diff_contents[] = $line;
+ }
+
+ // Is diffing line?
+ if (strstr($line, 'diff ' . $this->diff_options . ' '))
+ {
+ $next_line = $diff_contents[$num+1];
+ if (strpos($next_line, '***') === 0)
+ {
+ // *** phpbb208/admin/admin_board.php Sat Jul 10 20:16:26 2004
+ $next_line = explode("\t", $next_line);
+ $next_line = trim($next_line[0]);
+ $next_line = str_replace('*** ' . $package_name . '/', '', $next_line);
+ $diff_result[] = $next_line;
+ }
+ }
+
+ // Is binary?
+ if (preg_match('/^Binary files ' . $package_name . '\/(.*) and [a-z0-9_-]+\/\1 differ/i', $line, $match))
+ {
+ $binary[] = trim($match[1]);
+ }
+ }
+
+ // Now go through the list again and find out which files have how many changes...
+ $num_changes = array();
+
+ /* [1070] => diff -crN phpbb200/includes/usercp_avatar.php phpbb2023/includes/usercp_avatar.php
+ [1071] => *** phpbb200/includes/usercp_avatar.php Sat Jul 10 20:16:13 2004
+ [1072] => --- phpbb2023/includes/usercp_avatar.php Wed Feb 6 22:28:04 2008
+ [1073] => *** 6,12 ****
+ [1074] => ! * $Id$
+ [1075] => --- 6,12 ----
+ [1076] => *** 51,59 ****
+ [1077] => --- 51,60 ----
+ [1078] => *** 62,80 ****
+ [1079] => --- 63,108 ----
+ [1080] => *** 87,97 ****
+ */
+ while (($line = array_shift($special_diff_contents)) !== NULL)
+ {
+ $line = trim($line);
+
+ if (!$line)
+ {
+ continue;
+ }
+
+ // Is diffing line?
+ if (strstr($line, 'diff ' . $this->diff_options . ' '))
+ {
+ $next_line = array_shift($special_diff_contents);
+ if (strpos($next_line, '*** ') === 0)
+ {
+ // *** phpbb208/admin/admin_board.php Sat Jul 10 20:16:26 2004
+ $next_line = explode("\t", $next_line);
+ $next_line = trim($next_line[0]);
+ $next_line = str_replace('*** ' . $package_name . '/', '', $next_line);
+
+ $is_reached = false;
+ $prev_line = '';
+
+ while (!$is_reached)
+ {
+ $line = array_shift($special_diff_contents);
+
+ if (strpos($line, 'diff ' . $this->diff_options) === 0 || empty($special_diff_contents))
+ {
+ $is_reached = true;
+ array_unshift($special_diff_contents, $line);
+ continue;
+ }
+
+ if (strpos($line, '*** ') === 0 && strpos($line, ' ****') !== false)
+ {
+ $is_comment = false;
+ while (!(strpos($line, '--- ') === 0 && strpos($line, ' ----') !== false))
+ {
+ $line = array_shift($special_diff_contents);
+ if (strpos($line, ' Exp $') !== false || strpos($line, '$Id:') !== false)
+ {
+ $is_comment = true;
+ }
+ }
+
+ if (!$is_comment)
+ {
+ if (!isset($num_changes[$next_line]))
+ {
+ $num_changes[$next_line] = 1;
+ }
+ else
+ {
+ $num_changes[$next_line]++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Now remove those results not having changes
+ $return = array();
+
+ foreach ($diff_result as $key => $value)
+ {
+ if (isset($num_changes[$value]))
+ {
+ $return[] = $value;
+ }
+ }
+
+ foreach ($binary as $value)
+ {
+ $return[] = $value;
+ }
+
+ $diff_result = $return;
+ unset($return);
+ unset($special_diff_contents);
+
+ $result = array(
+ 'files' => array(),
+ 'binary' => array(),
+ 'all' => $diff_result,
+ );
+
+ $binary_extensions = array('gif', 'jpg', 'jpeg', 'png', 'ttf');
+
+ // Split into file and binary
+ foreach ($diff_result as $filename)
+ {
+ if (strpos($filename, '.') === false)
+ {
+ $result['files'][] = $filename;
+ continue;
+ }
+
+ $extension = explode('.', $filename);
+ $extension = array_pop($extension);
+
+ if (in_array($extension, $binary_extensions))
+ {
+ $result['binary'][] = $filename;
+ }
+ else
+ {
+ $result['files'][] = $filename;
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/build/diff_class.php b/build/diff_class.php
new file mode 100644
index 0000000000..0d7c2dcd3a
--- /dev/null
+++ b/build/diff_class.php
@@ -0,0 +1,1677 @@
+<?php
+/**
+*
+* @package build
+* @version $Id$
+* @copyright (c) 2000 Geoffrey T. Dairiki <dairiki@dairiki.org>
+* @copyright (c) 2010 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* Class used internally by WikiDiff to actually compute the diffs.
+*
+* 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 are (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
+*
+* Finally, some ideas (subdivision by NCHUNKS > 2, and some optimizations)
+* are my own.
+*/
+class _WikiDiffEngine
+{
+ var $edits; // List of editing operation to convert XV to YV.
+ var $xv = array(), $yv = array();
+
+ function _WikiDiffEngine($from_lines, $to_lines)
+ {
+ $n_from = sizeof($from_lines);
+ $n_to = sizeof($to_lines);
+
+ $endskip = 0;
+ // Ignore trailing and leading matching lines.
+ while ($n_from > 0 && $n_to > 0)
+ {
+ if ($from_lines[$n_from - 1] != $to_lines[$n_to - 1])
+ {
+ break;
+ }
+
+ $n_from--;
+ $n_to--;
+ $endskip++;
+ }
+
+ for ($skip = 0; $skip < min($n_from, $n_to); $skip++)
+ {
+ if ($from_lines[$skip] != $to_lines[$skip])
+ {
+ break;
+ }
+ }
+ $n_from -= $skip;
+ $n_to -= $skip;
+
+ $xlines = array();
+ $ylines = array();
+
+ // Ignore lines which do not exist in both files.
+ for ($x = 0; $x < $n_from; $x++)
+ {
+ $xhash[$from_lines[$x + $skip]] = 1;
+ }
+
+ for ($y = 0; $y < $n_to; $y++)
+ {
+ $line = $to_lines[$y + $skip];
+ $ylines[] = $line;
+
+ if (($this->ychanged[$y] = empty($xhash[$line])))
+ {
+ continue;
+ }
+ $yhash[$line] = 1;
+ $this->yv[] = $line;
+ $this->yind[] = $y;
+ }
+
+ for ($x = 0; $x < $n_from; $x++)
+ {
+ $line = $from_lines[$x + $skip];
+ $xlines[] = $line;
+
+ if (($this->xchanged[$x] = empty($yhash[$line])))
+ {
+ continue; // fixme? what happens to yhash/xhash when
+ // there are two identical lines??
+ }
+ $this->xv[] = $line;
+ $this->xind[] = $x;
+ }
+
+ // Find the LCS.
+ $this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
+
+ // Merge edits when possible
+ $this->_shift_boundaries($xlines, $this->xchanged, $this->ychanged);
+ $this->_shift_boundaries($ylines, $this->ychanged, $this->xchanged);
+
+ // Compute the edit operations.
+ $this->edits = array();
+
+ if ($skip)
+ {
+ $this->edits[] = $skip;
+ }
+
+ $x = 0;
+ $y = 0;
+
+ while ($x < $n_from || $y < $n_to)
+ {
+ // Skip matching "snake".
+ $x0 = $x;
+ $ncopy = 0;
+ while ($x < $n_from && $y < $n_to && !$this->xchanged[$x] && !$this->ychanged[$y])
+ {
+ ++$x;
+ ++$y;
+ ++$ncopy;
+ }
+
+ if ($x > $x0)
+ {
+ $this->edits[] = $x - $x0;
+ }
+
+ // Find deletes.
+ $x0 = $x;
+ $ndelete = 0;
+
+ while ($x < $n_from && $this->xchanged[$x])
+ {
+ ++$x;
+ ++$ndelete;
+ }
+
+ if ($x > $x0)
+ {
+ $this->edits[] = -($x - $x0);
+ }
+
+ // Find adds.
+ if (isset($this->ychanged[$y]) && $this->ychanged[$y])
+ {
+ $adds = array();
+ while ($y < $n_to && $this->ychanged[$y])
+ {
+ $adds[] = $ylines[$y++];
+ }
+ $this->edits[] = $adds;
+ }
+ }
+
+ if (!empty($endskip))
+ {
+ $this->edits[] = $endskip;
+ }
+ }
+
+ /* Divide 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 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++)
+ {
+ $_index = $flip ? $this->yv[$x] : $this->xv[$x];
+ $matches = (isset($ymatches[$_index])) ? $ymatches[$_index] : array();
+
+ if (!$matches)
+ {
+ continue;
+ }
+ reset($matches);
+
+ while (list ($junk, $y) = each($matches))
+ {
+ if (!isset($this->in_seq[$y]) || !$this->in_seq[$y])
+ {
+ $k = $this->_lcs_pos($y);
+ //if (!$k) die('assertion "!$k" failed');
+ $ymids[$k] = $ymids[$k-1];
+ break;
+ }
+ }
+
+ while (list ($junk, $y) = each($matches))
+ {
+ if ($y > $this->seq[$k-1])
+ {
+ //if ($y >= $this->seq[$k]) die('assertion failed');
+ // Optimization: this is a common case:
+ // next match is just replacing previous match.
+ $this->in_seq[$this->seq[$k]] = false;
+ $this->seq[$k] = $y;
+ $this->in_seq[$y] = 1;
+ }
+ else if (!isset($this->in_seq[$y]) || !$this->in_seq[$y])
+ {
+ $k = $this->_lcs_pos($y);
+ //if (!$k) die('assertion "!$k" failed');
+ $ymids[$k] = $ymids[$k-1];
+ }
+ }
+ }
+ }
+
+ $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
+ $ymid = $ymids[$this->lcs];
+
+ for ($n = 0; $n < $nchunks - 1; $n++)
+ {
+ $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
+ $y1 = $ymid[$n] + 1;
+ $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
+ }
+ $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
+
+ return array($this->lcs, $seps);
+ }
+
+ function _lcs_pos ($ypos)
+ {
+ $end = $this->lcs;
+ if ($end == 0 || $ypos > $this->seq[$end])
+ {
+ $this->seq[++$this->lcs] = $ypos;
+ $this->in_seq[$ypos] = 1;
+ return $this->lcs;
+ }
+
+ $beg = 1;
+ while ($beg < $end)
+ {
+ $mid = (int)(($beg + $end) / 2);
+
+ if ($ypos > $this->seq[$mid])
+ {
+ $beg = $mid + 1;
+ }
+ else
+ {
+ $end = $mid;
+ }
+ }
+
+ //if ($ypos == $this->seq[$end]) die("assertion failure");
+
+ $this->in_seq[$this->seq[$end]] = false;
+ $this->seq[$end] = $ypos;
+ $this->in_seq[$ypos] = 1;
+ return $end;
+ }
+
+ /* Find 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;
+ }
+ }
+ }
+
+ /* Adjust 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 have an excluded, identical line at the other.
+ * We are free to choose which identical line is included.
+ * `compareseq' usually chooses the one at the beginning,
+ * but usually it is cleaner to consider the following identical line
+ * to be the "change".
+ *
+ * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
+ */
+ function _shift_boundaries ($lines, &$changed, $other_changed)
+ {
+ $i = 0;
+ $j = 0;
+ $len = sizeof($lines);
+
+ while (1)
+ {
+ /*
+ * Scan forwards to find beginning of another run of changes.
+ * Also keep track of the corresponding point in the other file.
+ */
+
+ while ($i < $len && $changed[$i] == 0)
+ {
+ while ($other_changed[$j++])
+ {
+ continue;
+ }
+ $i++;
+ }
+
+ if ($i == $len)
+ {
+ break;
+ }
+
+ $start = $i;
+
+ // Find the end of this run of changes.
+ while (isset($changed[++$i]))
+ {
+ continue;
+ }
+
+ while (isset($other_changed[$j]) && $other_changed[$j])
+ {
+ $j++;
+ }
+
+ 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 && $lines[$start - 1] == $lines[$i - 1])
+ {
+ $changed[--$start] = 1;
+ $changed[--$i] = false;
+
+ while ($changed[$start - 1])
+ {
+ $start--;
+ }
+
+ while ($other_changed[--$j])
+ {
+ continue;
+ }
+ }
+
+ /*
+ * Set CORRESPONDING to the end of the changed run, at the last
+ * point where it corresponds to a changed run in the other file.
+ * CORRESPONDING == LEN means no such point has been found.
+ */
+ $corresponding = empty($other_changed[$j - 1]) ? $len : $i;
+
+ /*
+ * 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 ($changed[$i])
+ {
+ $i++;
+ }
+
+ while ($other_changed[++$j])
+ {
+ $corresponding = $i;
+ }
+ }
+ } while ($runlength != $i - $start);
+
+ /*
+ * If possible, move the fully-merged run of changes
+ * back to a corresponding run in the other file.
+ */
+ while ($corresponding < $i)
+ {
+ $changed[--$start] = 1;
+ $changed[--$i] = 0;
+
+ while ($other_changed[--$j])
+ {
+ continue;
+ }
+ }
+ }
+ }
+}
+
+/**
+* Class representing a diff between two files.
+*/
+class Diff
+{
+ var $edits;
+
+ /**
+ * Compute diff between files (or deserialize serialized WikiDiff.)
+ */
+ function Diff($from_lines = false, $to_lines = false)
+ {
+ if ($from_lines && $to_lines)
+ {
+ $compute = new _WikiDiffEngine($from_lines, $to_lines);
+ $this->edits = $compute->edits;
+ }
+ else if ($from_lines)
+ {
+ // $from_lines is not really from_lines, but rather
+ // a serialized Diff.
+ $this->edits = unserialize($from_lines);
+ }
+ else
+ {
+ $this->edits = array();
+ }
+ }
+
+ /**
+ * Compute reversed Diff.
+ *
+ * SYNOPSIS:
+ *
+ * $diff = new Diff($lines1, $lines2);
+ * $rev = $diff->reverse($lines1);
+ *
+ * // reconstruct $lines1 from $lines2:
+ * $out = $rev->apply($lines2);
+ */
+ function reverse ($from_lines)
+ {
+ $x = 0;
+ $rev = new Diff;
+
+ for (reset($this->edits); $edit = current($this->edits); next($this->edits))
+ {
+ if (is_array($edit))
+ { // Was an add, turn it into a delete.
+ $nadd = sizeof($edit);
+ if ($nadd == 0)
+ {
+ die("assertion error");
+ }
+ $edit = -$nadd;
+ }
+ else if ($edit > 0)
+ {
+ // Was a copy --- just pass it through. }
+ $x += $edit;
+ }
+ else if ($edit < 0)
+ { // Was a delete, turn it into an add.
+ $ndelete = -$edit;
+ $edit = array();
+
+ while ($ndelete-- > 0)
+ {
+ $edit[] = $from_lines[$x++];
+ }
+ }
+ else
+ {
+ die("assertion error");
+ }
+
+ $rev->edits[] = $edit;
+ }
+
+ return $rev;
+ }
+
+ /**
+ * Compose (concatenate) Diffs.
+ *
+ * SYNOPSIS:
+ *
+ * $diff1 = new Diff($lines1, $lines2);
+ * $diff2 = new Diff($lines2, $lines3);
+ * $comp = $diff1->compose($diff2);
+ *
+ * // reconstruct $lines3 from $lines1:
+ * $out = $comp->apply($lines1);
+ */
+ function compose ($that)
+ {
+ reset($this->edits);
+ reset($that->edits);
+
+ $comp = new Diff;
+ $left = current($this->edits);
+ $right = current($that->edits);
+
+ while ($left || $right)
+ {
+ if (!is_array($left) && $left < 0)
+ { // Left op is a delete.
+ $newop = $left;
+ $left = next($this->edits);
+ }
+ else if (is_array($right))
+ { // Right op is an add.
+ $newop = $right;
+ $right = next($that->edits);
+ }
+ else if (!$left || !$right)
+ {
+ die ("assertion error");
+ }
+ else if (!is_array($left) && $left > 0)
+ { // Left op is a copy.
+ if ($left <= abs($right))
+ {
+ $newop = $right > 0 ? $left : -$left;
+ $right -= $newop;
+
+ if ($right == 0)
+ {
+ $right = next($that->edits);
+ }
+ $left = next($this->edits);
+ }
+ else
+ {
+ $newop = $right;
+ $left -= abs($right);
+ $right = next($that->edits);
+ }
+ }
+ else
+ { // Left op is an add.
+ if (!is_array($left))
+ {
+ die('assertion error');
+ }
+ $nleft = sizeof($left);
+
+ if ($nleft <= abs($right))
+ {
+ if ($right > 0)
+ { // Right op is copy
+ $newop = $left;
+ $right -= $nleft;
+ }
+ else // Right op is delete
+ {
+ $newop = false;
+ $right += $nleft;
+ }
+
+ if ($right == 0)
+ {
+ $right = next($that->edits);
+ }
+ $left = next($this->edits);
+ }
+ else
+ {
+ unset($newop);
+
+ if ($right > 0)
+ {
+ for ($i = 0; $i < $right; $i++)
+ {
+ $newop[] = $left[$i];
+ }
+ }
+
+ $tmp = array();
+ for ($i = abs($right); $i < $nleft; $i++)
+ {
+ $tmp[] = $left[$i];
+ }
+ $left = $tmp;
+ $right = next($that->edits);
+ }
+ }
+
+ if (!$op)
+ {
+ $op = $newop;
+ continue;
+ }
+
+ if (!$newop)
+ {
+ continue;
+ }
+
+ if (is_array($op) && is_array($newop))
+ {
+ // Both $op and $newop are adds.
+ for ($i = 0; $i < sizeof($newop); $i++)
+ {
+ $op[] = $newop[$i];
+ }
+ }
+ else if (($op > 0 && $newop > 0) || ($op < 0 && $newop < 0))
+ { // $op and $newop are both either deletes or copies.
+ $op += $newop;
+ }
+ else
+ {
+ $comp->edits[] = $op;
+ $op = $newop;
+ }
+ }
+
+ if ($op)
+ {
+ $comp->edits[] = $op;
+ }
+
+ return $comp;
+ }
+
+ /* Debugging only:
+ function _dump ()
+ {
+ echo "<ol>";
+ for (reset($this->edits);
+ {
+ $edit = current($this->edits);
+ }
+ next($this->edits))
+ {
+ echo "<li>";
+ if ($edit > 0)
+ echo "Copy $edit";
+ else if ($edit < 0)
+ echo "Delete " . -$edit;
+ else if (is_array($edit))
+ {
+ echo "Add " . sizeof($edit) . "<ul>";
+ for ($i = 0; $i < sizeof($edit); $i++)
+ echo "<li>" . htmlspecialchars($edit[$i]);
+ echo "</ul>";
+ }
+ else
+ die("assertion error");
+ }
+ echo "</ol>";
+ }
+ */
+
+ /**
+ * Apply a Diff to a set of lines.
+ *
+ * SYNOPSIS:
+ *
+ * $diff = new Diff($lines1, $lines2);
+ *
+ * // reconstruct $lines2 from $lines1:
+ * $out = $diff->apply($lines1);
+ */
+ function apply ($from_lines)
+ {
+ $x = 0;
+ $xlim = sizeof($from_lines);
+
+ for (reset($this->edits); $edit = current($this->edits); next($this->edits))
+ {
+ if (is_array($edit))
+ {
+ reset($edit);
+ while (list ($junk, $line) = each($edit))
+ {
+ $output[] = $line;
+ }
+ }
+ else if ($edit > 0)
+ {
+ while ($edit--)
+ {
+ $output[] = $from_lines[$x++];
+ }
+ }
+ else
+ {
+ $x += -$edit;
+ }
+ }
+
+ if ($x != $xlim)
+ {
+ die(sprintf("Diff::apply: line count mismatch: %s != %s", $x, $xlim));
+ }
+
+ return $output;
+ }
+
+ /**
+ * Serialize a Diff.
+ *
+ * SYNOPSIS:
+ *
+ * $diff = new Diff($lines1, $lines2);
+ * $string = $diff->serialize;
+ *
+ * // recover Diff from serialized version:
+ * $diff2 = new Diff($string);
+ */
+ function serialize ()
+ {
+ return serialize($this->edits);
+ }
+
+ /**
+ * Return true if two files were equal.
+ */
+ function isEmpty()
+ {
+ if (sizeof($this->edits) > 1)
+ {
+ return false;
+ }
+
+ if (sizeof($this->edits) == 0)
+ {
+ return true;
+ }
+
+ // Test for: only edit is a copy.
+ return !is_array($this->edits[0]) && $this->edits[0] > 0;
+ }
+
+ /**
+ * Compute the length of the Longest Common Subsequence (LCS).
+ *
+ * This is mostly for diagnostic purposed.
+ */
+ function lcs()
+ {
+ $lcs = 0;
+ for (reset($this->edits); $edit = current($this->edits); next($this->edits))
+ {
+ if (!is_array($edit) && $edit > 0)
+ {
+ $lcs += $edit;
+ }
+ }
+
+ return $lcs;
+ }
+
+ /**
+ * Check a Diff for validity.
+ *
+ * This is here only for debugging purposes.
+ */
+ function _check ($from_lines, $to_lines)
+ {
+ $test = $this->apply($from_lines);
+ if (serialize($test) != serialize($to_lines))
+ {
+ die("Diff::_check: failed");
+ }
+
+ reset($this->edits);
+ $prev = current($this->edits);
+ $prevtype = is_array($prev) ? 'a' : ($prev > 0 ? 'c' : 'd');
+
+ while ($edit = next($this->edits))
+ {
+ $type = is_array($edit) ? 'a' : ($edit > 0 ? 'c' : 'd');
+ if ($prevtype == $type)
+ {
+ die("Diff::_check: edit sequence is non-optimal");
+ }
+ $prevtype = $type;
+ }
+ $lcs = $this->lcs();
+ echo "<strong>Diff Okay: LCS = $lcs</strong>\n";
+ }
+}
+
+/**
+* A class to format a Diff as HTML.
+*
+* Usage:
+*
+* $diff = new Diff($lines1, $lines2); // compute diff.
+*
+* $fmt = new DiffFormatter;
+* echo $fmt->format($diff, $lines1); // Output HTMLified standard diff.
+*
+* or to output reverse diff (diff's that would take $lines2 to $lines1):
+*
+* $fmt = new DiffFormatter(true);
+* echo $fmt->format($diff, $lines1);
+*/
+class DiffFormatter
+{
+ var $context_lines;
+ var $do_reverse_diff;
+ var $context_prefix, $deletes_prefix, $adds_prefix;
+
+ function DiffFormatter ($reverse = false)
+ {
+ $this->do_reverse_diff = $reverse;
+ $this->context_lines = 0;
+ $this->context_prefix = '&nbsp;&nbsp;';
+ $this->deletes_prefix = '&lt;&nbsp;';
+ $this->adds_prefix = '&gt;&nbsp;';
+ }
+
+ function format ($diff, $from_lines)
+ {
+ $html = '<table width="100%" bgcolor="black" cellspacing=2 cellpadding=2 border=0>' . "\n";
+ $html .= $this->_format($diff->edits, $from_lines);
+ $html .= "</table>\n";
+
+ return $html;
+ }
+
+ function _format ($edits, $from_lines)
+ {
+ $html = '';
+ $x = 0; $y = 0;
+ $xlim = sizeof($from_lines);
+
+ reset($edits);
+ while ($edit = current($edits))
+ {
+ if (!is_array($edit) && $edit >= 0)
+ { // Edit op is a copy.
+ $ncopy = $edit;
+ }
+ else
+ {
+ $ncopy = 0;
+
+ if (empty($hunk))
+ {
+ // Start of an output hunk.
+ $xoff = max(0, $x - $this->context_lines);
+ $yoff = $xoff + $y - $x;
+
+ if ($xoff < $x)
+ {
+ // Get leading context.
+ $context = array();
+
+ for ($i = $xoff; $i < $x; $i++)
+ {
+ $context[] = $from_lines[$i];
+ }
+ $hunk['c'] = $context;
+ }
+ }
+
+ if (is_array($edit))
+ {
+ // Edit op is an add.
+ $y += sizeof($edit);
+ $hunk[$this->do_reverse_diff ? 'd' : 'a'] = $edit;
+ }
+ else
+ {
+ // Edit op is a delete
+ $deletes = array();
+
+ while ($edit++ < 0)
+ {
+ $deletes[] = $from_lines[$x++];
+ }
+
+ $hunk[$this->do_reverse_diff ? 'a' : 'd'] = $deletes;
+ }
+ }
+
+ $next = next($edits);
+
+ if (!empty($hunk))
+ {
+ // Originally $ncopy > 2 * $this->context_lines, but we need to split as early as we can for creating MOD Text Templates. ;)
+ if (!$next || $ncopy > $this->context_lines)
+ {
+ // End of an output hunk.
+ $hunks[] = $hunk;
+ unset($hunk);
+
+ $xend = min($x + $this->context_lines, $xlim);
+
+ if ($x < $xend)
+ {
+ // Get trailing context.
+ $context = array();
+ for ($i = $x; $i < $xend; $i++)
+ {
+ $context[] = $from_lines[$i];
+ }
+ $hunks[] = array('c' => $context);
+ }
+
+ $xlen = $xend - $xoff;
+ $ylen = $xend + $y - $x - $yoff;
+ $xbeg = $xlen ? $xoff + 1 : $xoff;
+ $ybeg = $ylen ? $yoff + 1 : $yoff;
+
+ if ($this->do_reverse_diff)
+ {
+ list ($xbeg, $xlen, $ybeg, $ylen) = array($ybeg, $ylen, $xbeg, $xlen);
+ }
+
+ $html .= $this->_emit_diff($xbeg, $xlen, $ybeg, $ylen, $hunks);
+ unset($hunks);
+ }
+ else if ($ncopy)
+ {
+ $hunks[] = $hunk;
+
+ // Copy context.
+ $context = array();
+ for ($i = $x; $i < $x + $ncopy; $i++)
+ {
+ $context[] = $from_lines[$i];
+ }
+ $hunk = array('c' => $context);
+ }
+ }
+
+ $x += $ncopy;
+ $y += $ncopy;
+ }
+
+ return $html;
+ }
+
+ function _emit_lines($lines, $prefix, $color)
+ {
+ $html = '';
+ reset($lines);
+ while (list ($junk, $line) = each($lines))
+ {
+ $html .= "<tr bgcolor=\"$color\"><td><tt>$prefix</tt>";
+ $html .= "<tt>" . htmlspecialchars($line) . "</tt></td></tr>\n";
+ }
+ return $html;
+ }
+
+ function _emit_diff ($xbeg,$xlen,$ybeg,$ylen,$hunks)
+ {
+ // Save hunk...
+ $this->diff_hunks[] = $hunks;
+
+ $html = '<tr><td><table width="100%" bgcolor="white" cellspacing="0" border="0" cellpadding="4">
+ <tr bgcolor="#cccccc"><td><tt>' .
+ $this->_diff_header($xbeg, $xlen, $ybeg, $ylen) . '
+ </tt></td></tr>\n<tr><td>
+ <table width="100%" cellspacing="0" border="0" cellpadding="2">
+ ';
+
+ $prefix = array('c' => $this->context_prefix, 'a' => $this->adds_prefix, 'd' => $this->deletes_prefix);
+ $color = array('c' => '#ffffff', 'a' => '#ffcccc', 'd' => '#ccffcc');
+
+ for (reset($hunks); $hunk = current($hunks); next($hunks))
+ {
+ if (!empty($hunk['c']))
+ {
+ $html .= $this->_emit_lines($hunk['c'], $this->context_prefix, '#ffffff');
+ }
+
+ if (!empty($hunk['d']))
+ {
+ $html .= $this->_emit_lines($hunk['d'], $this->deletes_prefix, '#ccffcc');
+ }
+
+ if (!empty($hunk['a']))
+ {
+ $html .= $this->_emit_lines($hunk['a'], $this->adds_prefix, '#ffcccc');
+ }
+ }
+
+ $html .= "</table></td></tr></table></td></tr>\n";
+ return $html;
+ }
+
+ function _diff_header ($xbeg,$xlen,$ybeg,$ylen)
+ {
+ $what = $xlen ? ($ylen ? 'c' : 'd') : 'a';
+ $xlen = $xlen > 1 ? "," . ($xbeg + $xlen - 1) : '';
+ $ylen = $ylen > 1 ? "," . ($ybeg + $ylen - 1) : '';
+
+ return "$xbeg$xlen$what$ybeg$ylen";
+ }
+}
+
+/**
+* A class to format a Diff as a pretty HTML unified diff.
+*
+* Usage:
+*
+* $diff = new Diff($lines1, $lines2); // compute diff.
+*
+* $fmt = new UnifiedDiffFormatter;
+* echo $fmt->format($diff, $lines1); // Output HTMLified unified diff.
+*/
+class UnifiedDiffFormatter extends DiffFormatter
+{
+ function UnifiedDiffFormatter ($reverse = false, $context_lines = 3)
+ {
+ $this->do_reverse_diff = $reverse;
+ $this->context_lines = $context_lines;
+ $this->context_prefix = '&nbsp;';
+ $this->deletes_prefix = '-';
+ $this->adds_prefix = '+';
+ }
+
+ function _diff_header ($xbeg,$xlen,$ybeg,$ylen)
+ {
+ $xlen = $xlen == 1 ? '' : ",$xlen";
+ $ylen = $ylen == 1 ? '' : ",$ylen";
+
+ return "@@ -$xbeg$xlen +$ybeg$ylen @@";
+ }
+}
+
+/**
+* A class to format a Diff as MOD Template instuctions.
+*
+* Usage:
+*
+* $diff = new Diff($lines1, $lines2); // compute diff.
+*
+* $fmt = new BBCodeDiffFormatter;
+* echo $fmt->format($diff, $lines1); // Output MOD Actions.
+*/
+class BBCodeDiffFormatter extends DiffFormatter
+{
+ function BBCodeDiffFormatter ($reverse = false, $context_lines = 3, $debug = false)
+ {
+ $this->do_reverse_diff = $reverse;
+ $this->context_lines = $context_lines;
+ $this->context_prefix = '&nbsp;';
+ $this->deletes_prefix = '-';
+ $this->adds_prefix = '+';
+ $this->debug = $debug;
+ }
+
+ function format ($diff, $from_lines)
+ {
+ $html = $this->_format($diff->edits, $from_lines);
+
+ return $html;
+ }
+
+ function skip_lines(&$order_array, &$ordering)
+ {
+ if (sizeof($order_array['find_c']))
+ {
+ $text = implode('', $order_array['find_c']);
+ if ($text === "\n" || $text === "\t" || $text === '')
+ {
+ return true;
+ }
+ }
+
+ if (isset($order_array['add']) && sizeof($order_array['add']))
+ {
+ $text = implode('', $order_array['add']);
+ if ($text === "\n" || $text === "\t" || $text === '')
+ {
+ return true;
+ }
+ }
+
+ if (isset($order_array['replace']) && sizeof($order_array['replace']))
+ {
+ $text = implode('', $order_array['replace']);
+ if ($text === "\n" || $text === "\t" || $text === '')
+ {
+ return true;
+ }
+ }
+ }
+
+ function _emit_lines_bb($ybeg, &$ordering)
+ {
+ $html = '';
+
+ // Now adjust for bbcode display...
+ foreach ($ordering as $order_array)
+ {
+ // Skip useless empty lines...
+ if ($this->skip_lines($order_array, $ordering))
+ {
+ continue;
+ }
+
+ // Only include double-finds if the last find has very few code location indications...
+ if (isset($order_array['first_find_c']) && sizeof($order_array['first_find_c']))
+ {
+ $text = implode('', $order_array['find_c']);
+ if ($text === "\n" || $text === "\t" || $text === '')
+ {
+ continue;
+ }
+
+ if (strlen(implode('', $order_array['find_c'])) < 50 && is_array($order_array['first_find_c'][0]))
+ {
+ $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n";
+ $html .= implode("", $order_array['first_find_c'][0]);
+ $html .= "\n";
+ $ybeg += sizeof($order_array['first_find_c'][0]);
+ }
+ }
+
+ if (sizeof($order_array['find_c']))
+ {
+ $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n";
+ $html .= implode("", $order_array['find_c']);
+ $html .= "\n";
+ }
+
+ if (isset($order_array['replace']))
+ {
+ $html .= "#\n#-----[ REPLACE WITH ]---------------------------------------------\n#\n";
+ $html .= implode("", $order_array['replace']);
+ $html .= "\n";
+ }
+
+ if (isset($order_array['add']))
+ {
+ $html .= "#\n#-----[ AFTER, ADD ]---------------------------------------------\n#\n";
+ $html .= implode("", $order_array['add']);
+ $html .= "\n";
+ }
+
+ // There is no DELETE. :o
+ // Let's try to adjust it then...
+ if (isset($order_array['delete']))
+ {
+ $text = implode('', $order_array['delete']);
+ if ($text === "\n" || $text === "\t" || $text === '')
+ {
+ continue;
+ }
+
+ $ybeg += sizeof($order_array['find_c']);
+
+ $html .= "#\n#-----[ FIND ]---------------------------------------------\n# Around Line {$ybeg}\n";
+ $html .= implode("", $order_array['delete']);
+ $html .= "\n";
+
+ $html .= "#\n#-----[ REPLACE WITH ]---------------------------------------------\n# Just remove/delete the lines (replacing with an empty line)\n";
+ $html .= "\n";
+ $html .= "\n";
+ }
+ }
+
+ return $html;
+ }
+
+ function format_open($filename)
+ {
+ $html = '';
+ $html .= "#\n#-----[ OPEN ]--------------------------------------------- \n#\n{$filename}\n\n";
+
+ return $html;
+ }
+
+ function format_close($filename)
+ {
+ return '';
+ }
+
+ function _emit_diff ($xbeg, $xlen, $ybeg, $ylen, $hunks)
+ {
+
+ // Go through the hunks to determine which method we are using (AFTER, ADD; REPLACE WITH or DELETE)
+
+ // Remove the header...
+ if (sizeof($hunks) <= 2 && !isset($hunks[1]['a']) && !isset($hunks[1]['d']))
+ {
+ $reorder = false;
+ $orig_hunks = $hunks;
+
+ foreach ($hunks as $key => $hunk)
+ {
+ if (isset($hunk['a']) && isset($hunk['d']))
+ {
+ /* if (sizeof($hunk['a']) == 1 && sizeof($hunk['d']) == 1)
+ {
+ if (preg_match('/\* @version \$Id:.+\$$/', $hunk['a'][0]) && preg_match('/\* @version \$Id:.+\$$/', $hunk['d'][0]))
+ {
+ // Only remove this sole hunk...
+ unset($hunks[$key]);
+ $reorder = true;
+ continue;
+ }
+ }*/
+
+ // Compare the add and replace one...
+ $string_1 = rtrim(trim(implode('', $hunk['a'])));
+ $string_2 = rtrim(trim(implode('', $hunk['d'])));
+
+ if (strcmp($string_1, $string_2) === 0)
+ {
+ // Only remove this sole hunk...
+ unset($hunks[$key]);
+ $reorder = true;
+ continue;
+ }
+ }
+ }
+
+ if ($reorder)
+ {
+ // Now check if we have no more 'a' and 'd's
+ $hunks = array_merge($hunks, array());
+ }
+ }
+ else
+ {
+ $reorder = false;
+ $orig_hunks = $hunks;
+
+ foreach ($hunks as $key => $hunk)
+ {
+ if (isset($hunk['a']) && isset($hunk['d']))
+ {
+ /* if (sizeof($hunk['a']) == 1 && sizeof($hunk['d']) == 1)
+ {
+ if (preg_match('/\* @version \$Id:.+\$$/', $hunk['a'][0]) && preg_match('/\* @version \$Id:.+\$$/', $hunk['d'][0]))
+ {
+ // Only remove this sole hunk...
+ unset($hunks[$key]);
+ $reorder = true;
+ continue;
+ }
+ }*/
+
+ // Compare the add and replace one...
+ $string_1 = rtrim(trim(implode('', $hunk['a'])));
+ $string_2 = rtrim(trim(implode('', $hunk['d'])));
+
+ if (strcmp($string_1, $string_2) === 0)
+ {
+ // Only remove this sole hunk...
+ unset($hunks[$key]);
+ $reorder = true;
+ continue;
+ }
+ }
+ }
+
+ if ($reorder)
+ {
+ $hunks = array_merge($hunks, array());
+
+ if (sizeof($hunks) == 1 && sizeof($hunks[0]) == 1 && isset($hunks[0]['c']))
+ {
+ return;
+ }
+ else
+ {
+ $hunks = $orig_hunks;
+ }
+ }
+ }
+
+ if (sizeof($hunks) == 1 && sizeof($hunks[0]) == 1 && isset($hunks[0]['c']))
+ {
+ return;
+ }
+
+ $replace = false;
+ foreach ($hunks as $key => $hunk)
+ {
+ if (isset($hunk['d']) && isset($hunk['a']))
+ {
+ $replace = true;
+ break;
+ }
+ }
+
+ $ordering = array();
+ $cur_pos = 0;
+
+ // Replace-block
+ if ($replace)
+ {
+ foreach ($hunks as $hunk)
+ {
+ if (!isset($hunk['a']) && !isset($hunk['d']))
+ {
+ continue;
+ }
+
+ if (!empty($hunk['c']))
+ {
+ if (!isset($ordering[$cur_pos]['find_c']))
+ {
+ $ordering[$cur_pos]['find_c'] = $hunk['c'];
+ }
+ else
+ {
+ $ordering[$cur_pos]['end_c'] = $hunk['c'];
+ }
+ }
+
+ // Make sure we begin fresh...
+ if (!isset($ordering[$cur_pos]['replace']))
+ {
+ $ordering[$cur_pos]['first_find_c'][] = $ordering[$cur_pos]['find_c'];
+ $ordering[$cur_pos]['find_c'] = array();
+ $ordering[$cur_pos]['replace'] = array();
+ }
+
+ // Add the middle part if one exist...
+ if (isset($ordering[$cur_pos]['end_c']))
+ {
+ $ordering[$cur_pos]['find_c'] = array_merge($ordering[$cur_pos]['find_c'], $ordering[$cur_pos]['end_c']);
+ $ordering[$cur_pos]['replace'] = array_merge($ordering[$cur_pos]['replace'], $ordering[$cur_pos]['end_c']);
+ unset($ordering[$cur_pos]['end_c']);
+ }
+
+ if (isset($hunk['d']))
+ {
+ $ordering[$cur_pos]['find_c'] = array_merge($ordering[$cur_pos]['find_c'], $hunk['d']);
+ }
+
+ if (isset($hunk['a']))
+ {
+ $ordering[$cur_pos]['replace'] = array_merge($ordering[$cur_pos]['replace'], $hunk['a']);
+ }
+ }
+ }
+ else
+ {
+ foreach ($hunks as $hunk)
+ {
+ if (!empty($hunk['c']))
+ {
+ if (!isset($ordering[$cur_pos]['find_c']))
+ {
+ $ordering[$cur_pos]['find_c'] = $hunk['c'];
+ }
+ else
+ {
+ $ordering[$cur_pos]['end_c'] = $hunk['c'];
+ }
+ }
+
+ if (!empty($hunk['a']))
+ {
+ if (isset($ordering[$cur_pos]['delete']))
+ {
+ // If ordering is set with an delete entry, we will actually begin a new ordering array (to seperate delete from add)
+ $cur_pos++;
+ $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c'];
+ $ordering[$cur_pos]['add'] = $hunk['a'];
+ }
+ else
+ {
+ if (isset($ordering[$cur_pos]['add']))
+ {
+ // Now, we really need to be quite careful here...
+ if (isset($ordering[$cur_pos]['end_c']) && isset($hunk['c']) && isset($hunk['a']) && sizeof($hunk) == 2)
+ {
+ // There is a new find/add entry we did not catch... let's try to add a new entry then... but first check the hunk[a] contents...
+ $text = trim(implode("\n", $hunk['c']));
+ if ($text == "\n" || !$text)
+ {
+ $ordering[$cur_pos]['add'] = array_merge($ordering[$cur_pos]['add'], array("\n"), $hunk['a']);
+ }
+ else if (sizeof($hunk['c']) > 2 || strlen(implode('', $hunk['c'])) > 20)
+ {
+ $cur_pos++;
+ $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c'];
+ $ordering[$cur_pos]['add'] = $hunk['a'];
+ }
+ else
+ {
+ $cur_pos++;
+ $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c'];
+ $ordering[$cur_pos]['add'] = $hunk['a'];
+/* echo 'FIND STATEMENT TOO TINY';
+ echo ";".rawurlencode($text).";";
+ var_dump($hunk);
+ exit;*/
+ }
+ }
+ else
+ {
+ echo 'UNCATCHED ENTRY';
+ var_dump($hunks);
+ exit;
+ }
+ }
+ else
+ {
+ $ordering[$cur_pos]['add'] = $hunk['a'];
+ }
+ }
+ }
+ else if (!empty($hunk['d']))
+ {
+ if (isset($ordering[$cur_pos]['add']))
+ {
+ // If ordering is set with an add entry, we will actually begin a new ordering array (to seperate delete from add)
+ $cur_pos++;
+ $ordering[$cur_pos]['find_c'] = $ordering[($cur_pos - 1)]['end_c'];
+ $ordering[$cur_pos]['delete'] = $hunk['d'];
+ }
+ else
+ {
+ $ordering[$cur_pos]['delete'] = $hunk['d'];
+ }
+ }
+ }
+ }
+
+ $html = '';
+
+ return $this->_emit_lines_bb($ybeg, $ordering);
+ }
+
+ function _diff_header($xbeg, $xlen, $ybeg, $ylen)
+ {
+ }
+}
+
+
+/**
+* A class to format a Diff as MOD Template instuctions.
+*
+* Usage:
+*
+* $diff = new Diff($lines1, $lines2); // compute diff.
+*
+* $fmt = new BBCodeDiffFormatter;
+* echo $fmt->format($diff, $lines1); // Output MOD Actions.
+*/
+class MODXDiffFormatter extends BBCodeDiffFormatter
+{
+ function MODXDiffFormatter ($reverse = false, $context_lines = 3, $debug = false)
+ {
+ $this->do_reverse_diff = $reverse;
+ $this->context_lines = $context_lines;
+ $this->context_prefix = '&nbsp;';
+ $this->deletes_prefix = '-';
+ $this->adds_prefix = '+';
+ $this->debug = $debug;
+ }
+
+ function _emit_lines_bb($ybeg, &$ordering)
+ {
+ $html = '';
+
+ // Now adjust for bbcode display...
+ foreach ($ordering as $order_array)
+ {
+ // Skip useless empty lines...
+ if ($this->skip_lines($order_array, $ordering))
+ {
+ continue;
+ }
+
+ // Only include double-finds if the last find has very few code location indications...
+ if (sizeof($order_array['first_find_c']))
+ {
+ $text = implode('', $order_array['find_c']);
+ if ($text === "\n" || $text === "\t" || $text === '')
+ {
+ continue;
+ }
+
+ if (strlen(implode('', $order_array['find_c'])) < 50)
+ {
+ if (substr($html, -8) !== '</find>' . "\n")
+ {
+ $html .= ' <edit>' . "\n";
+ }
+
+ $html .= ' <comment lang="en">Around Line ' . $ybeg . '</comment>' . "\n";
+ $html .= ' <find>' . htmlspecialchars(implode('', $order_array['first_find_c'][0])) . '</find>' . "\n";
+ $ybeg += sizeof($order_array['first_find_c'][0]);
+ }
+ }
+
+ if (sizeof($order_array['find_c']))
+ {
+ if (substr($html, -8) !== '</find>' . "\n")
+ {
+ $html .= ' <edit>' . "\n";
+ }
+
+// $html .= ' <edit>' . "\n";
+ $html .= ' <comment lang="en">Around Line ' . $ybeg . '</comment>' . "\n";
+ $html .= ' <find>' . htmlspecialchars(implode('', $order_array['find_c'])) . '</find>' . "\n";
+ }
+
+ if (isset($order_array['replace']))
+ {
+ $html .= ' <action type="replace-with">' . htmlspecialchars(implode('', $order_array['replace'])) . '</action>' . "\n";
+ $html .= ' </edit>' . "\n";
+ }
+
+ if (isset($order_array['add']))
+ {
+ $html .= ' <action type="after-add">' . htmlspecialchars(implode('', $order_array['add'])) . '</action>' . "\n";
+ $html .= ' </edit>' . "\n";
+ }
+
+ // There is no DELETE. :o
+ // Let's try to adjust it then...
+ if (isset($order_array['delete']))
+ {
+ $text = implode('', $order_array['delete']);
+ if ($text === "\n" || $text === "\t" || $text === '')
+ {
+ continue;
+ }
+
+ $ybeg += sizeof($order_array['find_c']);
+
+ if (substr($html, -8) !== '</find>' . "\n")
+ {
+ $html .= ' <edit>' . "\n";
+ }
+ $html .= ' <comment lang="en">Around Line ' . $ybeg . ' / Just remove/delete the lines (replacing with an empty line)</comment>' . "\n";
+ $html .= ' <find>' . htmlspecialchars(implode('', $order_array['delete'])) . '</find>' . "\n";
+ $html .= ' <action type="replace-with"></action>' . "\n";
+ $html .= ' </edit>';
+ }
+ }
+
+ return $html;
+ }
+
+ function format_open($filename)
+ {
+ return '<open src="' . $filename . '">' . "\n";
+ }
+
+ function format_close($filename)
+ {
+ return '</open>' . "\n";
+ }
+
+ function _diff_header($xbeg, $xlen, $ybeg, $ylen)
+ {
+ }
+}
+
+?> \ No newline at end of file
diff --git a/build/package.php b/build/package.php
new file mode 100755
index 0000000000..50a9e76ab7
--- /dev/null
+++ b/build/package.php
@@ -0,0 +1,521 @@
+#!/usr/bin/env php
+<?php
+/**
+*
+* @package build
+* @version $Id$
+* @copyright (c) 2010 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+// The only thing i need to adjust. ;)
+// Please always add the latest version number to the end
+// Only have 5 releases here...
+// If RC8 drops remove the install/data directory
+//$versions = array('3.0.2', '3.0.3-RC1', '3.0.3', '3.0.4-RC1', '3.0.4', '3.0.5-RC1', '3.0.5', '3.0.6-RC1', '3.0.6-RC2', '3.0.6-RC3');
+//$versions = array('3.0.2', '3.0.3', '3.0.4', '3.0.5', '3.0.6', '3.0.7-RC1', '3.0.7');
+$versions = array('3.0.2', '3.0.3-RC1', '3.0.3', '3.0.4-RC1', '3.0.4', '3.0.5-RC1', '3.0.5', '3.0.6-RC1', '3.0.6-RC2', '3.0.6-RC3', '3.0.6-RC4', '3.0.6', '3.0.7-RC1');
+$verbose = false;
+
+require('build_helper.php');
+
+$package = new build_package($versions, $verbose);
+
+echo "Building Release Packages\n";
+echo "Now all three package types (patch, files, release) are built as well as the update package (update).\n";
+
+$package->begin_status('Remove temporary files');
+
+// Cleanup...
+$package->run_command('rm -Rv ' . $package->get('dest_dir'));
+$package->run_command('rm -Rv ' . $package->get('diff_dir'));
+$package->run_command('rm -Rv ' . $package->get('patch_directory'));
+$package->run_command('rm -Rv ' . $package->get('files_directory'));
+$package->run_command('rm -Rv ' . $package->get('update_directory'));
+$package->run_command('rm -Rv ' . $package->get('release_directory'));
+
+$package->begin_status('Create new directories');
+
+// Make sure the directories got removed
+while (file_exists($package->get('update_directory')))
+{
+ sleep(1);
+}
+
+if (!file_exists($package->get('dest_dir')))
+{
+ $package->run_command('mkdir ' . $package->get('dest_dir'));
+}
+
+if (!file_exists($package->get('diff_dir')))
+{
+ $package->run_command('mkdir ' . $package->get('diff_dir'));
+}
+
+if (!file_exists($package->get('patch_directory')))
+{
+ $package->run_command('mkdir ' . $package->get('patch_directory'));
+}
+
+if (!file_exists($package->get('files_directory')))
+{
+ $package->run_command('mkdir ' . $package->get('files_directory'));
+}
+
+if (!file_exists($package->get('update_directory')))
+{
+ $package->run_command('mkdir ' . $package->get('update_directory'));
+}
+
+if (!file_exists($package->get('release_directory')))
+{
+ $package->run_command('mkdir ' . $package->get('release_directory'));
+}
+
+$package->begin_status('Copy release files to clean release directory');
+
+// Create config.php file
+$package->run_command('touch ' . $package->get('dest_dir') . '/config.php');
+//$package->run_command('sudo chown www-data:www-data ' . $package->get('dest_dir') . '/config.php');
+
+// Create new directory structure
+foreach ($package->clean_directory_structure as $dir => $dir_struct)
+{
+ $package->create_directory($package->get('dest_dir') . '/' . $dir, $dir_struct);
+}
+
+// First step is to copy the new version over (clean structure)
+foreach ($package->files_to_copy as $file)
+{
+ $source_file = $package->locations['new_version'] . $file;
+ $dest_file = $package->get('dest_dir') . '/' . $file;
+
+ $package->run_command("cp -p $source_file $dest_file");
+}
+
+// fix line endings
+chdir($package->get('dest_dir'));
+$package->run_command($package->locations['new_version'] . 'develop/fix_files.sh');
+
+// Now clean up the permissions
+$package->begin_status('Adjust permissions');
+
+$package->adjust_permissions($package->get('dest_dir'));
+
+// Now create a version for diffing the version - copy the tree over to old_versions...
+$package->begin_status('Create diff directory for obtaining file differences');
+
+$package->run_command('cp -Rp ' . $package->get('dest_dir') . '/* ' . $package->get('diff_dir'));
+$package->run_command('cp -Rp ' . $package->get('dest_dir') . '/.htaccess ' . $package->get('diff_dir'));
+
+// Cleanup diff directory (only contents to diff)
+foreach ($package->remove_from_diff_structure as $remove_dir)
+{
+ $package->run_command('rm -Rv ' . $package->get('diff_dir') . '/' . $remove_dir);
+}
+
+// Now, first of all we need to rebuild all old packages we want to support
+foreach ($package->old_packages as $package_name => $tag_name)
+{
+ $package->begin_status('Create old packages directory for diffing to ' . $package_name);
+
+ chdir($package->locations['old_versions']);
+
+ if (is_dir($package->locations['old_versions'] . $package_name))
+ {
+ $package->run_command('rm -Rv ' . $package->locations['old_versions'] . $package_name);
+ }
+
+ // Now, create a new one...
+ $tag_name = 'release_' . str_replace(array('.', '_to_'), array('_', ''), $tag_name);
+
+ $package->run_command('svn export --non-interactive http://code.phpbb.com/svn/phpbb/tags/' . $tag_name . '/phpBB/ ' . $package_name);
+
+ $location = $package->locations['old_versions'] . $package_name;
+ chdir($location . '/');
+
+ $package->run_command($package->locations['new_version'] . 'develop/fix_files.sh');
+
+ // Now clean up the permissions
+ $package->begin_status('Adjust permissions for package ' . $package_name);
+
+ $package->adjust_permissions($location);
+
+ // Cleanup diff directory (only contents to diff)
+ foreach ($package->remove_from_diff_structure as $remove_dir)
+ {
+ $package->run_command('rm -Rv ' . $location . '/' . $remove_dir);
+ }
+}
+
+// Go trough all versions making a diff if we even have old versions
+// For phpBB 3.0.x we might choose a different update method, rendering the things below useless...
+if (sizeof($package->old_packages))
+{
+ chdir($package->locations['old_versions']);
+
+ // This array is for holding the filenames change
+ $diff_file_changes = array();
+
+ foreach ($package->old_packages as $_package_name => $dest_package_filename)
+ {
+ $package->begin_status('Creating patch/diff files for phpBB-' . $dest_package_filename . $package->get('new_version_number'));
+
+ $dest_package_filename = $package->get('patch_directory') . '/phpBB-' . $dest_package_filename . $package->get('new_version_number') . '.patch';
+ $package->run_command('diff ' . $package->diff_options . ' ' . $_package_name . ' ' . $package->get('simple_name') . ' > ' . $dest_package_filename);
+
+ // Parse this diff to determine file changes from the checked versions and save them
+ $diff_file_changes[$_package_name] = $package->collect_diff_files($dest_package_filename, $_package_name);
+ }
+
+ // Now put those files determined within the correct directories
+ foreach ($diff_file_changes as $_package_name => $file_contents)
+ {
+ $package->begin_status('Creating files-only informations for ' . $package->old_packages[$_package_name] . $package->get('new_version_number'));
+
+ $dest_filename_dir = $package->get('files_directory') . '/' . $package->old_packages[$_package_name] . $package->get('new_version_number');
+
+ if (!file_exists($dest_filename_dir))
+ {
+ $package->run_command('mkdir ' . $dest_filename_dir);
+ }
+
+ // Now copy the file contents
+ foreach ($file_contents['all'] as $file)
+ {
+ $source_filename = $package->get('dest_dir') . '/' . $file;
+ $dest_filename = $dest_filename_dir . '/' . $file;
+
+ // Create Directories along the way?
+ $file = explode('/', $file);
+ // Remove filename portion
+ $file[sizeof($file)-1] = '';
+
+ chdir($dest_filename_dir);
+ foreach ($file as $entry)
+ {
+ $entry = trim($entry);
+ if ($entry)
+ {
+ if (!file_exists('./' . $entry))
+ {
+ $package->run_command('mkdir ' . $entry);
+ }
+ chdir('./' . $entry);
+ }
+ }
+
+ $package->run_command('cp ' . $source_filename . ' ' . $dest_filename);
+ }
+ }
+
+ // Because there might be binary changes, we re-create the patch files... without parsing file differences.
+ $package->run_command('rm -Rv ' . $package->get('patch_directory'));
+
+ if (!file_exists($package->get('patch_directory')))
+ {
+ $package->run_command('mkdir ' . $package->get('patch_directory'));
+ }
+
+ chdir($package->locations['old_versions']);
+
+ foreach ($package->old_packages as $_package_name => $dest_package_filename)
+ {
+ $package->begin_status('Creating patch/diff files for phpBB-' . $dest_package_filename . $package->get('new_version_number'));
+
+ $dest_package_filename = $package->get('patch_directory') . '/phpBB-' . $dest_package_filename . $package->get('new_version_number') . '.patch';
+ $package->run_command('diff ' . $package->diff_options_long . ' ' . $_package_name . ' ' . $package->get('simple_name') . ' > ' . $dest_package_filename);
+ }
+
+ $packages = $diff_file_changes;
+
+ foreach ($packages as $_package_name => $file_contents)
+ {
+ $package->begin_status('Building specific update files for ' . $package->old_packages[$_package_name] . $package->get('new_version_number'));
+
+ $dest_filename_dir = $package->get('update_directory') . '/' . $package->old_packages[$_package_name] . $package->get('new_version_number');
+
+ if (!file_exists($dest_filename_dir))
+ {
+ $package->run_command('mkdir ' . $dest_filename_dir);
+ }
+
+ $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/docs ' . $dest_filename_dir);
+ $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/install ' . $dest_filename_dir);
+
+ $package->run_command('mkdir ' . $dest_filename_dir . '/install/update');
+ $package->run_command('mkdir ' . $dest_filename_dir . '/install/update/old');
+ $package->run_command('mkdir ' . $dest_filename_dir . '/install/update/new');
+
+ // Remove some files
+ $package->run_command('rm -v ' . $dest_filename_dir . '/install/install_install.php');
+ $package->run_command('rm -v ' . $dest_filename_dir . '/install/install_convert.php');
+ $package->run_command('rm -Rv ' . $dest_filename_dir . '/install/schemas');
+ $package->run_command('rm -Rv ' . $dest_filename_dir . '/install/convertors');
+
+ foreach ($file_contents['all'] as $index => $file)
+ {
+ if (strpos($file, 'recode_cjk') !== false)
+ {
+ unset($file_contents['all'][$index]);
+ }
+ }
+
+ // First of all, fill the 'old' directory
+ foreach ($file_contents['all'] as $file)
+ {
+ $source_filename = $package->locations['old_versions'] . $_package_name . '/' . $file;
+ $dest_filename = $dest_filename_dir . '/install/update/old/' . $file;
+
+ if (!file_exists($source_filename))
+ {
+ continue;
+ }
+
+ // Create Directories along the way?
+ $file = explode('/', $file);
+ // Remove filename portion
+ $file[sizeof($file)-1] = '';
+
+ chdir($dest_filename_dir . '/install/update/old');
+ foreach ($file as $entry)
+ {
+ $entry = trim($entry);
+ if ($entry)
+ {
+ if (!file_exists('./' . $entry))
+ {
+ $package->run_command('mkdir ' . $entry);
+ }
+ chdir('./' . $entry);
+ }
+ }
+
+ $package->run_command('cp ' . $source_filename . ' ' . $dest_filename);
+ }
+
+ // Then fill the 'new' directory
+ foreach ($file_contents['all'] as $file)
+ {
+ $source_filename = $package->locations['old_versions'] . $package->get('simple_name') . '/' . $file;
+ $dest_filename = $dest_filename_dir . '/install/update/new/' . $file;
+
+ if (!file_exists($source_filename))
+ {
+ continue;
+ }
+
+ // Create Directories along the way?
+ $file = explode('/', $file);
+ // Remove filename portion
+ $file[sizeof($file)-1] = '';
+
+ chdir($dest_filename_dir . '/install/update/new');
+ foreach ($file as $entry)
+ {
+ $entry = trim($entry);
+ if ($entry)
+ {
+ if (!file_exists('./' . $entry))
+ {
+ $package->run_command('mkdir ' . $entry);
+ }
+ chdir('./' . $entry);
+ }
+ }
+
+ $package->run_command('cp ' . $source_filename . ' ' . $dest_filename);
+ }
+
+ // Build index.php file for holding the file structure
+ $index_contents = '<?php
+
+if (!defined(\'IN_PHPBB\'))
+{
+ exit;
+}
+
+// Set update info with file structure to update
+$update_info = array(
+ \'version\' => array(\'from\' => \'' . str_replace('_to_', '', $package->old_packages[$_package_name]) . '\', \'to\' => \'' . $package->get('new_version_number') . '\'),
+';
+
+ if (sizeof($file_contents['all']))
+ {
+ $index_contents .= '\'files\' => array(\'' . implode("',\n\t'", $file_contents['all']) . '\'),
+';
+ }
+ else
+ {
+ $index_contents .= '\'files\' => array(),
+';
+ }
+
+ if (sizeof($file_contents['binary']))
+ {
+ $index_contents .= '\'binary\' => array(\'' . implode("',\n\t'", $file_contents['binary']) . '\'),
+';
+ }
+ else
+ {
+ $index_contents .= '\'binary\' => array(),
+';
+ }
+
+ $index_contents .= ');
+
+?' . '>';
+
+ $fp = fopen($dest_filename_dir . '/install/update/index.php', 'wt');
+ fwrite($fp, $index_contents);
+ fclose($fp);
+ }
+ unset($diff_file_changes);
+
+ $package->begin_status('Clean up all install files');
+
+ // Copy the install files to their respective locations
+ $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/docs ' . $package->get('patch_directory'));
+ $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/install ' . $package->get('patch_directory'));
+
+ // Remove some files
+ chdir($package->get('patch_directory') . '/install');
+
+ $package->run_command('rm -v install_install.php');
+ $package->run_command('rm -v install_update.php');
+ $package->run_command('rm -v install_convert.php');
+ $package->run_command('rm -Rv schemas');
+ $package->run_command('rm -Rv convertors');
+}
+
+// Build Main phpBB Release
+$compress_programs = array(
+// 'tar.gz' => 'tar -czf',
+ 'tar.bz2' => 'tar -cjf',
+ 'zip' => 'zip -r'
+);
+
+if (sizeof($package->old_packages))
+{
+ // Build Patch Files
+ chdir($package->get('patch_directory'));
+
+ foreach ($compress_programs as $extension => $compress_command)
+ {
+ $package->begin_status('Packaging phpBB Patch Files for ' . $extension);
+ $package->run_command('rm -v ../release_files/' . $package->get('release_filename') . '-patch.' . $extension);
+
+ // Build Package
+ $package->run_command($compress_command . ' ../release_files/' . $package->get('release_filename') . '-patch.' . $extension . ' *');
+
+ // Build MD5 Sum
+ $package->run_command('md5sum ../release_files/' . $package->get('release_filename') . '-patch.' . $extension . ' > ../release_files/' . $package->get('release_filename') . '-patch.' . $extension . '.md5');
+ }
+
+ // Build Files Package
+ chdir($package->get('files_directory'));
+
+ foreach ($compress_programs as $extension => $compress_command)
+ {
+ $package->begin_status('Packaging phpBB Files for ' . $extension);
+
+ $package->run_command('rm -v ../release_files/' . $package->get('release_filename') . '-files.' . $extension);
+ $package->run_command('mkdir ' . $package->get('files_directory') . '/release');
+ $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/docs ' . $package->get('files_directory') . '/release');
+ $package->run_command('cp -Rp ' . $package->get('dest_dir') . '/install ' . $package->get('files_directory') . '/release');
+
+ $package->run_command('rm -v ' . $package->get('files_directory') . '/release/install/install_install.php');
+ $package->run_command('rm -v ' . $package->get('files_directory') . '/release/install/install_update.php');
+ $package->run_command('rm -v ' . $package->get('files_directory') . '/release/install/install_convert.php');
+ $package->run_command('rm -Rv ' . $package->get('files_directory') . '/release/install/schemas');
+ $package->run_command('rm -Rv ' . $package->get('files_directory') . '/release/install/convertors');
+
+ // Pack files
+ foreach ($package->old_packages as $_package_name => $package_path)
+ {
+ chdir($package_path . $package->get('new_version_number'));
+ $command = ($extension == 'zip') ? 'zip -r' : 'tar cf';
+ $_ext = ($extension == 'zip') ? 'zip' : 'tar';
+ $package->run_command("$command ../release/phpBB-$package_path" . $package->get('new_version_number') . ".$_ext *");
+ chdir('..');
+ }
+
+ chdir('./release');
+ $package->run_command("$compress_command ../../release_files/" . $package->get('release_filename') . '-files.' . $extension . ' *');
+ // Build MD5 Sum
+ $package->run_command('md5sum ../../release_files/' . $package->get('release_filename') . '-files.' . $extension . ' > ../../release_files/' . $package->get('release_filename') . '-files.' . $extension . '.md5');
+ chdir('..');
+
+ $package->run_command('rm -Rv ' . $package->get('files_directory') . '/release');
+ }
+
+ // Build Update Package
+ foreach ($compress_programs as $extension => $compress_command)
+ {
+ chdir($package->get('update_directory'));
+
+ $package->begin_status('Packaging phpBB Update for ' . $extension);
+
+ $package->run_command('rm -v ../release_files/' . $package->get('release_filename') . '-update.' . $extension);
+ $package->run_command('mkdir ' . $package->get('update_directory') . '/release');
+
+ // Pack update files
+ $packages = $package->old_packages;
+
+ foreach ($packages as $_package_name => $package_path)
+ {
+ chdir($package_path . $package->get('new_version_number'));
+
+ $package->run_command('rm -v install/install_install.php');
+ $package->run_command('rm -v install/install_convert.php');
+ $package->run_command('rm -v includes/utf/data/recode_cjk.php');
+ $package->run_command('rm -Rv install/schemas');
+ $package->run_command('rm -Rv install/convertors');
+
+ $command = ($extension == 'zip') ? 'zip -r' : 'tar cf';
+ $_ext = ($extension == 'zip') ? 'zip' : 'tar';
+ $package->run_command("$command ../release/$package_path" . $package->get('new_version_number') . ".$_ext *");
+ chdir('..');
+
+ $last_version = $package_path . $package->get('new_version_number');
+
+// chdir('./release');
+// $package->run_command("$compress_command ../../release_files/" . $package->get('release_filename') . '-update.' . $extension . ' *');
+// chdir('..');
+
+ chdir('./' . $last_version);
+ // Copy last package over...
+ $package->run_command('rm -v ../release_files/phpBB-' . $last_version . ".$extension");
+ $package->run_command("$compress_command ../../release_files/phpBB-$last_version.$extension *");
+
+ // Build MD5 Sum
+ $package->run_command("md5sum ../../release_files/phpBB-$last_version.$extension > ../../release_files/phpBB-$last_version.$extension.md5");
+ chdir('..');
+ }
+
+ $package->run_command('rm -Rv ' . $package->get('update_directory') . '/release');
+ }
+
+}
+
+// Delete updater and convertor from main archive
+chdir($package->get('dest_dir') . '/install');
+
+// $package->run_command('rm -v database_update.php');
+$package->run_command('rm -v install_update.php');
+
+chdir($package->locations['package_dir']);
+foreach ($compress_programs as $extension => $compress_command)
+{
+ $package->begin_status('Packaging phpBB for ' . $extension);
+ $package->run_command('rm -v ./release_files/' . $package->get('release_filename') . ".{$extension}");
+
+ // Build Package
+ $package->run_command("$compress_command ./release_files/" . $package->get('release_filename') . '.' . $extension . ' ' . $package->get('package_name'));
+
+ // Build MD5 Sum
+ $package->run_command('md5sum ./release_files/' . $package->get('release_filename') . '.' . $extension . ' > ./release_files/' . $package->get('release_filename') . '.' . $extension . '.md5');
+}
+
+echo "Done.\n";