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