]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/stdlib.php
More infiltration of new object-based HTML generation.
[SourceForge/phpwiki.git] / lib / stdlib.php
1 <?php rcs_id('$Id: stdlib.php,v 1.85 2002-01-21 06:55:47 dairiki Exp $');
2
3 /*
4   Standard functions for Wiki functionality
5     WikiURL($pagename, $args, $get_abs_url)
6     StartTag($tag, $args)
7     Element($tag, $args, $content)
8     QElement($tag, $args, $content)
9     IconForLink($protocol_or_url)
10     LinkURL($url, $linktext)
11     LinkWikiWord($wikiword, $linktext)
12     LinkExistingWikiWord($wikiword, $linktext)
13     LinkImage($url, $alt)
14     class Stack { push($item), pop(), cnt(), top() }
15     CookSpaces($pagearray)
16     MakeWikiForm ($pagename, $args, $class, $button_text)
17     SplitQueryArgs ($query_args)
18     LinkPhpwikiURL($url, $text)
19     ParseAndLink($bracketlink)
20     ExtractWikiPageLinks($content)
21     LinkRelatedPages($dbi, $pagename)
22     split_pagename ($page)
23     NoSuchRevision ($page, $version)
24     TimezoneOffset ($time, $no_colon)
25     Iso8601DateTime ($time)
26     Rfc2822DateTime ($time)
27     CTime ($time)
28     __printf ($fmt)
29     __sprintf ($fmt)
30     __vsprintf ($fmt, $args)
31
32   function moved => LinkInterWikiLink($link, $linktext)
33     (see /lib/interwiki.php)
34   function moved => LinkUnknownWikiWord($wikiword, $linktext)
35     (see /lib/Theme.php)
36   function gone  => UpdateRecentChanges($dbi, $pagename, $isnewpage) 
37     (see /lib/plugin/RecentChanges.php)
38
39 */
40
41
42 function WikiURL($pagename, $args = '', $get_abs_url = false) {
43     if (is_array($args)) {
44         $enc_args = array();
45         foreach  ($args as $key => $val) {
46             $enc_args[] = urlencode($key) . '=' . urlencode($val);
47         }
48         $args = join('&', $enc_args);
49     }
50     
51     if (USE_PATH_INFO) {
52         $url = $get_abs_url ? SERVER_URL . VIRTUAL_PATH . "/" : "";
53         $url .= rawurlencode($pagename);
54         if ($args)
55             $url .= "?$args";
56     }
57     else {
58         $url = $get_abs_url ? SERVER_URL . SCRIPT_NAME : basename(SCRIPT_NAME);
59         $url .= "?pagename=" . rawurlencode($pagename);
60         if ($args)
61             $url .= "&$args";
62     }
63     return $url;
64 }
65
66 define('NO_END_TAG_PAT',
67        '/^' . join('|', array('area', 'base', 'basefont',
68                               'br', 'col', 'frame',
69                               'hr', 'img', 'input',
70                               'isindex', 'link', 'meta',
71                               'param')) . '$/i');
72
73 function StartTag($tag, $args = '') {
74     $s = "<$tag";
75     if (is_array($args)) {
76         while (list($key, $val) = each($args))
77             {
78                 if (is_string($val) || is_numeric($val))
79                     $s .= sprintf(' %s="%s"', $key, htmlspecialchars($val));
80                 else if ($val)
81                     $s .= " $key=\"$key\"";
82             }
83     }
84     return "$s>";
85 }
86
87
88 function Element($tag, $args = '', $content = '') {
89     $html = "<$tag";
90     if (!is_array($args)) {
91         $content = $args;
92         $args = false;
93     }
94     $html = StartTag($tag, $args);
95     if (preg_match(NO_END_TAG_PAT, $tag)) {
96         assert(! $content);
97         return preg_replace('/>$/', " />", $html);
98     } else {
99         $html .= $content;
100         $html .= "</$tag>";//FIXME: newline might not always be desired.
101     }
102     return $html;
103 }
104
105 function QElement($tag, $args = '', $content = '')
106 {
107     if (is_array($args))
108         return Element($tag, $args, htmlspecialchars($content));
109     else {
110         $content = $args;
111         return Element($tag, htmlspecialchars($content));
112     }
113 }
114
115 function IconForLink($protocol_or_url) {
116     global $Theme;
117
118     list ($proto) = explode(':', $protocol_or_url, 2);
119     $src = $Theme->getLinkIconURL($proto);
120     if (empty($src))
121         return '';
122     
123     return Element('img', array('src'   => $src,
124                                 'alt'   => $proto,
125                                 'class' => 'linkicon'));
126 }
127
128
129 function LinkURL($url, $linktext = '') {
130     // FIXME: Is this needed (or sufficient?)
131     if(ereg("[<>\"]", $url)) {
132         return Element('strong',
133                        QElement('u', array('class' => 'baduri'),
134                                 _("BAD URL -- remove all of <, >, \"")));
135     }
136     
137     $attr['href'] = $url;
138     
139     if (empty($linktext)) {
140         $linktext = $url;
141         $attr['class'] = 'rawurl';
142     } else
143         $attr['class'] = 'namedurl';
144     
145     return Element('a', $attr,
146                    IconForLink($url) . htmlspecialchars($linktext));
147 }
148
149 function LinkWikiWord($wikiword, $linktext = '', $version = false) {
150     $link = _LinkWikiWord($wikiword, $linktext, $version);
151     return $link->asXML();
152 }
153
154 // This function will be renamed LinkWikiWord once changeover
155 // to object based HTML generation is complete.
156 function _LinkWikiWord($wikiword, $linktext = '', $version = false) {
157     global $dbi, $Theme;
158     if ($dbi->isWikiPage($wikiword))
159         $link = $Theme->linkExistingWikiWord($wikiword, $linktext, $version);
160     else
161         $link = $Theme->linkUnknownWikiWord($wikiword, $linktext);
162     return $link;
163 }
164
165 //This function will be deleted once all references to it are updated.
166 function LinkExistingWikiWord($wikiword, $linktext = '', $version = false) {
167     global $Theme;
168     $link = $Theme->linkExistingWikiWord($wikiword, $linktext, $version);
169     return $link->asXML();
170 }
171
172
173 function LinkImage($url, $alt = '[External Image]') {
174     // FIXME: Is this needed (or sufficient?)
175     //  As long as the src in htmlspecialchars()ed I think it's safe.
176     if(ereg("[<>\"]", $url)) {
177         return Element('strong',
178                        QElement('u', array('class' => 'baduri'),
179                                 _("BAD URL -- remove all of <, >, \"")));
180     }
181     return Element('img', array('src' => $url, 'alt' => $alt));
182 }
183
184 // converts spaces to tabs
185 function CookSpaces($pagearray) {
186     return preg_replace("/ {3,8}/", "\t", $pagearray);
187 }
188
189
190 class Stack {
191     var $items = array();
192     var $size = 0;
193     
194     function push($item) {
195         $this->items[$this->size] = $item;
196         $this->size++;
197         return true;
198     }  
199     
200     function pop() {
201         if ($this->size == 0) {
202             return false; // stack is empty
203         }  
204         $this->size--;
205         return $this->items[$this->size];
206     }  
207     
208     function cnt() {
209         return $this->size;
210     }  
211     
212     function top() {
213         if($this->size)
214             return $this->items[$this->size - 1];
215         else
216             return '';
217     }
218     
219 }  
220 // end class definition
221
222
223 function MakeWikiForm ($pagename, $args, $class, $button_text = '') {
224     $formargs['action'] = USE_PATH_INFO ? WikiURL($pagename) : SCRIPT_NAME;
225     $formargs['method'] = 'get';
226     $formargs['class']  = $class;
227     
228     $contents = '';
229     $input_seen = 0;
230     
231     while (list($key, $val) = each($args)) {
232         $a = array('name' => $key, 'value' => $val, 'type' => 'hidden');
233         
234         if (preg_match('/^ (\d*) \( (.*) \) ((upload)?) $/xi', $val, $m)) {
235             $input_seen++;
236             $a['type'] = 'text';
237             $a['size'] = $m[1] ? $m[1] : 30;
238             $a['value'] = $m[2];
239             if ($m[3])
240                 {
241                     $a['type'] = 'file';
242                     $formargs['enctype'] = 'multipart/form-data';
243                     $contents .= Element('input',
244                                          array('name'  => 'MAX_FILE_SIZE',
245                                                'value' =>  MAX_UPLOAD_SIZE,
246                                                'type'  => 'hidden'));
247                     $formargs['method'] = 'post';
248                 }
249         }
250         
251         $contents .= Element('input', $a);
252     }
253     
254     $row = Element('td', $contents);
255     
256     if (!empty($button_text)) {
257         $row .= Element('td',
258                         Element('input', array('type'  => 'submit',
259                                                'class' => 'button',
260                                                'value' => $button_text)));
261     }
262     
263     return Element('form', $formargs,
264                    Element('table', array('cellspacing' => 0,
265                                           'cellpadding' => 2,
266                                           'border'      => 0),
267                            Element('tr', $row)));
268 }
269
270 function SplitQueryArgs ($query_args = '') 
271 {
272     $split_args = split('&', $query_args);
273     $args = array();
274     while (list($key, $val) = each($split_args))
275         if (preg_match('/^ ([^=]+) =? (.*) /x', $val, $m))
276             $args[$m[1]] = $m[2];
277     return $args;
278 }
279
280 function LinkPhpwikiURL($url, $text = '') {
281     $args = array();
282     
283     if (!preg_match('/^ phpwiki: ([^?]*) [?]? (.*) $/x', $url, $m))
284         return Element('strong',
285                        QElement('u', array('class' => 'baduri'),
286                                 'BAD phpwiki: URL'));
287     if ($m[1])
288         $pagename = urldecode($m[1]);
289     $qargs = $m[2];
290     
291     if (empty($pagename) &&
292         preg_match('/^(diff|edit|links|info)=([^&]+)$/', $qargs, $m))
293         {
294             // Convert old style links (to not break diff links in
295             // RecentChanges).
296             $pagename = urldecode($m[2]);
297             $args = array("action" => $m[1]);
298         }
299     else {
300         $args = SplitQueryArgs($qargs);
301     }
302     
303     if (empty($pagename))
304         $pagename = $GLOBALS['pagename'];
305     
306     if (isset($args['action']) && $args['action'] == 'browse')
307         unset($args['action']);
308     /*FIXME:
309       if (empty($args['action']))
310       $class = 'wikilink';
311       else if (is_safe_action($args['action']))
312       $class = 'wikiaction';
313     */
314     if (empty($args['action']) || is_safe_action($args['action']))
315         $class = 'wikiaction';
316     else {
317         // Don't allow administrative links on unlocked pages.
318         // FIXME: Ugh: don't like this...
319         global $dbi;
320         $page = $dbi->getPage($GLOBALS['pagename']);
321         if (!$page->get('locked'))
322             return QElement('u', array('class' => 'wikiunsafe'),
323                             _("Lock page to enable link"));
324         
325         $class = 'wikiadmin';
326     }
327     
328     // FIXME: ug, don't like this
329     if (preg_match('/=\d*\(/', $qargs))
330         return MakeWikiForm($pagename, $args, $class, $text);
331     if ($text)
332         $text = htmlspecialchars($text);
333     else
334         $text = QElement('span', array('class' => 'rawurl'), $url);
335     
336     return Element('a', array('href'  => WikiURL($pagename, $args),
337                               'class' => $class),
338                    $text);
339 }
340
341 function ParseAndLink($bracketlink) {
342     global $dbi, $AllowedProtocols, $InlineImages;
343     global $InterWikiLinkRegexp;
344     
345     // $bracketlink will start and end with brackets; in between will
346     // be either a page name, a URL or both separated by a pipe.
347     
348     // strip brackets and leading space
349     preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
350     // match the contents 
351     preg_match("/([^|]+)(\|)?([^|]+)?/", $match[2], $matches);
352     
353     if (isset($matches[3])) {
354         // named link of the form  "[some link name | http://blippy.com/]"
355         $URL = trim($matches[3]);
356         $linkname = trim($matches[1]);
357           $linktype = 'named';
358     } else {
359         // unnamed link of the form "[http://blippy.com/] or [wiki page]"
360         $URL = trim($matches[1]);
361         $linkname = '';
362         $linktype = 'simple';
363     }
364     
365     if ($dbi->isWikiPage($URL)) {
366         $link['type'] = "wiki-$linktype";
367         $link['link'] = LinkExistingWikiWord($URL, $linkname);
368     } elseif (preg_match("#^($AllowedProtocols):#", $URL)) {
369         // if it's an image, embed it; otherwise, it's a regular link
370         if (preg_match("/($InlineImages)$/i", $URL)) {
371             $link['type'] = "image-$linktype";
372               $link['link'] = LinkImage($URL, $linkname);
373         } else {
374             $link['type'] = "url-$linktype";
375             $link['link'] = LinkURL($URL, $linkname);
376         }
377     } elseif (preg_match("#^phpwiki:(.*)#", $URL, $match)) {
378         $link['type'] = "url-wiki-$linktype";
379         $link['link'] = LinkPhpwikiURL($URL, $linkname);
380     } elseif (preg_match("#^\d+$#", $URL)) {
381         $link['type'] = "footnote-$linktype";
382         $link['link'] = $URL;
383     } elseif (function_exists('LinkInterWikiLink') &&
384               preg_match("#^$InterWikiLinkRegexp:#", $URL)) {
385           $link['type'] = "interwiki-$linktype";
386           $link['link'] = LinkInterWikiLink($URL, $linkname);
387       } else {
388           $link['type'] = "wiki-unknown-$linktype";
389           global $Theme;
390           $uww = $Theme->linkUnknownWikiWord($URL, $linkname);
391           $link['link'] = $uww->asXML();
392       }
393     
394     return $link;
395 }
396
397
398 function ExtractWikiPageLinks($content) {
399     global $WikiNameRegexp;
400     
401     if (is_string($content))
402         $content = explode("\n", $content);
403     
404     $wikilinks = array();
405     foreach ($content as $line) {
406         // remove plugin code
407         $line = preg_replace('/<\?plugin\s+\w.*?\?>/', '', $line);
408         // remove escaped '['
409         $line = str_replace('[[', ' ', $line);
410         
411         // bracket links (only type wiki-* is of interest)
412         $numBracketLinks = preg_match_all("/\[\s*([^\]|]+\|)?\s*(\S.*?)\s*\]/",
413                                           $line, $brktlinks);
414         for ($i = 0; $i < $numBracketLinks; $i++) {
415             $link = ParseAndLink($brktlinks[0][$i]);
416             if (preg_match("#^wiki#", $link['type']))
417                 $wikilinks[$brktlinks[2][$i]] = 1;
418             
419             $brktlink = preg_quote($brktlinks[0][$i]);
420             $line = preg_replace("|$brktlink|", '', $line);
421         }
422         
423         // BumpyText old-style wiki links
424         if (preg_match_all("/!?$WikiNameRegexp/", $line, $link)) {
425             for ($i = 0; isset($link[0][$i]); $i++) {
426                 if($link[0][$i][0] <> '!')
427                     $wikilinks[$link[0][$i]] = 1;
428             }
429         }
430     }
431     return array_keys($wikilinks);
432 }      
433
434 function LinkRelatedPages($dbi, $pagename)
435 {
436     // currently not supported everywhere
437     if(!function_exists('GetWikiPageLinks'))
438         return '';
439     
440     //FIXME: fix or toss?
441     $links = GetWikiPageLinks($dbi, $pagename);
442     
443     $txt = QElement('strong',
444                     sprintf (_("%d best incoming links:"), NUM_RELATED_PAGES));
445     for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
446         if(isset($links['in'][$i])) {
447             list($name, $score) = $links['in'][$i];
448             $txt .= LinkExistingWikiWord($name) . " ($score), ";
449         }
450     }
451     
452     $txt .= "\n" . Element('br');
453     $txt .= Element('strong',
454                     sprintf (_("%d best outgoing links:"), NUM_RELATED_PAGES));
455     for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
456         if(isset($links['out'][$i])) {
457             list($name, $score) = $links['out'][$i];
458         if($dbi->isWikiPage($name))
459                 $txt .= LinkExistingWikiWord($name) . " ($score), ";
460         }
461     }
462     
463     $txt .= "\n" . Element('br');
464     $txt .= Element('strong',
465                     sprintf (_("%d most popular nearby:"), NUM_RELATED_PAGES));
466     for($i = 0; $i < NUM_RELATED_PAGES; $i++) {
467         if(isset($links['popular'][$i])) {
468             list($name, $score) = $links['popular'][$i];
469         $txt .= LinkExistingWikiWord($name) . " ($score), ";
470         }
471     }
472     
473     return $txt;
474 }
475
476
477 /**
478  * Split WikiWords in page names.
479  *
480  * It has been deemed useful to split WikiWords (into "Wiki Words") in
481  * places like page titles. This is rumored to help search engines
482  * quite a bit.
483  *
484  * @param $page string The page name.
485  *
486  * @return string The split name.
487  */
488 function split_pagename ($page) {
489     
490     if (preg_match("/\s/", $page))
491         return $page;           // Already split --- don't split any more.
492     
493     // FIXME: this algorithm is Anglo-centric.
494     static $RE;
495     if (!isset($RE)) {
496         // This mess splits between a lower-case letter followed by
497         // either an upper-case or a numeral; except that it wont
498         // split the prefixes 'Mc', 'De', or 'Di' off of their tails.
499         $RE[] = '/([[:lower:]])((?<!Mc|De|Di)[[:upper:]]|\d)/';
500         // This the single-letter words 'I' and 'A' from any following
501         // capitalized words.
502         $RE[] = '/(?: |^)([AI])([[:upper:]][[:lower:]])/';
503         // Split numerals from following letters.
504         $RE[] = '/(\d)([[:alpha:]])/';
505         
506         foreach ($RE as $key => $val)
507             $RE[$key] = pcre_fix_posix_classes($val);
508     }
509     
510     foreach ($RE as $regexp)
511         $page = preg_replace($regexp, '\\1 \\2', $page);
512     return $page;
513 }
514
515 function NoSuchRevision ($page, $version) {
516     $html = Element('p', QElement('strong', gettext("Bad Version"))) . "\n";
517     $html .= QElement('p',
518                       sprintf(_("I'm sorry.  Version %d of %s is not in my database."),
519                               $version, $page->getName())) . "\n";
520     
521     include_once('lib/Template.php');
522     echo GeneratePage('MESSAGE', $html, _("Bad Version"));
523     ExitWiki ("");
524 }
525
526
527 /**
528  * Get time offset for local time zone.
529  *
530  * @param $time time_t Get offset for this time. Default: now.
531  * @param $no_colon boolean Don't put colon between hours and minutes.
532  * @return string Offset as a string in the format +HH:MM.
533  */
534 function TimezoneOffset ($time = false, $no_colon = false) {
535     if ($time === false)
536         $time = time();
537     $secs = date('Z', $time);
538     if ($secs < 0) {
539         $sign = '-';
540         $secs = -$secs;
541     }
542     else {
543         $sign = '+';
544     }
545     $colon = $no_colon ? '' : ':';
546     $mins = intval(($secs + 30) / 60);
547     return sprintf("%s%02d%s%02d",
548                    $sign, $mins / 60, $colon, $mins % 60);
549 }
550
551 /**
552  * Format time in ISO-8601 format.
553  *
554  * @param $time time_t Time.  Default: now.
555  * @return string Date and time in ISO-8601 format.
556  */
557 function Iso8601DateTime ($time = false) {
558     if ($time === false)
559         $time = time();
560     $tzoff = TimezoneOffset($time);
561     $date  = date('Y-m-d', $time);
562     $time  = date('H:i:s', $time);
563     return $date . 'T' . $time . $tzoff;
564 }
565
566 /**
567  * Format time in RFC-2822 format.
568  *
569  * @param $time time_t Time.  Default: now.
570  * @return string Date and time in RFC-2822 format.
571  */
572 function Rfc2822DateTime ($time = false) {
573     if ($time === false)
574         $time = time();
575     return date('D, j M Y H:i:s ', $time) . TimezoneOffset($time, 'no colon');
576 }
577
578 /**
579  * Format time to standard 'ctime' format.
580  *
581  * @param $time time_t Time.  Default: now.
582  * @return string Date and time in RFC-2822 format.
583  */
584 function CTime ($time = false)
585 {
586     if ($time === false)
587         $time = time();
588     return date("D M j H:i:s Y", $time);
589 }
590
591
592
593 /**
594  * Internationalized printf.
595  *
596  * This is essentially the same as PHP's built-in printf
597  * with the following exceptions:
598  * <ol>
599  * <li> It passes the format string through gettext().
600  * <li> It supports the argument reordering extensions.
601  * </ol>
602  *
603  * Example:
604  *
605  * In php code, use:
606  * <pre>
607  *    __printf("Differences between versions %s and %s of %s",
608  *             $new_link, $old_link, $page_link);
609  * </pre>
610  *
611  * Then in locale/po/de.po, one can reorder the printf arguments:
612  *
613  * <pre>
614  *    msgid "Differences between %s and %s of %s."
615  *    msgstr "Der Unterschiedsergebnis von %3$s, zwischen %1$s und %2$s."
616  * </pre>
617  *
618  * (Note that while PHP tries to expand $vars within double-quotes,
619  * the values in msgstr undergo no such expansion, so the '$'s
620  * okay...)
621  *
622  * One shouldn't use reordered arguments in the default format string.
623  * Backslashes in the default string would be necessary to escape the
624  * '$'s, and they'll cause all kinds of trouble....
625  */ 
626 function __printf ($fmt) {
627     $args = func_get_args();
628     array_shift($args);
629     echo __vsprintf($fmt, $args);
630 }
631
632 /**
633  * Internationalized sprintf.
634  *
635  * This is essentially the same as PHP's built-in printf with the
636  * following exceptions:
637  *
638  * <ol>
639  * <li> It passes the format string through gettext().
640  * <li> It supports the argument reordering extensions.
641  * </ol>
642  *
643  * @see __printf
644  */ 
645 function __sprintf ($fmt) {
646     $args = func_get_args();
647     array_shift($args);
648     return __vsprintf($fmt, $args);
649 }
650
651 /**
652  * Internationalized vsprintf.
653  *
654  * This is essentially the same as PHP's built-in printf with the
655  * following exceptions:
656  *
657  * <ol>
658  * <li> It passes the format string through gettext().
659  * <li> It supports the argument reordering extensions.
660  * </ol>
661  *
662  * @see __printf
663  */ 
664 function __vsprintf ($fmt, $args) {
665     $fmt = gettext($fmt);
666     // PHP's sprintf doesn't support variable with specifiers,
667     // like sprintf("%*s", 10, "x"); --- so we won't either.
668     
669     if (preg_match_all('/(?<!%)%(\d+)\$/x', $fmt, $m)) {
670         // Format string has '%2$s' style argument reordering.
671         // PHP doesn't support this.
672         if (preg_match('/(?<!%)%[- ]?\d*[^- \d$]/x', $fmt))
673             // literal variable name substitution only to keep locale
674             // strings uncluttered
675             trigger_error(sprintf(_("Can't mix '%s' with '%s' type format strings"),
676                                   '%1\$s','%s'), E_USER_WARNING);
677         
678         $fmt = preg_replace('/(?<!%)%\d+\$/x', '%', $fmt);
679         $newargs = array();
680         
681         // Reorder arguments appropriately.
682         foreach($m[1] as $argnum) {
683             if ($argnum < 1 || $argnum > count($args))
684                 trigger_error(sprintf(_("%s: argument index out of range"), 
685                                       $argnum), E_USER_WARNING);
686             $newargs[] = $args[$argnum - 1];
687         }
688         $args = $newargs;
689     }
690     
691     // Not all PHP's have vsprintf, so...
692     array_unshift($args, $fmt);
693     return call_user_func_array('sprintf', $args);
694 }
695
696
697 // Class introspections
698
699 /** Determine whether object is of a specified type.
700  *
701  * @param $object object An object.
702  * @param $class string Class name.
703  * @return bool True iff $object is a $class
704  * or a sub-type of $class. 
705  */
706 function isa ($object, $class) 
707 {
708     $lclass = strtolower($class);
709
710     return is_object($object)
711         && ( get_class($object) == strtolower($lclass)
712              || is_subclass_of($object, $lclass) );
713 }
714
715 /** Determine whether (possible) object has method.
716  *
717  * @param $object mixed Object
718  * @param $method string Method name
719  * @return bool True iff $object is an object with has method $method.
720  */
721 function can ($object, $method) 
722 {
723     return is_object($object) && method_exists($object, strtolower($method));
724 }
725
726 // (c-file-style: "gnu")
727 // Local Variables:
728 // mode: php
729 // tab-width: 8
730 // c-basic-offset: 4
731 // c-hanging-comment-ender-p: nil
732 // indent-tabs-mode: nil
733 // End:   
734 ?>