diff options
-rwxr-xr-x | build/build_diff.php | 408 | ||||
-rw-r--r-- | build/build_helper.php | 485 | ||||
-rw-r--r-- | build/diff_class.php | 1677 | ||||
-rwxr-xr-x | build/package.php | 521 |
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 = ' '; + $this->deletes_prefix = '< '; + $this->adds_prefix = '> '; + } + + 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 = ' '; + $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 = ' '; + $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 = ' '; + $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"; |