Avoid leaking file descriptor when looking for binary files
[phorkie.git] / src / phorkie / File.php
index 3c6c56d7e432912d00c47db9546fd7aed9cc62ee..97ae47e934e14d974ab062d0882ec478a603bfd8 100644 (file)
@@ -38,6 +38,16 @@ class File
         return $this->path;
     }
 
+    /**
+     * Get the filename usable as HTML anchor.
+     *
+     * @return string
+     */
+    function getAnchorName()
+    {
+        return str_replace(' ', '-', $this->getFilename());
+    }
+
     /**
      * Return the full path to the file
      *
@@ -55,13 +65,16 @@ class File
      */
     public function getExt()
     {
-        return substr($this->path, strrpos($this->path, '.') + 1);
+        return strtolower(substr($this->path, strrpos($this->path, '.') + 1));
     }
 
     public function getContent()
     {
         if ($this->repo->hash) {
-            return $this->repo->getVc()->getCommand('show')
+            //quick hack until https://pear.php.net/bugs/bug.php?id=19385 is fixed
+            $cmd = new GitCommandBinary($this->repo->getVc());
+            $cmd->setSubCommand('show');
+            return $cmd
                 ->addArgument($this->repo->hash . ':' . $this->path)
                 ->execute();
         }
@@ -71,51 +84,63 @@ class File
 
     public function getRenderedContent(Tool_Result $res = null)
     {
-        $ext   = $this->getExt();
-        $class = '\\phorkie\\Renderer_Unknown';
-
-        if (isset($GLOBALS['phorkie']['languages'][$ext]['renderer'])) {
-            $class = $GLOBALS['phorkie']['languages'][$ext]['renderer'];
-        } else if (isset($GLOBALS['phorkie']['languages'][$ext]['mime'])) {
-            $type = $GLOBALS['phorkie']['languages'][$ext]['mime'];
-            if (substr($type, 0, 5) == 'text/') {
-                $class = '\\phorkie\\Renderer_Geshi';
-            } else if (substr($type, 0, 6) == 'image/') {
-                $class = '\\phorkie\\Renderer_Image';
-            }
-        }
-
-        $rend = new $class();
-        return $rend->toHtml($this, $res);
+        $cache = new Renderer_Cache();
+        return $cache->toHtml($this, $res);
     }
 
     /**
      * Get a link to the file
      *
-     * @param string $type Link type. Supported are:
-     *                     - "raw"
-     *                     - "tool"
-     * @param string $option
+     * @param string $type   Link type. Supported are:
+     *                       - "display"
+     *                       - "raw"
+     *                       - "tool"
+     * @param string $option Additional option, e.g. tool name
+     * @param boolean $full   Return full URL or normal relative
      *
      * @return string
      */
-    public function getLink($type, $option = null)
+    public function getLink($type, $option = null, $full = false)
     {
         if ($type == 'raw') {
-            return '/' . $this->repo->id . '/raw/' . $this->getFilename();
+            if ($this->repo->hash === null) {
+                $link = $this->repo->id . '/raw/' . $this->getFilename();
+            } else {
+                $link = $this->repo->id . '/rev-raw/' . $this->repo->hash
+                    . '/' . $this->getFilename();
+            }
         } else if ($type == 'tool') {
-            return '/' . $this->repo->id . '/tool/' . $option . '/' . $this->getFilename();
+            $link = $this->repo->id
+                . '/tool/' . $option
+                . '/' . $this->getFilename();
+        } else if ($type == 'display') {
+            $link = $this->repo->id . '#' . $this->getFilename();
+        } else {
+            throw new Exception('Unknown type');
         }
-        throw new Exception('Unknown type');
+
+        if ($full) {
+            $link = Tools::fullUrl($link);
+        }
+        return $link;
     }
 
+    /**
+     * @return string Mime type of file, NULL if no type detected
+     */
     public function getMimeType()
     {
         $ext = $this->getExt();
-        if (!isset($GLOBALS['phorkie']['languages'][$ext])) {
-            return null;
+        if (isset($GLOBALS['phorkie']['languages'][$ext])) {
+            return $GLOBALS['phorkie']['languages'][$ext]['mime'];
+        }
+
+        $mte = new \MIME_Type_Extension();
+        $type = $mte->getMIMEType($this->getFilename());
+        if (!\PEAR::isError($type)) {
+            return $type;
         }
-        return $GLOBALS['phorkie']['languages'][$ext]['mime'];
+        return null;
     }
 
     /**
@@ -123,6 +148,10 @@ class File
      */
     public function getToolInfos()
     {
+        if ($this->repo->hash !== null) {
+            return array();
+        }
+
         $tm = new Tool_Manager();
         return $tm->getSuitable($this);
     }
@@ -135,13 +164,57 @@ class File
     public function isText()
     {
         $ext = $this->getExt();
-        if (!isset($GLOBALS['phorkie']['languages'][$ext]['mime'])) {
+        if ($ext == '') {
+            return $this->isNonBinary();
+        }
+
+        $type = $this->getMimeType();
+        if ($type === null) {
+            return $this->isNonBinary();
+        }
+        return substr($type, 0, 5) === 'text/'
+            || $type == 'application/javascript'
+            || substr($type, -4) == '+xml'
+            || substr($type, -5) == '+json';
+    }
+
+    /**
+     * Look at the file's bytes and guess if it's binary or not.
+     *
+     * @return boolean True if it's most likely plain text
+     */
+    public function isNonBinary()
+    {
+        $fp = fopen($this->getFullPath(), 'r');
+        if (!$fp) {
             return false;
         }
 
-        $type = $GLOBALS['phorkie']['languages'][$ext]['mime'];
-        return substr($type, 0, 5) === 'text/';
+        //When multibyte extension is not installed,
+        // we only allow files with ASCII characters.
+        // Files with UTF-8 characters will not be detected as text.
+        $hasMb = function_exists('mb_detect_encoding');
+
+        $pos = 0;
+        $data = '';
+        while (false !== ($char = fgetc($fp)) && ++$pos < 100) {
+            $data .= $char;
+            if (!$hasMb && ord($char) > 128) {
+                fclose($fp);
+                return false;
+            }
+        }
+        fclose($fp);
+
+        if (!$hasMb) {
+            return true;
+        }
+
+        if (mb_detect_encoding($data) === false) {
+            return false;
+        }
+        return true;
     }
 }
 
-?>
\ No newline at end of file
+?>