11 defined(
'JPATH_PLATFORM') or die;
13 jimport('joomla.filesystem.file');
29 const SRC_FILE =
'/^---\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A';
34 const DST_FILE =
'/^\\+\\+\\+\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A';
39 const HUNK =
'/@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@($)/A';
44 const SPLIT =
'/(\r\n)|(\r)|(\n)/';
50 protected $sources = array();
56 protected $destinations = array();
62 protected $removals = array();
68 protected $patches = array();
83 protected function __construct()
94 public static function getInstance()
96 if (!isset(static::$instance))
98 static::$instance =
new static;
101 return static::$instance;
111 public function reset()
113 $this->sources = array();
114 $this->destinations = array();
115 $this->removals = array();
116 $this->patches = array();
129 public function apply()
131 foreach ($this->patches as $patch)
134 $lines = self::splitLines($patch[
'udiff']);
137 while (self::findHeader($lines, $src, $dst))
141 if ($patch[
'strip'] === null)
143 $src = $patch[
'root'] . preg_replace(
'#^([^/]*/)*#',
'', $src);
144 $dst = $patch[
'root'] . preg_replace(
'#^([^/]*/)*#',
'', $dst);
148 $src = $patch[
'root'] . preg_replace(
'#^([^/]*/){' . (
int) $patch[
'strip'] .
'}#',
'', $src);
149 $dst = $patch[
'root'] . preg_replace(
'#^([^/]*/){' . (
int) $patch[
'strip'] .
'}#',
'', $dst);
153 while (self::findHunk($lines, $src_line, $src_size, $dst_line, $dst_size))
158 $this->applyHunk($lines, $src, $dst, $src_line, $src_size, $dst_line, $dst_size);
164 throw new RuntimeException(
'Invalid Diff');
173 foreach ($this->destinations as $file => $content)
177 if (isset($this->sources[$file]))
179 $this->sources[$file] = $content;
187 foreach ($this->removals as $file)
191 if (isset($this->sources[$file]))
193 unset($this->sources[$file]);
201 $this->destinations = array();
204 $this->removals = array();
207 $this->patches = array();
223 public function addFile($filename, $root = JPATH_BASE, $strip = 0)
225 return $this->add(file_get_contents($filename), $root, $strip);
239 public function add($udiff, $root = JPATH_BASE, $strip = 0)
241 $this->patches[] = array(
243 'root' => isset($root) ? rtrim($root, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR :
'',
259 protected static function splitLines($data)
261 return preg_split(self::SPLIT, $data);
278 protected static function findHeader(&$lines, &$src, &$dst)
281 $line = current($lines);
284 while ($line !==
false && !preg_match(self::SRC_FILE, $line, $m))
286 $line = next($lines);
299 $line = next($lines);
303 throw new RuntimeException(
'Unexpected EOF');
307 if (!preg_match(self::DST_FILE, $line, $m))
309 throw new RuntimeException(
'Invalid Diff file');
316 if (next($lines) ===
false)
318 throw new RuntimeException(
'Unexpected EOF');
340 protected static function findHunk(&$lines, &$src_line, &$src_size, &$dst_line, &$dst_size)
342 $line = current($lines);
344 if (preg_match(self::HUNK, $line, $m))
346 $src_line = (int) $m[1];
354 $src_size = (int) $m[3];
357 $dst_line = (int) $m[4];
365 $dst_size = (int) $m[6];
368 if (next($lines) ===
false)
370 throw new RuntimeException(
'Unexpected EOF');
397 protected function applyHunk(&$lines, $src, $dst, $src_line, $src_size, $dst_line, $dst_size)
401 $line = current($lines);
408 $src_left = $src_size;
409 $dst_left = $dst_size;
413 if (!isset($line[0]))
420 elseif ($line[0] ==
'-')
424 throw new RuntimeException(
JText::sprintf(
'JLIB_FILESYSTEM_PATCHER_REMOVE_LINE', key($lines)));
427 $source[] = substr($line, 1);
430 elseif ($line[0] ==
'+')
434 throw new RuntimeException(
JText::sprintf(
'JLIB_FILESYSTEM_PATCHER_ADD_LINE', key($lines)));
437 $destin[] = substr($line, 1);
440 elseif ($line !=
'\\ No newline at end of file')
442 $line = substr($line, 1);
449 if ($src_left == 0 && $dst_left == 0)
454 $src_lines = & $this->getSource($src);
456 if (!isset($src_lines))
458 throw new RuntimeException(
JText::sprintf(
'JLIB_FILESYSTEM_PATCHER_UNEXISING_SOURCE', $src));
466 $dst_lines = & $this->getDestination($dst, $src);
467 $src_bottom = $src_line + count($source);
469 for ($l = $src_line;$l < $src_bottom;$l++)
471 if ($src_lines[$l] != $source[$l - $src_line])
473 throw new RuntimeException(
JText::sprintf(
'JLIB_FILESYSTEM_PATCHER_FAILED_VERIFY', $src, $l));
477 array_splice($dst_lines, $dst_line, count($source), $destin);
481 $this->destinations[$dst] = $destin;
486 $this->removals[] = $src;
494 $line = next($lines);
497 while ($line !==
false);
498 throw new RuntimeException(
'Unexpected EOF');
510 protected function &getSource($src)
512 if (!isset($this->sources[$src]))
514 if (is_readable($src))
516 $this->sources[$src] = self::splitLines(file_get_contents($src));
520 $this->sources[$src] = null;
524 return $this->sources[$src];
537 protected function &getDestination($dst, $src)
539 if (!isset($this->destinations[$dst]))
541 $this->destinations[$dst] = $this->getSource($src);
544 return $this->destinations[$dst];