]> 4ch.mooo.com Git - test.git/blob - lib/lib_common.php
ebf918b850688a497dfc52eac3362730bc2be02e
[test.git] / lib / lib_common.php
1 <?php
2 /**
3  * Pixmicat! Common Library
4  *
5  * 存放常用函式供主程式引入
6  *
7  * @package PMCLibrary
8  * @version $Id: lib_common.php 690 2009-04-06 01:30:13Z scribe $
9  * @date $Date: 2009-04-06 09:30:13 +0800 (星期一, 06 四月 2009) $
10  */
11
12 /**
13  * 更改 PHP 捕捉錯誤功能並顯示自訂錯誤
14  */
15 function PMCCore_errorHandler($errno, $errstr, $errfile, $errline){
16         header('Content-type: text/plain;charset=utf-8');
17         switch($errno){
18                 case E_USER_ERROR:
19                 case E_ERROR:
20                         exit('Error: '.$errstr);
21                         break;
22                 case E_WARNING:
23                         exit('Warning: '.$errstr.' '.$errfile.' #'.$errline);
24                         break;
25         }
26 }
27
28 /* css data */
29 $style_load = false;
30 $style_bar = '';
31
32 if(file_exists(DATA_DIR.CSV_SS)){ // Load FuuR4 CSS data
33         $style_data = DATA_DIR.CSV_SS;
34         $style_bar .= '[';
35         $lines = explode("\n", file_get_contents($style_data));
36         foreach($lines as $null => $line){
37                 if($line != ''){
38                         list($style_name, $style_short, $style_path, $style_rel) = explode (',', $line);
39                         $style_path = CSS_DIR.$style_path;
40                         if(file_exists($style_path)){
41                                 if($style_rel) $style_rel = 'stylesheet';
42                                 else $style_rel = 'alternate stylesheet';
43                                 $style_load .= '<link rel="'.$style_rel.'" type="text/css" href="'.$style_path.'" title="'.$style_name.'" />'."\n";
44                                 $style_js = format_js_var($style_name);
45                                 $style_title = str_replace('[what]', $style_name, _T('style_tooltip'));
46                                 $style_bar .= ' <a href="javascript:set_stylesheet('.$style_js.')" title="'.$style_title.'">'.$style_short.'</a> |';
47                         }
48                 }
49         }
50
51         $style_bar .= ']';
52
53         // Tidy up trailing seperators and empty brackets
54         $style_bar = str_replace('|]', ']', $style_bar);
55         $style_bar = str_replace('[] ', '', $style_bar);
56         $style_bar = str_replace('[]', '', $style_bar);
57 }
58
59 // Load stylesheet setting from config.php
60 // if either of the datafiles are missing or empty
61 if(str_replace('[]', '', $style_bar) == '') $style_load .= '<link rel="stylesheet" type="text/css" href="'.CSS_DIR.CSSFILE.'" />';
62
63 // style sheet js loader
64 $jsdonk = 'var style_cookie = "';
65 $jsdonk .= ($style_bar) ? 'yo_style' : '';
66 $jsdonk .= '";';
67 // http://www.magmagateau.com/fuukaba/ is the source of the code above w
68 ////++++
69
70 /* 輸出表頭 */
71 /* ヘッダ */
72 function head(&$dat,$resno=0){
73         global $PTE, $PMS, $language, $style_load, $style_bar, $jsdonk;
74         $title = '';
75
76         // Title code from old yotsubanome //++++---- suigintou v.3.0+ code
77         if(SHOWTITLEIMG == 1){
78                 $title = '<img src="'.TITLEIMG.'" alt="'.TITLE.'" />';
79                 if(SHOWTITLETXT) $title .= '<br />';
80         }elseif(SHOWTITLEIMG == 2){
81                 $title = '<img src="'.TITLEIMG.'" onclick="this.src=this.src;" alt="'.TITLE.'" />';
82                 if(SHOWTITLETXT) $title .= '<br />';
83         }
84         if(SHOWTITLETXT) $title .= TITLE;
85 //++++----
86         $pte_vals = array('{$TITLE}'=>TITLE,'{$TITLEHEAD}'=>$title,'{$TITLECOM}'=>strip_tags(TITLECOM),'{$RESTO}'=>$resno?$resno:'','{$STYLE_LOAD}'=>$style_load,'{$STYLE_BAR}'=>$style_bar,'{$JS_DIR}'=>JS_DIR,'{$CSS_DIR}'=>CSS_DIR,'{$JSDONK}'=>$jsdonk,'{$FAV_ICON}'=>FAV_ICON,'{$LANGUAGE}'=>PIXMICAT_LANGUAGE);
87         $dat .= $PTE->ParseBlock('HEADER',$pte_vals);
88         $PMS->useModuleMethods('Head', array(&$dat,$resno)); // "Head" Hook Point
89         $pte_vals+=array('{$ALLOW_UPLOAD_EXT}' => ALLOW_UPLOAD_EXT,
90                 '{$JS_REGIST_WITHOUTCOMMENT}' => str_replace('\'', '\\\'', _T('regist_withoutcomment')),
91                 '{$JS_REGIST_UPLOAD_NOTSUPPORT}' => str_replace('\'', '\\\'', _T('regist_upload_notsupport')),
92                 '{$JS_CONVERT_SAKURA}' => str_replace('\'', '\\\'', _T('js_convert_sakura')));
93         $dat .= $PTE->ParseBlock('JSHEADER',$pte_vals);
94         $dat .= '</head>';
95         $pte_vals += array('{$TOP_LINKS}' => TOP_LINKS,
96                 '{$HOME}' => '[<a href="'.HOME.'" rel="_top">'._T('head_home').'</a>]',
97                 '{$STATUS}' => '[<a href="'.PHP_SELF.'?mode=status">'._T('head_info').'</a>]',
98                 '{$ADMIN}' => '[<a href="'.PHP_SELF.'?mode=admin">'._T('head_admin').'</a>]',
99                 '{$REFRESH}' => '[<a href="'.PHP_SELF2.'?">'._T('head_refresh').'</a>]',
100                 '{$SEARCH}' => (USE_SEARCH) ? '[<a href="'.PHP_SELF.'?mode=search">'._T('head_search').'</a>]' : '');
101         if(STYLEBAR) $pte_vals += array('{$HOOKLINKS}' => '<small>'.$style_bar.'</small>');
102         else $pte_vals += array('{$HOOKLINKS}' => '');
103         $PMS->useModuleMethods('Toplink', array(&$pte_vals['{$HOOKLINKS}'],$resno)); // "Toplink" Hook Point
104         $dat .= $PTE->ParseBlock('BODYHEAD',$pte_vals);
105 }
106
107 /* 發表用表單輸出 */
108 /* 投稿フォーム */
109 function form(&$dat, $resno, $iscollapse=true, $retURL=PHP_SELF, $name='', $mail='', $sub='', $com='', $cat='', $mode='regist'){
110         global $PTE, $PMS, $ADDITION_INFO, $language;
111         $pte_vals = array('{$SELF}'=>$retURL, '{$FORMTOP}'=>'', '{$MODE}'=>$mode);
112         $isedit = ($mode == 'edit'); // 是否為編輯模式
113         if($resno && !$isedit){
114                 $links = '[<a href="'.PHP_SELF2.'?'.time().'">'._T('return').'</a>]';
115                 $PMS->useModuleMethods('LinksAboveBar', array(&$links,'reply',$resno)); // "LinksAboveBar" Hook Point
116                 $pte_vals['{$FORMTOP}'] = '<div id="bannerlink">'.$links.'</div><div id="banner"><div class="theader">'._T('form_top').'</div></div>';
117         } //----class="bar_reply"
118         if(!$resno) $ntno = _T('form_newt');
119         else $ntno = _T('reply_btn').':'.$resno; // new thread & reply:$resno
120         if(USE_FLOATFORM && !$resno && $iscollapse) $pte_vals['{$FORMTOP}'] .= "\n".'[<span id="show" class="hide" onmouseover="showform();" onclick="showform();">'._T('form_showpostform').'</span><span id="hide" class="show" onmouseover="hideform();" onclick="hideform();">'._T('form_hidepostform').'</span>]';
121         $pte_vals += array('{$MAX_FILE_SIZE}' => MAX_KB * 1024,
122                 '{$RESTO}' => $resno ? '<input type="hidden" name="resto" value="'.$resno.'" />' : '',
123                 '{$FORM_NAME_TEXT}' => _T('form_name'),
124                 '{$FORM_NAME_FIELD}' => '<input class="hide" type="text" name="name" value="spammer" /><input class="inputtext" type="text" name="'.FT_NAME.'" id="fname" size="28" value="'.$name.'" />',
125                 '{$FORM_EMAIL_TEXT}' => _T('form_email'),
126                 '{$FORM_EMAIL_FIELD}' => '<input class="inputtext" type="text" name="'.FT_EMAIL.'" id="femail" size="28" value="'.$mail.'" /><input type="text" class="hide" name="email" value="foo@foo.bar" />',
127                 '{$FORM_TOPIC_TEXT}' => _T('form_topic'),
128                 '{$FORM_TOPIC_FIELD}' => '<input class="hide" value="DO NOT FIX THIS" type="text" name="sub" /><input class="inputtext" type="text" name="'.FT_SUBJECT.'" id="fsub" size="35" value="'.$sub.'" />',
129                 '{$FORM_SUBMIT}' => ' <input type="submit" name="sendbtn" value="'._T('form_submit_btn').'" /><small>「'.$ntno.'」</small>',
130                 '{$FORM_COMMENT_TEXT}' => _T('form_comment'),
131                 '{$FORM_COMMENT_FIELD}' => '<textarea class="inputtext" name="'.FT_COMMENT.'" id="fcom" cols="48" rows="4">'.$com.'</textarea><textarea name="com" class="hide" cols="48" rows="4">EID OG SMAPS</textarea>',
132                 '{$FORM_DELETE_PASSWORD_FIELD}' => '<input class="inputtext" type="password" name="pwd" size="8" maxlength="8" value="" />',
133                 '{$FORM_DELETE_PASSWORD_TEXT}' => _T('form_delete_password'),
134                 '{$FORM_DELETE_PASSWORD_NOTICE}' => _T('form_delete_password_notice'),
135                 '{$FORM_EXTRA_COLUMN}' => '',
136                 '{$FORM_NOTICE}' => _T('form_notice',str_replace('|',', ',ALLOW_UPLOAD_EXT),MAX_KB,($resno ? MAX_RW : MAX_W),($resno ? MAX_RH : MAX_H)),
137                 '{$HOOKPOSTINFO}' => '',
138                 '{$ADDITION_INFO}' => $ADDITION_INFO,
139                 '{$FORM_NOTICE_NOSCRIPT}' => _T('form_notice_noscript'));
140         $PMS->useModuleMethods('PostForm', array(&$pte_vals['{$FORM_EXTRA_COLUMN}'])); // "PostForm" Hook Point
141         if(!$isedit && (RESIMG || !$resno)){
142                 $pte_vals += array('{$FORM_ATTECHMENT_TEXT}' => _T('form_attechment'),
143                         '{$FORM_ATTECHMENT_FIELD}' => '<input class="inputtext" type="file" name="upfile" id="fupfile" size="35" /><input class="hide" type="checkbox" name="reply" value="yes" />');
144                         if(!NO_TEXTONLY || NO_TEXTONLY == 1){
145                                 $pte_vals += array('{$FORM_NOATTECHMENT_TEXT}' => _T('form_noattechment'),
146                                 '{$FORM_NOATTECHMENT_FIELD}' => '<input type="checkbox" name="noimg" id="noimg" value="on" />');
147                         }
148         }
149         if(OPTION || USE_UPSERIES){ //++++ more options will be added on later
150                 $pte_vals['{$FORM_OPTION1_TEXT}'] = _T('form_option1');
151         }
152         if(USE_UPSERIES){ // 啟動連貼機能
153                 $pte_vals['{$FORM_CONTPOST_FIELD}'] = '<input type="checkbox" name="up_series" id="up_series" value="on"'.((isset($_GET["upseries"]) && $resno)?' checked="checked"':'').' />';
154                 $pte_vals['{$FORM_CONTPOST_TEXT}'] = _T('form_contpost');
155         }
156         if(OPTION){ //options hook
157                 $pte_vals['{$FORM_OPTION_HOOK}'] = '';
158         }
159         if(USE_CATEGORY){
160                 $pte_vals += array('{$FORM_CATEGORY_FIELD}' => '<input class="inputtext" type="text" name="category" size="28" value="'.$cat.'" />',
161                         '{$FORM_CATEGORY_TEXT}' => _T('form_category'),
162                         '{$FORM_CATEGORY_NOTICE}' => _T('form_category_notice'));
163         }
164         if(STORAGE_LIMIT) $pte_vals['{$FORM_NOTICE_STORAGE_LIMIT}'] = _T('form_notice_storage_limit',total_size(),STORAGE_MAX);
165         $PMS->useModuleMethods('PostInfo', array(&$pte_vals['{$HOOKPOSTINFO}'])); // "PostInfo" Hook Point
166
167         if(USE_FLOATFORM && !$resno && $iscollapse) $pte_vals['{$FORMBOTTOM}'] = '<script type="text/javascript">hideform();</script>';
168         $dat .= $PTE->ParseBlock('POSTFORM',$pte_vals);
169 }
170
171 /* 輸出頁尾文字 */
172 /* フッタ */
173 function foot(&$dat){
174         global $PTE, $PMS, $language;
175         $pte_vals = array('{$FOOTER}'=>'<!-- GazouBBS v3.0 --><!-- ふたば改0.8 --><!-- Pixmicat! --><!-- 四葉の芽改0.8 -->'."\n");
176         $PMS->useModuleMethods('Foot', array(&$pte_vals['{$FOOTER}'])); // "Foot" Hook Point
177         $pte_vals['{$FOOTER}'] .= '<small>- <a href="http://php.s3.to" rel="_top">GazouBBS</a> + <a href="http://www.2chan.net/" rel="_top">futaba</a> + <a href="http://pixmicat.openfoundry.org/" rel="_blank">Pixmicat!</a> + <a href="http://4ch.irc.su/" rel="_blank">yotsubanome</a> -</small>';
178         $pte_vals['{$FOOTER}'] .= '<p>
179         <a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml11" alt="Valid XHTML 1.1" height="31" width="88" /></a>
180         </p>';
181         $dat .= $PTE->ParseBlock('FOOTER',$pte_vals);
182 }
183
184 /* 網址自動連結 */
185 /* オートリンク */
186 function auto_link_callback($matches){
187         return (strtolower($matches[3]) == "</a>") ? $matches[0] : preg_replace('/(https?|ftp|news|gopher)(:\/\/[\w\+\$\;\?\.\{\}%,!#~*\/:@&=_-]+)/u', '<a href="$1$2" rel="_blank">$1$2</a>', $matches[0]);
188 }
189 function auto_link($proto){
190         $proto = preg_replace('|<br\s*/?>|',"\n",$proto);
191         $proto = preg_replace_callback('/(>|^)([^<]+?)(<.*?>|$)/m','auto_link_callback',$proto);
192         return str_replace("\n",'<br />',$proto);
193 }
194
195 /* 引用標註 */
196 function quoteLight($comment){
197         return preg_replace('/(^|<br \/>)((?:&gt;|>).*?)(?=<br \/>|$)/u', '$1<span class="unkfunc">$2</span>', $comment);
198 }
199 /* # quoting */
200 function quoteLight2($comment){
201         return preg_replace('/(^|<br \/>)((?:#|#).*?)(?=<br \/>|$)/u', '$1<span class="unkfunc2">$2</span>', $comment);
202 }
203
204 /* 取得完整的網址 */
205 function fullURL(){
206         return 'http://'.$_SERVER['HTTP_HOST'].substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], PHP_SELF));
207 }
208
209 /* 反櫻花字 */
210 function anti_sakura($str){
211         return preg_match('/[\x{E000}-\x{F848}]/u', $str);
212 }
213
214 /* 輸出錯誤畫面 */
215 /* エラー画面 */
216 function error($mes, $dest=''){
217         global $PTE;
218         if(is_file($dest)) unlink($dest);
219         $pte_vals = array('{$SELF2}'=>PHP_SELF2.'?'.time(), '{$MESG}'=>$mes, '{$RETURN_TEXT}'=>_T('return'), '{$BACK_TEXT}'=>_T('error_back'));
220         $dat = '';
221         head($dat);
222         $dat .= $PTE->ParseBlock('ERROR',$pte_vals);
223         foot($dat);
224         exit($dat);
225 }
226
227 /* 文字修整 */
228 /* テキスト整形 */
229 function CleanStr($str, $IsAdmin=false){
230         $str = trim($str); // 去除前後多餘空白
231         if(get_magic_quotes_gpc()) $str = stripslashes($str); // "\"斜線符號去除
232         // XML 1.1 Second Edition: 部分避免用字 (http://www.w3.org/TR/2006/REC-xml11-20060816/#charsets)
233         $str = preg_replace('/([\x1-\x8\xB-\xC\xE-\x1F\x7F-\x84\x86-\x9F\x{FDD0}-\x{FDDF}])/u', '', htmlspecialchars($str));
234
235         if($IsAdmin && CAP_ISHTML){ // 管理員開啟HTML
236                 $str = preg_replace('/&lt;(.*?)&gt;/', '<$1>', $str); // 如果有&lt;...&gt;則轉回<...>成為正常標籤
237         }
238         return $str;
239 }
240
241 /* 適用UTF-8環境的擬substr,取出特定數目字元
242 原出處:Sea Otter @ 2005.05.10
243 http://www.meyu.net/star/viewthread.php?tid=267&fpage=10 */
244 function str_cut($str, $maxlen=20){
245     $i = $l = 0; $len = strlen($str); $f = true; $return_str = $str;
246         while($i < $len){
247                 $chars = ord($str{$i});
248                 if($chars < 0x80){ $l++; $i++; }
249                 elseif($chars < 0xe0){ $l++; $i += 2; }
250                 elseif($chars < 0xf0){ $l += 2; $i += 3; }
251                 elseif($chars < 0xf8){ $l++; $i += 4; }
252         elseif($chars < 0xfc){ $l++; $i += 5; }
253                 elseif($chars < 0xfe){ $l++; $i += 6; }
254                 if(($l >= $maxlen) && $f){
255                         $return_str = substr($str, 0, $i);
256                         $f = false;
257                 }
258                 if(($l > $maxlen) && ($i <= $len)){
259                         $return_str = $return_str.'…';
260                         break;
261                 }
262     }
263         return $return_str;
264 }
265
266 /* 檢查瀏覽器和伺服器是否支援gzip壓縮方式 */
267 function CheckSupportGZip(){
268         $HTTP_ACCEPT_ENCODING = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : '';
269         if(headers_sent() || connection_aborted()) return 0; // 已送出資料,取消
270         if(!(function_exists('gzencode') && function_exists('ob_start') && function_exists('ob_get_clean'))) return 0; // 伺服器相關的套件或函式無法使用,取消
271         if(strpos($HTTP_ACCEPT_ENCODING, 'gzip')!==false) return 'gzip';
272         return 0;
273 }
274
275 /* 封鎖 IP / Hostname / DNSBL 綜合性檢查 */
276 function BanIPHostDNSBLCheck($IP, $HOST, &$baninfo){
277         if(!BAN_CHECK) return false; // Disabled
278         global $BANPATTERN, $DNSBLservers, $DNSBLWHlist;
279
280         // IP/Hostname Check
281         $HOST = strtolower($HOST);
282         $checkTwice = ($IP != $HOST); // 是否需檢查第二次
283         $IsBanned = false;
284         foreach($BANPATTERN as $pattern){
285                 $slash = substr_count($pattern, '/');
286                 if($slash==2){ // RegExp
287                         $pattern .= 'i';
288                 }elseif($slash==1){ // CIDR Notation
289                         if(matchCIDR($IP, $pattern)){ $IsBanned = true; break; }
290                         continue;
291                 }elseif(strpos($pattern, '*')!==false || strpos($pattern, '?')!==false){ // Wildcard
292                         $pattern = '/^'.str_replace(array('.', '*', '?'), array('\.', '.*', '.?'), $pattern).'$/i';
293                 }else{ // Full-text
294                         if($IP==$pattern || ($checkTwice && $HOST==strtolower($pattern))){ $IsBanned = true; break; }
295                         continue;
296                 }
297                 if(preg_match($pattern, $HOST) || ($checkTwice && preg_match($pattern, $IP))){ $IsBanned = true; break; }
298         }
299         if($IsBanned){ $baninfo = _T('ip_banned'); return true; }
300
301         // DNS-based Blackhole List(DNSBL) 黑名單
302         if(!$DNSBLservers[0]) return false; // Skip check
303         if(array_search($IP, $DNSBLWHlist)!==false) return false; // IP位置在白名單內
304         $rev = implode('.', array_reverse(explode('.', $IP)));
305         $lastPoint = count($DNSBLservers) - 1; if($DNSBLservers[0] < $lastPoint) $lastPoint = $DNSBLservers[0];
306         $isListed = false;
307         for($i = 1; $i <= $lastPoint; $i++){
308                 $query = $rev.'.'.$DNSBLservers[$i].'.'; // FQDN
309                 $result = gethostbyname($query);
310                 if($result && ($result != $query)){ $isListed = $DNSBLservers[$i]; break; }
311         }
312         if($isListed){ $baninfo = _T('ip_dnsbl_banned',$isListed); return true; }
313         return false;
314 }
315 function matchCIDR($addr, $cidr) {
316         list($ip, $mask) = explode('/', $cidr);
317         return (ip2long($addr) >> (32 - $mask) == ip2long($ip.str_repeat('.0', 3 - substr_count($ip, '.'))) >> (32 - $mask));
318 }
319
320 /* 後端登入權限管理 */
321 function adminAuthenticate($mode){
322         @session_start();
323         $loginkey = md5($_SERVER['HTTP_USER_AGENT'].ADMIN_PASS.$_SERVER['REMOTE_ADDR']);
324         switch($mode){
325                 case 'logout':
326                         if(isset($_SESSION['pmcLogin'])) unset($_SESSION['pmcLogin']);
327                         return true; break;
328                 case 'login':
329                         $_SESSION['pmcLogin'] = $loginkey;
330                         break;
331                 case 'check':
332                         if(isset($_SESSION['pmcLogin']) && $_SESSION['pmcLogin']==$loginkey){
333                                 session_regenerate_id(true); // 更換 Session id key 避免 Hijacking
334                                 return true;
335                         }
336                         return false;
337                         break;
338         }
339 }
340
341 /* 取得 (Transparent) Proxy 提供之 IP 參數 */
342 function getREMOTE_ADDR(){
343         // 同時有 VIA 和 FORWARDED_FOR 較可能為 Proxy
344         if(isset($_SERVER['HTTP_VIA']) && isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
345                 $tmp = preg_split('/[ ,]+/', $_SERVER['HTTP_X_FORWARDED_FOR']);
346                 // 防止 Squid "unknown" 問題,此種情況直接使用 REMOTE_ADDR
347                 return ($tmp[0] != 'unknown' ? $tmp[0] : $_SERVER['REMOTE_ADDR']);
348         }
349         return $_SERVER['REMOTE_ADDR'];
350 }
351
352 /* css style sheet */
353 function format_js_var($var_name){
354         return("'".$var_name."'");
355 }
356
357 # Identifies APNGs
358 # Written by Coda, functionified by Foone/Popcorn Mariachi#!9i78bPeIxI
359 # This code is in the public domain
360 # identify_apng returns:
361 # true if the file is an APNG
362 # false if it is any other sort of file (it is not checked for PNG validity)
363 # takes on argument, a filename.
364 /* APNG */
365 function identify_apng($filename){
366         global $apng;
367         $img_bytes = file_get_contents($filename);
368         if($img_bytes){
369                 if(strpos(substr($img_bytes, 0, strpos($img_bytes, 'IDAT')), 'acTL') !== false){
370                         return $apng = TRUE; // APNG
371                 }
372         }
373         return $apng = FALSE; // not APNG
374 }
375
376 /* total amount of ommited posts/images */ // suigintou v.3.0+ code
377 function _res($posts, $images = 0){
378         $omitp = ($posts) ? sp(_T('notice_omitted_posts'), $posts) : false;
379         $omiti = ($images) ? sp(_T('notice_omitted_images'), $images) : false;
380         return($omitp.$omiti);
381 }
382 /* plural strings for ommited post messages */
383 function sp($string, $value, $shownull = false){
384         // Determine whether to show string for zero values
385         if(!$value && $shownull || $value) $show = true;
386         elseif(!$value && !$shownull) $show = false;
387         else $show = $shownull;
388
389         if($show) $string = str_replace('[#]', number_format($value), $string);
390         else return(false);
391
392         // Separate words from plural forms
393         ereg('(.*)(\[)(.*)(\])(.*)', $string, $regs);
394         $plu_word_s = $regs[1];
395         $plu_bits = $regs[3];
396         $plu_word_e = $regs[5];
397
398         if(strstr($plu_bits, '|')){
399                 // To the left of the | is the single form; to the right, the plural form
400                 ereg('(.*)(\|)(.*)', $plu_bits, $regs);
401                 $single = $regs[1];
402                 $plural = $regs[3];
403         }else{
404                 // [|s] will work for words that should end in "s", but this bit handles [s]
405                 $single = '';
406                 $plural = $plu_bits;
407         }
408
409         // Spit it out, Smith
410         $plu_bit_d = ($value == -1 || $value == 1)?$single:$plural;
411         $plu_word = $plu_word_s.$plu_bit_d.$plu_word_e;
412
413         if($plu_word) return($plu_word); else return($string);
414 }
415 //++++----
416 //++++----
417 /* comment too long function */
418 // truncate $str to $max_lines lines and return $str and $abbr
419 // where $abbr = whether or not $str was actually truncated
420 function abbreviate($str, $max_lines){
421         if(!defined('MAX_LINES')){
422                 if(defined('BR_CHECK')) define('MAX_LINES', BR_CHECK);
423                 else define('MAX_LINES', 24);
424
425                 $max_lines = MAX_LINES;
426         }
427         $lines = explode("<br />", $str);
428         if(count($lines) > $max_lines){
429                 $abbr = 1;
430                 $lines = array_slice($lines, 0, $max_lines);
431                 $str = implode("<br />", $lines);
432         }else $abbr = 0;
433         //close spans after abbreviating
434         //XXX will not work with more html - use abbreviate_html from shiichan
435         $str .= str_repeat("</span>", substr_count($str, "<span") - substr_count($str, "</span"));
436         return array($str, $abbr);
437 }
438 ?>