<?php
/* 
 * $Id: func.inc.php,v 1.14 2005/02/26 08:58:38 youka Exp $
 */



/**
 * ページを指定してリダイレクトする。
 * 
 * @param	string	$pagename	リダイレクト先のページ名。
 */
function redirect($pagename)
{
	if(is_a($pagename, 'Page')){
		$pagename = $pagename->getpagename();
	}
	
	header('Location: ' . getURL($pagename));
	exit();
}


/**
 * Wikiのソースをブロック要素としてhtmlに変換する。
 * 
 * @param	string	$source	ソース
 * @param	string	$pagename	ページ名を$pagenameとして変換する。
 * @return	string	HTML形式の文字列。
 */
function convert_block($source, $pagename)
{
	$body = parse_block($source, $pagename);
	return HTMLConverter::visit($body);
}


/**
 * Wikiのソースをインライン要素としてhtmlに変換する。
 * 
 * @param	string	$source	ソース
 * @param	string	$pagename	ページ名を$pagenameとして変換する。
 * @return	string	HTML形式の文字列。
 */
function convert_inline($source, $pagename)
{
	$inline = parse_inline($source, $pagename);
	return HTMLConverter::visit($inline);
}


/**
 * 鮮度を取得する。
 * 
 * @param	int	$timestamp
 * @return	string	鮮度を表す文字列。
 */
function getold($timestamp)
{
	$life = TIME - $timestamp;
	if($life < 60){
		return $life . 's';
	}
	if($life < 60*60){
		return ((int)floor($life/60)) . 'm';
	}
	if($life < 60*60*24){
		return ((int)floor($life/(60*60))) . 'h';
	}
	return ((int)floor($life/(60*60*24))) . 'd';
}


/**
 * WikiName(BracketName)用リンクを作る。
 * 
 * @param	string	$pagename	ページ名
 * @param	string	$alias	aタグで囲まれる文字列。
 * @return	string	aタグ
 */
function makelink($pagename, $alias = '')
{
	$page = Page::getinstance($pagename);
	$url = getURL($pagename);
	$str = htmlspecialchars($alias == '' ? $pagename : $alias);
	$time = $page->gettimestamp();
	if($time != TIME || $page->getsource() != ''){
		$title = htmlspecialchars($pagename . ' (' . getold($time) . ')');
		return "<a href=\"$url\" title=\"$title\">$str</a>";
	}
	else{
		$title = '存在しないページ';
		return "<a class=\"noexistpage\" href=\"$url\" title=\"$title\">${str}</a>";
	}
}


/**
 * URLを取得する。
 * 
 * @param	string	pagename
 * @return	string	URL
 */
function getURL($pagename)
{
	$script = SCRIPTPATH;
	$encoded = preg_replace('/%2F/', '/', rawurlencode($pagename));
	return "{$script}/{$encoded}";
}


/**
 * InterWikiからリンクを作る。
 * 
 * @param	string	$interwikiname
 * @param	string	$str	リンク先に渡す文字列。
 * @param	string	$alias	aタグで囲まれる文字列。
 * @return	string	aタグ
 */
function makeinterwikilink($interwikiname, $str, $alias = '')
{
	$alias = htmlspecialchars($alias == '' ? "$interwikiname:$str" : $alias);
	
	$page = Page::getinstance('InterWikiName');
	$source = explode("\n", $page->getsource());
	foreach($source as $s){
		if(mb_ereg("^-\[$interwikiname\s+(.+?)(?:\s+([^\s\n]+))?\]", $s, $m)){
			if($m[2] != ''){
				$str = mb_convert_encoding($str, $m[2], 'UTF-8');
			}
			$encoded = rawurlencode($str);
			$url = htmlspecialchars(mb_ereg_replace('\$1', $encoded, $m[1]));
			if($url{0} == '?'){
				$url = SCRIPTPATH . $url;
			}
			return "<a href=\"$url\">$alias</a>";
		}
	}
	
	$ret = '<span class="nointerwikiname" title="InterWikiNameが定義されていません">';
	$ret .= $alias;
	$ret .= '</span>';
	return $ret;
}


/**
 * 相対パスを解決する。
 * 
 * @param	string	$pagename	相対パス。
 * @param	string	$basepath	解決の基準となるパス。
 * @return	string	フルパス。
 */
function resolvepath($pagename, $basepath = '')
{
	if(mb_ereg('^\.\.?/', $pagename)){
		$path = $basepath . '/./' . $pagename;
	}
	else{
		$path = $pagename;
	}
	
	$path = mb_split('/', $path);
	$ret = array();
	foreach($path as $p){
		if($p == '' || $p == '.'){
			continue;
		}
		if($p == '..'){
			array_pop($ret);
		}
		else{
			array_push($ret, $p);
		}
	}
	return join('/', $ret);
}


/**
 * ページ名中のディレクトリ名の部分を返す。
 * 
 * @param	string	$pagename	ページ名
 * @return	string	ディレクトリ部分がない場合は空文字列。
 */
function getdirname($pagename)
{
	$path = explode('/', resolvepath($pagename));
	array_pop($path);
	return join('/', $path);
}


/**
 * mb_ereg()の正規表現文字をクオートする。
 * 
 * @param	string	$str	クオートしたい文字列。
 * @return	string	クオートされた文字列。
 */
function mb_ereg_quote($str)
{
	return mb_ereg_replace('([.\\+*?\[^\]\$(){}=!<>|:])', '\\\1', $str);
}


/**
 * 入れ子配列対応array_map()。
 * 
 * @param	array	$var	処理対象の配列。
 * @param	string	$func	配列要素を受け取る関数の名前。
 * @return	array	$funcにかけられた配列。
 */
function map($var, $func)
 {
	if(is_array($var)){
		$ret = array();
		foreach($var as $k => $v){
			$ret[$k] = map($v, $func);
		}
		return $ret;
	}
	else{
		return $func($var);
	}
}


/**
 * ミリ秒単位で現在の時間を得る。
 * 
 * @return	double	現在の時間
 */
function mtime()
{
	$t=gettimeofday();
	return (double)($t['sec'].'.'.sprintf("%06d", $t['usec']));
}


/**
 * 前後の空白行を切り取る。
 * 
 * @param	string	$str
 * @return	string
 */
function linetrim($str)
{
	$text = explode("\n", $str);
	while($text != array() && trim($text[0]) == ''){
		array_shift($text);
	}
	while($text != array() && trim($text[count($text)-1]) == ''){
		array_pop($text);
	}
	return join("\n", $text);
}


/**
 * メールが有効になっている場合に管理者にメールを送る。
 * 
 * @param	string	$subject	メールの題名。UTF-8。
 * @param	string	$text	メール本文。UTF-8。
 */
function sendmail($subject, $text)
{
	if(!MAIL_USE){
		return;
	}
	
	ini_set('SMTP', MAIL_SMTP);
	ini_set('smtp_port', MAIL_SMTP_PORT);
	
	$header[] = 'From: ' . MAIL_FROM;
	$header[] = 'X-Mailer: PHP ' . phpversion() . ' / KinoWiki ' . KINOWIKI_VERSION;
	$info[] = 'WikiID: ' . WIKIID;
	$info[] = date('Y-m-d H:i:s', TIME);
	$info[] = 'REMOTE_HOST: ' . gethostbyaddr($_SERVER['REMOTE_ADDR']);
	$info[] = 'REMOTE_ADDR: ' . $_SERVER['REMOTE_ADDR'];
	$info[] = '----------------------------------------------------------------------';
	$info[] = $text;
	mb_language('uni');
	mb_send_mail(MAIL_TO, $subject, join("\r\n", $info), join("\r\n", $header));
}


/**
 * 隠しページかどうかを調べる。
 * 
 * @param	string	$pagename	 ページ名
 * @return	bool	隠しページの場合はtrue
 */
function ishiddenpage($pagename)
{
	return mb_substr($pagename, 0, 1) == ':' || mb_strpos($pagename, '/:') !== false;
}


/**
 * 隠しページをリストから削除する。
 * 
 * @param	array(string)	$list	ページ名の配列
 * @return	array(string)	隠しページを取り除いた配列
 */
function removehiddenpage($list)
{
	foreach($list as $p){
		if(!ishiddenpage($p)){
			$ret[] = $p;
		}
	}
	return $ret;
}


/**
 * 第２引数に文字列長以上の値を与えると空文字列を返すsubstr()。
 * 
 * substr()は第２引数に文字列長以上の値を与えるとfalseを返してくる。
 */
function _substr($string, $start, $length = null)
{
	if($start >= strlen($string)){
		return '';
	}
	
	if($length === null){
		return substr($string, $start);
	}
	else{
		return substr($string, $start, $length);
	}
}


/**
 * mailアドレスにシンプルな保護を施す（aタグ用）
 */
function protectmail_url($address)
{
	$encoded = chunk_split(bin2hex($address), 2, '%'); 
	return '%' . _substr($encoded, 0, strlen($encoded) - 1); 
}


/**
 * mailアドレスにシンプルな保護を施す（html地の文用）
 */
function protectmail_html($address)
{
	$encoded = chunk_split(bin2hex($address), 2, ';&#x'); 
	return '&#x' . _substr($encoded, 0, strlen($encoded) - 3); 
}


/**
 * 配列にキーがそれぞれ存在する事を確認する。
 * 
 * @param	array	$array	配列
 * @param	array(mixed)	$keys	確認するキー（複数）
 * @return	bool	全てのキーが存在すればtrue
 */
function keys_exists($array, $keys)
{
	foreach($keys as $k){
		if(!isset($array[$k])){
			return false;
		}
	}
	return true;
}

/**
 * 配列要素にヒットする正規表現を生成する。
 *
 * オートリンクとあいまいリンクの正規表現生成などに使用する。
 * 
 * @param	array(string)	&$pagelist	この関数の実行後、$pagelistの中身は保証されない。
 * @return	string	正規表現
 */
function makelinkexp(&$pagelist)
{
	if(count($pagelist) <= 1){
		return count($pagelist) == 0 ? '' : mb_ereg_quote($pagelist[0]);
	}
	
	$emptyflag = false;
	$bin = array();
	while($pagelist != array()){
		$pagename = array_pop($pagelist);
		if($pagename != ''){
			$bin[mb_substr($pagename, 0, 1)][] = mb_substr($pagename, 1);
		}
		else{
			$emptyflag = true;
		}
	}
	
	$key = array_keys($bin);
	foreach($key as $k){
		$ret[] = mb_ereg_quote($k) . makelinkexp($bin[$k]);
	}
	
	if(count($ret) == 1){
		return $emptyflag ? '(?:'.$ret[0].')?' : $ret[0];
	}
	else{
		return '(?:' . join('|', $ret) . ')' . ($emptyflag ? '?' : '');
	}
}


?>