From 63cb5853ff174b45e697ea0818816060805503aa Mon Sep 17 00:00:00 2001
From: Andreas Gohr <andi@splitbrain.org>
Date: Fri, 16 Jun 2006 17:49:06 +0200
Subject: [PATCH] aspell fix #836

darcs-hash:20060616154906-7ad00-6ace887070a70fda5f898f241aebb639230b53d8.gz
---
 inc/aspell.php | 168 +++++++++++++++++++++++++++++++------------------
 inc/common.php |  15 +++++
 2 files changed, 123 insertions(+), 60 deletions(-)

diff --git a/inc/aspell.php b/inc/aspell.php
index 0cf519b43..8c033e2c8 100644
--- a/inc/aspell.php
+++ b/inc/aspell.php
@@ -163,75 +163,123 @@ class Aspell{
      * @link     http://aspell.sf.net/man-html/Through-A-Pipe.html
      */
     function runAspell($text,&$out,&$err,$specials=null){
-        $this->_prepareArgs();
         if(empty($text)) return true;
-        //prepare file descriptors
-        $descspec = array(
-               0 => array('pipe', 'r'),  // stdin is a pipe that the child will read from
-               1 => array('pipe', 'w'),  // stdout is a pipe that the child will write to
-               2 => array('pipe', 'w')    // stderr is a file to write to
-        );
-
-        $process = proc_open(ASPELL_BIN.' -a'.$this->args, $descspec, $pipes);
-        $terse   = 1;   // terse mode active
-        if ($process) {
-            // write specials if given
-            if(is_array($specials)){
-                foreach($specials as $s){
-                    if ($s == '!') $terse = 0;
-                    fwrite($pipes[0],"$s\n");
-                }
+        $terse = true;
+
+        // prepare arguments
+        $this->_prepareArgs();
+        $command = ASPELL_BIN.' -a'.$this->args;
+        $stdin   = '';
+
+        // prepare specials
+        if(is_array($specials)){
+            foreach($specials as $s){
+                if ($s == '!') $terse = false;
+                $stdin .= "$s\n";
             }
+        }
 
-            // prepare text for Aspell and handle it over
-            $string = "^".str_replace("\n", "\n^",$text);
-            fwrite($pipes[0],$string);  // send text to Aspell
-            fclose($pipes[0]);
+        // prepare text
+        $stdin .= "^".str_replace("\n", "\n^",$text);
 
-            // read Aspells response from stdin
-            while (!feof($pipes[1])) {
-                $out .= fread($pipes[1], 8192);
-            }
-            fclose($pipes[1]);
-
-            // Aspell has a bug that can't be autodetected because both versions
-            // might produce the same output but under different conditions. So
-            // we check Aspells version number here to divide broken and working
-            // versions of Aspell.
-            $tmp = array();
-            preg_match('/^\@.*Aspell (\d+)\.(\d+).(\d+)/',$out,$tmp);
-            $this->version = $tmp[1]*100 + $tmp[2]*10 + $tmp[3];
-
-            if ($this->version <= 603)  // version 0.60.3
-                $r = $terse ? "\n*\n\$1" : "\n\$1"; // replacement for broken Aspell
-            else
-                $r = $terse ? "\n*\n" : "\n";    // replacement for good Aspell
-
-            // lines starting with a '?' are no realy misspelled words and some
-            // Aspell versions doesn't produce usable output anyway so we filter
-            // them out here.
-            $out = preg_replace('/\n\? [^\n\&\*]*([\n]?)/',$r, $out);
-
-            // read stderr
-            while (!feof($pipes[2])) {
-                $err .= fread($pipes[2], 8192);
+        // run aspell through the pipe
+        $rc = $this->execPipe($command,$stdin,$out,$err);
+        if(is_null($rc)){
+            $err = "Could not run Aspell '".ASPELL_BIN."'";
+            return false;
+        }
+
+        // Aspell has a bug that can't be autodetected because both versions
+        // might produce the same output but under different conditions. So
+        // we check Aspells version number here to divide broken and working
+        // versions of Aspell.
+        $tmp = array();
+        preg_match('/^\@.*Aspell (\d+)\.(\d+).(\d+)/',$out,$tmp);
+        $this->version = $tmp[1]*100 + $tmp[2]*10 + $tmp[3];
+
+        if ($this->version <= 603)  // version 0.60.3
+            $r = $terse ? "\n*\n\$1" : "\n\$1"; // replacement for broken Aspell
+        else
+            $r = $terse ? "\n*\n" : "\n";    // replacement for good Aspell
+
+        // lines starting with a '?' are no realy misspelled words and some
+        // Aspell versions doesn't produce usable output anyway so we filter
+        // them out here.
+        $out = preg_replace('/\n\? [^\n\&\*]*([\n]?)/',$r, $out);
+
+        if ($err){
+            //something went wrong
+            $err = "Aspell returned an error(".ASPELL_BIN." exitcode: $rc ):\n".$err;
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Runs the given command with the given input on STDIN
+     *
+     * STDOUT and STDERR are written to the given vars, the command's
+     * exit code is returned. If the pip couldn't be opened null is returned
+     *
+     * @author <richard at 2006 dot atterer dot net>
+     * @link http://www.php.net/manual/en/function.proc-open.php#64116
+     */
+    function execPipe($command,$stdin,&$stdout,&$stderr){
+        $descriptorSpec = array(0 => array("pipe", "r"),
+                                1 => array('pipe', 'w'),
+                                2 => array('pipe', 'w'));
+        $process = proc_open($command, $descriptorSpec, $pipes);
+        if(!$process) return null;
+
+        $txOff = 0;
+        $txLen = strlen($stdin);
+        $stdoutDone = FALSE;
+        $stderrDone = FALSE;
+
+        stream_set_blocking($pipes[0], 0); // Make stdin/stdout/stderr non-blocking
+        stream_set_blocking($pipes[1], 0);
+        stream_set_blocking($pipes[2], 0);
+
+        if ($txLen == 0) fclose($pipes[0]);
+        while (TRUE) {
+            $rx = array(); // The program's stdout/stderr
+            if (!$stdoutDone) $rx[] = $pipes[1];
+            if (!$stderrDone) $rx[] = $pipes[2];
+            $tx = array(); // The program's stdin
+            if ($txOff < $txLen) $tx[] = $pipes[0];
+            stream_select($rx, $tx, $ex = NULL, NULL, NULL); // Block til r/w possible
+
+            if (!empty($tx)) {
+                $txRet = fwrite($pipes[0], substr($stdin, $txOff, 8192));
+                if ($txRet !== FALSE) $txOff += $txRet;
+                if ($txOff >= $txLen) fclose($pipes[0]);
             }
-            fclose($pipes[2]);
-
-            // close process
-            $rc = proc_close($process);
-            if ($err){
-                //something went wrong
-                $err = "Aspell returned an error(".ASPELL_BIN." exitcode: $rc ):\n".$err;
-                return false;
+
+            foreach ($rx as $r) {
+                if ($r == $pipes[1]) {
+                    $stdout .= fread($pipes[1], 8192);
+                    if (feof($pipes[1])) {
+                        fclose($pipes[1]);
+                        $stdoutDone = TRUE;
+                    }
+                } else if ($r == $pipes[2]) {
+                    $stderr .= fread($pipes[2], 8192);
+                    if (feof($pipes[2])) {
+                        fclose($pipes[2]);
+                        $stderrDone = TRUE;
+                    }
+                }
             }
-            return true;
+            if (!is_resource($process)) break;
+            if ($txOff >= $txLen && $stdoutDone && $stderrDone) break;
         }
-        //opening failed
-        $err = "Could not run Aspell '".ASPELL_BIN."'";
-        return false;
+        return proc_close($process);
     }
 
+
+
+
     /**
      * Checks a single word for correctness
      *
diff --git a/inc/common.php b/inc/common.php
index 975c70098..e47b4e7a3 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -694,6 +694,21 @@ function dbg($msg,$hidden=false){
   (!$hidden) ? print '</pre>' : print "\n-->";
 }
 
+/**
+ * Print info to a log file
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function dbglog($msg){
+  global $conf;
+  $file = $conf['cachedir'].'/debug.log';
+  $fh = fopen($file,'a');
+  if($fh){
+    fwrite($fh,date('H:i:s ').$_SERVER['REMOTE_ADDR'].': '.$msg."\n");
+    fclose($fh);
+  }
+}
+
 /**
  * Add's an entry to the changelog
  *
-- 
GitLab