OSDN Git Service

Cleanup. Japanese comments => English.
[pukiwiki/pukiwiki.git] / lib / file.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone.
3 // $Id: file.php,v 1.11 2005/01/10 04:24:31 henoheno Exp $
4 //
5 // File related functions
6
7 // Get source(wiki text) data of the page
8 function get_source($page = NULL)
9 {
10         // Removing line-feeds: Because file() doesn't remove them.
11         return is_page($page) ? str_replace("\r", '', file(get_filename($page))) : array();
12 }
13
14 // Get last-modified filetime of the page
15 function get_filetime($page)
16 {
17         return is_page($page) ? filemtime(get_filename($page)) - LOCALZONE : 0;
18 }
19
20 // Get physical file name of the page
21 function get_filename($page)
22 {
23         return DATA_DIR . encode($page) . '.txt';
24 }
25
26 // Put a data(wiki text) into a physical file(diff, backup, text)
27 function page_write($page, $postdata, $notimestamp = FALSE)
28 {
29         global $trackback;
30
31         $postdata = make_str_rules($postdata);
32
33         // Create and write diff
34         $oldpostdata = is_page($page) ? join('', get_source($page)) : '';
35         $diffdata    = do_diff($oldpostdata, $postdata);
36         file_write(DIFF_DIR, $page, $diffdata);
37
38         // Create backup
39         make_backup($page, $postdata == ''); // Is $postdata null?
40
41         // Create wiki text
42         file_write(DATA_DIR, $page, $postdata, $notimestamp);
43
44         if ($trackback) {
45                 // TrackBack Ping
46                 $_diff = explode("\n", $diffdata);
47                 $plus  = join("\n", preg_replace('/^\+/', '', preg_grep('/^\+/', $_diff)));
48                 $minus = join("\n", preg_replace('/^-/',  '', preg_grep('/^-/',  $_diff)));
49                 tb_send($page, $plus, $minus);
50         }
51
52         links_update($page);
53 }
54
55 // User-defined rules (replace the source)
56 function make_str_rules($str)
57 {
58         global $str_rules, $fixed_heading_anchor;
59
60         $arr = explode("\n", $str);
61
62         $retvars = $matches = array();
63         foreach ($arr as $str) {
64                 if ($str != '' && $str{0} != ' ' && $str{0} != "\t") {
65                         foreach ($str_rules as $rule => $replace)
66                                 $str = preg_replace("/$rule/", $replace, $str);
67                 }
68                 
69                 // Adding fixed anchor into headings
70                 if ($fixed_heading_anchor &&
71                         preg_match('/^(\*{1,3}(.(?!\[#[A-Za-z][\w-]+\]))+)$/', $str, $matches))
72                 {
73                         // Generate ID:
74                         // A random alphabetic letter + 7 letters of random strings from md()
75                         $anchor = chr(mt_rand(ord('a'), ord('z'))) .
76                                 substr(md5(uniqid(substr($matches[1], 0, 100), 1)), mt_rand(0, 24), 7);
77                         $str = rtrim($matches[1]) . " [#$anchor]";
78                 }
79                 $retvars[] = $str;
80         }
81
82         return join("\n", $retvars);
83 }
84
85 // Output to a file
86 function file_write($dir, $page, $str, $notimestamp = FALSE)
87 {
88         global $update_exec;
89         global $_msg_invalidiwn;
90         global $notify, $notify_diff_only, $notify_to, $notify_subject, $notify_header;
91         global $smtp_server, $smtp_auth;
92
93         if (! is_pagename($page))
94                 die_message(str_replace('$1', htmlspecialchars($page),
95                             str_replace('$2', 'WikiName', $_msg_invalidiwn)));
96
97         $page      = strip_bracket($page);
98         $timestamp = FALSE;
99         $file      = $dir . encode($page) . '.txt';
100
101         if ($dir == DATA_DIR && $str == '' && file_exists($file)) {
102                 unlink($file);
103                 put_recentdeleted($page);
104         }
105
106         if ($str != '') {
107                 $str = preg_replace("/\r/", '', $str);
108                 $str = rtrim($str) . "\n";
109
110                 if ($notimestamp && file_exists($file))
111                         $timestamp = filemtime($file) - LOCALZONE;
112
113                 $fp = fopen($file, 'w') or
114                         die_message('Cannot write page file or diff file or other ' .
115                         htmlspecialchars($page) .
116                         '<br />Maybe permission is not writable or filename is too long');
117
118                 set_file_buffer($fp, 0);
119                 flock($fp, LOCK_EX);
120                 rewind($fp);
121                 fputs($fp, $str);
122                 flock($fp, LOCK_UN);
123                 fclose($fp);
124                 if ($timestamp) 
125                         touch($file, $timestamp + LOCALZONE);
126         }
127
128         // Clear is_page() cache
129         is_page($page, TRUE);
130
131         if (! $timestamp && $dir == DATA_DIR)
132                 put_lastmodified();
133
134         // Execute $update_exec here
135         if ($update_exec && $dir == DATA_DIR)
136                 system($update_exec . ' > /dev/null &');
137
138         if ($notify && $dir == DIFF_DIR) {
139                 if ($notify_diff_only) $str = preg_replace('/^[^-+].*\n/m', '', $str);
140                 $str .= "\n" .
141                         str_repeat('-', 30) . "\n" .
142                         'URI: ' . get_script_uri() . '?' . rawurlencode($page) . "\n" .
143                         'REMOTE_ADDR: ' . $_SERVER['REMOTE_ADDR'] . "\n";
144
145                 $subject = str_replace('$page', $page, $notify_subject);
146                 ini_set('SMTP', $smtp_server);
147                 mb_language(LANG);
148
149                 if ($smtp_auth) pop_before_smtp();
150                 mb_send_mail($notify_to, $subject, $str, $notify_header);
151         }
152 }
153
154 // Update RecentDeleted
155 function put_recentdeleted($page)
156 {
157         global $whatsdeleted, $maxshow_deleted;
158
159         if ($maxshow_deleted == 0) return;
160
161         $lines = $matches = array();
162         foreach (get_source($whatsdeleted) as $line)
163                 if (preg_match('/^-(.+) - (\[\[.+\]\])$/', $line, $matches))
164                         $lines[$matches[2]] = $line;
165
166         $_page = '[[' . $page . ']]';
167         if (isset($lines[$_page])) unset($lines[$_page]);
168
169         array_unshift($lines, '-' . format_date(UTIME) . ' - ' . $_page . "\n");
170         $lines = array_splice($lines, 0, $maxshow_deleted);
171
172         $fp = fopen(get_filename($whatsdeleted), 'w') or
173                 die_message('Cannot write page file ' .
174                 htmlspecialchars($whatsdeleted) .
175                 '<br />Maybe permission is not writable or filename is too long');
176
177         set_file_buffer($fp, 0);
178         flock($fp, LOCK_EX);
179         rewind($fp);
180         fputs($fp, join('', $lines));
181         fputs($fp, '#norelated' . "\n"); // :)
182         flock($fp, LOCK_UN);
183         fclose($fp);
184 }
185
186 // Update RecentChanges
187 function put_lastmodified()
188 {
189         global $maxshow, $whatsnew, $non_list, $autolink;
190
191         $pages = get_existpages();
192         $recent_pages = array();
193         $non_list_pattern = '/' . $non_list . '/';
194         foreach($pages as $page)
195                 if ($page != $whatsnew && ! preg_match($non_list_pattern, $page))
196                         $recent_pages[$page] = get_filetime($page);
197
198         // Sort decending order of last-modification date
199         arsort($recent_pages, SORT_NUMERIC);
200
201         // Create recent.dat (for recent.inc.php)
202         $fp = fopen(CACHE_DIR . 'recent.dat', 'w') or
203                 die_message('Cannot write cache file ' .
204                 CACHE_DIR . 'recent.dat' .
205                 '<br />Maybe permission is not writable or filename is too long');
206
207         set_file_buffer($fp, 0);
208         flock($fp, LOCK_EX);
209         rewind($fp);
210         foreach ($recent_pages as $page=>$time)
211                 fputs($fp, $time . "\t" . $page . "\n");
212         flock($fp, LOCK_UN);
213         fclose($fp);
214
215         // Create RecentChanges
216         $fp = fopen(get_filename($whatsnew), 'w') or
217                 die_message('Cannot write page file ' .
218                 htmlspecialchars($whatsnew) .
219                 '<br />Maybe permission is not writable or filename is too long');
220
221         set_file_buffer($fp, 0);
222         flock($fp, LOCK_EX);
223         rewind($fp);
224         foreach (array_splice(array_keys($recent_pages), 0, $maxshow) as $page) {
225                 $time      = $recent_pages[$page];
226                 $s_lastmod = htmlspecialchars(format_date($time));
227                 $s_page    = htmlspecialchars($page);
228                 fputs($fp, '-' . $s_lastmod . ' - [[' . $s_page . ']]' . "\n");
229         }
230         fputs($fp, '#norelated' . "\n"); // :)
231         flock($fp, LOCK_UN);
232         fclose($fp);
233
234         // For AutoLink
235         if ($autolink) {
236                 list($pattern, $pattern_a, $forceignorelist) =
237                         get_autolink_pattern($pages);
238
239                 $fp = fopen(CACHE_DIR . 'autolink.dat', 'w') or
240                         die_message('Cannot write autolink file ' .
241                         CACHE_DIR . '/autolink.dat' .
242                         '<br />Maybe permission is not writable');
243                 set_file_buffer($fp, 0);
244                 flock($fp, LOCK_EX);
245                 rewind($fp);
246                 fputs($fp, $pattern   . "\n");
247                 fputs($fp, $pattern_a . "\n");
248                 fputs($fp, join("\t", $forceignorelist) . "\n");
249                 flock($fp, LOCK_UN);
250                 fclose($fp);
251         }
252 }
253
254 // Get elapsed date of the pate
255 function get_pg_passage($page, $sw = TRUE)
256 {
257         global $show_passage;
258         if (! $show_passage) return '';
259
260         $time = get_filetime($page);
261         $pg_passage = ($time != 0) ? get_passage($time) : '';
262
263         return $sw ? "<small>$pg_passage</small>" : " $pg_passage";
264 }
265
266 // Last-Modified header
267 function header_lastmod($page = NULL)
268 {
269         global $lastmod;
270
271         if ($lastmod && is_page($page)) {
272                 pkwk_headers_sent();
273                 header('Last-Modified: ' .
274                         date('D, d M Y H:i:s', get_filetime($page)) . ' GMT');
275         }
276 }
277
278 // Get a page list of this wiki
279 function get_existpages($dir = DATA_DIR, $ext = '.txt')
280 {
281         $aryret = array();
282
283         $pattern = '((?:[0-9A-F]{2})+)';
284         if ($ext != '') $ext = preg_quote($ext, '/');
285         $pattern = '/^' . $pattern . $ext . '$/';
286
287         $dp = @opendir($dir) or
288                 die_message($dir . ' is not found or not readable.');
289         $matches = array();
290         while ($file = readdir($dp))
291                 if (preg_match($pattern, $file, $matches))
292                         $aryret[$file] = decode($matches[1]);
293         closedir($dp);
294
295         return $aryret;
296 }
297
298 // Get PageReading(pronounce-annotated) data in an array()
299 function get_readings()
300 {
301         global $pagereading_enable, $pagereading_kanji2kana_converter;
302         global $pagereading_kanji2kana_encoding, $pagereading_chasen_path;
303         global $pagereading_kakasi_path, $pagereading_config_page;
304         global $pagereading_config_dict;
305
306         $pages = get_existpages();
307
308         $readings = array();
309         foreach ($pages as $page) 
310                 $readings[$page] = '';
311
312         $deletedPage = FALSE;
313         $matches = array();
314         foreach (get_source($pagereading_config_page) as $line) {
315                 $line = chop($line);
316                 if(preg_match('/^-\[\[([^]]+)\]\]\s+(.+)$/', $line, $matches)) {
317                         if(isset($readings[$matches[1]])) {
318                                 // This page is not clear how to be pronounced
319                                 $readings[$matches[1]] = $matches[2];
320                         } else {
321                                 // This page seems deleted
322                                 $deletedPage = TRUE;
323                         }
324                 }
325         }
326
327         // If enabled ChaSen/KAKASI execution
328         if($pagereading_enable) {
329
330                 // Check there's non-clear-pronouncing page
331                 $unknownPage = FALSE;
332                 foreach ($readings as $page => $reading) {
333                         if($reading == '') {
334                                 $unknownPage = TRUE;
335                                 break;
336                         }
337                 }
338
339                 // Execute ChaSen/KAKASI, and get annotation
340                 if($unknownPage) {
341                         switch(strtolower($pagereading_kanji2kana_converter)) {
342                         case 'chasen':
343                                 if(! file_exists($pagereading_chasen_path))
344                                         die_message('ChaSen not found: ' . $pagereading_chasen_path);
345
346                                 $tmpfname = tempnam(CACHE_DIR, 'PageReading');
347                                 $fp = fopen($tmpfname, 'w') or
348                                         die_message('Cannot write temporary file "' . $tmpfname . '".' . "\n");
349                                 foreach ($readings as $page => $reading) {
350                                         if($reading != '') continue;
351                                         fputs($fp, mb_convert_encoding($page . "\n",
352                                                 $pagereading_kanji2kana_encoding, SOURCE_ENCODING));
353                                 }
354                                 fclose($fp);
355
356                                 $chasen = "$pagereading_chasen_path -F %y $tmpfname";
357                                 $fp     = popen($chasen, 'r');
358                                 if($fp === FALSE) {
359                                         unlink($tmpfname);
360                                         die_message('ChaSen execution failed: ' . $chasen);
361                                 }
362                                 foreach ($readings as $page => $reading) {
363                                         if($reading != '') continue;
364
365                                         $line = fgets($fp);
366                                         $line = mb_convert_encoding($line, SOURCE_ENCODING,
367                                                 $pagereading_kanji2kana_encoding);
368                                         $line = chop($line);
369                                         $readings[$page] = $line;
370                                 }
371                                 pclose($fp);
372
373                                 unlink($tmpfname) or
374                                         die_message('Temporary file can not be removed: ' . $tmpfname);
375                                 break;
376
377                         case 'kakasi':  /*FALLTHROUGH*/
378                         case 'kakashi':
379                                 if(! file_exists($pagereading_kakasi_path))
380                                         die_message('KAKASI not found: ' . $pagereading_kakasi_path);
381
382                                 $tmpfname = tempnam(CACHE_DIR, 'PageReading');
383                                 $fp       = fopen($tmpfname, 'w') or
384                                         die_message('Cannot write temporary file "' . $tmpfname . '".' . "\n");
385                                 foreach ($readings as $page => $reading) {
386                                         if($reading != '') continue;
387                                         fputs($fp, mb_convert_encoding($page . "\n",
388                                                 $pagereading_kanji2kana_encoding, SOURCE_ENCODING));
389                                 }
390                                 fclose($fp);
391
392                                 $kakasi = "$pagereading_kakasi_path -kK -HK -JK < $tmpfname";
393                                 $fp     = popen($kakasi, 'r');
394                                 if($fp === FALSE) {
395                                         unlink($tmpfname);
396                                         die_message('KAKASI execution failed: ' . $kakasi);
397                                 }
398
399                                 foreach ($readings as $page => $reading) {
400                                         if($reading != '') continue;
401
402                                         $line = fgets($fp);
403                                         $line = mb_convert_encoding($line, SOURCE_ENCODING,
404                                                 $pagereading_kanji2kana_encoding);
405                                         $line = chop($line);
406                                         $readings[$page] = $line;
407                                 }
408                                 pclose($fp);
409
410                                 unlink($tmpfname) or
411                                         die_message('Temporary file can not be removed: ' . $tmpfname);
412                                 break;
413
414                         case 'none':
415                                 $patterns = $replacements = $matches = array();
416                                 foreach (get_source($pagereading_config_dict) as $line) {
417                                         $line = chop($line);
418                                         if(preg_match('|^ /([^/]+)/,\s*(.+)$|', $line, $matches)) {
419                                                 $patterns[]     = $matches[1];
420                                                 $replacements[] = $matches[2];
421                                         }
422                                 }
423                                 foreach ($readings as $page => $reading) {
424                                         if($reading != '') continue;
425
426                                         $readings[$page] = $page;
427                                         foreach ($patterns as $no => $pattern)
428                                                 $readings[$page] = mb_convert_kana(mb_ereg_replace($pattern,
429                                                         $replacements[$no], $readings[$page]), 'aKCV');
430                                 }
431                                 break;
432
433                         default:
434                                 die_message('Unknown kanji-kana converter: ' . $pagereading_kanji2kana_converter . '.');
435                                 break;
436                         }
437                 }
438
439                 if($unknownPage || $deletedPage) {
440
441                         asort($readings); // Sort by pronouncing(alphabetical/reading) order
442                         $body = '';
443                         foreach ($readings as $page => $reading)
444                                 $body .= '-[[' . $page . ']] ' . $reading . "\n";
445
446                         page_write($pagereading_config_page, $body);
447                 }
448         }
449
450         // Pages that are not prounouncing-clear, return pagenames of themselves
451         foreach ($pages as $page) {
452                 if($readings[$page] == '')
453                         $readings[$page] = $page;
454         }
455
456         return $readings;
457 }
458
459 // Get a list of encoded files (must specify a directory and a suffix)
460 function get_existfiles($dir, $ext)
461 {
462         $pattern = '/^(?:[0-9A-F]{2})+' . preg_quote($ext, '/') . '$/';
463         $aryret = array();
464         $dp = @opendir($dir) or die_message($dir . ' is not found or not readable.');
465         while ($file = readdir($dp))
466                 if (preg_match($pattern, $file))
467                         $aryret[] = $dir . $file;
468         closedir($dp);
469         return $aryret;
470 }
471
472 // Get a list of related pages of the page
473 function links_get_related($page)
474 {
475         global $vars, $related;
476         static $links = array();
477
478         if (isset($links[$page])) return $links[$page];
479
480         // If possible, merge related pages generated by make_link()
481         $links[$page] = ($page == $vars['page']) ? $related : array();
482
483         // Get repated pages from DB
484         $links[$page] += links_get_related_db($vars['page']);
485
486         return $links[$page];
487 }
488 ?>