From 1bd6bbdebc26f9cd916f8f287cd2cabc07bee8d1 Mon Sep 17 00:00:00 2001
From: Patrick Brown <ptbrown@whoopdedo.org>
Date: Thu, 7 May 2015 01:37:11 -0400
Subject: [PATCH] Add io_replaceInFile

---
 inc/io.php | 158 +++++++++++++++++++++++++++++------------------------
 1 file changed, 86 insertions(+), 72 deletions(-)

diff --git a/inc/io.php b/inc/io.php
index b8a77b730..11a3fa77f 100644
--- a/inc/io.php
+++ b/inc/io.php
@@ -205,13 +205,7 @@ function _io_writeWikiPage_action($data) {
 }
 
 /**
- * Saves $content to $file.
- *
- * If the third parameter is set to true the given content
- * will be appended.
- *
- * Uses gzip if extension is .gz
- * and bz2 if extension is .bz2
+ * Internal function to save contents to a file.
  *
  * @author  Andreas Gohr <andi@splitbrain.org>
  *
@@ -220,73 +214,90 @@ function _io_writeWikiPage_action($data) {
  * @param bool   $append
  * @return bool true on success, otherwise false
  */
-function io_saveFile($file,$content,$append=false){
+function _io_saveFile($file, $content, $append) {
     global $conf;
     $mode = ($append) ? 'ab' : 'wb';
-
     $fileexists = file_exists($file);
-    io_makeFileDir($file);
-    io_lock($file);
+
     if(substr($file,-3) == '.gz'){
         $fh = @gzopen($file,$mode.'9');
-        if(!$fh){
-            msg("Writing $file failed",-1);
-            io_unlock($file);
-            return false;
-        }
+        if(!$fh) return false;
         gzwrite($fh, $content);
         gzclose($fh);
     }else if(substr($file,-4) == '.bz2'){
         if($append) {
             $bzcontent = bzfile($file);
-            if($bzcontent === false) {
-                msg("Writing $file failed", -1);
-                io_unlock($file);
-                return false;
-            }
+            if($bzcontent === false) return false;
             $content = $bzcontent.$content;
         }
         $fh = @bzopen($file,'w');
-        if(!$fh){
-            msg("Writing $file failed", -1);
-            io_unlock($file);
-            return false;
-        }
+        if(!$fh) return false;
         bzwrite($fh, $content);
         bzclose($fh);
     }else{
         $fh = @fopen($file,$mode);
-        if(!$fh){
-            msg("Writing $file failed",-1);
-            io_unlock($file);
-            return false;
-        }
+        if(!$fh) return false;
         fwrite($fh, $content);
         fclose($fh);
     }
 
     if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']);
-    io_unlock($file);
     return true;
 }
 
 /**
- * Delete exact linematch for $badline from $file.
+ * Saves $content to $file.
  *
- * Be sure to include the trailing newline in $badline
+ * If the third parameter is set to true the given content
+ * will be appended.
  *
  * Uses gzip if extension is .gz
+ * and bz2 if extension is .bz2
  *
- * 2005-10-14 : added regex option -- Christopher Smith <chris@jalakai.co.uk>
+ * @author  Andreas Gohr <andi@splitbrain.org>
  *
- * @author Steven Danz <steven-danz@kc.rr.com>
+ * @param string $file filename path to file
+ * @param string $content
+ * @param bool   $append
+ * @return bool true on success, otherwise false
+ */
+function io_saveFile($file, $content, $append=false) {
+    io_makeFileDir($file);
+    io_lock($file);
+    if(!_io_saveFile($file, $content, $append)) {
+        msg("Writing $file failed",-1);
+        io_unlock($file);
+        return false;
+    }
+    io_unlock($file);
+    return true;
+}
+
+/**
+ * Replace one or more occurrences of a line in a file.
  *
- * @param string $file    filename
- * @param string $badline exact linematch to remove
- * @param bool   $regex   use regexp?
+ * The default, when $maxlines is 0 is to delete all matches then append a single line.
+ * If $maxlines is -1, then every $oldline will be replaced with $newline, and $regex is true
+ * then preg captures are used. If $maxlines is greater than 0 then the first $maxlines
+ * matches are replaced with $newline.
+ *
+ * Be sure to include the trailing newline in $oldline
+ *
+ * Uses gzip if extension is .gz
+ * and bz2 if extension is .bz2
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ * @author Christopher Smith <chris@jalakai.co.uk>
+ * @author Patrick Brown <ptbrown@whoopdedo.org>
+ *
+ * @param string $file     filename
+ * @param string $oldline  exact linematch to remove
+ * @param string $newline  new line to insert
+ * @param bool   $regex    use regexp?
+ * @param int    $maxlines number of occurrences of the line to replace
  * @return bool true on success
  */
-function io_deleteFromFile($file,$badline,$regex=false){
+function io_replaceInFile($file, $oldline, $newline, $regex=false, $maxlines=0) {
     if (!file_exists($file)) return true;
 
     io_lock($file);
@@ -302,44 +313,31 @@ function io_deleteFromFile($file,$badline,$regex=false){
 
     // remove all matching lines
     if ($regex) {
-        $lines = preg_grep($badline,$lines,PREG_GREP_INVERT);
+        if($maxlines == 0) {
+            $lines = preg_grep($oldline, $lines, PREG_GREP_INVERT);
+        } else {
+            $lines = preg_replace($oldline, $newline, $lines, $maxlines);
+        }
     } else {
-        $pos = array_search($badline,$lines); //return null or false if not found
+        $count = 0;
+        $replaceline = $maxlines == 0 ? '' : $newline;
+        $pos = array_search($oldline,$lines); //return null or false if not found
         while(is_int($pos)){
-            unset($lines[$pos]);
-            $pos = array_search($badline,$lines);
+            $lines[$pos] = $replaceline;
+            if($maxlines > 0 && ++$count >= $maxlines) break;
+            $pos = array_search($oldline,$lines);
         }
     }
 
+    if($maxlines == 0 && ((string)$newline) !== '') {
+        $lines[] = $newline;
+    }
+
     if(count($lines)){
-        $content = join('',$lines);
-        if(substr($file,-3) == '.gz'){
-            $fh = @gzopen($file,'wb9');
-            if(!$fh){
-                msg("Removing content from $file failed",-1);
-                io_unlock($file);
-                return false;
-            }
-            gzwrite($fh, $content);
-            gzclose($fh);
-        }else if(substr($file,-4) == '.bz2'){
-            $fh = @bzopen($file,'w');
-            if(!$fh){
-                msg("Removing content from $file failed",-1);
-                io_unlock($file);
-                return false;
-            }
-            bzwrite($fh, $content);
-            bzclose($fh);
-        }else{
-            $fh = @fopen($file,'wb');
-            if(!$fh){
-                msg("Removing content from $file failed",-1);
-                io_unlock($file);
-                return false;
-            }
-            fwrite($fh, $content);
-            fclose($fh);
+        if(!_io_saveFile($file, join('',$lines), false)) {
+            msg("Removing content from $file failed",-1);
+            io_unlock($file);
+            return false;
         }
     }else{
         @unlink($file);
@@ -349,6 +347,22 @@ function io_deleteFromFile($file,$badline,$regex=false){
     return true;
 }
 
+/**
+ * Delete lines that match $badline from $file.
+ *
+ * Be sure to include the trailing newline in $badline
+ *
+ * @author Patrick Brown <ptbrown@whoopdedo.org>
+ *
+ * @param string $file    filename
+ * @param string $badline exact linematch to remove
+ * @param bool   $regex   use regexp?
+ * @return bool true on success
+ */
+function io_deleteFromFile($file,$badline,$regex=false){
+    return io_replaceInFile($file,$badline,null,$regex,0);
+}
+
 /**
  * Tries to lock a file
  *
-- 
GitLab