PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/file.php

https://github.com/miya5n/pukiwiki
PHP | 840 lines | 581 code | 147 blank | 112 comment | 160 complexity | c56805ba5afb7ab7538ab37db79e44ac MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. // PukiWiki - Yet another WikiWikiWeb clone.
  3. // $Id: file.php,v 1.95 2011/01/25 15:01:01 henoheno Exp $
  4. // Copyright (C)
  5. // 2002-2007 PukiWiki Developers Team
  6. // 2001-2002 Originally written by yu-ji
  7. // License: GPL v2 or (at your option) any later version
  8. //
  9. // File related functions
  10. // RecentChanges
  11. define('PKWK_MAXSHOW_ALLOWANCE', 10);
  12. define('PKWK_MAXSHOW_CACHE', 'recent.dat');
  13. // XHTML entities
  14. define('PKWK_ENTITIES_REGEX_CACHE', 'entities.dat');
  15. // AutoLink
  16. define('PKWK_AUTOLINK_REGEX_CACHE', 'autolink.dat');
  17. // AutoAlias
  18. define('PKWK_AUTOALIAS_REGEX_CACHE', 'autoalias.dat');
  19. // Get source(wiki text) data of the page
  20. // Returns FALSE if error occurerd
  21. function get_source($page = NULL, $lock = TRUE, $join = FALSE)
  22. {
  23. //$result = NULL; // File is not found
  24. $result = $join ? '' : array();
  25. // Compat for "implode('', get_source($file))",
  26. // -- this is slower than "get_source($file, TRUE, TRUE)"
  27. // Compat for foreach(get_source($file) as $line) {} not to warns
  28. $path = get_filename($page);
  29. if (file_exists($path)) {
  30. if ($lock || $join) {
  31. $fp = @fopen($path, 'r');
  32. if ($fp === FALSE) return FALSE;
  33. }
  34. if ($lock) flock($fp, LOCK_SH);
  35. if ($join) {
  36. $size = filesize($path);
  37. if ($size === FALSE) {
  38. $result = FALSE;
  39. } else if ($size == 0) {
  40. $result = '';
  41. } else {
  42. $result = fread($fp, $size); // Returns a value
  43. }
  44. } else {
  45. $result = file($path); // Returns an array
  46. }
  47. if ($lock) flock($fp, LOCK_UN);
  48. if ($lock || $join) {
  49. @fclose($fp);
  50. }
  51. if ($result !== FALSE) {
  52. // Removing line-feeds
  53. $result = str_replace("\r", '', $result);
  54. }
  55. }
  56. return $result;
  57. }
  58. // Get last-modified filetime of the page
  59. function get_filetime($page)
  60. {
  61. return is_page($page) ? filemtime(get_filename($page)) - LOCALZONE : 0;
  62. }
  63. // Get physical file name of the page
  64. function get_filename($page)
  65. {
  66. return DATA_DIR . encode($page) . '.txt';
  67. }
  68. // Put a data(wiki text) into a physical file(diff, backup, text)
  69. function page_write($page, $postdata, $notimestamp = FALSE)
  70. {
  71. global $autoalias, $aliaspage;
  72. if (PKWK_READONLY) return; // Do nothing
  73. $postdata = make_str_rules($postdata);
  74. // Create and write diff
  75. $oldpostdata = is_page($page) ? get_source($page, TRUE, TRUE) : '';
  76. $diffdata = do_diff($oldpostdata, $postdata);
  77. file_write(DIFF_DIR, $page, $diffdata);
  78. unset($oldpostdata, $diffdata);
  79. // Create backup
  80. make_backup($page, $postdata == ''); // Is $postdata null?
  81. // Create wiki text
  82. file_write(DATA_DIR, $page, $postdata, $notimestamp);
  83. links_update($page);
  84. // Update autoalias.dat (AutoAliasName)
  85. if ($autoalias && $page === $aliaspage) {
  86. $aliases = get_autoaliases();
  87. if (empty($aliases)) {
  88. // Remove
  89. @unlink(CACHE_DIR . PKWK_AUTOALIAS_REGEX_CACHE);
  90. } else {
  91. // Create or Update
  92. autolink_pattern_write(CACHE_DIR . PKWK_AUTOALIAS_REGEX_CACHE,
  93. get_autolink_pattern(array_keys($aliases), $autoalias));
  94. }
  95. }
  96. }
  97. // Modify original text with user-defined / system-defined rules
  98. function make_str_rules($source)
  99. {
  100. global $str_rules, $fixed_heading_anchor;
  101. $lines = explode("\n", $source);
  102. $count = count($lines);
  103. $modify = TRUE;
  104. $multiline = 0;
  105. $matches = array();
  106. for ($i = 0; $i < $count; $i++) {
  107. $line = & $lines[$i]; // Modify directly
  108. // Ignore null string and preformatted texts
  109. if ($line == '' || $line{0} == ' ' || $line{0} == "\t") continue;
  110. // Modify this line?
  111. if ($modify) {
  112. if (! PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK &&
  113. $multiline == 0 &&
  114. preg_match('/#[^{]*(\{\{+)\s*$/', $line, $matches)) {
  115. // Multiline convert plugin start
  116. $modify = FALSE;
  117. $multiline = strlen($matches[1]); // Set specific number
  118. }
  119. } else {
  120. if (! PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK &&
  121. $multiline != 0 &&
  122. preg_match('/^\}{' . $multiline . '}\s*$/', $line)) {
  123. // Multiline convert plugin end
  124. $modify = TRUE;
  125. $multiline = 0;
  126. }
  127. }
  128. if ($modify === FALSE) continue;
  129. // Replace with $str_rules
  130. foreach ($str_rules as $pattern => $replacement)
  131. $line = preg_replace('/' . $pattern . '/', $replacement, $line);
  132. // Adding fixed anchor into headings
  133. if ($fixed_heading_anchor &&
  134. preg_match('/^(\*{1,3}.*?)(?:\[#([A-Za-z][\w-]*)\]\s*)?$/', $line, $matches) &&
  135. (! isset($matches[2]) || $matches[2] == '')) {
  136. // Generate unique id
  137. $anchor = generate_fixed_heading_anchor_id($matches[1]);
  138. $line = rtrim($matches[1]) . ' [#' . $anchor . ']';
  139. }
  140. }
  141. // Multiline part has no stopper
  142. if (! PKWKEXP_DISABLE_MULTILINE_PLUGIN_HACK &&
  143. $modify === FALSE && $multiline != 0)
  144. $lines[] = str_repeat('}', $multiline);
  145. return implode("\n", $lines);
  146. }
  147. // Generate ID
  148. function generate_fixed_heading_anchor_id($seed)
  149. {
  150. // A random alphabetic letter + 7 letters of random strings from md5()
  151. return chr(mt_rand(ord('a'), ord('z'))) .
  152. substr(md5(uniqid(substr($seed, 0, 100), TRUE)),
  153. mt_rand(0, 24), 7);
  154. }
  155. // Read top N lines as an array
  156. // (Use PHP file() function if you want to get ALL lines)
  157. function file_head($file, $count = 1, $lock = TRUE, $buffer = NULL)
  158. {
  159. $array = array();
  160. $fp = @fopen($file, 'r');
  161. if ($fp === FALSE) return FALSE;
  162. set_file_buffer($fp, 0);
  163. if ($lock) flock($fp, LOCK_SH);
  164. rewind($fp);
  165. $index = 0;
  166. if ($buffer === NULL) {
  167. while (! feof($fp)) {
  168. $line = fgets($fp);
  169. if ($line != FALSE) $array[] = $line;
  170. if (++$index >= $count) break;
  171. }
  172. } else {
  173. $buffer = max(16, intval($buffer));
  174. while (! feof($fp)) {
  175. $line = fgets($fp, $buffer);
  176. if ($line != FALSE) $array[] = $line;
  177. if (++$index >= $count) break;
  178. }
  179. }
  180. if ($lock) flock($fp, LOCK_UN);
  181. if (! fclose($fp)) return FALSE;
  182. return $array;
  183. }
  184. // Output to a file
  185. function file_write($dir, $page, $str, $notimestamp = FALSE)
  186. {
  187. global $_msg_invalidiwn, $notify, $notify_diff_only, $notify_subject;
  188. global $whatsdeleted, $maxshow_deleted;
  189. if (PKWK_READONLY) return; // Do nothing
  190. if ($dir != DATA_DIR && $dir != DIFF_DIR) die('file_write(): Invalid directory');
  191. $page = strip_bracket($page);
  192. $file = $dir . encode($page) . '.txt';
  193. $file_exists = file_exists($file);
  194. // ----
  195. // Delete?
  196. if ($dir == DATA_DIR && $str === '') {
  197. // Page deletion
  198. if (! $file_exists) return; // Ignore null posting for DATA_DIR
  199. // Update RecentDeleted (Add the $page)
  200. add_recent($page, $whatsdeleted, '', $maxshow_deleted);
  201. // Remove the page
  202. unlink($file);
  203. // Update RecentDeleted, and remove the page from RecentChanges
  204. lastmodified_add($whatsdeleted, $page);
  205. // Clear is_page() cache
  206. is_page($page, TRUE);
  207. return;
  208. } else if ($dir == DIFF_DIR && $str === " \n") {
  209. return; // Ignore null posting for DIFF_DIR
  210. }
  211. // ----
  212. // File replacement (Edit)
  213. if (! is_pagename($page))
  214. die_message(str_replace('$1', htmlsc($page),
  215. str_replace('$2', 'WikiName', $_msg_invalidiwn)));
  216. $str = rtrim(preg_replace('/' . "\r" . '/', '', $str)) . "\n";
  217. $timestamp = ($file_exists && $notimestamp) ? filemtime($file) : FALSE;
  218. $fp = fopen($file, 'a') or die('fopen() failed: ' .
  219. htmlsc(basename($dir) . '/' . encode($page) . '.txt') .
  220. '<br />' . "\n" .
  221. 'Maybe permission is not writable or filename is too long');
  222. set_file_buffer($fp, 0);
  223. flock($fp, LOCK_EX);
  224. ftruncate($fp, 0);
  225. rewind($fp);
  226. fputs($fp, $str);
  227. flock($fp, LOCK_UN);
  228. fclose($fp);
  229. if ($timestamp) pkwk_touch_file($file, $timestamp);
  230. // Optional actions
  231. if ($dir == DATA_DIR) {
  232. // Update RecentChanges (Add or renew the $page)
  233. if ($timestamp === FALSE) lastmodified_add($page);
  234. // Command execution per update
  235. if (defined('PKWK_UPDATE_EXEC') && PKWK_UPDATE_EXEC)
  236. system(PKWK_UPDATE_EXEC . ' > /dev/null &');
  237. } else if ($dir == DIFF_DIR && $notify) {
  238. if ($notify_diff_only) $str = preg_replace('/^[^-+].*\n/m', '', $str);
  239. $summary = array();
  240. $summary['ACTION'] = 'Page update';
  241. $summary['PAGE'] = & $page;
  242. $summary['URI'] = get_script_uri() . '?' . rawurlencode($page);
  243. $summary['USER_AGENT'] = TRUE;
  244. $summary['REMOTE_ADDR'] = TRUE;
  245. pkwk_mail_notify($notify_subject, $str, $summary) or
  246. die('pkwk_mail_notify(): Failed');
  247. }
  248. is_page($page, TRUE); // Clear is_page() cache
  249. }
  250. // Update RecentDeleted
  251. function add_recent($page, $recentpage, $subject = '', $limit = 0)
  252. {
  253. if (PKWK_READONLY || $limit == 0 || $page == '' || $recentpage == '' ||
  254. check_non_list($page)) return;
  255. // Load
  256. $lines = $matches = array();
  257. foreach (get_source($recentpage) as $line) {
  258. if (preg_match('/^-(.+) - (\[\[.+\]\])$/', $line, $matches)) {
  259. $lines[$matches[2]] = $line;
  260. }
  261. }
  262. $_page = '[[' . $page . ']]';
  263. // Remove a report about the same page
  264. if (isset($lines[$_page])) unset($lines[$_page]);
  265. // Add
  266. array_unshift($lines, '-' . format_date(UTIME) . ' - ' . $_page .
  267. htmlsc($subject) . "\n");
  268. // Get latest $limit reports
  269. $lines = array_splice($lines, 0, $limit);
  270. // Update
  271. $fp = fopen(get_filename($recentpage), 'w') or
  272. die_message('Cannot write page file ' .
  273. htmlsc($recentpage) .
  274. '<br />Maybe permission is not writable or filename is too long');
  275. set_file_buffer($fp, 0);
  276. flock($fp, LOCK_EX);
  277. rewind($fp);
  278. fputs($fp, '#norelated' . "\n"); // :)
  279. fputs($fp, join('', $lines));
  280. flock($fp, LOCK_UN);
  281. fclose($fp);
  282. }
  283. // Update PKWK_MAXSHOW_CACHE itself (Add or renew about the $page) (Light)
  284. // Use without $autolink
  285. function lastmodified_add($update = '', $remove = '')
  286. {
  287. global $maxshow, $whatsnew, $autolink;
  288. // AutoLink implimentation needs everything, for now
  289. if ($autolink) {
  290. put_lastmodified(); // Try to (re)create ALL
  291. return;
  292. }
  293. if (($update == '' || check_non_list($update)) && $remove == '')
  294. return; // No need
  295. $file = CACHE_DIR . PKWK_MAXSHOW_CACHE;
  296. if (! file_exists($file)) {
  297. put_lastmodified(); // Try to (re)create ALL
  298. return;
  299. }
  300. // Open
  301. pkwk_touch_file($file);
  302. $fp = fopen($file, 'r+') or
  303. die_message('Cannot open ' . 'CACHE_DIR/' . PKWK_MAXSHOW_CACHE);
  304. set_file_buffer($fp, 0);
  305. flock($fp, LOCK_EX);
  306. // Read (keep the order of the lines)
  307. $recent_pages = $matches = array();
  308. foreach(file_head($file, $maxshow + PKWK_MAXSHOW_ALLOWANCE, FALSE) as $line) {
  309. if (preg_match('/^([0-9]+)\t(.+)/', $line, $matches)) {
  310. $recent_pages[$matches[2]] = $matches[1];
  311. }
  312. }
  313. // Remove if it exists inside
  314. if (isset($recent_pages[$update])) unset($recent_pages[$update]);
  315. if (isset($recent_pages[$remove])) unset($recent_pages[$remove]);
  316. // Add to the top: like array_unshift()
  317. if ($update != '')
  318. $recent_pages = array($update => get_filetime($update)) + $recent_pages;
  319. // Check
  320. $abort = count($recent_pages) < $maxshow;
  321. if (! $abort) {
  322. // Write
  323. ftruncate($fp, 0);
  324. rewind($fp);
  325. foreach ($recent_pages as $_page=>$time)
  326. fputs($fp, $time . "\t" . $_page . "\n");
  327. }
  328. flock($fp, LOCK_UN);
  329. fclose($fp);
  330. if ($abort) {
  331. put_lastmodified(); // Try to (re)create ALL
  332. return;
  333. }
  334. // ----
  335. // Update the page 'RecentChanges'
  336. $recent_pages = array_splice($recent_pages, 0, $maxshow);
  337. $file = get_filename($whatsnew);
  338. // Open
  339. pkwk_touch_file($file);
  340. $fp = fopen($file, 'r+') or
  341. die_message('Cannot open ' . htmlsc($whatsnew));
  342. set_file_buffer($fp, 0);
  343. flock($fp, LOCK_EX);
  344. // Recreate
  345. ftruncate($fp, 0);
  346. rewind($fp);
  347. foreach ($recent_pages as $_page=>$time)
  348. fputs($fp, '-' . htmlsc(format_date($time)) .
  349. ' - ' . '[[' . htmlsc($_page) . ']]' . "\n");
  350. fputs($fp, '#norelated' . "\n"); // :)
  351. flock($fp, LOCK_UN);
  352. fclose($fp);
  353. }
  354. // Re-create PKWK_MAXSHOW_CACHE (Heavy)
  355. function put_lastmodified()
  356. {
  357. global $maxshow, $whatsnew, $autolink;
  358. if (PKWK_READONLY) return; // Do nothing
  359. // Get WHOLE page list
  360. $pages = get_existpages();
  361. // Check ALL filetime
  362. $recent_pages = array();
  363. foreach($pages as $page)
  364. if ($page !== $whatsnew && ! check_non_list($page))
  365. $recent_pages[$page] = get_filetime($page);
  366. // Sort decending order of last-modification date
  367. arsort($recent_pages, SORT_NUMERIC);
  368. // Cut unused lines
  369. // BugTrack2/179: array_splice() will break integer keys in hashtable
  370. $count = $maxshow + PKWK_MAXSHOW_ALLOWANCE;
  371. $_recent = array();
  372. foreach($recent_pages as $key=>$value) {
  373. unset($recent_pages[$key]);
  374. $_recent[$key] = $value;
  375. if (--$count < 1) break;
  376. }
  377. $recent_pages = & $_recent;
  378. // Re-create PKWK_MAXSHOW_CACHE
  379. $file = CACHE_DIR . PKWK_MAXSHOW_CACHE;
  380. pkwk_touch_file($file);
  381. $fp = fopen($file, 'r+') or
  382. die_message('Cannot open' . 'CACHE_DIR/' . PKWK_MAXSHOW_CACHE);
  383. set_file_buffer($fp, 0);
  384. flock($fp, LOCK_EX);
  385. ftruncate($fp, 0);
  386. rewind($fp);
  387. foreach ($recent_pages as $page=>$time)
  388. fputs($fp, $time . "\t" . $page . "\n");
  389. flock($fp, LOCK_UN);
  390. fclose($fp);
  391. // Create RecentChanges
  392. $file = get_filename($whatsnew);
  393. pkwk_touch_file($file);
  394. $fp = fopen($file, 'r+') or
  395. die_message('Cannot open ' . htmlsc($whatsnew));
  396. set_file_buffer($fp, 0);
  397. flock($fp, LOCK_EX);
  398. ftruncate($fp, 0);
  399. rewind($fp);
  400. foreach (array_keys($recent_pages) as $page) {
  401. $time = $recent_pages[$page];
  402. $s_lastmod = htmlsc(format_date($time));
  403. $s_page = htmlsc($page);
  404. fputs($fp, '-' . $s_lastmod . ' - [[' . $s_page . ']]' . "\n");
  405. }
  406. fputs($fp, '#norelated' . "\n"); // :)
  407. flock($fp, LOCK_UN);
  408. fclose($fp);
  409. // For AutoLink
  410. if ($autolink){
  411. autolink_pattern_write(CACHE_DIR . PKWK_AUTOLINK_REGEX_CACHE,
  412. get_autolink_pattern($pages, $autolink));
  413. }
  414. }
  415. // update autolink data
  416. function autolink_pattern_write($filename, $autolink_pattern)
  417. {
  418. list($pattern, $pattern_a, $forceignorelist) = $autolink_pattern;
  419. $fp = fopen($filename, 'w') or
  420. die_message('Cannot open ' . $filename);
  421. set_file_buffer($fp, 0);
  422. flock($fp, LOCK_EX);
  423. rewind($fp);
  424. fputs($fp, $pattern . "\n");
  425. fputs($fp, $pattern_a . "\n");
  426. fputs($fp, join("\t", $forceignorelist) . "\n");
  427. flock($fp, LOCK_UN);
  428. fclose($fp);
  429. }
  430. // Get elapsed date of the page
  431. function get_pg_passage($page, $sw = TRUE)
  432. {
  433. global $show_passage;
  434. if (! $show_passage) return '';
  435. $time = get_filetime($page);
  436. $pg_passage = ($time != 0) ? get_passage($time) : '';
  437. return $sw ? '<small>' . $pg_passage . '</small>' : ' ' . $pg_passage;
  438. }
  439. // Last-Modified header
  440. function header_lastmod($page = NULL)
  441. {
  442. global $lastmod;
  443. if ($lastmod && is_page($page)) {
  444. pkwk_headers_sent();
  445. header('Last-Modified: ' .
  446. date('D, d M Y H:i:s', get_filetime($page)) . ' GMT');
  447. }
  448. }
  449. // Get a list of encoded files (must specify a directory and a suffix)
  450. function get_existfiles($dir = DATA_DIR, $ext = '.txt')
  451. {
  452. $aryret = array();
  453. $pattern = '/^(?:[0-9A-F]{2})+' . preg_quote($ext, '/') . '$/';
  454. $dp = @opendir($dir) or die_message($dir . ' is not found or not readable.');
  455. while (($file = readdir($dp)) !== FALSE) {
  456. if (preg_match($pattern, $file)) {
  457. $aryret[] = $dir . $file;
  458. }
  459. }
  460. closedir($dp);
  461. return $aryret;
  462. }
  463. // Get a page list of this wiki
  464. function get_existpages($dir = DATA_DIR, $ext = '.txt')
  465. {
  466. $aryret = array();
  467. $pattern = '/^((?:[0-9A-F]{2})+)' . preg_quote($ext, '/') . '$/';
  468. $dp = @opendir($dir) or die_message($dir . ' is not found or not readable.');
  469. $matches = array();
  470. while (($file = readdir($dp)) !== FALSE) {
  471. if (preg_match($pattern, $file, $matches)) {
  472. $aryret[$file] = decode($matches[1]);
  473. }
  474. }
  475. closedir($dp);
  476. return $aryret;
  477. }
  478. // Get PageReading(pronounce-annotated) data in an array()
  479. function get_readings()
  480. {
  481. global $pagereading_enable, $pagereading_kanji2kana_converter;
  482. global $pagereading_kanji2kana_encoding, $pagereading_chasen_path;
  483. global $pagereading_kakasi_path, $pagereading_config_page;
  484. global $pagereading_config_dict;
  485. $pages = get_existpages();
  486. $readings = array();
  487. foreach ($pages as $page)
  488. $readings[$page] = '';
  489. $deletedPage = FALSE;
  490. $matches = array();
  491. foreach (get_source($pagereading_config_page) as $line) {
  492. $line = chop($line);
  493. if(preg_match('/^-\[\[([^]]+)\]\]\s+(.+)$/', $line, $matches)) {
  494. if(isset($readings[$matches[1]])) {
  495. // This page is not clear how to be pronounced
  496. $readings[$matches[1]] = $matches[2];
  497. } else {
  498. // This page seems deleted
  499. $deletedPage = TRUE;
  500. }
  501. }
  502. }
  503. // If enabled ChaSen/KAKASI execution
  504. if($pagereading_enable) {
  505. // Check there's non-clear-pronouncing page
  506. $unknownPage = FALSE;
  507. foreach ($readings as $page => $reading) {
  508. if($reading == '') {
  509. $unknownPage = TRUE;
  510. break;
  511. }
  512. }
  513. // Execute ChaSen/KAKASI, and get annotation
  514. if($unknownPage) {
  515. switch(strtolower($pagereading_kanji2kana_converter)) {
  516. case 'chasen':
  517. if(! file_exists($pagereading_chasen_path))
  518. die_message('ChaSen not found: ' . $pagereading_chasen_path);
  519. $tmpfname = tempnam(realpath(CACHE_DIR), 'PageReading');
  520. $fp = fopen($tmpfname, 'w') or
  521. die_message('Cannot write temporary file "' . $tmpfname . '".' . "\n");
  522. foreach ($readings as $page => $reading) {
  523. if($reading != '') continue;
  524. fputs($fp, mb_convert_encoding($page . "\n",
  525. $pagereading_kanji2kana_encoding, SOURCE_ENCODING));
  526. }
  527. fclose($fp);
  528. $chasen = "$pagereading_chasen_path -F %y $tmpfname";
  529. $fp = popen($chasen, 'r');
  530. if($fp === FALSE) {
  531. unlink($tmpfname);
  532. die_message('ChaSen execution failed: ' . $chasen);
  533. }
  534. foreach ($readings as $page => $reading) {
  535. if($reading != '') continue;
  536. $line = fgets($fp);
  537. $line = mb_convert_encoding($line, SOURCE_ENCODING,
  538. $pagereading_kanji2kana_encoding);
  539. $line = chop($line);
  540. $readings[$page] = $line;
  541. }
  542. pclose($fp);
  543. unlink($tmpfname) or
  544. die_message('Temporary file can not be removed: ' . $tmpfname);
  545. break;
  546. case 'kakasi': /*FALLTHROUGH*/
  547. case 'kakashi':
  548. if(! file_exists($pagereading_kakasi_path))
  549. die_message('KAKASI not found: ' . $pagereading_kakasi_path);
  550. $tmpfname = tempnam(realpath(CACHE_DIR), 'PageReading');
  551. $fp = fopen($tmpfname, 'w') or
  552. die_message('Cannot write temporary file "' . $tmpfname . '".' . "\n");
  553. foreach ($readings as $page => $reading) {
  554. if($reading != '') continue;
  555. fputs($fp, mb_convert_encoding($page . "\n",
  556. $pagereading_kanji2kana_encoding, SOURCE_ENCODING));
  557. }
  558. fclose($fp);
  559. $kakasi = "$pagereading_kakasi_path -kK -HK -JK < $tmpfname";
  560. $fp = popen($kakasi, 'r');
  561. if($fp === FALSE) {
  562. unlink($tmpfname);
  563. die_message('KAKASI execution failed: ' . $kakasi);
  564. }
  565. foreach ($readings as $page => $reading) {
  566. if($reading != '') continue;
  567. $line = fgets($fp);
  568. $line = mb_convert_encoding($line, SOURCE_ENCODING,
  569. $pagereading_kanji2kana_encoding);
  570. $line = chop($line);
  571. $readings[$page] = $line;
  572. }
  573. pclose($fp);
  574. unlink($tmpfname) or
  575. die_message('Temporary file can not be removed: ' . $tmpfname);
  576. break;
  577. case 'none':
  578. $patterns = $replacements = $matches = array();
  579. foreach (get_source($pagereading_config_dict) as $line) {
  580. $line = chop($line);
  581. if(preg_match('|^ /([^/]+)/,\s*(.+)$|', $line, $matches)) {
  582. $patterns[] = $matches[1];
  583. $replacements[] = $matches[2];
  584. }
  585. }
  586. foreach ($readings as $page => $reading) {
  587. if($reading != '') continue;
  588. $readings[$page] = $page;
  589. foreach ($patterns as $no => $pattern)
  590. $readings[$page] = mb_convert_kana(mb_ereg_replace($pattern,
  591. $replacements[$no], $readings[$page]), 'aKCV');
  592. }
  593. break;
  594. default:
  595. die_message('Unknown kanji-kana converter: ' . $pagereading_kanji2kana_converter . '.');
  596. break;
  597. }
  598. }
  599. if($unknownPage || $deletedPage) {
  600. asort($readings, SORT_STRING); // Sort by pronouncing(alphabetical/reading) order
  601. $body = '';
  602. foreach ($readings as $page => $reading)
  603. $body .= '-[[' . $page . ']] ' . $reading . "\n";
  604. page_write($pagereading_config_page, $body);
  605. }
  606. }
  607. // Pages that are not prounouncing-clear, return pagenames of themselves
  608. foreach ($pages as $page) {
  609. if($readings[$page] == '')
  610. $readings[$page] = $page;
  611. }
  612. return $readings;
  613. }
  614. // Get a list of related pages of the page
  615. function links_get_related($page)
  616. {
  617. global $vars, $related;
  618. static $links = array();
  619. if (isset($links[$page])) return $links[$page];
  620. // If possible, merge related pages generated by make_link()
  621. $links[$page] = ($page === $vars['page']) ? $related : array();
  622. // Get repated pages from DB
  623. $links[$page] += links_get_related_db($vars['page']);
  624. return $links[$page];
  625. }
  626. // _If needed_, re-create the file to change/correct ownership into PHP's
  627. // NOTE: Not works for Windows
  628. function pkwk_chown($filename, $preserve_time = TRUE)
  629. {
  630. static $php_uid; // PHP's UID
  631. if (! isset($php_uid)) {
  632. if (extension_loaded('posix')) {
  633. $php_uid = posix_getuid(); // Unix
  634. } else {
  635. $php_uid = 0; // Windows
  636. }
  637. }
  638. // Lock for pkwk_chown()
  639. $lockfile = CACHE_DIR . 'pkwk_chown.lock';
  640. $flock = fopen($lockfile, 'a') or
  641. die('pkwk_chown(): fopen() failed for: CACHEDIR/' .
  642. basename(htmlsc($lockfile)));
  643. flock($flock, LOCK_EX) or die('pkwk_chown(): flock() failed for lock');
  644. // Check owner
  645. $stat = stat($filename) or
  646. die('pkwk_chown(): stat() failed for: ' . basename(htmlsc($filename)));
  647. if ($stat[4] === $php_uid) {
  648. // NOTE: Windows always here
  649. $result = TRUE; // Seems the same UID. Nothing to do
  650. } else {
  651. $tmp = $filename . '.' . getmypid() . '.tmp';
  652. // Lock source $filename to avoid file corruption
  653. // NOTE: Not 'r+'. Don't check write permission here
  654. $ffile = fopen($filename, 'r') or
  655. die('pkwk_chown(): fopen() failed for: ' .
  656. basename(htmlsc($filename)));
  657. // Try to chown by re-creating files
  658. // NOTE:
  659. // * touch() before copy() is for 'rw-r--r--' instead of 'rwxr-xr-x' (with umask 022).
  660. // * (PHP 4 < PHP 4.2.0) touch() with the third argument is not implemented and retuns NULL and Warn.
  661. // * @unlink() before rename() is for Windows but here's for Unix only
  662. flock($ffile, LOCK_EX) or die('pkwk_chown(): flock() failed');
  663. $result = touch($tmp) && copy($filename, $tmp) &&
  664. ($preserve_time ? (touch($tmp, $stat[9], $stat[8]) || touch($tmp, $stat[9])) : TRUE) &&
  665. rename($tmp, $filename);
  666. flock($ffile, LOCK_UN) or die('pkwk_chown(): flock() failed');
  667. fclose($ffile) or die('pkwk_chown(): fclose() failed');
  668. if ($result === FALSE) @unlink($tmp);
  669. }
  670. // Unlock for pkwk_chown()
  671. flock($flock, LOCK_UN) or die('pkwk_chown(): flock() failed for lock');
  672. fclose($flock) or die('pkwk_chown(): fclose() failed for lock');
  673. return $result;
  674. }
  675. // touch() with trying pkwk_chown()
  676. function pkwk_touch_file($filename, $time = FALSE, $atime = FALSE)
  677. {
  678. // Is the owner incorrected and unable to correct?
  679. if (! file_exists($filename) || pkwk_chown($filename)) {
  680. if ($time === FALSE) {
  681. $result = touch($filename);
  682. } else if ($atime === FALSE) {
  683. $result = touch($filename, $time);
  684. } else {
  685. $result = touch($filename, $time, $atime);
  686. }
  687. return $result;
  688. } else {
  689. die('pkwk_touch_file(): Invalid UID and (not writable for the directory or not a flie): ' .
  690. htmlsc(basename($filename)));
  691. }
  692. }
  693. ?>