From 6c0002048504e43b399abece0668afa2b5c87a07 Mon Sep 17 00:00:00 2001
From: Patrick Brown <ptbrown@whoopdedo.org>
Date: Fri, 8 May 2015 17:11:44 -0400
Subject: [PATCH] Limit number of lines to replace.

---
 _test/tests/inc/io_replaceinfile.test.php | 58 +++++++++++++++++++++++
 inc/io.php                                | 12 +++--
 2 files changed, 67 insertions(+), 3 deletions(-)
 create mode 100644 _test/tests/inc/io_replaceinfile.test.php

diff --git a/_test/tests/inc/io_replaceinfile.test.php b/_test/tests/inc/io_replaceinfile.test.php
new file mode 100644
index 000000000..98f21868f
--- /dev/null
+++ b/_test/tests/inc/io_replaceinfile.test.php
@@ -0,0 +1,58 @@
+<?php
+
+class io_replaceinfile_test extends DokuWikiTest {
+
+    /*
+     * dependency for tests needing zlib extension to pass
+     */
+    public function test_ext_zlib() {
+        if (!extension_loaded('zlib')) {
+            $this->markTestSkipped('skipping all zlib tests.  Need zlib extension');
+        }
+    }
+
+    /*
+     * dependency for tests needing zlib extension to pass
+     */
+    public function test_ext_bz2() {
+        if (!extension_loaded('bz2')) {
+            $this->markTestSkipped('skipping all bzip2 tests.  Need bz2 extension');
+        }
+    }
+
+    function _write($file){
+        $contents = "The\012Delete\012Delete\012Delete01\012Delete02\012Delete\012DeleteX\012Test\012";
+        io_saveFile($file, $contents);
+        // Replace one, no regex
+        $this->assertTrue(io_replaceInFile($file, "Delete\012", "Delete00\012", false, 1));
+        $this->assertEquals("The\012Delete00\012Delete\012Delete01\012Delete02\012Delete\012DeleteX\012Test\012", io_readFile($file));
+        // Replace all, no regex
+        $this->assertTrue(io_replaceInFile($file, "Delete\012", "DeleteX\012", false, -1));
+        $this->assertEquals("The\012Delete00\012DeleteX\012Delete01\012Delete02\012DeleteX\012DeleteX\012Test\012", io_readFile($file));
+        // Replace two, regex and backreference
+        $this->assertTrue(io_replaceInFile($file, "#Delete(\\d+)\012#", "\\1\012", true, 2));
+        $this->assertEquals("The\01200\012DeleteX\01201\012Delete02\012DeleteX\012DeleteX\012Test\012", io_readFile($file));
+        // Delete and insert, no regex
+        $this->assertTrue(io_replaceInFile($file, "DeleteX\012", "Replace\012", false, 0));
+        $this->assertEquals("The\01200\01201\012Delete02\012Test\012Replace\012", io_readFile($file));
+    }
+
+    function test_replace(){
+        $this->_write(TMP_DIR.'/test.txt');
+    }
+
+    /**
+     * @depends test_ext_zlib
+     */
+    function test_gzwrite(){
+        $this->_write(TMP_DIR.'/test.txt.gz');
+    }
+
+    /**
+     * @depends test_ext_bz2
+     */
+    function test_bzwrite(){
+        $this->_write(TMP_DIR.'/test.txt.bz2');
+    }
+
+}
diff --git a/inc/io.php b/inc/io.php
index 11a3fa77f..dbb42114b 100644
--- a/inc/io.php
+++ b/inc/io.php
@@ -313,10 +313,16 @@ function io_replaceInFile($file, $oldline, $newline, $regex=false, $maxlines=0)
 
     // remove all matching lines
     if ($regex) {
-        if($maxlines == 0) {
-            $lines = preg_grep($oldline, $lines, PREG_GREP_INVERT);
+        if($maxlines > 0) {
+            $matches = preg_grep($oldline, $lines);
+            $count = 0;
+            foreach($matches as $ix=>$m) {
+                $lines[$ix] = preg_replace($oldline, $newline, $m);
+                if(++$count >= $maxlines) break;
+            }
         } else {
-            $lines = preg_replace($oldline, $newline, $lines, $maxlines);
+            $lines = ($maxlines == 0) ? preg_grep($oldline, $lines, PREG_GREP_INVERT)
+                                      : preg_replace($oldline, $newline, $lines, $maxlines);
         }
     } else {
         $count = 0;
-- 
GitLab