OSDN Git Service

file_head() with non-lock option
[pukiwiki/pukiwiki.git] / lib / file.php
index 0f2bb9a..2aa1dd6 100644 (file)
@@ -1,13 +1,16 @@
 <?php
 // PukiWiki - Yet another WikiWikiWeb clone.
-// $Id: file.php,v 1.36 2005/08/01 15:04:29 henoheno Exp $
+// $Id: file.php,v 1.56 2006/04/10 14:41:10 henoheno Exp $
 // Copyright (C)
-//   2002-2005 PukiWiki Developers Team
+//   2002-2006 PukiWiki Developers Team
 //   2001-2002 Originally written by yu-ji
 // License: GPL v2 or (at your option) any later version
 //
 // File related functions
 
+define('PKWK_MAXSHOW_CACHE', 'recent.dat');
+define('PKWK_AUTOLINK_REGEX_CACHE', 'autolink.dat');
+
 // Get source(wiki text) data of the page
 function get_source($page = NULL, $lock = TRUE)
 {
@@ -77,7 +80,7 @@ function page_write($page, $postdata, $notimestamp = FALSE)
        links_update($page);
 }
 
-// Modify ogirinal text with user-defined / system-defined rules
+// Modify original text with user-defined / system-defined rules
 function make_str_rules($source)
 {
        global $str_rules, $fixed_heading_anchor;
@@ -145,6 +148,30 @@ function generate_fixed_heading_anchor_id($seed)
                mt_rand(0, 24), 7);
 }
 
+// Read top N lines as an array
+// (Use PHP file() function if you want to get ALL lines)
+function file_head($file, $count = 1, $buffer = 8192, $lock = TRUE)
+{
+       $index  = 0;
+       $array  = array();
+
+       $fp = @fopen($file, 'r');
+       if ($fp === FALSE) return FALSE;
+       set_file_buffer($fp, 0);
+
+       if ($lock) flock($fp, LOCK_SH);
+       while (! feof($fp)) {
+               $line = fgets($fp, $buffer);
+               if ($line != FALSE) $array[] = $line;
+               if (++$index >= $count) break;
+       }
+       if ($lock) flock($fp, LOCK_UN);
+
+       if(! fclose($fp)) return FALSE;
+
+       return $array;
+}
+
 // Output to a file
 function file_write($dir, $page, $str, $notimestamp = FALSE)
 {
@@ -152,76 +179,87 @@ function file_write($dir, $page, $str, $notimestamp = FALSE)
        global $whatsdeleted, $maxshow_deleted;
 
        if (PKWK_READONLY) return; // Do nothing
+       if ($dir != DATA_DIR && $dir != DIFF_DIR) die('file_write(): Invalid directory');
 
-       if (! is_pagename($page))
-               die_message(str_replace('$1', htmlspecialchars($page),
-                           str_replace('$2', 'WikiName', $_msg_invalidiwn)));
+       $page = strip_bracket($page);
+       $file = $dir . encode($page) . '.txt';
+       $file_exists = file_exists($file);
 
-       $page      = strip_bracket($page);
-       $file      = $dir . encode($page) . '.txt';
-       $timestamp = FALSE;
+       // ----
+       // Delete?
 
-       if ($str === '') {
-               if ($dir == DATA_DIR && file_exists($file)) {
-                       // File deletion
-                       unlink($file);
-                       add_recent($page, $whatsdeleted, '', $maxshow_deleted); // RecentDeleted
-               }
-       } else {
-               // File replacement (Edit)
-               $str = rtrim(preg_replace('/' . "\r" . '/', '', $str)) . "\n";
+       if ($dir == DATA_DIR && $str === '') {
+               // Page deletion
+               if (! $file_exists) return; // Ignore null posting for DATA_DIR
 
-               if ($notimestamp && file_exists($file))
-                       $timestamp = filemtime($file) - LOCALZONE;
+               // Update RecentDeleted (Add the $page)
+               add_recent($page, $whatsdeleted, '', $maxshow_deleted);
 
-               $fp = fopen($file, 'r+') or die('fopen() failed: ' .
-                       htmlspecialchars(basename($dir) . '/' . encode($page) . '.txt') .       
-                       '<br />' . "\n" .
-                       'Maybe permission is not writable or filename is too long');
-               set_file_buffer($fp, 0);
+               unlink($file);
 
-               flock($fp, LOCK_EX);
+               // Update RecentChanges (Remove the $page from RecentChanges)
+               put_lastmodified();
 
-               // Write
-               if (! ftruncate($fp, 0)) return FALSE;
-               rewind($fp);
-               fputs($fp, $str);
+               // Clear is_page() cache
+               is_page($page, TRUE);
 
-               flock($fp, LOCK_UN);
-
-               fclose($fp);
+               return;
 
-               if ($timestamp) pkwk_touch_file($file, $timestamp + LOCALZONE);
+       } else if ($dir == DIFF_DIR && $str === " \n") {
+               return; // Ignore null posting for DIFF_DIR
        }
 
-       // Clear is_page() cache
-       is_page($page, TRUE);
+       // ----
+       // File replacement (Edit)
 
-       if (! $timestamp && $dir == DATA_DIR)
-               put_lastmodified();
+       if (! is_pagename($page))
+               die_message(str_replace('$1', htmlspecialchars($page),
+                           str_replace('$2', 'WikiName', $_msg_invalidiwn)));
 
-       // Execute $update_exec here
-       if ($update_exec && $dir == DATA_DIR)
-               system($update_exec . ' > /dev/null &');
+       $str = rtrim(preg_replace('/' . "\r" . '/', '', $str)) . "\n";
+       $timestamp = ($file_exists && $notimestamp) ? filemtime($file) : FALSE;
 
-       if ($notify && $dir == DIFF_DIR) {
-               if ($notify_diff_only) $str = preg_replace('/^[^-+].*\n/m', '', $str);
+       $fp = fopen($file, 'a') or die('fopen() failed: ' .
+               htmlspecialchars(basename($dir) . '/' . encode($page) . '.txt') .       
+               '<br />' . "\n" .
+               'Maybe permission is not writable or filename is too long');
+       set_file_buffer($fp, 0);
+       flock($fp, LOCK_EX);
+       ftruncate($fp, 0);
+       rewind($fp);
+       fputs($fp, $str);
+       flock($fp, LOCK_UN);
+       fclose($fp);
+
+       if ($timestamp) pkwk_touch_file($file, $timestamp);
 
+       // Optional actions
+       if ($dir == DATA_DIR) {
+               // Update RecentChanges (Add or renew the $page)
+               if ($timestamp === FALSE) put_lastmodified();
+
+               // Execute $update_exec here
+               if ($update_exec) system($update_exec . ' > /dev/null &');
+
+       } else if ($dir == DIFF_DIR && $notify) {
+               if ($notify_diff_only) $str = preg_replace('/^[^-+].*\n/m', '', $str);
                $footer['ACTION'] = 'Page update';
                $footer['PAGE']   = & $page;
                $footer['URI']    = get_script_uri() . '?' . rawurlencode($page);
                $footer['USER_AGENT']  = TRUE;
                $footer['REMOTE_ADDR'] = TRUE;
-
                pkwk_mail_notify($notify_subject, $str, $footer) or
                        die('pkwk_mail_notify(): Failed');
        }
+
+       is_page($page, TRUE); // Clear is_page() cache
 }
 
 // Update RecentDeleted
 function add_recent($page, $recentpage, $subject = '', $limit = 0)
 {
-       if (PKWK_READONLY || $limit == 0 || $page == '' || $recentpage == '') return;
+       if (PKWK_READONLY || $limit == 0 || $page == '' || $recentpage == '' ||
+           check_non_list($page)) return;
 
        // Load
        $lines = $matches = array();
@@ -256,29 +294,34 @@ function add_recent($page, $recentpage, $subject = '', $limit = 0)
        fclose($fp);
 }
 
-// Update RecentChanges
+// Re-create PKWK_MAXSHOW_CACHE
+
 function put_lastmodified()
 {
-       global $maxshow, $whatsnew, $non_list, $autolink;
+       global $maxshow, $whatsnew, $autolink;
 
        if (PKWK_READONLY) return; // Do nothing
 
+       // Get whole page list
        $pages = get_existpages();
+
+       // Check ALL filetime
        $recent_pages = array();
-       $non_list_pattern = '/' . $non_list . '/';
        foreach($pages as $page)
-               if ($page != $whatsnew && ! preg_match($non_list_pattern, $page))
+               if ($page != $whatsnew && ! check_non_list($page))
                        $recent_pages[$page] = get_filetime($page);
 
        // Sort decending order of last-modification date
        arsort($recent_pages, SORT_NUMERIC);
 
-       // Create recent.dat (for recent.inc.php)
-       $fp = fopen(CACHE_DIR . 'recent.dat', 'w') or
-               die_message('Cannot write cache file ' .
-               CACHE_DIR . 'recent.dat' .
-               '<br />Maybe permission is not writable or filename is too long');
+       // Cut unused lines
+       $recent_pages = array_splice($recent_pages, 0, $maxshow);
 
+       // Re-create PKWK_MAXSHOW_CACHE
+       $fp = fopen(CACHE_DIR . PKWK_MAXSHOW_CACHE, 'w') or
+               die_message('Cannot write file ' .
+               'CACHE_DIR/' . PKWK_MAXSHOW_CACHE . '<br />' . "\n" .
+               'Maybe permission is not writable');
        set_file_buffer($fp, 0);
        flock($fp, LOCK_EX);
        rewind($fp);
@@ -289,14 +332,13 @@ function put_lastmodified()
 
        // Create RecentChanges
        $fp = fopen(get_filename($whatsnew), 'w') or
-               die_message('Cannot write page file ' .
-               htmlspecialchars($whatsnew) .
-               '<br />Maybe permission is not writable or filename is too long');
-
+               die_message('Cannot write file ' .
+               htmlspecialchars($whatsnew) . '<br />' . "\n" .
+               'Maybe permission is not writable or filename is too long');
        set_file_buffer($fp, 0);
        flock($fp, LOCK_EX);
        rewind($fp);
-       foreach (array_splice(array_keys($recent_pages), 0, $maxshow) as $page) {
+       foreach (array_keys($recent_pages) as $page) {
                $time      = $recent_pages[$page];
                $s_lastmod = htmlspecialchars(format_date($time));
                $s_page    = htmlspecialchars($page);
@@ -311,10 +353,10 @@ function put_lastmodified()
                list($pattern, $pattern_a, $forceignorelist) =
                        get_autolink_pattern($pages);
 
-               $fp = fopen(CACHE_DIR . 'autolink.dat', 'w') or
-                       die_message('Cannot write autolink file ' .
-                       CACHE_DIR . '/autolink.dat' .
-                       '<br />Maybe permission is not writable');
+               $fp = fopen(CACHE_DIR . PKWK_AUTOLINK_REGEX_CACHE, 'w') or
+                       die_message('Cannot write file ' .
+                       'CACHE_DIR/' . PKWK_AUTOLINK_REGEX_CACHE . '<br />' . "\n" .
+                       'Maybe permission is not writable');
                set_file_buffer($fp, 0);
                flock($fp, LOCK_EX);
                rewind($fp);
@@ -326,7 +368,7 @@ function put_lastmodified()
        }
 }
 
-// Get elapsed date of the pate
+// Get elapsed date of the page
 function get_pg_passage($page, $sw = TRUE)
 {
        global $show_passage;
@@ -418,7 +460,7 @@ function get_readings()
                                if(! file_exists($pagereading_chasen_path))
                                        die_message('ChaSen not found: ' . $pagereading_chasen_path);
 
-                               $tmpfname = tempnam(CACHE_DIR, 'PageReading');
+                               $tmpfname = tempnam(realpath(CACHE_DIR), 'PageReading');
                                $fp = fopen($tmpfname, 'w') or
                                        die_message('Cannot write temporary file "' . $tmpfname . '".' . "\n");
                                foreach ($readings as $page => $reading) {
@@ -454,7 +496,7 @@ function get_readings()
                                if(! file_exists($pagereading_kakasi_path))
                                        die_message('KAKASI not found: ' . $pagereading_kakasi_path);
 
-                               $tmpfname = tempnam(CACHE_DIR, 'PageReading');
+                               $tmpfname = tempnam(realpath(CACHE_DIR), 'PageReading');
                                $fp       = fopen($tmpfname, 'w') or
                                        die_message('Cannot write temporary file "' . $tmpfname . '".' . "\n");
                                foreach ($readings as $page => $reading) {